/**!
	HeroDevelopment ©2008
	http://www.herodev.com/

	Developer:	$Author: LCValentine $
	Language:	JS
	Class:		N/A
	Created:	2008-05-24
	Modified:	$Date: 2008-03-08 19:21:13 -0700 (Sat, 08 Mar 2008) $
	Revision:	$Rev: 85 $

	Dependancies: 

	[Description]
	A collection of commonly used methods.
 **/

/**
 * Returns the boolean of some value.
 * @param {Object} value
 * @return bool
 */
function bool( val ){
	return !!val;
}

function not( val ){
	return !val;
}

function num( val ){
	return parseFloat( val );
}

function str( val ){
	return val.toString();
}

/**
 * Determines whether an object is an Array. 
 * @param {Object} obj	The object to check
 * @return {Boolean}
 */
function isArray( obj ){
	return isTypeOf( obj, Array );
}

/**
 * Determines whether an object is an Function. 
 * @param {Object} obj	The object to check
 * @return {Boolean}
 */
function isFunction( obj ){
	return typeof obj == 'function';
}

function isNumber( obj ){
	return obj instanceof Number;
}

/**
 * Determines whether a value can be evaluated as purely numeric.
 * @param {Object} val	The value to check
 * @return {Boolean}
 */
function isNumeric( value ){
	return parseFloat( value ) == value;
}

/**
 * Determines whether an object is in fact just an object.
 * @param {Object} obj	The object to check
 * @return {Boolean}
 */
function isObject( obj ){
	return obj.constructor.toString().match( /function object\(/i );
}

function isObjective( obj ){
	return obj != null && isTypeOf( obj, Object );
}

/**
 * Determines if the object provided represents a scalar type (string or number).
 * @param {Object} obj	The object to check
 * @return {Boolean}
 */
function isScalar( obj ){
	return obj == null || !isObjective( obj );
}

/**
 * Determines if the object provided represents a string.
 * @param {Object} obj	The object to check
 * @return {Boolean}
 */
function isString( val ){
	try{
		return val.toString() === val;
	}catch( not ){}
	
	return false;
}

function isTypeOf( obj, cls ){
	return obj instanceof cls;
}


function callAsync( cb1, cb2 ){
	var cb = function(){
		var out = cb1();
		if( cb2 ){
			cb2( out );
		}
	}

	setTimeout( cb, 0 );
}

/**
 * Clones the members of one object to another.
 * NOTE:  References will not be dereferenced.
 * @param {Object} obj	The object to clone
 * @return {Object}	The clone
 */
function clone( obj, clone ){
	if( !clone ) clone = {};
    for( var prop in obj ){
		try{
        	var val = obj[ prop ];
			clone[ prop ] = val;
		}catch( prot ){
			continue;
		}
	}
    return clone;
}

function getNodePosition( node ){
	var pt = {
		'X': 0,
		'Y': 0
	};

	if( node.offsetParent ){	
		do{
			pt.X += node.offsetLeft;
			pt.Y += node.offsetTop;
		}while( node = node.offsetParent );
	}

	return pt;
}

/**
 * Attempts to get the measurements for the window provided or the current window.
 * @param {Window} domWin [Optional]	The window to get the measurements for
 * @return {Object}	The window's measurements
 */
function getWindowRects( domWin ){
	var coords = {};
	if( !domWin ){
		domWin = window;
	}

	var domDoc = domWin.document;

	if( domWin.innerHeight ){
		coords['Height'] = domWin.innerHeight;
		coords['Width']  = domWin.innerWidth;
	}else if( domDoc.documentElement && domDoc.documentElement.clientHeight ){
		coords['Height'] = domDoc.documentElement.clientHeight;
		coords['Width']  = domDoc.documentElement.clientWidth;
	}else if( domDoc.body ){
		coords['Height'] = domDoc.body.clientHeight;
		coords['Width']  = domDoc.body.clientWidth;
	}
	
	if( domWin.pageYOffset ){
		coords['Top']  = domWin.pageYOffset;
		coords['Left'] = domWin.pageXOffset;
	}else if( domDoc.documentElement && domDoc.documentElement.scrollTop ){
		coords['Top']  = domDoc.documentElement.scrollTop;
		coords['Left'] = domDoc.documentElement.scrollLeft;
	}else if( domDoc.body ){
		coords['Top']  = domDoc.body.scrollTop;
		coords['Left'] = domDoc.body.scrollLeft;
	}

	fullHeight1 = domDoc.body.scrollHeight;
	fullHeight2 = domDoc.body.offsetHeight
	if( fullHeight1 > fullHeight2 ){
		coords['fullHeight'] = domDoc.body.scrollHeight;
		coords['fullWidth']  = domDoc.body.scrollWidth;
	}else{
		coords['fullHeight'] = domDoc.body.offsetHeight;
		coords['fullWidth']  = domDoc.body.offsetWidth;
	}

	coords['Right']  = coords['Left'] + coords['Width'];
	coords['Bottom'] = coords['Top']  + coords['Height'];

	return coords;
}

function importScript( library ){
	library = '/'+ library.replace( '.', '/' ) +'.js';
	
	var script = createNode( 'script', { language : 'JavaScript', type : 'text/javascript', src : library } );
	document.getElementsByTagName( 'head' )[0].appendChild( script );
}

/**
 * Reads the GET variables from the URL and returns a keyed list.
 * @return {Object}
 */
function object2Query( object ){
	var query = '';
	if( object ){
		for( var prop in object ){
			var value = [];
			if (!tryGetSafe(object, prop, value)) {
				continue;
			}

			value = value[0];

			if( isScalar( value ) ){
				query += escape( prop ) +'='+ escape( value.toString() ) +'&';
			}else if( isArray( value ) ){
				for( var i = 0; i < value.length; i++ ){
					query += escape( prop ) +'[]='+ escape( value[ i ].toString() ) +'&';
				}
			}
		}
	}

	return query.trimRight( '&' );
}

function objectiveReference(){
	args = Array.toArray( arguments );
	var method = args.shift();
	var object = args.shift();

	return function(){
		return method.apply( object, arguments || args );
	}
}

/**
 * Reads the GET variables from the URL and returns a keyed list.
 * @param {String} The query string to parse
 * @return {Object}
 */
function parseQuery( query ){
	query = unescape( query );
	var pieces = query.split( /&/g );

	if( !isArray( pieces ) ){
		pieces = [ pieces ];
	}

	var kvPairs = {};
    for( var i = 0; i < pieces.length; i++ ){
		var parts = pieces[ i ].split( '=' );
    	var key = parts.shift();
		var val = parts.shift();

		if( key.indexOf( '[]' ) + 2 == key.length ){
			key = key.substring( 0, key.length - 2 );

			if( !isArray( kvPairs[ key ] ) ){
				kvPairs[ key ] = [];
			}

        	kvPairs[ key ].push( val );
		}else{
			kvPairs[ key ] = val;
		}
    }
    
    return kvPairs;
}

function staticize( fromNamespace, toNamespace ){
	var from = [];
	if( tryGetReference( fromNamespace, window, from ) ){
		from = from[0];
		to = [];
		if( tryGetReference( toNamespace, window, to ) ){
			to = to[0];
			for( var prop in from ){
				if( isFunction( from[ prop ] ) ){
					_staticize( prop, from, to );
				}
			}
		}
	}
}

function _staticize( prop, from, to ){
	to[ prop ] = function(){
		var args = Array.toArray( arguments );
		var node = args.shift();
		return from[ prop ].apply( node, args );
	}
}

function tryGetReference( namespace, ref, result ){
	if( isObjective( namespace ) ){
		result.push( namespace );
		return true;
	}else{
		var parts = namespace.split( '.' );
		if( !isArray( parts ) ) parts = [ parts ];
		ref = ref || window;

		while( parts.length ){
			var call = null;
			var pt = parts.shift();

			if( pt in ref ){
				ref = ref[ pt ];
			}else{
				return false;
			}
		}

		result.push( ref );
		return true;
	}
	
	return false;
}

function tryGetSafe( obj, prop, val ){
	try{
		val.push( obj[ prop ] );
		return true;
	}catch( e ){
		return false;
	}
}

/**
 * Reads through the first-level properties of an object and returns "key: value" for each.
 * @param {Object} obj	The object to iterate
 * @return {String}	The "key: value" sets separated by "\n"
 */
function dump(obj){
    var tmp = '';
    for( var prop in obj ){
		tmp += prop
        try{
            var val = obj[ prop ];
			tmp += ': '+ val +"\n";
        }catch( inv ){
			tmp += ": (unk)\n";
		}
    }
    return tmp;
}

//[ Array ]
Array.prototype.binarySearch = function( val ){
	var i = 0;

    for( i = 0; i < this.length; i++ ){
		if( this[ i ] < val ){
			continue;
		}else if( this[ i ] === val ){
            return i;
		}else if( this[ i ] > val ){
			break;
			//return ( i+1 ) * ( -1 );
		}
    }

	return ( i+1 ) * ( -1 );
}
Array.prototype.contains = function( val ){
	return this.indexOf( val ) != -1;
}
Array.prototype.filter = function( callback ){
	//clone args & self
	var args = Array.toArray( arguments ); 
	var bools = Array.toArray( this );
	bools.map.apply( bools, args );

	for( var i = bools.length - 1; i >= 0; i-- ){
		if( bools[ i ] === false ){
			this.splice( i, 1 );
		}
	}
	return this;
}
Array.prototype.mapFilter = function( callback ){
	this.map.apply( this, arguments );
	for( var i = this.length - 1; i >= 0; i-- ){
		if( this[ i ] === false ){
			this.splice( i, 1 );
		}
	}
	return this;
}
Array.prototype.indexOf = function( val ){
    for( var i = 0; i < this.length; i++ ){
        if( this[ i ] === val ){
            return i;
		}
    }
	return -1;
}
Array.prototype.map = function( callback ){
	if( arguments.length ){
		//remove the first item (the callback)
		//and insert a place for the value and its index
		var args = Array.toArray( arguments );
			args.splice( 0, 1, null, 0 );

		for( var i = 0; i < this.length; i++ ){
			args[0] = this[ i ];
			args[1] = i;

			this[ i ] = callback.apply( null, args );
		}
	}
	return this;
}
Array.prototype.merge = function(){
	if( isArray( arguments[0] ) ){
		for( var i = 0; i < arguments[0].length; i++ ){
			this.push( arguments[0][i] );
		}		
	}else{
		for( var i = 0; i < arguments.length; i++ ){
			this.push( arguments[i] );
		}
	}
}
Array.prototype.spliceValue = function( val ){
	var idx = this.indexOf( val );
	if( idx > -1 ){
		return this.splice( idx, 1 );
	}
}
Array.prototype.sum = function( val ){
	var sum = 0;
	for( var i = 0; i < this.length; i++ ){
		sum += num( this[ i ] );
	}
	return sum;
}
Array.toArray = function( list ){
	var array = [];
	if( !isString( list ) ){
		for( var i = 0; i < list.length; i++ ){
			array.push( list[ i ] );
		}
	}
	return array;
}
Array.prototype.walk = function( callback ){
	if( arguments.length ){
		//remove the first item (the callback)
		//and insert a place for the value and its index
		var args = Array.toArray( arguments );
			args.splice( 0, 1, null, 0 );

		for( var i = 0; i < this.length; i++ ){
			args[0] = this[ i ];
			args[1] = i;

			callback.apply( null, args );
		}
	}
	return this;
}

String.prototype.contains = function( val ){
	return this.indexOf( val ) != -1;
}
String.prototype.indent = function(){
	return "\t"+ this.replace( /\n/g, "\n\t" );
}
String.prototype.trim = function( charList ){
	return this.trimLeft( charList ).trimRight( charList );
}
String.prototype.trimLeft = function( charList ){
	var rex = charList ?
		new RegExp( '^['+ charList +']+' ) :
		/^\s+/;

	return this.replace( rex, '' );	
}
String.prototype.trimRight = function( charList ){
	var rex = charList ?
		new RegExp( '['+ charList +']+$' ) :
		/\s+$/;

	return this.replace( rex, '' );	
}

//Extend forward
if( !HD ){
	var HD = {};
}

HD.Nodes = {
	Document : 9,
	Element : 1,
	Text : 3
};

HD.State = {
	Dragging : false
};
