Improved bookmark to batch link Flickr images

Written . Tagged Bookmarklets, Firefox, JavaScript.

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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);