/**
 * 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).
 */

/*
 * Browser object
 */
function Browser() {
	d=document;
	this.agt=navigator.userAgent.toLowerCase();
	this.major = parseInt(navigator.appVersion);
	this.dom=(d.getElementById)?1:0; // true for ie6, ns6
	this.ns=(d.layers);
	this.ns4up=(this.ns && this.major >=4);
	this.ns6=(this.dom&&navigator.appName=="Netscape");
	this.op=this.agt.indexOf('opera')!=-1;
	this.ie=(d.all);
	this.ie4=(d.all&&!this.dom)?1:0;
	this.ie4up=(this.ie && this.major >= 4);
	this.ie5=(d.all&&this.dom);
	this.win=((this.agt.indexOf("win")!=-1) || (this.agt.indexOf("16bit")!=-1));
	this.mac=(this.agt.indexOf("mac")!=-1);
}

var oBw = new Browser();

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;
}

function writeHTML(o, h) { 
 if(oBw.ns) {
	 var doc = o.document;
	 doc.write(h);
	 doc.close();
	 return false;
 }
 if(o.innerHTML)
	 o.innerHTML = h; 
}

// w - beforeBegin,afterBegin,beforeEnd,afterEnd
// width - for ns 4 only
function insertHTML(o, h, w) {
	if (oBw.op) return;
	if (o.insertAdjacentHTML) { 
		o.insertAdjacentHTML(w, h);
		return;
	}
	if (oBw.ns) {
		writeHTML(o, h);
		return;
	}
	var r = o.ownerDocument.createRange();
	r.setStartBefore(o);
	var frag = r.createContextualFragment(h);
	insertObj(o, w, frag);
}

function insertObj(o,w,node) {
	switch (w) {
	case 'beforeBegin':
		o.parentNode.insertBefore(node, o)
		break;
	case 'afterBegin':
		o.insertBefore(node, o.firstChild);
		break;
	case 'beforeEnd':
		o.appendChild(node);
		break;
	case 'afterEnd':
		if (o.nextSibling){
			o.parentNode.insertBefore(node, o.nextSibling);
		} else {
			o.parentNode.appendChild(node)
		}
		break;
	}
}

/**
 * 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;
		}
	}
};

