var Prostotype = {
	version: 2.0
};

function $extend(dst, src) {
	if (src === undefined) {
		src = dst;
		dst = this;
	}
	for (var i in src) {
		dst[i] = src[i];
	}
	return dst;
}

function $extendSuper(dst, src) {
	if (src === undefined) {
		src = dst;
		dst = this;
	}
	for (var i in src) {
		if (typeof src[i] == "object") {
			if (src[i]) {
				if (typeof dst[i] != "object" || !dst[i]) {
					if (src[i].isArray !== undefined) { dst[i] = []; } else { dst[i] = {}; }
				}
				$extendSuper(dst[i], src[i]);
			} else {
				dst[i] = src[i]
			}
		} else {
			dst[i] = src[i];
		}
	}
	return dst;
}

/* MULTI-TRY */
function $try() {
	for (var i = 0; i < arguments.length; i++) {
		f = arguments[i];
		if (typeof f == "function") {	
			try { return f(); } catch (e) { }
		} else if (typeof f == "string") {
			try { return (new Function(f))(); } catch (e) { }
		}
	}
}

var $config = {
	ajax: function (obj) {
		obj = obj || this;
		$extendSuper(obj, {
			readyState: 0,
			config: {
				data: null,
				method: "post",
				url: window.location.href,
				byForm: false,
				doReload: false,
				headers: {
					"Content-type": "application/x-www-form-urlencoded",
					"Connection": "close"
				}
			}
		});
		return obj;
	},
	effect: function (obj) {
		obj = obj || this;
		$extendSuper(obj, {
			duration: 1000,
			tick: 50,
			proc: Function.empty,
			trans: Effect.trans.dflt
		});
		return obj;
	}
}

var Class = function ($parent, methods) {
	if (typeof $parent == "object") {
		methods = $parent;
		$parent = null;
	}
	methods = methods || {};
	var _class = function () {
		this.init.apply(this, arguments);
	};
	
	$extend(_class, {
		isClass: true,
		'$parent': $parent,
		'$childs': [],
		addMethods: function (obj) {
			$extend(_class.prototype, obj);
		}
	})
	
	$extend(_class.prototype, {
		$method: function () {
			var args = $a(arguments);
			var name = args.shift();
			if (typeof this[name] == "function") {
				this[name].apply(this, args);
			} else {
				this.__method(name, args);
			}
		}
	});
	
	if ($parent) {
		$extend(_class.prototype, $parent.prototype);
		_class.prototype.$parent = $parent.prototype;
		$parent.$childs.push(_class);
	}
	
	_class.addMethods(methods);
	
	if (!_class.prototype.init) {
		_class.prototype.init = Function.empty;
	}
	
	return _class;
};

/* EventListener from ActionScript */
var $_eventListener = function (obj) {
	obj = obj || this;
	$extend(obj, {
		eventListeners: [],
		addEventListener: function (_listener) {
			if (typeof _listener != "object") {
				return false;
			}
			this.eventListeners.push(_listener);
			return _listener;
		},
		removeEventListener: function (_listener) {
			return this.eventListeners.remove(_listener);
		},
		addEvent: function (_name, _func) {
			var _listener = {};
			_listener[_name] = _func;
			this.addEventListener(_listener);
			return _listener;
		},
		removeEvent: function (_listener) {
			return this.removeEventListener(_listener);
		},
		fireEvent: function(event, args) {
			for (var i = 0; i < this.eventListeners.length; i++) {
				if (this.eventListeners[i][event]) {
					this.eventListeners[i][event].apply(this, args);
				}
			}
		},
		createEvents: function (es) {
			es = es || [];
			if (typeof es == "string") {
				es = es.split(",").invoke("trim");
			}
			for (var i = 0; i < es.length; i++) {
				this.createEvent(es[i]);
			}
		},
		createEvent: function(e) {
			this[e] = function() { this.fireEvent(e, arguments) };
		}
	});
	return obj;
};

var $browser = new function () {
	this.dom = document.getElementById ? true : false;
	this.opera = window.opera ? true : false;
	this.webkit = document.childNodes && !document.all && !navigator.taintEnabled;
	this.mac = window.xpath ? true : false;
	this.gecko = document.getBoxObjectFor != null;
	this.ie = window.ActiveXObject ? true : false;
	this.ie7 = this.ie && (window.XMLHttpRequest ? true : false);
	this.ie6 = this.ie && !this.ie7;
	var flash = $try(
		"return navigator.plugins['Shockwave Flash'].description",
		"return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version')",
		"return false"
	);
	this.flash = flash ? true : false;
	if (this.flash) {
		flash = flash.match(/\d+/g);
		this.flashVer = Number((flash[0] || 0) + "." + (flash[1] || 0));
	} else {
		this.flashVer = 0;
	}
}(); 

/* Absolut SELECTOR */
(function () {
	var 
		SELECTOR_RE = />|<|\+|:|([^\s><\+:]+(:[^\d\-\s>+:][^\s>+:]+)?)/g,
		SELECTOR_TG = /[^.#:]+|[.#:]/g;
	function gEBI(doc, id) {
		return doc.getElementById(id);
	}
	function isNode(node) {
		if (!node) { return false; }
		if (node.nodeType == 3) { return false; }
		return true;
	}
	function addNode(a, node) {
		if (!node) { return; }
		if (node.nodeType == 3) { return; }
		a.push(node);
	}
	function pushNode(a, node) {
		if (!node) { return; }
		if (node.nodeType == 3) { return; }
		for (var i = 0; i < a.length; i++) {
			if (a[i] == node) {
				return;
			}
		}
		a.push(node);
	}
	function pushNodes(a, nodes) {
		for (var i = 0; i < nodes.length; i++) {
			pushNode(a, nodes[i]);
		}
	}
	function getMatch(str) {
		if ({"{EOL}": 1, ">": 1, "<": 1, "+": 1, ":": 1}[str]) { return false; }
		var res = {
			tag: false,
			className: false,
			id: false,
			extra: false
		}, raw = str.match(SELECTOR_TG);
		var i = 0;
		if (!{':': 1, '#': 1, '.': 1}[raw[0]]) { res.tag = raw[0].toUpperCase(); i++; }
		for (; i < raw.length; i++) {
			if (raw[i] == ".") {
				if (res.className) {
					res.className += " " + raw[++i];
				} else {
					res.className = raw[++i];
				}
			} else if (raw[i] == "#") {
				res.id = raw[++i];
			} else {
				if (res.extra) {
					res.extra.push(raw[++i]);
				} else {
					res.extra = [raw[++i]];
				}
			}
		}
		if (!res.tag && !res.className && !res.id && !res.extra) {
			return false;
		}
		return res;
	}
	
	function matchNode(node, sel) {
		if (!node) { return false; }
		if (node.nodeType == 3) { return false; }
		if (!sel) { return true; }
		var res = true;
		if (sel.tag) { res = res && (node.nodeName == sel.tag) }
		if (sel.id) { res = res && (node.id == sel.id) }
		if (sel.className) {
			if (node.className == "") { return false; }
			res = res && (node.className.getClasses().length == (node.className + " " + sel.className).getClasses().length);
		}
		if (sel.extra) { }
		return res;
	}
	
	window._$ = function () {
		var res = _$$.apply(this, arguments);
		if (res.length == 0) {
			return null;
		}
		return res[0];
	};
	
	window._$$ = function () {
		//var time = $uts();
		var doc = document;
		if (this.nodeName !== undefined ? this.nodeName == "#document" : false) {
			doc = this;
		}
		var args = $a(arguments), objs = [];
		for (var i = 0; i < args.length; i++) {
			if (typeof args[i] == "object") { if (args[i]) {
				if (args[i].isArray) {
					objs.push(args[i]);
				} else if (Object.isElement(args[i])) {
					objs.push([args[i]]);
				}
			} } else if (typeof args[i] == "string") {
				if (args[i] == "parent") {
					objs.push("<", ":", "0");
				} else {
					objs.push.apply(objs, args[i].match(SELECTOR_RE));
				}
			} else if (typeof args[i] == "number") {
				objs.push(":", args[i].toString());
			}
		}
		objs.push("{EOL}");
		var cur = [doc], res = [], pos = 0, sel, node, nodes;
		while (pos < objs.length - 1) {
			if (typeof objs[pos] == "object") {
				if (objs[pos]) {
					if (objs[pos].isArray) {
						res = objs[pos];
					} else {
						res = [objs[pos]];
					}
					pos++;
				} else {
					res = [];
				}
			} else {
				switch(objs[pos]) {
					case ">":
						if (objs[pos + 1] == "<") {
							res = cur;
							pos += 2;
						} else if ((objs[pos + 1] == ":") && (parseInt(objs[pos + 2]) == objs[pos + 2])) {
							pos += 2; objs[pos] = parseInt(objs[pos])
							for (var i = 0; i < cur.length; i++) {
								nodes = []; pushNodes(nodes, cur[i].childNodes);
								if (objs[pos] >= 0) {
									addNode(res, nodes[objs[pos]])
								} else {
									addNode(res, nodes[nodes.length + objs[pos]]);
								}
							}
							pos++;
						} else if (sel = getMatch(objs[pos + 1])) {
							for (var i = 0; i < cur.length; i++) { for (var j = 0; j < cur[i].childNodes.length; j++) {
								if (matchNode(node = cur[i].childNodes[j], sel)) {
									pushNode(res, node);
								}
							} }
							pos += 2;
						} else {
							if (objs[pos + 1] == "+")  { pos += 1; }
							pos++;
							for (var i = 0; i < cur.length; i++) {
								pushNodes(res, cur[i].childNodes);
							}
						}
						break;
					case "<":
						pos++;
						if (objs[pos] == ":") {
							pos++; objs[pos] = parseInt(objs[pos]);
							for (var j = 0; j < objs[pos]; j++) {
								for (var i = 0; i < cur.length; i++) {
									pushNode(res, cur[i].parentNode);
								}
								cur = res; res = [];
							}
							res = cur;
							pos++;
						} else {
							sel = getMatch(objs[pos]);
							for (var i = 0; i < cur.length; i++) {
								node = cur[i].parentNode;
								while (node) {
									if (matchNode(node, sel)) { pushNode(res, node); }
									node = node.parentNode;
								}
							}
							if (sel) {
								pos++;
							}
						}
						break;
					case ":":
						pos++;
						if (parseInt(objs[pos]) == objs[pos]) {
							objs[pos] = parseInt(objs[pos]);
							pushNode(res, cur[objs[pos] >= 0 ? objs[pos] : cur.length + objs[pos]]);
							pos++;
						}
						break;
					case "+":
						pos++;
						if (parseInt(objs[pos]) == objs[pos]) {
							objs[pos] = parseInt(objs[pos]);
							if (objs[pos] == 0) {
								res = cur;
							} else {
								var subl = objs[pos] > 0 ? "nextSibling" : "previousSibling"; objs[pos] = Math.abs(objs[pos]);
								for (var i = 0; i < cur.length; i++) {
									node = cur[i]; var j = 0;
									while ((j < objs[pos]) && !!node) {
										node = node[subl];
										if (isNode(node)) { j++; }
									}
									pushNode(res, node);
								}
							}
							pos++;
						} else {
							sel = getMatch(objs[pos]);
							for (var i = 0; i < cur.length; i++) { if (cur[i].parentNode) {
								for (var j = 0; j < cur[i].parentNode.childNodes.length; j++) {
									if (matchNode(node = cur[i].parentNode.childNodes[j], sel)) {
										pushNode(res, node);
									}
								}
							} }
							if (sel) {
								pos++;
							}
						}
						break;
					default:
						if (sel = getMatch(objs[pos++])) {
							if (sel.id ? (!!(node = gEBI(doc, sel.id)) ? matchNode(node, sel) : false) : false) {
								pushNode(res, node);
							} else {
								if (!sel.tag) { sel.tag = "*"; } // need to fix *!!!
								for (var i = 0; i < cur.length; i++) {
									nodes = cur[i].getElementsByTagName(sel.tag);
									for (var j = 0; j < nodes.length; j++) { if (matchNode(nodes[j], sel)) {
										pushNode(res, nodes[j]);
									} }
								}
							}
						}
						break;
				}
			}
			if (res.length == 0) {
				return res;
			}
			var l = pos + "/" + (objs.length - 1) + ":";
			res.each(function (a) {l += " " + a.nodeName + "." + a.className + "#" + a.id;});
			cur = res;
			res = [];
		}
		//log("time: " + ($uts() - time));
		return cur;
	};
})();

/* AJAX & FORMS */
var Ajax = Class({
	init: function (_config) {
		_config = _config || {};
		$config.ajax(this);
		$extendSuper(this.config, _config);
		$_eventListener(this);
		this.createEvents(["onInit", "onSend", "onSuccess", "onError", "onAbort"]);
		this.addEventListener(_config);
		this.onInit(arguments);
	},
	send: function () {
		if (!this.config.byForm) {
			this.transport = $try(
				'return new XMLHttpRequest()',
				'return new ActiveXObject("Msxml2.XMLHTTP")',
				'return new ActiveXObject("Microsoft.XMLHTTP")',
				'return false'
			);
			if (this.transport === false) {
				this.config.byForm = true;
			} else {
				var _this = this, trans = this.transport;
				trans.onreadystatechange = function () {
					_this.readyState = trans.readyState;
					if (trans.readyState == 2) {
						_this.onSend();
					}
					if (trans.readyState == 4) {
						try { if (!trans.status) {
							return; // abortion
						} } catch (e) {
							return; // abortion
						}
						if (trans.status == 200) {
							_this.onSuccess(trans.responseText);
						} else {
							var error = "{UNREADABLE}";
							try { error = trans.statusText; } catch (e) { }
							_this.onError("HTTP: (" + trans.status + ") " + error);
						}
					}
				}
			}
		}
		if (!this.config.byForm) {
			var data = Object.toUrlString(this.config.data), url = this.config.url, method = this.config.method;
			if (method == "get" && data != "") {
				url += (url.indexOf("?") == -1 ? "?" : "&" ) + data;
				data = null;
			}
			this.transport.open(method.toUpperCase(), url, true);
			for (var i in this.config.headers) {
				this.transport.setRequestHeader(i, this.config.headers[i]);
			}
			this.transport.setRequestHeader("Content-length", data ? data.length : 0);
			this.transport.send(data);
		} else {
			var _this = this, data = Object.toInputString(this.config.data), form = $c("form", {
				action: this.config.url,
				method: this.config.method,
				innerHTML: data,
				onsubmit: function () { _this.onSend(); },
				style: { display: "none" }
			});
			document.body.appendChild(form);
			if (this.config.doReload) {
				form.submit();
			}
		}
	},
	abort: function() {
		if (this.transport) {
			this.transport.abort();
		}
		this.onAbort();
	}
});

var AjaxQuick = function (_config) {
	var res = new Ajax(_config);
	res.send();
	return res;
};

$c = function (tag, props, inner) {
	var doc = document;
	if (this.nodeName !== undefined ? this.nodeName == "#document" : false) {
		doc = this;
	}
	props = props || {}
	var el = doc.createElement(tag);
	$extendSuper(el, props);
	if (typeof inner == "string") {
		el.innerHTML = inner;
	}
	if (typeof inner == "object") {
		if (inner.isArray) {
			for (var i = 0; i < inner.length; i++) {
				el.appendChild(inner[i]);
			}
		} else if (inner.nodeType) {
			el.appendChild(inner);
		}
	}
	//var span = doc.createElement("span"); span.appendChild(el); span.removeChild(span); //RENDER
	return __Element(el);
}

var Post = Class(Ajax, {
	init: function (_config) {
		_config = _config || {};
		$extend(_config, {
			byForm: true,
			doReload: true
		});
		this.$parent.init.call(this, _config);
	}
});

var PostQuick = function (_config) {
	var res = new Post(_config);
	res.send();
	return res;
};

/* DATE/TIME */
function $uts() { return (new Date()).getTime(); }

/* IMPROVE String */
$extend(String.prototype, {
	trim: function (left) {
		if (left == undefined) {
			return this.replace( /^\s+|\s+$/g, "" );
		}
		if (left) {
			return this.replace( /^\s+/g, "" );
		}
		return this.replace( /\s+$/g, "" );
	},
	test: function(regex, params){ return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this); },
	parseJSON: function () {
		try {
			return (new Function("return " + this))();
		} catch (e) {
			return null;
		}
	},
	getClasses: function () { return this.trim().split(/\s+/g).unique(); }
});

/* varrible types */
function isScalar(a) { return {"number": 1, "string": 1, "boolean": 1}[typeof a] ? true : false; }
var undefined;

/* IMPROVE Function */
Function.empty = function () {};
Function._false = function () { return false; }
Function.one = function (a) { return a; }

$extend(Function.prototype, {
	argumentNames: function () {
		var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("trim");
		return names.length == 1 && !names[0] ? [] : names;
	},
	toEffect: function (duration) {
		return (new Effect({
			duration: duration,
			proc: this
		}));
	},
	toTimeout: function (time) {
		return setTimeout(this, time);
	},
	toInterval: function (time) {
		return setInterval(this, time);
	}
});

/*IMPROVE Object */
$extend(Object, {
	extend: $extend,
	extendSuper: $extendSuper,
	toUrlString: function (obj) {
		obj = obj || this;
		var res = [];
		for (var i in obj) { if (isScalar(obj[i])) {
			res.push(encodeURIComponent(i) + "=" + encodeURIComponent(obj[i]));
		} }
		return res.join("&");
	},
	toInputString: function (obj) {
		obj = obj || this;
		var res = "", name, value;
		for (var i in obj) { if (isScalar(obj[i])) {
			name = i.toString().replace(/\\/g, "\\\\").replace(/\"/g, "\\\"");
			value = obj[i].toString().replace(/\\/g, "\\\\").replace(/\"/g, "\\\"");
			res += '<input type="hidden" name="' + name + '" value="' + value + '" />'
		}}
		return res;
	},
	isElement: function (obj) { return obj ? !!obj.nodeName || obj == window : false; }
});

/*IMPROVE Array */
$extend(Array.prototype, {
	each: function (func) {
		for (var i = 0; i < this.length; i++) { func(this[i], i, this); }
	},
	collect: function (func) {
		var res = [];
		for (var i = 0; i < this.length; i++) { res.push( func(this[i], i, this) ) }
		return res;
	},
	copy: function () { return this.collect(Function.one); },
	invoke: function (funcN, args) {
		return this.collect(function(val) { return val[funcN](args) });
	},
	uniquePush: function () {
		if (arguments.length == 0) { return; }
		for (var i = 0; i < arguments.length; i++) {
			if (!this.has(arguments[i])) { this.push(arguments[i]); }
		}
	},
	unique: function () {
		var res = [];
		res.uniquePush.apply(res, this);
		return res;
	},
	has: function (el) {
		for (var i = 0; i < this.length; i++) {
			if (el == this[i]) { return true; }
		}
		return false;
	},
	search: function (el) {
		for (var i = 0; i < this.length; i++) {
			if (el == this[i]) { return i; }
		}
		return -1;
	},
	remove: function (el) {
		var i = this.search(el);
		if (i >= 0) {
			this.splice(i, 1);
			return true;
		}
		return false;
	},
	toggle: function (el) {
		var i = this.search(el);
		if (i == -1) {
			this.push(el);
			return true;
		}
		this.splice(i, 1);
		return false;
	},
	toCoords: function() {
		this.x = this[0];
		this.y = this[1];
		return this;
	},
	toChain: function () {
		return (new Chain({queue: this}));
	},
	isArray: true
});

function $a(_arr) {
	var a = [];
	for (var i = 0; i < _arr.length; i++) { a.push(_arr[i]); }
	return a;
}

/* IMPROVE Event */
var _Event = new Class({
	init: function (event, win) {
		win = win || window;
		var doc = win.document;
		event = event || win.event;
		var type = event.type, target = event.target || event.srcElement;
		while (target && (target.nodeType ? target.nodeType == 3 : false)) target = target.parentNode;
		if (type.test(/key/)) {
			var code = event.which || event.keyCode, key = _Event.keyCodes[code] || null;
			if (type == 'keydown') {
				var fKey = code - 111;
				if (fKey > 0 && fKey < 13) key = 'f' + fKey;
			}
			key = key || String.fromCharCode(code).toLowerCase();
			this.code = code;
			this.key = key;
		} else if (type.match(/(click|mouse|menu)/i)) {
			doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.documentElement : doc.body;
			this.page = [event.pageX || event.clientX + doc.scrollLeft, event.pageY || event.clientY + doc.scrollTop].toCoords();
			this.client = [(event.pageX) ? event.pageX - win.pageXOffset : event.clientX, (event.pageY) ? event.pageY - win.pageYOffset : event.clientY].toCoords();
			if (type.match(/DOMMouseScroll|mousewheel/)){
				this.wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
			}
			this.rightClick = (event.which == 3) || (event.button == 2);
			this.related = null;
			if (type.match(/over|out/)){
				switch (type){
					case 'mouseover':
						this.related = event.relatedTarget || event.fromElement;
						break;
					case 'mouseout':
						this.related = event.relatedTarget || event.toElement;
						break;
				}
				/*if (!(function(){  // ??
					while (related && related.nodeType == 3) related = related.parentNode;
					return true;
				}).create({attempt: Browser.Engine.gecko})()) related = false; */ 
			}
			
		}
		$extend(this, {
			event: event,
			type: type,

			target: target,

			shift: event.shiftKey,
			control: event.ctrlKey,
			alt: event.altKey,
			meta: event.metaKey
		});
	},
	stopPropagation: function(){
		if (this.event.stopPropagation) {
			this.event.stopPropagation();
		} else {
			this.event.cancelBubble = true;
		}
	},
	preventDefault: function(){
		if (this.event.preventDefault) {
			this.event.preventDefault();
		} else {
			this.event.returnValue = false;
		}
	},
	isEvent: true
});

_Event.keyCodes = {
	13: 'enter',
	16: 'shift',
	17: 'control',
	18: 'alt',
	38: 'up',
	40: 'down',
	37: 'left',
	39: 'right',
	27: 'esc',
	32: 'space',
	8: 'backspace',
	9: 'tab',
	46: 'delete'
};

/* IMPROVE Element */
(function () {
	var Events = "Abort,Blur,Change,Click,DblClick,Error,Focus,KeyDown,KeyPress,KeyUp,Load,MouseDown,MouseMove,MouseOut,MouseOver,MouseUp,Reset,Resize,Select,Submit,Scroll,UnLoad,Close".split(",");
	var onEvents = Events.collect(function (e) { return "on" + e; });
	var events = Events.collect(function (e) { return e.toLowerCase(); });
	var onevents = events.collect(function (e) { return "on" + e; });
	
	window.__Element = function (el) {
		if (!el) { return null; }
		if (el.updates !== undefined ? el.updates.prosto : false) {
			return el;
		}
		/* eventListener like ActionScript */
		var oldListener = false, oldEvents = [];
		if (el.addEventListener) {
			el._aEL = el.addEventListener;
			oldListener = "_aEL";
			oldEvents = events;
		}
		if (el.attachEvent) {
			el._aE = el.attachEvent;
			oldListener = "_aE";
			oldEvents = onevents;
		}
		
		$extend (el, {
			eventListeners: [],
			eventList: {},
			addEventListener: function (ev_o) {
				if (typeof ev_o != "object") {
					return false;
				}
				var i = this.eventListeners.push(ev_o) - 1;
				for (var _ev in ev_o) {
					this.createEvent(_ev);
					this.eventList[_ev].push(i);
				}
				return ev_o;
			},
			addEvent: function(_ev, func) {
				var ev_o = {};
				ev_o[_ev] = func;
				return this.addEventListener(ev_o);
			},
			removeEventListener: function (ev_o) {
				var i  = this.eventListeners.search(ev_o);
				if (i != -1) {
					this.eventListeners[i] = {};
					for (var _ev in ev_o) {
						this.eventList[_ev].remove(i);
					}
					return true;
				} else {
					return false;
				}
			},
			removeEvent: function(ev_o) {
				return this.removeEventListener(ev_o);
			},
			createEvent: function(_ev) {
				if (!this.eventList[_ev]) {
					var i = onEvents.search(_ev)
					if (i != -1) {
						var _this = this;
						this[oldListener](oldEvents[i], function (event) {
							var _event = new _Event(event);
							_this.fireEvent(_ev, [_event]); 
						}, false );
					}
					this.eventList[_ev] = [];
					this[_ev] = function () { this.fireEvent(_ev, arguments) };
				}
			},
			createEvents: function(_evs) {
				if (typeof _evs == "string") {
					_evs = _evs.split(',');
				}
				for (var i = 0; i < _evs.length; i++) {
					this.createEvent(_evs[i]);
				}
			},
			fireEvent: function (_ev, args) {
				if (this.eventList[_ev]) {
					for (var i = 0; i < this.eventList[_ev].length; i++) {
						var res = this.eventListeners[this.eventList[_ev][i]][_ev].apply(this, args);
						if (typeof args[0] == "object") {
							if (args[0].isEvent ? res === false : false) {
								args[0].preventDefault();
								break;
							}
						}
					}
				}
			}
		});
		
		/*
		$_eventListener(el);
		el.fireEvent = function (fn, args) {
			for (var i = 0; i < this.eventListeners.length; i++) {
				if (this.eventListeners[i][fn]) {
					if (this.eventListeners[i][fn].apply(this, args) === false) {
						args[0].preventDefault();
						break;
					}
				}
			}
		}

		el.createEvents(onEvents);

		oldEvents.each(function (ev, i) {
			el[oldListener](ev, function (event) {
				var _event = new _Event(event);
				el.fireEvent(onEvents[i], [_event]);
			}, false);
		});

		/*onevents.each(function (ev, i) {
			if (typeof el[ev] == "function") {
				el.addEvent(onEvents[i], el[ev]);
			}
		});*/
		
		$extend(el, ElementMethods);
		el.updates = el.updates || {};
		el.updates.prosto = true;
		return el;
	}
	
	window.ElementMethods = {
		toggle: function () { this.style.display = this.style.display == "none" ? "" : "none"; },
		show: function () { this.style.display = ""; },
		hide: function () { this.style.display = "none"; },
		insertChildFirst: function (el) {
			if (this.hasChildNodes()) {
				this.insertBefore(el, this.firstChild);
			} else {
				this.appendChild(el);
			}
		},
		insertChildLast: function (el) {
			this.appendChild(el);
		},
		insertChildAfter: function (el, src) {
			if (src.nextSibling) {
				this.insertBefore(el, src.nextSibling);
			} else {
				this.appendChild(el);
			}
		},
		insertChildBefore: function (el, src) {
			this.insertBefore(el, src);
		},
		insertSiblingNext: function (el) {
			if (this.nextSibling) {
				this.parentNode.insertBefore(el, this.nextSibling);
			} else {
				this.parentNode.appendChild(el);
			}
		},
		insertSiblingPrevious: function (el) {
			this.parentNode.insertBefore(el, this);
		},
		removeElement: function () {
			if (this.parentNode) {
				this.parentNode.removeChild(this);
			}
		},
		replaceElement: function (el) {
			this.parentNode.replaceChild(el, this);
		},
		getClasses: function () { return this.className.getClasses(); },
		hasClass: function (className) { return this.getClasses().has(className.trim()); },
		toggleClass: function (className) {
			var classArr = this.getClasses(); 
			var res = classArr.toggle(className.trim())
			this.className = classArr.join(" ");
			return res;
		},
		removeClass: function (className) {
			var classArr = this.getClasses(); 
			classArr.remove(className.trim())
			this.className = classArr.join(" ");
		},
		addClass: function (className) {
			var classArr = this.getClasses(); 
			classArr.uniquePush(className.trim())
			this.className = classArr.join(" ");
		},
		absPos: function () {
			var res = [0, 0], obj = this;
			while (obj.offsetParent) {
				res[0] += obj.offsetLeft;
				res[1] += obj.offsetTop;
				obj = obj.offsetParent;
			}
			return res.toCoords();
		},
		applyStyle: function (style) {
			for (var i in style) { if (this.style[i] != style[i]) { this.style[i] = style[i]; } }
		},
		setOpacity: $browser.ie ? function (alpha) { this.style.filter = 'Alpha(Opacity=' + (alpha * 100) + ')'; } : function (alpha) { this.style.opacity = alpha; },
		getEffectFunction: function(name) {
			if (ElementEffects[name]) {
				var el = this;
				return function () { ElementEffects[name].proc.apply(el, arguments); }
			}
		}
	}
	
	var asEach = ("addEventListener,removeEventListener,addEvent,removeEvent,fireEvent,createEvent," + onEvents.join(",")).split(",");
	window.__Elements = function (els) {
		els.each(function (el) { __Element(el); });
		asEach.each(function (fn) {
			els[fn] = function () { var args = arguments; els.each(function (el) {
				el[fn].apply(el, args);
			});	};
		});
		return els;
	}
	
	window.ElementsMethods = {

	};
	
	window.$ = function () { return __Element(_$.apply(this, arguments)); }
	window.$$ = function () { return __Elements(_$$.apply(this, arguments)); }
	
	//Element(window);
	//Element(document);
})();

var Effect = Class({
	init: function (config) {
		$config.effect(this);
		$_eventListener(this);
		this.createEvents(["onStart", "onStop", "onInit"]);
		config = config || {};
		this.addEventListener(config);
		$extendSuper(this, config);
		this.onInit(config);
	},
	start: function () {
		if (this.interval) { this.stop(); }
		var _this = this, args = arguments;
		var engine = function () {
			var t = ($uts() - _this.time_start) / _this.duration;
			if (t < 1) {
				_this.proc.call(_this, _this.trans(t), args);
			} else {
				_this.proc.call(_this, _this.trans(1), args);
				_this.stop();
			}
		};
		this.onStart();
		this.time_start = $uts();
		this.proc.call(this, this.trans(0), args);
		this.interval = setInterval(engine, this.tick);
	},
	stop: function () {
		if (this.interval) {
			clearInterval(this.interval);
			this.interval = false;
		}
		this.onStop();
	},
	isEffect: true
});

$extend(Effect, {
	trans: {
		one: Function.one,
		exp: function(p) { return Math.pow(2, 8 * (p - 1)); },
		circ: function(p){ return 1 - Math.sin(Math.acos(p)); },
		sin: function(p){ return 1 - Math.sin((1 - p) * Math.PI / 2); },
		dflt: function (x) { return Math.sin((x - 0.5) * Math.PI) * 0.5 + 0.5; }
	}
});

var Chain = Class({
	init: function (config) {
		this.queue = [];
		$_eventListener(this);
		this.createEvents(["onStart", "onStop", "onInit"]);
		config = config || {};
		this.addEventListener(config);
		$extendSuper(this, config);
		this.onInit(config);
	},
	start: function () {
		this.counter = 0;
		this.onStart();
		this.step.apply(this, arguments);
	},
	step: function () {
		var _this = this, _args = arguments;
		if (this.counter >= this.queue.length) {
			this.onStop(0);
			return;
		}
		var current = this.queue[this.counter];
		while (typeof current == "function") {
			current.apply(current, _args);
			if (++this.counter >= this.queue.length) {
				this.onStop(0);
				return;
			}
			current = this.queue[this.counter];
		}
		if (typeof current == "object" ? current : false) {
			if (current.isChain || current.isEffect) {
				current.addEvent("onStop", function () { _this.step.apply(_this, _args); });
				this.counter++;
				current.start.apply(current, _args);
				return;
			}
		}
	},
	stop: function () {
		this.counter = this.queue.length;
	},
	isChain: true
});

var ElementEffects = {
	appear: {
		proc: function (t) {
			this.setOpacity(t);
		},
		onStart: function () {
			this.setOpacity(0);
			this.style.display = "";
		}
	}
};