XML = {};
/**
* Create a new Document object. If no arguments are specified,
* the document will be empty. If a root tag is specified, the document
* will contain that single root tag. If the root tag has a namespace
* prefix, the second argument must specify the URL that identifies the
* namespace.
*/
XML.newDocument = function(rootTagName, namespaceURL) {
	if (!rootTagName) rootTagName = "";
	if (!namespaceURL) namespaceURL = "";

	if (document.implementation && document.implementation.createDocument) {
		// This is the W3C standard way to do it
		return document.implementation.createDocument(namespaceURL,	rootTagName, null);
	}
	else // This is the IE way to do it
	{ 
		// Create an empty document as an ActiveX object
		// If there is no root element, this is all we have to do
		var doc = new ActiveXObject("MSXML2.DOMDocument");

		// If there is a root tag, initialize the document
		if (rootTagName) {
			// Look for a namespace prefix
			var prefix = "";
			var tagname = rootTagName;
			var p = rootTagName.indexOf(':');
			
			if (p != -1)
			{
				prefix = rootTagName.substring(0, p);
				tagname = rootTagName.substring(p+1);
			}

			// If we have a namespace, we must have a namespace prefix
			// If we don't have a namespace, we discard any prefix
			if (namespaceURL) 
			{
				if (!prefix) prefix = "a0"; // What Firefox uses
			}
			else 
				prefix = "";

			// Create the root element (with optional namespace) as a
			// string of text
			var text = "<" + (prefix?(prefix+":"):"") + tagname + (namespaceURL ? (" xmlns:" + prefix + '="' + namespaceURL +'"') : "") + "/>";
			// And parse that text into the empty document
			doc.loadXML(text);
		}

		return doc;
	}
};

/**
* Parse the XML document contained in the string argument and return
* a Document object that represents it.*/
XML.parse = function(text) {
	

	if (typeof DOMParser != "undefined") {
		// Mozilla, Firefox, and related browsers
		return (new DOMParser( )).parseFromString(text.toLowerCase(), "application/xml");
	}
	else if (typeof ActiveXObject != "undefined") {
		// Internet Explorer.
		var doc = XML.newDocument( ); // Create an empty document
		doc.loadXML(text); // Parse text into it
		return doc; // Return it
	}
};

/**
* XML.XPathExpression is a class that encapsulates an XPath query and its
* associated namespace prefix-to-URL mapping. Once an XML.XPathExpression
* object has been created, it can be evaluated one or more times (in one
* or more contexts) using the getNode( ) or getNodes( ) methods.
*
* The first argument to this constructor is the text of the XPath expression.
*
* If the expression includes any XML namespaces, the second argument must
* be a JavaScript object that maps namespace prefixes to the URLs that define
* those namespaces. The properties of this object are the prefixes, and
* the values of those properties are the URLs.
*/
XML.XPathExpression = function(xpathText, namespaces) {
	this.xpathText = xpathText; // Save the text of the expression
	this.namespaces = namespaces; // And the namespace mapping

	if (document.createExpression) {
	// If we're in a W3C-compliant browser, use the W3C API
	// to compile the text of the XPath query
	this.xpathExpr = document.createExpression(xpathText.toLowerCase(),
								// This function is passed a
								// namespace prefix and returns the URL.
								function(prefix) { return namespaces[prefix]; });
	}
	else {
		// Otherwise, we assume for now that we're in IE and convert the
		// namespaces object into the textual form that IE requires.
		this.namespaceString = "";
		if (namespaces != null) 
		{
			for(var prefix in namespaces) 
			{
				// Add a space if there is already something there
				if (this.namespaceString) this.namespaceString += ' ';
				// And add the namespace
				this.namespaceString += 'xmlns:' + prefix + '="' +
				namespaces[prefix] + '"';
			}
		}
	}
};

/**
* This is the getNodes( ) method of XML.XPathExpression. It evaluates the
* XPath expression in the specified context. The context argument should
* be a Document or Element object. The return value is an array
* or array-like object containing the nodes that match the expression.
*/
XML.XPathExpression.prototype.getNodes = function(context) {
	if (this.xpathExpr) {
		// If we are in a W3C-compliant browser, we compiled the
		// expression in the constructor. We now evaluate that compiled
		// expression in the specified context.
		var result = this.xpathExpr.evaluate(context, 
									// This is the result type we want
									XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
									null);
		// Copy the results we get into an array.
		var a = new Array(result.snapshotLength);
		for(var i = 0; i < result.snapshotLength; i++)
		{
			a[i] = result.snapshotItem(i);
		}
		return a;
	}
	else 
	{
		// If we are not in a W3C-compliant browser, attempt to evaluate
		// the expression using the IE API.
		try {
			// We need the Document object to specify namespaces
			var doc = context.ownerDocument;

			// If the context doesn't have ownerDocument, it is the Document
			if (doc == null) doc = context;

			// This is IE-specific magic to specify prefix-to-URL mapping
			doc.setProperty("SelectionLanguage", "XPath");
			doc.setProperty("SelectionNamespaces", this.namespaceString);

			// In IE, the context must be an Element not a Document,
			// so if context is a document, use documentElement instead
			if (context == doc) context = doc.documentElement;

			// Now use the IE method selectNodes( ) to evaluate the expression
			return context.selectNodes(this.xpathText);
		}
		catch(e) {
			// If the IE API doesn't work, we just give up
			//throw "XPath not supported by this browser.";
		}
	}
}

/**
* This is the getNode( ) method of XML.XPathExpression. It evaluates the
* XPath expression in the specified context and returns a single matching
* node (or null if no node matches). If more than one node matches,
* this method returns the first one in the document.
* The implementation differs from getNodes( ) only in the return type.
*/
XML.XPathExpression.prototype.getNode = function(context) {
	if (this.xpathExpr)
	{
		var result =this.xpathExpr.evaluate(context,
									// We just want the first match
									XPathResult.FIRST_ORDERED_NODE_TYPE,
									null);
		return result.singleNodeValue;
	}
	else
	{
		try {
			//var doc = context.ownerDocument;
			//if (doc == null) doc = context;
			//doc.setProperty("SelectionLanguage", "XPath");
			//doc.setProperty("SelectionNamespaces", this.namespaceString);
			//if (context == doc) context = doc.documentElement;

			// In IE call selectSingleNode instead of selectNodes
			return context.selectSingleNode(this.xpathText);
		}
		catch(e)
		{
			throw "XPath not supported by this browser.";
		}
	}
};

// A utility to create an XML.XPathExpression and call getNodes( ) on it
XML.getNodes = function(context, xpathExpr, namespaces) {
	return (new XML.XPathExpression(xpathExpr, namespaces)).getNodes(context);
};

// A utility to create an XML.XPathExpression and call getNode( ) on it
XML.getNode = function(context, xpathExpr, namespaces) {
	return (new XML.XPathExpression(xpathExpr, namespaces)).getNode(context);
}

XML.getNodeText = function(node)
{
	return node.text == undefined ? node.textContent : node.text;
}

XML.escape = function(str)
{
	XML.escape.symbols = {'"':'&quot;', '&':'&amp;', '<':'&lt;', '>':'&gt;'}
	XML.escape.rep = function(a) { return XML.escape.symbols[a]||a; }
	
	if (str === undefined || str === null)
		return '';
	
	return (str+'').replace(/([\"\&\<\>])/g, XML.escape.rep);
}

XML.unescape = function(str)
{
	XML.unescape.symbols = {'&quot;':'"', '&amp;':'&', '&lt;':'<', '&gt;':'>'}
	XML.unescape.rep = function(a) { return XML.unescape.symbols[a]||a; }

	if (str === undefined || str === null)
		return '';

	return (str+'').replace(/(\&quot\;|\&amp\;|\&lt\;|\&gt\;)/g, XML.unescape.rep);
	
}