1 // Flickr Group Comments
4 // Copyright (c) 2006, Pierre Andrews.
5 // Released under the GPL license
6 // http://www.gnu.org/copyleft/gpl.html
9 // @name Flickr Group Comments
10 // @namespace http://6v8.gamboni.org/Flickr-view-group-comments.html
11 // @description Display the recent comments in your groups directly in the recent change page
12 // @source http://6v8.gamboni.org/Flickr-view-group-comments.html
13 // @identifier http://6v8.gamboni.org/IMG/js/flickrgroupcomments.user.js
16 // @creator Pierre Andrews (mortimer.pa@free.fr)
17 // @include http://*flickr.com/recent.gne*
20 //======================================================================
23 //number of minutes before the next automatic reload.
24 //if set to false or 0, then there will never be automatic reloads
27 //======================================================================
31 var win = (unsafeWindow || window.wrappedJSObject || window);
35 name: "Flickr Group Comments",
36 namespace: "http://6v8.gamboni.org/Flickr-view-group-comments.html",
37 description: "Display the recent comments in your groups directly in the recent change page",
38 source: "http://6v8.gamboni.org/Flickr-view-group-comments.html", // script homepage/description URL
39 identifier: "http://6v8.gamboni.org/IMG/js/flickrgroupcomments.user.js",
40 version: "0.8", // version
41 date: (new Date(2006, 5, 26)) // update date
45 //======================================================================
46 //to do the closure and get the right this.
47 //adapted from http://persistent.info/greasemonkey/gmail.user.js
49 function getObjectMethodClosure11(object, method,args3) {
50 return function(arg) {
51 return object[method](arg,args3);
55 function getObjectMethodClosure12(object, method,args3,arg4) {
56 return function(arg) {
57 return object[method](arg,args3,arg4);
62 //======================================================================
65 // http status constants
68 // xmlhttprequest readystate
73 win.FlickrGroupComments = function() {;}
75 win.FlickrGroupComments.prototype = {
78 GM_addStyle(".EachGroup ul li.read {color: #AAAAAA; list-style-type: circle; font-size: 80%;}\
79 .EachGroup ul li.read a {color: #99ccff;} ");
80 var commentsLinks = document.evaluate("/html/body/div[@id='Main']/table[2]/tbody/tr/td[@id='Changes']/table/tbody/tr/td[2]/ul",
81 document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); // Get all group
83 for(var i = 0; i < commentsLinks.snapshotLength; i++) { // For each group...
84 var al = commentsLinks.snapshotItem(i);
90 var change = document.evaluate("//td[@id='Changes']/table[1]",
91 document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null
94 setTimeout(function() { document.location.reload(); } , AUTO_RELOAD*60*1000);
95 var reload = document.createElement('p');
96 reload.innerHTML = "Next automatic reload in: "+AUTO_RELOAD+" min";
97 reload.setAttribute("style","background-image: url(../images/dotted.gif); background-repeat: repeat-x; background-position: left top;padding-top:10px;");
98 change.parentNode.insertBefore(reload,change.nextSibling);
100 var timeout = function() {
101 var next = AUTO_RELOAD-(cnt++);
102 reload.innerHTML = "Next automatic reload in: "+
105 setTimeout(timeout, 60*1000);
107 setTimeout(timeout, 250);
113 prepareUL: function(element) {
114 var read = GM_getValue('flickrgroupcomments.read');
116 element.parentNode.parentNode.addEventListener('click',
117 getObjectMethodClosure11(this,"markAllAsRead",element),
120 news = element.getElementsByTagName("li");
121 for(var i=0;i<news.length;i++) {
123 var as = li.getElementsByTagName('A');
124 if(as.length>0 && !/More\.\.\./.test(as[0].innerHTML)) {
126 var re = new RegExp('<'+as[0].href+',([0-9]+)>');
129 matches = /\(([0-9]+) repl(ies|y)\)/.exec(li.innerHTML);
131 num = parseInt(matches[1])+1;
134 if(read && (matches = re.exec(read))) {
136 // if we have already marked this one as read, but the
137 // stored number is different, then it's like if we did not read it.
138 // (see comment below)
139 if(num >= 0 && num != matches[1]) show = false;
141 li.className = 'read';
146 var showA = li.appendChild(document.createElement('A'));
147 showA.innerHTML = " Show...";
148 showA.title="Display the message(s).";
149 showA.setAttribute("style","font-size:85%;color:grey;text-decoration: underline;");
151 showA.addEventListener('click',
152 getObjectMethodClosure12(this,"getGroupId",li,element),
155 li.addEventListener('click',
156 getObjectMethodClosure11(this,"markAsRead",li),
163 markAllAsRead: function(event, ul) {
164 if(event.target == ul || event.target.tagName == 'TD'|| event.target.tagName == 'H2') {
165 var news = ul.getElementsByTagName("li");
166 for(var i=0;i<news.length;i++) {
167 this.markAsRead(null,news[i]);
173 markAsRead: function(event,li) {
175 if(event.target == li.fgcShowA) return;
177 var as = li.getElementsByTagName('A');
178 if(as.length>0 && !/More\.\.\./.test(as[0].innerHTML)) {
179 li.className = 'read';
182 var matches = /\(([0-9]+) repl(ies|y)\)/.exec(li.innerHTML);
184 num = parseInt(matches[1])+1;
187 var read = GM_getValue('flickrgroupcomments.read');
188 if(read) read += '<'+as[0].href+','+num+'>';
189 else read = '<'+as[0].href+','+num+'>';
190 GM_setValue('flickrgroupcomments.read',read);
195 getGroupId: function(event,li,element) {
196 var showA = li.fgcShowA;
197 showA.style.display = 'none';
199 var newDiv = li.fgceNewDiv;
201 newDiv = document.createElement("DIV");
202 newDiv.innerHTML = '<img src="http://www.flickr.com/images/pulser2.gif" style="vertical-align:middle;margin-right:4px;border:0px #ffffff" />';
203 li.fgceNewDiv = newDiv;
206 var closeA = li.fgcCloseA;
208 closeA = li.appendChild(document.createElement('A'));
209 closeA.addEventListener('click',function() {
210 newDiv.style.display='none';
211 showA.style.display='block';
212 closeA.style.display='none';
213 li.className = 'read';
215 closeA.innerHTML = " Hide";
216 closeA.title="Hide the messages.";
217 closeA.setAttribute("style","font-size:85%;color:grey;text-decoration: underline;");
219 li.appendChild(newDiv);
221 if(!element.fgceGroupID) {
222 matches = /(flickr.com)?\/groups\/(.+?)\//.exec(element.innerHTML);
225 var groupName = matches[2];
228 onload : getObjectMethodClosure12(this,"process_groupID",li,element),
229 url : "http://www.flickr.com/services/rest/?method=flickr.urls.lookupGroup&api_key=e8c3239ff04c102ce2d6ed885bf99005&url=" + encodeURIComponent("http://flickr.com/groups/"+groupName),
232 GM_xmlhttpRequest( details );
235 this.groupIDReceived(element.fgceGroupID,li,element);
240 process_groupID: function(req,li,element) {
241 if(DEBUG) GM_log(req.responseText);
242 var rsp = req.responseText.replace(/<\?xml.*\?>/,'');
243 var rsp = new XML(rsp);
246 this.error( "Could not understand Flickr's response.", 0, req, li);
248 var stat = rsp.@stat;
250 this.error( "Could not find status of Flickr request", 0, req,li);
251 } else if (stat != 'ok') {
252 if (stat == 'fail') {
253 var err_node = rsp.err[0];
254 var code = err_node.@code;
255 var err_msg = err_node.@msg;
256 this.error( err_msg, code, req,li);
258 this.error("Unknown error status: '" + stat + "'", 0, req,li)
261 element.fgceGroupID = rsp..@id[0];
262 this.groupIDReceived(element.fgceGroupID,li,element);
269 groupIDReceived: function(id,li,element) {
270 if(!element.rssFeed) {
273 onload : getObjectMethodClosure12(this,"getGroupFeed",li,element),
274 url : "http://flickr.com/groups_feed.gne?id="+id+"&format=rss_200",
277 GM_xmlhttpRequest( details );
279 this.getGroupFeed(null,li,element);
284 getGroupFeed: function(req,li,element) {
285 if(DEBUG) GM_log(req.responseText);
287 if(!element.rssFeed) {
288 var rsp = req.responseText.replace(/<\?xml.*\?>/,'');
292 this.error( "Could not parse rss.", 0, req,li);
295 element.rssFeed = rsp;
298 this.getLastMessage(li,element.rssFeed);
302 getLastMessage: function(threadLi,rss) {
303 var links = threadLi.getElementsByTagName("a");
304 var newDiv = threadLi.fgceNewDiv;
305 newDiv.style.display = 'block';
306 if(links.length > 0){
307 var matches = /^([0-9]+) new replies$/.exec(links[0].innerHTML);
311 var num = parseInt(matches[1]);
312 href = links[1].href;
313 } else if(links[0].innerHTML == "New reply") {
315 href = links[1].href;
317 var matches = /\(([0-9]+) repl(ies|y)\)/.exec(threadLi.innerHTML);
319 num = parseInt(matches[1])+1;
322 href = links[0].href;
324 //mark this one as read with X messages.
325 //we need to keep track of the number for the case of the "New Topic ... (X replies)"
326 //where the href is the same but does not always point to the same number of message
327 var read = GM_getValue('flickrgroupcomments.read');
328 if(read) read += '<'+links[0].href+','+num+'>';
329 else read = '<'+links[0].href+','+num+'>';
330 GM_setValue('flickrgroupcomments.read',read);
331 newDiv.innerHTML = this.findMessages(rss,href,num);
336 findMessages: function(rss,link,num) {
338 var messArray = new Array();
340 //we have to introduce the www. as the rss links always contains it,
341 // but you could have links in the web page without them.
342 link = link.replace(/^http:\/\/(www.)?/,"http://www.");
343 for each(item in rss..item) {
344 if(item.link.indexOf(link) == 0 && cnt <= num) {
345 //mark all the one we see here are read too.
346 var read = GM_getValue('flickrgroupcomments.read');
347 if(read) read += '<'+item.link+',1>';
348 else read = '<'+item.link+',1>';
350 //TODO: here we could only push it if it has not been read yet.
351 messArray.push(item.description);
353 } else if(cnt > num) {
358 mess="sorry, message not found";
360 mess = "<ul style=\"list-style-type:none;margin: 0; padding:0;\">";
361 for(var i=messArray.length-1;i>=0;i--)
362 mess += "<li style=\"background-image: url(../images/dotted.gif); background-repeat: repeat-x; background-position: left bottom;padding-left: 1em;padding-bottom:1em;\">"+messArray[i]+"</li>";
368 error: function(err_msg, code, req, li) {
369 if(li.fgceNewDiv) li.fgceNewDiv.innerHTML = '<b style="color:red">'+err_msg+'</b>';
373 //======================================================================
376 window.addEventListener("load", function () {
379 // update automatically (http://userscripts.org/scripts/show/2296)
380 win.UserScriptUpdates.requestAutomaticUpdates(SCRIPT);
383 var flickrgp = new win.FlickrGroupComments();