2008-07-24

Fun with Greasemonkey: @require

After the release of Greasemonkey 0.8, the door is open for @require helper libraries, the user script take on Ruby's require, or the C family's #include pre-processor directive. For security reasons, the required library is downloaded a single time at script installation time, and from a functional perspective, is equivalent to pasting the script or scripts referenced at the top of your script, in the order of the @require lines listed. (==UserScript== headers in the referenced files are presently ignored, so a library may not yet use @require or @resource lines of its own.)

@require in practice makes user scripting scale down better, which is essentially to lower the threshold towards getting a job done, to a level where implementation cost to scratch an itch is so low that rather than feeling that mild annoyance with something twice, you address the issue once, and never see it happen again -- on that site, or the web at large. We are still far from there (in as much as "getting there" would ever happen -- that threshold can always be chipped off further, I assure you) -- but @require is healthy progress.

This is what the feature can look like in an example script of mine which adds image captions to online comics, by default Dinosaur Comics and xkcd (expect another upcoming post about $x and $X):
// ==UserScript==
// @name      Image title captions
// @namespace http://code.google.com/p/ecmanaut/
// @require   http://ecmanaut.googlecode.com/svn/trunk/lib/gm/$x$X.js
// ==/UserScript==

I'll be sharing some of my own helpers here, in a little series of "Fun with Greasemonkey" posts. I encourage you to write, and share, your own, on your blog, as well. User script authorship is a very social form of software, especially when you share your results on userscripts.org, and your best practices, for instance by way of blogging.

2008-06-05

Javascript books

I just received two javascript books I recently ordered from Amazon (the US branch, for once, since the dirt cheap dollar made shipping across the Atlantic notably favourable to shipping across the English Channel): John Resig's Pro javascript techniques and Douglas Crockford's Javascript: the good parts.

I had for the longest time (since the nineties) been of the opinion that the only javascript book worth getting was David Flanagan's Javascript: the definitive guide, which I at the time found excellent in it's third edition and still good (and much updated) in its fourth (by my cursory looks and used for reference, at least), but as two of my favourite authors on the subject of javascript (the last being Dean Edwards, whose primarily means of expression on the subject, so far, at least, is terse, solid code) have been published since then, it was time to challenge that assumption. And I'm quite glad to say that both were up to par.

Pro javascript techniques really shows Resig's experience in library writing, and shares lots of useful input and practices, and provides an enjoyable and rather useful walk-through of the popular javascript libraries of its day, which manages to still be fairly relevant, in spite of the evolution they have seen since its publication. Much the same applies to the bits and details it shares on browsers and the tool-chain, but that is to be expected. (I am really looking forward to its successor, Secrets of the javascript ninja, much of which can already be read online.) Particularly enjoyably and usefully, he shows off strengths of each library without much fuss.

As for Crockford's Javascript: the good parts, it follows his usual seasoned narrative which has earned him nicknames such as "the Yoda of javascript", and keeps a close focus on the more timeless parts of the core language itself, devoid of all the clutter and temporally challenged aspects of the document and browser object models. It follows a clean-slate approach to describing the beautiful parts of the language most worth learning, while warning about the pitfalls of some of its worst warts, and is just as enlightening as the rest of Doug's work. It's something of a feel-good book for javascript buffs wanting to meditate over their use of the language -- or at least it is for myself.

Great companions for a season where the weather merits some good outdoors rather than indoors geekery. ;-)

2008-04-10

Expensive, yet responsive, live interaction

Let's say you have a slider widget that users can drag to filter a largeish table so it only shows rows where some column is greater than the a threshold, defined by the slider. That's an application with rather strict real-time constraints, but filtering the table is expensive; a naïve solution that refilters the table with every drag event you get, or even just every event that changes your threshold value, will still be too slow for the interface to feel responsive.

I just came up with a rather elegant solution for that problem, which makes sure that your expensive code won't make the interface feel sluggish, by postponing calls to it that come in a more rapid succession than the time it would have taken for it to run. Or, to be precise, the time it took last time it was called. It's a simple wrapper function you can reuse anywhere:

// returns a function that only runs expensive function
// fn after no call to it has been made for n ms, or
// 100, if not given, or the time fn took last time, if
// it has been run at least once and no n was given.
function expensive(fn, n) {
function run() {
fn.timeout = null;
var timer = n || new Date;
fn();
if (!n) duration = (new Date) - timer;
}
var duration;
return function postpone() {
if (fn.timeout)
clearTimeout(fn.timeout);
fn.timeout = setTimeout(run, n || duration || 100);
};
}


You might of course want to add some argument and this re-binding to it, for cases where the callback isn't self-contained, but then it gets a bit more cluttered, and I really just wanted to show the principle here. Typical usage is replacing your node.addEventListener("mousemove", callback, false) with the wrapped node.addEventListener("mousemove", expensive(callback), false) version, and suddenly, the UI is not only bearable, but probably even rather pleasant to use.