
/**************************************************************/
/***** Console Functions **************************************/
/**************************************************************/

if(!window.console)
{
	window.console =
	{
		log: function(){}
	};
}

function log()
{
	console.log.apply(console, arguments);
}

/**************************************************************/
/***** Common Functions ***************************************/
/**************************************************************/

function $A(a)
{
	var tmp = [];
	for(var i = 0, l = a.length; i < l; i++)
		tmp[i] = a[i];
	return tmp;
};

function $extend(a, b)
{
	for(var p in b)
		a[p] = b[p];
	return a;
};

function $merge()
{
	var m = {};
	$A(arguments).each(function(o)
	{
		for(var p in o)
		{
			var op = m[p], np = o[p];
			if(op && typeof op == 'object' && typeof np == 'object' && !op.push)
				m[p] = $merge(op, np);
			else
				m[p] = o[p];
		}
	});
	return m;
};

function $native()
{
	for(var i = 0, l = arguments.length; i < l; i++)
		arguments[i].extend = function(o)
		{
			for(var p in o)
				if(!this.prototype[p])
					this.prototype[p] = o[p];
		};
};
$native(Array, Function, Number, String);

function $stopEvent(e)
{
	e = e || window.event;
	if(e.stopPropagation)
		e.stopPropagation();
	else
		e.cancelBubble = true;
	if(e.preventDefault)
		e.preventDefault();
	else
		e.returnValue = false;
	return e;
};

/**************************************************************/
/***** Compatibility ******************************************/
/**************************************************************/

window.ie = !!window.ActiveXObject;

window.safari = (/Safari|Khtml/i).test(navigator.userAgent);

/**************************************************************/
/***** Class **************************************************/
/**************************************************************/

var Class = function(o)
{
	var k = function()
	{
		return this.init ? this.init.apply(this, arguments) : this;
	};
	$extend(k, this);
	k.prototype = o || {};
	k.prototype.setOptions = Class.setOptions;
	k.constructor = Class; return k;
};

Class.setOptions = function(o)
{
	this.options = $merge(this.options, o || {});
};

/**************************************************************/
/***** Extend Functions ***************************************/
/**************************************************************/

Array.extend(
{
	contains: function(i)
	{
		return this.indexOf(i) != -1;
	},
	
	filter: function(fn)
	{
		var tmp = [];
		for(var i = 0, l = this.length; i < l; i++)
			if(fn.call(this, this[i], i, this))
				tmp.push(this[i]);
		return tmp;
	},
	
	forEach: function(fn)
	{
		for(var i = 0, l = this.length; i < l; i++)
			fn.call(this, this[i], i, this);
		return this;
	},
	
	getLast: function()
	{
		return this[this.length - 1] || null;
	},
	
	indexOf: function(item)
	{
		for(var i = 0, l = this.length; i < l; i++)
			if(this[i] == item)
				return true;
		return false;
	}
});
Array.prototype.each = Array.prototype.forEach;

Function.extend(
{
	periodical: function(t, b)
	{
		var f1 = this;
		var f2 = function()
		{
			return f1.apply(b || window);
		}
		return setInterval(f2, t);
	}
});

String.extend(
{
	camelCase: function()
	{
		return this.replace(/\-\w/g, function(m)
		{
			return m.charAt(1).toUpperCase();
		});
	},

	contains: function(t, s)
	{
		s = s || '';
		return (s + this + s).indexOf(s + t + s) != -1;
	},
	
	hyphenate: function()
	{
		return this.replace(/[A-Z]/g, function(m)
		{
			return '-' + m.charAt(0).toLowerCase();
		});
	},
	
	toFloat: function()
	{
		return parseFloat(this);
	}
});

Number.prototype.toFloat = String.prototype.toFloat;

/**************************************************************/
/***** Element Extend Functions *******************************/
/**************************************************************/

var El = function(t, o)
{
	t = t || 'div'; o = o || {};
	var el = $(document.createElement(t));
	for(var p in o)
		el[p] = o[p];
	return el;
};

El.prototype =
{
	addClass: function(cl)
	{
		if(!this.hasClass(cl))
			this.className += ' '+ cl;
		return this;
	},

	appendTo: function(p)
	{
		p.appendChild(this);
		return this;
	},
	
	getByClass: function(cl, t)
	{
		if(document.evaluate)
		{
			var expr = (t || '*') + '[contains(concat(" ", @class, " "), " ' + cl + ' ")]';
			var xpath = document.evaluate('.//' + expr, this, null, 7, null);
			var tmp = [];
			for(var i = 0, l = xpath.snapshotLength; i < l; i++)
				tmp[i] = xpath.snapshotItem(i);
			return tmp;
		}
		else
		{
			var filterClass = function(el)
			{
				return $(el).hasClass(cl);
			};
			return this.getByTag(t || '*').filter(filterClass);
		}
	},
	
	getCoords: function()
	{
		var el = this, t = el.offsetTop, l = el.offsetLeft;
		while(el = el.offsetParent)
		{
			t += el.offsetTop; l += el.offsetLeft;
		}
		return {t: t, l: l, h: this.offsetHeight, w: this.offsetWidth};
	},
	
	getByTag: function(t)
	{
		return $A(this.getElementsByTagName(t || '*'));
	},
	
	getHeight: function(hidden)
	{
		return this[hidden ? 'scrollHeight' : 'offsetHeight']
		- (this.getStyle('borderTopWidth').toFloat() || 0)
		- (this.getStyle('borderBottomWidth').toFloat() || 0)
		- this.getStyle('paddingTop').toFloat()
		- this.getStyle('paddingBottom').toFloat();
	},
	
	getStyle: function(p, ps)
	{
		var v = this.style[p.camelCase()];
		if(document.defaultView && (v == '' || ps))
			v = document.defaultView.getComputedStyle(this, ps || null).getPropertyValue(p.hyphenate());
		else if(this.currentStyle && v == '')
			v = this.currentStyle[p.camelCase()];
		if(v == 'auto' && p == 'height')
			return this.getHeight();
		if(v == NaN)
			return 0;
		return v;
	},
	
	hasClass: function(cl)
	{
		return this.className && this.className.contains(cl, ' ');
	},
	
	hide: function(o)
	{
		if(this.offsetHeight == undefined || this.offsetHeight == 0)
			return;
		
		o = $merge(
		{
			duration: 600,
			onComplete: function(){},
			height: true,
			opacity: true
		}, o);
		
		//get height
		var h = this.getHeight(true);
		
		//set up styles
		if(o.overflow)
			this.style.overflow = 'hidden';
		if(o.opacity)
		{
			if(window.ie)
				this.style.filter = 'alpha(opacity=100)';
			else
				this.style.opacity = 1;
		}
		if(o.height)
			this.style.height = h + 'px';
		this.style.display = 'block';
		
		var fxObj = {};
		if(o.opacity)
			fxObj['opacity'] = [1, 0];
		if(o.height)
		{
			fxObj['height'] = [h, 0];
			['Top', 'Bottom'].each(function(d)
			{
				fxObj['margin' + d] = [0];
				fxObj['padding' + d] = [0];
			});
		}
		
		//start animation
		new Anim(this,
		{
			overflow: o.duration,
			overflow: o.overflow,
			onComplete: function(el)
			{
				el.style.display = 'none';
				el.style.opacity = '';
				el.style.height = '';
				el.style.overflow = '';
				['Top', 'Bottom'].each(function(d)
				{
					el.style['margin' + d] = '';
					el.style['padding' + d] = '';
				});
				o.onComplete.call(this, el);
			}
		}).start(fxObj);
	},

	htmlElement: true,
	
	remove: function()
	{
		this.parentNode.removeChild(this);
	},

	removeClass: function(cl)
	{
		this.className = (' '+ this.className +' ').replace(' '+ cl +' ', ' ');
		return this;
	},
	
	setStyles: function(o)
	{
		for(var p in o)
			this.style[p] = o[p];
		return this;
	},
	
	show: function(o)
	{
		//check if hidden
		if(this.offsetHeight && this.offsetHeight != 0)
			return;
		
		o = o || {};
		
		o = $merge(
		{
			duration: 600,
			onComplete: function(){},
			overflow: true,
			height: true,
			opacity: true,
			transition: function(i)
			{
				return (i <= 0.5) ? Math.pow((2 * i), 4) / 2 : (2 - Math.pow((2 * (1 - i)), 4)) / 2;
			}
		}, o);
		
		//get height & other stuff
		var c = $(this.cloneNode(true)).setStyles(
		{
			visibility: 'hidden', position: 'absolute', display: 'block', top: 0, left: 0, bottom: '', right: ''
		}).appendTo(this.parentNode);
		var h = c.getHeight();
		var fxObj = {};
		if(o.opacity)
			fxObj['opacity'] = [0, 1];
		if(o.height)
		{
			fxObj['height'] = [0, h];
			['Top', 'Bottom'].each(function(d)
			{
				fxObj['margin' + d] = [0, parseInt(c.getStyle('margin' + d))];
				fxObj['padding' + d] = [0, parseInt(c.getStyle('padding' + d))];
			});
		}
		
		c.remove();

		//set up styles
		var el = this;
		if(o.overflow)
			this.style.overflow = 'hidden';
		if(o.opacity)
		{
			if(window.ie)
				this.style.filter = 'alpha(opacity=0)';
			else
				this.style.opacity = 0;
		}
		if(o.height)
		{
			this.style.height = 0;
			['Top', 'Bottom'].each(function(d)
			{
				el.style['margin' + d] = 0;
				el.style['padding' + d] = 0;
			});
		}
		this.style.display = 'block';

		console.log(o, fxObj);
		//start animation
		new Anim(this,
		{
			duration: o.duration,
			overflow: o.overflow,
			transition: o.transition,
			onComplete: function(el)
			{
				el.style.opacity = '';
				el.style.height = '';
				el.style.overflow = '';
				['Top', 'Bottom'].each(function(d)
				{
					el.style['margin' + d] = '';
					el.style['padding' + d] = '';
				});
				o.onComplete.call(this, el);
				
			}
		}).start(fxObj);
	}
};

El.extend = function(o)
{
	$extend(El.prototype, o);
};

/**************************************************************/
/***** Event Listeners ****************************************/
/**************************************************************/

var Listeners =
{
	addEvent: function(t, fn)
	{
		if(this.addEventListener)
			this.addEventListener(t, fn, false);
		else
			this.attachEvent('on'+ t, fn);
		return this;
	},
	
	removeEvent: function(t, fn)
	{
		if(this.removeEventListener)
			this.removeEventListener(t, fn, false);
		else
			this.detachEvent('on'+ t, fn);
		return this;
	}
};

$extend(window, Listeners);
El.extend(Listeners);

/**************************************************************/
/***** Element Functions **************************************/
/**************************************************************/

function $(el)
{
	if(!el && el != 0)
		return null;
	if(el.htmlElement)
		return el;
	if(typeof el == 'string')
		el = document.getElementById(el);
	return (el) ? ((el.htmlElement) ? el : $extend(el, El.prototype)) : false;
};

/**************************************************************/
/***** Animation Functions ************************************/
/**************************************************************/

function $m(e, el)
{
	e = e || window.event;
	var r = e.relatedTarget;
	return (r != el && $A(el.getElementsByTagName('*')).indexOf(r) == -1);
};

var Anim = new Class(
{
	options:
	{
		duration: 600,
		overflow: true,
		onComplete: function(){},
		transition: function(i)
		{
			return (i <= 0.5) ? Math.pow((2 * i), 4) / 2 : (2 - Math.pow((2 * (1 - i)), 4)) / 2;
		}
	},
	init: function(el, o)
	{
		this.el = $(el);
		this.setOptions(o);
		return this;
	},
	start: function(o)
	{
		this.stop();
		this.now = {}; this.from = {}; this.to = {};
		for(var p in o)
		{
			var from = o[p][0], to = o[p][1];
			if(!to && to != 0)
				to = from, from = parseFloat(this.el.getStyle(p));
			this.from[p] = from; this.to[p] = to;
		}
		if(this.options.overflow)
			this.el.style.overflow = 'hidden';
		this.startTime = new Date().getTime();
		this.timer = this.step.periodical(1000/60, this);
	},
	
	calc: function()
	{
		for(var p in this.from)
		{
			var d = this.options.transition(this.d);
			this.now[p] = (this.to[p] - this.from[p]) * d + this.from[p];
		}
	},
	set: function()
	{
		for(var p in this.now)
		{
			var unit = (['opacity', 'zindex'].contains(p.toLowerCase())) ? '' : 'px';
			if(p.contains('color'))
				this.now[p] = parseInt(this.now[p]);
			if(p == 'opacity' && window.ie)
				this.el.style['filter'] = 'alpha(opacity='+ parseInt(this.now[p] * 100) +')';
			this.el.style[p] = this.now[p] + unit;
		}
	},
	
	step: function()
	{
		var time = new Date().getTime();
		if(time < this.startTime + this.options.duration)
		{
			this.d = (time - this.startTime) / this.options.duration;
			this.calc(); this.set();
		}
		else
		{
			this.stop();
			this.now = this.to;
			this.set();
			this.options.onComplete.call(this, this.el);
		}
	},
	stop: function()
	{
		clearInterval(this.timer);
	}
});

Anim.Slide = new Class(
{
	options:
	{
		duration: 600
	},
	init: function(el, o)
	{
		this.el = $(el);
		this.setOptions(o);
		
		this.wrap = new El('div');
		this.el.parentNode.insertBefore(this.wrap, this.el);
		this.wrap.appendChild(this.el);
		this.wrap.setStyles(
		{
			margin: this.el.getStyle('marginTop'), overflow: 'hidden'
		});
		this.el.style['marginTop'] = 0;
		
		this.elFx = new Anim(this.el, { duration: this.options.duration });
		this.wrapFx = new Anim(this.wrap, { duration: this.options.duration });
		
		return this;
	},
	
	slideIn: function()
	{
		var h = this.el.offsetHeight;
		this.start(-h, 0);
	},
	slideOut: function()
	{
		var h = this.el.offsetHeight;
		this.start(0, h);
	},
	toggle: function()
	{
		if(this.wrap.offsetHeight == 0)
			this.slideOut();
		else
			this.slideIn();
	},
	
	start: function(el, wrap)
	{
		this.stop();
		this.elFx.start(
		{
			marginTop: [el]
		});
		this.wrapFx.start(
		{
			height: [wrap]
		});
	},
	stop: function()
	{
		this.elFx.stop(); this.wrapFx.stop();
	}
});

/**************************************************************/
/***** Window onload Functions ********************************/
/**************************************************************/

var domready = (window.addEventListener && !window.safari && !window.opera) ? 'DOMContentLoaded' : 'load'

/**************************************************************/
/**************************************************************/

