- Fixed a bunch of bugs for existing paginations
[FlickrHacks.git] / _greasemonkey_ / flickrvirtualsets.user.js
bloba55cabec1870c2176053d98fc9021b9b1e1232f5
1 // ==UserScript==
2 // @name          Flickr Virtual Sets
3 // @version       2.1
4 // @description   Adding virtual sets to photo pages
5 // @namespace     http://webdev.yuan.cc/
6 // @include       http://flickr.com/*
7 // @include       http://www.flickr.com/*
9 // Author: .CK ( http://www.flickr.com/photos/ckyuan/ )
10 // Web site: http://webdev.yuan.cc/
12 // v1.0 06/27/06        initial release
13 // v1.1 06/30/06        Add "Your Favorites he/she owns" and "Your 
14 //                      Favorited Photos"
15 // v1.2 07/01/06        Mortimer? (Pierre Andrews) contributes "User most 
16 //                      interesting" and "Your most interesting"
17 // v1.3 07/01/06        Mortimer? (Pierre Andrews) contributes "Your photos
18 //                      in that pool" and "This user's photos in that pool"
19 // v2.0 07/12/06        Add virtual sets in "SET" page
20 //                      allow users to add photos in virtual sets to a real set
21 // v2.1 08/27/06        Mortimer? (Pierre Andrews) contributes "Relevant Photos"
23 // A Virtual Set is not a real photoset in Flickr. It's a set of photos
24 // sharing common properties or "things", say, your favorites, same tags,
25 // or same privacy settings. This script builds a navigating photostream
26 // box for each pre-defined virtual set. When you surfing photo pages,
27 // if a photo is in any of the virtual sets, the photostream box(es) will
28 // be opened automatically.
30 // In the "You" menu, this script will insert a link - "Your Virtual Sets"
31 // to let you choose which virtual sets you want to enable. The preference
32 // is stored in your Firefox. Now we have 6 pre-defined virtual sets:
34 // 1. Your Favorites - a collection of favorite photos
35 // 2. Recent Photos of Contacts - recent uploaded photos of your contacts,
36 //    one photo per user
37 // 3. Your Private Photos - all your private photos (not for friends and family)
38 // 4. Today Interesting - 500 interesting photos on uploaded day
39 // 5. Geotagged Photos - photos with "tag: geotagged" of the current user
40 // 6. Friends Only - photos only visible to friends of current user
41 // 7. Your Favorites owned by xxx - your favorites owned by current user 
42 // 8. Your Favorited Photos - your photos favorited by others
43 // 9. User Most Interesting - most interesting photos of current user
44 // 10. Your Most Interesting - most interesting photos of you
45 // 11. Your photos in that pool
46 // 12. This user's photos in that pool
48 // In the "Photo Sets" page, either yours or others, the virtual sets will be 
49 // inserted. Click any of the virtual sets, a floating div will pop up to show 
50 // the photo thumbs in that set. If it the virtual set belongs to you, you can 
51 // add them to a real set, either a new created one or an existing one.
52 // 
53 // ==/UserScript==
55 (function() {
57 var DEBUG = false;
58 if(DEBUG) GM_log('Starting GM Flickr Virtual Sets');
60 if(unsafeWindow) w = unsafeWindow;
61 else w = window;
62 var global_photos = w.global_photos;
63 var global_nsid = w.global_nsid;
65 function _gt(e) { return document.getElementsByTagName(e); }
66 function _gi(e) { return document.getElementById(e); }
67 function _ce(e) { return document.createElement(e); }
68 function _ct(e) { return document.createTextNode(e); }
70 var photo_id,ownersUrl,nsid,widget,posted,posted2;
71 var _fwd=false, _bwd=false;
72 var vsets = new Array();
73 var headers = { 'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey/Flickr Favorites Photostream', 'Accept': 'application/atom+xml,application/xml,text/xml' };
74 var context_loading = 'data:image/gif;base64,R0lGODlhSwBLAIAAAPPz8wAAACH5BAAAAAAALAAAAABLAEsAAAKDhI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8YhMUgIVJsB5g0qkOGhAerVeDc4swsvqchPi59iMLq/KWvbZjV6/52k6XM5tt+0H8P2nlrSlRFhoeIiYqLjI2Oj4CBkpOUlZaXmJmam5ydnp+QnaUQAAOw==';
75 // var context_loading = 'http://www.flickr.com/images/context_loading.gif';
76 w.showSet = new Array();
77 w.addtoSet = new Array();
79 var favorites = { 
80     title: 'Your Favorites',
81     slide: 'http://www.flickr.com/photos/' +global_nsid+ '/favorites/show/',
82     browse: 'http://www.flickr.com/photos/' +global_nsid+ '/favorites/', 
83     paged: true,
84     api_paged: true,
85     perpage: 36,
86     addself: false,
87     params: {
88         method: 'flickr.favorites.getList',
89         page: 1,
90         per_page: 500,
91         user_id: global_nsid
92     },
93     cache: true
96 var contacts = {
97     title: 'Recent Contacts Photos',
98     slide: 'http://www.flickr.com/photos/friends/show/', 
99     browse: 'http://www.flickr.com/photos/friends/',
100     paged: true,
101     api_paged: false,
102     perpage: 24,
103     addself: false,
104     params: {
105         method: 'flickr.photos.getContactsPhotos',
106         count: 500,
107         single_photo: 1
108     },
109     cache: false
112 var d = _gt('div');
113 // for(var i in d) if(d[i].className == 'Widget') widget = d[i];
114 for(var i=0;i<d.length;i++) {
115     if(d[i].className == 'Widget') {
116         widget = d[i];
117         break;
118     }
120 if(widget) {
121     posted = widget.getElementsByTagName('a')[1].href;
122     alias = widget.getElementsByTagName('a')[2].firstChild.innerHTML;
123     posted = posted.replace( /^.*date-posted\// , ''); 
124     posted2 = posted;
125     posted = posted.replace( /\//g , '-'); 
126     posted = posted.replace( /-$/ , ''); 
127     nsid = widget.getElementsByTagName('img')[0].src;
128     nsid = nsid.replace( /^.*buddyicons\//, '');
129     nsid = nsid.replace( /\.jpg.*$/, '');
131 var t = _gi('SubNav');
132 if(t) {
133     nsid = t.getElementsByTagName('img')[0].src;
134     nsid = nsid.replace( /^.*buddyicons\//, '');
135     nsid = nsid.replace( /\.jpg.*$/, '');
136     if( nsid == global_nsid ) isOwner = true;
137     else isOwner = false;
140 var interestingness = { 
141     title: 'Today Interesting',
142     slide: 'http://www.flickr.com/explore/',
143     browse: 'http://www.flickr.com/explore/interesting/'+ posted2,
144     paged: true,
145     api_paged: true,
146     perpage: 10,
147     addself: false,
148     params: {
149         method: 'flickr.interestingness.getList',
150         date: posted,
151         page: 1,
152         per_page: 500
153     },
154     cache: true
157 //var nsid = w.nextprev_currentContextID.replace(/stream/,'');
158 var geotagged = { 
159     title: 'Geotagged Photos',
160     slide: 'http://www.flickr.com/photos/' +nsid+ '/tags/geotagged/show/',
161     browse: 'http://www.flickr.com/photos/' +nsid+ '/tags/geotagged/',
162     paged: true,
163     api_paged: true,
164     perpage: 20,
165     addself: false,
166     params: {
167         method: 'flickr.photos.search',
168         user_id: nsid,
169         tags: 'geotagged',
170         page: 1,
171         per_page: 500
172     },
173     cache: true
176 var friends = { 
177     title: 'Friends Only',
178     slide: 'http://www.flickr.com/photos/' +nsid+ '/',
179     browse: 'http://www.flickr.com/photos/' +nsid+ '/',
180     paged: false,
181     api_paged: true,
182     perpage: 20,
183     addself: false,
184     params: {
185         method: 'flickr.photos.search',
186         user_id: nsid,
187         privacy_filter: 2,
188         page: 1,
189         per_page: 500
190     },
191     filters: {
192         isfriend: '1'
193     },
194     cache: true
197 var private = { 
198     title: 'Your Private Photos',
199     slide: 'http://www.flickr.com/photos/' +global_nsid+'/',
200     browse: 'http://www.flickr.com/photos/' +global_nsid+ '/',
201     paged: false,
202     api_paged: true,
203     perpage: 20,
204     addself: false,
205     params: {
206         method: 'flickr.photos.search',
207         user_id: global_nsid,
208         privacy_filter: 5,
209         page: 1,
210         per_page: 500
211     },
212     cache: true
215 var favorites2 = { 
216     title: 'Your Favs Owned by User',
217     slide: 'http://www.flickr.com/photos/' +global_nsid+ '/favorites/show/',
218     browse: 'http://www.flickr.com/photos/' +global_nsid+ '/favorites/', 
219     paged: true,
220     api_paged: true,
221     perpage: 36,
222     addself: false,
223     params: {
224         method: 'flickr.favorites.getList',
225         page: 1,
226         per_page: 500,
227         user_id: global_nsid
228     },
229     filters: {
230         owner: nsid
231     },
232     cache: true
235 var favorited = { 
236     title: 'Your Favorited Photos',
237     slide: '',
238     browse: 'http://www.flickr.com/photos/' +global_nsid+ '/popular-faves/', 
239     paged: true,
240     api_paged: true,
241     perpage: 20,
242     addself: false,
243     funct: searchFaved,
244     params: { },
245     filters: { },
246     cache: false
249 var userinteresting = {
250     title: 'This User Most Interesting',
251     slide: '',
252     browse: 'http://interestingby.isaias.com.mx/pm.php?id='+nsid+'&theme=white',
253     paged: false,
254     api_paged: false,
255     perpage: 100,
256     addself: false,
257     params: {
258        method: 'flickr.photos.search',
259        page: 1,
260        per_page: 100,
261        user_id: nsid,
262        sort: 'interestingness-desc'
263     },
264     filters: {},
265     cache: true
268 var yourinteresting = {
269     title: 'Your Most Interesting',
270     slide: '',
271     browse: 'http://www.flickr.com/photos/'+global_nsid+'/popular-interesting/',
272     paged: true,
273     api_paged: false,
274     perpage: 20,
275     addself: false,
276     params: {
277        method: 'flickr.photos.search',
278        page: 1,
279        per_page: 200,
280        user_id: global_nsid,
281        sort: 'interestingness-desc'
282     },
283     filters: {},
284     cache: true
287 var group_name = /\/in\/pool-([^\/]+)/.exec(document.location.pathname);
288 if(group_name) {
289     group_name = group_name[1]; 
290     group_id = w.nextprev_currentContextID.replace(/pool/,'');
291 } else {
292     group_name ='';
293     group_id = '';
295 var yourphotoinpool = {
296     title: 'Your Photos in that pool',
297     slide: '',
298     browse: 'http://www.flickr.com/groups/'+group_name+'/pool/'+global_nsid+'/',
299     group_name: group_name,
300     paged: true,
301     api_paged: true,
302     perpage: 30,
303     addself: false,
304     params: {
305         method: 'flickr.groups.pools.getPhotos',
306         page: 1,
307         per_page: 200,
308         group_id: group_id,
309         user_id: global_nsid
310     },
311     filters: {},
312     cache: true
315 var photoinpool = {
316     title: 'This user\'s photos in that pool',
317     slide: '',
318     browse: 'http://www.flickr.com/groups/'+group_name+'/pool/'+nsid+'/',
319     group_name: group_name,
320     paged: true,
321     api_paged: true,
322     perpage: 30,
323     addself: false,
324     params: {
325         method: 'flickr.groups.pools.getPhotos',
326         page: 1,
327         per_page: 200,
328         group_id: group_id,
329         user_id: nsid
330     },
331     filters: {},
332     cache: true
335 var re = /http:\/\/(www\.)?flickr\.com\/photos\/[^\/]+\/\d+/;
336 var re2 = /http:\/\/(www\.)?flickr\.com\/photos\/[^\/]+\/sets\/$/;
337 var re3 = /http:\/\/(www\.)?flickr\.com\/photos\/[^\/]+\/sets\/\d+\//;
338 if( re.test(document.location) || re3.test(document.location) ) {
339     var tags = '';
340     for(tag in w.global_photos[w.page_photo_id].tags_rawA) {
341         tag_value = w.global_photos[w.page_photo_id].tags_rawA[tag];
342         if(tag_value.indexOf('geo:')<0) tags += ','+ tag_value;
343     }
344     tags = tags.substr(1);
345     GM_log('Tags: ' + tags);
346 } else tags = '';
348 var relevantphotos = {
349     title: 'Relevant Photos',
350     slide: '',
351     browse: 'http://flickr.com/search/?q='+tags+'&m=tags&z=t',
352     paged: true,
353     api_paged: false,
354     perpage: 24,
355     addself: true,
356     params: {
357         method: 'flickr.photos.search',
358         tags: tags,
359         page: 1,
360         per_page: 100,
361         sort: 'relevance',
362         tag_mode: 'any'
363     },
364     cache: false
367 vsets['vset1'] = { enabled: function() { return GM_getValue('vset1',false); }, config: favorites };
368 vsets['vset2'] = { enabled: function() { return GM_getValue('vset2',false); }, config: contacts };
369 vsets['vset3'] = { enabled: function() { return GM_getValue('vset3',false); }, config: private };
370 vsets['vset4'] = { enabled: function() { return GM_getValue('vset4',false); }, config: interestingness };
371 vsets['vset5'] = { enabled: function() { return GM_getValue('vset5',false); }, config: geotagged };
372 vsets['vset6'] = { enabled: function() { return GM_getValue('vset6',false); }, config: friends };
373 vsets['vset7'] = { enabled: function() { return GM_getValue('vset7',false); }, config: favorites2 };
374 vsets['vset8'] = { enabled: function() { return GM_getValue('vset8',false); }, config: favorited };
375 vsets['vset9'] = { enabled: function() { return GM_getValue('vset9',false); }, config: userinteresting };
376 vsets['vset10'] = { enabled: function() { return GM_getValue('vset10',false); }, config: yourinteresting };
377 vsets['vset11'] = { enabled: function() { return GM_getValue('vset11',false); }, config: yourphotoinpool };
378 vsets['vset12'] = { enabled: function() { return GM_getValue('vset12',false); }, config: photoinpool };
379 vsets['vset13'] = { enabled: function() { return GM_getValue('vset13',false); }, config: relevantphotos };
382 function addCandyMenu() {
384     if(DEBUG) GM_log('Call addCandyMenu()');
386     var your_sets;
387     var links = _gi('candy_nav_menu_you').getElementsByTagName('a');
388     for(var i=0;i<links.length;i++) 
389         if(links[i].innerHTML=='Your Sets') your_sets = links[i];
391     if(DEBUG && your_sets) GM_log(your_sets.innerHTML);
393     var your_virtualsets = _ce('a');
394     your_virtualsets.title = 'Your virtual sets';
395     your_virtualsets.innerHTML = 'Your Virtual Sets';
396     your_virtualsets.href = 'javascript:;';
397     your_virtualsets.addEventListener('click', function() {
398         if( ! _gi('vset_menu') ) {
399             var vset_menu = _ce('div');
400             vset_menu.id = 'vset_menu';
401             vset_menu.style.position = 'absolute';
402             vset_menu.style.textAlign = 'left';
403             vset_menu.style.zIndex = 6000;
404             vset_menu.style.padding = '0px 10px 10px 10px';
405             vset_menu.style.margin = '60px 0px 0px 120px';
406             vset_menu.style.border = '1px solid #000';
407             vset_menu.style.color = '#4358c6';
408             vset_menu.style.font = '11px tahoma';
409             vset_menu.style.background = '#dfefff';
411             vset_menu.innerHTML = '<h4>Virtual Sets Settings</h4>';
412             for(var v in vsets) 
413                 vset_menu.innerHTML += '<input id="' +v+ '" type="checkbox"> ' +vsets[v].config.title+ '<br />';
414             vset_menu.innerHTML += '<input id="vset_save" type="button" class="Butt" value="Save"> ';
415             vset_menu.innerHTML += '<input id="vset_cancel" type="button" class="Butt" value="Cancel">';
417             _gi('Main').insertBefore(vset_menu, _gi('Main').firstChild);
418             _gi('vset_save').addEventListener('click', function() {
419                 _gi('vset_menu').style.display = 'none';
420                 for(var v in vsets) GM_setValue(v, _gi(v).checked);
421                 newPStreams();
422             }, true);
423             _gi('vset_cancel').addEventListener('click', function() {
424                 _gi('vset_menu').style.display = 'none';
425             }, true);
426         } else vset_menu = _gi('vset_menu');
427         _gi('vset_menu').style.display = 'block';
428         for(var v in vsets) _gi(v).checked = (GM_getValue(v,false)=='1') ? true : false;
429     }, true );
430     your_sets.parentNode.insertBefore(your_virtualsets, your_sets.nextSibling);
432     var your_popular = _ce('a');
433     your_popular.title = 'Your popular photos';
434     your_popular.innerHTML = 'Your Popular';
435     your_popular.href = your_sets.href.replace( /\/sets\//, '/popular-interesting/');
436     your_sets.parentNode.insertBefore(your_popular, your_virtualsets.nextSibling);
439 addCandyMenu();
441 var re = /http:\/\/(www\.)?flickr\.com\/photos\/[^\/]+\/\d+/;
442 var re2 = /http:\/\/(www\.)?flickr\.com\/photos\/[^\/]+\/sets\/$/;
443 var re3 = /http:\/\/(www\.)?flickr\.com\/photos\/[^\/]+\/sets\/\d+\//;
444 if( ! re.test(document.location) && ! (re2.test(document.location) || !re3.test(document.location))) return;
446 for(var i in global_photos) photo_id = global_photos[i].id;
447 if( photo_id ) {
448     var ownersUrl = global_photos[photo_id].ownersUrl;
449     var isPublic = global_photos[photo_id].isPublic;
450     var isOwner = global_photos[photo_id].isOwner;
453 function sign(params) {
455     params.api_key = w.global_magisterLudi;
456     params.auth_hash = w.global_auth_hash;
457     params.auth_token = w.global_auth_token;
459     var _11=[], _12='';
460     for(var p in params) {
461         params[p]=params[p];
462         _11.push(p);
463         _12+="&"+p+"="+w.escape_utf8(params[p]);
464     }
465     _11.sort();
466     var cal=w.global_flickr_secret;
467     if(cal!="") {
468         for(var i=0;i<_11.length;i++) cal+=_11[i]+params[_11[i]];
469         cal=w.md5_calcMD5(cal);
470         _12="api_sig="+cal+_12;
471     }
472     return _12;
475 function searchPhoto(params,callback,list,paged,filters) {
477     if(DEBUG) GM_log('Call searchPhoto()');
478     var _index = -1;
480     this.list = list;
482     var callAPI = function(url) {
484         if(DEBUG) { GM_log(url); }
485         GM_xmlhttpRequest({
486             method: 'GET', url: url, headers: headers,
487             onload: function(responseDetails) {
488 //              if(DEBUG) GM_log(responseDetails.responseText);
489                 var parser = new w.DOMParser();
490                 var dom = parser.parseFromString(responseDetails.responseText, "application/xml");
491                 var stat = dom.getElementsByTagName('rsp')[0].getAttribute('stat');
492                 if( stat == 'ok' ) {
493                     var pages = dom.getElementsByTagName('photos')[0].getAttribute('pages');
494                     var page = dom.getElementsByTagName('photos')[0].getAttribute('page');
495                     var total = dom.getElementsByTagName('photos')[0].getAttribute('total');
496                     var photos = dom.getElementsByTagName('photo');
497                     for(var i=0;i<photos.length;i++) {
498                         skip = false;
499                         for(var j in filters) if( photos[i].getAttribute(j) != filters[j] ) skip = true;
500                         if( skip ) continue;
501                         var index = list.length;
502                         list[index] = new Object();
503                         list[index].id = photos[i].getAttribute('id');
504                         list[index].owner = photos[i].getAttribute('owner');
505                         list[index].secret = photos[i].getAttribute('secret');
506                         list[index].server = photos[i].getAttribute('server');
507                         list[index].img_s = 'http://static.flickr.com/'+list[index].server+'/'+list[index].id+'_'+list[index].secret+'_s.jpg';
508                         list[index].img_m = 'http://static.flickr.com/'+list[index].server+'/'+list[index].id+'_'+list[index].secret+'_m.jpg';
509                         list[index].url = 'http://www.flickr.com/photos/' + list[index].owner + '/'+ list[index].id +'/';
510                         if( photo_id == list[index].id ) _index = index+1;
511                     }
512                     if( paged && pages>page ) {
513                         params.page++;
514                         url = 'http://www.flickr.com/services/rest/?' + sign(params);
515                         callAPI(url);
516                     } else callback(_index,list,total);
517                 } else callback(-1, new Array(),0);
518             }
519         });
520     }
521     var url = 'http://www.flickr.com/services/rest/?' + sign(params);
522     callAPI(url);
525 function searchFaved(params,callback,favs,paged,filters) {
527     var url = 'http://www.flickr.com/photos/' +global_nsid+ '/popular-faves/';
528     var _index=-1, total=0;
530     function callHTML(url,page) {
531         url1 = url + 'page' + page +'/';
532         if(DEBUG) GM_log(url1);
533         GM_xmlhttpRequest({
534             method: 'GET', 
535             url: url1,
536             headers: headers,
537             onload: function(responseDetails) {
538                 if(DEBUG) GM_log('Faved page' +page+ ' loaded');
539                 var i=(page-1)*20;
540                 var html = ''+responseDetails.responseText;
541                 var rExp = /[\r\t\n\f]/g;
542                 html = html.replace(rExp, '');
544                 if(page==1) {
545                     rExp = /<div class="Results">\((\d+) photos\)<\/div>/;
546                     var r = rExp.exec(html);
547                     total = 1*r[1];
548                     favs[total-1] = new Object();
549                 }
550                 rExp = /(http:\/\/static.flickr.com\/\d+\/\d+_\w+_s\.jpg)/;
551                 var imgExp = /http:\/\/static.flickr.com\/(\d+)\/(\d+)_(\w+)_s\.jpg/;
552                 while( rExp.test(html) ) {
553                     var m = rExp.exec(html);
554                     var n = imgExp.exec(m[1]);
555                     html = html.replace(rExp,'');
557                     favs[i] = new Object();
558                     favs[i].id = n[2];
559                     favs[i].owner = global_nsid;
560                     favs[i].secret = n[3];
561                     favs[i].server = n[1];
562                     favs[i].img_s = m[1];
563                     favs[i].img_m = 'http://static.flickr.com/'+favs[i].server+'/'+favs[i].id+'_'+favs[i].secret+'_m.jpg';
564                     favs[i].url = 'http://www.flickr.com/photos/' + favs[i].owner + '/'+ favs[i].id +'/';
565                     if( photo_id == favs[i].id ) _index = i+1;
566                     i++;
567                 }
568 //              if( page < (total-total%20)/20+1 ) callHTML(url,page+1);
569 //              else callback(_index,favs);
570                 if( paged && page==1 ) {
571                     for(var j=2; j<= (total-total%20)/20+1; j++) callHTML(url,j);
572                 }
573                 if( _index>-1 ) {
574                     var _i = _index;
575                     _index = -1;
576                     callback(_i,favs,total,false);
577                 } else callback(-1,favs,total,true);
578             }
579         });
580     }
581     callHTML(url,1);
584 function checkCache(id,key) {
586     var timestamp = 1*GM_getValue(id + '_time', '0');
587     var now = new Date();
588     if(DEBUG) GM_log(now.getTime() + ', ' + timestamp);
589     if( now.getTime() - timestamp > 2*60*60*1000 ) {
590         if(DEBUG) GM_log('Cache expired');
591         return true;
592     } else {
593         if(DEBUG) GM_log('Cache valid');
594         var str = GM_getValue(id, '');
595         var pairs = str.split(':');
596         if( pairs[0]!=key ) return true;
598         var items = pairs[1].split(',');
599         for(var i=0; i<items.length; i++) 
600             if( items[i] == photo_id ) return true;
601         return false;
602     }
605 function Photostream(config) {
607     if(DEBUG) GM_log('Load ' + config.title );
609     var index, box,  _fwd, _bwd;
610     var arrow_next, arrow_prev, div, box, browse_link, thumb_prev, thumb_next;
612     this.div = div;
613     this.photos = new Array();
614     this.config = config;
616     var key='';
617     var id = config.title.replace(/ /g, '_');
619     for(var i in config) {
620         if( typeof(config[i]) == 'object' ) for(var j in config[i]) key += j+config[i][j];
621         else key += i+config[i];
622     }
623     key = w.md5_calcMD5(key);
624     if( config.cache && !checkCache(id, key) ) return;
625     if(DEBUG) GM_log('Cache miss');
627     var insertPoint = _gi('other_contexts_p');
628     var load_vset = _ce('div');
629     load_vset.style.border = '1px solid #e3e3e3';
630     load_vset.style.background = '#f3f3f3';
631     load_vset.style.padding = '4px';
632     load_vset.innerHTML = '<img src="http://www.flickr.com/images/pulser2.gif" /> Loading ' + config.title;
633     if( insertPoint.nextSibling )
634         insertPoint.parentNode.insertBefore(load_vset,insertPoint.nextSibling);
635     else insertPoint.parentNode.appendChild(load_vset);
637     var move = function(photos,index) {
638         if( _fwd || _bwd ) { p = index-1; q = index; }
639         else { p = index-2; q = index; }
640         _fwd = false; _bwd = false;
642         if(p>=0) {
643             thumb_prev.src = context_loading;
644             thumb_prev.src = photos[p].img_s;
645             thumb_prev.parentNode.href = photos[p].url + ( (config.group_name) ? 'in/pool-'+config.group_name: '') +'/';
646         } else {
647             thumb_prev.src = 'http://www.flickr.com/images/placeholder_first_photo.gif';
648             thumb_prev.parentNode.href = '#';
649         }
650         if(q<=photos.length-1) {
651             thumb_next.src = context_loading;
652             thumb_next.src = photos[q].img_s;
653             thumb_next.parentNode.href = photos[q].url + ( (config.group_name) ? 'in/pool-'+config.group_name: '') +'/';
654         } else {
655             thumb_next.src = 'http://www.flickr.com/images/placeholder_last_photo.gif';
656             thumb_next.parentNode.href = '#';
657         }
658         page = (index-index%config.perpage)/config.perpage + 1;
659         browse_link.href = config.browse + ((config.paged) ? ('page'+page+'/') : '');
660     }
662     var callback = function(index,photos,total,wait) {
663         var str = key +':';
664         for(var i=0;i<photos.length;i++) {
665             if(photos[i]) str += photos[i].id +',';
666         }
667         var now = new Date();
668         GM_setValue(id,str);
669         GM_setValue(id+'_time', '' + now.getTime());
671         if( config.addself && index==-1 ) index=0;
672         if( index<0 ) {
673             if( !wait ) insertPoint.parentNode.removeChild(load_vset);
674             return;
675         }
677         var page = (index-index%config.perpage)/config.perpage + 1;
678         var divs = _gt('div');
679         for(var i=0;i<divs.length; i++) {
680             if( divs[i].className == 'ContextTop' ) {
681                 ps = divs[i];
682                 break;
683             }
684         }
685 //      var _bb = w.document.getElementById('nextprev_button_'+w.nextprev_currentContextID);
686         if( ! w.nextprev_currentContextID.match(/stream.*/) ) ps = _gi('contextDiv_'+w.nextprev_currentContextID);
688         div = ps.cloneNode(true);
689         div.id = 'fav_stream';
690         insertPoint.style.display = 'block';
692         var trs = div.getElementsByTagName('tr');
693         for(var i=0;i<trs.length;i++) trs[i].id = '';
694         trs[1].style.display = 'table-row';
695         trs[1].id = 'favorites';
696         box = trs[1];
698         var imgs = div.getElementsByTagName('img');
699         for(var i=0;i<imgs.length;i++) {
700             if( imgs[i].className == 'nextprev_button' ) var t_np = imgs[i];
701             if( imgs[i].className == 'nextprev_arrows_img_next' ) {
702                 arrow_next = imgs[i];
703                 arrow_next.style.visibility = 'visible';
704                 arrow_next.addEventListener('click', function() {
705                     index++;
706                     if( index > photos.length ) index = photos.length;
707                     _fwd = true;
708                     move(photos,index);
709                 }, true);
710             }
711             if( imgs[i].className == 'nextprev_arrows_img_prev' ) {
712                 arrow_prev = imgs[i];
713                 arrow_prev.style.visibility = 'visible';
714                 arrow_prev.addEventListener('click', function() {
715                     index--;
716                     if( index < 0 ) index = 0;
717                     _bwd = true;
718                     move(photos,index);
719                 }, true);
720             }
721         }
723         var tbox_open = true;
724         t_np.src = 'http://www.flickr.com/images/context_open.gif';
725         t_np.onclickHandler = function() {
726             if( tbox_open ) {
727                 box.style.display = 'none';
728                 this.src = 'http://www.flickr.com/images/context_closed.gif';
729             } else {
730                 box.style.display = 'table-row';
731                 this.src = 'http://www.flickr.com/images/context_open.gif';
732             }
733             tbox_open = !tbox_open;
734         }
735         t_np.addEventListener('click', t_np.onclickHandler, true);
737         load_vset.parentNode.replaceChild(div, load_vset);
739         var elms = div.getElementsByTagName('a');
740         for(var i=0; i<elms.length; i++) {
741 //          if( elms[i].className == 'currentContextLink' || elms[i].className == 'Grey' ) {
742             if( elms[i].className == 'currentContextLink' ) {
743                 elms[i].href = config.browse;
744                 elms[i].innerHTML = config.title + ' <br />(Virtual Set)';
745             }
746             if( elms[i].className == 'contextThumbLink' ) {
747                 browse_link = elms[i];
748                 browse_link.href = config.browse + ((config.paged) ? ('page'+page+'/') : '');
749             }
750         }
752         var divs = div.getElementsByTagName('div');
753         for(var i=0; i<divs.length; i++) {
754             if(divs[i].className == 'nextprev_contextThumbsDiv') var target = divs[i];
755             if(divs[i].className == 'photosNum') divs[i].innerHTML = photos.length;
756             if(divs[i].className == 'showLink') {
757                 if( config.slide=='' ) divs[i].style.display = 'none';
758                 else divs[i].getElementsByTagName('a')[0].href = config.slide;
759             }
760         }
761         var imgs = target.getElementsByTagName('img');
762         thumb_prev = imgs[0];
763         thumb_next = imgs[1];
764         thumb_prev.src = context_loading;
765         thumb_next.src = context_loading;
767         move(photos,index);
768     }
769     if(!config.funct) var stream = new searchPhoto(config.params,callback, this.photos, config.api_paged, config.filters);
770     else var stream = new config.funct(config.params,callback, this.photos, config.api_paged, config.filters);
773 function VirtualSet(config) {
775     if(DEBUG) GM_log('Load ' + config.title );
777     var index, box,  _fwd, _bwd;
778     var arrow_next, arrow_prev, div, box, browse_link, thumb_prev, thumb_next;
780     this.div = div;
781     this.config = config;
782     this.photos = new Array();
784     var key='';
785     var id = config.title.replace(/ /g, '_');
787     for(var i in config) {
788         if( typeof(config[i]) == 'object' ) for(var j in config[i]) key += j+config[i][j];
789         else key += i+config[i];
790     }
791     key = w.md5_calcMD5(key);
792 //    if( config.cache && !checkCache(id, key) ) return;
793     if(DEBUG) GM_log('Cache miss');
795     var insertPoint = _gi('SubNav');
796     var load_vset = _ce('div');
797     load_vset.className = 'Sets';
798     load_vset.innerHTML = '<div class="SetCase"><div class="setLinkDiv"><a id="vsetlink_' +id+ '" href="#" class="setLink"><img id="vsethumb_' +id+ '" src="'+context_loading+'" class="setThumb"></a></div></div>';
799     load_vset.innerHTML += '<h4><a id="vsetlink2_' +id+ '" href="#" class="Seta">' +config.title+ '</a></h4>';
800     load_vset.innerHTML += '<p style="color:#ff0084"><b id="vsetnum_'+id+'"></b> photos<br /><i>in this Virtual Set</i></p>';
801     if( insertPoint.nextSibling )
802         insertPoint.parentNode.insertBefore(load_vset,insertPoint.nextSibling);
803     else insertPoint.parentNode.appendChild(load_vset);
805     var callback = function(index,photos,total,wait) {
806         var setDiv;
807         var max = photos.length;
808         config.photos = photos;
809         if( photos.length<=0 ) {
810             insertPoint.parentNode.removeChild(load_vset);
811             return;
812         }
813         for(var i=0;i<photos.length;i++) if(!photos[i]) {
814             if(DEBUG) GM_log(config.title + ' not loaded yet ' + i +','+total);
815             return;
816         }
818         function showSet(page) {
819             var perpage = 24;
820             var total = photos.length;
821             if(page==undefined) page=1;
822             pages = (total-total%perpage)/perpage+1;
823             var str = '<table cellspacing="30">';
824             str += '<tr>';
825             str += '<td><h2>'+config.title+'</h2></td>';
826             str += '<td align="right"><a href="javascript:;" onclick="this.parentNode.parentNode.parentNode.parentNode.parentNode.style.display=\'none\';">Close</a></td>';
827             str += '</td></tr>';
828             str += '<tr><td width="240" valign="top">';
829             str += '<p><img src="' +photos[0].img_m+ '"></p>';
830             str += '<p><small>There are ' + total + ' photos in this virtual set.</small></p>';
831             if(isOwner) {
832                 str += '<p><b>Add to a real set?</b><br /><a href="javascript:;" onclick="addtoSet[\''+id+'\'](false)">Add to an existing set</a> | <a href="javascript:;" onclick="addtoSet[\''+id+'\'](true)">Add to a new set</a></p>';
833                 str += '<div id="photoset_list"></div>';
834                 str += '<p><span id="vset_log"></span></p>';
835             }
836             str += '</td>';
837             str += '<td width="320" valign="top"><div style="width:320px">';
838             var num = ((total/perpage)>page)? page*perpage: total;
839             if(DEBUG) GM_log('Page=' + page +' total='+total+' num='+num);
840             for(var i=(page-1)*perpage;i<num;i++) 
841                 str += '<a href="' +photos[i].url+ '" class="thumb_link"><img src="' +photos[i].img_s+ '" width=75 height=75 style="margin:0px 3px 3px 0px;" /></a>';
842             str += '</div></td></tr>';
843             str += '<tr><td colspan="2" align="center"><div class="Paginator">';
844             for(i=1;i<=pages;i++) {
845                 if(i==page) str += '<span class="this-page">'+i+'</span>';
846                 else str += '<a title="'+id+'" href="#vset" onclick="showSet[\''+id+'\']('+i+')">'+i+'</a> ';
847             }
848             str += '</div></td></tr></table>';
849             if( !_gi('setdiv_'+config.title) ) {
850                 setDiv = _ce('div');
851                 setDiv.id = 'setdiv_'+config.title;
852                 setDiv.style.position = 'absolute';
853                 setDiv.style.overflow = 'visible';
854                 setDiv.style.width = '650px';
855                 setDiv.style.padding = '0px 25px 0px 25px';
856                 setDiv.style.margin = '4px';
857                 setDiv.style.zIndex = 1000;
858                 setDiv.style.display = 'none';
859                 setDiv.style.border = '15px solid #eaeaea';
860                 setDiv.style.background = '#ffffff';
861                 if( insertPoint.nextSibling ) insertPoint.parentNode.insertBefore(setDiv,insertPoint.nextSibling);
862                 else insertPoint.parentNode.appendChild(setDiv);
863                 ntag = _ce('a');
864                 ntag.name="vset";
865                 setDiv.parentNode.insertBefore(ntag,setDiv);
866             } else setDiv = _gi('setdiv_'+config.title);
867             setDiv.innerHTML = str;
868         }
869         w.showSet[id] = showSet;
871         function addtoSet(newset,setid,setitle) {
873             var photosetid,photoseturl,ok=1,fail=0;
875             function addPhoto(id) {
876                 if(DEBUG) GM_log('add photo #' + id);
877                 if(id>=max) {
878                     if(setitle!=undefined) title = setitle;
879                     _gi('vset_log').innerHTML += '<p>Adding Completed.<br />Goto the set "<a href="'+photoseturl+'">'+title+'</a>".</p>';
880                     return;
881                 }
882                 var url = 'http://www.flickr.com/services/rest/?' + sign({
883                     method:'flickr.photosets.addPhoto',
884                     photoset_id: photosetid,
885                     photo_id: photos[id].id
886                 });
887                 GM_xmlhttpRequest({
888                     method: 'GET', url: url, headers: headers,
889                     onload: function(responseDetails) {
890                         var parser = new w.DOMParser();
891                         var dom = parser.parseFromString(responseDetails.responseText, "application/xml");
892                         var stat = dom.getElementsByTagName('rsp')[0].getAttribute('stat');
893                         if( stat == 'ok' ) ok++; 
894                         else {
895                             fail++;
896                             if(DEBUG) GM_log(responseDetails.responseText);
897                         }
898                         _gi('vset_log').innerHTML = ok + ' photos added ';
899                         _gi('vset_log').innerHTML += (fail) ? (', ' + fail +' photos fail.') : '';
900                         id++;
901                         addPhoto(id);
902                     }
903                 });
904             }
906             if(newset) {
907                 var title = prompt('New set title?');
908                 if(title==null) return;
909                 max = prompt('How many photos to add?\n (can use to add your top N photos or the first N photos in virtual set)',max);
910                 max = 1*max;
911                 if( max<=0 || max>photos.length) return;
912                 var url = 'http://www.flickr.com/services/rest/?' + sign({
913                     method:'flickr.photosets.create',
914                     title: title,
915                     primary_photo_id: photos[0].id
916                 });
917                 if(DEBUG) GM_log(url);
918                 GM_xmlhttpRequest({
919                     method: 'GET', url: url, headers: headers,
920                     onload: function(responseDetails) {
921                         var parser = new w.DOMParser();
922                         var dom = parser.parseFromString(responseDetails.responseText, "application/xml");
923                         var stat = dom.getElementsByTagName('rsp')[0].getAttribute('stat');
924                         if( stat == 'ok' ) {
925                             _gi('vset_log').innerHTML = 'Creating set....';
926                             photosetid = dom.getElementsByTagName('photoset')[0].getAttribute('id');
927                             photoseturl = dom.getElementsByTagName('photoset')[0].getAttribute('url');
928                             addPhoto(1);
929                         } else {
930                             var err = dom.getElementsByTagName('err')[0].getAttribute('msg');
931                             _gi('vset_log').innerHTML = 'Photoset created fail: ' + err;
932                         }
933                     }
934                 });
935             } else {
936                 if(setid==-1) return;
937                 if(setid==undefined) {
938                     max = prompt('How many photos to add?\n (can use to add your top N photos or the first N photos in virtual set)',max);
939                     max = 1*max;
940                     if( max<=0 || max>photos.length) return;
941                     _gi('vset_log').innerHTML = 'Loading your photosets....';
942                     var url = 'http://www.flickr.com/services/rest/?' + sign({
943                         method:'flickr.photosets.getList'
944                     });
945                     GM_xmlhttpRequest({
946                         method: 'GET', url: url, headers: headers,
947                         onload: function(responseDetails) {
948                             _gi('vset_log').innerHTML = '';
949                             var parser = new w.DOMParser();
950                             var dom = parser.parseFromString(responseDetails.responseText, "application/xml");
951                             var stat = dom.getElementsByTagName('rsp')[0].getAttribute('stat');
952                             if( stat == 'ok' ) {
953                                 var photosets = dom.getElementsByTagName('photoset');
954                                 var str = '<select onchange="addtoSet[\''+id+'\'](false,this.value,this.options[this.selectedIndex].text)"><option value="-1">Select Photoset....</optiom>';
955                                 for(var i=0;i<photosets.length;i++) {
956                                     photosetid = photosets[i].getAttribute('id');
957                                     primary = photosets[i].getAttribute('primary');
958                                     secret = photosets[i].getAttribute('secret');
959                                     server = photosets[i].getAttribute('server');
960                                     photoseturl = 'http://www.flickr.com/photos/'+global_nsid+'/sets/'+photosetid+'/';
961                                     text = new String(photosets[i].getElementsByTagName('title').item(0).firstChild.data);
962                                     title = text.substr(0,25) + ((text.length>25)?'...':'');
963                                     str += '<option value="'+photosetid+'">'+title+'</option>';
964                                 }
965                                 str += '</select>';
966                                 _gi('photoset_list').innerHTML = str;
967                             } else {
968                                 var err = dom.getElementsByTagName('err')[0].getAttribute('msg');
969                                 _gi('vset_log').innerHTML = 'Photoset created fail: ' + err;
970                             }
971                         }
972                     });
973                     return;
974                 }
975                 photosetid = setid;
976                 photoseturl = 'http://www.flickr.com/photos/'+global_nsid+'/sets/'+photosetid+'/';
977                 ok=0;
978                 addPhoto(0);
979             }
980         }
981         w.addtoSet[id] = addtoSet;
983         _gi('vsethumb_' +id).src = photos[0].img_s;
984 //      _gi('vsetlink_' +id).href = photos[0].url;
985         _gi('vsetlink_' +id).href = 'javascript:;';
986         _gi('vsetlink_' +id).addEventListener('click', function() {
987             if( !_gi('setdiv_'+config.title) ) {
988                 showSet();
989             }
990             if( setDiv.style.display == 'none' ) setDiv.style.display = 'block';
991             else setDiv.style.display = 'none';
992         }, true);
993         _gi('vsetlink2_' +id).href = 'javascript:;';
994         _gi('vsetlink2_' +id).addEventListener('click', function() {
995             if( !_gi('setdiv_'+config.title) ) {
996                 showSet();
997             }
998             if( setDiv.style.display == 'none' ) setDiv.style.display = 'block';
999             else setDiv.style.display = 'none';
1000         }, true);
1001         _gi('vsetnum_' +id).innerHTML = photos.length;
1002     }
1003 //    config.api_paged = false;
1004 //    config.params.per_page=1;
1005     if(!config.funct) var stream = new searchPhoto(config.params,callback, this.photos, config.api_paged, config.filters);
1006     else var stream = new config.funct(config.params,callback, this.photos, config.api_paged, config.filters);
1009 function showVirtualSet() {
1012 function getGroupId(vset) {
1013     var group_name = /\/in\/pool-([^\/]+)/.exec(document.location.pathname);
1014     if(group_name) {
1015         group_name = group_name[1];
1016         vsets[vset].config.browse = 'http://www.flickr.com/groups/'+group_name+'/pool/'+vsets[vset].config.params.user_id+'/';
1017         vsets[vset].config.params.group_id = w.nextprev_currentContextID.replace(/pool/,'');
1018         vsets[vset].config.group_name = group_name;
1019         
1020         var url = 'http://www.flickr.com/services/rest/?' + sign({
1021             method:"flickr.urls.lookupGroup",
1022             url: "http://www.flickr.com/groups/"+group_name
1023         });
1024         GM_xmlhttpRequest({
1025             method: 'GET', url: url, headers: headers,
1026             onload: function(responseDetails) {
1027                 var params = vsets[vset].config;
1028                 params.browse = 'http://www.flickr.com/groups/'+group_name+'/pool/'+params.params.user_id+'/';
1029                 var matches = /id="(.+?)"/.exec(responseDetails.responseText);
1030                 if(matches) params.params.group_id = matches[1];
1031                 vsets[vset].config.group_name = group_name;
1032                 vsets[vset].stream = new Photostream(vsets[vset].config);
1033             }
1034         });
1035     }
1038 function newPStreams() {
1039     var re = /http:\/\/(www\.)?flickr\.com\/photos\/[^\/]+\/\d+/;
1040     if( ! re.test(document.location) ) return;
1042     if( isOwner ) {
1043         if(vsets['vset3'].enabled() && !vsets['vset3'].stream && isPublic == 0 ) vsets['vset3'].stream = new Photostream(private);
1044         if(vsets['vset8'].enabled() && !vsets['vset8'].stream && _gi('fave_countSpan').innerHTML != '0 people' ) vsets['vset8'].stream = new Photostream(favorited);
1045         if(vsets['vset10'].enabled() && !vsets['vset10'].stream ) vsets['vset10'].stream = new Photostream(yourinteresting);
1046         if(vsets['vset11'].enabled() && !vsets['vset11'].stream && (document.location.pathname.indexOf('/in/pool-') >= 0)) vsets['vset11'].stream = new Photostream(vsets['vset11'].config);
1047     } else {
1048         if(vsets['vset1'].enabled() && !vsets['vset1'].stream && _gi('photo_gne_button_add_to_faves').src.match(/\/a_fave_grey.gif/) ) vsets['vset1'].stream = new Photostream(favorites); 
1049         if(vsets['vset7'].enabled() && !vsets['vset7'].stream && _gi('photo_gne_button_add_to_faves').src.match(/\/a_fave_grey.gif/) ) vsets['vset3'].stream = new Photostream(favorites2); 
1050         if(vsets['vset2'].enabled() && !vsets['vset2'].stream ) vsets['vset2'].stream = new Photostream(contacts);
1051         if(vsets['vset9'].enabled() && !vsets['vset9'].stream ) vsets['vset9'].stream = new Photostream(userinteresting);
1052         if(vsets['vset12'].enabled() && !vsets['vset12'].stream && (document.location.pathname.indexOf('/in/pool-') >= 0)) vsets['vset12'].stream = new Photostream(vsets['vset12'].config);
1053     }
1055     if(vsets['vset4'].enabled() && !vsets['vset4'].stream ) vsets['vset4'].stream = new Photostream(interestingness);
1056     if(vsets['vset13'].enabled() && !vsets['vset13'].stream ) vsets['vset13'].stream = new Photostream(relevantphotos);
1058     if(vsets['vset5'].enabled() && !vsets['vset5'].stream ) {
1059         for(var i in global_photos[photo_id].tagsA ) 
1060             if( global_photos[photo_id].tagsA[i] == 'geotagged' ) 
1061                 vsets['vset5'].stream = new Photostream(geotagged);
1062     }
1064     if( vsets['vset6'].enabled() && !vsets['vset6'].stream && isPublic == 0 ) vsets['vset6'].stream = new Photostream(friends);
1067 function newVsets() {
1068     var re = /http:\/\/(www\.)?flickr\.com\/photos\/[^\/]+\/sets\//;
1069     var re2 = /http:\/\/(www\.)?flickr\.com\/photos\/[^\/]+\/sets\/\d+\//;
1070     if( ! re.test(document.location) || re2.test(document.location) ) return;
1072     if(DEBUG) GM_log('In set page ' + isOwner);
1074     if( isOwner ) {
1075         if(vsets['vset3'].enabled() && !vsets['vset3'].stream ) vsets['vset3'].stream = new VirtualSet(private);
1076         if(vsets['vset8'].enabled() && !vsets['vset8'].stream ) vsets['vset8'].stream = new VirtualSet(favorited);
1077         if(vsets['vset10'].enabled() && !vsets['vset10'].stream ) vsets['vset10'].stream = new VirtualSet(yourinteresting);
1078     } else {
1079 //      if(vsets['vset1'].enabled() && !vsets['vset1'].stream ) vsets['vset1'].stream = new VirtualSet(favorites); 
1080         if(vsets['vset7'].enabled() && !vsets['vset7'].stream ) vsets['vset3'].stream = new VirtualSet(favorites2); 
1081 //      if(vsets['vset2'].enabled() && !vsets['vset2'].stream ) vsets['vset2'].stream = new VirtualSet(contacts);
1082         if(vsets['vset9'].enabled() && !vsets['vset9'].stream ) vsets['vset9'].stream = new VirtualSet(userinteresting);
1083     }
1084     if(vsets['vset4'].enabled() && !vsets['vset4'].stream ) {
1085 //      vsets['vset4'].stream = new VirtualSet(interestingness);
1086     }
1088     if(vsets['vset5'].enabled() && !vsets['vset5'].stream ) vsets['vset5'].stream = new VirtualSet(geotagged);
1089     if(vsets['vset6'].enabled() && !vsets['vset6'].stream ) vsets['vset6'].stream = new VirtualSet(friends);
1090     if(vsets['vset13'].enabled() && !vsets['vset13'].stream ) vsets['vset13'].stream = new VirtualSet(relevantphotos);
1093 newPStreams();
1094 newVsets();
1096 })();