blob: 429a56a4eccd1c6d94a0262adfa6ed6e3dbbd43b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
'use strict';
/** @module odatajs/utils */
function inBrowser() {
return typeof window !== 'undefined';
}
/** Creates a new ActiveXObject from the given progId.
* @param {String} progId - ProgId string of the desired ActiveXObject.
* @returns {Object} The ActiveXObject instance. Null if ActiveX is not supported by the browser.
* This function throws whatever exception might occur during the creation
* of the ActiveXObject.
*/
var activeXObject = function (progId) {
if (window.ActiveXObject) {
return new window.ActiveXObject(progId);
}
return null;
};
/** Checks whether the specified value is different from null and undefined.
* @param [value] Value to check ( may be null)
* @returns {Boolean} true if the value is assigned; false otherwise.
*/
function assigned(value) {
return value !== null && value !== undefined;
}
/** Checks whether the specified item is in the array.
* @param {Array} [arr] Array to check in.
* @param item - Item to look for.
* @returns {Boolean} true if the item is contained, false otherwise.
*/
function contains(arr, item) {
var i, len;
for (i = 0, len = arr.length; i < len; i++) {
if (arr[i] === item) {
return true;
}
}
return false;
}
/** Given two values, picks the first one that is not undefined.
* @param a - First value.
* @param b - Second value.
* @returns a if it's a defined value; else b.
*/
function defined(a, b) {
return (a !== undefined) ? a : b;
}
/** Delays the invocation of the specified function until execution unwinds.
* @param {Function} callback - Callback function.
*/
function delay(callback) {
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);
}
/** Throws an exception in case that a condition evaluates to false.
* @param {Boolean} condition - Condition to evaluate.
* @param {String} message - Message explaining the assertion.
* @param {Object} data - Additional data to be included in the exception.
*/
function djsassert(condition, message, data) {
if (!condition) {
throw { message: "Assert fired: " + message, data: data };
}
}
/** Extends the target with the specified values.
* @param {Object} target - Object to add properties to.
* @param {Object} values - Object with properties to add into target.
* @returns {Object} The target object.
*/
function extend(target, values) {
for (var name in values) {
target[name] = values[name];
}
return target;
}
function find(arr, callback) {
/** Returns the first item in the array that makes the callback function true.
* @param {Array} [arr] Array to check in. ( may be null)
* @param {Function} callback - Callback function to invoke once per item in the array.
* @returns The first item that makes the callback return true; null otherwise or if the array is null.
*/
if (arr) {
var i, len;
for (i = 0, len = arr.length; i < len; i++) {
if (callback(arr[i])) {
return arr[i];
}
}
}
return null;
}
function isArray(value) {
/** Checks whether the specified value is an array object.
* @param value - Value to check.
* @returns {Boolean} true if the value is an array object; false otherwise.
*/
return Object.prototype.toString.call(value) === "[object Array]";
}
/** Checks whether the specified value is a Date object.
* @param value - Value to check.
* @returns {Boolean} true if the value is a Date object; false otherwise.
*/
function isDate(value) {
return Object.prototype.toString.call(value) === "[object Date]";
}
/** Tests whether a value is an object.
* @param value - Value to test.
* @returns {Boolean} True is the value is an object; false otherwise.
* Per javascript rules, null and array values are objects and will cause this function to return true.
*/
function isObject(value) {
return typeof value === "object";
}
/** Parses a value in base 10.
* @param {String} value - String value to parse.
* @returns {Number} The parsed value, NaN if not a valid value.
*/
function parseInt10(value) {
return parseInt(value, 10);
}
/** Renames a property in an object.
* @param {Object} obj - Object in which the property will be renamed.
* @param {String} oldName - Name of the property that will be renamed.
* @param {String} newName - New name of the property.
* This function will not do anything if the object doesn't own a property with the specified old name.
*/
function renameProperty(obj, oldName, newName) {
if (obj.hasOwnProperty(oldName)) {
obj[newName] = obj[oldName];
delete obj[oldName];
}
}
/** Default error handler.
* @param {Object} error - Error to handle.
*/
function throwErrorCallback(error) {
throw error;
}
/** Removes leading and trailing whitespaces from a string.
* @param {String} str String to trim
* @returns {String} The string with no leading or trailing whitespace.
*/
function trimString(str) {
if (str.trim) {
return str.trim();
}
return str.replace(/^\s+|\s+$/g, '');
}
/** Returns a default value in place of undefined.
* @param [value] Value to check (may be null)
* @param defaultValue - Value to return if value is undefined.
* @returns value if it's defined; defaultValue otherwise.
* This should only be used for cases where falsy values are valid;
* otherwise the pattern should be 'x = (value) ? value : defaultValue;'.
*/
function undefinedDefault(value, defaultValue) {
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"];
/** Gets information about the components of the specified URI.
* @param {String} uri - URI to get information from.
* @return {Object} An object with an isAbsolute flag and part names (scheme, authority, etc.) if available.
*/
function getURIInfo(uri) {
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;
}
/** Builds a URI string from its components.
* @param {Object} uriInfo - An object with uri parts (scheme, authority, etc.).
* @returns {String} URI string.
*/
function getURIFromInfo(uriInfo) {
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;
/** Normalizes the casing of a URI.
* @param {String} uri - URI to normalize, absolute or relative.
* @returns {String} The URI normalized to lower case.
*/
function normalizeURICase(uri) {
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();
});
}
/** Normalizes a possibly relative URI with a base URI.
* @param {String} uri - URI to normalize, absolute or relative
* @param {String} base - Base URI to compose with (may be null)
* @returns {String} The composed URI if relative; the original one if absolute.
*/
function normalizeURI(uri, base) {
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);
}
/** Merges the path of a relative URI and a base URI.
* @param {String} uriPath - Relative URI path.
* @param {String} basePath - Base URI path.
* @returns {String} A string with the merged path.
*/
function mergeUriPathWithBase(uriPath, basePath) {
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;
}
/** Removes the special folders . and .. from a URI's path.
* @param {string} path - URI path component.
* @returns {String} Path without any . and .. folders.
*/
function removeDotsFromPath(path) {
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;
}
function convertByteArrayToHexString(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;
}
function decodeBase64(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;
}
function getBase64IndexValue(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;
}
}
function addBase64Padding(binaryString) {
while (binaryString.length < 6) {
binaryString = "0" + binaryString;
}
return binaryString;
}
function getJsonValueArraryLength(data) {
if (data && data.value) {
return data.value.length;
}
return 0;
}
function sliceJsonValueArray(data, start, end) {
if (data === undefined || data.value === undefined) {
return data;
}
if (start < 0) {
start = 0;
}
var length = getJsonValueArraryLength(data);
if (length < end) {
end = length;
}
var newdata = {};
for (var property in data) {
if (property == "value") {
newdata[property] = data[property].slice(start, end);
} else {
newdata[property] = data[property];
}
}
return newdata;
}
function concatJsonValueArray(data, concatData) {
if (concatData === undefined || concatData.value === undefined) {
return data;
}
if (data === undefined || Object.keys(data).length === 0) {
return concatData;
}
if (data.value === undefined) {
data.value = concatData.value;
return data;
}
data.value = data.value.concat(concatData.value);
return data;
}
function endsWith(input, search) {
return input.indexOf(search, input.length - search.length) !== -1;
}
function startsWith (input, search) {
return input.indexOf(search) === 0;
}
function getFormatKind(format, defaultFormatKind) {
var formatKind = defaultFormatKind;
if (!assigned(format)) {
return formatKind;
}
var normalizedFormat = format.toLowerCase();
switch (normalizedFormat) {
case "none":
formatKind = 0;
break;
case "minimal":
formatKind = 1;
break;
case "full":
formatKind = 2;
break;
default:
break;
}
return formatKind;
}
exports.inBrowser = inBrowser;
exports.activeXObject = activeXObject;
exports.assigned = assigned;
exports.contains = contains;
exports.defined = defined;
exports.delay = delay;
exports.djsassert = djsassert;
exports.extend = extend;
exports.find = find;
exports.getURIInfo = getURIInfo;
exports.isArray = isArray;
exports.isDate = isDate;
exports.isObject = isObject;
exports.normalizeURI = normalizeURI;
exports.normalizeURICase = normalizeURICase;
exports.parseInt10 = parseInt10;
exports.renameProperty = renameProperty;
exports.throwErrorCallback = throwErrorCallback;
exports.trimString = trimString;
exports.undefinedDefault = undefinedDefault;
exports.decodeBase64 = decodeBase64;
exports.convertByteArrayToHexString = convertByteArrayToHexString;
exports.getJsonValueArraryLength = getJsonValueArraryLength;
exports.sliceJsonValueArray = sliceJsonValueArray;
exports.concatJsonValueArray = concatJsonValueArray;
exports.startsWith = startsWith;
exports.endsWith = endsWith;
exports.getFormatKind = getFormatKind;