UNPKG

@esri/solution-common

Version:

Provides general helper functions for @esri/solution.js.

959 lines 32.7 kB
"use strict"; /** @license * Copyright 2018 Esri * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.uniqueStringList = exports.regExTest = exports.getTemplateById = exports.cleanLayerId = exports.cleanLayerBasedItemId = exports.cleanItemId = exports.hasDatasource = exports.globalStringReplace = exports.getUniqueTitle = exports.hasTypeKeyword = exports.hasAnyKeyword = exports.getUTCTimestamp = exports.setProp = exports.setCreateProp = exports.idTest = exports.getPropWithDefault = exports.getProps = exports.getProp = exports.getIDs = exports.getSubgroupIds = exports.failWithIds = exports.fail = exports.deleteProps = exports.deleteProp = exports.deleteItemProps = exports.sanitizeJSONAndReportChanges = exports.compareJSONProperties = exports.compareJSONNoEmptyStrings = exports.compareJSON = exports.cloneObject = exports.saveBlobAsFile = exports.jsonToJson = exports.jsonToFile = exports.jsonToBlob = exports.getTemplatedIds = exports.getSpecifiedWordRegEx = exports.getAgoIdTemplateRegEx = exports.getAgoIdRegEx = exports.generateGUID = exports.generateEmptyCreationResponse = exports.delay = exports.dedupe = exports.createShortId = exports.createLongId = exports.convertIModel = exports.checkUrlPathTermination = exports.blobToText = exports.blobToFile = exports.blobToJson = exports.appendQueryParam = void 0; exports._padPositiveNum = exports._getRandomNumberInRange = void 0; /** * Provides general helper functions. * * @module generalHelpers */ const hub_common_1 = require("@esri/hub-common"); const libConnectors_1 = require("./libConnectors"); // ------------------------------------------------------------------------------------------------------------------ // /** * Returns a URL with a query parameter appended * * @param url URL to append to * @param parameter Query parameter to append, prefixed with "?" or "&" as appropriate to what url already has * @returns New URL combining url and parameter */ function appendQueryParam(url, parameter) { return url + (url.indexOf("?") === -1 ? "?" : "&") + parameter; } exports.appendQueryParam = appendQueryParam; /** * Extracts JSON from a Blob. * * @param blob Blob to use as source * @returns A promise that will resolve with JSON or null */ function blobToJson(blob) { return new Promise((resolve) => { blobToText(blob).then((blobContents) => { try { resolve(JSON.parse(blobContents)); } catch (err) { resolve(null); } }, () => resolve(null)); }); } exports.blobToJson = blobToJson; /** * Converts a Blob to a File. * * @param blob Blob to use as source * @param filename Name to use for file * @param mimeType MIME type to override blob's MIME type * @returns File created out of Blob and filename */ function blobToFile(blob, filename, mimeType) { return new File([blob], filename ? filename : "", { type: mimeType ?? blob.type, // Blobs default to type="" }); } exports.blobToFile = blobToFile; /** * Extracts text from a Blob. * * @param blob Blob to use as source * @returns A promise that will resolve with text read from blob */ function blobToText(blob) { return new Promise((resolve) => { const reader = new FileReader(); reader.onload = function (evt) { // Disable needed because Node requires cast const blobContents = evt.target.result; resolve(blobContents ? blobContents : ""); // not handling ArrayContents variant }; reader.readAsText(blob); }); } exports.blobToText = blobToText; /** * Checks that a URL path ends with a slash. * * @param url URL to check * @returns URL, appended with slash if missing */ function checkUrlPathTermination(url) { return url ? (url.endsWith("/") ? url : url + "/") : url; } exports.checkUrlPathTermination = checkUrlPathTermination; /** * Converts a hub-style item into a solutions-style item, the difference being handling of resources. * * @param hubModel Hub-style item * @return solutions-style item */ function convertIModel(hubModel) { const item = { ...hubModel, }; item.resources = hubModel?.resources ? Object.values(hubModel.resources) : []; return item; } exports.convertIModel = convertIModel; /** * Creates a random 32-character alphanumeric string. * * @returns A lowercase 32-char alphanumeric string * @internal */ function createLongId() { // createId gets a random number, converts it to base 36 representation, then grabs chars 2-8 return (0, hub_common_1.createId)("") + (0, hub_common_1.createId)("") + (0, hub_common_1.createId)("") + (0, hub_common_1.createId)(""); } exports.createLongId = createLongId; /** * Creates a random 8-character alphanumeric string that begins with an alphabetic character. * * @returns An alphanumeric string in the range [a0000000..zzzzzzzz] */ function createShortId() { // Return a random number, but beginning with an alphabetic character so that it can be used as a valid // dotable property name. Used for unique identifiers that do not require the rigor of a full UUID - // i.e. node ids, process ids, etc. const min = 0.2777777777777778; // 0.a in base 36 const max = 0.9999999999996456; // 0.zzzzzzzz in base 36 return (_getRandomNumberInRange(min, max).toString(36) + "0000000").substr(2, 8); } exports.createShortId = createShortId; /** * Copies an input list removing duplicates. * * @param input List to be deduped * @returns Deduped list; order of items in input is not maintained */ function dedupe(input = []) { if (input.length === 0) { return []; } const dedupedList = new Set(input); const output = []; dedupedList.forEach((value) => output.push(value)); return output; } exports.dedupe = dedupe; /** * Performs an asynchronous delay. * * @param ms Milliseconds to delay * @returns Promise when delay is complete */ function delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } exports.delay = delay; /** * Flags a failure to create an item from a template. * * @param itemType The AGO item type * @param id Item id to include in response * @returns Empty creation response */ function generateEmptyCreationResponse(itemType, id = "") { return { item: null, id, type: itemType, postProcess: false, }; } exports.generateEmptyCreationResponse = generateEmptyCreationResponse; /** * Generates a version 4 2-bit variant GUID. * * @returns A GUID * @see {@link https://en.wikipedia.org/wiki/Universally_unique_identifier}, section "Version 4 (random)" */ function generateGUID() { /** @license * Copyright 2013 Steve Fenton * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * for the specific language governing permissions and limitations under the License. */ try { return crypto.randomUUID().replace(/-/g, ""); } catch { return "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, function (c) { // eslint-disable-next-line no-bitwise const r = (Math.random() * 16) | 0; // eslint-disable-next-line no-bitwise const v = c === "x" ? r : (r & 0x3) | 0x8; return v.toString(16); }); } } exports.generateGUID = generateGUID; /** * Returns a regular expression matching a global search for a 32-character AGO-style id. * * @returns Regular expression */ function getAgoIdRegEx() { return /\b([0-9A-Fa-f]){32}\b/g; } exports.getAgoIdRegEx = getAgoIdRegEx; /** * Returns a regular expression matching a global search for a 32-character AGO-style id as a Solution template variable. * * @returns Regular expression */ function getAgoIdTemplateRegEx() { return /{{\b([0-9A-Fa-f]){32}\b}}/g; } exports.getAgoIdTemplateRegEx = getAgoIdTemplateRegEx; /** * Returns a regular expression matching a global search for a word such as an AGO-style id. * * @param word Word to search for, bounded by regular expression word boundaries (\b) * @returns Regular expression */ function getSpecifiedWordRegEx(word) { return new RegExp(`\\b${word}\\b`, "g"); } exports.getSpecifiedWordRegEx = getSpecifiedWordRegEx; /** * Extracts templated ids from a block of text. * * @param text Text to scan for the pattern "{{[0-9A-F]{32}.itemId}}" * @returns List of ids found in the text; braces and ".itemId" are removed; returns empty list if no matches */ function getTemplatedIds(text) { const idTest = /{{[0-9A-F]{32}.itemId}}/gi; let ids = []; const matches = text.match(idTest); if (matches) { ids = matches.map((id) => id.replace("{{", "").replace(".itemId}}", "")); } return ids; } exports.getTemplatedIds = getTemplatedIds; /** * Converts JSON to a Blob. * * @param json JSON to use as source * @returns A blob from the source JSON */ function jsonToBlob(json) { const uint8array = new TextEncoder().encode(JSON.stringify(json)); const blobOptions = { type: "application/octet-stream" }; return new Blob([uint8array], blobOptions); } exports.jsonToBlob = jsonToBlob; /** * Converts JSON to a File. * * @param json JSON to use as source * @param filename Name to use for file * @param mimeType MIME type to override blob's MIME type * @returns File created out of JSON and filename */ function jsonToFile(json, filename, mimeType = "application/json") { return blobToFile(jsonToBlob(json), filename, mimeType); } exports.jsonToFile = jsonToFile; /** * Makes a unique copy of JSON by stringifying and parsing. * * @param json JSON to use as source * @returns A JSON object from the source JSON */ function jsonToJson(json) { return JSON.parse(JSON.stringify(json)); } exports.jsonToJson = jsonToJson; /** * Saves a blob to a file. * * @param filename Name to give file * @param blob Blob to save * @returns Promise resolving when operation is complete */ // Function is only used for live testing, so excluding it from coverage for now /* istanbul ignore next */ function saveBlobAsFile(filename, blob) { return new Promise((resolve) => { const dataUrl = URL.createObjectURL(blob); const linkElement = document.createElement("a"); linkElement.setAttribute("href", dataUrl); linkElement.setAttribute("download", filename); linkElement.style.display = "none"; document.body.appendChild(linkElement); linkElement.click(); document.body.removeChild(linkElement); setTimeout(() => { URL.revokeObjectURL(dataUrl); resolve(null); }, 500); }); } exports.saveBlobAsFile = saveBlobAsFile; /** * Makes a deep clone, including arrays but not functions. * * @param obj Object to be cloned * @returns Clone of obj * @example * ```js * import { cloneObject } from "utils/object-helpers"; * const original = { foo: "bar" } * const copy = cloneObject(original) * copy.foo // "bar" * copy === original // false * ``` */ function cloneObject(obj) { let clone = {}; // first check array if (Array.isArray(obj)) { clone = obj.map(cloneObject); } else if (typeof obj === "object") { if (obj instanceof File) { const fileOptions = obj.type ? { type: obj.type } : undefined; clone = new File([obj], obj.name, fileOptions); } else { for (const i in obj) { if (obj[i] != null && typeof obj[i] === "object") { clone[i] = cloneObject(obj[i]); } else { clone[i] = obj[i]; } } } } else { clone = obj; } return clone; } exports.cloneObject = cloneObject; /** * Compares two JSON objects using JSON.stringify. * * @param json1 First object * @param json2 Second object * @returns True if objects are the same */ function compareJSON(json1, json2) { return JSON.stringify(json1) === JSON.stringify(json2); } exports.compareJSON = compareJSON; /** * Compares two JSON objects using JSON.stringify, converting empty strings to nulls. * * @param json1 First object * @param json2 Second object * @returns True if objects are the same */ function compareJSONNoEmptyStrings(json1, json2) { const jsonStr1 = JSON.stringify(json1).replace(/":""/g, '":null'); const jsonStr2 = JSON.stringify(json2).replace(/":""/g, '":null'); return jsonStr1 === jsonStr2; } exports.compareJSONNoEmptyStrings = compareJSONNoEmptyStrings; /** * Compares two JSON objects property by property and reports each mismatch. * * @param json1 First object * @param json2 Second object * @returns A list of mismatch report strings */ function compareJSONProperties(json1, json2) { let mismatches = []; const type1 = _typeof_null(json1); const type2 = _typeof_null(json2); if (type1 !== type2) { // Ignore "undefined" vs. "null" and vice versa /* istanbul ignore else */ if ((type1 !== "undefined" && type1 !== "null") || (type2 !== "null" && type2 !== "undefined")) { mismatches.push("Type difference: " + type1 + " vs. " + type2); } } else { if (json1 !== json2) { switch (type1) { case "boolean": mismatches.push("Value difference: " + json1 + " vs. " + json2); break; case "number": mismatches.push("Value difference: " + json1 + " vs. " + json2); break; case "string": mismatches.push('String difference: "' + json1 + '" vs. "' + json2 + '"'); break; case "object": const keys1 = Object.keys(json1); const keys2 = Object.keys(json2); if (keys1.length !== keys2.length || JSON.stringify(keys1) !== JSON.stringify(keys2)) { if (Array.isArray(json1) && Array.isArray(json2)) { mismatches.push("Array length difference: [" + keys1.length + "] vs. [" + keys2.length + "]"); } else { mismatches.push("Props difference: " + JSON.stringify(keys1) + " vs. " + JSON.stringify(keys2)); } } else { for (let k = 0; k < keys1.length; ++k) { const submismatches = compareJSONProperties(json1[keys1[k]], json2[keys2[k]]); if (submismatches.length > 0) { mismatches = mismatches.concat(submismatches); } } } break; } } } return mismatches; } exports.compareJSONProperties = compareJSONProperties; /** * Sanitizes JSON and echoes changes to console. * * @param json JSON to sanitize * @param sanitizer Instance of Sanitizer class * @returns Sanitized version of `json` * @see https://github.com/esri/arcgis-html-sanitizer#sanitize-json */ function sanitizeJSONAndReportChanges(json, sanitizer) { const sanitizedJSON = (0, libConnectors_1.sanitizeJSON)(json, sanitizer); const mismatches = compareJSONProperties(json, sanitizedJSON); if (mismatches.length > 0) { console.warn("Changed " + mismatches.length + (mismatches.length === 1 ? " property" : " properties")); mismatches.forEach((mismatch) => console.warn(" " + mismatch)); } return sanitizedJSON; } exports.sanitizeJSONAndReportChanges = sanitizeJSONAndReportChanges; function deleteItemProps(itemTemplate) { const propsToRetain = [ "accessInformation", "appCategories", "banner", "categories", "culture", "description", "documentation", "extent", "groupDesignations", "industries", "languages", "licenseInfo", "listed", "name", "properties", "proxyFilter", "screenshots", "size", "snippet", "spatialReference", "tags", "title", "type", "typeKeywords", "url", ]; const propsToDelete = Object.keys(itemTemplate).filter((k) => propsToRetain.indexOf(k) < 0); deleteProps(itemTemplate, propsToDelete); return itemTemplate; } exports.deleteItemProps = deleteItemProps; /** * Deletes a property from an object. * * @param obj Object with property to delete * @param path Path into an object to property, e.g., "data.values.webmap", where "data" is a top-level property * in obj */ function deleteProp(obj, path) { const pathParts = path.split("."); if (Array.isArray(obj)) { obj.forEach((child) => deleteProp(child, path)); } else { const subpath = pathParts.slice(1).join("."); if (typeof obj[pathParts[0]] !== "undefined") { if (pathParts.length === 1) { delete obj[path]; } else { deleteProp(obj[pathParts[0]], subpath); } } } } exports.deleteProp = deleteProp; /** * Deletes properties from an object. * * @param obj Object with properties to delete * @param props Array of properties on object that should be deleted */ function deleteProps(obj, props) { props.forEach((prop) => { deleteProp(obj, prop); }); } exports.deleteProps = deleteProps; /** * Creates an AGO-style JSON failure response with success property. * * @param e Optional error information * @returns JSON structure with property success set to false and optionally including `e` */ function fail(e) { if (e) { return { success: false, error: e.response?.error || e.error || e }; } else { return { success: false }; } } exports.fail = fail; /** * Creates an AGO-style JSON failure response with success property and extended with ids list. * * @param ids List of ids * @param e Optional error information * @returns JSON structure with property success set to false and optionally including `e` */ function failWithIds(itemIds, e) { if (e) { return { success: false, itemIds, error: e.error || e }; } else { return { success: false, itemIds }; } } exports.failWithIds = failWithIds; /** * Extracts subgroup ids from item tags * * @param tags Tags in an item * @returns List of subgroup ids; subgroups are identified using tags that begin with "group." and end with a group id, * e.g., "group.8d515625ee9f49d7b4f6c6cb2a389151"; non-matching tags are ignored */ function getSubgroupIds(tags) { if (tags) { const containedGroupPrefix = "group."; return tags .filter((tag) => tag.startsWith(containedGroupPrefix)) .map((tag) => tag.substring(containedGroupPrefix.length)); } else { return []; } } exports.getSubgroupIds = getSubgroupIds; /** * Extracts the ids from a string * * @param v String to examine * @returns List of id strings found * @example * get id from * bad3483e025c47338d43df308c117308 * {bad3483e025c47338d43df308c117308 * =bad3483e025c47338d43df308c117308 * do not get id from * http: *something/name_bad3483e025c47338d43df308c117308 * {{bad3483e025c47338d43df308c117308.itemId}} * bad3483e025c47338d43df308c117308bad3483e025c47338d43df308c117308 */ function getIDs(v) { // lookbehind is not supported in safari // cannot use /(?<!_)(?<!{{)\b[0-9A-F]{32}/gi // use groups and filter out the ids that start with {{ return regExTest(v, /({*)(\b[0-9A-F]{32}\b)/gi).reduce(function (acc, _v) { /* istanbul ignore else */ if (_v.indexOf("{{") < 0) { acc.push(_v.replace("{", "")); } return acc; }, []); } exports.getIDs = getIDs; /** * Gets a property out of a deeply nested object. * Does not handle anything but nested object graph * * @param obj Object to retrieve value from * @param path Path into an object, e.g., "data.values.webmap", where "data" is a top-level property * in obj * @returns Value at end of path */ function getProp(obj, path) { return path.split(".").reduce(function (prev, curr) { /* istanbul ignore next no need to test undefined scenario */ return prev ? prev[curr] : undefined; }, obj); } exports.getProp = getProp; /** * Returns an array of values from an object based on an array of property paths. * * @param obj Object to retrieve values from * @param props Array of paths into the object e.g., "data.values.webmap", where "data" is a top-level property * @returns Array of the values plucked from the object; only defined values are returned */ function getProps(obj, props) { return props.reduce((a, p) => { const v = getProp(obj, p); if (v) { a.push(v); } return a; }, []); } exports.getProps = getProps; /** * Get a property out of a deeply nested object * Does not handle anything but nested object graph * * @param obj Object to retrieve value from * @param path Path into an object, e.g., "data.values.webmap", where "data" is a top-level property * in obj * @param defaultV Optional value to use if any part of path--including final value--is undefined * @returns Value at end of path */ function getPropWithDefault(obj, path, defaultV) { const value = path.split(".").reduce(function (prev, curr) { /* istanbul ignore next no need to test undefined scenario */ return prev ? prev[curr] : undefined; }, obj); if (typeof value === "undefined") { return defaultV; } else { return value; } } exports.getPropWithDefault = getPropWithDefault; /** * Updates a list of the items dependencies if more are found in the * provided value. * * @param v a string value to check for ids * @param deps a list of the items dependencies */ function idTest(v, deps) { const ids = getIDs(v); ids.forEach((id) => { /* istanbul ignore else */ if (deps.indexOf(id) === -1) { deps.push(id); } }); } exports.idTest = idTest; /** * Sets a deeply nested property of an object. * Creates the full path if it does not exist. * * @param obj Object to set value of * @param path Path into an object, e.g., "data.values.webmap", where "data" is a top-level property in obj * @param value The value to set at the end of the path */ function setCreateProp(obj, path, value) { const pathParts = path.split("."); pathParts.reduce((a, b, c) => { if (c === pathParts.length - 1) { a[b] = value; return value; } else { if (!a[b]) { a[b] = {}; } return a[b]; } }, obj); } exports.setCreateProp = setCreateProp; /** * Sets a deeply nested property of an object. * Does nothing if the full path does not exist. * * @param obj Object to set value of * @param path Path into an object, e.g., "data.values.webmap", where "data" is a top-level property in obj * @param value The value to set at the end of the path */ function setProp(obj, path, value) { if (getProp(obj, path)) { const pathParts = path.split("."); pathParts.reduce((a, b, c) => { if (c === pathParts.length - 1) { a[b] = value; return value; } else { return a[b]; } }, obj); } } exports.setProp = setProp; /** * Creates a timestamp string using the current UTC date and time. * * @returns Timestamp formatted as YYYYMMDD_hhmm_ssmmm, with month one-based and all values padded with zeroes on the * left as needed (`ssmmm` stands for seconds from 0..59 and milliseconds from 0..999) * @private */ function getUTCTimestamp() { const now = new Date(); return (_padPositiveNum(now.getUTCFullYear(), 4) + _padPositiveNum(now.getUTCMonth() + 1, 2) + _padPositiveNum(now.getUTCDate(), 2) + "_" + _padPositiveNum(now.getUTCHours(), 2) + _padPositiveNum(now.getUTCMinutes(), 2) + "_" + _padPositiveNum(now.getUTCSeconds(), 2) + _padPositiveNum(now.getUTCMilliseconds(), 3)); } exports.getUTCTimestamp = getUTCTimestamp; /** * Tests if an object's `item.typeKeywords` or `typeKeywords` properties has any of a set of keywords. * * @param jsonObj Object to test * @param keywords List of keywords to look for in jsonObj * @returns Boolean indicating result */ function hasAnyKeyword(jsonObj, keywords) { const typeKeywords = getProp(jsonObj, "item.typeKeywords") || jsonObj.typeKeywords || []; return keywords.reduce((a, kw) => { if (!a) { a = typeKeywords.includes(kw); } return a; }, false); } exports.hasAnyKeyword = hasAnyKeyword; /** * Tests if an object's `item.typeKeywords` or `typeKeywords` properties has a specific keyword. * * @param jsonObj Object to test * @param keyword Keyword to look for in jsonObj * @returns Boolean indicating result */ function hasTypeKeyword(jsonObj, keyword) { const typeKeywords = getProp(jsonObj, "item.typeKeywords") || jsonObj.typeKeywords || []; return typeKeywords.includes(keyword); } exports.hasTypeKeyword = hasTypeKeyword; /** * Will return the provided title if it does not exist as a property * in one of the objects at the defined path. Otherwise the title will * have a numerical value attached. * * @param title The root title to test * @param templateDictionary Hash of the facts * @param path to the objects to evaluate for potantial name clashes * @returns string The unique title to use */ function getUniqueTitle(title, templateDictionary, path) { title = title ? title.trim() : "_"; const objs = getProp(templateDictionary, path) || []; const titles = objs.map((obj) => { return obj.title; }); let newTitle = title; let i = 0; while (titles.indexOf(newTitle) > -1) { i++; newTitle = title + " " + i; } return newTitle; } exports.getUniqueTitle = getUniqueTitle; /** * Performs string replacement on every string in an object. * * @param obj Object to scan and to modify * @param pattern Search pattern in each string * @param replacement Replacement for matches to search pattern * @returns Modified obj is returned */ function globalStringReplace(obj, pattern, replacement) { if (obj) { Object.keys(obj).forEach((prop) => { const propObj = obj[prop]; if (propObj) { /* istanbul ignore else */ if (typeof propObj === "object") { globalStringReplace(propObj, pattern, replacement); } else if (typeof propObj === "string") { obj[prop] = obj[prop].replace(pattern, replacement); } } }); } return obj; } exports.globalStringReplace = globalStringReplace; /** * Tests if an array of DatasourceInfos has a given item and layer id already. * * @param datasourceInfos Array of DatasourceInfos to evaluate * @param itemId The items id to check for * @param layerId The layers id to check for * @returns Boolean indicating result */ function hasDatasource(datasourceInfos, itemId, layerId) { return datasourceInfos.some((ds) => ds.itemId === itemId && ds.layerId === layerId); } exports.hasDatasource = hasDatasource; /** * remove templatization from item id to compare * * @example * \{\{934a9ef8efa7448fa8ddf7b13cef0240.itemId\}\} * returns 934a9ef8efa7448fa8ddf7b13cef0240 */ function cleanItemId(id) { return id ? id.replace("{{", "").replace(".itemId}}", "") : id; } exports.cleanItemId = cleanItemId; /** * remove templatization from layer based item id to compare * * @example * \{\{934a9ef8efa7448fa8ddf7b13cef0240.layer0.itemId\}\} * returns 934a9ef8efa7448fa8ddf7b13cef0240 */ function cleanLayerBasedItemId(id) { return id ? id.replace("{{", "").replace(/([.]layer([0-9]|[1-9][0-9])[.](item|layer)Id)[}]{2}/, "") : id; } exports.cleanLayerBasedItemId = cleanLayerBasedItemId; /** * remove templatization from layer id to compare * * @example * \{\{934a9ef8efa7448fa8ddf7b13cef0240.layer0.layerId\}\} * returns 0 */ function cleanLayerId(id) { return id?.toString() ? parseInt(id .toString() .replace(/[{]{2}.{32}[.]layer/, "") .replace(/[.]layerId[}]{2}/, ""), 10) : id; } exports.cleanLayerId = cleanLayerId; /** * Get template from list of templates by ID * * @param templates Array of item templates to search * @param id of template we are searching for * * @returns Template associated with the user provided id argument */ function getTemplateById(templates, id) { let template; templates.some((_template) => { if (_template.itemId === id) { template = _template; return true; } return false; }); return template; } exports.getTemplateById = getTemplateById; /** * Evaluates a value with a regular expression * * @param v a string value to test with the expression * @param ex the regular expresion to test with * @returns an array of matches */ function regExTest(v, ex) { return v && ex.test(v) ? v.match(ex) : []; } exports.regExTest = regExTest; /** * Removes duplicates from a list of strings. * * @param list List to be de-duped * @returns List of unique strings */ function uniqueStringList(list) { return list.filter(hub_common_1.unique); } exports.uniqueStringList = uniqueStringList; // ------------------------------------------------------------------------------------------------------------------ // /** * Creates a random number between two values. * * @param min Inclusive minimum desired value * @param max Non-inclusive maximum desired value * @returns Random number in the range [min, max) */ function _getRandomNumberInRange(min, max) { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random#Getting_a_random_number_between_two_values // © 2006 IvanWills // MIT license https://opensource.org/licenses/mit-license.php return Math.random() * (max - min) + min; } exports._getRandomNumberInRange = _getRandomNumberInRange; /** * Pads the string representation of a number to a minimum width. Requires modern browser. * * @param n Number to pad * @param totalSize Desired *minimum* width of number after padding with zeroes * @returns Number converted to string and padded on the left as needed * @private */ function _padPositiveNum(n, totalSize) { let numStr = n.toString(); const numPads = totalSize - numStr.length; if (numPads > 0) { numStr = "0".repeat(numPads) + numStr; // TODO IE11 does not support repeat() } return numStr; } exports._padPositiveNum = _padPositiveNum; /** * Implements rejected ECMA proposal to change `typeof null` from "object" to "null". * * @param value Value whose type is sought * @returns "null" if `value` is null; `typeof value` otherwise * @see https://web.archive.org/web/20160331031419/http://wiki.ecmascript.org:80/doku.php?id=harmony:typeof_null * @private */ function _typeof_null(value) { return value === null ? "null" : typeof value; } //# sourceMappingURL=generalHelpers.js.map