@lowentry/utils 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LeTypes.js +121 -0
- package/LeUtils.js +1289 -115
- package/index.js +2 -2
- package/package.json +1 -1
package/LeUtils.js
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {ISSET, IS_OBJECT, STRING, INT, FLOAT, FLOAT_ANY, INT_LAX} from './LeTypes.js';
|
|
1
|
+
import {ISSET, IS_OBJECT, STRING, INT_LAX, FLOAT_LAX, INT_LAX_ANY, FLOAT_LAX_ANY} from './LeTypes.js';
|
|
3
2
|
|
|
4
3
|
|
|
5
4
|
export const LeUtils = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Parses the given version string, and returns an object with the major, minor, and patch numbers, as well as some comparison functions.
|
|
7
|
+
*
|
|
8
|
+
* Expects a version string such as:
|
|
9
|
+
* - "1"
|
|
10
|
+
* - "1.2"
|
|
11
|
+
* - "1.2.3"
|
|
12
|
+
* - "1.2.3 anything"
|
|
13
|
+
* - "1.2.3-anything"
|
|
14
|
+
*
|
|
15
|
+
* @param {string|*} versionString
|
|
16
|
+
* @returns {{major: (number), minor: (number), patch: (number), toString: (function(): string), equals: (function(string|*): boolean), smallerThan: (function(string|*): boolean), smallerThanOrEquals: (function(string|*): boolean), largerThan: (function(string|*): boolean), largerThanOrEquals: (function(string|*): boolean)}}
|
|
17
|
+
*/
|
|
17
18
|
parseVersionString:
|
|
18
19
|
(versionString) =>
|
|
19
20
|
{
|
|
@@ -104,6 +105,15 @@ export const LeUtils = {
|
|
|
104
105
|
return THIS;
|
|
105
106
|
},
|
|
106
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Returns true if the array or object contains the given value.
|
|
110
|
+
*
|
|
111
|
+
* Values are compared by casting both of them to a string.
|
|
112
|
+
*
|
|
113
|
+
* @param {array|object|Function} array
|
|
114
|
+
* @param {*} value
|
|
115
|
+
* @returns {boolean}
|
|
116
|
+
*/
|
|
107
117
|
contains:
|
|
108
118
|
(array, value) =>
|
|
109
119
|
{
|
|
@@ -124,6 +134,15 @@ export const LeUtils = {
|
|
|
124
134
|
return result;
|
|
125
135
|
},
|
|
126
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Returns true if the array or object contains the given value.
|
|
139
|
+
*
|
|
140
|
+
* Values are compared by casting both of them to a string, and then lowercasing them.
|
|
141
|
+
*
|
|
142
|
+
* @param {array|object|Function} array
|
|
143
|
+
* @param {*} value
|
|
144
|
+
* @returns {boolean}
|
|
145
|
+
*/
|
|
127
146
|
containsCaseInsensitive:
|
|
128
147
|
(array, value) =>
|
|
129
148
|
{
|
|
@@ -144,6 +163,187 @@ export const LeUtils = {
|
|
|
144
163
|
return result;
|
|
145
164
|
},
|
|
146
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Returns true if the array or object contains all the given values.
|
|
168
|
+
*
|
|
169
|
+
* Values are compared by casting both of them to a string.
|
|
170
|
+
*
|
|
171
|
+
* @param {array|object|Function} array
|
|
172
|
+
* @param {array|object|Function} values
|
|
173
|
+
* @returns {boolean}
|
|
174
|
+
*/
|
|
175
|
+
containsAll:
|
|
176
|
+
(array, values) =>
|
|
177
|
+
{
|
|
178
|
+
if(!array)
|
|
179
|
+
{
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
let result = true;
|
|
183
|
+
LeUtils.each(values, function(value)
|
|
184
|
+
{
|
|
185
|
+
if(!LeUtils.contains(array, value))
|
|
186
|
+
{
|
|
187
|
+
result = false;
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
return result;
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Returns true if the array or object contains all the given values.
|
|
196
|
+
*
|
|
197
|
+
* Values are compared by casting both of them to a string, and then lowercasing them.
|
|
198
|
+
*
|
|
199
|
+
* @param {array|object|Function} array
|
|
200
|
+
* @param {array|object|Function} values
|
|
201
|
+
* @returns {boolean}
|
|
202
|
+
*/
|
|
203
|
+
containsAllCaseInsensitive:
|
|
204
|
+
(array, values) =>
|
|
205
|
+
{
|
|
206
|
+
if(!array)
|
|
207
|
+
{
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
let result = true;
|
|
211
|
+
LeUtils.each(values, function(value)
|
|
212
|
+
{
|
|
213
|
+
if(!LeUtils.containsCaseInsensitive(array, value))
|
|
214
|
+
{
|
|
215
|
+
result = false;
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
return result;
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Returns true if the array or object contains any of the given values.
|
|
224
|
+
*
|
|
225
|
+
* Values are compared by casting both of them to a string.
|
|
226
|
+
*
|
|
227
|
+
* @param {array|object|Function} array
|
|
228
|
+
* @param {array|object|Function} values
|
|
229
|
+
* @returns {boolean}
|
|
230
|
+
*/
|
|
231
|
+
containsAny:
|
|
232
|
+
(array, values) =>
|
|
233
|
+
{
|
|
234
|
+
if(!array)
|
|
235
|
+
{
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
let result = false;
|
|
239
|
+
LeUtils.each(values, function(value)
|
|
240
|
+
{
|
|
241
|
+
if(LeUtils.contains(array, value))
|
|
242
|
+
{
|
|
243
|
+
result = true;
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
return result;
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Returns true if the array or object contains any of the given values.
|
|
252
|
+
*
|
|
253
|
+
* Values are compared by casting both of them to a string, and then lowercasing them.
|
|
254
|
+
*
|
|
255
|
+
* @param {array|object|Function} array
|
|
256
|
+
* @param {array|object|Function} values
|
|
257
|
+
* @returns {boolean}
|
|
258
|
+
*/
|
|
259
|
+
containsAnyCaseInsensitive:
|
|
260
|
+
(array, values) =>
|
|
261
|
+
{
|
|
262
|
+
if(!array)
|
|
263
|
+
{
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
let result = false;
|
|
267
|
+
LeUtils.each(values, function(value)
|
|
268
|
+
{
|
|
269
|
+
if(LeUtils.containsCaseInsensitive(array, value))
|
|
270
|
+
{
|
|
271
|
+
result = true;
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
return result;
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Returns true if the array or object contains none of the given values.
|
|
280
|
+
*
|
|
281
|
+
* Values are compared by casting both of them to a string.
|
|
282
|
+
*
|
|
283
|
+
* @param {array|object|Function} array
|
|
284
|
+
* @param {array|object|Function} values
|
|
285
|
+
* @returns {boolean}
|
|
286
|
+
*/
|
|
287
|
+
containsNone:
|
|
288
|
+
(array, values) =>
|
|
289
|
+
{
|
|
290
|
+
if(!array)
|
|
291
|
+
{
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
let result = true;
|
|
295
|
+
LeUtils.each(values, function(value)
|
|
296
|
+
{
|
|
297
|
+
if(LeUtils.contains(array, value))
|
|
298
|
+
{
|
|
299
|
+
result = false;
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
return result;
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Returns true if the array or object contains none of the given values.
|
|
308
|
+
*
|
|
309
|
+
* Values are compared by casting both of them to a string, and then lowercasing them.
|
|
310
|
+
*
|
|
311
|
+
* @param {array|object|Function} array
|
|
312
|
+
* @param {array|object|Function} values
|
|
313
|
+
* @returns {boolean}
|
|
314
|
+
*/
|
|
315
|
+
containsNoneCaseInsensitive:
|
|
316
|
+
(array, values) =>
|
|
317
|
+
{
|
|
318
|
+
if(!array)
|
|
319
|
+
{
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
let result = true;
|
|
323
|
+
LeUtils.each(values, function(value)
|
|
324
|
+
{
|
|
325
|
+
if(LeUtils.containsCaseInsensitive(array, value))
|
|
326
|
+
{
|
|
327
|
+
result = false;
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
return result;
|
|
332
|
+
},
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* @callback LeUtils~__eachCallback
|
|
336
|
+
* @param {*} value
|
|
337
|
+
* @param {*} index
|
|
338
|
+
*/
|
|
339
|
+
/**
|
|
340
|
+
* Loops through each element in the given array or object, and calls the callback for each element.
|
|
341
|
+
*
|
|
342
|
+
* @param {*[]|object|Function} elements
|
|
343
|
+
* @param {LeUtils~__eachCallback} callback
|
|
344
|
+
* @param {boolean} [optionalSkipHasOwnPropertyCheck]
|
|
345
|
+
* @returns {*[]|object|Function}
|
|
346
|
+
*/
|
|
147
347
|
each:
|
|
148
348
|
(elements, callback, optionalSkipHasOwnPropertyCheck = false) =>
|
|
149
349
|
{
|
|
@@ -180,6 +380,115 @@ export const LeUtils = {
|
|
|
180
380
|
return elements;
|
|
181
381
|
},
|
|
182
382
|
|
|
383
|
+
/**
|
|
384
|
+
* Like LeUtils.each(), except that it expects an async callback.
|
|
385
|
+
*
|
|
386
|
+
* @param {*[]|object|function} elements
|
|
387
|
+
* @param {LeUtils~__eachCallback} asyncCallback
|
|
388
|
+
* @param {number} [optionalParallelCount]
|
|
389
|
+
* @param {boolean} [optionalSkipHasOwnPropertyCheck]
|
|
390
|
+
* @returns {*[]|object|function}
|
|
391
|
+
*/
|
|
392
|
+
eachAsync:
|
|
393
|
+
(() =>
|
|
394
|
+
{
|
|
395
|
+
const eachAsyncParallel = async (elements, asyncCallback, optionalParallelCount, optionalSkipHasOwnPropertyCheck) =>
|
|
396
|
+
{
|
|
397
|
+
let promises = [];
|
|
398
|
+
let doBreak = false;
|
|
399
|
+
await LeUtils.eachAsync(elements, async (element, index) =>
|
|
400
|
+
{
|
|
401
|
+
while(promises.length > optionalParallelCount)
|
|
402
|
+
{
|
|
403
|
+
let newPromises = [];
|
|
404
|
+
LeUtils.each(promises, (promise) =>
|
|
405
|
+
{
|
|
406
|
+
if(!promise.__lowentry_utils__promise_is_done__)
|
|
407
|
+
{
|
|
408
|
+
newPromises.push(promise);
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
promises = newPromises;
|
|
412
|
+
if(promises.length > optionalParallelCount)
|
|
413
|
+
{
|
|
414
|
+
await Promise.any(promises);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if(doBreak)
|
|
419
|
+
{
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const promise = (async () =>
|
|
424
|
+
{
|
|
425
|
+
if((await asyncCallback.call(element, element, index)) === false)
|
|
426
|
+
{
|
|
427
|
+
doBreak = true;
|
|
428
|
+
}
|
|
429
|
+
promise.__lowentry_utils__promise_is_done__ = true;
|
|
430
|
+
})();
|
|
431
|
+
promises.push(promise);
|
|
432
|
+
}, optionalSkipHasOwnPropertyCheck);
|
|
433
|
+
await Promise.all(promises);
|
|
434
|
+
return elements;
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
return async (elements, asyncCallback, parallelCount = 1, optionalSkipHasOwnPropertyCheck = false) =>
|
|
438
|
+
{
|
|
439
|
+
if((elements !== null) && (typeof elements !== 'undefined'))
|
|
440
|
+
{
|
|
441
|
+
parallelCount = INT_LAX(parallelCount);
|
|
442
|
+
if(parallelCount > 1)
|
|
443
|
+
{
|
|
444
|
+
return await eachAsyncParallel(elements, asyncCallback, parallelCount, optionalSkipHasOwnPropertyCheck);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if(Array.isArray(elements))
|
|
448
|
+
{
|
|
449
|
+
for(let index = 0; index < elements.length; index++)
|
|
450
|
+
{
|
|
451
|
+
if((await asyncCallback.call(elements[index], elements[index], index)) === false)
|
|
452
|
+
{
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
else if((typeof elements === 'object') || (typeof elements === 'function'))
|
|
458
|
+
{
|
|
459
|
+
for(let index in elements)
|
|
460
|
+
{
|
|
461
|
+
if((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(elements, index))
|
|
462
|
+
{
|
|
463
|
+
if((await asyncCallback.call(elements[index], elements[index], index)) === false)
|
|
464
|
+
{
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
else
|
|
471
|
+
{
|
|
472
|
+
console.warn('Executed LeUtils.eachAsync() on an invalid type: [' + (typeof elements) + ']', elements);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return elements;
|
|
476
|
+
};
|
|
477
|
+
})(),
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* @callback LeUtils~__filterCallback
|
|
481
|
+
* @param {*} value
|
|
482
|
+
* @param {*} index
|
|
483
|
+
*/
|
|
484
|
+
/**
|
|
485
|
+
* Loops through the given elements, and returns a new array or object, with only the elements that didn't return false from the callback.
|
|
486
|
+
*
|
|
487
|
+
* @param {*[]|object|Function} elements
|
|
488
|
+
* @param {LeUtils~__filterCallback} callback
|
|
489
|
+
* @param {boolean} [optionalSkipHasOwnPropertyCheck]
|
|
490
|
+
* @returns {*[]|object|Function}
|
|
491
|
+
*/
|
|
183
492
|
filter:
|
|
184
493
|
(elements, callback, optionalSkipHasOwnPropertyCheck = false) =>
|
|
185
494
|
{
|
|
@@ -220,6 +529,19 @@ export const LeUtils = {
|
|
|
220
529
|
return elements;
|
|
221
530
|
},
|
|
222
531
|
|
|
532
|
+
/**
|
|
533
|
+
* @callback LeUtils~__mapCallback
|
|
534
|
+
* @param {*} value
|
|
535
|
+
* @param {*} index
|
|
536
|
+
*/
|
|
537
|
+
/**
|
|
538
|
+
* Loops through the given elements, and returns a new array or object, with the elements that were returned from the callback.
|
|
539
|
+
*
|
|
540
|
+
* @param {*[]|object|Function} elements
|
|
541
|
+
* @param {LeUtils~__mapCallback} callback
|
|
542
|
+
* @param {boolean} [optionalSkipHasOwnPropertyCheck]
|
|
543
|
+
* @returns {*[]|object|Function}
|
|
544
|
+
*/
|
|
223
545
|
map:
|
|
224
546
|
(elements, callback, optionalSkipHasOwnPropertyCheck = false) =>
|
|
225
547
|
{
|
|
@@ -254,6 +576,19 @@ export const LeUtils = {
|
|
|
254
576
|
return elements;
|
|
255
577
|
},
|
|
256
578
|
|
|
579
|
+
/**
|
|
580
|
+
* @callback LeUtils~__mapToArrayCallback
|
|
581
|
+
* @param {*} value
|
|
582
|
+
* @param {*} index
|
|
583
|
+
*/
|
|
584
|
+
/**
|
|
585
|
+
* Loops through the given elements, and returns a new array, with the elements that were returned from the callback. Always returns an array.
|
|
586
|
+
*
|
|
587
|
+
* @param {*[]|object|Function} elements
|
|
588
|
+
* @param {LeUtils~__mapToArrayCallback} callback
|
|
589
|
+
* @param {boolean} [optionalSkipHasOwnPropertyCheck]
|
|
590
|
+
* @returns {*[]}
|
|
591
|
+
*/
|
|
257
592
|
mapToArray:
|
|
258
593
|
(elements, callback, optionalSkipHasOwnPropertyCheck = false) =>
|
|
259
594
|
{
|
|
@@ -285,6 +620,20 @@ export const LeUtils = {
|
|
|
285
620
|
return result;
|
|
286
621
|
},
|
|
287
622
|
|
|
623
|
+
/**
|
|
624
|
+
* @callback LeUtils~__mapToArraySortedCallback
|
|
625
|
+
* @param {*} value
|
|
626
|
+
* @param {*} index
|
|
627
|
+
*/
|
|
628
|
+
/**
|
|
629
|
+
* Loops through the given elements, and returns a new array, with the elements that were returned from the callback. The elements will be sorted by the result from the given comparator. Always returns an array.
|
|
630
|
+
*
|
|
631
|
+
* @param {*[]|object|Function} elements
|
|
632
|
+
* @param {LeUtils~__sortKeysComparatorCallback} comparator
|
|
633
|
+
* @param {LeUtils~__mapToArraySortedCallback} callback
|
|
634
|
+
* @param {boolean} [optionalSkipHasOwnPropertyCheck]
|
|
635
|
+
* @returns {*[]}
|
|
636
|
+
*/
|
|
288
637
|
mapToArraySorted:
|
|
289
638
|
(elements, comparator, callback, optionalSkipHasOwnPropertyCheck = false) =>
|
|
290
639
|
{
|
|
@@ -297,6 +646,19 @@ export const LeUtils = {
|
|
|
297
646
|
return result;
|
|
298
647
|
},
|
|
299
648
|
|
|
649
|
+
/**
|
|
650
|
+
* @callback LeUtils~__sortKeysComparatorCallback
|
|
651
|
+
* @param {*} elementA
|
|
652
|
+
* @param {*} elementB
|
|
653
|
+
*/
|
|
654
|
+
/**
|
|
655
|
+
* Loops through the given elements, and returns a new array, with the keys from the given elements, sorted by the result from the given comparator. Always returns an array.
|
|
656
|
+
*
|
|
657
|
+
* @param {*[]|object|Function} elements
|
|
658
|
+
* @param {LeUtils~__sortKeysComparatorCallback} comparator
|
|
659
|
+
* @param {boolean} [optionalSkipHasOwnPropertyCheck]
|
|
660
|
+
* @returns {*[]}
|
|
661
|
+
*/
|
|
300
662
|
sortKeys:
|
|
301
663
|
(elements, comparator, optionalSkipHasOwnPropertyCheck = false) =>
|
|
302
664
|
{
|
|
@@ -329,6 +691,52 @@ export const LeUtils = {
|
|
|
329
691
|
return keys;
|
|
330
692
|
},
|
|
331
693
|
|
|
694
|
+
/**
|
|
695
|
+
* Turns the given value(s) into a 1 dimensional array.
|
|
696
|
+
*
|
|
697
|
+
* Does the same thing as Array.flat(Infinity).
|
|
698
|
+
*
|
|
699
|
+
* @param {*} array
|
|
700
|
+
* @returns {*[]}
|
|
701
|
+
*/
|
|
702
|
+
flattenArray:
|
|
703
|
+
(() =>
|
|
704
|
+
{
|
|
705
|
+
const flattenArrayRecursive = (result, array) =>
|
|
706
|
+
{
|
|
707
|
+
if(!Array.isArray(array))
|
|
708
|
+
{
|
|
709
|
+
result.push(array);
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
array.forEach((entry) =>
|
|
713
|
+
{
|
|
714
|
+
flattenArrayRecursive(result, entry);
|
|
715
|
+
});
|
|
716
|
+
};
|
|
717
|
+
|
|
718
|
+
return (array) =>
|
|
719
|
+
{
|
|
720
|
+
if(!Array.isArray(array))
|
|
721
|
+
{
|
|
722
|
+
return [array];
|
|
723
|
+
}
|
|
724
|
+
let result = [];
|
|
725
|
+
array.forEach((entry) =>
|
|
726
|
+
{
|
|
727
|
+
flattenArrayRecursive(result, entry);
|
|
728
|
+
});
|
|
729
|
+
return result;
|
|
730
|
+
};
|
|
731
|
+
})(),
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Compares two values. Primarily used for sorting.
|
|
735
|
+
*
|
|
736
|
+
* @param {*} a
|
|
737
|
+
* @param {*} b
|
|
738
|
+
* @returns {number}
|
|
739
|
+
*/
|
|
332
740
|
compare:
|
|
333
741
|
(a, b) =>
|
|
334
742
|
{
|
|
@@ -343,9 +751,23 @@ export const LeUtils = {
|
|
|
343
751
|
return 0;
|
|
344
752
|
},
|
|
345
753
|
|
|
754
|
+
/**
|
|
755
|
+
* Compares two numbers. Primarily used for sorting.
|
|
756
|
+
*
|
|
757
|
+
* @param {number} a
|
|
758
|
+
* @param {number} b
|
|
759
|
+
* @returns {number}
|
|
760
|
+
*/
|
|
346
761
|
compareNumbers:
|
|
347
762
|
(a, b) => a - b,
|
|
348
763
|
|
|
764
|
+
/**
|
|
765
|
+
* Compares two numeric strings. Primarily used for sorting.
|
|
766
|
+
*
|
|
767
|
+
* @param {string|number} a
|
|
768
|
+
* @param {string|number} b
|
|
769
|
+
* @returns {number}
|
|
770
|
+
*/
|
|
349
771
|
compareNumericStrings:
|
|
350
772
|
(a, b) =>
|
|
351
773
|
{
|
|
@@ -358,59 +780,53 @@ export const LeUtils = {
|
|
|
358
780
|
return (a.length < b.length) ? -1 : 1;
|
|
359
781
|
},
|
|
360
782
|
|
|
783
|
+
/**
|
|
784
|
+
* Returns true if the given object is empty, false otherwise.
|
|
785
|
+
*
|
|
786
|
+
* @param {object} obj
|
|
787
|
+
* @param [optionalSkipHasOwnPropertyCheck]
|
|
788
|
+
* @returns {boolean}
|
|
789
|
+
*/
|
|
361
790
|
isEmptyObject:
|
|
362
|
-
(obj) =>
|
|
791
|
+
(obj, optionalSkipHasOwnPropertyCheck = false) =>
|
|
363
792
|
{
|
|
364
|
-
|
|
365
|
-
for(let name in obj)
|
|
793
|
+
for(let field in obj)
|
|
366
794
|
{
|
|
367
|
-
|
|
795
|
+
if((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(obj, field))
|
|
796
|
+
{
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
368
799
|
}
|
|
369
800
|
return true;
|
|
370
801
|
},
|
|
371
802
|
|
|
803
|
+
/**
|
|
804
|
+
* Returns the number of fields in the given object.
|
|
805
|
+
*
|
|
806
|
+
* @param {object} obj
|
|
807
|
+
* @param [optionalSkipHasOwnPropertyCheck]
|
|
808
|
+
* @returns {number}
|
|
809
|
+
*/
|
|
372
810
|
getObjectFieldsCount:
|
|
373
|
-
(obj) =>
|
|
811
|
+
(obj, optionalSkipHasOwnPropertyCheck = false) =>
|
|
374
812
|
{
|
|
375
813
|
let count = 0;
|
|
376
|
-
for(let
|
|
814
|
+
for(let field in obj)
|
|
377
815
|
{
|
|
378
|
-
|
|
379
|
-
}
|
|
380
|
-
return count;
|
|
381
|
-
},
|
|
382
|
-
|
|
383
|
-
flattenArray:
|
|
384
|
-
(() =>
|
|
385
|
-
{
|
|
386
|
-
const flattenArrayRecursive = (result, array) =>
|
|
387
|
-
{
|
|
388
|
-
if(!Array.isArray(array))
|
|
816
|
+
if((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(obj, field))
|
|
389
817
|
{
|
|
390
|
-
|
|
391
|
-
return;
|
|
818
|
+
count++;
|
|
392
819
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
});
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
return (array) =>
|
|
400
|
-
{
|
|
401
|
-
if(!Array.isArray(array))
|
|
402
|
-
{
|
|
403
|
-
return [array];
|
|
404
|
-
}
|
|
405
|
-
let result = [];
|
|
406
|
-
array.forEach((entry) =>
|
|
407
|
-
{
|
|
408
|
-
flattenArrayRecursive(result, entry);
|
|
409
|
-
});
|
|
410
|
-
return result;
|
|
411
|
-
};
|
|
412
|
-
})(),
|
|
820
|
+
}
|
|
821
|
+
return count;
|
|
822
|
+
},
|
|
413
823
|
|
|
824
|
+
/**
|
|
825
|
+
* Returns true if the given function is a generator function (like: "function* (){}"), returns false otherwise.
|
|
826
|
+
*
|
|
827
|
+
* @param {Function} func
|
|
828
|
+
* @returns {boolean}
|
|
829
|
+
*/
|
|
414
830
|
isGeneratorFunction:
|
|
415
831
|
(() =>
|
|
416
832
|
{
|
|
@@ -426,7 +842,7 @@ export const LeUtils = {
|
|
|
426
842
|
{
|
|
427
843
|
}.constructor;
|
|
428
844
|
|
|
429
|
-
const PossibleGeneratorFunctionNames = Array.from(new Set(['GeneratorFunction', 'AsyncFunction', 'AsyncGeneratorFunction', GeneratorFunction.name, GeneratorFunction.displayName, AsyncGeneratorFunction.name, AsyncGeneratorFunction.displayName])).filter(
|
|
845
|
+
const PossibleGeneratorFunctionNames = Array.from(new Set(['GeneratorFunction', 'AsyncFunction', 'AsyncGeneratorFunction', GeneratorFunction.name, GeneratorFunction.displayName, AsyncGeneratorFunction.name, AsyncGeneratorFunction.displayName])).filter((element) =>
|
|
430
846
|
{
|
|
431
847
|
return (element && (element !== RegularFunction.name) && (element !== RegularFunction.displayName));
|
|
432
848
|
});
|
|
@@ -446,10 +862,70 @@ export const LeUtils = {
|
|
|
446
862
|
};
|
|
447
863
|
})(),
|
|
448
864
|
|
|
865
|
+
/**
|
|
866
|
+
* @callback LeUtils~__setTimeoutCallback
|
|
867
|
+
* @param {number} deltaTime
|
|
868
|
+
*/
|
|
869
|
+
/**
|
|
870
|
+
* Executes the callback after the given number of milliseconds. Passes the elapsed time in seconds to the callback.
|
|
871
|
+
*
|
|
872
|
+
* To cancel the timeout, call remove() on the result of this function (example: "const timeoutHandler = LeUtils.setTimeout((deltaTime)=>{}, 1000); timeoutHandler.remove();")
|
|
873
|
+
*
|
|
874
|
+
* @param {LeUtils~__setTimeoutCallback} callback ([number] deltaTime)
|
|
875
|
+
* @param {number} ms
|
|
876
|
+
* @returns {{remove:Function}}
|
|
877
|
+
*/
|
|
878
|
+
setTimeout:
|
|
879
|
+
(callback, ms) =>
|
|
880
|
+
{
|
|
881
|
+
ms = FLOAT_LAX(ms);
|
|
882
|
+
|
|
883
|
+
let lastTime = performance.now();
|
|
884
|
+
let handler = setTimeout(() =>
|
|
885
|
+
{
|
|
886
|
+
const currentTime = performance.now();
|
|
887
|
+
try
|
|
888
|
+
{
|
|
889
|
+
callback((currentTime - lastTime) / 1000);
|
|
890
|
+
}
|
|
891
|
+
catch(e)
|
|
892
|
+
{
|
|
893
|
+
console.error(e);
|
|
894
|
+
}
|
|
895
|
+
lastTime = currentTime;
|
|
896
|
+
}, ms);
|
|
897
|
+
|
|
898
|
+
return {
|
|
899
|
+
remove:
|
|
900
|
+
() =>
|
|
901
|
+
{
|
|
902
|
+
if(handler !== null)
|
|
903
|
+
{
|
|
904
|
+
clearTimeout(handler);
|
|
905
|
+
handler = null;
|
|
906
|
+
}
|
|
907
|
+
},
|
|
908
|
+
};
|
|
909
|
+
},
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* @callback LeUtils~__setIntervalCallback
|
|
913
|
+
* @param {number} deltaTime
|
|
914
|
+
*/
|
|
915
|
+
/**
|
|
916
|
+
* Executes the callback every given number of milliseconds. Passes the time difference in seconds between the last frame and now to it.
|
|
917
|
+
*
|
|
918
|
+
* To remove the interval, call remove() on the result of this function (example: "const intervalHandler = LeUtils.setInterval((deltaTime)=>{}, 1000); intervalHandler.remove();")
|
|
919
|
+
*
|
|
920
|
+
* @param {LeUtils~__setIntervalCallback} callback ([number] deltaTime)
|
|
921
|
+
* @param {number} [intervalMs]
|
|
922
|
+
* @param {boolean} [fireImmediately]
|
|
923
|
+
* @returns {{remove:Function}}
|
|
924
|
+
*/
|
|
449
925
|
setInterval:
|
|
450
|
-
(callback, intervalMs, fireImmediately) =>
|
|
926
|
+
(callback, intervalMs = 1000, fireImmediately = false) =>
|
|
451
927
|
{
|
|
452
|
-
intervalMs =
|
|
928
|
+
intervalMs = FLOAT_LAX_ANY(intervalMs, 1000);
|
|
453
929
|
|
|
454
930
|
if(fireImmediately)
|
|
455
931
|
{
|
|
@@ -466,7 +942,7 @@ export const LeUtils = {
|
|
|
466
942
|
let lastTime = performance.now();
|
|
467
943
|
let handler = setInterval(() =>
|
|
468
944
|
{
|
|
469
|
-
|
|
945
|
+
const currentTime = performance.now();
|
|
470
946
|
try
|
|
471
947
|
{
|
|
472
948
|
callback((currentTime - lastTime) / 1000);
|
|
@@ -491,34 +967,36 @@ export const LeUtils = {
|
|
|
491
967
|
};
|
|
492
968
|
},
|
|
493
969
|
|
|
494
|
-
|
|
495
|
-
|
|
970
|
+
/**
|
|
971
|
+
* @callback LeUtils~__setAnimationFrameTimeoutCallback
|
|
972
|
+
* @param {number} deltaTime
|
|
973
|
+
*/
|
|
974
|
+
/**
|
|
975
|
+
* Executes the callback after the given number of frames. Passes the elapsed time in seconds to the callback.
|
|
976
|
+
*
|
|
977
|
+
* To cancel the timeout, call remove() on the result of this function (example: "const timeoutHandler = LeUtils.setAnimationFrameTimeout((deltaTime){}, 5); timeoutHandler.remove();")
|
|
978
|
+
*
|
|
979
|
+
* @param {LeUtils~__setAnimationFrameTimeoutCallback} callback ([number] deltaTime)
|
|
980
|
+
* @param {number} [frames]
|
|
981
|
+
* @returns {{remove:Function}}
|
|
982
|
+
*/
|
|
983
|
+
setAnimationFrameTimeout:
|
|
984
|
+
(callback, frames = 1) =>
|
|
496
985
|
{
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
if(fireImmediately)
|
|
500
|
-
{
|
|
501
|
-
try
|
|
502
|
-
{
|
|
503
|
-
callback(0);
|
|
504
|
-
}
|
|
505
|
-
catch(e)
|
|
506
|
-
{
|
|
507
|
-
console.error(e);
|
|
508
|
-
}
|
|
509
|
-
}
|
|
986
|
+
frames = INT_LAX_ANY(frames, 1);
|
|
510
987
|
|
|
511
988
|
let run = true;
|
|
512
989
|
let requestAnimationFrameId = null;
|
|
513
990
|
let lastTime = performance.now();
|
|
514
|
-
let frames = intervalFrames;
|
|
515
991
|
const tick = () =>
|
|
516
992
|
{
|
|
517
993
|
if(run)
|
|
518
994
|
{
|
|
519
995
|
if(frames <= 0)
|
|
520
996
|
{
|
|
521
|
-
|
|
997
|
+
run = false;
|
|
998
|
+
requestAnimationFrameId = null;
|
|
999
|
+
const currentTime = performance.now();
|
|
522
1000
|
try
|
|
523
1001
|
{
|
|
524
1002
|
callback((currentTime - lastTime) / 1000);
|
|
@@ -528,17 +1006,13 @@ export const LeUtils = {
|
|
|
528
1006
|
console.error(e);
|
|
529
1007
|
}
|
|
530
1008
|
lastTime = currentTime;
|
|
531
|
-
|
|
1009
|
+
return;
|
|
532
1010
|
}
|
|
533
1011
|
frames--;
|
|
534
|
-
|
|
535
|
-
if(run)
|
|
536
|
-
{
|
|
537
|
-
requestAnimationFrameId = window?.requestAnimationFrame(tick);
|
|
538
|
-
}
|
|
1012
|
+
requestAnimationFrameId = (typeof window === 'undefined') ? setTimeout(tick, 1000 / 60) : requestAnimationFrame(tick);
|
|
539
1013
|
}
|
|
540
1014
|
};
|
|
541
|
-
|
|
1015
|
+
tick();
|
|
542
1016
|
|
|
543
1017
|
return {
|
|
544
1018
|
remove:
|
|
@@ -547,43 +1021,75 @@ export const LeUtils = {
|
|
|
547
1021
|
run = false;
|
|
548
1022
|
if(requestAnimationFrameId !== null)
|
|
549
1023
|
{
|
|
550
|
-
cancelAnimationFrame(requestAnimationFrameId);
|
|
1024
|
+
(typeof window === 'undefined') ? clearTimeout(requestAnimationFrameId) : cancelAnimationFrame(requestAnimationFrameId);
|
|
551
1025
|
requestAnimationFrameId = null;
|
|
552
1026
|
}
|
|
553
1027
|
},
|
|
554
1028
|
};
|
|
555
1029
|
},
|
|
556
1030
|
|
|
557
|
-
|
|
558
|
-
|
|
1031
|
+
/**
|
|
1032
|
+
* @callback LeUtils~__setAnimationFrameIntervalCallback
|
|
1033
|
+
* @param {number} deltaTime
|
|
1034
|
+
*/
|
|
1035
|
+
/**
|
|
1036
|
+
* Executes the callback every given number of frames. Passes the time difference in seconds between the last frame and now to it.
|
|
1037
|
+
*
|
|
1038
|
+
* To remove the interval, call remove() on the result of this function (example: "const intervalHandler = LeUtils.setAnimationFrameInterval((deltaTime)=>{}, 5); intervalHandler.remove();")
|
|
1039
|
+
*
|
|
1040
|
+
* @param {LeUtils~__setAnimationFrameIntervalCallback} callback ([number] deltaTime)
|
|
1041
|
+
* @param {number} [intervalFrames]
|
|
1042
|
+
* @param {boolean} [fireImmediately]
|
|
1043
|
+
* @returns {{remove:Function}}
|
|
1044
|
+
*/
|
|
1045
|
+
setAnimationFrameInterval:
|
|
1046
|
+
(callback, intervalFrames = 1, fireImmediately = false) =>
|
|
559
1047
|
{
|
|
560
|
-
|
|
1048
|
+
intervalFrames = INT_LAX_ANY(intervalFrames, 1);
|
|
1049
|
+
|
|
1050
|
+
if(fireImmediately)
|
|
1051
|
+
{
|
|
1052
|
+
try
|
|
1053
|
+
{
|
|
1054
|
+
callback(0);
|
|
1055
|
+
}
|
|
1056
|
+
catch(e)
|
|
1057
|
+
{
|
|
1058
|
+
console.error(e);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
561
1061
|
|
|
562
1062
|
let run = true;
|
|
563
1063
|
let requestAnimationFrameId = null;
|
|
1064
|
+
let lastTime = performance.now();
|
|
1065
|
+
let frames = intervalFrames;
|
|
564
1066
|
const tick = () =>
|
|
565
1067
|
{
|
|
566
1068
|
if(run)
|
|
567
1069
|
{
|
|
568
1070
|
if(frames <= 0)
|
|
569
1071
|
{
|
|
570
|
-
|
|
571
|
-
requestAnimationFrameId = null;
|
|
1072
|
+
let currentTime = performance.now();
|
|
572
1073
|
try
|
|
573
1074
|
{
|
|
574
|
-
callback();
|
|
1075
|
+
callback((currentTime - lastTime) / 1000);
|
|
575
1076
|
}
|
|
576
1077
|
catch(e)
|
|
577
1078
|
{
|
|
578
1079
|
console.error(e);
|
|
579
1080
|
}
|
|
580
|
-
|
|
1081
|
+
lastTime = currentTime;
|
|
1082
|
+
frames = intervalFrames;
|
|
581
1083
|
}
|
|
582
1084
|
frames--;
|
|
583
|
-
|
|
1085
|
+
|
|
1086
|
+
if(run)
|
|
1087
|
+
{
|
|
1088
|
+
requestAnimationFrameId = (typeof window === 'undefined') ? setTimeout(tick, 1000 / 60) : requestAnimationFrame(tick);
|
|
1089
|
+
}
|
|
584
1090
|
}
|
|
585
1091
|
};
|
|
586
|
-
tick();
|
|
1092
|
+
(typeof window === 'undefined') ? setTimeout(tick, 1000 / 60) : requestAnimationFrame(tick);
|
|
587
1093
|
|
|
588
1094
|
return {
|
|
589
1095
|
remove:
|
|
@@ -592,35 +1098,45 @@ export const LeUtils = {
|
|
|
592
1098
|
run = false;
|
|
593
1099
|
if(requestAnimationFrameId !== null)
|
|
594
1100
|
{
|
|
595
|
-
cancelAnimationFrame(requestAnimationFrameId);
|
|
1101
|
+
(typeof window === 'undefined') ? clearTimeout(requestAnimationFrameId) : cancelAnimationFrame(requestAnimationFrameId);
|
|
596
1102
|
requestAnimationFrameId = null;
|
|
597
1103
|
}
|
|
598
1104
|
},
|
|
599
1105
|
};
|
|
600
1106
|
},
|
|
601
1107
|
|
|
602
|
-
|
|
603
|
-
|
|
1108
|
+
/**
|
|
1109
|
+
* Returns a promise, which will be resolved after the given number of milliseconds.
|
|
1110
|
+
*
|
|
1111
|
+
* @param {number} ms
|
|
1112
|
+
* @returns {Promise}
|
|
1113
|
+
*/
|
|
1114
|
+
promiseTimeout:
|
|
1115
|
+
(ms) =>
|
|
604
1116
|
{
|
|
605
|
-
|
|
606
|
-
if(
|
|
1117
|
+
ms = FLOAT_LAX(ms);
|
|
1118
|
+
if(ms <= 0)
|
|
607
1119
|
{
|
|
608
|
-
return
|
|
1120
|
+
return new Promise(resolve => resolve());
|
|
609
1121
|
}
|
|
610
|
-
return
|
|
1122
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
611
1123
|
},
|
|
612
1124
|
|
|
613
|
-
|
|
614
|
-
|
|
1125
|
+
/**
|
|
1126
|
+
* Returns a promise, which will be resolved after the given number of frames.
|
|
1127
|
+
*
|
|
1128
|
+
* @param {number} frames
|
|
1129
|
+
* @returns {Promise}
|
|
1130
|
+
*/
|
|
1131
|
+
promiseAnimationFrameTimeout:
|
|
1132
|
+
(frames) =>
|
|
615
1133
|
{
|
|
616
|
-
|
|
1134
|
+
frames = INT_LAX(frames);
|
|
1135
|
+
if(frames <= 0)
|
|
617
1136
|
{
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
callback();
|
|
622
|
-
}
|
|
623
|
-
};
|
|
1137
|
+
return new Promise(resolve => resolve());
|
|
1138
|
+
}
|
|
1139
|
+
return new Promise(resolve => LeUtils.setAnimationFrameTimeout(resolve, frames));
|
|
624
1140
|
},
|
|
625
1141
|
|
|
626
1142
|
/**
|
|
@@ -631,13 +1147,19 @@ export const LeUtils = {
|
|
|
631
1147
|
* - Mobile: True
|
|
632
1148
|
* - Tablet: False
|
|
633
1149
|
* - Desktop: False
|
|
1150
|
+
*
|
|
1151
|
+
* @returns {boolean}
|
|
634
1152
|
*/
|
|
635
1153
|
platformIsMobile:
|
|
636
1154
|
() =>
|
|
637
1155
|
{
|
|
1156
|
+
if(typeof window === 'undefined')
|
|
1157
|
+
{
|
|
1158
|
+
return false;
|
|
1159
|
+
}
|
|
638
1160
|
// noinspection JSDeprecatedSymbols, JSUnresolvedReference
|
|
639
1161
|
/** navigator.userAgentData.mobile doesn't return the correct value on some platforms, so this is a work-around, code from: http://detectmobilebrowsers.com **/
|
|
640
|
-
const a = STRING(window
|
|
1162
|
+
const a = STRING(window.navigator?.userAgent || window.navigator?.vendor || window.opera || '');
|
|
641
1163
|
const b = a.substring(0, 4);
|
|
642
1164
|
return !!(
|
|
643
1165
|
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series([46])0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i
|
|
@@ -650,24 +1172,43 @@ export const LeUtils = {
|
|
|
650
1172
|
/**
|
|
651
1173
|
* Returns true if the user has a cursor (mouse, touchpad, etc).
|
|
652
1174
|
* In this context, a cursor is defined as an input device that can hover over elements without necessarily interacting with them.
|
|
1175
|
+
*
|
|
1176
|
+
* @returns {boolean}
|
|
653
1177
|
*/
|
|
654
1178
|
platformHasCursor:
|
|
655
1179
|
() =>
|
|
656
1180
|
{
|
|
657
|
-
|
|
1181
|
+
if(typeof window === 'undefined')
|
|
1182
|
+
{
|
|
1183
|
+
return true;
|
|
1184
|
+
}
|
|
1185
|
+
return !LeUtils.platformIsMobile() && !window.matchMedia('(any-hover: none)')?.matches;
|
|
658
1186
|
},
|
|
659
1187
|
|
|
660
|
-
|
|
661
|
-
|
|
1188
|
+
/**
|
|
1189
|
+
* Returns the given string, with the first character capitalized.
|
|
1190
|
+
*
|
|
1191
|
+
* @param {String} string
|
|
1192
|
+
* @returns {string}
|
|
1193
|
+
*/
|
|
1194
|
+
capitalize:
|
|
1195
|
+
(string) =>
|
|
662
1196
|
{
|
|
663
|
-
|
|
664
|
-
if(
|
|
1197
|
+
string = STRING(string).trim();
|
|
1198
|
+
if(string.length <= 0)
|
|
665
1199
|
{
|
|
666
|
-
return
|
|
1200
|
+
return string;
|
|
667
1201
|
}
|
|
668
|
-
return
|
|
1202
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
669
1203
|
},
|
|
670
1204
|
|
|
1205
|
+
/**
|
|
1206
|
+
* Returns true if the given string ends with any of the given characters or words.
|
|
1207
|
+
*
|
|
1208
|
+
* @param {string} string
|
|
1209
|
+
* @param {string|string[]} endingCharsStringOrArray
|
|
1210
|
+
* @returns {boolean}
|
|
1211
|
+
*/
|
|
671
1212
|
endsWithAny:
|
|
672
1213
|
(string, endingCharsStringOrArray) =>
|
|
673
1214
|
{
|
|
@@ -693,6 +1234,13 @@ export const LeUtils = {
|
|
|
693
1234
|
return result;
|
|
694
1235
|
},
|
|
695
1236
|
|
|
1237
|
+
/**
|
|
1238
|
+
* Returns true if the given string starts with any of the given characters or words.
|
|
1239
|
+
*
|
|
1240
|
+
* @param {string} string
|
|
1241
|
+
* @param {string|string[]} startingCharsStringOrArray
|
|
1242
|
+
* @returns {boolean}
|
|
1243
|
+
*/
|
|
696
1244
|
startsWithAny:
|
|
697
1245
|
(string, startingCharsStringOrArray) =>
|
|
698
1246
|
{
|
|
@@ -718,6 +1266,12 @@ export const LeUtils = {
|
|
|
718
1266
|
return result;
|
|
719
1267
|
},
|
|
720
1268
|
|
|
1269
|
+
/**
|
|
1270
|
+
* Trims the end of the given string, by removing the given characters from it.
|
|
1271
|
+
*
|
|
1272
|
+
* @param {string} string
|
|
1273
|
+
* @param {string|string[]} trimCharsStringOrArray
|
|
1274
|
+
*/
|
|
721
1275
|
trimEnd:
|
|
722
1276
|
(string, trimCharsStringOrArray) =>
|
|
723
1277
|
{
|
|
@@ -749,6 +1303,12 @@ export const LeUtils = {
|
|
|
749
1303
|
return string;
|
|
750
1304
|
},
|
|
751
1305
|
|
|
1306
|
+
/**
|
|
1307
|
+
* Trims the start of the given string, by removing the given characters from it.
|
|
1308
|
+
*
|
|
1309
|
+
* @param {string} string
|
|
1310
|
+
* @param {string|string[]} trimCharsStringOrArray
|
|
1311
|
+
*/
|
|
752
1312
|
trimStart:
|
|
753
1313
|
(string, trimCharsStringOrArray) =>
|
|
754
1314
|
{
|
|
@@ -780,10 +1340,22 @@ export const LeUtils = {
|
|
|
780
1340
|
return string;
|
|
781
1341
|
},
|
|
782
1342
|
|
|
1343
|
+
/**
|
|
1344
|
+
* Trims the start and end of the given string, by removing the given characters from it.
|
|
1345
|
+
*
|
|
1346
|
+
* @param {string} string
|
|
1347
|
+
* @param {string|string[]} trimCharsStringOrArray
|
|
1348
|
+
*/
|
|
783
1349
|
trim:
|
|
784
1350
|
(string, trimCharsStringOrArray) => LeUtils.trimEnd(LeUtils.trimStart(string, trimCharsStringOrArray), trimCharsStringOrArray),
|
|
785
1351
|
|
|
786
|
-
|
|
1352
|
+
/**
|
|
1353
|
+
* Returns the given string, trims the start and end, and makes sure it ends with a valid sentence ending character (such as !?;.).
|
|
1354
|
+
*
|
|
1355
|
+
* @param {string} sentence
|
|
1356
|
+
* @returns {string}
|
|
1357
|
+
*/
|
|
1358
|
+
purgeSentence:
|
|
787
1359
|
(sentence) =>
|
|
788
1360
|
{
|
|
789
1361
|
sentence = LeUtils.trimEnd(STRING(sentence).trim(), '.: \r\n\t');
|
|
@@ -791,6 +1363,26 @@ export const LeUtils = {
|
|
|
791
1363
|
return sentence;
|
|
792
1364
|
},
|
|
793
1365
|
|
|
1366
|
+
/**
|
|
1367
|
+
* Attempts to obtain and return an error message from the given error, regardless of what is passed to this function.
|
|
1368
|
+
*
|
|
1369
|
+
* @param {*} error
|
|
1370
|
+
* @returns {string}
|
|
1371
|
+
*/
|
|
1372
|
+
purgeErrorMessage:
|
|
1373
|
+
(error) =>
|
|
1374
|
+
{
|
|
1375
|
+
const message = STRING(((typeof error === 'string') ? error : (error.message ?? JSON.stringify(error))));
|
|
1376
|
+
const messageParts = message.split('threw an error:');
|
|
1377
|
+
return messageParts[messageParts.length - 1].trim();
|
|
1378
|
+
},
|
|
1379
|
+
|
|
1380
|
+
/**
|
|
1381
|
+
* Increases the given numeric string by 1, this allows you to increase a numeric string without a limit.
|
|
1382
|
+
*
|
|
1383
|
+
* @param {string} string
|
|
1384
|
+
* @returns {string}
|
|
1385
|
+
*/
|
|
794
1386
|
increaseNumericStringByOne:
|
|
795
1387
|
(string) =>
|
|
796
1388
|
{
|
|
@@ -832,6 +1424,11 @@ export const LeUtils = {
|
|
|
832
1424
|
return string;
|
|
833
1425
|
},
|
|
834
1426
|
|
|
1427
|
+
/**
|
|
1428
|
+
* Generates a string that is guaranteed to be unique (across the entire frontend).
|
|
1429
|
+
*
|
|
1430
|
+
* @returns {string}
|
|
1431
|
+
*/
|
|
835
1432
|
uniqueId:
|
|
836
1433
|
(() =>
|
|
837
1434
|
{
|
|
@@ -881,10 +1478,587 @@ export const LeUtils = {
|
|
|
881
1478
|
};
|
|
882
1479
|
})(),
|
|
883
1480
|
|
|
1481
|
+
/**
|
|
1482
|
+
* Returns a data URL of a 1x1 transparent pixel.
|
|
1483
|
+
*
|
|
1484
|
+
* @returns {string}
|
|
1485
|
+
*/
|
|
884
1486
|
getEmptyImageSrc:
|
|
885
1487
|
() =>
|
|
886
1488
|
{
|
|
887
1489
|
// noinspection SpellCheckingInspection
|
|
888
1490
|
return 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
|
|
889
1491
|
},
|
|
1492
|
+
|
|
1493
|
+
/**
|
|
1494
|
+
* Calculates and returns the percentage of the part and total ((part / total) * 100).
|
|
1495
|
+
*
|
|
1496
|
+
* @param {number|string} part
|
|
1497
|
+
* @param {number|string} total
|
|
1498
|
+
* @returns {number}
|
|
1499
|
+
*/
|
|
1500
|
+
getPercentage:
|
|
1501
|
+
(part, total) =>
|
|
1502
|
+
{
|
|
1503
|
+
part = FLOAT_LAX(part);
|
|
1504
|
+
total = FLOAT_LAX(total);
|
|
1505
|
+
if(total <= 0)
|
|
1506
|
+
{
|
|
1507
|
+
return 100;
|
|
1508
|
+
}
|
|
1509
|
+
return Math.max(0, Math.min(100, ((part / total) * 100)));
|
|
1510
|
+
},
|
|
1511
|
+
|
|
1512
|
+
/**
|
|
1513
|
+
* Returns the pixels of the given Image object.
|
|
1514
|
+
*
|
|
1515
|
+
* @param {HTMLImageElement} image
|
|
1516
|
+
* @returns {Uint8ClampedArray}
|
|
1517
|
+
*/
|
|
1518
|
+
getImagePixels:
|
|
1519
|
+
(image) =>
|
|
1520
|
+
{
|
|
1521
|
+
if(!document)
|
|
1522
|
+
{
|
|
1523
|
+
return new Uint8ClampedArray();
|
|
1524
|
+
}
|
|
1525
|
+
const canvas = document.createElement('canvas');
|
|
1526
|
+
document.body.appendChild(canvas);
|
|
1527
|
+
try
|
|
1528
|
+
{
|
|
1529
|
+
const ctx = canvas.getContext('2d');
|
|
1530
|
+
const width = Math.floor(image.width);
|
|
1531
|
+
const height = Math.floor(image.height);
|
|
1532
|
+
if((width <= 0) || (height <= 0))
|
|
1533
|
+
{
|
|
1534
|
+
canvas.width = 1;
|
|
1535
|
+
canvas.height = 1;
|
|
1536
|
+
}
|
|
1537
|
+
else
|
|
1538
|
+
{
|
|
1539
|
+
canvas.width = width;
|
|
1540
|
+
canvas.height = height;
|
|
1541
|
+
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
1542
|
+
}
|
|
1543
|
+
return ctx.getImageData(0, 0, canvas.width, canvas.height).data;
|
|
1544
|
+
}
|
|
1545
|
+
finally
|
|
1546
|
+
{
|
|
1547
|
+
canvas.parentNode.removeChild(canvas);
|
|
1548
|
+
}
|
|
1549
|
+
},
|
|
1550
|
+
|
|
1551
|
+
/**
|
|
1552
|
+
* Returns the data URL (mimetype "image/png") of a colored version of the given Image object.
|
|
1553
|
+
*
|
|
1554
|
+
* @param {HTMLImageElement} image
|
|
1555
|
+
* @param {string} color
|
|
1556
|
+
* @returns {string}
|
|
1557
|
+
*/
|
|
1558
|
+
getColoredImage:
|
|
1559
|
+
(image, color) =>
|
|
1560
|
+
{
|
|
1561
|
+
if(!document)
|
|
1562
|
+
{
|
|
1563
|
+
return LeUtils.getEmptyImageSrc();
|
|
1564
|
+
}
|
|
1565
|
+
const canvas = document.createElement('canvas');
|
|
1566
|
+
document.body.appendChild(canvas);
|
|
1567
|
+
try
|
|
1568
|
+
{
|
|
1569
|
+
const ctx = canvas.getContext('2d');
|
|
1570
|
+
const width = Math.floor(image.width);
|
|
1571
|
+
const height = Math.floor(image.height);
|
|
1572
|
+
if((width <= 0) || (height <= 0))
|
|
1573
|
+
{
|
|
1574
|
+
canvas.width = 1;
|
|
1575
|
+
canvas.height = 1;
|
|
1576
|
+
}
|
|
1577
|
+
else
|
|
1578
|
+
{
|
|
1579
|
+
canvas.width = width;
|
|
1580
|
+
canvas.height = height;
|
|
1581
|
+
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
1582
|
+
}
|
|
1583
|
+
ctx.globalCompositeOperation = 'source-in';
|
|
1584
|
+
ctx.fillStyle = color;
|
|
1585
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
1586
|
+
return canvas.toDataURL('image/png');
|
|
1587
|
+
}
|
|
1588
|
+
finally
|
|
1589
|
+
{
|
|
1590
|
+
canvas.parentNode.removeChild(canvas);
|
|
1591
|
+
}
|
|
1592
|
+
},
|
|
1593
|
+
|
|
1594
|
+
/**
|
|
1595
|
+
* Returns the hex color of the given RGB(A).
|
|
1596
|
+
*
|
|
1597
|
+
* @param {number[]} rgb
|
|
1598
|
+
* @returns {string}
|
|
1599
|
+
*/
|
|
1600
|
+
rgbToHex:
|
|
1601
|
+
(rgb) =>
|
|
1602
|
+
{
|
|
1603
|
+
return '#' + rgb.map((x) =>
|
|
1604
|
+
{
|
|
1605
|
+
const hex = x.toString(16);
|
|
1606
|
+
return ((hex.length === 1) ? '0' + hex : hex);
|
|
1607
|
+
}).join('');
|
|
1608
|
+
},
|
|
1609
|
+
|
|
1610
|
+
/**
|
|
1611
|
+
* Returns the Lab of the given RGB.
|
|
1612
|
+
*
|
|
1613
|
+
* @param {number[]} rgb
|
|
1614
|
+
* @returns {number[]}
|
|
1615
|
+
*/
|
|
1616
|
+
rgbToLab:
|
|
1617
|
+
(rgb) =>
|
|
1618
|
+
{
|
|
1619
|
+
let r = rgb[0] / 255;
|
|
1620
|
+
let g = rgb[1] / 255;
|
|
1621
|
+
let b = rgb[2] / 255;
|
|
1622
|
+
r = (r > 0.04045) ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
|
|
1623
|
+
g = (g > 0.04045) ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
|
|
1624
|
+
b = (b > 0.04045) ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
|
|
1625
|
+
let x = (r * 0.4124 + g * 0.3576 + b * 0.1805) / 0.95047;
|
|
1626
|
+
let y = (r * 0.2126 + g * 0.7152 + b * 0.0722);
|
|
1627
|
+
let z = (r * 0.0193 + g * 0.1192 + b * 0.9505) / 1.08883;
|
|
1628
|
+
x = (x > 0.008856) ? Math.pow(x, 1 / 3) : (7.787 * x) + 16 / 116;
|
|
1629
|
+
y = (y > 0.008856) ? Math.pow(y, 1 / 3) : (7.787 * y) + 16 / 116;
|
|
1630
|
+
z = (z > 0.008856) ? Math.pow(z, 1 / 3) : (7.787 * z) + 16 / 116;
|
|
1631
|
+
return [(116 * y) - 16, 500 * (x - y), 200 * (y - z)];
|
|
1632
|
+
},
|
|
1633
|
+
|
|
1634
|
+
/**
|
|
1635
|
+
* Returns the HSL(A) of the given RGB(A).
|
|
1636
|
+
*
|
|
1637
|
+
* @param {number[]} rgb
|
|
1638
|
+
* @returns {number[]}
|
|
1639
|
+
*/
|
|
1640
|
+
rgbToHsl:
|
|
1641
|
+
(rgb) =>
|
|
1642
|
+
{
|
|
1643
|
+
const r = rgb[0] / 255;
|
|
1644
|
+
const g = rgb[1] / 255;
|
|
1645
|
+
const b = rgb[2] / 255;
|
|
1646
|
+
|
|
1647
|
+
const max = Math.max(r, g, b);
|
|
1648
|
+
const min = Math.min(r, g, b);
|
|
1649
|
+
let h, s, l = (max + min) / 2;
|
|
1650
|
+
|
|
1651
|
+
if(max === min)
|
|
1652
|
+
{
|
|
1653
|
+
h = s = 0;
|
|
1654
|
+
}
|
|
1655
|
+
else
|
|
1656
|
+
{
|
|
1657
|
+
const d = max - min;
|
|
1658
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
1659
|
+
switch(max)
|
|
1660
|
+
{
|
|
1661
|
+
case r:
|
|
1662
|
+
h = (g - b) / d + (g < b ? 6 : 0);
|
|
1663
|
+
break;
|
|
1664
|
+
case g:
|
|
1665
|
+
h = (b - r) / d + 2;
|
|
1666
|
+
break;
|
|
1667
|
+
case b:
|
|
1668
|
+
h = (r - g) / d + 4;
|
|
1669
|
+
break;
|
|
1670
|
+
}
|
|
1671
|
+
h /= 6;
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
if(rgb.length >= 4)
|
|
1675
|
+
{
|
|
1676
|
+
return [h, s, l, rgb[3] / 255];
|
|
1677
|
+
}
|
|
1678
|
+
return [h, s, l];
|
|
1679
|
+
},
|
|
1680
|
+
|
|
1681
|
+
/**
|
|
1682
|
+
* Returns the difference (calculated with DeltaE) of the Lab values of the given RGB values.
|
|
1683
|
+
*
|
|
1684
|
+
* Returns a number:
|
|
1685
|
+
*
|
|
1686
|
+
* <pre>
|
|
1687
|
+
* < 1.0 is not perceptible by human eyes
|
|
1688
|
+
* 1-2 is perceptible through close observation
|
|
1689
|
+
* 2-10 is perceptible at a glance
|
|
1690
|
+
* 11-49 is more similar than opposite
|
|
1691
|
+
* 100 is exactly the opposite color
|
|
1692
|
+
* </pre>
|
|
1693
|
+
*
|
|
1694
|
+
* @param {number[]} rgbA
|
|
1695
|
+
* @param {number[]} rgbB
|
|
1696
|
+
* @returns {number}
|
|
1697
|
+
*/
|
|
1698
|
+
getDifferenceBetweenRgb:
|
|
1699
|
+
(rgbA, rgbB) =>
|
|
1700
|
+
{
|
|
1701
|
+
const labA = LeUtils.rgbToLab(rgbA);
|
|
1702
|
+
const labB = LeUtils.rgbToLab(rgbB);
|
|
1703
|
+
return LeUtils.getDifferenceBetweenLab(labA, labB);
|
|
1704
|
+
},
|
|
1705
|
+
|
|
1706
|
+
/**
|
|
1707
|
+
* Returns the difference (calculated with DeltaE) of the given Lab values.
|
|
1708
|
+
*
|
|
1709
|
+
* Returns a number:
|
|
1710
|
+
*
|
|
1711
|
+
* <pre>
|
|
1712
|
+
* < 1.0 is not perceptible by human eyes
|
|
1713
|
+
* 1-2 is perceptible through close observation
|
|
1714
|
+
* 2-10 is perceptible at a glance
|
|
1715
|
+
* 11-49 is more similar than opposite
|
|
1716
|
+
* 100 is exactly the opposite color
|
|
1717
|
+
* </pre>
|
|
1718
|
+
*
|
|
1719
|
+
* @param {number[]} labA
|
|
1720
|
+
* @param {number[]} labB
|
|
1721
|
+
* @returns {number}
|
|
1722
|
+
*/
|
|
1723
|
+
getDifferenceBetweenLab:
|
|
1724
|
+
(labA, labB) =>
|
|
1725
|
+
{
|
|
1726
|
+
const deltaL = labA[0] - labB[0];
|
|
1727
|
+
const deltaA = labA[1] - labB[1];
|
|
1728
|
+
const deltaB = labA[2] - labB[2];
|
|
1729
|
+
const c1 = Math.sqrt(labA[1] * labA[1] + labA[2] * labA[2]);
|
|
1730
|
+
const c2 = Math.sqrt(labB[1] * labB[1] + labB[2] * labB[2]);
|
|
1731
|
+
const deltaC = c1 - c2;
|
|
1732
|
+
let deltaH = deltaA * deltaA + deltaB * deltaB - deltaC * deltaC;
|
|
1733
|
+
deltaH = deltaH < 0 ? 0 : Math.sqrt(deltaH);
|
|
1734
|
+
const sc = 1.0 + 0.045 * c1;
|
|
1735
|
+
const sh = 1.0 + 0.015 * c1;
|
|
1736
|
+
const deltaLKlsl = deltaL / (1.0);
|
|
1737
|
+
const deltaCkcsc = deltaC / (sc);
|
|
1738
|
+
const deltaHkhsh = deltaH / (sh);
|
|
1739
|
+
const i = deltaLKlsl * deltaLKlsl + deltaCkcsc * deltaCkcsc + deltaHkhsh * deltaHkhsh;
|
|
1740
|
+
return i < 0 ? 0 : Math.sqrt(i);
|
|
1741
|
+
},
|
|
1742
|
+
|
|
1743
|
+
/**
|
|
1744
|
+
* Returns the RGB(A) of the given RGB(A) values, based on the given percentage (0-100).
|
|
1745
|
+
* This allows you to define a gradient of colors to fade in between, rather than just having a start and an end color.
|
|
1746
|
+
*
|
|
1747
|
+
* Usage:
|
|
1748
|
+
*
|
|
1749
|
+
* <pre>
|
|
1750
|
+
* LeUtils.getRgbOfGradient({
|
|
1751
|
+
* 0: [255, 0, 0],
|
|
1752
|
+
* 33: [255, 255, 0],
|
|
1753
|
+
* 66: [0, 255, 0],
|
|
1754
|
+
* 100:[0, 255, 255],
|
|
1755
|
+
* }, 45.1234);
|
|
1756
|
+
* </pre>
|
|
1757
|
+
*
|
|
1758
|
+
* @param {{[percentage]: number[]}} gradient
|
|
1759
|
+
* @param {number} percentage
|
|
1760
|
+
* @returns {number[]}
|
|
1761
|
+
*/
|
|
1762
|
+
getRgbOfGradient:
|
|
1763
|
+
(gradient, percentage) =>
|
|
1764
|
+
{
|
|
1765
|
+
percentage = Math.max(0, Math.min(100, FLOAT_LAX(percentage)));
|
|
1766
|
+
|
|
1767
|
+
let closest = null;
|
|
1768
|
+
LeUtils.each(gradient, (color, percent) =>
|
|
1769
|
+
{
|
|
1770
|
+
percent = INT_LAX(percent);
|
|
1771
|
+
if(closest === null)
|
|
1772
|
+
{
|
|
1773
|
+
closest = [percent, Math.abs(percentage - percent)];
|
|
1774
|
+
}
|
|
1775
|
+
else
|
|
1776
|
+
{
|
|
1777
|
+
const difference = Math.abs(percentage - percent);
|
|
1778
|
+
if(difference < closest[1])
|
|
1779
|
+
{
|
|
1780
|
+
closest = [percent, difference];
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
});
|
|
1784
|
+
if(closest === null)
|
|
1785
|
+
{
|
|
1786
|
+
return null;
|
|
1787
|
+
}
|
|
1788
|
+
closest = closest[0];
|
|
1789
|
+
|
|
1790
|
+
let higher = 99999;
|
|
1791
|
+
let lower = -99999;
|
|
1792
|
+
LeUtils.each(gradient, (color, percent) =>
|
|
1793
|
+
{
|
|
1794
|
+
percent = INT_LAX(percent);
|
|
1795
|
+
if(percent < closest)
|
|
1796
|
+
{
|
|
1797
|
+
if(percent > lower)
|
|
1798
|
+
{
|
|
1799
|
+
lower = percent;
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
if(percent > closest)
|
|
1803
|
+
{
|
|
1804
|
+
if(percent < higher)
|
|
1805
|
+
{
|
|
1806
|
+
higher = percent;
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
});
|
|
1810
|
+
if(higher === 99999)
|
|
1811
|
+
{
|
|
1812
|
+
higher = null;
|
|
1813
|
+
}
|
|
1814
|
+
if(lower === -99999)
|
|
1815
|
+
{
|
|
1816
|
+
lower = null;
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
if(((higher === null) && (lower === null)) || (higher === lower))
|
|
1820
|
+
{
|
|
1821
|
+
return gradient[closest];
|
|
1822
|
+
}
|
|
1823
|
+
else if((higher !== null) && (lower !== null))
|
|
1824
|
+
{
|
|
1825
|
+
const higherDifference = Math.abs(higher - percentage);
|
|
1826
|
+
const lowerDifference = Math.abs(percentage - lower);
|
|
1827
|
+
if(higherDifference > lowerDifference)
|
|
1828
|
+
{
|
|
1829
|
+
higher = closest;
|
|
1830
|
+
}
|
|
1831
|
+
else
|
|
1832
|
+
{
|
|
1833
|
+
lower = closest;
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
else if(lower === null)
|
|
1837
|
+
{
|
|
1838
|
+
lower = closest;
|
|
1839
|
+
}
|
|
1840
|
+
else
|
|
1841
|
+
{
|
|
1842
|
+
higher = closest;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
if(lower > higher)
|
|
1846
|
+
{
|
|
1847
|
+
const tmp = higher;
|
|
1848
|
+
higher = lower;
|
|
1849
|
+
lower = tmp;
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
const total = (higher - lower);
|
|
1853
|
+
const part = (percentage - lower);
|
|
1854
|
+
return LeUtils.getRgbBetween(gradient[lower], gradient[higher], ((part / total) * 100));
|
|
1855
|
+
},
|
|
1856
|
+
|
|
1857
|
+
/**
|
|
1858
|
+
* Returns the RGB(A) between the two given RGB(A) values, based on the given percentage (0-100).
|
|
1859
|
+
*
|
|
1860
|
+
* @param {number[]} startRgb
|
|
1861
|
+
* @param {number[]} endRgb
|
|
1862
|
+
* @param {number} percentage
|
|
1863
|
+
* @returns {number[]}
|
|
1864
|
+
*/
|
|
1865
|
+
getRgbBetween:
|
|
1866
|
+
(startRgb, endRgb, percentage) =>
|
|
1867
|
+
{
|
|
1868
|
+
percentage = FLOAT_LAX(percentage);
|
|
1869
|
+
const partEnd = Math.max(0, Math.min(1, (percentage / 100.0)));
|
|
1870
|
+
const partStart = (1 - partEnd);
|
|
1871
|
+
const length = Math.min(startRgb.length, endRgb.length);
|
|
1872
|
+
let result = [];
|
|
1873
|
+
for(let i = 0; i < length; i++)
|
|
1874
|
+
{
|
|
1875
|
+
result.push(Math.max(0, Math.min(255, Math.round((startRgb[i] * partStart) + (endRgb[i] * partEnd)))));
|
|
1876
|
+
}
|
|
1877
|
+
return result;
|
|
1878
|
+
},
|
|
1879
|
+
|
|
1880
|
+
/**
|
|
1881
|
+
* An implementation of the btoa function, which should work in all environments.
|
|
1882
|
+
*
|
|
1883
|
+
* @param {string} string
|
|
1884
|
+
* @returns {string}
|
|
1885
|
+
*/
|
|
1886
|
+
btoa:
|
|
1887
|
+
(string) =>
|
|
1888
|
+
{
|
|
1889
|
+
if(typeof btoa === 'function')
|
|
1890
|
+
{
|
|
1891
|
+
return btoa(string);
|
|
1892
|
+
}
|
|
1893
|
+
return Buffer.from(string).toString('base64');
|
|
1894
|
+
},
|
|
1895
|
+
|
|
1896
|
+
/**
|
|
1897
|
+
* An implementation of the atob function, which should work in all environments.
|
|
1898
|
+
*
|
|
1899
|
+
* @param {string} base64string
|
|
1900
|
+
* @returns {string}
|
|
1901
|
+
*/
|
|
1902
|
+
atob:
|
|
1903
|
+
(base64string) =>
|
|
1904
|
+
{
|
|
1905
|
+
if(typeof atob === 'function')
|
|
1906
|
+
{
|
|
1907
|
+
return atob(base64string);
|
|
1908
|
+
}
|
|
1909
|
+
return Buffer.from(base64string, 'base64').toString();
|
|
1910
|
+
},
|
|
1911
|
+
|
|
1912
|
+
/**
|
|
1913
|
+
* Encodes a UTF-8 string into a base64 string.
|
|
1914
|
+
*
|
|
1915
|
+
* @param {string} string
|
|
1916
|
+
* @returns {string}
|
|
1917
|
+
*/
|
|
1918
|
+
utf8ToBase64:
|
|
1919
|
+
(string) =>
|
|
1920
|
+
{
|
|
1921
|
+
return LeUtils.btoa(encodeURIComponent(string).replace(/%([0-9A-F]{2})/g, (match, p1) => String.fromCharCode(parseInt(p1, 16))));
|
|
1922
|
+
},
|
|
1923
|
+
|
|
1924
|
+
/**
|
|
1925
|
+
* Decodes a base64 string back into a UTF-8 string.
|
|
1926
|
+
*
|
|
1927
|
+
* @param {string} base64string
|
|
1928
|
+
* @returns {string}
|
|
1929
|
+
*/
|
|
1930
|
+
base64ToUtf8:
|
|
1931
|
+
(base64string) =>
|
|
1932
|
+
{
|
|
1933
|
+
return decodeURIComponent(LeUtils.atob(base64string).split('').map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join(''));
|
|
1934
|
+
},
|
|
1935
|
+
|
|
1936
|
+
/**
|
|
1937
|
+
* Converts a base64 string into a hex string.
|
|
1938
|
+
*
|
|
1939
|
+
* @param {string} base64string
|
|
1940
|
+
* @returns {string}
|
|
1941
|
+
*/
|
|
1942
|
+
base64ToHex:
|
|
1943
|
+
(base64string) =>
|
|
1944
|
+
{
|
|
1945
|
+
return LeUtils.atob(base64string).split('').map((c) => ('0' + c.charCodeAt(0).toString(16)).slice(-2)).join('');
|
|
1946
|
+
},
|
|
1947
|
+
|
|
1948
|
+
/**
|
|
1949
|
+
* Converts a hex string into a base64 string.
|
|
1950
|
+
*
|
|
1951
|
+
* @param {string} hexstring
|
|
1952
|
+
* @returns {string}
|
|
1953
|
+
*/
|
|
1954
|
+
hexToBase64:
|
|
1955
|
+
(hexstring) =>
|
|
1956
|
+
{
|
|
1957
|
+
return LeUtils.btoa(hexstring.match(/\w{2}/g).map((a) => String.fromCharCode(parseInt(a, 16))).join(''));
|
|
1958
|
+
},
|
|
1959
|
+
|
|
1960
|
+
/**
|
|
1961
|
+
* Converts a base64 string into bytes (Uint8Array).
|
|
1962
|
+
*
|
|
1963
|
+
* @param {string} base64string
|
|
1964
|
+
* @returns {Uint8Array}
|
|
1965
|
+
*/
|
|
1966
|
+
base64ToBytes:
|
|
1967
|
+
(base64string) =>
|
|
1968
|
+
{
|
|
1969
|
+
const binary = LeUtils.atob(base64string);
|
|
1970
|
+
const len = binary.length;
|
|
1971
|
+
let data = new Uint8Array(len);
|
|
1972
|
+
for(let i = 0; i < len; i++)
|
|
1973
|
+
{
|
|
1974
|
+
data[i] = binary.charCodeAt(i);
|
|
1975
|
+
}
|
|
1976
|
+
return data;
|
|
1977
|
+
},
|
|
1978
|
+
|
|
1979
|
+
/**
|
|
1980
|
+
* Converts bytes into a base64 string.
|
|
1981
|
+
*
|
|
1982
|
+
* @param {ArrayLike<number>|ArrayBufferLike} arraybuffer
|
|
1983
|
+
* @returns {string}
|
|
1984
|
+
*/
|
|
1985
|
+
bytesToBase64:
|
|
1986
|
+
(arraybuffer) =>
|
|
1987
|
+
{
|
|
1988
|
+
const bytes = new Uint8Array(arraybuffer);
|
|
1989
|
+
const len = bytes.byteLength;
|
|
1990
|
+
let binary = '';
|
|
1991
|
+
for(let i = 0; i < len; i++)
|
|
1992
|
+
{
|
|
1993
|
+
binary += String.fromCharCode(bytes[i]);
|
|
1994
|
+
}
|
|
1995
|
+
return LeUtils.btoa(binary);
|
|
1996
|
+
},
|
|
1997
|
+
|
|
1998
|
+
/**
|
|
1999
|
+
* Loads the value from the browser, returns undefined if the value doesn't exist.
|
|
2000
|
+
*
|
|
2001
|
+
* @param {string} id
|
|
2002
|
+
* @returns {*}
|
|
2003
|
+
*/
|
|
2004
|
+
localStorageGet:
|
|
2005
|
+
(id) =>
|
|
2006
|
+
{
|
|
2007
|
+
if(typeof window === 'undefined')
|
|
2008
|
+
{
|
|
2009
|
+
return;
|
|
2010
|
+
}
|
|
2011
|
+
let result = window.localStorage.getItem('LeUtils_' + id);
|
|
2012
|
+
if(typeof result !== 'string')
|
|
2013
|
+
{
|
|
2014
|
+
return;
|
|
2015
|
+
}
|
|
2016
|
+
try
|
|
2017
|
+
{
|
|
2018
|
+
result = JSON.parse(result);
|
|
2019
|
+
if(typeof result['-'] !== 'undefined')
|
|
2020
|
+
{
|
|
2021
|
+
return result['-'];
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
catch(e)
|
|
2025
|
+
{
|
|
2026
|
+
}
|
|
2027
|
+
},
|
|
2028
|
+
|
|
2029
|
+
/**
|
|
2030
|
+
* Saves the given data in the browser.
|
|
2031
|
+
*
|
|
2032
|
+
* @param {string} id
|
|
2033
|
+
* @param {*} data
|
|
2034
|
+
*/
|
|
2035
|
+
localStorageSet:
|
|
2036
|
+
(id, data) =>
|
|
2037
|
+
{
|
|
2038
|
+
if(typeof window === 'undefined')
|
|
2039
|
+
{
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2042
|
+
if(typeof data === 'undefined')
|
|
2043
|
+
{
|
|
2044
|
+
window.localStorage.removeItem('LeUtils_' + id);
|
|
2045
|
+
return;
|
|
2046
|
+
}
|
|
2047
|
+
window.localStorage.setItem('LeUtils_' + id, JSON.stringify({'-':data}));
|
|
2048
|
+
},
|
|
2049
|
+
|
|
2050
|
+
/**
|
|
2051
|
+
* Removes the data from the browser.
|
|
2052
|
+
*
|
|
2053
|
+
* @param {string} id
|
|
2054
|
+
*/
|
|
2055
|
+
localStorageRemove:
|
|
2056
|
+
(id) =>
|
|
2057
|
+
{
|
|
2058
|
+
if(typeof window === 'undefined')
|
|
2059
|
+
{
|
|
2060
|
+
return;
|
|
2061
|
+
}
|
|
2062
|
+
window.localStorage.removeItem('LeUtils_' + id);
|
|
2063
|
+
},
|
|
890
2064
|
};
|