2006-02-23

User script configuration: best practices

While my link finder bookmarklet is kind of a neat way of looking up where a link back to you is in a page, to see the link context (without the hassle of reading the entire page, and perhaps even hovering every last link when they are not very apparent), it's kind of hasslesome and manual to invoke. Why not have a user script always do the job automatically for you?

On further consideration, I think I'd like to do it like this: set up an URL regexp registry and a matching icon image registry, and scan pages for links to URLs matching anything in the registry, adding the appropriate icon to the link, when found. Unless the present URL also matches either of the regexps, in which case we don't do anything at all. For the first nine or ten outbound matched links found (we probably rarely encounter that many, anyway) also add a numeric accesskey to zoom to each occurrence at the touch of two buttons (or three, depending on your browser).

Configuration: here comes the fun part. Only sissies and those with browsers which still lack provisions for storing user script configuration data (Opera, at present) configure user scripts by editing the raw script content. My preferred way of doing script configuration, for domain inspecific scripts such as this, where there is no previously known DOM (and indeed CSS) layout to hook into to add an UI, is to set up the script with one, somewhere on the web, and have it detect the presence of that page, to inject its UI there in turn. The script quite literally gets a home page, where you can incidentally also write about, link updates to the script and have the script detect and show that it is out of date and could use reinstalling.

GM_registerMenuCommand could of course also be used to create some series of prompt() and confirm() dialogues to do configuration, but that is a cheap man's unwieldy "quick hack" interface, and not very pleasant to use for anything but throw-away prototype/proof-of-concept hacks.

For this particular script, I'm thinking of exposing the lists of regexps and icon URLs, so you can edit them to your heart's content. I'll make a companion bookmarklet to go with it, to invoke on a page you would want to add to the list of sites you would like to track, that heads away to the config page, where a new regexp is suggested to match the referrer page domain, and, if the referring page had a favicon (more correctly: a <link rel="shortcut icon"> directive), that URL selected by default. Add some "delete" action links to taste, and I think we have a fairly complete config setup.

This could turn into a rather useful hack for all of us blog narcissists out there.

2006-02-18

Javascript libraries handling keyboard events?

I was just peeking at the BSD licensed YAHOO.util.Event library, wondering when there will show up a Javascript library that wraps keyboard event handling, taking key repeat into consideration. (This post is one ideal spot to announce and market your such a library, should you write or have happen upon one.)

The Google Maps API (v1) has most of this, but doesn't expose it to user level, and especially not so with real names for identifiers, documentation or any other bits to make it usable for an applications level developer.

To interface the keyboard as a control device for an application, you basically want these features:

  • keydown, keyup and keypress event callbacks for specific keys, keypress being fired on consecutive keydown and keyup events for the key in question, and not being fired by the host operating system's keyrepeat features

  • not triggering said callbacks when some text input field has keyboard focus

  • a query method to ask whether a specific key is in pressed down now.

  • a query method to ask what keys are in pressed down state now

  • a symbolic key name handling abstraction, to rid your code of unreadable key code constants

  • additional bonus points for allowing polling consumption of keypress events rather than just by way of fired callbacks right when the event is fired
Note that I am not talking about custom text input controls now; that is a separate use case issue the HTML input and textarea tags should hopefully suffice for, even though I know they don't as soon as you want to do anything advanced, such as handling selections, cut and paste and the like. I leave any such issues now, though; this article only concerns keyboard controls for full screen type applications, such as games for instance, or presentations.

Google Maps is an excellent example of an application that makes good use for these controls. When run with keyboard controls on, you can pan the map using the arrow keys, for instance, or whole pages using Page Up, Page Down, Home and End. (The Maps API does not enable this by default, but you can add them fairly easily add it -- if I remember correctly, it's done using map.registerKeyHandlers(window.document).)

A naïve implementation, registering with the DOM to receive keypress all events, would buffer up a busload of keypresses (due to key repeat, as set up in the end user's operating system) when a user keeps an arrow key down to scroll the map continuously, and keep processing queued-up events for some length of time after the user releases the key, which is clearly not what you want. Similarly, an application for a set top box interface I wrote at work last year, where icons scroll into view on pressing the remote control keys for "next" and "previous", had the same problem:

  • If you tap a key four times, you expect an action to be performed four times.

  • If you keep a key pressed, you expect some action to be performed continuously until you let go of the key
A keyboard handling library should leverage this mode of operation expected by the user, enabling you to easily realize that model. It is neither trivial nor very difficult, but it does need some base support along the lines of those discussed above. For the sake of fleshing out the details of the APIs, let's discuss how such an application would handle input (shamelessly modelled on one that the company I work for sells; feel free to contact sales@easyview.org if interested).

easyView

Pressing right or left scrolls the next icon into focus, rolling the line of icons one step in either direction. When in a "moving" state, we react instantly to events that would reverse scroll direction, but do not process events that would move in the same direction again, until the completion of one move step. When there, we consume a keypress, if enqueued, or keep scrolling, if we were told that the key is still in a pressed state. If no keys were on the input queue, and no key was still pressed down, we stop scrolling at the icon we ended up on.

As always, the best way of encouraging the broad masses to do good user interface design, is to show how it's done, and to make doing it really easy. The first is what tutorials and articles (such as this one) do, the second what libraries do. (Finally, making it possible at all is what the W3C does.) Your help in bringing about better overall design of new things coming is greatly appreciated, whichever end of the spectrum you contribute to.

2006-02-17

Translate using bookmark keywords

If you are familiar with Google Language Tools, you know that there are provisions for turning perfect foreign language pages into severely broken but sometimes human parsable English, or sometimes the other way too. It's just not very accessable being on some remote web page, when I want to translate this page, now. And before someone mentions browser extensions that do that kind of thing, I'll add that they add clutter to menus or toolbars, which I don't want.

But there is a very trivial solution anyway: the bookmarklet's little sister, Smart Keywords, as they are apparently called today, or Smart Bookmarks, or Bookmark Keywords, as they were called when the feature came to Mozilla in 2002. How quickly names change over time, eh?

Anyway, the idea is simple enough: treat the browser address field a bit like a command line. Type a command name, optionally followed by a space and a parameter, press return, and the browser looks up the command URL from your bookmarks, using the command name as a keyword (rather than having you chase through your unwieldy maze of bookmarks) and replaces occurrences of %s in it with the parameter you might have passed. (If you don't pass a parameter, the %s is kept intact, at least in Firefox.) And like so many other really basic ideas like this, it's immensely useful.

Today I made myself a handful of keyword bookmarks for Google Language tools, one for each language, translating from German, French, Italian, Portuguese, Japanese, Korean and simplified Chinese to English, naming them by their ISO-639-1 two-letter language codes, de, fr, it, pt, ja, ko, cn. (Actually, I made two for Spanish and Japanese, as I tend to forget it's es and not sp, and ja and not jp.)

When you have bookmarked them (or those you want), visit the bookmark properties view and name them in the Keyword field. I tucked all of mine into a bookmark folder I called "translate" way down in the least accessable parts of my bookmarks, so they won't get in the way of anything.

Here is where the fun begins. Whenever I end up on a page I can't read due to it being written in some unfamiliar language, but would want to get at least a vague concept of, I prepend "ja " to the addess field and hit return, or whichever language the page was written in. Totally keyboard driven goodness, never more than seven key strokes away; Ctrl-L, Home, j a Space Return.

The expressiveness of the command line, the swift accessibility of built-in keyboard shortcuts and the clean user interface of an extensionless browser. Just the way I like it. The user interface is all neatly tucked away in your head. Non-programmers can make these things all on their own too, and it's not limited to just searches, either, as you noted above.

And for us programmers, it's trivial to marry them with common bookmarklet making, if you make the occasional bookmarklet that requires user input, typically otherwise done by way of prompt(). %s works just as described above, in a javascript bookmarklet URL, too. Keep innovating, making the most of the tools at your disposal!