fixed sets... added support for user tags
[FlickrHacks.git] / _greasemonkey_ / flickrsnap.user.js
blob8a324affb31c5689c83c8c5bbe4a4e24f52bc448
1 // Flickr Snap
2 // v0.1
3 // 2006-05-09
4 // Copyright (c) 2006, Pierre Andrews.
5 // Released under the GPL license
6 // http://www.gnu.org/copyleft/gpl.html
7 //
8 // ==UserScript==
9 // @name Flickr Snap
10 // @namespace http://6v8.gamboni.org/Flickr-Snap.html
11 // @description Add thumbnails of flickr image where a link goes there.
12 // @source http://6v8.gamboni.org/Flickr-Snap.html
13 // @identifier http://6v8.gamboni.org/IMG/js/flickrsnap.user.js
14 // @version 0.1
15 // @date 2006-05-09
16 // @creator Pierre Andrews (mortimer.pa@free.fr)
17 // @include *
18 // @exclude http://flickr.com*
19 // @exclude http://www.flickr.com*
20 // @exclude http:/del.icio.us*
21 // ==/UserScript==
23 (function () {
25 var win = (unsafeWindow || window.wrappedJSObject || window);
27 //update information
28 var SCRIPT = {
29 name: "Flickr Snap",
30 namespace: "http://6v8.gamboni.org/Flickr-Snap.html",
31 description: "Add thumbnails of flickr image where a link goes there.",
32 source: "http://6v8.gamboni.org/Flickr-Snap.html", // script homepage/description URL
33 identifier: "http://6v8.gamboni.org/IMG/js/flickrsnap.user.js",
34 version: "0.1", // version
35 date: (new Date(2006, 5, 09)) // update date
36 .valueOf()
39 //======================================================================
42 //======================================================================
44 // constants
45 // http status constants
46 var OK = 200;
48 // xmlhttprequest readystate
49 var COMPLETE = 4;
51 var DEBUG = false;
53 //======================================================================
54 //exception
55 var procException = function(msg, code, req) {
56 this.msg = msg;
57 this.code =code;
58 this.req = req;
62 //======================================================================
63 //to do the closure and get the right this.
64 //adapted from http://persistent.info/greasemonkey/gmail.user.js
66 function getObjectMethodClosure(object, method) {
67 return function() {
68 return object[method]();
72 function getObjectMethodClosure0(object, method,args) {
73 return function() {
74 return object[method](args);
78 function getObjectMethodClosure1(object, method) {
79 return function(arg) {
80 return object[method](arg);
85 function getObjectMethodClosure11(object, method,args3) {
86 return function(arg) {
87 return object[method](arg,args3);
91 function getObjectMethodClosure2(object, method) {
92 return function(arg,arg2) {
93 return object[method](arg,arg2);
96 function getObjectMethodClosure21(object, method,args3) {
97 return function(arg,arg2) {
98 return object[method](arg,arg2,args3);
105 //======================================================================
106 //Simple calls to flickr REST API, from the batch enhancer script
107 // needs the md5 and status_msg code above
109 win.FlickrAPI = function(){;}
111 win.FlickrAPI.prototype = { // flickr api
113 init: function(api_key,shared_secret) {
114 this.api_key = api_key;
115 this.shared_secret = shared_secret;
116 if(shared_secret) this.auth_token = GM_getValue('auth_'+this.api_key);
118 if (this.shared_secret && !this.auth_token) {
119 this.askForAuth();
124 askForAuth: function() {
125 this.flickr_api_call("flickr.auth.getFrob",
126 {api_sig: this.getMethodSig("flickr.auth.getFrob", {api_key: this.api_key})},
127 getObjectMethodClosure2(this,'frob_loaded'),
128 getObjectMethodClosure1(this,'frob_failed'));
131 frob_loaded: function(req, rsp) {
133 this.frob = rsp..frob[0];
134 if(DEBUG) GM_log("received Frob "+this.frob);
135 var api_sig = this.getMethodSig(false, {api_key: this.api_key,frob:this.frob,perms:"read"});
136 var url= "http://flickr.com/services/auth/?api_key="+this.api_key+"&perms=read&frob="+this.frob+"&api_sig="+api_sig;
137 //Here, we need the status_msg code
138 status_msg.msgbox2("This script needs to be authorized. <br>" +
139 "<b style=\"font-variant:small-caps;\">Click [<a onclick='window.open(\""+url+"\"); return false'>Step1</a>]</b>, " +
140 "follow the instructions in the popup window,<br> " +
141 "then return here click Step2.<br> " +
142 "Popup blockers may cause this not to work.<br>You'll only have to do this once.","Step2",getObjectMethodClosure1(this,'getToken'));
145 frob_failed: function(e) {
146 status_msg.msgbox('Couldn\'t authorize, for whatever reason.');
149 token_loaded: function(req,rsp) {
150 status_msg.hide();
151 var token = rsp..token[0];
152 this.nsid = rsp..user.@nsid[0];
154 if(DEBUG) GM_log("authenticated with user "+this.nsid+": "+token);
155 this.auth = token;
157 GM_setValue('auth_'+this.api_key,""+token);
160 token_failed:function(e) {
161 status_msg.msgbox('Couldn\'t authorize, for whatever reason.');
164 // set it all up
166 getToken: function()
168 status_msg.show('authorizing...');
169 var api_sig = this.getMethodSig("flickr.auth.getToken", {api_key: this.api_key,frob:this.frob});
170 this.flickr_api_call("flickr.auth.getToken",
171 {frob: this.frob,api_sig: api_sig},
172 getObjectMethodClosure2(this,'token_loaded'),
173 getObjectMethodClosure1(this,'token_failed'));
176 do_req: function ( method, proc_request, url, referer, data ) {
177 var headers = new Object();
178 var details = {
179 method : method,
180 onload : function(d) { proc_request(d) },
181 url : url,
182 header : headers
185 if (referer != null)
186 headers['Referer'] = referer;
188 if (data != null) {
189 headers['Content-Type'] = 'application/x-www-form-urlencoded';
190 details['data'] = data;
193 GM_xmlhttpRequest( details );
198 // a proc just spins around waiting for the thing to succeed or fail
199 // then calls a callback, if we got 200 OK message.
200 make_proc: function (op_name, ok_cb, fail_cb) {
202 return function(req) {
204 try {
205 // init progress
206 document.body.style.cursor = 'progress';
208 if (req.readyState != COMPLETE) {
209 return;
212 // if (alert_response) { alert(req.responseText); }
214 if( req.status != OK ) {
215 throw new procException( op_name + " request status was '" + req.status + "'", 0, req )
218 ok_cb(req);
220 } catch(e) {
222 // clean up progress
223 document.body.style.cursor = 'default';
226 if (e instanceof procException) {
227 if( fail_cb != null )
228 fail_cb( e );
229 else {
230 GM_log( e.msg );
231 if (DEBUG) {
232 GM_log(e.req.responseText);
235 } else {
236 throw(e);
240 // clean up progress
242 document.body.style.cursor = 'default';
247 // this is wraps the spinning proc like above,
248 // except it parses the flickr api response a little before deciding all is well,
249 // and passing control to the all-is-well callback
250 make_flickr_api_proc: function(op_name, ok_cb, fail_cb) {
252 function parse_and_ok_cb(req) {
253 if(DEBUG) GM_log(req.responseText);
254 var rsp = req.responseText.replace(/<\?xml.*\?>/,'');
255 var rsp = new XML(rsp);
256 // var rsp = req.responseXML.getElementsByTagName('rsp').item(0);
258 if (rsp == null) {
259 throw new procException( "Could not understand Flickr's response.", 0, req );
262 var stat = rsp.@stat;
263 if (stat == null) {
264 throw new procException( "Could not find status of Flickr request", 0, req);
267 if (stat != 'ok') {
268 if (stat == 'fail') {
269 var err_node = rsp.err[0];
270 var code = err_node.@code;
271 var err_msg = err_node.@msg;
272 throw new procException( err_msg, code, req );
273 } else {
274 throw new procException("Unknown error status: '" + stat + "'", 0, req)
278 ok_cb(req, rsp);
281 return this.make_proc(op_name, parse_and_ok_cb, fail_cb);
284 getMethodSig: function(method, args)
286 var data = new Array();
287 var names = new Array();
288 var sig = this.shared_secret;
290 if(method) {
291 data['method'] = method;
292 names.push('method');
294 for (var key in args) {
295 data[key] = args[key];
296 names.push(key);
298 names.sort();
299 for (i in names) {
300 sig += names[i] + data[names[i]];
302 return hex_md5(sig);
306 // construct a flickr api request, with method and args,
307 // if that worked, call callback with request object.
308 flickr_api_call: function( method, args, ok_cb, fail_cb,with_auth) {
310 var http_method = args['http_method'];
311 http_method = ( http_method ? http_method : 'GET' );
312 delete args['http_method'];
314 args['api_key'] = this.api_key;
316 if (this.shared_secret && with_auth && this.auth_token) {
317 args['auth_token'] = this.auth_token;
318 args['api_sig'] = this.getMethodSig(method, args);
319 } else if(DEBUG) GM_log('not signing: ' + method);
322 var url = 'http://www.flickr.com/services/rest/?method=' + encodeURIComponent(method);
324 for (var key in args) {
325 url += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(args[key]);
327 if(DEBUG) GM_log(url);
329 var proc = this.make_flickr_api_proc( method, ok_cb, fail_cb )
331 this.do_req(http_method, proc, url, null, null)
336 //======================================================================
338 var MAX_DAY_COUNT = 5;
339 var NUMBER_OF_LAST_PHOTOS = 8;
341 win.FlickrSnap = function() {;}
344 win.FlickrSnap.prototype = {
345 init: function(key,secret) {
346 this.api = new win.FlickrAPI();
347 this.api.init(key,false);
348 var auctionLinks = document.evaluate(
349 "/html/body//a",
350 document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); // Get all group
352 for(var i = 0; i < auctionLinks.snapshotLength; i++) { // For each LINK...
353 var al = auctionLinks.snapshotItem(i);
354 this.processA(al);
357 checkImage: function(link) {
358 for(var i=0;i<link.childNodes.length;i++) {
359 if(link.childNodes.item(i).nodeName == 'IMG') {
360 return true;
364 processA: function(link) {
365 if(matches = /^(http:\/\/)?(www\.)?flickr.com\/photos\/[^\/]+?\/([0-9]+)/i.exec(link.href)) {
366 if(!this.checkImage(link)) { //the link is not on an image
367 this.api.flickr_api_call('flickr.photos.getSizes',
368 { photo_id: matches[3], http_method: 'POST' },
369 getObjectMethodClosure21(this,'getSizes_done', link),
370 getObjectMethodClosure1(this,'request_failed'));
372 } else if(matches = /^(http:\/\/)?(www\.)?flickr.com\/photos\/[^\/]+?\/sets\/([0-9]+)/i.exec(link.href)) {
373 if(!this.checkImage(link)) { //the link is not on an image
374 this.api.flickr_api_call('flickr.photosets.getPhotos',
375 { photoset_id: matches[3], http_method: 'POST' },
376 getObjectMethodClosure21(this,'getSetPhotos_done', link),
377 getObjectMethodClosure1(this,'request_failed'));
382 request_failed: function(err) {
383 GM_log(err.msg);
385 getSizes_done: function(req,rsp,link) {
386 img = link.appendChild(document.createElement('IMG'));
387 img.src = rsp..size.@source[0];
389 getSetPhotos_done: function(req,rsp,link) {
390 var primary = rsp..photo.(@isprimary == '1');
391 img = link.appendChild(document.createElement('IMG'));
392 img.src = "http://static.flickr.com/"+primary.@server+"/"+primary.@id+'_'+primary.@secret+'_s.jpg';
396 // update automatically (http://userscripts.org/scripts/show/2296)
397 try {
398 window.addEventListener("load", function () {
399 try {
400 win.UserScriptUpdates.requestAutomaticUpdates(SCRIPT);
401 } catch (ex) {}
403 var flickrgp = new win.FlickrSnap();
404 flickrgp.init( "e8c3239ff04c102ce2d6ed885bf99005");
405 }, false);
406 } catch (ex) {}
408 })();