function $$(elmt) {
	elmt.getStyle = function (prop) {
		if (this.style && this.style[prop] && this.style[prop] !== "") {
			return this.style[prop];
		} else {
			// try to get it from a stylesheet
			// taken from http://www.javascriptkit.com/dhtmltutors/dhtmlcascade4.shtml
			var retval;
			if (this.currentStyle) { //if IE5+
				retval = this.currentStyle[prop];
			} else if (window.getComputedStyle) { //if NS6+
				var elstyle = window.getComputedStyle(this, "");
				retval = elstyle.getPropertyValue(String.toHyphenated(prop));
			}
			return retval;
		}
	};
	elmt.hide = function () {
		if (elmt.style) { elmt.style.display = 'none'; }
		return elmt;
	};
	elmt.show = function (display, table) {
		if (!isset(typeof(display)) || display === null) { display = 'block'; }
		if (!!table) {
			// from http://blog.delaguardia.com.mx/index.php?op=ViewArticle&articleId=28&blogId=1
			try {
				if (elmt.style) {
					if (elmt.nodeName.toUpperCase() === 'TR') { elmt.style.display = 'table-row'; }
					else if (elmt.nodeName.toUpperCase() === 'TABLE') { elmt.style.display = 'table'; }
					else if (elmt.nodeName.toUpperCase() === 'TD') { elmt.style.display = 'table-column'; }
				}
			} catch(e) { if (elmt.style) { elmt.style.display = display; } }
		} else if (elmt.style) { elmt.style.display = display; }
		elmt.removeClassName('hide');
		return elmt;
	};
	elmt.hidden = function () { return this.style && this.getStyle("display") === 'none'; }
	elmt.create = function (nodeName, id, classname, innerHTML) {
		if ( elmt !== document) {
			var el = document.create(nodeName, id, classname, innerHTML);
			elmt.appendChild(el);
			return el;
		}
	};
	elmt.isNode = function (nodeName) {
		return elmt.nodeName && typeof(nodeName)==='string' && elmt.nodeName.toUpperCase() === nodeName.toUpperCase();
	};
	elmt.empty = function () {
		while (elmt.hasChildNodes()) { elmt.removeChild(elmt.firstChild); } // Container
		if (isset(typeof(this.value))) {
			elmt.value = ''; // Input
			elmt.setAttribute("value", ''); // Input
		}
		return elmt;
	};
	elmt.update = function (val) {
		if (val !== 0 && !val) { val = ''; }
		if (elmt.nodeName) {
			if (elmt.isNode('input') || elmt.isNode('textarea')) {
				elmt.setAttribute("value", val);
				elmt.value = val;
			} else if (elmt.isNode("table")) {
				if (!val || val === '') { elmt.empty(); }
				else {
					throw("The innerHTML property of a table can not be set in IE.");
				}
			} else if (isset(typeof(elmt.innerHTML))) { elmt.innerHTML = val; }
		}
		return elmt;
	};
	elmt.setClassName = function (className) {
		if (!isset(typeof(className))) { className = ''; }
		elmt.className = className;
		return elmt;
	};
	/// Add a class to an element (if it hasn't already been added)
	elmt.addClassName = function (className) {
		if (elmt.className && elmt.className !== '') {
			if (!elmt.hasClassName(className)) { elmt.className = className + ' ' + elmt.className; }
		} else { elmt.setClassName(className); }
		return elmt.hasClassName(className);
	};
	/// Remove a class to an element (if it exists)
	elmt.removeClassName = function (className) {
		if (elmt.hasClassName(className)) {
			elmt.className = elmt.className.replace(new RegExp("(^|\\s)" + className + "(\\s|$)"), " ");
			if (elmt.className && typeof(elmt.className)==="string") { elmt.className = elmt.className.trim(); }
		}
		return !elmt.hasClassName(className);
	};
	elmt.hasClassName = function (className) {
		return elmt.className &&
			elmt.className.length !== 0 &&
			(elmt.className === className || elmt.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")));
	};
	elmt.insertAfter = function (node) { elmt.parentNode.insertBefore(node, elmt.nextSibling); };
	return elmt;
}

function $$$(id) {
	var el = null;
	if (document.getElementById) { el = document.getElementById(id); }
	else if (document.all) { el = document.all[id]; }
	else if (document.layers) { el = document.layers[id]; }
	return el !== null ? $$(el) : null;
}

String.toHyphenated = function (str) {
	var tmp;
	tmp = str.substr(1).substr(0,str.length-2);
	tmp = tmp.replace(/([A-Z])/g, '-$1');
	return (str.substr(0,1) + tmp + str.substr(-1)).toLowerCase();
};

document.create = function (nodeName, id, classname, innerHTML) {
	var type, forID;
	if (nodeName.substring(0,5) === "input" && nodeName !== "input") {
		type = nodeName.substring(6);
		nodeName = "input";
		// enables "input:checkbox" eg
	}
	if (nodeName.substring(0,5) === "label" && nodeName !== "label") {
		forID = nodeName.substring(6);
		nodeName = "label";
		// enables label:inputid" eg
	}
	var el = document.createElement(nodeName);
	if (id && id.length > 0) { el.id = id; }
	$$(el);
	if (isset(typeof(classname)) && classname) { el.addClassName(classname); }
	if (type && nodeName === "input") { el.setAttribute("type", type); }
	if (forID && nodeName === "label") { el.setAttribute("for", forID); }
	if (isset(typeof(innerHTML)) && innerHTML !== false) { el.update(innerHTML); }
	return $$(el);
};

// usage: isset(typeof(variable)) returns true if variable is defined.
function isset(ref) { return ref.toString ? ref.toString().toLowerCase() !== "undefined" : false; }

//============= Cookie functions ==============\\
// adapted from quirksmode.org/js/cookies.html
var Cookie = {
	// get set and check cookies
	createCookie: function (name,value,days,expires) {
		var date;
		if (!isset(typeof days) && !isset(typeof expires)) {
			expires = '';
		} else {
			if (!days) { days = expires / (24 * 60 * 60 * 1000); }
			if (days) {
				date = new Date();
				date.setTime(date.getTime()+(days*24*60*60*1000));
				expires = "; expires="+date.toGMTString();
			}
		}
		document.cookie = name+"="+value+expires+"; path=/";
	},
	readCookie: function (name) {
		var nameEQ = name + "=", i,
			ca = document.cookie.split(';'),
			data = null;
		for (i=0; i < ca.length; ++i) {
			var c = ca[i];
			while (c.charAt(0)===' ') { c = c.substring(1); }
			if (c.indexOf(nameEQ) === 0) { data = c.substring(nameEQ.length); break; }
		}
		return data;
	},
	eraseCookie: function (name) { Cookie.createCookie(name,0,-1); }
};

//================= DOMContentLoaded Setup ==================\\
// more on this below
var startStack=function (){},
	lastStack=function (){},
	registerOnLoad = function (func, last) {
		var orgOnLoad;
		if (!last) {
			orgOnLoad = startStack;
			startStack = function () {
				orgOnLoad();
				func();
				return;
			};
		} else {
			orgOnLoad = lastStack;
			lastStack = function () {
				orgOnLoad();
				func();
				return;
			};
		}
	};

window.addLoad = function () { registerOnLoad(arguments[0],arguments[1]); };

Function.prototype.bind = function (who) {
	var func = this, args = [], i;
	for (i=1; i<arguments.length; ++i) { args.push(arguments[i]); }
	return function () { return func.apply(who,args); };
};

// Function.curry allows you to pre-prepare some of a function's arguments
// crockford, pg 44, js: the good parts
Function.prototype.curry = function () {
	var slice = Array.prototype.slice,
		args = slice.apply(arguments),
		that = this;
	return function () { return that.apply(null, args.concat(slice.apply(arguments))); };
};

//================= DOMContentLoaded ==================\\
// from http://ajaxian.com/archives/ajaxian-featured-tutorial-when-is-your-page-ready

var ranOnload = false; // Flag to determine if we've ran the starting stack already.
	
if (document.addEventListener) {
	// Mozilla actually has a DOM READY event.
	document.addEventListener("DOMContentLoaded", function () { if (!ranOnload) { ranOnload = true; startStack(); }}, false);
} else if (document.all && !window.opera) {
	// This is the IE style which exploits a property of the (standards defined) defer attribute
	document.write("<scr" + "ipt id='DOMReady' defer=true " + "src=//:><\/scr" + "ipt>");
	document.getElementById("DOMReady").onreadystatechange = function () {
		if (this.readyState==="complete" && !ranOnload) { ranOnload = true; startStack(); }
	};
}
	
var orgOnLoad = window.onload;
window.onload = function () {
	if (typeof(orgOnLoad)==='function') { orgOnLoad(); }
	if (!ranOnload) {
		ranOnload = true;
		startStack();
	}
};
