/*
 * Script: Classy.js
 * Author: Charles Demers (charles.demers6@gmail.com)
 * Version: 1.0
 * 
 * Inspired by Mootools (http://www.mootools.net)
 * and Douglas Crockford (http://javascript.crockford.com)
 * 
 * Provides: [Classy]
 */
	
var Classy = {
	version:1.0
};

Classy.typeOf = function(obj){
	if(obj === undefined){ return 'undefined'; }
	if(obj === null){ return 'null'; }
	if(obj instanceof Array){ return 'array'; }
	if(obj instanceof RegExp){ return 'regexp'; }
	if(obj instanceof Date){ return 'date'; }
	if(typeof obj == "number" && !isFinite(obj)){ return 'nan'; }
	if(obj.callee){ return 'arguments'; }
	if(obj.item){ return 'collection'; }
	if(obj.nodeName){
		if(obj.nodeType == 1){ return 'element'; }
		if(obj.nodeTYpe == 3){ return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace'; }
	}
	return typeof obj;
};

Classy.unlink = function(obj){
	switch (typeOf(obj)){
		case 'object':
			return Classy.Object.clone(obj);
		break;
		case 'array':
			return Classy.Array.clone(obj);
		break;
		default: 
			return obj;
		break;
	}
	return null;
};


Classy.Delay = function(func, delay, scope, args){
	var t = Classy.typeOf(scope);
	var s,a;
	if(t == "array"){
		s = func;
		a = scope;
	} else {
		s = (scope) ? scope : func;
		a = (args) ? args : [];
	}
	this.timer = (setTimeout(function(){
		func.apply(s,a);
	},delay));
	return this.timer;
};
Classy.Delay.prototype.clear = function(){
	clearTimeout(this.timer);
};
Classy.Periodical = function(func, delay, scope, args){
	var t = Classy.typeOf(scope);
	var s,a;
	if(t == "array"){
		s = func;
		a = scope;
	} else {
		s = (scope) ? scope : func;
		a = (args) ? args : [];
	}
	this.timer = (setInterval(function(){
		func.apply(s,a);
	},delay));
};
Classy.Periodical.prototype.clear = function(){
	clearInterval(this.timer);
};


/*
 * Provides : Array utils
 */
Classy.Array = {
	clone:function(arr){
		var ret = [];
		for (var i=0, l=obj.length; i<l; i++){ ret[i] = Classy.unlink(arr[i]); }
		return ret;
	},
	associate:function(arr,keys){
		var ret = {};
		for(var i=0,l=keys.length; i<l; i++){
			ret[keys[i]] = arr[i];
		}
		return ret;
	},
	clean:function(arr){
		var ret = [];
		for(var i=0,l=arr.length; i<l; i++){
			if(arr[i] !== null){
				ret[i] = arr[i];
			}
		}
		return ret;
	},
	combine:function(arr){
		var ret = arr.clone();
		for(var i=0, l=arr.length; i<l; i++){
			if(Classy.Array.contains(ret,arr[i]) === false){
				ret.push(arr[i]);
			}
		}
		return ret;
	},
	contains:function(arr,obj,from){
		from = (from) ? from : 0;
		for(var i=from, l=arr.length; i<l; i++){
			if(arr[i] === obj){ return true; }
		}
		return false;
	},
	empty:function(arr){
		for(var i=arr.length; i>=0; i--){ delete arr[i]; }
		arr.length = 0;
		return arr;
	},
	every:function(arr,fn,scope){
		scope = (scope) ? scope : arr;
		for(var i=0, l=arr.length; i<l; i++){
			if(!fn.call(scope,arr[i],i,arr)){ return false; }
		}
		return true;
	},
	extend:function(arr,extend){
		for(var i=0, l=extend.length; i<l; i++){ arr.push(extend[i]); }
		return arr;
	},
	filter:function(arr,fn,scope){
		scope = (scope) ? scope : arr;
		var ret = [];
		for(var i=0,l=arr.length; i<l; i++){
			if(fn.call(scope,arr[i],i,arr)){ ret.push(arr[i]); }
		}
		return ret;
	},
	flatten:function(arr){
		var ret = [];
		for(var i=0,l=arr.length; i<l; i++){
			if(arr[i] instanceof Array){
				var level = Classy.Array.flatten(arr[i]);
				for(var j=0,len=level.length; j<len; j++){
					ret.push(level[j]);
				}
			} else {
				ret.push(arr[i]);
			}
		}
		return ret;
	},
	forEach:function(arr,fn,scope){
		scope = (scope) ? scope : arr;
		for(var i=0, l=arr.length; i<l; i++){ fn.call(scope,arr[i],i); }
	},
	getLast:function(arr){
		return arr[arr.length-1] || null;
	},
	getRandom:function(arr){
		return arr[Classy.Number.random(0,arr.length-1)] || null;
	},
	include:function(arr,obj){
		if(Classy.Array.contains(arr,obj) === false){
			arr.push(obj);
		}
		return arr;
	},
	indexOf:function(arr,obj,from){
		from = (from) ? from : 0;
		for(var i=from, l=arr.length; i<l; i++){
			if(arr[i] === obj){ return i; }
		}
		return -1;
	},
	indexesOf:function(arr,obj,from){
		var ret = [];
		from = (from) ? from : 0;
		for(var i=from, l=arr.length; i<l; i++){
			if(arr[i] === obj){ ret.push(i); }
		}
		if(ret.length > 0){ return ret; }
		return null;
	},
	lastIndexOf:function(arr,obj,from){
		from = (from) ? from : arr.length;
		for(var i=from; i>=0; i--){
			if(arr[i] === obj){ return i; }
		}
		return -1;
	},
	lastIndexesOf:function(arr,obj,from){
		var ret = [];
		from = (from) ? from : arr.length;
		for(var i=from; i>=0; i--){
			if(arr[i] === obj){ ret.push(i); }
		}
		if(ret.length > 0){ return ret; }
		return null;
	},
	map:function(arr,fn,scope){
		scope = (scope) ? scope : arr;
		var ret = [];
		for(var i=0,l=arr.length; i<l; i++){
			ret.push(fn.call(scope,arr[i],i,arr));
		}
		return ret;
	},
	remove:function(arr,obj){
		for(var i=0,l=arr.length; i<l; i++){
			if(arr[i] === obj){
				arr.splice(i,1);
			}
		}
		return arr;
	},
	some:function(arr,fn,scope){
		scope = (scope) ? scope : arr;
		for(var i=0, l=arr.length; i<l; i++){
			if(fn.call(scope,arr[i],i,arr)){ return true; }
		}
		return false;
	},
	unique:function(arr){
		var ret = [];
		for(var i=0,l=arr.length; i<l; i++){
			if(ret.indexOf(arr[i]) == -1){
				ret.push(arr[i]);
			}
		}
		return ret;
	}
};


/*
 * Provides : Object utils
 */
Classy.Object = {
	
	clone:function(obj){
		var ret = {};
		for(var n in obj){ ret[n] = Classy.unlink(obj[n]); }
		return ret;
	},
	implement:function(obj,implementing){
		for(var n in implemeting){
			if(obj.hasOwnProperty(n) === false){
				obj[n] = implementing[n];
			}
		}
		return obj;
	},
	extend:function(obj,extending){
		for(var n in extending){
			obj[n] = extending[n];
		}
		return obj;
	},
	add:function(obj,key,value){
		if(!obj.hasOwnProperty(key)){ obj[key] = value; }
		return this;
	},
	combine:function(obj,combining){
		for(var n in combining){
			if(!obj.hasOwnProperty(n)){
				obj[n] = combining[n];
			}
		}
		return obj;
	},
	empty:function(obj){
		for(var n in obj){
			if(obj.hasOwnProperty(n)){
				delete obj[n];
			}
		}
		return obj;
	},
	every:function(obj,fn,scope){
		scope = (scope) ? scope : obj;
		for(var n in obj){
			if(obj.hasOwnProperty(n)){
				if(!fn.call(scope,obj[n],n,obj)){ return false; }
			}
		}
		return true;
	},
	forEach:function(obj,fn,scope){
		scope = (scope) ? scope : obj;
		for(var n in obj){
			if(obj.hasOwnProperty(n)){
				fn.call(scope,obj[n],n,obj);
			}
		}
	},
	filter:function(obj,fn,scope){
		scope = (scope) ? scope : obj;
		var ret = {};
		for(var n in obj){
			if(obj.hasOwnProperty(n)){
				if(fn.call(scope,obj[n],n,obj)){
					ret[n] = obj[n];
				}
			}
		}
		return ret;
	},
	getKeys:function(obj){
		var ret = [];
		for(var n in obj){
			if(obj.hasOwnProperty(n)){
				ret.push(n);
			}
		}
		return ret;
	},
	getValues:function(obj){
		var ret = [];
		for(var n in this){
			if(obj.hasOwnProperty(n)){
				ret.push(obj[n]);
			}
		}
		return ret;
	},
	has:function(obj,key){
		return obj.hasOwnProperty(key);
	},
	hasValue:function(obj,value){
		for(var n in obj){
			if(obj.hasOwnProperty(n)){
				if(obj[n] == value){ return true; }
			}
		}
		return false;
	},
	keyOf:function(obj,value){
		for(var n in obj){
			if(obj.hasOwnProperty(p)){
				if(obj[n] == value){ return n; }
			}
		}
		return false;
	},
	keysOf:function(obj,value){
		var ret = [];
		for(var n in obj){
			if(obj.hasOwnProperty(n)){
				if(obj[n] == value){ ret.push(n); }
			}
		}
		if(ret.length > 0){ return ret; }
		return false;
	},
	length:function(obj){
		var ret = 0;
		for(var n in obj){ 
			if(obj.hasOwnProperty(n)){ ret++; }
		}
		return ret;
	},
	map:function(obj,fn,scope){
		scope = (scope) ? scope : obj;
		var ret = {};
		for(var n in obj){
			if(Classy.Object.has(obj,n)){
				ret[n] = fn.call(scope,obj[n],n,obj);
			}
		}
		return ret;
	},
	remove:function(obj,key){
		if(Classy.Object.has(obj,key)){
			delete obj[key];
		}
		return obj;
	},
	removeObject:function(obj,removing){
		for(var n in obj){
			if(Classy.Object.has(obj,n)){
				if(obj[n] == removing){ delete obj[n]; }
			}
		}
		return obj;
	},
	some:function(obj,fn,scope){
		scope = (scope) ? scope : obj;
		for(var n in obj){
			if(Classy.Object.has(obj,n)){
				if(fn.call(scope,obj[n],n,obj)){ return true; }
			}
		}
		return false;
	}
};


/*
 * Provides : Number utils
 */
Classy.Number = {
	random:function(min,max){
		return Math.floor(Math.random() * (max - min + 1) + min);
	},
	limit:function(num,min,max){
		if(num < min){
			return min;
		} else if(num > max){
			return max;
		} else {
			return num;
		}
	}
};


/*
 * Provides : String utils
 */
Classy.String = {
	camelCase:function(str){
		var strList = str.split("-");
		if(strList.length == 1){ return strList[0]; }
	
		var camelizedStr = strList[0];
		for(var i=1, l=strList.length; i<l; i++){
			var string = strList[i];
			camelizedStr += string.charAt(0).toUpperCase() + string.substring(1);
		}
		return camelizedStr;
	},
	capitalize:function(str){
		var strList = str.split(" ");
		var capitalizedStr = "";
		for(var i=0, l=strList.length; i<l; i++){
			var string = strList[i];
			capitalizedStr += string.charAt(0).toUpperCase() + string.substring(1)+" ";
		}
		return capitalizedStr.substring(0,str.length);
	},
	hyphenate:function(str){
		var hyphenatedStr = str.charAt(0);
		for(var i=1, l=str.length; i<l; i++){
			var curChar = str.charAt(i);
			if(/[A-Z]/.test(curChar)){
				hyphenatedStr += "-"+curChar.toLowerCase();
			} else {
				hyphenatedStr += curChar;
			}
		}
		return hyphenatedStr;
	},
	stripTags:function(str){
		return str.replace(/<\/?[^>]+>/gi,'');
	},
	toFloat:function(str){
		var number = parseFloat(str);
		return isFinite(number) ? number : null;
	},
	toInt:function(str){
		var number = parseInt(str,10);
		return isFinite(number) ? number : null;
	},
	supplant:function(str,o,regexp){
		regexp = regexp || new RegExp(/{([^{}]*)}/g);
		return str.replace(regexp,
			function (a, b) {
				var r = o[b];
				return typeof r === 'string' || typeof r === 'number' ? r : a;
			}
		);
	},
	trim:function(str){
		return str.replace(/^\s+|\s+$/g, "");
	}
};


/*
 * Provides : Function utils
 */
Classy.Function = {
	bind:function(func,scope, args){
		args = (Classy.typeOf(args) == "array") ? args : [args];
		return function(){
			func.apply(scope, args);
		};
	}
};
