/**
 * Throughout, whitespace is defined as one of the characters
 *  "\t" TAB \u0009
 *  "\n" LF  \u000A
 *  "\r" CR  \u000D
 *  " "  SPC \u0020
 *
 * This does not use Javascript's "\s" because that includes non-breaking
 * spaces (and also some other characters).
 */
function get_object(id, d) {
	var i,x,nl;
	if(!d) d = document; 
	if(!(x = d[id]) && d.all) { nl = d.all[id]; if(nl) x = (nl.length && nl.length > 0) ? nl[0] : nl;}
	for (i = 0; !x && i < d.forms.length; i++) x = d.forms[i][id];
	for(i = 0; !x && d.layers && i < d.layers.length; i++) x = get_object(id, d.layers[i].document);
	if(!x && document.getElementById) x = document.getElementById(id);
	return x;
}

function get_anchor(id, d) {
	var x;
	
	if(!d) d = document; 
	x = d.anchors[id];
	if(!x && d.all) { x = d.anchors(id); if(!x) x = d.all[id]; }
	if(!x && d.getElementById) x = d.getElementById(id);
	return x;
}


/**
 * Determine whether a node's text content is entirely whitespace.
 *
 * @param nod  A node implementing the |CharacterData| interface (i.e.,
 *             a |Text|, |Comment|, or |CDATASection| node
 * @return     True if all of the text content of |nod| is whitespace,
 *             otherwise false.
 */
function is_all_ws( nod )
{
	// Use ECMA-262 Edition 3 String and RegExp features
	return !(/[^\t\n\r ]/.test(nod.data));
}


/**
 * Determine if a node should be ignored by the iterator functions.
 *
 * @param nod  An object implementing the DOM1 |Node| interface.
 * @return     true if the node is:
 *                1) A |Text| node that is all whitespace
 *                2) A |Comment| node
 *             and otherwise false.
 */

function is_ignorable( nod )
{
	return ( nod.nodeType == 8) || // A comment node
				 ( (nod.nodeType == 3) && is_all_ws(nod) ); // a text node, all ws
}

/**
 * Version of |previousSibling| that skips nodes that are entirely
 * whitespace or comments.  (Normally |previousSibling| is a property
 * of all DOM nodes that gives the sibling node, the node that is
 * a child of the same parent, that occurs immediately before the
 * reference node.)
 *
 * @param sib  The reference node.
 * @return     Either:
 *               1) The closest previous sibling to |sib| that is not
 *                  ignorable according to |is_ignorable|, or
 *               2) null if no such node exists.
 */
function node_before( sib )
{
	while ((sib = sib.previousSibling)) {
		if (!is_ignorable(sib)) return sib;
	}
	return null;
}

/**
 * Version of |nextSibling| that skips nodes that are entirely
 * whitespace or comments.
 *
 * @param sib  The reference node.
 * @return     Either:
 *               1) The closest next sibling to |sib| that is not
 *                  ignorable according to |is_ignorable|, or
 *               2) null if no such node exists.
 */
function node_after( sib )
{
	while ((sib = sib.nextSibling)) {
		if (!is_ignorable(sib)) return sib;
	}
	return null;
}

/**
 * Version of |lastChild| that skips nodes that are entirely
 * whitespace or comments.  (Normally |lastChild| is a property
 * of all DOM nodes that gives the last of the nodes contained
 * directly in the reference node.)
 *
 * @param sib  The reference node.
 * @return     Either:
 *               1) The last child of |sib| that is not
 *                  ignorable according to |is_ignorable|, or
 *               2) null if no such node exists.
 */
function last_child( par )
{
	var res=par.lastChild;
	while (res) {
		if (!is_ignorable(res)) return res;
		res = res.previousSibling;
	}
	return null;
}

/**
 * Version of |firstChild| that skips nodes that are entirely
 * whitespace and comments.
 *
 * @param sib  The reference node.
 * @return     Either:
 *               1) The first child of |sib| that is not
 *                  ignorable according to |is_ignorable|, or
 *               2) null if no such node exists.
 */
function first_child( par )
{
	var res=par.firstChild;
	while (res) {
		if (!is_ignorable(res)) return res;
		res = res.nextSibling;
	}
	return null;
}

/**
 * Version of |previousSibling| that skips nodes that are entirely
 * whitespace and comments.
 *
 * @param sib  The reference node.
 * @return     Either:
 *               1) The previous siblink of |sib| that is not
 *                  ignorable according to |is_ignorable|, or
 *               2) null if no such node exists.
 */
function previous_siblink( par )
{
	var res=par.previousSibling;
	while (res) {
		if (!is_ignorable(res)) return res;
		res = res.previousSibling;
	}
	return null;
}

/**
 * Version of |nextSibling| that skips nodes that are entirely
 * whitespace and comments.
 *
 * @param sib  The reference node.
 * @return     Either:
 *               1) The next siblink of |sib| that is not
 *                  ignorable according to |is_ignorable|, or
 *               2) null if no such node exists.
 */
function next_siblink( par )
{
	var res=par.nextSibling;
	while (res) {
		if (!is_ignorable(res)) return res;
		res = res.nextSibling;
	}
	return null;
}

/**
 * Version of |data| that doesn't include whitespace at the beginning
 * and end and normalizes all whitespace to a single space.  (Normally
 * |data| is a property of text nodes that gives the text of the node.)
 *
 * @param txt  The text node whose data should be returned
 * @return     A string giving the contents of the text node with
 *             whitespace collapsed.
 */
function data_of( txt )
{
	var data = txt.data;
	// Use ECMA-262 Edition 3 String and RegExp features
	data = data.replace(/[\t\n\r ]+/g, " ");
	if (data.charAt(0) == " ")
		data = data.substring(1, data.length);
	if (data.charAt(data.length - 1) == " ")
		data = data.substring(0, data.length - 1);
	return data;
}

function child_with_name( par, name ) {
	var res = par.firstChild;
	while (res) {
		if (!is_ignorable(res) && (res.tagName.toLowerCase() == name.toLowerCase())) return res;
		res = res.nextSibling;
	}
}

function next_with_name( par, name )
{
	var res=par.nextSibling;
	while (res) {
		if (!is_ignorable(res) && (res.tagName.toLowerCase() == name.toLowerCase())) return res;
		res = res.nextSibling;
	}
	return null;
}

function previous_with_name( par, name )
{
	var res=par.previousSibling;
	while (res) {
		if (!is_ignorable(res) && (res.tagName.toLowerCase() == name.toLowerCase())) return res;
		res = res.previousSibling;
	}
	return null;
}

function descendant_with_name( par, name ) {
	var nl = par.getElementsByTagName(name);
	if(nl.length > 0)
		return nl[0];
	return null;
}

function parent_with_name( child, name ) {
	var res = child.parentNode;
	while (res) {
		if (!is_ignorable(res) && (res.tagName.toLowerCase() == name.toLowerCase())) return res;
		res = res.parentNode;
	}
}

function newDocument() {
	if(document.all) {
		return new ActiveXObject("MSXML2.DOMDocument");
	} else { 
		return document.implementation.createDocument('', '', null);
	}
}

function newFreeThreadedDocument() {
	if(document.all) {
		return new ActiveXObject("MSXML2.FreeThreadedDOMDocument");
	} else { 
		return document.implementation.createDocument('', '', null);
	}
}

function addLoadListener(document, listenerFunc) {
	if(this.document.addEventListener) {
		document.addEventListener('load', listenerFunc, false);
	} else { 
		document.onreadystatechange = listenerFunc;
	}
}

function XMLDocument(documentURI) {
	this.documentURI = documentURI;
	this.loaded = false;
	if(document.all) {
		this.document = new ActiveXObject("MSXML2.FreeThreadedDOMDocument");
	} else { 
		this.document = document.implementation.createDocument('', '', null);
	}
	this.document.async = false;
}

XMLDocument.prototype= {
	load: function() { 
		this.document.load(this.documentURI); 
	},

	addLoadingListener: function(listenerFunc) {
		if(this.document.addEventListener) {
			this.document.addEventListener('load', listenerFunc, false);
		} else { 
			this.document.onreadystatechange = listenerFunc;
		}
		this.document.async = true;
	},

	isLoaded: function() {
		if(! this.loaded) {
			if(this.document && this.document.readyState && this.document.readyState != 4) {
				return false;
			} else {
				this.loaded = true;
				return true;
			}
		} else {
			return true;
		}
	}
};
