
// from John Resig
function _contains(a, b) {
  return a.contains ?
    a != b && a.contains(b) :
    !!(a.compareDocumentPosition(b) & 16);
}

function _px(num){
	return Math.round(num) + 'px';
}

function _pc(num){
	return (Math.round(num*10000)/100.0) + '%';
}

function _isLeftClick(e) {
	return (((e.which) && (e.which == 1)) ||
		((e.button) && (e.button == 1)));
}

function _add(c,el) {
	el.className += ' '+ c;
}

function _del(c,el) {
	el.className = el.className.replace(new RegExp('(^|\\s+)'+c+'\\b','g'),'');
}

function _cloneAnchor(from, to, embed, target) {
	if (to.target != '_self' && to.href && to.href != "") {
		// see: http://www.hedgerwow.com/360/bugs/ie-anchor-bug.html
		to.href = " " + from.href.replace(/e=[^&#]+/, "e="+embed);
		if (target) {
			to.target = "E"+embed;
			if (!to.onclick)
				to.onclick = startThrobber;
		}
		if (to.style.display != from.style.display) {
			to.style.display = from.style.display;
		}
	}
}

var copies;
function _deepcopy(el) {
	if (copies == null) {
		copies = document.createElement('div');
		copies.style.display = 'none';
		document.body.appendChild(copies);
	}
	
	var copy = el.cloneNode(true);
	copies.appendChild(copy);
	return copy;
}

var properties = {
	"title": {
		"get": function() {
			return document.title;
		},
		"set": function(title) {
			document.title = title;
		}
	},
	"query": {
		"get": function() {
			return document.getElementById('query').value;
		},
		"set": function(query) {
			document.getElementById('query').value = query;
			document.title = "MrTaggy: " + query;
		}
	},
	"header": {
		"get": function() {
			return document.getElementById('header').getElementsByTagName('H1')[0].innerHTML;
		},
		"set": function(contents) {
			document.getElementById('header').getElementsByTagName('H1')[0].innerHTML = contents;
		}
	},
	"link": {
		"get": function() {
			return document.getElementById('info').firstChild.innerHTML;
		},
		"set": function(link) {
			document.getElementById('info').firstChild.innerHTML = link;
		}
	},
	"redirect": {
		"get": function() {
			return _deepcopy(document.getElementById('redirect'));
		},
		"set": function(el) {
			var redirect = document.getElementById('redirect');
			
			redirect.style.display = el.style.display;
			redirect.innerHTML = el.innerHTML;

			var elLinks = el.getElementsByTagName('A');
			var myLinks = redirect.getElementsByTagName('A');
			for (var i=0; i<myLinks.length; i++) {
				_cloneAnchor(elLinks[i], myLinks[i], getEmbed(), shouldAnimate());
			}
			
			//if (redirect.style.display != 'none') {
			//	document.getElementById("urllist").style.display = 'none';
			//} else {
			//	document.getElementById("urllist").style.display = 'block';
			//}
		}
	},
	"stats": {
		"get": function() {
			return document.getElementById('stats').innerHTML;
		},
		"set": function(stats) {
			if (stats)
				document.getElementById('stats').innerHTML = stats;
		}
	},
	"quicklinks": {
		"get": function() {
			return _deepcopy(document.getElementById('quicklinks'));
		},
		"set": function(el) {
			var mine = document.getElementById('quicklinks');
			mine.style.display = el.style.display;
			mine.innerHTML = el.innerHTML;
		}
	}
};

function getProperties() {
	var pageProps = {};
	
	for (var p in properties) {
		pageProps[p] = properties[p]['get']();
	}
	
	return pageProps;
}

function setProperties(newProps) {
	for (var p in properties) {
		properties[p]['set'](newProps[p]);
	}
}

function getEmbed() {
	return document.getElementById('embed').value;
}

var myProps;

var myURLResults = null;
var myTagResults = null;
var currentURLResults = null;
var currentTagResults = null;
function init() {
	if (myURLResults == null)
		myURLResults = this.getURLResults();
	if (myTagResults == null)
		myTagResults = this.getTagResults();
	if (currentURLResults == null)
		currentURLResults = this.getURLResults();
	if (currentTagResults == null)
		currentTagResults = this.getTagResults();
		
	if (myProps == null)
		myProps = getProperties();
}

var started = false;
function start() {
	if (shouldAnimate()) {
		startThrobber();
	}
	started = true;

	init();
	
	mutateTo(currentURLResults, myURLResults, 2);
	mutateTo(currentTagResults, myTagResults, 1);
	
	setProperties(myProps);
}

function mutate(page) {
	if (shouldAnimate()) {
		startThrobber();
	}
	init();
	
	if (started) {
		mutateTo(currentURLResults, getURLResults(page), 2);
		mutateTo(currentTagResults, getTagResults(page), 1);
	} else {
		started = true;
		mutateTo(currentURLResults, getURLResults(page), 0);
		mutateTo(currentTagResults, getTagResults(page), 0);
	}
	
	setProperties(page.getProperties());
}

function mutateTo(myResults,newResults,duration) {
	myResults._reset();

	var embed = getEmbed();
	var target = shouldAnimate();
	for (var i=0; i<newResults.length; i++) {
		var newItems = newResults[i].items;
		
		MERGE: for (var j=0; j<newItems.length; j++) {
			var result = newItems[j];
			
			for (var m=0; m<myResults.length; m++) {
				var myItems = myResults[m].items;
				
				for (var n=0; n<myItems.length; n++) {
					if (myItems[n].equals(result)) {
						myItems[n].match(result, embed, target);
						if (i != m || j != n)
							myResults._moveTo(m,n,i,j);
						else
							myResults._stayAt(m,n);
						
						continue MERGE;
					}
				}
			}
			
			myResults._addAt(i, j, result.clone(document, embed, target));
		}
		
		var newLinks = newResults[i].links;
		var myLinks = myResults[i].links;
		for (var j=0; j<newLinks.length; j++) {
			if (myLinks.length > j) {
				_cloneAnchor(newLinks[j], myLinks[j], embed, target);
			}
		}
	}
	
	for (var i=0; i<myResults.length-1; i++) {
		var newItems = newResults[i].items;
		var myItems = myResults[i].items;
		
		for (var j=myItems.length-1; j>=newItems.length;j--) {
			myResults._remove(i,j);
		}
	}
	
	myResults._normalize(duration);
}

function over(link, newsrc) {
	var img = link.getElementsByTagName('IMG')[0];
	var src = img.src;
	img.src = newsrc;
	
	link.onmouseout = function() {
		img.src = src;
		link.onmouseout = null;
	}
}

var isMrTaggy = true;

function getURLResults(page) {
	if (!page)
		page = this;
	
	return createResults(page.document.getElementById('urllist'), createURL, 3000);
}

function getTagResults(page) {
	if (!page)
		page = this;
		
	return createResults(page.document.getElementById('tagresults'), createTag, 2000);
}

function createResults(ul, creator, zIndex) {
	var results = [];
	results.element = ul;
	var lists = [];
	
	var children = ul.childNodes;
	for (var i=0; i<children.length; i++) {
		if (children[i].tagName == 'LI')
			lists.push(children[i]);
	}
	
	for (var i=0; i<lists.length; i++) {
		var element = lists[i];
		var subresults = new Actor(element, function(){ return i });
		subresults.items = [];
		subresults.list = element.getElementsByTagName('UL')[0];
		
		subresults.links = [];
		var anchors = element.getElementsByTagName('A');
		for (var j=0; j<anchors.length; j++) {
			if (!_contains(subresults.list, anchors[j])) {
				subresults.links.push(anchors[j]);
			}
		}
		
		var items = element.getElementsByTagName('LI');
	
		for (var j=0; j<items.length; j++) {
			subresults.items.push(creator(items[j]));
		}
			
		results.push(subresults);
	}
	
	for (p in resultsPrototype) {
		results[p] = resultsPrototype[p];
	}
	
	results.toDelete = results[results.length - 1];
	results.zIndex = zIndex;
	
	return results;
}

var ARRIVING = 0;
var MOVINGUP = 1;
var MOVINGDN = 2;
var LEAVING  = 3;
var resultsPrototype = {
	_addToList: function (list,x,result) {
		if (list.element.style.display != 'block') {
			list.element.style.display = 'block';
		}
		// insert
		if (list.items.length > x) {
			list.list.insertBefore(result.element, list.items[x].element);
			list.items.splice(x,0,result);
		} else {
			list.list.appendChild(result.element);
			list.items.push(result);
		}
	},
	
	_removeFromList: function (list,x) {
		// remove
		list.list.removeChild(list.items[x].element);
		list.items.splice(x,1);
	},
	
	_offsetAfter: function (a,b,o) {
		var items = this[a].items;
		for (var j=b+1; j<items.length; j++) {
			items[j].offsetBy(o);
		}

		for (var i=a; i<this.length; i++) {
			this[i].offsetBy(o);
		}
	},
	
	_addAt: function (a,b,result) {
		// Prepare for adding
		result.state = window.ARRIVING;
		result.element.className += " new";
		result.setOpacity(0);
		result.setLeft(-30);
		result.targetOpacity = 1;
		result.targetLeft = 0;
		result.element.style.zIndex = this.zIndex - b;
		result.offsetTop = 0;
		
		this._addToList(this[a],b,result);
	},
	
	_stayAt: function (m, n) {
		// prepare for staying
		var result = this[m].items[n];
		result.state = window.MOVINGUP;
		result.targetOpacity = 1;
		result.targetLeft = 0;
		result.element.style.zIndex = this.zIndex - n;
	},
	
	_moveTo: function (m, n, a, b) {
		// prepare for moving
		var result = this[m].items[n];
		result.state = window.MOVINGUP;
		result.targetOpacity = 1;
		result.targetLeft = 0;
		result.element.style.zIndex = this.zIndex - b;
		
		this._removeFromList(this[m],n);
		this._addToList(this[a],b,result);
	},
	
	_remove: function(m,n) {
		var a = this.length - 1;
		var b = this[a].items.length;
		
		this._moveTo(m,n,a,b);
		
		// prepare for removal
		var result = this[a].items[b];
		result.state = window.LEAVING;
		result.targetOpacity = 0;
		result.targetLeft = -30;
	},
	
	_normalize: function(dur) {
		if (this.animator) {
			this.animator.cancel();
		}
		
		// reposition elements at their previous vertical offset (before items were added/moved)
		var offset = 0;
		for (var i=0; i<this.length; i++) {
			var result = this[i];
			if (i < this.length - 1) {
				result.offsetBy(result.offsetTop - this._getOffsetTop(result.element));
				result.targetOffset = offset;
				
				if (result.items.length == 0) {
					if (result.element.style.display != 'none') {
						//result.targetOpacity = 0;
						for (var j=i+1; j<this.length; j++)
							if (this[j].element.style.display != 'none')
								break;
						var next = this[j];
						
						offset += (result.element.offsetTop - result.offset) - (next.element.offsetTop - (next.offset || 0));
					}
				} else {
					//result.targetOpacity = 1;
				}
			} else {
				result.targetOffset = result.offset;
				result.targetOpacity = 1;
			}
			result.element.style.zIndex = this.zIndex - i;
			result.prepare();
		
			var items = result.items;
			for (var j=0; j<items.length; j++) {
				var item = items[j];
				switch (item.state) {
				case window.MOVINGUP:
					item.offsetBy(item.offsetTop - this._getOffsetTop(item.element));
					if (item.offset<0) {
						item.state = window.MOVINGDN;
					} else if (item.offset>0) {
						item.element.className += " up";	
					}
					item.targetOffset = 0;
					break;
				case window.LEAVING:
					item.offsetBy(item.offsetTop - this._getOffsetTop(item.element));
					item.targetOffset = item.offset;
					break;
				}
				item.prepare();
			}
		}
		
		// Animate elements to their natural positions
		var results = this;
		this.animator = new Animator([
			/*{from: 0, to: 1, doThis: function(t) {
				for (var i=0; i<results.length; i++) {
					results[i].animate(t);
					var items = results[i].items;
					for (var j=0; j<items.length; j++) {
						items[j].animate(t);
					}
				}
			}}*/
			{from: 0, to: 0.4, doThis: function(t) {
				for (var i=0; i<results.length; i++) {
					var items = results[i].items;
					for (var j=0; j<items.length; j++) {
						if (items[j].state == LEAVING)
							items[j].animate(t);
					}
				}
			}},
			{from: 0.3, to: 0.55, doThis: function(t) {
				for (var i=0; i<results.length; i++) {
					var items = results[i].items;
					for (var j=0; j<items.length; j++) {
						if (items[j].state == MOVINGDN)
							items[j].animate(t);
					}
				}
			}},
			{from: 0.45, to: 0.7, doThis: function(t) {
				for (var i=0; i<results.length; i++) {
					results[i].animate(t);
					var items = results[i].items;
					for (var j=0; j<items.length; j++) {
						if (items[j].state == MOVINGUP)
							items[j].animate(t);
					}
				}
			}},
			{from: 0.6, to: 1, doThis: function(t) {
				for (var i=0; i<results.length; i++) {
					var items = results[i].items;
					for (var j=0; j<items.length; j++) {
						if (items[j].state == ARRIVING)
							items[j].animate(t);
					}
				}
			}}],
		dur, function() {
			/* */
			var o = 0;
			for (var i=0; i<results.length-1; i++) {
				var result = results[i];
				if (result.items.length == 0) {
					for (var j=i+1; j<results.length; j++)
						if (results[j].element.style.display != 'none')
							break;
					var next = results[j];
					
					var t = next.element.offsetTop;
					result.element.style.display = 'none';
					o += (next.element.offsetTop - t) || 0;
				} else {
					result.offsetBy(-o);
				}
			}
			var list = results[results.length - 1];
			for (var j=list.items.length-1; j>=0; j--)
				results._removeFromList(list, j);
			/* */
			 
			stopThrobber();
		});
		
		this.animator.animate();
	},
	
	_reset: function() {
		if (this.animator) {
			this.animator.cancel();
		}
		
		
		//var t = this.element.offsetHeight;
		
		// Record current vertical offsets to match later
		for (var i=0; i<this.length-1; i++) {
			var result = this[i];
			//result.offsetBy(t - this.element.offsetHeight);
			//if (result.element.style.display != 'block') {
				//if (result.items.length == 0)
				//	result.setOpacity(0);
			//	result.element.style.display = 'block';
			//}
			
			if (result.element.style.display == 'none') {
				for (var j=i+1; j<this.length; j++)
					if (this[j].element.style.display != 'none')
						break;
				var next = this[j];
				
				result.offsetTop = this._getOffsetTop(next.element) - (next.offset || 0);
				
				var items = result.items;
				for (var j=0; j<items.length; j++) {
					items[j].offsetTop = 0;
				}
			} else {
				result.offsetTop = this._getOffsetTop(result.element);
		
				var items = result.items;
				for (var j=0; j<items.length; j++) {
					items[j].offsetTop = this._getOffsetTop(items[j].element);
				}
			}
		}
	},
	
	_getOffsetTop: function(el) {
		var t = 0;

		while (el && _contains(this.element,el)) {
			t += el.offsetTop;
			el = el.offsetParent;
		}
		
		return t;
	}
};

function Animator(spans, dur, andThen) {
	this.timer = null;
	
	for (var i=0; i<spans.length; i++) {
		spans[i].dur = spans[i].to - spans[i].from;
	}

	var start = null;
	var animator = this;
	this.animate = function() {
		if (start == null)
			start = (new Date()).getTime();
		var now = (new Date()).getTime();
		
		if (dur > 0)
			var t = Math.min(1.0,(now - start)/(dur*1000));
		else
			var t = 1.0;
		
		for (var i=0; i<spans.length; i++) {
			var span = spans[i];
			if (t >= span.from && !span.done) {
		
				var ft = 0.5 + 0.5*Math.sin(((t - span.from)/span.dur - 0.5)*Math.PI);
				if (t >= span.to) {
					ft = 1;
					span.done = true;
				}
				
				span.doThis(ft);
			}
		}
		
		if (t < 1) {
			animator.timeout = setTimeout(animator.animate, 50);
		} else if (andThen) {
			andThen();
		}
	}
	this.cancel = function() {
		if (animator.timeout) {
			clearTimeout(animator.timeout);
			animator.timeout = null;
		}
	}
}

function createTag(li) {
	return new Actor(li, 
		function() {
			var spans = li.getElementsByTagName('SPAN');
			for (var i=0; i<spans.length; i++)
				if (spans[i].className == 'id')
					return spans[i].innerHTML;
		}
	);
}

function createURL(li) {
	return new Actor(li, 
		function() {
			var divs = li.getElementsByTagName('DIV');
			for (var i=0; i<divs.length; i++) {
				if (divs[i].className == 'URL') {
					return divs[i].innerHTML;
				}
			}
			return li.getElementsByTagName('DIV')[0].href;
		}
	);
}

function Actor(element, idFunc) {
	this.element = element;
	this.idFunc = idFunc;
	this.id = idFunc();
	this.offset = 0;
	this.opacity = 1;
	this.left = 0;
	this.targetOpacity = 1;
	this.targetLeft = 0;
	this.targetOffset = 0;
}

Actor.prototype = {
	equals: function(other) {
		return this.id == other.id;
	},
	
	clone: function(doc, embed, target) {
		var element = doc.createElement('LI');
		
		var clone = new Actor(element, this.idFunc);
		clone.match(this, embed, target);
		
		return clone;
	},
	
	match: function(result, embed, target) {
		this.element.innerHTML = result.element.innerHTML;
		
		this.element.className = result.element.className;
		
		var myLinks = this.element.getElementsByTagName('A');
		var newLinks = result.element.getElementsByTagName('A');
		
		for (var i=0; i<myLinks.length; i++) {
			if (newLinks.length > i) {
				_cloneAnchor(newLinks[i], myLinks[i], embed, target);
			}
			myLinks[i].onmouseout = null;
		}
	},
	
	offsetBy: function(y) {
		if (y == 0)
			return;
		this.offset += y;
		this.element.style.top = this.offset + 'px';
	},
	
	setOpacity: function(o) {
		if (o == this.opacity)
			return;
		if (o == 0) {
			this.element.style.visibility = 'hidden';
		} else {
			this.element.style.visibility = 'visible';
		}
		this.opacity = o;
		
		o = (o == 1) ? 0.9999 : o;
		this.element.style.filter = "alpha(opacity:"+(o*100)+")";
		this.element.style.opacity = o;
	},
	
	setLeft: function(x) {
		x = Math.round(x);
		if (x == this.left)
			return; 
		
		this.element.style.left = x + "px";
		this.left = x;
	},
	
	prepare: function() {
		this.startOffset = this.offset;
		this.startOpacity = this.opacity;
		this.startLeft = this.left;
	},
	
	animate: function(t) {
		this.offsetBy(Math.round(t*this.targetOffset + (1 - t)*this.startOffset) - this.offset);
		this.setOpacity(t*this.targetOpacity + (1 - t)*this.startOpacity);
		this.setLeft(t*this.targetLeft + (1 - t)*this.startLeft);
	}
};

function tagclick(link) {
	var tag = link.firstChild.innerHTML;
	var querybox = document.getElementById('query');
	var pattern = new RegExp("(^|\\s)"+tag+"(\\s|$)");
	
	if (!querybox.value.match(pattern)) {
		querybox.value += " "+ tag;
	} else {
		querybox.form.submit();
	}
	return false;
}

function shouldAnimate() {
	var str = document.cookie;
	return !str || !str.match(/(^|;)\s*animate\s*=\s*no\s*(;|$)/);
}

function setAnimate(yes) {
	setLoadInBackground(yes);
	if (yes) {
		document.cookie = 'animate=yes'
	} else {
		document.cookie = 'animate=no'
	}
}

function startThrobber() {
	document.getElementById('throbber').style.visibility = 'visible';
}

function stopThrobber() {
	document.getElementById('throbber').style.visibility = 'hidden';
}

function setLoadInBackground(b) {
	var name = 'E'+window.getEmbed();

	var frame = function(t) { return t != "_self" && t != "_blank" };
	
	if (b) {
		for (var i=0; i<document.links.length; i++) {
			var link = document.links[i];
			if (frame(link.target)) {
				link.target = name;
				if (!link.onclick)
					link.onclick = startThrobber;
			}
		}
		for (var i=0; i<document.forms.length; i++) {
			var form = document.forms[i];
			if (frame(form.target)) {
				form.target = name;
				if (!form.onsubmit)
					form.onsubmit = startThrobber;
			}
		}
	} else {
		for (var i=0; i<document.links.length; i++) {
			var link = document.links[i];
			if (frame(link.target)) {
				link.target = "";
				if (link.onclick == startThrobber)
					link.onclick = null;
			}
		}
		for (var i=0; i<document.forms.length; i++) {
			var form = document.forms[i];
			if (frame(form.target)) {
				form.target = "";
				if (form.onsubmit == startThrobber)
					form.onsubmit = null;
			}
		}
	}
}

var hasLoaded = false;
function doLoad() {
	if (hasLoaded) return;
	hasLoaded = true
	
	if (!window.parent || window.parent==window || !window.parent.isMrTaggy) {
		document.getElementById('query').focus();
		window.setLoadInBackground(shouldAnimate());
	}
	
	try {
		if (window.parent && window.parent!=window && window.parent.isMrTaggy) {
			window.parent.mutate(window);
		}
	} catch (e) {
		
	}
}
window.onload = doLoad;
