String.prototype.supplant = function (o) {
    // From http://javascript.crockford.com/remedial.html
    return this.replace(/\{([^{}]*)\}/g, function (a, b) {
        var r = o[b];
        return typeof r === 'string' || typeof r === 'number' ? r : a;
    });
};

function to_qs(o) {
    var params = [];
    for (var k in o) if (o.hasOwnProperty(k) && o[k] !== undefined) {
        params.push(k + '=' + o[k]);
    }
    return '?' + params.join('&');
}


// http://www.flickr.com/services/api/flickr.photos.search.html
// http://www.flickr.com/services/api/misc.urls.html
function Flickr(options) {
    var that = this;
    var api_url = 'http://api.flickr.com/services/rest/';
    var photo_url = 'http://flickr.com/photos/{owner}/{id}';
    var thumb_url =
        'http://farm{farm}.static.flickr.com/{server}/{id}_{secret}_{size}.jpg';
    this.options = options;
    options.size = options.size || 's';  // s=75x75, t=100x?, m=240x?

    function link_thumb(p) {
        p.size = that.options.size;  // to make supplant easier below
        var a = document.createElement('a');
        var img = document.createElement('img');
        a.appendChild(img);
        a.href = photo_url.supplant(p);
        img.src = thumb_url.supplant(p);
        img.title = p.title;
        if (p.size === 's') {  // otherwise we don't know the size
            img.width = 75;
            img.height = 75;
        }
        return a;
    }

    function response(r, target) {
        if (r.stat === 'fail') {
            var fail = document.createTextNode('Flickr failed: ' + r.message);
            target.appendChild(fail);
            return;
        }
        var photos = r.photos.photo;
        for (var i in photos) if (photos.hasOwnProperty(i)) {
            target.appendChild(link_thumb(photos[i]));
        }
    }

    this.load = function (element) {
        if (typeof element === 'string') {
            element = document.getElementById(element);
        }
        if (!element) {
            return;
        }

        // This mess is because the Flickr API requires us to provide the
        // callback function by name; we can't just call this.response().
        for (var i = 0; Flickr.hasOwnProperty('callback' + i); ++i) {}
        var callback = 'callback' + i;
        Flickr[callback] = function (r) { response(r, element); };

        var params = {
            format: 'json',
            method: 'flickr.photos.search',
            jsoncallback: 'Flickr.' + callback,
            api_key: this.options.api_key,
            tags: this.options.tags,
            tag_mode: this.options.tag_mode,
            user_id: this.options.user_id,
            per_page: this.options.count
        };

        var script = document.createElement('script');
        script.src = api_url + to_qs(params);
        document.getElementsByTagName('head')[0].appendChild(script);
    };
}
