/* 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({
	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;
	}
});
String.extend({
	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);
	}
});
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};
	}
});

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 = {
		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.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.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);
	}

});

