1 // Flickr Scientist Photographers tools
4 // Copyright (c) 2006, Pierre Andrews.
5 // Released under the GPL license
6 // http://www.gnu.org/copyleft/gpl.html
9 // @name Flickr Scientist Photographers tools
10 // @namespace http://6v8.gamboni.org/
11 // @description tools for administrators of the scientist photographers group.
14 // @creator Pierre Andrews (mortimer.pa@free.fr)
15 // @include http://*flickr.com/groups/sciencegroup*
22 //======================================================================
26 name
: "Flickr Scientist Photographers tools",
27 namespace: "http://6v8.gamboni.org/",
28 description
: "Tools for administrators of the scientist photographers group.",
29 identifier
: "http://6v8.gamboni.org/IMG/js/flickrscientistphotograp.user.js",
30 version
: "0.2", // version
31 date
: (new Date(2006, 6, 24)) // update date
36 //======================================================================
37 //to do the closure and get the right this.
38 //adapted from http://persistent.info/greasemonkey/gmail.user.js
40 function getObjectMethodClosure(object
, method
) {
41 return function(arg
) {
42 return object
[method
](arg
);
46 function getObjectMethodClosure0(object
, method
, arg
) {
48 return object
[method
](arg
);
52 function getObjectMethodClosure01(object
, method
, arg
,arg1
) {
54 return object
[method
](arg
,arg1
);
58 if(unsafeWindow
.console
)
59 unsafeWindow
.console
.log(arguments
);
64 var FlickrSPAdminTools = function() {this.init();}
66 FlickrSPAdminTools
.prototype = {
70 selectedPhotos
: new Array(),
71 removePhotosList
: new Array(),
75 var threadTitle
= document
.evaluate(
76 "//td[@id='GoodStuff']/h2",
77 document
, null, XPathResult
.FIRST_ORDERED_NODE_TYPE
, null
79 var del
= document
.evaluate("//a[@class='Warning']",
80 document
, null, XPathResult
.FIRST_ORDERED_NODE_TYPE
, null
82 if(threadTitle
&& del
&& del
.innerHTML
== "delete topic" && (threadTitle
.innerHTML
.indexOf('Selection Week') >= 0 || threadTitle
.innerHTML
.indexOf('Selection week') >= 0 || threadTitle
.innerHTML
.indexOf('selection week') >= 0 || threadTitle
.innerHTML
.indexOf('Selection') >= 0)) {
85 /* var adminLink = document.evaluate(
86 "//a[@href='/groups/sciencegroup/admin/']",
87 document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null
90 this.addCleanPoolTool(adminLink.parentNode);
94 showHideDec: function(vote
,voteDec
) {
95 if(vote
.value
!="x") voteDec
.style
.display
= 'inline';
96 else voteDec
.style
.display
= 'none';
99 addVotingTool: function() {
100 var message
= document
.evaluate(
101 "//textarea[@name='message']",
102 document
, null, XPathResult
.FIRST_ORDERED_NODE_TYPE
, null
105 var images
= document
.evaluate(
106 "//table[@class='TopicReply']//td[@class='Said']/p//img",
107 document
, null, XPathResult
.UNORDERED_NODE_SNAPSHOT_TYPE
, null);
108 for(var i
= 0; i
< images
.snapshotLength
; i
++) {
109 var img
= images
.snapshotItem(i
);
110 var vote
= document
.createElement('select');
111 var voteDec
= document
.createElement('select');
113 for(var j
=1; j
<10; j
++) {
114 html
+= '<option>'+j
+"</option>\n";
116 vote
.innerHTML
= "<option>x</option>\n"+html
+"<option>10</option>\n";
117 voteDec
.innerHTML
= "<option>0</option>\n"+html
;
118 voteDec
.style
.display
='none';
119 vote
.addEventListener('change',getObjectMethodClosure01(this,'showHideDec',vote
,voteDec
),true);
120 img
.parentNode
.parentNode
.insertBefore(vote
,img
.parentNode
);
121 img
.parentNode
.parentNode
.insertBefore(document
.createTextNode('.'),img
.parentNode
);
122 img
.parentNode
.parentNode
.insertBefore(voteDec
,img
.parentNode
);
123 img
.parentNode
.parentNode
.insertBefore(document
.createElement('br'),img
.parentNode
);
124 this.imgSrc
.push(new Array(img
.src
.replace(/\.jpg/,'_t.jpg'),img
.parentNode
.href
));
125 this.votes
.push(vote
);
126 this.votes
.push(voteDec
);
128 var button
= document
.createElement('button');
129 button
.innerHTML
= "VOTE";
130 button
.type
='button';
131 button
.className
= 'Butt';
133 button
.addEventListener('click',function() {
135 for(var k
= 0;k
< self
.votes
.length
;k
=k
+2) {
136 msg
+= (k
/2+1)+'- '+self
.votes
[k
].value
;
137 if(self
.votes
[k
+1].style
.display
== 'inline') msg
+= '.'+self
.votes
[k
+1].value
;
146 var buttonTotal
= document
.createElement('button');
147 buttonTotal
.innerHTML
= "Make Total";
148 buttonTotal
.type
='button';
149 buttonTotal
.className
= 'Butt';
151 buttonTotal
.addEventListener('click',function() {
152 var paraphs
= document
.evaluate("//table[@class='TopicReply']//td[@class='Said']/p",
153 document
, null, XPathResult
.UNORDERED_NODE_SNAPSHOT_TYPE
, null);
154 var grades
= new Array();
156 for(var i
= 0; i
< paraphs
.snapshotLength
; i
++) {
157 var paragraph
= paraphs
.snapshotItem(i
);
158 if(paragraph
.innerHTML
.indexOf('<img') < 0) {
159 var lines
= paragraph
.innerHTML
.split('<br>');
162 for(var j
= 0;j
<lines
.length
;j
++) {
163 if(!grades
[idx
]) grades
[idx
] = new Array();
164 if(matches
= /^\s*([0-9]+-\s*)?([0-9x]+(.[0-9]+)?)\s*.*$/mi.exec(lines
[j
])) {
165 var value
= parseFloat(matches
[2]);
166 if(value
!= NaN
|| matches
[2] == 'x' || matches
[2] == 'X') {
168 var l
= lines
[j
].replace(/^\s*[0-9]+-/,'');
169 grades
[idx
++].push(value
);
175 var author
= document
.evaluate("h4/a[2]",
176 paragraph
.parentNode
, null, XPathResult
.FIRST_ORDERED_NODE_TYPE
, null
178 if(!author
) author
= document
.evaluate("h4/a[1]",
179 paragraph
.parentNode
, null, XPathResult
.FIRST_ORDERED_NODE_TYPE
, null
182 self
.voters
.push(self
.makeAuthorName(author
.innerHTML
));
186 var scores
= new Array();
187 for(var i
=0; i
< grades
.length
;i
++) {
188 if(grades
[i
].length
> 0) {
189 message
.value
+= (i
+1)+'- ';
193 for(var j
=0; j
<grades
[i
].length
;j
++) {
194 line
+= ', '+self
.voters
[j
]+' '+grades
[i
][j
];
195 if(isNaN(grades
[i
][j
])) nan
++;
200 nan
+= length
-grades
[i
].length
;
202 M8_log((i
+1)+"-"+tot
+"/"+(grades
[i
].length
-nan
));
203 var moy
= tot
/(grades
[i
].length
-nan
);
204 line
= line
.replace(/NaN/g,moy
);
207 tot
= parseInt(tot
*100)/100;
208 scores
.push(new Array(tot
,i
));
209 line
= line
.substr(1);
210 message
.value
+= line
+' = '+tot
+"\n";
213 scores
.sort(function(A
, B
) {
219 var last
= scores
[idx
][0];
221 var places
= new Array("First", "Second", "Third");
222 message
.value
+= "\n--------------------\n\n";
227 while(scores
[idx
][0] == last
) {
228 sel
+= ' and '+(scores
[idx
][1]+1);
229 img
+= '<a href="'+self
.imgSrc
[scores
[idx
][1]][1]+'"><img src="'+self
.imgSrc
[scores
[idx
][1]][0]+'"/></a>'+"\n";
232 if(idx
< scores
.length
)
233 last
= scores
[idx
][0];
238 message
.value
+=places
[l
]+" Place: Image "+sel
+" ("+scores
[idx
-1][0]+" points)\n";
239 message
.value
+= img
+"\n";
243 message
.parentNode
.parentNode
.insertBefore(buttonTotal
,message
.parentNode
.nextSibling
);
244 message
.parentNode
.parentNode
.insertBefore(button
,message
.parentNode
.nextSibling
);
249 //======================================================================
251 //======================================================================
253 addCleanPoolTool: function(links
) {
254 links
.innerHTML
+= '<img width="1" height="11" alt="" src="/images/subnavi_dots.gif"/>';
255 var clean
= links
.appendChild(document
.createElement('a'));
256 clean
.innerHTML
= "Clean Pool";
258 clean
.style
.color
= 'red';
259 clean
.addEventListener('click',getObjectMethodClosure(this,"cleanPool"),true);
262 cleanPool: function() {
263 var back
= document
.body
.appendChild(document
.createElement('div'));
264 back
.id
="poolCleaningBack";
265 back
.setAttribute('style',"position:absolute;background-color: black;opacity: 0.35; display: block; left: 0pt;");
266 back
.style
.width
= document
.body
.clientWidth
+'px';
267 back
.style
.height
= document
.body
.clientHeight
+'px';
268 back
.style
.top
= document
.body
.scrollTop
+'px';
269 var modal
= document
.body
.appendChild(document
.createElement('div'));
270 modal
.id
="poolCleaning";
271 modal
.setAttribute('style',"position:absolute;background:white;border: 3px solid black;width: 300px;display: block; left: 442px;");
272 modal
.innerHTML
= '<div style="padding:12px;background-color: #EEEEEE;clear:both;font-size: 14px;">Remove Photos that were not selected</div>';
273 modal
.style
.top
= document
.body
.scrollTop
+(document
.body
.clientHeight
/2)+'px';
275 var dialog
= modal
.appendChild(document
.createElement('div'));
276 dialog
.setAttribute('style',"padding: 18px 16px;clear:both;");
277 var content
= dialog
.appendChild(document
.createElement('div'));
278 content
.innerHTML
= "Fetching Photos";
280 var caution
= dialog
.appendChild(document
.createElement('div'));
281 caution
.style
.margin
= "1em";
282 caution
.style
.paddingTop
= "1em";
283 caution
.style
.borderTop
= "1px solid black"
284 caution
.innerHTML
+= '<strong>BEWARE</strong> This operation is not reversible and will remove any photo in the pool that has not been tagged with "SP selection" or posted since Monday.';
286 var buttons
= dialog
.appendChild(document
.createElement('div'));
287 var ok
= buttons
.appendChild(document
.createElement('button'));
291 ok
.innerHTML
= '<img id="fgpe_pulser" src="http://www.flickr.com/images/pulser2.gif" style="vertical-align:middle;margin-right:4px;border:0px #ffffff" />';
292 var cancel
= buttons
.appendChild(document
.createElement('button'));
293 cancel
.type
='button';
294 cancel
.className
= 'Butt';
295 cancel
.innerHTML
= 'Cancel';
297 cancel
.addEventListener('click',function() {
298 document
.body
.removeChild(back
);
299 document
.body
.removeChild(modal
);
303 ok
.addEventListener('click',getObjectMethodClosure01(this,'removePhotos',ok
,content
),true);
305 modal
.style
.top
= document
.body
.scrollTop
+((document
.body
.clientHeight
-modal
.scrollHeight
)/2)+'px';
306 this.fetchGroupPhotos(ok
,content
);
310 fetchGroupPhotos: function(okButton
,contentDiv
) {
314 flickr_groups_pools_getPhotos_onLoad: function(success
, responseXML
, responseText
, params
){
315 if(success
) self
.process_taggedID(responseText
,okButton
,contentDiv
);
317 contentDiv
.innerHTML
= "There was an error fetching photos";
318 M8_log(responseText
);
323 unsafeWindow
.F
.API
.callMethod('flickr.groups.pools.getPhotos', {tags
:'spselection',group_id
:"38873539@N00",per_page
:PER_PAGE
}, listener
);
327 process_taggedID: function(rsp
,okButton
,contentDiv
) {
328 var rsp
= rsp
.replace(/<\?xml.*\?>/,'');
331 contentDiv
.innerHTML
= "There was an error fetching photos";
334 for each(photo
in rsp
..photo
) {
335 this.selectedPhotos
.push(parseInt(photo
.@id
));
340 flickr_groups_pools_getPhotos_onLoad: function(success
, responseXML
, responseText
, params
){
342 var rsp
= responseText
.replace(/<\?xml.*\?>/,'');
346 contentDiv
.innerHTML
= "There was an error fetching photos";
349 var thisweek
= new Date();
350 thisweek
= thisweek
.getTime()
352 +((24-thisweek
.getHours())*3600+(60-thisweek
.getMinutes())*60
354 -thisweek
.getDay()*3600*24)*1000;
355 for each(photo
in rsp
..photo
) {
356 if(photo
.@dateadded
<= (thisweek
/1000)) {
357 if(self
.selectedPhotos
.indexOf(parseInt(photo
.@id
)) < 0) {
358 self
.removePhotosList
.push(photo
.@id
);
362 contentDiv
.innerHTML
= self
.removePhotosList
.length
+ ' photos found.';
363 okButton
.disabled
= false;
364 okButton
.innerHTML
= "Remove";
367 contentDiv
.innerHTML
= "There was an error fetching photos";
368 M8_log(responseText
);
373 unsafeWindow
.F
.API
.callMethod('flickr.groups.pools.getPhotos', {group_id
:"38873539@N00",per_page
:PER_PAGE
}, listener
);
377 removePhotos: function(okButton
,contentDiv
) {
378 contentDiv
.innerHTML
= "removing...";
379 okButton
.innerHTML
= '<img id="fgpe_pulser" src="http://www.flickr.com/images/pulser2.gif" style="vertical-align:middle;margin-right:4px;border:0px #ffffff" />';
381 for(var i
=0;i
<this.removePhotosList
.length
;i
++) {
382 unsafeWindow
.F
.API
.callMethod('flickr.groups.pools.remove', {group_id
:"38873539@N00",photo_id
:this.removePhotosList
[i
]}, {
383 flickr_groups_pools_remove_onLoad: function(success
, responseXML
, responseText
, params
) {
384 if(success
) self
.removed
++;
385 else M8_log("Cannot remove photo "+params
['photo_id']+": " +responseText
);
389 this.waitForAllRemove();
392 waitForAllRemove: function() {
393 if(this.removed
>= this.removePhotosList
.length
) {
394 document
.body
.removeChild(document
.getElementById('poolCleaning'));
395 document
.body
.removeChild(document
.getElementById('poolCleaningBack'));
397 setTimeout(getObjectMethodClosure(this,'waitForAllRemove'),1000);
401 makeAuthorName: function(name
) {
402 var sp
= name
.toLowerCase().replace(/(^[_'.]|['_.]$)/g,'').replace(/[_'.]/gi,' ');
405 sp
= sp
[0].substr(0,1)+'.'+sp
[1].substr(0,1)+'.';
407 sp
= sp
[0].substr(0,2);
412 //======================================================================
415 window
.addEventListener("load", function () {
418 // update automatically (http://userscripts.org/scripts/show/2296)
419 //win.UserScriptUpdates.requestAutomaticUpdates(SCRIPT);
422 new FlickrSPAdminTools();