lib/xml.js (444 lines of code) (raw):

/* * 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;