/*
License: GNU Lesser General Public License (http://www.gnu.org/licenses/lgpl.html)
Copyright (C) 2005 tagnetic.org. Please do not remove this copyright/license comment.
*/
//*************************************************************************
//Start cross-browser extras useBrowserExtras
//*************************************************************************
/**
* Defines a DOMParser class that is usable in Mozilla, Safari, IE and Opera.
* Taken directly from: http://erik.eae.net/archives/2005/07/03/20.19.18/
*
* Example usage:
* var parser = new DOMParser();
* var xmlDom = parser.parseFromString(xmlString);
*
* Then you can use the normal DOM methods to work with the XML.
*/
if (typeof DOMParser == "undefined")
{
DOMParser = function () {};
DOMParser.prototype.parseFromString = function (str, contentType)
{
if (typeof ActiveXObject != "undefined")
{
var d = new ActiveXObject("MSXML.DomDocument");
d.loadXML(str);
return d;
}
else if (typeof XMLHttpRequest != "undefined")
{
var req = new XMLHttpRequest;
req.open("GET", "data:" + (contentType || "application/xml") +
";charset=utf-8," + encodeURIComponent(str), false);
if (req.overrideMimeType)
req.overrideMimeType(contentType);
req.send(null);
return req.responseXML;
}
}
}
/**
* Adds an Array.filter method if it doesn't already exist.
* Taken directly from:
* http://erik.eae.net/playground/arrayextras/
* Also see Erik's blog post:
* http://erik.eae.net/archives/2005/06/05/17.53.19/
*/
//http://developer-test.mozilla.org/docs/Core_JavaScript_1.5_Reference:Objects:Array:filter
if (!Array.prototype.filter)
{
Array.prototype.filter = function (f, obj)
{
var l = this.length; // must be fixed during loop... see docs
var res = [];
for (var i = 0; i < l; i++)
{
if (f.call(obj, this[i], i, this))
res.push(this[i]);
}
return res;
};
}
//*************************************************************************
//End cross-browser extras
//*************************************************************************
//*************************************************************************
//Start SimpleDom useSimpleDom
//*************************************************************************
SDom =
{
//*************************************************************************
/**
* Factory for creating a simple dom object.
*
* @param {Object} domNode: A DOM Node. Can be a DOM Document.
*
* @param {String} rootElement: If domNode is a DOM Document,
* specify a root element to use as the first SimpleDom object.
*
* For the following XML document:
*
*
* A SimpleDom object will have
* the following structure:
* node._sdNode = true. Use to find out if this is a SimpleDom node.
* node._n = String. The node name (typically element name).
* node._v = String .If there was only one child for this node, and that node
* was a text or cdata node. If there are more than one text/cdata nodes,
* or text/cdata nodes mixed with element nodes, this property will not
* be defined.
* node._a = a hashmap object of attributes. For instance, use node._a['foo']
* or nod._a.foo to access the foo attribute of a node.
* node._c = an array of the child elements, in the order that they appear in the
* XML. If there were no children, or if the only child was a text/cdata
* node, then this property will not be defined.
*
* In addition to child elements being stored in the _c array, child elements are
* also stored off the node by their nodeName. So, if there was a
* element under the node, it could be accessed by:
*
* node.phonenumber or node['phonenumber']
*
* If there are more than one child element with the same name (for instance,
* more than one element), then that name will be an array:
*
* node.phonenumber.length would give the length of the array.
*
* To find out if a named member of a node is just a SimpleDom object or an array
* of SimpleDomObjects, the ._n and ._v properties will be undefined for an array.
* Also, there will be no ._sdNode = true property.
* So for the phonenumber case where it is an array,
*
* node.phonenumber._n or node.phonenumber._v or node._sdNode
*
* will be undefined. You could use a:
*
* typeof(node.phonenumber._sdNode) == 'undefined')
* or
* !node.phonenumber._sdNode
*
* check to see if it is an array. Testing for node.phonenumber.length is not
* advised, since length could be used as the name of an element in the DOM.
*
* Notes:
* - It may not work so well with XML elements that start with an underscore (_).
* - It does not support names spaces officially. Whatever is returned
* by the DOM property node.nodeName is used as the name of the node.
* - It is mostly useful for creating a read-only JavaScript structure.
* There is a SDom.serialize that will convert a SimpleDom structure to
* an XML string, but that XML is not guaranteed to be equivalent to
* the XML that was used to create the SimpleDom.
* - Only Element, CDATA and Text nodes are processed.
*/
create: function(domNode, rootElementName)
{
var result = null;
//If this is a document node, find root element and start with that.
if (domNode.nodeType == 9)
{
if (rootElementName)
{
var roots = domNode.getElementsByTagName(rootElementName);
if (roots && roots[0])
result = new SDom._construct(roots[0]);
}
}
return result;
},
//*************************************************************************
/**
* Class method to convert a SimpleDom to a string. Does not guarantee that
* the string output will match the XML string that was used to create a
* SimpleDom.
*/
serialize: function(simpleDom)
{
var result = '';
if (!simpleDom)
return result;
//If the simpleDom is an array, then call the serialize method for each
//item in the array.
if (simpleDom instanceof Array)
{
for (var index = 0; index < simpleDom.length; index++)
result += SDom.serialize(simpleDom[index]);
return result;
}
//If not an array, Make sure it is a simple dom node.
//If not, it must be a string (or something that can convert
//properly to a string).
if (!simpleDom._sdNode)
return simpleDom;
//Get the attributes
var attributes = '';
if (simpleDom._a)
{
for (var param in simpleDom._a)
attributes += ' ' + param + '="' + simpleDom._a[param] + '"';
}
//If no value or children, empty node.
if (typeof(simpleDom._v) == 'undefined' && typeof(simpleDom._c) == 'undefined')
return '<' + simpleDom._n + attributes + ' />';
//Make the start tag
result = '<' + simpleDom._n + attributes + '>';
if (simpleDom._v)
result += simpleDom._v;
else if (simpleDom._c)
{
result += SDom.serialize(simpleDom._c);
}
result += '' + simpleDom._n + '>';
return result;
},
//*************************************************************************
//*************************************************************************
//*************************************************************************
/**
* @private Constructor for creating a simple dom object.
* Do not call this method directly.
* Use the factory method SDom.create() instead.
*/
_construct: function(domNode, rootElementName)
{
//Set a property indicating this object is a simple dom node.
//Some of things attached in a simple dom structure will just
//be strings (for text and cdata nodes).
this._sdNode = true;
this._n = domNode.nodeName;
//Get the attributes
var attributes = domNode.attributes;
var i, node;
if (attributes && attributes.length > 0)
{
this._a = new Object();
for (i = 0; i < attributes.length; i++)
{
node = attributes.item(i);
this._a[node.nodeName] = node.nodeValue;
}
}
//Get the children
var childIndex = 0;
var namedChildList;
var children = domNode.childNodes;
if (children && children.length > 0)
{
//Find out if there is only one child and if it is a text node (3) or cdata node (4).
if (children.length == 1 && (children[0].nodeType == 3 || children[0].nodeType == 4))
{
this._v = children[0].nodeValue;
}
else
{
this._c = new Array();
//Iterate through children and add to the dom.
for (i = 0; i < children.length; i++)
{
node = children[i];
//Only handling text, cdata and other elements right now.
switch(node.nodeType)
{
case 1: //Element
this._c[childIndex] = new SDom._construct(node);
//Also add the child as a named property of the node.
if (!this[node.nodeName])
this[node.nodeName] = this._c[childIndex];
else
{
if (this[node.nodeName]._sdNode)
{
//If the named property is a sdom node already,
//convert the named property into an array.
var temp = this[node.nodeName];
this[node.nodeName] = new Array();
this[node.nodeName].push(temp);
}
this[node.nodeName].push(this._c[childIndex]);
}
childIndex++;
break;
case 3: //Text Node
case 4: //CData Node
this._c[childIndex] = node.nodeValue;
childIndex++;
break;
}
}
}
}
}
}
//*************************************************************************
//End SimpleDom
//*************************************************************************