The amazing adventures of Doug Hughes

Archive for August, 2010

Google Chrome Extensions are Crazy Easy

Occasionally I have to work on web applications that are horribly slow. Some complex applications can take several minutes to completely reload. Im sure Im not alone in this experience. And so, when working on these sites, what do you do while waiting for stuff to load? If you’re like me, you go to another tab and work on something else while you wait for that tab to load (or you troll around Twitter, etc).

This isnt really a problem except that more often than not I fail to notice when the other tab has finished loaded and end up wasting time. Because of this, Ive long wished that there were a tab extension that would could play a sound when a tab finishes loading. However, look as I may, I never found anything that did what I wanted.

In the past Ive considered writing a simple extension for this. The thing is, until recently Ive been a fairly diehard Firefox user. Having written a now-defunct Firefox extension, I know what a flaming pain in the ass it is to write Firefox extensions. There was no way I was going to write this extension for Firefox.

However, I recently decided to take Google Chrome for a real test drive and Ive been using it exclusively for a few weeks. During this period I also began working on a website that takes a very long time to reload. So long, in fact, that I became frustrated enough to go and research whats involved in making extensions for Chrome. And, as it turns out, theres not a whole lot involved!

In Firefox, when you write an extension you can change or override just about any feature of the browser. For example, if you dont like how bookmarks system works, you can pretty much replace it with something you do like. The thing is, with all that power and flexibility comes a boatload of complexity. I really dont even have the patience to write about the hoops I had to jump through to get started. Let it suffice to say that I had to run the browser in a different test user account and, if I recall correctly, reload the browser each time I made a chance to my extension. Then theres the fact that Firefox extensions are written using XUL (XML User Interface Language). And there was more fun too. In summary: Its a pain and no fun at all.

Chrome, by contrast, is much more limiting in what you can do. However, its API is a dream to work with. In Chrome most extensions are either browser actions, which are browser-wide, or page actions, which only affect the current page. Both of these are quite easy to write, but Ill focus on browser actions since thats what I wrote.

In addition to the two basic extension points, Chrome also provides a nice API for interacting with browser features such as bookmarks and tabs. Like everything else in Chrome, these APIs are very simple and easy to use.

At a minimum, Chrome extensions are made up of HTML files and a manifest file written in JSON. The manifest defines basic information like the name, version, description, icons, etc.

It also defines a background page. A background page is a page that is loaded by the extension and runs constantly behind the scenes. This is useful for storing state information and providing the core features of the plugin.

The manifest also allows you to define browser actions. Browser actions define an image or button that appears to the right of the address bar and provides an entry point into your extension. When you click on that icon a popup you create appears.

Here was the manifest for my plugin:

{
  "name": "Tab Bing!",
  "version": "1.1",
  "description": "This extension produces a 'bing' when a tab finishes loading.",
  "icons": { "128": "bell_128.png" },
  "background_page": "background.html",
  "browser_action": {
    "default_title": "",
    "default_icon": "bell_19.png",
    "default_popup": "popup.html"
  },
  "permissions": [
    "tabs"
  ]
}

The manifest file above defines the name, etc, of my extension. It also says that the background page, background.html, will be loaded and run behind the scenes at all times. Additionally, there will be a button to the right of the nav menu. Clicking this button will show the popup, popup.html.

At this point all I need to do is create the files referenced and load the extension into Chrome. Just for an example, here is the directory structure of my extension:

Files in Tab Bing!

You can ignore the mp3 files and play.png file as theyre my extension’s assets. Really, what I have is three very simple: manifest.json, background.html, and popup.html.

If I wanted to load my extension into my browser how I can do so by putting the extension tab into developer mode. I do this by opening the extensions tab and clicking on the Developer mode link.

Developer Mode

After that I can click on Load Unpacked Extension and simply choose the directory my extension is in. At this point I can see my button appear next to the tool bar. Clicking the button shows a popup window containing the contents of my popup.html file. It really doesnt get much simpler than this!

Now that I have the basics of my Chrome extension in place I can start writing my extensions features.

The way my extension works is to allow the user to indicate if they want the current tab to bing when done loading. To support this Ive created a simple popup that collects a few options from the user. These options are stored by the background.html file that is persistent for the duration of the browsers lifespan.

So, heres my popup.html:


body{
	width: 300px;
	font-family: verdana;
}

	var background = chrome.extension.getBackgroundPage();
	var tab = null;
	var tabSettings = null;
	var previewedSound = null;
	// get the current tab
	chrome.tabs.getSelected(null, function(_tab){
		tab = _tab;

		insureElementExists();

		tabSettings = background.bingTabs[tab.id];

		// check the bing box
		document.getElementById("bing").checked = tabSettings.bing;

		// select the sound
		var sound = document.getElementById("sound");
		for(var i = 0 ; i < sound.options.length; i++){
			var option = sound.options[i];
			if(option.value == tabSettings.sound){
				sound.selectedIndex = i;
				break;
			}
		}

		// check the repeat box
		document.getElementById("repeat").checked = tabSettings.repeat;
	});

	function toggleBing(bing){
		background.bingTabs[tab.id].bing = bing;
	}

	function selectSound(sound){
		background.bingTabs[tab.id].sound = sound;
		preview(sound);
	}

	function toggleRepeat(repeat){
		background.bingTabs[tab.id].repeat = repeat;
	}

	function insureElementExists(){
		if(background.bingTabs[tab.id] == undefined){
			background.bingTabs[tab.id] = {
				bing: false,
				sound: "Bing",
				repeat: false,
				playing: null
			}
		}
	}

	function preview(sound){
		if(previewedSound != null){
			background.stop(previewedSound);
		}
		previewedSound = background.play(sound, false);
	}

<p>
	 Make this tab bing when loaded.
</p>
<p>
	Choose a sound:

		Alarm
		Bing
		Bleep
		Door Buzzer
		Rooster
		Siren
		Smoke Alarm
		Whoop

	<img src="play.png" />
</p>
<p>
	 Repeat until the tab is selected.
</p>

The first thing you may note is that this looks pretty much like any HTML file. Thats because it is! Take a look at the body and note that Im using HTML form elements to define my popup. Heck, I even use CSS to format the popup!

Heres what it looks like in the browser:

The Extension's Popup

Looking at the JavaScript in the popup youll see that I have this line of code first:

var background = chrome.extension.getBackgroundPage();

This returns a reference to the background.html page. Once I have this reference I can call functions on it. Before I show that though, let me show you the code for the background.html:


	chrome.tabs.onUpdated.addListener(
		function(tabId, changeInfo, tab) {
			if(changeInfo.status == "complete"){
				if(bingTabs[tabId] != undefined &amp;&amp; bingTabs[tabId].bing){
					if(currentTab != tabId &amp;&amp; bingTabs[tabId].repeat){
						bingTabs[tabId].playing = play(bingTabs[tabId].sound, true);
					} else {
						bingTabs[tabId].playing = play(bingTabs[tabId].sound, false);
					}
				}
			}
		}
	);

	chrome.tabs.getSelected(null, function(tab){
		currentTab = tab.id;
	});

	chrome.tabs.onSelectionChanged.addListener(
		function(tabId, selectInfo){
			currentTab = tabId;

			stop(bingTabs[tabId].playing);
		}
	);

	var bingTabs = {};

	function bing(id){
		sound = bingTabs[id].sound;
		play(sound, false);
	}

	function play(sound, repeat){
		var audio = new Audio(sound + ".mp3");

		if(repeat){
			audio.loop = true;
		}
		audio.play();

		return audio;
	}

	function stop(sound){
		sound.pause();
	}

Not a very long script is it? Its entirely JavaScript too!

What this does, is make use of the Chrome extension API to listen for tab updated events. When they occur it looks to see if that specific tab is set to bing. If so, it bings by using the HTML 5 audio API to play an MP3 file.

Going back to the popup.html, when you check the checkbox to make the tab bing, the checkboxs onChange event calls the toggleBing() function. This toggleBing() function simply calls the background page and tells it to make that tab bing when done loading.

What I really think is cool about Chrome extensions are how quickly I got up to speed.It literally only took about half an hour and I was off to the races. The Chrome extensions documentation is terrific to! And once I had a working version I was able to quickly and easily make changes to add additional features. You do have to click reload in the extensions tab, but that beats the pants off reloading the entire browser with each change.

Additionally, with Firefox, it was really very difficult to publish the extension to addons.mozilla.com. For Chrome it was as simple as uploading my extensions folder to Googles Chrome extensions website.

The biggest gripe I have, though, is that Chrome’s simplicity is also a little limiting. I cant completely change any feature within the browser. But, in all honesty, I think its a good tradeoff for a much nicer user experience.

If you’re interested you can download my Tab Bing! Chrome extension here.

I figure that next Ill make my Tab Bing! extension make the tabs icon blink when a tab is loaded! What extensions would you create if you had the time and know-how?

A Handy Way To Convert US Time Zones

Todays blog entry is a bit off my typical subjects on this blog. But, as someone who works from home for clients across the US, I often have to figure out what, for example, 9am Eastern Time is in Pacific Time.

As the title suggests, I have a ahem handy way to figure this out. What I do is use my knuckles. Specifically, there are four knuckles on each of your hand (excluding your thumb). There are also four time zones in the US. So, if you need to convert from one time zone or another, you simply choose the knuckle corresponding to the time zone and count up to the right and down to the left. Heres an image that helps demonstrate the concept:

The idea is that if someone asks you to attend a meeting at 3pm Pacific Time and youre in Central Time, you simply start at the Pacific knuckle and count up to Central. Thus, 3pm Pacific is 5pm Central. And, vice versa. If someone in Eastern Time suggests a 3pm meeting and youre in Mountain Time, you start with the right-most knuckle and count down to find out that 3pm Eastern is 1pm Mountain.

For those of you who are naturals at figuring out time zone differences this may be a little less than useful. For everyone else, I hope you enjoy!

And, while Im on it, this idea came to me from a similar trick I use to figure out the number of days in each month.

What you do is start from your left-most knuckle and count months to the right. In this case you count both the knuckles and the space between the knuckles. So the left most knuckle is January, the first space is February, the second knuckle is March, etc. (The last space and knuckle on your right hand isnt used.) Heres an illustration:

Any knuckle-month has 31 days and any space-month has 30 days. The only exception is February, which usually has 28 unless youre in a leap year and it has 29.

What do you think? Do you use any hand-based mnemonics?

Tag Cloud