//global functions and objects
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
function MergeObjects(obj1, obj2, pre){
	for(var i in obj2){
		obj1[(pre || "") + i] = obj2[i];
	}
	return obj1;
}
Object.extend = MergeObjects;

//DOM functions
function $(id, doc){
	return $Node((doc || document).getElementById(id));
}
function $T(tName, parent, bool_first){
	parent = parent ? _elem(parent) : document;
	var arr = $A(parent.getElementsByTagName(tName), function(item){return $Node(item)});
	return bool_first ? arr[0] : arr;
}
function $C(className, parent, tagName, not_array){
	parent = (parent) ? _elem(parent) : document;
	tagName = tagName ? tagName : "*";
	var arr = [];
	var collection = $T(tagName, parent);
	for(var i = 0; i < collection.length; i++){
		if(collection[i].className.has_separated_value(className)){
			arr.push(collection[i]);
		}
	}
	if(arr.length == 0){
		return false;
	}else{
		if(not_array){
			return arr[0];
		}else{
			return arr;		
		}
	}
}
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 _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);		
		}
	}
}
function undefined(obj){
	return (typeof obj == "undefined");
}

//Boolean.prototype
Boolean.prototype.each = function(){
	return false;
};

//Array.prototype
Array.prototype.each = function(iterator, DUMP){
	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;
};
if(typeof Array.prototype.indexOf == "undefined"){
	Array.prototype.indexOf = function(item){
		var index = -1;
		this.each(function(it, ind){
			if(it == item){
				index = ind;
				throw $break;
			}
		});
		return index;
	}
};
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 = Array.prototype.filter = 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 false;
	}
	var ret = this[num];
	for(var i = num; i < this.length - 1; i++){
		this[i] = this[i+1];
	}
	this.pop();
	return ret;
};
Array.prototype.remove_v = function(v){
	this.remove(this.indexOf(v));
	return this;
}
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.fill = function(num, val, inst){
	var arr = this;
	num.times(function(){
		var v = inst ? new val.constructor() : val;
		arr.push(v);
	});
	return arr;
}
Array.prototype.splitAt = function(pos){
	var before = [];
	var after = [];
	this.each(function(item, ind){
		if(ind < pos){
			before.push(item);
		}else if(ind > pos){
			after.push(item);
		}
	});
	return [before, after];
};
Array.prototype.chunk = function(num){
	var chunked = [];
	var part = [];
	this.each(function(item, ind){
		part.push(item);
		if((ind + 1) % num == 0){
			chunked.push(part);
			part = [];
		}
	});
	if(part.length){
		chunked.push(part);
	}
	return chunked;
};
Array.prototype.to_matrix = function(x, y){
	return new Matrix(x, y, this);
};
//Number.prototype
Number.prototype.signOf = function(){
	if(this == 0){
		return 1;
	}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.times = function(iterator){
	for(var i = 0; i < this; i++){
		iterator(i);
	}
};
//String.prototype
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;
};
String.prototype.addClass = function(cName){
	if(this.has_separated_value(cName)){
		return this;
	}else{
		return this + " " + cName;
	}
};
String.prototype.isUpper = function(){
	return this.toUpperCase() == this;
};

//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));
	}	
};


//Debug functions
function toConsole(what, console){
	if(typeof what=="undefined"){
		what = "[undefined]";
	}
	console = console ? console : typeof document.body != "undefined" ? document.body : null;
	if(!console){
		document.write(what);
	}
	var oDiv = document.createElement('DIV');
	oDiv.className = toConsole.defaultElemClassName;
	oDiv.appendChild(document.createTextNode(what));
	_elem(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);
};
function printObject(o, parent){
	for(var i in o){
		print(i + " => " + o[i], parent);
	}
}
var print_r = printObject;

function clearPrinted(){
	var elems = $C(toConsole.defaultElemClassName);
	if(!elems){
		return;
	}
	elems.each(function(el){
		el.parentNode.removeChild(el);
	})
}
function _eF(x){
	return x;
}
var __toString = function(){
	var str = __toString.bounds.left + "object " + this.__name + __toString.bounds.right;
	return str;
};
__toString.bounds = {
	left: "[[",
	right: "]]"
};
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] = '';
	}
}
function Element(name, attrs, attrs2){
	var el = document.createElement(name);
	if(attrs){
		for(var i in attrs){
			el[i] = attrs[i];
		}
	}
	if(attrs2){
		for(var i in attrs2){
			el.setAttribute(i, attrs2[i]);
		}
	}
	return $Node(el);
}
function $Node(node){
	if(!node || node.nodeType != 1){
		return node;
	}
	return Object.extend(node, Node_extendFunctions);
}
var Node_extendFunctions = {
	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;
	},
	clear: function(){
		while(this.firstChild){
			this.removeChild(this.firstChild);
		}
	}
};
function getEvent(e){
	return {
		target: e.srcElement || e.target,
		relTarget: e.toElement || e.relatedTarget,
		c_x: e.clientX, c_y: e.clientY,
		x: typeof e.offsetX != 'undefined' ? e.offsetX : e.layerX,
		y: typeof e.offsetY != 'undefined' ? e.offsetY : e.layerY
	};
}