- Fixed a bunch of bugs for existing paginations
[FlickrHacks.git] / _greasemonkey_ / flickrgrouppageenhancer.user.js
blob72357517fa4b959e0c2c06317e6bc37d664df695
1 // Flickr Group Page Enhancer
2 // v0.6
3 // 2006-006-06
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 Group Page Enhancer
10 // @namespace   http://6v8.gamboni.org/Flickr-Groug-Page-Enhancer.html
11 // @description Add more information on the groups
12 // @source         http://6v8.gamboni.org/Flickr-Groug-Page-Enhancer.html
13 // @identifier     http://6v8.gamboni.org/IMG/js/flickrgrouppageenhancer.user.js
14 // @version        0.6
15 // @date           2006-06-06
16 // @creator        Pierre Andrews (mortimer.pa@free.fr)
17 // @include     http://*flickr.com/groups
18 // @include http://*flickr.com/groups/
19 // @include http://*flickr.com/groups/#*
20 // @include http://*flickr.com/people/*
21 // ==/UserScript==
23 (function () {
25         
27         var win = (unsafeWindow || window.wrappedJSObject || window);
29         //update information
30         var SCRIPT = {
31                 name: "Flickr Group Page Enhancer",
32                 namespace: "http://6v8.gamboni.org/Flickr-Groug-Page-Enhancer.html",
33                 description: "Add more information on the groups",
34                 source: "http://6v8.gamboni.org/Flickr-Groug-Page-Enhancer.html",                       // script homepage/description URL
35                 identifier: "http://6v8.gamboni.org/IMG/js/flickrgrouppageenhancer.user.js",
36                 version: "0.6",                                                         // version
37                 date: (new Date(2006, 6, 6))            // update date
38                 .valueOf()
39         };
41         //======================================================================
43         
44         /*
45          * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
46          * Digest Algorithm, as defined in RFC 1321.
47          * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
48          * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
49          * Distributed under the BSD License
50          * See http://pajhome.org.uk/crypt/md5 for more info.
51          */
53         /*
54          * Configurable variables. You may need to tweak these to be compatible with
55          * the server-side, but the defaults work in most cases.
56          */
57         var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
58         var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
59         var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
61         /*
62          * These are the functions you'll usually want to call
63          * They take string arguments and return either hex or base-64 encoded strings
64          */
65         function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
66         function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
67         function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
68         function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
69         function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
70         function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
72         /*
73          * Perform a simple self-test to see if the VM is working
74          */
75         function md5_vm_test()
76         {
77                 return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
78         }
80         /*
81          * Calculate the MD5 of an array of little-endian words, and a bit length
82          */
83         function core_md5(x, len)
84         {
85                 /* append padding */
86                 x[len >> 5] |= 0x80 << ((len) % 32);
87                 x[(((len + 64) >>> 9) << 4) + 14] = len;
89                 var a =  1732584193;
90                 var b = -271733879;
91                 var c = -1732584194;
92                 var d =  271733878;
94                 for(var i = 0; i < x.length; i += 16)
95                         {
96                                 var olda = a;
97                                 var oldb = b;
98                                 var oldc = c;
99                                 var oldd = d;
101                                 a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
102                                 d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
103                                 c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
104                                 b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
105                                 a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
106                                 d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
107                                 c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
108                                 b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
109                                 a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
110                                 d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
111                                 c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
112                                 b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
113                                 a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
114                                 d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
115                                 c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
116                                 b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
118                                 a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
119                                 d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
120                                 c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
121                                 b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
122                                 a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
123                                 d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
124                                 c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
125                                 b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
126                                 a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
127                                 d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
128                                 c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
129                                 b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
130                                 a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
131                                 d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
132                                 c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
133                                 b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
135                                 a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
136                                 d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
137                                 c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
138                                 b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
139                                 a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
140                                 d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
141                                 c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
142                                 b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
143                                 a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
144                                 d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
145                                 c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
146                                 b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
147                                 a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
148                                 d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
149                                 c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
150                                 b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
152                                 a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
153                                 d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
154                                 c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
155                                 b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
156                                 a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
157                                 d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
158                                 c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
159                                 b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
160                                 a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
161                                 d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
162                                 c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
163                                 b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
164                                 a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
165                                 d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
166                                 c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
167                                 b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
169                                 a = safe_add(a, olda);
170                                 b = safe_add(b, oldb);
171                                 c = safe_add(c, oldc);
172                                 d = safe_add(d, oldd);
173                         }
174                 return Array(a, b, c, d);
176         }
178         /*
179          * These functions implement the four basic operations the algorithm uses.
180          */
181         function md5_cmn(q, a, b, x, s, t)
182         {
183                 return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
184         }
185         function md5_ff(a, b, c, d, x, s, t)
186         {
187                 return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
188         }
189         function md5_gg(a, b, c, d, x, s, t)
190         {
191                 return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
192         }
193         function md5_hh(a, b, c, d, x, s, t)
194         {
195                 return md5_cmn(b ^ c ^ d, a, b, x, s, t);
196         }
197         function md5_ii(a, b, c, d, x, s, t)
198         {
199                 return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
200         }
202         /*
203          * Calculate the HMAC-MD5, of a key and some data
204          */
205         function core_hmac_md5(key, data)
206         {
207                 var bkey = str2binl(key);
208                 if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
210                 var ipad = Array(16), opad = Array(16);
211                 for(var i = 0; i < 16; i++)
212                         {
213                                 ipad[i] = bkey[i] ^ 0x36363636;
214                                 opad[i] = bkey[i] ^ 0x5C5C5C5C;
215                         }
217                 var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
218                 return core_md5(opad.concat(hash), 512 + 128);
219         }
221         /*
222          * Add integers, wrapping at 2^32. This uses 16-bit operations internally
223          * to work around bugs in some JS interpreters.
224          */
225         function safe_add(x, y)
226         {
227                 var lsw = (x & 0xFFFF) + (y & 0xFFFF);
228                 var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
229                 return (msw << 16) | (lsw & 0xFFFF);
230         }
232         /*
233          * Bitwise rotate a 32-bit number to the left.
234          */
235         function bit_rol(num, cnt)
236         {
237                 return (num << cnt) | (num >>> (32 - cnt));
238         }
240         /*
241          * Convert a string to an array of little-endian words
242          * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
243          */
244         function str2binl(str)
245         {
246                 var bin = Array();
247                 var mask = (1 << chrsz) - 1;
248                 for(var i = 0; i < str.length * chrsz; i += chrsz)
249                         bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
250                 return bin;
251         }
253         /*
254          * Convert an array of little-endian words to a string
255          */
256         function binl2str(bin)
257         {
258                 var str = "";
259                 var mask = (1 << chrsz) - 1;
260                 for(var i = 0; i < bin.length * 32; i += chrsz)
261                         str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
262                 return str;
263         }
265         /*
266          * Convert an array of little-endian words to a hex string.
267          */
268         function binl2hex(binarray)
269         {
270                 var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
271                 var str = "";
272                 for(var i = 0; i < binarray.length * 4; i++)
273                         {
274                                 str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
275                                         hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
276                         }
277                 return str;
278         }
280         /*
281          * Convert an array of little-endian words to a base-64 string
282          */
283         function binl2b64(binarray)
284         {
285                 var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
286                 var str = "";
287                 for(var i = 0; i < binarray.length * 4; i += 3)
288                         {
289                                 var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
290                                         | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
291                                         |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
292                                 for(var j = 0; j < 4; j++)
293                                         {
294                                                 if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
295                                                 else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
296                                         }
297                         }
298                 return str;
299         }
303         //======================================================================
305          // constants
306          // http status constants
307         var OK = 200;
308         
309         // xmlhttprequest readystate
310         var COMPLETE = 4;
311         
312         var DEBUG = false;
314         //======================================================================
315         //exception
316         var procException =  function(msg, code, req) {
317                         this.msg = msg;
318                         this.code =code;
319                         this.req = req;
320         };
321                 
322         
323         //======================================================================
324         //to do the closure and get the right this.
325         //adapted from http://persistent.info/greasemonkey/gmail.user.js
327         function getObjectMethodClosure(object, method) {
328                 return function() {
329                         return object[method](); 
330                 }
331         }
333         function getObjectMethodClosure0(object, method,args) {
334                 return function() {
335                         return object[method](args); 
336                 }
337         }
339         function getObjectMethodClosure1(object, method) {
340                 return function(arg) {
341                         return object[method](arg); 
342                 }
343         }
345         
346         function getObjectMethodClosure11(object, method,args3) {
347                 return function(arg) {
348                         return object[method](arg,args3); 
349                 }
350         }
352         function getObjectMethodClosure2(object, method) {
353                 return function(arg,arg2) {
354                         return object[method](arg,arg2); 
355                 }
356         }
357         function getObjectMethodClosure21(object, method,args3) {
358                 return function(arg,arg2) {
359                         return object[method](arg,arg2,args3); 
360                 }
361         }
362         
365         //======================================================================
366         //message and prompt
367         //this code blatantly lifted from the Flickr Super Batch scripts
368         //http://webdev.yuan.cc/
369         var status_msg_container = document.createElement('div');
370         status_msg_container.style.left = '50%';
371         status_msg_container.style.width = '400px';
372         //status_msg_container.style.height = '400px';
373         status_msg_container.style.zIndex = 60000;
374         //status_msg_container.style.overflow = 'visible';
375         status_msg_container.style.position = 'absolute';
376         status_msg_container.style.display = 'none';
377         //status_msg_container.style.textAlign = 'center';
378         status_msg_container.innerHTML = '<div id="status_msg_fgpe" style="position:relative;left: -50%;top:100px;background:#d5eaff;padding:10px;font:bold 12px Arial, Helvetica, sans-serif; color:#000000;border:solid 1px #ccddee;"></div>';
379         
380         var ip = document.evaluate( '//div[@class="TopBar"]',                              
381                                 document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null
382                                 ).singleNodeValue;              
383         ip.appendChild(status_msg_container);
384         
385         
386         var status_msg = document.getElementById('status_msg_fgpe');
387         status_msg.submitfunc = function() {};
388         status_msg.on = false;
389         status_msg.show = function(msg) {
390                 this.on = true;
391                 this.innerHTML = '<img src="http://www.flickr.com/images/pulser2.gif" style="vertical-align:middle;margin-right:4px;border:0px #ffffff" />';
392                 this.innerHTML += msg;
393                 status_msg_container.style.display = 'block';
394         }
395         status_msg.msgbox = function(msg) {
396                 this.on = true;
397                 this.innerHTML = msg;
398                 this.innerHTML += "<div align=center>[<a id='closeBox'>close</a>]</div>";
399                 a = document.getElementById('closeBox');
400                 a.addEventListener('click',this.clicky,true);
401                 status_msg_container.style.display = 'block';
402         }
403         status_msg.msgbox2 = function(msg,button,callback) {
404                 this.on = true;
405                 this.innerHTML = msg;
406                 this.innerHTML += "<div align=center>[<a id='closeBox'>"+button+"</a>]</div>";
407                 a = document.getElementById('closeBox');
408                 var self = this;
409                 a.addEventListener('click',function(event) {self.clicky2(event,callback)},true);
410                 status_msg_container.style.display = 'block';
411         }
412         status_msg.hide = function() {
413             status_msg.on = false;
414             status_msg_container.style.display = 'none';
415                         status_msg_container.style.textAlign = 'left';
416         }
417         status_msg.prompt = function(msg, fn) {
418                 status_msg.submitfunc = fn;
419                 this.on = true;
420                 this.innerHTML = msg;           
421                 var form = document.createElement('form');
422                 form.addEventListener('submit',this.entered,true);
423                 var input = document.createElement('input');            
424                 input.id = 'woowoo';
425                 input.type = 'text';
426                 form.appendChild(input);
427                 input = document.createElement('input');
428                 input.type = 'submit';
429                 input.value = 'ok';
430                 form.appendChild(input);
431                 this.appendChild(form);
432                 status_msg_container.style.display = 'block';
433                                 
434         }
435         status_msg.entered = function(event) {
436                 var input = document.getElementById('woowoo');
437                 status_msg.submitfunc(input.value);
438                 event.preventDefault();
439                 event.returnValue = false;
440                 event.cancel = true;
441                 return false;
442         }
443         status_msg.clicky = function(event) {
444                 status_msg.hide();
445                 event.preventDefault();
446                 event.returnValue = false;
447                 event.cancel = true;
448                 return false;
449         }
450         status_msg.clicky2 = function(event,callback) {
451                 status_msg.hide();
452                 event.preventDefault();
453                 event.returnValue = false;
454                 event.cancel = true;
455                 callback();
456                 return false;
457         }
460         //======================================================================
461         //Simple calls to flickr REST API, from the batch enhancer script
462         // needs the md5 and status_msg code above
464         win.FlickrAPI = function(){;}
465         
466         win.FlickrAPI.prototype = {     // flickr api 
467                 
468                 init: function(api_key,shared_secret) {
469                         this.api_key = api_key;
470                         this.shared_secret = shared_secret;
471                         this.auth_token = GM_getValue('auth_'+this.api_key);
472                         
473                         if (this.shared_secret && !this.auth_token) {
474                                 this.askForAuth();
475                                 
476                         } 
477                 },
479                 askForAuth: function() {
480                         this.flickr_api_call("flickr.auth.getFrob",
481                                 {api_sig: this.getMethodSig("flickr.auth.getFrob", {api_key: this.api_key})}, 
482                                                                  getObjectMethodClosure2(this,'frob_loaded'),
483                                                                  getObjectMethodClosure1(this,'frob_failed'));
484                 },
486                 frob_loaded: function(req, rsp) {
488                         this.frob = rsp..frob[0];
489                         if(DEBUG) GM_log("received Frob "+this.frob);
490                         var api_sig = this.getMethodSig(false, {api_key: this.api_key,frob:this.frob,perms:"read"});
491                         var url= "http://flickr.com/services/auth/?api_key="+this.api_key+"&perms=read&frob="+this.frob+"&api_sig="+api_sig;
492                         //Here, we need the status_msg code
493                         status_msg.msgbox2("This script needs to be authorized. <br>" +
494                                                           "<b style=\"font-variant:small-caps;\">Click [<a onclick='window.open(\""+url+"\"); return false'>Step1</a>]</b>, " +
495                                                           "follow the instructions in the popup window,<br> " +
496                                                           "then return here click Step2.<br> " +
497                                                           "Popup blockers may cause this not to work.<br>You'll only have to do this once.","Step2",getObjectMethodClosure1(this,'getToken'));
498                 },
500                 frob_failed: function(e) {
501                         status_msg.msgbox('Couldn\'t authorize, for whatever reason.');
502                 },
503                 
504                 token_loaded: function(req,rsp) {               
505                         status_msg.hide();
506                         var token = rsp..token[0];
507                         this.nsid = rsp..user.@nsid[0];         
508                         
509                         if(DEBUG) GM_log("authenticated with user "+this.nsid+": "+token);
510                         this.auth = token;
512                         GM_setValue('auth_'+this.api_key,""+token);
513                 },
514                 
515                 token_failed:function(e) {
516                         status_msg.msgbox('Couldn\'t authorize, for whatever reason.');
517                 },
518                 
519                 // set it all up
520         
521                 getToken: function()
522                 {
523                         status_msg.show('authorizing...');
524                         var api_sig = this.getMethodSig("flickr.auth.getToken", {api_key: this.api_key,frob:this.frob});
525                         this.flickr_api_call("flickr.auth.getToken",
526                         {frob: this.frob,api_sig: api_sig},
527                                                                  getObjectMethodClosure2(this,'token_loaded'),
528                                                                  getObjectMethodClosure1(this,'token_failed'));
529                 },      
531                 do_req: function ( method, proc_request, url, referer, data ) {
532                         var headers = new Object();
533                         var details = {
534                                 method    : method,
535                                 onload    : function(d) { proc_request(d) },
536                                 url       : url,
537                                 header    : headers
538                         };
540                         if (referer != null)
541                                 headers['Referer'] = referer;
542                         
543                         if (data != null) {
544                                 headers['Content-Type'] = 'application/x-www-form-urlencoded';
545                                 details['data']         = data;
546                         }
547                         
548                         GM_xmlhttpRequest( details );
549                 },
550                 
551                 
552                 
553                 // a proc just spins around waiting for the thing to succeed or fail
554                 // then calls a callback, if we got 200 OK message.
555                 make_proc: function (op_name, ok_cb, fail_cb) {
556                         
557                         return function(req) { 
558                                 
559                                 try {
560                                         // init progress
561                                         document.body.style.cursor = 'progress';
562                                         
563                                 if (req.readyState != COMPLETE) {
564                                         return;
565                                 }
566                                         
567                                         // if (alert_response) { alert(req.responseText); }
568                                         
569                                         if( req.status != OK ) {
570                                                 throw new procException( op_name + " request status was '" + req.status + "'", 0, req )
571                                         }
572                                         
573                                         ok_cb(req);
574                                         
575                                 } catch(e) {
576                                         
577                                         // clean up progress
578                                         document.body.style.cursor = 'default';
579                                         
580                                         
581                                         if (e instanceof procException) {
582                                                 if( fail_cb != null )
583                                                         fail_cb( e );
584                                                 else {
585                                                         GM_log( e.msg );
586                                                         if (DEBUG) {
587                                                                 GM_log(e.req.responseText);
588                                                         }
589                                                 }
590                                         } else {
591                                                 throw(e);
592                                         }
593                                 }
594                                 
595                                 // clean up progress
596                                 
597                                 document.body.style.cursor = 'default';
598                         }
599                 },
602                 // this is wraps the spinning proc like above,
603                 // except it parses the flickr api response a little before deciding all is well,
604                 // and passing control to the all-is-well callback
605                 make_flickr_api_proc: function(op_name, ok_cb, fail_cb) {
607                         function parse_and_ok_cb(req) {
608                                 if(DEBUG) GM_log(req.responseText);
609                                 var rsp = req.responseText.replace(/<\?xml.*\?>/,'');
610                                 var rsp = new XML(rsp);
611                                 // var rsp = req.responseXML.getElementsByTagName('rsp').item(0);
612                                 
613                                 if (rsp == null) {
614                                         throw new procException( "Could not understand Flickr's response.", 0, req );
615                                 }
616                                 
617                                 var stat = rsp.@stat;
618                                 if (stat == null) {
619                                         throw new procException( "Could not find status of Flickr request", 0, req);
620                                 }
621                                 
622                                 if (stat != 'ok') {
623                                         if (stat == 'fail') {
624                                                 var err_node = rsp.err[0];
625                                                 var code = err_node.@code;
626                                                 var err_msg = err_node.@msg;
627                                                 throw new procException( err_msg, code, req );
628                                         } else {
629                                                 throw new procException("Unknown error status: '" + stat + "'", 0, req)
630                                         }
631                                 }
632                                 
633                                 ok_cb(req, rsp);
634                         }
635                         
636                         return this.make_proc(op_name, parse_and_ok_cb, fail_cb);
637                 },
639                 getMethodSig: function(method, args)
640                 {
641                         var data = new Array();         
642                         var names = new Array();
643                         var sig = this.shared_secret;
644                         
645                         if(method) {
646                                 data['method'] = method;        
647                                 names.push('method');
648                         }
649                         for (var key in args) {
650                                 data[key] = args[key];
651                                 names.push(key);
652                         }               
653                         names.sort();
654                         for (i in names) {
655                                 sig += names[i] + data[names[i]];
656                         }               
657                         return hex_md5(sig);
658                 },
661                 // construct a flickr api request, with method and args, 
662                 // if that worked, call callback with request object.
663                 flickr_api_call: function( method, args, ok_cb, fail_cb,with_auth) {
664                         
665                         var http_method = args['http_method'];
666                         http_method = ( http_method ? http_method : 'GET' );
667                         delete args['http_method'];
669                         args['api_key'] = this.api_key;
670                         
671                         if (this.shared_secret && with_auth && this.auth_token) {
672                                 args['auth_token'] = this.auth_token;
673                                 args['api_sig'] = this.getMethodSig(method, args);
674                         } else if(DEBUG) GM_log('not signing: ' + method);
675                         
676                         
677                         var url = 'http://www.flickr.com/services/rest/?method=' + encodeURIComponent(method);
678                         
679                         for (var key in args) {
680                                 url += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(args[key]);
681                         }
682                         if(DEBUG) GM_log(url);
684                         var proc = this.make_flickr_api_proc( method, ok_cb, fail_cb )
685                         
686                         this.do_req(http_method, proc, url, null, null)
687                 },
689         }
691         //======================================================================
693         var MAX_DAY_COUNT = 5;
694         var NUMBER_OF_LAST_PHOTOS = 8;
695         
696         win.FlickrGroupPage = function() {;}
698                 
699         win.FlickrGroupPage.prototype = {
700                 init: function(key,secret) {
701                         this.api = new win.FlickrAPI();
702                         this.api.init(key,secret);
703                         
704                         this.user_id = unsafeWindow.global_nsid;
705                         var auctionLinks = document.evaluate(
706                                                                                                  "/html/body/div[@id='Main']/table[2]/tbody/tr/td[1]/ul/li|/html/body/div[@id='Main']/div/table[@id='ProfileInfo']/tbody/tr/td[@id='Left']/table[1]/tbody/tr/td/ul/li",                                                                           
707                                                                                                  
708                                                                                                  document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);  // Get all group
709                         
710                         for(var i = 0; i < auctionLinks.snapshotLength; i++) {  // For each group...
711                                 
712                                 var al = auctionLinks.snapshotItem(i);
713                                 var showA = al.appendChild(document.createElement('A'));
714                                 showA.innerHTML = "More...";
715                                 showA.title="Display more information about this group.";
716                                 showA.href="#"+"fgpe_groupLI_"+i;
717                                 showA.setAttribute("style","font-size:85%;color:grey;text-decoration: underline;");
718                                 al.fgpeShowA = showA;
719                                 al.id="fgpe_groupLI_"+i;
720                                 showA.addEventListener('click', getObjectMethodClosure21(this,'fetchGroupInfo',al), true);
721                         }               
722                 },
723                 fetchGroupInfo: function(a,b,groupLI) {
725                         var showA = groupLI.fgpeShowA;
726                         showA.style.display = 'none';
728                         var closeA = groupLI.fgpeCloseA;
729                         if(!closeA) {
730                                 closeA = groupLI.appendChild(document.createElement('A'));
731                                 closeA.addEventListener('click',function() {item.style.display='none';showA.style.display='block';closeA.style.display='none';}, true);
732                                 closeA.innerHTML = "Hide";      
733                                 closeA.title="Hide the additional informations on this group.";
734                                 closeA.href="#"+groupLI.id;             
735                                 closeA.setAttribute("style","font-size:85%;color:grey;text-decoration: underline;");
736                         }
737                         
738                         var item = groupLI.fgpeItem;
739                         if(!item) {
740                                 var matches = /<a href="\/groups\/(.*?)">/.exec(groupLI.innerHTML);
741                                 if(matches) {
742                                         var id = matches[1];
743                                         item = groupLI.appendChild(document.createElement('DIV'));
744                                         item.setAttribute("style","border:1px solid black; font-size:80%; padding: 0 7px;");
745                                         if(groupLI.parentNode.parentNode.className == "List") item.style.width="640px";
746                                         item.innerHTML = '<img id="fgpe_pulser" src="http://www.flickr.com/images/pulser2.gif" style="vertical-align:middle;margin-right:4px;border:0px #ffffff" />';                           
747                                         
748                                         this.api.flickr_api_call('flickr.urls.lookupGroup',
749                                                                                          { url: "http://www.flickr.com/groups/"+id, http_method: 'POST' },
750                                                                                          getObjectMethodClosure21(this,'lookupGroup_done', item),
751                                                                                          getObjectMethodClosure11(this,'request_failed',item));
752                                         groupLI.fgpeItem = item;
753                                 }
754                         } else {
755                                 item.style.display="block";
756                         }
757                 },
758                         
759                 request_failed: function(err,item) {
760                         var groupLI = item.parent;
761                         item.style.display='none';
762                         groupLI.fgpeShowA.style.display='block';
763                         groupLI.fgpeCloseA.style.display='none';
764                         status_msg.msgbox('Sorry, there was an error.');
765                         GM_log(err.msg);
766                 },
767                 lookupGroup_done: function(req,rsp,item) {
768                         var id = rsp.group[0].@id;
769                         if(this.user_id) {
770                                 this.api.flickr_api_call('flickr.groups.pools.getPhotos',
771                                                                                  { group_id: id, user_id: this.user_id,per_page: MAX_DAY_COUNT+1, http_method: 'POST' },
772                                                                                  getObjectMethodClosure21(this,'displayLastDate_done', new Array(item,id)),
773                                                                                  getObjectMethodClosure11(this,'displayLastDate_failed',new Array(item,id)),true);
774                         }
775                         item.innerHTML += '<a href="http://www.krazydad.com/gustavog/FlickRandom.pl?group='+id+'" title="See random pictures from the group">FlickrRandom</a><br/>';
776                         this.postPoneDescription(new Array(item,id,0));
777                         this.postPonePhotos(new Array(item,id,0));
778                 },
779                 postPoneDescription: function(args) {
780                         var item = args[0];
781                         var id = args[1];
782                         var cnt = args[2]++;
783                         if(item.fgpeCompleteDate || cnt > 10){
784                                 this.api.flickr_api_call('flickr.groups.getInfo',
785                                         {group_id: id,  http_method: 'POST' },
786                                                                                  getObjectMethodClosure21(this,'addDescription_done', new Array(item)),
787                                                                                  getObjectMethodClosure11(this,'request_failed',item),true);            
788                         } else {
789                                 setTimeout(getObjectMethodClosure0(this,'postPoneDescription',args),1000);
790                         }
791                 },
792                 addDescription_done: function(req,rsp, arrayArgs) {
793                         var item = arrayArgs[0];
794                         item.innerHTML += "<div style=\"color:black;max-height:20ex;border:1px dashed black; padding: 0 3px;overflow:auto;\">"+rsp..description[0]+"</div>";
795                         item.fgpeCompleteDescription = true;
796                 },
798                 postPonePhotos: function(args) {
799                         var item = args[0];
800                         var id = args[1];
801                         var cnt = args[2]++;
802                         if(item.fgpeCompleteDescription || cnt > 10){
803                                 this.api.flickr_api_call('flickr.groups.pools.getPhotos',
804                                         {group_id: id, per_page: NUMBER_OF_LAST_PHOTOS, http_method: 'POST' },
805                                                                                  getObjectMethodClosure21(this,'displayGroupPhotos_done', new Array(item,id)),
806                                                                                  getObjectMethodClosure11(this,'request_failed',item),true);            
807                         } else {
808                                 setTimeout(getObjectMethodClosure0(this,'postPonePhotos',args),1000);
809                         }
810                 },
811                 addPhotoLi_done: function(req,rsp,arrayArgs) {
812                         var photo = arrayArgs[0];
813                         var item = arrayArgs[1];
814                         var groupID = arrayArgs[2];
815                         item.innerHTML += "<li style=\"display:inline;\"><a titel=\""+photo.@title+"\" href=\"http://www.flickr.com/photos/"+photo.@owner+"/"+photo.@id+"/in/pool-"+groupID+"\"><img alt=\""+photo.@title+" (square size)\" src=\""+rsp..size.@source[0]+"\"/></a></li>";
816                 },
817                 
818                 displayGroupPhotos_done: function(req,rsp,arrayArgs) {
819                         var item = arrayArgs[0];
820                         var groupID = arrayArgs[1];
821                         var total = 'no';
822                         if(rsp.photo.@total) total = rsp.photos.@total;
823                         item.innerHTML += total+" photo(s)";
824                         item.innerHTML += '<ul style="list-style-type:none;">';
825                         for each(photo in rsp..photo) {
826                                 this.api.flickr_api_call('flickr.photos.getSizes',
827                                                                                  { photo_id: photo.@id, http_method: 'POST' },
828                                                                                  getObjectMethodClosure21(this,'addPhotoLi_done',new Array(photo,item,groupID)),
829                                                                                  getObjectMethodClosure11(this,'request_failed',item));
830                                                                                  }
831                         item.innerHTML += '</ul>';
832                         item.removeChild(document.getElementById('fgpe_pulser'));
833                 },
835                 displayLastDate_failed: function(err,item) {
836                         unsafeWindow.console.log(item);
837                         var item = args[0];
838                         var group_id = args[1];
839                         if(document.location.pathname.indexOf('/people') >= 0) {
840                                 //we are in a user profile.     
841                                 //get his id:
842                                 var hisid = document.evaluate("/html/body/div[@id='Main']/div[3]/table[@id='ProfileInfo']/tbody/tr/td[@id='Right']/p[1]/a",
843                                                                                                 document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null
844                                 ).singleNodeValue;      
845                                 if(hisid) {
846                                         var matches = /\?to=(.+)$/.exec(hisid.href);
847                                         if(matches) {
848                                                 item.innerHTML += "<a title=\"View The photos this user posted\" href=\"http://flickr.com/groups/"+group_id+"/pool/"+matches[1]+"\">Current user contributions.</a> ";                          
849                                         }
850                                 }
851                         }
852                 },
854                 displayLastDate_done: function(req,rsp,args) {
855                         var item = args[0];
856                         var group_id = args[1];
857                         var html = '';
858                         if(parseInt(rsp..photos.@total) > 0) {
859                                 var first_date =  new Date(parseInt(rsp..photo.@dateadded[0])*1000);
860                                 var cnt = 0;
861                                 for each (photo in rsp..photo) {
862                                         var last = parseInt(photo.@dateadded[0])*1000;
863                                         var date = new Date(last);
864                                         var diff = (Date.now()-date.getTime())/(1000*3600*24);
865                                         if(diff < 1) {
866                                                 cnt++;
867                                         } else break;
868                                 }
869                                 if(cnt == 0) 
870                                         html = "<span style=\"color:green !important;\">";
871                                 else 
872                                         html = "<span style=\"color:red !important;\">";
873                                 html += "<a title=\"View The photos you posted\" href=\"http://flickr.com/groups/"+group_id+"/pool/"+this.user_id+"\">";
874                                 html += "Your Last Post:</a> "+first_date.toLocaleString()+"</span>";
875                                 if(cnt > 0) {
876                                         if(cnt <= 5) html += ",today: "+cnt;
877                                         else if (cnt > 5) html += ",today: >5";
878                                 }
879                                 html += "<br/>";
880                         }                       
881                         if(document.location.pathname.indexOf('/people') >= 0) {
882                                 //we are in a user profile.     
883                                 //get his id:
884                                 var hisid = document.evaluate("/html/body/div[@id='Main']/div[3]/table[@id='ProfileInfo']/tbody/tr/td[@id='Right']/p[1]/a",
885                                                                                                 document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null
886                                 ).singleNodeValue;      
887                                 if(hisid) {
888                                         var matches = /\?to=(.+)$/.exec(hisid.href);
889                                         if(matches) {
890                                                 html += "<a title=\"View The photos this user posted\" href=\"http://flickr.com/groups/"+group_id+"/pool/"+matches[1]+"\">Current user contributions.</a> ";                            
891                                         }
892                                 }
893                         }
894                         item.innerHTML += html;
895                         item.fgpeCompleteDate = true;
896                 }
897         }
898                 
899         // update automatically (http://userscripts.org/scripts/show/2296)
900         try {
901                 window.addEventListener("load", function () {
902                         try {
903                                 win.UserScriptUpdates.requestAutomaticUpdates(SCRIPT);
904                         } catch (ex) {} 
905                         
906                         var flickrgp = new win.FlickrGroupPage();
907                         flickrgp.init( "e8c3239ff04c102ce2d6ed885bf99005","247fcd31d50cdef4");  
908                 }, false);
909         } catch (ex) {}
911 })();