// x-ext.js
// x-ext v1.0.0, Cross-Browser DHTML Library extensions from Ohio Department of transportation
// Copyright (c) 2004 Ohio Department of Transportation
// This library is distributed under the terms of the LGPL (gnu.org)

/**
 * Returns a collection of elements with a particular tag name foud within the
 * scope of the parent element.
 */
function xGetElementsByTagName(tagName, parentEle) {
	var elements = null;
	if (!parentEle) parentEle = document;
	if(!(parentEle=xGetElementById(parentEle))) return;
	if (!tagName) tagName = '*';
	if (parentEle.getElementsByTagName) {elements = parentEle.getElementsByTagName(tagName);}
	else if (document.all) {elements = document.all.tags(tagName);}
	return elements;
}

/**
 * Returns the first element with a particular tag name found within the
 * scope of the parent element.
 */
function xGetFirstElementByTagName(tagName, parentEle) {
	var elements = null;
	if (!parentEle) parentEle = document;
	if(!(parentEle=xGetElementById(parentEle))) return;
	if (!tagName) tagName = '*';
	if (parentEle.getElementsByTagName) {elements = parentEle.getElementsByTagName(tagName);}
	else if (document.all) {elements = document.all.tags(tagName);}
	if (elements && elements.length > 0) return elements[0];
	else return null;
}

/**
 * Returns the second element with a particular tag name found within the
 * scope of the parent element.
 */
function xGetSecondElementByTagName(tagName, parentEle) {
	var elements = null;
	if (!parentEle) parentEle = document;
	if(!(parentEle=xGetElementById(parentEle))) return;
	if (!tagName) tagName = '*';
	if (parentEle.getElementsByTagName) {elements = parentEle.getElementsByTagName(tagName);}
	else if (document.all) {elements = document.all.tags(tagName);}
	if (elements && elements.length > 1) return elements[1];
	else return null;
}

/**
 * Walks up the DOM tree beginning at ele until an element with a tagName
 * is found. If inclusive is set to true, the initial node, ele, is checked
 * to see if it matches the tagName. If it does, it returns the initial element.
 */
function xGetFirstAncestorByTagName(tagName, ele, inclusive) {
	if (!tagName) tagName = '.*';
	var re = new RegExp('\\b'+tagName+'\\b');
	if (!ele) ele = document;
	if (inclusive && ele.tagName.search(re) != -1) return ele;
	var e = ele.parentNode;
	while (e && e.tagName.search(re) < 0) e = e.parentNode;
	return e;
}

function xGetFirstAncestorByAttributeName(attrName, ele, inclusive) {
	if (!attrName) return null;
	if (!ele) ele = document;
	if (inclusive && ele.getAttribute(attrName) != null) return ele;
	var e = ele.parentNode;
	while (e && e.getAttribute(attrName) == null) e = e.parentNode;
	return e;
}

/**
 * Returns an array of elements that have the attribute name as a property. Only
 * returns elements that are in the scope of the parentEle.
 */
function xGetElementsByAttributeName(attrName, parentEle) {
	var elements = new Array();
	if (!parentEle) parentEle = document;
	if(!xDef(parentEle)) return;
	if (!attrName) return null;
    xGetElementsByAttributeName0(attrName, parentEle, elements);
	return elements;
}

function xGetElementsByAttributeName0(attrName, ele, elements) {
	if (ele.getAttribute && ele.getAttribute(attrName) != null) elements[elements.length] = ele;
	var children = xGetChildren(ele);
	for (var i = 0; i < children.length; i++) {
		xGetElementsByAttributeName0(attrName, children[i], elements);
	}
}


/**
 * Adds an event listener to one or more elements that have a particular
 * className.
 */
function xAddEventListenerByClass(e,classname,eventType,eventListener,useCapture) {
	if(!(e=xGetElementById(e))) return;
	var elems=xGetElementsByClassName(classname,e);
	//alert('adding event "' + eventType + '" to ' + elems.length + ' elements.');
	xAddEventListeners(elems,eventType,eventListener,useCapture);
}

/**
 * Removes an event listener from one or more elements that have a particular
 * className.
 */
function xRemoveEventListenerByClass(e,classname,eventType,eventListener,useCapture) {
	if(!(e=xGetElementById(e))) return;
	var elems=xGetElementsByClassName(classname,e);
	xRemoveEventListeners(elems,eventType,eventListener,useCapture);
}

/**
 * Adds an event listener to an array of elements.
 */
function xAddEventListeners(elements,eventType,eventListener,useCapture) {
	if(!elements) return;
	for (var i=0;i<elements.length;i++)
		xAddEventListener(elements[i],eventType,eventListener,useCapture);
}

/**
 * Adds an event listener to an array of elements.
 */
function xRemoveEventListeners(elements,eventType,eventListener,useCapture) {
	if(!elements) return;
	for (var i=0;i<elements.length;i++)
		xRemoveEventListener(elements[i],eventType,eventListener,useCapture);
}

/**
 * Primarily a debug method that returns a String representation of text contained
 * within a particular element.
 */
function xGetTextForElement(obj) {
	var str="";
	for (var i=0;i < obj.childNodes.length;i++) {
		// Element node - walk children
		if (obj.childNodes[i].nodeType==1)
			str+=xGetTextForElement(obj.childNodes[i]);
		else
		// Text Node - extract contents
		if (obj.childNodes[i].nodeType==3)
			str = obj.childNodes[i].data;
	}
	return str;
}

/**
 * Convenience method to add a className to an elements className property.
 * The function doesn't waste time checking to see if the new classname is
 * already in use; it simply appends the new classname.
 */
function xAddClassName(ele, classname) {
	ele = xGetElementById(ele);
	var regexp = new RegExp('(^|\\s)' + classname + '($|\\s)', 'g');
	if (ele.className && ele.className.search(regexp) != -1) return;
	var clazz = ele.className.split(/\s+/);
	clazz.push(classname);
	ele.className = clazz.join(' ');
}


/**
 * Removes a classname from the element's className property.
 * Uses regex for speed.
 */
function xRemoveClassName(ele, classname) {
	ele = xGetElementById(ele);
	var regexp = new RegExp('(^|\\s)' + classname + '($|\\s)', 'g');
	if (ele.className){
	    ele.className = ele.className.replace(regexp,'');
	}
}

function xHasClassName(ele, classname) {
	ele = xGetElementById(ele);
	var regexp = new RegExp('(^|\\s)' + classname + '($|\\s)', 'g');
	if (ele.className){
	    return ele.className.search(regexp) != -1;
	}else{
	    return false;
	}
}

/**
 * There are a couple inconsistencies between the children collection in IE
 * and Firefox. This method will return all child elements of a parent element.
 * Keep in mind that element refers to nodeType = 1, so things like textnodes
 * are not included. Passing in an optional tagname parameter will restrict the
 * children to those matching the tagname.
 */
function xGetChildren(ele, tagname) {
	ele = xGetElementById(ele);
	var children = new Array();
	for (var i = 0; i < ele.childNodes.length; i++)
		if (ele.childNodes[i].nodeType == 1) {
			if (tagname && ele.childNodes[i].tagName != tagname) continue;
			children[children.length] = ele.childNodes[i];
		}
	return children;
}

/**
 * Stops the event from propogating using the specific IE or other way of
 * doing things.
 */
function xCancelEvent(e) {
	if (e.stopPropagation) e.stopPropagation();
	else e.cancelBubble = true;
}

function xWaitCursor(on) {
	var body = xGetFirstElementByTagName('BODY');
	if (on) {
		body.style.cursor = 'wait';
	} else
		body.style.cursor = 'default';
}

function xWalkTree(node, callback) {
    callback(node)
	for (var i = node.childNodes.length - 1; i >= 0; i--)
		xWalkTree(node.childNodes[i], callback);
}

String.prototype.endsWith = function(s) {
	var i = this.indexOf(s);
	return i == this.length - s.length;
}

String.prototype.trim = function() {
	return this.replace(/^\s+/g, '').replace(/\s+$/g, '');
}



