@mitre/jsonix
Version:
Jsonix (JSON interfaces for XML) is a JavaScript library which allows converting between XML and JSON structures.
1,610 lines (1,560 loc) • 216 kB
JavaScript
var _jsonix_factory = function (_jsonix_xmldom, _jsonix_xmlhttprequest, _jsonix_fs) {
// Complete Jsonix script is included below
var Jsonix = {
singleFile: true
};
Jsonix.Util = {};
Jsonix.Util.extend = function (destination, source) {
destination = destination || {};
if (source) {
/*jslint forin: true */
for (var property in source) {
var value = source[property];
if (value !== undefined) {
destination[property] = value;
}
}
/**
* IE doesn't include the toString property when iterating over an
* object's properties with the for(property in object) syntax.
* Explicitly check if the source has its own toString property.
*/
/*
* FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
* prototype object" when calling hawOwnProperty if the source object is
* an instance of window.Event.
*/
// REWORK
// Node.js
var sourceIsEvt = typeof window !== 'undefined' && window !== null && typeof window.Event === "function" && source instanceof window.Event;
if (!sourceIsEvt && source.hasOwnProperty && source.hasOwnProperty('toString')) {
destination.toString = source.toString;
}
}
return destination;
};
Jsonix.Class = function () {
var Class = function () {
this.initialize.apply(this, arguments);
};
var extended = {};
var empty = function () {
};
var parent, initialize, Type;
for (var i = 0, len = arguments.length; i < len; ++i) {
Type = arguments[i];
if (typeof Type == "function") {
// make the class passed as the first argument the superclass
if (i === 0 && len > 1) {
initialize = Type.prototype.initialize;
// replace the initialize method with an empty function,
// because we do not want to create a real instance here
Type.prototype.initialize = empty;
// the line below makes sure that the new class has a
// superclass
extended = new Type();
// restore the original initialize method
if (initialize === undefined) {
delete Type.prototype.initialize;
} else {
Type.prototype.initialize = initialize;
}
}
// get the prototype of the superclass
parent = Type.prototype;
} else {
// in this case we're extending with the prototype
parent = Type;
}
Jsonix.Util.extend(extended, parent);
}
Class.prototype = extended;
return Class;
};
Jsonix.XML = {
XMLNS_NS: 'http://www.w3.org/2000/xmlns/',
XMLNS_P: 'xmlns'
};
Jsonix.DOM = {
isDomImplementationAvailable: function () {
if (typeof _jsonix_xmldom !== 'undefined') {
return true;
} else if (typeof document !== 'undefined' && Jsonix.Util.Type.exists(document.implementation) && Jsonix.Util.Type.isFunction(document.implementation.createDocument)) {
return true;
} else {
return false;
}
},
createDocument: function () {
// REWORK
// Node.js
if (typeof _jsonix_xmldom !== 'undefined') {
return new (_jsonix_xmldom.DOMImplementation)().createDocument();
} else if (typeof document !== 'undefined' && Jsonix.Util.Type.exists(document.implementation) && Jsonix.Util.Type.isFunction(document.implementation.createDocument)) {
return document.implementation.createDocument('', '', null);
} else if (typeof ActiveXObject !== 'undefined') {
return new ActiveXObject('MSXML2.DOMDocument');
} else {
throw new Error('Error created the DOM document.');
}
},
serialize: function (node) {
Jsonix.Util.Ensure.ensureExists(node);
// REWORK
// Node.js
if (typeof _jsonix_xmldom !== 'undefined') {
return (new (_jsonix_xmldom).XMLSerializer()).serializeToString(node);
} else if (Jsonix.Util.Type.exists(XMLSerializer)) {
return (new XMLSerializer()).serializeToString(node);
} else if (Jsonix.Util.Type.exists(node.xml)) {
return node.xml;
} else {
throw new Error('Could not serialize the node, neither XMLSerializer nor the [xml] property were found.');
}
},
parse: function (text) {
Jsonix.Util.Ensure.ensureExists(text);
if (typeof _jsonix_xmldom !== 'undefined') {
return (new (_jsonix_xmldom).DOMParser()).parseFromString(text, 'application/xml');
} else if (typeof DOMParser != 'undefined') {
return (new DOMParser()).parseFromString(text, 'application/xml');
} else if (typeof ActiveXObject != 'undefined') {
var doc = Jsonix.DOM.createDocument('', '');
doc.loadXML(text);
return doc;
} else {
var url = 'data:text/xml;charset=utf-8,' + encodeURIComponent(text);
var request = new XMLHttpRequest();
request.open('GET', url, false);
if (request.overrideMimeType) {
request.overrideMimeType("text/xml");
}
request.send(null);
return request.responseXML;
}
},
load: function (url, callback, options) {
var request = Jsonix.Request.INSTANCE;
request.issue(
url,
function (transport) {
var result;
if (Jsonix.Util.Type.exists(transport.responseXML) && Jsonix.Util.Type.exists(transport.responseXML.documentElement)) {
result = transport.responseXML;
} else if (Jsonix.Util.Type.isString(transport.responseText)) {
result = Jsonix.DOM.parse(transport.responseText);
} else {
throw new Error('Response does not have valid [responseXML] or [responseText].');
}
callback(result);
}, function (transport) {
throw new Error('Could not retrieve XML from URL [' + url + '].');
}, options);
},
xlinkFixRequired: null,
isXlinkFixRequired: function () {
if (Jsonix.DOM.xlinkFixRequired === null) {
if (typeof navigator === 'undefined') {
Jsonix.DOM.xlinkFixRequired = false;
}
else if (!!navigator.userAgent && (/Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor))) {
var doc = Jsonix.DOM.createDocument();
var el = doc.createElement('test');
el.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', 'urn:test');
doc.appendChild(el);
var testString = Jsonix.DOM.serialize(doc);
Jsonix.DOM.xlinkFixRequired = (testString.indexOf('xmlns:xlink') === -1);
}
else {
Jsonix.DOM.xlinkFixRequired = false;
}
}
return Jsonix.DOM.xlinkFixRequired;
}
};
Jsonix.Request = Jsonix
.Class({
// REWORK
factories: [function () {
return new XMLHttpRequest();
}, function () {
return new ActiveXObject('Msxml2.XMLHTTP');
}, function () {
return new ActiveXObject("Msxml2.XMLHTTP.6.0");
}, function () {
return new ActiveXObject("Msxml2.XMLHTTP.3.0");
}, function () {
return new ActiveXObject('Microsoft.XMLHTTP');
}, function () {
// Node.js
if (typeof _jsonix_xmlhttprequest !== 'undefined') {
var XMLHttpRequest = _jsonix_xmlhttprequest.XMLHttpRequest;
return new XMLHttpRequest();
}
else {
return null;
}
}],
initialize: function () {
},
issue: function (url, onSuccess, onFailure, options) {
Jsonix.Util.Ensure.ensureString(url);
if (Jsonix.Util.Type.exists(onSuccess)) {
Jsonix.Util.Ensure.ensureFunction(onSuccess);
} else {
onSuccess = function () {
};
}
if (Jsonix.Util.Type.exists(onFailure)) {
Jsonix.Util.Ensure.ensureFunction(onFailure);
} else {
onFailure = function () {
};
}
if (Jsonix.Util.Type.exists(options)) {
Jsonix.Util.Ensure.ensureObject(options);
} else {
options = {};
}
var transport = this.createTransport();
var method = Jsonix.Util.Type.isString(options.method) ? options.method
: 'GET';
var async = Jsonix.Util.Type.isBoolean(options.async) ? options.async
: true;
var proxy = Jsonix.Util.Type.isString(options.proxy) ? options.proxy
: Jsonix.Request.PROXY;
var user = Jsonix.Util.Type.isString(options.user) ? options.user
: null;
var password = Jsonix.Util.Type.isString(options.password) ? options.password
: null;
if (Jsonix.Util.Type.isString(proxy) && (url.indexOf("http") === 0)) {
url = proxy + encodeURIComponent(url);
}
if (Jsonix.Util.Type.isString(user)) {
transport.open(method, url, async, user, password);
} else {
transport.open(method, url, async);
}
if (Jsonix.Util.Type.isObject(options.headers)) {
for (var header in options.headers) {
if (options.headers.hasOwnProperty(header)) {
transport.setRequestHeader(header,
options.headers[header]);
}
}
}
var data = Jsonix.Util.Type.exists(options.data) ? options.data
: null;
if (!async) {
transport.send(data);
this.handleTransport(transport, onSuccess, onFailure);
} else {
var that = this;
if (typeof window !== 'undefined') {
transport.onreadystatechange = function () {
that.handleTransport(transport, onSuccess,
onFailure);
};
window.setTimeout(function () {
transport.send(data);
}, 0);
} else {
transport.onreadystatechange = function () {
that.handleTransport(transport, onSuccess, onFailure);
};
transport.send(data);
}
}
return transport;
},
handleTransport: function (transport, onSuccess, onFailure) {
if (transport.readyState == 4) {
if (!transport.status || (transport.status >= 200 && transport.status < 300)) {
onSuccess(transport);
}
if (transport.status && (transport.status < 200 || transport.status >= 300)) {
onFailure(transport);
}
}
},
createTransport: function () {
for (var index = 0, length = this.factories.length; index < length; index++) {
try {
var transport = this.factories[index]();
if (transport !== null) {
return transport;
}
} catch (e) {
// TODO log
}
}
throw new Error('Could not create XML HTTP transport.');
},
CLASS_NAME: 'Jsonix.Request'
});
Jsonix.Request.INSTANCE = new Jsonix.Request();
Jsonix.Request.PROXY = null;
Jsonix.Schema = {};
Jsonix.Model = {};
Jsonix.Util.Type = {
exists: function (value) {
return (typeof value !== 'undefined' && value !== null);
},
isUndefined: function (value) {
return typeof value === 'undefined';
},
isString: function (value) {
return typeof value === 'string';
},
isBoolean: function (value) {
return typeof value === 'boolean';
},
isObject: function (value) {
return typeof value === 'object';
},
isFunction: function (value) {
return typeof value === 'function';
},
isNumber: function (value) {
return (typeof value === 'number') && !isNaN(value);
},
isNumberOrNaN: function (value) {
return (value === +value) || (Object.prototype.toString.call(value) === '[object Number]');
},
isNaN: function (value) {
return Jsonix.Util.Type.isNumberOrNaN(value) && isNaN(value);
},
isArray: function (value) {
// return value instanceof Array;
return !!(value && value.concat && value.unshift && !value.callee);
},
isDate: function (value) {
return !!(value && value.getTimezoneOffset && value.setUTCFullYear);
},
isRegExp: function (value) {
return !!(value && value.test && value.exec && (value.ignoreCase || value.ignoreCase === false));
},
isNode: function (value) {
return (typeof Node === "object" || typeof Node === "function") ? (value instanceof Node) : (value && (typeof value === "object") && (typeof value.nodeType === "number") && (typeof value.nodeName === "string"));
},
isEqual: function (a, b, report) {
var doReport = Jsonix.Util.Type.isFunction(report);
// TODO rework
var _range = function (start, stop, step) {
var args = slice.call(arguments);
var solo = args.length <= 1;
var start_ = solo ? 0 : args[0];
var stop_ = solo ? args[0] : args[1];
var step_ = args[2] || 1;
var len = Math.max(Math.ceil((stop_ - start_) / step_), 0);
var idx = 0;
var range = new Array(len);
while (idx < len) {
range[idx++] = start_;
start_ += step_;
}
return range;
};
var _keys = Object.keys || function (obj) {
if (Jsonix.Util.Type.isArray(obj)) {
return _range(0, obj.length);
}
var keys = [];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
keys[keys.length] = key;
}
}
return keys;
};
// Check object identity.
if (a === b) {
return true;
}
// Check if both are NaNs
if (Jsonix.Util.Type.isNaN(a) && Jsonix.Util.Type.isNaN(b)) {
return true;
}
// Different types?
var atype = typeof a;
var btype = typeof b;
if (atype != btype) {
if (doReport) {
report('Types differ [' + atype + '], [' + btype + '].');
}
return false;
}
// Basic equality test (watch out for coercions).
if (a == b) {
return true;
}
// One is falsy and the other truthy.
if ((!a && b) || (a && !b)) {
if (doReport) {
report('One is falsy, the other is truthy.');
}
return false;
}
// Check dates' integer values.
if (Jsonix.Util.Type.isDate(a) && Jsonix.Util.Type.isDate(b)) {
return a.getTime() === b.getTime();
}
// Both are NaN?
if (Jsonix.Util.Type.isNaN(a) && Jsonix.Util.Type.isNaN(b)) {
return false;
}
// Compare regular expressions.
if (Jsonix.Util.Type.isRegExp(a) && Jsonix.Util.Type.isRegExp(b)) {
return a.source === b.source && a.global === b.global && a.ignoreCase === b.ignoreCase && a.multiline === b.multiline;
}
if (Jsonix.Util.Type.isNode(a) && Jsonix.Util.Type.isNode(b)) {
var aSerialized = Jsonix.DOM.serialize(a);
var bSerialized = Jsonix.DOM.serialize(b);
if (aSerialized !== bSerialized) {
if (doReport) {
report('Nodes differ.');
report('A=' + aSerialized);
report('B=' + bSerialized);
}
return false;
}
else {
return true;
}
}
// If a is not an object by this point, we can't handle it.
if (atype !== 'object') {
return false;
}
// Check for different array lengths before comparing contents.
if (Jsonix.Util.Type.isArray(a) && (a.length !== b.length)) {
if (doReport) {
report('Lengths differ.');
report('A.length=' + a.length);
report('B.length=' + b.length);
}
return false;
}
// Nothing else worked, deep compare the contents.
var aKeys = _keys(a);
var bKeys = _keys(b);
// Different object sizes?
if (aKeys.length !== bKeys.length) {
if (doReport) {
report('Different number of properties [' + aKeys.length + '], [' + bKeys.length + '].');
}
for (var andex = 0; andex < aKeys.length; andex++) {
if (doReport) {
report('A [' + aKeys[andex] + ']=' + a[aKeys[andex]]);
}
}
for (var bndex = 0; bndex < bKeys.length; bndex++) {
if (doReport) {
report('B [' + bKeys[bndex] + ']=' + b[bKeys[bndex]]);
}
}
return false;
}
// Recursive comparison of contents.
for (var kndex = 0; kndex < aKeys.length; kndex++) {
var key = aKeys[kndex];
if (!(key in b) || !Jsonix.Util.Type.isEqual(a[key], b[key], report)) {
if (doReport) {
report('One of the properties differ.');
report('Key: [' + key + '].');
report('Left: [' + a[key] + '].');
report('Right: [' + b[key] + '].');
}
return false;
}
}
return true;
},
cloneObject: function (source, target) {
target = target || {};
for (var p in source) {
if (source.hasOwnProperty(p)) {
target[p] = source[p];
}
}
return target;
},
defaultValue: function () {
var args = arguments;
if (args.length === 0) {
return undefined;
}
else {
var defaultValue = args[args.length - 1];
var typeOfDefaultValue = typeof defaultValue;
for (var index = 0; index < args.length - 1; index++) {
var candidateValue = args[index];
if (typeof candidateValue === typeOfDefaultValue) {
return candidateValue;
}
}
return defaultValue;
}
}
};
Jsonix.Util.NumberUtils = {
isInteger: function (value) {
return Jsonix.Util.Type.isNumber(value) && ((value % 1) === 0);
}
};
Jsonix.Util.StringUtils = {
trim: (!!String.prototype.trim) ?
function (str) {
Jsonix.Util.Ensure.ensureString(str);
return str.trim();
} :
function (str) {
Jsonix.Util.Ensure.ensureString(str);
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
},
/* isEmpty : function(str) {
var wcm = Jsonix.Util.StringUtils.whitespaceCharactersMap;
for (var index = 0; index < str.length; index++)
{
if (!wcm[str[index]])
{
return false;
}
}
return true;
}, */
isEmpty: function (str) {
var length = str.length;
if (!length) {
return true;
}
for (var index = 0; index < length; index++) {
var c = str[index];
if (c === ' ') {
// skip
}
else if (c > '\u000D' && c < '\u0085') {
return false;
}
else if (c < '\u00A0') {
if (c < '\u0009') {
return false;
}
else if (c > '\u0085') {
return false;
}
}
else if (c > '\u00A0') {
if (c < '\u2028') {
if (c < '\u180E') {
if (c < '\u1680') {
return false;
}
else if (c > '\u1680') {
return false;
}
}
else if (c > '\u180E') {
if (c < '\u2000') {
return false;
}
else if (c > '\u200A') {
return false;
}
}
}
else if (c > '\u2029') {
if (c < '\u205F') {
if (c < '\u202F') {
return false;
}
else if (c > '\u202F') {
return false;
}
}
else if (c > '\u205F') {
if (c < '\u3000') {
return false;
}
else if (c > '\u3000') {
return false;
}
}
}
}
}
return true;
},
isNotBlank: function (str) {
return Jsonix.Util.Type.isString(str) && !Jsonix.Util.StringUtils.isEmpty(str);
},
whitespaceCharacters: '\u0009\u000A\u000B\u000C\u000D \u0085\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000',
whitespaceCharactersMap: {
'\u0009': true,
'\u000A': true,
'\u000B': true,
'\u000C': true,
'\u000D': true,
' ': true,
'\u0085': true,
'\u00A0': true,
'\u1680': true,
'\u180E': true,
'\u2000': true,
'\u2001': true,
'\u2002': true,
'\u2003': true,
'\u2004': true,
'\u2005': true,
'\u2006': true,
'\u2007': true,
'\u2008': true,
'\u2009': true,
'\u200A': true,
'\u2028': true,
'\u2029': true,
'\u202F': true,
'\u205F': true,
'\u3000': true
},
splitBySeparatorChars: function (str, separatorChars) {
Jsonix.Util.Ensure.ensureString(str);
Jsonix.Util.Ensure.ensureString(separatorChars);
var len = str.length;
if (len === 0) {
return [];
}
if (separatorChars.length === 1) {
return str.split(separatorChars);
}
else {
var list = [];
var sizePlus1 = 1;
var i = 0;
var start = 0;
var match = false;
var lastMatch = false;
var max = -1;
var preserveAllTokens = false;
// standard case
while (i < len) {
if (separatorChars.indexOf(str.charAt(i)) >= 0) {
if (match || preserveAllTokens) {
lastMatch = true;
if (sizePlus1++ == max) {
i = len;
lastMatch = false;
}
list.push(str.substring(start, i));
match = false;
}
start = ++i;
continue;
}
lastMatch = false;
match = true;
i++;
}
if (match || (preserveAllTokens && lastMatch)) {
list.push(str.substring(start, i));
}
return list;
}
}
};
Jsonix.Util.Ensure = {
ensureBoolean: function (value) {
if (!Jsonix.Util.Type.isBoolean(value)) {
throw new Error('Argument [' + value + '] must be a boolean.');
}
},
ensureString: function (value) {
if (!Jsonix.Util.Type.isString(value)) {
throw new Error('Argument [' + value + '] must be a string.');
}
},
ensureNumber: function (value) {
if (!Jsonix.Util.Type.isNumber(value)) {
throw new Error('Argument [' + value + '] must be a number.');
}
},
ensureNumberOrNaN: function (value) {
if (!Jsonix.Util.Type.isNumberOrNaN(value)) {
throw new Error('Argument [' + value + '] must be a number or NaN.');
}
},
ensureInteger: function (value) {
if (!Jsonix.Util.Type.isNumber(value)) {
throw new Error('Argument [' + value + '] must be an integer, but it is not a number.');
} else if (!Jsonix.Util.NumberUtils.isInteger(value)) {
throw new Error('Argument [' + value + '] must be an integer.');
}
},
ensureDate: function (value) {
if (!(value instanceof Date)) {
throw new Error('Argument [' + value + '] must be a date.');
}
},
ensureObject: function (value) {
if (!Jsonix.Util.Type.isObject(value)) {
throw new Error('Argument [' + value + '] must be an object.');
}
},
ensureArray: function (value) {
if (!Jsonix.Util.Type.isArray(value)) {
throw new Error('Argument [' + value + '] must be an array.');
}
},
ensureFunction: function (value) {
if (!Jsonix.Util.Type.isFunction(value)) {
throw new Error('Argument [' + value + '] must be a function.');
}
},
ensureExists: function (value) {
if (!Jsonix.Util.Type.exists(value)) {
throw new Error('Argument [' + value + '] does not exist.');
}
}
};
Jsonix.XML.QName = Jsonix.Class({
key: null,
namespaceURI: null,
localPart: null,
prefix: null,
string: null,
initialize: function (one, two, three) {
var namespaceURI;
var localPart;
var prefix;
var key;
var string;
if (!Jsonix.Util.Type.exists(two)) {
namespaceURI = '';
localPart = one;
prefix = '';
} else if (!Jsonix.Util.Type.exists(three)) {
namespaceURI = Jsonix.Util.Type.exists(one) ? one : '';
localPart = two;
var colonPosition = two.indexOf(':');
if (colonPosition > 0 && colonPosition < two.length) {
prefix = two.substring(0, colonPosition);
localPart = two.substring(colonPosition + 1);
} else {
prefix = '';
localPart = two;
}
} else {
namespaceURI = Jsonix.Util.Type.exists(one) ? one : '';
localPart = two;
prefix = Jsonix.Util.Type.exists(three) ? three : '';
}
this.namespaceURI = namespaceURI;
this.localPart = localPart;
this.prefix = prefix;
this.key = (namespaceURI !== '' ? ('{' + namespaceURI + '}') : '') + localPart;
this.string = (namespaceURI !== '' ? ('{' + namespaceURI + '}') : '') + (prefix !== '' ? (prefix + ':') : '') + localPart;
},
toString: function () {
return this.string;
},
// foo:bar
toCanonicalString: function (namespaceContext) {
var canonicalPrefix = namespaceContext ? namespaceContext.getPrefix(this.namespaceURI, this.prefix) : this.prefix;
return this.prefix + (this.prefix === '' ? '' : ':') + this.localPart;
},
clone: function () {
return new Jsonix.XML.QName(this.namespaceURI, this.localPart, this.prefix);
},
equals: function (that) {
if (!that) {
return false;
} else {
return (this.namespaceURI == that.namespaceURI) && (this.localPart == that.localPart);
}
},
CLASS_NAME: "Jsonix.XML.QName"
});
Jsonix.XML.QName.fromString = function (qNameAsString, namespaceContext, defaultNamespaceURI) {
var leftBracket = qNameAsString.indexOf('{');
var rightBracket = qNameAsString.lastIndexOf('}');
var namespaceURI;
var prefixedName;
if ((leftBracket === 0) && (rightBracket > 0) && (rightBracket < qNameAsString.length)) {
namespaceURI = qNameAsString.substring(1, rightBracket);
prefixedName = qNameAsString.substring(rightBracket + 1);
} else {
namespaceURI = null;
prefixedName = qNameAsString;
}
var colonPosition = prefixedName.indexOf(':');
var prefix;
var localPart;
if (colonPosition > 0 && colonPosition < prefixedName.length) {
prefix = prefixedName.substring(0, colonPosition);
localPart = prefixedName.substring(colonPosition + 1);
} else {
prefix = '';
localPart = prefixedName;
}
// If namespace URI was not set and we have a namespace context, try to find the namespace URI via this context
if (namespaceURI === null) {
if (prefix === '' && Jsonix.Util.Type.isString(defaultNamespaceURI)) {
namespaceURI = defaultNamespaceURI;
}
else if (namespaceContext) {
namespaceURI = namespaceContext.getNamespaceURI(prefix);
}
}
// If we don't have a namespace URI, assume '' by default
// TODO document the assumption
if (!Jsonix.Util.Type.isString(namespaceURI)) {
namespaceURI = defaultNamespaceURI || '';
}
return new Jsonix.XML.QName(namespaceURI, localPart, prefix);
};
Jsonix.XML.QName.fromObject = function (object) {
Jsonix.Util.Ensure.ensureObject(object);
if (object instanceof Jsonix.XML.QName || (Jsonix.Util.Type.isString(object.CLASS_NAME) && object.CLASS_NAME === 'Jsonix.XML.QName')) {
return object;
}
var localPart = object.localPart || object.lp || null;
Jsonix.Util.Ensure.ensureString(localPart);
var namespaceURI = object.namespaceURI || object.ns || '';
var prefix = object.prefix || object.p || '';
return new Jsonix.XML.QName(namespaceURI, localPart, prefix);
};
Jsonix.XML.QName.fromObjectOrString = function (value, namespaceContext, defaultNamespaceURI) {
if (Jsonix.Util.Type.isString(value)) {
return Jsonix.XML.QName.fromString(value, namespaceContext, defaultNamespaceURI);
}
else {
return Jsonix.XML.QName.fromObject(value);
}
};
Jsonix.XML.QName.key = function (namespaceURI, localPart) {
Jsonix.Util.Ensure.ensureString(localPart);
if (namespaceURI) {
var colonPosition = localPart.indexOf(':');
var localName;
if (colonPosition > 0 && colonPosition < localPart.length) {
localName = localPart.substring(colonPosition + 1);
} else {
localName = localPart;
}
return '{' + namespaceURI + '}' + localName;
} else {
return localPart;
}
};
Jsonix.XML.Calendar = Jsonix.Class({
year: NaN,
month: NaN,
day: NaN,
hour: NaN,
minute: NaN,
second: NaN,
fractionalSecond: NaN,
timezone: NaN,
date: null,
initialize: function (data) {
Jsonix.Util.Ensure.ensureObject(data);
// Year
if (Jsonix.Util.Type.exists(data.year)) {
Jsonix.Util.Ensure.ensureInteger(data.year);
Jsonix.XML.Calendar.validateYear(data.year);
this.year = data.year;
} else {
this.year = NaN;
}
// Month
if (Jsonix.Util.Type.exists(data.month)) {
Jsonix.Util.Ensure.ensureInteger(data.month);
Jsonix.XML.Calendar.validateMonth(data.month);
this.month = data.month;
} else {
this.month = NaN;
}
// Day
if (Jsonix.Util.Type.exists(data.day)) {
Jsonix.Util.Ensure.ensureInteger(data.day);
if (Jsonix.Util.NumberUtils.isInteger(data.year) && Jsonix.Util.NumberUtils.isInteger(data.month)) {
Jsonix.XML.Calendar.validateYearMonthDay(data.year, data.month, data.day);
} else if (Jsonix.Util.NumberUtils.isInteger(data.month)) {
Jsonix.XML.Calendar.validateMonthDay(data.month, data.day);
} else {
Jsonix.XML.Calendar.validateDay(data.day);
}
this.day = data.day;
} else {
this.day = NaN;
}
// Hour
if (Jsonix.Util.Type.exists(data.hour)) {
Jsonix.Util.Ensure.ensureInteger(data.hour);
Jsonix.XML.Calendar.validateHour(data.hour);
this.hour = data.hour;
} else {
this.hour = NaN;
}
// Minute
if (Jsonix.Util.Type.exists(data.minute)) {
Jsonix.Util.Ensure.ensureInteger(data.minute);
Jsonix.XML.Calendar.validateMinute(data.minute);
this.minute = data.minute;
} else {
this.minute = NaN;
}
// Second
if (Jsonix.Util.Type.exists(data.second)) {
Jsonix.Util.Ensure.ensureInteger(data.second);
Jsonix.XML.Calendar.validateSecond(data.second);
this.second = data.second;
} else {
this.second = NaN;
}
// Fractional second
if (Jsonix.Util.Type.exists(data.fractionalSecond)) {
Jsonix.Util.Ensure.ensureNumber(data.fractionalSecond);
Jsonix.XML.Calendar.validateFractionalSecond(data.fractionalSecond);
this.fractionalSecond = data.fractionalSecond;
} else {
this.fractionalSecond = NaN;
}
// Timezone
if (Jsonix.Util.Type.exists(data.timezone)) {
if (Jsonix.Util.Type.isNaN(data.timezone)) {
this.timezone = NaN;
} else {
Jsonix.Util.Ensure.ensureInteger(data.timezone);
Jsonix.XML.Calendar.validateTimezone(data.timezone);
this.timezone = data.timezone;
}
} else {
this.timezone = NaN;
}
var initialDate = new Date(0);
initialDate.setUTCFullYear(this.year || 1970);
initialDate.setUTCMonth(this.month - 1 || 0);
initialDate.setUTCDate(this.day || 1);
initialDate.setUTCHours(this.hour || 0);
initialDate.setUTCMinutes(this.minute || 0);
initialDate.setUTCSeconds(this.second || 0);
initialDate.setUTCMilliseconds((this.fractionalSecond || 0) * 1000);
var timezoneOffset = -60000 * (this.timezone || 0);
this.date = new Date(initialDate.getTime() + timezoneOffset);
},
CLASS_NAME: "Jsonix.XML.Calendar"
});
Jsonix.XML.Calendar.MIN_TIMEZONE = -14 * 60;
Jsonix.XML.Calendar.MAX_TIMEZONE = 14 * 60;
Jsonix.XML.Calendar.DAYS_IN_MONTH = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
Jsonix.XML.Calendar.fromObject = function (object) {
Jsonix.Util.Ensure.ensureObject(object);
if (Jsonix.Util.Type.isString(object.CLASS_NAME) && object.CLASS_NAME === 'Jsonix.XML.Calendar') {
return object;
}
return new Jsonix.XML.Calendar(object);
};
Jsonix.XML.Calendar.validateYear = function (year) {
if (year === 0) {
throw new Error('Invalid year [' + year + ']. Year must not be [0].');
}
};
Jsonix.XML.Calendar.validateMonth = function (month) {
if (month < 1 || month > 12) {
throw new Error('Invalid month [' + month + ']. Month must be in range [1, 12].');
}
};
Jsonix.XML.Calendar.validateDay = function (day) {
if (day < 1 || day > 31) {
throw new Error('Invalid day [' + day + ']. Day must be in range [1, 31].');
}
};
Jsonix.XML.Calendar.validateMonthDay = function (month, day) {
Jsonix.XML.Calendar.validateMonth(month);
var maxDaysInMonth = Jsonix.XML.Calendar.DAYS_IN_MONTH[month - 1];
if (day < 1 || day > Jsonix.XML.Calendar.DAYS_IN_MONTH[month - 1]) {
throw new Error('Invalid day [' + day + ']. Day must be in range [1, ' + maxDaysInMonth + '].');
}
};
Jsonix.XML.Calendar.validateYearMonthDay = function (year, month, day) {
// #93 TODO proper validation of 28/29 02
Jsonix.XML.Calendar.validateYear(year);
Jsonix.XML.Calendar.validateMonthDay(month, day);
};
Jsonix.XML.Calendar.validateHour = function (hour) {
if (hour < 0 || hour > 23) {
throw new Error('Invalid hour [' + hour + ']. Hour must be in range [0, 23].');
}
};
Jsonix.XML.Calendar.validateMinute = function (minute) {
if (minute < 0 || minute > 59) {
throw new Error('Invalid minute [' + minute + ']. Minute must be in range [0, 59].');
}
};
Jsonix.XML.Calendar.validateSecond = function (second) {
if (second < 0 || second > 59) {
throw new Error('Invalid second [' + second + ']. Second must be in range [0, 59].');
}
};
Jsonix.XML.Calendar.validateFractionalSecond = function (fractionalSecond) {
if (fractionalSecond < 0 || fractionalSecond > 59) {
throw new Error('Invalid fractional second [' + fractionalSecond + ']. Fractional second must be in range [0, 1).');
}
};
Jsonix.XML.Calendar.validateTimezone = function (timezone) {
if (timezone < Jsonix.XML.Calendar.MIN_TIMEZONE || timezone > Jsonix.XML.Calendar.MAX_TIMEZONE) {
throw new Error('Invalid timezone [' + timezone + ']. Timezone must not be in range [' + Jsonix.XML.Calendar.MIN_TIMEZONE + ', ' + Jsonix.XML.Calendar.MAX_TIMEZONE + '].');
}
};
Jsonix.XML.Input = Jsonix.Class({
root: null,
node: null,
attributes: null,
eventType: null,
pns: null,
initialize: function (node) {
Jsonix.Util.Ensure.ensureExists(node);
this.root = node;
var rootPnsItem =
{
'': ''
};
rootPnsItem[Jsonix.XML.XMLNS_P] = Jsonix.XML.XMLNS_NS;
this.pns = [rootPnsItem];
},
hasNext: function () {
// No current node, we've not started yet
if (this.node === null) {
return true;
} else if (this.node === this.root) {
var nodeType = this.node.nodeType;
// Root node is document, last event type is END_DOCUMENT
if (nodeType === 9 && this.eventType === 8) {
return false;
}
// Root node is element, last event type is END_ELEMENT
else if (nodeType === 1 && this.eventType === 2) {
return false;
} else {
return true;
}
} else {
return true;
}
},
next: function () {
if (this.eventType === null) {
return this.enter(this.root);
}
// START_DOCUMENT
if (this.eventType === 7) {
var documentElement = this.node.documentElement;
if (documentElement) {
return this.enter(documentElement);
} else {
return this.leave(this.node);
}
} else if (this.eventType === 1) {
var firstChild = this.node.firstChild;
if (firstChild) {
return this.enter(firstChild);
} else {
return this.leave(this.node);
}
} else if (this.eventType === 2) {
var nextSibling = this.node.nextSibling;
if (nextSibling) {
return this.enter(nextSibling);
} else {
return this.leave(this.node);
}
} else {
return this.leave(this.node);
}
},
enter: function (node) {
var nodeType = node.nodeType;
this.node = node;
this.attributes = null;
// Document node
if (nodeType === 1) {
// START_ELEMENT
this.eventType = 1;
this.pushNS(node);
return this.eventType;
} else if (nodeType === 2) {
// ATTRIBUTE
this.eventType = 10;
return this.eventType;
} else if (nodeType === 3) {
var nodeValue = node.nodeValue;
if (Jsonix.Util.StringUtils.isEmpty(nodeValue)) {
// SPACE
this.eventType = 6;
} else {
// CHARACTERS
this.eventType = 4;
}
return this.eventType;
} else if (nodeType === 4) {
// CDATA
this.eventType = 12;
return this.eventType;
} else if (nodeType === 5) {
// ENTITY_REFERENCE_NODE = 5
// ENTITY_REFERENCE
this.eventType = 9;
return this.eventType;
} else if (nodeType === 6) {
// ENTITY_DECLARATION
this.eventType = 15;
return this.eventType;
} else if (nodeType === 7) {
// PROCESSING_INSTRUCTION
this.eventType = 3;
return this.eventType;
} else if (nodeType === 8) {
// COMMENT
this.eventType = 5;
return this.eventType;
} else if (nodeType === 9) {
// START_DOCUMENT
this.eventType = 7;
return this.eventType;
} else if (nodeType === 10) {
// DTD
this.eventType = 12;
return this.eventType;
} else if (nodeType === 12) {
// NOTATION_DECLARATION
this.eventType = 14;
return this.eventType;
} else {
// DOCUMENT_FRAGMENT_NODE = 11
throw new Error("Node type [" + nodeType + '] is not supported.');
}
},
leave: function (node) {
if (node.nodeType === 9) {
if (this.eventType == 8) {
throw new Error("Invalid state.");
} else {
this.node = node;
this.attributes = null;
// END_ELEMENT
this.eventType = 8;
return this.eventType;
}
} else if (node.nodeType === 1) {
if (this.eventType == 2) {
var nextSibling = node.nextSibling;
if (nextSibling) {
return this.enter(nextSibling);
}
} else {
this.node = node;
this.attributes = null;
// END_ELEMENT
this.eventType = 2;
this.popNS();
return this.eventType;
}
}
var nextSibling1 = node.nextSibling;
if (nextSibling1) {
return this.enter(nextSibling1);
} else {
var parentNode = node.parentNode;
this.node = parentNode;
this.attributes = null;
if (parentNode.nodeType === 9) {
this.eventType = 8;
} else {
this.eventType = 2;
}
return this.eventType;
}
},
getName: function () {
var node = this.node;
if (Jsonix.Util.Type.isString(node.nodeName)) {
if (Jsonix.Util.Type.isString(node.namespaceURI)) {
return new Jsonix.XML.QName(node.namespaceURI, node.nodeName);
} else {
return new Jsonix.XML.QName(node.nodeName);
}
} else {
return null;
}
},
getNameKey: function () {
var node = this.node;
if (Jsonix.Util.Type.isString(node.nodeName)) {
return Jsonix.XML.QName.key(node.namespaceURI, node.nodeName);
} else {
return null;
}
},
getText: function () {
return this.node.nodeValue;
},
nextTag: function () {
var et = this.next();
// TODO isWhiteSpace
while (et === 7 || et === 4 || et === 12 || et === 6 || et === 3 || et === 5) {
et = this.next();
}
if (et !== 1 && et !== 2) {
// TODO location
throw new Error('Expected start or end tag.');
}
return et;
},
skipElement: function () {
if (this.eventType !== Jsonix.XML.Input.START_ELEMENT) {
throw new Error("Parser must be on START_ELEMENT to skip element.");
}
var numberOfOpenTags = 1;
var et;
do {
et = this.nextTag();
numberOfOpenTags += (et === Jsonix.XML.Input.START_ELEMENT) ? 1 : -1;
} while (numberOfOpenTags > 0);
return et;
},
getElementText: function () {
if (this.eventType != 1) {
throw new Error("Parser must be on START_ELEMENT to read next text.");
}
var et = this.next();
var content = '';
while (et !== 2) {
if (et === 4 || et === 12 || et === 6 || et === 9) {
content = content + this.getText();
} else if (et === 3 || et === 5) {
// Skip PI or comment
} else if (et === 8) {
// End document
throw new Error("Unexpected end of document when reading element text content.");
} else if (et === 1) {
// End element
// TODO location
throw new Error("Element text content may not contain START_ELEMENT.");
} else {
// TODO location
throw new Error("Unexpected event type [" + et + "].");
}
et = this.next();
}
return content;
},
retrieveElement: function () {
var element;
if (this.eventType === 1) {
element = this.node;
} else if (this.eventType === 10) {
element = this.node.parentNode;
} else {
throw new Error("Element can only be retrieved for START_ELEMENT or ATTRIBUTE nodes.");
}
return element;
},
retrieveAttributes: function () {
var attributes;
if (this.attributes) {
attributes = this.attributes;
} else if (this.eventType === 1) {
attributes = this.node.attributes;
this.attributes = attributes;
} else if (this.eventType === 10) {
attributes = this.node.parentNode.attributes;
this.attributes = attributes;
} else {
throw new Error("Attributes can only be retrieved for START_ELEMENT or ATTRIBUTE nodes.");
}
return attributes;
},
getAttributeCount: function () {
var attributes = this.retrieveAttributes();
return attributes.length;
},
getAttributeName: function (index) {
var attributes = this.retrieveAttributes();
if (index < 0 || index >= attributes.length) {
throw new Error("Invalid attribute index [" + index + "].");
}
var attribute = attributes[index];
if (Jsonix.Util.Type.isString(attribute.namespaceURI)) {
return new Jsonix.XML.QName(attribute.namespaceURI, attribute.nodeName);
} else {
return new Jsonix.XML.QName(attribute.nodeName);
}
},
getAttributeNameKey: function (index) {
var attributes = this.retrieveAttributes();
if (index < 0 || index >= attributes.length) {
throw new Error("Invalid attribute index [" + index + "].");
}
var attribute = attributes[index];
return Jsonix.XML.QName.key(attribute.namespaceURI, attribute.nodeName);
},
getAttributeValue: function (index) {
var attributes = this.retrieveAttributes();
if (index < 0 || index >= attributes.length) {
throw new Error("Invalid attribute index [" + index + "].");
}
var attribute = attributes[index];
return attribute.value;
},
getAttributeValueNS: null,
getAttributeValueNSViaElement: function (namespaceURI, localPart) {
var element = this.retrieveElement();
return element.getAttributeNS(namespaceURI, localPart);
},
getAttributeValueNSViaAttribute: function (namespaceURI, localPart) {
var attributeNode = this.getAttributeNodeNS(namespaceURI, localPart);
if (Jsonix.Util.Type.exists(attributeNode)) {
return attributeNode.nodeValue;
}
else {
return null;
}
},
getAttributeNodeNS: null,
getAttributeNodeNSViaElement: function (namespaceURI, localPart) {
var element = this.retrieveElement();
return element.getAttributeNodeNS(namespaceURI, localPart);
},
getAttributeNodeNSViaAttributes: function (namespaceURI, localPart) {
var attributeNode = null;
var attributes = this.retrieveAttributes();
var potentialNode, fullName;
for (var i = 0, len = attributes.length; i < len; ++i) {
potentialNode = attributes[i];
if (potentialNode.namespaceURI === namespaceURI) {
fullName = (potentialNode.prefix) ? (potentialNode.prefix + ':' + localPart) : localPart;
if (fullName === potentialNode.nodeName) {
attributeNode = potentialNode;
break;
}
}
}
return attributeNode;
},
getElement: function () {
if (this.eventType === 1 || this.eventType === 2) {
// Go to the END_ELEMENT
this.eventType = 2;
return this.node;
} else {
throw new Error("Parser must be on START_ELEMENT or END_ELEMENT to return current element.");
}
},
pushNS: function (node) {
var pindex = this.pns.length - 1;
var parentPnsItem = this.pns[pindex];
var pnsItem = Jsonix.Util.Type.isObject(parentPnsItem) ? pindex : parentPnsItem;
this.pns.push(pnsItem);
pindex++;
var reference = true;
if (node.attributes) {
var attributes = node.attributes;
var alength = attributes.length;
if (alength > 0) {
// If given node has attributes
for (var aindex = 0; aindex < alength; aindex++) {
var attribute = attributes[aindex];
var attributeName = attribute.nodeName;
var p = null;
var ns = null;
var isNS = false;
if (attributeName === 'xmlns') {
p = '';
ns = attribute.value;
isNS = true;
}
else if (attributeName.substring(0, 6) === 'xmlns:') {
p = attributeName.substring(6);
ns = attribute.value;
isNS = true;
}
// Attribute is a namespace declaration
if (isNS) {
if (reference) {
pnsItem = Jsonix.Util.Type.cloneObject(this.pns[pnsItem], {});
this.pns[pindex] = pnsItem;
reference = false;
}
pnsItem[p] = ns;
}
}
}
}
},
popNS: function () {
this.pns.pop();
},
getNamespaceURI: function (p) {
var pindex = this.pns.length - 1;
var pnsItem = this.pns[pindex];
pnsItem = Jsonix.Util.Type.isObject(pnsItem) ? pnsItem : this.pns[pnsItem];
return pnsItem[p];
},
CLASS_NAME: "Jsonix.XML.Input"
});
Jsonix.XML.Input.prototype.getAttributeValueNS = (Jsonix.DOM.isDomImplementationAvailable()) ? Jsonix.XML.Input.prototype.getAttributeValueNSViaElement : Jsonix.XML.Input.prototype.getAttributeValueNSViaAttribute;
Jsonix.XML.Input.prototype.getAttributeNodeNS = (Jsonix.DOM.isDomImplementationAvailable()) ? Jsonix.XML.Input.prototype.getAttributeNodeNSViaElement : Jsonix.XML.Input.prototype.getAttributeNodeNSViaAttributes;
Jsonix.XML.Input.START_ELEMENT = 1;
Jsonix.XML.Input.END_ELEMENT = 2;
Jsonix.XML.Input.PROCESSING_INSTRUCTION = 3;
Jsonix.XML.Input.CHARACTERS = 4;
Jsonix.XML.Input.COMMENT = 5;
Jsonix.XML.Input.SPACE = 6;
Jsonix.XML.Input.START_DOCUMENT = 7;
Jsonix.XML.Input.END_DOCUMENT = 8;
Jsonix.XML.Input.ENTITY_REFERENCE = 9;
Jsonix.XML.Input.ATTRIBUTE = 10;
Jsonix.XML.Input.DTD = 11;
Jsonix.XML.Input.CDATA = 12;
Jsonix.XML.Input.NAMESPACE = 13;
Jsonix.XML.Input.NOTATION_DECLARATION = 14;
Jsonix.XML.Input.ENTITY_DECLARATION = 15;
Jsonix.XML.Output = Jsonix.Class({
document: null,
documentElement: null,
node: null,
nodes: null,
nsp: null,
pns: null,
namespacePrefixIndex: 0,
xmldom: null,
initialize: function (options) {
// REWORK
if (typeof ActiveXObject !== 'undefined') {
this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
} else {
this.xmldom = null;
}
this.nodes = [];
var rootNspItem =
{
'': ''
};
rootNspItem[Jsonix.XML.XMLNS_NS] = Jsonix.XML.XMLNS_P;
if (Jsonix.Util.Type.isObject(options)) {
if (Jsonix.Util.Type.isObject(options.namespacePrefixes)) {
Jsonix.Util.Type.cloneObject(options.namespacePrefixes, rootNspItem);
}
}
this.nsp = [rootNspItem];
var rootPnsItem =
{
'': ''
};
rootPnsItem[Jsonix.XML.XMLNS_P] = Jsonix.XML.XMLNS_NS;
this.pns = [rootPnsItem];
},
destroy: function () {
this.xmldom = null;
},
writeStartDocument: function () {
// TODO Check
var doc = Jsonix.DOM.createDocument();
this.document = doc;
return this.push(doc);
},
writeEndDocument: function () {
return this.pop();
},
writeStartElement: function (name) {
Jsonix.Util.Ensure.ensureObject(name);
var localPart = name.localPart || name.lp || null;
Jsonix.Util.Ensure.ensureString(localPart);
var ns = name.namespaceURI || name.ns || null;
var namespaceURI = Jsonix.Util.Type.isString(ns) ? ns : '';
var p = name.prefix || name.p;
var prefix = this.getPrefix(namespaceURI, p);
var qualifiedName = (!prefix ? localPart : prefix + ':' + localPart);
var element;
if (Jsonix.Util.Type.isFunction(this.document.createElementNS)) {
element = this.document.createElementNS(namespaceURI, qualifiedName);
}
else if (this.xmldom) {
element = this.xmldom.createNode(1, qualifiedName, namespaceURI);
} else {
throw new Error("Could not create an element node.");
}
this.peek().appendChild(element);
this.push(element);
this.declareNamespace(namespaceURI, prefix);
if (this.documentElement === null) {
this.documentElement = element;
this.declareNamespaces();
}
return element;
},
writeEndElement: function () {
return this.pop();
},
writeCharacters: function (text) {
var node;
if (Jsonix.Util.Type.isFunction(this.document.createTextNode)) {
node = this.document.createTextNode(text);
}
else if (this.xmldom) {
node = this.xmldom.createTextNode(text);
} else {
throw new Error("Could not create a text node.");
}
this.peek().appendChild(node);
return node;
},
writeCdata: function (text) {
var parts = text.split(']]>');
for (var index = 0; index < parts.length; index++) {
if (index + 1 < parts.length) {
parts[index] = parts[index] + ']]';
parts[index + 1] = '>' + parts[index + 1];
}
}
var node;
for (var jndex = 0; jndex < parts.length; jndex++) {
node = this.writeCdataWithoutCdend(parts[jndex]);
}
return node;
},
writeCdataWithoutCdend: function (text) {
var node;
if (Jsonix.Util.Type.isFunction(this.document.createCDATASection)) {
node = this.document.createCDATASection(text);
}
else if (this.xmldom) {
node = this.xmldom.createCDATASection(text);
} else {
throw new Error("Could not create a CDATA section node.");
}
this.peek().appendChild(node);
return node;
},
writeAttribute: function (name, value) {
Jsonix.Util.Ensure.ensureString(value);
Jsonix.Util.Ensure.ensureObject(name);
var localPart = name.localPart || name.lp || null;
Jsonix.Util.Ensure.ensureString(localPart);
var ns = name.namespaceURI || name.ns || null;
var namespaceURI = Jsonix.Util.Type.isString(ns) ? ns : '';
var p = name.prefix || name.p || null;
var prefix = this.getPrefix(namespaceURI, p);
var qualifiedName = (!prefix ? localPart : prefix + ':' + localPart);
var node = this.peek();
if (namespaceURI === '') {
node.setAttribute(qualifiedName, value);
} else {
if (node.setAttributeNS) {
node.setAttributeNS(namespaceURI, qualifiedName, value);
} else {
if (this.xmldom) {
var attribute = this.document.createNode(2, qualifiedName, namespaceURI);
attribute.nodeValue = value;
node.setAttributeNode(attribute);