Sergio and the sigil

Chrome Extension Development

Posted by Sergio on 2010-06-28

Do you like JavaScript? Have you been looking for a reaon or an idea to learn and start using HTML5? Google Chrome extesions are a great way to get into HTML5 and all its new APIs with bite sized applications.

Anatomy of a Chrome Extension

A Chrome extesion is nothing more than a tiny website that runs hosted inside Chrome. Like any website, it consists of regular web components. Grossly simplifying it is just a directory full of files, such as JavaScript, CSS, images, HTML5, and anything else you usually add to a web page.

Because it runs inside the browser, you have access to things like browser events, browsing history, and open tabs.

Essentially, your extension gets loaded as an HTML page called background.html where you can put any common/global functions and variables. This page is never visible and even if you don't provide a background.html in your extension, Chrome will load an empty one for you. All other pages in your extension can access the background page's functions.

Here's a diagram of a common scenario.

Today's Brew

Our sample extension will be something simple but that at the same time will let us explore interesting aspects of extensions development, namely local storage and cross-domain requests.

The sample is also a hat tip to the valuable work Chris Alcock does with his The Morning Brew, collecting some of the best links for all of us.

What we will do here is create an extension that will show us his latests links at the click of a button.

Introduce yourself

The extension needs to inform Chrome a few details about itself, such as name, default icons, along with any permissions it requests to access privileged browser features. This is done through the manifest file, which is just a JSON document. Here's our manifest file, aptly named manifest.json.

{
  "name": "Today's Brew",
  "version": "1.0",
  "description": "Latest links from The Morning Brew.",
  "icons": { 
    "48": "icon48.png",
    "128": "icon128.png" 
  },
  "browser_action": {
    "default_icon": "icon.png",
    "default_popup": "popup.html"
  },
  "permissions": [
    "http://feeds.feedburner.com/",
    "tabs"
  ]
}

What you can do

Extensions can perform several different jobs, like showing notifications, providing new themes, changing some of the default Chrome pages, etc. One other thing they can do are the browser actions, which basically means adding a button to the toolbar that acts independent of what page is being shown, as if it were just another browser feature.

If you look at our manifest file you'll notice we declared a browser_action, with its icon and the page it opens wen clicked.

Our extension does not have a custom background page, we will just rely on the default one Chrome will give us and put all of our logic in the popup page that we will open.

What you need to do that

Here's how our extension will look like when we're done.

In our extension, other than manipulating our own extension's HTML dynamically (which doesn't require any special permissions) we will need to fetch the RSS feed from Feedburner and eventually open new tabs as the user clicks on the links.

That's what you can see in the permissions section of the manifest file above.

Getting down to business

To create our extension we start by creating an empty directory and adding our manifest.json file and all the icon image files that we mentioned in that manifest file.

Then we create our popup.html file, which will be pretty empty and will be populated with the content we will retrieve from the RSS feed. Here's that file.

<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="popup.js"></script>
<div id="main">
  <h1></h1>
  <div id="content">
  </div>
</div>

As you can see, we will be using jQuery so we should also add that file to our directory. We will leave all the beautification of the popup in the popup.css file, which I won't bother showing here; you can download it along with the rest of the code at the end of this article.

The other file referenced by the HTML is popup.js. That's where all the action happens.

$(document).ready(function(){
  SERGIOPEREIRA.samples.todaysBrew.init();
});

var SERGIOPEREIRA = {
  samples: {
    todaysBrew: {
      state: { },
      feedUrl: 'http://feeds.feedburner.com/ReflectivePerspective?format=xml',
      todaysUrl: '',
      maxAgeHours: 12, // keep it for 12 hours
      
      init: function(){
        $('#content h3').live('click', function(){
          $('#content ul:visible').slideUp();
          $(this).next().slideToggle();
        });

        $('#content li a').live('click', function(){
          $('#content ul:visible').slideUp();
          chrome.tabs.create({url: this.href});
        });

        $('h1').click(function(){
          chrome.tabs.create({url: SERGIOPEREIRA.samples.todaysBrew.todaysUrl});
        });
        
        if(typeof chrome != 'undefined') {
          this.state = localStorage.getItem('latestBrew');

          if(this.state){
            var now = new Date();
            var minTimestamp = new Date();
            minTimestamp.setHours(minTimestamp.getHours() - this.maxAgeHours);
            minTimestamp = minTimestamp.toJSON();

            if(this.state.timestamp > minTimestamp) {
              this.renderLatest(this.state.latestData);
              return;
            }
          }
          this.refresh();
        }
      },
      refresh: function(){
        console.log('will get feed data...');
        $.get(this.feedUrl, function(xml, status, xhr){
          SERGIOPEREIRA.samples.todaysBrew.update(xml);
        });
      },
      update: function(feedXml) {
        var latest = this.getFirstItem(feedXml);
        this.state = { };
        this.state.latestData = latest;
        this.state.timestamp = new Date();
        localStorage['latestBrew'] = JSON.stringify(this.state);
        this.renderLatest(latest);
      },
      renderLatest: function(latest){
        $('#main>h1').text(latest.title);
        $('#content').html(latest.content);
        this.todaysUrl = latest.url;
      },      
      getFirstItem: function(feedXml){
        var items = feedXml.evaluate("//channel/item", feedXml, 
                                 null, XPathResult.ANY_TYPE, null); 

        var item = items.iterateNext(); 
        if (item) {
          return this.createItem(item);
        }
      },      
      createItem: function(postXml) {
        return { 
          title: this.readElementText(postXml, 'title'),
          url: this.readElementText(postXml, 'feedburner:origLink'),
          content: this.readElementText(postXml, 'content:encoded')
        };
      },
      mapElements: function(contextElement, path, map){
        var result = [ ];
        var items = contextElement.ownerDocument.evaluate(path, contextElement, 
                                this.namespaceResolver, XPathResult.ANY_TYPE, null); 
        
        var item = items.iterateNext(); 
        var i = 0;
        while (item) {
          result.push( map(item, i++) );
          item = items.iterateNext();
        }

        return result;
      },      
      readElementText: function(contextElement, path){
        var results = contextElement.ownerDocument.evaluate(path, contextElement, 
                                   this.namespaceResolver, XPathResult.ANY_TYPE, null); 
        var first = results.iterateNext(); 

        if (first) {
          return first.textContent;
        }
      },      
      namespaceResolver: function(prefix) {
        if(prefix == 'content') {
          return 'http://purl.org/rss/1.0/modules/content/';
        }
        if(prefix == 'feedburner') {
          return 'http://rssnamespace.org/feedburner/ext/1.0';
        }
      }

    }
  }
};

Wow, that's a lot of JavaScript at once, right? Hopefully you'll notice that most of it is just to parse the RSS xml.

Only a few parts of this code deserve commentary. The refresh method (line 45) is the one that retrieves the RSS data. It uses the jQuery.get method to do so. Once the data arrives, it will invoke update, which will use the parsing methods to get an object representing the latest news item in the data.

      update: function(feedXml) {
        var latest = this.getFirstItem(feedXml);
        this.state = { };
        this.state.latestData = latest;
        this.state.timestamp = new Date();
        localStorage['latestBrew'] = JSON.stringify(this.state);
        this.renderLatest(latest);
      },

The above code also shows the use of two important APIs that Chrome implements. The localStorage is a way to persist information that lives in the client machine and lasts even after the browser closes. We use it to remeber our last results and avoid fetching and parsing the RSS each time the popup is opened.

The other API is the native JSON object that can replace any dedicated library we are used to have in cross-browser websites. We need to stringify the data because we can only save strings in the local storage.

With the parsed data at hand we just need to replace the content in those empty html tags in popup.html with the information we have.

Adding some life with events

The last piece of this puzzle are the jQuery event handlers that we created in the init method. They make some of the elements clickable, including the links, which open new tabs using chrome.tabs.create(). Also note starting on line 29 that if we find recent local data we use that instead of refreshing the content from the RSS feed.

Let's load this thing up

Now we just need to run it and see how it goes. Go to the extesions page, expand the Developer Mode area and click Load unpacked extension....

Then simply browse to your extension's directory and select it. The extension should now be listed as seen below.

While you're developing your extension you can debug it using the developer tools included in Chrome. To do that, right click the extension button and select Inspect popup.

You can download the code for this extension and play with it all you want. I'm still early into learning this so feel free to give me pointers or ask questions.

jQuery Custom Element and Global Events

Posted by Sergio on 2010-02-21

This last week I learned a new thing about jQuery custom events, particularly the ones of global nature. There's good documentation and examples about custom element events, but not much for the global ones.

Why do we need custom events?

Custom events make it easier to keep complex pages under control. They are a pillar for loosely-coupled UI scripts. Let's start with a simple example.

Suppose we have a fairly complex and dynamic page where many elements are Ajax-editable, using in-place editors or any other approach that posts updates to the server. Depending on how quickly the server responds to the request, there's a chance the user can start another simultaneous request before the first one finishes, maybe even seeing inconsistent results, by clicking a button too soon.

In our example — a fraction of what a real complex page would be — what we want to do is disable some of these buttons while the data is being changed, and re-enable them once we hear back from the server.

Click the field to edit it:<br>

<input type="text" readonly="readonly" id="email" name="email"
   value="joe@doe.com" style="background-color: #eee;"/> 

<input type="button" class="userOperation" id="sendButton" value="Send Message">
<input type="button" class="userOperation" id="summaryButton" value="Summary">

Custom Element Events

Let's tackle this problem first with the custom element events. Below is a summary of how these custom events are used.

$('#publisher').trigger('eventName');

$('#publisher1').bind('eventName', function() {
   //eventName happened. React here.
   $('#subscriber1').doStuff();
   $('#subscriber2').doOtherStuff();
   // more...
});

In this case we will make the elements being edited announce that they entered edit mode so that any other element can act on that announcement.

$('#email').
click(function(){
	$(this).removeAttr('readonly').css({backgroundColor: ''});
	$(this).trigger('editStart');
}).
blur(function(){
	$(this).attr('readonly', 'readonly').css({backgroundColor: '#eee'});
	$.post('/updateEmail', $('#email').serialize(), function() {
		$(this).trigger('editComplete');
	});
}).
bind('editStart', function(){
	// "this" is the #email element
	console.log('edit started, this =  ' + this.id);
	$('.userOperation').attr('disabled', 'disabled');
}).
bind('editComplete', function(){
	// "this" is the #email element
	console.log('edit complete, this =  ' + this.id);
	$('.userOperation').removeAttr('disabled');		
});

$('#sendButton').click(function(){
	//code to send a message
	alert('Message sent');
});

$('#summaryButton').click(function(){
	//code to generate summary
	alert('Summary created');
});

This approach works well in the beginning but gets really ugly as more elements need to publish their own similar events or when other new elements need to do somethings with these events too. We will need to bind handlers to all these element's events and the code inside these handlers will start getting longer and probably too far from the rest of the code that relates to it.

One step forward with page level events

Since the events we are producing here really reflect the document state more than any individual field's state, let's move that event to a more top level element, namely the body element:

$('#email').
click(function(){
	$(this).removeAttr('readonly').css({backgroundColor: ''});
	$('body').trigger('editStart');
}).
blur(function(){
	$(this).attr('readonly', 'readonly').css({backgroundColor: '#eee'});
	$.post('/updateEmail', $('#email').serialize(), function() {
		$('body').trigger('editComplete');
	});
});

$('body').
bind('editStart', function(){
	// "this" is the body element
	console.log('edit started, this =  ' + this.tagName);
	$('.userOperation').attr('disabled', 'disabled');
}).
bind('editComplete', function(){
	// "this" is the body element
	console.log('edit complete, this =  ' + this.tagName);
	$('.userOperation').removeAttr('disabled');		
});

$('#sendButton').click(function(){
	//code to send a message
	alert('Message sent');
});

$('#summaryButton').click(function(){
	//code to generate summary
	alert('Summary created');
});

Now we're getting somewhere. We reduced the number of event sources to just one, so guaranteed less duplication. But it still has some shortcomings.

The code is still bound to a different element than the one we want to operate on. What I mean by that is that the event handlers are in the context of the elements publishing the event and the code in the handlers is typically geared towards the elements that need to react to that event, that is, the this keyword is less useful than in most of your common event handlers.

The pattern of these page-level events is:

$('body').trigger('eventName');

$('body').bind('eventName', function() {
   //eventName happened. React here.
   $('#subscriber1').doStuff();
   $('#subscriber2').doOtherStuff();
   // more...
});

But wait, jQuery has real global events too

I had settled down with using the above style of global events until someone at work pointed out that there's another way of doing this, which unfortunately isn't as well discussed: the custom global events.

Here's our code using global custom events:

$('#email').click(function(){
	$(this).removeAttr('readonly').css({backgroundColor: ''});
	$.event.trigger('editStart');
}).blur(function(){
	$(this).attr('readonly', 'readonly').css({backgroundColor: '#eee'});
	$.post('/updateEmail', $('#email').serialize(), function() {
		$.event.trigger('editComplete');
	});
});

$('.userOperation').bind('editStart', function(){
	// "this" is a .userOperation button
	console.log('edit started, button: ' + this.id);
	$('.userOperation').attr('disabled', 'disabled');
}).bind('editComplete', function(){
	// "this" is a .userOperation button
	console.log('edit complete, button: ' + this.id);
	$('.userOperation').removeAttr('disabled');		
});

$('#sendButton').click(function(){
	//code to send a message
	alert('Message sent');
});

$('#summaryButton').click(function(){
	//code to generate summary
	alert('Summary created');
});

What is great about this type of event is that they are in the context of the subscribing elements, as if these elements were the publishers of the event, much like the majority of the event handling code we write.

They also allow us to move more code next to the other event handler for the subscribing elements, and even chain them all together. As an example, let's modify the event handlers of the #sendButton element to add some different behavior when the editStart event happens.

$('#sendButton').click(function(){
	//code to send a message
	alert('Message sent');
}).bind('editStart', function(){
	// "this" is the #sendButton button
	this.value = 'Send message (please refresh)';
	// change the click event handler.
	$(this).unbind('click').click(function(){
		alert('Sorry, refresh page before sending message');
	});
});

And here is the simplified representation of the global events code.

$.event.trigger('eventName');

$('#subscriber1').bind('eventName', function() {
   //eventName happened. React here.
   $(this).doStuff();
});

$('#subscriber2').bind('eventName', function() {
   //eventName happened. React here.
   $(this).doOtherStuff();
});
//more...

Conclusion

Event-based programming is the usual way we write UI code. By understanding the different types of events that jQuery provides we can allow our UI to grow without getting into a messy nightmare of event handling code scattered all over the place.

Guided Tour: jQuery - Array wannabes

Posted by Sergio on 2009-12-22
This post is part of a series called the Guided Tours.

In this second installment we are still looking inside the jQuery code. Trust me, even if it's hard to digest, you can still learn enough if you focus on a little bit at a time.

The code we are interested in today is the following.

//from jQuery 1.3.2
get: function( num ) {
	return num === undefined ?

		// Return a 'clean' array
		Array.prototype.slice.call( this ) : 

		// Return just the object
		this[ num ];
},

That's the code for the command jQuery.fn.get(index), which returns the element at the given index or the entire array if the index is omitted.

The JavaScript idiom that is really interesting in this function is that strangely long function call Array.prototype.slice.call( this ).

That line is needed because we need to return an array and, even though they look like one, the jQuery objects aren't arrays. And they aren't alone in that.

If it walks like a duck...

The Array object is one that we can't avoid becoming familiar with in JavaScript. They are everywhere. Data is passed to functions as arrays. Data is returned in arrays. Or are they?

Aside from jQuery objects there are at least two other important occurrences of data structures that are used like arrays but really aren't: the arguments variable and the DOM NodeList collections.

The arguments variable is the list of parameters passed to the current function and the NodeList is what is returned from members of the the DOM API such as document.getElementsByTagName() or element.childNodes. Both of these types have a length property and expose their items with indices, like arguments[1] or elements[0].

But don't let this small coincidence fool you. As soon as you stop paying attention and try to use another array method, like push, shift, join you'll have your dreams shattered and a TypeErrorto handle.

Help me, jQuery

To solve the above problem and return a real array object instead of the jQuery object itself, the code used the technique we highlighted.

Array.prototype.slice.call( this )

The idea is to use the array.slice() instance method to create a new array. The slice method returns a chunk of the array and it takes two optional parameters (the boundaries) that, when omitted, make the function return a copy of the array itself.

I've written about the prototype object

Guided Tour: jQuery - guard and default operators

Posted by Sergio on 2009-12-09
This post is part of a series called the Guided Tours.

I'm sure I'm not alone when I say that one of the best ways to improve our coding skills is by reading code written by someone else. Sometimes we don't realize how lucky we are to have so much source code at our fingertips, namely Open Source Software.

The guided tours

With this post I'll start another unbound series where I highlight some interesting piece of code that I studied. I'll share my notes and do my best to explain what I learned from it.

jQuery: Eating the elephant one bite at a time

I wouldn't dare to start things off with a complete overview of jQuery. It's a massive chunk of JavaScript and I'm afraid there isn't an easy entry point in the source code.

I chose to find parts of it that are relatively easy to explain separately from the rest and that contain something worth explaining. I'll do at least a few of those from jQuery but my plan is to not keep this series tied to jQuery or even JavaScript.

The code under the microscope

We're going to take a look at jQuery.fn.text(), which returns the textual content of all the elements in the jQuery wrapped set, combined in a single string.

Here's the code from jQuery version 1.3.2.

text: function( text ) {
  if ( typeof text !== "object" && text != null )
    return this.empty().append( 
	   (this[0] && this[0].ownerDocument || document).createTextNode( text ) 
	);

  var ret = "";

  jQuery.each( text || this, function(){
    jQuery.each( this.childNodes, function(){
      if ( this.nodeType != 8 )
        ret += this.nodeType != 1 ?
          this.nodeValue :
          jQuery.fn.text( [ this ] );
    });
  });

  return ret;
},

The text() function can be called with or without arguments, so initially it tries to detect if a string argument was passed to it, in which case it will be made the content of the elements in the jQuery object.

Be aware that in all methods defined inside jQuery.fn the value of this will be the current jQuery object.

The first thing that caught my attention was the following expression:

(this[0] && this[0].ownerDocument || document)

In the context of the code it's expected to return a DOM document object, but it's a boolean expression, isn't it? What gives?

Well, yes, it is a boolean expression. That leads me to explain a subtle but powerful difference between boolean operators in JavaScript to many other languages you may be more used to.

Guard and Default operators

The way I like to describe the boolean operators && and || is: They return the operand that short-circuited and resolved the expression. To put in a different way, when the JS interpreter detects that one of the operands has a value that makes the remainder of the comparison irrelevant, it stops right there and that operand (not necessarily true or false) becomes the result of the boolean expression.

Truthy and Falsy: In the context of a boolean expression, any value in JavaScript has a boolean meaning. It's actually easy to memorize which mean which. The values that are treated as false are: false, null, undefined, 0, "" (empty string), and NaN. We call them falsy. Everything else is treated as true and we call them truthy.

Here are some examples:

ABA && B
false123false
null123null
01230
undefined123undefined
undefinednullundefined
123nullnull
123456456
123"text""text"
"text"truetrue
ABA || B
false123123
null123123
0123123
undefined123123
undefinednullnull
123null123
123456123
123"text"123
"text"true"text"

Because of the above behavior, these boolean operators are often used as Guard or Default operators. The guard operation is commonly used when you want to avoid a null or undefined reference error:

//someObj can be null. text will also be null in that case.
var text = someObj && someObj.toString();

Which is a shorthand for:

var text = null;
if (someObj !== null) {
  text = someObj.toString();
}

The default operation is arguably a much more common occurrence. We see it a lot when functions support optional arguments with default values. When a value is not given for a function parameter, it becomes undefined. We can detect that and give a default value like this:

function addAll(numbersArray, step) {
  step = step || 1;
  var sum = 0;
  for (var i = 0; i < numbersArray.length; i += step) {
    sum += numbersArray[i];
  }
  return sum;
}
addAll([1, 2, 3, 4, 5, 6]); // ==> 21
addAll([1, 2, 3, 4, 5, 6], 3); // ==> 5

In the above example, the step parameter is optional. Not providing it would cause a problem if we hadn't defaulted it to one right at the beginning of the function.

Wow. We sure covered a lot of stuff just to explain a simple boolean expression. The good news is that we will see a lot of that in the jQuery code (or in pretty much any decent JS code base) so it's good to understand it well.

Back to our original expression.

(this[0] && this[0].ownerDocument || document)

Armed with our new understanding we can finally read this expression as: If this.ownerDocument exists I want that, otherwise just give me the global document object. This returned DOM document object will be the owner document of new text value being inserted.

What about the rest of that function

It's funny that a tiny bit of that function became this long post. But the remainder of the function, in its majority, isn't really all that interesting in terms of JavaScript. It's mostly boring DOM navigation, done recursively. If you know how to use the jQuery.each() utility function, you can figure out that code on your own.

Our time here is up and we have a whole lot more of code sightseeing to do.