@microsoft/windows-admin-center-sdk
Version:
Microsoft - Windows Admin Center Shell
1,344 lines • 95.3 kB
JavaScript
/* eslint-disable unused-imports/no-unused-vars */
/* eslint-disable no-console */
/* eslint-disable max-len */
// this code has been worked on other platform, and refactoring may cause other problem so keeps as is.
var MsftSme;
(function (MsftSme) {
'use strict';
const global = window;
const FunctionGlobal = Function;
const MathGlobal = Math;
const ObjectGlobal = Object;
// See Mark Miller’s explanation of what this does.
// http://wiki.ecmascript.org/doku.php?id=conventions:safe_meta_programming
//
// For example:
// const array_slice: <T>(target: T[]|IArguments, start?: number, end?: number) => T[] = MsPortalFx.uncurryThis(Array.prototype.slice);
// Then the call can be strong typed, rather than call/apply with only runtime check.
//
// This also **might** have the nice side-effect of reducing the size of
// the minified code by reducing x.call() to merely x()
const call = FunctionGlobal.call;
const apply = FunctionGlobal.apply;
function uncurryThis(f) {
return function () {
return call.apply(f, arguments);
};
}
MsftSme.uncurryThis = uncurryThis;
const _applyCall = uncurryThis(apply);
MsftSme.applyCall = _applyCall;
MsftSme.applyUncurry = _applyCall; // most uncurry function can be call by _applyCall except for array_concat; (see below.)
// declare variable such that minimize better and code readibility
const array_prototype = Array.prototype;
const array_prototype_concat = array_prototype.concat; // array concat does not work well with uncurryThis since it flatten the arguments array.
const array_prototype_push = array_prototype.push;
const array_filter = uncurryThis(array_prototype.filter);
const array_forEach = uncurryThis(array_prototype.forEach);
const array_join = uncurryThis(array_prototype.join);
const array_push = uncurryThis(array_prototype_push);
const array_slice = uncurryThis(array_prototype.slice);
const array_splice = uncurryThis(array_prototype.splice);
// eslint-disable-next-line unused-imports/no-unused-vars
const array_indexof = uncurryThis(array_prototype.indexOf);
const array_isArray = Array.isArray;
const string_prototype = String.prototype;
const string_concat = uncurryThis(string_prototype.concat);
const string_split = uncurryThis(string_prototype.split);
const string_substring = uncurryThis(string_prototype.substring);
const string_indexOf = uncurryThis(string_prototype.indexOf);
const string_toLowerCase = uncurryThis(string_prototype.toLowerCase);
const object_hasOwnProperty = uncurryThis(ObjectGlobal.prototype.hasOwnProperty);
const object_propertyIsEnumerable = uncurryThis(ObjectGlobal.prototype.propertyIsEnumerable);
const object_keys_original = ObjectGlobal.keys;
const _undefined = undefined;
const object_freeze = ObjectGlobal.freeze;
const functionType = "function";
const stringType = "string";
const numberType = "number";
const objectType = "object";
const undefinedType = "undefined";
class LocalStorageHandler {
/**
* Gets the local storage.
* @param window the window object to get local storage from.
* @returns The local storage.
*/
static getLocalStorage(window) {
try {
return window ? window.localStorage : localStorage;
}
catch (error) {
console.warn('Error getting the localStorage:', error);
}
}
/**
* Sets the value for the localStorage by key.
* @param key The key to set value.
* @param value The value for the key.
*/
static setItem(key, value, window) {
try {
const localStorageTarget = window ? window.localStorage : localStorage;
localStorageTarget.setItem(key, value);
}
catch (error) {
console.warn('Error saving to localStorage:', error);
}
}
/**
* Gets the value from localStorage by key.
* @param key The key to get value.
* @returns The value for the key passed in.
*/
static getItem(key, window) {
try {
const localStorageTarget = window ? window.localStorage : localStorage;
return localStorageTarget.getItem(key);
}
catch (error) {
console.warn('Error retrieving from localStorage:', error);
}
return undefined;
}
/**
* Removes the value for the localStorage by key.
* @param key The key to remove value.
*/
static removeItem(key, window) {
try {
const localStorageTarget = window ? window.localStorage : localStorage;
localStorageTarget.removeItem(key);
}
catch (error) {
console.warn('Error removing from localStorage:', error);
}
}
/**
* Clears the current localStorage.
*/
static clear(window) {
try {
const localStorageTarget = window ? window.localStorage : localStorage;
localStorageTarget.clear();
}
catch (error) {
console.warn('Error clearing localStorage:', error);
}
}
/**
* Gets the length of the local storage
*/
static getLength(window) {
let length;
try {
const localStorageTarget = window ? window.localStorage : localStorage;
length = localStorageTarget.length;
}
catch (error) {
console.warn('Error getting localStorage length:', error);
}
return length;
}
/*
* Gets the key by index.
*/
static getKey(index, window) {
let value;
try {
const localStorageTarget = window ? window.localStorage : localStorage;
value = localStorageTarget.key(index);
}
catch (error) {
console.warn('Error getting value from localStorage by key:', error);
}
return value;
}
}
MsftSme.LocalStorageHandler = LocalStorageHandler;
class SessionStorageHandler {
/**
* Gets the session storage.
* @returns The session storage.
*/
static getSessionStorage() {
try {
return sessionStorage;
}
catch (error) {
console.warn('Error getting the sessionStorage:', error);
}
}
/**
* Sets the value for the sessionStorage by key.
* @param key The key to set value.
* @param value The value for the key.
*/
static setItem(key, value) {
try {
SessionStorageHandler.getSessionStorage().setItem(key, value);
}
catch (error) {
console.warn('Error saving to sessionStorage:', error);
}
}
/**
* Gets the value from sessionStorage by key.
* @param sessionStorageTarget The sessionStorage to use.
* @param key The key to get value.
* @returns The value for the key passed in.
*/
static getItem(key) {
try {
return SessionStorageHandler.getSessionStorage().getItem(key);
}
catch (error) {
console.warn('Error retrieving from sessionStorage:', error);
}
return '{}';
}
/**
* Removes the value for the sessionStorage by key.
* @param sessionStorageTarget The sessionStorage to use.
* @param key The key to remove value.
*/
static removeItem(key) {
try {
SessionStorageHandler.getSessionStorage().removeItem(key);
}
catch (error) {
console.warn('Error removing from sessionStorage:', error);
}
}
/**
* Clears the current sessionStorage.
* @param sessionStorageTarget The sessionStorage to use.
*/
static clear() {
try {
SessionStorageHandler.getSessionStorage().clear();
}
catch (error) {
console.warn('Error clearing sessionStorage:', error);
}
}
/**
* Gets the length of the sessionStorage
* @param sessionStorageTarget The sessionStorage to use.
* @returns
*/
static getLength() {
let length;
try {
length = SessionStorageHandler.getSessionStorage().length;
}
catch (error) {
console.warn('Error getting sessionStorage length:', error);
}
return length;
}
/* Gets the key by index.
* @param sessionStorageTarget The sessionStorage to use.
* @returns
*/
static getKey(index) {
let value;
try {
value = SessionStorageHandler.getSessionStorage().key(index);
}
catch (error) {
console.warn('Error getting value from sessionStorage by key:', error);
}
return value;
}
}
MsftSme.SessionStorageHandler = SessionStorageHandler;
/**
* For testing only. Use Object.keys.
*/
function _objectKeysPolyfill(o) {
switch (typeof o) {
case stringType:
const arr = [];
for (let i in o) {
array_push(arr, i);
}
return arr;
case functionType:
case objectType:
case undefinedType:
return object_keys_original(o);
default: // probably some other native type
return [];
}
}
MsftSme._objectKeysPolyfill = _objectKeysPolyfill;
// intentionally put inside an anonymous function.
// Presence of try-catch impacts the browser's ability
// to optimize code in the same function context.
(() => {
try {
object_keys_original("");
}
catch (err) {
// it threw so this browser is in ES5 mode (probably IE11)
// let's polyfill Object.keys
ObjectGlobal.keys = _objectKeysPolyfill;
}
})();
const object_keys = ObjectGlobal.keys;
function isTypeOf(obj, type) {
return typeof obj === type;
}
function isDate(obj) {
return obj instanceof Date;
}
function isFunction(obj) {
return isTypeOf(obj, functionType);
}
MsftSme.isFunction = isFunction;
function isNumber(obj) {
return isTypeOf(obj, numberType);
}
MsftSme.isNumber = isNumber;
const regex_NonSpace = /\S/;
const regex_WhiteSpace = /\s/;
function getDisposeFunc(value) {
let dispose = value && value.dispose;
return isTypeOf(dispose, functionType) && dispose;
}
function forEachKey(obj, iterator) {
array_forEach(object_keys(obj), (k) => {
iterator(k, obj[k]);
});
}
MsftSme.forEachKey = forEachKey;
/**
* Shortcut for Object.keys(obj || {}).length.
* @return number.
*/
function keysLength(obj) {
return object_keys(obj || {}).length;
}
MsftSme.keysLength = keysLength;
/**
* Determines whether an object has properties on it.
* Will return true for the following inputs: [], {}, "", 0, 1, true, false, new Date(), function() {}.
* Will return false for the following inputs: [1], {a:1}, "123".
* @return boolean.
*/
function isEmpty(obj) {
return !keysLength(obj);
}
MsftSme.isEmpty = isEmpty;
/**
* Detect a value is Disposable.
*
* @param value The value to check against value.dispose is a function.
* @return boolean.
*/
function isDisposable(value) {
return !!getDisposeFunc(value);
}
MsftSme.isDisposable = isDisposable;
function _disposeDisposable(value) {
if (array_isArray(value)) {
array_forEach(value, _disposeDisposable);
}
const dispose = getDisposeFunc(value);
if (dispose) {
dispose.call(value); // dispose typically rely on this is the object.
}
}
/**
* call value.dispose() if a value is Disposable.
*
* @param value The value to call value.dispose()
* @return boolean;
*/
MsftSme.disposeDisposable = function ( /*...values: any[]*/) {
array_forEach(arguments, _disposeDisposable);
};
/**
* Detect a value is null.
*
* @param value The value to check against null.
* @return boolean.
*/
function isNull(value) {
return value === null;
}
MsftSme.isNull = isNull;
/**
* Detect a value is undefined.
*
* @param value The value to check against undefined.
* @return boolean.
*/
function isUndefined(value) {
return value === _undefined;
}
MsftSme.isUndefined = isUndefined;
/**
* Indicates whether the specified object is null or undefined.
*
* @param value The value to test.
* @returns True if the value parameter is null or undefined; otherwise, false.
*/
function isNullOrUndefined(value) {
return value === null || value === _undefined;
}
MsftSme.isNullOrUndefined = isNullOrUndefined;
/**
* Indicates whether the specified object is not null or undefined.
*
* @param value The value to test.
* @returns True if the value parameter is null or undefined; otherwise, false.
*/
function notNullOrUndefined(value) {
return value !== null && value !== _undefined;
}
MsftSme.notNullOrUndefined = notNullOrUndefined;
/**
* Checks if the string is null, undefined or whitespace.
*
* @param value The target string.
* @return true if the string is null, undefined or whitespace; otherwise, false.
*/
function isNullOrWhiteSpace(value) {
// http://jsperf.com/empty-string-test-regex-vs-trim/4
return isNullOrUndefined(value) || (isTypeOf(value, stringType) && !regex_NonSpace.test(value)); // if can't find any characters other than space.
}
MsftSme.isNullOrWhiteSpace = isNullOrWhiteSpace;
/**
* Checks if a string contains a white space
*
* Space, tab, line feed (newline), carriage return, form feed, and vertical tab characters are all
* considered white space characters - https://learn.microsoft.com/en-us/cpp/c-language/white-space-characters?view=msvc-170
* @param value the target string
* @returns true if string contains a white space character else false
*/
function containsWhiteSpace(value) {
return regex_WhiteSpace.test(value);
}
MsftSme.containsWhiteSpace = containsWhiteSpace;
//#region Array
/**
* Finds the index of the first element of an array that matches the predicate.
*
* @param predicate The Predicate function.
* @param startIndex The starting index. If negative, it find from the end of the array.
* If you want to continue the next search from the back you much pass in startIndex = (prevReturn - length -1)
*
* @return The first index that matches the predicate.
*/
function findIndex(array, predicate, startIndex = 0) {
if (array) {
let length = array.length;
let stop = length;
let step = 1;
let index = startIndex;
if (length) {
if (startIndex < 0) {
index += length;
step = stop = -1;
}
if (!predicate) {
return index < length && index >= 0 ? index : -1;
}
while (index !== stop) {
if (predicate(array[index], index, array)) {
return index;
}
index += step;
}
}
}
return -1;
}
MsftSme.findIndex = findIndex;
/**
* Finds the first element of an array that matches the predicate.
*
* @param predicate The Predicate function.
* @param startIndex The starting index. If negative, it find from the end of the array.
* If you want to continue the next search from the back you much pass in startIndex = (prevReturn - length -1)
*
* @return The first element that matches the predicate.
*/
function find(array, predicate, startIndex) {
const index = findIndex(array, predicate, startIndex);
return index < 0 ? _undefined : array[index];
}
MsftSme.find = find;
/**
* Returns the first element of the sequence.
*
* @return The element
*/
function first(array) {
return array ? array[0] : _undefined;
}
MsftSme.first = first;
/**
* Returns the last element of the sequence.
*
* @return The element
*/
function last(array) {
const length = array ? array.length : 0;
return length ? array[length - 1] : _undefined;
}
MsftSme.last = last;
/**
* Removes all values that equal the given item and returns them as an array
*
* @param item The value to be removed.
* @return The removed items.
*/
function remove(array, itemOrPredicate, startIndex = 0) {
const removedItems = [];
let removedCount = 0;
let predicate;
if (isTypeOf(itemOrPredicate, functionType)) {
predicate = itemOrPredicate;
}
for (let i = startIndex, l = array.length; i < l; i++) {
const item = array[i];
if (removedCount) {
array[i - removedCount] = item; //shift the item to the offset
}
if (itemOrPredicate === item || (predicate && predicate(item))) {
removedCount++;
array_push(removedItems, item);
}
}
if (removedCount) {
array_splice(array, -1 * removedCount); // remove that many item form the end;
}
return removedItems;
}
MsftSme.remove = remove;
// This function use findIndex, put it here for minimizer friendly
// sourceUnique is a flag to optimize the performance, set to true if you know source is unique already.
function pushUnique(uniqueTarget, source, predicate, sourceUnique = false) {
if (array_isArray(source)) {
let getIndex = predicate ?
(value) => findIndex(uniqueTarget, (resultValue) => predicate(resultValue, value)) :
(value) => uniqueTarget.indexOf(value);
let appendTarget = (sourceUnique) ? [] : uniqueTarget; // if source is already unique, we accumlate to a sperated array to increase the perf.
for (let i = 0, l = source.length; i < l; i++) {
let value = source[i];
if (getIndex(value) < 0) {
array_push(appendTarget, value);
}
}
if (sourceUnique) { // if source is unique, we append to a uniqueTarget .
array_prototype_push.apply(uniqueTarget, appendTarget);
}
}
return uniqueTarget;
}
MsftSme.pushUnique = pushUnique;
/**
* Returns a unique set from this array based on the predicate.
*
* @param predicate The predicate function. Added to the result if the predicate returns false.
* @return A new array with the unique values.
*/
function unique(array, predicate) {
return pushUnique([], array, predicate);
}
MsftSme.unique = unique;
function union() {
const result = [];
let lastArrayIndex = arguments.length - 2;
let predicate = arguments[lastArrayIndex + 1];
// If the predicate is not a function, it means that it is the last array to union.
if (!isTypeOf(predicate, functionType)) {
predicate = _undefined;
lastArrayIndex++;
}
for (let i = 0; i <= lastArrayIndex; i++) {
let source = unique(arguments[i], predicate); // make a smaller set
pushUnique(result, source, predicate, true /* source Unique*/);
}
return result;
}
MsftSme.union = union;
/**
* Merge multiple T, T[] into a combine T[] exclude null or undefined arguments.
*
* @param data, a list fo T, T[]
* @returns concattenated array.
*/
MsftSme.merge = function ( /*...data: T[]*/) {
// Don't use TypeScript's built-in "... rest" args syntax for perf-critical
// paths because it constructs the args array even if you don't need it,
let ret = [];
let data = array_filter(arguments, notNullOrUndefined);
return array_prototype_concat.apply(ret, data);
};
/**
* Projects each element of a sequence to a sequence and flattens the resulting sequences into one sequence.
*
* @param selector The projection function.
* @return A flattened array.
*/
function mapMany(array, selector) {
return MsftSme.merge.apply(null, array.map(selector));
}
MsftSme.mapMany = mapMany;
/**
* Sorts an array using a stable sort algorithm.
*
* This method returns a new array, it does not sort in place.
*
* @param compare The Compare function.
* @return Sorted array.
*/
function stableSort(array, compare) {
const array2 = array.map((v, i) => {
return { i: i, v: v };
});
array2.sort((a, b) => {
return compare(a.v, b.v) || (a.i - b.i);
});
return array2.map(v => v.v);
}
MsftSme.stableSort = stableSort;
function toString(item) {
return item ? item.toString() : String(item);
}
function extendArrayIntoMap(objToExtend, sourceItems, getKeyCallback, getValueCallback, onlyIfNotExist) {
getKeyCallback = getKeyCallback || toString;
// The use of args here is to reduce the array creation call and make sure the function context this is the sourceItems.
let args = [sourceItems, /*item*/ , /*index*/ , ""];
for (let i = 0, l = sourceItems.length; i < l; i++) {
let item = sourceItems[i];
args[1] = item;
args[2] = i;
args[3] = _undefined;
let key = call.apply(getKeyCallback, args);
if (!onlyIfNotExist || objToExtend[key] === _undefined) {
args[3] = key; // This is convient for the get value function to know the key that previous interpreted by getKeyCallback
let value = getValueCallback ? call.apply(getValueCallback, args) : item;
// Only extend this key if the value return is not undefined.
if (value !== _undefined) {
objToExtend[key] = value;
}
}
}
}
MsftSme.extendArrayIntoMap = extendArrayIntoMap;
function extendStringMapIntoMap(objToExtend, sourceItems, getValueCallback, onlyIfNotExist) {
// The use of args here is to reduce the array creation call and make sure the function context this is the sourceItems.
let args = [sourceItems, /*item*/ , /*key*/];
forEachKey(sourceItems, (key, item) => {
if (!onlyIfNotExist || objToExtend[key] === _undefined) {
args[1] = item;
args[2] = key; // This is convient for the get value function to know the key that previous interpreted by getKeyCallback
let value = getValueCallback ? call.apply(getValueCallback, args) : (item || key);
// Only extend this key if the value return is not undefined.
if (value !== _undefined) {
objToExtend[key] = value;
}
}
});
}
MsftSme.extendStringMapIntoMap = extendStringMapIntoMap;
function getStringMapFunc(keys) {
if (array_isArray(keys)) {
// make a copy of keys so that future changes to the input array do not impact the behavior of the returned function.
keys = array_slice(keys);
}
else {
keys = arguments;
}
return function ( /* arguments*/) {
// Most people call .hasOwnProperty or .constructor (which it should not)
// since there is no guarantee that any object return to have those function -Expecially in generic function.
// http://stackoverflow.com/questions/12017693/why-use-object-prototype-hasownproperty-callmyobj-prop-instead-of-myobj-hasow
// Unfortunately, this need to be changed.
let ret = {};
array_forEach(arguments, (value, index) => {
let key = keys[index];
if (value !== _undefined) {
ret[key] = value;
}
});
return ret;
};
}
MsftSme.getStringMapFunc = getStringMapFunc;
/**
* Helper funciton to create a object lightweight constructor
*
* @param keys the ordered argument keys
*
* @return The function that will return string map base on the arguments index order of keys
*/
function applyStringMapFunc(keys) {
return getStringMapFunc.apply(_undefined, keys);
}
MsftSme.applyStringMapFunc = applyStringMapFunc;
/**
* Helper funciton to create a object of type NameValue<N, T>
*
* @param name name
* @param value value
*
* @return an object of NameValue<N, T>
*/
MsftSme.getNameValue = getStringMapFunc("name", "value");
/**
* Get a list of typeScript Enum into Array
*
* @param tsEnumeration The Type script Enum Array
* @param sort optional whether to sort by enum's value
* @return all NameValue<string, number>[] for this typeScriptEnum
*/
function getEnumArray(tsEnumeration, sort) {
let retVal = [];
forEachKey(tsEnumeration, (key, val) => {
if (isTypeOf(key, stringType) && isTypeOf(val, numberType)) {
array_push(retVal, MsftSme.getNameValue(key, val));
}
});
return sort ? retVal.sort((a, b) => {
return a.value - b.value;
}) : retVal;
}
MsftSme.getEnumArray = getEnumArray;
/**
* Coerce an input into an array if it isn't already one.
*/
function makeArray(input) {
if (!array_isArray(input)) {
if (isNullOrUndefined(input)) {
input = [];
}
else {
input = [input];
}
}
return input;
}
MsftSme.makeArray = makeArray;
//#endregion Array
//#region Date
/**
* Checks if given dates are equal.
*
* @param left Left hand side date.
* @param left Right hand side date.
* @return True if left date is equal to right date.
*/
function areEqualDates(left, right) {
return isDate(left) && isDate(right) && !(left > right || left < right);
}
MsftSme.areEqualDates = areEqualDates;
/**
* Round down the date.getTime() to seconds
*
* @param date.
* @return the getTime in seconds
*/
function toSeconds(date) {
// The old function use toString to trim off microseconds to time comparsion for stablesort
// return new Date(x.toString()).getTime(); --- this is slow:
// http://jsperf.com/truncating-decimals
// x = new Date()
//Wed Feb 17 2016 12:15:39 GMT- 0800(Pacific Standard Time)
//y = new Date(x.toString()).getTime()
//1455740139000
//z = (x.getTime() / 1000) | 0
//1455740139
return (date.getTime() / 1000) | 0;
}
MsftSme.toSeconds = toSeconds;
//#endregion Date
//#region Math
const hexCharsLowerCase = string_split("0123456789abcdef", "");
const hexBytesLower = [];
array_forEach(hexCharsLowerCase, (upper) => {
array_forEach(hexCharsLowerCase, (lower) => {
array_push(hexBytesLower, upper + lower);
});
});
const sizeOfGuidInBytes = 20;
const tempUintArray = new Uint8Array(sizeOfGuidInBytes);
const tempStringArray = new Array(sizeOfGuidInBytes);
function applyAndOr(index, and, or) {
const temp = tempUintArray[index] & and;
tempUintArray[index] = temp | or;
}
////// TODO: const globalCrypto = <Crypto>(window.crypto || (<any>window).msCrypto);
const globalCrypto = (window.crypto || window.msCrypto);
// c.f. rfc4122 (UUID version 4 = xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx)
// xx xx xx xx - xx xx - 4x xx - yx xx - xx xx xx xx xx xx
// 00 11 22 33 4 55 66 7 88 99 10 11 12 13 14 15 16 17 18 19
function newGuidCrypto() {
globalCrypto.getRandomValues(tempUintArray);
applyAndOr(8, 0x0F, 0x40); // set upper half of the 8th byte to 0x4
applyAndOr(11, 0x3F, 0x80); // set the two most significant bits of the 11th byte to 10b
for (let i = 0; i < sizeOfGuidInBytes; i++) {
tempStringArray[i] = hexBytesLower[tempUintArray[i]];
}
tempStringArray[4] = tempStringArray[7] = tempStringArray[10] = tempStringArray[13] = "-";
return MsftSme.applyUncurry(string_concat, "", tempStringArray);
}
/**
* HelperReturns hex number string.
*
* @param len The number of the output string length
* @return a hexnumber string of length len
*/
function getRandomHexString(len) {
let tmp;
let ret = new Array(len);
// This implementation optimization of speed mimimize the cost of Math.random.
// equal chance for all number
while (len) {
tmp = 4294967296 * MathGlobal.random() | 0; // get the max integer our of 32 digit.
let times = 8; // for every random number we can harvest 8 times.
while (times--) {
ret[--len] = hexCharsLowerCase[tmp & 0xF]; // fill from the back.
if (len < 0) {
return ret; // we filled all the bucket, return now.
}
tmp >>>= 4; // zero fill right shift to ensure return is always positive number.
}
}
}
function newGuidFallback() {
// c.f. rfc4122 (UUID version 4 = xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx)
const guid = getRandomHexString(36);
guid[8] = guid[13] = guid[18] = guid[23] = "-"; // fill in the dash.
guid[14] = "4"; // set the 4 in the guid.
// "Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively"
// guid[19] = hexValues[8 + (Math.random() * 4) | 0]; /*clockSequenceHi*/
guid[19] = hexCharsLowerCase[8 + (hexCharsLowerCase.indexOf(guid[19]) & 0x3)]; // Since guid[19] is already random. reused the numbe by get its index rather than another Math.random call.
return _applyCall(string_concat, "", guid);
}
/**
* Returns a GUID such as xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx.
*
* @return New GUID.
*/
MsftSme.newGuid = globalCrypto ? newGuidCrypto : newGuidFallback;
const maxCounter = 0xFFF;
function toHexString(counter) {
return hexBytesLower[counter >> 4] + hexCharsLowerCase[counter & 0xF];
}
function toHex(str) {
let result = '';
for (let i = 0; i < str.length; i++) {
result += str.charCodeAt(i).toString(16);
}
return result;
}
MsftSme.toHex = toHex;
/**
* Returns a function that can generate globally unique identifiers.
* Generates a new guid every 4096 calls and concatenates it with an
* auto incrementing number. This maintains a complient GUID 4 format
* if no prefix is added.
*
* @return a globally unique string generating function.
*/
function getUniqueIdGenerator(prefix) {
prefix = prefix ? (prefix + "-") : "";
// use a range for counter that gives us 3 digits with minimum effort
let guid = "";
let counter = maxCounter;
return () => {
if (++counter > maxCounter) {
counter = 0;
}
if (!counter) {
guid = prefix + MsftSme.newGuid().substring(0, 33);
}
return guid + toHexString(counter);
};
}
MsftSme.getUniqueIdGenerator = getUniqueIdGenerator;
/**
* Returns a function that can generate unique id under the prefix
* Concatenates prefix with an auto incrementing number.
*
* @return a unique string generating function which return a prefix with auto incrementing number
*/
function getIdGenerator(prefix) {
// use two counters and
// limit the size of the lower counter because
// toString is expensive for large numbers
let counterUpper = -1;
let counterLower = maxCounter;
let realPrefix = "";
return () => {
if (++counterLower > maxCounter) {
counterLower = 0;
counterUpper++;
realPrefix = prefix + counterUpper.toString(16) + "-";
}
return realPrefix + toHexString(counterLower);
};
}
MsftSme.getIdGenerator = getIdGenerator;
/**
* Returns a globally unique identifier string.
* Lighter-weight than newGuid.
*
* @return a globally unique string.
*/
MsftSme.getUniqueId = getUniqueIdGenerator();
/**
* Rounds a number to the specified precision.
*
* @param number The number to round.
* @param precision The precision to round the number to. Defaults to 0.
* @returns The rounded number.
*/
function round(number, precision) {
precision = MathGlobal.pow(10, precision || 0);
return MathGlobal.round(Number(number) * precision) / precision;
}
MsftSme.round = round;
/**
* Truncates a number to the integer part.
*
* @param value The number to truncate.
* @return The integer number.
*/
function truncate(value) {
// Converts to integer by bit operation
return value | 0;
}
MsftSme.truncate = truncate;
/**
* Returns the result of the boolean exclusive-or operator.
*
* @param a First operand.
* @param b Second operand.
* @return true if the arguments have different values, false otherwise.
*/
function xor(a, b) {
return a ? !b : b;
}
MsftSme.xor = xor;
/**
* Returns the result of the bitwise and operator. Required because tslint disables
* bitwise operators by default.
*
* @param a First operand.
* @param b Second operand.
* @return numerical result of applying bitwise AND to a & b
*/
function applyBitwiseAnd(a, b) {
return a & b;
}
MsftSme.applyBitwiseAnd = applyBitwiseAnd;
/**
* Returns the result of the bitwise or operator. Required because tslint disables
* bitwise operators by default.
*
* @param a First operand.
* @param b Second operand.
* @return numerical result of applying bitwise OR to a & b
*/
function applyBitwiseOr(a, b) {
return a | b;
}
MsftSme.applyBitwiseOr = applyBitwiseOr;
//#endregion Map
//#region Number
/**
* Generates a random integer between min and max inclusive.
*
* @param min The minimum integer result.
* @param max The maximum integer result.
* @return A random integer.
*/
function random(min, max) {
if (min === _undefined || max === _undefined || min > max) {
return;
}
return MathGlobal.floor(MathGlobal.random() * (max - min + 1)) + min;
}
MsftSme.random = random;
//#endregion Number
//#region Object
/**
* Determines whether an object has a property with the specified name.
* @param target the object to check.
* @param v A property name.
*/
MsftSme.hasOwnProperty = object_hasOwnProperty;
/**
* Determines whether an object has an enumerable property with the specified name.
* @param target the object to check.
* @param v A property name.
*/
MsftSme.propertyIsEnumerable = object_propertyIsEnumerable;
/**
* Returns a boolean reflecting whether two scalar values (not object-typed, not array-typed, not function-typed)
* are equal. Accounts for the fact that JavaScript Date derives from Object.
* The caller is responsible for supplying exclusively number-, string- or Date-typed values here.
*
* @param left The first scalar value.
* @param right The second scalar value.
* @return A boolean reflecting whether the two scalar values are equal.
*/
function areEqual(left, right) {
return left === right || areEqualDates(left, right);
}
MsftSme.areEqual = areEqual;
/**
* Verifies that two arrays are equal.
*
* @param array1 The array to check.
* @param array2 The array to compare the first array to.
* @returns A value indicating whether or not the two arrays are equal.
*/
function arrayEquals(array1, array2) {
if (array1 === array2) {
return true;
}
else if (!array1 || !array2) {
return false;
}
else if (array1.length !== array2.length) {
return false;
}
else {
for (let i = 0; i < array1.length; i++) {
if (array1[i] !== array2[i]) {
return false;
}
}
return true;
}
}
MsftSme.arrayEquals = arrayEquals;
function getTypeOf(x) {
let typeOfX = typeof x;
if (typeOfX === objectType) {
if (x === null) {
typeOfX = "null";
}
else if (array_isArray(x)) {
typeOfX = "array";
}
else if (isDate(x)) {
typeOfX = "date";
}
}
return typeOfX;
}
MsftSme.getTypeOf = getTypeOf;
/**
* Checks for deep equality of two object.
*
* @param a Object 1
* @param b Object 2
* @param mapFunc Optional parameter, used convert value conversion use on the value to compare
* @return true if both of the object contains the same information; false otherwise;
*/
// eslint-disable-next-line unused-imports/no-unused-vars
function deepEqualsMap(a, b, mapFunc) {
if (mapFunc) {
a = mapFunc(a);
b = mapFunc(b);
}
if (a === b) {
return true;
}
else if (!a || !b) {
return false;
}
let typeofInput = getTypeOf(a);
if (typeofInput !== getTypeOf(b)) {
return false;
}
switch (typeofInput) {
case "array":
const aArray = a;
return a.length === b.length &&
aArray.every((v, index) => deepEqualsMap(v, b[index], mapFunc));
case "date":
return a.getTime() === b.getTime();
case objectType:
const keysOfInput = object_keys(a);
return keysOfInput.length === keysLength(b) &&
keysOfInput.every((key) => deepEqualsMap(a[key], b[key], mapFunc));
default:
// basic type that failed the === check
return false;
}
}
/**
* Checks if a given value is a javascript object or not.
*
* @param value Value to test.
* @return True if value is an object, false otherwise.
*/
function isObject(value) {
return typeof value === objectType;
}
MsftSme.isObject = isObject;
/**
* Checks if a given value is a plain object or not.
*
* @param value Value to test.
* @return True if value is an object, false otherwise.
*/
function isPlainObject(value) {
return getTypeOf(value) === objectType;
}
MsftSme.isPlainObject = isPlainObject;
/**
* Maps each value of the input object. Values that map to null or undefined are skipped.
*
* @param obj Input object whose properties are to be mapped.
* @param callback Invoked for each property of the object to perform the mapping.
* @param arg An Optional value that can be passed to callback.
* @return An array of mapped values.
*/
function map(obj, callback, arg) {
let callBackArgs = [obj, /*item*/ , /*key*/ , arg];
let keys = object_keys(obj);
let ret = keys.map((key) => {
callBackArgs[1] = obj[key]; // item;
callBackArgs[2] = key; // key;
return call.apply(callback, callBackArgs);
});
// Flatten any nested arrays and exclude null, undefined items.
return MsftSme.merge.apply(null, ret);
}
MsftSme.map = map;
/**
* Shallow copy from a key/value pairs object.
*
* @param to An un-typed object to be populated.
* @param from An un-typed object with values to populate.
* @param scopes Scoped down the list for shallowCopy
*/
function shallowCopyFromObject(to, from, scopes) {
// http://jsperf.com/shallowcopyobjects/3
scopes = scopes || object_keys(from);
for (let i = 0; i < scopes.length; i++) {
let key = scopes[i];
let value = from[key];
if (value !== _undefined) {
to[key] = value;
}
}
}
MsftSme.shallowCopyFromObject = shallowCopyFromObject;
/**
* Merges a property from a destination object from a source object
* @param dest The destination object
* @param src The source object
* @param propName The name of the property to assign
*/
function deepAssignProperty(dest, src, propName) {
let value = src[propName];
// if there is no value, dont assign.
if (isNullOrUndefined(value)) {
return;
}
if (!isObject(value)) {
/**
* If the src prop is not an object then set the destination prop directly.
*/
dest[propName] = value;
}
else if (isObject(value) && isEmpty(value)) {
/**
* If the object has no children prop then set the destination prop directly.
*
* Handles a special case of a date object as it cannot be shallow copied in
* the same major as any other supported type.
* Shown here: https://github.com/davidmarkclements/rfdc/blob/master/index.js
*/
if (value instanceof Date) {
dest[propName] = new Date(value);
}
else {
// Generic object will be shallow copied as it has no properties.
dest[propName] = array_isArray(value) ? [...value] : { ...value };
}
}
else if (isObject(value) && !MsftSme.hasOwnProperty(dest, propName) || (typeof (dest[propName]) !== typeof (value))) {
/** If the src prop is an object and the prop is not defined, create new object
* and copy over src.
*
* Or if the dest and src prop do not match types,
* pave over dest with src.
*/
dest[propName] = deepAssignInternal(array_isArray(value) ? [] : {}, value);
}
else {
// Otherwise merge to src prop with the dest prop.
dest[propName] = deepAssignInternal(dest[propName], value);
}
}
/**
* Internal method for merging one object with another
* @param dest The destination object
* @param src The source object
*/
function deepAssignInternal(dest, src) {
// if the to destination is the same object as the source, save some time by just returning
if (dest === src) {
return dest;
}
// loop through the src objects properties and merge them deeply to the dest
for (let propName in src) {
if (MsftSme.hasOwnProperty(src, propName)) {
deepAssignProperty(dest, src, propName);
}
}
// loop through the src symbols properties and merge them deeply to the dest
if (Object.getOwnPropertySymbols) {
let symbols = Object.getOwnPropertySymbols(src);
symbols.forEach(symbol => {
if (MsftSme.propertyIsEnumerable(src, symbol)) {
deepAssignProperty(dest, src, symbol);
}
});
}
// return the destination object
return dest;
}
/**
* Merges a set of source objects to a destination object. Does not
* handle objects with circular references.
*
* Supported types:
* - Object
* - Array
* - Number
* - String
* - null
* - Date
*
* @param dest The destination object
* @param sources the source objects
*/
function deepAssign(dest, ...sources) {
// merge all sources into the dest object in order
sources.forEach(src => {
return deepAssignInternal(dest, src || {});
});
// return the dest object
return dest;
}
MsftSme.deepAssign = deepAssign;
/**
* Copies a source object with any nested objects. Does not
* handle objects with circular references.
* @param dest The source object
*
* Supported types:
* - Object
* - Array
* - Number
* - String
* - null
* - Date
*
* @return a new object that is a copy of the source object
*/
function deepCopy(source) {
return deepAssign({}, source);
}
MsftSme.deepCopy = deepCopy;
/**
* Searches an object for a value at a given path.
* @param object The object to search in
* @param path the path to walk
* @param firstPass internal use, indicates that this is the first pass.
* @returns the object at the end of the path
*/
function getValueInternal(object, path, firstPass) {
// return null if either argument is not provided
if (isNullOrUndefined(object) || isNullOrUndefined(path)) {
return null;
}
// always convert path to string
let strPath = '' + path;
// on the first pass, replace delimiters in the object path with *
if (firstPass) {
strPath = strPath.replace(/\]\.|\.|\[|\]/g, "*");
}
// find the next delimiter
let index = strPath.indexOf('*');
// if there are no more delimiters, return the object at the path
if (index === -1) {
return object[strPath];
}
// split the path at the delimiter. Use the first segment as our next object and the second segment for the remaining path
let next = strPath.slice(0, index);
let remainingPath = strPath.slice(index + 1);
if (object[next] !== undefined && remainingPath.length > 0) {
// dive in recursively to the next object
return getValueInternal(object[next], remainingPath, false);
}
// we found our target object. Return it
return object[next];
}
/**
* Searchs an object for a value at a given path.
* @param object The object to search in
* @param path the path to walk
* @returns the object at the end of the path
*/
function getValue(object, path) {
// call our internal get value function
return getValueInternal(object, path, true);
}
MsftSme.getValue = getValue;
/**
* Sets a value in an object at a given path
* @param object The object to search in
* @param path the path to walk
* @param value the value to set at object.path
* @param firstPass internal use, indicates that this is the first pass.
* @returns the object at the end of the path
*/
function setValueInternal(object, path, value, firstPass) {
if (isNullOrUndefined(object)) {
object = {};
}
if (isNullOrUndefined(path)) {
return null;
}
// always convert path to string
let strPath = '' + path;
// on the first pass, replace delimiters in the object path with *
if (firstPass) {
strPath = strPath.replace(/\]\.|\.|\[|\]/g, "*");
}
// find the next delimiter
let index = strPath.indexOf('*');
// if there are no more delimiters, return the object at the path
if (index === -1) {
object[strPath] = value;
return object;
}
// split the path at the delimiter. Use the first segment as our next object and the second segment for the remaining path
let next = strPath.slice(0, index);
let remainingPath = strPath.slice(index + 1);
if (remainingPath.length > 0) {
// dive in recursively to the next object
return setValueInternal(object[next], remainingPath, value, false);
}
object[next] = value;
return object;
}
/**
* Sets a value in an object at a given path
* @param object The object to search in
* @param path the path