//-----------------------------------------------------------------
// Javascript XML Parser Version 1.0
// by Nicholas C. Zakas
// February 12, 2001
//-----------------------------------------------------------------
// Software License
// Copyright (c) 2001 Nicholas C. Zakas.  All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer. 
//
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in
//    the documentation and/or other materials provided with the
//    distribution.
//
// 3. The end-user documentation included with the redistribution,
//    if any, must include the following acknowledgment:  
//       "This product includes software developed by the
//        Nicholas C. Zakas (http://www.nczonline.net/)."
//    Alternately, this acknowledgment may appear in the software itself,
//    if and wherever such third-party acknowledgments normally appear.
//
// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED.  IN NO EVENT SHALL NICHOLAS C. ZAKAS  BE LIABLE FOR ANY 
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------
// Any questions, comments, or suggestions should be e-mailed to 
// nicholas@nczonline.net.  For more information, please visit
// http://www.nczonline.net/.
//
// Also Required:
//	arrayex.js
//
// Revision history:
//	February 12, 2001 - Released Version 1.0
//-----------------------------------------------------------------
// This file is separated into two sections:
//	1. Objects and object methods
//	2. Helper functions
//
// Also required:
//	arrayex.js
//----------------------------------------------------------------

//node types (note: some types not used)
var NODE_ELEMENT = 1;
var NODE_ATTRIBUTE = 2;
var NODE_TEXT = 3;
var NODE_CDATA_SECTION = 4;
var NODE_ENTITY_REFERENCE = 5;
var NODE_ENTITY = 6;
var NODE_PROCESSING_INSTRUCTION = 7;
var NODE_COMMENT = 8;
var NODE_DOCUMENT = 9;
var NODE_DOCUMENT_TYPE = 10;
var NODE_DOCUMENT_FRAGMENT = 11;
var NODE_NOTATION = 12;

//error messages
var ERR_NO_DOCUMENT_ELEMENT = "Error! No documentElement specified!";

//----------------------------------------------------------------
// 1: OBJECTS AND OBJECT METHODS
//----------------------------------------------------------------

//-----------------------------------------------------------------
// Object jsXMLAttribute
// This object is akin to IXMLDOMAttribute object.
//
// Parameters:
//	name (String) - the name of the attribute.
//	value (String) - the value of the attribute.
//-----------------------------------------------------------------
function jsXMLAttribute(name, value) {
	this.name = name;
	this.value = value;
}

//-----------------------------------------------------------------
// Object jsXMLNode
// This object is akin to IXMLDOMNode object.
//
// Parameters:
//	None.
//-----------------------------------------------------------------
function jsXMLNode() {

	//attributes
	this.attributes = new Array;
	this.childNodes = new Array;
	this.firstChild = null;
	this.lastChild = null;
	this.namespaceURI = "";
	this.nextSibling = null;
	this.nodeName = "";
	this.nodeType = -1;
	this.ownerDocument = null;
	this.parentNode = null;
	this.previousSibling = null;
	
	//methods
	this.appendChild = _jsXMLNode_appendChild;
	this.cloneNode = _jsXMLNode_cloneNode;
	this.findAttribute = _jsXMLNode_findAttribute;
	this.findChild = _jsXMLNode_findChild;
	this.getAttribute = _jsXMLNode_getAttribute;
	this.hasChildNodes = _jsXMLNode_hasChildNodes;
	this.insertBefore = _jsXMLNode_insertBefore;
	this.removeAttribute = _jsXMLNode_removeAttribute;
	this.removeChild = _jsXMLNode_removeChild;
	this.replaceChild = _jsXMLNode_replaceChild;
	this.setAttribute = _jsXMLNode_setAttribute;
}

//-----------------------------------------------------------------
// Method jsXMLNode.appendChild()
// This method adds a new child to this Nodes childNodes list.
//
// Parameters:
//	new_node (jsXMLNode) - the node to add.
// Returns:
//	A jsXMLNode object representing new_node.
//-----------------------------------------------------------------
function _jsXMLNode_appendChild(new_node) {

	//set the parentNode
	new_node.parentNode = this;
	
	//set the node's information
	new_node.ownerDocument = this.ownerDocument;
	
	//assign value for previous sibling
	if (this.childNodes.length > 0) {
		new_node.previousSibling = 	this.childNodes[this.childNodes.length - 1];
		new_node.previousSibling.nextSibling = new_node;
	}
	
	//add this new_node to the array of childNodes
	this.childNodes[this.childNodes.length] = new_node;
	
	//return the new new_node
	return new_node;
}

//-----------------------------------------------------------------
// Method jsXMLNode.cloneNode()
// This method duplicates the current node and returns it as the value.
//
// Parameters:
//	recurse_children (boolean) - copy all of the Node's children too?
// Returns:
//	A jsXMLNode object that is a copy of the current Node.
//-----------------------------------------------------------------
function _jsXMLNode_cloneNode(recurse_children) {

	//create a new element
	var objNode = new jsXMLNode(this.tagName);
	
	//copy over the attributes
	//objNode.attributes = new Array(this.attributes);
	
	// bex mods
	objNode.attributes = new Array();
	for (var i=0; i<this.attributes.length; i++)
	{
		var newAttr = new jsXMLAttribute(this.attributes[i].name, this.attributes[i].value);
		objNode.attributes[i] = newAttr;
	}
	objNode.tagName = this.tagName;
	objNode.nodeType = this.nodeType;
	// end bex mods
	
	//copy over other info
	objNode.baseName = this.baseName;
	objNode.prefix = this.prefix;
	
	
	
	//copy the children?
	if (recurse_children) {
	
		//copy each of the children, recursively
		for (var i=0; i < this.childNodes.length; i++) {
			//clone the child
			var objChild = this.childNodes[i].cloneNode(true);
			
			//copy it into the new childNodes array
			objNode.appendChild(objChild);
		}
	}
	
	return objNode;
}

//-----------------------------------------------------------------
// Method jsXMLNode.findChild()
// This method finds the location of a given child in the childNodes
// list.
//
// Parameters:
//	this_node (jsXMLNode) - the child to find.
// Returns:
//	An integer representing the child location in the list, -1 if not found.
//-----------------------------------------------------------------
function _jsXMLNode_findChild(this_node) {

	//iterator, found flag, return object
	var i = 0, bFound = false;
	
	//search
	while (i < this.childNodes.length && !bFound) {
		if (this.childNodes[i] == this_node)
			bFound = true;
		else
			i++;
	}
	
	//return the object
	return (bFound ? i : -1);
}

//-----------------------------------------------------------------
// Method jsXMLNode.hasChildNodes()
// This method determines if this Node has any childNodes.
//
// Parameters:
//	Nothing.
// Returns:
//	A boolean - true if the Node has childNodes, false if not.
//-----------------------------------------------------------------
function _jsXMLNode_hasChildNodes() {
	return (this.childNodes.length > 0);
}

//-----------------------------------------------------------------
// Method jsXMLNode.insertBefore()
// This method adds a new node into the spot before the given node.
//
// Parameters:
//	new_node (jsXMLNode) - the node to add.
//	this_node (jsXMLNode) - the place to put new_node before.
// Returns:
//	Nothing.
//-----------------------------------------------------------------
function _jsXMLNode_insertBefore(new_node, this_node) {

	//check for no valid node
	if (this_node == null) {
		this.appendChild(new_node);
		return;
	}
	
	//find the child
	var iLoc = this.findChild(this_node);
	
	//get front part of array
	var arrFront = this.childNodes.slice(0, iLoc);
	
	//get back of array
	var arrBack = this.childNodes.slice(iLoc, this.childNodes.length);
	
	//delete old array
	delete this.childNodes;
	
	//create new array
	this.childNodes = arrFront.concat(new_node);
	
	//assign sibling info
	if (this.childNodes.length > 2) {
		new_node.previousSibling = 	this.childNodes[this.childNodes.length - 2];
		new_node.previousSibling.nextSibling = new_node;
	} else {
		new_node.previousSibling = 	null;
	}
	
	//add the end of the array
	this.childNodes.push(arrBack);
	
	//assign sibling info
	new_node.nextSibling = this.childNodes[iLoc+1];
	
	//set the parentNode
	new_node.parentNode = this;
}

//-----------------------------------------------------------------
// Method jsXMLNode.removeChild()
// This method removes a given child.
//
// Parameters:
//	this_node (jsXMLNode) - the node to remove.
// Returns:
//	A jsXMLNode object representing this_node.
//-----------------------------------------------------------------
function _jsXMLNode_removeChild(this_node) {

	//find the child
	var iLoc = this.findChild(this_node);

	//if the child is found, remove it
	if (iLoc > -1)
		this.childNodes.remove(iLoc);
	
	//return the old node
	return this_node;
}

//-----------------------------------------------------------------
// Method jsXMLNode.replaceChild()
// This method replaces the given child with a new node.
//
// Parameters:
//	new_node (jsXMLNode) - the node to add in old_node's place.
//	old_node (jsXMLNode) - the place to put new_node.
// Returns:
//	A jsXMLNode object representing old_node.
//-----------------------------------------------------------------
function _jsXMLNode_replaceChild(new_node, old_node) {

	//find the child
	var iLoc = this.findChild(old_node);
	
	//replace this location with the new node
	this.childNodes[iLoc] = new_node;
	
	//update siblings
	new_node.nextSibling = old_node.nextSibling;
	new_node.previousSibling = old_node.previousSibling;
	old_node.nextSibling = null;
	old_node.previousSibling = null;
	
	//update owner document
	new_node.ownerDocument = old_node.ownerDocument;
	old_node.ownerDocument = null;

	//return the old node
	return old_node;
}

//-----------------------------------------------------------------
// Method jsXMLNode.findAttribute()
// This method finds the location of a given attribute in the attributes
// list.
//
// Parameters:
//	attr_name (String) - the attribute name to find.
// Returns:
//	An integer representing the attribute location in the list, -1 if not found.
//-----------------------------------------------------------------
function _jsXMLNode_findAttribute(attr_name) {

	//iterator, found flag, return object
	var i = 0, bFound = false;
	
	//search
	while (i < this.attributes.length && !bFound) {
		if (this.attributes[i].name == attr_name)
			bFound = true;
		else
			i++;
	}
	
	//return the object
	return (bFound ? i : -1);
}

//-----------------------------------------------------------------
// Method jsXMLNode.getAttribute()
// This method gets the string value for the given attribute name.
//
// Parameters:
//	attr_name (String) - the attribute name to retrieve the value of.
// Returns:
//	The String value of the attribute, an empty string if it doesn't exist.
//-----------------------------------------------------------------
function _jsXMLNode_getAttribute(attr_name) {

	//find the attribute
	var iLoc = this.findAttribute(attr_name);
	var objAttribute = (iLoc > -1 ? this.attributes[iLoc] : null);
	
	//if it exists, return the value
	//return (objAttribute.value ? objAttribute.value : "");
	
	// bex mods
	return ((objAttribute && objAttribute.value) ? objAttribute.value : "");
	// end bex mods
}

//-----------------------------------------------------------------
// Method jsXMLNode.removeAttribute()
// This method removes an attribute from this element.
//
// Parameters:
//	attr_name (String) - the attribute name to remove.
// Returns:
//	Nothing.
//-----------------------------------------------------------------
function _jsXMLNode_removeAttribute(attr_name) {

	//find the attribute
	var iLoc = this.findAttribute(attr_name);
	
	//if the attribute is found, remove it
	if (iLoc > -1)
		this.attributes.remove(iLoc);
}


//-----------------------------------------------------------------
// Method jsXMLNode.setAttribute()
// This method sets an attribute to a given value.
//
// Parameters:
//	attr_name (String) - the name of the attribute.
//	value (String) - the value of the attribute.
// Returns:
//	Nothing.
//-----------------------------------------------------------------
function _jsXMLNode_setAttribute(attr_name, value) {

	//find the attribute
	var iLoc = this.findAttribute(attr_name);
	var objAttribute = (iLoc > -1 ? this.attributes[iLoc] : null);
	
	//if it exists, set the value
	if (objAttribute != null)
		objAttribute.value = value;
	else {
		//it doesn't exist, so create it
		objAttribute = new jsXMLAttribute(attr_name, value);
		
		//add it to the attributes array
		this.attributes[this.attributes.length] = objAttribute;
	}
}

//-----------------------------------------------------------------
// Object jsXMLProcessingInstruction
// This object is akin to IXMLDOMProcessingInstruction object.
//
// Parameters:
//	none.
//-----------------------------------------------------------------
function jsXMLProcessingInstruction() {

	//inherited from hsXMLNode
	this.superclass = jsXMLNode;
	this.superclass();
	
	//properties
	this.nodeType = NODE_PROCESSING_INSTRUCTION;
	
	//methods
	this.toString = _jsXMLProcessingInstruction_toString;
}

//-----------------------------------------------------------------
// Method jsXMLProcessingInstruction.toString()
// This method returns the XML contained in the element.
//
// Parameters:
//	None.
// Returns:
//	An XML string.
//-----------------------------------------------------------------
function _jsXMLProcessingInstruction_toString() {

	//declare iterator and return string
	var i = 0, retstr = "";
	
	//create opening tag
	retstr = "<?xml";

	//add this tag's attributes to the string
	for (i=0; i < this.attributes.length; i++)
		retstr += " " + this.attributes[i].name + "=\"" + this.attributes[i].value + "\"";

	//end the tag
	retstr += "?>";

	//asign the XML	
	return retstr;
}

//-----------------------------------------------------------------
// Object jsXMLElement
// This object is akin to IXMLDOMElement object.
//
// Parameters:
//	tagName (String) - the tagName for the element.
//-----------------------------------------------------------------
function jsXMLElement(tagName) {

	//inherited from hsXMLNode
	this.superclass = jsXMLNode;
	this.superclass();
	
	//attributes
	this.nodeType = NODE_ELEMENT;
	this.tagName = tagName;
	
	//check for tagname in the form prefix:baseName
	if (tagName.indexOf(":") > -1) {
		this.prefix = tagName.substr(0, tagName.indexOf(":"));
		this.baseName = tagName.substr(tagName.indexOf(":") + 1, tagName.length);	
	} else {
		this.prefix = "";
		this.baseName = "";
	}
	
	//methods
	this.getElementsByTagName = _jsXMLElement_getElementsByTagName;
	this.toString = _jsXMLElement_toString;
}


//-----------------------------------------------------------------
// Method jsXMLElement.getElementsByTagName()
// This method creates a list of elements with a given tag name.
//
// Parameters:
//	tagName (String) - the tagName of the elements you are looking for.
// Returns:
//	An Array of jsXMLElement objects with the given tagName.
//-----------------------------------------------------------------
function _jsXMLElement_getElementsByTagName(tagName) {

	//the array of elements, begins empty
	var arrResult = new Array;

	//check this node
	if (this.tagName == tagName)
		arrResult.push(this);
		
	//check the childNodes
	for(var i=0; i < this.childNodes.length; i++) {
		if (this.childNodes[i].nodeType == NODE_ELEMENT) {
			
			//get the results for this child
			var arrChildResult = this.childNodes[i].getElementsByTagName(tagName);
			
			//add the results to the array
			arrResult.push(arrChildResult);
		}			
	}
	
	//return the resulting array
	return arrResult;
}

//-----------------------------------------------------------------
// Method jsXMLElement.toString()
// This method returns the XML contained in the element.
//
// Parameters:
//	None.
// Returns:
//	An XML string.
//-----------------------------------------------------------------
function _jsXMLElement_toString() {

	//declare iterator and return string
	var i = 0, retstr = "";
	
	//create opening tag
	retstr = "<" + this.tagName;

	//add this tag's attributes to the string
	for (i=0; i < this.attributes.length; i++)
		retstr += " " + this.attributes[i].name + "=\"" + this.attributes[i].value + "\"";

	//end the tag
	if (!this.hasChildNodes()) {
		retstr += "/>";
	} else {
	
		//end the tag
		retstr += ">";
	
		//add XML for the children of this node
		//if there are children, then don't count the text for this tag
		//otherwise, include the text
		if (this.hasChildNodes()) {
			for (i=0; i < this.childNodes.length; i++)
				retstr += this.childNodes[i].toString();
		}

		//create closing tag
		retstr += "</" + this.tagName + ">";
	}

	//asign the XML	
	return retstr;
}

//-----------------------------------------------------------------
// This object is akin to IXMLDOMCharacterData object.
//
// Parameters:
//	data (String) - the data to hold.
//-----------------------------------------------------------------
function jsXMLCharacterData(data) {

	//assign text data
	this.data = data;
	this.nextSibling = null;
	this.ownerDocument = null;
	this.parentNode = null;
	this.previousSibling = null;
	
	//methods
	this.appendData = _jsXMLCharacterData_appendData;
	this.deleteData = _jsXMLCharacterData_deleteData;
	this.insertData = _jsXMLCharacterData_insertData;
	this.replaceData = _jsXMLCharacterData_replaceData;
	this.splitText = _jsXMLCharacterData_splitText;
	this.substringData = _jsXMLCharacterData_substringData;
	this.toString = _jsXMLCharacterData_toString;
}

//-----------------------------------------------------------------
// Method jsXMLCharacterData.appendData()
// This method appends the given text to the node data.
//
// Parameters:
//	text(String) - the text to append to th node data.
// Returns:
//	Nothing.
//-----------------------------------------------------------------
function _jsXMLCharacterData_appendData(text) {
	this.data += text;
}

//-----------------------------------------------------------------
// Method jsXMLCharacterData.deleteData()
// This method deletes the characters between char_offset and
// char_offset + num_chars.
//
// Parameters:
//	char_offset (int) - the index of the character to begin with.
//	num_chars (int) - the number of characters to delete.
// Returns:
//	Nothing.
//-----------------------------------------------------------------
function _jsXMLCharacterData_deleteData(char_offset, num_chars) {

	//get the characters up to and including that at char_offset
	var strFront = this.data.substr(0, char_offset);
	
	//get the characters after char_offset
	var strBack = this.data.substr(num_chars + char_offset, this.data.length - strFront.length - num_chars);
	
	//now merge these two
	this.data = strFront + strBack;
}

//-----------------------------------------------------------------
// Method jsXMLCharacterData.insertData()
// This method inserts the given text into the data at the location
// specified by char_offset.
//
// Parameters:
//	char_offset (int) - the index of the character to begin with.
//	text (String) - the text to insert into the node data.
// Returns:
//	Nothing.
//-----------------------------------------------------------------
function _jsXMLCharacterData_insertData(char_offset, text) {

	//get the characters up to and including that at char_offset
	var strFront = this.data.substr(0, char_offset);
	
	//get the characters after char_offset
	var strBack = this.data.substr(char_offset, this.data.length - strFront.length);
	
	//now merge these two with the inserted text
	this.data = strFront + text + strBack;
}

//-----------------------------------------------------------------
// Method jsXMLCharacterData.replaceData()
// This method takes a String and replaces the characters between
// char_offset and char_offset + num_chars with it.
//
// Parameters:
//	char_offset (int) - the index of the character to begin with.
//	num_chars (int) - the number of characters to replace.
//	text (String) - the string to insert into this node's data.
// Returns:
//	A string.
//-----------------------------------------------------------------
function _jsXMLCharacterData_replaceData(char_offset, num_chars, text) {

	//get the characters up to and including that at char_offset
	var strFront = this.data.substr(0, char_offset);
	
	//get the characters after char_offset
	var strBack = this.data.substr(num_chars + char_offset, this.data.length - strFront.length - num_chars);
	
	//now merge these two
	this.data = strFront + text + strBack;
}

//-----------------------------------------------------------------
// Method jsXMLCharacterData.splitText()
// This method splits this node into two nodes, with the data being
// split at char_offset.
//
// Parameters:
//	char_offset (int) - the index of the character to split at.
// Returns:
//	A string.
//-----------------------------------------------------------------
function _jsXMLCharacterData_splitText(char_offset) {
	//get the characters up to and including that at char_offset
	var strFront = this.data.substr(0, char_offset);
	
	//get the characters after char_offset
	var strBack = this.data.substr(char_offset, this.data.length - strFront.length);
	
	//now merge these two
	this.data = strFront;
	
	//create a new text node and add it to its parent
	var objNode = null;
	
	//create a new node
	switch(this.nodeType) {
		case NODE_TEXT:
			objNode = new jsXMLText(strBack);
			break;
		case NODE_CDATA_SECTION:
			objNode = new jsXMLCDataSection(strBack);
			break;
		case NODE_COMMENT:
			objNode = new jsXMLComment(strBack);
	}

	//add it after this one
	this.parentNode.insertBefore(objNode, this.nextSibling);
}

//-----------------------------------------------------------------
// Method jsXMLCharacterData.substringData()
// This method returns a String representing the characters between
// char_offset and char_offset + num_chars.
//
// Parameters:
//	char_offset (int) - the index of the character to begin with.
//	num_chars (int) - the number of characters to copy.
// Returns:
//	A string.
//-----------------------------------------------------------------
function _jsXMLCharacterData_substringData(char_offset, num_chars) {
	return this.data.substr(char_offset, num_chars);
}

//-----------------------------------------------------------------
// Method jsXMLCharacterData.toString()
// This method returns the data contained in the node.
//
// Parameters:
//	None.
// Returns:
//	A string.
//-----------------------------------------------------------------
function _jsXMLCharacterData_toString() {
	return this.data;
}

//-----------------------------------------------------------------
// Object jsXMLText
// This object is akin to IXMLDOMText ActiveX object.
//
// Parameters:
//	data (String) - text data.
//-----------------------------------------------------------------
function jsXMLText(data) {

	//inherited from hsXMLNode
	this.superclass = jsXMLCharacterData;
	this.superclass();

	//properties
	this.data = data;
	
	//assign node type
	this.nodeType = NODE_TEXT;
}

//-----------------------------------------------------------------
// Object jsXMLCDataSection
// This object is akin to IXMLDOMCDataSection ActiveX object.
//
// Parameters:
//	data (String) - text data.
//-----------------------------------------------------------------
function jsXMLCDataSection(data) {

	//inherited from jsXMLCharacterData
	this.superclass = jsXMLCharacterData;
	this.superclass();

	//propertoes
	this.data = data;
	this.nodeType = NODE_CDATA_SECTION;
	
	//methods
	this.toString = _jsXMLCDataSection_toString;
}

//-----------------------------------------------------------------
// Method jsXMLCDataSection.toString()
// This method returns the data contained in the node as a CDATA section
// string.
//
// Parameters:
//	None.
// Returns:
//	A string.
//-----------------------------------------------------------------
function _jsXMLCDataSection_toString() {
	return "<![CDATA[" + this.data + "]]>";
}

//-----------------------------------------------------------------
// Object jsXMLComment
// This object is akin to IXMLDOMComment ActiveX object.
//
// Parameters:
//	data (String) - text data.
//-----------------------------------------------------------------
function jsXMLComment(data) {

	//inherited from jsXMLCharacterData
	this.superclass = jsXMLCharacterData;
	this.superclass();
	
	//properties
	this.data = data;
	this.nodeType = NODE_COMMENT;
	
	//methods
	this.toString = _jsXMLComment_toString;
}

//-----------------------------------------------------------------
// Method jsXMLComment.toString()
// This method returns the data contained in the node as a comment
// string.
//
// Parameters:
//	None.
// Returns:
//	A string.
//-----------------------------------------------------------------
function _jsXMLComment_toString() {
	return "<!--" + this.data + "-->";
}

//-----------------------------------------------------------------
// Object jsXMLDocument
// This object is akin to IXMLDOMDocument ActiveX object.
//
// Parameters:
//	tagName (String) - the tagName of the document Element.
//-----------------------------------------------------------------
function jsXMLDocument() {

	//properties
	this.documentElement = null;
	this.parseError = false;
	
	//methods
	this.createElement = _jsXMLDocument_createElement;
	this.createNodeFromXML = _jsXMLDocument_createNodeFromXML;
	this.getElementsByTagName = _jsXMLDocument_getElementsByTagName;
	this.loadXML = _jsXMLDocument_loadXML;
	this.toString = _jsXMLDocument_toString;
}

//-----------------------------------------------------------------
// Method jsXMLDocument.createElement()
// This method creates an element with the given tagName and returns
// it as the function result.
//
// Parameters:
//	None.
// Returns:
//	A jsXMLElement object.
//-----------------------------------------------------------------
function _jsXMLDocument_createElement(tagName) {
	return new jsXMLElement(tagName);
}

//-----------------------------------------------------------------
// Method jsXMLDocument.toString()
// This method returns the XML contained in the document.
//
// Parameters:
//	None.
// Returns:
//	An XML string.
//-----------------------------------------------------------------
function _jsXMLDocument_toString() {
	return "<?xml version=\"1.0\"?>" + this.documentElement.toString();
}

//-----------------------------------------------------------------
// Method jsXMLDocument.loadXML()
// This method loads a string of XML and creates the appropriate
// JavaScript DOM to represent it.
//
// Parameters:
//	strXML (String) - an XML string.
// Returns:
//	Nothing.
//-----------------------------------------------------------------
function _jsXMLDocument_loadXML(strXML) {

	//node pointer
	var curNode = null;
	
	//stack of nodes
	var arrStack = new Array;
	
	//array of tokens in the XML
	var arrTokens = getTokensFromXML(strXML);

	//take care of document element
	this.documentElement = this.createNodeFromXML(arrTokens[1]);
	
	//set the current node to the documentElement
	curNode = this.documentElement;
	
	//evaluate all tokens
	for (var i=2; i < arrTokens.length; i++) {
		
		//check to see if this is an opening tag or a closing tag
		if (arrTokens[i].substr(0,2) == "</") {

			//closing tag, pop last parent off the stack
			curNode = arrStack.pop();

		} else {
			//create the new node
			var objNode = this.createNodeFromXML(arrTokens[i]);
			
			//add this node to its parent
			curNode.appendChild(objNode);
			
			//check to see if this node is closed or not
			if ((objNode.nodeType == NODE_ELEMENT) && (arrTokens[i].substr(arrTokens[i].length-2, 2) != "/>")) {

				//push old parent onto the stack
				arrStack.push(curNode);
				
				//set new parent
				curNode = objNode;
			}
		}	
	}	
}

//-----------------------------------------------------------------
// Method jsXMLDocument.getElementsByTagName()
// This method creates a list of elements with a given tag name.
//
// Parameters:
//	tagName (String) - the tagName of the elements you are looking for.
// Returns:
//	An Array of jsXMLElement objects with the given tagName.
//-----------------------------------------------------------------
function _jsXMLDocument_getElementsByTagName(tagName) {

	//check to see if there is a documentElement, if there is,
	//call the documentElement's getElementsByTagName method.
	if (!this.documentElement)
		alert(ERR_NO_DOCUMENT_ELEMENT);
	else
		return this.documentElement.getElementsByTagName(tagName);
	
}

//-----------------------------------------------------------------
// Method jsDocument.createNodeFromXML()
// This function takes a string and converts it into a jsXMLElement.
// The string must be in the format of:
//		<[tagName] [attrib1name]="[attrib1value]"...[attribNname]="[attribNvalue]"
// Note the lack of a closing ">"
//
// Parameters:
//	strXML (String) - an XML string in the above form.
// Returns:
//	A jsXMLElement object representing the string.
//-----------------------------------------------------------------
function _jsXMLDocument_createNodeFromXML(strXML) {
	
	//determine what type of data this is
	switch(strXML.charAt(0)) {
	
		//it's some sort of tag
		case '<':
			
			//get the tagName
			var strTagName = getTagNameFromXML(strXML);

			//determine what this is
			if (strTagName == "?xml") {
				//it's a processing instruction, ditch the closing ?>
				strXML = strXML.substr(0, strXML.length - 2);
				
				//get the attributes
				var arrAttributes = strXML.split(" ");

				//create the new element
				var objNode = new jsXMLProcessingInstruction;

				if (arrAttributes.length > 1) {
					//now get all the attributes
					for (var i=1; i < arrAttributes.length; i++) {
				
						//get the current string
						var strAttribute = arrAttributes[i];
					
						//make sure that this is more than just a blank space
						if (strAttribute.length > 1) {
							if (strAttribute.charAt(strAttribute.length-1) == "/")
								strAttribute = strAttribute.substr(0, strAttribute.length - 1);
							
							//get the name/value pair
							var arrNV = strAttribute.split("=");
						
							//set the attribute value (make sure to cut off quotes in the value)
							objNode.setAttribute(arrNV[0], arrNV[1].substr(1, arrNV[1].length-2));
						}	
					}	
				}

				//return the created node
				return objNode;
			} else if (strTagName == "![CDATA[") {
				return new jsXMLCDataSection(getCDataText(strXML));
			} else if (strTagName == "!--") {
				return new jsXMLComment(getCommentText(strXML));
			} else {

				//it's an HTML-like tag, ditch the closing signal
				strXML = strXML.substr(0, strXML.length - 1);
				
				//get the attributes
				var arrAttributes = strXML.split(" ");

				//create the new element
				var objNode = new jsXMLElement(strTagName);

				if (arrAttributes.length > 1) {
					//now get all the attributes
					for (var i=1; i < arrAttributes.length; i++) {
				
						//get the current string
						var strAttribute = arrAttributes[i];
					
						//make sure that this is more than just a blank space
						if (strAttribute.length > 1) {
							if (strAttribute.charAt(strAttribute.length-1) == "/")
								strAttribute = strAttribute.substr(0, strAttribute.length - 1);
							
							//get the name/value pair
							var arrNV = strAttribute.split("=");
						
							//set the attribute value (make sure to cut off quotes in the value)
							objNode.setAttribute(arrNV[0], arrNV[1].substr(1, arrNV[1].length-2));
						}	
					}	
				}

				//return the created node
				return objNode;
			} 
		
			//use the tagName
			break;
		
		//it's text
		default:
			//it's text
			objNode = new jsXMLText(strXML);
			
			//return the created node
			return objNode;
	}
}

//----------------------------------------------------------------
// 2: HELPER FUNCTIONS
//----------------------------------------------------------------
function getTokensFromXML(strXML) {

	var arrTemp = new Array;
	var arrTokens = new Array;
	
	//iterate through the characters
	for (var i=0; i < strXML.length; i++) {
		var c = strXML.charAt(i);
		
		//push this character into the temporary array
		if (c != '<')
			arrTemp.push(c);

		//check the value
		switch (c) {
			case '<':
				if (arrTemp.length > 0) {
					arrTokens.push(arrTemp.join(""));
					delete arrTemp;
					arrTemp = new Array;
				}
				arrTemp.push("<");
				break;
		 	case '>':
				arrTokens.push(arrTemp.join(""));
				delete arrTemp;
				arrTemp = new Array;
		}
	
	}
	
	return arrTokens;
}


function getTagNameFromXML(strXML) {

	//first check for comment
	if (strXML.indexOf("<!--") == 0)
		return "!--";
		
	//check for CDATA Section
	if (strXML.indexOf("<![CDATA[") == 0)
		return "![CDATA[";
	
	//get custom tag name
	var reTag = /<([\w\?\!\-\[]+)/;
	strXML.match(reTag);
	return RegExp.$1;
}

function getCommentText(strXML) {
	return strXML.substr(4, strXML.length - 7);
}

function getCDataText(strXML) {
	return strXML.substr(9, strXML.length - 12);
}
