node-datajs
Version:
datajs (version 1.1.2) - javascript library for OData
1,140 lines (968 loc) • 560 kB
JavaScript
(function(global){
if (typeof window === "undefined") {
window = this;
window.require = require; // for breezejs
window.XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
window.DOMParser = require('xmldom').DOMParser;
}
window.datajs = window.datajs || {};
window.OData = window.OData || {};
})(this);
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// datajs.js
(function (global, undefined) {
var datajs = window.datajs || {};
var odata = window.OData || {};
// AMD support
if (typeof define === 'function' && define.amd) {
define('datajs', datajs);
define('OData', odata);
} else {
window.datajs = datajs;
window.OData = odata;
}
datajs.version = {
major: 1,
minor: 1,
build: 1
};
// INCLUDE: utils.js
// INCLUDE: xml.js
// INCLUDE: deferred.js
// INCLUDE: odata-utils.js
// INCLUDE: odata-net.js
// INCLUDE: odata-handler.js
// INCLUDE: odata-gml.js
// INCLUDE: odata-xml.js
// INCLUDE: odata-atom.js
// INCLUDE: odata-metadata.js
// INCLUDE: odata-json-light.js
// INCLUDE: odata-json.js
// INCLUDE: odata-batch.js
// INCLUDE: odata.js
// INCLUDE: store-dom.js
// INCLUDE: store-indexeddb.js
// INCLUDE: store-memory.js
// INCLUDE: store.js
// INCLUDE: cache-source.js
// INCLUDE: cache.js
})(this);
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// utils.js
(function (global, undefined) {
var datajs = window.datajs || {};
// CONTENT START
var activeXObject = function (progId) {
/// <summary>Creates a new ActiveXObject from the given progId.</summary>
/// <param name="progId" type="String" mayBeNull="false" optional="false">
/// ProgId string of the desired ActiveXObject.
/// </param>
/// <remarks>
/// This function throws whatever exception might occur during the creation
/// of the ActiveXObject.
/// </remarks>
/// <returns type="Object">
/// The ActiveXObject instance. Null if ActiveX is not supported by the
/// browser.
/// </returns>
if (window.ActiveXObject) {
return new window.ActiveXObject(progId);
}
return null;
};
var assigned = function (value) {
/// <summary>Checks whether the specified value is different from null and undefined.</summary>
/// <param name="value" mayBeNull="true" optional="true">Value to check.</param>
/// <returns type="Boolean">true if the value is assigned; false otherwise.</returns>
return value !== null && value !== undefined;
};
var contains = function (arr, item) {
/// <summary>Checks whether the specified item is in the array.</summary>
/// <param name="arr" type="Array" optional="false" mayBeNull="false">Array to check in.</param>
/// <param name="item">Item to look for.</param>
/// <returns type="Boolean">true if the item is contained, false otherwise.</returns>
var i, len;
for (i = 0, len = arr.length; i < len; i++) {
if (arr[i] === item) {
return true;
}
}
return false;
};
var defined = function (a, b) {
/// <summary>Given two values, picks the first one that is not undefined.</summary>
/// <param name="a">First value.</param>
/// <param name="b">Second value.</param>
/// <returns>a if it's a defined value; else b.</returns>
return (a !== undefined) ? a : b;
};
var delay = function (callback) {
/// <summary>Delays the invocation of the specified function until execution unwinds.</summary>
/// <param name="callback" type="Function">Callback function.</param>
if (arguments.length === 1) {
window.setTimeout(callback, 0);
return;
}
var args = Array.prototype.slice.call(arguments, 1);
window.setTimeout(function () {
callback.apply(this, args);
}, 0);
};
// DATAJS INTERNAL START
var djsassert = function (condition, message, data) {
/// <summary>Throws an exception in case that a condition evaluates to false.</summary>
/// <param name="condition" type="Boolean">Condition to evaluate.</param>
/// <param name="message" type="String">Message explaining the assertion.</param>
/// <param name="data" type="Object">Additional data to be included in the exception.</param>
if (!condition) {
throw { message: "Assert fired: " + message, data: data };
};
};
// DATAJS INTERNAL END
var extend = function (target, values) {
/// <summary>Extends the target with the specified values.</summary>
/// <param name="target" type="Object">Object to add properties to.</param>
/// <param name="values" type="Object">Object with properties to add into target.</param>
/// <returns type="Object">The target object.</returns>
for (var name in values) {
target[name] = values[name];
}
return target;
};
var find = function (arr, callback) {
/// <summary>Returns the first item in the array that makes the callback function true.</summary>
/// <param name="arr" type="Array" optional="false" mayBeNull="true">Array to check in.</param>
/// <param name="callback" type="Function">Callback function to invoke once per item in the array.</param>
/// <returns>The first item that makes the callback return true; null otherwise or if the array is null.</returns>
if (arr) {
var i, len;
for (i = 0, len = arr.length; i < len; i++) {
if (callback(arr[i])) {
return arr[i];
}
}
}
return null;
};
var isArray = function (value) {
/// <summary>Checks whether the specified value is an array object.</summary>
/// <param name="value">Value to check.</param>
/// <returns type="Boolean">true if the value is an array object; false otherwise.</returns>
return Object.prototype.toString.call(value) === "[object Array]";
};
var isDate = function (value) {
/// <summary>Checks whether the specified value is a Date object.</summary>
/// <param name="value">Value to check.</param>
/// <returns type="Boolean">true if the value is a Date object; false otherwise.</returns>
return Object.prototype.toString.call(value) === "[object Date]";
};
var isObject = function (value) {
/// <summary>Tests whether a value is an object.</summary>
/// <param name="value">Value to test.</param>
/// <remarks>
/// Per javascript rules, null and array values are objects and will cause this function to return true.
/// </remarks>
/// <returns type="Boolean">True is the value is an object; false otherwise.</returns>
return typeof value === "object";
};
var parseInt10 = function (value) {
/// <summary>Parses a value in base 10.</summary>
/// <param name="value" type="String">String value to parse.</param>
/// <returns type="Number">The parsed value, NaN if not a valid value.</returns>
return parseInt(value, 10);
};
var renameProperty = function (obj, oldName, newName) {
/// <summary>Renames a property in an object.</summary>
/// <param name="obj" type="Object">Object in which the property will be renamed.</param>
/// <param name="oldName" type="String">Name of the property that will be renamed.</param>
/// <param name="newName" type="String">New name of the property.</param>
/// <remarks>
/// This function will not do anything if the object doesn't own a property with the specified old name.
/// </remarks>
if (obj.hasOwnProperty(oldName)) {
obj[newName] = obj[oldName];
delete obj[oldName];
}
};
var throwErrorCallback = function (error) {
/// <summary>Default error handler.</summary>
/// <param name="error" type="Object">Error to handle.</param>
throw error;
};
var trimString = function (str) {
/// <summary>Removes leading and trailing whitespaces from a string.</summary>
/// <param name="str" type="String" optional="false" mayBeNull="false">String to trim</param>
/// <returns type="String">The string with no leading or trailing whitespace.</returns>
if (str.trim) {
return str.trim();
}
return str.replace(/^\s+|\s+$/g, '');
};
var undefinedDefault = function (value, defaultValue) {
/// <summary>Returns a default value in place of undefined.</summary>
/// <param name="value" mayBeNull="true" optional="true">Value to check.</param>
/// <param name="defaultValue">Value to return if value is undefined.</param>
/// <returns>value if it's defined; defaultValue otherwise.</returns>
/// <remarks>
/// This should only be used for cases where falsy values are valid;
/// otherwise the pattern should be 'x = (value) ? value : defaultValue;'.
/// </remarks>
return (value !== undefined) ? value : defaultValue;
};
// Regular expression that splits a uri into its components:
// 0 - is the matched string.
// 1 - is the scheme.
// 2 - is the authority.
// 3 - is the path.
// 4 - is the query.
// 5 - is the fragment.
var uriRegEx = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#:]+)?(\?[^#]*)?(#.*)?/;
var uriPartNames = ["scheme", "authority", "path", "query", "fragment"];
var getURIInfo = function (uri) {
/// <summary>Gets information about the components of the specified URI.</summary>
/// <param name="uri" type="String">URI to get information from.</param>
/// <returns type="Object">
/// An object with an isAbsolute flag and part names (scheme, authority, etc.) if available.
/// </returns>
var result = { isAbsolute: false };
if (uri) {
var matches = uriRegEx.exec(uri);
if (matches) {
var i, len;
for (i = 0, len = uriPartNames.length; i < len; i++) {
if (matches[i + 1]) {
result[uriPartNames[i]] = matches[i + 1];
}
}
}
if (result.scheme) {
result.isAbsolute = true;
}
}
return result;
};
var getURIFromInfo = function (uriInfo) {
/// <summary>Builds a URI string from its components.</summary>
/// <param name="uriInfo" type="Object"> An object with uri parts (scheme, authority, etc.).</param>
/// <returns type="String">URI string.</returns>
return "".concat(
uriInfo.scheme || "",
uriInfo.authority || "",
uriInfo.path || "",
uriInfo.query || "",
uriInfo.fragment || "");
};
// Regular expression that splits a uri authority into its subcomponents:
// 0 - is the matched string.
// 1 - is the userinfo subcomponent.
// 2 - is the host subcomponent.
// 3 - is the port component.
var uriAuthorityRegEx = /^\/{0,2}(?:([^@]*)@)?([^:]+)(?::{1}(\d+))?/;
// Regular expression that matches percentage enconded octects (i.e %20 or %3A);
var pctEncodingRegEx = /%[0-9A-F]{2}/ig;
var normalizeURICase = function (uri) {
/// <summary>Normalizes the casing of a URI.</summary>
/// <param name="uri" type="String">URI to normalize, absolute or relative.</param>
/// <returns type="String">The URI normalized to lower case.</returns>
var uriInfo = getURIInfo(uri);
var scheme = uriInfo.scheme;
var authority = uriInfo.authority;
if (scheme) {
uriInfo.scheme = scheme.toLowerCase();
if (authority) {
var matches = uriAuthorityRegEx.exec(authority);
if (matches) {
uriInfo.authority = "//" +
(matches[1] ? matches[1] + "@" : "") +
(matches[2].toLowerCase()) +
(matches[3] ? ":" + matches[3] : "");
}
}
}
uri = getURIFromInfo(uriInfo);
return uri.replace(pctEncodingRegEx, function (str) {
return str.toLowerCase();
});
};
var normalizeURI = function (uri, base) {
/// <summary>Normalizes a possibly relative URI with a base URI.</summary>
/// <param name="uri" type="String">URI to normalize, absolute or relative.</param>
/// <param name="base" type="String" mayBeNull="true">Base URI to compose with.</param>
/// <returns type="String">The composed URI if relative; the original one if absolute.</returns>
if (!base) {
return uri;
}
var uriInfo = getURIInfo(uri);
if (uriInfo.isAbsolute) {
return uri;
}
var baseInfo = getURIInfo(base);
var normInfo = {};
var path;
if (uriInfo.authority) {
normInfo.authority = uriInfo.authority;
path = uriInfo.path;
normInfo.query = uriInfo.query;
} else {
if (!uriInfo.path) {
path = baseInfo.path;
normInfo.query = uriInfo.query || baseInfo.query;
} else {
if (uriInfo.path.charAt(0) === '/') {
path = uriInfo.path;
} else {
path = mergeUriPathWithBase(uriInfo.path, baseInfo.path);
}
normInfo.query = uriInfo.query;
}
normInfo.authority = baseInfo.authority;
}
normInfo.path = removeDotsFromPath(path);
normInfo.scheme = baseInfo.scheme;
normInfo.fragment = uriInfo.fragment;
return getURIFromInfo(normInfo);
};
var mergeUriPathWithBase = function (uriPath, basePath) {
/// <summary>Merges the path of a relative URI and a base URI.</summary>
/// <param name="uriPath" type="String>Relative URI path.</param>
/// <param name="basePath" type="String">Base URI path.</param>
/// <returns type="String">A string with the merged path.</returns>
var path = "/";
var end;
if (basePath) {
end = basePath.lastIndexOf("/");
path = basePath.substring(0, end);
if (path.charAt(path.length - 1) !== "/") {
path = path + "/";
}
}
return path + uriPath;
};
var removeDotsFromPath = function (path) {
/// <summary>Removes the special folders . and .. from a URI's path.</summary>
/// <param name="path" type="string">URI path component.</param>
/// <returns type="String">Path without any . and .. folders.</returns>
var result = "";
var segment = "";
var end;
while (path) {
if (path.indexOf("..") === 0 || path.indexOf(".") === 0) {
path = path.replace(/^\.\.?\/?/g, "");
} else if (path.indexOf("/..") === 0) {
path = path.replace(/^\/\..\/?/g, "/");
end = result.lastIndexOf("/");
if (end === -1) {
result = "";
} else {
result = result.substring(0, end);
}
} else if (path.indexOf("/.") === 0) {
path = path.replace(/^\/\.\/?/g, "/");
} else {
segment = path;
end = path.indexOf("/", 1);
if (end !== -1) {
segment = path.substring(0, end);
}
result = result + segment;
path = path.replace(segment, "");
}
}
return result;
};
var convertByteArrayToHexString = function (str) {
var arr = [];
if (window.atob === undefined) {
arr = decodeBase64(str);
} else {
var binaryStr = window.atob(str);
for (var i = 0; i < binaryStr.length; i++) {
arr.push(binaryStr.charCodeAt(i));
}
}
var hexValue = "";
var hexValues = "0123456789ABCDEF";
for (var j = 0; j < arr.length; j++) {
var t = arr[j];
hexValue += hexValues[t >> 4];
hexValue += hexValues[t & 0x0F];
}
return hexValue;
};
var decodeBase64 = function (str) {
var binaryString = "";
for (var i = 0; i < str.length; i++) {
var base65IndexValue = getBase64IndexValue(str[i]);
var binaryValue = "";
if (base65IndexValue !== null) {
binaryValue = base65IndexValue.toString(2);
binaryString += addBase64Padding(binaryValue);
}
}
var byteArray = [];
var numberOfBytes = parseInt(binaryString.length / 8, 10);
for (i = 0; i < numberOfBytes; i++) {
var intValue = parseInt(binaryString.substring(i * 8, (i + 1) * 8), 2);
byteArray.push(intValue);
}
return byteArray;
};
var getBase64IndexValue = function (character) {
var asciiCode = character.charCodeAt(0);
var asciiOfA = 65;
var differenceBetweenZanda = 6;
if (asciiCode >= 65 && asciiCode <= 90) { // between "A" and "Z" inclusive
return asciiCode - asciiOfA;
} else if (asciiCode >= 97 && asciiCode <= 122) { // between 'a' and 'z' inclusive
return asciiCode - asciiOfA - differenceBetweenZanda;
} else if (asciiCode >= 48 && asciiCode <= 57) { // between '0' and '9' inclusive
return asciiCode + 4;
} else if (character == "+") {
return 62;
} else if (character == "/") {
return 63;
} else {
return null;
}
};
var addBase64Padding = function (binaryString) {
while (binaryString.length < 6) {
binaryString = "0" + binaryString;
}
return binaryString;
};
// DATAJS INTERNAL START
datajs.activeXObject = activeXObject;
datajs.assigned = assigned;
datajs.contains = contains;
datajs.defined = defined;
datajs.delay = delay;
datajs.djsassert = djsassert;
datajs.extend = extend;
datajs.find = find;
datajs.getURIInfo = getURIInfo;
datajs.isArray = isArray;
datajs.isDate = isDate;
datajs.isObject = isObject;
datajs.normalizeURI = normalizeURI;
datajs.normalizeURICase = normalizeURICase;
datajs.parseInt10 = parseInt10;
datajs.renameProperty = renameProperty;
datajs.throwErrorCallback = throwErrorCallback;
datajs.trimString = trimString;
datajs.undefinedDefault = undefinedDefault;
datajs.decodeBase64 = decodeBase64;
datajs.convertByteArrayToHexString = convertByteArrayToHexString;
// DATAJS INTERNAL END
// CONTENT END
})(this);
/// <reference path="odata-utils.js" />
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// deferred.js
(function (global, undefined) {
// CONTENT START
var forwardCall = function (thisValue, name, returnValue) {
/// <summary>Creates a new function to forward a call.</summary>
/// <param name="thisValue" type="Object">Value to use as the 'this' object.</param>
/// <param name="name" type="String">Name of function to forward to.</param>
/// <param name="returnValue" type="Object">Return value for the forward call (helps keep identity when chaining calls).</param>
/// <returns type="Function">A new function that will forward a call.</returns>
return function () {
thisValue[name].apply(thisValue, arguments);
return returnValue;
};
};
var DjsDeferred = function () {
/// <summary>Initializes a new DjsDeferred object.</summary>
/// <remarks>
/// Compability Note A - Ordering of callbacks through chained 'then' invocations
///
/// The Wiki entry at http://wiki.commonjs.org/wiki/Promises/A
/// implies that .then() returns a distinct object.
////
/// For compatibility with http://api.jquery.com/category/deferred-object/
/// we return this same object. This affects ordering, as
/// the jQuery version will fire callbacks in registration
/// order regardless of whether they occur on the result
/// or the original object.
///
/// Compability Note B - Fulfillment value
///
/// The Wiki entry at http://wiki.commonjs.org/wiki/Promises/A
/// implies that the result of a success callback is the
/// fulfillment value of the object and is received by
/// other success callbacks that are chained.
///
/// For compatibility with http://api.jquery.com/category/deferred-object/
/// we disregard this value instead.
/// </remarks>
this._arguments = undefined;
this._done = undefined;
this._fail = undefined;
this._resolved = false;
this._rejected = false;
};
DjsDeferred.prototype = {
then: function (fulfilledHandler, errorHandler /*, progressHandler */) {
/// <summary>Adds success and error callbacks for this deferred object.</summary>
/// <param name="fulfilledHandler" type="Function" mayBeNull="true" optional="true">Success callback.</param>
/// <param name="errorHandler" type="Function" mayBeNull="true" optional="true">Error callback.</param>
/// <remarks>See Compatibility Note A.</remarks>
if (fulfilledHandler) {
if (!this._done) {
this._done = [fulfilledHandler];
} else {
this._done.push(fulfilledHandler);
}
}
if (errorHandler) {
if (!this._fail) {
this._fail = [errorHandler];
} else {
this._fail.push(errorHandler);
}
}
//// See Compatibility Note A in the DjsDeferred constructor.
//// if (!this._next) {
//// this._next = createDeferred();
//// }
//// return this._next.promise();
if (this._resolved) {
this.resolve.apply(this, this._arguments);
} else if (this._rejected) {
this.reject.apply(this, this._arguments);
}
return this;
},
resolve: function (/* args */) {
/// <summary>Invokes success callbacks for this deferred object.</summary>
/// <remarks>All arguments are forwarded to success callbacks.</remarks>
if (this._done) {
var i, len;
for (i = 0, len = this._done.length; i < len; i++) {
//// See Compability Note B - Fulfillment value.
//// var nextValue =
this._done[i].apply(null, arguments);
}
//// See Compatibility Note A in the DjsDeferred constructor.
//// this._next.resolve(nextValue);
//// delete this._next;
this._done = undefined;
this._resolved = false;
this._arguments = undefined;
} else {
this._resolved = true;
this._arguments = arguments;
}
},
reject: function (/* args */) {
/// <summary>Invokes error callbacks for this deferred object.</summary>
/// <remarks>All arguments are forwarded to error callbacks.</remarks>
if (this._fail) {
var i, len;
for (i = 0, len = this._fail.length; i < len; i++) {
this._fail[i].apply(null, arguments);
}
this._fail = undefined;
this._rejected = false;
this._arguments = undefined;
} else {
this._rejected = true;
this._arguments = arguments;
}
},
promise: function () {
/// <summary>Returns a version of this object that has only the read-only methods available.</summary>
/// <returns>An object with only the promise object.</returns>
var result = {};
result.then = forwardCall(this, "then", result);
return result;
}
};
var createDeferred = function () {
/// <summary>Creates a deferred object.</summary>
/// <returns type="DjsDeferred">
/// A new deferred object. If jQuery is installed, then a jQuery
/// Deferred object is returned, which provides a superset of features.
/// </returns>
if (window.jQuery && window.jQuery.Deferred) {
return new window.jQuery.Deferred();
} else {
return new DjsDeferred();
}
};
// DATAJS INTERNAL START
window.datajs.createDeferred = createDeferred;
window.datajs.DjsDeferred = DjsDeferred;
// DATAJS INTERNAL END
// CONTENT END
})(this);
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// xml.js
(function (global, undefined) {
var datajs = window.datajs;
var activeXObject = datajs.activeXObject;
var djsassert = datajs.djsassert;
var extend = datajs.extend;
var isArray = datajs.isArray;
var isObject = datajs.isObject;
var normalizeURI = datajs.normalizeURI;
// CONTENT START
// URI prefixes to generate smaller code.
var http = "http://";
var w3org = http + "www.w3.org/"; // http://www.w3.org/
var xhtmlNS = w3org + "1999/xhtml"; // http://www.w3.org/1999/xhtml
var xmlnsNS = w3org + "2000/xmlns/"; // http://www.w3.org/2000/xmlns/
var xmlNS = w3org + "XML/1998/namespace"; // http://www.w3.org/XML/1998/namespace
var mozillaParserErroNS = http + "www.mozilla.org/newlayout/xml/parsererror.xml";
var hasLeadingOrTrailingWhitespace = function (text) {
/// <summary>Checks whether the specified string has leading or trailing spaces.</summary>
/// <param name="text" type="String">String to check.</param>
/// <returns type="Boolean">true if text has any leading or trailing whitespace; false otherwise.</returns>
var re = /(^\s)|(\s$)/;
return re.test(text);
};
var isWhitespace = function (text) {
/// <summary>Determines whether the specified text is empty or whitespace.</summary>
/// <param name="text" type="String">Value to inspect.</param>
/// <returns type="Boolean">true if the text value is empty or all whitespace; false otherwise.</returns>
var ws = /^\s*$/;
return text === null || ws.test(text);
};
var isWhitespacePreserveContext = function (domElement) {
/// <summary>Determines whether the specified element has xml:space='preserve' applied.</summary>
/// <param name="domElement">Element to inspect.</param>
/// <returns type="Boolean">Whether xml:space='preserve' is in effect.</returns>
while (domElement !== null && domElement.nodeType === 1) {
var val = xmlAttributeValue(domElement, "space", xmlNS);
if (val === "preserve") {
return true;
} else if (val === "default") {
break;
} else {
domElement = domElement.parentNode;
}
}
return false;
};
var isXmlNSDeclaration = function (domAttribute) {
/// <summary>Determines whether the attribute is a XML namespace declaration.</summary>
/// <param name="domAttribute">Element to inspect.</param>
/// <returns type="Boolean">
/// True if the attribute is a namespace declaration (its name is 'xmlns' or starts with 'xmlns:'; false otherwise.
/// </returns>
var nodeName = domAttribute.nodeName;
return nodeName == "xmlns" || nodeName.indexOf("xmlns:") === 0;
};
var safeSetProperty = function (obj, name, value) {
/// <summary>Safely set as property in an object by invoking obj.setProperty.</summary>
/// <param name="obj">Object that exposes a setProperty method.</param>
/// <param name="name" type="String" mayBeNull="false">Property name.</param>
/// <param name="value">Property value.</param>
try {
obj.setProperty(name, value);
} catch (_) { }
};
var msXmlDom3 = function () {
/// <summary>Creates an configures new MSXML 3.0 ActiveX object.</summary>
/// <remakrs>
/// This function throws any exception that occurs during the creation
/// of the MSXML 3.0 ActiveX object.
/// <returns type="Object">New MSXML 3.0 ActiveX object.</returns>
var msxml3 = activeXObject("Msxml2.DOMDocument.3.0");
if (msxml3) {
safeSetProperty(msxml3, "ProhibitDTD", true);
safeSetProperty(msxml3, "MaxElementDepth", 256);
safeSetProperty(msxml3, "AllowDocumentFunction", false);
safeSetProperty(msxml3, "AllowXsltScript", false);
}
return msxml3;
};
var msXmlDom = function () {
/// <summary>Creates an configures new MSXML 6.0 or MSXML 3.0 ActiveX object.</summary>
/// <remakrs>
/// This function will try to create a new MSXML 6.0 ActiveX object. If it fails then
/// it will fallback to create a new MSXML 3.0 ActiveX object. Any exception that
/// happens during the creation of the MSXML 6.0 will be handled by the function while
/// the ones that happend during the creation of the MSXML 3.0 will be thrown.
/// <returns type="Object">New MSXML 3.0 ActiveX object.</returns>
try {
var msxml = activeXObject("Msxml2.DOMDocument.6.0");
if (msxml) {
msxml.async = true;
}
return msxml;
} catch (_) {
return msXmlDom3();
}
};
var msXmlParse = function (text) {
/// <summary>Parses an XML string using the MSXML DOM.</summary>
/// <remakrs>
/// This function throws any exception that occurs during the creation
/// of the MSXML ActiveX object. It also will throw an exception
/// in case of a parsing error.
/// <returns type="Object">New MSXML DOMDocument node representing the parsed XML string.</returns>
var dom = msXmlDom();
if (!dom) {
return null;
}
dom.loadXML(text);
var parseError = dom.parseError;
if (parseError.errorCode !== 0) {
xmlThrowParserError(parseError.reason, parseError.srcText, text);
}
return dom;
};
var xmlThrowParserError = function (exceptionOrReason, srcText, errorXmlText) {
/// <summary>Throws a new exception containing XML parsing error information.</summary>
/// <param name="exceptionOrReason">
/// String indicatin the reason of the parsing failure or
/// Object detailing the parsing error.
/// </param>
/// <param name="srcText" type="String">
/// String indicating the part of the XML string that caused the parsing error.
/// </param>
/// <param name="errorXmlText" type="String">XML string for wich the parsing failed.</param>
if (typeof exceptionOrReason === "string") {
exceptionOrReason = { message: exceptionOrReason };
}
throw extend(exceptionOrReason, { srcText: srcText || "", errorXmlText: errorXmlText || "" });
};
var xmlParse = function (text) {
/// <summary>Returns an XML DOM document from the specified text.</summary>
/// <param name="text" type="String">Document text.</param>
/// <returns>XML DOM document.</returns>
/// <remarks>This function will throw an exception in case of a parse error.</remarks>
var domParser = window.DOMParser && new window.DOMParser();
var dom;
if (!domParser) {
dom = msXmlParse(text);
if (!dom) {
xmlThrowParserError("XML DOM parser not supported");
}
return dom;
}
try {
dom = domParser.parseFromString(text, "text/xml");
} catch (e) {
xmlThrowParserError(e, "", text);
}
var element = dom.documentElement;
var nsURI = element.namespaceURI;
var localName = xmlLocalName(element);
// Firefox reports errors by returing the DOM for an xml document describing the problem.
if (localName === "parsererror" && nsURI === mozillaParserErroNS) {
var srcTextElement = xmlFirstChildElement(element, mozillaParserErroNS, "sourcetext");
var srcText = srcTextElement ? xmlNodeValue(srcTextElement) : "";
xmlThrowParserError(xmlInnerText(element) || "", srcText, text);
}
// Chrome (and maybe other webkit based browsers) report errors by injecting a header with an error message.
// The error may be localized, so instead we simply check for a header as the
// top element or descendant child of the document.
if (localName === "h3" && nsURI === xhtmlNS || xmlFirstDescendantElement(element, xhtmlNS, "h3")) {
var reason = "";
var siblings = [];
var cursor = element.firstChild;
while (cursor) {
if (cursor.nodeType === 1) {
reason += xmlInnerText(cursor) || "";
}
siblings.push(cursor.nextSibling);
cursor = cursor.firstChild || siblings.shift();
}
reason += xmlInnerText(element) || "";
xmlThrowParserError(reason, "", text);
}
return dom;
};
var xmlQualifiedName = function (prefix, name) {
/// <summary>Builds a XML qualified name string in the form of "prefix:name".</summary>
/// <param name="prefix" type="String" maybeNull="true">Prefix string.</param>
/// <param name="name" type="String">Name string to qualify with the prefix.</param>
/// <returns type="String">Qualified name.</returns>
return prefix ? prefix + ":" + name : name;
};
var xmlAppendText = function (domNode, textNode) {
/// <summary>Appends a text node into the specified DOM element node.</summary>
/// <param name="domNode">DOM node for the element.</param>
/// <param name="text" type="String" mayBeNull="false">Text to append as a child of element.</param>
if (hasLeadingOrTrailingWhitespace(textNode.data)) {
var attr = xmlAttributeNode(domNode, xmlNS, "space");
if (!attr) {
attr = xmlNewAttribute(domNode.ownerDocument, xmlNS, xmlQualifiedName("xml", "space"));
xmlAppendChild(domNode, attr);
}
attr.value = "preserve";
}
domNode.appendChild(textNode);
return domNode;
};
var xmlAttributes = function (element, onAttributeCallback) {
/// <summary>Iterates through the XML element's attributes and invokes the callback function for each one.</summary>
/// <param name="element">Wrapped element to iterate over.</param>
/// <param name="onAttributeCallback" type="Function">Callback function to invoke with wrapped attribute nodes.</param>
var attributes = element.attributes;
var i, len;
for (i = 0, len = attributes.length; i < len; i++) {
onAttributeCallback(attributes.item(i));
}
};
var xmlAttributeValue = function (domNode, localName, nsURI) {
/// <summary>Returns the value of a DOM element's attribute.</summary>
/// <param name="domNode">DOM node for the owning element.</param>
/// <param name="localName" type="String">Local name of the attribute.</param>
/// <param name="nsURI" type="String">Namespace URI of the attribute.</param>
/// <returns type="String" maybeNull="true">The attribute value, null if not found.</returns>
var attribute = xmlAttributeNode(domNode, localName, nsURI);
return attribute ? xmlNodeValue(attribute) : null;
};
var xmlAttributeNode = function (domNode, localName, nsURI) {
/// <summary>Gets an attribute node from a DOM element.</summary>
/// <param name="domNode">DOM node for the owning element.</param>
/// <param name="localName" type="String">Local name of the attribute.</param>
/// <param name="nsURI" type="String">Namespace URI of the attribute.</param>
/// <returns>The attribute node, null if not found.</returns>
var attributes = domNode.attributes;
if (attributes.getNamedItemNS) {
return attributes.getNamedItemNS(nsURI || null, localName);
}
return attributes.getQualifiedItem(localName, nsURI) || null;
};
var xmlBaseURI = function (domNode, baseURI) {
/// <summary>Gets the value of the xml:base attribute on the specified element.</summary>
/// <param name="domNode">Element to get xml:base attribute value from.</param>
/// <param name="baseURI" mayBeNull="true" optional="true">Base URI used to normalize the value of the xml:base attribute.</param>
/// <returns type="String">Value of the xml:base attribute if found; the baseURI or null otherwise.</returns>
var base = xmlAttributeNode(domNode, "base", xmlNS);
return (base ? normalizeURI(base.value, baseURI) : baseURI) || null;
};
var xmlChildElements = function (domNode, onElementCallback) {
/// <summary>Iterates through the XML element's child DOM elements and invokes the callback function for each one.</summary>
/// <param name="element">DOM Node containing the DOM elements to iterate over.</param>
/// <param name="onElementCallback" type="Function">Callback function to invoke for each child DOM element.</param>
xmlTraverse(domNode, /*recursive*/false, function (child) {
if (child.nodeType === 1) {
onElementCallback(child);
}
// continue traversing.
return true;
});
};
var xmlFindElementByPath = function (root, namespaceURI, path) {
/// <summary>Gets the descendant element under root that corresponds to the specified path and namespace URI.</summary>
/// <param name="root">DOM element node from which to get the descendant element.</param>
/// <param name="namespaceURI" type="String">The namespace URI of the element to match.</param>
/// <param name="path" type="String">Path to the desired descendant element.</param>
/// <returns>The element specified by path and namespace URI.</returns>
/// <remarks>
/// All the elements in the path are matched against namespaceURI.
/// The function will stop searching on the first element that doesn't match the namespace and the path.
/// </remarks>
var parts = path.split("/");
var i, len;
for (i = 0, len = parts.length; i < len; i++) {
root = root && xmlFirstChildElement(root, namespaceURI, parts[i]);
}
return root || null;
};
var xmlFindNodeByPath = function (root, namespaceURI, path) {
/// <summary>Gets the DOM element or DOM attribute node under root that corresponds to the specified path and namespace URI.</summary>
/// <param name="root">DOM element node from which to get the descendant node.</param>
/// <param name="namespaceURI" type="String">The namespace URI of the node to match.</param>
/// <param name="path" type="String">Path to the desired descendant node.</param>
/// <returns>The node specified by path and namespace URI.</returns>
/// <remarks>
/// This function will traverse the path and match each node associated to a path segement against the namespace URI.
/// The traversal stops when the whole path has been exahusted or a node that doesn't belogong the specified namespace is encountered.
///
/// The last segment of the path may be decorated with a starting @ character to indicate that the desired node is a DOM attribute.
/// </remarks>
var lastSegmentStart = path.lastIndexOf("/");
var nodePath = path.substring(lastSegmentStart + 1);
var parentPath = path.substring(0, lastSegmentStart);
var node = parentPath ? xmlFindElementByPath(root, namespaceURI, parentPath) : root;
if (node) {
if (nodePath.charAt(0) === "@") {
return xmlAttributeNode(node, nodePath.substring(1), namespaceURI);
}
return xmlFirstChildElement(node, namespaceURI, nodePath);
}
return null;
};
var xmlFirstChildElement = function (domNode, namespaceURI, localName) {
/// <summary>Returns the first child DOM element under the specified DOM node that matches the specified namespace URI and local name.</summary>
/// <param name="domNode">DOM node from which the child DOM element is going to be retrieved.</param>
/// <param name="namespaceURI" type="String" optional="true">The namespace URI of the element to match.</param>
/// <param name="localName" type="String" optional="true">Name of the element to match.</param>
/// <returns>The node's first child DOM element that matches the specified namespace URI and local name; null otherwise.</returns>
return xmlFirstElementMaybeRecursive(domNode, namespaceURI, localName, /*recursive*/false);
};
var xmlFirstDescendantElement = function (domNode, namespaceURI, localName) {
/// <summary>Returns the first descendant DOM element under the specified DOM node that matches the specified namespace URI and local name.</summary>
/// <param name="domNode">DOM node from which the descendant DOM element is going to be retrieved.</param>
/// <param name="namespaceURI" type="String" optional="true">The namespace URI of the element to match.</param>
/// <param name="localName" type="String" optional="true">Name of the element to match.</param>
/// <returns>The node's first descendant DOM element that matches the specified namespace URI and local name; null otherwise.</returns>
if (domNode.getElementsByTagNameNS) {
var result = domNode.getElementsByTagNameNS(namespaceURI, localName);
return result.length > 0 ? result[0] : null;
}
return xmlFirstElementMaybeRecursive(domNode, namespaceURI, localName, /*recursive*/true);
};
var xmlFirstElementMaybeRecursive = function (domNode, namespaceURI, localName, recursive) {
/// <summary>Returns the first descendant DOM element under the specified DOM node that matches the specified namespace URI and local name.</summary>
/// <param name="domNode">DOM node from which the descendant DOM element is going to be retrieved.</param>
/// <param name="namespaceURI" type="String" optional="true">The namespace URI of the element to match.</param>
/// <param name="localName" type="String" optional="true">Name of the element to match.</param>
/// <param name="recursive" type="Boolean">
/// True if the search should include all the descendants of the DOM node.
/// False if the search should be scoped only to the direct children of the DOM node.
/// </param>
/// <returns>The node's first descendant DOM element that matches the specified namespace URI and local name; null otherwise.</returns>
var firstElement = null;
xmlTraverse(domNode, recursi