/*
  «js-core» — JavaScript framework, version 2.5.6
  © Dmitry Korobkin, 2008
  http://code.google.com/p/js-core/
*/

Object.prototype.each = function(fn, arg) {
	if(this.length != undefined) {
		var i, length = this.length;
		for(i = 0; i < length; i++) fn.apply(this[i], arg || []);
	}
	else for(var key in this) if(this.hasOwnProperty(key)) fn.apply(key, arg || []);
	return this;
};

Object.prototype.extend = function(hash) {
	if(hash) hash.each(function(obj) {
		obj[this] = hash[this];
	}, [this]);
	return this;
};

Function.prototype.extend(function(ie) {
	return {
		context: function(obj) {
			var fn = this;
			return function _fn() {
				return fn.call(obj, arguments[0]);
			};
		},
		event: function() {
			return ie ? function() {
				return window.event;
			} : function() {
				return this.arguments[0];
			};
		}(),
		preventDefault: function() {
			return ie ? function() {
				this.event().returnValue = false;
			} : function() {
				this.event().preventDefault();
			};
		}(),
		stopPropagation: function() {
			return ie ? function() {
				this.event().cancelBubble = true;
			} : function() {
				this.event().stopPropagation();
			};
		}(),
		handler: function(target) {
			return function() {
				return this.event()[target];
			};
		}(ie ? 'srcElement' : 'target')
	};
}(/*@cc_on 1 @*/));

if(!Array.prototype.forEach) Array.prototype.forEach = function(fn, context) {
	var i, length = this.length;
	for(i = 0; i < length; i++) fn.call(context, this[i], i, this);
};

String.prototype.extend({
	trimLeft: function() {
		return this.replace(/^\s+/, '');
	},
	trimRight: function() {
		return this.replace(/\s+$/, '');
	},
	trimSpaces: function() {
		return this.replace(/\s{2,}/g, ' ');
	},
	trim: function(arg) {
		if(arg) {
			var str = this;
			core.array(arg).each(function() {
				str = core.trim[this](str);
			});
			return str;
		}
		else return core.trim['both'](this);
	}
});

var core = {
	ie: 0 /*@cc_on + ScriptEngineMinorVersion() @*/,
	trim: {
		left: function(str) {
			return str.trimLeft();
		},
		right: function(str) {
			return str.trimRight();
		},
		spaces: function(str) {
			return str.trimSpaces();
		},
		both: function(str) {
			return str.trimLeft().trimRight();
		},
		all: function(str) {
			return str.trimSpaces().trimLeft().trimRight();
		}
	},
	cache: {},
	clear: function(node) {
		node.hasChildNodes() ? this.cache = {} : delete this.cache[node.id];
		return node;
	},
	incache: function(id) {
		return this.cache[id] || (this.cache[id] = document.getElementById(id));
	},
	isstr: function(arg) {
		return typeof arg == 'string';
	},
	id: function(arg) {
		return this.isstr(arg) ? this.incache(arg) : arg;
	},
	tag: function(ie) {
		return ie ? function(hash) {
			var node = hash.node || document, array = [];
			Array.prototype.forEach.call(hash.tag ? node.getElementsByTagName(hash.tag) : (this.ie == 5 ? node.all : node.getElementsByTagName('*')), function(el) {
				array.push(el);
			});
			return array;
		} : function(hash) {
			return (hash.node || document).getElementsByTagName(hash.tag || '*');
		};
	}(/*@cc_on 1 @*/),
	create: function(arg) {
		return this.isstr(arg) ? document.createElement(arg) : arg;
	},
	insert: function(node, arg, before) {
		return node.insertBefore(this.create(arg), before);
	},
	sibling: function(node, dir, tag) {
		while(node = node[dir]) if(node.nodeType == 1 && (tag ? node.tagName == tag.toUpperCase() : true)) return node;
	},
	bind: function() {
		return window.addEventListener ? function(node, type, listener) {
			node.addEventListener(type, listener, false);
		} : function(expr) {
			return function(node, type, listener) {
				node.attachEvent('on' + type, expr.test(listener) ? listener.context(node) : listener);
			};
		}(/^function\s*\(/);
	}(),
	unbind: function() {
		return window.removeEventListener ? function(node, type, listener) {
			node.removeEventListener(type, listener, false);
		} : function(node, type, listener) {
			node.detachEvent('on' + type, listener);
		};
	}(),
	instr: function(str, search) {
		return str.search('\\b' +  search + '\\b') + 1;
	},
	replace: function(str, search, replace) {
		return str.replace(new RegExp('\\b' + search + '\\b'), replace || '').trim(['all']);
	},
	array: function(arg) {
		return arg.split ? this.trim['all'](arg).split(/\s+/) : arg;
	},
	attr: function(hash) {
		if(hash.attr.length) {
			var array = [];
			this.tag({node: hash.node, tag: hash.tag}).each(function() {
				var key = true;
				core.array(hash.attr).each(function(node) {
					if(!node[this]) return key = false;
				}, [this]);
				if(key) array.push(this);
			});
			return array;
		}
		else if(hash.node) hash.attr.each(function() {
			hash.node[this] = hash.attr[this];
		});
	},
	value: function(hash) {
		var array = [];
		this.tag({node: hash.node, tag: hash.tag}).each(function() {
			var key = true;
			hash.attr.each(function(node) {
				if(node[this] != hash.attr[this]) key = false;
			}, [this]);
			if(key) array.push(this);
		});
		return array;		
	},
	child: function(node, tags) {
		var i, children = node.childNodes, length = children.length, array = [];
		if(tags) {
			if(tags.join) tags = tags.join(' ');
			tags = tags.toUpperCase();
		}
		for(i = 0; i < length; i++) if(children[i].nodeType == 1) if(tags ? this.instr(tags, children[i].tagName) : true) array.push(children[i]);
		return array;
	},
	tags: function(hash) {
		if(hash.tag) {
			var array = [];
			this.array(hash.tag).each(function() {
				core.tag({node: hash.node, tag: this}).each(function() {
					array.push(this);
				});
			});
			return array;
		}
		else return this.tag({node: hash.node});
	},
	values: function(hash) {
		var array = [], val = this.isstr(hash.value) ? hash.value : hash.value.join(' ');
		core.attr({node: hash.node, tag: hash.tag, attr: hash.attr}).each(function() {
			var key = false;
			core.array(this[hash.attr]).each(function() {
				if(core.instr(val, this)) key = true;
			});
			if(key) array.push(this);
		});
		return array;
	},
	css: function(node, property) {
		if(node.currentStyle) return node.currentStyle[property];
		else if(window.getComputedStyle) return document.defaultView.getComputedStyle(node, null).getPropertyValue(property);
	},
	dom: {
		init: [],
		ready: function() {
			if(!arguments.callee.done) {
				arguments.callee.done = true;
				if(core.dom.timer) clearInterval(core.dom.timer);
				this.init.each(function() { this(); });
				this.init = [];
			}
		},
		check: function() {
			var listener = function() {
				core.dom.ready();
			};
			if(document.addEventListener) document.addEventListener('DOMContentLoaded', listener, false);
			if(/KHTML|WebKit/i.test(navigator.userAgent))
				core.dom.timer = setInterval(function() {
					if(/loaded|complete/.test(document.readyState)) core.dom.ready();
				}, 10);
			/*@cc_on document.write('<SCRIPT onreadystatechange="if(this.readyState==\'complete\') core.dom.ready()" defer=defer src=\/\/:><\/SCRIPT>'); @*/
			core.bind(window, 'load', listener);
		}
	}
};

function preventDefault() {
	arguments.callee.preventDefault();
}

function stopPropagation() {
	arguments.callee.stopPropagation();
}

function _$(arg) {
	this.node = core.id(arg);
}

_$.prototype = {
	child: function(tag, bool) {
		if(tag) return typeof tag == 'boolean' ? core.tags({node: this.node}) : (bool ? core.tags({node: this.node, tag: tag}) : core.child(this.node, tag));
		else return core.child(this.node);
	},
	parent: function() {
		return new _$(this.node.parentNode);
	},
	append: function(arg) {
		return new _$(this.node.appendChild(core.create(arg)));
	},
	prepend: function(arg) {
		return new _$(core.insert(this.node, arg, this.node.firstChild));
	},
	after: function(arg) {
		return new _$(core.insert(this.node.parentNode, arg, this.node.nextSibling));
	},
	before: function(arg) {
		return new _$(core.insert(this.node.parentNode, arg, this.node));
	},
	appendTo: function(arg) {
		core.id(arg).appendChild(this.node);
		return new _$(arg);
	},
	prependTo: function(arg) {
		var node = core.id(arg);
		core.insert(node, this.node, node.firstChild);
		return new _$(arg);
	},
	insertAfter: function(arg) {
		var node = core.id(arg);
		return new _$(core.insert(node.parentNode, this.node, node.nextSibling));
	},
	insertBefore: function(arg) {
		var node = core.id(arg);
		return new _$(core.insert(node.parentNode, this.node, node));
	},
	next: function(tag) {
		return new _$(core.sibling(this.node, 'nextSibling', tag));
	},
	prev: function(tag) {
		return new _$(core.sibling(this.node, 'previousSibling', tag));
	},
	clone: function(bool) {
		return new _$(this.node.cloneNode(bool !== false));
	},
	replace: function(arg) {
		try {
			return this.before(core.create(arg));
		}
		catch(e) {}
		finally {
			this.remove();
		}
	},
	wrap: function(arg) {
		return this.before(arg).append(this.node);
	},
	el: function(arg) {
		return arg ? this.replace(core.id(arg)) : this.node;
	},
	empty: function() {
		core.clear(this.node);
		while(this.node.firstChild) this.node.removeChild(this.node.firstChild);
		return this;
	},
	remove: function() {
		core.clear(this.node).parentNode.removeChild(this.node);
		return null;
	},
	html: function(str) {
		if(str != undefined) {
			this.node.innerHTML = str;
			return this;
		}
		else return this.node.innerHTML;
	},
	text: function(str) {
		if(str != undefined) {
			this.empty().node.appendChild(document.createTextNode(str));
			return this;
		}
		else return this.node.innerText || this.node.textContent;
	},
	useDefault: function(type, def) {
		var prefix = 'preventDefaultOn';
		if(def) {
			this.node[prefix + type] = false;
			core.unbind(this.node, type, preventDefault);
		}
		else if(!this.node[prefix + type]) {
			this.node[prefix + type] = true;
			core.bind(this.node, type, preventDefault);
		}
		return this;
	},
	bind: function(type, listener, def) {
		core.bind(this.node, type, listener);
		if(typeof def == 'boolean') this.useDefault(type, def);
		return this;
	},
	unbind: function(type, listener, def) {
		core.unbind(this.node, type, listener);
		if(typeof def == 'boolean') this.useDefault(type, def);
		return this;
	},
	exist: function(exist, die) {
		if(exist && this.node) exist.call(this.node);
		else if(die && !this.node) die();
		return !!this.node;
	},
	hasClass: function(arg) {
		if(arg) {
			var key = false;
			core.array(arg).each(function(str) {
				if(!core.instr(str, this)) return key = true;
			}, [this.node.className]);
			return !key;
		}
		else return !!this.node.className;
	},
	addClass: function(arg) {
		core.array(arg).each(function(node) {
			if(!core.instr(node.className, this)) node.className += ' ' + this; 
		}, [this.node]);
		return this;
	},
	removeClass: function(arg) {
		arg ? core.array(arg).each(function(node) {
			node.className = core.replace(node.className, this);
		}, [this.node]) : this.node.className = '';
		return this;
	},
	toggleClass: function(arg1, arg2) {
		if(arg2) {
			var i, array1 = core.array(arg1), array2 = core.array(arg2), length = array1.length;
			for(i = 0; i < length; i++) this.node.className = core.replace(this.node.className, array1[i], array2[i]);
		}
		else if(arg1) core.array(arg1).each(function(obj) {
			obj.hasClass(this) ? obj.removeClass(this) : obj.addClass(this);
		}, [this]);
		else this.removeClass();
		return this;
	},
	attr: function(arg) {
		if(arg.join || arg.split) {
			var array = [];
			core.array(arg).each(function(node) {
				array.push(node[this]);
			}, [this.node]);
			return array.length == 1 ? array[0] : array;
		}
		else {
			core.attr({node: this.node, attr: arg});
			return this;
		}
	},
	removeAttr: function(arg) {
		core.array(arg).each(function(node) {
			node[this] = null;
		}, [this.node]);
		return this;
	},
	val: function(str) {
		return str != undefined ? this.attr({value: str}): this.attr('value');
	},
	find: function(arg, tag) {
		return arg.join || arg.split ? core.attr({node: this.node, tag: tag, attr: arg}) : core.value({node: this.node, tag: tag, attr: arg});
	},
	findAttr: function(attr, value, tag) {
		return core.values({node: this.node, tag: tag, attr: attr, value: value});
	},
	is: function(arg, tag) {
		if(arg) {
			if(!arg.join && !arg.split) {
				var key = false;
				arg.each(function(node) {
					if(node[this] != arg[this]) key = true;
				}, [this.node]);
				if(tag && !key) key = this.node.tagName != tag.toUpperCase();
				return !key;
			}
			else return this.node.tagName == arg.toUpperCase();
		}
		else return this.exist();
	},
	findClass: function(arg, tag) {
		return core.values({node: this.node, tag: tag, attr: 'className', value: arg});
	},
	css: function(arg) {
		if(arg.join || arg.split) {
			var array = [];
			core.array(arg).each(function(node) {
				array.push(core.css(node, this));
			}, [this.node]);
			return array.length == 1 ? array[0] : array;
		}
		else {
			core.attr({node: this.node.style, attr: arg});
			return this;
		}		
	},
	hide: function() {
		return this.css({display: 'none', visibility: 'hidden'});
	},
	show: function(type) {
		return this.css({display: type || 'block', visibility: 'visible'});
	},
	visible: function() {
		return this.css(['display']) != 'none' && this.css(['visibility']) != 'hidden';
	},
	toggle: function(type) {
		return this.visible() ? this.hide() : this.show(type);
	},
	enabled: function(bool) {
		return typeof bool == 'boolean' ? (bool ? this.removeAttr('disabled') : this.attr({disabled: 'disabled'})) : !this.attr(['disabled']);
	},
	id: function(str) {
		if(str) {
			if(core.cache[this.node.id]) delete core.cache[this.node.id];
			this.node.id = str;
			return this;
		}
		else return this.node.id;
	},
	run: function(fn, arg) {
		fn.apply(this.node, arg || []);
		return this;
	},
	serialize: function() {
		return this.node.outerHTML || new XMLSerializer().serializeToString(this.node);
	}
};

function $(arg) {
	return new _$(arg);
}

$.extend({
	t: function(tag) {
		return core.tags({tag: tag});
	},
	n: function(tag) {
		return new _$(document.createElement(tag));
	},
	c: function(arg, tag) {
		return _$.prototype.findClass(arg, tag);
	},
	a: function(arg, tag) {
		return _$.prototype.find(arg, tag);
	},
	f: function(attr, value, tag) {
		return _$.prototype.findAttr(attr, value, tag);
	},
	ready: function(fn) {
		core.dom.init.push(fn);
	},
	foreach: function(obj, fn) {
		return (obj || {}).each(function() {
			fn.call(obj, this, obj[this]);
		});
	},
	makeArray: function() {
		return core.ie ? function(list) {
			var array = [];
			Array.prototype.forEach.call(list, function(el) {
				array.push(el);
			});
			return array;
		} : function(list) {
			return Array.prototype.slice.call(list);
		};
	}()
});

function Timer(time, fn, arg) {
	this.extend({time: time, fn: fn, arg: arg, enabled: false});
}

Timer.prototype = {
	start: function() {
		if (!this.enabled) {
			var timer = this;
			this.interval = setInterval(function(){
				timer.fn.apply(timer, timer.arg || []);
			}, this.time);
			this.enabled = true;
		}
		return this;
	},
	stop: function() {
		clearInterval(this.interval);
		this.enabled = false;
		return this;
	},
	repeat: function(amount, fn, arg) {
		if(fn) this.extend({callback: {fn: fn, arg: arg}});
		var timer = this;
		if(amount) setTimeout(function() {
			timer.fn.apply(timer, timer.arg || []);
			timer.repeat(--amount);
			if(!amount && timer.callback) timer.callback.fn.apply(timer, timer.callback.arg || []);
		}, this.time);
		return this;
	}
};

core.dom.check();