There are 4 previous versions of this script.
// FlickrCommentWhen
// version 0.2.1
// 2010-01-14
// Copyright (c) 2009, Ryan Gallagher <http://mailhide.recaptcha.net/d?k=01s3QDaQrXNmFOMTttoahPPg==&c=45a75vsPJHUAbaWaf7hEep0XEaP0u0DkmwFiSB8MBeM=>
// Released under the GPL license
// http://www.gnu.org/copyleft/gpl.html
//
// --------------------------------------------------------------------
// This is a Greasemonkey user script.
//
// To install, you need Greasemonkey: http://greasemonkey.mozdev.org/
// Then restart Firefox and revisit this script.
// Under Tools, there will be a new menu item to "Install User Script".
// Accept the default configuration and install.
//
// IF YOU ARE UPGRADING FROM A PREVIOUS VERSION OF THIS SCRIPT, go to
// Tools/Manage User Scripts and manually uninstall the previous
// version before installing this one. This script lacks an auto-update
// prompt.
//
// Go here for latest version:
// http://userscripts.org/scripts/show/66212
//
// To uninstall, go to Tools/Manage User Scripts, select this script,
// and click Uninstall.
//
// --------------------------------------------------------------------
// What It Does:
//
// www.flickr.com (photo page & photoset comments page):
//
// This script augments the footer of each photo comment with a
// localized date/time string for the posted date. It reflects the
// value of 'datecreate' as returned via the API for comments.
//
// This date will be GMT but displayed in your timezone depending
// on how your browser Javascript implementation handles
// 'Date.toLocaleString'.
//
// The new date text is wrapped with a hidden link that will take you
// to the commenting user's archive page for that day, nice for
// interacting with photos uploaded around the time the comment was
// left. If they didn't upload anything that day you can always step
// back to the month level in the archives.
//
// --------------------------------------------------------------------
// Change Log:
//
// v0.1 2010-01-11
// * Initial Version
//
// v0.2 2010-01-13
// * Name changed to expand to include all comment types.
// * Added support for Set comments pages.
// * Added a linking to commenter's archive for that year/month/day
//
// v0.2.1 2010-01-14
// * Bug fix archive link, was using a number for day of week.
//
// --------------------------------------------------------------------
// To Do:
//
// * Update to include date/time string for gallery comments?
// * Update to include date/time string for group discussion posts?
// * Consider linking to month archives instead of days?
//
// --------------------------------------------------------------------
// ==UserScript==
// @name FlickrCommentWhen
// @namespace http://www.ryangallagher.name
// @description Add an actual date-time to flickr comments, instead of just the approx age.
// @include http://*.flickr.*/photos/*/*
// @exclude http://*.flickr.*/photos/*/*/favorites*
// @exclude http://*.flickr.*/photos/*/*/meta*
// @exclude http://*.flickr.*/photos/*/*/stats*
// @exclude http://*.flickr.*/photos/*/*/sizes*
// @exclude http://*.flickr.*/photos/*/*/nearby*
// ==/UserScript==
window.addEventListener("load", function() { FlickrCommentWhen() }, false);
// wrapper function
var FlickrCommentWhen = function() { // {{{
// specific key
var apiKey = 'dd9ab53f739f011e7d2031ccd0eaef8e';
// extra check, avoid extra work when includes/excludes are bad.
if (!correctPage()) {
return;
}
//alert('FlickrCommentsWhen running...');
// otherwise do it.
var photoId = getPhotoId();
var setId = getSetId();
if (setId !== false && hasCommentNodes()) {
//alert('doing a set: ' + setId);
processComments(apiKey, setId, 'flickr.photosets.comments.getList');
} else if (photoId !== false && hasCommentNodes()) {
//alert('doing a photo: ' + photoId);
processComments(apiKey, photoId, 'flickr.photos.comments.getList');
}
return;
} // }}}
// function test for correct page
var correctPage = function() { // {{{
var host = location.hostname;
var path = location.pathname;
// quickly check host first, in case includes are foobar.
if (!host.match('flickr')) {
return false;
}
// photo page test.
if (path.match('photos\/[^\/]+\/[0-9]+\/?')) {
return true;
}
// set comments page test.
if (path.match('photos\/[^\/]+\/sets\/[0-9]+\/comments\/?')) {
return true;
}
// otherwise no good.
return false;
} // }}}
// function for get photo-id
var getPhotoId = function() { // {{{
if (location.pathname.match('photos\/[^\/]+\/[0-9]+\/?')) {
return location.pathname.split('/')[3];
} else {
return false;
}
} // }}}
// function get set-id
var getSetId = function() { // {{{
if (location.pathname.match('photos\/[^\/]+\/sets\/[0-9]+\/comments\/?')) {
return location.pathname.split('/')[4];
} else {
return false;
}
} // }}}
// function test for comments.
var hasCommentNodes = function() { // {{{
var test = false;
var xp = 'count(//td[@class="Comment"]|//div[@class="comment-block"])';
var xpr = document.evaluate( xp, document, null, XPathResult.ANY_TYPE, null);
if (xpr.numberValue >= 1) {
test = true;
}
//alert('page has comments test: ' + xpr.numberValue);
return test;
} // }}}
// do the work.
var processComments = function(key, id, method) { // {{{
// storage arrays for date/id lookup
var ids = new Array();
var meta = new Array();
// api request onload function
var getCommentsListResponse = function(response) {
var data = eval('(' + response.responseText + ')');
var comments = data.comments.comment;
for (var i = 0; i < comments.length; i++) {
// store the part of the id used for the permalink only.
// store unix timestamp
var com = comments[i];
// hacky bit to get around different attrib names returned by flickr.
var datecreate = false;
if (com['datecreate']) datecreate = com['datecreate'];
if (com['date_create']) datecreate = com['date_create'];
// store the ids in lookup array.
ids[ids.length] = com['id'].split('-')[2];
// store comment meta bits.
meta[meta.length] = new Array(datecreate, com['author']);
}
// proceed to modifying the page.
if (comments.length >= 1) {
upgradeComments(ids.slice(),meta.slice()); // pass by value
}
}
// api request
GM_xmlhttpRequest({
method: 'GET'
, url: 'http://api.flickr.com/services/rest/'
+ '?method=' + method
+ '&format=json&nojsoncallback=1'
+ '&api_key=' + key
+ '&photo_id=' + id // only one will be valid.
+ '&photoset_id= ' + id // only one will be valid.
, onload: getCommentsListResponse
});
return;
} // }}}
// function for processing the comments with new dates.
var upgradeComments = function(ids,meta) { // {{{
var nodes = getNodes();
// for each node found.
for (i = 0; i < nodes.length; i++) {
var id = null;
// locate the comment permalink (in order to get id)
xp = 'a[contains(@href, "/comment") or contains(@href, "/#comment")]';
xpr = document.evaluate(xp, nodes[i], null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
if (xpr.snapshotLength != 1) return false; // whoops?
// dig out the comment id (or the part used for permalinks).
var re = new RegExp('\/#?comment([0-9]+)\/?');
var match = re.exec(xpr.snapshotItem(0).getAttribute('href'));
if (match[1]) id = match[1];
// definitely a better way to do the date lookup, just tired.
var index = null;
for (j = 0; j < ids.length; j++) {
if (ids[j] == id) {
index = j;
}
}
var dateObj = new Date(meta[index][0] * 1000);
var archiveUrl = '/photos/' + meta[index][1]
+ '/archives/date-posted/'
+ dateObj.getFullYear() + '/'
+ pad(dateObj.getMonth() + 1, 2) + '/'
+ pad(dateObj.getDate(), 2) + '/';
//alert(archiveUrl);
var txt = document.createElement('span');
txt.setAttribute('class', 'commentWhen');
var link = document.createElement('a');
link.setAttribute('href', archiveUrl);
link.setAttribute('style', 'text-decoration:none; color:inherit; background-color:inherit;');
link.appendChild(document.createTextNode(dateFormat(dateObj)));
txt.appendChild(link);
nodes[i].appendChild(document.createElement('br'));
nodes[i].appendChild(txt);
}
return;
} // }}}
// function for formatting the date
var dateFormat = function(dateObj) { // {{{
return dateObj.toLocaleString();
} // }}}
// function for getting the comment nodes script will act upon
var getNodes = function() { // {{{
var nodes = new Array();
// note: photosets use table layout, very flexible XPATH, might break.
// note: group invites have different html layout.
var xp = '//td[@class="Comment"]//small|//div[@class="comment-block"]//small';
var xpr = document.evaluate( xp, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (i = 0; i < xpr.snapshotLength; i++) {
nodes[i] = xpr.snapshotItem(i);
}
//alert('found comment nodes: ' + nodes.length);
return nodes;
} // }}}
// number padding function
var pad = function(number, length) { // {{{
var str = '' + number;
while (str.length < length) {
str = '0' + str;
}
return str;
} // }}}