//	dom-support.js

//	TODO: Put global functions in a namespace. 

//	Copy properties from the source to the destination. 
function copyProperties( dest, src )
{
	if (dest && src)
	{
		for (var name in src)
			dest[name] = src[name];
	}
	
	return dest;
}

//	Copy properties from the source to the destination. 
function combineProperties()
{
	var result = new Object();
	for (var i = 0; i < arguments.length; ++i)
		copyProperties( result, arguments[i] );
	return result;
}

//	Trim white space from the front and back of the string. 
//	Return a new string, does not modify this string. 
String.prototype.trim = function()
{
	return this.replace( /^\s+|\s+$/g, "" );
}

//	Append an object onto the back of the array. 
String.prototype.startsWith = function( arg )
{
	if (arg.length == 0)
		return true;
	
	if (this.length < arg.length)
		return false;
	
	return this.slice( 0, arg.length ) == arg;
}

//	Append an object onto the back of the array. 
String.prototype.endsWith = function( arg )
{
	if (arg.length == 0)
		return true;
	
	if (this.length < arg.length)
		return false;
	
	return this.slice( -arg.length ) == arg;
}

//	Return the front element of the array. 
Array.prototype.front = function()
{
	return (0 < this.length) ? this[0] : null;
}

//	Return the back element of the array. 
Array.prototype.back = function()
{
	return (0 < this.length) ? this[this.length - 1] : null;
}

//	Return the back element of the array without removing it. 
Array.prototype.top = function()
{
	return (0 < this.length) ? this[this.length - 1] : null;
}

//	Insert an object onto the front of the array. 
Array.prototype.pushFront = function( obj )
{
	return this.unshift( obj );
}

//	Append an object onto the back of the array. 
Array.prototype.pushBack = function( obj )
{
	return this.push( obj );
}

//	See "http://www.brockman.se/writing/method-references.html.utf8" for explanation of closures. 
//	Convert an array-like object, like Arguments, to a real array. 
function toArray( pseudoArray )
{
	var results = [];
	if (pseudoArray)
	{
		for (var i = 0; i < pseudoArray.length; ++i)
			results.push( pseudoArray[i] );
	}
	return results;
}

//	Bind a function to an object and extra arguments. 
Function.prototype.bind = function( object )
{
	var method = this;
	var boundArgs = toArray( arguments ).slice( 1 );
	return function() 
	{
		return method.apply( object, boundArgs.concat( toArray( arguments ) ) );
	};
}

//	Bind an event listener to an object and extra arguments. 
Function.prototype.bindEventListener = function( object )
{
	var method = this;
	var boundArgs = toArray( arguments ).slice( 1 );
	return function( event ) 
	{
		return method.apply( object, [(event || window.event)].concat( boundArgs ) );
	};
}

//	DOM helpers. 
function findChildElement( node, name )
{
	for (var i = 0; i < node.childNodes.length; ++i)
	{
		if (node.childNodes[i].nodeType == 1 && 
			node.childNodes[i].nodeName.toLowerCase() == name.toLowerCase())
		{
			return node.childNodes[i];
		}
	}
	
	return null;
}

function selectElement( node, path )
{
	var where = path.indexOf( "/" );
	if (where == -1)
		return findChildElement( node, path );
	else
		return selectElement( findChild( node, path.substring( 0, where ) ), path.substring( where + 1 ) );
}

function getNodeText( node )
{
	//	Note: node.textContent is a part of the level 3 DOM standard. 
	//	Don't know which browsers support it. 
	//	textContent is recursive, this function is not. 
	
	var text = "";
	
	if (node && node.hasChildNodes())
	{
		for (var i = 0; i < node.childNodes.length; ++i)
		{
			var child = node.childNodes.item( i );
			if (child.nodeType == 3)
			{
				text += child.nodeValue;
			}
		}
	}
	
	return text.trim();
}

function getFirstElementByTagName( node, tag )
{
	var tags = node.getElementsByTagName( tag );
	if (0 < tags.length)
		return tags[0];
	
	return null;
}

function removeChildren( node )
{
	if (node && node.childNodes && node.childNodes.length)
	{
		while (0 < node.childNodes.length)
			node.removeChild( node.childNodes[0] );
	}
}

//	Style helpers. 
function getCurrentHeight( elem, pseudoClass )
{
	if (elem == null)
		return 0;
	else
	if (elem.offsetHeight)
		return elem.offsetHeight;
	else
	if (elem.currentStyle)
		return parseDim( elem.currentStyle.height );
	else
	if (document.defaultView && document.defaultView.getComputedStyle)
		return parseDim( document.defaultView.getComputedStyle( elem, pseudoClass ).height );
	else
	{
		alert( "no document.defaultView.getComputedStyle" );
		//	throw exception?
		return 0;
	}
}

function getCurrentWidth( elem, pseudoClass )
{
	if (elem == null)
		return 0;
	else
	if (elem.offsetWidth)
		return elem.offsetWidth;
	else
	if (elem.currentStyle)
		return parseDim( elem.currentStyle.width );
	else
	if (document.defaultView && document.defaultView.getComputedStyle)
		return parseDim( document.defaultView.getComputedStyle( elem, pseudoClass ).width );
	else
	{
		alert( "no defaultView.getComputedStyle" );
		//	throw exception?
		return 0;
	}
}

//	Parse a string like '24px' and ensure the units are pixels. 
function parseDim( dimension )
{
	var t = typeof dimension;
	if (t == 'number')
		return dimension;
	else
	if (t != 'string')
		dimension = dimension.toString();
	
	if (dimension == 'auto')
		return 0;
	
	var a = dimension.match( /(\d+)([\w%]*)/ );
	if (a == null || a.length < 2)
		return 0;
	
	if (a[2] != 'px')
		alert( "dimension '" + dimension + "' not in pixels\n'" + a[1] + "', '" + a[2] + "'" );
	
	return parseInt( a[1] );
}

//	Help to manage multiple class names in element's class attribute. 
//	Note: The class name functions here are pretty simple. 
//	Don't use class names that are subsets of eachother. 
//	For example, removing the class name 'class-foo' will munge the class name 'class-foo-bar'. 
function removeClassName( elem, className )
{	try {
		if (elem.className != null)
			elem.className = elem.className.replace( className, "" ).trim();
	} catch (e) {
		//if the element does not have a class attribute set, do not try to remove one.
	}
}

function addClassName( elem, className )
{
	if (elem.className.indexOf( className ) == -1)
		if (elem.className != null)
			elem.className = (elem.className + " " + className).trim();
		else
			elem.className = className;
}

function swapClassName( elem, oldClassName, newClassName )
{
	removeClassName( elem, oldClassName );
	addClassName( elem, newClassName );
}

function hasClassName( elem, className )
{
	return (0 <= elem.className.indexOf( className ));
}

