// Copyright (C) 2007 Cognos Incorporated. All Rights Reserved.
// Cognos and the Cognos logo are trademarks of Cognos Incorporated.

function XMLParser(s, parent)
{
	if (s == null) {
		return null;
	}
	if (/^\s*</.test(s)) {
		// remove leading spaces
		s = s.replace(/^\s*/, '');
		if (s.charAt(1) == '/') {
			// end of an element
			var reParent = new RegExp("^</" + parent.getName() + "\\s*>", "gi");
			if (!reParent.test(s)) {
				alert("invalid XML " + parent.getName() + "\n" + s);
				return null;
			}

			return XMLParser(s.replace(RegExp.lastMatch,""), parent.parentNode);
		}
		else {
			// new element
			var reNewElement = /^\s*<([\w:\-_\.]+)/;
			if ( reNewElement.test(s) ) {
				var nodeName = RegExp.$1;
				var e = new XMLElement(nodeName, parent);

				var patternToRemove = new RegExp("^<" + nodeName + "[^>]*>");

				s = s.replace(patternToRemove, "");
				var currentNodeText = RegExp.lastMatch;

				var attributesPattern = /([\w:\-_\.]+)="([^"]*)"/gi;
				var attrMatches = currentNodeText.match(attributesPattern);

				if (attrMatches != null) {
					for (var i=0; i<attrMatches.length; i++) {
						var attrText = attrMatches[i];
						(/([\w:\-_\.]+)\s*=\s*"(.*)"/).test(attrText);
						e.setAttribute(RegExp.$1, RegExp.$2);
					}
				}
				if ( ! (/\/>$/).test(currentNodeText) ) {
					XMLParser(s, e);
					return e;
				}
				else {
					XMLParser(s, parent);
					return e;
				}
			}
		}
	}
	else {
		// text node
		if (s && parent)
		{
			var textNodePattern = new RegExp("([^<]*)</" + parent.getName() + "\\s*[^>]*>", "gi");
			textNodePattern.test(s);
			var nodeValue = RegExp.$1;
			parent.setValue(nodeValue);
			return (XMLParser(s.replace(nodeValue, ""), parent));
		}
	}
	return null;
}

function XMLElement(s, parent)
{
	this.nodeName = s;
	this.nodeValue = "";
	this.attributes = new Array();
	this.childNodes = new Array();
	this.parentNode = parent;
	if (this.parentNode) {
		this.parentNode.appendChild(this);
	}
}

XMLElement.prototype.appendChild = function (e) { this.childNodes[this.childNodes.length] = e; };

XMLElement.prototype.hasChildNodes = function ()
{
	if (this.childNodes.length > 0) {
		return true;
	}
	else {
		return false;
	}
};

XMLElement.prototype.findChildByName = function (n, deepWalk)
{
	if (this.getName() == n) {
		return(this);
	}
	for (var i = 0; i < this.childNodes.length; i++) {
		if (this.childNodes[i].getName() == n) {
			return this.childNodes[i];
		}
	}

	if (deepWalk != false) {
		for (i = 0; i < this.childNodes.length; i++) {
			var foundChild = this.childNodes[i].findChildByName(n, deepWalk);
			if (foundChild) {
				return foundChild;
			}
		}
	}
	return null;
};

XMLElement.prototype.findChildWithAttribute = function (attr, val)
{
	for (var i = 0; i < this.childNodes.length; i++) {
		if (this.childNodes[i].getAttribute(attr) == val) {
			return this.childNodes[i];
		}
	}
	return null;
};

XMLElement.prototype.getElementsByTagName = function (s, deep)
{
	var a = new Array();
	for (var i = 0; i < this.childNodes.length; i++) {
		if (this.childNodes[i].getName() == s) {
			a[a.length] = this.childNodes[i];
		}
	}
	if (deep != false) {
		for (i = 0; i < this.childNodes.length; i++) {
			var aChild = this.childNodes[i].getElementsByTagName(s);
			for (var j = 0; j < aChild.length; j++) {
				a[a.length] = aChild[j];
			}
		}
	}
	return a;
};

XMLElement.prototype.getName = function () { return this.nodeName; };
XMLElement.prototype.getValue = function () { return this.nodeValue; };
XMLElement.prototype.getAttribute = function (a) { var retval = this.attributes["_" + a]; return (retval == null ? "" : retval); };
XMLElement.prototype.setAttribute = function (a, v) { this.attributes["_" + a] = v; };
XMLElement.prototype.setValue = function (v) { this.nodeValue = v; };

XMLElement.prototype.toString = function()
{
	var s = "<" + this.getName();
	for (var i in this.attributes) {
		s += " " + i.substring(1) + "=\"" + this.attributes[i] + "\"";
	}
	s += ">" + this.getValue();
	for (var j = 0; j < this.childNodes.length; j++) {
		s += this.childNodes[j].toString();
	}
	s += "</" + this.getName() + ">";
	return s;
};

function XMLBuilderLoadXMLFromString (markup)
{
	var xmlDocument = null;
	if (typeof DOMParser != 'undefined')
	{
		xmlDocument = new DOMParser().parseFromString(markup, 'application/xml');
	}
	else if (typeof ActiveXObject != 'undefined')
	{
		try
		{
			xmlDocument = new ActiveXObject('Microsoft.XMLDOM');
			xmlDocument.loadXML(markup);
		}
		catch (e)
		{
		}
	}
	return xmlDocument;
}

function XMLBuilderCreateXMLDocument (rootElementName, namespaceURI, documentType)
{
	var xmlDocument = null;
	namespaceURI = namespaceURI || '';
	documentType = documentType || null;

	if (document.implementation && document.implementation.createDocument)
	{
		if(typeof namespaceURI == "undefined")
			namespaceURI = "http://www.w3.org/2000/xmlns/";
				
		xmlDocument = document.implementation.createDocument(namespaceURI, rootElementName, documentType);
	}
	else if (typeof ActiveXObject != 'undefined')
	{
		try
		{
			xmlDocument = new ActiveXObject('Microsoft.XMLDOM');
			var rootElement = xmlDocument.createNode(1, rootElementName, namespaceURI);
			xmlDocument.appendChild(rootElement);
		}
		catch (e)
		{
		}
	}
	return xmlDocument;
}

function XMLBuilderCreateElementNS (namespaceURI, elementName, ownerDocument)
{
	var element = null;
	if (typeof ownerDocument.createElementNS != 'undefined')
	{
		if(typeof namespaceURI == "undefined")
			namespaceURI = "http://www.w3.org/2000/xmlns/";
				
		element = ownerDocument.createElementNS(namespaceURI, elementName);
	}
	else if (typeof ownerDocument.createNode != 'undefined')
	{
		element = ownerDocument.createNode(1, elementName, namespaceURI);
	}
	return element;
}

function XMLBuilderSetAttributeNodeNS (element, attributeName, attributeValue, namespaceURI)
{
	if (typeof element.setAttributeNS != 'undefined')
	{
		if(typeof namespaceURI == "undefined")
			namespaceURI = "http://www.w3.org/2000/xmlns/";
			
		element.setAttributeNS(namespaceURI, attributeName, attributeValue);
	}
	else if (typeof element.ownerDocument != 'undefined' && typeof element.ownerDocument.createNode != 'undefined')
	{
		var attribute = element.ownerDocument.createNode(2, attributeName, namespaceURI);
		attribute.nodeValue = attributeValue;
		element.setAttributeNode(attribute);
	}
}

function XMLBuilderSerializeNode (node)
{
	if (typeof XMLSerializer != 'undefined')
	{
		try
		{
			return new XMLSerializer().serializeToString(node);
		}
		catch (e)
		{
			//Can end up here if the node was not an XML object
		}
	}
	else if (typeof node.xml != 'undefined')
	{
		return node.xml;
	}
	return '';
}

function XMLHelper_GetText(node)
{
	var text="";
	var childNodes = node.childNodes;
	for(var i = 0; i < childNodes.length; ++i)
	{
		if(childNodes[i].nodeType == 3)
			text += childNodes[i].nodeValue;
	}
	
	return text;
}

function XMLHelper_GetLocalName(node)
{
	if(typeof node.baseName != "undefined")
		return node.baseName;
	else
		return node.localName;
}

function XMLHelper_FindChildByTagName(node, name, deepWalk)
{
	// validate deep walk argument (must be "true" or "false")
	if(typeof deepWalk == "undefined" || (deepWalk != true && deepWalk != false))
		deepWalk = true;
		
	if (XMLHelper_GetLocalName(node) == name) 
	{
		return(node);
	}
	
	var i;
	
	for (i = 0; i < node.childNodes.length; i++) 
	{
		if (XMLHelper_GetLocalName(node.childNodes[i]) == name) 
		{
			return node.childNodes[i];
		}
	}

	if (deepWalk != false) 
	{
		for (i = 0; i < node.childNodes.length; i++) 
		{
			var foundChild = XMLHelper_FindChildByTagName(node.childNodes[i], name, deepWalk);
			if (foundChild) 
			{
				return foundChild;
			}
		}
	}
	return null;
}

function XMLHelper_FindChildrenByTagName(oNode, sName, bDeepWalk)
{
	// validate deep walk argument (must be "true" or "false")
	if (typeof bDeepWalk == "undefined" || (bDeepWalk != true && bDeepWalk != false))
	{
		bDeepWalk = true;
	}
		
	var aFoundNodes = new Array();
	var oNodeChildren = oNode.childNodes;
	
	for (var idxChildNodes = 0; idxChildNodes < oNodeChildren.length; idxChildNodes++) 
	{
		if (XMLHelper_GetLocalName(oNodeChildren[idxChildNodes]) == sName)
		{
			aFoundNodes[aFoundNodes.length] = oNodeChildren[idxChildNodes];
		}

		if (bDeepWalk === true) 
		{
			var oFoundChildren = XMLHelper_FindChildrenByTagName(oNodeChildren[idxChildNodes], sName, bDeepWalk);
			if (oFoundChildren.length > 0) 
			{
				aFoundNodes = aFoundNodes.concat(oFoundChildren);
			}
		}
	}
	return aFoundNodes;
}

function XMLHelper_FindChildrenByAttribute(oNode, sName, sValue, bDeepWalk, bGetOnlyFirstChild)
{
	// validate deep walk argument (must be "true" or "false")
	if (typeof bDeepWalk == "undefined" || (bDeepWalk != true && bDeepWalk != false))
	{
		bDeepWalk = true;
	}
	
	if (typeof sValue != "string" && typeof sValue != "number")
	{
		sValue = null;
	}
	else
	{
		sValue = sValue.toString();
	}
	
	var aFoundNodes = new Array();
	var oNodeChildren = oNode.childNodes;
	
	for (var idxChildNodes = 0; idxChildNodes < oNodeChildren.length; idxChildNodes++) 
	{
		var oChild = oNodeChildren[idxChildNodes];
		if (oChild.nodeType == 1)
		{
			var sNodeAttr = oChild.getAttribute(sName);
			if (sNodeAttr !== null)
			{
				if (sValue === null || sNodeAttr == sValue)
				{
					if (bGetOnlyFirstChild)
					{
						return new Array(oChild);
					}
					else
					{
						aFoundNodes[aFoundNodes.length] = oChild;
					}
				}
			}

			if (bDeepWalk === true) 
			{
				var oFoundChildren = XMLHelper_FindChildrenByAttribute(oChild, sName, sValue, bDeepWalk, bGetOnlyFirstChild);
				if (oFoundChildren.length > 0) 
				{
					if (bGetOnlyFirstChild)
					{
						if (oFoundChildren.length == 1)
						{
							return oFoundChildren;
						}
						else
						{
							return new Array(oFoundChildren[0]);
						}
					}
					else
					{
						aFoundNodes = aFoundNodes.concat(oFoundChildren);
					}
				}
			}
		}
	}
	return aFoundNodes;
}

/*
	Here are a few examples of how to use the XMLBuilder:
	
	First you need to create an XML Document.  This document INCLUDES the root node of the XML.
	var rootElement = XMLBuilderCreateXMLDocument("root", "");
	--> Here we're not specifying a default nameSpaceURI or documentType, these are optional

	*** We now have created the following XML:
	<root/>

	Next, you can either create a node in the document using the document element you created above
	var node1Element = rootElement.createElement("node1");
	
	OR you can create a element based on a namespace using the document element (rootElement in our case).
	var node1Element = XMLBuilderCreateElementNS("http://someNamespace", "node1", rootElement); 
	
	Next, you can set attributes on the element
	node1Element.setAttribute("attrX", "valY");
	
	OR you can set namespaces on the element (or both of course)
	XMLBuilderSetAttributeNodeNS(node1Element, "xmlns:xs", "http://www.w3.org/2001/XMLSchema", "");
	--> Here we don't need to define the final parameter as it's part of the xmlns namespace.

	Note that if you declare a namespaces on the element which is not an "xmlns", you need to specify the namespace URI of that namespace.
	XMLBuilderSetAttributeNodeNS(node1Element, "xsi:type", "SOAP-ENC:Array", "http://www.w3.org/2001/XMLSchema-instance");
	--> In this case, since we uses "xsi", instead of "xmlns", we had to provide the final parameter.
	--> Note that when declaring the final parameter, you don't need to define "xsi" as an xmlns, it's included for free

	Next, you need to append the child node to the document node.  Note that for children of the document element, 
	you need to append as a child of the "documentElement", not the rootElement.
	rootElement.documentElement.appendChild(node1Element);

	For every other element you create (other than document elements) you can just use the following.
	node1Element.appendChild(node2Element);

	*** We now have created the following XML:
	<root>
		<node1 attrX="valY" xmlns:xs="http://www.w3.org/2001/XMLSchema"
			xmlns:xsi="http://www.w2.org/2001/XMLSchema-instance" xsi:type="SOAP-ENC:Array"/>
	</root>
	--> Note that the xmlns for xsi is automatically included for free.
*/