/** -- Prototyping
------------------------------------------------------------------------------ **/

checkArguments = function(arg1, arg2, thsObj) {
	if (!arg1) return false;

	if (typeof arg1 == 'string') return [ thsObj, arg1 ];
	else return [ arg1, arg2 ];
}

addClassName = function() {
	var classNm, obj = checkArguments(arguments[0], arguments[1], this);
	if (!obj) return false;
	classNm = obj[1];
	obj = obj[0];

	if (obj.className === '') obj.className = classNm;
	else if (!obj.className.match(new RegExp("(^| )" + classNm + "( |$)"))) obj.className += ' ' + classNm;
	return obj;
}

removeClassName = function() {
	var classNm, obj = checkArguments(arguments[0], arguments[1], this);
	if (!obj) return false;
	classNm = obj[1];
	obj = obj[0];
	
	obj.className = obj.className.replace(new RegExp("(^| )" + classNm + "( |$)"), '');
	return obj;
}

hasClassName = function() {
	var classNm, obj = checkArguments(arguments[0], arguments[1], this);
	if (!obj) return false;
	classNm = obj[1];
	obj = obj[0];

	return Boolean(obj.className.match(new RegExp("(^| )" + classNm + "( |$)")));
}

show = function() {
	var obj = arguments[0] ? arguments[0] : this;

	if (!obj.style) return false;
	obj.style.display = '';
	return obj;
}

hide = function() {
	var obj = arguments[0] ? arguments[0] : this;

	if (!obj.style) return false;
	obj.style.display = 'none';
	return obj;
}

proto = function(obj) {
	obj.addClassName = addClassName;
	obj.removeClassName = removeClassName;
	obj.hasClassName = hasClassName;
	obj.show = show;
	obj.hide = hide
	return obj;
}

Event = {}
Event.stop = function(event) {
	if (event.preventDefault) {
		event.preventDefault();
		event.stopPropagation();
	} else {
		event.returnValue = false;
		event.cancelBubble = true;
	}
}



function addListener(element, type, func, overwrite, bubbling) {
	if (typeof element == 'string') element = $(element);
	if (!element) return throwError('global::addListener - element not found');

	bubbling = bubbling || false;
	overwrite = overwrite || false;
	var newFunc = function(e) { func.call(e.target || e.srcElement, e); }

	if (overwrite) element['on'+type] = '';

	if(window.addEventListener) {
		element.addEventListener(type, newFunc, bubbling);
		return true;
	} else if(window.attachEvent) {
		element.attachEvent('on' + type, newFunc);
		return true;
	}
	return throwError('global:addListener - couldn\'t attach event to element');
}



/***=================================================================
/* Note: requires that the following code be
/* placed just before the </body> tag:
/*
/* <script type="text/javascript">document.DOMReady = true;</script>
/*================================================================***/

document.DOMReady = false;
onDOMLoad = function(f) {
	var self = this;
	this.func = f;
	this.timer = 0;
	this.limit = 4000;

	this.interval = setInterval(function() {
		if (self.checkDOM() || onDOMLoad.pageLoaded) self.killInterval(true);
		else if (self.time >= self.limit) self.killInterval(false);
		else self.timer++;
	}, 20);
}

onDOMLoad.prototype = {
	constructor : onDOMLoad,

	pageLoaded : false,

	killInterval : function(success) {
		clearInterval(this.interval);
		if (success) this.func();
		else addListener(window, 'load', this.func);
	},

	checkDOM : function() {
		return document.DOMReady;
	}
}

addListener(window, 'load', function() { document.DOMReady = true; });



function $() {
	var elmts = [], elmt, i;
	for(i = 0; i < arguments.length; i++) {
		if (typeof arguments[i] == 'string') elmt = document.getElementById(arguments[i]);
		else elmt = arguments[i];
		elmts.push(elmt ? proto(elmt) : false);
	}
	if (elmts.length == 1) elmts = elmts[0];
	return elmts;
}

$$ = function(path, parent, cache) {
	if (cache && __$$_cache[path]) return __$$_cache[path];
	var set = [];
	if (!parent) parent = document;
	if (typeof path == 'string') path = path.split(',');
	for(var i = 0, ln = path.length; i < ln; i++) set = set.concat(__$$(path[i], parent));
	for(i = 0, ln = set.length; i < ln; i++) set[i] = proto(set[i]);
	if (cache) __$$_cache[path] = set;
	return set;
}

__$$ = function(path, parent) {
	path = path.split(' ');
	var current = '', set = [];
	while(current === '') { current = path.shift().replace(/^[\t ]+|[\t ]+$/g, ''); }

	for(var i = 0, ln = $$_selectors.length; i < ln; i++) {
		//alert(current + '\n' + $$_selectors[i][0].source + '\n' + current.match($$_selectors[i][0]));
		if (current.match($$_selectors[i][0])) {
			parent = $$_selectors[i][1](current, parent);
			break;
		}
	}

	if (i == ln) return false; //Bad selector used

	if (parent.length == 0 || path.length == 0) return parent;
	else {
		path = path.join(' ');
		for(i = 0, ln = parent.length; i < ln; i++) set = set.concat(__$$(path, parent[i]));
		return set;
	}
}

__$$_cache = {}

var $$_tagRegEx = '\\w[\\d\\w]*';
var $$_testSet = function(selector, container, testFunc) {
	var set = [];
	if (selector === '') selector = '*';
	selector = __$$(selector, container);
	for(var i = 0, ln = selector.length; i < ln; i++) {
		if (testFunc(selector[i])) set.push(selector[i]);
	}
	return set;
}

var $$_selectors = [
	[ new RegExp('^\\*$'), function(selector, container) { return duplicate(container.getElementsByTagName(selector), true); } ],
	[ new RegExp('^' + $$_tagRegEx + '$', 'i'), function(selector, container) { return duplicate(container.getElementsByTagName(selector), true); } ],
	[
		new RegExp('^(' + $$_tagRegEx + ')?\\[[\\w]+(=".+")?]$', 'i'), //attribute (either set or set to something specific)
		function(selector, container) {
			var attr = selector.match(/\[[\w]+(=".+")?]$/), val = attr[1];
			attr = attr[0];
			selector = selector.replace(attr, '');
			attr = attr.replace(new RegExp('\\[|]|' + val, 'g'), '');
			if (val) val = val.replace(/[="]/g, '');
			else val = /.+/;
			return $$_testSet(selector, container, function(elmt) { var attrVal = elmt.getAttribute(attr); if (!attrVal) return false; else return attrVal.match(val); });
		}
	],
	[
		new RegExp('^(' + $$_tagRegEx + ')?\\.[-_\\w\\d]+$', 'i'), //class
		function(selector, container) {
			var className = selector.match(/\.[-_\w\d]+$/)[0].substr(1);
			selector = selector.replace('.' + className, '');
			return $$_testSet(selector, container, function(elmt) { return hasClassName(elmt, className); });
		}
	],
	[
		new RegExp('^(' + $$_tagRegEx + ')?#[-_\\w\\d]+$', 'i'), //id
		function(selector, container) {
			var idName = selector.match(/#[-_\w\d]+$/)[0].substr(1), obj = $(idName);
			selector = selector.replace('#' + idName, '');
			if (selector.length > 0 && obj.nodeName.toLowerCase() != selector) return [];
			else return [ obj ];
		}
	],
	[
		new RegExp('^(.+?)?>.+$'), //Direct children of parent
		function(selector, container) {
			var selectors = selector.split('>'), set = [];
			if (selectors[0] != '') container = __$$(selectors[0], container);
			else container = [ container ];
			for(var i = 0, ln = container.length; i < ln; i++) {
				selector = __$$(selectors[1], container[i]);
				for(var j = 0, len = selector.length; j < len; j++) {
					if (selector[j].parentNode == container[i]) set.push(selector[j]);
				}
			}
			return set;
		}
	]
]



throwError = function(err, loud, email) {
	var server_sig = $('server_sig');
	if (!server_sig) email = false;
	throwError.log.push(err);
	if (loud) alert(err);
}

throwError.log = [];

duplicate = function(obj, asArray) {
	var newObj = asArray ? [] : {};
	if (asArray) {
		for(var x = 0, ln = obj.length; x < ln; x++) newObj[x] = obj[x];
	} else {
		for(var x in obj) {
			if (typeof obj[x] == 'object') newObj[x] = duplicate(obj[x]);
			else newObj[x] = obj[x];
		}
	}
	return newObj;
}

duplicate = function(obj, asArray) {
	var newObj = asArray ? [] : {};
	if (asArray) {
		for(var x = 0, ln = obj.length; x < ln; x++) newObj[x] = obj[x];
	} else {
		for(var x in obj) {
			if (typeof obj[x] == 'object') newObj[x] = duplicate(obj[x]);
			else newObj[x] = obj[x];
		}
	}
	return newObj;
}

mergeCopy = function() {
	for(var i = 0, ln = arguments.length, args = []; i < ln; i++) args[i] = arguments[i];
	args.unshift([]);
	return merge.apply(window, args);
}

merge = function() {
	for(var i = 0, ln = arguments.length, args = []; i < ln; i++) args[i] = arguments[i];
	for(var x, i = 1, ln = args.length; i < ln; i++) {
		for(x in  args[i]) {
			if (typeof x == 'number' || x.match(/^[0-9]+$/)) args[0][args[0].length] = args[i][x];
			else if (typeof args[i][x] != 'object' || typeof args[0][x] != 'object') args[0][x] = args[i][x];
			else args[0][x] = merge(args[0][x], args[i][x]);
		}
	}
	return args[0];
}

equals = function(val1, val2) {
	if (typeof val1 != 'object' && typeof val2 != 'object' && val1 != val2) return false;
	if (val1.length != val2.length) return false;
	
	for(var i = 0, ln = this.length; i < ln; i++) {
		if (typeof val1[i] != typeof val2[i]) return false;
		if (val1[i] !== val2[i]) return false;
	}
	
	return true;
}

contains = function(arr, arg) {
	var i;
	for(i = 0; i < arr.length; i++) {
		if (typeof arg == 'object') {
			if (arr[i].equals(arg)) return true;
		} else {
			if (typeof arr[i] == 'string') {
				if (arr[i].toLowerCase() == arg.toLowerCase()) return true;
			} else if (arr[i] === arg) {
				return true;
			}
		}
	}
	return false;
}



function parseURL(url) {
	if (!url) url = window.location.href;
	var urlObject = {};
	url = url.split('?')[1];
	if (!url) return urlObject;

	url = url.split(/&/g);
	for(var i = 0, ln = url.length; i < ln; i++) {
		url[i] = url[i].split('=');
		urlObject[url[i][0]] = url[i][1];
	}
	return urlObject;
}

function unparseURL(urlObj) {
	var queryStr = [], i = 0;
	for(var x in urlObj) queryStr[i++] = x + '=' + urlObj[x];
	return '?' + queryStr.join('&');
}


var Cabinet = function(container, options) {
	container = $(container);
	if (container.cabinet) return container.cabinet;
	else container.cabinet = this;

	this.container = $(container);
	this.options = merge({
		drawerClassName : 'drawer',
		drawer : {
			closeable : false
		}
	}, options || {});

	var drawers = $$('.' + this.options.drawerClassName, this.container), selectedDrawer = parseURL().urlHash;
	this.drawers = [];
	for(var d, i = 0, ln = drawers.length; i < ln; i++) {
		d = new Drawer(drawers[i], this.options.drawer, this);
		this.drawers.push(d);
		if (selectedDrawer && d.name == selectedDrawer) selectedDrawer = i;
	}

	if (typeof selectedDrawer == 'number') selectedDrawer = this.drawers[selectedDrawer];
	else selectedDrawer = this.drawers[0];
	selectedDrawer.open();
	this.currentDrawer = selectedDrawer;
}

Cabinet.prototype = {
	constructor : Cabinet,

	openDrawer : function(which) {
		if (!typeof which == 'number') {
			for(var i = 0, ln = this.drawers.length; i < ln; i++) if (this.drawers[i].name == which) break;
			which = i;
		}
		if (this.currentDrawer === which) return;

		if (this.currentDrawer !== false) this.drawers[this.currentDrawer].close();
		this.drawers[which].open();
		this.currentDrawer = this.drawers[which];
	}
}




//Drawers

var Drawer = function(container, options, parent) {
	var self = this;

	container = $(container);
	if (container.drawer) return container.drawer;
	else container.drawer = this;

	this.container = container;
	this.name = this.container.id;
	this.parent = parent;

	this.options = merge({
		closeable : true,
		handleClass : 'handle',
		contentClass : 'content',
		heightClass : 'height',
		openClass : 'open',
		onFinish : false,
		onOpen : false,
		onClose : false,
		styleFaderOptions : {
			originalStyleDef : { height : '0px' },
			activeStyleDef : { height : '0px' }
		}
	}, options || {});

	this.options.styleFaderOptions.onFinish = function() {
		self.position = self.direction;
		self.direction = 0;
		if (self.options.onFinish) self.options.onFinish.call(self);
		if (self.position == 1) {
			if (self.options.onOpen) self.options.onOpen.call(self);
			addClassName(self.handle, self.options.handleClass + '_' + self.options.openClass);
		}
		if (self.position == -1) {
			if (self.options.onClose) self.options.onClose.call(self);
			removeClassName(self.handle, self.options.handleClass + '_' + self.options.openClass);
		}
	}

	this.handle = $$('.' + this.options.handleClass, this.container)[0];
	this.content = $$('.' + this.options.contentClass, this.container)[0];
	this.styleFader;

	this.direction = 0;
	this.position = -1;

	Event.addListener(this.handle, 'click', function(e) { self.toggle(); });
	this.styleFader = new StyleFader(this.content, this.options.styleFaderOptions);

	this.getContentSize();
}

Drawer.prototype = {
	constructor : Drawer,

	toggle : function() {
		if (this.direction == -1 || this.position == -1) {
			if (this.parent) {
				if (this.parent.currentDrawer) this.parent.currentDrawer.close();
				this.parent.currentDrawer = this;
			}
			this.open();
		} else if (this.options.closeable) this.close();
	},

	open : function() {
		this.direction = 1;
		this.position = 0;
		this.styleFader.ascend();
	},

	close : function() {
		this.direction = -1;
		this.position = 0;
		this.styleFader.descend();
	},

	getContentSize : function() {
		var heightCheck = $$('.' + this.options.heightClass, this.content)[0], height;
		if (!heightCheck) heightCheck = this.content;
		this.contentSize = heightCheck.offsetHeight || heightCheck.clientHeight || 300;

		this.styleFader.changeStyleDef('active', { height : this.contentSize });
		return this.contentSize;
	}
}




StyleFader = function(element, options) {
	this.options = merge({
		time : 15,
		originalStyleDef : {},
		activeStyleDef : {},
		equationTemplate : 'linear',
		onFinish : false
	}, options || {});

	if (element) element.styleFader = this;
	this.element = element;

	var eqs = {
		linear : 'X',
		sine : '-1 * Math.cos(X*Math.PI)/2 + .5',
		exponential : 'Math.pow(X, 2)'
	}

	if (eqs[this.options.equationTemplate]) this.options.equationTemplate = eqs[this.options.equationTemplate];

	this.isIE = browserIsIE();

	this.__cycle = false;
	this.cycleOptions = [ 'ascend', 'descend' ];

	this.fadeTimer = false;
	this.elapsedTime = 0;
	this.finishTimer = false;

	this.currentDirection = 0;
	this.currentPosition = -1;

	this.changeStyleDef();
}

StyleFader.prototype = {
	constructor : StyleFader,

	ascend : function(elmt) {
		this.cycleStop();
		this.start(elmt, 1);
	},
	
	descend : function(elmt) {
		this.cycleStop();
		this.start(elmt, -1);
	},

	start : function(elmt, dir) {
		if (!dir) dir = (this.currentPosition + this.currentDirection) * -1;
		if (this.fadeTimer || this.currentPosition == dir * (-1)) {
			this.setEquation();
			this.element.innerHTML = this.equation;
			this.currentDirection = dir;
			this.currentPosition = 0;
			if (!this.fadeTimer) this.cont(elmt);
		}
	},

	stop : function() {
		clearInterval(this.fadeTimer);
		this.fadeTimer = false;
	},

	cont : function(elmt) {
		var self = this;
		if (!elmt) elmt = this.element;
		this.fadeTimer = setInterval(function() {
			self.increment(elmt);
			self.checkTime();
		}, 10);
	},
	
	cycleStart : function(elmt) {
		this.__cycle = elmt || this.element;
		if (!this.fadeTimer) {
			if (this.currentDirection != 0) this.cont(elmt);
			else this.start(elmt, this.currentPosition * -1);
		}
	},

	cycleStop : function() {
		this.__cycle = false;
	},
	
	checkTime : function() {
		if (this.elapsedTime <= 0 || this.elapsedTime >= this.options.time) this.finish();
	},

	finish : function() {
		this.stop();
		this.currentPosition = this.currentDirection;
		this.currentDirection = 0;
		if (this.elapsedTime < 0) this.elapsedTime == 0;
		else if (this.elapsedTime > this.options.time) this.elapsedTime = this.options.time;

		if (this.__cycle) {
			this.start(this.__cycle, this.currentPosition * -1);
		} else if (this.options.onFinish) {
			this.options.onFinish.call(this);
		}
	},
	
	increment : function(elmt) {
		var startSet, finishSet, newDef = {};
		this.elapsedTime += this.currentDirection*1;

		for(var x in this.options.originalStyleDef) newDef[x] = this.getValue(this.options.originalStyleDef[x], this.options.activeStyleDef[x]);
		this.setStyles(elmt, newDef);
	},
	
	setStyles : window.setStyles,

	getValue : function(start, end) {
		var newValue, type, unit;
		if (typeof start == 'object') {
			newValue = [];
			for(var y in start) newValue.push(this.validizeValue(this.applyEquation(start[y], end[y]), start[y], end[y]) + '%');
			if (newValue.length == 3) type = 'rgb';
			else type = 'rgba';
			newValue = type + '(' + newValue.join(',') + ')';
		} else {
			start += '';
			end += '';

			unit = start.match(/^[0-9.]+[^0-9]+$/);
			if (unit != null) unit = start.substring(start.indexOf(start.match(/[^0-9.]/)));
			else unit = '';

			start = parseFloat(start);
			end = parseFloat(end);
			newValue = this.validizeValue(this.applyEquation(start, end), start, end) + unit;
		}
		return newValue;
	},

	applyEquation : function(Yi, Yf) { //time is x axis, unit is y axis. Template equations have limits 0,0 and 1,1
		return eval(this.equation.replace(/X/g, this.elapsedTime));
	},

	setEquation : function() { //Set the equation every time we start an action, just in case the parameters have been changed.
		//this.equation = this.options.equationTemplate.replace(/Xf/g, this.options.time);
		var fx = this.options.equationTemplate.replace(/X/g, '(X/' + this.options.time + ')');
		this.equation = fx + ' + (Yf - Yi + (' + fx.replace(/X/g, '0') + ') - (' + fx.replace(/X/g, this.options.time) + ')) * X / ' + this.options.time + ' + Yi - (' + fx.replace(/X/g, '0') + ')'; //Shift y axis to accommodate values
	},

	validizeValue : function(value, start, end) {
		var upperLimit, lowerLimit;
		if (end - start <= 0) {
			upperLimit = start;
			lowerLimit = end;
		} else {
			upperLimit = end;
			lowerLimit = start;
		}

		if (value < lowerLimit) return lowerLimit;
		else if (value > upperLimit) return upperLimit;
		else return value;
	},

	changeStyleDef : function(which, props) {
		if (typeof which != 'undefined') this.options[which + 'StyleDef'] = merge(this.options[which + 'StyleDef'], props);

		for(var x in this.options.originalStyleDef) {
			if (x.indexOf('olor') != -1 || (x.indexOf('pacity') != -1 && this.isIE && this.options.originalStyleDef[x] <= 1)) { //if the value needs special attention....
				if (x.indexOf('olor') != -1) {
					this.options.originalStyleDef[x] = this.convertColorToRGBPercent(this.options.originalStyleDef[x]);
					this.options.activeStyleDef[x] = this.convertColorToRGBPercent(this.options.activeStyleDef[x]);
				} else {
					this.options.originalStyleDef[x] = this.options.originalStyleDef[x] * 100;
					this.options.activeStyleDef[x] = this.options.activeStyleDef[x] * 100;
				}
			}
		}
	},

	convertColorToRGBPercent : function(color) {
		if (typeof color == 'object') return color;

		if (color.indexOf('rgb') != -1) {
			color = color.substring(color.indexOf('(') + 1*1, color.lastIndexOf(')')).split(',');
			for(var x in color) {
				if (color[x].indexOf('%') != -1) color[x] = parseInt(color[x]);
				else color[x] = parseInt(color[x])/255 * 100;
			}
		} else if (color.indexOf('#') != -1) {
			color = color.substring(1);
			color = [ color.slice(0,2), color.slice(2,4), color.slice(4) ];
			for(var x in color) color[x] = parseInt(color[x],16)/255 * 100;
		} else {
			return throwError('global::StyleFader::convertColorToRGBPercent - color is not in acceptable format');
		}
		return color;
	}
}
