// =========================================================================
//
// tinyxmldom.js - an XML DOM parser in JavaScript compressed for downloading
//
//	This is the classic DOM that has shipped with XML for <SCRIPT>
//  since the beginning. For a more standards-compliant DOM, you may
//  wish to use the standards-compliant W3C DOM that is included
//  with XML for <SCRIPT> versions 3.0 and above
//
//
// version 3.1
//
// =========================================================================
//
// Copyright (C) 2000 - 2002, 2003 Michael Houghton (mike@idle.org), Raymond Irving and David Joham (djoham@yahoo.com)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// Visit the XML for <SCRIPT> home page at http://xmljs.sourceforge.net
//
var whitespace = "\n\r\t ";
var quotes = "\"'";
function convertEscapes(str) {
	var gt;
	gt = -1;
	while (str.indexOf("&lt;", gt + 1) > -1) {
		var gt = str.indexOf("&lt;", gt + 1);
		var newStr = str.substr(0, gt);
		newStr += "<";
		newStr = newStr + str.substr(gt + 4, str.length);
		str = newStr;
	}
	gt = -1;
	while (str.indexOf("&gt;", gt + 1) > -1) {
		var gt = str.indexOf("&gt;", gt + 1);
		var newStr = str.substr(0, gt);
		newStr += ">";
		newStr = newStr + str.substr(gt + 4, str.length);
		str = newStr;
	}
	gt = -1;
	while (str.indexOf("&amp;", gt + 1) > -1) {
		var gt = str.indexOf("&amp;", gt + 1);
		var newStr = str.substr(0, gt);
		newStr += "&";
		newStr = newStr + str.substr(gt + 5, str.length);
		str = newStr;
	}
	return str;
}
function convertToEscapes(str) {
	var gt = -1;
	while (str.indexOf("&", gt + 1) > -1) {
		gt = str.indexOf("&", gt + 1);
		var newStr = str.substr(0, gt);
		newStr += "&amp;";
		newStr = newStr + str.substr(gt + 1, str.length);
		str = newStr;
	}
	gt = -1;
	while (str.indexOf("<", gt + 1) > -1) {
		var gt = str.indexOf("<", gt + 1);
		var newStr = str.substr(0, gt);
		newStr += "&lt;";
		newStr = newStr + str.substr(gt + 1, str.length);
		str = newStr;
	}
	gt = -1;
	while (str.indexOf(">", gt + 1) > -1) {
		var gt = str.indexOf(">", gt + 1);
		var newStr = str.substr(0, gt);
		newStr += "&gt;";
		newStr = newStr + str.substr(gt + 1, str.length);
		str = newStr;
	}
	return str;
}
function _displayElement(domElement, strRet) {
	if (domElement == null) {
		return;
	}
	if (!(domElement.nodeType == "ELEMENT")) {
		return;
	}
	var tagName = domElement.tagName;
	var tagInfo = "";
	tagInfo = "<" + tagName;
	var attributeList = domElement.getAttributeNames();
	for (var intLoop = 0; intLoop < attributeList.length; intLoop++) {
		var attribute = attributeList[intLoop];
		tagInfo = tagInfo + " " + attribute + "=";
		tagInfo = tagInfo + "\"" + domElement.getAttribute(attribute) + "\"";
	}
	tagInfo = tagInfo + ">";
	strRet = strRet + tagInfo;
	if (domElement.children != null) {
		var domElements = domElement.children;
		for (var intLoop = 0; intLoop < domElements.length; intLoop++) {
			var childNode = domElements[intLoop];
			if (childNode.nodeType == "COMMENT") {
				strRet = strRet + "<!--" + childNode.content + "-->";
			} else {
				if (childNode.nodeType == "TEXT") {
					var cont = trim(childNode.content, true, true);
					strRet = strRet + childNode.content;
				} else {
					if (childNode.nodeType == "CDATA") {
						var cont = trim(childNode.content, true, true);
						strRet = strRet + "<![CDATA[" + cont + "]]>";
					} else {
						strRet = _displayElement(childNode, strRet);
					}
				}
			}
		}
	}
	strRet = strRet + "</" + tagName + ">";
	return strRet;
}
function firstWhiteChar(str, pos) {
	if (isEmpty(str)) {
		return -1;
	}
	while (pos < str.length) {
		if (whitespace.indexOf(str.charAt(pos)) != -1) {
			return pos;
		} else {
			pos++;
		}
	}
	return str.length;
}
function isEmpty(str) {
	return (str == null) || (str.length == 0);
}
function trim(trimString, leftTrim, rightTrim) {
	if (isEmpty(trimString)) {
		return "";
	}
	if (leftTrim == null) {
		leftTrim = true;
	}
	if (rightTrim == null) {
		rightTrim = true;
	}
	var left = 0;
	var right = 0;
	var i = 0;
	var k = 0;
	if (leftTrim == true) {
		while ((i < trimString.length) && (whitespace.indexOf(trimString.charAt(i++)) != -1)) {
			left++;
		}
	}
	if (rightTrim == true) {
		k = trimString.length - 1;
		while ((k >= left) && (whitespace.indexOf(trimString.charAt(k--)) != -1)) {
			right++;
		}
	}
	return trimString.substring(left, trimString.length - right);
}
function XMLDoc(source, errFn) {
	this.topNode = null;
	this.errFn = errFn;
	this.createXMLNode = _XMLDoc_createXMLNode;
	this.error = _XMLDoc_error;
	this.getUnderlyingXMLText = _XMLDoc_getUnderlyingXMLText;
	this.handleNode = _XMLDoc_handleNode;
	this.hasErrors = false;
	this.insertNodeAfter = _XMLDoc_insertNodeAfter;
	this.insertNodeInto = _XMLDoc_insertNodeInto;
	this.loadXML = _XMLDoc_loadXML;
	this.parse = _XMLDoc_parse;
	this.parseAttribute = _XMLDoc_parseAttribute;
	this.parseDTD = _XMLDoc_parseDTD;
	this.parsePI = _XMLDoc_parsePI;
	this.parseTag = _XMLDoc_parseTag;
	this.removeNodeFromTree = _XMLDoc_removeNodeFromTree;
	this.replaceNodeContents = _XMLDoc_replaceNodeContents;
	this.selectNode = _XMLDoc_selectNode;
	this.selectNodeText = _XMLDoc_selectNodeText;
	this.source = source;
	if (this.parse()) {
		if (this.topNode != null) {
			return this.error("expected close " + this.topNode.tagName);
		} else {
			return true;
		}
	}
}
function _XMLDoc_createXMLNode(strXML) {
	return new XMLDoc(strXML, this.errFn).docNode;
}
function _XMLDoc_error(str) {
	this.hasErrors = true;
	if (this.errFn) {
		this.errFn("ERROR: " + str);
	} else {
		if (this.onerror) {
			this.onerror("ERROR: " + str);
		}
	}
	return 0;
}
function _XMLDoc_getTagNameParams(tag, obj) {
	var elm = -1, e, s = tag.indexOf("[");
	var attr = [];
	if (s >= 0) {
		e = tag.indexOf("]");
		if (e >= 0) {
			elm = tag.substr(s + 1, (e - s) - 1);
		} else {
			obj.error("expected ] near " + tag);
		}
		tag = tag.substr(0, s);
		if (isNaN(elm) && elm != "*") {
			attr = elm.substr(1, elm.length - 1);
			attr = attr.split("=");
			if (attr[1]) {
				s = attr[1].indexOf("\"");
				attr[1] = attr[1].substr(s + 1, attr[1].length - 1);
				e = attr[1].indexOf("\"");
				if (e >= 0) {
					attr[1] = attr[1].substr(0, e);
				} else {
					obj.error("expected \" near " + tag);
				}
			}
			elm = -1;
		} else {
			if (elm == "*") {
				elm = -1;
			}
		}
	}
	return [tag, elm, attr[0], attr[1]];
}
function _XMLDoc_getUnderlyingXMLText() {
	var strRet = "";
	strRet = strRet + "<?xml version=\"1.0\"?>";
	if (this.docNode == null) {
		return;
	}
	strRet = _displayElement(this.docNode, strRet);
	return strRet;
}
function _XMLDoc_handleNode(current) {
	if ((current.nodeType == "COMMENT") && (this.topNode != null)) {
		return this.topNode.addElement(current);
	} else {
		if ((current.nodeType == "TEXT") || (current.nodeType == "CDATA")) {
			if (this.topNode == null) {
				if (trim(current.content, true, false) == "") {
					return true;
				} else {
					return this.error("expected document node, found: " + current);
				}
			} else {
				return this.topNode.addElement(current);
			}
		} else {
			if ((current.nodeType == "OPEN") || (current.nodeType == "SINGLE")) {
				var success = false;
				if (this.topNode == null) {
					this.docNode = current;
					current.parent = null;
					success = true;
				} else {
					success = this.topNode.addElement(current);
				}
				if (success && (current.nodeType != "SINGLE")) {
					this.topNode = current;
				}
				current.nodeType = "ELEMENT";
				return success;
			} else {
				if (current.nodeType == "CLOSE") {
					if (this.topNode == null) {
						return this.error("close tag without open: " + current.toString());
					} else {
						if (current.tagName != this.topNode.tagName) {
							return this.error("expected closing " + this.topNode.tagName + ", found closing " + current.tagName);
						} else {
							this.topNode = this.topNode.getParent();
						}
					}
				}
			}
		}
	}
	return true;
}
function _XMLDoc_insertNodeAfter(referenceNode, newNode) {
	var parentXMLText = this.getUnderlyingXMLText();
	var selectedNodeXMLText = referenceNode.getUnderlyingXMLText();
	var originalNodePos = parentXMLText.indexOf(selectedNodeXMLText) + selectedNodeXMLText.length;
	var newXML = parentXMLText.substr(0, originalNodePos);
	newXML += newNode.getUnderlyingXMLText();
	newXML += parentXMLText.substr(originalNodePos);
	var newDoc = new XMLDoc(newXML, this.errFn);
	return newDoc;
}
function _XMLDoc_insertNodeInto(referenceNode, insertNode) {
	var parentXMLText = this.getUnderlyingXMLText();
	var selectedNodeXMLText = referenceNode.getUnderlyingXMLText();
	var endFirstTag = selectedNodeXMLText.indexOf(">") + 1;
	var originalNodePos = parentXMLText.indexOf(selectedNodeXMLText) + endFirstTag;
	var newXML = parentXMLText.substr(0, originalNodePos);
	newXML += insertNode.getUnderlyingXMLText();
	newXML += parentXMLText.substr(originalNodePos);
	var newDoc = new XMLDoc(newXML, this.errFn);
	return newDoc;
}
function _XMLDoc_loadXML(source) {
	this.topNode = null;
	this.hasErrors = false;
	this.source = source;
	return this.parse();
}
function _XMLDoc_parse() {
	var pos = 0;
	err = false;
	while (!err) {
		var closing_tag_prefix = "";
		var chpos = this.source.indexOf("<", pos);
		var open_length = 1;
		var open;
		var close;
		if (chpos == -1) {
			break;
		}
		open = chpos;
		var str = this.source.substring(pos, open);
		if (str.length != 0) {
			err = !this.handleNode(new XMLNode("TEXT", this, str));
		}
		if (chpos == this.source.indexOf("<?", pos)) {
			pos = this.parsePI(this.source, pos + 2);
			if (pos == 0) {
				err = true;
			}
			continue;
		}
		if (chpos == this.source.indexOf("<!DOCTYPE", pos)) {
			pos = this.parseDTD(this.source, chpos + 9);
			if (pos == 0) {
				err = true;
			}
			continue;
		}
		if (chpos == this.source.indexOf("<!--", pos)) {
			open_length = 4;
			closing_tag_prefix = "--";
		}
		if (chpos == this.source.indexOf("<![CDATA[", pos)) {
			open_length = 9;
			closing_tag_prefix = "]]";
		}
		chpos = this.source.indexOf(closing_tag_prefix + ">", chpos);
		if (chpos == -1) {
			return this.error("expected closing tag sequence: " + closing_tag_prefix + ">");
		}
		close = chpos + closing_tag_prefix.length;
		str = this.source.substring(open + 1, close);
		var n = this.parseTag(str);
		if (n) {
			err = !this.handleNode(n);
		}
		pos = close + 1;
	}
	return !err;
}
function _XMLDoc_parseAttribute(src, pos, node) {
	while ((pos < src.length) && (whitespace.indexOf(src.charAt(pos)) != -1)) {
		pos++;
	}
	if (pos >= src.length) {
		return pos;
	}
	var p1 = pos;
	while ((pos < src.length) && (src.charAt(pos) != "=")) {
		pos++;
	}
	var msg = "attributes must have values";
	if (pos >= src.length) {
		return this.error(msg);
	}
	var paramname = trim(src.substring(p1, pos++), false, true);
	while ((pos < src.length) && (whitespace.indexOf(src.charAt(pos)) != -1)) {
		pos++;
	}
	if (pos >= src.length) {
		return this.error(msg);
	}
	msg = "attribute values must be in quotes";
	var quote = src.charAt(pos++);
	if (quotes.indexOf(quote) == -1) {
		return this.error(msg);
	}
	p1 = pos;
	while ((pos < src.length) && (src.charAt(pos) != quote)) {
		pos++;
	}
	if (pos >= src.length) {
		return this.error(msg);
	}
	if (!node.addAttribute(paramname, trim(src.substring(p1, pos++), false, true))) {
		return 0;
	}
	return pos;
}
function _XMLDoc_parseDTD(str, pos) {
	var firstClose = str.indexOf(">", pos);
	if (firstClose == -1) {
		return this.error("error in DTD: expected '>'");
	}
	var closing_tag_prefix = "";
	var firstOpenSquare = str.indexOf("[", pos);
	if ((firstOpenSquare != -1) && (firstOpenSquare < firstClose)) {
		closing_tag_prefix = "]";
	}
	while (true) {
		var closepos = str.indexOf(closing_tag_prefix + ">", pos);
		if (closepos == -1) {
			return this.error("expected closing tag sequence: " + closing_tag_prefix + ">");
		}
		pos = closepos + closing_tag_prefix.length + 1;
		if (str.substring(closepos - 1, closepos + 2) != "]]>") {
			break;
		}
	}
	return pos;
}
function _XMLDoc_parsePI(str, pos) {
	var closepos = str.indexOf("?>", pos);
	return closepos + 2;
}
function _XMLDoc_parseTag(src) {
	if (src.indexOf("!--") == 0) {
		return new XMLNode("COMMENT", this, src.substring(3, src.length - 2));
	}
	if (src.indexOf("![CDATA[") == 0) {
		return new XMLNode("CDATA", this, src.substring(8, src.length - 2));
	}
	var n = new XMLNode();
	n.doc = this;
	if (src.charAt(0) == "/") {
		n.nodeType = "CLOSE";
		src = src.substring(1);
	} else {
		n.nodeType = "OPEN";
	}
	if (src.charAt(src.length - 1) == "/") {
		if (n.nodeType == "CLOSE") {
			return this.error("singleton close tag");
		} else {
			n.nodeType = "SINGLE";
		}
		src = src.substring(0, src.length - 1);
	}
	if (n.nodeType != "CLOSE") {
		n.attributes = new Array();
	}
	if (n.nodeType == "OPEN") {
		n.children = new Array();
	}
	src = trim(src, true, true);
	if (src.length == 0) {
		return this.error("empty tag");
	}
	var endOfName = firstWhiteChar(src, 0);
	if (endOfName == -1) {
		n.tagName = src;
		return n;
	}
	n.tagName = src.substring(0, endOfName);
	var pos = endOfName;
	while (pos < src.length) {
		pos = this.parseAttribute(src, pos, n);
		if (this.pos == 0) {
			return null;
		}
	}
	return n;
}
function _XMLDoc_removeNodeFromTree(node) {
	var parentXMLText = this.getUnderlyingXMLText();
	var selectedNodeXMLText = node.getUnderlyingXMLText();
	var originalNodePos = parentXMLText.indexOf(selectedNodeXMLText);
	var newXML = parentXMLText.substr(0, originalNodePos);
	newXML += parentXMLText.substr(originalNodePos + selectedNodeXMLText.length);
	var newDoc = new XMLDoc(newXML, this.errFn);
	return newDoc;
}
function _XMLDoc_replaceNodeContents(referenceNode, newContents) {
	var newNode = this.createXMLNode("<X>" + newContents + "</X>");
	referenceNode.children = newNode.children;
	return this;
}
function _XMLDoc_selectNode(tagpath) {
	tagpath = trim(tagpath, true, true);
	var srcnode, node, tag, params, elm, rg;
	var tags, attrName, attrValue, ok;
	srcnode = node = ((this.source) ? this.docNode : this);
	if (!tagpath) {
		return node;
	}
	if (tagpath.indexOf("/") == 0) {
		tagpath = tagpath.substr(1);
	}
	tagpath = tagpath.replace(tag, "");
	tags = tagpath.split("/");
	tag = tags[0];
	if (tag) {
		if (tagpath.indexOf("/") == 0) {
			tagpath = tagpath.substr(1);
		}
		tagpath = tagpath.replace(tag, "");
		params = _XMLDoc_getTagNameParams(tag, this);
		tag = params[0];
		elm = params[1];
		attrName = params[2];
		attrValue = params[3];
		node = (tag == "*") ? node.getElements() : node.getElements(tag);
		if (node.length) {
			if (elm < 0) {
				srcnode = node;
				var i = 0;
				while (i < srcnode.length) {
					if (attrName) {
						if (srcnode[i].getAttribute(attrName) != attrValue) {
							ok = false;
						} else {
							ok = true;
						}
					} else {
						ok = true;
					}
					if (ok) {
						node = srcnode[i].selectNode(tagpath);
						if (node) {
							return node;
						}
					}
					i++;
				}
			} else {
				if (elm < node.length) {
					node = node[elm].selectNode(tagpath);
					if (node) {
						return node;
					}
				}
			}
		}
	}
}
function _XMLDoc_selectNodeText(tagpath) {
	var node = this.selectNode(tagpath);
	if (node != null) {
		return node.getText();
	} else {
		return null;
	}
}
function XMLNode(nodeType, doc, str) {
	if (nodeType == "TEXT" || nodeType == "CDATA" || nodeType == "COMMENT") {
		this.content = str;
	} else {
		this.content = null;
	}
	this.attributes = null;
	this.children = null;
	this.doc = doc;
	this.nodeType = nodeType;
	this.parent = "";
	this.tagName = "";
	this.addAttribute = _XMLNode_addAttribute;
	this.addElement = _XMLNode_addElement;
	this.getAttribute = _XMLNode_getAttribute;
	this.getAttributeNames = _XMLNode_getAttributeNames;
	this.getElementById = _XMLNode_getElementById;
	this.getElements = _XMLNode_getElements;
	this.getText = _XMLNode_getText;
	this.getParent = _XMLNode_getParent;
	this.getUnderlyingXMLText = _XMLNode_getUnderlyingXMLText;
	this.removeAttribute = _XMLNode_removeAttribute;
	this.selectNode = _XMLDoc_selectNode;
	this.selectNodeText = _XMLDoc_selectNodeText;
	this.toString = _XMLNode_toString;
}
function _XMLNode_addAttribute(attributeName, attributeValue) {
	this.attributes["_" + attributeName] = attributeValue;
	return true;
}
function _XMLNode_addElement(node) {
	node.parent = this;
	this.children[this.children.length] = node;
	return true;
}
function _XMLNode_getAttribute(name) {
	if (this.attributes == null) {
		return null;
	}
	return this.attributes["_" + name];
}
function _XMLNode_getAttributeNames() {
	if (this.attributes == null) {
		var ret = new Array();
		return ret;
	}
	var attlist = new Array();
	for (var a in this.attributes) {
		attlist[attlist.length] = a.substring(1);
	}
	return attlist;
}
function _XMLNode_getElementById(id) {
	var node = this;
	var ret;
	if (node.getAttribute("id") == id) {
		return node;
	} else {
		var elements = node.getElements();
		var intLoop = 0;
		while (intLoop < elements.length) {
			var element = elements[intLoop];
			ret = element.getElementById(id);
			if (ret != null) {
				break;
			}
			intLoop++;
		}
	}
	return ret;
}
function _XMLNode_getElements(byName) {
	if (this.children == null) {
		var ret = new Array();
		return ret;
	}
	var elements = new Array();
	for (var i = 0; i < this.children.length; i++) {
		if ((this.children[i].nodeType == "ELEMENT") && ((byName == null) || (this.children[i].tagName == byName))) {
			elements[elements.length] = this.children[i];
		}
	}
	return elements;
}
function _XMLNode_getText() {
	if (this.nodeType == "ELEMENT") {
		if (this.children == null) {
			return null;
		}
		var str = "";
		for (var i = 0; i < this.children.length; i++) {
			var t = this.children[i].getText();
			str += (t == null ? "" : t);
		}
		return str;
	} else {
		if (this.nodeType == "TEXT") {
			return convertEscapes(this.content);
		} else {
			return this.content;
		}
	}
}
function _XMLNode_getParent() {
	return this.parent;
}
function _XMLNode_getUnderlyingXMLText() {
	var strRet = "";
	strRet = _displayElement(this, strRet);
	return strRet;
}
function _XMLNode_removeAttribute(attributeName) {
	if (attributeName == null) {
		return this.doc.error("You must pass an attribute name into the removeAttribute function");
	}
	var attributes = this.getAttributeNames();
	var intCount = attributes.length;
	var tmpAttributeValues = new Array();
	for (intLoop = 0; intLoop < intCount; intLoop++) {
		tmpAttributeValues[intLoop] = this.getAttribute(attributes[intLoop]);
	}
	this.attributes = new Array();
	for (intLoop = 0; intLoop < intCount; intLoop++) {
		if (attributes[intLoop] != attributeName) {
			this.addAttribute(attributes[intLoop], tmpAttributeValues[intLoop]);
		}
	}
	return true;
}
function _XMLNode_toString() {
	return "" + this.nodeType + ":" + (this.nodeType == "TEXT" || this.nodeType == "CDATA" || this.nodeType == "COMMENT" ? this.content : this.tagName);
}


