@igo2/utils
Version:
1,085 lines (1,064 loc) • 37.7 kB
JavaScript
import { Observable, Subject } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';
/* eslint-disable */
const ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
class Base64 {
static PADCHAR = '=';
static ALPHA = ALPHA;
static getByte(s, i) {
const x = s.charCodeAt(i);
return x;
}
static getByte64(s, i) {
const idx = this.ALPHA.indexOf(s.charAt(i));
return idx;
}
static decode(s) {
let pads = 0, i, b10, imax = s.length, x = [];
s = String(s);
if (imax === 0) {
return s;
}
if (s.charAt(imax - 1) === this.PADCHAR) {
pads = 1;
if (s.charAt(imax - 2) === this.PADCHAR) {
pads = 2;
}
imax -= 4;
}
for (i = 0; i < imax; i += 4) {
b10 =
(this.getByte64(s, i) << 18) |
(this.getByte64(s, i + 1) << 12) |
(this.getByte64(s, i + 2) << 6) |
this.getByte64(s, i + 3);
x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 255, b10 & 255));
}
switch (pads) {
case 1:
b10 =
(this.getByte64(s, i) << 18) |
(this.getByte64(s, i + 1) << 12) |
(this.getByte64(s, i + 2) << 6);
x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 255));
break;
case 2:
b10 = (this.getByte64(s, i) << 18) | (this.getByte64(s, i + 1) << 12);
x.push(String.fromCharCode(b10 >> 16));
break;
}
return x.join('');
}
static encode(s) {
s = String(s);
let i, b10, x = [], imax = s.length - (s.length % 3);
if (s.length === 0) {
return s;
}
for (i = 0; i < imax; i += 3) {
b10 =
(this.getByte(s, i) << 16) |
(this.getByte(s, i + 1) << 8) |
this.getByte(s, i + 2);
x.push(this.ALPHA.charAt(b10 >> 18));
x.push(this.ALPHA.charAt((b10 >> 12) & 63));
x.push(this.ALPHA.charAt((b10 >> 6) & 63));
x.push(this.ALPHA.charAt(b10 & 63));
}
switch (s.length - imax) {
case 1:
b10 = this.getByte(s, i) << 16;
x.push(this.ALPHA.charAt(b10 >> 18) +
this.ALPHA.charAt((b10 >> 12) & 63) +
this.PADCHAR +
this.PADCHAR);
break;
case 2:
b10 = (this.getByte(s, i) << 16) | (this.getByte(s, i + 1) << 8);
x.push(this.ALPHA.charAt(b10 >> 18) +
this.ALPHA.charAt((b10 >> 12) & 63) +
this.ALPHA.charAt((b10 >> 6) & 63) +
this.PADCHAR);
break;
}
return x.join('');
}
}
function customCacheHasher(parameters) {
return parameters.map((param) => {
if (typeof param === 'object' &&
param != null &&
typeof param.toString === 'function') {
return param.toString();
}
return param !== undefined ? JSON.parse(JSON.stringify(param)) : param;
});
}
function getNumber(v, endposition, length) {
const mask = (1 << length) - 1;
return (v >> endposition) & mask;
}
class Compression {
base64Index = new Map();
indexBase64 = new Map();
constructor() {
this.generateBase64Index();
}
generateBase64Index() {
// https://fr.wikipedia.org/wiki/Base64
// A-Z => [0, 25]
for (let i = 0; i < 26; i++) {
this.base64Index.set(String.fromCharCode('A'.charCodeAt(0) + i), i);
this.indexBase64.set(i, String.fromCharCode('A'.charCodeAt(0) + i));
}
// a-z => [26, 51]
for (let i = 0; i < 26; i++) {
this.base64Index.set(String.fromCharCode('a'.charCodeAt(0) + i), i + 26);
this.indexBase64.set(i + 26, String.fromCharCode('a'.charCodeAt(0) + i));
}
// 0-9 => [52, 61]
for (let i = 0; i < 10; i++) {
this.base64Index.set(String.fromCharCode('0'.charCodeAt(0) + i), i + 52);
this.indexBase64.set(i + 52, String.fromCharCode('0'.charCodeAt(0) + i));
}
// + / => [62, 63]
this.base64Index.set('+', 62);
this.base64Index.set('/', 63);
this.indexBase64.set(62, '+');
this.indexBase64.set(63, '/');
}
compressBlob(blob) {
if (!blob) {
return;
}
const observable = new Observable((observer) => {
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onload = () => {
const base64 = reader.result.valueOf();
const text64 = base64.substr(base64.indexOf(',') + 1);
const compressed = this.compressStringBase64(text64);
const compressedData = {
length: text64.length,
type: blob.type,
object: compressed
};
observer.next(compressedData);
};
});
return observable;
}
decompressBlob(compressedData) {
const object = compressedData.object;
const length = compressedData.length;
const decompressed = this.decompressStringBase64(object, length);
const byteCharacters = atob(decompressed);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], { type: compressedData.type });
return blob;
}
compressStringBase64(s) {
let out = '';
let bits = 16;
let chr = 0;
let rem = 0;
for (const c of s) {
const value = this.base64Index.get(c);
if (bits > 6) {
bits -= 6;
chr += value << bits;
}
else {
rem = 6 - bits;
chr += value >> rem;
out += String.fromCharCode(chr);
chr = value << (16 - rem);
bits = 16 - rem;
}
}
if (s.length % 8 !== 0) {
out += String.fromCharCode(chr);
}
return String.fromCharCode(9731) + out;
}
decompressStringBase64(c, length) {
if (!c) {
return;
}
if (c.charCodeAt(0) !== 9731) {
return c;
}
let chr = 0;
let rem = 0;
let bits = 16;
let out = '';
let j = 1;
let value = c.charCodeAt(j);
for (let i = 0; i < length; i++) {
if (bits > 6) {
bits -= 6;
chr = getNumber(value, bits, 6);
out += this.indexBase64.get(chr);
}
else {
rem = 6 - bits;
chr = getNumber(value, 0, bits) << rem;
value = c.charCodeAt(++j);
chr += getNumber(value, 16 - rem, rem);
out += this.indexBase64.get(chr);
bits = 16 - rem;
}
}
return out;
}
}
var ChangeType;
(function (ChangeType) {
ChangeType["ADDED"] = "added";
ChangeType["DELETED"] = "deleted";
ChangeType["MODIFIED"] = "modified";
})(ChangeType || (ChangeType = {}));
class StringUtils {
static diff(s1, s2, p = 4) {
if (!s1 && !s2) {
return '';
}
if (!s1) {
return '<span class="inserted">' + s2 + '</span>';
}
if (!s2) {
return '<span class="deleted">' + s1 + '</span>';
}
s1 = s1.toString();
s2 = s2.toString();
const changeData = StringUtils.getChanges(s1, s2, '', p);
const nextS = s2.slice(changeData.mtc.length + changeData.ins.length + changeData.sbs.length); // remaining part of "s"
const nextThis = s1.slice(changeData.mtc.length + changeData.del.length + changeData.sbs.length); // remaining part of "this"
let result = ''; // the glorious result
if (changeData.del.length > 0) {
changeData.del = '<span class="deleted">' + changeData.del + '</span>';
}
if (changeData.ins.length > 0) {
changeData.ins = '<span class="inserted">' + changeData.ins + '</span>';
}
result = changeData.mtc + changeData.del + changeData.ins + changeData.sbs;
result +=
nextThis !== '' || nextS !== ''
? StringUtils.diff(nextThis, nextS, p)
: '';
return result;
}
static getMatchingSubstring(s, l, m) {
// returns the first matching substring in-between the two strings
let i = 0;
let match = false;
const slen = s.length;
const o = { fis: slen, mtc: m, sbs: '' }; // temporary object used to construct the cd (change data) object
while (i < slen) {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
l[i] === s[i]
? match
? (o.sbs += s[i]) // o.sbs holds the matching substring itsef
: ((match = true), (o.fis = i), (o.sbs = s[i]))
: match
? (i = slen) // stop after the first found substring
: // eslint-disable-next-line no-self-assign
(i = i);
++i;
}
return o;
}
static getChanges(s1, s2, m, p) {
const isThisLonger = s1.length >= s1.length ? true : false;
let [longer, shorter] = isThisLonger ? [s1, s2] : [s2, s1]; // assignment of longer and shorter by es6 destructuring
let bi = 0; // base index designating the index of first mismacthing character in both strings
while (shorter[bi] === longer[bi] && bi < shorter.length) {
++bi;
} // make bi the index of first mismatching character
longer = longer.split('').slice(bi); // as the longer string will be rotated it is converted into array
shorter = shorter.slice(bi); // shorter and longer now starts from the first mismatching character
const len = longer.length; // length of the longer string
let cd = {
fis: shorter.length, // the index of matching string in the shorter string
fil: len, // the index of matching string in the longer string
sbs: '', // the matching substring itself
mtc: m + s2.slice(0, bi)
}; // if exists mtc holds the matching string at the front
let sub = { sbs: '' }; // returned substring per 1 character rotation of the longer string
if (shorter !== '') {
for (let rc = 0; rc < len && sub.sbs.length < p; rc++) {
// rc -> rotate count, p -> precision factor
sub = StringUtils.getMatchingSubstring(shorter, StringUtils.rotateArray(longer, rc), cd.mtc); // rotate longer string 1 char and get substring
sub.fil =
rc < len - sub.fis
? sub.fis + rc // mismatch is longer than the mismatch in short
: sub.fis - len + rc; // mismatch is shorter than the mismatch in short
if (sub.sbs.length > cd.sbs.length) {
cd = sub; // only keep the one with the longest substring.
}
}
}
// insert the mismatching delete subsrt and insert substr to the cd object and attach the previous substring
[cd.del, cd.ins] = isThisLonger
? [longer.slice(0, cd.fil).join(''), shorter.slice(0, cd.fis)]
: [shorter.slice(0, cd.fis), longer.slice(0, cd.fil).join('')];
return cd.del.indexOf(' ') === -1 ||
cd.ins.indexOf(' ') === -1 ||
cd.del === '' ||
cd.ins === '' ||
cd.sbs === ''
? cd
: StringUtils.getChanges(cd.del, cd.ins, cd.mtc, p);
}
static rotateArray(array, n) {
const len = array.length;
const res = new Array(array.length);
if (n % len === 0) {
return array.slice();
}
else {
for (let i = 0; i < len; i++) {
res[i] = array[(i + (len + (n % len))) % len];
}
}
return res;
}
static isValidNumber(value) {
return !isNaN(Number(value));
}
static isOctalNumber(value) {
return (StringUtils.isValidNumber(value) &&
value.startsWith('0') &&
value.length > 1);
}
}
class ChangeUtils {
static findChanges(obj1, obj2, ignoreKeys = []) {
const items = {
added: [],
deleted: [],
modified: []
};
if (!obj1 || !obj2) {
return items;
}
const obj1Clone = [...obj1];
const obj2Clone = [...obj2];
for (const fromItem of obj1Clone) {
const index = obj2Clone.findIndex((s) => s.id === fromItem.id);
if (index === -1) {
items.deleted.push({
change: { type: ChangeType.DELETED },
value: fromItem
});
continue;
}
const toItem = obj2Clone.splice(index, 1)[0];
const fromItemClone = JSON.parse(JSON.stringify(fromItem));
const toItemClone = JSON.parse(JSON.stringify(toItem));
const keysChanged = ChangeUtils.compareObject(fromItemClone, toItemClone, undefined, ignoreKeys);
if (keysChanged.length) {
items.modified.push({
change: {
type: ChangeType.MODIFIED,
keysChanged
},
value: fromItemClone,
oldValue: fromItem,
newValue: toItem
});
}
}
items.added = obj2Clone.map((itemAdded) => {
return {
change: { type: ChangeType.ADDED },
value: itemAdded
};
});
return items;
}
static compareObject(fromItem, toItem, baseKey, ignoreKeys = []) {
const fromItemClone = JSON.parse(JSON.stringify(fromItem));
const toItemClone = JSON.parse(JSON.stringify(toItem));
const keys = new Set([
...Object.keys(fromItem),
...Object.keys(toItem)
]);
let keysChanged = [];
keys.forEach((key) => {
const keyString = baseKey ? `${baseKey}.${key}` : key;
if (ignoreKeys.indexOf(keyString) !== -1) {
return;
}
if (Array.isArray(fromItem[key])) {
fromItem[key] = fromItem[key].join(',<br>');
}
if (Array.isArray(toItem[key])) {
toItem[key] = toItem[key].join(',<br>');
}
if (typeof fromItem[key] === 'object' &&
typeof toItem[key] === 'object' &&
fromItem[key] !== null &&
toItem[key] !== null) {
keysChanged = keysChanged.concat(this.compareObject(fromItem[key], toItem[key], keyString));
}
else {
if (fromItem[key] !== toItem[key]) {
keysChanged.push({
key: keyString,
oldValue: fromItemClone[key],
newValue: toItemClone[key]
});
fromItem[key] = StringUtils.diff(fromItem[key], toItem[key]);
}
}
});
return keysChanged;
}
}
const ISO8601_REGEX = new RegExp('(d{4}-[01]d-[0-3]dT[0-2]d:[0-5]d:[0-5]d.d+)|(d{4}-[01]d-[0-3]dT[0-2]d:[0-5]d:[0-5]d)|(d{4}-[01]d-[0-3]dT[0-2]d:[0-5]d)');
const ISO8601_UTCTIME_REGEX = new RegExp('(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\\.[0-9]+)?(Z)?');
function isIsoDate(value) {
if (!ISO8601_REGEX.test(value) && !ISO8601_UTCTIME_REGEX.test(value)) {
return false;
}
const d = new Date(value);
return d instanceof Date && !isNaN(d.getTime());
}
class DomUtils {
static create(doc, tagName, options) {
const el = doc.createElement(tagName);
for (const key in options) {
el[key] = options[key];
}
return el;
}
static remove(node) {
if (!node.parentNode) {
return;
}
node.parentNode.removeChild(node);
}
}
async function createExcelWorkBook(workSheet, workSheetName) {
const { utils } = await import('xlsx');
return utils.book_new(workSheet, workSheetName);
}
async function addExcelSheetToWorkBook(title, rows, workBook, opts) {
const { utils } = await import('xlsx');
const worksheet = utils.json_to_sheet(rows, opts?.json2SheetOpts);
/* calculate column width */
if (rows?.length) {
worksheet['!cols'] = getColumnsInfo(rows);
}
const SHEET_NAME_MAX_LENGTH = 31;
let sheetName = title.length >= SHEET_NAME_MAX_LENGTH
? title.substring(0, SHEET_NAME_MAX_LENGTH)
: title;
if (workBook.SheetNames.includes(sheetName)) {
sheetName = `${sheetName.substring(0, SHEET_NAME_MAX_LENGTH - 3)}_${workBook.SheetNames.length}`;
}
utils.book_append_sheet(workBook, worksheet, sheetName, opts?.bookAppendSheetOpts?.roll);
}
async function writeExcelFile(workBook, filename, opts) {
const cleanedFileName = filename.toLowerCase().endsWith('.xlsx')
? filename.slice(0, -5)
: filename;
const { writeFile } = await import('xlsx');
writeFile(workBook, `${cleanedFileName}.xlsx`, opts);
}
function getColumnsInfo(rows) {
const columns = Object.keys(rows[0]);
return columns.map((column) => ({
wch: getColumnMaxWidth(column, rows)
}));
}
function getColumnMaxWidth(column, rows) {
return rows.reduce((width, row) => Math.max(width, row[column]?.toString().length ?? 0), column.length);
}
/**
* Trigger download of a file
*
* @param content File content
* @param mimeType File mime type
* @param fileName File name
*/
function downloadContent(content, mimeType, fileName) {
const uri = `data:${mimeType},${encodeURIComponent(content)}`;
downloadFromUri(uri, fileName);
}
function downloadBlob(blob, fileName) {
const url = window.URL.createObjectURL(blob);
downloadFromUri(url, fileName);
}
/**
* Trigger download of a file
*
* @param content File content
* @param mimeType File mime type
* @param fileName File name
*/
function downloadFromUri(uri, fileName) {
const element = document.createElement('a');
element.setAttribute('href', uri);
element.setAttribute('download', fileName);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
/**
* Validate if string is valid json object
* @param jsonString
* @return boolean
*/
function isValidJSON(jsonString) {
try {
return JSON.parse(jsonString) && !!jsonString;
}
catch {
return false;
}
}
/* eslint-disable no-prototype-builtins */
class ObjectUtils {
static resolve(obj, key) {
const keysArray = key.replace(/\[/g, '.').replace(/\]/g, '').split('.');
let current = obj;
while (keysArray.length) {
if (typeof current !== 'object') {
return undefined;
}
current = current[keysArray.shift()];
}
return current;
}
static isObject(item) {
return (item &&
typeof item === 'object' &&
!Array.isArray(item) &&
item !== null &&
!(item instanceof Date));
}
static mergeDeep(target, source, ignoreUndefined = false) {
const output = Object.assign({}, target);
if (ObjectUtils.isObject(target) && ObjectUtils.isObject(source)) {
Object.keys(source)
.filter((key) => !ignoreUndefined || source[key] !== undefined)
.forEach((key) => {
if (ObjectUtils.isObject(source[key])) {
if (!(key in target)) {
Object.assign(output, { [key]: source[key] });
}
else {
output[key] = ObjectUtils.mergeDeep(target[key], source[key], ignoreUndefined);
}
}
else {
Object.assign(output, { [key]: source[key] });
}
});
}
return output;
}
static copyDeep(src) {
const target = Array.isArray(src) ? [] : {};
for (const prop in src) {
if (src.hasOwnProperty(prop)) {
const value = src[prop];
if (value && typeof value === 'object') {
target[prop] = this.copyDeep(value);
}
else {
target[prop] = value;
}
}
}
return target;
}
static removeDuplicateCaseInsensitive(obj) {
const summaryCapitalizeObject = {};
const capitalizeObject = {};
const upperCaseCount = [];
for (const property in obj) {
if (obj.hasOwnProperty(property)) {
const upperCaseProperty = property.toUpperCase();
if (!summaryCapitalizeObject.hasOwnProperty(upperCaseProperty)) {
summaryCapitalizeObject[upperCaseProperty] = [
{ [property]: obj[property] }
];
}
else {
summaryCapitalizeObject[upperCaseProperty].push({
[property]: obj[property]
});
}
// counting the number of uppercase lettersMna
upperCaseCount.push({
key: property,
count: property.replace(/[^A-Z]/g, '').length
});
}
}
for (const capitalizedProperty in summaryCapitalizeObject) {
if (summaryCapitalizeObject.hasOwnProperty(capitalizedProperty)) {
if (summaryCapitalizeObject.hasOwnProperty(capitalizedProperty)) {
const capitalizedPropertyObject = summaryCapitalizeObject[capitalizedProperty];
if (capitalizedPropertyObject.length === 1) {
// for single params (no duplicates)
const singlePossibility = capitalizedPropertyObject[0];
capitalizeObject[capitalizedProperty] =
singlePossibility[Object.keys(singlePossibility)[0]];
}
else if (capitalizedPropertyObject.length > 1) {
// defining the closest to lowercase property
const paramClosestToLowercase = upperCaseCount
.filter((f) => f.key.toLowerCase() === capitalizedProperty.toLowerCase())
.reduce((prev, current) => {
return prev.y < current.y ? prev : current;
});
capitalizeObject[paramClosestToLowercase.key.toUpperCase()] =
obj[paramClosestToLowercase.key];
}
}
}
}
for (const property in obj) {
if (obj.hasOwnProperty(property)) {
delete obj[property];
}
}
for (const property in capitalizeObject) {
if (capitalizeObject.hasOwnProperty(property)) {
obj[property] = capitalizeObject[property];
}
}
}
static removeUndefined(obj) {
const output = {};
if (ObjectUtils.isObject(obj)) {
Object.keys(obj)
.filter((key) => obj[key] !== undefined)
.forEach((key) => {
if (ObjectUtils.isObject(obj[key]) || Array.isArray(obj[key])) {
output[key] = ObjectUtils.removeUndefined(obj[key]);
}
else {
output[key] = obj[key];
}
});
return output;
}
if (Array.isArray(obj)) {
return obj.map((o) => ObjectUtils.removeUndefined(o));
}
return obj;
}
static removeNull(obj) {
const output = {};
if (ObjectUtils.isObject(obj)) {
Object.keys(obj)
.filter((key) => obj[key] !== null)
.forEach((key) => {
if (ObjectUtils.isObject(obj[key]) || Array.isArray(obj[key])) {
output[key] = ObjectUtils.removeNull(obj[key]);
}
else {
output[key] = obj[key];
}
});
return output;
}
if (Array.isArray(obj)) {
return obj.map((o) => ObjectUtils.removeNull(o));
}
return obj;
}
static naturalCompare(a, b, direction = 'asc', nullsFirst) {
if (direction === 'desc') {
b = [a, (a = b)][0];
}
// nullsFirst = undefined : end if direction = 'asc', first if direction = 'desc'
// nullsFirst = true : always first
// nullsFirst = false : always end
if (a === null ||
a === '' ||
a === undefined ||
b === null ||
b === '' ||
b === undefined) {
const nullScore = a === b
? 0
: a === undefined
? 3
: b === undefined
? -3
: a === null
? 2
: b === null
? -2
: a === ''
? 1
: -1;
if (direction === 'desc') {
return nullsFirst !== false ? nullScore : nullScore * -1;
}
return nullsFirst === true ? nullScore * -1 : nullScore;
}
const ax = [];
const bx = [];
a = '' + a;
b = '' + b;
a.replace(/(\d+)|(\D+)/g, (_, $1, $2) => {
ax.push([$1 || Infinity, $2 || '']);
});
b.replace(/(\d+)|(\D+)/g, (_, $1, $2) => {
bx.push([$1 || Infinity, $2 || '']);
});
while (ax.length && bx.length) {
const an = ax.shift();
const bn = bx.shift();
const nn = an[0] - bn[0] || an[1].localeCompare(bn[1]);
if (nn) {
return nn;
}
}
return ax.length - bx.length;
}
/**
* Return true if two object are equivalent.
* Objects are considered equivalent if they have the same properties and
* if all of their properties (first-level only) share the same value.
* @param obj1 First object
* @param obj2 Second object
* @returns Whether two objects arer equivalent
*/
static objectsAreEquivalent(obj1, obj2) {
if (obj1 === obj2) {
return true;
}
const obj1Props = Object.getOwnPropertyNames(obj1);
const obj2Props = Object.getOwnPropertyNames(obj2);
if (obj1Props.length !== obj2Props.length) {
return false;
}
for (const prop of obj1Props) {
if (obj1[prop] !== obj2[prop]) {
return false;
}
}
return true;
}
/**
* Return a new object with an array of keys removed
* @param obj Source object
* @param keys Keys to remove
* @returns A new object
*/
static removeKeys(obj, keys) {
const newObj = Object.keys(obj)
.filter((key) => keys.indexOf(key) < 0)
.reduce((_obj, key) => {
_obj[key] = obj[key];
return _obj;
}, {});
return newObj;
}
static isEmpty(obj) {
return Object.keys(obj).length === 0;
}
}
class NumberUtils {
static roundToNDecimal(num, decimal = 3) {
const roundFactor = Math.pow(10, decimal);
return Math.round(num * roundFactor) / roundFactor;
}
}
/** Utility function to create a K:V from a list of strings */
function strEnum(o) {
return o.reduce((res, key) => {
res[key] = key;
return res;
}, Object.create(null));
}
const PATH_PREBUILT_THEMES = 'assets/igo2/core/theming/prebuilt-themes';
const LINK_ID = 'theme-styles';
function loadTheme(doc, themeName, path = PATH_PREBUILT_THEMES) {
const src = `${path}/${themeName}.css`;
const head = doc.getElementsByTagName('head')[0];
const themeLink = doc.getElementById(LINK_ID);
if (themeLink) {
themeLink.href = src;
}
else {
createHtmlLink(doc, src, head);
}
}
function createHtmlLink(doc, src, parent) {
const element = DomUtils.create(doc, 'link', {
id: LINK_ID,
rel: 'stylesheet',
href: src
});
parent.appendChild(element);
}
const TREE_SEPERATOR = '.';
class Tree {
config;
_data;
getChildren;
getId;
getLevel;
constructor(initialData, config) {
this.config = config;
this.getChildren = config.getChildren;
this.getId = config.getId;
this.getLevel = config.getLevel;
this._data = this.sortDeep(initialData);
}
get data() {
return this._data;
}
get flattened() {
return this.flatten([...this.data]);
}
add(...nodes) {
this.sortDeep(nodes);
this._data.unshift(...nodes);
return nodes;
}
addBefore(beforeId, ...nodes) {
this.sortDeep(nodes);
this._addBefore(beforeId, this._data, ...nodes);
}
remove(...nodes) {
return this._remove(...nodes);
}
clear() {
this._data = [];
}
exist(node) {
const id = this.getId(node);
return this.flattened.some((layer) => this.getId(layer) === id);
}
/**
* Move a node to a different position
* @param node
* @param beforeTo The position of index into the tree. If -1 move at the end
*/
moveTo(beforeTo, ...nodes) {
const clonedBeforeTo = [...beforeTo];
const lastIndex = clonedBeforeTo.pop();
const recipient = this.getAncestorAtPosition(clonedBeforeTo);
const beforeId = lastIndex === -1 || lastIndex >= recipient.length
? null
: this.getId(recipient[lastIndex]);
const nodesToMove = nodes.filter((node) => beforeId !== this.getId(node));
return this.move(beforeId, recipient, ...nodesToMove);
}
getPosition(node) {
const id = this.getId(node);
return this._getPosition(id);
}
_addBefore(beforeId, recipient = this._data, ...nodes) {
if (!beforeId) {
recipient.push(...nodes);
return nodes;
}
const beforeIndex = this.getIndex(beforeId, recipient);
recipient.splice(beforeIndex, 0, ...nodes);
return nodes;
}
_remove(...nodes) {
return nodes.reduce((acc, node) => {
const ancestor = this.getNodeAncestor(node);
if (!ancestor) {
return;
}
const index = this.getIndex(this.getId(node), ancestor);
if (index === -1) {
return;
}
ancestor.splice(index, 1);
return acc.concat(node);
}, []);
}
/**
* Move an node before an id
* @param node Node to be move
* @param recipient
* @param beforeId
*/
move(beforeId, recipient, ...nodes) {
this._remove(...nodes);
return this._addBefore(beforeId, recipient, ...nodes);
}
_getPosition(id, ancestorsIndex = [], values = this._data) {
let indexList;
values.some((value, index) => {
if (this.getId(value) === id) {
indexList = ancestorsIndex.concat(index);
return true;
}
const children = this.getChildren(value);
if (children) {
const groupIndexList = this._getPosition(id, ancestorsIndex.concat(index), children);
if (groupIndexList) {
indexList = groupIndexList;
return true;
}
}
return false;
});
return indexList;
}
getAncestorAtPosition(position) {
if (!position.length) {
return this._data;
}
const node = this.getNodeByPosition(position);
return this.getChildren(node);
}
/** Recursive */
sortDeep(data = this._data) {
data.forEach((node) => {
const children = this.getChildren(node);
if (children) {
this.sortDeep(children);
}
});
return this.sort(data);
}
sort(children) {
const sorted = children.sort((a, b) => this.getLevel(a) - this.getLevel(b));
return this.config.reverse ? sorted.reverse() : sorted;
}
/** Recursive */
_getNodeById(id, data = this._data) {
let node;
data.some((item) => {
if (this.getId(item) === id) {
node = item;
return true;
}
const children = this.getChildren(item);
if (children) {
node = this._getNodeById(id, children);
if (node) {
return true;
}
}
return false;
});
return node;
}
getNodeByPosition(indexes) {
if (indexes.length > 1) {
return indexes.reduce((previousValue, index) => {
const ancestor = previousValue
? this.getChildren(previousValue)
: this._data;
return this._getByIndex(index, ancestor);
}, null);
}
else {
return this._getByIndex(indexes[0]);
}
}
_getByIndex(index, ancestor = this._data) {
return ancestor[index];
}
getNodeAncestor(node) {
const id = this.getId(node);
return this.getAncestorById(id);
}
/** Recursive */
getAncestorById(id, data = this._data) {
let ancestor;
data.some((item) => {
if (this.getId(item) === id) {
ancestor = data;
return true;
}
const children = this.getChildren(item);
if (children) {
ancestor = this.getAncestorById(id, children);
if (ancestor) {
return true;
}
}
return false;
});
return ancestor;
}
/** Recursive */
flatten(nodes) {
return nodes.reduce((list, node) => {
const children = this.getChildren(node);
if (children) {
const flattened = this.flatten(children);
list.push(node, ...flattened);
}
else {
list.push(node);
}
return list;
}, []);
}
getIndex(id, ancestor) {
return ancestor.findIndex((node) => this.getId(node) === id);
}
}
function S4() {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}
function uuid() {
const id = `${S4()}${S4()}-${S4()}-${S4()}-${S4()}-${S4()}${S4()}${S4()}`;
return id.toLowerCase();
}
var SubjectStatus;
(function (SubjectStatus) {
SubjectStatus[SubjectStatus["Error"] = 0] = "Error";
SubjectStatus[SubjectStatus["Done"] = 1] = "Done";
SubjectStatus[SubjectStatus["Working"] = 2] = "Working";
SubjectStatus[SubjectStatus["Waiting"] = 3] = "Waiting";
})(SubjectStatus || (SubjectStatus = {}));
class Watcher {
status$ = new Subject();
status$$;
get status() {
return this._status;
}
set status(value) {
this._status = value;
this.status$.next(value);
}
_status;
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
subscribe(callback, scope) {
this.watch();
this.status$$ = this.status$.pipe(distinctUntilChanged()).subscribe(() => {
callback.call(scope, this);
});
}
unsubscribe() {
this.unwatch();
if (this.status$$ !== undefined) {
this.status$$.unsubscribe();
this.status$$ = undefined;
}
this.status = SubjectStatus.Waiting;
}
}
/*
* Public API Surface of utils
*/
/**
* Generated bundle index. Do not edit.
*/
export { Base64, ChangeType, ChangeUtils, Compression, DomUtils, NumberUtils, ObjectUtils, S4, StringUtils, SubjectStatus, TREE_SEPERATOR, Tree, Watcher, addExcelSheetToWorkBook, createExcelWorkBook, customCacheHasher, downloadBlob, downloadContent, downloadFromUri, isIsoDate, isValidJSON, loadTheme, strEnum, uuid, writeExcelFile };
//# sourceMappingURL=igo2-utils.mjs.map