
/* Test for various browsers */
window.xpath = !!(document.evaluate);
if (window.ActiveXObject) window.ie = window[window.XMLHttpRequest ? 'ie7' : 'ie6'] = true;
else if (document.childNodes && !document.all && !navigator.taintEnabled) window.khtml = window.webkit = window[window.xpath ? 'webkit420' : 'webkit419'] = true;
else if (document.getBoxObjectFor != null) window.gecko = true;

function factorial(num) {
	if (num <= 1) {
		return Math.max(num, 0);
	} else {
		return num * factorial(num - 1);
	}
}


// if (!console) {
// 	var console = {log: function(lg) {}};
// }
// 
// var Console = console;

function $Q(el) {
	if (typeof el == 'string') {
		return document.getElementById(el);
	} else {
		return el;
	}
};


Function.prototype.bind = function(bind) {
	var fn = this;
	return function(){
		return fn.apply(bind, arguments);
	};
};

Function.prototype.pass = function(args, bind){
	var fn = this;
	if (typeof args != 'array') args = [args];
	return function(){
		return fn.apply(bind || fn._proto_ || fn, args);
	};
};

if (!Array.prototype.forEach){
	Array.prototype.forEach = function(fn, bind){
		for(var i = 0; i < this.length ; i++) fn.call(bind, this[i], i);
	};
}

Array.prototype.each = Array.prototype.forEach;

Array.prototype.includes = function(el) {
	for (var i=0;i < this.length;i++) {
		if (this[i] === el) {
			return true;
		}
	}
	return false;
};

Array.prototype.test = function(item){
	for (var i = 0; i < this.length; i++){
		if (this[i] == item) return true;
	};
	return false;
};

Array.prototype.grep = function(regex) {
	var result = [];
	
	for (var i=0;i < this.length;i++) {
		if ((typeof regex === 'string' && this[i] === regex) || this[i].match(regex) != null) {
			result.push(this[i]);
		}
	}
	
	return result;
};

var $ = $Q;

var Ajax = function(url, options) {
	this.options = options || {};
	this.options.method = this.options.method || 'get';
	this.url = url;
	this.transport = this.getTransport();
};

Ajax.prototype = {
	onStateChange: function() {
		if (this.transport.readyState != 4) {
			return;
		} else {
			if (this.running && this.transport && this.transport.status == 200) {
				if (this.options.onComplete) {
					this.options.onComplete(this.transport.responseText);
				}
				if (this.options.evalResponse) {
					eval(this.transport.responseText);
				}
			}
		}
	},
	
	request: function() {
		this.running = true;
		this.transport.open((this.options.method == 'post') ? 'POST' : 'GET', this.url, true);
		if (this.options.method == 'post' && this.options.postBody) {
			// ; charset=UTF-8
			// try {this.transport.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");} catch (e) {}
			// try {this.transport.setRequestHeader("Content-length", (this.options.postBody || '').length);} catch (e) {}
			// try {this.transport.setRequestHeader("Connection", "close");} catch (e) {}
			this.transport.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
			this.transport.setRequestHeader("Content-length", (this.options.postBody || '').length);
			this.transport.setRequestHeader("Connection", "close");
		}
		var that = this;
		this.transport.onreadystatechange = function() {that.onStateChange();};
		if (this.options.method == 'post') {
			this.transport.send(this.options.postBody || '');
		} else {
			this.transport.send(null);
		}
		
		return false;
	},
	
	getTransport: function() {
		if (window.XMLHttpRequest) return new XMLHttpRequest();
		else if (window.ActiveXObject) return new ActiveXObject('Microsoft.XMLHTTP');
	},
	
	cancel: function() {
		this.running = false;
		this.transport.abort();
		this.transport.onreadystatechange = function () {};
		this.transport = this.getTransport();
	}
	
};

String.prototype.trim = function() { return this.replace(/^\s+|\s+$/, '').replace(/^\s+|\s+$/, ''); };


/**/






// Another singleton
/**/
var Lib = {
	// Return the pages url parameters
	getParams: function() {
		var loc = document.location.href;

		if (loc.indexOf('?') >= 0) {
			var paramstr = loc.split('?')[1];
			var params = Lib._getParamsFrom(paramstr);
		}
		return params || {};
	},
	
	_getParamsFrom: function(str) {
		var params = {};
		
		var parts = str.replace(/[#][^#]*$/, '').split('&');
		
		for (var i=0;i < parts.length;i++) {
			var subparts = parts[i].split('=');
			if (typeof subparts[1] !== 'undefined') {
				params[subparts[0]] = unescape(subparts[1]);
			}
		}
		return params;		
	},
	
	getElementPosition: function(el, overflown) {
		overflown = overflown || [];
		var left = 0, top = 0;
		do {
			left += el.offsetLeft || 0;
			top += el.offsetTop || 0;
			el = el.offsetParent;
		} while (el);
		
		overflown.each(function(element){
			left -= element.scrollLeft || 0;
			top -= element.scrollTop || 0;
		});
		return {'x': left, 'y': top};
	},
	
	getMousePos: function(e, containers) {
		var posx = 0;
		var posy = 0;
		if (e.pageX || e.pageY) {
			posx = e.pageX;
			posy = e.pageY;
		} else if (e.clientX || e.clientY) {
			/*
			var de = document.documentElement;
	        var b = document.body;
	        posx = e.clientX + (de.scrollLeft || b.scrollLeft) - (de.clientLeft || 0);
	        posy = e.clientY + (de.scrollTop || b.scrollTop) - (de.clientTop || 0);
			*/
			posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
			posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
		}
		
		containers = containers || [];
		for (var i=0;i < containers.length;i++) {
			var element = containers[i];
			posx += element.scrollLeft || 0;
			posy += element.scrollTop || 0;
		}
		
		return {x: posx, y: posy};
	},
	
	getComputedStyle: function(el, prop) {
		var strValue = "";
		if(document.defaultView && document.defaultView.getComputedStyle){
			strValue = document.defaultView.getComputedStyle(el, "").getPropertyValue(prop);
		} else if (el.currentStyle) {
			prop = prop.replace(/-(w)/g, function (strMatch, p1){
				return p1.toUpperCase();
			});
			strValue = el.currentStyle[prop];
		}
		
		
		if (strValue === 'auto' || strValue == '0px') {
			if (prop === 'height') {
				strValue = el.offsetHeight;
			} else if (prop === 'width'){
				strValue = el.offsetWidth;
			}
		}
		return strValue;
	},
	
	
	swapNodes: function(e1, e2) {
		// IE has swapNode, Gecko doesn't
		if (!e1.swapNode) {
			var itemtmp = e1.cloneNode(1);
			var parent = e1.parentNode;
			e2 = parent.replaceChild(itemtmp, e2);
			parent.replaceChild(e2, e1);
			parent.replaceChild(e1, itemtmp);
			itemtmp = null;
/*
			ns = e1.nextSibling;
			pn = e1.parentNode;
			e2.parentNode.replaceChild(e1, e2);
			pn.insertBefore(e2, ns); 
*/
		} else {
			e1.swapNode(e2);
		}
	},
	
	// TODO: we can make this with prototype if we want
	swapArray: function(array, index1, index2) {
		var tmp = array[index1];
		array[index1] = array[index2];
		array[index2] = tmp;
	},
	
	stopPropagation: function(e) {
		e = e || window.event;
		
		if( e.stopPropagation ) {
			e.stopPropagation();
		} else {
			e.cancelBubble = true;
		}
		
		// if (e.preventDefault) {
		// 	e.preventDefault();
		// } else {
		// 	e.returnValue = false;	
		// }
		
	},
	
	purgeNodes: function(d) {
		this._purgeNodes(d);
		//setTimeout(function() {this._purgeNodes(d);}.bind(this), Math.round(Math.random() * 15000));
		if (typeof CollectGarbage != 'undefined') CollectGarbage();
	},
	
	purgeNode: function(d) {
		this._purgeNode(d);
		//setTimeout(function() {this._purgeNode(d);}.bind(this), Math.round(Math.random() * 15000));
		if (typeof CollectGarbage != 'undefined') CollectGarbage();
	},
	
	/* Douglas Crockfords memory leak purge function    *
	 * http://javascript.crockford.com/memory/leak.html */
	_purgeNodes: function(d) {
		this._purgeNode(d);

		var a = d.childNodes, i, l;
		if (a) {
			l = a.length;
			for (i = 0; i < l; i += 1) {
				//setTimeout(function() {
					this._purgeNodes(d.childNodes[i]);
				//}.bind(this), Math.round(Math.random() * 15000));
			}
		}
	},
	
	/* Only purges a single node, non-recursevly */
	_purgeNode: function(d) {
		var a = d.attributes, i, l, n;
		
		if (a) {
			l = a.length;
			for (i = 0; i < l; i += 1) {
				if (typeof(a[i]) !== 'undefined' && a[i]) {
					n = a[i].name;
					if (n && typeof d[n] === 'function') {
						d[n] = null;
					}
				}
			}
		}
	},
	
	extend: function(obj1, obj2) {
		for (var prop in obj2) {
			obj1[prop] = obj2[prop];
		}
		return obj1;
	}


};


var myGC = function() {
	this.objs = [];
	
	this.free = function() {
		for (var i=0;i < this.objs.length;i++) {
			var obj = this.objs[i];
			/*if (obj[0] instanceof Array) {
				var subobj = obj[0];
				for (var k=0;k < subobj.length;k++) {
					subobj[k] = null;
				}
			} else {*/
				obj[0][obj[1]] = null;
			//}
		}
		this.objs = null;
		gc = null;
	};
	
	/* To add an object to the gc, specify its parent object and
	 * the property which you wish to clear in an array 
	 * example: gc.add([this, 'list'], [this, 'block']);*/
	
	this.add = function() {
		for (var i=0;i < arguments.length;i++) {
			this.objs.push(arguments[i]);
		}
	};
};


// Initialize a GC on the window, unloaded when done
var gc = new myGC();

var Menu = function(options, position, clickEvent) {
	this.clickEvent = clickEvent;
	this.div = document.createElement('div');
	this.shiv = document.createElement('iframe');
	
	this.div.id = 'dropmenu';
	this.div.className = 'dropmenu';
	this.div.style.cssText = 'width: ' + (position.width || 150) + 'px;top: ' + position.top + 'px;left: ' + position.left + 'px;';
	this.shiv.style.cssText = 'position: absolute;opacity: 0;height: ' + (options.length * 18) + 'px;width: ' + (position.width || 150) + 'px;top: ' + position.top + 'px;left: ' + position.left + 'px;';
	
	
	var divs = [];
	for (var i=0;i < options.length;i++) {
		divs.push('<div class="menuline">');
		divs.push(options[i].key);
		divs.push('</div>');
	}
	
	this.div.innerHTML = divs.join('');
	
	var nodes = this.div.childNodes;
	
	var mouse_over = function() {
		this.style.backgroundColor = '#0000FF';
		this.style.color = '#FFF';
	};
	var mouse_out = function() {
		this.style.color = '#000';
		this.style.backgroundColor = "#FFF";
	};
	
	// Attach event to each div in the menu
	for (var i=0;i < nodes.length;i++) {
		nodes[i].onclick = function() {
			var optionval = options[i].value;
			return function() {
				this.clickEvent(optionval);
			}.bind(this);
		}.bind(this)();
		
		nodes[i].onmouseover = mouse_over;
		nodes[i].onmouseout = mouse_out;
	}
	
	// Add event to hide the div
	setTimeout(function() {
			document.body.onclick = function() {
				document.body.removeChild(this.shiv);
				document.body.removeChild(this.div);
				document.body.onclick = null;
			}.bind(this);
		}.bind(this)
	, 0);
	
	document.body.appendChild(this.shiv);
	document.body.appendChild(this.div);
};





/**  onDomReady */

function init() {
	// quit if this function has already been called
	if (arguments.callee.done) return;

	// flag this function so we don't do the same thing twice
	arguments.callee.done = true;

	// kill the timer
	if (_timer) clearInterval(_timer);

	// Call each function we added with addEvent
	var evtary = window.ondomready._events;
	if (typeof evtary !== 'undefined') {
		for (var i=0;i < evtary.length;i++) {
			evtary[i]();
		}
	}
	window.ondomready._has_loaded = true;
};

/* for Mozilla/Opera9 */
if (document.addEventListener) {
	document.addEventListener("DOMContentLoaded", init, false);
}

if (document.attachEvent) {
	document.write("<scr" + "ipt id=__ie_onload defer src=javascript:void(0)><\/scr" + "ipt>");
	var script = document.getElementById("__ie_onload");
	script.onreadystatechange = function() {
		if (this.readyState == "complete") {
			init(); // call the onload handler
		}
	};
	
}

/* for Safari */
if (/WebKit/i.test(navigator.userAgent)) { // sniff
	var _timer = setInterval(function() {
		if (/loaded|complete/.test(document.readyState)) {
			init(); // call the onload handler
		}
	}, 10);
}

/* for other browsers */
window.onload = init;

/**  end onDomReady */

window.ondomready = {};
window.ondomready.addEvent = function(fun) {
	if (window.ondomready._has_loaded === true) {
		// If it has already loaded, then we can just call this now
		fun();
	} else {
		if (!window.ondomready._events) window.ondomready._events = [];
		window.ondomready._events.push(fun);
	}
};








/****************************************************************
 * JSON - toJSONString function 
 * By Ryan Stout - note that we don't extend object.prototype
 ***************************************************************/

function toJson(obj) {
	switch (typeof obj) {
		case 'object':
			if (obj) {
				var list = [];
				if (obj instanceof Array) {
					for (var i=0;i < obj.length;i++) {
						list.push(toJson(obj[i]));
					}
					return '[' + list.join(',') + ']';
				} else {
					for (var prop in obj) {
						list.push('"' + prop + '":' + toJson(obj[prop]));
					}
					return '{' + list.join(',') + '}';
				}
			} else {
				return 'null';
			}
		case 'string':
			return '"' + obj.replace(/(["'])/g, '\\$1') + '"';
		case 'number':
		case 'boolean':
			return new String(obj);
	}
}


/* / json parser */








/* Javascript cookie handlers */
var Cookie = {
	set: function(name,value,days){
		var today = new Date();
		var expire = new Date();
		if (days==null || days==0) days=1;
		expire.setTime(today.getTime() + 3600000*24*days);
		document.cookie = name + "=" + escape(value) + ";expires="+expire.toGMTString();
	},
	
	get: function(name) {
		var reg = new RegExp(name + '=([^;]+)', 'i');
		var result = reg.exec(document.cookie);

		if (result && result[1]) {
			return unescape(result[1]);
		} else {
			return null;
		}
	},
	
	destroy: function(name) {
		document.cookie = name + "=;expires=Thu, 01-Jan-1970 00:00:01 GMT";
	}
};
/* / end javascript cookie handlers */



