UNPKG

js-utl

Version:

A collection of JS utility functions to be used across several applications or libraries.

1,493 lines (1,365 loc) 244 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define("JSUtl", [], factory); else if(typeof exports === 'object') exports["JSUtl"] = factory(); else root["JSUtl"] = factory(); })((typeof self !== 'undefined' ? self : this), function() { return /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ "./src/modules/array.js": /*!******************************!*\ !*** ./src/modules/array.js ***! \******************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "unshiftArray": () => (/* binding */ unshiftArray), /* harmony export */ "cloneArray": () => (/* binding */ cloneArray), /* harmony export */ "arraySliceFromValueToValue": () => (/* binding */ arraySliceFromValueToValue), /* harmony export */ "areArrayItemsAllCoercibleToNumber": () => (/* binding */ areArrayItemsAllCoercibleToNumber), /* harmony export */ "arrayOrArrayLike": () => (/* binding */ arrayOrArrayLike), /* harmony export */ "lastOfArray": () => (/* binding */ lastOfArray), /* harmony export */ "firstOfArray": () => (/* binding */ firstOfArray), /* harmony export */ "arrayFindReverse": () => (/* binding */ arrayFindReverse), /* harmony export */ "arrayMax": () => (/* binding */ arrayMax), /* harmony export */ "arrayMin": () => (/* binding */ arrayMin), /* harmony export */ "sortNums": () => (/* binding */ sortNums) /* harmony export */ }); /* * Copyright (c) 2022 Anton Bagdatyev (Tonix) * * 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. */ /** * Array-related utility functions. */ /** * Unshifts an array. * * @param {Array} arr The array. * @param {*} item The item to unshift. * @return {undefined} */ function unshiftArray(arr, item) { let len = arr.length; while (len) { arr[len] = arr[len - 1]; len--; } arr[0] = item; } /** * Clones an array. * * @param {Array} arr The array to clone. * @return {Array} The cloned array. */ function cloneArray(arr) { return arr.slice(0); } /** * Gets a slice of an array from a value up until another. * * @param {Array} arr The input array. * @param {number} from The "from" lower value. * @param {number} to The "two" upper value. * @return {Array} The slice as a new array. */ function arraySliceFromValueToValue(arr, from, to) { const ret = []; let include = false; for (const value of arr) { if (!include && value == from) { ret.push(value); include = true; } else if (include && value == to) { ret.push(value); break; } else if (include) { ret.push(value); } } return ret; } /** * Tests whether all the elements of an array are coercible to a number or not. * * @param {Array} array An array. * @return {boolean} True if all the elements are coercible to a number, false otherwise. */ function areArrayItemsAllCoercibleToNumber(array) { const res = !array.some(isNaN); return res; } /** * Copies an array or converts an array-like object to a new array. * * @param {*} arg Array or array-like object. * @return {Array} An array. */ function arrayOrArrayLike(arg) { return Array.prototype.slice.call(arg); } /** * Returns the last element of the given array. * * @param {Array} array An array. * @return {*} The last element of the array or undefined if there isn't one. */ function lastOfArray(array) { return array[array.length - 1]; } /** * Returns the first element of the given array. * * @param {Array} array An array. * @return {*} The first element of the array or undefined if there isn't one. */ function firstOfArray(array) { return array[0]; } /** * Like {@link Array.prototype.find}, but in reverse order. * * @param {Array} array An array. * @param {Function} fn Function to use for the test. The function will receive the array element as parameter. * @return {*} The first element which satisfies the test in the array by seeking for the element in reverse order * (i.e. the last element of the array for which the test is satisfied). * If no element satisfies the test, "undefined" is returned. */ function arrayFindReverse(array, fn) { let l = array.length; let ret = void 0; while (l) { l--; if (fn(array[l])) { ret = array[l]; break; } } return ret; } /** * Finds the maximum value of an array of numbers. * * @param {number[]} array An array of numbers. * @return {number|undefined} The maximum value of the array, or "undefined" * if the given array is empty. */ const arrayMax = array => array.length ? array.reduce( (carry, current) => (current > carry ? current : carry), -Infinity ) : void 0; /** * Finds the minimum value of an array of numbers. * * @param {number[]} array An array of numbers. * @return {number|undefined} The minimum value of the array, or "undefined" * if the given array is empty. */ const arrayMin = array => array.length ? array.reduce( (carry, current) => (current < carry ? current : carry), +Infinity ) : void 0; /** * Sorts an array of numbers returning a new array with the sorted * numbers (does not mutate the original). * * @param {number[]} arrayOfNums An array of numbers. * @param {boolean} [desc] True for descending order, false for ascending order (default). * @return {number[]} A new array with the sorted numbers. */ const sortNums = (arrayOfNums, desc = false) => [...arrayOfNums].sort((a, b) => !desc // asc ? a - b : // desc b - a ); /***/ }), /***/ "./src/modules/bitwise.js": /*!********************************!*\ !*** ./src/modules/bitwise.js ***! \********************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "turnNthBitOff": () => (/* binding */ turnNthBitOff), /* harmony export */ "turnNthBitOn": () => (/* binding */ turnNthBitOn), /* harmony export */ "toggleNthBit": () => (/* binding */ toggleNthBit), /* harmony export */ "checkNthBitOn": () => (/* binding */ checkNthBitOn) /* harmony export */ }); /* * Copyright (c) 2022 Anton Bagdatyev (Tonix) * * 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. */ /** * Bitwise utility functions. */ /** * Turns nth bit off. * * @param {number} num A number. * @param {number} nth Nth bit to turn off (starting from 1 for the rightmost bit). * @return {number} New number with the nth bit turned off. */ const turnNthBitOff = (num, nth) => num & ~(1 << (nth - 1)); /** * Turns nth bit on. * * @param {number} num A number. * @param {number} nth Nth bit to turn on (starting from 1 for the rightmost bit). * @return {number} New number with the nth bit turned off. */ const turnNthBitOn = (num, nth) => num | (1 << (nth - 1)); /** * Toggles nth bit. * * @param {number} num A number. * @param {number} nth Nth bit to toggle (starting from 1 for the rightmost bit). * @return {number} New number with the nth bit toggled. */ const toggleNthBit = (num, nth) => num ^ (1 << (nth - 1)); /** * Checks if nth bit is on. * * @param {number} num A number. * @param {number} nth Nth bit to check. * @param {number} 0 if the nth bit is off, otherwise a number greater than 0 if the nth bit is on. */ const checkNthBitOn = (num, nth) => num & (1 << (nth - 1)); /***/ }), /***/ "./src/modules/callback.js": /*!*********************************!*\ !*** ./src/modules/callback.js ***! \*********************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "delay": () => (/* binding */ delay), /* harmony export */ "debounce": () => (/* binding */ debounce), /* harmony export */ "throttle": () => (/* binding */ throttle) /* harmony export */ }); /* harmony import */ var _core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./core */ "./src/modules/core/index.js"); /* * Copyright (c) 2022 Anton Bagdatyev (Tonix) * * 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. */ /** * Utility functions for callback handling (like debouncing, throttling). */ /** * Delays execution of a callback and cancels a previously registered callback if it wasn't executed yet. */ const delay = (function () { let timer = 0; /** * Inner function. * * @param {Function} callback The callback to execute. * @param {number} ms Milliseconds to wait before executing the callback. * @return {undefined} */ return function (callback, ms) { clearTimeout(timer); timer = setTimeout(function () { callback(); }, ms); }; })(); /** * Debounces a function. * * @param {Function} fn A function. * @param {number} wait Wait interval in milliseconds. * @return {Function} A new function, debounced. */ function debounce(fn, wait) { let timer = void 0; return function (...args) { !(0,_core__WEBPACK_IMPORTED_MODULE_0__.isUndefined)(timer) && clearTimeout(timer); timer = setTimeout(function () { fn(...args); }, wait); }; } /** * Throttles a function. * * @see https://www.sitepoint.com/throttle-scroll-events/ * * @param {Function} fn A function. * @param {number} wait Wait interval in milliseconds. * @return {Function} A new function, throttled. */ function throttle(fn, wait) { let time = Date.now(); return function (...args) { if (time + wait - Date.now() < 0) { fn(...args); time = Date.now(); } }; } /***/ }), /***/ "./src/modules/color.js": /*!******************************!*\ !*** ./src/modules/color.js ***! \******************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "getLuminance": () => (/* binding */ getLuminance), /* harmony export */ "intToRGBHexString": () => (/* binding */ intToRGBHexString), /* harmony export */ "colorFromString": () => (/* binding */ colorFromString) /* harmony export */ }); /* harmony import */ var _hash__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./hash */ "./src/modules/hash.js"); /* * Copyright (c) 2022 Anton Bagdatyev (Tonix) * * 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. */ /** * Color-related utility functions. */ /** * Gets a color's luminance. * * @param {number} color Color int value (RGB). * @return {number} An int greater than 160 if the color is considered dark and less than or * equal to 160 if the color is considered light. */ function getLuminance(color) { const c = parseInt(color, 16); const r = (c & 0xff0000) >> 16; const g = (c & 0x00ff00) >> 8; const b = c & 0x0000ff; return 0.299 * r + 0.587 * g + 0.114 * b; } /** * Converts an integer to an RGB hex (hexadecimal) string. * * @param {number} i An integer. * @return {string} An RGB hex string (uppercase). */ function intToRGBHexString(i) { const c = (i & 0x00ffffff).toString(16).toUpperCase(); return "00000".substring(0, 6 - c.length) + c; } /** * Converts a string to an RGB hex (hexadecimal) string representing a color. * * @param {string} str The string. * @return {string} The color as an RGB hex string (uppercase). */ const colorFromString = str => { const hash = (0,_hash__WEBPACK_IMPORTED_MODULE_0__.hashString)(str); return intToRGBHexString(hash); }; /***/ }), /***/ "./src/modules/combinatorics.js": /*!**************************************!*\ !*** ./src/modules/combinatorics.js ***! \**************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "yieldCombinationsWithoutRepetition": () => (/* binding */ yieldCombinationsWithoutRepetition), /* harmony export */ "uniqueProgressiveIncrementalCombinations": () => (/* binding */ uniqueProgressiveIncrementalCombinations), /* harmony export */ "yieldUniqueProgressiveIncrementalCombinations": () => (/* binding */ yieldUniqueProgressiveIncrementalCombinations), /* harmony export */ "yieldAllSubsequences": () => (/* binding */ yieldAllSubsequences), /* harmony export */ "yieldUniqueSubsequences": () => (/* binding */ yieldUniqueSubsequences), /* harmony export */ "yieldPermutations": () => (/* binding */ yieldPermutations) /* harmony export */ }); /* harmony import */ var _core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./core */ "./src/modules/core/index.js"); /* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./array */ "./src/modules/array.js"); /* * Copyright (c) 2022 Anton Bagdatyev (Tonix) * * 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. */ /** * Combinatorics utility functions. */ /** * Yields all the combinations of an array without repetitions (binomial coefficient). * * @generator * @param {Array} items An array. * @param {number} numberOfItemsPerCombination The number of elements in each combination * (this function assumes it to be less than "items.length" * and greater than 0). * @param {boolean} yieldCopy True if the yielded combination should be a copy (default) or * the internal array used during the generation of the current combination. * @yields {Array} The next combination. */ const yieldCombinationsWithoutRepetition = function* ( items, numberOfItemsPerCombination, yieldCopy = true ) { const l = items.length; const prefix = []; const recurse = function* recurse( items, l, prefix, numberOfItemsPerCombination, nextIndex = 0 ) { // Same as: // if (nextIndex === numberOfItemsPerCombination) { if (prefix.length === numberOfItemsPerCombination) { // Base case: yield yieldCopy ? (0,_array__WEBPACK_IMPORTED_MODULE_1__.arrayOrArrayLike)(prefix) : prefix; } else { // Recurrent case: for ( let i = nextIndex; i < l && // Remaining needed items numberOfItemsPerCombination - prefix.length <= // Remaining available items l - i; i++ ) { prefix.push(items[i]); yield* recurse(items, l, prefix, numberOfItemsPerCombination, i + 1); prefix.pop(); } } }; yield* recurse(items, l, prefix, numberOfItemsPerCombination); }; /** * Generate unique, progressive and incremental combinations. * * @param {Array} items An array of items. * @return {Array[]} An array of arrays, each representing a unique progressive incremental combination. * An empty array is returned if the given items array is empty. */ const uniqueProgressiveIncrementalCombinations = items => { const len = items.length; if (len === 0) { return []; } const last = (0,_array__WEBPACK_IMPORTED_MODULE_1__.arrayOrArrayLike)(items); // Shallow copy/clone of the given array. if (len === 1) { // [1] => [[1]] return [last]; } const ret = []; // [1], [2], [3], ..., [n] items.map(item => ret.push([item])); if (len > 2) { // There are at least three items. for ( let numberOfItemsPerCombination = 2; numberOfItemsPerCombination < len; numberOfItemsPerCombination++ ) { for (const combination of yieldCombinationsWithoutRepetition( items, numberOfItemsPerCombination )) { ret.push(combination); } } } ret.push(last); return ret; }; /** * Yields unique, progressive and incremental combinations. * * @generator * @param {Array} items An array of items. * @param {boolean} yieldCopy True if some of the yielded combinations should be a copy (default) * of the corresponding internal array used during the generation of the current combination * or that same array should be returned (if "yieldCopy" is "false"). * @yields {Array} An array, each representing the next unique progressive incremental combination. * An empty array is yielded if the given items array is empty. */ const yieldUniqueProgressiveIncrementalCombinations = function* ( items, yieldCopy = true ) { const len = items.length; if (len === 0) { return; } const last = yieldCopy ? (0,_array__WEBPACK_IMPORTED_MODULE_1__.arrayOrArrayLike)(items) : items; // Shallow copy/clone of the given array. if (len === 1) { // [1] => [[1]] yield last; return; } // [1], [2], [3], ..., [n] yield* (0,_core__WEBPACK_IMPORTED_MODULE_0__.mapYield)(items, item => [item]); if (len > 2) { // There are at least three items. for ( let numberOfItemsPerCombination = 2; numberOfItemsPerCombination < len; numberOfItemsPerCombination++ ) { for (const combination of yieldCombinationsWithoutRepetition( items, numberOfItemsPerCombination, yieldCopy )) { yield combination; } } } yield last; }; /** * Yields all the subsequences of the given array of items. * * @generator * @param {Array} items An array of items to use to yield subsequences. * @yields {Array} The next subsequence. */ const yieldAllSubsequences = function* (items) { const l = items.length; for (let i = 0; i <= l; i++) { for (const combination of yieldCombinationsWithoutRepetition(items, i)) { yield combination; } } }; /** * Yields only the unique subsequences of the given array of items. * * @generator * @param {Array} items An array of items to use to yield subsequences. * @yields {Array} The next unique subsequence. */ const yieldUniqueSubsequences = function* (items) { const map = new Map(); yield []; for (const subsequence of yieldAllSubsequences(items)) { if (!(0,_core__WEBPACK_IMPORTED_MODULE_0__.nestedMapHas)(map, subsequence)) { (0,_core__WEBPACK_IMPORTED_MODULE_0__.nestedMapSet)(map, subsequence, true); yield subsequence; } } }; /** * Yields all the permutations of the given array of items. * * @generator * @param {Array} items An array of items to use to yield permutations. * @param {boolean} yieldCopy True if the yielded permutation should be a copy (default) or * the internal array used during the generation of the current permutation. * @yields {Array} The next permutation. */ const yieldPermutations = function* (items, yieldCopy = true) { const currentPermutationPrefix = []; const currentPermutationIndicesMap = {}; const permute = function* () { if (currentPermutationPrefix.length === items.length) { yield yieldCopy ? (0,_array__WEBPACK_IMPORTED_MODULE_1__.arrayOrArrayLike)(currentPermutationPrefix) : currentPermutationPrefix; } else { for (let i = 0; i < items.length; i++) { if (currentPermutationIndicesMap[i]) { continue; } currentPermutationPrefix.push(items[i]); currentPermutationIndicesMap[i] = true; yield* permute(); delete currentPermutationIndicesMap[i]; currentPermutationPrefix.pop(); } } }; yield* permute(); }; /***/ }), /***/ "./src/modules/constraint.js": /*!***********************************!*\ !*** ./src/modules/constraint.js ***! \***********************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "filterInt": () => (/* binding */ filterInt), /* harmony export */ "filterFloat": () => (/* binding */ filterFloat) /* harmony export */ }); /* * Copyright (c) 2022 Anton Bagdatyev (Tonix) * * 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. */ /** * Constraints and filtering utility functions. */ /** * Filters the given string so that it only includes digit (0-9) * and an optional leading sign ("+", which is removed if present, or "-"), * i.e. represents a valid integer parsable with "Number.parseInt()". * * @param {string} str The input string. * @return {string} The valid input string or an empty string if there isn't any digit. */ function filterInt(str) { let filtered = str.replace(/[^0-9]/g, ""); filtered = filtered.replace(/^[0]+([1-9])/, "$1"); filtered = filtered.replace(/^[0]+$/, "0"); if (str && filtered.length && str[0] === "-") { filtered = `-${filtered}`; } return filtered; } /** * Filters the given string so that it only includes digits (0-9), a decimal separator ("." character) * and an optional leading sign ("+", which is removed if present, or "-"), * i.e. represents a valid float parsable with "Number.parseFloat()". * * @param {string} str The input string. * @return {string} The valid float string or an empty string if there isn't any digit or decimal separator. * If there isn't any digit in the input string and there is a decimal separator ("." character), * the returned string will be "0.", i.e. a parsable "0." string. */ function filterFloat(str) { let filtered = str.replace(/[^0-9.]/g, ""); const regex = /(\..*)\./g; const replace = "$1"; do { filtered = filtered.replace(regex, replace); } while (filtered != filtered.replace(regex, replace)); filtered === "." ? (filtered = "0.") : filtered; filtered = filtered.replace(/^[0]+([1-9])/, "$1"); filtered = filtered.replace(/^[0]+($|\.)/, "0$1"); if (str && filtered.length && str[0] === "-") { filtered = `-${filtered}`; } return filtered; } /***/ }), /***/ "./src/modules/convert.js": /*!********************************!*\ !*** ./src/modules/convert.js ***! \********************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "b2d": () => (/* binding */ b2d), /* harmony export */ "d2b": () => (/* binding */ d2b) /* harmony export */ }); /* * Copyright (c) 2022 Anton Bagdatyev (Tonix) * * 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. */ /** * Utility functions related to conversion of data from one format to another. */ /** * Binary to decimal. * * @param {string} x Binary string. * @return {number} Decimal number. */ const b2d = x => parseInt(x, 2); /** * Decimal to binary. * * @param {number} x Decimal number. * @return {string} Binary representation. */ const d2b = x => x.toString(2); /***/ }), /***/ "./src/modules/core/compare.js": /*!*************************************!*\ !*** ./src/modules/core/compare.js ***! \*************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "is": () => (/* binding */ is), /* harmony export */ "objectPropEqual": () => (/* binding */ objectPropEqual), /* harmony export */ "shallowEqual": () => (/* binding */ shallowEqual) /* harmony export */ }); /** * Copyright (c) 2013-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * * @typechecks * */ /** * @type {Function} */ const hasOwnProperty = Object.prototype.hasOwnProperty; /** * Function implementing "Object.is" behaviour. * * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is * * @param {*} x The first value to compare. * @param {*} y The second value to compare. * @return {boolean} A boolean indicating whether or not the two arguments are the same value. */ function is(x, y) { // SameValue algorithm if (x === y) { // Steps 1-5, 7-10 // Steps 6.b-6.e: +0 != -0 return x !== 0 || 1 / x === 1 / y; } else { // Step 6.a: NaN == NaN return x !== x && y !== y; } } /** * Checks whether a prop of an object equals in the other object (shallow comparison). * * @param {Object} objA The first object. * @param {Object} objB The second object. * @param {string} prop The name of the property. * @return {boolean} True if the value of "prop" in "objA" is shallowly equal to the value of "prop" in "objB". */ function objectPropEqual(objA, objB, prop) { return hasOwnProperty.call(objB, prop) && is(objA[prop], objB[prop]); } /** * Performs equality by iterating through keys on an object and returning "false" * when any key has values which are not strictly equal between the arguments. * Returns "true" when the values of all keys are strictly equal. * * @see https://stackoverflow.com/questions/22266826/how-can-i-do-a-shallow-comparison-of-the-properties-of-two-objects-with-javascri#answer-37636728 * * @param {*} objA First object. * @param {*} objB Second object. * @return {boolean} */ function shallowEqual(objA, objB) { if (is(objA, objB)) { return true; } if ( typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null ) { return false; } const keysA = Object.keys(objA); const keysB = Object.keys(objB); if (keysA.length !== keysB.length) { return false; } // Test for A's keys different from B. for (let i = 0; i < keysA.length; i++) { const prop = keysA[i]; if (!objectPropEqual(objA, objB, prop)) { return false; } } return true; } /***/ }), /***/ "./src/modules/core/index.js": /*!***********************************!*\ !*** ./src/modules/core/index.js ***! \***********************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "shallowEqual": () => (/* reexport safe */ _compare__WEBPACK_IMPORTED_MODULE_0__.shallowEqual), /* harmony export */ "objectPropEqual": () => (/* reexport safe */ _compare__WEBPACK_IMPORTED_MODULE_0__.objectPropEqual), /* harmony export */ "is": () => (/* reexport safe */ _compare__WEBPACK_IMPORTED_MODULE_0__.is), /* harmony export */ "config": () => (/* binding */ config), /* harmony export */ "isObjectEmpty": () => (/* binding */ isObjectEmpty), /* harmony export */ "isObject": () => (/* binding */ isObject), /* harmony export */ "isPlainObject": () => (/* binding */ isPlainObject), /* harmony export */ "isArray": () => (/* binding */ isArray), /* harmony export */ "isCallable": () => (/* binding */ isCallable), /* harmony export */ "isEmpty": () => (/* binding */ isEmpty), /* harmony export */ "isEmptyOr0": () => (/* binding */ isEmptyOr0), /* harmony export */ "getGlobalObject": () => (/* binding */ getGlobalObject), /* harmony export */ "uniqueId": () => (/* binding */ uniqueId), /* harmony export */ "nestedPropertyValue": () => (/* binding */ nestedPropertyValue), /* harmony export */ "getNestedPropertyValue": () => (/* binding */ getNestedPropertyValue), /* harmony export */ "hasNestedPropertyValue": () => (/* binding */ hasNestedPropertyValue), /* harmony export */ "setNestedPropertyValue": () => (/* binding */ setNestedPropertyValue), /* harmony export */ "nestedMapSet": () => (/* binding */ nestedMapSet), /* harmony export */ "nestedMapHas": () => (/* binding */ nestedMapHas), /* harmony export */ "nestedMapGet": () => (/* binding */ nestedMapGet), /* harmony export */ "nestedTreeMapSet": () => (/* binding */ nestedTreeMapSet), /* harmony export */ "nestedTreeMapHas": () => (/* binding */ nestedTreeMapHas), /* harmony export */ "nestedTreeMapGet": () => (/* binding */ nestedTreeMapGet), /* harmony export */ "mapYield": () => (/* binding */ mapYield), /* harmony export */ "deepArrayCompare": () => (/* binding */ deepArrayCompare), /* harmony export */ "deepObjectCompare": () => (/* binding */ deepObjectCompare), /* harmony export */ "nestedObjectConstructValue": () => (/* binding */ nestedObjectConstructValue), /* harmony export */ "cloneDeeplyJSON": () => (/* binding */ cloneDeeplyJSON), /* harmony export */ "isReferenceType": () => (/* binding */ isReferenceType), /* harmony export */ "isPrimitiveType": () => (/* binding */ isPrimitiveType), /* harmony export */ "hasCyclicReference": () => (/* binding */ hasCyclicReference), /* harmony export */ "typeToStr": () => (/* binding */ typeToStr), /* harmony export */ "cloneObjDeeply": () => (/* binding */ cloneObjDeeply), /* harmony export */ "deepObjectExtend": () => (/* binding */ deepObjectExtend), /* harmony export */ "deepObjectCloningExtend": () => (/* binding */ deepObjectCloningExtend), /* harmony export */ "extend": () => (/* binding */ extend), /* harmony export */ "extendDecorate": () => (/* binding */ extendDecorate), /* harmony export */ "shallowExtend": () => (/* binding */ shallowExtend), /* harmony export */ "includesTypeCoercion": () => (/* binding */ includesTypeCoercion), /* harmony export */ "isUndefined": () => (/* binding */ isUndefined), /* harmony export */ "isInt": () => (/* binding */ isInt), /* harmony export */ "ctypeDigit": () => (/* binding */ ctypeDigit), /* harmony export */ "isIntegerOrIntegerStr": () => (/* binding */ isIntegerOrIntegerStr), /* harmony export */ "findIndex": () => (/* binding */ findIndex), /* harmony export */ "firstPropValue": () => (/* binding */ firstPropValue), /* harmony export */ "isStrictlyTrue": () => (/* binding */ isStrictlyTrue), /* harmony export */ "isTruthy": () => (/* binding */ isTruthy), /* harmony export */ "allTruthy": () => (/* binding */ allTruthy), /* harmony export */ "allNotUndefined": () => (/* binding */ allNotUndefined), /* harmony export */ "isJSONString": () => (/* binding */ isJSONString), /* harmony export */ "noOpFn": () => (/* binding */ noOpFn), /* harmony export */ "partialShallowEqual": () => (/* binding */ partialShallowEqual), /* harmony export */ "shallowObjectDiff": () => (/* binding */ shallowObjectDiff), /* harmony export */ "str": () => (/* binding */ str), /* harmony export */ "mapObject": () => (/* binding */ mapObject), /* harmony export */ "mapToObject": () => (/* binding */ mapToObject), /* harmony export */ "propSelection": () => (/* binding */ propSelection), /* harmony export */ "prototypeChainProperties": () => (/* binding */ prototypeChainProperties), /* harmony export */ "prop": () => (/* binding */ prop), /* harmony export */ "defineProperty": () => (/* binding */ defineProperty), /* harmony export */ "completeObjectAssign": () => (/* binding */ completeObjectAssign) /* harmony export */ }); /* harmony import */ var _compare__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./compare */ "./src/modules/core/compare.js"); /* * Copyright (c) 2022 Anton Bagdatyev (Tonix) * * 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. */ /** * Core utility functions. */ /** * Optional configuration with useful properties. * * @type {Object} */ const config = { uniqueIdPrefix: "", elementUniqueIdPrefix: "", checkNetworkURI: null, }; /** * Tests if an object is empty. * * @param {Object} obj The object to test. * @return {boolean} "true" if the given object is empty (does not have own properties), "false" otherwise. */ function isObjectEmpty(obj) { for (const prop in obj) { if (Object.prototype.hasOwnProperty.call(obj, prop)) { return false; } } return true; } /** * @type {string} */ const objPrototypeToString = Object.prototype.toString.call({}); /** * Tests if a variable is an object. * * @param {*} obj The variable to test. * @return {boolean} "true" if "obj" is indeed an object, "false" otherwise. */ const isObject = function (obj) { return objPrototypeToString === Object.prototype.toString.call(obj); }; /** * Tests if a variable is a plain object (i.e. "{}", an object literal). * * @param {*} obj The variable to test. * @return {boolean} "true" if "obj" is a plain object, "false" otherwise. */ const isPlainObject = obj => { return ( obj !== null && typeof obj === "object" && obj.constructor === Object && isObject(obj) ); }; /** * Tests to see whether something is an array or not. * * @param {*} something A variable to check whether it is an array or not. * @return {boolean} True if the parameter passed in is an array, false otherwise. */ function isArray(something) { return ( Object.prototype.toString.call(something) === Object.prototype.toString.call([]) ); } /** * Tests if the given value is callable. * * @param {*} v The value. * @return {boolean} True if callable, false otherwise. */ function isCallable(v) { return typeof v === "function"; } /** * Tests if a variable is empty returning true for empty strings and empty arrays. * * @param {*} data The variable to test. * @return {boolean} True if the variable is empty, false otherwise. */ function isEmpty(data) { return !data || data.length === 0; } /** * Tests if a variable is empty or 0 ("0" string) returning true for empty strings, * empty arrays, the "0" string and empty values. * * @param {*} data The variable to test. * @return {boolean} True if the variable is empty or "0", false otherwise. */ function isEmptyOr0(data) { return !data || data === "0" || data.length === 0; } /** * Returns a reference to the global object. * * @return {Window|global} The global object (this function is cross-platform aware). */ function getGlobalObject() { return typeof __webpack_require__.g !== "undefined" ? __webpack_require__.g : window; } /** * @type {string} */ const JSUtlUniqueIdCounterProp = "JSUtlUniqueIdCounterLEzKKl87QCDxwVH"; /** * Generates a unique ID which can be used as an "id" attribute. * * @param {string|undefined} [uniqueIdPrefix] Local unique ID prefix which overrides the prefix * set on the "config" configuration object. * @return {string} The unique ID. */ function uniqueId(uniqueIdPrefix = void 0) { const globalObject = getGlobalObject(); globalObject[JSUtlUniqueIdCounterProp] = globalObject[JSUtlUniqueIdCounterProp] || 0; globalObject[JSUtlUniqueIdCounterProp]++; const uniqueIdCounter = globalObject[JSUtlUniqueIdCounterProp]; const uniqueId = (uniqueIdPrefix || config.uniqueIdPrefix) + uniqueIdCounter; return uniqueId; } /** * Gets a nested value of an object given an array of nested property names (keys). * * @param {Object} data JS POJO object. * @param {Array} props Array of object nested keys. * @return {*} The leaf value. */ function nestedPropertyValue(data, props) { let root = data; for (let i = 0; i < props.length; i++) { const prop = props[i]; root = root[prop]; } return root; } /** * Alias for "nestedPropertyValue". * * @alias */ const getNestedPropertyValue = nestedPropertyValue; /** * Checks if a nested value of an object given an array of nested property names (keys) exists. * * @param {Object} data JS POJO object. * @param {Array} props Array of object nested keys. * @return {boolean} True if the nested key exists, false otherwise. */ function hasNestedPropertyValue(data, props) { if (!props.length) { return false; } let root = data; for (let i = 0; i < props.length; i++) { const prop = props[i]; if (!root[prop]) { return false; } root = root[prop]; } return true; } /** * Sets a nested value of an object given an array of nested property names (keys). * * @param {Object} data JS POJO object. * @param {Array} props Array of object nested keys. * @param {*} value Leaf value. * @return {undefined} */ function setNestedPropertyValue(data, props, value) { if (!props.length) { return; } let root = data; let prev = null; for (let i = 0; i < props.length; i++) { const prop = props[i]; if (typeof root[prop] !== "object") { root[prop] = {}; } prev = root; root = root[prop]; } if (prev) { prev[props[props.length - 1]] = value; } } /** * Sets a nested value on a nested map. * * @param {Map|WeakMap} map A map or weak map. * @param {Array} keys Array of keys to traverse. Each key will lead to a nested map. * @param {*} value The value to set at the inner key. * @return {undefined} */ const nestedMapSet = (map, keys, value) => { let i = 0; let current = map; while (i < keys.length - 1) { const key = keys[i]; const nested = current.get(key); if (nested instanceof Map || nested instanceof WeakMap) { current = nested; } else { const newMap = new Map(); current.set(key, newMap); current = newMap; } i++; } current.set(keys[i], value); }; /** * Tests if a map has the given nested keys. * * @param {Map|WeakMap} map A map or weak map. * @param {Array} keys Array of keys to check. Each key represents a nested map. * @return {boolean} "true" if all the nested keys exist, false otherwise. */ const nestedMapHas = (map, keys) => { let current = map; let i = 0; const l = keys.length; while ( (current instanceof Map || current instanceof WeakMap) && current.has(keys[i]) && i < l ) { current = current.get(keys[i]); i++; } return i == l; }; /** * Gets a value from a nested map. * * @param {Map|WeakMap} map A map or weak map. * @param {Array} keys Array of keys. Each key represents a nested map. * @return {*} The value of the map or "undefined" if there is no value for the given nested keys. */ const nestedMapGet = (map, keys) => { let current = map; let i = 0; const l = keys.length; while ( (current instanceof Map || current instanceof WeakMap) && current.has(keys[i]) && i < l ) { current = current.get(keys[i]); i++; } return i == l ? current : void 0; }; /** * @type {Symbol} */ const treeMapSubtree = Symbol("treeMapSubtree"); /** * Sets a nested value on a nested tree map. * * @param {Map|WeakMap} rootMap A map or weak map to use as the root. * @param {Array} keys Array of keys to traverse. Each key will lead to a nested node of the tree map. * @param {*} value The value to set at the inner nested key. * @return {undefined} */ const nestedTreeMapSet = (rootMap, keys, value) => { let i = 0; let current = rootMap; const MapConstructor = rootMap instanceof WeakMap ? WeakMap : Map; while (i < keys.length - 1) { const key = keys[i]; const nested = current.get(key); if (nested) { current = nested[treeMapSubtree] || (nested[treeMapSubtree] = new MapConstructor()); } else { const newMap = new MapConstructor(); const node = { [treeMapSubtree]: newMap, value: void 0, }; current.set(key, node); current = newMap; } i++; } const key = keys[i]; !current.has(key) ? current.set(key, { value, }) : (current.get(key).value = value); }; /** * Tests if a tree map has the given nested keys. * * @param {Map|WeakMap} rootMap The root of the map or weak map. * @param {Array} keys Array of keys to check. Each key represents a nested node of the tree map. * @return {boolean} "true" if all the nested keys exist, false otherwise. */ const nestedTreeMapHas = (rootMap, keys) => { let current = rootMap; let i = 0; const l = keys.length; while ( (current instanceof Map || current instanceof WeakMap) && current.has(keys[i]) && i < l ) { current = current.get(keys[i])[treeMapSubtree]; i++; } return i == l; }; /** * Gets a value from a nested tree map. * * @param {Map|WeakMap} rootMap The root of the map or weak map. * @param {Array} keys Array of keys. Each key represents a nested node of the tree map. * @return {*} The value of the tree map or "undefined" if there is no value for the given nested keys. */ const nestedTreeMapGet = (rootMap, keys) => { let current = rootMap; let i = 0; const lastIndex = keys.length - 1; while ( (current instanceof Map || current instanceof WeakMap) && current.has(keys[i]) && i < lastIndex ) { current = current.get(keys[i])[treeMapSubtree]; i++; } if (i === lastIndex && current) { const lastKey = keys[i]; if (current.has(lastKey)) { const nested = current.get(lastKey); return nested.value; } } return void 0; }; /** * Yields values of an array mapping the yielded value. * * @generator * @param {Array} items An array of items. * @param {*} fn The function to call. * The function will receive, in order the nth item, * the index of the item in the array of items and t