crowley code!

Archive for August, 2007

8/23 A few reasons TiVo is better than Comcast DVR (1)

comcast, tivo

  1. It doesn’t lose it’s mind when trying to resolve recording conflicts.
  2. The user interface actually uses the whole screen instead of just half.
  3. Amazon Unbox, punks.
  4. The user interface responds immediately rather than seconds later.
  5. The remote doesn’t promote carpal tunnel syndrome.
  6. It doesn’t sporadically forget to record sound.

This is just a quick few I’ve noticed in my first couple of weeks back in the land of TiVo. It’s good to be home.

8/22 jQuery conditionals plugin (2)

javascript, jquery

jQuery provides a ton of ways to select sets of elements but pretty much no way to pare down a selection in an arbitrary way. Fortunately for me it’s easy to extend jQuery, so I wrote a plugin that allows you to apply a function to your set of elements to pare the set down before applying an action. Source first, talk later:

jQuery.fn.if = function(arg) {
    var elems = [];
    var all = this.get();
    var ii = all.length;
    for (var i = 0; i < ii; ++i) {
        if (arg.apply(all[i])) {
            elems.push(all[i]);
        }
    }
    return jQuery(elems);
};

jquery.if.js (version 0.1)

The plugin takes as its argument a function that must return a boolean value. This function will be applied to every argument in the current set. Those that the function finds true will be returned in the final subset.

The use-case that inspired this quickie plugin is this: I wanted an action to happen to form fields that were empty, which previously required a bit of work. If I had more work to do with each match or a more complex matching requirement, this could get ugly.

$('input.promptable').focus(function() {
    var to = $(this);
    if ('' == to.val()) {
        to.val(this.id);
        to.addClass('prompt');
    }
});

With this plugin, the chaining action jQuery is known for is not interrupted. I can select all input elements with the promptable class and pare down that selection to only those whose value is empty. On the set returned there I can set values and add classes without breaking the chain.

$('input.promptable').focus(function() {
    $(this).if(function() {
        return '' == $(this).val();
    }).val(this.id).addClass('prompt');
});

jquery.if.js (version 0.1)

8/19 BarCampBlock gets me thinking (5)

barcamp, barcampblock, hadoop, oauth, openid

BarCampBlock was yesterday in Palo Alto and was awesomely nerdy and I actually learned a lot. I’ll spare the play by play of a strange day and night and keep it geeky here.

Mike and I participated in a discussion of our responsibilities with user data led by Leah Culver. We centered on what was public and what was private and this led into a lengthy and educational digression into the legal issues surrounding user data. My conclusion: when I start a company, I will without a doubt break more than few laws.

Next stop for Mike and I was a talk on grid computing, data grids and Hadoop. This was admittedly way over my head. The approaches and software we talked about are probably the future as we generate more data and process it in ever more complex ways. I noticed that grid software, like enterprise web apps before, is starting out in Java. Since I hate Java, I think I’m going to wait for Powerset to get Ruby up to speed.

And the rest of the day had a very OpenID theme. First some SixApart guys, including former SixApart dude and LiveJournal creator Brad Fitzpatrick talked about their plans for abstracting the friending action common to way too many new websites to a lower, more automated level. This will do two cool things: first it will prevent repetitive friending all over the web and second it will create a more complete “social graph.” But most importantly, I can be just a bit lazier next time I dream up a vaguely socially-networked web app.

This segued nicely into a talk about oauth and standardizing authentication for open web APIs. Flickr’s API is pretty cool but would be even cooler with some friends. Lowering barriers to launching a good API is key.

8/10 Cost analysis of running a server at your house (5)

economics, hosting

I have always been a big fan of running my own server to have complete control over pretty much everything. I like having the power to break everything, sometimes just so I can put it back together. But is this economically sensible?

Last month, PG&E charged me $0.1143 per kilowatt-hour for electricity. My machine has a 400 watt power supply, but by best estimates it draws about 50 watts just sitting around serving web traffic. Running all day, every day, this will cost me about $4.11 for the 36 kilowatt-hours burned. Junk change. Services from (mt), TextDrive and Amazon run $50, $45 and $72 per month respectively.

I could say case-closed right here and now but let’s make this interesting. I made a notable omission in my comparison above — bandwidth. I have the distinct displeasure of being a Comcast customer and theoretically have 768Kbps upload speed. Checking with Speakeasy, I actually have 1526Kbps upload to San Francisco and 1518Kbps upload to New York City. Sweet! Whether you believe this as little as I do is unimportant — it’s a far cry from the 100Mb+ connection available to every self-respecting datacenter.

Right now the homepage of this site is 22KB (and is by far the most visited page on the site). Nevermind the poor visitor-engagement, this means that at my current upload bandwidth I can serve 8.625 requests per second. This is downright poor compared to the 568 requests per second theoretically possible on a 100Mb connection. At the moment though, all that extra capacity will just go to waste.

Of course, what would be the fun in hosting a website in an environment with stable power? PG&E sometimes cuts the power just to keep me on my toes or keep me from waking up for work, but how much does this really cost me? I don’t make any money from any of my sites (not even AdSense), but if I did, here’s what I’d consider. For every hour a site is down, you lose some amount of money. If that amount times the number of hours of downtime per month is greater than the cost of reliable hosting, maybe the reliable hosting is worthwhile. However, even reliable hosting goes down sometimes, so you can’t count all of your downtime at home against the price difference.

Whew. So now with a little qualification, I can still sleep comfortably with my server fans running and my would-be hosting money in my pocket. I need a lot more traffic and a lot more income before I spring for root-access on a box in a datacenter.

It should be mentioned that I serve this site from a TextDrive shared acount because I destroy my server so often that a simple Wordpress install does well to stay away.

8/8 Bugfix for nsIStringBundle (3)

javascript, xulrunner

XULRunner provides a nice way to localize apps through the use of custom document-type declarations (*.dtd files) and *.properties files used from within your Javascripts (more info on ). The markup below brings one of these “string bundles” into the app:

<stringbundleset>
	<stringbundle id="locale" src="chrome://app/locale/main.properties" />
</stringbundleset>

Then just one line of Javascript brings it from the XUL world into the Javascript world. The string bundle element itself contains methods getString and getFormattedString that will bring localized content into your Javascript.

var locale = document.getElementById('locale');
locale.getString('foo');
locale.getFormattedString('bar', [asdf, qwerty]);

But getFormattedString doesn’t work right in XULRunner 1.9a7. It substitutes only the first character of the string (in my experience). Here is a transparent Javascript-only fix:

var locale = document.getElementById('locale');

// Now hack locale.getFormattedString to work like it should
locale.getFormattedString = function(id, args) {
	var str = locale.getString(id);
	var ii = args.length;
	for (var i = 0; i < ii; ++i) {
		var regex = new RegExp('%' + (i + 1) + '\$[ds]‘);
		str = str.replace(regex, args[i]);
	}
	return str;
};

This properly takes care of the replacement of each element in args. In the example above where the variables asdf and qwerty were passed, the contents of asdf would replace %1$s and the contents of qwerty would replace %2$s. Note the numbering of the placeholders differing from normal array indexing. Also note that for semantic reasons, a d can be used in the placeholder to denote a number, but no special case is necessary in Javascript. Happy localizing!

8/7 Threading in Gecko 1.9 (6)

javascript, xulrunner

Things change quickly in Internet-land, and between versions 1.8 and 1.9, the Gecko SDK received a major upgrade to its thread library. What once took multiple XPCOM objects, more than few hacks and a prayer for good measure can now be done in a single Javascript file with minimal interference from the world of C++ beneath.

Here is a sample of how one might spin an event off onto a background thread acting as a job queue. The execution is fairly straightforward: a background thread is created; the UI dispatches events onto this background thread; the job on the thread dispatches an event back to the UI thread when it is finished.

Before we start, let’s define the objects used to cross the thread boundary. They must be XPCOM objects (but can be implemented in pure Javascript and don’t even need to live in the components/ directory) and must implement the nsIRunnable interface. This interface requires that QueryInterface respond positively to nsIRunnable and that a run() function be defined.

var Foo = function(id) {
	this.id = id;
};
Foo.prototype = {
	run: function() {
		try {

			// Do some work, for now just print something
			Components.utils.reportError('This is where the background work happens.');

			// Callback to the main thread
			main.dispatch(new FooCallback(this.id, 'SUCCESS'),
				background.DISPATCH_NORMAL);

		} catch (err) {
			Components.utils.reportError(err);
		}
	},
	QueryInterface: function(iid) {
		if (iid.equals(Ci.nsIRunnable) || iid.equals(Ci.nsISupports)) {
			return this;
		}
		throw Components.results.NS_ERROR_NO_INTERFACE;
	}
};
var FooCallback = function(id, result) {
	this.id = id;
	this.result = result;
};
FooCallback.prototype = {
	run: function() {
		try {

			// This is where we can work with the main thread after the job
			alert('id: ' + id + ', result: ' + result);

		} catch (err) {
			Components.utils.reportError(err);
		}
	},
	QueryInterface: function(iid) {
		if (iid.equals(Ci.nsIRunnable) || iid.equals(Ci.nsISupports)) {
			return this;
		}
		throw Components.results.NS_ERROR_NO_INTERFACE;
	}
};

Here is the code we need to actually use these objects to spawn a background thread and dispatch a job to it. The callback will end up on the main thread’s event queue.

background = Cc['@mozilla.org/thread-manager;1'].getService().newThread(0);
main = Cc['@mozilla.org/thread-manager;1'].getService().mainThread;
background.dispatch(new Foo(id), background.DISPATCH_NORMAL);

The first two lines here should be executed on application startup and will leave thread references in main and background. The third line actually puts a job on the background queue. This can be called anywhere and will likely be used inside a loop to push a lot of compute-intensive work onto the background thread, allowing the UI thread to remain responsive.

A note about the id passed to the thread objects: this is not strictly necessary, but I find it very convenient to have the option of tracking individual jobs through the event queue. The id is especially useful when you’re using the callback to update a certain element of the UI with the result of the job. Then in the callback it’s as simple as document.getElementById(this.id).

Apologies for the bits-and-pieces examples. I’m ripping this out of unreleased code so I don’t have time to create a demo app for each one. I recommend starting with Mark Finkle’s hello world.

8/3 XUL overlays demystified (2)

xulrunner

This assumes just a little knowledge of how to get a basic Hello World XULRunner app going. If you need help, I swear by Mark Finkle.

Every would-be Firefox extension developer (myself certainly included) has at some point sparred with overlays and been vanquished. They are not the friendliest things, as they are actually quite powerful and can handle a wide range of tasks. When building a Firefox extension, you build overlays to hook into the menus, create sidebars and toolbars, add status icons and do most anything else you can dream up.

To understand overlays, it helps to first get a look at the main application <window>. Here is a simple application window:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://foo/skin/main.css" type="text/css"?>
<?xul-overlay href="chrome://foo/content/overlay_asdf.xul"?>
<?xul-overlay href="chrome://foo/content/overlay_qwerty.xul"?>
<!DOCTYPE window SYSTEM "chrome://uploadr/locale/foo.dtd">
<window id="main" title="&title;" width="750" height="500"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
	<script src="chrome://uploadr/content/main.js" />
	<toolbox>
		<menubar id="menu">
			<menu id="menu_file" label="&menu.file;">
				<menupopup>
					<menuitem id="menu_go" label="&menu.file.go;"
					oncommand="go();" />
					<menuitem id="menu_FileQuitItem" label="&menu.file.exit;"
					oncommand="exit();" />
				</menupopup>
			</menu>
		</menubar>
	</toolbox>
	<vbox flex="1">
		<hbox id="view_asdf" flex="1" />
		<vbox id="view_qwerty" flex="1" />
	</vbox>
</window>

This application does a couple of things of note. First, it sets up a XULRunner application window with a menu and a couple of boxes, but more importantly, at the top, it includes the two overlays we’re about to write.

Before we write the overlays, though, let’s add to the main.css file we’re including to make this interesting. The two overlays are going to be two different views in our application so we won’t want to see them both at the same time. For starters we’ll just hide #view_qwerty.

#view_qwerty {
	display: none;
}

Now come the overlays. An <overlay> is contructed very much like a <window> and can include all of the same types of content. The trick is in how they are inserted into the DOM. When an overlay is being processed, elements whose ID is shared by a similarly typed element in the main window, the contents of the overlay element are appended to the contents of the same element in the main window.

Knowing that, let’s write our two overlays. First overlay_asdf.xul and then overlay_qwerty.xul.

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://foo/skin/main.css" type="text/css"?>
<!DOCTYPE overlay SYSTEM "chrome://foo/locale/main.dtd">
<overlay id="overlay_asdf"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
	<hbox id="view_asdf" flex="1">
		<html:p>This is view asdf.</html:p>
	</hbox>
</overlay>
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://foo/skin/main.css" type="text/css"?>
<!DOCTYPE overlay SYSTEM "chrome://foo/locale/main.dtd">
<overlay id="overlay_qwerty"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
	<vbox id="view_qwerty" flex="1">
		<html:p>This is view qwerty.</html:p>
	</vbox>
</overlay>

The structure of the overlays is very similar to the structure of the window, so we’ll just take that as is and move on. The important thing to notice here is the way that #view_asdf is a <hbox> just as it is in the main window, while #view_qwerty is a <vbox>, again as it is in the main window. This is not strictly required, as the elements must only be similar, but because boxes want to be horizontal, you can introduce weirdness into your app if you’re careless about matching these tags up. So the moral of the story is to match your overlay tags with the tag being overlaid in the main window.

So now we have an app that, when run, will show us a window with a menu and a paragraph proclaiming “This is view asdf.” Too exciting. One little bit of Javascript in main.js will bring it all together.

var go = function() {
	var asdf = document.getElementById('view_asdf');
	var qwerty = document.getElementById('view_qwerty');
	if ('none' == asdf.style.display) {
		asdf.style.display = '-moz-box';
		qwerty.style.display = 'none';
	} else {
		asdf.style.display = 'none';
		qwerty.style.display = '-moz-box';
	}
};

With any luck, running your app will be able to toggle between its two views by selecting the “Go” option from the “File” menu. Here’s a peek under the hood. When XULRunner brings in your overlays, it’s updating the main DOM. The end result is a DOM that looks like this:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://foo/skin/main.css" type="text/css"?>
<?xul-overlay href="chrome://foo/content/overlay_asdf.xul"?>
<?xul-overlay href="chrome://foo/content/overlay_qwerty.xul"?>
<!DOCTYPE window SYSTEM "chrome://uploadr/locale/foo.dtd">
<window id="main" title="&title;" width="750" height="500"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
	<script src="chrome://uploadr/content/main.js" />
	<toolbox>
		<menubar id="menu">
			<menu id="menu_file" label="&menu.file;">
				<menupopup>
					<menuitem id="menu_go" label="&menu.file.go;"
					oncommand="go();" />
					<menuitem id="menu_FileQuitItem" label="&menu.file.exit;"
					oncommand="exit();" />
				</menupopup>
			</menu>
		</menubar>
	</toolbox>
	<vbox flex="1">
		<hbox id="view_asdf" flex="1">
			<html:p>This is view asdf.</html:p>
		</hbox>
		<vbox id=”view_qwerty” flex=”1″>
			<html:p>This is view qwerty.</html:p>
		</vbox>
	</vbox>
</window>

The overlays buy us code separation, shorter files, and more sanity. Three cheers for overlays!

8/1 Faceball (1)

faceball, flickrhq

The speculation is over — I finally played Faceball.  Allspaw and I traded wins yesterday afternoon after work and I am all over the Faceball bandwagon now.  Something about getting continually hit in the face just gets you in the right frame of mind to throw things back at the other guy.  The game really motivates itself and yet has just enough rules to be civilized.  And to top it off, it appeals to my childhood obsession with throwing things indoors.