The Pug Automatic

Improved bookmark to batch link Flickr images

Written March 10, 2007. Tagged JavaScript, Firefox, Bookmarklets.

A couple of months ago, I wrote a bookmarklet to blog all images from a Flickr page. The idea is that you can go to pretty much any Flickr page – perhaps the images you uploaded today – and get the code to post all of them in a blog or forum without having to manually copy that information from each image in turn.

Flickr have since changed their image naming scheme so that the URLs of full-size images are not predictable (in any obvious way) from any property of smaller images.

I've now rewritten it into a pretty big bookmarklet (4 912 bytes when URL encoded – does this make it a regular bookmark? A supermarklet?) that uses the Flickr JSONP API to look up the names of full-size images.

Output can be provided as linked HTML images, linked HTML images within a LiveJournal LJ-cut, or forum BBCode. Unlike that of the old bookmarklet, the output is presented in a pretty overlay that can be discarded for further browsing.

Using API calls to look up the URL of the full-size image is somewhat slower than the old bookmarklet (which no longer works), but in return, you get fancy visuals – images scheduled for look-up are faded in opacity, and return to full opaqueness once the information is retrieved. If an image stays opaque, that suggests the image in question is non-public.

It works in Firefox. I've confirmed it does not work in Safari. I won't spend time making it compatible, but if someone else wants support in other browsers, please do investigate and post a patch in the comments.

Bookmark this: Flickr batch linker. I suggest using "bat" for a bookmark keyword.

Pass the bookmarklet a (case-insensitive) argument beginning with L to get an LJ-cut, or B for BBCode. Anything else, or no argument, will give you HTML. So usage could e.g. be either of

bat
bat html
bat bb
bat lj

The un-encoded code follows. It should be URL encoded before bookmarking it, but the "%s" is a Firefox quick-search placeholder (for argument passing) that should not be encoded.

As far as I could figure out, Flickr API keys are per application rather than per user, so it's included in the code.

function $(id) { return document.getElementById(id); }
function destroy(el) { el.parentNode.removeChild(el); }

function get_source_for(id) {
var request = document.getElementsByTagName("head")[0].appendChild(document.createElement("script"));
request.id = "fbat_request_" + id;
request.src = "http://api.flickr.com/services/rest/?method=flickr.photos.getSizes&format=json&api_key=58b74bb27f70254a67c34ed95710bc12&photo_id=" + id;
$("fbat_" + id).style.opacity = 0.3;
}

function jsonFlickrApi(json) {
counter -= 1;

if (json["stat"] == "fail") return;

var source, sizes = json["sizes"]["size"];
for (var i=0; i < sizes.length; i++) {
source = sizes[i]["source"];
if (sizes[i]["label"]=="Original") break;
}
var id = source.match(/\/(\d+)_/)[1];

$("fbat_" + id).style.opacity = 1;
destroy($("fbat_request_" + id));

results[id]["source"] = source;
if (!counter) finish();
}

function finish() {
var key, image, images = [];
for (i=0; i<ordered_keys.length; i++) {
image = results[ordered_keys[i]];
if (!image["source"]) continue;
if (format.match(/^b/i)) // b, BB, BBcode, ...
images.push('[img]' + image["source"] + '[/img]');
else
images.push('<a href="' + image["href"] + '" title="' + image["alt"] + '"><img src="' + image["source"] + '" alt="'+ image["alt"] +'" /></a>');
}

var output, output_prefix, output_suffix;
output_prefix = output_suffix = "";
if (format.match(/^l/i)) { // l, LJ, LJcut, ...
output_prefix = "<lj-cut text=\"The photos.\">\n\n";
output_suffix = "\n\n</lj-cut>";
}
output = output_prefix + images.join("\n\n") + output_suffix;

present(output);
}

function present(output) {
var overlay = document.createElement("div");
with (overlay.style) {
position = 'fixed';
top = left = 0;
width = height = '100%';
textAlign = 'center';
backgroundColor = '#FFF';
}
document.body.appendChild(overlay);
overlay.innerHTML = "<h3>Copy this code, then click without the box to discard.</h3><textarea style='width:90%;height:80%;border:1px solid #000;background:#FEFEFE;padding:1em;' id='fbat_output'>" + output + "</textarea>";
$('fbat_output').select();
content.addEventListener('click', function() { destroy(overlay); }, false);
$('fbat_output').addEventListener('click', function(e) { e.stopPropagation(); }, false);
}

var counter = 0, fail = 0, ordered_keys = [], results = {};
var m, id, image, images = document.images;
var format = "%s";

for (i=0; i<images.length; i++) {
image = images[i];
if (
(m = image.src.match(/http:\/\/.*?static\.flickr\.com\/\d+\/(\d+)_[a-z\d]+(?:_[a-z])?\.jpg/)) &&
!image.className.match(/\b(setThumb|nextprev_thumb)\b/) &&
image.id != "primary_photo_img"
) {
id = m[1];
image.id = "fbat_" + id;
ordered_keys.push(id);
results[id] = {"alt" : image.alt, "href" : image.parentNode.href};
counter += 1;
get_source_for(id);
}
}

void(0);