var gHtmlEscapeChars = new Object();
//gHtmlEscapeChars[" "] = "&nbsp;";
gHtmlEscapeChars["&"] = "&amp;";
gHtmlEscapeChars["\""] = "&quot;";
gHtmlEscapeChars["<"] = "&lt;";
gHtmlEscapeChars[">"] = "&gt;";
gHtmlEscapeChars["'"] = "&squo;";

var xmlNODE_ELEMENT = 1,
	xmlNODE_ATTRIBUTE  = 2,
	xmlNODE_TEXT  = 3,
	xmlNODE_CDATA_SECTION  = 4,
	xmlNODE_ENTITY_REFERENCE  = 5,
	xmlNODE_ENTITY  = 6,
	xmlNODE_PROCESSING_INSTRUCTION  = 7,
	xmlNODE_COMMENT  = 8,
	xmlNODE_DOCUMENT  = 9,
	xmlNODE_DOCUMENT_TYPE  = 10,
	xmlNODE_DOCUMENT_FRAGMENT  = 11,
	xmlNODE_NOTATION  = 12;
	
XMLUtils = new Object;
XMLUtils.bMozInited = false;

XMLUtils.getNodeValue = function(oNode, bRaw) {
	var ret = null;
	if (bRaw == undefined)
		bRaw = false;
	if (oNode) {
		if (oNode.nodeType == xmlNODE_ELEMENT) {
			var child1 = oNode.firstChild;
			if (child1) {
				if (child1.nodeType == xmlNODE_TEXT) {
					if (bRaw)
						ret = child1.nodeValue;
					else
						ret = XMLUtils.valueOf(child1.nodeValue);
				}
				else
					ret = child1;
			}
		}
		else if (oNode.nodeType == xmlNODE_TEXT) {
			if (bRaw)
				ret = oNode.nodeValue;
			else
				ret = XMLUtils.valueOf(oNode.nodeValue);
		}
	}
	return ret;
}

XMLUtils.setNodeValue = function(oNode, oValue) {
	var bSet = false;
	if (oNode) {
		if (oNode.nodeType == xmlNODE_ELEMENT) {
			var child1 = oNode.firstChild;
			if (child1) {
				if (child1.nodeType == xmlNODE_TEXT) {
					child1.nodeValue = oValue;
					bSet = true;
				}
			}
			else { // no first child
				var doc = oNode.ownerDocument;
				var textNode = doc.createTextNode(String(oValue));
				oNode.appendChild(textNode);
				bSet = true;
			}
		}
		if (! bSet) {
			oNode.nodeValue = oValue;
		}
	}
}

// xmlDoc can be any node in the xml tree
XMLUtils.getValueByNodeName = function(xmlDoc, nodeName, defaultValue) {
	var node = xmlDoc.selectSingleNode(nodeName);
	var ret = null;
	if (node)
		ret = XMLUtils.getNodeValue(node);
	if (ret == null) {
		XtrazDebug.trace(3, "node", nodeName, "not found, using default value", defaultValue);
		ret = defaultValue;
	}
	return ret;
}

XMLUtils.setValueByNodeName = function(xmlDoc, nodeName, nodeValue /* more name/value pairs */) {
	var node;
	for (var i = 2; i < arguments.length; i += 2) {
		nodeName = arguments[i - 1];
		nodeValue = arguments[i];
		if (nodeValue != undefined && nodeValue != null) { // ignore null fields
			node = xmlDoc.selectSingleNode(nodeName);
			if (node) {
				XMLUtils.setNodeValue(node, nodeValue);
			}
			else {
				XtrazDebug.trace("setValueByNodeName: node", nodeName,"not found");
			}
		}
	}
}

XMLUtils.addTextNode = function(oNode, sTag, sData) {
	if (XtrazUtils.isEmpty(sData))
		sData = '';
	var node;

	var doc = oNode.ownerDocument;
	var node = doc.createElement(sTag);
	textNode = doc.createTextNode(sData);
	node.appendChild(textNode);
	oNode.appendChild(node);

	return node;
}

// dst and src must be on the same level
XMLUtils.mergeXML = function(dst, src) {
	var srcNode = src.firstChild;
	var destNode, name1, name2;
	var destDoc = dst.ownerDocument;
	if (! destDoc)
		destDoc = dst;
	while (srcNode) {
		XtrazDebug.trace("merging node", srcNode.nodeName);
		destNode = dst.selectSingleNode(srcNode.nodeName);
		if (destNode) {
			XtrazDebug.trace("found dest node type", destNode.nodeType);
			if (srcNode.nodeType == xmlNODE_ELEMENT) { // must succeed, text nodes don't have names
				var child1 = srcNode.firstChild;
				var child2 = destNode.firstChild;
				if (child1) {
					if (child1.nodeType == xmlNODE_ELEMENT) {
						XtrazDebug.trace("found src child type element");
						var bMerged = false;
						if (child2) {
							if (child2.nodeType == xmlNODE_ELEMENT) {
								XtrazDebug.trace("found dest child type element, recursing");
								XMLUtils.mergeXML(destNode, srcNode);
								bMerged = true;
							}
						}
						if (! bMerged) {
							if (child2)
								destNode.removeChild(child2);
							destNode.appendChild(child1.cloneNode(true));
							XtrazDebug.trace("appended child", child1.nodeName);
						}
					}
					else if (child1.nodeType == xmlNODE_TEXT) { // source is textNode
						XtrazDebug.trace("source is text node");
						if (child2) {
							if (child2.nodeType == xmlNODE_TEXT) {
								child2.nodeValue = child1.nodeValue;
							}
							else { // child2 is element
								while (child2.firstChild)
									child2.removeChild(child2.firstChild);
								child2.appendChild(destDoc.createTextNode(child1.value));
							}
						}
						else {
						
						}
					}
				}
				else { // no first child
				// do nothing
				}
			}
			else if (srcNode.nodeType == xmlNODE_TEXT) {
				if (destNode.nodeType == xmlNODE_TEXT) {
					XtrazD
					destNode.nodeValue = srcNode.nodeValue;
				}
			}
		}
		else {
			XtrazDebug.trace("no matching destnode, importing node", srcNode.nodeName);
			try {
				destNode = srcNode.cloneNode(true);
			}
			catch (e) {
				XtrazDebug.trace("improt node", e);
			}
			XtrazDebug.trace("created imported node", typeof(destNode));
			XtrazDebug.trace("appended", typeof(dst.appendChild(destNode)));
//			XtrazDebug.trace("appended");
		}
		srcNode = srcNode.nextSibling;
	}
}

XMLUtils.getNode = function(xmlDoc, sTag) {
	var ret = xmlDoc.selectSingleNode(sTag);
	if (! ret) {
		var doc = xmlDoc.ownerDocument;
		if (! doc) {
			doc =xmlDoc;
			if (xmlDoc.documentElement)
				xmlDoc = xmlDoc.documentElement;
		}
		ret =doc.createElement(sTag);
		xmlDoc.appendChild(ret);
	}
	return ret;
}

XMLUtils.valueOf = function(sValue) {
	var ret = sValue;
	var t = typeof(sValue);
	switch (t) {
		default:
			ret = String(sValue);
			break;
		case "string":
			var nValue = parseInt(sValue);
			if (String(nValue) == sValue) {
				ret = nValue;
			}
			else {
				var lvalue = sValue.toLowerCase();
				switch (lvalue) {
					case 'false':
						ret = false;
						break;
					case 'true':
						ret = true;
						break;
					case 'null':
						ret = null;
						break;
				}
			}
			break;
		}
	return ret;
}

XMLUtils.getParseError = function(xmlDoc) {
	var ret = 0;
	if (xmlDoc) {
		if (xmlDoc.parseError != undefined) {
			ret = xmlDoc.parseError.errorCode;
		}
		else if (xmlDoc.documentElement != undefined) {
			if (xmlDoc.documentElement.nodeName == 'parsererror')
				ret = 1;
		}
	}
	else { // no doc
		ret = -1;
	}	
	return ret;
}

XMLUtils.xmlToHash = function(xmlDoc, nodeName) {
	var node;
  
	if (XtrazUtils.isEmpty(nodeName)) {
		node = xmlDoc;
	}
	else {
		if (nodeName.charAt(0) != "/")
			nodeName = "//" + nodeName;
		node = 	xmlDoc.selectSingleNode(nodeName);
		
	}
	
	var currNode = node.firstChild;
	var obj = new Object;
	while(currNode) {
		try{
			var name = currNode.tagName;
			if (name.length > 0) // access name freely, we're in a try/catch context
				obj[name] = XMLUtils.getNodeValue(currNode, true);
		}
		catch(e){
		}
		currNode = currNode.nextSibling;
	}
	return  obj;
}

/********** Mozilla DOM extension ************/

function _Node_getXML() {
    
    //create a new XMLSerializer
	var objXMLSerializer = new XMLSerializer;
	
	//get the XML string
	var strXML = objXMLSerializer.serializeToString(this);
	
	//return the XML string
	return strXML;
}


function _Document_onload() {

//change the readyState
    changeReadyState(this, 4);

}


function _Document_load(strURL) {

	//change the readyState
    changeReadyState(this, 1);
	
	//call the original load method
	return this.__load__(strURL);
}

function changeReadyState(objDOMDocument, iReadyState) {
	
	//change the readyState
	objDOMDocument.readyState = iReadyState;
	
	//if there is an onreadystatechange event handler, run it
	if (objDOMDocument.onreadystatechange != null && typeof objDOMDocument.onreadystatechange == "function")
		objDOMDocument.onreadystatechange();
}

function XMLNodeList(i){
	this.length = i;
};

XMLNodeList.prototype = new Array(0);

XMLNodeList.prototype.constructor = Array;

XMLNodeList.prototype.item = function(i) {
	return (i < 0 || i >= this.length)?null:this[i];
};

XMLNodeList.prototype.expr = "";

/*********** End Mozilla DOM Extension ************/

XMLUtils.initMozDOM = function() {
	if (XMLUtils.bMozInited)
		return;
	try {
		XtrazDebug.alert("creating moz dom");
		XMLUtils.bMozInited = true;
		Node.prototype.__defineGetter__("xml", _Node_getXML);
		XMLDocument.prototype.readyState = "0";
		XMLDocument.prototype.__load__ = XMLDocument.prototype.load;
		XMLDocument.prototype.onreadystatechange = null;
		XMLDocument.prototype.setProperty  = function(x,y){};


		XMLDocument.prototype.load = _Document_load;
			//create a DOMParser
		XMLDocument.prototype.loadXML = function(strXML) {
        	changeReadyState(this, 1);

			var objDOMParser = new DOMParser();
			
			//create new document from string
			var objDoc = objDOMParser.parseFromString(strXML, "text/xml");
			if (XMLUtils.getParseError(objDoc) != 0) {
			
			}
			else {
				while (this.hasChildNodes()) {
					this.removeChild(this.lastChild);
				}
				for (var i=0; i < objDoc.childNodes.length; i++) {
					//import the node
					var objImportedNode = this.importNode(objDoc.childNodes[i], true);
						
					//append the child to the current document
					this.appendChild(objImportedNode);
			
				} //End
			}
	      	  changeReadyState(this, 4);
		} //End: function
		
		XMLDocument.prototype.selectSingleNode = function(sExpr, contextNode){
			var ctx = contextNode?contextNode:null;
			sExpr = "("+sExpr+")[1]";
			var nodeList = this.selectNodes(sExpr, ctx);
			if(nodeList.length > 0)
				return nodeList.item(0);
			else
				return null;
		}


		XMLDocument.prototype.selectNodes = function(sExpr, contextNode){
			var nsDoc = this;
			var nsresolver = this.createNSResolver(this.documentElement);
			var oResult = this.evaluate(sExpr,
					(contextNode?contextNode:this),
					nsresolver,
					XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
			var nodeList = new XMLNodeList(oResult.snapshotLength);
			nodeList.expr = sExpr;
			for(var i=0;i<nodeList.length;i++) {
				nodeList[i] = oResult.snapshotItem(i);
			}
			return nodeList;
		};

		Element.prototype.selectSingleNode = function(sExpr){
			var doc = this.ownerDocument;
			if(doc.selectSingleNode)
				return doc.selectSingleNode(sExpr, this);
			else
				throw "Method selectNodes is only supported by XML Elements";
		};

		Element.prototype.selectNodes = function(sExpr){
			var doc = this.ownerDocument;
			if(doc.selectNodes)
				return doc.selectNodes(sExpr, this);
			else
				throw "Method selectNodes is only supported by XML Elements";
		};

	}
	catch (er) {
		XtrazDebug.alert("caught error while initializing moz dom", er.message);
	}
}


XMLUtils.makeDocument = function(bAsync) {
	if (undefined == bAsync)
		bAsync = false;
	var doc = null;
	try {
		doc = new ActiveXObject("msxml2.DOMDocument");
		doc.setProperty("SelectionLanguage", "XPath");
	}
	catch (e) {
		XtrazDebug.trace("XMLUtils.makeDocument: failed to create XML Document (Msxml2)", e.description);
		try {
			doc = new ActiveXObject("Msxml.DOMDocument");
			
		}
		catch(e) {
			try {
				doc = new ActiveXObject("Microsoft.XMLDOM");
				
			}
			catch (e) {
				XtrazDebug.trace("XMLUtils.makeDocument: failed to create XML Document (Microsfot)", e.description);
			}
		}
//		doc.setProperty("SelectionLanguage", "XPath");
	}
	
//		XtrazDebug.alert("XMLUtils.makeDocument: failed to create XML Document", e.description);
	if (! doc) {
		XtrazDebug.trace("initing moz dom");
		try {
			XMLUtils.initMozDOM();
			doc = document.implementation.createDocument("", "", null);
			doc.addEventListener("load", _Document_onload, false);
		}
		catch (er) {
			XtrazDebug.alert("createdocument failed", er.message);
		}
	}
	if (doc) {
		doc.async = bAsync;
		doc.resolveExternals = false;
		doc.validateOnParse = false;
		// Load an XML file into the DOM instance.
		// Display the content of the object.
	}
	return doc;
}

XMLUtils.loadString = function(src, bAsync) {
	var doc = XMLUtils.makeDocument(bAsync);
	doc.loadXML(src);
	
	return doc;
}

XMLUtils.loadDocument = function(src) {
	var doc = XMLUtils.makeDocument(false);
	if (doc) {
		doc.load(src);
	}
	return doc;
}

XMLUtils.loadDocumentAsync = function(src, owner, callback) {
	var doc = XMLUtils.makeDocument(true);
	if (doc) {
		var f = function() {
			var dis = arguments.callee;
			var doc = dis.doc;
			if (doc.readyState == 4) {
				dis.owner[dis.callback](doc);
			}
		}
		f.doc = doc;
		f.owner = owner;
		f.callback = callback;
		doc.onreadystatechange = f;
		doc.load(src);
	}
	return doc;
}

