/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
'use strict';
/** @module odatajs/xml */
var utils = require('./utils.js');
var activeXObject = utils.activeXObject;
var djsassert = utils.djsassert;
var extend = utils.extend;
var isArray = utils.isArray;
var normalizeURI = utils.normalizeURI;
// 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";
/** Checks whether the specified string has leading or trailing spaces.
* @param {String} text - String to check.
* @returns {Boolean} true if text has any leading or trailing whitespace; false otherwise.
*/
function hasLeadingOrTrailingWhitespace(text) {
var re = /(^\s)|(\s$)/;
return re.test(text);
}
/** Determines whether the specified text is empty or whitespace.
* @param {String} text - Value to inspect.
* @returns {Boolean} true if the text value is empty or all whitespace; false otherwise.
*/
function isWhitespace(text) {
var ws = /^\s*$/;
return text === null || ws.test(text);
}
/** Determines whether the specified element has xml:space='preserve' applied.
* @param domElement - Element to inspect.
* @returns {Boolean} Whether xml:space='preserve' is in effect.
*/
function isWhitespacePreserveContext(domElement) {
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;
}
/** Determines whether the attribute is a XML namespace declaration.
* @param domAttribute - Element to inspect.
* @return {Boolean} True if the attribute is a namespace declaration (its name is 'xmlns' or starts with 'xmlns:'; false otherwise.
*/
function isXmlNSDeclaration(domAttribute) {
var nodeName = domAttribute.nodeName;
return nodeName == "xmlns" || nodeName.indexOf("xmlns:") === 0;
}
/** Safely set as property in an object by invoking obj.setProperty.
* @param obj - Object that exposes a setProperty method.
* @param {String} name - Property name
* @param value - Property value.
*/
function safeSetProperty(obj, name, value) {
try {
obj.setProperty(name, value);
} catch (_) { }
}
/** Creates an configures new MSXML 3.0 ActiveX object.
* @returns {Object} New MSXML 3.0 ActiveX object.
* This function throws any exception that occurs during the creation
* of the MSXML 3.0 ActiveX object.
*/
function msXmlDom3() {
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;
}
/** Creates an configures new MSXML 6.0 or MSXML 3.0 ActiveX object.
* @returns {Object} New MSXML 3.0 ActiveX object.
* 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.
*/
function msXmlDom() {
try {
var msxml = activeXObject("Msxml2.DOMDocument.6.0");
if (msxml) {
msxml.async = true;
}
return msxml;
} catch (_) {
return msXmlDom3();
}
}
/** Parses an XML string using the MSXML DOM.
* @returns {Object} New MSXML DOMDocument node representing the parsed XML string.
* 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.
*/
function msXmlParse(text) {
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;
}
/** Throws a new exception containing XML parsing error information.
* @param exceptionOrReason - String indicating the reason of the parsing failure or Object detailing the parsing error.
* @param {String} srcText - String indicating the part of the XML string that caused the parsing error.
* @param {String} errorXmlText - XML string for wich the parsing failed.
*/
function xmlThrowParserError(exceptionOrReason, srcText, errorXmlText) {
if (typeof exceptionOrReason === "string") {
exceptionOrReason = { message: exceptionOrReason };
}
throw extend(exceptionOrReason, { srcText: srcText || "", errorXmlText: errorXmlText || "" });
}
/** Returns an XML DOM document from the specified text.
* @param {String} text - Document text.
* @returns XML DOM document.
* This function will throw an exception in case of a parse error
*/
function xmlParse(text) {
var domParser = undefined;
if (utils.inBrowser()) {
domParser = window.DOMParser && new window.DOMParser();
} else {
domParser = new (require('xmldom').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;
}
/** Builds a XML qualified name string in the form of "prefix:name".
* @param {String} prefix - Prefix string (may be null)
* @param {String} name - Name string to qualify with the prefix.
* @returns {String} Qualified name.
*/
function xmlQualifiedName(prefix, name) {
return prefix ? prefix + ":" + name : name;
}
/** Appends a text node into the specified DOM element node.
* @param domNode - DOM node for the element.
* @param {String} textNode - Text to append as a child of element.
*/
function xmlAppendText(domNode, textNode) {
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;
}
/** Iterates through the XML element's attributes and invokes the callback function for each one.
* @param element - Wrapped element to iterate over.
* @param {Function} onAttributeCallback - Callback function to invoke with wrapped attribute nodes.
*/
function xmlAttributes(element, onAttributeCallback) {
var attributes = element.attributes;
var i, len;
for (i = 0, len = attributes.length; i < len; i++) {
onAttributeCallback(attributes.item(i));
}
}
/** Returns the value of a DOM element's attribute.
* @param domNode - DOM node for the owning element.
* @param {String} localName - Local name of the attribute.
* @param {String} nsURI - Namespace URI of the attribute.
* @returns {String} - The attribute value, null if not found (may be null)
*/
function xmlAttributeValue(domNode, localName, nsURI) {
var attribute = xmlAttributeNode(domNode, localName, nsURI);
return attribute ? xmlNodeValue(attribute) : null;
}
/** Gets an attribute node from a DOM element.
* @param domNode - DOM node for the owning element.
* @param {String} localName - Local name of the attribute.
* @param {String} nsURI - Namespace URI of the attribute.
* @returns The attribute node, null if not found.
*/
function xmlAttributeNode(domNode, localName, nsURI) {
var attributes = domNode.attributes;
if (attributes.getNamedItemNS) {
return attributes.getNamedItemNS(nsURI || null, localName);
}
return attributes.getQualifiedItem(localName, nsURI) || null;
}
/** Gets the value of the xml:base attribute on the specified element.
* @param domNode - Element to get xml:base attribute value from.
* @param [baseURI] - Base URI used to normalize the value of the xml:base attribute ( may be null)
* @returns {String} Value of the xml:base attribute if found; the baseURI or null otherwise.
*/
function xmlBaseURI(domNode, baseURI) {
var base = xmlAttributeNode(domNode, "base", xmlNS);
return (base ? normalizeURI(base.value, baseURI) : baseURI) || null;
}
/** Iterates through the XML element's child DOM elements and invokes the callback function for each one.
* @param domNode - DOM Node containing the DOM elements to iterate over.
* @param {Function} onElementCallback - Callback function to invoke for each child DOM element.
*/
function xmlChildElements(domNode, onElementCallback) {
xmlTraverse(domNode, /*recursive*/false, function (child) {
if (child.nodeType === 1) {
onElementCallback(child);
}
// continue traversing.
return true;
});
}
/** Gets the descendant element under root that corresponds to the specified path and namespace URI.
* @param root - DOM element node from which to get the descendant element.
* @param {String} namespaceURI - The namespace URI of the element to match.
* @param {String} path - Path to the desired descendant element.
* @return The element specified by path and namespace URI.
* 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.
*/
function xmlFindElementByPath(root, namespaceURI, path) {
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;
}
/** Gets the DOM element or DOM attribute node under root that corresponds to the specified path and namespace URI.
* @param root - DOM element node from which to get the descendant node.
* @param {String} namespaceURI - The namespace URI of the node to match.
* @param {String} path - Path to the desired descendant node.
* @return The node specified by path and namespace URI.
* 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.
*/
function xmlFindNodeByPath(root, namespaceURI, path) {
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;
}
/** Returns the first child DOM element under the specified DOM node that matches the specified namespace URI and local name.
* @param domNode - DOM node from which the child DOM element is going to be retrieved.
* @param {String} [namespaceURI] -
* @param {String} [localName] -
* @return The node's first child DOM element that matches the specified namespace URI and local name; null otherwise.
*/
function xmlFirstChildElement(domNode, namespaceURI, localName) {
return xmlFirstElementMaybeRecursive(domNode, namespaceURI, localName, /*recursive*/false);
}
/** Returns the first descendant DOM element under the specified DOM node that matches the specified namespace URI and local name.
* @param domNode - DOM node from which the descendant DOM element is going to be retrieved.
* @param {String} [namespaceURI] -
* @param {String} [localName] -
* @return The node's first descendant DOM element that matches the specified namespace URI and local name; null otherwise.
*/
function xmlFirstDescendantElement(domNode, namespaceURI, localName) {
if (domNode.getElementsByTagNameNS) {
var result = domNode.getElementsByTagNameNS(namespaceURI, localName);
return result.length > 0 ? result[0] : null;
}
return xmlFirstElementMaybeRecursive(domNode, namespaceURI, localName, /*recursive*/true);
}
/** Returns the first descendant DOM element under the specified DOM node that matches the specified namespace URI and local name.
* @param domNode - DOM node from which the descendant DOM element is going to be retrieved.
* @param {String} [namespaceURI] -
* @param {String} [localName] -
* @param {Boolean} recursive
* - 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.
* @return The node's first descendant DOM element that matches the specified namespace URI and local name; null otherwise.
*/
function xmlFirstElementMaybeRecursive(domNode, namespaceURI, localName, recursive) {
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;
}
/** Gets the concatenated value of all immediate child text and CDATA nodes for the specified element.
* @param xmlElement - Element to get values for.
* @returns {String} Text for all direct children.
*/
function xmlInnerText(xmlElement) {
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;
}
/** Returns the localName of a XML node.
* @param domNode - DOM node to get the value from.
* @returns {String} localName of domNode.
*/
function xmlLocalName(domNode) {
return domNode.localName || domNode.baseName;
}
/** Returns the namespace URI of a XML node.
* @param domNode - DOM node to get the value from.
* @returns {String} Namespace URI of domNode.
*/
function xmlNamespaceURI(domNode) {
return domNode.namespaceURI || null;
}
/** Returns the value or the inner text of a XML node.
* @param domNode - DOM node to get the value from.
* @return Value of the domNode or the inner text if domNode represents a DOM element node.
*/
function xmlNodeValue(domNode) {
if (domNode.nodeType === 1) {
return xmlInnerText(domNode);
}
return domNode.nodeValue;
}
/** Walks through the descendants of the domNode and invokes a callback for each node.
* @param domNode - DOM node whose descendants are going to be traversed.
* @param {Boolean} recursive
* - 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 {Boolean} onChildCallback - Called for each child
* @returns {String} Namespace URI of node.
*/
function xmlTraverse(domNode, recursive, onChildCallback) {
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();
}
}
}
/** Returns the next sibling DOM element of the specified DOM node.
* @param domNode - DOM node from which the next sibling is going to be retrieved.
* @param {String} [namespaceURI] -
* @param {String} [localName] -
* @return The node's next sibling DOM element, null if there is none.
*/
function xmlSiblingElement(domNode, namespaceURI, localName) {
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;
}
/** Creates a new empty DOM document node.
* @return New DOM document node.
*
* 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.
*/
function xmlDom() {
var implementation = window.document.implementation;
return (implementation && implementation.createDocument) ?
implementation.createDocument(null, null, null) :
msXmlDom();
}
/** Appends a collection of child nodes or string values to a parent DOM node.
* @param parent - DOM node to which the children will be appended.
* @param {Array} children - Array containing DOM nodes or string values that will be appended to the parent.
* @return The parent with the appended children or string values.
* 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.
*/
function xmlAppendChildren(parent, children) {
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;
}
/** Appends a child node or a string value to a parent DOM node.
* @param parent - DOM node to which the child will be appended.
* @param child - Child DOM node or string value to append to the parent.
* @return The parent with the appended child or string value.
* 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.
*/
function xmlAppendChild(parent, child) {
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;
}
/** Creates a new DOM attribute node.
* @param dom - DOM document used to create the attribute.
* @param {String} namespaceURI - Namespace URI.
* @param {String} qualifiedName - Qualified OData name
* @param {String} value - Value of the new attribute
* @return DOM attribute node for the namespace declaration.
*/
function xmlNewAttribute(dom, namespaceURI, qualifiedName, value) {
var attribute =
dom.createAttributeNS && dom.createAttributeNS(namespaceURI, qualifiedName) ||
dom.createNode(2, qualifiedName, namespaceURI || undefined);
attribute.value = value || "";
return attribute;
}
/** Creates a new DOM element node.
* @param dom - DOM document used to create the DOM element.
* @param {String} namespaceURI - Namespace URI of the new DOM element.
* @param {String} qualifiedName - Qualified name in the form of "prefix:name" of the new DOM element.
* @param {Array} [children] Collection of child DOM nodes or string values that are going to be appended to the new DOM element.
* @return New DOM element.
* 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.
*/
function xmlNewElement(dom, namespaceURI, qualifiedName, children) {
var element =
dom.createElementNS && dom.createElementNS(nampespaceURI, qualifiedName) ||
dom.createNode(1, qualifiedName, nampespaceURI || undefined);
return xmlAppendChildren(element, children || []);
}
/** Creates a namespace declaration attribute.
* @param dom - DOM document used to create the attribute.
* @param {String} namespaceURI - Namespace URI.
* @param {String} prefix - Namespace prefix.
* @return DOM attribute node for the namespace declaration.
*/
function xmlNewNSDeclaration(dom, namespaceURI, prefix) {
return xmlNewAttribute(dom, xmlnsNS, xmlQualifiedName("xmlns", prefix), namespaceURI);
}
/** Creates a new DOM document fragment node for the specified xml text.
* @param dom - DOM document from which the fragment node is going to be created.
* @param {String} text XML text to be represented by the XmlFragment.
* @return New DOM document fragment object.
*/
function xmlNewFragment(dom, text) {
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;
}
/** Creates new DOM text node.
* @param dom - DOM document used to create the text node.
* @param {String} text - Text value for the DOM text node.
* @return DOM text node.
*/
function xmlNewText(dom, text) {
return dom.createTextNode(text);
}
/** Creates a new DOM element or DOM attribute node as specified by path and appends it to the DOM tree pointed by root.
* @param dom - DOM document used to create the new node.
* @param root - DOM element node used as root of the subtree on which the new nodes are going to be created.
* @param {String} namespaceURI - Namespace URI of the new DOM element or attribute.
* @param {String} prefix - Prefix used to qualify the name of the new DOM element or attribute.
* @param {String} path - Path string describing the location of the new DOM element or attribute from the root element.
* @return DOM element or attribute node for the last segment of the path.
* 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.
*/
function xmlNewNodeByPath(dom, root, namespaceURI, prefix, path) {
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;
}
/** Returns the text representation of the document to which the specified node belongs.
* @param domNode - Wrapped element in the document to serialize.
* @returns {String} Serialized document.
*/
function xmlSerialize(domNode) {
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" };
}
/** Returns the XML representation of the all the descendants of the node.
* @param domNode - Node to serialize.
* @returns {String} The XML representation of all the descendants of the node.
*/
function xmlSerializeDescendants(domNode) {
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;
}
/** Returns the XML representation of the node and all its descendants.
* @param domNode - Node to serialize
* @returns {String} The XML representation of the node and all its descendants.
*/
function xmlSerializeNode(domNode) {
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" };
}
exports.http = http;
exports.w3org = w3org;
exports.xmlNS = xmlNS;
exports.xmlnsNS = xmlnsNS;
exports.hasLeadingOrTrailingWhitespace = hasLeadingOrTrailingWhitespace;
exports.isXmlNSDeclaration = isXmlNSDeclaration;
exports.xmlAppendChild = xmlAppendChild;
exports.xmlAppendChildren = xmlAppendChildren;
exports.xmlAttributeNode = xmlAttributeNode;
exports.xmlAttributes = xmlAttributes;
exports.xmlAttributeValue = xmlAttributeValue;
exports.xmlBaseURI = xmlBaseURI;
exports.xmlChildElements = xmlChildElements;
exports.xmlFindElementByPath = xmlFindElementByPath;
exports.xmlFindNodeByPath = xmlFindNodeByPath;
exports.xmlFirstChildElement = xmlFirstChildElement;
exports.xmlFirstDescendantElement = xmlFirstDescendantElement;
exports.xmlInnerText = xmlInnerText;
exports.xmlLocalName = xmlLocalName;
exports.xmlNamespaceURI = xmlNamespaceURI;
exports.xmlNodeValue = xmlNodeValue;
exports.xmlDom = xmlDom;
exports.xmlNewAttribute = xmlNewAttribute;
exports.xmlNewElement = xmlNewElement;
exports.xmlNewFragment = xmlNewFragment;
exports.xmlNewNodeByPath = xmlNewNodeByPath;
exports.xmlNewNSDeclaration = xmlNewNSDeclaration;
exports.xmlNewText = xmlNewText;
exports.xmlParse = xmlParse;
exports.xmlQualifiedName = xmlQualifiedName;
exports.xmlSerialize = xmlSerialize;
exports.xmlSerializeDescendants = xmlSerializeDescendants;
exports.xmlSiblingElement = xmlSiblingElement;