You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
742 lines
22 KiB
742 lines
22 KiB
/*
|
|
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;
|
|
};
|
|
};
|
|
});
|
|
|