There are 4 previous versions of this script.
// Flickr Search Tagging
// (c) Dariusz Grabka 2008
// --------------------------------------------------------------------
//
// This is a Greasemonkey user script.
//
// It allows the user to propose tags to the authors of other images.
// It makes it a little easier by keeping around your last 1 - 4 queries,
// just in case you forgot what you were looking for :)
//
// This is part of a Master's thesis. All rights are strictly reserved.
//
// 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.
//
// To uninstall, go to Tools/Manage User Scripts,
// select "Flickr Searching Tagging", and click Uninstall.
//
// --------------------------------------------------------------------
//
// ==UserScript==
// @name Flickr Search Tagging
// @namespace http://grabka.org/projects/searchtagging/
// @description Tag anyone's photos, with a little help from your search terms.
// @include http://*.flickr.com/*
// @include http://flickr.com/*
// ==/UserScript==
// translate-able text
var t = new Object(); t.en = new Object();
t.en.tags_title = "Proposed Tags";
t.en.searchtags_title = "Recent Searches";
t.en.duplicate_error = "Looks like one of the tags you proposed is a duplicate.";
t.en.spam_error = "Oops, you've exceeded the number of tags you can add in one day. Try again tomorrow :).";
t.en.generic_error = "Oops, an unexpected error occurred.";
// set the language
var lang = t.en;
// the url which is called for AJAX stuff
var fst_ajax_url = "http://grabka.org/projects/searchtagging/";
/*
===============
START FUNCTIONS
===============
*/
// returns true if the current user can tag the photo being viewed
function canTag () {
// for the experiment, this is always false;
return false;
// ----
if (document.getElementById("addtagbox")) {
return true;
} else {
return false;
}
}
// returns at tag text link for the given tag text
// will add a delete and a [use] link if the user can tag the photo
function buildTagLink(txt, can_tag) {
var etxt = escape(txt.replace(" ", "", "g"));
var tag = "<a href=\"/photos/tags/" + etxt + "/\" title=\"Click this icon to see other photos and videos tagged with "+ txt +"\" class=\"globe\" onMouseOver=\"this.childNodes[0].src='http://l.yimg.com/www.flickr.com/images/icon_globe_over.gif';\" onMouseOut=\"this.childNodes[0].src='http://l.yimg.com/www.flickr.com/images/icon_globe.gif';\"><img src=\"http://l.yimg.com/www.flickr.com/images/icon_globe.gif\" width=\"16\" height=\"16\" class=\"icon\" alt=\"Click this icon to see all public photos and videos tagged with " + txt + "\" /></a> <a href=\"" + user + "tags/" + etxt +"/\" class=\"Plain\">" + txt + "</a>";
if (can_tag) {
tag = tag + " <a href=\"#\" onClick=\"javascript:proposeTerm(\'"+txt+"\'); return false;\" class=\"Grey\">[use]</a> <a href=\"" + fst_ajax_url + "delete/" + p_id + "/" + etxt + "\" title=\"Delete this tag?\" class=\"Grey\">[x]</a>";
}
return tag;
}
// gup - gets the value of a variable from the URL
// courtesy of http://www.netlobo.com/url_query_string_javascript.html
function gup( name ) {
name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
var regexS = "[\\?&]"+name+"=([^&#]*)";
var regex = new RegExp( regexS );
var results = regex.exec( window.location.href );
if( results == null )
return "";
else
return results[1];
}
// insertAfter .. I can't believe this doesn't exist in javascript
function insertAfter(new_node, existing_node) {
// if the existing node has a following sibling, insert the current
// node before it. otherwise appending it to the parent nodeis_
// will correctly place it just after the existing node.
if (existing_node.nextSibling) {
// there is a next sibling. insert before it using the mutual
// parent's insertBefore() method.
existing_node.parentNode.insertBefore(new_node, existing_node.nextSibling);
} else {
// there is no next sibling. append to the end of the parent's
// node list.
existing_node.parentNode.appendChild(new_node);
}
} // insertAfter()
// build the display for the search tags
function buildSearchTags() {
var str = "<style>.fst_query1 a { font-size: 1.5em; text-decoration: none; } .fst_query2 a { font-size: 1.2em; text-decoration: none; } .fst_query3 a { font-size: 1.0em; text-decoration: none; } .fst_query4 a { font-size: 0.8em; text-decoration: none; }</style>";
var term;
var terms = GM_getValue("query1", "");
// if there is nothing in the first query string, then there is nothing to display ... probably :)
if (terms.length < 1)
{
return "<div>No recent searches.</div>";
}
var terms_array = terms.split(",");
for each (term in terms_array) {
str = str + "<span class=\"fst_query1\"><a href=\"#\" onClick=\"javascript:proposeTerm(\'" + term + "\'); return false;\">" + term + "</a></span> ";
}
terms = GM_getValue("query2", "");
terms_array = terms.split(",");
for each (term in terms_array) {
str = str + "<span class=\"fst_query2\"><a href=\"#\" onClick=\"javascript:proposeTerm(\'" + term + "\'); return false;\">" + term + "</a></span> ";
}
terms = GM_getValue("query3", "");
terms_array = terms.split(",");
for each (term in terms_array) {
str = str + "<span class=\"fst_query3\"><a href=\"#\" onClick=\"javascript:proposeTerm(\'" + term + "\'); return false;\">" + term + "</a></span> ";
}
terms = GM_getValue("query4", "");
terms_array = terms.split(",");
for each (term in terms_array) {
str = str + "<span class=\"fst_query4\"><a href=\"#\" onClick=\"javascript:proposeTerm(\'" + term + "\'); return false;\">" + term + "</a></span> ";
}
return str;
}
// build the form that has the "propose" text field and button in it
function buildProposalForm() {
var form = "<form action=\"\" method=\"get\" id=\"proposedtagadderform\" name=\"proposedtagadderform\"><input onblur=\"\" type=\"text\" name=\"pt\" style=\"margin-bottom: 0px; width:150px\" id=\"proposetagbox\" value=\"\"><input type=\"submit\" class=\"SmallButt\" value=\"PROPOSE\"></form>";
return form;
}
// standardises a raw query from a URL, turns it into a string
function decodeQuery(q) {
// decode the query, change it lower case
q = decodeURI(q);
q = q.toLowerCase();
// terms should be separated with +'s
q = q.replace(" ", "+", "g");
// find things that are quoted, treat them as one word
// store them in quoted_tags and nuke 'em
var quoted_tags = q.match(/\"[\.\:\%a-z0-9\-\+]+\"/g);
q = q.replace(/\"[\.\:\%a-z0-9\-\+]+\"/g, "", "g");
// now get the rest of the tags, the ones without quotation marks
var simple_tags = q.match(/[\.\:\%a-z0-9\-]+/g);
// store them in a new tag array
var tag_array = new Array();
var tag;
for each (tag in simple_tags) {
tag_array.push(tag);
}
for each (tag in quoted_tags) {
var x = tag.replace('"', "", 'g'); // strip the quotations marks, don't need them anymore
tag_array.push(x);
}
tag_array.sort(); // sort the array :)
q = tag_array.toString(); // and make it a string!
return q;
}
// function to store the query in one of the four storage storage spots in the browser
function storeQuery(q) {
q = decodeQuery(q);
GM_log("FST - storing " + q);
var q1 = GM_getValue("query1");
if (q1 == q) return;
var q2 = GM_getValue("query2");
if (q2 == q) {
GM_setValue("query1", q);
GM_setValue("query2", q1);
return;
}
var q3 = GM_getValue("query3");
if (q3 == q) {
GM_setValue("query1", q);
GM_setValue("query2", q1);
GM_setValue("query3", q2);
return;
}
GM_setValue("query1", q);
GM_setValue("query2", q1);
GM_setValue("query3", q2);
GM_setValue("query4", q3);
}
// a rough sleep function
// courtesy of http://www.phpied.com/sleep-in-javascript/
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
/*
================
END OF FUNCTIONS
================
*/
// get the photo ID, the user id, owner id if any
var p_id = unsafeWindow.page_photo_id;
var user_id = unsafeWindow.global_nsid;
var owner_id;
if (unsafeWindow.page_p)
{
var owner_url = unsafeWindow.page_p.ownersUrl;
var url_array = owner_url.split("/");
var owner_id = url_array[2];
}
if (!user_id) user_id = "0";
if (!owner_id) owner_id = "0";
// create the div container for the whole thing
var fst = document.createElement("div");
fst.id = "fst";
// deal with the incoming tag proposals, if any
var proposing = /\?pt\=/.test(document.URL);
var proposing_done = false;
if (proposing) {
// make the URL for the proposal
// get the tags
var ptags = (gup("pt"));
ptags = decodeQuery(ptags);
if (ptags.length > 0) {
// get all of the search keywords
var allkeywords = GM_getValue("query1", "") + ";" + GM_getValue("query2", "") + ";" + GM_getValue("query3", "") + ";" + GM_getValue("query4", "");
// build the URL
var get_url = fst_ajax_url + "add/" + p_id + "/" + user_id + "/" + ptags + "/" + owner_id + "/" + allkeywords;
GM_xmlhttpRequest({
method: 'GET',
url: get_url,
headers: {
'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
'Accept': 'application/json',
},
onload: function(responseDetails) {
// once we get here, proposing is "done" on the server side
proposing_done = true;
// if the response from the server was ok ..
if (responseDetails.status != 200) { GM_log(responseDetails); return; }
// get the tags out of the response text
var feedback = eval('(' + responseDetails.responseText + ')');
// make sure the status returned OK
if (feedback.status != "ok") {
var err = document.createElement("div");
err.id = "error";
err.className = "Problem_small";
if (/Duplicate\ entry/.test(feedback.error)) {
// we could have a duplicate tag being proposed ...
GM_log("Duplicate tag error");
err.innerHTML = lang.duplicate_error;
// not showing the duplicate tag error .. obvious and annoying
// fst.parentNode.insertBefore(err, fst);
} else if (/Exceeded\ daily/.test(feedback.error)) {
// or a spam problem ...
GM_log("Daily tag limit: " + feedback.error);
err.innerHTML = lang.spam_error;
// show this error to the user
fst.parentNode.insertBefore(err, fst);
} else {
// report any other error
GM_log("Tag proposal error: " + feedback.error);
err.innerHTML = lang.generic_error;
}
err.style.marginTop = "10px";
}
}
});
} // if length of ptags
}
// if this is stuff true, we're on a photo page (probably)
var photo_page = /\/photos\//.test(document.URL);
if (p_id && photo_page) {
// user, ownership, etc. from the page
var user = unsafeWindow.page_p.ownersUrl;
// get the tags for the current image
// that url should return JSON formatted proposed tags for the given photo id
var get_url = fst_ajax_url + "get/" + p_id;
// the proposed tags container
var proposedTags = document.createElement("div");
proposedTags.id = "proposedTags";
// the proposed tags title
var proposedTagsTitle = document.createElement("h4");
proposedTagsTitle.id = "proposedTagsTitle";
proposedTagsTitle.innerHTML = lang.tags_title;
// add the title as the first thing
proposedTags.insertBefore(proposedTagsTitle, proposedTags.firstChild);
// make the ajax call to get the already propsed tags
GM_xmlhttpRequest({
method: 'GET',
url: get_url,
headers: {
'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
'Accept': 'application/json',
},
onload: function(responseDetails) {
if (proposing)
{
sleep(500);
}
// if the response from the server was ok ..
if (responseDetails.status != 200) return;
// get the tags out of the response text
var photo_tags = eval('(' + responseDetails.responseText + ')');
// make sure the status returned OK
if (photo_tags.status != "ok") return;
if (photo_tags.count > 0) {
// insert the tags after the H4
var insertHere = proposedTagsTitle;
for (var i = 0; i < photo_tags.count; i++) {
// create a new div element
var tag = document.createElement("div");
tag.id = "fsttag" + i;
tag.innerHTML = buildTagLink(photo_tags.tags[i].text, canTag());
// append it
insertAfter(tag, insertHere);
insertHere = tag;
}
}
}
});
// add the proposed tags to the FST block
fst.insertBefore(proposedTags, fst.firstChild);
// the form for adding proposed tags
var proposalForm = document.createElement("div");
proposalForm.id = "proposalForm";
proposalForm.innerHTML = buildProposalForm();
proposalForm.style.marginTop = "10px";
insertAfter(proposalForm, proposedTags);
// tags from searches
var searchTags = document.createElement("div");
searchTags.id = "searchTags";
// the title for the search tags
var searchTagsTitle = document.createElement("h4");
searchTagsTitle.id = "searchTagsTitle";
searchTagsTitle.innerHTML = lang.searchtags_title;
// add the title as the first thing
searchTags.insertBefore(searchTagsTitle, searchTags.firstChild);
// append the actual tags
var searchTagList = document.createElement("div");
searchTagList.id = "searchTagList";
searchTagList.style.marginLeft = "20px";
searchTagList.innerHTML = buildSearchTags();
insertAfter(searchTagList, searchTagsTitle);
// add the whole thing after the proposal form
insertAfter(searchTags, proposalForm);
// some stuff changes if you can tag a photo
if (canTag()) {
proposalForm.style.display = "none";
unsafeWindow.tagrs_showForm();
var boxName = "addtagbox";
} else {
var boxName = "proposetagbox";
}
// add some scripts that we'll need
var scripts = document.createElement("div");
scripts.innerHTML = "<script>function proposeTerm (term) { var inputBox = document.getElementById(\"" + boxName + "\"); inputBox.value = inputBox.value + \" \" + \'\"\' + term + \'\"\'; }</script>";
insertAfter(scripts, proposalForm);
} // end of "if photo page"
// if there is a q= and a /search/ in the URL, it's probably the result of a search query
var q_matches = /q=/.test(document.URL);
var search_matches = /\/search\//.test(document.URL);
if (q_matches && search_matches) {
// get the value of q variables, and unescape them
var query = (gup("q"));
storeQuery(query);
}
// figure out where to insert this mess we've created
var insertHere = document.getElementById("tagadder");
if (!insertHere) insertHere = document.getElementById("thetags");
if (!insertHere) insertHere = document.getElementById("moderation");
// display the final output here
if (insertHere) { insertAfter(fst, insertHere); }
