// UFIS! Universal Flickr Image Search
// version 001 ALPHA
// no copyright
// done by lscherff
// --------------------------------------------------------------------
//
// 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.
//
// To uninstall, go to Extras/Manage User Scripts,
// select "UFIS!", and click Uninstall.
//
// --------------------------------------------------------------------
//
// ==UserScript==
// @name          UFIS!
// @namespace     http://www.khm.de/~lscherff/ufis
// @description   Add a button to enable instant flickr CBIR and tag search for any JPG you come across.
// @include       *
// @exclude       http://www.khm.de/~lscherff/ufis/*
// ==/UserScript==
//
// --------------------------------------------------------------------
//
// To turn the script on/off click: Extras/User Script Commands/Toggle UFIS!
// 
// --------------------------------------------------------------------
//
// Version: 001 ALPHA
// Todo:
// - Use another CBIR system (one with more diverse image data and HTTP API)
// - Eliminate common words using some wordnet interface (http://wordnet.princeton.edu/links#web)
// - Button-images are not placed correctly over images that float right/center 

var flicks;
var retrs;
var gifts;
var flickrTags;
var currentURI;
var yOffset, xOffset;
var viewFIS;
var tmpView;
var drag;
var dragX;
var dragY;

var imgStyle = 'margin: 2px; border: none; float: none; ';
var imgDivStyle = 'font-size: 12px; color: red; border: 5px solid green; ';
var blaStyle = 'position: absolute; bottom: 5px; left: 2px; border: solid black 1px; padding: 0px; margin: 0px; width: 10px; height: 10px; ';

function done() {
	if (tmpView) document.body.removeChild(tmpView);
	tmpView = null;
	var srcImg = document.createElement('img'); srcImg.src = currentURI;
	var width = srcImg.width + 488; if (width==448) width = 468;	// middle image determines total width
	var x = xOffset - width/2; if (x<20) x = 20;
	var y = yOffset-100; if (y<0) y = 0;
	if (viewFIS) {
		x = parseInt(viewFIS.style.left);
		y = parseInt(viewFIS.style.top);
		document.body.removeChild(viewFIS);
	}
	viewFIS = document.createElement('div');
	viewFIS.style.position = 'absolute';
	viewFIS.style.zIndex = '1001';
	
	viewFIS.style.width = width+'px';
	viewFIS.style.top = y+'px';
	viewFIS.style.left = x+'px';
	viewFIS.style.backgroundColor = 'white';
	viewFIS.style.border = 'solid black 1px';
	viewFIS.style.color = '#505050';
	viewFIS.style.fontFamily = 'Helvetica,Arial,sans-serif !important';
	viewFIS.style.fontSize = '10pt !important';
	viewFIS.style.textAlign = 'left';
	viewFIS.addEventListener('mousedown', function(event) {
		drag = true;
		dragX = event.pageX;
		dragY = event.pageY;
	}, true);
	viewFIS.addEventListener('mouseup', function(event) {
		if (drag) drag = false;
	}, true);
	viewFIS.addEventListener('mousemove', function(event) {
		if (drag) {
			viewFIS.style.left = (parseInt(viewFIS.style.left)+(-dragX+event.pageX)) + 'px';
			viewFIS.style.top = (parseInt(viewFIS.style.top)+(-dragY+event.pageY)) + 'px';
			dragX = event.pageX;
			dragY = event.pageY;
		}
	}, true);
	document.body.appendChild(viewFIS);
	
	viewFIS.innerHTML = '<h1 style="font-family: Times New Roman; font-size: 14pt; padding: 4px; "><a href="http://www.khm.de/~lscherff/ufis" target="_blank" style="text-decoration: none; color: #c0c0c0; font-family: Times New Roman;">UFIS!</a> is giving you some <a href="http://www.khm.de/~lscherff/ufis" target="_blank" style="text-decoration: none; color: #c0c0c0; font-family: Times New Roman; ">similar</a> images.</h1>';
	
	var retDiv = document.createElement('div');
	if (retrs.firstChild) {
		var p = document.createElement('p');
		p.innerHTML = '...via <a href="http://labs.systemone.at/retrievr" target="_blank" style="text-decoration: none; color: #c0c0c0; font-family: Helvetica,Arial,sans-serif; ">retrievr</a>';
		retDiv.appendChild(p);
		insertImages(retDiv,0,retrs,6);
	} else retDiv.innerHTML += '<p style="margin: 2px;"><a href="http://labs.systemone.at/retrievr/" target="_blank" style="text-decoration: none; color: #c0c0c0; font-family: Helvetica,Arial,sans-serif; ">retrievr</a> did not find any images</p>';
	retDiv.style.cssFloat = 'left';
	retDiv.style.textAlign = 'right';
	retDiv.style.verticalAlign = 'top';
	retDiv.style.width = '240px';
	viewFIS.appendChild(retDiv);
	
	var flickDiv = document.createElement('div');
	if (flicks.firstChild) {
		var p = document.createElement('p');
		var ft = flickrTags.split(','); var fs = '';
		for (var i=0; i<ft.length; i++) { fs += '<a href="http://www.flickr.com/photos/tags/'+ft[i]+'" target="_blank" style="text-decoration: none; color: #c0c0c0; font-family: Helvetica,Arial,sans-serif; ">'+ft[i]+'</a>'; if (i<ft.length-1) fs += ', '; }
		//p.innerHTML = '...via <a href="http://flickr.com/search/" target="_blank" style="text-decoration: none; color: #c0c0c0; font-family: Helvetica,Arial,sans-serif; ">flickr tag search</a> (using tags '+flickrTags.replace(/,/g,', ')+')'
		p.innerHTML = '...via <a href="http://flickr.com/search/" target="_blank" style="text-decoration: none; color: #c0c0c0; font-family: Helvetica,Arial,sans-serif; ">flickr tag search</a> (using tags '+fs+')'
		flickDiv.appendChild(p);
		insertImages(flickDiv,0,flicks,6);
	} else 	flickDiv.innerHTML += '<p style="margin: 2px;"><a href="http://www.flickr.com/photos/search/" target="_blank" style="text-decoration: none; color: #c0c0c0; font-family: Helvetica,Arial,sans-serif; font-size: 10pt; ">flickr tag search</a> did not find any images for tag(s) '+flickrTags.replace(/,/g,', ')+'</p>';
	flickDiv.style.cssFloat = 'right';
	flickDiv.style.verticalAlign = 'top';
	flickDiv.style.width = '240px';
	viewFIS.appendChild(flickDiv);
	
	var middleDiv = document.createElement('div');
	middleDiv.style.margin = '0 240px 0 240px';
	middleDiv.style.cssFloat = 'none';
	srcImg.style.margin = '4px';
	middleDiv.appendChild(srcImg);
	viewFIS.appendChild(middleDiv);
	
	var giftDiv = document.createElement('div');
	giftDiv.style.clear = 'both';
	giftDiv.style.top = '20px';
	if (gifts.firstChild) {
		var p = document.createElement('p');
		p.innerHTML = '...via <a href="http://www.khm.de/~lscherff/ufis/aMGHA.html" target="_blank" style="text-decoration: none; color: #c0c0c0; font-family: Helvetica,Arial,sans-serif; ">the GIFT</a>';
		giftDiv.appendChild(p);
		insertImages(giftDiv,0,gifts,10);
	} else giftDiv.innerHTML += '<p style="margin: 2px;"><a href="http://www.khm.de/~lscherff/ufis/aMGHA.html" target="_blank" style="text-decoration: none; color: #c0c0c0; font-family: Helvetica,Arial,sans-serif; ">the GIFT</a> did not find any images</p>';
	viewFIS.appendChild(giftDiv);
	
	var closeFIS = document.createElement('img');
	with (closeFIS.style) {
		position = 'absolute';
		top = '4px';
		right = '4px';
		border = 'solid black 1px';
	}
	closeFIS.src = 'http://www.khm.de/~lscherff/ufis/close.gif';
	closeFIS.addEventListener('click', function(event) {
		document.body.removeChild(viewFIS);
		viewFIS = null;
		drag = false;
		event.stopPropagation();
		event.preventDefault();
	}, true);
	viewFIS.appendChild(closeFIS);
}

function insertImages(targetDiv,from,sourceDiv,nr) {
	var child = targetDiv.firstChild;
	while (child.nextSibling) { var next = child.nextSibling; targetDiv.removeChild(child); child = next; }
	if (from>0) {
		var lesser = document.createElement('p');
		var lessLnk = document.createElement('a');
		var lessFrom = from-nr; if (lessFrom<0) lessfrom = 0;
		lessLnk.appendChild(document.createTextNode((lessFrom+1)+' - '+(lessFrom+nr)));
		lessLnk.addEventListener('click', function(event) {
			insertImages(targetDiv,lessFrom,sourceDiv,nr);
			event.stopPropagation();
			event.preventDefault();
		}, true);
		lesser.appendChild(lessLnk); 
		targetDiv.insertBefore(lesser,child);
	}
	var stop = from+nr;
	for (var i=from; i<stop&&i<sourceDiv.childNodes.length; i++) {
		var il = sourceDiv.childNodes[i].cloneNode(true);
		il.replaceChild(createContainer(il.firstChild.cloneNode(true)), il.firstChild);
		targetDiv.insertBefore(il, child);
	}
	var moreFrom = from+nr;
	if 	(moreFrom<sourceDiv.childNodes.length) {
		var morer = document.createElement('p');
		var moreLnk = document.createElement('a');
		var moreTo = moreFrom+nr; if (moreTo>=sourceDiv.childNodes.length) moreTo = sourceDiv.childNodes.length;
		moreLnk.appendChild(document.createTextNode('next '+(moreFrom+1)+' - '+moreTo+' (of '+sourceDiv.childNodes.length+')'));
		moreLnk.addEventListener('click', function(event) {
			insertImages(targetDiv,moreFrom,sourceDiv,nr);
			event.stopPropagation();
			event.preventDefault();
		}, true);
		morer.appendChild(moreLnk); 
		targetDiv.insertBefore(morer,child);
	}
}

function parseFlickrResponse(responseDetails) {
	GM_log('PFR');
	var text = responseDetails.responseText;
	var parser = new DOMParser();
	var responseDOM = parser.parseFromString(responseDetails.responseText, "application/xml");
	flicks = document.createElement('div');
	flicks.setAttribute('style',imgDivStyle);
	var photos = responseDOM.getElementsByTagName('photo');
	for (var i=0; i<photos.length; i++) {
		var imgLink = document.createElement('a');
		imgLink.href = 'http://www.flickr.com/photos/'+photos[i].getAttribute('owner')+'/'+photos[i].getAttribute('id');
		imgLink.target = '_blank';
		var img = document.createElement('img');
		img.src = 'http://static.flickr.com/'+photos[i].getAttribute('server')+'/'+photos[i].getAttribute('id')+'_'+photos[i].getAttribute('secret')+'_t.jpg';
		img.setAttribute('style', imgStyle);
		img.setAttribute('flickrId',photos[i].getAttribute('id'));
		imgLink.appendChild(img);
		flicks.appendChild(imgLink);
	}
	if (retrs!=null && gifts!=null) done();
}

function parseGIFTResponse(responseDetails) {
	GM_log('PGR');
	var parser = new DOMParser();
	gifts = document.createElement('div');
	gifts.setAttribute('style', imgDivStyle);
	var responseDOM = parser.parseFromString(responseDetails.responseText, "application/xml");
	if (responseDOM.getElementsByTagName('error').length>0) {
		gifts.appendChild(document.createTextNode('GIFT error: '+responseDOM.getElementsByTagName('error')[0].getAttribute('message')+'!'));
	} else {
		var results = responseDOM.getElementsByTagName('query-result-element');
		if (results.length!=0) {
			for (var i=0; i<results.length; i++) {
				var imgLink = document.createElement('a');
				imgLink.target = '_blank';
				var img = document.createElement('img');
				img.src = results[i].getAttribute('thumbnail-location');
				img.setAttribute('style', 'margin: 2px; border: none; float: none; ');
				var photoId = results[i].getAttribute('image-location').match(/\/([^_^\/]*)_/)[1];
				var secret = results[i].getAttribute('image-location').match(/_([^_]*)_/)[1];
				img.setAttribute('flickrId', photoId);
				imgLink.appendChild(img);
				gifts.appendChild(imgLink);
				GM_xmlhttpRequest({
					method: 'GET',
					url: 'http://www.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=86e1a4a541d4c0442c4a37592780d14f&photo_id='+photoId+'&secret='+secret,
					headers: {
						'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey/0.3',
						'Accept': 'application/xhtml+xml',
					},
					onload: function(responseDetails) {
						var parser = new DOMParser();
						var responseDOM = parser.parseFromString(responseDetails.responseText, "application/xml");
						var id = responseDOM.getElementsByTagName('photo')[0].getAttribute('id');
						var URL = 'http://www.flickr.com/photos/'+responseDOM.getElementsByTagName('owner')[0].getAttribute('nsid')+'/'+id;
						var imgLink = gifts.firstChild; var found = false;
						var i = 0;
						while (imgLink!=null && !found) {
							if (imgLink.firstChild) if (found = (imgLink.firstChild.getAttribute('flickrId')==id)) imgLink.href = URL;
							imgLink = imgLink.nextSibling;
						} 
					}
				});
			}
		}
	}
	if (retrs!=null && flicks!=null) done();
}

function parseRetrievrImages(responseDetails) {
	GM_log('PRI');
	GM_setValue('flickr',false);
	var html = responseDetails.responseText;
	var imgTags = html.match(/<a href[^<]*<img[^>]*><\/a>/g);
	retrs = document.createElement('div');
	retrs.setAttribute('style', imgDivStyle);
	for (var i=0; i<imgTags.length; i++) {
		var pattern = /<a href=\\"([^\\]*)[^<]*<img src=\\"([^\\]*)\\".*/;
		pattern.exec(imgTags[i]);
		var aHref = RegExp.$1;
		var imgSrc = RegExp.$2;
		if (imgSrc.indexOf('http://')>-1 && aHref.indexOf('http://'>-1)) {
			var imgLink = document.createElement('a');
			imgLink.href = aHref;
			var img = document.createElement('img');
			img.src = imgSrc;
			img.setAttribute('style', imgStyle);
			img.setAttribute('flickrId', imgSrc.match(/\/([^_^\/]*)_/)[1]);
			imgLink.appendChild(img);
			retrs.appendChild(imgLink);
		}
	}
	//if (retrs.firstChild) retrs.removeChild(retrs.firstChild);	// don't ask me why \todo check!
	if (flicks!=null && gifts!=null) done();
}

function parseRetrievrResponse(responseDetails) {
	GM_log('PRR');
	GM_setValue('retrievr',false);
	var html = responseDetails.responseText;
	var iframeTag = html.match(/<iframe .*>/g);
	var pattern = /(src=")([^"]*)(".*)/;
	pattern.exec(iframeTag);
	var src = RegExp.$2;
	src = 'http://labs.systemone.at' + src;
	GM_xmlhttpRequest({
		method: 'GET',
		url: src,
		headers: {
			'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey/0.3',
			'Accept': 'application/xhtml+xml',
		},
		onload: parseRetrievrImages
	});
}

function retrieveImages(img) {
    if (tmpView) document.body.removeChild(tmpView);
	tmpView = null;
	tmpView = document.createElement('div');
	tmpView.style.padding = '2px';
	tmpView.style.position = 'absolute';
	tmpView.style.backgroundColor = 'white';
	tmpView.style.border = 'solid black 1px';
	tmpView.style.top = yOffset-10+'px';
	tmpView.style.left = xOffset-40+'px';
	var tp = document.createElement('span');
	tp.style.paddingRight = '30px';
	tp.appendChild(document.createTextNode("UFIS! is searching... "));
	tmpView.appendChild(tp);
	var abort = document.createElement('img');
	abort.style.position = 'absolute';
	abort.style.top = '2px';
	abort.style.right = '2px';
	abort.style.border = 'solid black 1px';
	abort.src = 'http://www.khm.de/~lscherff/ufis/close.gif';
	abort.addEventListener('click', function(event) {
		document.body.removeChild(tmpView);
		tmpView = null;
		flicks = null; retrs = null; gifts = null;
		flickrTags = '';
		event.stopPropagation();
		event.preventDefault();
	}, true);
	tmpView.appendChild(abort);
	document.body.appendChild(tmpView);

	flicks = null; retrs = null; gifts = null;
	flickrTags = '';
	drag = false;
	currentURI = img.src;
	
	// get from retrievr
	GM_xmlhttpRequest({
		method: 'GET',
		url: 'http://labs.systemone.at/retrievr/?url=' + img.src,
		headers: {
			'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey/0.3',
			'Accept': 'application/xhtml+xml',
		},
		onload: parseRetrievrResponse
	});
	// is URI a flickr image?
	if (img.src.indexOf('http://static.flickr.com')>-1) {
		var flickrId = img.getAttribute('flickrId');
		if (flickrId=='') {
			var pattern = /(static.flickr.com\/[^\/]*\/)([^_]*)/;
			pattern.exec(URI);
			var flickrId = RegExp.$2;
		}
		if (flickrId!='') {
			GM_xmlhttpRequest({
				method: 'GET',
				url: 'http://www.flickr.com/services/rest/?method=flickr.tags.getListPhoto&api_key=86e1a4a541d4c0442c4a37592780d14f&photo_id='+flickrId,
				headers: {
					'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey/0.3',
					'Accept': 'application/xhtml+xml',
				},
				onload: function(responseDetails) {
					var parser = new DOMParser();
					var responseDOM = parser.parseFromString(responseDetails.responseText, "application/xml");
					var tags = responseDOM.getElementsByTagName('tag');
					for (var i=0; i<tags.length; i++) {
						flickrTags += tags[i].firstChild.nodeValue;
						if (i<tags.length-1) flickrTags+=',';
					}
					getFromFlickr();
				}
			});
		}
	} else {
		if (flickrTags=='') {
			alt = img.getAttribute('alt');
			if (alt == '' || alt == null) {
				// use page title
				var title = document.getElementsByTagName("title");
				if (title.length>0) {
					alt = title[0].text;
					alt = alt.replace(/ /g,',');
				} 
				if (alt == '' || alt == 'null') {
					// use filename
					alt = img.src.match(/\/[^\/]*\.jpg/)[0];
					alt = alt.substring(1,alt.length-4);
					alt = alt.replace(/ /g,',');
				}
			} else alt = alt.replace(/ /g,',');
			flickrTags = alt;
			getFromFlickr();
		}
	}
	GM_xmlhttpRequest({
		method: 'GET',
		url: 'http://www.khm.de/~lscherff/GIFT-API/?results=25&examples='+img.src+'&relevances=1.0',
		headers: {
			'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey/0.3',
			'Accept': 'application/xhtml+xml',
		},
		onload: parseGIFTResponse
	});
}

function getFromFlickr() {
	var flickrURI = 'http://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=86e1a4a541d4c0442c4a37592780d14f&per_page=25&page=1&tags='+flickrTags;
	GM_xmlhttpRequest({
		method: 'GET',
		url: flickrURI,
		headers: {
			'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey/0.3',
			'Accept': 'application/xhtml+xml',
		},
		onload: parseFlickrResponse
	});
}

function createContainer(img) {
	var ct = document.createElement('span');
	ct.style.position = 'relative';
	var blaImage = document.createElement('img');
	blaImage.setAttribute('src', 'http://www.khm.de/~lscherff/ufis/ufis.gif');
	blaImage.setAttribute('width','10');
	blaImage.setAttribute('height','10');
	blaImage.setAttribute('alt', 'find similar images for: '+img.getAttribute('alt'));
	blaImage.setAttribute('style', blaStyle);
	blaImage.addEventListener('click', function(event) {
		yOffset = event.pageY;
		xOffset = event.pageX;
		retrieveImages(img);
		event.stopPropagation();
		event.preventDefault();
	}, true);
	ct.appendChild(img);
	ct.appendChild(blaImage);
	return ct;
}

function addFlink() {
	var imgs = document.getElementsByTagName('img');
	for (var i=0; i<imgs.length; ) {
		var image = imgs[i];
		if (image.getAttribute('src') && image.getAttribute('src').indexOf('.gif')<0 && image.getAttribute('src').indexOf('.jpg')>0) {
			var parent = image.parentNode;
			var URI;
			var src = image.getAttribute('src');
			if (src.indexOf('/') == 0) src = src.substring(1,src.length);
			if (image.getAttribute('src').search('http://')>-1) URI = src;
			else {
				URI = 'http://' + location.hostname + location.pathname;
				if (URI.lastIndexOf('/') < URI.length-1)	URI = URI.substring(0,URI.lastIndexOf('/')+1);
				URI = URI + src;
			}
			image.parentNode.replaceChild(createContainer(image.cloneNode(true)), image);
			i+=2;
		} else i++;
	}
}

GM_registerMenuCommand('Toggle UFIS!', function(event) {
	var on = GM_getValue('on', false);
	on = !on;
	GM_setValue('on',on);
	if (on) addFlink();
});

if (!GM_getValue('on', false)) return;
else window.addEventListener(
		'load', 
		addFlink(),
		true);