Posts categorized “Web”.

Not blocking the UI in tight JavaScript loops

Everyone’s written a JavaScript loop that just loops over all the {LIs, links, divs} on a page*, and it’s pretty standard. Something like

var lis = document.getElementsByTagName("li");
for (var i=0; i<lis.length; i++) { // yes this could be more efficient, don't care
  // do something here to lis[i]
};

or, if you’re using jQuery:

$("li").each(function() {
  // do something here to this
});

This is problematic if there are, say, 2000 LI elements on the page, and what you’re doing in the loop is semi-intensive (imagine you’re creating a couple of extra elements to append to each of those LIs, or something like that). The reason this is a problem is that JavaScript is single-threaded. A tight loop like this hangs the browser until it’s finished, you get the “this script has been running for a long time” dialog, and the user interface doesn’t update while you’re in this kind of loop. You might think: aha, this will take a long time, so I’ll have some sort of a progress monitor thing:

var lis = document.getElementsByTagName("li");
for (var i=0; i<lis.length; i++) { // yes this could be more efficient, don't care
  // do something here to lis[i]
  progressMonitor.innerHTML = "processing list item " + i; // fail
};

but that doesn’t work. What happens is that the browser freezes until the loop finishes. Annoying, but there it is.

One approach to getting around this is with timeouts rather than a for loop.

var lis = document.getElementsByTagName("li");
var counter = 0;
function doWork() {
  // do something here to lis[i]
  counter += 1;
  progressMonitor.innerHTML = "processing list item " + counter;
  if (counter < lis.length) {
    setTimeout(doWork, 1);
  }
};
setTimeout(doWork, 1);

so you move the bit of work you need to do into a function, and that function re-schedules itself repeatedly, using setTimeout. This time, your user interface will indeed update, and your progress monitor will show where you’re up to. There are a couple of caveats with this: it’ll take a bit longer, and you’re no longer guaranteed to have things processed in the order you expect, but they’re minor issues.

For doing this in jQuery, a tiny plugin:

jQuery.eachCallback = function(arr, process, callback) {
    var cnt = 0;
    function work() {
        var item = arr[cnt];
        process.apply(item);
        callback.apply(item, [cnt]);
        cnt += 1;
        if (cnt < arr.length) {
            setTimeout(work, 1);
        }
    }
    setTimeout(work, 1);
};
jQuery.fn.eachCallback = function(process, callback) {
    var cnt = 0;
    var jq = this;
    function work() {
        var item = jq.get(cnt);
        process.apply(item);
        callback.apply(item, [cnt]);
        cnt += 1;
        if (cnt < jq.length) {
            setTimeout(work, 1);
        }
    }
    setTimeout(work, 1);
};

and now you can do

$.eachCallback(someArray, function() {
  // "this" is the array item, just like $.each
}, function(loopcount) {
  // here you get to do some UI updating
  // loopcount is how far into the loop you are
});

$("li").eachCallback(function() {
  // do something to this
}, function(loopcount) {
  // update the UI
});

Not always a useful technique, but when you need it, you need it.

Working with CouchDB

I’ve been working with CouchDB as a database in which applications can store their data; there’s an increasing trend recently for applications to start using databases to store their data rather than flat files, and I personally think it’s a jolly good trend. There’s been rumours for years about the idea of a database-backed filesystem, where instead of pathnames you use queries and so on; that’s never happened (and I’m not convinced it will ever happen), but individual apps can get most of the benefits of that by using a database to store their data. I like CouchDB for this because it has a number of advantages over simple databases like SQLite; for example, CouchDB does replication, meaning that I can get all my data on all my machines. This is a genuinely lovely property. It’s been really interesting talking to the Tomboy team about how this sort of procedure ties in with what they’re doing; we’ve been working on their Snowy server and talking about its API and how it should work, too. Cool times ahead for this stuff. I’m really excited!

Absolute beginners guide to Google Maps JavaScript

A mate of mine has been building a relatively complex website for Potton, the town he lives in. In six months he’s gone from knowing nothing about Django or JavaScript to building something pretty cool with lots of Google maps and so on, and he’s started a series writing up what he’s learned for other people in the same position. First essay, an Absolute beginners guide to Google Maps JavaScript.

Games in pure SVG

In the “stupid experiments” category…

If you’ve got nothing better to do for three hours in an evening, why not experiment a bit with SVG? That’s what I thought, earlier on this evening.

So: cave.svg, a game for people with no graphics criticism ability and only one finger. One single SVG file, with all the controlling JavaScript therein. Inspired by SFCave, a game I played a million jillion years ago on a Palm IIIx and which I was astounded to discover has a website and a Java version and everything.

I have discovered the following things about SVG this evening.

  1. It is dog slow. I mean, slo-o-o-o-o-o-o-ow. You’ve-finished-the-exam-and-there’s-still-an-hour-to-go slow. Sitting in a traffic jam for three hours and the kids keep asking for an ice cream slow.
  2. It works in Firefox and Opera. I’ve tested in Midori, which is a WebKit browser, and it seems to work there too except that the fonts display as black-on-black, which means that either (a) Midori misimplents the spec (10% chance) or (b) I’m doing something wrong (90% chance). It’s probably broken the same way in Safari too, but the game seems to work.
  3. You have to care about XML at inopportune moments. I spent twenty minutes trying to work out why createElement didn’t work before sighing and using createElementNS.
  4. You can specify all the sizes in percentages! So it works at any size at all and the browser handles it all for you! Resize the game while you’re playing and it all still carries on working and scales for you! Do that with canvas!
  5. The previous point appears to be the only thing where SVG scores over canvas. For everything else using SVG on the web seems rather like having your scrotum gently resting between a pit bull’s teeth. It makes everything slightly more awkward than it ought to be… and any moment now you know the pain is coming.
  6. Did I mention slow? Can somebody please tell me what kind of a world we live in where my dual-core 2×2GHz PC can’t render a screen made out of rectangles at more than 10fps without dropping keypress events? I mean, come on.
  7. It’s not very optimised code (but it shouldn’t need to be). It would probably be a lot faster if I actually did things an SVGish way, by which I mean use the transform attribute and so on, but there are so many things I’d rather do than matrix arithmetic that it’s not even funny. Up to and including eating a pound of fish fingers with broken glass in.

In my head for a while has been a slight disappointment that everyone writing games or graphics things using JavaScript has gone for canvas (which is one step away from being a plugin — it gives you a white box and you draw in it) rather than the more web-ish SVG (which works like HTML and can be intermixed and everything). I am no longer disappointed. People don’t avoid dynamic SVG on the web because they’re wrong. People avoid dynamic SVG on the web because it’s quite shit.

Auto-discoverable local websites

Epiphany, the Gnome web browser, has a nice feature where it will automatically discover websites on your local network and add them to your bookmarks list. It occurred to me that this would be useful for a workshop I’m running later this week; have some materials in a website, and then everyone at the workshop will just have it appear in their bookmarks. Well, they will if they’re using Epiphany, or Konqueror, and (since most of them will likely be Mac people) they will in Safari as well (I think).

So, I thought: I want a piece of that action. I want to create such a website and have Safari etc pick it up. But how do you do that? Ross Burton to the rescue; Ross explains how to advertise a website over Zeroconf (Bonjour for Mac people, but it’s all the same technology). So, just drop a file in that /etc/avahi/services folder and it all works.

And then I thought: how do I test it works? (I don’t use Epiphany, I use Firefox.) BonjourFoxy to the rescue too; it browses the local network for websites that are so advertised and displays them in a sidebar; works for all platforms.

So, this oughta work. (People with both Macs and Ubuntu; I’d appreciate it if you could use Ross’s docs to advertise a local webpage with a specific path on the Ubuntu box and then see if Safari on the Mac picks it up.)

Met police “anti-terror” poster campaign

The Metropolitan police in London have unveiled a pernicious, paranoid new poster campaign encouraging Londoners to report their neighbours for doing things which “look suspicious”. Boing Boing skewers this Fatherland-like approach pretty effectively, and have run a poster remix campaign to encourage people to remix the posters with some more appropriate messaging. Now you can do the same with the greatest of ease, through the “Make your own Metropolitan Police terrorism poster” site.

Publishing screenshots and files quickly

I’ve just looked at Skitch after someone used it to share a screenshot with me, and thoughts floated across my head about how I might use that too. Then I thought, now, hang on a second, I already have a website to publish images on, but I have to save them, then scp them up, and so on, and that’s fiddly and annoying. Then I thought, this could be easier.

So, Places > Connect to Server, and fill in SSH details for kryogenix.org, with the folder being my “random” folder on the website, and tick “add bookmark” with the bookmark name being “kryogenix random folder”. Now, to take a screenshot and publish it, I just hit Print Screen (which takes a screenshot and offers to save it), say “Save”, and pick “kryogenix random folder” from the drop-down list of where to save it to. Publishing screenshots with three clicks. That’ll do nicely.

(Skitch does other stuff, as I understand it, like cropping images and adding annotations. It would be cool if the Gnome screenshot tool allowed these things too, but I can live without them.)

Python ur-developer

In response to the deep psychological case-study that is “Beards of Python“, I present the Python ur-developer, which is what you get if you average them all together. Next time you find yourself annoyed that urlparse doesn’t handle daap:// URLs properly, or find yourself thankful that Python 3.0 is fixing some of your problems, this is the guy to blame or buy beer for. Which of your friends does he most look like?

Python ur-developer

Roman dates in Wordpress

In honour of Tom’s migration from Blosxom to Wordpress (thus joining the rest of us here in the twenty-first century), and in recognition of how I owe him a present and all, I’ve decided to alleviate his pain.

You see, because Tom’s a nutcase, he had all the dates displayed on his previous Blosxom weblog in the Latin form, using a Perl program to convert dates to Roman that he developed. Clever and elegant. Pointless, natch, but he seemed to enjoy it. However, the move to Wordpress has broken that, because Wordpress isn’t written in Perl, it’s written in PHP. So Perl plugins don’t work for it.

So, I have taken it upon myself to convert his Perl Roman date converter to a Wordpress PHP plugin. Download the Wordpress Roman Dates plugin here. Install by dropping into your wp-content/plugins folder and naming it romandates.php.

Nota bene primus (since we’re doing Latin stuff): this isn’t very idiomatic PHP; it’s a direct conversion from the (not very idiomatic either) Perl original
Nota bene secundus: this would have been impossible for anyone who wasn’t me, because Tom didn’t actually publish the source of his Perl program. But I’m his sysadmin, heh heh heh.

Updates on the “ARIA stylesheet” hack

My ARIA “stylesheet” hack came up on the WAI-XTech mailing list. Some of my commenters here, and some of the people on the list, seem to have misapprehended what the proposal was designed to do; I’ve mailed the list, but it seems useful to post these clarifications here too.

  1. I am not proposing adding ARIA to CSS. An “ARIA stylesheet” looks like a CSS stylesheet because that’s a convenient way to apply properties to elements, but they aren’t the same thing.
  2. I’m not proposing that the technique requires JavaScript. My suggestion is that browsers natively understand how to retrieve an “ARIA stylesheet” and apply the ARIA attributes defined therein to
    elements, as per the selectors in the “stylesheet”. The JavaScript implementation was just to demonstrate how it would work, and a prototype for how people might support older browsers which don’t have this native “ARIA parsing”, in the same way that there are JavaScript libraries which mimic HTML5 WebForms handling for older browsers that don’t yet support <input type="range">.
  3. The “ARIA stylesheet” only dictates the initial setup of a page, in much the same way that a CSS stylesheet does for presentation. If I use JavaScript to alter the style of an element, that change is not
    reflected in the CSS stylesheet text, but it is obtainable through element.style or getComputedStyle(). In the same way, altering an ARIA attribute on an element through JavaScript would work perfectly well, and the current value is obtainable through element.getAttribute("aria-role") or similar.
  4. I understand that ARIA is semantic and therefore belongs more directly in the HTML than mere presentation does. However, distinct advantages to moving ARIA information out into a separate document do exist:
    1. It allows me to “ARIAify” an entire website very easily, much as having CSS in a separate stylesheet allows me to change the design of a site very easily. At the moment, to “ARIAify” a website I have to edit every single page in great detail. If a site is already properly marked up and relatively consistent in the way it uses that markup, adding ARIA roles and attributes could be done relatively easily by adding one file, the ARIA stylesheet, and a <link rel="aria" href="..."> element to the head of each page.
    2. It avoids the validation problem, that ARIA attributes will currently cause a document to fail validation
    3. It reduces bandwidth required to serve a site, meaning that sites load faster and servers work faster and cheaper, in two ways:
      1. The “ARIA stylesheet” can be cached by browsers, meaning that it needs only be served once per site; embedding attributes in the markup means that every page gets larger and the attributes are not cached
      2. User agents that are not tied into native accessiblity APIs (for example, users who do not have the accessibility APIs switched on because they don’t need them) do not need to fetch the ARIA stylesheet at all, saving even more bandwidth

Hope this helps clarify my thinking.

Comments are welcomed.

A WAI-ARIA “stylesheet”

Yesterday at the Multipack Presents event, Matt Machell spoke about WAI-ARIA, which is a way of marking up HTML with accessibility hints. (Think of it as an accessibility microformat, pretty much.) Anyway, the way it works is by adding loads of new attributes to HTML: to say “this div is actually being used as a slider”, you say <div id="myslider" aria-role="slider">. There are lots of similar new aria-* attributes: aria-describedby, aria-minvalue, and so on.

Anyway, I asked: why did loads of new attributes get invented? Why not have an “aria stylesheet”, where instead of <div id="myslider" aria-role="slider">, you put #myslider { role: slider } in a separate ARIA “stylesheet” file? And Matt said, hm, dunno, why don’t you write something to do that?

So here it is. This is in no way complete. Requires jQuery, too. What you do is add

<link rel="aria" href="first.aria">

to the head of the page, then include jQuery:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js"></script>

and then finally have this snippet of code:

<script>
$(document).ready(function(){
  $("link[rel=aria]").each(function(){
    $.get(this.getAttribute("href"), function(data) {
      $.each(data.split("}"),function(idx, stanza){
        var parts = stanza.split("{");
        if (parts.length != 2) return;
        var sel = parts[0];
        $.each(parts[1].split(";"), function(idx, directive) {
          var bits = directive.split(":");
          if (bits.length != 2) return;
          $(sel).attr("aria-"+bits[0].replace(/^\s+|\s+$/g,''),
          bits[1].replace(/^\s+|\s+$/g,''));
        });
      });
    });
  });
});
</script>

which takes an “ARIA stylesheet” which looks like

#first {
  role: slider;
  described-by: #second;
  value-min: 10;
  value-max: 30;
}
p + p {
  role: main;
}

and applies the specified ARIA attributes to the specified elements, by CSS selector.

This is rambunctiously untested, and the parser for the “stylesheet” is pretty noddy and not at all error-tolerant. If people find this useful as a concept then I’ll try and trick it up into something a bit better.

Whizzy JavaScript stuff

Yesterday I did a talk at the inaugural Multipack Presents event in Birmingham, about some whizzy JavaScript stuff that people might not know about. It covered things that are nearly available for use everywhere (getElementsByClassName, Array.forEach) right up to things that only have an implementation at the moment in one browser (server-sent events (Opera), local SQL database storage (Webkit)). The idea was to say firstly “look at all this cool stuff that’s coming up” but more importantly to say “look at all this cool stuff you can use right now if you want to!”

Talk available for browsing and downloading over at the Whizzy JavaScript Stuff talk page, anyway.

Thanks to the Multipack for having me; I hope there are many more of these events! There are event photos on Flickr, and I’m told there may be (incomplete) video of my talk available at some point (when there is I’ll link to it).

YUI3 and Launchpad

For the last week I’ve been in Berlin with the Launchpad JavaScript team building a set of cool widgets using YUI3 that will gradually start to appear in Launchpad and make its UI nicer. The LP guys have started to write up the things that they’re doing over on the Launchpad blog. I’ve learned quite a lot about YUI3 in this week; I need to do a writeup (or more probably a set of individual writeups and tips about stuff I’ve learned).

Now is not the best time, though, since YUI is being incredibly frustrating with its widgets; they don’t clean up after themselves, so if your widget creates HTML then it’s your responsibility to remove that HTML from the document later. And my widget seems to be creating its HTML twice, which is less than excellent. Frustration abounds.

That aside, though, this has been a good week; got a lot done, and I want to see Canonical be part of the web community like we’re already part of the Gnome/Linux community. Building cool stuff and talking about it and releasing it is going to help, there.

IE6’s backlash

Lots of conversation about IE6 holding everything back (Christian, Robert, et al), which is something that people have been talking about for ages (me last year, me in 2005, Zeldman in 2001 in a more general sense). A point to remember here: no-one (well, no-one serious) is suggesting that IE6 users get a site which says “you are using IE6: bugger off.” Instead, the suggestion really is that IE6 get the same treatment as Netscape 4: it gets the unenhanced version of your sites. You’re already building sites that work in every browser (if you’re not, go away and start doing that), and you’re already building sites that give Netscape 4 the version without many enhancements. Why not move IE6 into that category?

If your argument is that IE6 people will not like your site without enhancements because it doesn’t do as much or doesn’t work as well then…you’re not building sites properly. Stop doing that.

Opera Web Standards Curriculum: the JavaScript version

The Opera guys have been putting together a Web Standards Curriculum — a sort of school textbook of modern web development. Anyway, after a certain amount of cajoling from Chris “Dark Sanatic” Mills I came through on my promise to actually write some articles for it. They’ve now been published in the JavaScript section, if you like that sort of thing: one on modifying the HTML DOM tree and one on simple animation. Enjoy.

(I am proud I was not as whiny as Nyman — if you saw excerpts of my emails to Mills & co they’d all say “oh, piss, yes, I did say I’d do that, didn’t I? I’ll get it done this week, promise.” Who’d be an editor, eh?)

Working with Internet Explorer

Building HTA desktop apps with Internet Explorer is a real treat for a web developer. You get to work with desktop features such as native drag and drop, system icons, reading and writing files, encrypted SQL server embedded databases, and chrome-less, non-rectangular windows (even with HTML!). You also have access to all of the features of Windows, the most popular operating system in the world, like audio and video, sound transforms, uploads and downloads, byte arrays, cryptography, and loads more. Best of all, you get to have a single target environment, in Internet Explorer — JavaScript expressions in CSS, VML and HTML+TIME, and no compatibility issues with other browsers. It’ll also work out of the box on nearly 90% of the computers in the world without installing anything else — no “runtimes”, no “installers”, no extra code required. Just do it.

Does this sound like a stupid thing to say?

Then it sounds just as stupid when people say it about Adobe AIR.

Of course it’s easier to develop HTML apps when you have only one target environment. The problem is that that environment is still controlled by one company.

Those of you about to say “but, but, but, AIR is cross-platform! fight the evil that is Windows!”, feel free to replace “Internet Explorer” and “HTA” above with “Firefox” and “xulrunner” or “Prism”.

Update: it has been correctly pointed out to me that this post was a bit harsh. I had a headache and was up early, is my excuse. While I absolutely stand by the points made here (AIR is no more the Open Web than Internet Explorer HTAs are; why do people who hate HTAs love AIR?), I should have been more gentlemanly about the point. Throwing bricks helps no-one. My apologies, Mike.

blurry

annoying tease

Giving searchhi a bit of a makeover

A user of one of the scripts I wrote a while back, searchhi, dropped me a mail to say: it would be handy if I could take some action if no hits are found. After a bit of probing, it turned out that they were using it for an in-page search — what searchhi actually does is look at the referrer to see if you came here from a Google/Yahoo search and highlight the stuff you searched for — so you could fill in a search form and then search the words in the page and highlight them. I’m at a bit of a loss as to how the built-in browser search (which is Ctrl-F for everyone, I think, although Macs might be different, and I use / on Firefox) doesn’t satisfy this need, but needs must and all that. Hacking both of those things in (have a <form class="searchhi"> in the page and its first textbox will automatically be used as the search entry) was relatively quick.

The nice thing about this is that I took the opportunity to tidy the script up a bit; it was written some time ago, and created its functions in the global namespace, was careless about var, that sort of thing. I’m now a bit happier with it — it passes JSLint for a start, modulo that I disagree with Douglas about single-line if — by which I mean that I don’t cringe when I look at it.

(I haven’t documented the changes on the searchhi page itself, because the in-page search stuff is obviously JavaScript-dependent and that’s not how you should do it, so I don’t want to encourage people to use it.)

Back from UDS

Last week I was at the Ubuntu Developer Summit, the six-monthly big meetup for Canonical employees and the Ubuntu community to get together and work out what’s in the next version of Ubuntu Linux and how to do it. It was fascinating meeting everyone and seeing it all up close. Lots of stuff went on: I got interviewed by the Ubuntu UK guys for their podcast; I caught up with a few people I hadn’t seen for a while and lots of people I’d never met face-to-face before; I threw together a quick “lifestream” bit of JavaScript which would track a tag across various sites and it ended up on Launchpad (so if you want a trivial way to display a lifestream for a conference, grab the script and make it better); I asked everyone I could think of whether I should insist on buying a laptop with Ubuntu on it and which one I should get; we went to the Computer History Museum and indulged in the geek oneupmanship game of saying “owned one of those, owned one of those, wanted one of those” (apparently the PlayStation 1 is now Computer History); and I drank a fair amount of beer. Was a good week.

I was there because I was paid to be so by Canonical. I start work for them at the beginning of January. While there I met the people who will be my team when I start, and saw some of what they’re working on, and I’m really rather excited. I’m going to be part of Online Services. Can’t wait to get going on it. I get to make the desktop I use be better.

Sending posts from Twitter to Identi.ca part 2

A while back I wrote a reflector which sent posts from Twitter to Identi.ca if you want to post to both. However, that required running your own code, which is hardly in the spirit of Web 2.0 and cloud computing and all that. So, now, here’s how to send your posts from Twitter to Identi.ca without running any software yourself.

  1. Sign into Twitterfeed with your OpenID (Ubuntu users, if you don’t have an OpenID you can use your Launchpad ID: https://launchpad.net/~fred-bloggs )
  2. Say “Create new Twitter feed”
  3. Change it to be a laconi.ca feed
  4. Set the feed URL to be http://pipes.yahoo.com/pipes/pipe.run?_id=WvUZFt_G3RG6Veiqbbsjiw&_render=rss&twitteruser=YOUR_TWITTER_USERNAME
  5. …and you are done

The pipes.yahoo.com stuff is there because if you just use twitterfeed to send your Twitter user RSS feed direct to identi.ca then all your posts have “sil: ” at the beginning (or whatever your username is). The Yahoo pipe strips that off.

If you want to post to Identi.ca and have your posts show up on Twitter* then Identi.ca does it for you; just set it up in the Settings section of Identi.ca.

Helvetireader in Mozilla Prism

Jon Hicks has just released Helvetireader, a lovely minimalist theme for Google Reader. He’s implemented it as a GreaseMonkey script which pulls in the CSS file direct from helvetireader.com, which is a cool touch. He also notes that “the Helvetireader user script is ideal for using with Fluid.app to create a standalone application”. It is, of course, also ideal for using with Mozilla Prism for those of us not on a Mac (or those on a Mac who prefer Prism to Fluid). Grab helvetireader.webapp for all the well-styled-Google-Reader-in-its-own-window you’ll ever need.

(Those of you of a technical bent: all I did was add a webapp.css saying @import url(http://www.helvetireader.com/css/helvetireader.css); to greader.webapp, so you could do that yourself and add any extra customisation you want to that file.)

behave!

After watching @darrenf (a) tell an awful lot of people to behave on Twitter and (b) have to manually type it in every time, thus providing wear and tear on his hands which mean that he can’t fix bugs instead of me, I present: behave.user.js, a Greasemonkey script to tell people to behave on Twitter. Just hover over a name and click the ! to tell that person to behave. Winner.

Trying to understand YUI 3

I’ve been looking at YUI 3, because I ought to know about it and I currently don’t. I used YUI 2 for a few things and found myself perpetually frustrated with the documentation and the general feel of it, but I figured that maybe that was just me. (Disclosure: I’m a jQuery guy. Use it for everything. So I’m biased, right out of the gate.) Anyway, my frustration with the YUI documentation doesn’t seem to have been alleviated in version 3. This is a case study in how someone new to the YUI 3 library sees the documentation. (Well, it’s really a set of stream-of-consciousness rants, because that’s the best way I can think of getting my sense of confusion and discombobulation across.)

The underlying problem I always had with the YUI 2 documentation was that the examples show how to do one specific thing but don’t explain why to do it that specific way, and don’t seem very generalisable to do other similar things. So they’re hard to learn from. The reference manual, on the other hand, is useless. It gives you a textbox and says “Start typing to find a property/method/event/config”. If you already know what you’re looking for, if you know which function you want to use but can’t remember the order of the parameters that you need to pass to it, then that’s fine. It’s a dictionary. But you can’t learn a language from a dictionary, and you can’t learn the YUI from the reference manual. I hardly ever find myself thinking “I need to use the YUI.Get.script function but I can’t remember the name of the parameters”. Instead, I find myself thinking “I want to execute a JSON-P script; how do I do it?” Now, the jQuery documentation has a similar problem, to some extent, but critically it shows you all the functions in a list, so I can look through them and work out which is likely to be the one I want. The YUI does not. The jQuery docs also have inline executable examples with each function, so I can see working code and test it. The YUI reference manual does not.

But I shouldn’t start with the reference manual. The YUI docs specifically say to , so I did so.

The very first example is showing how to attach an event handler to a node. Fine as far as it goes (and I like the idea that you get passed a full-on event object to your function whether you’re in a browser that supports that or not), but…the code in the example just drifts in space. It’s not wrapped in an onDOMContentLoaded or anything. (I admit it’s the first example, which means you don’t want to load people down with infrastructure just so they can write a click function.) So, since there’s a working example in the page, I looked in the source myself, and we get

YUI({combine: true, timeout: 10000}).use("node",
function(Y) {
 ... code from the example...
});

Ah, OK, so that’s onDOMContentLoaded, is it? a new YUI function. I’m sure it’ll be explained later.

Moving on, the next example explains onAvailable, onContentReady, and event:ready — three different “is the thing ready for me to use” load events. (All of which make sense at different times, and are potentially useful.) That’s all good, but…the previous example, even after I’d looked in the code, didn’t use any of them! How did that work?

Ah, a feeling of doom creeps over me. More disclosure: I really, really don’t like the Yahoo practice of putting your JS inline at the bottom of the page. I think it means that my JS isn’t separated out into different files where it’s easy to edit, it makes refactoring the page hard because you have to care exactly where your script elements are and be careful if you move HTML elements around, and it’s a lot easier to teach people to make their JS unobtrusive if you can offer the rule “Put all your JavaScript in separate files and don’t have any anywhere in your HTML”. I know there are good reasons why Yahoo do it, and I accept those reasons. If I build a site which gets as many hits as they do then I’ll test both ways and the extra three milliseconds you get from having the JS in-page without DOMContentLoaded may make a big difference. For most of the stuff I write, though, I don’t like it and try to not use it. So, the first example was obviously reliant on that (because it’s not hooking any load event at all), but didn’t say so. Moving on, the next example I read, about nodes, does the same thing. As far as I can tell, nothing anywhere in these examples has said “make sure you put this example code after the things it’s referring to”. If you don’t do that (say, if you put it in the head because that’s where you’ve been taught that JavaScript should go) then it plain won’t work, and you won’t know why. (Maybe I missed the 72-point red writing which says this? I hope I did.)

Next example I read (event delegation, a thing I like very much): doesn’t show why the clicked items turn green (obviously CSS, and indeed it doesn’t take long to find the link in the source), but also…when I click on one of the items and it turns green, there’s a subtle animation effect to it, as if the green colour expands out from the centre of the item. It’s quite a nice effect. I can’t for the life of me find what’s doing it, though. (Doesn’t do it in Midori, a webkit browser, so maybe it’s Firefox?)

I spent ten minutes trying to work out why clicking on an item sets a class (”yui-pass”) on that item and then alters text in all non-yui-pass items, rather than just using e.target. Of course, the reason is that a second click on a different item should set that second item to green and not unset the first green item. Fine and dandy: a note about that in the example would have helped, though. Yes, the examples are there to demonstrate YUI, not to explain how to produce a specific effect, but…what people want is a specific effect. Explaining how it works would help with that. Explaining the YUI parts of an example but not putting them in the context of the rest of the code is like explaining how to build a house with a two-hour lecture on bricks without ever mentioning that you need mortar to stick them together. “The rest of the code” here is a couple of lines, but the example would make much more sense if it was all explained.

Once I’d got this far, I started thinking, hm, hasn’t changed, still the docs are written for someone who isn’t me and I don’t get them. So I started dotting around picking and choosing bits to look at rather than going through step-by-step. One of the features I like most about jQuery is that I can call a JSON-P script and still specify my callback function inline, rather than having to separate it out and give it a name and pass that name as callback=myCallback to the JSON-P script. (It gets given a name under-the-covers by jQuery, of course, so that the callback does have something to call, but I don’t have to think about it. I like inline functions.) It’s be useful to me if YUI 3 could do that, so I thought I’d have a look to see if it’s possible. I don’t think it is, but…I can’t tell. The docs don’t seem to mention the idea of loading scripts that call a callback function at all. Instead, I think you’re supposed to use YUI.Get.script and read the JSON out of the nodes array that you get passed. Perhaps. Here we come back to my problem with the YUI docs: I know what I want to do, but not how to do it, and there’s no way to answer my question. If there isn’t an example dealing with it, I am out of luck.

Someone tell me how I ought to be reading the documentation. Maybe I’m missing something. But it does rather feel to me like the docs are reference material: they’re there for people who already understand YUI. The examples are incomplete — focusing on just the part you’re trying to show is understandable, but if the example won’t work without the other parts then you have to show the other parts too! If your example only works if it’s wrapped with YUI().use("node"), function(Y) { ... }) then you have to show that. If it only works if it’s wrapped in DOMContentLoaded, or if it’s placed at the bottom of the page in a script element, then you have to say that. Yes, it’s a ballache to have to explain how events work and how loading works and not get to any real concrete examples until chapter 3 or 4 of your documentation. But people need to know that stuff or your examples will not work when someone uses them without knowing the context.

When the meme comes around

blah blah blah meme blah blah blah fifth sentence on page 56 of nearest book blah blah blah.

“Thorn, that’s beautiful!”

Not much of a sentence. On the other hand, the book is The Complete One-Volume Edition of Bone, the graphic novel.

Memo to self: do not search the internet for “bone” again in a hurry.

Page 56 is about as far as I’ve got with the book so far. It’s pretty heavy going, what with it being 1400 pages and all. I was lent it by Tim when I issued a cry for help for not having books. (All my books got packed up when I assumed I’d be moving house in short order…and then the economy swirled down the toilet and no-one’s buying houses. So I have a big house that I rattle around in on my own, and no books. Tim delivered me a box full just to keep me going after I was reduced to reading the back of tomato ketchup bottles just to stay sane. And then the first thing I read from his delivered collection was the incredibly cheery Maus.)

Has anyone else read this Bone thing? If you have, did you enjoy it? If you didn’t, did you keep reading anyway? I feel guilty if I abandon a book. Something about erudition. But if I didn’t read then I’d have to read Twitter all day instead and I’d go out of my mind.

(Yeah, yeah, go outside and enjoy the sunshine. Whatever.)

Did you know that Wikipedia does ISBN searches? Oh, you did. OK.

Top Gear

If you wanted me to do something on the 19th November 2008, I won’t be available: I’ll be busy watching Top Gear being filmed. Fantastic.

Free SMSes through Google Calendar

Right, first of all: don’t do this.

Drew commented on Twitter that it ought to be possible to mash up Twitter with Google Calendar to get SMS alerts for Twitter direct messages, because Google will send you SMS reminders for an event in your calendar. (Twitter stopped doing this a while ago, because sending SMSes costs money in the UK and they couldn’t afford it. Google, being a bit better capitalised, can afford it, thus far at least.) So, the idea is that you watch for a direct message in Twitter, then you create a Google calendar event for six minutes from now with the text of the message, and set an SMS reminder on that calendar event for five minutes from now. Then wait one minute, and you get an SMS with the text of the Twitter message, and you didn’t have to pay for it.

So, send_sms_via_gcal does the latter end of that. Python, requires the Python GData API.

It’s not very useful, though. You get an SMS message which has lots of Google calendar stuff in it and so on, and it truncates the actual message you wanted to the first 57 characters. So not all that helpful. It litters your calendar with reminder messages that you have to go and clean up manually (which could be cleaned up automatically without too much difficulty, but you’d have to write a separate thing which fired once an hour or so and cleaned up old messages.)

I suspect the Google calendar people will also take a shockingly dim view of their SMS reminder stuff being so flagrantly misused, too, which is why I said to not do it. SMS reminders are pretty nice; don’t make Google turn them off.

Video of “Secrets of JavaScript Closures” available

The Secrets of JavaScript Closures talk that I delivered at Fronteers 2008 was filmed, and the video is now available. You can watch it with streaming Flash video from the presentation page. (I’ve dropped a mail to ask whether I can re-encode it as downloadable Ogg Theora for people who don’t have Flash.)

An “up” button in the browser

At @media Ajax today, in the panel discussion session, someone said that they’d really like an “Up” button in the browser. (Apparently Seamonkey had it, or Opera did, or something.) For people who want it, here’s a bookmarklet: up

It looks for a <link rel="up" href="..."> tag and uses it if there is one; if there isn’t, it chops the last part off the URL.

Secrets of JavaScript Closures

Just come off stage after doing my presentation at Fronteers 2008 on the secrets of JavaScript closures. Go download it if you like that sort of thing.

(I’ve corrected the error that I got called on while I was on stage. Er. Oops.)

Launch launch launch

OK, the new Capital Radio website, built on Django and full of loveliness, is now out. There’s six months of our lives that none of us are going to get back.

Actually, I’m pretty proud of it. Nice one, team.

Facebook doesn’t really support IE6

Blimey. I didn’t realise that Facebook are trending down support for IE6. The “new look” is disabled, and if you use the old look you get a big message complaining about your browser choice:

\"You may want to upgrade your browser [from IE6]\", says Facebook

'You may want to upgrade your browser (from IE6)', says Facebook

Two interesting things here: first, they recommend that you try another browser, and give a list of Firefox, Safari, and Flock as well as “upgrade to Internet Explorer 7″. Flock? I bet the Opera people are a bit hacked off about that.

Second: there have been a few cases so far of people dropping support for IE6 (MobileMe, not that that really counts because all its users are Mac people, and 37 Signals, ditto), but nothing remotely as high-profile as Facebook. This is the boot starting to descend, I think. IE6 is already the bugbear of the industry (and has been for some time: I said “Internet Explorer is the new Netscape 4” in 2005 and I was hardly the first!); how long before we see support for it drop to Netscape 4 levels of “you get the unenhanced non-JavaScript version”?

I’d like to see more people publish browser stats for their websites. Yes, they’re unreliable, yes people change their user agent, blah blah blah. They’ll give us an indication, though; how many people out there are using IE6? Google Analytics tells me that 36% of my visitors are using IE, and 37% of those are using IE6, which means that IE6 visitors to my site are down to under 15%. (If you’re not using Analytics, analog -G -A +a +B <apache logfile> will give you a browser list, as will many other things.) Other people will doubtless differ, and I’d be thoroughly interested in seeing more of these percentages from sites with a different user-base to mine. If you’re a company, tell us what percentage of your users are using IE6! We’re not going to get stats out of Google or Yahoo or the BBC, but non-behemoths will do fine here. Everyone else, start thinking: where’s the cut-off point? How low does IE6’s market share need to go before it’s reasonable to not devote extra development time to it?

“Extra” is the keyword there — people thinking “hey, Opera/Safari/Firefox 3/IE8 has less than 15% market share in my statistics, let’s cut them off, Mr. Microsoft Hater” need to consider that modern browsers don’t (or at least shouldn’t) take any extra development time to work around their idiosyncrasies. (In practice, Safari does require more extra development time than I’d like, I find, but its market share is high enough (or the idiosyncracies are infrequent enough) that supporting it is broadly worth the effort.)

So: if you have IE6 stats, publish them. If you’re a web hacker: when should we cut off the ailing IE6’s life support? Speak now…

Readable non-harvestable email addresses with CSS

Stupid hack I’ve just thought of. My email address is:

@silogenixkry.org

Spam harvesters looking at that will see the following code:

<p style="padding-left: 5em"><span>@</span><span
 style="margin-left: -2.5em">sil</span><span
 style="margin-left: 3.5em">ogenix</span><span
 style="margin-left: -5.3em">kry</span><span
 style="margin-left: 4.1em">.org</span></p>

Or, with HTML stripped, @silogenixkry.org, which ain’t an email address. It does it by breaking up the address into bits, putting the bits into HTML in the wrong order, and reassembling the bits into a readable order with judicious use of CSS.* It requires a certain amount of fiddling to get the margins right such that (a) the address shows up in the right order and (b) changes in font-size don’t screw it up. I’d write a tiny web-service to do it to a supplied address if I could be bothered; lazyweb, go for it. Of course, if everyone uses this, harvesters will learn how to interpret CSS (and this is relatively trivial to do in this case). Might keep your name off the lists for a little while longer, though.

Certification

Lots of discussion on Planet Gnome about self-signed certificates and SSL and so on. I wonder if the Linux distros should get together and create a new CA, and then install that CA’s root certificate in browsers? So that way, things like various project bugzillas will have a legit SSL certificate without having to pay if they don’t want to. Of course, this new FreeSoftwareProjectCA would still have to go through the same verification processes to ensure that a given certificate is being asked for by the right people, etc, etc.

Obviously, the root certificate would only be installed in your browsers if you get them from your distro (because the distros would add them to their browser packages) — this means that people on Windows or who install their own copy of Firefox (or whatever) would still get the “this is a certificate I don’t recognise” warning. However, that’s no worse off than it is now, and I think it’s reasonable to assume that people who use bug-tracking sites for free software projects running on a free software OS are disproportionately people using that OS who will therefore have the certificate.

(Update: johnath says “StartSSL, in the Firefox 3 root store, offers [SSL certificates] for free“, which might have the same effect; I don’t know whether StartSSL’s root certificate is in other browsers, but that’s no worse than the idea that I propose above.)

Fronteers 2008

The site for Fronteers 2008 is now up — Fronteers is a group of web hackers in the Netherlands who are putting on a web conference in September. Lots of cool speakers (Bert Bos, Dean Edwards*, Christian Heilmann*, Nate Koechley, Tom Occhino, and me, among others). It’s good to see another addition to the conference calendar, especially one where they’re looking for in-depth treatment of subjects. This is hardly surprising considering that it’s being run by ppk, but they’re explicitly going for an intermediate-or-better audience. There are lots of speaking slots at every conference devoted to introductions: our industry is starting to mature enough that it’s reasonable to skip right over that. It was described to me by ppk as “a conference that treats a few important topics exhaustively instead of one that lightly touches on many subjects”, which I think is a great idea. At the moment I’m trying to decide between talking about closures and talking about event delegation: anyone got a huge preference? See you in Amsterdam.

HTML5 video element about to land in Firefox 3.1

Firefox 3.1 is about to support the HTML5 <video> element, and Opera already does. This means that both those browsers will have support for inline video in the browser without plugins. As usual, Internet Explorer lag behind, and sadly Safari does as well — they’re fast at implementing lots of stuff over at the WebKit team, but Apple don’t like easy video that isn’t in a patented format, so the Safari <video> support only plays stuff that QuickTime can do. Hopefully both corporate browsers will come around, or perhaps the WebKit team can add <video> support for Ogg Theora and then the Safari team can take it back out again if they need to — since Epiphany, the Gnome browser, is going to be WebKit, it would be great to have native video support in WebKit. (Do the WebKit Gtk hackers have commit access to the WebKit source in order to add this, I wonder?)

I’ve just pinged the blip.tv people — since they already support Ogg Theora (via Fluendo’s Cortado Java applet), they should be able to add <video> support pretty easily. (Just wrap <video src=”/video/play/12345″>…</video> around the existing player, so browsers with HTML5 support will play the video inline and others will fall back to the current player.)

Nice one Opera and Firefox teams!

OpenID login and APIs

Does anyone have an example of a site which:

  1. Offers OpenID login
  2. Has a remote API that allows you to log in using your OpenID rather than a username and password

How do you provide this, as a site owner? It’s not clear to me that OpenID works for machines to log into things (unless I go and set my OpenID to “always allow access” and then write a screen-scraper module for my OpenID provider).

This seems like something of a flaw in the OpenID concept. Hopefully I’m missing something.

Update: OAuth isn’t the answer here. My use-case for this is, say, a little script that allows me to post to Identi.ca. OAuth requires me (the “Consumer”) to request a “Consumer Secret” and a “Consumer Key” from Identi.ca. From my reading of the OAuth spec, that’s supposed to be specific to the script, not specific to the person running the script, which means that I can’t open-source the script (because then everyone will know my Consumer Secret). So in order for me to write an application that uses OAuth to authenticate to a site, either I can’t open source it, or everyone using the application has to apply for their own Consumer Secret and Consumer Key; at that point you might as well just set a password and not use OpenID! The OAuth spec says that “Service Providers should not use the Consumer Secret alone to verify the identity of the Consumer“, and goes on to “Where possible, other factors such as IP address should be used as well“, which as far as I can tell means “we like closed-source programs; if you want to open source something, then we don’t know how to solve that problem, so you lose”. Correct me if I’m wrong.

blah blah new design blah

New site design. Those of you who didn’t like the pink one (i.e., everyone) may not like this either. Once again my lack of design skill runs amok. On the other hand, now I can pretend that it’s minimalism rather than that I have the artistic ability of a rubber mallet.

Of course, as with all things, Mark was there first and with greater gusto, but if that was enough of a reason to stop people doing stuff then there’d be no internet.

Feel free to let me know if anything doesn’t work in Firefox or Opera or Safari. (It’s tested in FF3, Opera 9.5, and Midori, but only pretty briefly.) I can’t test in IE7 because IEs4Linux doesn’t properly run it yet* and it looked sort of OK in IE6, and that’ll do me, I think. This is graded browser support in the real world, meaning that my browser is Grade A and everything else is some letter in the Greek alphabet like omicron.

Actually, that should be “such as omicron”, there. Tom once told me that he didn’t like the TV programme “People Like Us” because it should have been called People Such As Us. Grammar is important, especially if you’re running an anti-child abuse campaign.

Speaking of such esoteric matters, it seems that Firefox supports the soft hyphen &shy; these days. Which­is­really­great­if­it­actually­works­and­well­done­the­Mozilla­team­only­five­years­later.

Typography is important, too, but I don’t understand that, Richard Rutter notwithstanding. On the other hand, I have now used line-height for the first time ever. Eric Meyer, watch out.

It’s Friday night. Time for a pint.

Identi.ca, Twitter, and posting between the two

It’s now the trendy thing to write an essay about why Twitter is doomed, or why it’s not doomed and all its competitors are doomed instead. And god knows I wouldn’t want to break the back of that trend.

I’ve been playing with Identi.ca, a Twitter clone written in PHP. Lots of Twitter people are becoming frustrated with seeing the famous Fail Whale on Twitter’s “we are currently down” page, and people are starting to look elsewhere. There are lots (and lots and lots) of alterni-Twitters (which is a problem I’ll come back to in a moment), but identi.ca is attracting one of the communities I operate in, open source people. This is because identi.ca is entirely open source: the codebase is called Laconica. This is a nice idea.

First things first, though: I don’t want to update two places at once. Identica* doesn’t have an API (yet), but I wanted to post things to Twitter and have them appear on Identica too. So, I present the Twitter Identi.ca Reflector. Download as a tarball or check it out from Subversion or browse the source. The README tells you how to set it up. It uses Jabber to post to Identica, which isn’t ideal, but (as mentioned) there’s no API. So now you can send Twitter posts to Identi.ca, which is helpful.

Now, why Twitter is good, why Identi.ca is good, and why everything else isn’t. If you just wanted to post Twitter comments to Identi.ca then you can stop reading now.

Twitter looks like it ought to be easy to clone, and it isn’t. This is why there are lots of Twitter clones, and why none of them have taken off. The basic principle of microblogging is indeed an easy one, I agree. But all the other Twitter-alikes are missing something. They don’t have SMS sending, or SMS following, or they don’t have an API, or they don’t have lots of desktop clients, or they have one desktop client that doesn’t run on everyone’s machine, or they don’t do IM. What makes Twitter good is two things: the first is that you can get at it in so many ways. And because there are so many ways, people can build lots of things on top of it. For example, if you want to do the reverse of what the above reflector does (post your messages to Identi.ca and have them show up on Twitter) then you don’t need an extra program, because it already exists: Twitterfeed. Simply sign up at Twitterfeed, and use it to send posts from your Identica feed to Twitter, and it’s all done. That’s the power of having an expressive and complete set of APIs. It can’t be underestimated.

The second reason Twitter is good is that everyone’s already there. They got first-mover advantage. There’s no point going to an alternative because none of your friends are there. This is also the reason that Twitter has fifty desktop clients and that things like Twitterfeed exist; it’s worth the investment. It does mean that if people leave Twitter they’ll all leave together and the bottom drops out of their market, but that’s the way the internet cookie crumbles. Photo sharing sites have the same issue — it’s difficult to build a Flickr competitor because everyone’s already at Flickr, so none of the “social” stuff happens elsewhere because you never hit a critical mass of people.

So, what’s good about Identi.ca then? Well, for me, the big things are open-sourceness and federation. The underlying codebase being open source is a huge win, from my perspective. It means people other than the Identi.ca team can work on adding new features, it means that we can see what’s going on, and it means that there are more open source programs in the world. I like this. Other people may not, but what the hell.

The other advantage is federation. This is all about the OpenMicroBlogging specification; it basically blows away the “all my friends are at Twitter so I must be too” point. It means that you can subscribe on one microblogging service to people on other microblogging services. I could be at Identi.ca, you could be at Twitter, someone else could be at Jaiku or Pownce or wherever else, and we all read one another’s messages, happy as Larry. It levels the playing field. I can subscribe Identi.ca to Twitter without problems. This is a great idea which is rather hampered by the fact that basically no-one has implemented it yet. It’s in Laconica, though, so Identica has it. It helps get around the scaling problems that Twitter are having, too: you don’t need one centralised Twitter service any more. There can be lots of little islands, all of which talk to one another. No more scaling problems. No more Fail Whale. People who need the extras that Twitter provides can use Twitter quite happily (as I’m doing; I like SMS!), people who don’t need that but do need other services can use something else that provides those other services.

The basic concept of microblogging has been commoditised thanks to OpenMicroBlogging — it’s become a simple thing to implement anywhere. Microblogging services can now compete on which extras they offer.

That’s why I like Identi.ca. Oh, and they let you create your account with OpenID, which I did. It’s a win all round. Development’s going on at a fast pace on the Laconica codebase, so expect to see more and more appear over there. Blizzard’s already added identi.ca support to Whoisi, so things are moving quickly. I don’t know whether the trickle of Twitterites in Identi.ca’s direction will continue, but thanks to the reflector code above, I’m now on Identi.ca without putting in any extra effort. That’s identi.ca/sil for those of you who want to track it.

Controlling Banshee over D-Bus

I needed to be able to control Banshee from another application, and it turns out it has a lovely D-Bus API.

Skip to the next song*:

import dbus
bus = dbus.SessionBus()
banshee = bus.get_object("org.bansheeproject.Banshee",
  "/org/bansheeproject/Banshee/PlaybackController")
banshee.Next(False) # use banshee.Previous(False) to skip back one song

Start Banshee playing (this works even if Banshee isn’t running; D-Bus starts it up!):

import dbus
bus = dbus.SessionBus()
banshee = bus.get_object("org.bansheeproject.Banshee",
  "/org/bansheeproject/Banshee/PlayerEngine") # note PlayerEngine, not PlaybackController
banshee.Play() # use banshee.Pause() to pause playback

I found these commands using D-Feet*, J5’s D-Bus browser thing, which is great. The program I’m using to call this stuff is open source Enso from the Humanized team, which is also pretty darn useful.

labelify: a jQuery plugin to add labels to your textboxes

A fairly common design pattern in web forms is to have some explanatory help text for a textfield appear inside the text field, and then remove it when the user clicks into that field. It has the benefit of putting the help precisely where the user’s looking.

Labelify is a jQuery plugin that does this for you, as simply as possible. It handles a number of corner cases, and it’s quite customisable if you need it to be, while still working as simply as possible out of the box.

Enjoy.

Browser version icons

Ian Lloyd’s released browserversionicons.com, a set of icons for popular web browsers branded with the version number. This is useful for those of us who have fifteen web browsers installed at different versions for web testing.

It’s a resource for Mac people — the icons are in .icns format, which is some Mac-specific thing — but I thought it’d be useful for Linux people too. So, I converted all the icns icons to PNG, using icns2png* and Ian kindly added those icons to the download pages. I’ve also done a brief screencast of how to change your browser icons to use different ones — be gentle with me, it’s my first screencast.

Good work, Ian. Let’s see more of that. (Oh, and if you get a chance to produce icons for versions of IE greater than 5.2, that’d be handy too.)

(oh, strangely, there is an open bug for Gtk support for .icns format, and apparently Bastien committed the fix to trunk last November (so it should be there in my Gnome 2.22.2, right?), but you can’t set a .icns file as an icon for a .desktop file/launcher. Not sure whether that’s because I don’t have the patch or because the patch doesn’t do that; if someone can tell me, I’ll file another bug.)

Non-capturing groups in a regexp

Bridge to engine room, geek factor nine. Those of you who read this for musings about the world, stop reading now.

Alex at work has just alerted me to the existence of non-capturing groups in regular expressions. I had no idea these existed, and they’re pretty useful if you’re doing RE matching. If you’re trying to match a string which might be “fish, chips and ketchup”, might be “fish, chips and peas”, and might not contain the “and chips” at all, and what you care about is what’s last on the list (the “peas” or “ketchup”) then I’d have used a regexp like /fish(, chips)? and (.*)$/. Matching that against “fish, chips and peas” will give you back a three-item tuple, ("fish, chips and peas", ", chips", "peas"). (Test with JavaScript) You need the brackets around “, chips” in the regexp because you want to treat it as a group. However, it ends up in the results, and that’s really irritating.

Now I know about non-capturing groups, I’d do this: /fish(?:, chips)? and (.*)$/. The ?: after the opening bracket of the group means “don’t capture this group”. So now the results you get back are ("fish, chips and peas", "peas") — the chips, which we don’t care about, are not mentioned! (Test with JavaScript)

Another useful little trick to add to my toolbox. Cheers, Alex. Everyone who is reading this and thinking “I knew about this ages ago”, why didn’t you tell me?

Immaturity

I probably shouldn’t be laughing as much as I am about having just created this, because it’s not the most mature thing I’ve ever done. Hey, what can I tell you; it’s been a long week.

<a href="http://adactio.com" rel="enemy">Jeremy Keith</a>

Not going to Guadec

This year, for the first time in a few years, I’m not going to be able to make it to Guadec. I’m pretty sad about this — it’s always a fantastic time — but I’m not going to be there. It looks like it’s going to be insanely great, though, as ever, so have a laugh, all. I shall try and write up the things I was going to talk about into a guide to cool things that you can do in gedit (which I was planning to do anyway, before deciding to turn it into a Guadec talk!)

Beyond 404 @media/barcamp talk

Beyond 404, the talk I did at @media 2008 and BarCamp London. On the horribly ponderous subject of HTTP response codes, but I suspect that if you think you already know this stuff, you may be surprised at what you don’t know.

Twitter

Alright, I give in. Everyone’s using the bloody thing.

http://twitter.com/sil

Auditions

Lots of posts stacked up over Heathrow at the moment, like one about LugRadio Live USA, but for now I just have one bit of news. This morning, Niamh, my daughter, went to an audition to join the Stagecoach Talent Agency, and she was hugely successful. So successful, in fact, that they’re already putting her forward for an audition for a part on CBeebies, the children’s TV channel in the UK.

It is possible that I may burst with pride.

Fifteen years from now when she wins the Best Actress Oscar, I can look back and say “this is where it started”. CeCe Bloom, eat your heart out.

So proud. They said she was excellent.

gDay

Q: Wait a second. I live in the US. Does this mean I’m now 2 days behind Australia?
A: Yes. Yes it does.

the gDay MATE future-search-engine announcement

I don’t normally like April Fools’ stuff on the web, but I’ll make an exception for this one :)

Hackergotchi

Would someone better with the Gimp than me like to do me a hackergotchi for Planet Gnome? I’ve been meaning to do it for ages and not got around to it. This snap at Flickr seems like a good candidate, but go wild if you can think of a better one (CC-derivs-licenced Flickr pics of me as a starting point :))

Overriding a single field in the Django admin, using newforms-admin

Django has been gradually changing the way their automatically-created admin system works to use the newforms-admin code, which makes lots of cool new things possible. However, because newforms-admin is rather new (ha!), it’s not brilliantly documented. One of the things I wanted to do today was to make one field use a custom field-editing widget that I’d created, rather than Django’s default textbox, in the Django admin system. You do that like this.

In newforms-admin, you specify admin options for a model by creating an extra ModelAdmin class for it:

class Vehicle(models.Model):
  colour = models.CharField()
  name = models.CharField()

class VehicleAdmin(admin.ModelAdmin):
  search_fields = ["name", "colour"]

admin.site.register(Vehicle, VehicleAdmin)

Imagine that we wanted to build a custom widget to allow people to choose a colour by clicking on a colour swatch. To do this, you need to actually create your custom widget. So, in a file custom_widgets.py, you create your widget. The easiest way to do this is to subclass one of the existing widgets (TextInput is a good one here, because that’s a normal textbox, which is what gets used by default for CharFields) and then change its render method:

import django.newforms as forms
from string import Template
from django.utils.safestring import mark_safe

class ColourChooserWidget(forms.TextInput):
  def render(self, name, value, attrs=None):
    tpl = Template(u"""<h1>There would be a colour widget here, for value $colour</h1>""")
    return mark_safe(tpl.substitute(colour=value))

There are a few interesting wrinkles in there.

First, overriding render() changes the HTML that your widget prints when asked to display itself by the admin system. (I haven’t actually implemented the widget there; left as an exercise for the reader, that bit.)

Second, you need to call mark_safe() on the HTML you return, otherwise the admin will escape it.

Third, all input to mark_safe() must be Unicode, hence the u""" at the beginning of the string. The parameter value already is Unicode, but any strings you provide must also be explicitly Unicode strings; otherwise, mark_safe() fails silently — the string will be escaped.

Fourth, you don’t have to use string.Template, but it’s pretty convenient.

Once you’ve created your custom widget, you have to hook it up to your model. In our example, we need to change VehicleAdmin:

from custom_widgets import ColourChooserWidget

...

class VehicleAdmin(admin.ModelAdmin):
  search_fields = ["name", "colour"]
  def formfield_for_dbfield(self, db_field, **kwargs):
    if db_field.name == 'colour':
      kwargs['widget'] = ColourChooserWidget
    return super(ArticleOptions,self).formfield_for_dbfield(db_field,**kwargs)

The formfield_for_dbfield() function gets called for each of the fields in your model; for the one we care about (colour in this example), override widget in the kwargs and then carry on with the rest of the function, and that hooks it up.

That should be it; now, in the Django admin system, your colour field will use your ColourChooserWidget for editing.