﻿// depends on Utilities.js

/**
 *	XBElem is an abstract data type
 *
 *	An XBElem allows the developer to interace with one object that can communicate with
 *	all supported browsers. Current browser support is IE 6.0 Win, Firefox.
 *
 *	A note about IE 6: Using attachEvent gives the event handler access to the parameter 'e'
 *	which is handy because Firefox/Mozilla always have the 'e' parameter, so between the 
 *	browsers th code is a little more similar.
 */

var defaultHiliteClassName = "hilite";
var elem;
//var buttonToTie;
 
/**
 *	CONSTRUCTOR
 *
 *	node can be either a DOM node or the Id of the desired node.
 */
function XBElem(node)
{
	this.getText = getText;
	this.setText = setText;
	this.addClassName = addClassName;
	this.removeClassName = removeClassName;
	this.hilite = hilite;
	this.resetHilite = resetHilite;
	this.toggleDisplay = toggleDisplay;
	this.show = show;
	this.hide = hide;
	this.getAncestorByTagName = getAncestorByTagName;
	this.getAncestorByClassName = getAncestorByClassName;
	this.tieToButton = tieToButton;
	this.addEventHandler = addEventHandler;
	elem = getElement(node);
	this.DOMNode = elem;
	
}
XBElem.prototype.cancelEventDefault = cancelEventDefault;

/**
 * Retrieves the text of the XBElem. E.G. for the element <span>foo</span> would return "foo"
 * Calling getText on an XBElem, the 'node' parameter is ignored.
 * If the overhead of an XBElem object is not necessary or desirable, you can pass in a DOM Node
 * and getText will return the text of that node.
 */

function getText(node)
{
	node = node || this.DOMNode;
	return node[(typeof node.innerText != "undefined") ? "innerText" : "textContent"];
}

/**
 * Sets the text of the XMElem. E.G. the element <span></span> would become <span>foo</span>.
 * Calling setText on an XBElem, the 'node' parameter is ignored.
 * If the overhead of an XBElem object is not necessary or desirable, you can pass in a DOM Node
 * and setText will set the text of that node.
 */
function setText(text, node)
{
	node = this.DOMNode || node;
	try
	{
		node[(typeof node.innerText != "undefined") ? "innerText" : "textContent"] = text;
		return text;
	}
	catch (e)
	{
		return null;
	}
}

/**
 * Appends the given class name to the XBElem. When calling addClasName from an XBElem, the 'node'
 * parameter is ignored.
 * If the overhead of an XBElem object is not necessary or desirable, you can pass in a DOM Node
 * and addClassName will add the className to that node.
 */
function addClassName(custClassName,node)
{
	node = this.DOMNode || node;
	node.className += " " + custClassName;
}

/**
 * Removes the given class name to the XBElem.
 * If the overhead of an XBElem object is not necessary or desirable, you can pass in a DOM Node
 * and removeClassName will remove the className from that node.
 */
function removeClassName(custClassName,node)
{
	node = this.DOMNode || node;
	node.className = removeText(node.className, custClassName);
}

/**
 *	Hilites the XBElem with either the defaultHiliteClassName or the className passed in.
 *	The className parameter will override the default.
 */
function hilite(className)
{
	className = className || defaultHiliteClassName;
	addClassName(className, this.DOMNode);
}

/**
 *	Resets the hilite on the XBElem by removing either the defaultHiliteClassName or the className passed in.
 *	The className parameter will override the default.
 */
function resetHilite(className)
{
	className = className || defaultHiliteClassName;
	removeClassName(className, this.DOMNode);
}

/**
 *	Toggles the display of the XBElem.
 */
function toggleDisplay()
{
	if (this.DOMNode.style.display == "none")
	{
		show(this.DOMNode);
	}
	else
	{
		hide(this.DOMNode);
	}
}

/**
 *	Resets the display property of the XBElem's style object. Calling 'show' on an XBElem, the
 *	'node' parameter is ignored.
 *	Can be used as a global method by calling 'show(node)' and passing a DOM Node object.
 */
function show(node)
{
	node = node || this.DOMNode;//if called from 'toggleDisplay' 'this' refers to the window.
	node.style.display = "";
}

/**
 *	Sets the display property of the XBElem's style object to 'none'. Calling 'hide' on an XBElem, the
 *	'node' parameter is ignored.
 *	Can be used as a global method by calling 'hide(node)' and passing a DOM Node object.
 */
function hide(node)
{
	node = node || this.DOMNode;//if called from 'toggleDisplay' 'this' refers to the window.
	node.style.display = "none";
}

/**
 *	Retrieves the 1st-encountered ancestor of the XBElem with the given tag name;
 */
function getAncestorByTagName(tagName)
{
	return getAncestorByProperty(this.DOMNode, "tagName", tagName);
}

/**
 *	Retrieves the 1st-encountered ancestor of the XBElem with the given class name;
 */
function getAncestorByClassName(className)
{
	return getAncestorByProperty(this.DOMNode, "className", className);
}

/**
 *	Ties the XBElem to the given button. That is, when the enter key is pressed on the XBElem,
 *	the given button is clicked.
 */
function tieToButton(btn)
{
    
	this.DOMNode.buttonToTie = getElement(btn);
	addEventHandler("onkeypress", buttonTieFunction, this.DOMNode);
}

/**
 *	Adds an event handler to the XBElem for the given event (eventName) by the given method (identifier).
 *	Can be used as a global method if a DOM Node object is passed in. If addEventHandler is called on an
 *	XBElem object, the 'node' parameter is ignored.
 */
function addEventHandler(eventName, identifier, node)
{
	node = this.DOMNode || node;//if called from 'tieToButton' the node is passed in because 'this' refers to the window.
	
	eventName = eventName.replace(/^on/i, "");	//Allow user to include "on" at in the event name. E.G. "onclick" vs "click"
	
	if (typeof node.attachEvent != "undefined")
	{
		node.attachEvent("on" + eventName, identifier);
	}
	else if (typeof node.addEventListener != "undefined")
	{
		node.addEventListener(eventName, identifier, false);
	}
}

/**
 *	Removes an event handler from the XBElem for the given event (eventName) by the given method (identifier).
 *	Can be used as a global method if a DOM Node object is passed in. If removeEventHandler is called on an
 *	XBElem object, the 'node' parameter is ignored.
 */
function removeEventHandler(eventName, identifier, node)
{
	node = this.DOMNode || node;//if called from 'tieToButton' the node is passed in because 'this' refers to the window.
	
	eventName = eventName.replace(/^on/i, "");	//Allow user to include "on" at in the event name. E.G. "onclick" vs "click"
	
	if (typeof node.detachEvent != "undefined")
	{
		node.detachEvent("on" + eventName, identifier);
	}
	else if (typeof node.removeEventListener != "undefined")
	{
		node.removeEventListener(eventName, identifier, false);
	}
}


/*PRIVATE METHODS*/


/**
 *	Removes the given text from the given string.
 */
function removeText(str, text)
{
	var re = new RegExp(text,"g");
	return str.replace(re, "");
}

/**
 *	Returns the first-encountered ancestor having the given property set to the given value.
 */
function getAncestorByProperty(node, sProperty, sValue)
{
	try
	{
		if (typeof node == 'object') 
		{
			var ElementFound = false;
			var target = node;
			
			while (!ElementFound) {
				target = target.parentNode;
				
				// Exit Condiion: The document element (nodeType 9) is the end of the line. Parent element not found
				if (target.nodeType == 9) 
				{
					return null;
				}
				
				if (target[sProperty].toLowerCase() == sValue.toLowerCase()) {
					ElementFound = true;
					return target;
				}
			}
		}
	}
	catch (e)
	{
		return null;
	}
}

/**
 *	The onkeypress handler used by tieToButton that clicks the button when enter is pressed.
 */
function buttonTieFunction(e)
{
	//var keyCode = (typeof window.event != "undefined") ? window.event.keyCode : e.keyCode;
	if (e.keyCode != 13) {return;}
	var tgt = e.srcElement || e.target;
	if(tgt.buttonToTie != null)
	if(typeof tgt.buttonToTie.click != "undefined")
	{
	    tgt.buttonToTie.click();
	}
	else// if(tgt.buttonToTie.nodeName.toLowerCase()=="A")
	{
	    document.location=tgt.buttonToTie.href;
	    
	}
	    
	
	XBElem.prototype.cancelEventDefault(e);
	
}



/*STATIC METHODS*/



/**
 *	Cancels the default action of the given event.
 */
function cancelEventDefault(evt)
{
	//if (evt.preventDefault == null)
	if(!evt.preventDefault)
	{
		evt.returnValue = false;
	}
	else
	{
		evt.preventDefault();
	}
	
}
