@lowentry/utils 1.14.1 → 1.15.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/src/LeUtils.js CHANGED
@@ -4,7 +4,7 @@ import {ISSET, IS_OBJECT, IS_ARRAY, STRING, INT_LAX, FLOAT_LAX, INT_LAX_ANY, FLO
4
4
 
5
5
 
6
6
  /**
7
- * @param {LeUtils~TransactionalValue} transactionalValue
7
+ * @param {LeUtils_TransactionalValue} transactionalValue
8
8
  */
9
9
  const checkTransactionalValue = (transactionalValue) =>
10
10
  {
@@ -17,7 +17,7 @@ const checkTransactionalValue = (transactionalValue) =>
17
17
  };
18
18
 
19
19
  /**
20
- * @param {LeUtils~TransactionalValue} transactionalValue
20
+ * @param {LeUtils_TransactionalValue} transactionalValue
21
21
  * @param {string} changeId
22
22
  * @returns {{index:number, value:*}|null}
23
23
  */
@@ -75,7 +75,7 @@ export const LeUtils = {
75
75
 
76
76
  if((document.readyState === 'interactive') || (document.readyState === 'complete'))
77
77
  {
78
- return LeUtils.setTimeout(callback, 0);
78
+ return LeUtils.setTimeout(() => callback(), 0);
79
79
  }
80
80
  else
81
81
  {
@@ -435,17 +435,11 @@ export const LeUtils = {
435
435
  return result;
436
436
  },
437
437
 
438
- /**
439
- * @callback LeUtils~__findIndexValueCallback
440
- * @param {*} value
441
- * @param {*} index
442
- * @returns {boolean|undefined}
443
- */
444
438
  /**
445
439
  * Finds the first element in the given array or object that returns true from the callback, and returns an object with the index and value.
446
440
  *
447
441
  * @param {*[]|object|Function} elements
448
- * @param {LeUtils~__findIndexValueCallback} callback
442
+ * @param {(value:*, index:*) => boolean|void} callback
449
443
  * @param {boolean} [optionalSkipHasOwnPropertyCheck]
450
444
  * @returns {{index:*, value:*}|null}
451
445
  */
@@ -460,7 +454,7 @@ export const LeUtils = {
460
454
  result = {index, value};
461
455
  return false;
462
456
  }
463
- });
457
+ }, optionalSkipHasOwnPropertyCheck);
464
458
  return result;
465
459
  },
466
460
 
@@ -468,7 +462,7 @@ export const LeUtils = {
468
462
  * Finds the first element in the given array or object that returns true from the callback, and returns the index.
469
463
  *
470
464
  * @param {*[]|object|Function} elements
471
- * @param {LeUtils~__findIndexValueCallback} callback
465
+ * @param {(value:*, index:*) => boolean|void} callback
472
466
  * @param {boolean} [optionalSkipHasOwnPropertyCheck]
473
467
  * @returns {*|null}
474
468
  */
@@ -479,7 +473,7 @@ export const LeUtils = {
479
473
  * Finds the first element in the given array or object that returns true from the callback, and returns the value.
480
474
  *
481
475
  * @param {*[]|object|Function} elements
482
- * @param {LeUtils~__findIndexValueCallback} callback
476
+ * @param {(value:*, index:*) => boolean|void} callback
483
477
  * @param {boolean} [optionalSkipHasOwnPropertyCheck]
484
478
  * @returns {*|null}
485
479
  */
@@ -487,50 +481,222 @@ export const LeUtils = {
487
481
  (elements, callback, optionalSkipHasOwnPropertyCheck = false) => LeUtils.findIndexValue(elements, callback, optionalSkipHasOwnPropertyCheck)?.value ?? null,
488
482
 
489
483
  /**
490
- * @callback LeUtils~__eachCallback
491
- * @param {*} value
484
+ * Returns the value at the given index in the given elements.
485
+ *
486
+ * @param {*} elements
492
487
  * @param {*} index
493
- * @returns {boolean|undefined}
488
+ * @param {boolean} [optionalSkipHasOwnPropertyCheck]
489
+ * @returns {*}
490
+ */
491
+ getValueAtIndex:
492
+ (elements, index, optionalSkipHasOwnPropertyCheck = false) =>
493
+ {
494
+ if((elements === null) || (typeof elements === 'undefined'))
495
+ {
496
+ return undefined;
497
+ }
498
+ if(Array.isArray(elements))
499
+ {
500
+ return elements[index];
501
+ }
502
+ if((typeof elements === 'object') && (elements?.constructor === Object))
503
+ {
504
+ if((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(elements, index))
505
+ {
506
+ return elements[index];
507
+ }
508
+ return undefined;
509
+ }
510
+ if(elements instanceof Map)
511
+ {
512
+ return elements.get(index);
513
+ }
514
+ if(elements instanceof Set)
515
+ {
516
+ return index;
517
+ }
518
+ if(ArrayBuffer.isView(elements) && !(elements instanceof DataView))
519
+ {
520
+ return elements[index];
521
+ }
522
+ if(typeof elements === 'string')
523
+ {
524
+ return elements.charAt(index);
525
+ }
526
+ if(typeof elements?.[Symbol.iterator] === 'function')
527
+ {
528
+ let i = 0;
529
+ for(const value of elements)
530
+ {
531
+ if(i === index)
532
+ {
533
+ return value;
534
+ }
535
+ i++;
536
+ }
537
+ return undefined;
538
+ }
539
+ if(typeof elements?.forEach === 'function')
540
+ {
541
+ let result = undefined;
542
+ let shouldContinue = true;
543
+ elements.forEach((value, i) =>
544
+ {
545
+ if(shouldContinue)
546
+ {
547
+ if(i === index)
548
+ {
549
+ result = value;
550
+ shouldContinue = false;
551
+ }
552
+ }
553
+ });
554
+ return result;
555
+ }
556
+ if((typeof elements === 'object') || (typeof elements === 'function'))
557
+ {
558
+ if((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(elements, index))
559
+ {
560
+ return elements[index];
561
+ }
562
+ return undefined;
563
+ }
564
+ return undefined;
565
+ },
566
+
567
+ /**
568
+ * Checks if the given elements can be iterated over using LeUtils.each().
569
+ *
570
+ * @param {*} elements
571
+ * @returns {boolean}
494
572
  */
573
+ supportsEach:
574
+ (elements) =>
575
+ {
576
+ if((elements === null) || (typeof elements === 'undefined'))
577
+ {
578
+ return false;
579
+ }
580
+ return !!(
581
+ (Array.isArray(elements))
582
+ || ((typeof elements === 'object') && (elements?.constructor === Object))
583
+ || (typeof elements === 'string')
584
+ || (typeof elements?.[Symbol.iterator] === 'function')
585
+ || (typeof elements?.forEach === 'function')
586
+ || ((typeof elements === 'object') || (typeof elements === 'function'))
587
+ );
588
+ },
589
+
495
590
  /**
496
- * Loops through each element in the given array or object, and calls the callback for each element.
591
+ * Returns an iterator that iterates over each element in the given array or object, yielding an array with the value and the index/key.
497
592
  *
498
- * @param {*[]|object|Function} elements
499
- * @param {LeUtils~__eachCallback} callback
593
+ * @param {*} elements
500
594
  * @param {boolean} [optionalSkipHasOwnPropertyCheck]
501
- * @returns {*[]|object|Function}
595
+ * @returns {Generator<*, void, *>}
502
596
  */
503
- each:
504
- (elements, callback, optionalSkipHasOwnPropertyCheck = false) =>
597
+ eachIterator:
598
+ function* (elements, optionalSkipHasOwnPropertyCheck = false)
505
599
  {
506
- if((elements !== null) && (typeof elements !== 'undefined'))
600
+ if((elements === null) || (typeof elements === 'undefined'))
601
+ {
602
+ return;
603
+ }
604
+ if(Array.isArray(elements))
605
+ {
606
+ for(let i = 0; i < elements.length; i++)
607
+ {
608
+ yield [elements[i], i];
609
+ }
610
+ return;
611
+ }
612
+ if((typeof elements === 'object') && (elements?.constructor === Object))
507
613
  {
508
- if(Array.isArray(elements))
614
+ for(const i in elements)
509
615
  {
510
- for(let index = 0; index < elements.length; index++)
616
+ if((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(elements, i))
511
617
  {
512
- if(callback.call(elements[index], elements[index], index) === false)
513
- {
514
- break;
515
- }
618
+ yield [elements[i], i];
516
619
  }
517
620
  }
518
- else if((typeof elements === 'object') || (typeof elements === 'function'))
621
+ return;
622
+ }
623
+ if(elements instanceof Map)
624
+ {
625
+ for(const [i, value] of elements)
626
+ {
627
+ yield [value, i];
628
+ }
629
+ return;
630
+ }
631
+ if(elements instanceof Set)
632
+ {
633
+ for(const value of elements)
634
+ {
635
+ yield [value, value];
636
+ }
637
+ return;
638
+ }
639
+ if(typeof elements === 'string')
640
+ {
641
+ for(let i = 0; i < elements.length; i++)
642
+ {
643
+ yield [elements.charAt(i), i];
644
+ }
645
+ return;
646
+ }
647
+ if(typeof elements?.[Symbol.iterator] === 'function')
648
+ {
649
+ let i = 0;
650
+ for(const value of elements)
651
+ {
652
+ yield [value, i];
653
+ i++;
654
+ }
655
+ return;
656
+ }
657
+ if(typeof elements?.forEach === 'function')
658
+ {
659
+ const buffer = [];
660
+ elements.forEach((value, i) =>
519
661
  {
520
- for(let index in elements)
662
+ buffer.push([value, i]);
663
+ });
664
+ for(const entry of buffer)
665
+ {
666
+ yield entry;
667
+ }
668
+ return;
669
+ }
670
+ if((typeof elements === 'object') || (typeof elements === 'function'))
671
+ {
672
+ for(const i in elements)
673
+ {
674
+ if((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(elements, i))
521
675
  {
522
- if((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(elements, index))
523
- {
524
- if(callback.call(elements[index], elements[index], index) === false)
525
- {
526
- break;
527
- }
528
- }
676
+ yield [elements[i], i];
529
677
  }
530
678
  }
531
- else
679
+ return;
680
+ }
681
+ console.warn('Executed LeUtils.eachIterator() on an invalid type: [' + (typeof elements) + ']', elements);
682
+ },
683
+
684
+ /**
685
+ * Loops through each element in the given array or object, and calls the callback for each element.
686
+ *
687
+ * @param {*} elements
688
+ * @param {(value:*, index?:*) => boolean|void} callback
689
+ * @param {boolean} [optionalSkipHasOwnPropertyCheck]
690
+ * @returns {*}
691
+ */
692
+ each:
693
+ (elements, callback, optionalSkipHasOwnPropertyCheck = false) =>
694
+ {
695
+ for(const [value, key] of LeUtils.eachIterator(elements, optionalSkipHasOwnPropertyCheck))
696
+ {
697
+ if(callback.call(value, value, key) === false)
532
698
  {
533
- console.warn('Executed LeUtils.each() on an invalid type: [' + (typeof elements) + ']', elements);
699
+ break;
534
700
  }
535
701
  }
536
702
  return elements;
@@ -539,54 +705,55 @@ export const LeUtils = {
539
705
  /**
540
706
  * Like LeUtils.each(), except that it expects an async callback.
541
707
  *
542
- * @param {*[]|object|function} elements
543
- * @param {LeUtils~__eachCallback} asyncCallback
708
+ * @param {*} elements
709
+ * @param {(value:*, index?:*) => Promise<boolean|undefined>} asyncCallback
544
710
  * @param {number} [optionalParallelCount]
545
711
  * @param {boolean} [optionalSkipHasOwnPropertyCheck]
546
- * @returns {*[]|object|function}
712
+ * @returns {Promise<*>}
547
713
  */
548
714
  eachAsync:
549
715
  (() =>
550
716
  {
717
+ /**
718
+ * Instead of waiting for every promise individually, this function will queue up multiple promises at once, then wait for any of them to finish, before adding more, until it has looped through all elements.
719
+ * Then, at the end, it will wait for the remaining promises to finish.
720
+ */
551
721
  const eachAsyncParallel = async (elements, asyncCallback, optionalParallelCount, optionalSkipHasOwnPropertyCheck) =>
552
722
  {
553
- let promises = [];
723
+ const runningPromises = new Set();
554
724
  let doBreak = false;
555
- await LeUtils.eachAsync(elements, async (element, index) =>
725
+ await LeUtils.eachAsync(elements, async (value, index) =>// loop through each element
556
726
  {
557
- while(promises.length > optionalParallelCount)
727
+ if(doBreak)
558
728
  {
559
- let newPromises = [];
560
- LeUtils.each(promises, (promise) =>
561
- {
562
- if(!promise.__lowentry_utils__promise_is_done__)
563
- {
564
- newPromises.push(promise);
565
- }
566
- });
567
- promises = newPromises;
568
- if(promises.length > optionalParallelCount)
569
- {
570
- await Promise.any(promises);
571
- }
729
+ return false;
572
730
  }
573
731
 
574
- if(doBreak)
732
+ // if no spot is available, wait for one to finish
733
+ while(runningPromises.size >= optionalParallelCount)
575
734
  {
576
- return false;
735
+ await Promise.race(runningPromises);
736
+ if(doBreak)
737
+ {
738
+ return false;
739
+ }
577
740
  }
578
741
 
742
+ // process this element, by creating a promise, and adding it to the queue
579
743
  const promise = (async () =>
580
744
  {
581
- if((await asyncCallback.call(element, element, index)) === false)
745
+ if((await asyncCallback.call(value, value, index)) === false)
582
746
  {
583
747
  doBreak = true;
584
748
  }
585
- promise.__lowentry_utils__promise_is_done__ = true;
586
749
  })();
587
- promises.push(promise);
588
- }, optionalSkipHasOwnPropertyCheck);
589
- await Promise.all(promises);
750
+ runningPromises.add(promise);
751
+ promise.finally(() =>
752
+ {
753
+ runningPromises.delete(promise);
754
+ });
755
+ }, 1, optionalSkipHasOwnPropertyCheck);
756
+ await Promise.all(runningPromises);
590
757
  return elements;
591
758
  };
592
759
 
@@ -600,166 +767,163 @@ export const LeUtils = {
600
767
  return await eachAsyncParallel(elements, asyncCallback, parallelCount, optionalSkipHasOwnPropertyCheck);
601
768
  }
602
769
 
603
- if(Array.isArray(elements))
604
- {
605
- for(let index = 0; index < elements.length; index++)
606
- {
607
- if((await asyncCallback.call(elements[index], elements[index], index)) === false)
608
- {
609
- break;
610
- }
611
- }
612
- }
613
- else if((typeof elements === 'object') || (typeof elements === 'function'))
770
+ for(const [value, key] of LeUtils.eachIterator(elements, optionalSkipHasOwnPropertyCheck))
614
771
  {
615
- for(let index in elements)
772
+ if((await asyncCallback.call(value, value, key)) === false)
616
773
  {
617
- if((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(elements, index))
618
- {
619
- if((await asyncCallback.call(elements[index], elements[index], index)) === false)
620
- {
621
- break;
622
- }
623
- }
774
+ break;
624
775
  }
625
776
  }
626
- else
627
- {
628
- console.warn('Executed LeUtils.eachAsync() on an invalid type: [' + (typeof elements) + ']', elements);
629
- }
630
777
  }
631
778
  return elements;
632
779
  };
633
780
  })(),
634
781
 
635
782
  /**
636
- * @callback LeUtils~__filterCallback
637
- * @param {*} value
638
- * @param {*} index
639
- * @returns {boolean|undefined}
783
+ * Returns an empty simplified collection (array, object, or Map), based on the given elements.
784
+ *
785
+ * Usage:
786
+ *
787
+ * ```js
788
+ * const [success, collection, add] = LeUtils.getEmptySimplifiedCollection(elements);
789
+ * ```
790
+ *
791
+ * @param {*} elements
792
+ * @returns {[boolean, *[]|object|Map, (value:*,index:*)=>void]}
640
793
  */
794
+ getEmptySimplifiedCollection:
795
+ (elements) =>
796
+ {
797
+ if((elements === null) || (typeof elements === 'undefined'))
798
+ {
799
+ return [false, [], (value, index) =>
800
+ {
801
+ }];
802
+ }
803
+
804
+ let collection = null;
805
+ let add = null;
806
+ if(Array.isArray(elements))
807
+ {
808
+ collection = [];
809
+ add = (value, index) =>
810
+ {
811
+ collection.push(value);
812
+ };
813
+ }
814
+ else if((typeof elements === 'object') && (elements?.constructor === Object))
815
+ {
816
+ collection = {};
817
+ add = (value, index) =>
818
+ {
819
+ collection[index] = value;
820
+ };
821
+ }
822
+ else if(elements instanceof Map)
823
+ {
824
+ collection = new Map();
825
+ add = (value, index) =>
826
+ {
827
+ collection.set(index, value);
828
+ };
829
+ }
830
+ else if((typeof elements === 'string') || (typeof elements?.[Symbol.iterator] === 'function') || (typeof elements?.forEach === 'function'))
831
+ {
832
+ collection = [];
833
+ add = (value, index) =>
834
+ {
835
+ collection.push(value);
836
+ };
837
+ }
838
+ else if((typeof elements === 'object') || (typeof elements === 'function'))
839
+ {
840
+ collection = {};
841
+ add = (value, index) =>
842
+ {
843
+ collection[index] = value;
844
+ };
845
+ }
846
+ else
847
+ {
848
+ console.warn('Executed LeUtils.getEmptySimplifiedCollection() on an invalid type: [' + (typeof elements) + ']', elements);
849
+ return [false, [], (value, index) =>
850
+ {
851
+ }];
852
+ }
853
+ return [true, collection, add];
854
+ },
855
+
641
856
  /**
642
- * Loops through the given elements, and returns a new array or object, with only the elements that returned true (or a value equals to true) from the callback.
857
+ * Loops through the given elements, and returns a new collection, with only the elements that returned true (or a value equals to true) from the callback.
643
858
  * If no callback is given, it will return all elements that are of a true value (for example, values that are: not null, not undefined, not false, not 0, not an empty string, not an empty array, not an empty object).
644
859
  *
645
- * @param {*[]|object|Function} elements
646
- * @param {LeUtils~__filterCallback} [callback]
860
+ * @param {*} elements
861
+ * @param {(value:*, index:*) => boolean|undefined} [callback]
647
862
  * @param {boolean} [optionalSkipHasOwnPropertyCheck]
648
- * @returns {*[]|object|Function}
863
+ * @returns {*}
649
864
  */
650
865
  filter:
651
866
  (elements, callback, optionalSkipHasOwnPropertyCheck = false) =>
652
867
  {
653
- if((elements !== null) && (typeof elements !== 'undefined'))
868
+ const [success, collection, add] = LeUtils.getEmptySimplifiedCollection(elements);
869
+ if(!success)
654
870
  {
655
- if(Array.isArray(elements))
656
- {
657
- let result = [];
658
- for(let index = 0; index < elements.length; index++)
659
- {
660
- if((!callback && elements[index]) || (callback && callback.call(elements[index], elements[index], index)))
661
- {
662
- result.push(elements[index]);
663
- }
664
- }
665
- return result;
666
- }
667
- else if((typeof elements === 'object') || (typeof elements === 'function'))
871
+ return elements;
872
+ }
873
+
874
+ LeUtils.each(elements, (value, index) =>
875
+ {
876
+ if(!callback)
668
877
  {
669
- let result = {};
670
- for(let index in elements)
878
+ if(value)
671
879
  {
672
- if((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(elements, index))
673
- {
674
- if((!callback && elements[index]) || (callback && callback.call(elements[index], elements[index], index)))
675
- {
676
- result[index] = elements[index];
677
- }
678
- }
880
+ add(value, index);
679
881
  }
680
- return result;
681
882
  }
682
- else
883
+ else if(callback.call(value, value, index))
683
884
  {
684
- console.warn('Executed LeUtils.filter() on an invalid type: [' + (typeof elements) + ']', elements);
885
+ add(value, index);
685
886
  }
686
- }
687
- return elements;
887
+ }, optionalSkipHasOwnPropertyCheck);
888
+ return collection;
688
889
  },
689
890
 
690
891
  /**
691
- * @callback LeUtils~__mapCallback
692
- * @param {*} value
693
- * @param {*} index
694
- * @returns {*}
695
- */
696
- /**
697
- * Loops through the given elements, and returns a new array or object, with the elements that were returned from the callback.
892
+ * Loops through the given elements, and returns a new collection, with the elements that were returned from the callback.
698
893
  *
699
- * @param {*[]|object|Function} elements
700
- * @param {LeUtils~__mapCallback} [callback]
894
+ * @param {*} elements
895
+ * @param {(value:*, index:*) => *} [callback]
701
896
  * @param {boolean} [optionalSkipHasOwnPropertyCheck]
702
- * @returns {*[]|object|Function}
897
+ * @returns {*}
703
898
  */
704
899
  map:
705
900
  (elements, callback, optionalSkipHasOwnPropertyCheck = false) =>
706
901
  {
707
- if((elements !== null) && (typeof elements !== 'undefined'))
902
+ const [success, collection, add] = LeUtils.getEmptySimplifiedCollection(elements);
903
+ if(!success)
708
904
  {
709
- if(Array.isArray(elements))
710
- {
711
- let result = [];
712
- for(let index = 0; index < elements.length; index++)
713
- {
714
- if(!callback)
715
- {
716
- result[index] = elements[index];
717
- }
718
- else
719
- {
720
- result[index] = callback.call(elements[index], elements[index], index);
721
- }
722
- }
723
- return result;
724
- }
725
- else if((typeof elements === 'object') || (typeof elements === 'function'))
905
+ return elements;
906
+ }
907
+
908
+ LeUtils.each(elements, (value, index) =>
909
+ {
910
+ if(!callback)
726
911
  {
727
- let result = {};
728
- for(let index in elements)
729
- {
730
- if((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(elements, index))
731
- {
732
- if(!callback)
733
- {
734
- result[index] = elements[index];
735
- }
736
- else
737
- {
738
- result[index] = callback.call(elements[index], elements[index], index);
739
- }
740
- }
741
- }
742
- return result;
912
+ add(value, index);
743
913
  }
744
914
  else
745
915
  {
746
- console.warn('Executed LeUtils.map() on an invalid type: [' + (typeof elements) + ']', elements);
916
+ add(callback.call(value, value, index), index);
747
917
  }
748
- }
749
- return elements;
918
+ }, optionalSkipHasOwnPropertyCheck);
919
+ return collection;
750
920
  },
751
921
 
752
- /**
753
- * @callback LeUtils~__mapToArrayCallback
754
- * @param {*} value
755
- * @param {*} index
756
- * @returns {*}
757
- */
758
922
  /**
759
923
  * Loops through the given elements, and returns a new array, with the elements that were returned from the callback. Always returns an array.
760
924
  *
761
- * @param {*[]|object|Function} elements
762
- * @param {LeUtils~__mapToArrayCallback} [callback]
925
+ * @param {*} elements
926
+ * @param {(value:*, index:*) => *} [callback]
763
927
  * @param {boolean} [optionalSkipHasOwnPropertyCheck]
764
928
  * @returns {*[]}
765
929
  */
@@ -767,59 +931,26 @@ export const LeUtils = {
767
931
  (elements, callback, optionalSkipHasOwnPropertyCheck = false) =>
768
932
  {
769
933
  let result = [];
770
- if((elements !== null) && (typeof elements !== 'undefined'))
934
+ LeUtils.each(elements, (value, index) =>
771
935
  {
772
- if(Array.isArray(elements))
936
+ if(!callback)
773
937
  {
774
- for(let index = 0; index < elements.length; index++)
775
- {
776
- if(!callback)
777
- {
778
- result.push(elements[index]);
779
- }
780
- else
781
- {
782
- result.push(callback.call(elements[index], elements[index], index));
783
- }
784
- }
785
- }
786
- else if((typeof elements === 'object') || (typeof elements === 'function'))
787
- {
788
- for(let index in elements)
789
- {
790
- if((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(elements, index))
791
- {
792
- if(!callback)
793
- {
794
- result.push(elements[index]);
795
- }
796
- else
797
- {
798
- result.push(callback.call(elements[index], elements[index], index));
799
- }
800
- }
801
- }
938
+ result.push(value);
802
939
  }
803
940
  else
804
941
  {
805
- console.warn('Executed LeUtils.mapToArray() on an invalid type: [' + (typeof elements) + ']', elements);
942
+ result.push(callback.call(value, value, index));
806
943
  }
807
- }
944
+ }, optionalSkipHasOwnPropertyCheck);
808
945
  return result;
809
946
  },
810
947
 
811
- /**
812
- * @callback LeUtils~__mapToArraySortedCallback
813
- * @param {*} value
814
- * @param {*} index
815
- * @returns {*}
816
- */
817
948
  /**
818
949
  * 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.
819
950
  *
820
- * @param {*[]|object|Function} elements
821
- * @param {LeUtils~__sortKeysComparatorCallback} comparator
822
- * @param {LeUtils~__mapToArraySortedCallback} [callback]
951
+ * @param {*} elements
952
+ * @param {(valueA:*, valueB:*) => number} comparator
953
+ * @param {(value:*, index:*) => *} [callback]
823
954
  * @param {boolean} [optionalSkipHasOwnPropertyCheck]
824
955
  * @returns {*[]}
825
956
  */
@@ -828,31 +959,26 @@ export const LeUtils = {
828
959
  {
829
960
  const keys = LeUtils.sortKeys(elements, comparator, optionalSkipHasOwnPropertyCheck);
830
961
  let result = [];
831
- for(let i = 0; i < keys.length; i++)
962
+ for(const key of keys)
832
963
  {
964
+ const value = LeUtils.getValueAtIndex(elements, key, optionalSkipHasOwnPropertyCheck);
833
965
  if(!callback)
834
966
  {
835
- result.push(elements[keys[i]]);
967
+ result.push(value);
836
968
  }
837
969
  else
838
970
  {
839
- result.push(callback.call(elements[keys[i]], elements[keys[i]], keys[i]));
971
+ result.push(callback.call(value, value, key));
840
972
  }
841
973
  }
842
974
  return result;
843
975
  },
844
976
 
845
- /**
846
- * @callback LeUtils~__sortKeysComparatorCallback
847
- * @param {*} elementA
848
- * @param {*} elementB
849
- * @returns {number}
850
- */
851
977
  /**
852
978
  * 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.
853
979
  *
854
- * @param {*[]|object|Function} elements
855
- * @param {LeUtils~__sortKeysComparatorCallback} comparator
980
+ * @param {*} elements
981
+ * @param {(valueA:*, valueB:*) => number} comparator
856
982
  * @param {boolean} [optionalSkipHasOwnPropertyCheck]
857
983
  * @returns {*[]}
858
984
  */
@@ -860,31 +986,12 @@ export const LeUtils = {
860
986
  (elements, comparator, optionalSkipHasOwnPropertyCheck = false) =>
861
987
  {
862
988
  let keys = [];
863
- if((elements !== null) && (typeof elements !== 'undefined'))
989
+ LeUtils.each(elements, (value, index) =>
864
990
  {
865
- if(Array.isArray(elements))
866
- {
867
- for(let index = 0; index < elements.length; index++)
868
- {
869
- keys.push(index);
870
- }
871
- }
872
- else if((typeof elements === 'object') || (typeof elements === 'function'))
873
- {
874
- for(let index in elements)
875
- {
876
- if((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(elements, index))
877
- {
878
- keys.push(index);
879
- }
880
- }
881
- }
882
- else
883
- {
884
- console.warn('Executed LeUtils.sortKeys() on an invalid type: [' + (typeof elements) + ']', elements);
885
- }
886
- }
887
- keys.sort((a, b) => comparator(elements[a], elements[b]));
991
+ keys.push(index);
992
+ }, optionalSkipHasOwnPropertyCheck);
993
+
994
+ keys.sort((a, b) => comparator(LeUtils.getValueAtIndex(elements, a, optionalSkipHasOwnPropertyCheck), LeUtils.getValueAtIndex(elements, b, optionalSkipHasOwnPropertyCheck)));
888
995
  return keys;
889
996
  },
890
997
 
@@ -927,6 +1034,46 @@ export const LeUtils = {
927
1034
  };
928
1035
  })(),
929
1036
 
1037
+ /**
1038
+ * Turns the given value(s) into a 1 dimensional array.
1039
+ *
1040
+ * Compared to LeUtils.flattenArray(), this function also supports objects, Maps, Sets, and other iterable objects.
1041
+ *
1042
+ * @param {*} elements
1043
+ * @param {boolean} [optionalSkipHasOwnPropertyCheck]
1044
+ * @returns {*[]}
1045
+ */
1046
+ flattenToArray:
1047
+ (() =>
1048
+ {
1049
+ const flattenToArrayRecursive = (result, elements, optionalSkipHasOwnPropertyCheck) =>
1050
+ {
1051
+ if(!LeUtils.supportsEach(elements))
1052
+ {
1053
+ result.push(elements);
1054
+ return;
1055
+ }
1056
+ LeUtils.each(elements, entry =>
1057
+ {
1058
+ flattenToArrayRecursive(result, entry, optionalSkipHasOwnPropertyCheck);
1059
+ }, optionalSkipHasOwnPropertyCheck);
1060
+ };
1061
+
1062
+ return (elements, optionalSkipHasOwnPropertyCheck = false) =>
1063
+ {
1064
+ if(!LeUtils.supportsEach(elements))
1065
+ {
1066
+ return [elements];
1067
+ }
1068
+ let result = [];
1069
+ LeUtils.each(elements, entry =>
1070
+ {
1071
+ flattenToArrayRecursive(result, entry, optionalSkipHasOwnPropertyCheck);
1072
+ }, optionalSkipHasOwnPropertyCheck);
1073
+ return result;
1074
+ };
1075
+ })(),
1076
+
930
1077
  /**
931
1078
  * Compares two values. Primarily used for sorting.
932
1079
  *
@@ -957,13 +1104,26 @@ export const LeUtils = {
957
1104
  compareNumericStrings:
958
1105
  (a, b) =>
959
1106
  {
960
- a = STRING(a).trim();
961
- b = STRING(b).trim();
962
- if(a.length === b.length)
1107
+ const aParts = STRING(a).split('.');
1108
+ const bParts = STRING(b).split('.');
1109
+ for(let i = 0; i < Math.min(aParts.length, bParts.length); i++)
1110
+ {
1111
+ a = aParts[i].trim();
1112
+ b = bParts[i].trim();
1113
+ if(a.length !== b.length)
1114
+ {
1115
+ return (a.length < b.length) ? -1 : 1;
1116
+ }
1117
+ if(a !== b)
1118
+ {
1119
+ return (a < b) ? -1 : 1;
1120
+ }
1121
+ }
1122
+ if(aParts.length !== bParts.length)
963
1123
  {
964
- return (a < b) ? -1 : ((a > b) ? 1 : 0);
1124
+ return (aParts.length < bParts.length) ? -1 : 1;
965
1125
  }
966
- return (a.length < b.length) ? -1 : 1;
1126
+ return 0;
967
1127
  },
968
1128
 
969
1129
  /**
@@ -1043,9 +1203,9 @@ export const LeUtils = {
1043
1203
  {
1044
1204
  }.constructor;
1045
1205
 
1046
- const PossibleGeneratorFunctionNames = Array.from(new Set(['GeneratorFunction', 'AsyncFunction', 'AsyncGeneratorFunction', GeneratorFunction.name, GeneratorFunction.displayName, AsyncGeneratorFunction.name, AsyncGeneratorFunction.displayName])).filter((element) =>
1206
+ const PossibleGeneratorFunctionNames = Array.from(new Set(['GeneratorFunction', 'AsyncFunction', 'AsyncGeneratorFunction', GeneratorFunction.name, AsyncGeneratorFunction.name])).filter((element) =>
1047
1207
  {
1048
- return (element && (element !== RegularFunction.name) && (element !== RegularFunction.displayName));
1208
+ return (element && (element !== RegularFunction.name));
1049
1209
  });
1050
1210
 
1051
1211
  return (func) =>
@@ -1059,20 +1219,17 @@ export const LeUtils = {
1059
1219
  {
1060
1220
  return false;
1061
1221
  }
1222
+ // noinspection JSUnresolvedVariable
1062
1223
  return ((constructor.name && PossibleGeneratorFunctionNames.includes(constructor.name)) || (constructor.displayName && PossibleGeneratorFunctionNames.includes(constructor.displayName)));
1063
1224
  };
1064
1225
  })(),
1065
1226
 
1066
- /**
1067
- * @callback LeUtils~__setTimeoutCallback
1068
- * @param {number} deltaTime
1069
- */
1070
1227
  /**
1071
1228
  * Executes the callback after the given number of milliseconds. Passes the elapsed time in seconds to the callback.
1072
1229
  *
1073
1230
  * To cancel the timeout, call remove() on the result of this function (example: "const timeoutHandler = LeUtils.setTimeout((deltaTime)=>{}, 1000); timeoutHandler.remove();")
1074
1231
  *
1075
- * @param {LeUtils~__setTimeoutCallback} callback ([number] deltaTime)
1232
+ * @param {(deltaTime:number) => *} callback
1076
1233
  * @param {number} ms
1077
1234
  * @returns {{remove:Function}}
1078
1235
  */
@@ -1091,6 +1248,7 @@ export const LeUtils = {
1091
1248
  ms = FLOAT_LAX(ms);
1092
1249
 
1093
1250
  let lastTime = performance.now();
1251
+ /** @type {number|null} */
1094
1252
  let handler = window.setTimeout(() =>
1095
1253
  {
1096
1254
  const currentTime = performance.now();
@@ -1118,16 +1276,12 @@ export const LeUtils = {
1118
1276
  };
1119
1277
  },
1120
1278
 
1121
- /**
1122
- * @callback LeUtils~__setIntervalCallback
1123
- * @param {number} deltaTime
1124
- */
1125
1279
  /**
1126
1280
  * Executes the callback every given number of milliseconds. Passes the time difference in seconds between the last frame and now to it.
1127
1281
  *
1128
1282
  * To remove the interval, call remove() on the result of this function (example: "const intervalHandler = LeUtils.setInterval((deltaTime)=>{}, 1000); intervalHandler.remove();")
1129
1283
  *
1130
- * @param {LeUtils~__setIntervalCallback} callback ([number] deltaTime)
1284
+ * @param {(deltaTime:number) => *} callback
1131
1285
  * @param {number} [intervalMs]
1132
1286
  * @param {boolean} [fireImmediately]
1133
1287
  * @returns {{remove:Function}}
@@ -1159,6 +1313,7 @@ export const LeUtils = {
1159
1313
  }
1160
1314
 
1161
1315
  let lastTime = performance.now();
1316
+ /** @type {number|null} */
1162
1317
  let handler = window.setInterval(() =>
1163
1318
  {
1164
1319
  const currentTime = performance.now();
@@ -1186,16 +1341,12 @@ export const LeUtils = {
1186
1341
  };
1187
1342
  },
1188
1343
 
1189
- /**
1190
- * @callback LeUtils~__setAnimationFrameTimeoutCallback
1191
- * @param {number} deltaTime
1192
- */
1193
1344
  /**
1194
1345
  * Executes the callback after the given number of frames. Passes the elapsed time in seconds to the callback.
1195
1346
  *
1196
1347
  * To cancel the timeout, call remove() on the result of this function (example: "const timeoutHandler = LeUtils.setAnimationFrameTimeout((deltaTime){}, 5); timeoutHandler.remove();")
1197
1348
  *
1198
- * @param {LeUtils~__setAnimationFrameTimeoutCallback} callback ([number] deltaTime)
1349
+ * @param {(deltaTime:number) => *} callback
1199
1350
  * @param {number} [frames]
1200
1351
  * @returns {{remove:Function}}
1201
1352
  */
@@ -1256,16 +1407,12 @@ export const LeUtils = {
1256
1407
  };
1257
1408
  },
1258
1409
 
1259
- /**
1260
- * @callback LeUtils~__setAnimationFrameIntervalCallback
1261
- * @param {number} deltaTime
1262
- */
1263
1410
  /**
1264
1411
  * Executes the callback every given number of frames. Passes the time difference in seconds between the last frame and now to it.
1265
1412
  *
1266
1413
  * To remove the interval, call remove() on the result of this function (example: "const intervalHandler = LeUtils.setAnimationFrameInterval((deltaTime)=>{}, 5); intervalHandler.remove();")
1267
1414
  *
1268
- * @param {LeUtils~__setAnimationFrameIntervalCallback} callback ([number] deltaTime)
1415
+ * @param {(deltaTime:number) => *} callback
1269
1416
  * @param {number} [intervalFrames]
1270
1417
  * @param {boolean} [fireImmediately]
1271
1418
  * @returns {{remove:Function}}
@@ -1346,7 +1493,7 @@ export const LeUtils = {
1346
1493
  * Returns a promise, which will be resolved after the given number of milliseconds.
1347
1494
  *
1348
1495
  * @param {number} ms
1349
- * @returns {Promise}
1496
+ * @returns {Promise<number>}
1350
1497
  */
1351
1498
  promiseTimeout:
1352
1499
  (ms) =>
@@ -1354,7 +1501,7 @@ export const LeUtils = {
1354
1501
  ms = FLOAT_LAX(ms);
1355
1502
  if(ms <= 0)
1356
1503
  {
1357
- return new Promise(resolve => resolve(undefined));
1504
+ return new Promise(resolve => resolve(0));
1358
1505
  }
1359
1506
  return new Promise(resolve => LeUtils.setTimeout(resolve, ms));
1360
1507
  },
@@ -1363,7 +1510,7 @@ export const LeUtils = {
1363
1510
  * Returns a promise, which will be resolved after the given number of frames.
1364
1511
  *
1365
1512
  * @param {number} frames
1366
- * @returns {Promise}
1513
+ * @returns {Promise<number>}
1367
1514
  */
1368
1515
  promiseAnimationFrameTimeout:
1369
1516
  (frames) =>
@@ -1371,7 +1518,7 @@ export const LeUtils = {
1371
1518
  frames = INT_LAX(frames);
1372
1519
  if(frames <= 0)
1373
1520
  {
1374
- return new Promise(resolve => resolve(undefined));
1521
+ return new Promise(resolve => resolve(0));
1375
1522
  }
1376
1523
  return new Promise(resolve => LeUtils.setAnimationFrameTimeout(resolve, frames));
1377
1524
  },
@@ -1380,7 +1527,7 @@ export const LeUtils = {
1380
1527
  * Allows you to do a fetch, with built-in retry and abort functionality.
1381
1528
  *
1382
1529
  * @param {string} url
1383
- * @param {{[retries]:number|null, [delay]:number|((attempt:number)=>number)|null}|null} [options]
1530
+ * @param {{retries?:number|null, delay?:number|((attempt:number)=>number)|null}|object|null} [options]
1384
1531
  * @returns {{then:Function, catch:Function, finally:Function, remove:Function, isRemoved:Function}}
1385
1532
  */
1386
1533
  fetch:
@@ -1470,8 +1617,8 @@ export const LeUtils = {
1470
1617
  * Allows you to do a fetch, with built-in retry functionality. Caches on the requested URL, so that the same URL will not be fetched multiple times.
1471
1618
  *
1472
1619
  * @param {string} url
1473
- * @param {{[retries]:number|null, [delay]:number|((attempt:number)=>number)|null, [verify]:((data:*, response:*)=>void)|null}|null} [options]
1474
- * @param {((response:*) => *)|null} [responseFunction] A function that will be called with the response object, and should return the data to be cached.
1620
+ * @param {{retries?:number|null, delay?:number|((attempt:number)=>number)|null, [verify]:((data:*,response:*)=>void)|null}|null} [options]
1621
+ * @param {((response:*)=>*)|null} [responseFunction] A function that will be called with the response object, and should return the data to be cached.
1475
1622
  * @returns {Promise<*>}
1476
1623
  */
1477
1624
  cachedFetch:
@@ -1794,7 +1941,7 @@ export const LeUtils = {
1794
1941
  * - foo-bar
1795
1942
  * - foo_bar
1796
1943
  *
1797
- * @param {string} names
1944
+ * @param {string[]} names
1798
1945
  * @returns {string[]}
1799
1946
  */
1800
1947
  generateNamePermutations:
@@ -1852,7 +1999,7 @@ export const LeUtils = {
1852
1999
  }
1853
2000
  if(c < '9')
1854
2001
  {
1855
- c++;
2002
+ c = String.fromCharCode(c.charCodeAt(0) + 1);
1856
2003
  string = string.substring(0, i) + c + string.substring(i + 1);// string[i] = (char + 1);
1857
2004
  break;
1858
2005
  }
@@ -1994,7 +2141,7 @@ export const LeUtils = {
1994
2141
  return bytes;
1995
2142
  };
1996
2143
 
1997
- return (now = null) =>
2144
+ return (/** @type {number|null|undefined} */ now = null) =>
1998
2145
  {
1999
2146
  if(ISSET(now))
2000
2147
  {
@@ -2051,7 +2198,7 @@ export const LeUtils = {
2051
2198
  {
2052
2199
  part = FLOAT_LAX(part);
2053
2200
  total = FLOAT_LAX(total);
2054
- if(total <= 0)
2201
+ if(total === 0)
2055
2202
  {
2056
2203
  return 100;
2057
2204
  }
@@ -2078,22 +2225,18 @@ export const LeUtils = {
2078
2225
  const ctx = canvas.getContext('2d');
2079
2226
  const width = Math.floor(image.width);
2080
2227
  const height = Math.floor(image.height);
2081
- if((width <= 0) || (height <= 0))
2228
+ if(!ctx || (width <= 0) || (height <= 0))
2082
2229
  {
2083
- canvas.width = 1;
2084
- canvas.height = 1;
2085
- }
2086
- else
2087
- {
2088
- canvas.width = width;
2089
- canvas.height = height;
2090
- ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
2230
+ return new Uint8ClampedArray();
2091
2231
  }
2232
+ canvas.width = width;
2233
+ canvas.height = height;
2234
+ ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
2092
2235
  return ctx.getImageData(0, 0, canvas.width, canvas.height).data;
2093
2236
  }
2094
2237
  finally
2095
2238
  {
2096
- canvas.parentNode.removeChild(canvas);
2239
+ canvas?.parentNode?.removeChild(canvas);
2097
2240
  }
2098
2241
  },
2099
2242
 
@@ -2118,17 +2261,13 @@ export const LeUtils = {
2118
2261
  const ctx = canvas.getContext('2d');
2119
2262
  const width = Math.floor(image.width);
2120
2263
  const height = Math.floor(image.height);
2121
- if((width <= 0) || (height <= 0))
2122
- {
2123
- canvas.width = 1;
2124
- canvas.height = 1;
2125
- }
2126
- else
2264
+ if(!ctx || (width <= 0) || (height <= 0))
2127
2265
  {
2128
- canvas.width = width;
2129
- canvas.height = height;
2130
- ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
2266
+ return LeUtils.getEmptyImageSrc();
2131
2267
  }
2268
+ canvas.width = width;
2269
+ canvas.height = height;
2270
+ ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
2132
2271
  ctx.globalCompositeOperation = 'source-in';
2133
2272
  ctx.fillStyle = color;
2134
2273
  ctx.fillRect(0, 0, canvas.width, canvas.height);
@@ -2136,7 +2275,7 @@ export const LeUtils = {
2136
2275
  }
2137
2276
  finally
2138
2277
  {
2139
- canvas.parentNode.removeChild(canvas);
2278
+ canvas?.parentNode?.removeChild(canvas);
2140
2279
  }
2141
2280
  },
2142
2281
 
@@ -2165,13 +2304,18 @@ export const LeUtils = {
2165
2304
  hexToRgb:
2166
2305
  (hexstring) =>
2167
2306
  {
2307
+ const initialHexstring = hexstring;
2168
2308
  hexstring = hexstring.replace(/[^0-9A-F]/gi, '');
2169
2309
  const hasAlpha = ((hexstring.length === 4) || (hexstring.length === 8));
2170
2310
  while(hexstring.length < 6)
2171
2311
  {
2172
2312
  hexstring = hexstring.replace(/(.)/g, '$1$1');
2173
2313
  }
2174
- const result = hexstring.match(/\w{2}/g).map((a) => parseInt(a, 16));
2314
+ const result = hexstring.match(/\w{2}/g)?.map((a) => parseInt(a, 16));
2315
+ if(!result || (result.length < 3))
2316
+ {
2317
+ throw new Error('Invalid hex color: "' + hexstring + '" (was given "' + initialHexstring + '")');
2318
+ }
2175
2319
  return [
2176
2320
  result[0],
2177
2321
  result[1],
@@ -2194,12 +2338,10 @@ export const LeUtils = {
2194
2338
  const b = rgb[2] / 255;
2195
2339
  const max = Math.max(r, g, b);
2196
2340
  const min = Math.min(r, g, b);
2197
- let h, s, l = (max + min) / 2;
2198
- if(max === min)
2199
- {
2200
- h = s = 0;
2201
- }
2202
- else
2341
+ let h = 0;
2342
+ let s = 0;
2343
+ let l = (max + min) / 2;
2344
+ if(max !== min)
2203
2345
  {
2204
2346
  const d = max - min;
2205
2347
  s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
@@ -2258,12 +2400,10 @@ export const LeUtils = {
2258
2400
  const h = hsl[0];
2259
2401
  const s = hsl[1];
2260
2402
  const l = hsl[2];
2261
- let r, g, b;
2262
- if(s === 0)
2263
- {
2264
- r = g = b = l;
2265
- }
2266
- else
2403
+ let r = 1;
2404
+ let g = 1;
2405
+ let b = 1;
2406
+ if(s !== 0)
2267
2407
  {
2268
2408
  const q = (l < 0.5) ? (l * (1 + s)) : (l + s - (l * s));
2269
2409
  const p = (2 * l) - q;
@@ -2304,13 +2444,13 @@ export const LeUtils = {
2304
2444
  *
2305
2445
  * Returns a number:
2306
2446
  *
2307
- * <pre>
2308
- * < 1.0 is not perceptible by human eyes
2447
+ * ```js
2448
+ * < 1 is not perceptible by human eyes
2309
2449
  * 1-2 is perceptible through close observation
2310
2450
  * 2-10 is perceptible at a glance
2311
2451
  * 11-49 is more similar than opposite
2312
2452
  * 100 is exactly the opposite color
2313
- * </pre>
2453
+ * ```
2314
2454
  *
2315
2455
  * @param {number[]} rgbA
2316
2456
  * @param {number[]} rgbB
@@ -2329,13 +2469,13 @@ export const LeUtils = {
2329
2469
  *
2330
2470
  * Returns a number:
2331
2471
  *
2332
- * <pre>
2333
- * < 1.0 is not perceptible by human eyes
2472
+ * ```js
2473
+ * < 1 is not perceptible by human eyes
2334
2474
  * 1-2 is perceptible through close observation
2335
2475
  * 2-10 is perceptible at a glance
2336
2476
  * 11-49 is more similar than opposite
2337
2477
  * 100 is exactly the opposite color
2338
- * </pre>
2478
+ * ```
2339
2479
  *
2340
2480
  * @param {number[]} labA
2341
2481
  * @param {number[]} labB
@@ -2367,16 +2507,16 @@ export const LeUtils = {
2367
2507
  *
2368
2508
  * Usage:
2369
2509
  *
2370
- * <pre>
2510
+ * ```js
2371
2511
  * LeUtils.getRgbOfGradient({
2372
2512
  * 0: [255, 0, 0],
2373
2513
  * 33: [255, 255, 0],
2374
2514
  * 66: [0, 255, 0],
2375
2515
  * 100:[0, 255, 255],
2376
2516
  * }, 45.1234);
2377
- * </pre>
2517
+ * ```
2378
2518
  *
2379
- * @param {{[percentage]: number[]}} gradient
2519
+ * @param {{percentage?:number[]}} gradient
2380
2520
  * @param {number} percentage
2381
2521
  * @returns {number[]}
2382
2522
  */
@@ -2404,12 +2544,14 @@ export const LeUtils = {
2404
2544
  });
2405
2545
  if(closest === null)
2406
2546
  {
2407
- return null;
2547
+ return [0, 0, 0];
2408
2548
  }
2409
2549
  closest = closest[0];
2410
2550
 
2411
- let higher = 99999;
2412
- let lower = -99999;
2551
+ const HIGHER = 99999;
2552
+ const LOWER = -99999;
2553
+ let higher = HIGHER;
2554
+ let lower = LOWER;
2413
2555
  LeUtils.each(gradient, (color, percent) =>
2414
2556
  {
2415
2557
  percent = INT_LAX(percent);
@@ -2428,20 +2570,12 @@ export const LeUtils = {
2428
2570
  }
2429
2571
  }
2430
2572
  });
2431
- if(higher === 99999)
2432
- {
2433
- higher = null;
2434
- }
2435
- if(lower === -99999)
2436
- {
2437
- lower = null;
2438
- }
2439
2573
 
2440
- if(((higher === null) && (lower === null)) || (higher === lower))
2574
+ if(((higher === HIGHER) && (lower === LOWER)) || (higher === lower))
2441
2575
  {
2442
2576
  return gradient[closest];
2443
2577
  }
2444
- else if((higher !== null) && (lower !== null))
2578
+ else if((higher !== HIGHER) && (lower !== LOWER))
2445
2579
  {
2446
2580
  const higherDifference = Math.abs(higher - percentage);
2447
2581
  const lowerDifference = Math.abs(percentage - lower);
@@ -2454,7 +2588,7 @@ export const LeUtils = {
2454
2588
  lower = closest;
2455
2589
  }
2456
2590
  }
2457
- else if(lower === null)
2591
+ else if(lower === LOWER)
2458
2592
  {
2459
2593
  lower = closest;
2460
2594
  }
@@ -2575,7 +2709,12 @@ export const LeUtils = {
2575
2709
  hexToBase64:
2576
2710
  (hexstring) =>
2577
2711
  {
2578
- return LeUtils.btoa(hexstring.replace(/[^0-9A-F]/gi, '').match(/\w{2}/g).map((a) => String.fromCharCode(parseInt(a, 16))).join(''));
2712
+ const hexResult = hexstring.replace(/[^0-9A-F]/gi, '').match(/\w{2}/g)?.map((a) => String.fromCharCode(parseInt(a, 16)))?.join('');
2713
+ if(!hexResult)
2714
+ {
2715
+ throw new Error('Invalid hex string: "' + hexstring + '"');
2716
+ }
2717
+ return LeUtils.btoa(hexResult);
2579
2718
  },
2580
2719
 
2581
2720
  /**
@@ -2600,7 +2739,7 @@ export const LeUtils = {
2600
2739
  /**
2601
2740
  * Converts bytes into a base64 string.
2602
2741
  *
2603
- * @param {ArrayLike<number>|ArrayBufferLike} arraybuffer
2742
+ * @param {ArrayLike<number>|ArrayBuffer} arraybuffer
2604
2743
  * @returns {string}
2605
2744
  */
2606
2745
  bytesToBase64:
@@ -2657,11 +2796,7 @@ export const LeUtils = {
2657
2796
  }
2658
2797
  try
2659
2798
  {
2660
- result = JSON.parse(result);
2661
- if(typeof result['-'] !== 'undefined')
2662
- {
2663
- return result['-'];
2664
- }
2799
+ return JSON.parse(result)?.['-'];
2665
2800
  }
2666
2801
  catch(e)
2667
2802
  {
@@ -2770,8 +2905,8 @@ export const LeUtils = {
2770
2905
  * This way, you can have values that aren't the same be treated as if they are. This can be used to deal with issues such as floating point errors for example.
2771
2906
  *
2772
2907
  * @param {*[]} elements
2773
- * @param {Function} comparator
2774
- * @returns {{getElements: (function(): *[]), getComparator: (function(): Function), size: (function(): number), isEmpty: (function(): boolean), contains: (function(*): boolean), first: (function(): *|undefined), last: (function(): *|undefined), pollFirst: (function(): *|undefined), pollLast: (function(): *|undefined), add: function(*), addAll: function(*[]|object), getEqualValue: (function(*): (*)), getEqualValueOrAdd: (function(*): (*))}}
2908
+ * @param {(valueA:*, valueB:*) => number} comparator
2909
+ * @returns {{getElements:(()=>*[]), getComparator:(()=>((valueA:*,valueB:*)=>number)), size:(()=>number), isEmpty:(()=>boolean), contains:((value:*)=>boolean), first:(()=>*|undefined), last:(()=>*|undefined), pollFirst:(()=>*|undefined), pollLast:(()=>*|undefined), add:((value:*)=>void), addAll:((values:*)=>void), getEqualValue:((value:*)=>*), getEqualValueOrAdd:((value:*)=>*)}}
2775
2910
  */
2776
2911
  createTreeSet:
2777
2912
  (elements, comparator) =>
@@ -2823,7 +2958,7 @@ export const LeUtils = {
2823
2958
  /**
2824
2959
  * Returns the comparator of the set.
2825
2960
  *
2826
- * @returns {Function}
2961
+ * @returns {(valueA:*, valueB:*) => number}
2827
2962
  */
2828
2963
  getComparator:
2829
2964
  () => comparator,
@@ -2904,7 +3039,7 @@ export const LeUtils = {
2904
3039
  /**
2905
3040
  * Adds all the given values to the set. Will only do so if no equal value already exists.
2906
3041
  *
2907
- * @param {*[]|object} values
3042
+ * @param {*} values
2908
3043
  */
2909
3044
  addAll:
2910
3045
  (values) =>
@@ -2951,7 +3086,7 @@ export const LeUtils = {
2951
3086
  },
2952
3087
 
2953
3088
  /**
2954
- * @typedef {Object} LeUtils~TransactionalValue
3089
+ * @typedef {Object} LeUtils_TransactionalValue
2955
3090
  * @property {*} value
2956
3091
  * @property {{id:string, value:*}[]} changes
2957
3092
  */
@@ -2964,7 +3099,7 @@ export const LeUtils = {
2964
3099
  * This allows you to make multiple unconfirmed changes, and confirm or cancel each of them individually at any time.
2965
3100
  *
2966
3101
  * @param {*} [value]
2967
- * @returns {LeUtils~TransactionalValue}
3102
+ * @returns {LeUtils_TransactionalValue}
2968
3103
  */
2969
3104
  createTransactionalValue:
2970
3105
  (value) =>
@@ -2979,7 +3114,7 @@ export const LeUtils = {
2979
3114
  /**
2980
3115
  * Returns true if the given value is a valid TransactionalValue, returns false if it isn't.
2981
3116
  *
2982
- * @param {LeUtils~TransactionalValue} transactionalValue
3117
+ * @param {LeUtils_TransactionalValue} transactionalValue
2983
3118
  * @returns {boolean}
2984
3119
  */
2985
3120
  isTransactionalValueValid:
@@ -2991,7 +3126,7 @@ export const LeUtils = {
2991
3126
  /**
2992
3127
  * Returns true if the given value is a TransactionalValue, false otherwise.
2993
3128
  *
2994
- * @param {LeUtils~TransactionalValue} transactionalValue
3129
+ * @param {LeUtils_TransactionalValue} transactionalValue
2995
3130
  * @returns {string}
2996
3131
  */
2997
3132
  transactionalValueToString:
@@ -3016,7 +3151,7 @@ export const LeUtils = {
3016
3151
  /**
3017
3152
  * Sets the committed value of the given TransactionalValue to the given value. Clears out the previously uncommitted changes.
3018
3153
  *
3019
- * @param {LeUtils~TransactionalValue} transactionalValue
3154
+ * @param {LeUtils_TransactionalValue} transactionalValue
3020
3155
  * @param {*} value
3021
3156
  */
3022
3157
  transactionSetAndCommit:
@@ -3035,7 +3170,7 @@ export const LeUtils = {
3035
3170
  * Sets the value of the given TransactionalValue to the given value, without yet committing it, meaning it can be committed or cancelled later.
3036
3171
  * It returns the ID of the change, which can be used to commit or cancel the change later.
3037
3172
  *
3038
- * @param {LeUtils~TransactionalValue} transactionalValue
3173
+ * @param {LeUtils_TransactionalValue} transactionalValue
3039
3174
  * @param {*} value
3040
3175
  * @returns {string}
3041
3176
  */
@@ -3056,7 +3191,7 @@ export const LeUtils = {
3056
3191
  * Commits the change with the given ID, making it the new committed value.
3057
3192
  * Returns true if the change was found and committed, returns false if it was already overwritten by a newer committed change.
3058
3193
  *
3059
- * @param {LeUtils~TransactionalValue} transactionalValue
3194
+ * @param {LeUtils_TransactionalValue} transactionalValue
3060
3195
  * @param {string} changeId
3061
3196
  * @returns {boolean}
3062
3197
  */
@@ -3078,7 +3213,7 @@ export const LeUtils = {
3078
3213
  * Cancels the change with the given ID, removing it from the uncommitted changes.
3079
3214
  * Returns true if the change was found and removed, returns false if it was already overwritten by a newer committed change.
3080
3215
  *
3081
- * @param {LeUtils~TransactionalValue} transactionalValue
3216
+ * @param {LeUtils_TransactionalValue} transactionalValue
3082
3217
  * @param {string} changeId
3083
3218
  * @returns {boolean}
3084
3219
  */
@@ -3099,7 +3234,7 @@ export const LeUtils = {
3099
3234
  * Returns true if the change was found, meaning it can still make a difference to the final committed value of this TransactionalValue.
3100
3235
  * Returns false if it was already overwritten by a newer committed change, meaning that this change can no longer make a difference to the final committed value of this TransactionalValue.
3101
3236
  *
3102
- * @param {LeUtils~TransactionalValue} transactionalValue
3237
+ * @param {LeUtils_TransactionalValue} transactionalValue
3103
3238
  * @param {string} changeId
3104
3239
  * @returns {boolean}
3105
3240
  */
@@ -3113,7 +3248,7 @@ export const LeUtils = {
3113
3248
  /**
3114
3249
  * Returns the committed value of the given TransactionalValue.
3115
3250
  *
3116
- * @param {LeUtils~TransactionalValue} transactionalValue
3251
+ * @param {LeUtils_TransactionalValue} transactionalValue
3117
3252
  * @returns {*}
3118
3253
  */
3119
3254
  transactionGetCommittedValue:
@@ -3126,7 +3261,7 @@ export const LeUtils = {
3126
3261
  /**
3127
3262
  * Returns the value (including any uncommitted changes made to it) of the given TransactionalValue.
3128
3263
  *
3129
- * @param {LeUtils~TransactionalValue} transactionalValue
3264
+ * @param {LeUtils_TransactionalValue} transactionalValue
3130
3265
  * @returns {*}
3131
3266
  */
3132
3267
  transactionGetValue:
@@ -3192,7 +3327,7 @@ export const LeUtils = {
3192
3327
  * ```
3193
3328
  *
3194
3329
  * @param {string} name
3195
- * @returns {{worker: Worker, sendMessage: function(Object, {timeout: number|undefined}|undefined): Promise<Object>}}
3330
+ * @returns {{worker:Worker|null, sendMessage:(data:Object,options:{timeout:number|undefined}|undefined)=>Promise<Object>}}
3196
3331
  */
3197
3332
  createWorkerThread:
3198
3333
  (name) =>
@@ -3201,7 +3336,7 @@ export const LeUtils = {
3201
3336
  {
3202
3337
  return {
3203
3338
  worker: null,
3204
- sendMessage:new Promise((resolve, reject) =>
3339
+ sendMessage:(data, options) => new Promise((resolve, reject) =>
3205
3340
  {
3206
3341
  reject('Workers are not supported in this environment');
3207
3342
  }),
@@ -3268,7 +3403,7 @@ export const LeUtils = {
3268
3403
  *
3269
3404
  * @param {string} workerName
3270
3405
  * @param {Object} data
3271
- * @param {{timeout: number|undefined}} [options]
3406
+ * @param {{timeout:number|undefined}} [options]
3272
3407
  * @returns {Promise<Object>}
3273
3408
  */
3274
3409
  sendWorkerMessage:
@@ -3314,7 +3449,7 @@ export const LeUtils = {
3314
3449
  return () => true;
3315
3450
  }
3316
3451
  const inputTypes = ['text', 'search', 'email', 'number', 'password', 'tel', 'time', 'url', 'week', 'month', 'date', 'datetime-local'];
3317
- return () => !((document?.activeElement?.tagName?.toLowerCase() === 'input') && inputTypes.includes(document?.activeElement?.type?.toLowerCase()));
3452
+ return () => !((document?.activeElement?.tagName?.toLowerCase() === 'input') && inputTypes.includes(document?.activeElement?.getAttribute('type')?.toLowerCase() ?? ''));
3318
3453
  })(),
3319
3454
 
3320
3455
  /**