
// 
// XmlNode.js
// 
// A JavaScript object that provides methods and attributes for an XML element/node.
// This object is used primarily with XmlParser.js.
// 
// 
// Copyright (C) 2003-2005 Kody Brown. All Rights Reserved.
// 
// This is my own personal project maintained by me and is not owned nor supported by my employer, 
// my previous employer(s), or anyone else. This code and/or application is presented as is, without 
// expressed or implied warranty of any kind. Use it at your own risk. Period. By downloading and/or 
// using any file or information from this web-site (http://www.wasatchwizard.com/) you agree to be 
// held to these terms.
// 
// You are given rights to use this code/application for personal and/or professional use provided 
// that the copyright and this notice remains included and unchanged.
// 
// 


function XmlNode(name_or_xml, text)
{
//alert("XmlNode('" + name_or_xml + "', '" + text + "')");
// member variables
	this.name = "";
	this.innerText = "";
	this.outerText = "";
	this.innerHTML = "";
	this.outerHTML = "";

	this.children = new Array();
	this.attributes = new Array();

// member functions
	this.setOuterHTML = XmlNode_SetOuterHTML;

// initialization
	if (undefined != name_or_xml)
	{
		if (name_or_xml.substring(0, 1) == "<")
		{
			this.setOuterHTML(name_or_xml);
		}
		else
		{
			this.name = (undefined != name_or_xml) ? name_or_xml : "";
			this.innerText = (undefined != text) ? text : "";
		}
	}

	return this;
}

function XmlNode_SetOuterHTML(xml)
{
	// 
	// parse the XML stream here and populate attributes and children
	// 

	this.name = "";
	this.innerText = "";
	this.outerText = xml;
	this.innerHTML = "";
	this.outerHTML = xml;
	this.innerXML = "";
	this.outerXML = xml;

	this.children = new Array();
	this.attributes = new Array();

	// 
	// 
	// 

	//alert('XmlNode_SetOuterHTML\n\n' + xml);

	var tempPos = -1;
	var startTag = "";
	var startPos = -1;
	var endPos = -1;
	var endTag = "";

	var temp = "";
	var subxml = "";
	var tagCount = 0;

	var re;
	var ar;
		
	var nextChar = "";
	var next2ndChar = "";

	tempPos = xml.indexOf("<");
	if (tempPos > -1)
	{
		xml = xml.substring(tempPos + 1);
		//alert(xml);
		
		var re = new RegExp(/\s|\/|>/); // looks for: ' ' | / | >
		var ar = re.exec(xml);
		if (null == ar)
		{
			alert("XML is not well-formed");
			return;
		}
		//alert("ar.index = " + ar.index);
		this.name = xml.substring(0, ar.index);
		//alert("this.name = " + this.name);
		xml = trimAll(xml.substring(ar.index));
		//alert("xml = " + xml);
		
		re = new RegExp(/\/>|>|\?>/); // looks for: /> | > | ?>
		ar = re.exec(xml);
		if (null == ar)
		{
			alert("XML is not well-formed");
			return;
		}
		//alert("ar[0] = " + ar[0]);
		var attribs = xml.substring(0, ar.index);
		//alert("attribs = " + attribs);
		
		if (attribs.length > 0)
		{
			// the tag has attributes
			this.attributes = ParseAttributes(attribs);
			//alert("this.attributes.length = " + this.attributes.length);
		}
		if (ar[0] == "?>" || ar[0] == "/>")
			endPos = 2;
		else
			endPos = 1;
		xml = xml.substring(ar.index + endPos);
		//alert("xml = " + xml);
		
		if ("?xml" == this.name)
			endPos = xml.length;
		else if (ar[0] != "/>")
			endPos = xml.indexOf("</" + this.name + ">");
		else
			endPos = xml.length;
			
		if (ar[0] != "/>" && -1 == endPos)
		{
			alert("XML is not well-formed: missing end tag '</" + this.name + ">'");
			return;
		}
		xml = xml.substring(0, endPos);
		//alert("xml = " + xml);
		
		// 
		// xml now has only the innerHTML content
		// 
		this.innerText = xml;
		this.innerHTML = xml;
		this.innerXML = xml;
		
		
		if (ar[0] != "/>")
		{
			while (xml.length > 0)
			{
				if ("?xml" == this.name && tagCount > 0)
				{
					alert("XML is not well-formed: only one document root element is allowed");
					return;
				}
				
				tempPos = xml.indexOf("<");
				if (tempPos > -1)
				{
					if (tempPos != 0)
					{
						//alert("adjusting tempPos");
						xml = xml.substring(tempPos);
						tempPos = 0;
					}
					//alert(tempPos);
					//alert("xml = " + xml);
					
					re = new RegExp(/<\!--|<\!\[CDATA\[/);
					ar = re.exec(xml);
					//alert("ar.index = " + ar.index + "\n\nxml = " + xml);
					if (null != ar && 0 == ar.index)
					{
						//alert("found a " + ar[0]);
						if (ar[0] == "<!--")
							endTag = "-->";
						else 
							endTag = "]]>";
						endPos = xml.indexOf(endTag);
						//alert(endPos);
						if (-1 == endPos)
						{
							alert("XML is not well-formed: missing closing tag '" + endTag + "'");
							return;
						}
						
						temp = trimAll(xml.substring(ar[0].length, endPos));
						//temp = xml
						//alert(temp);
						
						var subnode = new XmlNode(ar[0] == "<!--" ? "#comment" : "#cdata", temp);
						this.children[this.children.length] = subnode;
						
						endPos += endTag.length;
						xml = trimAll(xml.substring(endPos));
						//alert(xml);
					}
					else
					{
						// didn't find a <!-- or <![CDATA[ 
						re = new RegExp(/\W/);
						ar = re.exec(xml.substring(1));
						if (null == ar)
						{
							alert("XML is not well-formed: missing closing wicket '>' in '" + xml.substring(0, 25) + " ...'");
							return;
						}
						//alert("ar[0] = " + ar[0]);
						tag = xml.substring(1, ar.index + 1);
						//alert("tag = " + tag);
						
						endTag = "</" + tag + ">";
						endPos = xml.indexOf(endTag);
						if (-1 == endPos)
						{
							endTag = "/>";
							endPos = xml.indexOf(endTag);
						}
						//alert(tag + ", " + ar[0] + ", " + endPos + "\n\n" + xml);
						
						if (-1 == endPos)
						{
							alert("XML is not well-formed: missing closing tag '</" + tag + ">'");
							return;
						}
						endPos += endTag.length;
						subxml = xml.substring(0, endPos);
						//alert("subxml = " + subxml);
					
						var subnode = new XmlNode(subxml);
						this.children[this.children.length] = subnode;
						
						xml = trimAll(xml.substring(endPos));
					}
						
					tagCount++;
				}
				else
				{
					// 
					// text / content 
					// 
					re = new RegExp(/</);
					ar = re.exec(xml);
					if (null != ar)
					{
						endPos = ar.index;
					}
					else
					{
						endPos = xml.length;
					}
					temp = xml.substring(0, endPos);
					//alert(temp);
									
					var subnode = new XmlNode("#ctext", temp);
					this.children[this.children.length] = subnode;
					
					xml = trimAll(xml.substring(endPos));
					//alert(xml);
				}
			}
		}
	}
	else
	{
		// not sure how I could ever get here..
		alert("unknown error #1\n\nxml = \n" + xml);
	}
	
	return;
}

function NodeAttribute(name, value)
{
	this.name = name;
	this.value = value;
}


function ParseAttributes(attribstr)
{
	var attributes = new Array();
	
	var i = -1; // start position in string
	var j = -1; // end position in string
	var name = "";
	var value = "";

	while (j < attribstr.length)
	{
		i = attribstr.indexOf("=", j);
		if (i > -1)
		{
			name = trim(attribstr.substring(j, i));
			i += 2; // increment past the first "
			
			j = attribstr.indexOf("\"", i);
			value = attribstr.substring(i, j);
			i = j++;
			
			//alert("name = " + name + "\nvalue = " + value);
			attributes[attributes.length] = new NodeAttribute(name, value);
		}
		else
		{
			break;
		}
	}
	
	return attributes;
}





//function evalIt()
//{
//	var root = parseXml(document.getElementById('_xml').value);
//	var text = document.getElementById('evaltext').value;
//	eval("alert(" + text + ");");
//}

//function parseIt()
//{
//	var root = parseXml(document.getElementById('_xml').value);
//	alert("------------------ COMPLETED PARSING ------------------ ");
//	alert(displayNode(root, 0));
//}

function displayNode(node, indent)
{
	//alert('displayNode()');
	var msg = "";
	var tabs = ""; for (var i=0; i<indent; i++) { tabs += "\t"; }
	
	if ("#comment" == node.name)
	{
		msg += "<!-- " + node.innerText + " -->\n";
	}
	else if ("#cdata" == node.name)
	{
		msg += "<![CDATA[ " + node.innerText + " ]]>\n";
	}
	else if ("#ctext" == node.name)
	{
		msg += node.innerText + "\n";
	}
	else
	{
		msg += tabs + "<" + node.name;
	
		// attributes
		if (null != node["attributes"] && node["attributes"].length > 0)
		{
			for (var i=0; i<node["attributes"].length; i++)
			{
				msg += tabs + " " + node["attributes"][i].name + "=\"" + node["attributes"][i].value + "\"";
			}
		}
		if (node.name == "?xml")
		{
			msg += "?";
		}
		msg += ">\n";
	
		// children
		if (null != node["children"] && node["children"].length > 0)
		{
			for (var i=0; i<node["children"].length; i++)
			{
				msg += displayNode(node["children"][i], indent + 1);
			}
		}

		if (node.name != "?xml")
		{
			msg += tabs + "</" + node.name + ">\n";
		}
	}
	
	return msg;
}


