/* dom extend by mootools */
function $time(){
	return new Date().getTime()
}

function $clear(timer){
	clearTimeout(timer);
	clearInterval(timer);
	return null
}

function $merge(){
	var mix = {};
	for (var i = 0; i < arguments.length; i++){
		for (var property in arguments[i]){
			var ap = arguments[i][property];
			var mp = mix[property];
			if (mp && typeof(ap) == 'object' && typeof(mp) == 'object') mix[property] = $merge(mp, ap);
			else mix[property] = ap
		}
	}
	return mix
}

function $Merge(previous, current){
	if (previous && previous != current){
		var type = typeof (current);
		if (type != typeof(previous)) return current;
		switch(type){
			case 'function':
				var merged = function(){
					this.parent = arguments.callee.parent;
					return current.apply(this, arguments)
				};
				merged.parent = previous;
				return merged;
			case 'object': return $merge(previous, current);
		}
	}
	return current
}

function $chk(obj){
	return !!(obj || obj === 0);
};

function $pick(obj, picked){
	return (obj!=undefined) ? obj : picked;
};

var $clone = function(constructor, func){
	var newObj = constructor || function(){};
	var properties = new func(null);
	for (var property in properties){
		if(typeof this[property] == 'object'){
			$clone(constructor, this[property]);
		}else{
			newObj.prototype[property] = properties[property];
		}
	}
	return newObj;
};

var $extend = function(){
	var args = arguments;
	if (!args[1]) args = [this, args[0]];
	for (var property in args[1]) args[0][property] = args[1][property];
	return args[0];
};
var $extendObj = function(){
	for (var i = 0, l = arguments.length; i < l; i++){
		arguments[i].extend = $extend;
	}
};
$extendObj(window, document);

var $native = function(){
	for (var i = 0, l = arguments.length; i < l; i++){
		arguments[i].extend = function(props){
			for (var prop in props){
				if (!this.prototype[prop]) this.prototype[prop] = props[prop];
				if (!this[prop]) this[prop] = $native.generic(prop);
			}
		};
	}
};
$native.generic = function(prop){
	return function(bind){
		return this.prototype[prop].apply(bind, Array.prototype.slice.call(arguments, 1));
	};
};
$native(Function, Array, String, Number);

Function.extend({
	create: function(options){
		var fn = this;
		options = $merge({
			'bind': fn,
			'event': false,
			'arguments': null,
			'delay': false,
			'periodical': false,
			'attempt': false
		}, options);
		if ($chk(options.arguments) && typeof(options.arguments) != 'array') options.arguments = [options.arguments];
		return function(event){
			var args;
			if (options.event){
				event = event || window.event;
				args = [(options.event === true) ? event : new options.event(event)];
				if (options.arguments) args.extend(options.arguments);
			}
			else args = options.arguments || arguments;
			var returns = function(){
				return fn.apply($pick(options.bind, fn), args);
			};
			if (options.delay) return setTimeout(returns, options.delay);
			if (options.periodical) return setInterval(returns, options.periodical);
			if (options.attempt) try {return returns();} catch(err){return false;};
			return returns();
		};
	},

	delay: function(delay, bind, args){
		var fn = this;
		return setTimeout(function(){return fn.apply(bind || fn, args || arguments);}, delay);
	},

	periodical: function(interval, bind, args){
		var fn = this;
		return setInterval(function(){return fn.apply(bind || fn, args || arguments);}, interval);
	},
	
	clone: function(constructor){
		return $clone(constructor, this);//.cloneFunction();
	},
	
	extend: function(properties){
		var Class = this.clone(properties.constructor);
		for (var property in properties){
			Class.prototype[property] = $Merge(Class.prototype[property], properties[property]);
		}
		return Class;
	}

});
Array.extend({
	map: function(fn, bind){
		var results = [];
		for (var i = 0, j = this.length; i < j; i++) results[i] = fn.call(bind, this[i], i, this);
		return results;
	},
	remove: function(item){
		var i = 0;
		var len = this.length;
		while (i < len){
			if (this[i] === item){
				this.splice(i, 1);
				len--;
			} else {
				i++;
			}
		}
		return this;
	},
	indexOf: function(item, from){
		var len = this.length;
		for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
			if (this[i] === item) return i;
		}
		return -1;
	},
	contains: function(item, from){
		return this.indexOf(item, from) != -1;
	},
	rgbToHex: function(array){
		if (this.length < 3) return false;
		if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
		var hex = [];
		for (var i = 0; i < 3; i++){
			var bit = (this[i] - 0).toString(16);
			hex.push((bit.length == 1) ? '0' + bit : bit);
		}
		return array ? hex : '#' + hex.join('');
	},
	hexToRgb: function(array){
		if (this.length != 3) return false;
		var rgb = [];
		for (var i = 0; i < 3; i++){
			rgb.push(parseInt((this[i].length == 1) ? this[i] + this[i] : this[i], 16));
		}
		return array ? rgb : 'rgb(' + rgb.join(',') + ')';
	}
});
String.extend({
	hyphenate: function(){
		return this.replace(/\w[A-Z]/g, function(match){
			return (match.charAt(0) + '-' + match.charAt(1).toLowerCase());
		});
	},
	contains: function(string, s){
		return (s) ? (s + this + s).indexOf(s + string + s) > -1 : this.indexOf(string) > -1;
	},
	camelCase: function(){
		return this.replace(/-\D/g, function(match){
			return match.charAt(1).toUpperCase();
		});
	},
	test: function(regex, params){
		return ((typeof(regex) == 'string') ? new RegExp(regex, params) : regex).test(this);
	},
	rgbToHex: function(array){
		var rgb = this.match(/\d{1,3}/g);
		return (rgb) ? rgb.rgbToHex(array) : false;
	},
	hexToRgb: function(array){
		var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
		return (hex) ? hex.slice(1).hexToRgb(array) : false;
	}
	,escapeRegExp: function(){
		return this.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
	}
});
Number.extend({
	limit: function(min, max){
		return Math.min(max, Math.max(min, this));
	}
});

window.xpath = !!(document.evaluate);
if (window.ActiveXObject) window.ie = window[window.XMLHttpRequest ? 'ie7' : 'ie6'] = true;
else if (document.childNodes && !document.all && !navigator.taintEnabled) window.webkit = window[window.xpath ? 'webkit420' : 'webkit419'] = true;
else if (document.getBoxObjectFor != null) window.gecko = true;


if (typeof HTMLElement == 'undefined'){
	var HTMLElement = function(){};
	//if (window.webkit) document.createElement("iframe"); //fixes safari
	//HTMLElement.prototype = (window.webkit) ? window["[[DOMElement.prototype]]"] : {};
};
HTMLElement.prototype.htmlElement = function(){};

if (window.ie6) try {document.execCommand("BackgroundImageCache", false, true);} catch(e){};

var Element = function(){};
Element.extend = function(properties){
	for (var property in properties){
		HTMLElement.prototype[property] = properties[property];
		Element.prototype[property] = properties[property];
		Element[property] = $native.generic(property);
	}
};

function _$(el){
	if (!el) return null;
	if ([window, document].contains(el)) return el;
	var type = typeof(el);
	if (type == 'string'){
		el = document.getElementById(el);
		type = (el) ? 'element' : false;
	}
	if (type != 'element' && type != 'object') return null;
	if (['object', 'embed'].contains(el.tagName.toLowerCase())) return el;
	$extend(el, Element.prototype);
	el.htmlElement = function(){};
	el.$tmp = {'opacity': 1};
	return el;
};

Element.Methods = {
	Listeners: {
		addListener: function(type, fn){
			if (this.addEventListener) this.addEventListener(type, fn, false);
			else this.attachEvent('on' + type, fn);
			return this;
		},

		removeListener: function(type, fn){
			if (this.removeEventListener) this.removeEventListener(type, fn, false);
			else this.detachEvent('on' + type, fn);
			return this;
		}
	}
};

window.extend(Element.Methods.Listeners);
document.extend(Element.Methods.Listeners);
Element.extend(Element.Methods.Listeners);


var Event = function(){}.extend({

	constructor: function(event){
		if (event && event.$extended) return event;
		this.$extended = true;
		event = event || window.event;
		this.event = event;
		this.type = event.type;
		this.target = event.target || event.srcElement;
		if (this.target.nodeType == 3) this.target = this.target.parentNode;
		this.shift = event.shiftKey;
		this.control = event.ctrlKey;
		this.alt = event.altKey;
		this.meta = event.metaKey;
		if (['DOMMouseScroll', 'mousewheel'].contains(this.type)){
			this.wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
		} else if (this.type.contains('key')){
			this.code = event.which || event.keyCode;
			for (var name in Event.keys){
				if (Event.keys[name] == this.code){
					this.key = name;
					break;
				}
			}
			if (this.type == 'keydown'){
				var fKey = this.code - 111;
				if (fKey > 0 && fKey < 13) this.key = 'f' + fKey;
			}
			this.key = this.key || String.fromCharCode(this.code).toLowerCase();
		} else if (this.type.test(/(click|mouse|menu)/)){
			this.page = {
				'x': event.pageX || event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft),
				'y': event.pageY || event.clientY + (document.documentElement.scrollTop ||document.body.scrollTop)
			};
			this.client = {
				'x': event.pageX ? event.pageX - window.pageXOffset : event.clientX,
				'y': event.pageY ? event.pageY - window.pageYOffset : event.clientY
			};
			this.offset = {
				'x': event.offsetX || event.layerX,
				'y': event.offsetY || event.layerY
			};
			this.rightClick = (event.which == 3) || (event.button == 2);
			switch(this.type){
				case 'mouseover': this.relatedTarget = event.relatedTarget || event.fromElement; break;
				case 'mouseout': this.relatedTarget = event.relatedTarget || event.toElement;
			}
			this.fixRelatedTarget();
		}
		return this;
	},

	stop: function(){
		return this.stopPropagation().preventDefault();
	},

	stopPropagation: function(){
		if (this.event.stopPropagation) this.event.stopPropagation();
		else this.event.cancelBubble = true;
		return this;
	},

	preventDefault: function(){
		if (this.event.preventDefault) this.event.preventDefault();
		else this.event.returnValue = false;
		return this;
	}

});
Event.fix = {

	relatedTarget: function(){
		if (this.relatedTarget && this.relatedTarget.nodeType == 3) this.relatedTarget = this.relatedTarget.parentNode;
	},

	relatedTargetGecko: function(){
		try {Event.fix.relatedTarget.call(this);} catch(e){this.relatedTarget = this.target;}
	}

};

Event.prototype.fixRelatedTarget = (window.gecko) ? Event.fix.relatedTargetGecko : Event.fix.relatedTarget;

Element.NativeEvents = [
	'click', 'dblclick', 'mouseup', 'mousedown', //mouse buttons
	'mousewheel', 'DOMMouseScroll', //mouse wheel
	'mouseover', 'mouseout', 'mousemove', //mouse movement
	'keydown', 'keypress', 'keyup', //keys
	'load', 'unload', 'beforeunload', 'resize', 'move', //window
	'focus', 'blur', 'change', 'submit', 'reset', 'select', //forms elements
	'error', 'abort', 'contextmenu', 'scroll' //misc
];
Element.Methods.Events = {
	addEvent: function(type, fn){
		var realType = type;
		if (!this.addEventListener) fn = fn.create({'bind': this, 'event': true});
		return (Element.NativeEvents.contains(realType)) ? this.addListener(realType, fn) : this;
	},
	removeEvent: function(type, fn){
		return (Element.NativeEvents.contains(type)) ? this.removeListener(type, fn) : this;
	}

};
window.extend(Element.Methods.Events);
document.extend(Element.Methods.Events);
Element.extend(Element.Methods.Events);

Function.extend({
	bindWithEvent: function(bind, args){
		return this.create({'bind': bind, 'arguments': args, 'event': Event});
	}

});

Element.extend({
	setStyle: function(property, value){
		switch(property){
			case 'opacity': return this.setOpacity(parseFloat(value));
			case 'float': property = (window.ie) ? 'styleFloat' : 'cssFloat';
		}
		property = property.camelCase();
		switch(typeof(value)){
			case 'number': if (!['zIndex', 'zoom'].contains(property)) value += 'px'; break;
			case 'array': value = 'rgb(' + value.join(',') + ')';
		}
		this.style[property] = value;
		return this;
	},
	setOpacity: function(opacity){
		if (opacity == 0){
			if (this.style.visibility != "hidden") this.style.visibility = "hidden";
		} else {
			if (this.style.visibility != "visible") this.style.visibility = "visible";
		}
		if (!this.currentStyle || !this.currentStyle.hasLayout) this.style.zoom = 1;
		if (window.ie) this.style.filter = (opacity == 1) ? '' : "alpha(opacity=" + opacity * 100 + ")";
		this.style.opacity = this.$tmp.opacity = opacity;
		return this;
	},
	getStyle: function(property){
		property = property.camelCase();
		var result = this.style[property];
		if (!$chk(result)){
			if (property == 'opacity') return this.$tmp.opacity;
			result = [];
			for (var style in Element.Styles){
				if (property == style){
					Element.Styles[style].each(function(s){
						var style = this.getStyle(s);
						result.push(parseInt(style) ? style : '0px');
					}, this);
					if (property == 'border'){
						var every = result.every(function(bit){
							return (bit == result[0]);
						});
						return (every) ? result[0] : false;
					}
					return result.join(' ');
				}
			}
			if (property.contains('border')){
				if (Element.Styles.border.contains(property)){
					return ['Width', 'Style', 'Color'].map(function(p){
						return this.getStyle(property + p);
					}, this).join(' ');
				} else if (Element.borderShort.contains(property)){
					return ['Top', 'Right', 'Bottom', 'Left'].map(function(p){
						return this.getStyle('border' + p + property.replace('border', ''));
					}, this).join(' ');
				}
			}
			if (document.defaultView) result = document.defaultView.getComputedStyle(this, null).getPropertyValue(property.hyphenate());
			else if (this.currentStyle) result = this.currentStyle[property];
		}
		if (window.ie) result = Element.fixStyle(property, result, this);
		if (result && property.test(/color/i) && result.contains('rgb')){
			return result.split('rgb').splice(1,4).map(function(color){
				return color.rgbToHex();
			}).join(' ');
		}
		return result;
	},
	getPosition : function(offsetParent){
		var el = this, left = 0, top = 0;
		do {
			left += el.offsetLeft || 0;
			top += el.offsetTop || 0;
			el = el.offsetParent;
		} while (el && el != offsetParent);
		return {'x': left, 'y': top};
	}
});
Element.fixStyle = function(property, result, element){
	if ($chk(parseInt(result))) return result;
	if (['height', 'width'].contains(property)){
		var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'];
		var size = 0;
		values.each(function(value){
			size += element.getStyle('border-' + value + '-width').toInt() + element.getStyle('padding-' + value).toInt();
		});
		return element['offset' + property.capitalize()] - size + 'px';
	} else if (property.test(/border(.+)Width|margin|padding/)){
		return '0px';
	}
	return result;
};

window.extend({
	getHeight: function(){
		var d = document.documentElement.clientHeight;
		var b = document.body.clientHeight;
		return d < b && d > 0 ? d : b;
	}, 
	getScrollTop: function(){
		return document.documentElement.scrollTop || document.body.scrollTop;
	}
});

var Fx = {};
Fx.Base = function(){
	this.options = {
		onComplete: function(){},
		onStop: function(){},
		transition: function(p){
			return -(Math.cos(Math.PI * p) - 1) / 2;
		},
		duration: 500,
		unit: 'px',
		wait: true,
		fps: 50
	};
	
	this.step = function(){
		var time = $time();
		if (time < this.time + this.options.duration){
			this.delta = this.options.transition((time - this.time) / this.options.duration);
			this.setNow();
			this.increase();
		} else {
			this.stop(true);
			this.set(this.to);
			this.options.onComplete.call(this);
			//this.callChain();
		}
	};

	this.set = function(to){
		this.now = to;
		this.increase();
		return this;
	};

	this.setNow = function(){
		this.now = this.compute(this.from, this.to);
	};

	this.compute =  function(from, to){
		return (to - from) * this.delta + from;
	};
	
	this.start = function(from, to){
		if (!this.options.wait) this.stop();
		else if (this.timer) return this;
		this.from = from;
		this.to = to;
		this.change = this.to - this.from;
		this.time = $time();
		this.timer = this.step.periodical(Math.round(1000 / this.options.fps), this);
		return this;
	};
	
	this.stop = function(end){
		if (!this.timer) return this;
		this.timer = $clear(this.timer);
		if(this.options.onStop)this.options.onStop();
		return this;
	}/*compatibility*/;

	
	return this;
};

Fx.CSS = {

	select: function(property, to){
		if (property.test(/color/i)) return this.Color;
		var type = typeof(to);
		if ((type == 'array') || (type == 'string' && to.contains(' '))) return this.Multi;
		return this.Single;
	},

	parse: function(el, property, fromTo){
		if (!fromTo.push) fromTo = [fromTo];
		var from = fromTo[0], to = fromTo[1];
		if (!$chk(to)){
			to = from;
			from = el.getStyle(property);
		}
		var css = this.select(property, to);
		return {'from': css.parse(from), 'to': css.parse(to), 'css': css};
	}

};
Fx.CSS.Single = {

	parse: function(value){
		return parseFloat(value);
	},

	getNow: function(from, to, fx){
		return fx.compute(from, to);
	},

	getValue: function(value, unit, property){
		if (unit == 'px' && property != 'opacity') value = Math.round(value);
		return value + unit;
	}

};

Fx.CSS.Multi = {

	parse: function(value){
		return value.push ? value : value.split(' ').map(function(v){
			return parseFloat(v);
		});
	},

	getNow: function(from, to, fx){
		var now = [];
		for (var i = 0; i < from.length; i++){ now[i] = fx.compute(from[i], to[i]);};
		return now;
	},

	getValue: function(value, unit, property){
		if (unit == 'px' && property != 'opacity') value = value.map(Math.round);
		return value.join(unit + ' ') + unit;
	}

};
Fx.CSS.Color = {

	parse: function(value){
		return value.push ? value : value.hexToRgb(true);
	},

	getNow: function(from, to, fx){
		var now = [];
		for (var i = 0; i < from.length; i++){ now[i] = Math.round(fx.compute(from[i], to[i]))};
		return now;
	},

	getValue: function(value){
		return value ? 'rgb(' + value.join(',') + ')' : '';
	}

};


Fx.Style = Fx.Base.extend({
	constructor: function(el, property, options){
		this.element =  $(el);
		this.property = property;
		this.options = $merge(this.options, options);

	},
	
	options : {
		fps: 50
	},


	setNow: function(){
		this.now = this.css.getNow(this.from, this.to, this);
	},
	set: function(to){
		this.css = Fx.CSS.select(this.property, to);
		return this.parent(this.css.parse(to));
	},
	start: function(from, to){
		if (this.timer && this.options.wait) return this;
		var parsed = Fx.CSS.parse(this.element, this.property, [from, to]);
		this.css = parsed.css;
		return this.parent(parsed.from, parsed.to);
	},

	increase: function(){
		this.element.setStyle(this.property, this.css.getValue(this.now, this.options.unit, this.property));
	}

});
Fx.Property = Fx.Base.extend({
	constructor: function(el, property, options){
		this.element =  _$(el);
		this.property = property;
		this.options = $merge(this.options, options);
	},
	
	options : {
		fps: 50
	},

	start: function(from, to){
		if (this.timer && this.options.wait) return this;
		return this.parent(from, to);
	},

	increase: function(){
		if(this.now < 0)	this.stop();
		this.element[this.property] = this.now;
	}

});

Fx.Cfn = Fx.Base.extend({
	constructor: function(fn, options, bind){
		this.fn = fn;
		this.options = $merge(this.options, options);
		this.bind = bind;
	},
	
	increase: function(){
		this.fn.call(this.bind, this.now);
	}

});


var Cookie = {

	options: {
		domain: false,
		path: false,
		duration: false,
		secure: false
	},

	/*
	Property: set
		Sets a cookie in the browser.

	Arguments:
		key - the key (name) for the cookie
		value - the value to set, cannot contain semicolons
		options - an object representing the Cookie options. See Options below. Default values are stored in Cookie.options.

	Options:
		domain - the domain the Cookie belongs to. If you want to share the cookie with pages located on a different domain, you have to set this value. Defaults to the current domain.
		path - the path the Cookie belongs to. If you want to share the cookie with pages located in a different path, you have to set this value, for example to "/" to share the cookie with all pages on the domain. Defaults to the current path.
		duration - the duration of the Cookie before it expires, in days.
					If set to false or 0, the cookie will be a session cookie that expires when the browser is closed. This is default.
		secure - Stored cookie information can be accessed only from a secure environment.

	Returns:
		An object with the options, the key and the value. You can give it as first parameter to Cookie.remove.

	Example:
		>Cookie.set('username', 'Harald'); // session cookie (duration is false), or ...
		>Cookie.set('username', 'JackBauer', {duration: 1}); // save this for 1 day

	*/

	set: function(key, value, options){
		options = $merge(this.options, options);
		value = encodeURIComponent(value);
		if (options.domain) value += '; domain=' + options.domain;
		if (options.path) value += '; path=' + options.path;
		if (options.duration){
			var date = new Date();
			date.setTime(date.getTime() + options.duration * 24 * 60 * 60 * 1000);
			value += '; expires=' + date.toGMTString();
		}
		if (options.secure) value += '; secure';
		document.cookie = key + '=' + value;
		return $extend(options, {'key': key, 'value': value});
	},

	/*
	Property: get
		Gets the value of a cookie.

	Arguments:
		key - the name of the cookie you wish to retrieve.

	Returns:
		The cookie string value, or false if not found.

	Example:
		>Cookie.get("username") //returns JackBauer
	*/

	get: function(key){
		var value = document.cookie.match('(?:^|;)\\s*' + key.escapeRegExp() + '=([^;]*)');
		return value ? decodeURIComponent(value[1]) : false;
	},

	/*
	Property: remove
		Removes a cookie from the browser.

	Arguments:
		cookie - the name of the cookie to remove or a previous cookie (for domains)
		options - optional. you can also pass the domain and path here. Same as options in <Cookie.set>

	Examples:
		>Cookie.remove('username') //bye-bye JackBauer, cya in 24 hours
		>
		>var myCookie = Cookie.set('username', 'Aaron', {domain: 'mootools.net'}); // Cookie.set returns an object with all values need to remove the cookie
		>Cookie.remove(myCookie);
	*/

	remove: function(cookie, options){
		if ($type(cookie) == 'object') this.set(cookie.key, '', $merge(cookie, {duration: -1}));
		else this.set(cookie, '', $merge(options, {duration: -1}));
	}

};


