//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){
	for(var i in obj2){
		obj1[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), $Node);
	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(arr.length == 1 && 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){
	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 = 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;
};
Array.prototype.add = function(v){
	this.push(v);
	return this;
};
Array.prototype.remove_v = function(v){
	this.remove(this.indexOf(v));
	return 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.pc = function(val){
	return (this / 100) * val;
};
//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;
};

//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 $Node(elem){
	if(!elem){
		return null;
	}
	for(var i in Node_extendFucntions){
		if(typeof elem[i] == 'undefined'){
			elem[i] = Node_extendFucntions[i];
		}
	}
	return elem;
}
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;
	}
};
