blob: 895a4c7b2170185a33e144e71e85b65d3ff15549 [file] [log] [blame]
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// xml.js
(function (window, undefined) {
var datajs = window.datajs;
var activeXObject = datajs.activeXObject;
var djsassert = datajs.djsassert;
var extend = datajs.extend;
var isArray = datajs.isArray;
var isObject = datajs.isObject;
var normalizeURI = datajs.normalizeURI;
// CONTENT START
// URI prefixes to generate smaller code.
var http = "http://";
var w3org = http + "www.w3.org/"; // http://www.w3.org/
var xhtmlNS = w3org + "1999/xhtml"; // http://www.w3.org/1999/xhtml
var xmlnsNS = w3org + "2000/xmlns/"; // http://www.w3.org/2000/xmlns/
var xmlNS = w3org + "XML/1998/namespace"; // http://www.w3.org/XML/1998/namespace
var mozillaParserErroNS = http + "www.mozilla.org/newlayout/xml/parsererror.xml";
var hasLeadingOrTrailingWhitespace = function (text) {
/// <summary>Checks whether the specified string has leading or trailing spaces.</summary>
/// <param name="text" type="String">String to check.</param>
/// <returns type="Boolean">true if text has any leading or trailing whitespace; false otherwise.</returns>
var re = /(^\s)|(\s$)/;
return re.test(text);
};
var isWhitespace = function (text) {
/// <summary>Determines whether the specified text is empty or whitespace.</summary>
/// <param name="text" type="String">Value to inspect.</param>
/// <returns type="Boolean">true if the text value is empty or all whitespace; false otherwise.</returns>
var ws = /^\s*$/;
return text === null || ws.test(text);
};
var isWhitespacePreserveContext = function (domElement) {
/// <summary>Determines whether the specified element has xml:space='preserve' applied.</summary>
/// <param name="domElement">Element to inspect.</param>
/// <returns type="Boolean">Whether xml:space='preserve' is in effect.</returns>
while (domElement !== null && domElement.nodeType === 1) {
var val = xmlAttributeValue(domElement, "space", xmlNS);
if (val === "preserve") {
return true;
} else if (val === "default") {
break;
} else {
domElement = domElement.parentNode;
}
}
return false;
};
var isXmlNSDeclaration = function (domAttribute) {
/// <summary>Determines whether the attribute is a XML namespace declaration.</summary>
/// <param name="domAttribute">Element to inspect.</param>
/// <returns type="Boolean">
/// True if the attribute is a namespace declaration (its name is 'xmlns' or starts with 'xmlns:'; false otherwise.
/// </returns>
var nodeName = domAttribute.nodeName;
return nodeName == "xmlns" || nodeName.indexOf("xmlns:") === 0;
};
var safeSetProperty = function (obj, name, value) {
/// <summary>Safely set as property in an object by invoking obj.setProperty.</summary>
/// <param name="obj">Object that exposes a setProperty method.</param>
/// <param name="name" type="String" mayBeNull="false">Property name.</param>
/// <param name="value">Property value.</param>
try {
obj.setProperty(name, value);
} catch (_) { }
};
var msXmlDom3 = function () {
/// <summary>Creates an configures new MSXML 3.0 ActiveX object.</summary>
/// <remakrs>
/// This function throws any exception that occurs during the creation
/// of the MSXML 3.0 ActiveX object.
/// <returns type="Object">New MSXML 3.0 ActiveX object.</returns>
var msxml3 = activeXObject("Msxml2.DOMDocument.3.0");
if (msxml3) {
safeSetProperty(msxml3, "ProhibitDTD", true);
safeSetProperty(msxml3, "MaxElementDepth", 256);
safeSetProperty(msxml3, "AllowDocumentFunction", false);
safeSetProperty(msxml3, "AllowXsltScript", false);
}
return msxml3;
};
var msXmlDom = function () {
/// <summary>Creates an configures new MSXML 6.0 or MSXML 3.0 ActiveX object.</summary>
/// <remakrs>
/// This function will try to create a new MSXML 6.0 ActiveX object. If it fails then
/// it will fallback to create a new MSXML 3.0 ActiveX object. Any exception that
/// happens during the creation of the MSXML 6.0 will be handled by the function while
/// the ones that happend during the creation of the MSXML 3.0 will be thrown.
/// <returns type="Object">New MSXML 3.0 ActiveX object.</returns>
try {
var msxml = activeXObject("Msxml2.DOMDocument.6.0");
if (msxml) {
msxml.async = true;
}
return msxml;
} catch (_) {
return msXmlDom3();
}
};
var msXmlParse = function (text) {
/// <summary>Parses an XML string using the MSXML DOM.</summary>
/// <remakrs>
/// This function throws any exception that occurs during the creation
/// of the MSXML ActiveX object. It also will throw an exception
/// in case of a parsing error.
/// <returns type="Object">New MSXML DOMDocument node representing the parsed XML string.</returns>
var dom = msXmlDom();
if (!dom) {
return null;
}
dom.loadXML(text);
var parseError = dom.parseError;
if (parseError.errorCode !== 0) {
xmlThrowParserError(parseError.reason, parseError.srcText, text);
}
return dom;
};
var xmlThrowParserError = function (exceptionOrReason, srcText, errorXmlText) {
/// <summary>Throws a new exception containing XML parsing error information.</summary>
/// <param name="exceptionOrReason">
/// String indicatin the reason of the parsing failure or
/// Object detailing the parsing error.
/// </param>
/// <param name="srcText" type="String">
/// String indicating the part of the XML string that caused the parsing error.
/// </param>
/// <param name="errorXmlText" type="String">XML string for wich the parsing failed.</param>
if (typeof exceptionOrReason === "string") {
exceptionOrReason = { message: exceptionOrReason };
}
throw extend(exceptionOrReason, { srcText: srcText || "", errorXmlText: errorXmlText || "" });
};
var xmlParse = function (text) {
/// <summary>Returns an XML DOM document from the specified text.</summary>
/// <param name="text" type="String">Document text.</param>
/// <returns>XML DOM document.</returns>
/// <remarks>This function will throw an exception in case of a parse error.</remarks>
var domParser = window.DOMParser && new window.DOMParser();
var dom;
if (!domParser) {
dom = msXmlParse(text);
if (!dom) {
xmlThrowParserError("XML DOM parser not supported");
}
return dom;
}
try {
dom = domParser.parseFromString(text, "text/xml");
} catch (e) {
xmlThrowParserError(e, "", text);
}
var element = dom.documentElement;
var nsURI = element.namespaceURI;
var localName = xmlLocalName(element);
// Firefox reports errors by returing the DOM for an xml document describing the problem.
if (localName === "parsererror" && nsURI === mozillaParserErroNS) {
var srcTextElement = xmlFirstChildElement(element, mozillaParserErroNS, "sourcetext");
var srcText = srcTextElement ? xmlNodeValue(srcTextElement) : "";
xmlThrowParserError(xmlInnerText(element) || "", srcText, text);
}
// Chrome (and maybe other webkit based browsers) report errors by injecting a header with an error message.
// The error may be localized, so instead we simply check for a header as the
// top element or descendant child of the document.
if (localName === "h3" && nsURI === xhtmlNS || xmlFirstDescendantElement(element, xhtmlNS, "h3")) {
var reason = "";
var siblings = [];
var cursor = element.firstChild;
while (cursor) {
if (cursor.nodeType === 1) {
reason += xmlInnerText(cursor) || "";
}
siblings.push(cursor.nextSibling);
cursor = cursor.firstChild || siblings.shift();
}
reason += xmlInnerText(element) || "";
xmlThrowParserError(reason, "", text);
}
return dom;
};
var xmlQualifiedName = function (prefix, name) {
/// <summary>Builds a XML qualified name string in the form of "prefix:name".</summary>
/// <param name="prefix" type="String" maybeNull="true">Prefix string.</param>
/// <param name="name" type="String">Name string to qualify with the prefix.</param>
/// <returns type="String">Qualified name.</returns>
return prefix ? prefix + ":" + name : name;
};
var xmlAppendText = function (domNode, textNode) {
/// <summary>Appends a text node into the specified DOM element node.</summary>
/// <param name="domNode">DOM node for the element.</param>
/// <param name="text" type="String" mayBeNull="false">Text to append as a child of element.</param>
if (hasLeadingOrTrailingWhitespace(textNode.data)) {
var attr = xmlAttributeNode(domNode, xmlNS, "space");
if (!attr) {
attr = xmlNewAttribute(domNode.ownerDocument, xmlNS, xmlQualifiedName("xml", "space"));
xmlAppendChild(domNode, attr);
}
attr.value = "preserve";
}
domNode.appendChild(textNode);
return domNode;
};
var xmlAttributes = function (element, onAttributeCallback) {
/// <summary>Iterates through the XML element's attributes and invokes the callback function for each one.</summary>
/// <param name="element">Wrapped element to iterate over.</param>
/// <param name="onAttributeCallback" type="Function">Callback function to invoke with wrapped attribute nodes.</param>
var attributes = element.attributes;
var i, len;
for (i = 0, len = attributes.length; i < len; i++) {
onAttributeCallback(attributes.item(i));
}
};
var xmlAttributeValue = function (domNode, localName, nsURI) {
/// <summary>Returns the value of a DOM element's attribute.</summary>
/// <param name="domNode">DOM node for the owning element.</param>
/// <param name="localName" type="String">Local name of the attribute.</param>
/// <param name="nsURI" type="String">Namespace URI of the attribute.</param>
/// <returns type="String" maybeNull="true">The attribute value, null if not found.</returns>
var attribute = xmlAttributeNode(domNode, localName, nsURI);
return attribute ? xmlNodeValue(attribute) : null;
};
var xmlAttributeNode = function (domNode, localName, nsURI) {
/// <summary>Gets an attribute node from a DOM element.</summary>
/// <param name="domNode">DOM node for the owning element.</param>
/// <param name="localName" type="String">Local name of the attribute.</param>
/// <param name="nsURI" type="String">Namespace URI of the attribute.</param>
/// <returns>The attribute node, null if not found.</returns>
var attributes = domNode.attributes;
if (attributes.getNamedItemNS) {
return attributes.getNamedItemNS(nsURI || null, localName);
}
return attributes.getQualifiedItem(localName, nsURI) || null;
};
var xmlBaseURI = function (domNode, baseURI) {
/// <summary>Gets the value of the xml:base attribute on the specified element.</summary>
/// <param name="domNode">Element to get xml:base attribute value from.</param>
/// <param name="baseURI" mayBeNull="true" optional="true">Base URI used to normalize the value of the xml:base attribute.</param>
/// <returns type="String">Value of the xml:base attribute if found; the baseURI or null otherwise.</returns>
var base = xmlAttributeNode(domNode, "base", xmlNS);
return (base ? normalizeURI(base.value, baseURI) : baseURI) || null;
};
var xmlChildElements = function (domNode, onElementCallback) {
/// <summary>Iterates through the XML element's child DOM elements and invokes the callback function for each one.</summary>
/// <param name="element">DOM Node containing the DOM elements to iterate over.</param>
/// <param name="onElementCallback" type="Function">Callback function to invoke for each child DOM element.</param>
xmlTraverse(domNode, /*recursive*/false, function (child) {
if (child.nodeType === 1) {
onElementCallback(child);
}
// continue traversing.
return true;
});
};
var xmlFindElementByPath = function (root, namespaceURI, path) {
/// <summary>Gets the descendant element under root that corresponds to the specified path and namespace URI.</summary>
/// <param name="root">DOM element node from which to get the descendant element.</param>
/// <param name="namespaceURI" type="String">The namespace URI of the element to match.</param>
/// <param name="path" type="String">Path to the desired descendant element.</param>
/// <returns>The element specified by path and namespace URI.</returns>
/// <remarks>
/// All the elements in the path are matched against namespaceURI.
/// The function will stop searching on the first element that doesn't match the namespace and the path.
/// </remarks>
var parts = path.split("/");
var i, len;
for (i = 0, len = parts.length; i < len; i++) {
root = root && xmlFirstChildElement(root, namespaceURI, parts[i]);
}
return root || null;
};
var xmlFindNodeByPath = function (root, namespaceURI, path) {
/// <summary>Gets the DOM element or DOM attribute node under root that corresponds to the specified path and namespace URI.</summary>
/// <param name="root">DOM element node from which to get the descendant node.</param>
/// <param name="namespaceURI" type="String">The namespace URI of the node to match.</param>
/// <param name="path" type="String">Path to the desired descendant node.</param>
/// <returns>The node specified by path and namespace URI.</returns>
/// <remarks>
/// This function will traverse the path and match each node associated to a path segement against the namespace URI.
/// The traversal stops when the whole path has been exahusted or a node that doesn't belogong the specified namespace is encountered.
///
/// The last segment of the path may be decorated with a starting @ character to indicate that the desired node is a DOM attribute.
/// </remarks>
var lastSegmentStart = path.lastIndexOf("/");
var nodePath = path.substring(lastSegmentStart + 1);
var parentPath = path.substring(0, lastSegmentStart);
var node = parentPath ? xmlFindElementByPath(root, namespaceURI, parentPath) : root;
if (node) {
if (nodePath.charAt(0) === "@") {
return xmlAttributeNode(node, nodePath.substring(1), namespaceURI);
}
return xmlFirstChildElement(node, namespaceURI, nodePath);
}
return null;
};
var xmlFirstChildElement = function (domNode, namespaceURI, localName) {
/// <summary>Returns the first child DOM element under the specified DOM node that matches the specified namespace URI and local name.</summary>
/// <param name="domNode">DOM node from which the child DOM element is going to be retrieved.</param>
/// <param name="namespaceURI" type="String" optional="true">The namespace URI of the element to match.</param>
/// <param name="localName" type="String" optional="true">Name of the element to match.</param>
/// <returns>The node's first child DOM element that matches the specified namespace URI and local name; null otherwise.</returns>
return xmlFirstElementMaybeRecursive(domNode, namespaceURI, localName, /*recursive*/false);
};
var xmlFirstDescendantElement = function (domNode, namespaceURI, localName) {
/// <summary>Returns the first descendant DOM element under the specified DOM node that matches the specified namespace URI and local name.</summary>
/// <param name="domNode">DOM node from which the descendant DOM element is going to be retrieved.</param>
/// <param name="namespaceURI" type="String" optional="true">The namespace URI of the element to match.</param>
/// <param name="localName" type="String" optional="true">Name of the element to match.</param>
/// <returns>The node's first descendant DOM element that matches the specified namespace URI and local name; null otherwise.</returns>
if (domNode.getElementsByTagNameNS) {
var result = domNode.getElementsByTagNameNS(namespaceURI, localName);
return result.length > 0 ? result[0] : null;
}
return xmlFirstElementMaybeRecursive(domNode, namespaceURI, localName, /*recursive*/true);
};
var xmlFirstElementMaybeRecursive = function (domNode, namespaceURI, localName, recursive) {
/// <summary>Returns the first descendant DOM element under the specified DOM node that matches the specified namespace URI and local name.</summary>
/// <param name="domNode">DOM node from which the descendant DOM element is going to be retrieved.</param>
/// <param name="namespaceURI" type="String" optional="true">The namespace URI of the element to match.</param>
/// <param name="localName" type="String" optional="true">Name of the element to match.</param>
/// <param name="recursive" type="Boolean">
/// True if the search should include all the descendants of the DOM node.
/// False if the search should be scoped only to the direct children of the DOM node.
/// </param>
/// <returns>The node's first descendant DOM element that matches the specified namespace URI and local name; null otherwise.</returns>
var firstElement = null;
xmlTraverse(domNode, recursive, function (child) {
if (child.nodeType === 1) {
var isExpectedNamespace = !namespaceURI || xmlNamespaceURI(child) === namespaceURI;
var isExpectedNodeName = !localName || xmlLocalName(child) === localName;
if (isExpectedNamespace && isExpectedNodeName) {
firstElement = child;
}
}
return firstElement === null;
});
return firstElement;
};
var xmlInnerText = function (xmlElement) {
/// <summary>Gets the concatenated value of all immediate child text and CDATA nodes for the specified element.</summary>
/// <param name="domElement">Element to get values for.</param>
/// <returns type="String">Text for all direct children.</returns>
var result = null;
var root = (xmlElement.nodeType === 9 && xmlElement.documentElement) ? xmlElement.documentElement : xmlElement;
var whitespaceAlreadyRemoved = root.ownerDocument.preserveWhiteSpace === false;
var whitespacePreserveContext;
xmlTraverse(root, false, function (child) {
if (child.nodeType === 3 || child.nodeType === 4) {
// isElementContentWhitespace indicates that this is 'ignorable whitespace',
// but it's not defined by all browsers, and does not honor xml:space='preserve'
// in some implementations.
//
// If we can't tell either way, we walk up the tree to figure out whether
// xml:space is set to preserve; otherwise we discard pure-whitespace.
//
// For example <a> <b>1</b></a>. The space between <a> and <b> is usually 'ignorable'.
var text = xmlNodeValue(child);
var shouldInclude = whitespaceAlreadyRemoved || !isWhitespace(text);
if (!shouldInclude) {
// Walk up the tree to figure out whether we are in xml:space='preserve' context
// for the cursor (needs to happen only once).
if (whitespacePreserveContext === undefined) {
whitespacePreserveContext = isWhitespacePreserveContext(root);
}
shouldInclude = whitespacePreserveContext;
}
if (shouldInclude) {
if (!result) {
result = text;
} else {
result += text;
}
}
}
// Continue traversing?
return true;
});
return result;
};
var xmlLocalName = function (domNode) {
/// <summary>Returns the localName of a XML node.</summary>
/// <param name="domNode">DOM node to get the value from.</param>
/// <returns type="String">localName of domNode.</returns>
return domNode.localName || domNode.baseName;
};
var xmlNamespaceURI = function (domNode) {
/// <summary>Returns the namespace URI of a XML node.</summary>
/// <param name="node">DOM node to get the value from.</param>
/// <returns type="String">Namespace URI of domNode.</returns>
return domNode.namespaceURI || null;
};
var xmlNodeValue = function (domNode) {
/// <summary>Returns the value or the inner text of a XML node.</summary>
/// <param name="node">DOM node to get the value from.</param>
/// <returns>Value of the domNode or the inner text if domNode represents a DOM element node.</returns>
if (domNode.nodeType === 1) {
return xmlInnerText(domNode);
}
return domNode.nodeValue;
};
var xmlTraverse = function (domNode, recursive, onChildCallback) {
/// <summary>Walks through the descendants of the domNode and invokes a callback for each node.</summary>
/// <param name="domNode">DOM node whose descendants are going to be traversed.</param>
/// <param name="recursive" type="Boolean">
/// True if the traversal should include all the descenants of the DOM node.
/// False if the traversal should be scoped only to the direct children of the DOM node.
/// </param>
/// <returns type="String">Namespace URI of node.</returns>
var subtrees = [];
var child = domNode.firstChild;
var proceed = true;
while (child && proceed) {
proceed = onChildCallback(child);
if (proceed) {
if (recursive && child.firstChild) {
subtrees.push(child.firstChild);
}
child = child.nextSibling || subtrees.shift();
}
}
};
var xmlSiblingElement = function (domNode, namespaceURI, localName) {
/// <summary>Returns the next sibling DOM element of the specified DOM node.</summary>
/// <param name="domNode">DOM node from which the next sibling is going to be retrieved.</param>
/// <param name="namespaceURI" type="String" optional="true">The namespace URI of the element to match.</param>
/// <param name="localName" type="String" optional="true">Name of the element to match.</param>
/// <returns>The node's next sibling DOM element, null if there is none.</returns>
var sibling = domNode.nextSibling;
while (sibling) {
if (sibling.nodeType === 1) {
var isExpectedNamespace = !namespaceURI || xmlNamespaceURI(sibling) === namespaceURI;
var isExpectedNodeName = !localName || xmlLocalName(sibling) === localName;
if (isExpectedNamespace && isExpectedNodeName) {
return sibling;
}
}
sibling = sibling.nextSibling;
}
return null;
};
var xmlDom = function () {
/// <summary>Creates a new empty DOM document node.</summary>
/// <returns>New DOM document node.</returns>
/// <remarks>
/// This function will first try to create a native DOM document using
/// the browsers createDocument function. If the browser doesn't
/// support this but supports ActiveXObject, then an attempt to create
/// an MSXML 6.0 DOM will be made. If this attempt fails too, then an attempt
/// for creating an MXSML 3.0 DOM will be made. If this last attemp fails or
/// the browser doesn't support ActiveXObject then an exception will be thrown.
/// </remarks>
var implementation = window.document.implementation;
return (implementation && implementation.createDocument) ?
implementation.createDocument(null, null, null) :
msXmlDom();
};
var xmlAppendChildren = function (parent, children) {
/// <summary>Appends a collection of child nodes or string values to a parent DOM node.</summary>
/// <param name="parent">DOM node to which the children will be appended.</param>
/// <param name="children" type="Array">Array containing DOM nodes or string values that will be appended to the parent.</param>
/// <returns>The parent with the appended children or string values.</returns>
/// <remarks>
/// If a value in the children collection is a string, then a new DOM text node is going to be created
/// for it and then appended to the parent.
/// </remarks>
if (!isArray(children)) {
return xmlAppendChild(parent, children);
}
var i, len;
for (i = 0, len = children.length; i < len; i++) {
children[i] && xmlAppendChild(parent, children[i]);
}
return parent;
};
var xmlAppendChild = function (parent, child) {
/// <summary>Appends a child node or a string value to a parent DOM node.</summary>
/// <param name="parent">DOM node to which the child will be appended.</param>
/// <param name="child">Child DOM node or string value to append to the parent.</param>
/// <returns>The parent with the appended child or string value.</returns>
/// <remarks>
/// If child is a string value, then a new DOM text node is going to be created
/// for it and then appended to the parent.
/// </remarks>
djsassert(parent !== child, "xmlAppendChild() - parent and child are one and the same!");
if (child) {
if (typeof child === "string") {
return xmlAppendText(parent, xmlNewText(parent.ownerDocument, child));
}
if (child.nodeType === 2) {
parent.setAttributeNodeNS ? parent.setAttributeNodeNS(child) : parent.setAttributeNode(child);
} else {
parent.appendChild(child);
}
}
return parent;
};
var xmlNewAttribute = function (dom, namespaceURI, qualifiedName, value) {
/// <summary>Creates a new DOM attribute node.</summary>
/// <param name="dom">DOM document used to create the attribute.</param>
/// <param name="prefix" type="String">Namespace prefix.</param>
/// <param name="namespaceURI" type="String">Namespace URI.</param>
/// <returns>DOM attribute node for the namespace declaration.</returns>
var attribute =
dom.createAttributeNS && dom.createAttributeNS(namespaceURI, qualifiedName) ||
dom.createNode(2, qualifiedName, namespaceURI || undefined);
attribute.value = value || "";
return attribute;
};
var xmlNewElement = function (dom, nampespaceURI, qualifiedName, children) {
/// <summary>Creates a new DOM element node.</summary>
/// <param name="dom">DOM document used to create the DOM element.</param>
/// <param name="namespaceURI" type="String">Namespace URI of the new DOM element.</param>
/// <param name="qualifiedName" type="String">Qualified name in the form of "prefix:name" of the new DOM element.</param>
/// <param name="children" type="Array" optional="true">
/// Collection of child DOM nodes or string values that are going to be appended to the new DOM element.
/// </param>
/// <returns>New DOM element.</returns>
/// <remarks>
/// If a value in the children collection is a string, then a new DOM text node is going to be created
/// for it and then appended to the new DOM element.
/// </remarks>
var element =
dom.createElementNS && dom.createElementNS(nampespaceURI, qualifiedName) ||
dom.createNode(1, qualifiedName, nampespaceURI || undefined);
return xmlAppendChildren(element, children || []);
};
var xmlNewNSDeclaration = function (dom, namespaceURI, prefix) {
/// <summary>Creates a namespace declaration attribute.</summary>
/// <param name="dom">DOM document used to create the attribute.</param>
/// <param name="namespaceURI" type="String">Namespace URI.</param>
/// <param name="prefix" type="String">Namespace prefix.</param>
/// <returns>DOM attribute node for the namespace declaration.</returns>
return xmlNewAttribute(dom, xmlnsNS, xmlQualifiedName("xmlns", prefix), namespaceURI);
};
var xmlNewFragment = function (dom, text) {
/// <summary>Creates a new DOM document fragment node for the specified xml text.</summary>
/// <param name="dom">DOM document from which the fragment node is going to be created.</param>
/// <param name="text" type="String" mayBeNull="false">XML text to be represented by the XmlFragment.</param>
/// <returns>New DOM document fragment object.</returns>
var value = "<c>" + text + "</c>";
var tempDom = xmlParse(value);
var tempRoot = tempDom.documentElement;
var imported = ("importNode" in dom) ? dom.importNode(tempRoot, true) : tempRoot;
var fragment = dom.createDocumentFragment();
var importedChild = imported.firstChild;
while (importedChild) {
fragment.appendChild(importedChild);
importedChild = importedChild.nextSibling;
}
return fragment;
};
var xmlNewText = function (dom, text) {
/// <summary>Creates new DOM text node.</summary>
/// <param name="dom">DOM document used to create the text node.</param>
/// <param name="text" type="String">Text value for the DOM text node.</param>
/// <returns>DOM text node.</returns>
return dom.createTextNode(text);
};
var xmlNewNodeByPath = function (dom, root, namespaceURI, prefix, path) {
/// <summary>Creates a new DOM element or DOM attribute node as specified by path and appends it to the DOM tree pointed by root.</summary>
/// <param name="dom">DOM document used to create the new node.</param>
/// <param name="root">DOM element node used as root of the subtree on which the new nodes are going to be created.</param>
/// <param name="namespaceURI" type="String">Namespace URI of the new DOM element or attribute.</param>
/// <param name="namespacePrefix" type="String">Prefix used to qualify the name of the new DOM element or attribute.</param>
/// <param name="Path" type="String">Path string describing the location of the new DOM element or attribute from the root element.</param>
/// <returns>DOM element or attribute node for the last segment of the path.</returns>
/// <remarks>
/// This function will traverse the path and will create a new DOM element with the specified namespace URI and prefix
/// for each segment that doesn't have a matching element under root.
///
/// The last segment of the path may be decorated with a starting @ character. In this case a new DOM attribute node
/// will be created.
/// </remarks>
var name = "";
var parts = path.split("/");
var xmlFindNode = xmlFirstChildElement;
var xmlNewNode = xmlNewElement;
var xmlNode = root;
var i, len;
for (i = 0, len = parts.length; i < len; i++) {
name = parts[i];
if (name.charAt(0) === "@") {
name = name.substring(1);
xmlFindNode = xmlAttributeNode;
xmlNewNode = xmlNewAttribute;
}
var childNode = xmlFindNode(xmlNode, namespaceURI, name);
if (!childNode) {
childNode = xmlNewNode(dom, namespaceURI, xmlQualifiedName(prefix, name));
xmlAppendChild(xmlNode, childNode);
}
xmlNode = childNode;
}
return xmlNode;
};
var xmlSerialize = function (domNode) {
/// <summary>
/// Returns the text representation of the document to which the specified node belongs.
/// </summary>
/// <param name="root">Wrapped element in the document to serialize.</param>
/// <returns type="String">Serialized document.</returns>
var xmlSerializer = window.XMLSerializer;
if (xmlSerializer) {
var serializer = new xmlSerializer();
return serializer.serializeToString(domNode);
}
if (domNode.xml) {
return domNode.xml;
}
throw { message: "XML serialization unsupported" };
};
var xmlSerializeDescendants = function (domNode) {
/// <summary>Returns the XML representation of the all the descendants of the node.</summary>
/// <param name="domNode" optional="false" mayBeNull="false">Node to serialize.</param>
/// <returns type="String">The XML representation of all the descendants of the node.</returns>
var children = domNode.childNodes;
var i, len = children.length;
if (len === 0) {
return "";
}
// Some implementations of the XMLSerializer don't deal very well with fragments that
// don't have a DOMElement as their first child. The work around is to wrap all the
// nodes in a dummy root node named "c", serialize it and then just extract the text between
// the <c> and the </c> substrings.
var dom = domNode.ownerDocument;
var fragment = dom.createDocumentFragment();
var fragmentRoot = dom.createElement("c");
fragment.appendChild(fragmentRoot);
// Move the children to the fragment tree.
for (i = 0; i < len; i++) {
fragmentRoot.appendChild(children[i]);
}
var xml = xmlSerialize(fragment);
xml = xml.substr(3, xml.length - 7);
// Move the children back to the original dom tree.
for (i = 0; i < len; i++) {
domNode.appendChild(fragmentRoot.childNodes[i]);
}
return xml;
};
var xmlSerializeNode = function (domNode) {
/// <summary>Returns the XML representation of the node and all its descendants.</summary>
/// <param name="domNode" optional="false" mayBeNull="false">Node to serialize.</param>
/// <returns type="String">The XML representation of the node and all its descendants.</returns>
var xml = domNode.xml;
if (xml !== undefined) {
return xml;
}
if (window.XMLSerializer) {
var serializer = new window.XMLSerializer();
return serializer.serializeToString(domNode);
}
throw { message: "XML serialization unsupported" };
};
// DATAJS INTERNAL START
datajs.http = http;
datajs.w3org = w3org;
datajs.xmlNS = xmlNS;
datajs.xmlnsNS = xmlnsNS;
datajs.hasLeadingOrTrailingWhitespace = hasLeadingOrTrailingWhitespace;
datajs.isXmlNSDeclaration = isXmlNSDeclaration;
datajs.xmlAppendChild = xmlAppendChild;
datajs.xmlAppendChildren = xmlAppendChildren;
datajs.xmlAttributeNode = xmlAttributeNode;
datajs.xmlAttributes = xmlAttributes;
datajs.xmlAttributeValue = xmlAttributeValue;
datajs.xmlBaseURI = xmlBaseURI;
datajs.xmlChildElements = xmlChildElements;
datajs.xmlFindElementByPath = xmlFindElementByPath;
datajs.xmlFindNodeByPath = xmlFindNodeByPath;
datajs.xmlFirstChildElement = xmlFirstChildElement;
datajs.xmlFirstDescendantElement = xmlFirstDescendantElement;
datajs.xmlInnerText = xmlInnerText;
datajs.xmlLocalName = xmlLocalName;
datajs.xmlNamespaceURI = xmlNamespaceURI;
datajs.xmlNodeValue = xmlNodeValue;
datajs.xmlDom = xmlDom;
datajs.xmlNewAttribute = xmlNewAttribute;
datajs.xmlNewElement = xmlNewElement;
datajs.xmlNewFragment = xmlNewFragment;
datajs.xmlNewNodeByPath = xmlNewNodeByPath;
datajs.xmlNewNSDeclaration = xmlNewNSDeclaration;
datajs.xmlNewText = xmlNewText;
datajs.xmlParse = xmlParse;
datajs.xmlQualifiedName = xmlQualifiedName;
datajs.xmlSerialize = xmlSerialize;
datajs.xmlSerializeDescendants = xmlSerializeDescendants;
datajs.xmlSiblingElement = xmlSiblingElement;
// DATAJS INTERNAL END
// CONTENT END
})(this);