- Fixed a bunch of bugs for existing paginations
[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()
37         };
39         //======================================================================
42         //======================================================================
44          // constants
45          // http status constants
46         var OK = 200;
47         
48         // xmlhttprequest readystate
49         var COMPLETE = 4;
50         
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;
59         };
60                 
61         
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](); 
69                 }
70         }
72         function getObjectMethodClosure0(object, method,args) {
73                 return function() {
74                         return object[method](args); 
75                 }
76         }
78         function getObjectMethodClosure1(object, method) {
79                 return function(arg) {
80                         return object[method](arg); 
81                 }
82         }
84         
85         function getObjectMethodClosure11(object, method,args3) {
86                 return function(arg) {
87                         return object[method](arg,args3); 
88                 }
89         }
91         function getObjectMethodClosure2(object, method) {
92                 return function(arg,arg2) {
93                         return object[method](arg,arg2); 
94                 }
95         }
96         function getObjectMethodClosure21(object, method,args3) {
97                 return function(arg,arg2) {
98                         return object[method](arg,arg2,args3); 
99                 }
100         }
101         
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(){;}
110         
111         win.FlickrAPI.prototype = {     // flickr api 
112                 
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);
117                         
118                         if (this.shared_secret && !this.auth_token) {
119                                 this.askForAuth();
120                                 
121                         } 
122                 },
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'));
129                 },
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'));
143                 },
145                 frob_failed: function(e) {
146                         status_msg.msgbox('Couldn\'t authorize, for whatever reason.');
147                 },
148                 
149                 token_loaded: function(req,rsp) {               
150                         status_msg.hide();
151                         var token = rsp..token[0];
152                         this.nsid = rsp..user.@nsid[0];         
153                         
154                         if(DEBUG) GM_log("authenticated with user "+this.nsid+": "+token);
155                         this.auth = token;
157                         GM_setValue('auth_'+this.api_key,""+token);
158                 },
159                 
160                 token_failed:function(e) {
161                         status_msg.msgbox('Couldn\'t authorize, for whatever reason.');
162                 },
163                 
164                 // set it all up
165         
166                 getToken: function()
167                 {
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'));
174                 },      
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
183                         };
185                         if (referer != null)
186                                 headers['Referer'] = referer;
187                         
188                         if (data != null) {
189                                 headers['Content-Type'] = 'application/x-www-form-urlencoded';
190                                 details['data']         = data;
191                         }
192                         
193                         GM_xmlhttpRequest( details );
194                 },
195                 
196                 
197                 
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) {
201                         
202                         return function(req) { 
203                                 
204                                 try {
205                                         // init progress
206                                         document.body.style.cursor = 'progress';
207                                         
208                                 if (req.readyState != COMPLETE) {
209                                         return;
210                                 }
211                                         
212                                         // if (alert_response) { alert(req.responseText); }
213                                         
214                                         if( req.status != OK ) {
215                                                 throw new procException( op_name + " request status was '" + req.status + "'", 0, req )
216                                         }
217                                         
218                                         ok_cb(req);
219                                         
220                                 } catch(e) {
221                                         
222                                         // clean up progress
223                                         document.body.style.cursor = 'default';
224                                         
225                                         
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);
233                                                         }
234                                                 }
235                                         } else {
236                                                 throw(e);
237                                         }
238                                 }
239                                 
240                                 // clean up progress
241                                 
242                                 document.body.style.cursor = 'default';
243                         }
244                 },
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);
257                                 
258                                 if (rsp == null) {
259                                         throw new procException( "Could not understand Flickr's response.", 0, req );
260                                 }
261                                 
262                                 var stat = rsp.@stat;
263                                 if (stat == null) {
264                                         throw new procException( "Could not find status of Flickr request", 0, req);
265                                 }
266                                 
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)
275                                         }
276                                 }
277                                 
278                                 ok_cb(req, rsp);
279                         }
280                         
281                         return this.make_proc(op_name, parse_and_ok_cb, fail_cb);
282                 },
284                 getMethodSig: function(method, args)
285                 {
286                         var data = new Array();         
287                         var names = new Array();
288                         var sig = this.shared_secret;
289                         
290                         if(method) {
291                                 data['method'] = method;        
292                                 names.push('method');
293                         }
294                         for (var key in args) {
295                                 data[key] = args[key];
296                                 names.push(key);
297                         }               
298                         names.sort();
299                         for (i in names) {
300                                 sig += names[i] + data[names[i]];
301                         }               
302                         return hex_md5(sig);
303                 },
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) {
309                         
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;
315                         
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);
320                         
321                         
322                         var url = 'http://www.flickr.com/services/rest/?method=' + encodeURIComponent(method);
323                         
324                         for (var key in args) {
325                                 url += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(args[key]);
326                         }
327                         if(DEBUG) GM_log(url);
329                         var proc = this.make_flickr_api_proc( method, ok_cb, fail_cb )
330                         
331                         this.do_req(http_method, proc, url, null, null)
332                 },
334         }
336         //======================================================================
338         var MAX_DAY_COUNT = 5;
339         var NUMBER_OF_LAST_PHOTOS = 8;
340         
341         win.FlickrSnap = function() {;}
343                 
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
351                         
352                         for(var i = 0; i < auctionLinks.snapshotLength; i++) {  // For each LINK...
353                                 var al = auctionLinks.snapshotItem(i);
354                                         this.processA(al);
355                         }               
356                 },
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;
361                                 }
362                         }
363                 },
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'));
371                                 }
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'));
378                                 }
380                         }
381                 },
382                 request_failed: function(err) {
383                         GM_log(err.msg);
384                 },
385                 getSizes_done: function(req,rsp,link) {
386                         img = link.appendChild(document.createElement('IMG'));
387                         img.src = rsp..size.@source[0];
388                 },
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';
393                 }
394         }
395                 
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) {} 
402                         
403                         var flickrgp = new win.FlickrSnap();
404                         flickrgp.init( "e8c3239ff04c102ce2d6ed885bf99005");     
405                 }, false);
406         } catch (ex) {}
408 })();