text: // SpryXML.js - version 0.4 - Spry Pre-Release 1.6
  //
  // Copyright (c) 2006. Adobe Systems Incorporated.
  // All rights reserved.
  //
  // Redistribution and use in source and binary forms, with or without
  // modification, are permitted provided that the following conditions are met:
  //
  //   * Redistributions of source code must retain the above copyright notice,
  //     this list of conditions and the following disclaimer.
  //   * 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.
  //   * Neither the name of Adobe Systems Incorporated nor the names of its
  //     contributors may be used to endorse or promote products derived from this
  //     software without specific prior written permission.
  //
  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  // AND ANY EXPRESS 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.
  
  var Spry; if (!Spry) Spry = {}; if (!Spry.XML) Spry.XML = {}; if (!Spry.XML.Schema) Spry.XML.Schema = {};
  
  Spry.XML.Schema.Node = function(nodeName)
  {
  	this.nodeName = nodeName;
  	this.isAttribute = false;
  	this.appearsMoreThanOnce = false;
  	this.children = new Array;
  };
  
  Spry.XML.Schema.Node.prototype.toString = function (indentStr)
  {
  	if (!indentStr)
  		indentStr = "";
  
  	var str = indentStr + this.nodeName;
  
  	if (this.appearsMoreThanOnce)
  		str += " (+)";
  
  	str += "\n";
  
  	var newIndentStr = indentStr + "    ";
  
  	for (var $childName in this.children)
  	{
  		var child = this.children[$childName];
  		if (child.isAttribute)
  			str += newIndentStr + child.nodeName + "\n";
  		else
  			str += child.toString(newIndentStr);
  	}
  
  	return str;
  };
  
  Spry.XML.Schema.mapElementIntoSchemaNode = function(ele, schemaNode)
  {
  	if (!ele || !schemaNode)
  		return;
  
  	// Add all attributes as children to schemaNode!
  
  	var i = 0;
  	for (i = 0; i < ele.attributes.length; i++)
  	{
  		var attr = ele.attributes.item(i);
  		if (attr && attr.nodeType == 2 /* Node.ATTRIBUTE_NODE */)
  		{
  			var attrName = "@" + attr.name;
  
  			// We don't track the number of times an attribute appears
  			// in a given element so we only handle the case where the
  			// attribute doesn't already exist in the schemaNode.children array.
  
  			if (!schemaNode.children[attrName])
  			{
  				var attrObj = new Spry.XML.Schema.Node(attrName);
  				attrObj.isAttribute = true;
  				schemaNode.children[attrName] = attrObj;
  			}
  		}
  	}
  
  	// Now add all of element's element children as children of schemaNode!
  
  	var child = ele.firstChild;
  	var namesSeenSoFar = new Array;
    
  	while (child)
  	{
  		if (child.nodeType == 1 /* Node.ELEMENT_NODE */)
  		{
  			var childSchemaNode = schemaNode.children[child.nodeName];
  
  			if (!childSchemaNode)
  			{
  				childSchemaNode = new Spry.XML.Schema.Node(child.nodeName);
  				if (childSchemaNode)
  					schemaNode.children[child.nodeName] = childSchemaNode;
  			}
  
  			if (childSchemaNode)
  			{
  				if (namesSeenSoFar[childSchemaNode.nodeName])
  					childSchemaNode.appearsMoreThanOnce = true;
  				else
  					namesSeenSoFar[childSchemaNode.nodeName] = true;
  			}
  
  			Spry.XML.Schema.mapElementIntoSchemaNode(child, childSchemaNode);
  		}
  
  		child = child.nextSibling;
  	} 
  };
  
  Spry.XML.getSchemaForElement = function(ele)
  {
  	if (!ele)
  		return null;
  
  	schemaNode = new Spry.XML.Schema.Node(ele.nodeName);
  	Spry.XML.Schema.mapElementIntoSchemaNode(ele, schemaNode);
  
  	return schemaNode;
  };
  
  Spry.XML.getSchema = function(xmlDoc)
  {
  	if (!xmlDoc)
  		return null;
  
  	// Find the first element in the document that doesn't start with "xml".
  	// According to the XML spec tags with names that start with "xml" are reserved
  	// for future use.
  
  	var node = xmlDoc.firstChild;
  
  	while (node)
  	{
  		if (node.nodeType == 1 /* Node.ELEMENT_NODE */)
  			break;
  
  		node = node.nextSibling;
  	}
  
  	return Spry.XML.getSchemaForElement(node);
  };
  
  Spry.XML.nodeHasValue = function(node)
  {
  	if (node)
  	{
  		var child = node.firstChild;
  		if (child && child.nextSibling == null && (child.nodeType == 3 /* Node.TEXT_NODE */ || child.nodeType == 4 /* CDATA_SECTION_NODE */))
  			return true;
  	}
  	return false;
  };
  
  Spry.XML.XObject = function()
  {
  };
  
  Spry.XML.XObject.prototype._value = function()
  {
  	var val = this["#text"];
  	if (val != undefined)
  		return val;
  	return this["#cdata-section"];
  };
  
  Spry.XML.XObject.prototype._hasValue = function()
  {
  	return this._value() != undefined;
  };
  
  Spry.XML.XObject.prototype._valueIsText = function()
  {
  	return this["#text"] != undefined;
  };
  
  Spry.XML.XObject.prototype._valueIsCData = function()
  {
  	return this["#cdata-section"] != undefined;
  };
  
  Spry.XML.XObject.prototype._propertyIsArray = function(prop)
  {
  	var val = this[prop];
  	if (val == undefined)
  		return false;
  	return (typeof val == "object" && val.constructor == Array);
  };
  
  Spry.XML.XObject.prototype._getPropertyAsArray = function(prop)
  {
  	var arr = [];
  	var val = this[prop];
  	if (val != undefined)
  	{
  		if (typeof val == "object" && val.constructor == Array)
  			return val;
  		arr.push(val);
  	}
  	return arr;
  };
  
  Spry.XML.XObject.prototype._getProperties = function()
  {
  	var props = [];
  	for (var p in this)
  	{
  		if (!/^_/.test(p))
  			props.push(p);
  	}
  	return props;
  };
  
  Spry.XML.nodeToObject = function(node)
  {
  	if (!node)
  		return null;
  
  	var obj = new Spry.XML.XObject();
  
  	// Add all attributes as properties of the object.
  
  	for (var i = 0; i < node.attributes.length; i++)
  	{
  		var attr = node.attributes[i];
  		var attrName = "@" + attr.name;
  		obj[attrName] = attr.value;
  	}
  
  	var child;
  
  	if (Spry.XML.nodeHasValue(node))
  	{	
  		try
  		{
  			child = node.firstChild;
  
  			if (child.nodeType == 3 /* TEXT_NODE */)
  				obj[child.nodeName] = Spry.Utils.encodeEntities(child.data);
  			else if (child.nodeType == 4 /* CDATA_SECTION_NODE */)
  				obj[child.nodeName] = child.data;
  		} catch (e) { Spry.Debug.reportError("Spry.XML.nodeToObject() exception caught: " + e + "\n"); }
  	}
  	else
  	{
  		// The node has no value, so run through any element children
  		// it may have and add them as properties.
  
  		child = node.firstChild;
  		while (child)
  		{
  			if (child.nodeType == 1 /* Node.ELEMENT_NODE */)
  			{
  				var isArray = false;
  				var tagName = child.nodeName;
  				if (obj[tagName])
  				{
  					if (obj[tagName].constructor != Array)
  					{
  						var curValue = obj[tagName];
  						obj[tagName] = new Array;
  						obj[tagName].push(curValue);
  					}
  					isArray = true;
  				}
  
  				var childObj = Spry.XML.nodeToObject(child);
  				
  				if (isArray)
  					obj[tagName].push(childObj);
  				else
  					obj[tagName] = childObj;
  			}
  			child = child.nextSibling;
  		}
  	}
  	return obj;
  };
  
  // Spry.XML.documentToObject - Utility method for creating a
  // JavaScript object with properties and nested objects that
  // mirror an XML document tree structure.
  //
  // Sample XML:
  //
  //		<employees>
  //			<employee id="1000">
  //				<name>John Doe</name>
  //			</employee>
  //			<employee id="2000">
  //				<name><![CDATA[Jane Smith]]></name>
  //			</employee>
  //		</employees>
  //
  // Object returned by documentToObject():
  //
  //		{
  //			employees:
  //				{
  //					employee:
  //						[
  //							{
  //								@id: "1000",
  //								name: { "#text": "John Doe" }
  //							},
  //							{
  //								@id: "2000",
  //								name: { "#cdata-section": "Jane Smith" }
  //							}
  //						]
  //				}
  //		}
  
  Spry.XML.documentToObject = function(xmlDoc)
  {
  	var obj = null;
  	if (xmlDoc && xmlDoc.firstChild)
  	{
  		var child = xmlDoc.firstChild;
  		while (child)
  		{
  			if (child.nodeType == 1 /* Node.ELEMENT_NODE */)
  			{
  				obj = new Spry.XML.XObject();
  				obj[child.nodeName] = Spry.XML.nodeToObject(child);
  				break;
  			}
  			child = child.nextSibling;
  		}
  	}
  	return obj;
  };
  