parent
90b880633e
commit
7665a74eb7
@ -1,742 +0,0 @@ |
|||||||
/* |
|
||||||
Copyright 2015 Axinom |
|
||||||
Copyright 2011-2013 Abdulla Abdurakhmanov |
|
||||||
Original sources are available at https://code.google.com/p/x2js/
|
|
||||||
|
|
||||||
Licensed 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. |
|
||||||
*/ |
|
||||||
|
|
||||||
// This fork is maintained at https://github.com/Axinom/x2js
|
|
||||||
|
|
||||||
/* |
|
||||||
Supported export methods: |
|
||||||
* AMD |
|
||||||
* <script> (window.X2JS) |
|
||||||
* Node.js |
|
||||||
|
|
||||||
Limitations: |
|
||||||
* Attribute namespace prefixes are not parsed as such. |
|
||||||
* Overall the serialization/deserializaton code is "best effort" and not foolproof. |
|
||||||
*/ |
|
||||||
|
|
||||||
// Module definition pattern used is returnExports from https://github.com/umdjs/umd
|
|
||||||
(function (root, factory) { |
|
||||||
"use strict"; |
|
||||||
|
|
||||||
/*** we |
|
||||||
global define |
|
||||||
if (typeof define === 'function' && define.amd) { |
|
||||||
// AMD. Register as an anonymous module.
|
|
||||||
define([], factory); |
|
||||||
} else if (typeof module === 'object' && module.exports) { |
|
||||||
// Node. Does not work with strict CommonJS, but only CommonJS-like
|
|
||||||
// environments that support module.exports, like Node.
|
|
||||||
module.exports = factory(require("xmldom").DOMParser); |
|
||||||
} else { |
|
||||||
// Browser globals (root is window)
|
|
||||||
root.X2JS = factory(); |
|
||||||
} |
|
||||||
we ***/ |
|
||||||
|
|
||||||
/*** we ***/ |
|
||||||
module.exports = factory(require("../xmldom/dom-parser").DOMParser); |
|
||||||
/*** we ***/ |
|
||||||
|
|
||||||
|
|
||||||
})(this, function (CustomDOMParser) { |
|
||||||
"use strict"; |
|
||||||
|
|
||||||
// We return a constructor that can be used to make X2JS instances.
|
|
||||||
return function X2JS(config) { |
|
||||||
var VERSION = "3.1.0"; |
|
||||||
|
|
||||||
config = config || {}; |
|
||||||
|
|
||||||
function initConfigDefaults() { |
|
||||||
// If set to "property" then <element>_asArray will be created
|
|
||||||
// to allow you to access any element as an array (even if there is only one of it).
|
|
||||||
config.arrayAccessForm = config.arrayAccessForm || "none"; |
|
||||||
|
|
||||||
// If "text" then <empty></empty> will be transformed to "".
|
|
||||||
// If "object" then <empty></empty> will be transformed to {}.
|
|
||||||
config.emptyNodeForm = config.emptyNodeForm || "text"; |
|
||||||
|
|
||||||
// Allows attribute values to be converted on the fly during parsing to objects.
|
|
||||||
// "test": function(name, value) { return true; }
|
|
||||||
// "convert": function(name, value) { return parseFloat(value);
|
|
||||||
// convert() will be called for every attribute where test() returns true
|
|
||||||
// and the return value from convert() will replace the original value of the attribute.
|
|
||||||
config.attributeConverters = config.attributeConverters || []; |
|
||||||
|
|
||||||
// Any elements that match the paths here will have their text parsed
|
|
||||||
// as an XML datetime value (2011-11-12T13:00:00-07:00 style).
|
|
||||||
// The path can be a plain string (parent.child1.child2),
|
|
||||||
// a regex (/.*\.child2/) or function(elementPath).
|
|
||||||
config.datetimeAccessFormPaths = config.datetimeAccessFormPaths || []; |
|
||||||
|
|
||||||
// Any elements that match the paths listed here will be stored in JavaScript objects
|
|
||||||
// as arrays even if there is only one of them. The path can be a plain string
|
|
||||||
// (parent.child1.child2), a regex (/.*\.child2/) or function(elementName, elementPath).
|
|
||||||
config.arrayAccessFormPaths = config.arrayAccessFormPaths || []; |
|
||||||
|
|
||||||
// If true, a toString function is generated to print nodes containing text or cdata.
|
|
||||||
// Useful if you want to accept both plain text and CData as equivalent inputs.
|
|
||||||
if (config.enableToStringFunc === undefined) { |
|
||||||
config.enableToStringFunc = true; |
|
||||||
} |
|
||||||
|
|
||||||
// If true, empty text tags are ignored for elements with child nodes.
|
|
||||||
if (config.skipEmptyTextNodesForObj === undefined) { |
|
||||||
config.skipEmptyTextNodesForObj = true; |
|
||||||
} |
|
||||||
|
|
||||||
// If true, whitespace is trimmed from text nodes.
|
|
||||||
if (config.stripWhitespaces === undefined) { |
|
||||||
config.stripWhitespaces = true; |
|
||||||
} |
|
||||||
|
|
||||||
// If true, double quotes are used in generated XML.
|
|
||||||
if (config.useDoubleQuotes === undefined) { |
|
||||||
config.useDoubleQuotes = true; |
|
||||||
} |
|
||||||
|
|
||||||
// If true, the root element of the XML document is ignored when converting to objects.
|
|
||||||
// The result will directly have the root element's children as its own properties.
|
|
||||||
if (config.ignoreRoot === undefined) { |
|
||||||
config.ignoreRoot = false; |
|
||||||
} |
|
||||||
|
|
||||||
// Whether XML characters in text are escaped when reading/writing XML.
|
|
||||||
if (config.escapeMode === undefined) { |
|
||||||
config.escapeMode = true; |
|
||||||
} |
|
||||||
|
|
||||||
// Prefix to use for properties that are created to represent XML attributes.
|
|
||||||
if (config.attributePrefix === undefined) { |
|
||||||
config.attributePrefix = "_"; |
|
||||||
} |
|
||||||
|
|
||||||
// If true, empty elements will created as self closing elements (<element />)
|
|
||||||
// If false, empty elements will be created with start and end tags (<element></element>)
|
|
||||||
if (config.selfClosingElements === undefined) { |
|
||||||
config.selfClosingElements = true; |
|
||||||
} |
|
||||||
|
|
||||||
// If this property defined as false and an XML element has CData node ONLY, it will be converted to text without additional property "__cdata"
|
|
||||||
if (config.keepCData === undefined) { |
|
||||||
config.keepCData = false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function initRequiredPolyfills() { |
|
||||||
function pad(number) { |
|
||||||
var r = String(number); |
|
||||||
if (r.length === 1) { |
|
||||||
r = '0' + r; |
|
||||||
} |
|
||||||
return r; |
|
||||||
} |
|
||||||
// Hello IE8-
|
|
||||||
if (typeof String.prototype.trim !== 'function') { |
|
||||||
String.prototype.trim = function trim() { |
|
||||||
return this.replace(/^\s+|^\n+|(\s|\n)+$/g, ''); |
|
||||||
}; |
|
||||||
} |
|
||||||
if (typeof Date.prototype.toISOString !== 'function') { |
|
||||||
// Implementation from http://stackoverflow.com/questions/2573521/how-do-i-output-an-iso-8601-formatted-string-in-javascript
|
|
||||||
Date.prototype.toISOString = function toISOString() { |
|
||||||
var MS_IN_S = 1000; |
|
||||||
|
|
||||||
return this.getUTCFullYear() |
|
||||||
+ '-' + pad(this.getUTCMonth() + 1) |
|
||||||
+ '-' + pad(this.getUTCDate()) |
|
||||||
+ 'T' + pad(this.getUTCHours()) |
|
||||||
+ ':' + pad(this.getUTCMinutes()) |
|
||||||
+ ':' + pad(this.getUTCSeconds()) |
|
||||||
+ '.' + String((this.getUTCMilliseconds() / MS_IN_S).toFixed(3)).slice(2, 5) |
|
||||||
+ 'Z'; |
|
||||||
}; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
initConfigDefaults(); |
|
||||||
initRequiredPolyfills(); |
|
||||||
|
|
||||||
var DOMNodeTypes = { |
|
||||||
"ELEMENT_NODE": 1, |
|
||||||
"TEXT_NODE": 3, |
|
||||||
"CDATA_SECTION_NODE": 4, |
|
||||||
"COMMENT_NODE": 8, |
|
||||||
"DOCUMENT_NODE": 9 |
|
||||||
}; |
|
||||||
|
|
||||||
function getDomNodeLocalName(domNode) { |
|
||||||
var localName = domNode.localName; |
|
||||||
if (localName == null) { |
|
||||||
// Yeah, this is IE!!
|
|
||||||
localName = domNode.baseName; |
|
||||||
} |
|
||||||
if (localName == null || localName === "") { |
|
||||||
// ==="" is IE too
|
|
||||||
localName = domNode.nodeName; |
|
||||||
} |
|
||||||
return localName; |
|
||||||
} |
|
||||||
|
|
||||||
function getDomNodeNamespacePrefix(node) { |
|
||||||
return node.prefix; |
|
||||||
} |
|
||||||
|
|
||||||
function escapeXmlChars(str) { |
|
||||||
if (typeof str === "string") |
|
||||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, '''); |
|
||||||
else |
|
||||||
return str; |
|
||||||
} |
|
||||||
|
|
||||||
function unescapeXmlChars(str) { |
|
||||||
return str.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, '&'); |
|
||||||
} |
|
||||||
|
|
||||||
function ensureProperArrayAccessForm(element, childName, elementPath) { |
|
||||||
switch (config.arrayAccessForm) { |
|
||||||
case "property": |
|
||||||
if (!(element[childName] instanceof Array)) |
|
||||||
element[childName + "_asArray"] = [element[childName]]; |
|
||||||
else |
|
||||||
element[childName + "_asArray"] = element[childName]; |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
if (!(element[childName] instanceof Array) && config.arrayAccessFormPaths.length > 0) { |
|
||||||
var match = false; |
|
||||||
|
|
||||||
for (var i = 0; i < config.arrayAccessFormPaths.length; i++) { |
|
||||||
var arrayPath = config.arrayAccessFormPaths[i]; |
|
||||||
if (typeof arrayPath === "string") { |
|
||||||
if (arrayPath === elementPath) { |
|
||||||
match = true; |
|
||||||
break; |
|
||||||
} |
|
||||||
} else if (arrayPath instanceof RegExp) { |
|
||||||
if (arrayPath.test(elementPath)) { |
|
||||||
match = true; |
|
||||||
break; |
|
||||||
} |
|
||||||
} else if (typeof arrayPath === "function") { |
|
||||||
if (arrayPath(childName, elementPath)) { |
|
||||||
match = true; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (match) |
|
||||||
element[childName] = [element[childName]]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function xmlDateTimeToDate(prop) { |
|
||||||
// Implementation based up on http://stackoverflow.com/questions/8178598/xml-datetime-to-javascript-date-object
|
|
||||||
// Improved to support full spec and optional parts
|
|
||||||
var MINUTES_PER_HOUR = 60; |
|
||||||
|
|
||||||
var bits = prop.split(/[-T:+Z]/g); |
|
||||||
|
|
||||||
var d = new Date(bits[0], bits[1] - 1, bits[2]); |
|
||||||
var secondBits = bits[5].split("\."); |
|
||||||
d.setHours(bits[3], bits[4], secondBits[0]); |
|
||||||
if (secondBits.length > 1) |
|
||||||
d.setMilliseconds(secondBits[1]); |
|
||||||
|
|
||||||
// Get supplied time zone offset in minutes
|
|
||||||
if (bits[6] && bits[7]) { |
|
||||||
var offsetMinutes = bits[6] * MINUTES_PER_HOUR + Number(bits[7]); |
|
||||||
var sign = /\d\d-\d\d:\d\d$/.test(prop) ? '-' : '+'; |
|
||||||
|
|
||||||
// Apply the sign
|
|
||||||
offsetMinutes = 0 + (sign === '-' ? -1 * offsetMinutes : offsetMinutes); |
|
||||||
|
|
||||||
// Apply offset and local timezone
|
|
||||||
d.setMinutes(d.getMinutes() - offsetMinutes - d.getTimezoneOffset()); |
|
||||||
} else if (prop.indexOf("Z", prop.length - 1) !== -1) { |
|
||||||
d = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds())); |
|
||||||
} |
|
||||||
|
|
||||||
// d is now a local time equivalent to the supplied time
|
|
||||||
return d; |
|
||||||
} |
|
||||||
|
|
||||||
function convertToDateIfRequired(value, childName, fullPath) { |
|
||||||
if (config.datetimeAccessFormPaths.length > 0) { |
|
||||||
var pathWithoutTextNode = fullPath.split("\.#")[0]; |
|
||||||
|
|
||||||
for (var i = 0; i < config.datetimeAccessFormPaths.length; i++) { |
|
||||||
var candidatePath = config.datetimeAccessFormPaths[i]; |
|
||||||
if (typeof candidatePath === "string") { |
|
||||||
if (candidatePath === pathWithoutTextNode) |
|
||||||
return xmlDateTimeToDate(value); |
|
||||||
} else if (candidatePath instanceof RegExp) { |
|
||||||
if (candidatePath.test(pathWithoutTextNode)) |
|
||||||
return xmlDateTimeToDate(value); |
|
||||||
} else if (typeof candidatePath === "function") { |
|
||||||
if (candidatePath(pathWithoutTextNode)) |
|
||||||
return xmlDateTimeToDate(value); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return value; |
|
||||||
} |
|
||||||
|
|
||||||
function deserializeRootElementChildren(rootElement) { |
|
||||||
var result = {}; |
|
||||||
var children = rootElement.childNodes; |
|
||||||
|
|
||||||
// Alternative for firstElementChild which is not supported in some environments
|
|
||||||
for (var i = 0; i < children.length; i++) { |
|
||||||
var child = children.item(i); |
|
||||||
if (child.nodeType === DOMNodeTypes.ELEMENT_NODE) { |
|
||||||
var childName = getDomNodeLocalName(child); |
|
||||||
|
|
||||||
if (config.ignoreRoot) |
|
||||||
result = deserializeDomChildren(child, childName); |
|
||||||
else |
|
||||||
result[childName] = deserializeDomChildren(child, childName); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
function deserializeElementChildren(element, elementPath) { |
|
||||||
var result = {}; |
|
||||||
result.__cnt = 0; |
|
||||||
|
|
||||||
var nodeChildren = element.childNodes; |
|
||||||
|
|
||||||
// Child nodes.
|
|
||||||
for (var iChild = 0; iChild < nodeChildren.length; iChild++) { |
|
||||||
var child = nodeChildren.item(iChild); |
|
||||||
var childName = getDomNodeLocalName(child); |
|
||||||
|
|
||||||
if (child.nodeType === DOMNodeTypes.COMMENT_NODE) |
|
||||||
continue; |
|
||||||
|
|
||||||
result.__cnt++; |
|
||||||
|
|
||||||
// We deliberately do not accept everything falsey here because
|
|
||||||
// elements that resolve to empty string should still be preserved.
|
|
||||||
if (result[childName] == null) { |
|
||||||
result[childName] = deserializeDomChildren(child, elementPath + "." + childName); |
|
||||||
ensureProperArrayAccessForm(result, childName, elementPath + "." + childName); |
|
||||||
} else { |
|
||||||
if (!(result[childName] instanceof Array)) { |
|
||||||
result[childName] = [result[childName]]; |
|
||||||
ensureProperArrayAccessForm(result, childName, elementPath + "." + childName); |
|
||||||
} |
|
||||||
|
|
||||||
result[childName][result[childName].length] = deserializeDomChildren(child, elementPath + "." + childName); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Attributes
|
|
||||||
for (var iAttribute = 0; iAttribute < element.attributes.length; iAttribute++) { |
|
||||||
var attribute = element.attributes.item(iAttribute); |
|
||||||
result.__cnt++; |
|
||||||
|
|
||||||
var adjustedValue = attribute.value; |
|
||||||
for (var iConverter = 0; iConverter < config.attributeConverters.length; iConverter++) { |
|
||||||
var converter = config.attributeConverters[iConverter]; |
|
||||||
if (converter.test.call(null, attribute.name, attribute.value)) |
|
||||||
adjustedValue = converter.convert.call(null, attribute.name, attribute.value); |
|
||||||
} |
|
||||||
|
|
||||||
result[config.attributePrefix + attribute.name] = adjustedValue; |
|
||||||
} |
|
||||||
|
|
||||||
// Node namespace prefix
|
|
||||||
var namespacePrefix = getDomNodeNamespacePrefix(element); |
|
||||||
if (namespacePrefix) { |
|
||||||
result.__cnt++; |
|
||||||
result.__prefix = namespacePrefix; |
|
||||||
} |
|
||||||
|
|
||||||
if (result["#text"]) { |
|
||||||
result.__text = result["#text"]; |
|
||||||
|
|
||||||
if (result.__text instanceof Array) { |
|
||||||
result.__text = result.__text.join("\n"); |
|
||||||
} |
|
||||||
|
|
||||||
if (config.escapeMode) |
|
||||||
result.__text = unescapeXmlChars(result.__text); |
|
||||||
|
|
||||||
if (config.stripWhitespaces) |
|
||||||
result.__text = result.__text.trim(); |
|
||||||
|
|
||||||
delete result["#text"]; |
|
||||||
|
|
||||||
if (config.arrayAccessForm === "property") |
|
||||||
delete result["#text_asArray"]; |
|
||||||
|
|
||||||
result.__text = convertToDateIfRequired(result.__text, "#text", elementPath + ".#text"); |
|
||||||
} |
|
||||||
|
|
||||||
if (result.hasOwnProperty('#cdata-section')) { |
|
||||||
result.__cdata = result["#cdata-section"]; |
|
||||||
delete result["#cdata-section"]; |
|
||||||
|
|
||||||
if (config.arrayAccessForm === "property") |
|
||||||
delete result["#cdata-section_asArray"]; |
|
||||||
} |
|
||||||
|
|
||||||
if (result.__cnt === 1 && result.__text) { |
|
||||||
result = result.__text; |
|
||||||
} else if (result.__cnt === 0 && config.emptyNodeForm === "text") { |
|
||||||
result = ''; |
|
||||||
} else if (result.__cnt > 1 && result.__text !== undefined && config.skipEmptyTextNodesForObj) { |
|
||||||
if (config.stripWhitespaces && result.__text === "" || result.__text.trim() === "") { |
|
||||||
delete result.__text; |
|
||||||
} |
|
||||||
} |
|
||||||
delete result.__cnt; |
|
||||||
|
|
||||||
if (!config.keepCData && (!result.hasOwnProperty('__text') && result.hasOwnProperty('__cdata'))) { |
|
||||||
return (result.__cdata ? result.__cdata : ''); |
|
||||||
} |
|
||||||
|
|
||||||
if (config.enableToStringFunc && (result.__text || result.__cdata)) { |
|
||||||
result.toString = function toString() { |
|
||||||
return (this.__text ? this.__text : '') + (this.__cdata ? this.__cdata : ''); |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
function deserializeDomChildren(node, parentPath) { |
|
||||||
if (node.nodeType === DOMNodeTypes.DOCUMENT_NODE) { |
|
||||||
return deserializeRootElementChildren(node); |
|
||||||
} else if (node.nodeType === DOMNodeTypes.ELEMENT_NODE) { |
|
||||||
return deserializeElementChildren(node, parentPath); |
|
||||||
} else if (node.nodeType === DOMNodeTypes.TEXT_NODE || node.nodeType === DOMNodeTypes.CDATA_SECTION_NODE) { |
|
||||||
return node.nodeValue; |
|
||||||
} else { |
|
||||||
return null; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function serializeStartTag(jsObject, elementName, attributeNames, selfClosing) { |
|
||||||
var resultStr = "<" + ((jsObject && jsObject.__prefix) ? (jsObject.__prefix + ":") : "") + elementName; |
|
||||||
|
|
||||||
if (attributeNames) { |
|
||||||
for (var i = 0; i < attributeNames.length; i++) { |
|
||||||
var attributeName = attributeNames[i]; |
|
||||||
var attributeValue = jsObject[attributeName]; |
|
||||||
|
|
||||||
if (config.escapeMode) |
|
||||||
attributeValue = escapeXmlChars(attributeValue); |
|
||||||
|
|
||||||
resultStr += " " + attributeName.substr(config.attributePrefix.length) + "="; |
|
||||||
|
|
||||||
if (config.useDoubleQuotes) |
|
||||||
resultStr += '"' + attributeValue + '"'; |
|
||||||
else |
|
||||||
resultStr += "'" + attributeValue + "'"; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (!selfClosing) |
|
||||||
resultStr += ">"; |
|
||||||
else |
|
||||||
resultStr += " />"; |
|
||||||
|
|
||||||
return resultStr; |
|
||||||
} |
|
||||||
|
|
||||||
function serializeEndTag(jsObject, elementName) { |
|
||||||
return "</" + ((jsObject && jsObject.__prefix) ? (jsObject.__prefix + ":") : "") + elementName + ">"; |
|
||||||
} |
|
||||||
|
|
||||||
function endsWith(str, suffix) { |
|
||||||
return str.indexOf(suffix, str.length - suffix.length) !== -1; |
|
||||||
} |
|
||||||
|
|
||||||
function isSpecialProperty(jsonObj, propertyName) { |
|
||||||
if ((config.arrayAccessForm === "property" && endsWith(propertyName.toString(), ("_asArray"))) |
|
||||||
|| propertyName.toString().indexOf(config.attributePrefix) === 0 |
|
||||||
|| propertyName.toString().indexOf("__") === 0 |
|
||||||
|| (jsonObj[propertyName] instanceof Function)) |
|
||||||
return true; |
|
||||||
else |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
function getDataElementCount(jsObject) { |
|
||||||
var count = 0; |
|
||||||
|
|
||||||
if (jsObject instanceof Object) { |
|
||||||
for (var propertyName in jsObject) { |
|
||||||
if (isSpecialProperty(jsObject, propertyName)) |
|
||||||
continue; |
|
||||||
|
|
||||||
count++; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return count; |
|
||||||
} |
|
||||||
|
|
||||||
function getDataAttributeNames(jsObject) { |
|
||||||
var names = []; |
|
||||||
|
|
||||||
if (jsObject instanceof Object) { |
|
||||||
for (var attributeName in jsObject) { |
|
||||||
if (attributeName.toString().indexOf("__") === -1 |
|
||||||
&& attributeName.toString().indexOf(config.attributePrefix) === 0) { |
|
||||||
names.push(attributeName); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return names; |
|
||||||
} |
|
||||||
|
|
||||||
function serializeComplexTextNodeContents(textNode) { |
|
||||||
var result = ""; |
|
||||||
|
|
||||||
if (textNode.__cdata) { |
|
||||||
result += "<![CDATA[" + textNode.__cdata + "]]>"; |
|
||||||
} |
|
||||||
|
|
||||||
if (textNode.__text) { |
|
||||||
if (config.escapeMode) |
|
||||||
result += escapeXmlChars(textNode.__text); |
|
||||||
else |
|
||||||
result += textNode.__text; |
|
||||||
} |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
function serializeTextNodeContents(textNode) { |
|
||||||
var result = ""; |
|
||||||
|
|
||||||
if (textNode instanceof Object) { |
|
||||||
result += serializeComplexTextNodeContents(textNode); |
|
||||||
} else if (textNode !== null) { |
|
||||||
if (config.escapeMode) |
|
||||||
result += escapeXmlChars(textNode); |
|
||||||
else |
|
||||||
result += textNode; |
|
||||||
} |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
function serializeArray(elementArray, elementName, attributes) { |
|
||||||
var result = ""; |
|
||||||
|
|
||||||
if (elementArray.length === 0) { |
|
||||||
result += serializeStartTag(elementArray, elementName, attributes, true); |
|
||||||
} else { |
|
||||||
for (var i = 0; i < elementArray.length; i++) { |
|
||||||
result += serializeJavaScriptObject(elementArray[i], elementName, getDataAttributeNames(elementArray[i])); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
function serializeJavaScriptObject(element, elementName, attributes) { |
|
||||||
var result = ""; |
|
||||||
|
|
||||||
if ((element === undefined || element === null || element === '') && config.selfClosingElements) { |
|
||||||
result += serializeStartTag(element, elementName, attributes, true); |
|
||||||
} else if (typeof element === 'object') { |
|
||||||
if (Object.prototype.toString.call(element) === '[object Array]') { |
|
||||||
result += serializeArray(element, elementName, attributes); |
|
||||||
} else if (element instanceof Date) { |
|
||||||
result += serializeStartTag(element, elementName, attributes, false); |
|
||||||
result += element.toISOString(); |
|
||||||
result += serializeEndTag(element, elementName); |
|
||||||
} else { |
|
||||||
var childElementCount = getDataElementCount(element); |
|
||||||
if (childElementCount > 0 || element.__text || element.__cdata) { |
|
||||||
result += serializeStartTag(element, elementName, attributes, false); |
|
||||||
result += serializeJavaScriptObjectChildren(element); |
|
||||||
result += serializeEndTag(element, elementName); |
|
||||||
} else if (config.selfClosingElements) { |
|
||||||
result += serializeStartTag(element, elementName, attributes, true); |
|
||||||
} else { |
|
||||||
result += serializeStartTag(element, elementName, attributes, false); |
|
||||||
result += serializeEndTag(element, elementName); |
|
||||||
} |
|
||||||
} |
|
||||||
} else { |
|
||||||
result += serializeStartTag(element, elementName, attributes, false); |
|
||||||
result += serializeTextNodeContents(element); |
|
||||||
result += serializeEndTag(element, elementName); |
|
||||||
} |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
function serializeJavaScriptObjectChildren(jsObject) { |
|
||||||
var result = ""; |
|
||||||
|
|
||||||
var elementCount = getDataElementCount(jsObject); |
|
||||||
|
|
||||||
if (elementCount > 0) { |
|
||||||
for (var elementName in jsObject) { |
|
||||||
if (isSpecialProperty(jsObject, elementName)) |
|
||||||
continue; |
|
||||||
|
|
||||||
var element = jsObject[elementName]; |
|
||||||
var attributes = getDataAttributeNames(element); |
|
||||||
|
|
||||||
result += serializeJavaScriptObject(element, elementName, attributes); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
result += serializeTextNodeContents(jsObject); |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
function parseXml(xml) { |
|
||||||
if (xml === undefined) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
if (typeof xml !== "string") { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
var parser = null; |
|
||||||
var domNode = null; |
|
||||||
|
|
||||||
if (CustomDOMParser) { |
|
||||||
// This branch is used for node.js, with the xmldom parser.
|
|
||||||
parser = new CustomDOMParser(); |
|
||||||
|
|
||||||
domNode = parser.parseFromString(xml, "text/xml"); |
|
||||||
} else if (window && window.DOMParser) { |
|
||||||
parser = new window.DOMParser(); |
|
||||||
var parsererrorNS = null; |
|
||||||
|
|
||||||
var isIEParser = window.ActiveXObject || "ActiveXObject" in window; |
|
||||||
|
|
||||||
// IE9+ now is here
|
|
||||||
if (!isIEParser) { |
|
||||||
try { |
|
||||||
parsererrorNS = parser.parseFromString("INVALID", "text/xml").childNodes[0].namespaceURI; |
|
||||||
} catch (err) { |
|
||||||
parsererrorNS = null; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
try { |
|
||||||
domNode = parser.parseFromString(xml, "text/xml"); |
|
||||||
if (parsererrorNS !== null && domNode.getElementsByTagNameNS(parsererrorNS, "parsererror").length > 0) { |
|
||||||
domNode = null; |
|
||||||
} |
|
||||||
} catch (err) { |
|
||||||
domNode = null; |
|
||||||
} |
|
||||||
} else { |
|
||||||
// IE :(
|
|
||||||
if (xml.indexOf("<?") === 0) { |
|
||||||
xml = xml.substr(xml.indexOf("?>") + 2); |
|
||||||
} |
|
||||||
|
|
||||||
/* global ActiveXObject */ |
|
||||||
domNode = new ActiveXObject("Microsoft.XMLDOM"); |
|
||||||
domNode.async = "false"; |
|
||||||
domNode.loadXML(xml); |
|
||||||
} |
|
||||||
|
|
||||||
return domNode; |
|
||||||
} |
|
||||||
|
|
||||||
this.asArray = function asArray(prop) { |
|
||||||
if (prop === undefined || prop === null) { |
|
||||||
return []; |
|
||||||
} else if (prop instanceof Array) { |
|
||||||
return prop; |
|
||||||
} else { |
|
||||||
return [prop]; |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
this.toXmlDateTime = function toXmlDateTime(dt) { |
|
||||||
if (dt instanceof Date) { |
|
||||||
return dt.toISOString(); |
|
||||||
} else if (typeof (dt) === 'number') { |
|
||||||
return new Date(dt).toISOString(); |
|
||||||
} else { |
|
||||||
return null; |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
this.asDateTime = function asDateTime(prop) { |
|
||||||
if (typeof (prop) === "string") { |
|
||||||
return xmlDateTimeToDate(prop); |
|
||||||
} else { |
|
||||||
return prop; |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
/* |
|
||||||
Internally the logic works in a cycle: |
|
||||||
DOM->JS - implemented by custom logic (deserialization). |
|
||||||
JS->XML - implemented by custom logic (serialization). |
|
||||||
XML->DOM - implemented by browser. |
|
||||||
*/ |
|
||||||
|
|
||||||
// Transformns an XML string into DOM-tree
|
|
||||||
this.xml2dom = function xml2dom(xml) { |
|
||||||
return parseXml(xml); |
|
||||||
}; |
|
||||||
|
|
||||||
// Transforms a DOM tree to JavaScript objects.
|
|
||||||
this.dom2js = function dom2js(domNode) { |
|
||||||
return deserializeDomChildren(domNode, null); |
|
||||||
}; |
|
||||||
|
|
||||||
// Transforms JavaScript objects to a DOM tree.
|
|
||||||
this.js2dom = function js2dom(jsObject) { |
|
||||||
var xml = this.js2xml(jsObject); |
|
||||||
return parseXml(xml); |
|
||||||
}; |
|
||||||
|
|
||||||
// Transformns an XML string into JavaScript objects.
|
|
||||||
this.xml2js = function xml2js(xml) { |
|
||||||
var domNode = parseXml(xml); |
|
||||||
if (domNode != null) |
|
||||||
return this.dom2js(domNode); |
|
||||||
else |
|
||||||
return null; |
|
||||||
}; |
|
||||||
|
|
||||||
// Transforms JavaScript objects into an XML string.
|
|
||||||
this.js2xml = function js2xml(jsObject) { |
|
||||||
return serializeJavaScriptObjectChildren(jsObject); |
|
||||||
}; |
|
||||||
|
|
||||||
this.getVersion = function getVersion() { |
|
||||||
return VERSION; |
|
||||||
}; |
|
||||||
}; |
|
||||||
}); |
|
Loading…
Reference in new issue