Mark my links

For a few days now, I have had the joy of browsing the web with Mark my links, a Greasemonkey user script that supersedes the Find links to you! bookmarklet I made a month ago. (Direct install link, Change Log and userscripts.org entry here.)

screen shots of Mark my links
Where the bookmarklet would only give you access keys to scroll the page to the first ten links bound for your page or pages, the user script also adds little favicons before the links (for a visual cue of which link was pointing where), and centers the view on them, as you press Alt+1..9 (and Alt+0 for link number ten). Also, you can now tab away to cycle through the rest of your inbound links, skipping past all other links in the page.

It's an experience similar to my first encounter with Greasemonkey, and after that my first encounter with BookBurro for book browsing. All of a sudden, it's my web. If someone links to me on a page I read, I see my icon there, right before the link. And if I browse my back links, reading up on pages linking to me where I know a link to me is present somewhere, homing in on it is but a keypress away. Just like that. It shows up before Google search results, in mail I read at GMail, on my Blogger profile page, at userscripts.org and, well, everywhere.

And most of all, it's really easy to set up and add new sites and link patterns to. A click on this bookmarklet on a site you want to add, and you are transfered to the config page, another click on "add" and a third on "Save changes" and you're done. And if the site did not have a favicon and you want to see a more specific one than my link love heart heart, click the heart and provide a better 16 by 16 pixel image instead before saving. That's all there is to it.

And should you want to give people reading your blog or web site a quick option to add your site to the system with a specific icon not used as your favicon, for some reason, you can do that too, linking that same configuration page, appending a # character and the URL for your preferred image to the end of the URL.

It doesn't end with sites, though. You can add file format indicators to links to pdf PDF files or greasemonkey Greasemonkey scripts (when the suffix is in plain view in the URL), GMail mail icons to mailto: links, or even mailto links at specific email providers, like this one, using suitable patterns. Pick up on links about you or some interest of yours by way of topic tracking, indicating links with some specific keyword present in the URL. Your imagination is the limit.

Maybe a good next hack might be creating some additional tools for enabling easy sharing of Mark my links setups (or just smaller constellations of matchers, for that matter). I have a few ideas up my sleeve already. And maybe I ought to move out most of the documentation and/or script history from the config page too to pages of their own, while at it; I got a bit carried away wanting to release it quickly.

So dig in. And digg it, perhaps? I did a bit of a site overhaul the other day, moving graphics to a greased weasel of a web server and adding a bit of a comfy bookmarking panel below the post tags. Hopefully things shouldn't load quite as slowly now, except for the main page which still loads Google Maps. Google Maps isn't a greased weasel quite yet.

Mark my links: configuration / updates

Latest stable version: 1.10. What is this?

What is this?

This is the joint configuration, updates and past history page of Mark my links, a tool to make the web a bit more friendly and centered on yourself, giving you the option of marking links to sites you care about (yours?) with icons, wherever you roam.

The configuration form above (which does not appear until you have installed Firefox (1.5 or later), Greasemonkey and the user script itself) has a prominent text field looking much like the location field of your browser. This is intentionally so; you can paste URLs into it, and click the tiny "add" icon (c/o the Silk icons set by Mark James) to the right of it to have links to that site show up prefixed with the site favicon during future browsing.

Below this URL field shows a list of all the sites (and other rules) and their icons. Upon just having installed the script, this site and user scripts are the only ones it is aware of; try adding a few sites, if you like. A new line with your domain name, prefixed "Site", and with a greenish border appears at the bottom of the list. What does this mean?

The green border around the input field signals that the URL presently in the (mock) URL field, had it been a link on a web page, would have been marked up with its icon. If you have another 16 by 16 pixels large icon you would want to use instead of that used for the site, or my default pick, if your site had no favicon (a cuteish heart, for link love), you can click the icon to give another URL. If you don't have it on the web, or don't want the script to fetch it from the web, you may instead opt to turn your image file into a data: URI, for instance at the data: URI kitchen.

When you are done setting up sites the way you like, remember to click the Save changes button. Not much will appear to happen, except if you reload the page, your choices will still be there, and if you opted not to click save first, they will have been forgotten.

Go off browsing the web, and see who links to you where. As a nice side feature, the ten first links on any page pointing your way can be zoomed to by pressing Alt+1 to Alt+9 (and Alt+0 for the tenth link). Tab your way forward from there; they are all added in one go, so you should not have to tab through any uninteresting links.

Can I mark links to specific file types too?

Yes, as long as they are given away by the URL. Time for the next feature insight: if you click the "Site" column, it will toggle to read "URL". Which means that for this row, the matcher regexp will be applied not only on the host name, but on the entire URL. The Greasemonkey icon is an example of that. Here, the regexp will be applied to the entire URL (minus the fragment, that last bit starting from # onwards). The scary-looking example will match a .user.js extension (URL query present or not).

Advanced options

Okay, that was nifty. That regexp bit had a nice ring to it; fill me in.

The basic mode of operation described above will match the host name of links to the text in the input field. So while I could have one line for ecmanaut and another for my idea blog (now actually a joint project with others who occasionally get ideas they don't have the time to address right away but want to blog about before they forget about it), I might make that line read (ecmanaut|some-assembly-required).blogspot.com instead to have my icon applied to links to either blog. If I try pasting some URLs from them in the URL field, sure enough, that regexp stays green. Nice.

Try out some regexps of your own. You may notice that it turns red once in a while while you are in the middle of something. This means that it's a broken regexp (don't rush me; I'll add the closing brace in a minute; sheesh). That redness just says we won't allow you to save the configuration until you fixed the problem.

Behind the scenes, in Site mode, this regexp is actually anchored to the host part of the link URL (with protocol and host parts lowercased), and it must match the full host; if a :4711 port is there, the link won't be marked with your image. Also, it means that links to www.google.com won't be marked by the regexp google.com; the regexp is anchored on both ends. Add a (www.)? prefix to it, if you like, or perhaps .* while at it. (And of course you may also opt to mark dots as \. rather than the catch-all wildcard; I have opted not to by default as it hurts readability and scares away the less technically minded. Bear with me; I want this to be useful to as many as possible, without enforcing stupid policies like replacing dots with quoted dots behind the scenes. Yes, I knew you would understand.)

In URL mode, your regexp is applied verbatim to all links.

Change Log

Version 1.10, released 2006-05-01

Added a workaround kludge for mozilla bug 340024 to make the configuration page work again in Firefox

Version 1.9, released 2006-03-17

Interface improvement; moved the checkbox next to the icon itself for context relevance and added a helpful clickable column header for some short documentation of the feature.

Version 1.8, released 2006-03-17

Added favicon overriding support. Check the checkbox to have pages matching the rule have your preferred favicon rather than the page default. Very useful for improving tab findability in windows with many tabs where page authors have failed to provide their own (or unique / identifiable) favicons.

Version 1.7, released 2006-03-04

Prior versions injected all icons in one batch, and then left them hanging where they were even as page layout changed around them. This version is experimenting with relayouting as the page changes, assuming page change is initated by mouse clicks. In other news, hidden links (in hidden divs, for instance) previously ended up with an icon at the top of the screen; now they are not shown at all. Moved code around a bit for improved readability.

Version 1.6, released 2006-03-02

Bugfix: removed needless double filtering and case insensitizing. Site matchers using patterns containing \b, \s and similar should work now. Also, the case insensitizing is applied to links prior to testing, rather than the regexps themselves.

Version 1.5, released 2006-03-01

Bugfix: sites lacking favicons were not properly given the default link love icon. Also added a note with the add new site bookmarklet to the user script documentation section.

Version 1.4, released 2006-02-28

Code generalizations for config page live matching. First public release.

Version 1.3, 2006-02-27

Some additional fixes to use the default icon in all cases it should show up, and not waste space needlessly storing it on disk. Added the user script Greasemonkey icon to the default configuration.

Version 1.2, 2006-02-27

Trimmed down my test setup to one default site (which is as easy to get rid of as it is useful visually to initially get acquainted with the config UI), and came up with a catchy name.

Version 1.1, 2006-02-27

There, now you can set favicons to data: URLs again. Dropping debug mode, getting ready for initial publication.

Version 1.0, 2006-02-26

Mostly cleanups and removing debug. Some fixes to the favicons UI.

Version 0.9, 2006-02-25

Abandoned core/UI feature split, moving the lot to the user script. I'll still be able to add some afterhthoughts on the config page, though; no sweat. Added some code to mark newer versions released here, so the script will alert the user of an available upgrade path when stopping by to reconfigure. Fixed a presumed event handler leak. Added save functionality and somewhat buggy favicon fetching.

Version 0.8, released 2006-02-25

Base sketches on a config UI; still read only, but now read and play, experimenting with core functionality in the user script and some on the page itself. An embedded heart shaped fallback favicon for sites for all of you poor souls who do not have a favicon of your own. This baby lets you see the link love flow to you too.

Version 0.7, 2006-02-24

Initial version. Basic injection and a read-only user interface without any bells or whistles. Thanks to Mor Roses for his take on converting E4X nodes to DOM nodes, making this hack more of a learning experience to play with. E4X is still not very mature working with like this, it seems; lots of time has gone to waste on syntax errors hidden somewhere between where they happened and a mysteriously nonfunctional greasemonkey script.

Userscript draft, published 2006-02-23

Userscript idea. Drafted a sketchy outline of functionality.

Bookmarklet, published 2006-02-01

Original custom bookmarklet generator implementation.


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.


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).


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.


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!


Sunlight over the year

Ever reflected upon how the sun is not as high in the sky on a given time throughout the year? In Sweden, at about 60 degrees latitude, it's very noticable how short the days become in winter, how late they start and how early they end.

Last week end, I was playing with John Walker's Earth viewer, a seasoned tool that has been on the web for the better year of the past decade more or less unchanged, and as excellent throughout this entire time. Few services have that kind of life span on the web.

Anyway, I wanted to animate the sun's reach over the year, for a given time of day, over perhaps 25 frames spread evenly across the rest of the year, and loop it. Not a very difficult application to write, with Walker's service generating the actual imagery, so before long I had a working setup, peeking down at me from 20,000 kilometers above:

Sunlight over the year


2006-02-11 09:00:00 UTC

If you are not particularly interested in the local daylight conditions where I live (chances are good you are not :-), you can always pick a more interesting spot on the face of the earth via my blog header, at least if you are reading this on my index page or on the day archive page, which feature that map. Just double click some spot on either of the map views in the blog header, and the globe will adjust to the new coordinates. Have fun!


coComment: a new contender in the two-way web

The two way conversation web just got a new player, after the ad hoc comment blogging hack piggybacking atop Del.icio.us as a public data repository backend, Commentosphere doing the same with a more custom crafted Ning playground backend and allowing for a bit more semantical data stored about comment threading.

Where these hacks have largely been a manual business for the end user, but which works across the entire web, coComment has gone the other way about it, and covers only a select few blogging platforms, presumably growing over time.

My own approach landed somewhere in the middle ground between these routes, starting from the manual though deadly precise Del.icio.us comment blogging style, partially automating the process for Blogger blogs with a Greasemonkey hack of mine. I did not move on to cover other platforms, largely due to a lack of ambition combined with my high expectations about getting good permalinks, to the very anchor of my comments -- so I can easily find and link to the spot on a page where I wrote something.

In this respect, coComment does not go as far -- it presently only picks up the comments you write and tie them to you, integrating the data with a few services for syndication and the like from their own site, where you can also track conversations by other members running the same system, and blogs where the blog owner has crafted his comment form to also run incoming messages by the coComment repository. There are lots of plans and high hopes about the kind of things we will be able to do with the coComment site and it's data, though, and they seem set on extending their coverage to a larger platform set, and involving external contributors who want to take part in helping this thing fly. An excellent combination.

At the coComment base site, the service is rather similar to off-site comment layering frameworks such as Hoodwink (where comments never actually touch a destination blog, though, but rather hang somewhere on a side server for people who opt in on the comment network's data only). Members may browse the site, fetch RSS feeds for their comments or those of other members. At present, these feeds are not as customizable as those of i e Hoodwink, though, where you may browse by any combination of blog, comment author and discussions where a particular author has taken part. They unfortunately only store (or present, at least) comment text though, so any links and similar HTML features of your comments are shaved off at the moment. I suppose it's time I head over to their feedback pages to share some user input.

To become a member in the beta program, you need to ask for a code to be let in, but said code is at least now sent out within a few days of your asking for it, so the hurdle isn't very high to leap.

Technically, the coComment implement their service using the cross browser, though laborious way of the browser bookmarklet. Brian Benzinger, apparently sharing my belief in automation through user scripts, made a Greasemonkey script for automatically injecting the coComment hookup code on blogs supported by coComment at the time. Not quite happy about a script I'd have to update (and find out when to update) manually as soon as coComment added another service (in the short time since he wrote it, they seem to have covered Flickr too), I cooked my own, which updates itself daily from coComment (userscripts.org entry).

It detects injectable pages the same way the bookmarklet injection code does (using the same ruleset as found in today's version of the injected code), but it appears it might need tightening up a bit not to inject on all pages matching those criterias -- the common Blogger post editor seems to be recognized as a comment posting facility, too. It will be interesting to see whether this post shows up in the system or not.

I just tailored my user script to exclude the blog editor from processing, just to be on the safe side, even though coComment did not actually seem to pick up anything on my posting this entry. Feel free to give a shout if there are any other pages on other blogging systems where I ought to do the same thing, by the way. Don't forget to include the full URL(s), so I can update the script accordingly.

And to coComment: feel free to use and promote this script for your user base. It works with Firefox, does not work with Opera (since Opera does not implement any persistent user script storage such as that offered by Greasemonkey's GM_setValue / GM_getValue), and it might work with Internet Explorer running Reify Turnabout. Installation is about as easy as the bookmarklet installation, the work on your part is as low, and running it is trivial, beyond even needing documentation.


Google Toolbar keyboard tips

Ever since I installed the Google Toolbar extension, I have been meaning to find out how to use it in mouseless mode to do the kind of things I usually use it for -- web search, image search and site search, mostly. Figuring it's so easy to add a hot key or twelve, I had kind of suspected that there would be some to instantly fire off a search as an Image search or Site search, right from the Google search field -- say, via Ctrl+I and Ctrl+S. No need to try those out, though; it is not done that way. Not yet, anyway.

I peeked through the source code today, and here is a run-down of what I learned (and have found out through prior experimentation), interspersed with other reflections, hints and tips:

  • Any AccessKey not already bound in a page (or used to invoke a Firefox menu), will bring keyboard input focus to the Google Toolbar search field. At least on a Windows system that means Alt+something puts you in the google bar. The mac equivalent is probably Command+something. I use Alt+§ (a very rarely used key that sits right below the escape key on Swedish keyboards).

    The default Firefox search field remains at Ctrl+K, and is of course still a useful resource, even if you have the google toolbar, as the googlebar does not have the beautiful search plugin interface offered by the default Firefox field. (How else would you search Sinfest comic strips? ;-)

    You don't need to keep the Firefox search bar on any toolbar, though; if you do not, it will just pop up when you press Ctrl+K, so you may hide it to save some screen real estate, even if you still use it for the odd one-off search plugin. It is not quite as good as the original panel mode though, as you can't switch search plugins quickly with Ctrl+Up/Down in the popup version. :-/ (If anyone has a bugzilla ticket id handy I'd be glad to cast a vote.)

  • The Google search field also has the Ctrl+Up/Down to switch search type though, between the few offered; web search, image search, "I'm feeling lucky" search, Google Groups, news article search, Froogle and GMail respectively, in that order. So Mail and Site search are only a step away each from the default mode. You can only switch search mode like this if you have checked the option "Remember last search type" in the Google Toolbar options tab "Search", otherwise Ctrl+Up/Down won't do anything at all.

  • Alt+Up/Down browses your past search history.

  • Up/Down on their own browses suggested common searches starting with what you have already typed.
When pressing return to fire off the search, you have a few other options too:

  • With the Alt key pressed, the result page gets loaded into a new tab.

  • With the Shift key pressed, the "I'm feeling lucky" search mode is invoked, so you instantly go to the first page found.

  • The modifiers stack, so Shift+Alt+return loads the lucky page into a new tab. They do not stack with your choice of search type, though, in case you had first picked Image, Site or Groups search, for instance, prior to hitting Shift+return - shift is Lucky Web Search, period.

  • The Control modifier is not reacted on in any way in this context.
Possible room for future improvement, there? I am considering doing some hacking of my own to orthogonalize "feeling lucky" and site search, for instance, so they can be bound on a modifier each and actually work as I would expect them to. But that is plenty of material for another later article.