/bootstrap/001_bootstrap/bootstrap/node_modules/x2js/tests_config.js |
@@ -0,0 +1,218 @@ |
(function (root, factory) { |
'use strict'; |
|
if (typeof module === 'object' && module.exports) { |
// Node. Does not work with strict CommonJS, but only CommonJS-like |
// environments that support module.exports, like Node. |
factory(require('./x2js'), require('qunit-cli')); |
} else { |
// Browser globals (root is window) |
factory(root.X2JS, root.QUnit); |
} |
})(this, function (X2JS, QUnit) { |
'use strict'; |
|
// Test cases are full of magic numbers and that's fine. |
/* eslint-disable no-magic-numbers */ |
|
QUnit.module('Configuration options'); |
|
QUnit.test('Default attribute prefix', function (assert) { |
var xml = '<document><element attribute="value" /></document>'; |
var x = new X2JS(); |
var js = x.xml2js(xml); |
|
assert.ok(js.document); |
assert.ok(js.document.element); |
assert.ok(js.document.element._attribute); |
assert.strictEqual(js.document.element._attribute, 'value'); |
}); |
|
QUnit.test('Empty attribute prefix', function (assert) { |
var xml = '<document><element attribute="value" /></document>'; |
var x = new X2JS({ |
'attributePrefix': '' |
}); |
var js = x.xml2js(xml); |
|
assert.ok(js.document); |
assert.ok(js.document.element); |
assert.ok(js.document.element.attribute); |
assert.strictEqual(js.document.element.attribute, 'value'); |
}); |
|
QUnit.test('Custom nonempty attribute prefix', function (assert) { |
var xml = '<document><element attribute="value" /></document>'; |
var x = new X2JS({ |
'attributePrefix': '$$' |
}); |
var js = x.xml2js(xml); |
|
assert.ok(js.document); |
assert.ok(js.document.element); |
assert.ok(js.document.element.$$attribute); |
assert.strictEqual(js.document.element.$$attribute, 'value'); |
}); |
|
QUnit.test('Attribute converters run but only when appropriate', function (assert) { |
var xml = '<Root><element test1="FAIL" test2="success 2.1">first</element><element test1="FAIL 1.2" test2="success 2.2">second</element></Root>'; |
var x = new X2JS({ |
'attributeConverters': [ |
{ |
'test': function (name, value) { |
return name === 'test1'; |
}, |
'convert': function (name, value) { |
return 'success 1.*'; |
} |
} |
] |
}); |
var js = x.xml2js(xml); |
|
assert.ok(js.Root); |
assert.ok(js.Root.element); |
assert.ok(js.Root.element.length); |
assert.ok(js.Root.element[0]); |
assert.ok(js.Root.element[0]._test1); |
assert.ok(js.Root.element[0]._test2); |
assert.strictEqual(js.Root.element[0]._test1, 'success 1.*'); |
assert.strictEqual(js.Root.element[0]._test2, 'success 2.1'); |
assert.strictEqual(js.Root.element[1]._test1, 'success 1.*'); |
assert.strictEqual(js.Root.element[1]._test2, 'success 2.2'); |
}); |
|
QUnit.test('Root element is dropped with ignoreRoot=true', function (assert) { |
var xml = '<document><element attribute="value" /></document>'; |
var x = new X2JS({ |
'ignoreRoot': true |
}); |
var js = x.xml2js(xml); |
|
assert.notOk(js.document); |
assert.ok(js.element); |
}); |
|
QUnit.test('Array access-form override via path', function (assert) { |
var xml = '<document><element attribute="value" /></document>'; |
var x = new X2JS({ |
'arrayAccessFormPaths': [ |
'document.element' |
] |
}); |
var js = x.xml2js(xml); |
|
assert.ok(js.document); |
assert.ok(js.document.element); |
assert.ok(js.document.element.length); |
assert.strictEqual(js.document.element.length, 1); |
assert.ok(js.document.element[0], 'value'); |
}); |
|
QUnit.test('Array access-form override via regex', function (assert) { |
var xml = '<document><element attribute="value" /></document>'; |
var x = new X2JS({ |
'arrayAccessFormPaths': [ |
/.*\.element$/ |
] |
}); |
var js = x.xml2js(xml); |
|
assert.ok(js.document); |
assert.ok(js.document.element); |
assert.ok(js.document.element.length); |
assert.strictEqual(js.document.element.length, 1); |
assert.ok(js.document.element[0], 'value'); |
}); |
|
QUnit.test('Array access-form override via function', function (assert) { |
var xml = '<document><element attribute="value" /></document>'; |
var x = new X2JS({ |
'arrayAccessFormPaths': [ |
function (elementName, elementPath) { |
return elementName === 'element'; |
} |
] |
}); |
var js = x.xml2js(xml); |
|
assert.ok(js.document); |
assert.ok(js.document.element); |
assert.ok(js.document.element.length); |
assert.strictEqual(js.document.element.length, 1); |
assert.ok(js.document.element[0], 'value'); |
}); |
|
QUnit.test('Datetime parsing via path', function (assert) { |
var xml = '<document><datetimeElement>2002-10-10T12:00:00+04:00</datetimeElement></document>'; |
var x = new X2JS({ |
'datetimeAccessFormPaths': [ |
'document.datetimeElement' |
] |
}); |
var js = x.xml2js(xml); |
|
assert.ok(js.document); |
assert.ok(js.document.datetimeElement); |
assert.ok(js.document.datetimeElement instanceof Date); |
assert.strictEqual(js.document.datetimeElement.getFullYear(), 2002); |
}); |
|
QUnit.test('Datetime parsing via regex', function (assert) { |
var xml = '<document><datetimeElement>2002-10-10T12:00:00+04:00</datetimeElement></document>'; |
var x = new X2JS({ |
'datetimeAccessFormPaths': [ |
/.*\.datetimeElement$/ |
] |
}); |
var js = x.xml2js(xml); |
|
assert.ok(js.document); |
assert.ok(js.document.datetimeElement); |
assert.ok(js.document.datetimeElement instanceof Date); |
assert.strictEqual(js.document.datetimeElement.getFullYear(), 2002); |
}); |
|
QUnit.test('Datetime parsing via function', function (assert) { |
var xml = '<document><datetimeElement>2002-10-10T12:00:00+04:00</datetimeElement></document>'; |
var x = new X2JS({ |
'datetimeAccessFormPaths': [ |
function (elementPath) { |
return elementPath === 'document.datetimeElement'; |
} |
] |
}); |
var js = x.xml2js(xml); |
|
assert.ok(js.document); |
assert.ok(js.document.datetimeElement); |
assert.ok(js.document.datetimeElement instanceof Date); |
assert.strictEqual(js.document.datetimeElement.getFullYear(), 2002); |
}); |
|
QUnit.test('Datetime parsing in different formats', function (assert) { |
var xml = '<document>' + |
'<datetimeElement>2002-10-10T12:00:00+04:00</datetimeElement>' + |
'<datetimeElement>2002-10-10T12:00:00Z</datetimeElement>' + |
'<datetimeElement>2002-10-10T12:00:00</datetimeElement>' + |
'<datetimeElement>2002-10-10T12:00:00Z</datetimeElement>' + |
'</document>'; |
var x = new X2JS({ |
'datetimeAccessFormPaths': [ |
'document.datetimeElement' |
] |
}); |
var js = x.xml2js(xml); |
|
assert.ok(js.document); |
assert.ok(js.document.datetimeElement); |
assert.ok(js.document.datetimeElement.length); |
assert.strictEqual(js.document.datetimeElement.length, 4); |
|
for (var i = 0; i < js.document.datetimeElement.length; i++) { |
assert.ok(js.document.datetimeElement[i]); |
assert.ok(js.document.datetimeElement[i] instanceof Date); |
assert.strictEqual(js.document.datetimeElement[i].getFullYear(), 2002); |
} |
}); |
}); |
/bootstrap/001_bootstrap/bootstrap/node_modules/x2js/tests_js2xml.js |
@@ -0,0 +1,336 @@ |
(function (root, factory) { |
'use strict'; |
|
if (typeof module === 'object' && module.exports) { |
// Node. Does not work with strict CommonJS, but only CommonJS-like |
// environments that support module.exports, like Node. |
factory(require('./x2js'), require('qunit-cli')); |
} else { |
// Browser globals (root is window) |
factory(root.X2JS, root.QUnit); |
} |
})(this, function (X2JS, QUnit) { |
'use strict'; |
|
QUnit.module('Converting JavaScript objects to XML'); |
|
QUnit.test('Element with attribute', function (assert) { |
var js = { |
'document': { |
'element': { |
'_attribute': 'value' |
} |
} |
}; |
var x = new X2JS(); |
var xml = x.js2xml(js); |
|
var expected = '<document>' + |
'<element attribute="value" />' + |
'</document>'; |
|
// Implementation does not guarantee formatting so the test is somewhat fragile. |
assert.strictEqual(xml, expected); |
}); |
|
QUnit.test('Element with attribute and selfClosingElements set to false', function (assert) { |
var js = { |
'document': { |
'element': { |
'_attribute': 'value' |
} |
} |
}; |
var x = new X2JS({ |
'selfClosingElements': false |
}); |
var xml = x.js2xml(js); |
|
var expected = '<document>' + |
'<element attribute="value"></element>' + |
'</document>'; |
|
// Implementation does not guarantee formatting so the test is somewhat fragile. |
assert.strictEqual(xml, expected); |
}); |
|
QUnit.test('Element with attribute and selfClosingElements set to true', function (assert) { |
var js = { |
'document': { |
'element': { |
'_attribute': 'value' |
} |
} |
}; |
var x = new X2JS({ |
'selfClosingElements': true |
}); |
var xml = x.js2xml(js); |
|
var expected = '<document>' + |
'<element attribute="value" />' + |
'</document>'; |
|
// Implementation does not guarantee formatting so the test is somewhat fragile. |
assert.strictEqual(xml, expected); |
}); |
|
QUnit.test('Element with attribute containing XML characters', function (assert) { |
var js = { |
'document': { |
'element': { |
'_attribute': 'va&lue<' |
} |
} |
}; |
var x = new X2JS(); |
var xml = x.js2xml(js); |
|
var expected = '<document>' + |
'<element attribute="va&lue<" />' + |
'</document>'; |
|
// Implementation does not guarantee formatting so the test is somewhat fragile. |
assert.strictEqual(xml, expected); |
}); |
|
QUnit.test('Just a string', function (assert) { |
var js = { |
'document': { |
'elementY': 'hello there' |
} |
}; |
var x = new X2JS(); |
var xml = x.js2xml(js); |
|
var expected = '<document>' + |
'<elementY>hello there</elementY>' + |
'</document>'; |
|
// Implementation does not guarantee formatting so the test is somewhat fragile. |
assert.strictEqual(xml, expected); |
}); |
|
QUnit.test('String with XML characters', function (assert) { |
var js = { |
'document': { |
'elementY': 'hello &there<' |
} |
}; |
var x = new X2JS(); |
var xml = x.js2xml(js); |
|
var expected = '<document>' + |
'<elementY>hello &there<</elementY>' + |
'</document>'; |
|
// Implementation does not guarantee formatting so the test is somewhat fragile. |
assert.strictEqual(xml, expected); |
}); |
|
QUnit.test('CDATA', function (assert) { |
var js = { |
'document': { |
'elementZ': { '__cdata': 'hello again' } |
} |
}; |
var x = new X2JS(); |
var xml = x.js2xml(js); |
|
var expected = '<document>' + |
'<elementZ><![CDATA[hello again]]></elementZ>' + |
'</document>'; |
|
// Implementation does not guarantee formatting so the test is somewhat fragile. |
assert.strictEqual(xml, expected); |
}); |
|
QUnit.test('CDATA with XML characters', function (assert) { |
var js = { |
'document': { |
'elementZ': { '__cdata': 'hello &again<' } |
} |
}; |
var x = new X2JS(); |
var xml = x.js2xml(js); |
|
var expected = '<document>' + |
'<elementZ><![CDATA[hello &again<]]></elementZ>' + |
'</document>'; |
|
// Implementation does not guarantee formatting so the test is somewhat fragile. |
assert.strictEqual(xml, expected); |
}); |
|
QUnit.test('Empty string as value', function (assert) { |
var js = { |
'document': { |
'elementU': '' |
} |
}; |
var x = new X2JS(); |
var xml = x.js2xml(js); |
|
var expected = '<document>' + |
'<elementU />' + |
'</document>'; |
|
// Implementation does not guarantee formatting so the test is somewhat fragile. |
assert.strictEqual(xml, expected); |
}); |
|
QUnit.test('Zero as value', function (assert) { |
var js = { |
'document': { |
'element': 0 |
} |
}; |
var x = new X2JS(); |
var xml = x.js2xml(js); |
|
var expected = '<document>' + |
'<element>0</element>' + |
'</document>'; |
|
assert.strictEqual(xml, expected); |
}); |
|
QUnit.test('Empty string as value with selfClosingElements set to false', function (assert) { |
var js = { |
'document': { |
'elementU': '' |
} |
}; |
var x = new X2JS({ |
'selfClosingElements': false |
}); |
|
var xml = x.js2xml(js); |
|
var expected = '<document>' + |
'<elementU></elementU>' + |
'</document>'; |
|
// Implementation does not guarantee formatting so the test is somewhat fragile. |
assert.strictEqual(xml, expected); |
}); |
|
QUnit.test('Basic array', function (assert) { |
var js = { |
'document': { |
'elementV': [ |
{ 'x': 't' }, |
{ 'm': 'n' } |
] |
} |
}; |
var x = new X2JS(); |
var xml = x.js2xml(js); |
|
var expected = '<document>' + |
'<elementV><x>t</x></elementV>' + |
'<elementV><m>n</m></elementV>' + |
'</document>'; |
|
// Implementation does not guarantee formatting so the test is somewhat fragile. |
assert.strictEqual(xml, expected); |
}); |
|
QUnit.test('Array of empty strings', function (assert) { |
var js = { |
'document': { |
'elementX': ['', ''] |
} |
}; |
var x = new X2JS(); |
var xml = x.js2xml(js); |
|
var expected = '<document>' + |
'<elementX />' + |
'<elementX />' + |
'</document>'; |
|
// Implementation does not guarantee formatting so the test is somewhat fragile. |
assert.strictEqual(xml, expected); |
}); |
|
QUnit.test('Array of empty strings with selfClosingElements set to false', function (assert) { |
var js = { |
'document': { |
'elementX': ['', ''] |
} |
}; |
var x = new X2JS({ |
'selfClosingElements': false |
}); |
var xml = x.js2xml(js); |
|
var expected = '<document>' + |
'<elementX></elementX>' + |
'<elementX></elementX>' + |
'</document>'; |
|
// Implementation does not guarantee formatting so the test is somewhat fragile. |
assert.strictEqual(xml, expected); |
}); |
|
QUnit.test('Attributes in array', function (assert) { |
var js = { |
'document': { |
'elementV': [ |
{ |
'x': 't', |
'_a': 'a' |
}, |
{ |
'm': 'n', |
'_b': 'b' |
} |
] |
} |
}; |
var x = new X2JS(); |
var xml = x.js2xml(js); |
|
var expected = '<document>' + |
'<elementV a="a"><x>t</x></elementV>' + |
'<elementV b="b"><m>n</m></elementV>' + |
'</document>'; |
|
// Implementation does not guarantee formatting so the test is somewhat fragile. |
assert.strictEqual(xml, expected); |
}); |
|
QUnit.test('Namespaces', function (assert) { |
var js = { |
'document': { |
'__prefix': 'ns', |
'_ns:xmlns': 'http://example.com', |
'elementV': [ |
{ |
'__prefix': 'ns', |
'x': 't', |
'_a': 'a' |
}, |
{ |
'm': { |
'__text': 'n', |
'__prefix': 'ns' |
}, |
'_b': 'b' |
} |
] |
} |
}; |
var x = new X2JS(); |
var xml = x.js2xml(js); |
|
var expected = '<ns:document ns:xmlns="http://example.com">' + |
'<ns:elementV a="a"><x>t</x></ns:elementV>' + |
'<elementV b="b"><ns:m>n</ns:m></elementV>' + |
'</ns:document>'; |
|
// Implementation does not guarantee formatting so the test is somewhat fragile. |
assert.strictEqual(xml, expected); |
}); |
}); |
/bootstrap/001_bootstrap/bootstrap/node_modules/x2js/x2js.js |
@@ -0,0 +1,732 @@ |
/* |
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. |
*/ |
|
/* |
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"; |
|
/* 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(); |
} |
})(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.1"; |
|
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; |
}; |
}; |
}); |