/**
@author bibby
**/



/**
@author Crockford , bibby
A better typeof. Accurately returns null and array.and the objects Number, String, Boolean
Can also be used as an "is_a" or a simple test for truth.
examples:
	var foo = 'bar';
	is(foo); // true;
	is(foo,'string'); // true
	is(foo,'type'); // string
@param mixed variable
@param string (optional) data type
**/
var is = function (v,type)
{
	if(typeof type == 'undefined')
		return !!v;
	
	type=type.toLowerCase();
	if(type == 'nan')
	{
		return isNaN(v);
	}
	
	var vt = typeof v;
	if(vt === 'object')
	{
		if(v)
		{
			if (typeof v.length === 'number' && !(v.propertyIsEnumerable('length')) && typeof v.splice === 'function') 
			{              
				vt = 'array';
			}
			
			// last chance to return new Number() as a number :P
			if(v instanceof Number)
				vt = 'number';
			if(v instanceof String)
				vt = 'string';
			if(v instanceof Boolean)
				vt = 'boolean';
		}
		else
		{
			vt = 'null';
		}
	}
	
	if(type=="type")
		return vt;
	
	return vt == type ? true : false;
};


/*
@author bibby
@author (DCrockford, Dedwards, JResig, prototype guys, 10% of entire javascript writing community)
@param object: key=value pairs. Can be used to mash up objects or set multiple key:values
@param bool:  overwrite existing members?
*/
Object.prototype.extend = function(o,over)
{
	if(!is(o,'object'))
		return this;
	for(i in o)
		if(is(over) || (!is(over) && !is(this[i])))
			this[i]=o[i];
	return this;
}

Object.prototype.extend({
	extends:function(cls,over)
	{
		if(is(cls,'function'))
		{
			try
			{
				this.extend(new cls,over);
			}
			catch(e)
			{
				return e;
			}
		}		
		else if(is(cls,'object'))
			this.extend(cls,over);
		return this;
	}
});


Array.prototype.extend(
{
	pointer:0,
	each:function(callback)
	{
		if(is(callback,'function'))
		{
			for(var i = 0; i < this.length; i++)
				callback.call(this[i],i);
		}
	},
	
	reset:function()
	{
		this.pointer = 0;
	},
	
	current:function()
	{
		return this[this.pointer];
	},
	
	hasNext:function()
	{
		return is(this[this.pointer+1]);
	},
	
	
	hasPrev:function()
	{
		return is(this[this.pointer-1]);
	},
	
	next:function()
	{
		if(this.hasNext() == false)
			return false;
		else
		{
			this.pointer++;
			return this.current();
		}
	},
	
	prev:function()
	{
		if(this.hasPrev() == false)
			return false;
		else
		{
			this.pointer--;
			return this.current();
		}
	},
	
	'in':function(val)
	{
		var ret = false;
		for(var index=0; index<this.length; index++)
		{
			if(this[index] == val)
			{
				ret = index;
				break;
			}
		}
		return ret;
	},
	
	/* filter out falsey values, [0,undefined,''] */
	filterFalse:function()
	{
		return this.filter(function(i)
		{
			return is(i);
		});
	},
	
	/* filter out values given as arguments */
	filterOut:function()
	{
		return this.filter(function(value,index,args)
		{
			return !is(args.in(value),'number');
		},arguments);
	}
});



Math.extend(
{
	range:function(a,b)
	{
		if(!is(a,'number') || !is(b,'number'))
			return false;
		
		var ret = [], c = a>b?[b,a]:[a,b], i;
		for(i = Math.floor(c[0]); i<=Math.floor(c[1]); i++)
			ret.push(i);
		
		return ret;
	},
	rand:function(a,b)
	{
		if(!is(a,'number') || !is(b,'number'))
			return false;
		
		if(a==b)
			return a;
		
		var c = a>b?[b,a]:[a,b]
		return Math.floor((Math.random()*((c[1]+1)-c[0])) + c[0]);
	},
	
	/* add up values
	@param mixed  array:sums array values , or sums up numeric arguments to any end
	@return int
	Math.sum(1,2,3,4);  // same as
	Math.sum([1,2,3,4]);
	*/
	sum:function(args)
	{
		if(!is(args,'array'))
			args = arguments;
		
		if(!is(args.filter,'function'))
			args.filter = [].filter;
		
		var s=0, vals = args.filter(function(i){ return is(i,'number'); });
		for(var i = 0; i<vals.length;i++)
			s+=vals[i];
		
		return s;
	},
	
	/* average values
	@param mixed  array:avg array values , or avg numeric arguments to any end
	@return float
	*/
	avg:function(args)
	{
		if(!is(args,'array'))
			args = arguments;
		
		if(!is(args.filter,'function'))
			args.filter = [].filter;
		
		var s=0, vals = args.filter(function(i){ return is(i,'number'); });
		if(vals.length==0)
			return 0;
		for(var i = 0; i<vals.length;i++)
			s+=vals[i];
		
		return s/vals.length;
	}
});
