So a bookmarklet is a little JavaScript script that’s intended to be run from a web browser’s bookmarks bar or menu. The reason they work as “bookmarks” is that the JavaScript source code is crammed into the form of a URL using the “javascript:” scheme.
Developing or modifying bookmarklets can be irritating, to say the least, because of this requirement that the JavaScript code be in the form of a URL.
For example, here’s a simple bit of JavaScript that (1) gets the title of the current web page; (2) replaces all punctuation in that string with exclamation marks;1 and (3) shows an alert dialog with the new string.
var str = document.title;
str = str.replace(/[[:punct:]]/g, '!');
alert(str);
Pretty simple. But that same script, in the form of a bookmarklet, looks like this:
javascript:var%20str%20=%20document.title;str%20=%20str.replace
(/[[:punct:]]/g,%20%27!%27);alert(str);
In real life, that’s all on one line; I added a line break to make it fit better on screen. (The same goes for all subsequent bookmarklet examples in this article.) In addition to removing line breaks, spaces need to be escaped (%20), and it’s a good idea to escape single- and double-quotes, too.2 You also need to escape non-ASCII characters, if any.
So the problem developing bookmarklets is this: You want to write and edit normal JavaScript code, but you need to publish hard-to-read URLs.
My solution: A “Make Bookmarklet” Perl script that I run as a BBEdit filter that (1) takes as input a file containing JavaScript source code; (2) creates a bookmarklet URL from that source code; and then (3) places the bookmarklet code in a comment at the first line of your JavaScript source, but otherwise preserves your original script.
So, for example, given this trivial JavaScript:
var str = document.title;
alert(str);
My “Make Bookmarklet” script will return:
// javascript:var%20str%20=%20document.title;alert(str);
var str = document.title;
alert(str);
If you run it again, with a comment on the first line that begins with “//Â javascript:”, it will replace that comment rather than add another one. The idea is that you can keep running the “Make Bookmarklet” script each time you make a change to your JavaScript code.
And, as an added bonus, the bookmarklet URL string is also placed on the clipboard, which means you can run the script, and then immediately switch to your browser and paste it into the browser’s bookmarks editor.
#!/usr/bin/env perl
#
# http://daringfireball.net/2007/03/javascript_bookmarklet_builder
# Licence: http://www.opensource.org/licenses/mit-license.php
use strict;
use warnings;
use URI::Escape qw(uri_escape_utf8);
use open IO => ":utf8", # UTF8 by default
":std"; # Apply to STDIN/STDOUT/STDERR
my $src = do { local $/; <> };
# Zap the first line if there's already a bookmarklet comment:
$src =~ s{^// ?javascript:.+\n}{};
my $bookmarklet = $src;
for ($bookmarklet) {
s{^\s*//.+\n}{}gm; # Kill comments.
s{\t}{ }gm; # Tabs to spaces
s{[ ]{2,}}{ }gm; # Space runs to one space
s{^\s+}{}gm; # Kill line-leading whitespace
s{\s+$}{}gm; # Kill line-ending whitespace
s{\n}{}gm; # Kill newlines
}
# Escape single- and double-quotes, spaces, control chars, unicode:
$bookmarklet = "javascript:" .
uri_escape_utf8($bookmarklet, qq('" \x00-\x1f\x7f-\xff));
print "// $bookmarklet\n" . $src;
# Put bookmarklet on clipboard:
`/bin/echo -n '$bookmarklet' | /usr/bin/pbcopy`;
Here’s how to use it with BBEdit 8.6 or TextWrangler 2.2. If you use some other editor, there’s probably some easy way to use shell script filters with your editor, too — there’s nothing BBEdit-specific in this script. (It ought to work as a service via ThisService, too, but I haven’t tried that.)
Copy the above Perl script and paste it into a new text window.
Save it in your ~/Library/Application Support/BBEdit/Unix Support/Unix Filters/ folder. (Substitute “TextWrangler” for “BBEdit” if necessary.) I named mine “Make Bookmarklet”, but you can name it whatever you want.
When run in a document window with a range of selected text, the selection will be passed as input and replaced with the output. If no text is selected, the entire contents of the window will be used as input and replaced with the output. With this script, it’s generally easiest to run it with no selection.
This script works well for me, but it hasn’t exactly been widely tested. If it eats your source code, Undo is your friend. If you spot anything wrong or wish to suggest improvements, please do let me know.
Worth noting: the [:punct:] predefined character class works in Safari, but doesn’t seem to work in Mozilla-based browsers such as Firefox or Camino. So, you probably shouldn’t use that in production. ↩
It’s unclear to me what characters must be escaped in a bookmarklet URL. Some sources suggest that other punctuation characters, such as brackets and semicolons, ought to be escaped, too, but I can see no practical reason to do so. If you want to be really conservative and escape just about everything, change this line:
uri_escape_utf8($bookmarklet, qq('" \x00-\x1f\x7f-\xff));
to:
uri_escape_utf8($bookmarklet);
Personally, I prefer to keep the bookmarklet URL itself as readable as possible. ↩