Feb 122010
 

Google Chrome recently opened up their extensions API. Though Chrome had supported extensions quite early on in its lifecycle, the API was still a work in progress and one needed to open Chrome in the developer mode to play around. A few weeks back, Chrome announced that extensions are now enabled in normal mode as well.

So I decided to poke around the API and write a simple extension. Though it doesnt compare to FireFox yet with much of the browser still not accessible, but I am sure future versions will bring much better customization capability. The extension I wrote is very simple and just displays the latest trending topics from twitter. The source can be downloaded here and the packaged version here.

Lets see the anatomy of a Chrome Extension. An Extension consists of three main parts

  • Manifest
  • UI
  • All your other files like scripts, stylesheets, images, etc

The manifest is a json file which contains the metadata about your extension. Details like Title, Author etc

{
  "name": "Twitter Trends",
  "version": "1.0",
  "description": "Displays Current Trending Topics",
  "icons": { "128": "icon.png" },
  "browser_action": {
    "default_title": "Twitter Trends",
    "default_icon": "icon.png",
    "popup": "Background.html"
  },
  "permissions": [
    "tabs",
    "http://twitter.trends.free.fr/*"
  ]
}

As you can see, it contains basic details like name, version, icon etc. The popup attribute tells us the page to open when the browser button is clicked. As you can see I have specified a normal HTML page here. A popup extension is nothing but a simple HTML page opened when the toolbar button is clicked. Another interesting part is the permissions section. Here we can specify what all websites the extension can make AJAX calls to retrieve data. Since it matches based on wildcards, a * denotes all possible subdirectories under the website.

Now the HTML part. Since its a normal HTML page, you can include references to scripts, images, style sheets etc. The extension directory works as the root and all paths must be relative to it. Since its a simple page, I included all the scripts and styling information in a single page.

First the styling information. Always helps to keep this separate.

  body {
  font-family: helvetica, arial, sans-serif;
  font-size: 12px;
  overflow: hidden;
  background-color:#ddeef6;
  }
 
  #title
  {
      font-size:14px;
      overflow:hidden;
  }
 
  #mainDiv
  {
      min-width:175px;
      margin-top:5px;
  }
 
  .topicDisplayClass
  {
      display:block;
     min-height:2.2em;
     cursor:pointer;
  }
 
  .linkClass
  {
      color:#2276bb;
      text-decoration:none;
      vertical-align:middle;
      padding-left:2px;
  }
 
  .topicDisplayClass:Hover
  {
      background-color:#edfeff;
  }

Next the javascript. This is the most important part of the extension. What we do here is retrieve the RSS feed for the trending topics and parse it as an xmlDocument. After iterating thorough this xml, a span element is dynamically created for each item and it is appended into our main content holder. This main function call is delayed using the setTimeout function. We do this in order to let the user see the popup and see some progress which data is being fetched. Writing this in the onload event would make the user wait for the whole process to complete before seeing the popup.

/* Developer  : Ganesh Ranganathan
* Date       : 02/12/2010
* Description: This is a Google Chrome Extension created to display
            latest trending topics on twitter. */
 
//Global variables: the xmlHttpRequest and Feed URL
var _req;
var _feedURL = 'http://twitter.trends.free.fr/feed/';
 
function main() {
    //Call the loading method after a slight delay to allow
    //Chrome to load the extension body. Else it looks ugly with
    //the pressed button waiting for the feed to load.
    setTimeout("delayedCall()", 20);
 
}
 
function delayedCall() {
    //No browser compatibility code Yay!!!
    _req = new XMLHttpRequest();
    //Hooking up the readystate changed event handler.
    _req.onreadystatechange = function() {
        if (_req.readyState == 4 && _req.status == 200) {
            //Come here only if all data is loaded.
            var doc = _req.responseText;
            var parser = new DOMParser();
            //Parsing the Data into the XML dom for manipulation
            var xmlDoc = parser.parseFromString(doc, "text/xml");
            var mainDiv = document.getElementById('mainDiv');
            mainDiv.innerHTML = "";
            //Getting all the items
            var items = xmlDoc.getElementsByTagName('item');
 
            for (var i = 0; i < items.length; i++) {
                //Iterating through all the items and getting the title
                //and link. The description isnt a big deal in this feed.
                var title = items[i].getElementsByTagName('title');
                var link = items[i].getElementsByTagName('link');
                //Create an anchor and a span
                var spanNode = document.createElement('span');
                var linkNode = document.createElement('a');
                //Set Link to span
                linkNode.setAttribute('href', link);
                linkNode.innerText = title[0].childNodes[0].nodeValue;
                linkNode.className = 'linkClass';
                spanNode.appendChild(linkNode);
                spanNode.className = 'topicDisplayClass';
                var str = link[0].childNodes[0].nodeValue
                //Hooking the click event handler to the span
                //It calls the chrome tabs API to open the clikced
                //URL in a new tab
                spanNode.addEventListener("click",new Function("clickHandler('"+str+"')"),false);
                mainDiv.appendChild(spanNode);
            }
        }
    }
 
    //Without these two the AJAX request would never get sent.
    _req.open("GET", _feedURL, true);
    _req.send(null);
}
function clickHandler(str) {
    chrome.tabs.create({ url: str });
}

The markup is largely simple. Just the main content holder which contains a progress bar which is displayed to user while we load the data. Once that is done, the progress bar is replaced with the span nodes containing the trending topics.

<span id="title">Trending Topics</span>
<div id="mainDiv">
<!-- A Progress Bar to show the user something is being done -->
<img src="ajax-loader.gif" alt="" width="175px" /></div>

Now the manifest, html and image files need to be in a single folder. After that go to Chrome and click the customize dropdown button, and select Extensions. In the extensions page, select Load Unpacked Extension and select the folder where the extension files reside.

Once the extension is loaded, it shows as unpacked and you can do all the testing here by reloading it everytime any changes are done.

After testing, we can choose the pack option to package the extension for download. A .crx file would be generated which can be distributed for download. Below are some screenshots of the extension we just created.

Data is loading