function $A(obj, iterator){
	var arr = [];
	for(var i = 0; i < obj.length; i++){
		arr.push(iterator ? iterator(obj[i]) : obj[i]);
	}
	return arr;
}
function $O(item, index){
	return {item: item, index: index};
}
var $break = new Object();
var $continue = new Object();
//Object functions
Object.copy = function(obj){
	var obj_c = {};
	for(var i in obj){
		obj_c[i] = obj[i];
	}
	return obj_c;
}
function MergeObjects(obj1, obj2){
	for(var i in obj2){
		obj1[i] = obj2[i];
	}
	return obj1;
}
Object.extend = MergeObjects;
function _undefined(obj){
	return (typeof obj == "undefined");
}

var __toString = function(){
	var str = __toString.bounds.left + "object " + this.__name + __toString.bounds.right;
	return str;
};
__toString.bounds = {
	left: "[[",
	right: "]]"
};

function $(id, doc){
	return $Node((doc || document).getElementById(id));
}
function $_(id, doc){
	return (doc || document).getElementById(id);
}
function $T(tName, parent, FIRST){
	var arr = $A((parent || document).getElementsByTagName(tName), $Node);
	return FIRST ? arr[0] : arr;
}
function $T_(tName, parent, FIRST){
	var arr = $A((parent || document).getElementsByTagName(tName));
	return FIRST ? arr[0] : arr;
}
function $C(className, parent, tagName, FIRST){
	var detected = (parent && parent.IS_ARRAY ? parent : ($T(tagName || "*", parent) || [])).detects(detectFuncs.className.bind(className));
	return detected.length ? (FIRST ? detected[0] : detected) : null;
}
function $C_arr(arr_classNames, parent, tagName, iterator){
	var ret_arr = [];
	parent = parent ? _elem(parent) : document.body;
	tagName = tagName ? tagName : "*" ;
	iterator = iterator ? iterator : function(val){return val;};
	var collection = $T(tagName, parent);
	collection.each(function(col, c_ind){
		arr_classNames.each(function(cName, ind){
			if(col.className.has_separated_value(cName)){
				ret_arr[ind] = 	iterator(col);
				throw $continue;
			}
		});
	});
	return ret_arr;
}
function $I(type, parent, FIRST){
	var detected = (parent && parent.IS_ARRAY ? parent : ($T("INPUT", parent) || [])).detects(detectFuncs.type.bind(type));
	return detected.length ? (FIRST ? detected[0] : detected) : null;
}
function $Node(elem){//extend node with usefull functions
	if(!elem){
		return null;
	}
	for(var i in Node_extendFucntions){
		if(typeof elem[i] == 'undefined'){
			elem[i] = Node_extendFucntions[i];
		}
	}
	return elem;
}
function $F(form){//getFormElements
	var detected = ($T("INPUT", form) || []).concat($T("SELECT", form) || []).concat($T("TEXTAREA", form) || []);
	return detected.length ? detected : null;
}
function $N(name, parent, FIRST){//getFormElementByName
	var detected = (parent && parent.IS_ARRAY ? parent :  ($F(parent) || [])).detects(detectFuncs.name.bind(name));
	return detected.length ? (FIRST ? detected[0] : detected) : null;
}
var detectFuncs = {
	name: function(el){
		return el.name.toLowerCase() == this.toLowerCase();
	},
	className: function(el){
		return el.hasClass(this);
	},
	type: function(el){
		return el.type.toLowerCase() == this.toLowerCase();
	},
	value: function(el){
		return el.value == this;	
	}
}
var Node_extendFucntions = {
	addClass: function(c){
		if(this.hasClass(c)){
			return false;
		}
		this.className = this.className.split(" ").add(c).join(" ");
		return true;
	},
	removeClass: function(c){
		if(!this.hasClass(c)){
			return false;
		}
		this.className = this.className.split(" ").remove_v(c).join(" ");
		return true;
	},
	setClass: function(c){
		this.className = c;
	},
	replaceClass: function(old_c, c){
		this.className = this.className.replace(old_c, c);
	},	
	hasClass: function(className){
		return this.className.split(" ")._indexOf(className) != -1;
	},
	app: function(elem){
		if(!elem || !elem.nodeType){
			return false;
		}
		this.appendChild(elem);
		return true;
	},
	prep: function(elem, before_elem){
		if(!elem || !elem.nodeType){
			return false;
		}
		if(!this.firstChild){
			this.app(elem)
		}else{
			this.insertBefore(elem, before_elem || this.firstChild);
		}
		return true;
	},
	remove: function(elem){
		if(!elem || !elem.nodeType || elem.parentNode != this){
			return false;
		}
		this.removeChild(elem);
		return true;
	},
	print: function(value, CLEAR){
		if(CLEAR){
			clearElem(this);
		}
		this.app(Text(value));
	},
	hide: function(vis){
		if(vis){
			this.style.visibility = "hidden";
		}else{
			this.style.display = "none";
		}
	},
	show: function(vis){
		if(vis){
			if(typeof vis == 'string'){
				this.style.display = vis;
			}else{
				this.style.visibility = 'visible';
			}
		}else{
			this.style.display = 'block';
		}
	},
	stretch: function(node){
		var par = node || this.parentNode;
		this.style.width = par.offsetWidth + 'px';
		this.style.height = par.offsetHeight + 'px';
	},
	clear: function(){
		clearElem(this);
	}
};
Node_extendFucntions.append = Node_extendFucntions.app;
Node_extendFucntions.prepend = Node_extendFucntions.prep;

//Function.prototype
Function.prototype.bind = function(){
	var _args = [];
	var _method = this;
	var _object = arguments[0];
	for(var i = 1; i < arguments.length; i++){
		_args.push(arguments[i]);
	}
	var retfunc = function(){
		return _method.apply(_object, $A(arguments).concat(_args));
	};
	retfunc.bound = $A(arguments);
	return retfunc;
};
Function.prototype.bindAvoidingEvent = function(){
	var _args = [];
	var _method = this;
	var _object = arguments[0];
	for(var i = 1; i < arguments.length; i++){
		_args.push(arguments[i]);
	}
	return function(){
		return _method.apply(_object, $A(_args).concat(arguments));
	}	
};
function _isChild(elem, parent){
	if(!elem)
		return false;
	var par = elem.parentNode;
	try{
		while(par && par != parent && par.nodeType != 9){
			par = par.parentNode;
		}
	}catch(e){
		alert(alert(par.nodeType));
	}
	return !par ? false : (par == parent);
}
function clearElem(elem){
	elem = _elem(elem);
	while(elem.firstChild){
		elem.removeChild(elem.firstChild);
	}
}
function _elem(obj){
	if(obj.nodeName){
		return obj;
	}else{
		if(obj.elem){
			return obj.elem;
		}
		if(obj.moveable){
			return _elem(obj.moveable);
		}
		if(obj.resizeable){
			return _elem(obj.resizeable);		
		}
	}
}

//Boolean.prototype
Boolean.prototype.each = function(){
	return false;
};

//Array.prototype
Array.prototype.each = function(iterator){
	try{
		for(var i = 0; i < this.length; i++){
			try{
				var ret = iterator(this[i], i);
				if(ret != null){
					this[i] = ret;
				}
			}catch(e){
				if(e != $continue)
					throw e;
			}
		}
	}catch(e){
		if(e != $break)
			throw e;
	}
	return this;
};
Array.prototype.clone = function(){
	var arr = [];
	this.each(function(item){
		arr.push(item);
	});
	return arr;
};
Array.prototype._indexOf = function(item){
	var index = -1;
	this.each(function(it, ind){
		if(it == item){
			index = ind;
			throw $break;
		}
	});
	return index;
}
if(_undefined(Array.prototype.indexOf)){
	Array.prototype.indexOf = Array.prototype._indexOf;
};
Array.prototype.random = function(REM){
	var ind = Math.round(Math.random() * (this.length - 1));
	var item = this[ind];
	if(REM){
		this.remove(ind);
	}
	return item;
};
Array.prototype.last = function(){
	return this[this.length - 1];
};
Array.prototype.invert = Array.prototype.reverse;
Array.prototype.detect = function(iterator, obj){
	var ret;
	this.each(function(item, index){
		if((iterator || _eF)(item, index)){
			ret = obj ? $O(item, index) : item;
			throw $break;
		}
	});
	return ret;
};
Array.prototype.detects = function(iterator, obj){
	var ret = [];
	this.each(function(item, index){
		if((iterator || _eF)(item, index)){
			ret.push(obj ? $O(item, index) : item);
		}
	});
	return ret;
};
Array.prototype.remove = function(num){
	if(num >= this.length || num < 0){
		return;
	}
	var ret = this[num];
	for(var i = num; i < this.length - 1; i++){
		this[i] = this[i+1];
	}
	this.pop();
	return ret;
};
Array.prototype.insert = function(value, position){
	position = (position && position > 0) ? position : 0;
	if(position >= this.length){
		this.push(value);
		return;
	}
	for(var i = this.length - 1; i >= position; i--){
		this[i+1] = this[i];
	}
	this[position] = value;
	return this;
};
Array.prototype.add = function(v){
	this.push(v);
	return this;
};
Array.prototype.remove_v = function(v){
	this.remove(this.indexOf(v));
	return this;
}
Array.prototype.has = function(val){
	return (this.indexOf(val) != -1);
}
Array.prototype.clean = function(val){
	var indexes = [];
	this.each(function(item, index){
		if(item == val){
			indexes.push(index);
		}
	});
	indexes.each(function(ind){
		this.remove(ind);
	}.bind(this));
	return this;
}
Array.prototype.before = function(item){
	return this.slice(0, this.indexOf(item));
};
Array.prototype.after = function(item){
	return this.slice(this.indexOf(item) + 1);
};
Array.prototype.next = function(item, LOOP){
	var ind = this.indexOf(item);
	if(ind == -1){
		return false;
	}
	if(ind == this.length - 1){
		if(LOOP){
			return this[0];
		}else{
			return false;
		}
	}else{
		return this[ind + 1];
	}
};
Array.prototype.previous = function(item, LOOP){
	var ind = this.indexOf(item);
	if(ind == -1){
		return false;
	}
	if(ind == 0){
		if(LOOP){
			return this.last();
		}else{
			return false;
		}
	}else{
		return this[ind - 1];
	}
};
Array.prototype.replace = function(val, pos){
	var ind = this.indexOf(val);
	if(ind < pos){
		this.remove(ind);
		this.insert(val, pos - 1);
	}else{
		this.remove(ind);
		this.insert(val, pos);	
	}
};
Array.prototype.shuffle = function(){
	var cloned = this.clone();
	var shuffled = [];
	while(cloned.length){
		shuffled.push(cloned.random(1));
	}
	return shuffled;
};
Array.prototype.interplace = function(num1, num2){
	var val1 = this[num1];
	var val2 = this[num2];
	this[num2] = val1;
	this[num1] = val2;
};
Array.prototype.bool = function(param){
	return this[Number(Boolean(param))];
}
Array.prototype.IS_ARRAY = true;
Number.prototype.signOf = function(){
	if(this == 0){
		return 1;
	}else{
		return Math.abs(this) / this;
	}
};
Number.prototype._signOf = function(){
	if(this == 0){
		return 0;
	}else{
		return Math.abs(this) / this;	
	}
};
Number.prototype.oppSign = function(){
	return this.signOf * (-1);
};
Number.prototype.sq = function(){
	return this * this;
};
Number.prototype.power = function(pow){
	if(pow == 0){
		return 1;
	}else if(pow < 0){
		throw "Sorry, I'm bad at mathematics. I can't raise a number to a negative power";
	}
	var num = this;
	for(var i = 1; i < pow; i++){
		num *= this;
	}
	return num;
};
Number.prototype._in = function(from, to, STRICT){
	if(STRICT){
		return (this > from && this < to);
	}else{
		return (this >= from && this <= to);
	}
};
Number.prototype.times = function(iterator){
	for(var i = 0; i < Math.abs(this); i++){
		iterator(i * this.signOf());
	}
};
function toConsole(what, console){
	if(_undefined(what)){
		what = "[undefined]";
	}
	console = console ? console : !_undefined(document.body) ? document.body : null;
	if(!console){
		document.write(what);
	}
	var oDiv = document.createElement('DIV');
	oDiv.className = toConsole.defaultElemClassName;
	oDiv.appendChild(document.createTextNode(what));
	console.appendChild(oDiv);
	if(console.onprint){
		console.onprint.fire();
	}
}
toConsole.defaultElemClassName = "printed-elem";
var print = toConsole;
String.prototype.print = Array.prototype.print = Number.prototype.print = Boolean.prototype.print = Function.prototype.print = function(console){
	print(this, console);
};
String.prototype.camelize = function(){
	var arrThis = this.split('-');
	if(arrThis.length == 1){
		return this;
	}else{
		var wordCamelized = arrThis[0];
		var firstSymbol;
		for(var i = 1; i < arrThis.length; i++){
			firstSymbol = arrThis[i].substr(0, 1);
			arrThis[i] = arrThis[i].substr(1);
			arrThis[i] = firstSymbol.toUpperCase() + arrThis[i];
			wordCamelized+=arrThis[i];
		}
		return wordCamelized;
	}
};
String.prototype.s_each = function(iterator){
	var arr = this.split('');
	arr.each(iterator);
};
String.prototype.no_print_characters_only = function(){
	var ret = true;
	this.s_each(function(chr){
		if(chr.charCodeAt(0) >= 30){
			ret = false;
			throw $break;
		}
	});
	return ret;
};
String.prototype.splitBy = function(num){
	if(num <= 1){
		return this.split('')
	}
	var splitted = [];
	var start = 0;
	for(var i = 0; i < this.length; i+=num){
		splitted.push(this.substr(i, num))
	}
	return splitted;
};
String.prototype.multiply = function(){
	return this + this;
};
String.prototype.reverse = String.prototype.invert = function(){
	var str = "";
	for(var i = this.length - 1; i >= 0; i--){
		str += this.charAt(i);
	}
	return str;
};
String.prototype.eq = function(str){
	var uThis = this.toUpperCase();
	return (uThis == str.toUpperCase());
};
String.prototype.has_separated_value = function(value, separator){
	var ret = false;
	var reg = new RegExp("^" + value + "$");
	var strings = this.split(separator || " ");
	strings.each(function(s){
		if(s.match(reg)){
			ret = true;
			throw $break;
		}
	});
	return ret;
};
function printObject(o, parent){
	for(var i in o){
		print(i + " => " + o[i], parent);
	}
}
var print_r = printObject;
function __init_settings(settings){
	var obj = Object.extend(Object.copy(this.constructor.__default), settings || {});
	return obj;
}
function __init_iterators(iterators){
	return Object.extend(Object.copy(this.constructor.__iterators), iterators || {});
}
function __iterate(item, name){
	if(!this.iterators[name]){
		return item;
	}
	this.iterators[name].each(function(obj_iter){
		item = obj_iter.func.call(this, item);
	}.bind(this));
	return item;
}
function __init_events(){
	this._events.each(function(obj){
		if(obj.func_name){
			if(obj.type == "before"){
				createBeforeEvent(this, obj.func_name, obj.name)
			}else if(obj.type == "after"){
				createAfterEvent(this, obj.func_name, obj.name);
			}
		}else{
			this["on" + obj.name] = new DOMEvent(this);
		}
	}.bind(this));
}
function __to_instances(){
	if(this.constructor._TO_INSTANCES){
		this.constructor._instances.push(this);
	}
}
var default_prototype_functions = {
	init_settings: __init_settings, init_iterators: __init_iterators, iterate: __iterate, init_events: __init_events, toString: __toString, to_instances: __to_instances
};

function $Radios(tpl_collection, label_settings, iterators){
	this.collection = tpl_collection;
	this.radios = this.elems = [];
	this.labels = [];
	this.disabled = [];
	this.onchange = new DOMEvent(this);
	this.ondisable = new DOMEvent(this);
	this.onenable = new DOMEvent(this);
	this.onadd = new DOMEvent(this);
	this.onremove = new DOMEvent(this);
	this.settings = this.init_settings(label_settings);
	this.iterators = this.init_iterators(iterators);
	this.init();
}
$Radios.prototype.init_settings = __init_settings;
$Radios.prototype.init_iterators = __init_iterators;
$Radios.prototype.init = function(){
	this.collection.each(function(item, ind){
		this.init_radio(item, ind);
		this.init_label(item, ind);
	}.bind(this));
	if(this.collection.length){
		this.pattern = this.collection[0].cloneNode(true);
		this.parent = this.collection[0].parentNode;
	}
};
$Radios.prototype.init_radio = function(item, ind){
	var rad = $I("radio", item, 1);
	this.radios.push(rad);
	if(rad.checked){
		this.checked = rad;
		this.value = rad.value;
	}
	if(rad.disabled){
		this.disabled.push(rad);
	}
	rad.model = this;
	rad.root = item;
	item._radio = rad;
	rad.onclick = this.click;
	this.iterators.radio.each(function(iter){
		rad = iter(rad);
	});
};
$Radios.prototype.init_label = function(item, ind){
	var lab = this.find_label(item);
	this.labels.push(lab);
	if(!lab){
		return null;
	}
	lab.radio = item._radio;
	lab.model = this;
	lab.root = item;
	lab.value = lab.firstChild.nodeValue;
	lab.onclick = this.click.bind(lab.radio);	
	item._label = lab;
	item._radio.label = lab;
	this.iterators.label.each(function(iter){
		lab = iter(lab);
	});
};
$Radios.prototype.find_label = function(item){
	var detected = $T(this.settings.labelTag, item);
	if(!detected){
		return null;
	}
	if(this.settings.labelClass){
		return $C(this.settings.labelClass, detected, null, 1);
	}else{
		return detected[0];
	}	
};
$Radios.prototype.check = function(rad){
	if(rad.disabled){
		return false;
	}
	var old_checked = this.checked;
	rad.checked = true;
	this.checked = rad;
	this.value = rad.value;
	this.onchange.fire(rad, old_checked);
	return true;
};
$Radios.prototype.click = function(){

	this.model.check(this);
};
$Radios.prototype.remove = function(rad){
	var ind = this.elems.indexOf(rad);
	var item = this.collection[ind];
	var lab = this.labels[ind];

	this.parent.remove(item);
	this.collection.remove(ind)
	this.elems.remove(ind);
	this.labels.remove(ind);
	this.onremove.fire(rad, rad.label, rad.root);
};
$Radios.prototype.change_label = function(lab, val){
	clearElem(lab);
	lab.app(Text(val));
	lab.value = val;
}
$Radios.prototype.add = function(rad_val, label_val){
	var item = this.pattern.cloneNode(true);
	this.collection.push(item);
	var ind = this.collection.length - 1;
	this.parent.app(item);
	this.init_radio(item, ind);
	this.init_label(item, ind);
	if(item._label){
		this.change_label(item._label, label_val);
	}
	item._radio.value = rad_val;
	
	this.onadd.fire(item._radio, item._label, item);
};
$Radios.prototype.disable = function(rad){
	if(rad.disabled){
		return false;
	}
	rad.disabled = 1;
	this.disabled.push(rad);
	this.ondisable.fire(rad);
	return true;
};
$Radios.prototype.enable = function(rad){
	if(!rad.disabled){
		return false;
	}
	rad.disabled = 0;
	this.disabled.remove_v(rad);
	this.onenable.fire(rad);
	return true;
};
$Radios.prototype.__name = "radios";
$Radios.prototype.toString = __toString;

$Radios.__default = {
	labelClass: "label",
	labelTag: "SPAN"
};
$Radios.__iterators = {
	radio: [], label: []
};

function $Checkbox(checkbox, label){
	this.elem = checkbox;
	this.name = this.elem.name;
	this.label = label || null;
	this.oncheck = new DOMEvent(this);
	this.onuncheck = new DOMEvent(this);
	this.ondisable = new DOMEvent(this);
	this.onenable = new DOMEvent(this);
	this.init();
}
$Checkbox.prototype.init = function(){
	this.init_check();
	this.init_label();
};
$Checkbox.prototype.init_check = function(){
	this.CHECKED = this.elem.checked;
	this.DISABLED = this.elem.disabled;
	this.elem.model = this;
	this.elem.onclick = this.click.bind(this);
};
$Checkbox.prototype.init_label = function(){
	if(!this.label){
		return false;
	}
	this.label.value = this.label.firstChild.nodeValue;
	this.label.model = this;
	this.label.onclick = this.click.bind(this);
};
$Checkbox.prototype.check = function(){
	if(this.CHECKED || this.DISABLED){
		return false;
	}
	this.elem.checked = 1;
	this.CHECKED = true;
	this.oncheck.fire();
	return true;
};
$Checkbox.prototype.uncheck = function(){
	if(!this.CHECKED || this.DISABLED){
		return false;
	}
	this.elem.checked = 0;
	this.CHECKED = false;
	this.onuncheck.fire();
	return true;
};
$Checkbox.prototype.click = function(){
	if(this.CHECKED){
		this.uncheck();
	}else{
		this.check();
	}
};
$Checkbox.prototype.set_name = function(name){
	this.elem.name = name;
	this.name = name;
};
$Checkbox.prototype.set_label = function(val){
	this.label.value = val;
	clearElem(this.label);
	this.label.app(Text(val));
}
$Checkbox.prototype.disable = function(){
	if(this.DISABLED){
		return false;
	}
	this.DISABLED = true;
	this.elem.disabled = 1;
	this.ondisable.fire();
	return true;
};
$Checkbox.prototype.enabel = function(){
	if(!this.DISABLED){
		return false;
	}
	this.DISABLED = false;
	this.elem.disabled = 0;
	this.onenable.fire();
	return true;
};
function $Checkset(checkbox_collection){

}
function _eF(x){
	return x;
}
function clearElem(elem){
	if(!elem){
		return false;
	}
	while(elem.firstChild){
		elem.removeChild(elem.firstChild);
	}
}
function Text(val){
	return document.createTextNode(val);
}
function Element(tName, attribs){
	var el = document.createElement(tName);
	var obj = attribs || {};
	for(var i in obj){
		el[i] = obj[i]
	}
	return $Node(el);
}

function serialize_inputs(root, inputs, prefix){
	inputs.each(function(ip){
		root[(prefix || "_") + ip.name] = ip.value;
	});
}
function set_form_values(rv, inputs, root){
	var ips = inputs;
	if(!inputs.IS_ARRAY){
		ips = $F(inputs);
	}
	for(var i in rv){
		var el = $N(i, ips, 1);
		if(el){
			el.value = rv[i];
		}else{
			if(root){
				root["_" + i] = rv[i];
			}
		}
	}
}
function get_form_values(inputs){
	var ips = inputs;
	if(!inputs.IS_ARRAY){
		ips = $F(inputs);
	}

	var qo = {};
	ips.each(function(ip){
		if(ip.type != "button" && ip.type != "image" && ip.type != "submit"){
			qo[ip.name] = ip.value;
		}
	});
	return qo;
}
var HELP = {
	iterators: {
		serialize_inputs: {
			func: function(item){
				serialize_inputs(item, $I("hidden", item));
				return item;
			},
			name: 'serizlize inputs'
		}
	}
}
function stopPropagation(e){
	var ev = window.event || e;
	if(ev.stopPropagation){
		ev.stopPropagation();
	}else{
		ev.cancelBubble = true;
	}
}
function preventDefault(e){
	if(e.preventDefault){
		e.preventDefault();
	}else{
		e.returnValue = false;
	}
}
function AttachEvent(objectTarget, eventType, funcionHandler){
	if (objectTarget.addEventListener) { //for DOM-compliant browsers
		objectTarget.addEventListener(eventType, funcionHandler, false);
	}
	else if(objectTarget.attachEvent){ //for IE
		objectTarget.attachEvent('on' + eventType, funcionHandler);
	}
	else{ //for all others
		objectTarget['on' + eventType] = funcionHandler;
	}

}
function DetachEvent(objectTarget, eventType, funcionHandler){
	if (objectTarget.addEventListener){ //for DOM-compliant browsers
		objectTarget.removeEventListener(eventType, funcionHandler, false);
	}
	else if(objectTarget.attachEvent){ //for IE
		objectTarget.detachEvent('on' + eventType, funcionHandler);
	}
	else { //for all others
		objectTarget['on' + eventType] = '';
	}
}
