/*
 * Copyright (c) 1997-2009, Ron Lussier. All rights reserved.
 *
 * This software is an unpublished work subject to a confidentiality agreement
 * and protected by copyright and trade secret law. Unauthorized copying,
 * redistribution or other use of this work is prohibited. All copies must
 * retain this copyright notice. Any use or exploitation of this work without
 * authorization could subject the perpetrator to criminal and civil liability.
 *
 * The above copyright notice does not indicate actual or intended publication
 * of this source code.
 */

/*---------------------------------------------------------------------------------------------------------
 * This file contains the core code used by Earthenberm applications for dom manipulation.
 *
 * This file requires the following EARTHENBERM files to be included:
 *      eb-common.js
 *---------------------------------------------------------------------------------------------------------*/

if (typeof EB == "undefined") {
	/**
	 * The EARTHENBERM global namespace object.
	 */
	var EB = {};
	}

if (typeof EB.dom == "undefined") {
	/**
	 * The EARTHENBERM domain object model global namespace object.
	 */
	EB.dom = {};
	}

/*
 * Utility shortcut that checks to see if its parameter is a string.  If it is, the
 * function returns the DOM object with that string as an ID.  Otherwise, the parameter is
 * simply returned.
 *
 * For brevity, this function is not part of the EB.dom class.
 *
 * @param x		the element ID or element to return.
 */
function id( x ) {
	if (typeof x == 'string') {
		return document.getElementById(x);
		}

		return x;
	}

/*
 * Returns true if the specified 'descendant' is truly a descendant of 'ancestor', otherwise
 * returns false.
 */
EB.dom.isDescendant = function( descendant, ancestor ) {
	if (descendant == ancestor) {
		return true;
	}
	else {
		while((descendant = descendant.parentNode)) {
			if (descendant == ancestor) {
				return true;
			}
		}
	}

	return false;
}

/**
 * This method clears the contents of the page.  It is called when the user is about to be redirected to a
 * new location, and it keeps them from performing any actions on the current page.
 * TODO: this is not working in IE7!!!
 *
 * @param hideElementID		An optional ID of a HTML element to hide.  The interstitial content will be
 *							placed within this element.  If this is not specified, the entire page is hidden.
 * @param onGray			true if the spinner that is shown should have a gray background.
 */
EB.dom.clearPage = function(hideElementID, onGray)
{
	if (typeof hideElementID !== 'string') {
		hideElementID = 'PageContent';
	}
	
	if (typeof onGray !== 'boolean') {
		onGray = false;
	}

	var		hideElement = id(hideElementID);
	
	if (hideElement) {
		var		spinner = id('InterstitialDisplay');

		document.title = 'Loading...';
		hideElement.style.visibility = 'hidden';

		hideElement.parentNode.appendChild(spinner);
		spinner.style.display = 'block';
		
		window.scrollTo(0, 0);
	}
}


/**
 * This method clears the contents of the page.  It is called when the user is about to be redirected to a
 * new location, and it keeps them from performing any actions on the current page.
 */
EB.dom.clearPageAndRedirect = function(newURL, hideElementID, onGray)
{
	if (typeof newURL === 'string') {
		EB.dom.gotoURL(newURL);
		EB.dom.clearPage(hideElementID, onGray);
	}
}

/**
 * This method will tell the browser to go to the url specified by newLoc.  If you
 * pass just a page name with no namespace it assumes the current namespace. 
 */
EB.dom.gotoURL = function(newURL) {
	window.location = newURL;
	}

/**
 * This method will scroll the window so that the specified named anchor is in view. 
 *
 * @param namedAnchor		The named anchor to scroll into view, for example '#top'.
 */
EB.dom.scrollToAnchor = function( namedAnchor ) {
	if (typeof namedAnchor == 'string') {
		window.location.replace( namedAnchor );
		}
	}

/*
 * This method will return true if the specified element exists in the DOM and false otherwise.
 * The parameter should be an element name.
 */
EB.dom.elementExists = function(element) {
	if (typeof element != 'string') {
		return false;
		}
		
	var result = document.getElementById(element);
	
	if (result == null)
		return false;
	
	if (typeof result != 'object') {
		return false;
		}
		
	return true;
	}

/*
 * Set the width and height for the specified element.  Either an element object or
 * element ID may be passed.
 */
EB.dom.setElementSize = function(element,w,h) {
	element = id(element);

    element.style.width = w +"px";
    element.style.height = h +"px";
	}

/*
 * Set the width for the specified element.  Either an element object or
 * element ID may be passed.
 */
EB.dom.setElementWidth = function(element,w) {
	element = id(element);

    element.style.width = w +"px";
	}

/*
 * Set the height for the specified element.  Either an element object or
 * element ID may be passed.
 */
EB.dom.setElementHeight = function(element,h) {
	element = id(element);

    element.style.height = h +"px";
	}

/*
 * Set the top position for the specified element.  Either an element object or
 * element ID may be passed.
 */
EB.dom.setElementTop = function(element,t) {
	element = id(element);

    element.style.top = t +"px";
	}

/*
 * Set the left position for the specified element.  Either an element object or
 * element ID may be passed.
 */
EB.dom.setElementLeft = function(element,l) {
	element = id(element);

    element.style.left = l +"px";
	}

/*
 * Set the 'display' attribute for the specified element.  Either an element object or
 * element ID may be passed.
 *
 * The display option may be one of the following strings:
 *      'none' - the element is not displayed
 *      'inline' - the element is displayed with no surrounding line breaks.
 *      'block' - the element is displayed on its own line.
 */
EB.dom.setElementDisplay = function(element,disp) {
	element = id(element);

    element.style.display = disp;
	}

/*
 * Retrieve the 'display' attribute for the specified element.  Either an element object or
 * element ID may be passed.
 *
 * The display option may be one of the following strings:
 *      'none' - the element is not displayed
 *      'inline' - the element is displayed with no surrounding line breaks.
 *      'block' - the element is displayed on its own line.
 */
EB.dom.getElementDisplay = function(element,disp) {
	element = id(element);

    return element.style.display;
}

/*
 * Set the opacity for the specified element.  Either an element object or
 * element ID may be passed.
 *
 * An opacity of '0.0' means that the object is invisible; an opacity of '1.0' means that
 * the object is fully visible.
 */
EB.dom.setElementOpacity = function(element,op) {
	element = id(element);

    element.style.opacity = op;
}

/*
 * Set the 'src' attribute for the specified element.  Either an element object or
 * element ID may be passed.
 */
EB.dom.setElementSrc = function(element,src) {
	element = id(element);

   	element.src = src;
}

/*
 * Set the 'href' attribute for the specified element.  Either an element object or
 * element ID may be passed.
 */
EB.dom.setElementHref = function(element,href) {
	element = id(element);

   	element.href = href;
}

/*
 * Set the innter HTML for the specified element.  Either an element object or
 * element ID may be passed.
 */
EB.dom.setElementInnerHTML = function(element,content) {
	element = id(element);

	element.innerHTML = content;
}

EB.dom.getStyle = function(element, styleName) {
	if (element.style[styleName]) {
		return element.style[styleName];
	}
	else if (element.currentStyle) {
		return element.currentStyle[styleName];
	}
	else if (document.defaultView && document.defaultView.getComputedStyle) {
		var		style = document.defaultView.getComputedStyle( element, null );
		var		styleValue = style[styleName];

		return styleValue;
	}
	else {
		return null;
	}
}


/*
 * Return an array with the current scroll position.
 */
EB.dom.getPageScroll = function() {

	var 	xScroll;
	var		yScroll;

	if (self.pageYOffset) {

		yScroll = self.pageYOffset;
		xScroll = self.pageXOffset;
	}
	else if (document.documentElement && document.documentElement.scrollTop) {	 // Explorer 6 Strict

		yScroll = document.documentElement.scrollTop;
		xScroll = document.documentElement.scrollLeft;
	}
	else if (document.body) {													// all other Explorers

		yScroll = document.body.scrollTop;
		xScroll = document.body.scrollLeft;	
	}

	arrayPageScroll = new Array( xScroll, yScroll );
 
	return arrayPageScroll;
}

/*
 * Returns an array with page width, height and window width, height
 */
EB.dom.getPageSize = function() {
	
	var 	xScroll;
	var		yScroll;

	if (window.innerHeight && window.scrollMaxY) {

		xScroll = window.innerWidth + window.scrollMaxX;
		yScroll = window.innerHeight + window.scrollMaxY;
	}
	else if (document.body.scrollHeight > document.body.offsetHeight){		// all but Explorer Mac

		xScroll = document.body.scrollWidth;
		yScroll = document.body.scrollHeight;
	}
	else {					// Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari

		xScroll = document.body.offsetWidth;
		yScroll = document.body.offsetHeight;
	}

	var		windowWidth;
	var		windowHeight;

	if (self.innerHeight) {													// all except Explorer
		if (document.documentElement.clientWidth) {
			windowWidth = document.documentElement.clientWidth; 
		}
		else {
			windowWidth = self.innerWidth;
		}
		windowHeight = self.innerHeight;
	}
	else if (document.documentElement && document.documentElement.clientHeight) {	// Explorer 6 Strict Mode

		windowWidth = document.documentElement.clientWidth;
		windowHeight = document.documentElement.clientHeight;
	}
	else if (document.body) {												// other Explorers

		windowWidth = document.body.clientWidth;
		windowHeight = document.body.clientHeight;
	}	

	// for small pages with total height less then height of the viewport
	
	if (yScroll < windowHeight) {
		pageHeight = windowHeight;
	}
	else { 
		pageHeight = yScroll;
	}

	// for small pages with total width less then width of the viewport

	if (xScroll < windowWidth) {
		pageWidth = xScroll;		
	}
	else {
		pageWidth = windowWidth;
	}

	var		pageSizeArray = new Array( pageWidth, pageHeight, windowWidth, windowHeight );

	return pageSizeArray;
}


/*---------------------------------------------------------------------------------------------------------
 *	CSS Class manipulation functions
 *---------------------------------------------------------------------------------------------------------*/

/**
 * Return the elements matching a specified class.  The returned elements may be limited to be the
 * children of a specified node and/or only a specifed tag name.
 *
 * @returns		an array of elements matching the search.  the array may be empty if no elements match.
 */
EB.dom.getElementsByClass = function(searchClass, node, tag) {
	var		classElements = new Array();

	if ( node == null ) {
		node = document;
	}
	if ( tag == null ) {
		tag = '*';
	}

	var		els = node.getElementsByTagName(tag);
	var		elsLen = els.length;
	var		pattern = new RegExp( "(^|\\\\s)" + searchClass + "(\\\\s|$)" );
	
	for (i = 0, j = 0; i < elsLen; i++) {
		if ( pattern.test(els[i].className) ) {
			classElements[j] = els[i];
			j++;
		}
	}

	return classElements;
}

EB.dom.hasClass = function(element, className) {
	element = id(element);

   	var		regExp = new RegExp('(\\s|^)' + className + '(\\s|$)');

	return element.className.match(regExp);
}
 
EB.dom.addClass = function(element, className) {
	element = id(element);

	if (!EB.dom.hasClass(element, className)) {
		element.className += ' ' + className;
	}
}
 
EB.dom.removeClass = function(element, className) {
	element = id(element);

	if (EB.dom.hasClass(element, className)) {
    	var		regExp = new RegExp('(\\s|^)' + className + '(\\s|$)');

		element.className = element.className.replace(regExp, ' ');
	}
}


/*---------------------------------------------------------------------------------------------------------
 *	Coordinate-manipulation functions
 *---------------------------------------------------------------------------------------------------------*/

/*
 * Static coordinate factory & coordinate methods.
 */
EB.coordinates = {

	create : function( x, y )
	{
		return new EB.dom.coord( this, x, y );
	},

	origin : function()
	{
		return this.create( 0, 0 );
	},

	/*
	 * FIXME: Safari 1.2, returns (0,0) on absolutely positioned elements
	 */
	topLeftPosition : function( element )
	{
		var		left = parseInt( EB.dom.getStyle( element, 'left' ) );
		var		left = isNaN( left ) ? 0 : left;
		var		top = parseInt( EB.dom.getStyle( element, 'top' ) );
		var		top = isNaN( top ) ? 0 : top;

		return this.create( left, top );
	},

	bottomRightPosition : function( element )
	{
		return this.topLeftPosition( element ).plus( this._size( element ) );
	},

	topLeftOffset : function( element )
	{
		var		offset = this._offset( element );
		var		parent = element.offsetParent;

		while (parent)
		{
			offset = offset.plus( this._offset( parent) );
			parent = parent.offsetParent;
		}

		return offset;
	},

	bottomRightOffset : function( element )
	{
		return this.topLeftOffset( element ).plus( this.create( element.offsetWidth, element.offsetHeight ) );
	},

	scrollOffset : function()
	{
		if (window.pageXOffset) {
			return this.create( window.pageXOffset, window.pageYOffset );
		}
		else if (document.documentElement) {
			return this.create( document.body.scrollLeft + document.documentElement.scrollLeft, 
								document.body.scrollTop + document.documentElement.scrollTop );
		}
		else if (document.body.scrollLeft >= 0) {
			return this.create( document.body.scrollLeft, document.body.scrollTop );
		}
		else {
			return this.create( 0, 0 );
		}
	},

	clientSize : function()
	{
		if (window.innerHeight >= 0) {
			return this.create( window.innerWidth, window.innerHeight );
		}
		else if (document.documentElement) {
			return this.create( document.documentElement.clientWidth,
								document.documentElement.clientHeight );
		}
		else if (document.body.clientHeight >= 0) {
			return this.create( document.body.clientWidth,
								document.body.clientHeight );
		}
		else {
			return this.create( 0, 0 );
		}
	},

	/**
	 * @return		The mouse coordinates relative to the window (technically the browser client
	 *				area).
	 */
	mousePosition : function( event )
	{
		event = EB.dom.fixEvent(event);
		
		return this.create( event.clientX, event.clientY );
	},

	/**
	 * @return		The mouse coordinates relative to the document.
	 */
	mouseOffset : function( event )
	{
		event = EB.dom.fixEvent( event );
		
		if ((event.pageX >= 0) || (event.pageX < 0)) {
			return this.create( event.pageX, event.pageY );
		}
		else if ((event.clientX >= 0) || (event.clientX < 0)) {
			return this.mousePosition( event ).plus( this.scrollOffset() );
		}
	},

	_size : function( element )
	{
		return this.create( element.offsetWidth, element.offsetHeight );
	},

	_offset : function( element )
	{
		return this.create( element.offsetLeft, element.offsetTop );
	}
}

/**
 * Constructor for a coordinate object.
 */
EB.dom.coord = function( factory, x, y ) {
	this.factory = factory;
	this.x = isNaN(x) ? 0 : x;
	this.y = isNaN(y) ? 0 : y;
}

/**
 * Add new methods to the coordinate object via the prototype.
 */
EB.dom.coord.prototype = {
	toString : function()
	{
		return '(' + this.x + ',' + this.y + ')';
	},

	plus : function( that )
	{
		return this.factory.create( this.x + that.x, this.y + that.y );
	},

	minus : function( that )
	{
		return this.factory.create( this.x - that.x, this.y - that.y );
	},

	min : function( that ) {
		return this.factory.create( Math.min( this.x , that.x ),
									Math.min( this.y , that.y ) );
	},

	max : function( that )
	{
		return this.factory.create( Math.max( this.x , that.x ),
									Math.max( this.y , that.y ) );
	},

	constrainTo : function( coord1, coord2 )
	{
		var		min = coord1.min( coord2 );
		var		max = coord1.max( coord2 );

		return this.max( min ).min( max );
	},

	distance : function( that )
	{
		return Math.sqrt( Math.pow( this.x - that.x, 2 ) +
						  Math.pow( this.y - that.y, 2 ) );
	},

	reposition : function( element ) {
		element.style['top'] = this.y + 'px';
		element.style['left'] = this.x + 'px';
	}
}

/**
 * Finds the script element with the specified ID, and then executes the contents of that element.
 */
EB.dom.executeScript = function(element) {
	element = id(element);

	if ((typeof element != 'undefined') && (element != null)) {
		if ((typeof element == 'object') && (typeof element.tagName != 'undefined')) {
			if (element.tagName == 'SCRIPT') {
				if (typeof element.innerHTML != 'undefined') {
					eval( element.innerHTML );
				}
			}
		}
	}
}

/**
 * Enables or disables an anchor tag.  When disabled, any 'onclick' method assigned with the
 * anchor will be removed unless the allowClicks parameter is passed as 'true'.
 *
 * @param anchor		The anchor ID or dom element.
 * @param disable		True if the anchor should be disabled, and false if it should be enabled.
 *						This parameter is optional, and defaults to 'true'.
 * @param allowClicks	True if the onclick method should be left on the anchor.  If this value
 *						is not specified or false, then any onclick method will be removed from the
 *						anchor when it is disabled.
 */
EB.dom.disableAnchor = function( anchor, disable, allowClicks ) {

	if (typeof disable == 'undefined') {
		disable = true;
	}

	if (typeof disable != 'boolean') {
		throw "The 'disable' parameter to EB.dom.disableAnchor must be a boolean value.";
	}

	anchor = id( anchor );

	if (disable) {
		var		href = anchor.getAttribute( 'href' );
		
		if (href && (href != '') && (href != null)) {
			anchor.setAttribute( 'href_bak', href );
		}
		anchor.removeAttribute( 'href' );

		var		onclick = anchor.getAttribute( 'onclick' );

		if (onclick && (onclick != '') && (onclick != null)) {
			anchor.setAttribute( 'onclick_bak', onclick );
		}
		anchor.removeAttribute( 'onclick' );
		}
	else {
		anchor.setAttribute( 'href', anchor.getAttribute('href_bak') );
		anchor.removeAttribute( 'href_bak' );

		anchor.setAttribute( 'onclick', anchor.getAttribute('onclick_bak') );
		anchor.removeAttribute( 'onclick_bak' );
	}
}

/**
 * Return the value of the checked radio button in a group.  If no buttons are checked, or there
 * are no radio buttons, then an empty string is returned.
 *
 * @param radioObject		The radio button object to be searched.  Note that this can be a 
 *							single radio button or a group of radio buttons.
 */
EB.dom.getRadioValue = function( radioObject ) {

	if (!radioObject) {
		return '';
	}

	var		radioLength = radioObject.length;

	if (radioLength == undefined) {
		if(radioObject.checked) {
			return radioObject.value;
		}
		else {
			return '';
		}
	}

	for (var i = 0; i < radioLength; i++) {
		if (radioObject[i].checked) {
			return radioObject[i].value;
		}
	}

	return '';
}

/**
 * Check the radio button with the specified value.  If the specified value doesn't exist, then
 * all buttons are unchecked.
 *
 * @param radioObject	The radio button object to be searched.  Note that this can be a 
 *						single radio button or a group of radio buttons.
 * @param value			The value to be checked.
 */
EB.dom.setRadioValue = function( radioObject, newValue ) {

	if (!radioObject) {
		return;
	}

	var		radioLength = radioObject.length;

	if (radioLength == undefined) {
		radioObject.checked = (radioObject.value == newValue.toString());
		return;
	}

	for (var i = 0; i < radioLength; i++) {
		radioObject[i].checked = false;

		if (radioObject[i].value == newValue.toString()) {
			radioObject[i].checked = true;
		}
	}
}

/**
 * This function will set all of the check boxes in a specified container to a specified state.
 * An optional single checkbox may be excepted from being changed.
 *
 * @param formName				The name of the form containing the checkboxes.
 * @param container				The object or object ID containing all of the checkboxes to be
 *								returned.  This value may be ommitted or null.
 * @param checked				true if the boxes should be checked, and false if they should be
 *								cleared.  If this value is not specified, it defaults to true.
 * @param exceptionCheckbox		If this parameter is specified, it can specify a checkbox (or
 *								checkbox ID) that will not be returned by this function.
 */
EB.dom.setCheckboxes = function( formName, container, checked, exceptionCheckbox  ) {
	var		form = document.forms[formName];
	var		container = id(container);

	if (typeof container == 'undefined') {
		container = null;
	}
	else if (typeof container == 'string') {
		container = id( container );
	}

	if (typeof checked != 'boolean') {
		checked = true;
	}

	if (typeof exceptionCheckbox == 'undefined') {
		exceptionCheckbox = null;
	}
	else if (typeof exceptionCheckbox == 'string') {
		exceptionCheckbox = id( exceptionCheckbox );
	}
	
	for (var i = 0; i < form.elements.length; i++) {
		var		thisElement = form.elements[i];

		if (thisElement != exceptionCheckbox) {

			if ((container == null) || EB.dom.isDescendant( thisElement, container )) {

				var		elementType = thisElement.getAttribute( 'type' );

				if (elementType == 'checkbox') {
					thisElement.checked = checked;
				}
			}
		}
	}
}

/**
 * This function get an Array of all of the checkboxes in the specified form and having the
 * specified container as a parent that are either checked or unchecked.
 *
 * @param formName				The name of the form containing the checkboxes.
 * @param container				The object or object ID containing all of the checkboxes to be
 *								returned.  This value may be ommitted or null.
 * @param checked				true if the boxes should be checked, and false if they should be
 *								cleared.  This value may be ommitted or passed as null if all
 *								checkboxes in the specified container and form are desired.
 * @param exceptionCheckbox		If this parameter is specified, it can specify a checkbox (or
 *								checkbox ID) that will not be returned by this function.
 *
 * @returns						An Array of checkboxes meeting the criteria specified.  This
 *								array may have a length of zero if no checkboxes meet the criteria.
 */
EB.dom.getCheckboxes = function( formName, container, checked, exceptionCheckbox  ) {
	var		form = document.forms[formName];
	var		container = id(container);
	var		results = new Array();

	if (typeof container == 'undefined') {
		container = null;
	}
	else if (typeof container == 'string') {
		container = id( container );
	}

	if (typeof checked == 'undefined') {
		checked = null;
	}

	if (typeof exceptionCheckbox == 'undefined') {
		exceptionCheckbox = null;
	}
	else if (typeof exceptionCheckbox == 'string') {
		exceptionCheckbox = id( exceptionCheckbox );
	}
	
	for (var i = 0; i < form.elements.length; i++) {
		var		thisElement = form.elements[i];

		if (thisElement != exceptionCheckbox) {
			if ((container == null) || EB.dom.isDescendant( thisElement, container )) {

				var		elementType = thisElement.getAttribute( 'type' );

				if (elementType == 'checkbox') {

					if ((checked == null) || (thisElement.checked == checked)) {

						results.push( thisElement );
					}
				}
			}
		}
	}
	
	return results;
}

/**
 * This function will clear any selection options from the specified select list.
 *
 * @param selectList		The ID or object reference of the list to be cleared.
 */
EB.dom.clearList = function(selectList) {
	selectList = id(selectList);
	
	if (selectList && selectList.options && selectList.options.length) {
		var		selectOptions = selectList.options;
		
		for (var loop = 0; loop < selectOptions.length; loop++) {
			selectOptions[loop].selected = false;;
		}
	}
}

/**
 * This function will add title attributes to each item in a select list.  The title will be the same as
 * the list item text.
 *
 * @param selectList		The ID or object reference of the list to be modified.
 */
EB.dom.addListTitles = function(selectList) {
	selectList = id(selectList);

	if (selectList && selectList.options && selectList.options.length) {
		var		selectOptions = selectList.options;
	
		for (var loop = 0; loop < selectOptions.length; loop++) {
			var		title = selectOptions[loop].text;

			title = title.replace(/^[\s\-]*/, '');
			selectOptions[loop].title = title;
		}
	}
}

/**
 * This function will check whether the specified field has a purely numeric value.  This means
 * a value that has a standard numeric format.  Commas and/or a sign may optionally be allowed.
 * Leading and trailing whitespace is ignored.
 *
 * @param field					The field object or field ID to be checked.
 * @param allowSign				If this value is true, then the number may contain a '+' or '-'
 *								specifying the sign of the number.  This parameter is optional,
 *								and defaults to 'true'.
 * @param allowCommas			If this value is true, then commas will be ignored when checking.
 *								This parameter is optional, and defaults to 'false'.
 * @param emptyStringResult		The value to return if the field contains an empty string.
 *								This parameter is optional, and defaults to 'true'. 
 *
 * @return						true if the specified field has a valid numeric value,
 *								and false otherwise.
 */
EB.dom.hasNumericValue = function( field, allowSign, allowCommas, emptyStringResult ) {
	var		validNumber;
	
	if (typeof allowSign != 'boolean') {
		allowSign = true;
		}

	if (typeof allowCommas != 'boolean') {
		allowCommas = false;
		}

	if (typeof allowCommas != 'boolean') {
		allowCommas = false;
		}

	if (typeof emptyStringResult != 'boolean') {
		emptyStringResult = true;
		}

	field = id( field );
	
	if (!field) {
		// Field doesn't exist or doesn't have a value attribute, so it's not a valid number.
		return false;
	}
	
	var		fieldValue = field.value;
	
	fieldValue = fieldValue.trim();
	
	if (fieldValue.length <= 0) {
		return emptyStringResult;
	}
	
	var		validNumberPattern = '^';

	if (allowSign) {
		validNumberPattern += '[+-]?';
	}
	
	if (allowCommas) {
		validNumberPattern += '[0-9\\,]*';
	}
	else {
		validNumberPattern += '[0-9]*';
	}

	validNumberPattern += '(\\.[0-9]*)?$';
	
	validNumberPattern = new RegExp( validNumberPattern );

	var		valid = validNumberPattern.test( fieldValue );
	
	return valid;
}

/**
 * This function will check whether the specified field has a purely numeric value.  This means
 * a value that has a standard numeric format.  Commas and/or a sign may optionally be allowed.
 * Leading and trailing whitespace is ignored.
 *
 * @param name					The name of the cookie to set.
 * @param value					The value to assign to the cookie.  This value will be properly escaped
 *								if necessary.
 * @param expires				The number of days until this cookie expires.
 * @param secure				true if the cookie should be secure.
 */
EB.dom.setCookie = function(name, value, expires, path, domain, secure) {
	// set time, it's in milliseconds
	var today = new Date();

	today.setTime( today.getTime() );

	/*
	 * If the expires variable is set, make the correct expires time
	 */
	if ( expires ) {
		expires = expires * 1000 * 60 * 60 * 24;
	}

	var		expires_date = new Date(today.getTime() + expires);
	
	var		cookieString = name + "=" + escape( value ) +
							((expires) ? ";expires=" + expires_date.toGMTString() : "") + 
							((path) ? ";path=" + path : "") + 
							((domain) ? ";domain=" + domain : "") +
							((secure) ? ";secure" : "");

	document.cookie = cookieString;
}

/**
 * This function will return the value of a cookie with the specified name, if that cookie exists.
 * If the cookie is not found, null is returned.
 */
EB.dom.getCookie = function(name) {
	name = name.trim();

	/*
	 * First we'll split this cookie up into name/value pairs.
	 * (NOTE: document.cookie only returns name=value, not the other components)
	 */
	var		allCookies = document.cookie.split( ';' );

	for (var loop = 0; loop < allCookies.length; loop++) {
		var		tempCookie = '';
		var		cookieName = '';
		var		cookieValue = '';

		// Split apart each name=value pair
		tempCookie = allCookies[loop].split( '=' );

		// Trim left/right whitespace while we're at it
		cookieName = tempCookie[0].trim();
	
		// if the extracted name matches passed name
		if (cookieName === name)
		{
			cookieFound = true;

			// we need to handle case where cookie has no value but exists (no = sign, that is):
			if (tempCookie.length > 1)
			{
				cookieValue = unescape( tempCookie[1].trim() );
			}

			// note that in cases where cookie is initialized but no value, null is returned
			return cookieValue;
			break;
		}
	}

	return null;
}				

/**
 * Delete the specified cookie.
 */
EB.dom.deleteCookie = function( name, path, domain ) {
	if (EB.dom.getCookie(name)) {
		document.cookie = name + "=" +
							((path) ? ";path=" + path : "") +
							((domain) ? ";domain=" + domain : "") +
							";expires=Thu, 01-Jan-1970 00:00:01 GMT";
	}
}

/**
 * Returns true if the browser supports cookies, and false otherwise.
 */
EB.dom.cookiesEnabled = function() {
	var		cookieTextName = 'gdCookieTest';
	var		cookieTestValue = 'Billy Elliot: The Musical';
	var		enabled;

	if (EB.isIE()) {
		EB.dom.setCookie( cookieTextName, cookieTestValue, 1 );
		
		var		cookieVal = EB.dom.getCookie( cookieTextName );

		if (cookieVal && (cookieVal === cookieTestValue)) {
			enabled = true;
		}
		else {
			enabled = false;
		}
		
		EB.dom.deleteCookie(cookieTextName);
	}
	else {
		if (navigator) {
			enabled = navigator.cookieEnabled;
		}
		else {
			enabled = true;			// benefit of the doubt?
		}
	}

	return enabled;
}


/*---------------------------------------------------------------------------------------------------------
 *	Scrollbar width function
 *---------------------------------------------------------------------------------------------------------*/

/**
 * Cached scroll width & height.  (Why would they ever change?)
 */
EB.dom._cachedScrollWidth = -1;

/**
 * Returns the width of a scroll bar for the current system.
 */
EB.dom.getScrollerWidth = function() {
	if (EB.dom._cachedScrollWidth <= 0) {
	    var		testDiv = null;
	    var		contentDiv = null;
	    var		widthNoScrollBars = 0;
	    var		widthScrollBars = 0;

	    // Outer scrolling div
	    scrollingDiv = document.createElement('div');
	    scrollingDiv.style.position = 'absolute';
	    scrollingDiv.style.top = '-1000px';
	    scrollingDiv.style.left = '-1000px';
	    scrollingDiv.style.width = '100px';
	    scrollingDiv.style.height = '50px';
	    // Start with no scrollbar
	    scrollingDiv.style.overflow = 'hidden';
	    scrollingDiv.style.border = '1px dotted blue';

	    // Inner content div
	    contentDiv = document.createElement('div');
	    contentDiv.style.width = '100%';
	    contentDiv.style.height = '200px';

	    // Put the inner div in the scrolling div
	    scrollingDiv.appendChild(contentDiv);
	    // Append the scrolling div to the doc
	    document.body.appendChild(scrollingDiv);

	    // Width of the inner div sans scrollbar
	    widthNoScrollBars = contentDiv.offsetWidth;
	    // Add the scrollbar
	    scrollingDiv.style.overflow = 'scroll';
	    // Width of the inner div width scrollbar
	    widthScrollBars = contentDiv.offsetWidth;

		if (widthNoScrollBars == widthScrollBars) widthScrollBars = scrollingDiv.clientWidth;

	    // Remove the scrolling div from the doc
	    document.body.removeChild(scrollingDiv);

	    // Pixel width of the scroller
	    EB.dom._cachedScrollWidth = (widthNoScrollBars - widthScrollBars);
	}

	return EB.dom._cachedScrollWidth;
};


/*---------------------------------------------------------------------------------------------------------
 *	Event-manipulation functions
 *---------------------------------------------------------------------------------------------------------*/

/*
 * Resolve IE inconsistencies with a DHTML event.  This function should be called within every event handler
 * to resolve IE/standards discrepancies.
 *
 * @param event		The event that was passed to the event handler.  (For IE, this will be undefined.)
 *
 * @return			The event to process.
 */
EB.dom.fixEvent = function (event) {
	/*
	 * IE doesn't pass the event to the handler, but rather attaches it to the window.
	 */
	if (!event) {
		event = window.event;
	}

	if (event.target) {
		/*
		 * Check to see if the target is a TEXT_NODE, and if so, apply the event to the text node's
		 * parent.
		 */
		if (event.target.nodeType === 3) {
			event.target = event.target.parentNode;
		}
	}
	else if (event.srcElement) {
		/*
		 * srcElement is an IE-only property, and is the equivalent of the 'target' property on
		 * the standard event model.
		 */
		event.target = event.srcElement;
	}

	return event;
};


/*
 * Attach an event listener to a DOM object in a platform-independant way.
 *
 * @return			The event to process.
 */
EB.dom.attachEventListener = function (target, eventType, functionRef, capture) {
	if (typeof capture !== 'boolean') {
		capture = false;
	}

	/*
	 * function can be called with a pointer or a string representing a function name
	 */
	if (typeof functionRef === 'string') {
		functionRef = eval(functionRef);
	}

	if (typeof target.addEventListener != 'undefined') {
		target.addEventListener(eventType, functionRef, capture);
	}
	else if (typeof target.attachEvent != 'undefined') {
		target.attachEvent('on' + eventType, functionRef);
	}
	else {
		eventType = 'on' + eventType;
		
		if (typeof target[eventType] == 'function') {
			var		oldListener = target[eventType];

			target[eventType] = function() {
												oldListener();
											
												return functionRef();
										   };
		}
		else {
			target[eventType] = functionRef;
		}
	}
};

/*
 * Cancel event propagation.
 *
 * @param event		The event that was passed to the event handler.  (For IE, this will be undefined.)
 *
 * @return			the boolean value 'false'.
 */
EB.dom.stopEventPropagation = function (event) {
	if ((navigator.userAgent.toLowerCase().indexOf('msie') != -1) && document.all) {
		window.event.cancelBubble = true;
		window.event.returnValue = false;
	}
	else {
		event.stopPropagation();
	}
	
	return false;
};
