@optionfactory/fml 9.0.0-rc3 → 9.0.0-rc31
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/README.md +16 -88
- package/dist/client-errors.iife.js +40 -49
- package/dist/client-errors.iife.js.map +1 -1
- package/dist/client-errors.iife.min.js +1 -1
- package/dist/client-errors.iife.min.js.map +1 -1
- package/dist/custom-elements.json +491 -51
- package/dist/fml.css +25 -20
- package/dist/fml.css.map +1 -1
- package/dist/fml.iife.js +5866 -2563
- package/dist/fml.iife.js.map +1 -1
- package/dist/fml.iife.min.js +1 -1
- package/dist/fml.iife.min.js.map +1 -1
- package/dist/ftl.d.mts +425 -231
- package/dist/ftl.iife.js +774 -1044
- package/dist/ftl.iife.js.map +1 -1
- package/dist/ftl.iife.min.js +1 -1
- package/dist/ftl.iife.min.js.map +1 -1
- package/dist/ftl.min.mjs +1 -1
- package/dist/ftl.min.mjs.map +1 -1
- package/dist/ftl.mjs +774 -1044
- package/dist/ftl.mjs.map +1 -1
- package/dist/ful.css +25 -20
- package/dist/ful.css.map +1 -1
- package/dist/ful.d.mts +3048 -556
- package/dist/ful.iife.js +4977 -1395
- package/dist/ful.iife.js.map +1 -1
- package/dist/ful.iife.min.js +1 -1
- package/dist/ful.iife.min.js.map +1 -1
- package/dist/ful.min.mjs +1 -1
- package/dist/ful.min.mjs.map +1 -1
- package/dist/ful.mjs +4975 -1396
- package/dist/ful.mjs.map +1 -1
- package/dist/httpc.d.mts +143 -123
- package/dist/httpc.iife.js +163 -175
- package/dist/httpc.iife.js.map +1 -1
- package/dist/httpc.iife.min.js +1 -1
- package/dist/httpc.iife.min.js.map +1 -1
- package/dist/httpc.min.mjs +1 -1
- package/dist/httpc.min.mjs.map +1 -1
- package/dist/httpc.mjs +163 -175
- package/dist/httpc.mjs.map +1 -1
- package/dist/vscode.html-custom-data.json +239 -39
- package/dist/web-types.json +498 -51
- package/package.json +2 -2
package/dist/ftl.mjs
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A Map bounded by entry count, evicting
|
|
3
|
-
*
|
|
4
|
-
* expressions, compiled masks, formatter instances) from growing forever. The
|
|
5
|
-
* capacity is sized so eviction never happens on a sane page: hitting it means
|
|
6
|
-
* dynamically generated keys, where FIFO's worst case (evicting a hot entry)
|
|
7
|
-
* costs one recomputation.
|
|
2
|
+
* A Map bounded by entry count, evicting the oldest entry when full. A hit does
|
|
3
|
+
* not refresh an entry: eviction follows insertion order, not use.
|
|
8
4
|
*/
|
|
9
5
|
class BoundedCache {
|
|
10
6
|
#max;
|
|
@@ -33,6 +29,7 @@ class BoundedCache {
|
|
|
33
29
|
this.#entries.set(key, value);
|
|
34
30
|
return value;
|
|
35
31
|
}
|
|
32
|
+
/** @returns {number} the number of entries held */
|
|
36
33
|
get size() {
|
|
37
34
|
return this.#entries.size;
|
|
38
35
|
}
|
|
@@ -71,14 +68,10 @@ const nodes = {
|
|
|
71
68
|
},
|
|
72
69
|
};
|
|
73
70
|
|
|
74
|
-
// @generated by Peggy 5.1.
|
|
71
|
+
// @generated by Peggy 5.1.1.
|
|
75
72
|
//
|
|
76
73
|
// https://peggyjs.org/
|
|
77
74
|
|
|
78
|
-
//keyword classification lives here, not in the grammar's ordered choice: the
|
|
79
|
-
//identifier rule consumes the whole word and the action decides, so no keyword
|
|
80
|
-
//can grab a longer identifier's prefix. A Map, not a plain object: `in` on the
|
|
81
|
-
//latter would classify inherited names such as `constructor` as keywords
|
|
82
75
|
const keywords = new Map([
|
|
83
76
|
['true', true],
|
|
84
77
|
['false', false],
|
|
@@ -141,10 +134,9 @@ class peg$SyntaxError extends SyntaxError {
|
|
|
141
134
|
return s;
|
|
142
135
|
}
|
|
143
136
|
|
|
144
|
-
function
|
|
137
|
+
function escape(s, special) {
|
|
145
138
|
return unicodeEscape(s
|
|
146
|
-
.replace(
|
|
147
|
-
.replace(/"/g, "\\\"")
|
|
139
|
+
.replace(special, "\\$&")
|
|
148
140
|
.replace(/\0/g, "\\0")
|
|
149
141
|
.replace(/\t/g, "\\t")
|
|
150
142
|
.replace(/\n/g, "\\n")
|
|
@@ -153,18 +145,12 @@ class peg$SyntaxError extends SyntaxError {
|
|
|
153
145
|
.replace(/[\x10-\x1F\x7F-\x9F]/g, ch => "\\x" + hex(ch)));
|
|
154
146
|
}
|
|
155
147
|
|
|
148
|
+
function literalEscape(s) {
|
|
149
|
+
return escape(s, /[\\"]/g);
|
|
150
|
+
}
|
|
151
|
+
|
|
156
152
|
function classEscape(s) {
|
|
157
|
-
return
|
|
158
|
-
.replace(/\\/g, "\\\\")
|
|
159
|
-
.replace(/\]/g, "\\]")
|
|
160
|
-
.replace(/\^/g, "\\^")
|
|
161
|
-
.replace(/-/g, "\\-")
|
|
162
|
-
.replace(/\0/g, "\\0")
|
|
163
|
-
.replace(/\t/g, "\\t")
|
|
164
|
-
.replace(/\n/g, "\\n")
|
|
165
|
-
.replace(/\r/g, "\\r")
|
|
166
|
-
.replace(/[\x00-\x0F]/g, ch => "\\x0" + hex(ch))
|
|
167
|
-
.replace(/[\x10-\x1F\x7F-\x9F]/g, ch => "\\x" + hex(ch)));
|
|
153
|
+
return escape(s, /[\\\]^-]/g);
|
|
168
154
|
}
|
|
169
155
|
|
|
170
156
|
const DESCRIBE_EXPECTATION_FNS = {
|
|
@@ -282,14 +268,6 @@ function peg$parse(input, options) {
|
|
|
282
268
|
const peg$c53 = "<=";
|
|
283
269
|
const peg$c54 = "<";
|
|
284
270
|
|
|
285
|
-
const peg$r0 = /^[^\\{]/;
|
|
286
|
-
const peg$r1 = /^[0-9]/;
|
|
287
|
-
const peg$r2 = /^[^']/;
|
|
288
|
-
const peg$r3 = /^[^"]/;
|
|
289
|
-
const peg$r4 = /^[^\\`{]/;
|
|
290
|
-
const peg$r5 = /^[a-zA-Z$_]/;
|
|
291
|
-
const peg$r6 = /^[a-zA-Z$_0-9]/;
|
|
292
|
-
const peg$r7 = /^[ \t\n\r]/;
|
|
293
271
|
|
|
294
272
|
const peg$e0 = peg$literalExpectation("{{{{", false);
|
|
295
273
|
const peg$e1 = peg$literalExpectation("}}}}", false);
|
|
@@ -447,9 +425,10 @@ function peg$parse(input, options) {
|
|
|
447
425
|
let peg$maxFailExpected = options.peg$maxFailExpected || [];
|
|
448
426
|
let peg$silentFails = options.peg$silentFails | 0;
|
|
449
427
|
|
|
450
|
-
let peg$resultsCache =
|
|
428
|
+
let peg$resultsCache = new Map();
|
|
451
429
|
|
|
452
430
|
let peg$result;
|
|
431
|
+
let peg$charCode;
|
|
453
432
|
|
|
454
433
|
if (options.startRule) {
|
|
455
434
|
if (!(options.startRule in peg$startRuleFunctions)) {
|
|
@@ -568,7 +547,7 @@ function peg$parse(input, options) {
|
|
|
568
547
|
let s0, s1;
|
|
569
548
|
|
|
570
549
|
const key = peg$currPos * 38 + 0;
|
|
571
|
-
const cached = peg$resultsCache
|
|
550
|
+
const cached = peg$resultsCache.get(key);
|
|
572
551
|
|
|
573
552
|
if (cached) {
|
|
574
553
|
peg$currPos = cached.nextPos;
|
|
@@ -577,13 +556,13 @@ function peg$parse(input, options) {
|
|
|
577
556
|
}
|
|
578
557
|
|
|
579
558
|
s0 = [];
|
|
580
|
-
|
|
581
|
-
while (s1 !== peg$FAILED) {
|
|
582
|
-
s0.push(s1);
|
|
559
|
+
for (;;) {
|
|
583
560
|
s1 = peg$parseTemplatedExpression();
|
|
561
|
+
if (s1 === peg$FAILED) { break; }
|
|
562
|
+
s0.push(s1);
|
|
584
563
|
}
|
|
585
564
|
|
|
586
|
-
peg$resultsCache
|
|
565
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
587
566
|
|
|
588
567
|
return s0;
|
|
589
568
|
}
|
|
@@ -592,7 +571,7 @@ function peg$parse(input, options) {
|
|
|
592
571
|
let s0, s1, s2, s3, s4;
|
|
593
572
|
|
|
594
573
|
const key = peg$currPos * 38 + 1;
|
|
595
|
-
const cached = peg$resultsCache
|
|
574
|
+
const cached = peg$resultsCache.get(key);
|
|
596
575
|
|
|
597
576
|
if (cached) {
|
|
598
577
|
peg$currPos = cached.nextPos;
|
|
@@ -688,7 +667,7 @@ function peg$parse(input, options) {
|
|
|
688
667
|
}
|
|
689
668
|
}
|
|
690
669
|
|
|
691
|
-
peg$resultsCache
|
|
670
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
692
671
|
|
|
693
672
|
return s0;
|
|
694
673
|
}
|
|
@@ -697,7 +676,7 @@ function peg$parse(input, options) {
|
|
|
697
676
|
let s0;
|
|
698
677
|
|
|
699
678
|
const key = peg$currPos * 38 + 2;
|
|
700
|
-
const cached = peg$resultsCache
|
|
679
|
+
const cached = peg$resultsCache.get(key);
|
|
701
680
|
|
|
702
681
|
if (cached) {
|
|
703
682
|
peg$currPos = cached.nextPos;
|
|
@@ -713,7 +692,7 @@ function peg$parse(input, options) {
|
|
|
713
692
|
if (peg$silentFails === 0) { peg$fail(peg$e0); }
|
|
714
693
|
}
|
|
715
694
|
|
|
716
|
-
peg$resultsCache
|
|
695
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
717
696
|
|
|
718
697
|
return s0;
|
|
719
698
|
}
|
|
@@ -722,7 +701,7 @@ function peg$parse(input, options) {
|
|
|
722
701
|
let s0;
|
|
723
702
|
|
|
724
703
|
const key = peg$currPos * 38 + 3;
|
|
725
|
-
const cached = peg$resultsCache
|
|
704
|
+
const cached = peg$resultsCache.get(key);
|
|
726
705
|
|
|
727
706
|
if (cached) {
|
|
728
707
|
peg$currPos = cached.nextPos;
|
|
@@ -738,7 +717,7 @@ function peg$parse(input, options) {
|
|
|
738
717
|
if (peg$silentFails === 0) { peg$fail(peg$e1); }
|
|
739
718
|
}
|
|
740
719
|
|
|
741
|
-
peg$resultsCache
|
|
720
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
742
721
|
|
|
743
722
|
return s0;
|
|
744
723
|
}
|
|
@@ -747,7 +726,7 @@ function peg$parse(input, options) {
|
|
|
747
726
|
let s0;
|
|
748
727
|
|
|
749
728
|
const key = peg$currPos * 38 + 4;
|
|
750
|
-
const cached = peg$resultsCache
|
|
729
|
+
const cached = peg$resultsCache.get(key);
|
|
751
730
|
|
|
752
731
|
if (cached) {
|
|
753
732
|
peg$currPos = cached.nextPos;
|
|
@@ -763,7 +742,7 @@ function peg$parse(input, options) {
|
|
|
763
742
|
if (peg$silentFails === 0) { peg$fail(peg$e2); }
|
|
764
743
|
}
|
|
765
744
|
|
|
766
|
-
peg$resultsCache
|
|
745
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
767
746
|
|
|
768
747
|
return s0;
|
|
769
748
|
}
|
|
@@ -772,7 +751,7 @@ function peg$parse(input, options) {
|
|
|
772
751
|
let s0;
|
|
773
752
|
|
|
774
753
|
const key = peg$currPos * 38 + 5;
|
|
775
|
-
const cached = peg$resultsCache
|
|
754
|
+
const cached = peg$resultsCache.get(key);
|
|
776
755
|
|
|
777
756
|
if (cached) {
|
|
778
757
|
peg$currPos = cached.nextPos;
|
|
@@ -788,7 +767,7 @@ function peg$parse(input, options) {
|
|
|
788
767
|
if (peg$silentFails === 0) { peg$fail(peg$e3); }
|
|
789
768
|
}
|
|
790
769
|
|
|
791
|
-
peg$resultsCache
|
|
770
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
792
771
|
|
|
793
772
|
return s0;
|
|
794
773
|
}
|
|
@@ -797,7 +776,7 @@ function peg$parse(input, options) {
|
|
|
797
776
|
let s0;
|
|
798
777
|
|
|
799
778
|
const key = peg$currPos * 38 + 6;
|
|
800
|
-
const cached = peg$resultsCache
|
|
779
|
+
const cached = peg$resultsCache.get(key);
|
|
801
780
|
|
|
802
781
|
if (cached) {
|
|
803
782
|
peg$currPos = cached.nextPos;
|
|
@@ -813,7 +792,7 @@ function peg$parse(input, options) {
|
|
|
813
792
|
if (peg$silentFails === 0) { peg$fail(peg$e4); }
|
|
814
793
|
}
|
|
815
794
|
|
|
816
|
-
peg$resultsCache
|
|
795
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
817
796
|
|
|
818
797
|
return s0;
|
|
819
798
|
}
|
|
@@ -822,7 +801,7 @@ function peg$parse(input, options) {
|
|
|
822
801
|
let s0;
|
|
823
802
|
|
|
824
803
|
const key = peg$currPos * 38 + 7;
|
|
825
|
-
const cached = peg$resultsCache
|
|
804
|
+
const cached = peg$resultsCache.get(key);
|
|
826
805
|
|
|
827
806
|
if (cached) {
|
|
828
807
|
peg$currPos = cached.nextPos;
|
|
@@ -838,7 +817,7 @@ function peg$parse(input, options) {
|
|
|
838
817
|
if (peg$silentFails === 0) { peg$fail(peg$e5); }
|
|
839
818
|
}
|
|
840
819
|
|
|
841
|
-
peg$resultsCache
|
|
820
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
842
821
|
|
|
843
822
|
return s0;
|
|
844
823
|
}
|
|
@@ -847,7 +826,7 @@ function peg$parse(input, options) {
|
|
|
847
826
|
let s0, s1, s2, s3, s4, s5, s6;
|
|
848
827
|
|
|
849
828
|
const key = peg$currPos * 38 + 8;
|
|
850
|
-
const cached = peg$resultsCache
|
|
829
|
+
const cached = peg$resultsCache.get(key);
|
|
851
830
|
|
|
852
831
|
if (cached) {
|
|
853
832
|
peg$currPos = cached.nextPos;
|
|
@@ -857,211 +836,104 @@ function peg$parse(input, options) {
|
|
|
857
836
|
|
|
858
837
|
s0 = peg$currPos;
|
|
859
838
|
s1 = [];
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
peg$currPos++;
|
|
865
|
-
} else {
|
|
866
|
-
s4 = peg$FAILED;
|
|
867
|
-
if (peg$silentFails === 0) { peg$fail(peg$e6); }
|
|
868
|
-
}
|
|
869
|
-
if (s4 !== peg$FAILED) {
|
|
870
|
-
while (s4 !== peg$FAILED) {
|
|
871
|
-
s3.push(s4);
|
|
839
|
+
for (;;) {
|
|
840
|
+
s2 = peg$currPos;
|
|
841
|
+
s3 = [];
|
|
842
|
+
for (;;) {
|
|
872
843
|
s4 = input.charAt(peg$currPos);
|
|
873
|
-
if (peg$
|
|
844
|
+
if (s4 !== "" && !(peg$charCode = s4.charCodeAt(0), peg$charCode === 92 || peg$charCode === 123)) {
|
|
874
845
|
peg$currPos++;
|
|
875
846
|
} else {
|
|
876
847
|
s4 = peg$FAILED;
|
|
877
848
|
if (peg$silentFails === 0) { peg$fail(peg$e6); }
|
|
878
849
|
}
|
|
850
|
+
if (s4 === peg$FAILED) { break; }
|
|
851
|
+
s3.push(s4);
|
|
879
852
|
}
|
|
880
|
-
|
|
881
|
-
s3 = peg$FAILED;
|
|
882
|
-
}
|
|
883
|
-
if (s3 !== peg$FAILED) {
|
|
884
|
-
s2 = input.substring(s2, peg$currPos);
|
|
885
|
-
} else {
|
|
886
|
-
s2 = s3;
|
|
887
|
-
}
|
|
888
|
-
if (s2 === peg$FAILED) {
|
|
889
|
-
s2 = peg$currPos;
|
|
890
|
-
if (input.charCodeAt(peg$currPos) === 92) {
|
|
891
|
-
s3 = peg$c7;
|
|
892
|
-
peg$currPos++;
|
|
893
|
-
} else {
|
|
853
|
+
if (s3.length < 1) {
|
|
894
854
|
s3 = peg$FAILED;
|
|
895
|
-
if (peg$silentFails === 0) { peg$fail(peg$e7); }
|
|
896
855
|
}
|
|
897
856
|
if (s3 !== peg$FAILED) {
|
|
898
|
-
|
|
899
|
-
s4 = input.charAt(peg$currPos);
|
|
900
|
-
peg$currPos++;
|
|
901
|
-
} else {
|
|
902
|
-
s4 = peg$FAILED;
|
|
903
|
-
if (peg$silentFails === 0) { peg$fail(peg$e8); }
|
|
904
|
-
}
|
|
905
|
-
if (s4 !== peg$FAILED) {
|
|
906
|
-
s2 = peg$f4(s4);
|
|
907
|
-
} else {
|
|
908
|
-
peg$currPos = s2;
|
|
909
|
-
s2 = peg$FAILED;
|
|
910
|
-
}
|
|
857
|
+
s2 = input.substring(s2, peg$currPos);
|
|
911
858
|
} else {
|
|
912
|
-
|
|
913
|
-
s2 = peg$FAILED;
|
|
859
|
+
s2 = s3;
|
|
914
860
|
}
|
|
915
861
|
if (s2 === peg$FAILED) {
|
|
916
862
|
s2 = peg$currPos;
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
s4 = peg$c9;
|
|
863
|
+
if (input.charCodeAt(peg$currPos) === 92) {
|
|
864
|
+
s3 = peg$c7;
|
|
920
865
|
peg$currPos++;
|
|
921
866
|
} else {
|
|
922
|
-
|
|
923
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
867
|
+
s3 = peg$FAILED;
|
|
868
|
+
if (peg$silentFails === 0) { peg$fail(peg$e7); }
|
|
924
869
|
}
|
|
925
|
-
if (
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
if (input.charCodeAt(peg$currPos) === 123) {
|
|
929
|
-
s6 = peg$c9;
|
|
870
|
+
if (s3 !== peg$FAILED) {
|
|
871
|
+
if (input.length > peg$currPos) {
|
|
872
|
+
s4 = input.charAt(peg$currPos);
|
|
930
873
|
peg$currPos++;
|
|
931
874
|
} else {
|
|
932
|
-
|
|
933
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
934
|
-
}
|
|
935
|
-
peg$silentFails--;
|
|
936
|
-
if (s6 === peg$FAILED) {
|
|
937
|
-
s5 = undefined;
|
|
938
|
-
} else {
|
|
939
|
-
peg$currPos = s5;
|
|
940
|
-
s5 = peg$FAILED;
|
|
875
|
+
s4 = peg$FAILED;
|
|
876
|
+
if (peg$silentFails === 0) { peg$fail(peg$e8); }
|
|
941
877
|
}
|
|
942
|
-
if (
|
|
943
|
-
|
|
944
|
-
s3 = s4;
|
|
878
|
+
if (s4 !== peg$FAILED) {
|
|
879
|
+
s2 = peg$f4(s4);
|
|
945
880
|
} else {
|
|
946
|
-
peg$currPos =
|
|
947
|
-
|
|
948
|
-
}
|
|
949
|
-
} else {
|
|
950
|
-
peg$currPos = s3;
|
|
951
|
-
s3 = peg$FAILED;
|
|
952
|
-
}
|
|
953
|
-
if (s3 !== peg$FAILED) {
|
|
954
|
-
s2 = input.substring(s2, peg$currPos);
|
|
955
|
-
} else {
|
|
956
|
-
s2 = s3;
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
if (s2 !== peg$FAILED) {
|
|
961
|
-
while (s2 !== peg$FAILED) {
|
|
962
|
-
s1.push(s2);
|
|
963
|
-
s2 = peg$currPos;
|
|
964
|
-
s3 = [];
|
|
965
|
-
s4 = input.charAt(peg$currPos);
|
|
966
|
-
if (peg$r0.test(s4)) {
|
|
967
|
-
peg$currPos++;
|
|
968
|
-
} else {
|
|
969
|
-
s4 = peg$FAILED;
|
|
970
|
-
if (peg$silentFails === 0) { peg$fail(peg$e6); }
|
|
971
|
-
}
|
|
972
|
-
if (s4 !== peg$FAILED) {
|
|
973
|
-
while (s4 !== peg$FAILED) {
|
|
974
|
-
s3.push(s4);
|
|
975
|
-
s4 = input.charAt(peg$currPos);
|
|
976
|
-
if (peg$r0.test(s4)) {
|
|
977
|
-
peg$currPos++;
|
|
978
|
-
} else {
|
|
979
|
-
s4 = peg$FAILED;
|
|
980
|
-
if (peg$silentFails === 0) { peg$fail(peg$e6); }
|
|
981
|
-
}
|
|
881
|
+
peg$currPos = s2;
|
|
882
|
+
s2 = peg$FAILED;
|
|
982
883
|
}
|
|
983
884
|
} else {
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
if (s3 !== peg$FAILED) {
|
|
987
|
-
s2 = input.substring(s2, peg$currPos);
|
|
988
|
-
} else {
|
|
989
|
-
s2 = s3;
|
|
885
|
+
peg$currPos = s2;
|
|
886
|
+
s2 = peg$FAILED;
|
|
990
887
|
}
|
|
991
888
|
if (s2 === peg$FAILED) {
|
|
992
889
|
s2 = peg$currPos;
|
|
993
|
-
|
|
994
|
-
|
|
890
|
+
s3 = peg$currPos;
|
|
891
|
+
if (input.charCodeAt(peg$currPos) === 123) {
|
|
892
|
+
s4 = peg$c9;
|
|
995
893
|
peg$currPos++;
|
|
996
894
|
} else {
|
|
997
|
-
|
|
998
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
999
|
-
}
|
|
1000
|
-
if (s3 !== peg$FAILED) {
|
|
1001
|
-
if (input.length > peg$currPos) {
|
|
1002
|
-
s4 = input.charAt(peg$currPos);
|
|
1003
|
-
peg$currPos++;
|
|
1004
|
-
} else {
|
|
1005
|
-
s4 = peg$FAILED;
|
|
1006
|
-
if (peg$silentFails === 0) { peg$fail(peg$e8); }
|
|
1007
|
-
}
|
|
1008
|
-
if (s4 !== peg$FAILED) {
|
|
1009
|
-
s2 = peg$f4(s4);
|
|
1010
|
-
} else {
|
|
1011
|
-
peg$currPos = s2;
|
|
1012
|
-
s2 = peg$FAILED;
|
|
1013
|
-
}
|
|
1014
|
-
} else {
|
|
1015
|
-
peg$currPos = s2;
|
|
1016
|
-
s2 = peg$FAILED;
|
|
895
|
+
s4 = peg$FAILED;
|
|
896
|
+
if (peg$silentFails === 0) { peg$fail(peg$e9); }
|
|
1017
897
|
}
|
|
1018
|
-
if (
|
|
1019
|
-
|
|
1020
|
-
|
|
898
|
+
if (s4 !== peg$FAILED) {
|
|
899
|
+
s5 = peg$currPos;
|
|
900
|
+
peg$silentFails++;
|
|
1021
901
|
if (input.charCodeAt(peg$currPos) === 123) {
|
|
1022
|
-
|
|
902
|
+
s6 = peg$c9;
|
|
1023
903
|
peg$currPos++;
|
|
1024
904
|
} else {
|
|
1025
|
-
|
|
905
|
+
s6 = peg$FAILED;
|
|
1026
906
|
if (peg$silentFails === 0) { peg$fail(peg$e9); }
|
|
1027
907
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
if (input.charCodeAt(peg$currPos) === 123) {
|
|
1032
|
-
s6 = peg$c9;
|
|
1033
|
-
peg$currPos++;
|
|
1034
|
-
} else {
|
|
1035
|
-
s6 = peg$FAILED;
|
|
1036
|
-
if (peg$silentFails === 0) { peg$fail(peg$e9); }
|
|
1037
|
-
}
|
|
1038
|
-
peg$silentFails--;
|
|
1039
|
-
if (s6 === peg$FAILED) {
|
|
1040
|
-
s5 = undefined;
|
|
1041
|
-
} else {
|
|
1042
|
-
peg$currPos = s5;
|
|
1043
|
-
s5 = peg$FAILED;
|
|
1044
|
-
}
|
|
1045
|
-
if (s5 !== peg$FAILED) {
|
|
1046
|
-
s4 = [s4, s5];
|
|
1047
|
-
s3 = s4;
|
|
1048
|
-
} else {
|
|
1049
|
-
peg$currPos = s3;
|
|
1050
|
-
s3 = peg$FAILED;
|
|
1051
|
-
}
|
|
908
|
+
peg$silentFails--;
|
|
909
|
+
if (s6 === peg$FAILED) {
|
|
910
|
+
s5 = undefined;
|
|
1052
911
|
} else {
|
|
1053
|
-
peg$currPos =
|
|
1054
|
-
|
|
912
|
+
peg$currPos = s5;
|
|
913
|
+
s5 = peg$FAILED;
|
|
1055
914
|
}
|
|
1056
|
-
if (
|
|
1057
|
-
|
|
915
|
+
if (s5 !== peg$FAILED) {
|
|
916
|
+
s4 = [s4, s5];
|
|
917
|
+
s3 = s4;
|
|
1058
918
|
} else {
|
|
1059
|
-
|
|
919
|
+
peg$currPos = s3;
|
|
920
|
+
s3 = peg$FAILED;
|
|
1060
921
|
}
|
|
922
|
+
} else {
|
|
923
|
+
peg$currPos = s3;
|
|
924
|
+
s3 = peg$FAILED;
|
|
925
|
+
}
|
|
926
|
+
if (s3 !== peg$FAILED) {
|
|
927
|
+
s2 = input.substring(s2, peg$currPos);
|
|
928
|
+
} else {
|
|
929
|
+
s2 = s3;
|
|
1061
930
|
}
|
|
1062
931
|
}
|
|
1063
932
|
}
|
|
1064
|
-
|
|
933
|
+
if (s2 === peg$FAILED) { break; }
|
|
934
|
+
s1.push(s2);
|
|
935
|
+
}
|
|
936
|
+
if (s1.length < 1) {
|
|
1065
937
|
s1 = peg$FAILED;
|
|
1066
938
|
}
|
|
1067
939
|
if (s1 !== peg$FAILED) {
|
|
@@ -1069,7 +941,7 @@ function peg$parse(input, options) {
|
|
|
1069
941
|
}
|
|
1070
942
|
s0 = s1;
|
|
1071
943
|
|
|
1072
|
-
peg$resultsCache
|
|
944
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
1073
945
|
|
|
1074
946
|
return s0;
|
|
1075
947
|
}
|
|
@@ -1078,7 +950,7 @@ function peg$parse(input, options) {
|
|
|
1078
950
|
let s0, s2;
|
|
1079
951
|
|
|
1080
952
|
const key = peg$currPos * 38 + 9;
|
|
1081
|
-
const cached = peg$resultsCache
|
|
953
|
+
const cached = peg$resultsCache.get(key);
|
|
1082
954
|
|
|
1083
955
|
if (cached) {
|
|
1084
956
|
peg$currPos = cached.nextPos;
|
|
@@ -1097,7 +969,7 @@ function peg$parse(input, options) {
|
|
|
1097
969
|
s0 = peg$FAILED;
|
|
1098
970
|
}
|
|
1099
971
|
|
|
1100
|
-
peg$resultsCache
|
|
972
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
1101
973
|
|
|
1102
974
|
return s0;
|
|
1103
975
|
}
|
|
@@ -1106,7 +978,7 @@ function peg$parse(input, options) {
|
|
|
1106
978
|
let s0, s1, s3, s5, s7, s9;
|
|
1107
979
|
|
|
1108
980
|
const key = peg$currPos * 38 + 10;
|
|
1109
|
-
const cached = peg$resultsCache
|
|
981
|
+
const cached = peg$resultsCache.get(key);
|
|
1110
982
|
|
|
1111
983
|
if (cached) {
|
|
1112
984
|
peg$currPos = cached.nextPos;
|
|
@@ -1166,7 +1038,7 @@ function peg$parse(input, options) {
|
|
|
1166
1038
|
s0 = peg$parseElvisExpression();
|
|
1167
1039
|
}
|
|
1168
1040
|
|
|
1169
|
-
peg$resultsCache
|
|
1041
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
1170
1042
|
|
|
1171
1043
|
return s0;
|
|
1172
1044
|
}
|
|
@@ -1175,7 +1047,7 @@ function peg$parse(input, options) {
|
|
|
1175
1047
|
let s0, s1, s3, s5;
|
|
1176
1048
|
|
|
1177
1049
|
const key = peg$currPos * 38 + 11;
|
|
1178
|
-
const cached = peg$resultsCache
|
|
1050
|
+
const cached = peg$resultsCache.get(key);
|
|
1179
1051
|
|
|
1180
1052
|
if (cached) {
|
|
1181
1053
|
peg$currPos = cached.nextPos;
|
|
@@ -1215,7 +1087,7 @@ function peg$parse(input, options) {
|
|
|
1215
1087
|
s0 = peg$parseNullCoalescingExpression();
|
|
1216
1088
|
}
|
|
1217
1089
|
|
|
1218
|
-
peg$resultsCache
|
|
1090
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
1219
1091
|
|
|
1220
1092
|
return s0;
|
|
1221
1093
|
}
|
|
@@ -1224,7 +1096,7 @@ function peg$parse(input, options) {
|
|
|
1224
1096
|
let s0, s1, s3, s5;
|
|
1225
1097
|
|
|
1226
1098
|
const key = peg$currPos * 38 + 12;
|
|
1227
|
-
const cached = peg$resultsCache
|
|
1099
|
+
const cached = peg$resultsCache.get(key);
|
|
1228
1100
|
|
|
1229
1101
|
if (cached) {
|
|
1230
1102
|
peg$currPos = cached.nextPos;
|
|
@@ -1264,7 +1136,7 @@ function peg$parse(input, options) {
|
|
|
1264
1136
|
s0 = peg$parseOrExpression();
|
|
1265
1137
|
}
|
|
1266
1138
|
|
|
1267
|
-
peg$resultsCache
|
|
1139
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
1268
1140
|
|
|
1269
1141
|
return s0;
|
|
1270
1142
|
}
|
|
@@ -1273,7 +1145,7 @@ function peg$parse(input, options) {
|
|
|
1273
1145
|
let s0, s1, s2, s3, s5, s7;
|
|
1274
1146
|
|
|
1275
1147
|
const key = peg$currPos * 38 + 13;
|
|
1276
|
-
const cached = peg$resultsCache
|
|
1148
|
+
const cached = peg$resultsCache.get(key);
|
|
1277
1149
|
|
|
1278
1150
|
if (cached) {
|
|
1279
1151
|
peg$currPos = cached.nextPos;
|
|
@@ -1285,30 +1157,7 @@ function peg$parse(input, options) {
|
|
|
1285
1157
|
s1 = peg$parseAndExpression();
|
|
1286
1158
|
if (s1 !== peg$FAILED) {
|
|
1287
1159
|
s2 = [];
|
|
1288
|
-
|
|
1289
|
-
peg$parse_();
|
|
1290
|
-
if (input.substr(peg$currPos, 2) === peg$c22) {
|
|
1291
|
-
s5 = peg$c22;
|
|
1292
|
-
peg$currPos += 2;
|
|
1293
|
-
} else {
|
|
1294
|
-
s5 = peg$FAILED;
|
|
1295
|
-
if (peg$silentFails === 0) { peg$fail(peg$e14); }
|
|
1296
|
-
}
|
|
1297
|
-
if (s5 !== peg$FAILED) {
|
|
1298
|
-
peg$parse_();
|
|
1299
|
-
s7 = peg$parseAndExpression();
|
|
1300
|
-
if (s7 !== peg$FAILED) {
|
|
1301
|
-
s3 = s7;
|
|
1302
|
-
} else {
|
|
1303
|
-
peg$currPos = s3;
|
|
1304
|
-
s3 = peg$FAILED;
|
|
1305
|
-
}
|
|
1306
|
-
} else {
|
|
1307
|
-
peg$currPos = s3;
|
|
1308
|
-
s3 = peg$FAILED;
|
|
1309
|
-
}
|
|
1310
|
-
while (s3 !== peg$FAILED) {
|
|
1311
|
-
s2.push(s3);
|
|
1160
|
+
for (;;) {
|
|
1312
1161
|
s3 = peg$currPos;
|
|
1313
1162
|
peg$parse_();
|
|
1314
1163
|
if (input.substr(peg$currPos, 2) === peg$c22) {
|
|
@@ -1331,6 +1180,8 @@ function peg$parse(input, options) {
|
|
|
1331
1180
|
peg$currPos = s3;
|
|
1332
1181
|
s3 = peg$FAILED;
|
|
1333
1182
|
}
|
|
1183
|
+
if (s3 === peg$FAILED) { break; }
|
|
1184
|
+
s2.push(s3);
|
|
1334
1185
|
}
|
|
1335
1186
|
s0 = peg$f10(s1, s2);
|
|
1336
1187
|
} else {
|
|
@@ -1338,7 +1189,7 @@ function peg$parse(input, options) {
|
|
|
1338
1189
|
s0 = peg$FAILED;
|
|
1339
1190
|
}
|
|
1340
1191
|
|
|
1341
|
-
peg$resultsCache
|
|
1192
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
1342
1193
|
|
|
1343
1194
|
return s0;
|
|
1344
1195
|
}
|
|
@@ -1347,7 +1198,7 @@ function peg$parse(input, options) {
|
|
|
1347
1198
|
let s0, s1, s2, s3, s5, s7;
|
|
1348
1199
|
|
|
1349
1200
|
const key = peg$currPos * 38 + 14;
|
|
1350
|
-
const cached = peg$resultsCache
|
|
1201
|
+
const cached = peg$resultsCache.get(key);
|
|
1351
1202
|
|
|
1352
1203
|
if (cached) {
|
|
1353
1204
|
peg$currPos = cached.nextPos;
|
|
@@ -1359,30 +1210,7 @@ function peg$parse(input, options) {
|
|
|
1359
1210
|
s1 = peg$parseEqExpression();
|
|
1360
1211
|
if (s1 !== peg$FAILED) {
|
|
1361
1212
|
s2 = [];
|
|
1362
|
-
|
|
1363
|
-
peg$parse_();
|
|
1364
|
-
if (input.substr(peg$currPos, 2) === peg$c24) {
|
|
1365
|
-
s5 = peg$c24;
|
|
1366
|
-
peg$currPos += 2;
|
|
1367
|
-
} else {
|
|
1368
|
-
s5 = peg$FAILED;
|
|
1369
|
-
if (peg$silentFails === 0) { peg$fail(peg$e15); }
|
|
1370
|
-
}
|
|
1371
|
-
if (s5 !== peg$FAILED) {
|
|
1372
|
-
peg$parse_();
|
|
1373
|
-
s7 = peg$parseEqExpression();
|
|
1374
|
-
if (s7 !== peg$FAILED) {
|
|
1375
|
-
s3 = s7;
|
|
1376
|
-
} else {
|
|
1377
|
-
peg$currPos = s3;
|
|
1378
|
-
s3 = peg$FAILED;
|
|
1379
|
-
}
|
|
1380
|
-
} else {
|
|
1381
|
-
peg$currPos = s3;
|
|
1382
|
-
s3 = peg$FAILED;
|
|
1383
|
-
}
|
|
1384
|
-
while (s3 !== peg$FAILED) {
|
|
1385
|
-
s2.push(s3);
|
|
1213
|
+
for (;;) {
|
|
1386
1214
|
s3 = peg$currPos;
|
|
1387
1215
|
peg$parse_();
|
|
1388
1216
|
if (input.substr(peg$currPos, 2) === peg$c24) {
|
|
@@ -1405,6 +1233,8 @@ function peg$parse(input, options) {
|
|
|
1405
1233
|
peg$currPos = s3;
|
|
1406
1234
|
s3 = peg$FAILED;
|
|
1407
1235
|
}
|
|
1236
|
+
if (s3 === peg$FAILED) { break; }
|
|
1237
|
+
s2.push(s3);
|
|
1408
1238
|
}
|
|
1409
1239
|
s0 = peg$f11(s1, s2);
|
|
1410
1240
|
} else {
|
|
@@ -1412,7 +1242,7 @@ function peg$parse(input, options) {
|
|
|
1412
1242
|
s0 = peg$FAILED;
|
|
1413
1243
|
}
|
|
1414
1244
|
|
|
1415
|
-
peg$resultsCache
|
|
1245
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
1416
1246
|
|
|
1417
1247
|
return s0;
|
|
1418
1248
|
}
|
|
@@ -1421,7 +1251,7 @@ function peg$parse(input, options) {
|
|
|
1421
1251
|
let s0, s1, s2, s3, s5, s7;
|
|
1422
1252
|
|
|
1423
1253
|
const key = peg$currPos * 38 + 15;
|
|
1424
|
-
const cached = peg$resultsCache
|
|
1254
|
+
const cached = peg$resultsCache.get(key);
|
|
1425
1255
|
|
|
1426
1256
|
if (cached) {
|
|
1427
1257
|
peg$currPos = cached.nextPos;
|
|
@@ -1433,24 +1263,7 @@ function peg$parse(input, options) {
|
|
|
1433
1263
|
s1 = peg$parseRelExpression();
|
|
1434
1264
|
if (s1 !== peg$FAILED) {
|
|
1435
1265
|
s2 = [];
|
|
1436
|
-
|
|
1437
|
-
peg$parse_();
|
|
1438
|
-
s5 = peg$parseEqualityOp();
|
|
1439
|
-
if (s5 !== peg$FAILED) {
|
|
1440
|
-
peg$parse_();
|
|
1441
|
-
s7 = peg$parseRelExpression();
|
|
1442
|
-
if (s7 !== peg$FAILED) {
|
|
1443
|
-
s3 = peg$f12(s1, s5, s7);
|
|
1444
|
-
} else {
|
|
1445
|
-
peg$currPos = s3;
|
|
1446
|
-
s3 = peg$FAILED;
|
|
1447
|
-
}
|
|
1448
|
-
} else {
|
|
1449
|
-
peg$currPos = s3;
|
|
1450
|
-
s3 = peg$FAILED;
|
|
1451
|
-
}
|
|
1452
|
-
while (s3 !== peg$FAILED) {
|
|
1453
|
-
s2.push(s3);
|
|
1266
|
+
for (;;) {
|
|
1454
1267
|
s3 = peg$currPos;
|
|
1455
1268
|
peg$parse_();
|
|
1456
1269
|
s5 = peg$parseEqualityOp();
|
|
@@ -1467,6 +1280,8 @@ function peg$parse(input, options) {
|
|
|
1467
1280
|
peg$currPos = s3;
|
|
1468
1281
|
s3 = peg$FAILED;
|
|
1469
1282
|
}
|
|
1283
|
+
if (s3 === peg$FAILED) { break; }
|
|
1284
|
+
s2.push(s3);
|
|
1470
1285
|
}
|
|
1471
1286
|
s0 = peg$f13(s1, s2);
|
|
1472
1287
|
} else {
|
|
@@ -1474,7 +1289,7 @@ function peg$parse(input, options) {
|
|
|
1474
1289
|
s0 = peg$FAILED;
|
|
1475
1290
|
}
|
|
1476
1291
|
|
|
1477
|
-
peg$resultsCache
|
|
1292
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
1478
1293
|
|
|
1479
1294
|
return s0;
|
|
1480
1295
|
}
|
|
@@ -1483,7 +1298,7 @@ function peg$parse(input, options) {
|
|
|
1483
1298
|
let s0, s1, s2, s3, s5, s7;
|
|
1484
1299
|
|
|
1485
1300
|
const key = peg$currPos * 38 + 16;
|
|
1486
|
-
const cached = peg$resultsCache
|
|
1301
|
+
const cached = peg$resultsCache.get(key);
|
|
1487
1302
|
|
|
1488
1303
|
if (cached) {
|
|
1489
1304
|
peg$currPos = cached.nextPos;
|
|
@@ -1495,24 +1310,7 @@ function peg$parse(input, options) {
|
|
|
1495
1310
|
s1 = peg$parseNotExpression();
|
|
1496
1311
|
if (s1 !== peg$FAILED) {
|
|
1497
1312
|
s2 = [];
|
|
1498
|
-
|
|
1499
|
-
peg$parse_();
|
|
1500
|
-
s5 = peg$parseRelationalOp();
|
|
1501
|
-
if (s5 !== peg$FAILED) {
|
|
1502
|
-
peg$parse_();
|
|
1503
|
-
s7 = peg$parseNotExpression();
|
|
1504
|
-
if (s7 !== peg$FAILED) {
|
|
1505
|
-
s3 = peg$f14(s1, s5, s7);
|
|
1506
|
-
} else {
|
|
1507
|
-
peg$currPos = s3;
|
|
1508
|
-
s3 = peg$FAILED;
|
|
1509
|
-
}
|
|
1510
|
-
} else {
|
|
1511
|
-
peg$currPos = s3;
|
|
1512
|
-
s3 = peg$FAILED;
|
|
1513
|
-
}
|
|
1514
|
-
while (s3 !== peg$FAILED) {
|
|
1515
|
-
s2.push(s3);
|
|
1313
|
+
for (;;) {
|
|
1516
1314
|
s3 = peg$currPos;
|
|
1517
1315
|
peg$parse_();
|
|
1518
1316
|
s5 = peg$parseRelationalOp();
|
|
@@ -1529,6 +1327,8 @@ function peg$parse(input, options) {
|
|
|
1529
1327
|
peg$currPos = s3;
|
|
1530
1328
|
s3 = peg$FAILED;
|
|
1531
1329
|
}
|
|
1330
|
+
if (s3 === peg$FAILED) { break; }
|
|
1331
|
+
s2.push(s3);
|
|
1532
1332
|
}
|
|
1533
1333
|
s0 = peg$f15(s1, s2);
|
|
1534
1334
|
} else {
|
|
@@ -1536,7 +1336,7 @@ function peg$parse(input, options) {
|
|
|
1536
1336
|
s0 = peg$FAILED;
|
|
1537
1337
|
}
|
|
1538
1338
|
|
|
1539
|
-
peg$resultsCache
|
|
1339
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
1540
1340
|
|
|
1541
1341
|
return s0;
|
|
1542
1342
|
}
|
|
@@ -1545,7 +1345,7 @@ function peg$parse(input, options) {
|
|
|
1545
1345
|
let s0, s1, s3;
|
|
1546
1346
|
|
|
1547
1347
|
const key = peg$currPos * 38 + 17;
|
|
1548
|
-
const cached = peg$resultsCache
|
|
1348
|
+
const cached = peg$resultsCache.get(key);
|
|
1549
1349
|
|
|
1550
1350
|
if (cached) {
|
|
1551
1351
|
peg$currPos = cached.nextPos;
|
|
@@ -1578,7 +1378,7 @@ function peg$parse(input, options) {
|
|
|
1578
1378
|
s0 = peg$parseAccess();
|
|
1579
1379
|
}
|
|
1580
1380
|
|
|
1581
|
-
peg$resultsCache
|
|
1381
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
1582
1382
|
|
|
1583
1383
|
return s0;
|
|
1584
1384
|
}
|
|
@@ -1587,7 +1387,7 @@ function peg$parse(input, options) {
|
|
|
1587
1387
|
let s0, s1, s3, s4, s5;
|
|
1588
1388
|
|
|
1589
1389
|
const key = peg$currPos * 38 + 18;
|
|
1590
|
-
const cached = peg$resultsCache
|
|
1390
|
+
const cached = peg$resultsCache.get(key);
|
|
1591
1391
|
|
|
1592
1392
|
if (cached) {
|
|
1593
1393
|
peg$currPos = cached.nextPos;
|
|
@@ -1600,29 +1400,20 @@ function peg$parse(input, options) {
|
|
|
1600
1400
|
if (s1 !== peg$FAILED) {
|
|
1601
1401
|
peg$parse_();
|
|
1602
1402
|
s3 = [];
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
peg$
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
if (s4 !== peg$FAILED) {
|
|
1613
|
-
while (s4 !== peg$FAILED) {
|
|
1614
|
-
s3.push(s4);
|
|
1615
|
-
s4 = peg$currPos;
|
|
1616
|
-
s5 = peg$parseAccessExpression();
|
|
1617
|
-
if (s5 !== peg$FAILED) {
|
|
1618
|
-
peg$parse_();
|
|
1619
|
-
s4 = s5;
|
|
1620
|
-
} else {
|
|
1621
|
-
peg$currPos = s4;
|
|
1622
|
-
s4 = peg$FAILED;
|
|
1623
|
-
}
|
|
1403
|
+
for (;;) {
|
|
1404
|
+
s4 = peg$currPos;
|
|
1405
|
+
s5 = peg$parseAccessExpression();
|
|
1406
|
+
if (s5 !== peg$FAILED) {
|
|
1407
|
+
peg$parse_();
|
|
1408
|
+
s4 = s5;
|
|
1409
|
+
} else {
|
|
1410
|
+
peg$currPos = s4;
|
|
1411
|
+
s4 = peg$FAILED;
|
|
1624
1412
|
}
|
|
1625
|
-
|
|
1413
|
+
if (s4 === peg$FAILED) { break; }
|
|
1414
|
+
s3.push(s4);
|
|
1415
|
+
}
|
|
1416
|
+
if (s3.length < 1) {
|
|
1626
1417
|
s3 = peg$FAILED;
|
|
1627
1418
|
}
|
|
1628
1419
|
if (s3 !== peg$FAILED) {
|
|
@@ -1639,7 +1430,7 @@ function peg$parse(input, options) {
|
|
|
1639
1430
|
s0 = peg$parseGroupingExpression();
|
|
1640
1431
|
}
|
|
1641
1432
|
|
|
1642
|
-
peg$resultsCache
|
|
1433
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
1643
1434
|
|
|
1644
1435
|
return s0;
|
|
1645
1436
|
}
|
|
@@ -1648,7 +1439,7 @@ function peg$parse(input, options) {
|
|
|
1648
1439
|
let s0;
|
|
1649
1440
|
|
|
1650
1441
|
const key = peg$currPos * 38 + 19;
|
|
1651
|
-
const cached = peg$resultsCache
|
|
1442
|
+
const cached = peg$resultsCache.get(key);
|
|
1652
1443
|
|
|
1653
1444
|
if (cached) {
|
|
1654
1445
|
peg$currPos = cached.nextPos;
|
|
@@ -1664,7 +1455,7 @@ function peg$parse(input, options) {
|
|
|
1664
1455
|
}
|
|
1665
1456
|
}
|
|
1666
1457
|
|
|
1667
|
-
peg$resultsCache
|
|
1458
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
1668
1459
|
|
|
1669
1460
|
return s0;
|
|
1670
1461
|
}
|
|
@@ -1673,7 +1464,7 @@ function peg$parse(input, options) {
|
|
|
1673
1464
|
let s0, s1, s3;
|
|
1674
1465
|
|
|
1675
1466
|
const key = peg$currPos * 38 + 20;
|
|
1676
|
-
const cached = peg$resultsCache
|
|
1467
|
+
const cached = peg$resultsCache.get(key);
|
|
1677
1468
|
|
|
1678
1469
|
if (cached) {
|
|
1679
1470
|
peg$currPos = cached.nextPos;
|
|
@@ -1718,7 +1509,7 @@ function peg$parse(input, options) {
|
|
|
1718
1509
|
if (peg$silentFails === 0) { peg$fail(peg$e17); }
|
|
1719
1510
|
}
|
|
1720
1511
|
|
|
1721
|
-
peg$resultsCache
|
|
1512
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
1722
1513
|
|
|
1723
1514
|
return s0;
|
|
1724
1515
|
}
|
|
@@ -1727,7 +1518,7 @@ function peg$parse(input, options) {
|
|
|
1727
1518
|
let s0, s1, s2, s4, s6;
|
|
1728
1519
|
|
|
1729
1520
|
const key = peg$currPos * 38 + 21;
|
|
1730
|
-
const cached = peg$resultsCache
|
|
1521
|
+
const cached = peg$resultsCache.get(key);
|
|
1731
1522
|
|
|
1732
1523
|
if (cached) {
|
|
1733
1524
|
peg$currPos = cached.nextPos;
|
|
@@ -1786,7 +1577,7 @@ function peg$parse(input, options) {
|
|
|
1786
1577
|
if (peg$silentFails === 0) { peg$fail(peg$e20); }
|
|
1787
1578
|
}
|
|
1788
1579
|
|
|
1789
|
-
peg$resultsCache
|
|
1580
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
1790
1581
|
|
|
1791
1582
|
return s0;
|
|
1792
1583
|
}
|
|
@@ -1795,7 +1586,7 @@ function peg$parse(input, options) {
|
|
|
1795
1586
|
let s0, s1, s2, s4, s5, s6, s7, s9;
|
|
1796
1587
|
|
|
1797
1588
|
const key = peg$currPos * 38 + 22;
|
|
1798
|
-
const cached = peg$resultsCache
|
|
1589
|
+
const cached = peg$resultsCache.get(key);
|
|
1799
1590
|
|
|
1800
1591
|
if (cached) {
|
|
1801
1592
|
peg$currPos = cached.nextPos;
|
|
@@ -1837,30 +1628,7 @@ function peg$parse(input, options) {
|
|
|
1837
1628
|
s4 = null;
|
|
1838
1629
|
}
|
|
1839
1630
|
s5 = [];
|
|
1840
|
-
|
|
1841
|
-
if (input.charCodeAt(peg$currPos) === 44) {
|
|
1842
|
-
s7 = peg$c34;
|
|
1843
|
-
peg$currPos++;
|
|
1844
|
-
} else {
|
|
1845
|
-
s7 = peg$FAILED;
|
|
1846
|
-
if (peg$silentFails === 0) { peg$fail(peg$e25); }
|
|
1847
|
-
}
|
|
1848
|
-
if (s7 !== peg$FAILED) {
|
|
1849
|
-
peg$parse_();
|
|
1850
|
-
s9 = peg$parseTernaryExpression();
|
|
1851
|
-
if (s9 !== peg$FAILED) {
|
|
1852
|
-
peg$parse_();
|
|
1853
|
-
s6 = s9;
|
|
1854
|
-
} else {
|
|
1855
|
-
peg$currPos = s6;
|
|
1856
|
-
s6 = peg$FAILED;
|
|
1857
|
-
}
|
|
1858
|
-
} else {
|
|
1859
|
-
peg$currPos = s6;
|
|
1860
|
-
s6 = peg$FAILED;
|
|
1861
|
-
}
|
|
1862
|
-
while (s6 !== peg$FAILED) {
|
|
1863
|
-
s5.push(s6);
|
|
1631
|
+
for (;;) {
|
|
1864
1632
|
s6 = peg$currPos;
|
|
1865
1633
|
if (input.charCodeAt(peg$currPos) === 44) {
|
|
1866
1634
|
s7 = peg$c34;
|
|
@@ -1883,6 +1651,8 @@ function peg$parse(input, options) {
|
|
|
1883
1651
|
peg$currPos = s6;
|
|
1884
1652
|
s6 = peg$FAILED;
|
|
1885
1653
|
}
|
|
1654
|
+
if (s6 === peg$FAILED) { break; }
|
|
1655
|
+
s5.push(s6);
|
|
1886
1656
|
}
|
|
1887
1657
|
if (input.charCodeAt(peg$currPos) === 41) {
|
|
1888
1658
|
s6 = peg$c36;
|
|
@@ -1907,7 +1677,7 @@ function peg$parse(input, options) {
|
|
|
1907
1677
|
if (peg$silentFails === 0) { peg$fail(peg$e23); }
|
|
1908
1678
|
}
|
|
1909
1679
|
|
|
1910
|
-
peg$resultsCache
|
|
1680
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
1911
1681
|
|
|
1912
1682
|
return s0;
|
|
1913
1683
|
}
|
|
@@ -1916,7 +1686,7 @@ function peg$parse(input, options) {
|
|
|
1916
1686
|
let s0, s1, s3, s5;
|
|
1917
1687
|
|
|
1918
1688
|
const key = peg$currPos * 38 + 23;
|
|
1919
|
-
const cached = peg$resultsCache
|
|
1689
|
+
const cached = peg$resultsCache.get(key);
|
|
1920
1690
|
|
|
1921
1691
|
if (cached) {
|
|
1922
1692
|
peg$currPos = cached.nextPos;
|
|
@@ -1968,7 +1738,7 @@ function peg$parse(input, options) {
|
|
|
1968
1738
|
}
|
|
1969
1739
|
}
|
|
1970
1740
|
|
|
1971
|
-
peg$resultsCache
|
|
1741
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
1972
1742
|
|
|
1973
1743
|
return s0;
|
|
1974
1744
|
}
|
|
@@ -1977,7 +1747,7 @@ function peg$parse(input, options) {
|
|
|
1977
1747
|
let s0, s1, s2, s4, s5, s6, s7, s9;
|
|
1978
1748
|
|
|
1979
1749
|
const key = peg$currPos * 38 + 24;
|
|
1980
|
-
const cached = peg$resultsCache
|
|
1750
|
+
const cached = peg$resultsCache.get(key);
|
|
1981
1751
|
|
|
1982
1752
|
if (cached) {
|
|
1983
1753
|
peg$currPos = cached.nextPos;
|
|
@@ -2011,30 +1781,7 @@ function peg$parse(input, options) {
|
|
|
2011
1781
|
s4 = null;
|
|
2012
1782
|
}
|
|
2013
1783
|
s5 = [];
|
|
2014
|
-
|
|
2015
|
-
if (input.charCodeAt(peg$currPos) === 44) {
|
|
2016
|
-
s7 = peg$c34;
|
|
2017
|
-
peg$currPos++;
|
|
2018
|
-
} else {
|
|
2019
|
-
s7 = peg$FAILED;
|
|
2020
|
-
if (peg$silentFails === 0) { peg$fail(peg$e25); }
|
|
2021
|
-
}
|
|
2022
|
-
if (s7 !== peg$FAILED) {
|
|
2023
|
-
peg$parse_();
|
|
2024
|
-
s9 = peg$parseTernaryExpression();
|
|
2025
|
-
if (s9 !== peg$FAILED) {
|
|
2026
|
-
peg$parse_();
|
|
2027
|
-
s6 = s9;
|
|
2028
|
-
} else {
|
|
2029
|
-
peg$currPos = s6;
|
|
2030
|
-
s6 = peg$FAILED;
|
|
2031
|
-
}
|
|
2032
|
-
} else {
|
|
2033
|
-
peg$currPos = s6;
|
|
2034
|
-
s6 = peg$FAILED;
|
|
2035
|
-
}
|
|
2036
|
-
while (s6 !== peg$FAILED) {
|
|
2037
|
-
s5.push(s6);
|
|
1784
|
+
for (;;) {
|
|
2038
1785
|
s6 = peg$currPos;
|
|
2039
1786
|
if (input.charCodeAt(peg$currPos) === 44) {
|
|
2040
1787
|
s7 = peg$c34;
|
|
@@ -2057,6 +1804,8 @@ function peg$parse(input, options) {
|
|
|
2057
1804
|
peg$currPos = s6;
|
|
2058
1805
|
s6 = peg$FAILED;
|
|
2059
1806
|
}
|
|
1807
|
+
if (s6 === peg$FAILED) { break; }
|
|
1808
|
+
s5.push(s6);
|
|
2060
1809
|
}
|
|
2061
1810
|
if (input.charCodeAt(peg$currPos) === 41) {
|
|
2062
1811
|
s6 = peg$c36;
|
|
@@ -2085,7 +1834,7 @@ function peg$parse(input, options) {
|
|
|
2085
1834
|
if (peg$silentFails === 0) { peg$fail(peg$e27); }
|
|
2086
1835
|
}
|
|
2087
1836
|
|
|
2088
|
-
peg$resultsCache
|
|
1837
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
2089
1838
|
|
|
2090
1839
|
return s0;
|
|
2091
1840
|
}
|
|
@@ -2094,7 +1843,7 @@ function peg$parse(input, options) {
|
|
|
2094
1843
|
let s0;
|
|
2095
1844
|
|
|
2096
1845
|
const key = peg$currPos * 38 + 25;
|
|
2097
|
-
const cached = peg$resultsCache
|
|
1846
|
+
const cached = peg$resultsCache.get(key);
|
|
2098
1847
|
|
|
2099
1848
|
if (cached) {
|
|
2100
1849
|
peg$currPos = cached.nextPos;
|
|
@@ -2113,7 +1862,7 @@ function peg$parse(input, options) {
|
|
|
2113
1862
|
}
|
|
2114
1863
|
}
|
|
2115
1864
|
|
|
2116
|
-
peg$resultsCache
|
|
1865
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
2117
1866
|
|
|
2118
1867
|
return s0;
|
|
2119
1868
|
}
|
|
@@ -2122,7 +1871,7 @@ function peg$parse(input, options) {
|
|
|
2122
1871
|
let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
|
|
2123
1872
|
|
|
2124
1873
|
const key = peg$currPos * 38 + 26;
|
|
2125
|
-
const cached = peg$resultsCache
|
|
1874
|
+
const cached = peg$resultsCache.get(key);
|
|
2126
1875
|
|
|
2127
1876
|
if (cached) {
|
|
2128
1877
|
peg$currPos = cached.nextPos;
|
|
@@ -2146,25 +1895,18 @@ function peg$parse(input, options) {
|
|
|
2146
1895
|
}
|
|
2147
1896
|
s4 = peg$currPos;
|
|
2148
1897
|
s5 = [];
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
peg$
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
if (s6 !== peg$FAILED) {
|
|
2157
|
-
while (s6 !== peg$FAILED) {
|
|
2158
|
-
s5.push(s6);
|
|
2159
|
-
s6 = input.charAt(peg$currPos);
|
|
2160
|
-
if (peg$r1.test(s6)) {
|
|
2161
|
-
peg$currPos++;
|
|
2162
|
-
} else {
|
|
2163
|
-
s6 = peg$FAILED;
|
|
2164
|
-
if (peg$silentFails === 0) { peg$fail(peg$e30); }
|
|
2165
|
-
}
|
|
1898
|
+
for (;;) {
|
|
1899
|
+
s6 = input.charAt(peg$currPos);
|
|
1900
|
+
if ((peg$charCode = s6.charCodeAt(0), (peg$charCode >= 48 && peg$charCode <= 57))) {
|
|
1901
|
+
peg$currPos++;
|
|
1902
|
+
} else {
|
|
1903
|
+
s6 = peg$FAILED;
|
|
1904
|
+
if (peg$silentFails === 0) { peg$fail(peg$e30); }
|
|
2166
1905
|
}
|
|
2167
|
-
|
|
1906
|
+
if (s6 === peg$FAILED) { break; }
|
|
1907
|
+
s5.push(s6);
|
|
1908
|
+
}
|
|
1909
|
+
if (s5.length < 1) {
|
|
2168
1910
|
s5 = peg$FAILED;
|
|
2169
1911
|
}
|
|
2170
1912
|
if (s5 !== peg$FAILED) {
|
|
@@ -2178,22 +1920,16 @@ function peg$parse(input, options) {
|
|
|
2178
1920
|
}
|
|
2179
1921
|
if (s7 !== peg$FAILED) {
|
|
2180
1922
|
s8 = [];
|
|
2181
|
-
|
|
2182
|
-
if (peg$r1.test(s9)) {
|
|
2183
|
-
peg$currPos++;
|
|
2184
|
-
} else {
|
|
2185
|
-
s9 = peg$FAILED;
|
|
2186
|
-
if (peg$silentFails === 0) { peg$fail(peg$e30); }
|
|
2187
|
-
}
|
|
2188
|
-
while (s9 !== peg$FAILED) {
|
|
2189
|
-
s8.push(s9);
|
|
1923
|
+
for (;;) {
|
|
2190
1924
|
s9 = input.charAt(peg$currPos);
|
|
2191
|
-
if (peg$
|
|
1925
|
+
if ((peg$charCode = s9.charCodeAt(0), (peg$charCode >= 48 && peg$charCode <= 57))) {
|
|
2192
1926
|
peg$currPos++;
|
|
2193
1927
|
} else {
|
|
2194
1928
|
s9 = peg$FAILED;
|
|
2195
1929
|
if (peg$silentFails === 0) { peg$fail(peg$e30); }
|
|
2196
1930
|
}
|
|
1931
|
+
if (s9 === peg$FAILED) { break; }
|
|
1932
|
+
s8.push(s9);
|
|
2197
1933
|
}
|
|
2198
1934
|
s7 = [s7, s8];
|
|
2199
1935
|
s6 = s7;
|
|
@@ -2221,25 +1957,18 @@ function peg$parse(input, options) {
|
|
|
2221
1957
|
}
|
|
2222
1958
|
if (s5 !== peg$FAILED) {
|
|
2223
1959
|
s6 = [];
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
peg$
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
if (s7 !== peg$FAILED) {
|
|
2232
|
-
while (s7 !== peg$FAILED) {
|
|
2233
|
-
s6.push(s7);
|
|
2234
|
-
s7 = input.charAt(peg$currPos);
|
|
2235
|
-
if (peg$r1.test(s7)) {
|
|
2236
|
-
peg$currPos++;
|
|
2237
|
-
} else {
|
|
2238
|
-
s7 = peg$FAILED;
|
|
2239
|
-
if (peg$silentFails === 0) { peg$fail(peg$e30); }
|
|
2240
|
-
}
|
|
1960
|
+
for (;;) {
|
|
1961
|
+
s7 = input.charAt(peg$currPos);
|
|
1962
|
+
if ((peg$charCode = s7.charCodeAt(0), (peg$charCode >= 48 && peg$charCode <= 57))) {
|
|
1963
|
+
peg$currPos++;
|
|
1964
|
+
} else {
|
|
1965
|
+
s7 = peg$FAILED;
|
|
1966
|
+
if (peg$silentFails === 0) { peg$fail(peg$e30); }
|
|
2241
1967
|
}
|
|
2242
|
-
|
|
1968
|
+
if (s7 === peg$FAILED) { break; }
|
|
1969
|
+
s6.push(s7);
|
|
1970
|
+
}
|
|
1971
|
+
if (s6.length < 1) {
|
|
2243
1972
|
s6 = peg$FAILED;
|
|
2244
1973
|
}
|
|
2245
1974
|
if (s6 !== peg$FAILED) {
|
|
@@ -2276,7 +2005,7 @@ function peg$parse(input, options) {
|
|
|
2276
2005
|
if (peg$silentFails === 0) { peg$fail(peg$e28); }
|
|
2277
2006
|
}
|
|
2278
2007
|
|
|
2279
|
-
peg$resultsCache
|
|
2008
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
2280
2009
|
|
|
2281
2010
|
return s0;
|
|
2282
2011
|
}
|
|
@@ -2285,7 +2014,7 @@ function peg$parse(input, options) {
|
|
|
2285
2014
|
let s0, s1, s2, s3, s4;
|
|
2286
2015
|
|
|
2287
2016
|
const key = peg$currPos * 38 + 27;
|
|
2288
|
-
const cached = peg$resultsCache
|
|
2017
|
+
const cached = peg$resultsCache.get(key);
|
|
2289
2018
|
|
|
2290
2019
|
if (cached) {
|
|
2291
2020
|
peg$currPos = cached.nextPos;
|
|
@@ -2305,22 +2034,16 @@ function peg$parse(input, options) {
|
|
|
2305
2034
|
if (s1 !== peg$FAILED) {
|
|
2306
2035
|
s2 = peg$currPos;
|
|
2307
2036
|
s3 = [];
|
|
2308
|
-
|
|
2309
|
-
if (peg$r2.test(s4)) {
|
|
2310
|
-
peg$currPos++;
|
|
2311
|
-
} else {
|
|
2312
|
-
s4 = peg$FAILED;
|
|
2313
|
-
if (peg$silentFails === 0) { peg$fail(peg$e33); }
|
|
2314
|
-
}
|
|
2315
|
-
while (s4 !== peg$FAILED) {
|
|
2316
|
-
s3.push(s4);
|
|
2037
|
+
for (;;) {
|
|
2317
2038
|
s4 = input.charAt(peg$currPos);
|
|
2318
|
-
if (peg$
|
|
2039
|
+
if (s4 !== "" && !(peg$charCode = s4.charCodeAt(0), peg$charCode === 39)) {
|
|
2319
2040
|
peg$currPos++;
|
|
2320
2041
|
} else {
|
|
2321
2042
|
s4 = peg$FAILED;
|
|
2322
2043
|
if (peg$silentFails === 0) { peg$fail(peg$e33); }
|
|
2323
2044
|
}
|
|
2045
|
+
if (s4 === peg$FAILED) { break; }
|
|
2046
|
+
s3.push(s4);
|
|
2324
2047
|
}
|
|
2325
2048
|
s2 = input.substring(s2, peg$currPos);
|
|
2326
2049
|
if (input.charCodeAt(peg$currPos) === 39) {
|
|
@@ -2352,22 +2075,16 @@ function peg$parse(input, options) {
|
|
|
2352
2075
|
if (s1 !== peg$FAILED) {
|
|
2353
2076
|
s2 = peg$currPos;
|
|
2354
2077
|
s3 = [];
|
|
2355
|
-
|
|
2356
|
-
if (peg$r3.test(s4)) {
|
|
2357
|
-
peg$currPos++;
|
|
2358
|
-
} else {
|
|
2359
|
-
s4 = peg$FAILED;
|
|
2360
|
-
if (peg$silentFails === 0) { peg$fail(peg$e35); }
|
|
2361
|
-
}
|
|
2362
|
-
while (s4 !== peg$FAILED) {
|
|
2363
|
-
s3.push(s4);
|
|
2078
|
+
for (;;) {
|
|
2364
2079
|
s4 = input.charAt(peg$currPos);
|
|
2365
|
-
if (peg$
|
|
2080
|
+
if (s4 !== "" && !(peg$charCode = s4.charCodeAt(0), peg$charCode === 34)) {
|
|
2366
2081
|
peg$currPos++;
|
|
2367
2082
|
} else {
|
|
2368
2083
|
s4 = peg$FAILED;
|
|
2369
2084
|
if (peg$silentFails === 0) { peg$fail(peg$e35); }
|
|
2370
2085
|
}
|
|
2086
|
+
if (s4 === peg$FAILED) { break; }
|
|
2087
|
+
s3.push(s4);
|
|
2371
2088
|
}
|
|
2372
2089
|
s2 = input.substring(s2, peg$currPos);
|
|
2373
2090
|
if (input.charCodeAt(peg$currPos) === 34) {
|
|
@@ -2398,16 +2115,13 @@ function peg$parse(input, options) {
|
|
|
2398
2115
|
}
|
|
2399
2116
|
if (s1 !== peg$FAILED) {
|
|
2400
2117
|
s2 = [];
|
|
2401
|
-
|
|
2402
|
-
if (s3 === peg$FAILED) {
|
|
2403
|
-
s3 = peg$parseTemplateStringLiteral();
|
|
2404
|
-
}
|
|
2405
|
-
while (s3 !== peg$FAILED) {
|
|
2406
|
-
s2.push(s3);
|
|
2118
|
+
for (;;) {
|
|
2407
2119
|
s3 = peg$parseTemplateStringExpression();
|
|
2408
2120
|
if (s3 === peg$FAILED) {
|
|
2409
2121
|
s3 = peg$parseTemplateStringLiteral();
|
|
2410
2122
|
}
|
|
2123
|
+
if (s3 === peg$FAILED) { break; }
|
|
2124
|
+
s2.push(s3);
|
|
2411
2125
|
}
|
|
2412
2126
|
if (input.charCodeAt(peg$currPos) === 96) {
|
|
2413
2127
|
s3 = peg$c42;
|
|
@@ -2434,7 +2148,7 @@ function peg$parse(input, options) {
|
|
|
2434
2148
|
if (peg$silentFails === 0) { peg$fail(peg$e31); }
|
|
2435
2149
|
}
|
|
2436
2150
|
|
|
2437
|
-
peg$resultsCache
|
|
2151
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
2438
2152
|
|
|
2439
2153
|
return s0;
|
|
2440
2154
|
}
|
|
@@ -2443,7 +2157,7 @@ function peg$parse(input, options) {
|
|
|
2443
2157
|
let s0, s1, s3, s5;
|
|
2444
2158
|
|
|
2445
2159
|
const key = peg$currPos * 38 + 28;
|
|
2446
|
-
const cached = peg$resultsCache
|
|
2160
|
+
const cached = peg$resultsCache.get(key);
|
|
2447
2161
|
|
|
2448
2162
|
if (cached) {
|
|
2449
2163
|
peg$currPos = cached.nextPos;
|
|
@@ -2492,7 +2206,7 @@ function peg$parse(input, options) {
|
|
|
2492
2206
|
if (peg$silentFails === 0) { peg$fail(peg$e37); }
|
|
2493
2207
|
}
|
|
2494
2208
|
|
|
2495
|
-
peg$resultsCache
|
|
2209
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
2496
2210
|
|
|
2497
2211
|
return s0;
|
|
2498
2212
|
}
|
|
@@ -2501,134 +2215,71 @@ function peg$parse(input, options) {
|
|
|
2501
2215
|
let s0, s1, s2, s3, s4;
|
|
2502
2216
|
|
|
2503
2217
|
const key = peg$currPos * 38 + 29;
|
|
2504
|
-
const cached = peg$resultsCache
|
|
2218
|
+
const cached = peg$resultsCache.get(key);
|
|
2505
2219
|
|
|
2506
2220
|
if (cached) {
|
|
2507
2221
|
peg$currPos = cached.nextPos;
|
|
2508
2222
|
|
|
2509
2223
|
return cached.result;
|
|
2510
2224
|
}
|
|
2511
|
-
|
|
2512
|
-
peg$silentFails++;
|
|
2513
|
-
s0 = peg$currPos;
|
|
2514
|
-
s1 = [];
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
peg$currPos++;
|
|
2520
|
-
} else {
|
|
2521
|
-
s4 = peg$FAILED;
|
|
2522
|
-
if (peg$silentFails === 0) { peg$fail(peg$e40); }
|
|
2523
|
-
}
|
|
2524
|
-
if (s4 !== peg$FAILED) {
|
|
2525
|
-
while (s4 !== peg$FAILED) {
|
|
2526
|
-
s3.push(s4);
|
|
2527
|
-
s4 = input.charAt(peg$currPos);
|
|
2528
|
-
if (peg$r4.test(s4)) {
|
|
2529
|
-
peg$currPos++;
|
|
2530
|
-
} else {
|
|
2531
|
-
s4 = peg$FAILED;
|
|
2532
|
-
if (peg$silentFails === 0) { peg$fail(peg$e40); }
|
|
2533
|
-
}
|
|
2534
|
-
}
|
|
2535
|
-
} else {
|
|
2536
|
-
s3 = peg$FAILED;
|
|
2537
|
-
}
|
|
2538
|
-
if (s3 !== peg$FAILED) {
|
|
2539
|
-
s2 = input.substring(s2, peg$currPos);
|
|
2540
|
-
} else {
|
|
2541
|
-
s2 = s3;
|
|
2542
|
-
}
|
|
2543
|
-
if (s2 === peg$FAILED) {
|
|
2544
|
-
s2 = peg$currPos;
|
|
2545
|
-
if (input.charCodeAt(peg$currPos) === 92) {
|
|
2546
|
-
s3 = peg$c7;
|
|
2547
|
-
peg$currPos++;
|
|
2548
|
-
} else {
|
|
2549
|
-
s3 = peg$FAILED;
|
|
2550
|
-
if (peg$silentFails === 0) { peg$fail(peg$e7); }
|
|
2551
|
-
}
|
|
2552
|
-
if (s3 !== peg$FAILED) {
|
|
2553
|
-
if (input.length > peg$currPos) {
|
|
2554
|
-
s4 = input.charAt(peg$currPos);
|
|
2555
|
-
peg$currPos++;
|
|
2556
|
-
} else {
|
|
2557
|
-
s4 = peg$FAILED;
|
|
2558
|
-
if (peg$silentFails === 0) { peg$fail(peg$e8); }
|
|
2559
|
-
}
|
|
2560
|
-
if (s4 !== peg$FAILED) {
|
|
2561
|
-
s2 = peg$f28(s4);
|
|
2562
|
-
} else {
|
|
2563
|
-
peg$currPos = s2;
|
|
2564
|
-
s2 = peg$FAILED;
|
|
2565
|
-
}
|
|
2566
|
-
} else {
|
|
2567
|
-
peg$currPos = s2;
|
|
2568
|
-
s2 = peg$FAILED;
|
|
2569
|
-
}
|
|
2570
|
-
}
|
|
2571
|
-
if (s2 !== peg$FAILED) {
|
|
2572
|
-
while (s2 !== peg$FAILED) {
|
|
2573
|
-
s1.push(s2);
|
|
2574
|
-
s2 = peg$currPos;
|
|
2575
|
-
s3 = [];
|
|
2225
|
+
|
|
2226
|
+
peg$silentFails++;
|
|
2227
|
+
s0 = peg$currPos;
|
|
2228
|
+
s1 = [];
|
|
2229
|
+
for (;;) {
|
|
2230
|
+
s2 = peg$currPos;
|
|
2231
|
+
s3 = [];
|
|
2232
|
+
for (;;) {
|
|
2576
2233
|
s4 = input.charAt(peg$currPos);
|
|
2577
|
-
if (peg$
|
|
2234
|
+
if (s4 !== "" && !(peg$charCode = s4.charCodeAt(0), peg$charCode === 92 || peg$charCode === 96 || peg$charCode === 123)) {
|
|
2578
2235
|
peg$currPos++;
|
|
2579
2236
|
} else {
|
|
2580
2237
|
s4 = peg$FAILED;
|
|
2581
2238
|
if (peg$silentFails === 0) { peg$fail(peg$e40); }
|
|
2582
2239
|
}
|
|
2583
|
-
if (s4
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2240
|
+
if (s4 === peg$FAILED) { break; }
|
|
2241
|
+
s3.push(s4);
|
|
2242
|
+
}
|
|
2243
|
+
if (s3.length < 1) {
|
|
2244
|
+
s3 = peg$FAILED;
|
|
2245
|
+
}
|
|
2246
|
+
if (s3 !== peg$FAILED) {
|
|
2247
|
+
s2 = input.substring(s2, peg$currPos);
|
|
2248
|
+
} else {
|
|
2249
|
+
s2 = s3;
|
|
2250
|
+
}
|
|
2251
|
+
if (s2 === peg$FAILED) {
|
|
2252
|
+
s2 = peg$currPos;
|
|
2253
|
+
if (input.charCodeAt(peg$currPos) === 92) {
|
|
2254
|
+
s3 = peg$c7;
|
|
2255
|
+
peg$currPos++;
|
|
2594
2256
|
} else {
|
|
2595
2257
|
s3 = peg$FAILED;
|
|
2258
|
+
if (peg$silentFails === 0) { peg$fail(peg$e7); }
|
|
2596
2259
|
}
|
|
2597
2260
|
if (s3 !== peg$FAILED) {
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
s2 = s3;
|
|
2601
|
-
}
|
|
2602
|
-
if (s2 === peg$FAILED) {
|
|
2603
|
-
s2 = peg$currPos;
|
|
2604
|
-
if (input.charCodeAt(peg$currPos) === 92) {
|
|
2605
|
-
s3 = peg$c7;
|
|
2261
|
+
if (input.length > peg$currPos) {
|
|
2262
|
+
s4 = input.charAt(peg$currPos);
|
|
2606
2263
|
peg$currPos++;
|
|
2607
2264
|
} else {
|
|
2608
|
-
|
|
2609
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2265
|
+
s4 = peg$FAILED;
|
|
2266
|
+
if (peg$silentFails === 0) { peg$fail(peg$e8); }
|
|
2610
2267
|
}
|
|
2611
|
-
if (
|
|
2612
|
-
|
|
2613
|
-
s4 = input.charAt(peg$currPos);
|
|
2614
|
-
peg$currPos++;
|
|
2615
|
-
} else {
|
|
2616
|
-
s4 = peg$FAILED;
|
|
2617
|
-
if (peg$silentFails === 0) { peg$fail(peg$e8); }
|
|
2618
|
-
}
|
|
2619
|
-
if (s4 !== peg$FAILED) {
|
|
2620
|
-
s2 = peg$f28(s4);
|
|
2621
|
-
} else {
|
|
2622
|
-
peg$currPos = s2;
|
|
2623
|
-
s2 = peg$FAILED;
|
|
2624
|
-
}
|
|
2268
|
+
if (s4 !== peg$FAILED) {
|
|
2269
|
+
s2 = peg$f28(s4);
|
|
2625
2270
|
} else {
|
|
2626
2271
|
peg$currPos = s2;
|
|
2627
2272
|
s2 = peg$FAILED;
|
|
2628
2273
|
}
|
|
2274
|
+
} else {
|
|
2275
|
+
peg$currPos = s2;
|
|
2276
|
+
s2 = peg$FAILED;
|
|
2629
2277
|
}
|
|
2630
2278
|
}
|
|
2631
|
-
|
|
2279
|
+
if (s2 === peg$FAILED) { break; }
|
|
2280
|
+
s1.push(s2);
|
|
2281
|
+
}
|
|
2282
|
+
if (s1.length < 1) {
|
|
2632
2283
|
s1 = peg$FAILED;
|
|
2633
2284
|
}
|
|
2634
2285
|
if (s1 !== peg$FAILED) {
|
|
@@ -2655,7 +2306,7 @@ function peg$parse(input, options) {
|
|
|
2655
2306
|
if (peg$silentFails === 0) { peg$fail(peg$e39); }
|
|
2656
2307
|
}
|
|
2657
2308
|
|
|
2658
|
-
peg$resultsCache
|
|
2309
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
2659
2310
|
|
|
2660
2311
|
return s0;
|
|
2661
2312
|
}
|
|
@@ -2664,7 +2315,7 @@ function peg$parse(input, options) {
|
|
|
2664
2315
|
let s0, s1, s3, s5, s6, s7, s9;
|
|
2665
2316
|
|
|
2666
2317
|
const key = peg$currPos * 38 + 30;
|
|
2667
|
-
const cached = peg$resultsCache
|
|
2318
|
+
const cached = peg$resultsCache.get(key);
|
|
2668
2319
|
|
|
2669
2320
|
if (cached) {
|
|
2670
2321
|
peg$currPos = cached.nextPos;
|
|
@@ -2689,30 +2340,7 @@ function peg$parse(input, options) {
|
|
|
2689
2340
|
}
|
|
2690
2341
|
peg$parse_();
|
|
2691
2342
|
s5 = [];
|
|
2692
|
-
|
|
2693
|
-
if (input.charCodeAt(peg$currPos) === 44) {
|
|
2694
|
-
s7 = peg$c34;
|
|
2695
|
-
peg$currPos++;
|
|
2696
|
-
} else {
|
|
2697
|
-
s7 = peg$FAILED;
|
|
2698
|
-
if (peg$silentFails === 0) { peg$fail(peg$e25); }
|
|
2699
|
-
}
|
|
2700
|
-
if (s7 !== peg$FAILED) {
|
|
2701
|
-
peg$parse_();
|
|
2702
|
-
s9 = peg$parseTernaryExpression();
|
|
2703
|
-
if (s9 !== peg$FAILED) {
|
|
2704
|
-
peg$parse_();
|
|
2705
|
-
s6 = s9;
|
|
2706
|
-
} else {
|
|
2707
|
-
peg$currPos = s6;
|
|
2708
|
-
s6 = peg$FAILED;
|
|
2709
|
-
}
|
|
2710
|
-
} else {
|
|
2711
|
-
peg$currPos = s6;
|
|
2712
|
-
s6 = peg$FAILED;
|
|
2713
|
-
}
|
|
2714
|
-
while (s6 !== peg$FAILED) {
|
|
2715
|
-
s5.push(s6);
|
|
2343
|
+
for (;;) {
|
|
2716
2344
|
s6 = peg$currPos;
|
|
2717
2345
|
if (input.charCodeAt(peg$currPos) === 44) {
|
|
2718
2346
|
s7 = peg$c34;
|
|
@@ -2735,6 +2363,8 @@ function peg$parse(input, options) {
|
|
|
2735
2363
|
peg$currPos = s6;
|
|
2736
2364
|
s6 = peg$FAILED;
|
|
2737
2365
|
}
|
|
2366
|
+
if (s6 === peg$FAILED) { break; }
|
|
2367
|
+
s5.push(s6);
|
|
2738
2368
|
}
|
|
2739
2369
|
if (input.charCodeAt(peg$currPos) === 93) {
|
|
2740
2370
|
s6 = peg$c31;
|
|
@@ -2759,7 +2389,7 @@ function peg$parse(input, options) {
|
|
|
2759
2389
|
if (peg$silentFails === 0) { peg$fail(peg$e41); }
|
|
2760
2390
|
}
|
|
2761
2391
|
|
|
2762
|
-
peg$resultsCache
|
|
2392
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
2763
2393
|
|
|
2764
2394
|
return s0;
|
|
2765
2395
|
}
|
|
@@ -2768,7 +2398,7 @@ function peg$parse(input, options) {
|
|
|
2768
2398
|
let s0, s1, s3, s4, s5, s6, s7, s8, s9, s11, s13;
|
|
2769
2399
|
|
|
2770
2400
|
const key = peg$currPos * 38 + 31;
|
|
2771
|
-
const cached = peg$resultsCache
|
|
2401
|
+
const cached = peg$resultsCache.get(key);
|
|
2772
2402
|
|
|
2773
2403
|
if (cached) {
|
|
2774
2404
|
peg$currPos = cached.nextPos;
|
|
@@ -2820,50 +2450,7 @@ function peg$parse(input, options) {
|
|
|
2820
2450
|
}
|
|
2821
2451
|
s4 = peg$parse_();
|
|
2822
2452
|
s5 = [];
|
|
2823
|
-
|
|
2824
|
-
if (input.charCodeAt(peg$currPos) === 44) {
|
|
2825
|
-
s7 = peg$c34;
|
|
2826
|
-
peg$currPos++;
|
|
2827
|
-
} else {
|
|
2828
|
-
s7 = peg$FAILED;
|
|
2829
|
-
if (peg$silentFails === 0) { peg$fail(peg$e25); }
|
|
2830
|
-
}
|
|
2831
|
-
if (s7 !== peg$FAILED) {
|
|
2832
|
-
s8 = peg$parse_();
|
|
2833
|
-
s9 = peg$parseStringLiteral();
|
|
2834
|
-
if (s9 !== peg$FAILED) {
|
|
2835
|
-
peg$parse_();
|
|
2836
|
-
if (input.charCodeAt(peg$currPos) === 58) {
|
|
2837
|
-
s11 = peg$c15;
|
|
2838
|
-
peg$currPos++;
|
|
2839
|
-
} else {
|
|
2840
|
-
s11 = peg$FAILED;
|
|
2841
|
-
if (peg$silentFails === 0) { peg$fail(peg$e11); }
|
|
2842
|
-
}
|
|
2843
|
-
if (s11 !== peg$FAILED) {
|
|
2844
|
-
peg$parse_();
|
|
2845
|
-
s13 = peg$parseTernaryExpression();
|
|
2846
|
-
if (s13 !== peg$FAILED) {
|
|
2847
|
-
peg$parse_();
|
|
2848
|
-
s6 = [ s9, s13 ];
|
|
2849
|
-
} else {
|
|
2850
|
-
peg$currPos = s6;
|
|
2851
|
-
s6 = peg$FAILED;
|
|
2852
|
-
}
|
|
2853
|
-
} else {
|
|
2854
|
-
peg$currPos = s6;
|
|
2855
|
-
s6 = peg$FAILED;
|
|
2856
|
-
}
|
|
2857
|
-
} else {
|
|
2858
|
-
peg$currPos = s6;
|
|
2859
|
-
s6 = peg$FAILED;
|
|
2860
|
-
}
|
|
2861
|
-
} else {
|
|
2862
|
-
peg$currPos = s6;
|
|
2863
|
-
s6 = peg$FAILED;
|
|
2864
|
-
}
|
|
2865
|
-
while (s6 !== peg$FAILED) {
|
|
2866
|
-
s5.push(s6);
|
|
2453
|
+
for (;;) {
|
|
2867
2454
|
s6 = peg$currPos;
|
|
2868
2455
|
if (input.charCodeAt(peg$currPos) === 44) {
|
|
2869
2456
|
s7 = peg$c34;
|
|
@@ -2906,6 +2493,8 @@ function peg$parse(input, options) {
|
|
|
2906
2493
|
peg$currPos = s6;
|
|
2907
2494
|
s6 = peg$FAILED;
|
|
2908
2495
|
}
|
|
2496
|
+
if (s6 === peg$FAILED) { break; }
|
|
2497
|
+
s5.push(s6);
|
|
2909
2498
|
}
|
|
2910
2499
|
if (input.charCodeAt(peg$currPos) === 125) {
|
|
2911
2500
|
s6 = peg$c43;
|
|
@@ -2930,7 +2519,7 @@ function peg$parse(input, options) {
|
|
|
2930
2519
|
if (peg$silentFails === 0) { peg$fail(peg$e42); }
|
|
2931
2520
|
}
|
|
2932
2521
|
|
|
2933
|
-
peg$resultsCache
|
|
2522
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
2934
2523
|
|
|
2935
2524
|
return s0;
|
|
2936
2525
|
}
|
|
@@ -2939,7 +2528,7 @@ function peg$parse(input, options) {
|
|
|
2939
2528
|
let s0, s1, s2, s3, s4;
|
|
2940
2529
|
|
|
2941
2530
|
const key = peg$currPos * 38 + 32;
|
|
2942
|
-
const cached = peg$resultsCache
|
|
2531
|
+
const cached = peg$resultsCache.get(key);
|
|
2943
2532
|
|
|
2944
2533
|
if (cached) {
|
|
2945
2534
|
peg$currPos = cached.nextPos;
|
|
@@ -2997,7 +2586,7 @@ function peg$parse(input, options) {
|
|
|
2997
2586
|
if (peg$silentFails === 0) { peg$fail(peg$e43); }
|
|
2998
2587
|
}
|
|
2999
2588
|
|
|
3000
|
-
peg$resultsCache
|
|
2589
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
3001
2590
|
|
|
3002
2591
|
return s0;
|
|
3003
2592
|
}
|
|
@@ -3006,7 +2595,7 @@ function peg$parse(input, options) {
|
|
|
3006
2595
|
let s0, s1, s2, s3, s4;
|
|
3007
2596
|
|
|
3008
2597
|
const key = peg$currPos * 38 + 33;
|
|
3009
|
-
const cached = peg$resultsCache
|
|
2598
|
+
const cached = peg$resultsCache.get(key);
|
|
3010
2599
|
|
|
3011
2600
|
if (cached) {
|
|
3012
2601
|
peg$currPos = cached.nextPos;
|
|
@@ -3017,7 +2606,7 @@ function peg$parse(input, options) {
|
|
|
3017
2606
|
s0 = peg$currPos;
|
|
3018
2607
|
s1 = peg$currPos;
|
|
3019
2608
|
s2 = input.charAt(peg$currPos);
|
|
3020
|
-
if (peg$
|
|
2609
|
+
if ((peg$charCode = s2.charCodeAt(0), (peg$charCode >= 97 && peg$charCode <= 122) || (peg$charCode >= 65 && peg$charCode <= 90) || peg$charCode === 36 || peg$charCode === 95)) {
|
|
3021
2610
|
peg$currPos++;
|
|
3022
2611
|
} else {
|
|
3023
2612
|
s2 = peg$FAILED;
|
|
@@ -3025,22 +2614,16 @@ function peg$parse(input, options) {
|
|
|
3025
2614
|
}
|
|
3026
2615
|
if (s2 !== peg$FAILED) {
|
|
3027
2616
|
s3 = [];
|
|
3028
|
-
|
|
3029
|
-
if (peg$r6.test(s4)) {
|
|
3030
|
-
peg$currPos++;
|
|
3031
|
-
} else {
|
|
3032
|
-
s4 = peg$FAILED;
|
|
3033
|
-
if (peg$silentFails === 0) { peg$fail(peg$e46); }
|
|
3034
|
-
}
|
|
3035
|
-
while (s4 !== peg$FAILED) {
|
|
3036
|
-
s3.push(s4);
|
|
2617
|
+
for (;;) {
|
|
3037
2618
|
s4 = input.charAt(peg$currPos);
|
|
3038
|
-
if (peg$
|
|
2619
|
+
if ((peg$charCode = s4.charCodeAt(0), (peg$charCode >= 97 && peg$charCode <= 122) || (peg$charCode >= 65 && peg$charCode <= 90) || peg$charCode === 36 || peg$charCode === 95 || (peg$charCode >= 48 && peg$charCode <= 57))) {
|
|
3039
2620
|
peg$currPos++;
|
|
3040
2621
|
} else {
|
|
3041
2622
|
s4 = peg$FAILED;
|
|
3042
2623
|
if (peg$silentFails === 0) { peg$fail(peg$e46); }
|
|
3043
2624
|
}
|
|
2625
|
+
if (s4 === peg$FAILED) { break; }
|
|
2626
|
+
s3.push(s4);
|
|
3044
2627
|
}
|
|
3045
2628
|
s2 = [s2, s3];
|
|
3046
2629
|
s1 = s2;
|
|
@@ -3054,7 +2637,7 @@ function peg$parse(input, options) {
|
|
|
3054
2637
|
s0 = s1;
|
|
3055
2638
|
}
|
|
3056
2639
|
|
|
3057
|
-
peg$resultsCache
|
|
2640
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
3058
2641
|
|
|
3059
2642
|
return s0;
|
|
3060
2643
|
}
|
|
@@ -3063,7 +2646,7 @@ function peg$parse(input, options) {
|
|
|
3063
2646
|
let s0, s1;
|
|
3064
2647
|
|
|
3065
2648
|
const key = peg$currPos * 38 + 34;
|
|
3066
|
-
const cached = peg$resultsCache
|
|
2649
|
+
const cached = peg$resultsCache.get(key);
|
|
3067
2650
|
|
|
3068
2651
|
if (cached) {
|
|
3069
2652
|
peg$currPos = cached.nextPos;
|
|
@@ -3084,7 +2667,7 @@ function peg$parse(input, options) {
|
|
|
3084
2667
|
if (peg$silentFails === 0) { peg$fail(peg$e47); }
|
|
3085
2668
|
}
|
|
3086
2669
|
|
|
3087
|
-
peg$resultsCache
|
|
2670
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
3088
2671
|
|
|
3089
2672
|
return s0;
|
|
3090
2673
|
}
|
|
@@ -3093,7 +2676,7 @@ function peg$parse(input, options) {
|
|
|
3093
2676
|
let s0;
|
|
3094
2677
|
|
|
3095
2678
|
const key = peg$currPos * 38 + 35;
|
|
3096
|
-
const cached = peg$resultsCache
|
|
2679
|
+
const cached = peg$resultsCache.get(key);
|
|
3097
2680
|
|
|
3098
2681
|
if (cached) {
|
|
3099
2682
|
peg$currPos = cached.nextPos;
|
|
@@ -3118,7 +2701,7 @@ function peg$parse(input, options) {
|
|
|
3118
2701
|
}
|
|
3119
2702
|
}
|
|
3120
2703
|
|
|
3121
|
-
peg$resultsCache
|
|
2704
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
3122
2705
|
|
|
3123
2706
|
return s0;
|
|
3124
2707
|
}
|
|
@@ -3127,7 +2710,7 @@ function peg$parse(input, options) {
|
|
|
3127
2710
|
let s0;
|
|
3128
2711
|
|
|
3129
2712
|
const key = peg$currPos * 38 + 36;
|
|
3130
|
-
const cached = peg$resultsCache
|
|
2713
|
+
const cached = peg$resultsCache.get(key);
|
|
3131
2714
|
|
|
3132
2715
|
if (cached) {
|
|
3133
2716
|
peg$currPos = cached.nextPos;
|
|
@@ -3170,7 +2753,7 @@ function peg$parse(input, options) {
|
|
|
3170
2753
|
}
|
|
3171
2754
|
}
|
|
3172
2755
|
|
|
3173
|
-
peg$resultsCache
|
|
2756
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
3174
2757
|
|
|
3175
2758
|
return s0;
|
|
3176
2759
|
}
|
|
@@ -3179,7 +2762,7 @@ function peg$parse(input, options) {
|
|
|
3179
2762
|
let s0, s1;
|
|
3180
2763
|
|
|
3181
2764
|
const key = peg$currPos * 38 + 37;
|
|
3182
|
-
const cached = peg$resultsCache
|
|
2765
|
+
const cached = peg$resultsCache.get(key);
|
|
3183
2766
|
|
|
3184
2767
|
if (cached) {
|
|
3185
2768
|
peg$currPos = cached.nextPos;
|
|
@@ -3189,26 +2772,20 @@ function peg$parse(input, options) {
|
|
|
3189
2772
|
|
|
3190
2773
|
peg$silentFails++;
|
|
3191
2774
|
s0 = [];
|
|
3192
|
-
|
|
3193
|
-
if (peg$r7.test(s1)) {
|
|
3194
|
-
peg$currPos++;
|
|
3195
|
-
} else {
|
|
3196
|
-
s1 = peg$FAILED;
|
|
3197
|
-
if (peg$silentFails === 0) { peg$fail(peg$e54); }
|
|
3198
|
-
}
|
|
3199
|
-
while (s1 !== peg$FAILED) {
|
|
3200
|
-
s0.push(s1);
|
|
2775
|
+
for (;;) {
|
|
3201
2776
|
s1 = input.charAt(peg$currPos);
|
|
3202
|
-
if (peg$
|
|
2777
|
+
if ((peg$charCode = s1.charCodeAt(0), peg$charCode === 32 || peg$charCode === 9 || peg$charCode === 10 || peg$charCode === 13)) {
|
|
3203
2778
|
peg$currPos++;
|
|
3204
2779
|
} else {
|
|
3205
2780
|
s1 = peg$FAILED;
|
|
3206
2781
|
if (peg$silentFails === 0) { peg$fail(peg$e54); }
|
|
3207
2782
|
}
|
|
2783
|
+
if (s1 === peg$FAILED) { break; }
|
|
2784
|
+
s0.push(s1);
|
|
3208
2785
|
}
|
|
3209
2786
|
peg$silentFails--;
|
|
3210
2787
|
|
|
3211
|
-
peg$resultsCache
|
|
2788
|
+
peg$resultsCache.set(key, { nextPos: peg$currPos, result: s0 });
|
|
3212
2789
|
|
|
3213
2790
|
return s0;
|
|
3214
2791
|
}
|
|
@@ -3250,10 +2827,10 @@ function peg$parse(input, options) {
|
|
|
3250
2827
|
/**
|
|
3251
2828
|
* The one lookup against a data stack: the innermost overlay carrying the name
|
|
3252
2829
|
* wins, `self` is the innermost overlay itself, and a function overlay counts
|
|
3253
|
-
* like an object one.
|
|
3254
|
-
* imperative facades cannot drift from what a template sees.
|
|
2830
|
+
* like an object one.
|
|
3255
2831
|
* @param {any[]} dataStack
|
|
3256
2832
|
* @param {string|symbol} prop
|
|
2833
|
+
* @returns {any} undefined when no overlay carries the name
|
|
3257
2834
|
*/
|
|
3258
2835
|
const resolveInStack = (dataStack, prop) => {
|
|
3259
2836
|
if (prop === 'self') {
|
|
@@ -3270,6 +2847,8 @@ const resolveInStack = (dataStack, prop) => {
|
|
|
3270
2847
|
return undefined;
|
|
3271
2848
|
};
|
|
3272
2849
|
|
|
2850
|
+
const BOUND = Symbol('BOUND');
|
|
2851
|
+
|
|
3273
2852
|
/**
|
|
3274
2853
|
* Evaluates a parsed expression against a scope. One instance per evaluation:
|
|
3275
2854
|
* it holds the modules and the data stack that names resolve against, and keeps
|
|
@@ -3278,9 +2857,11 @@ const resolveInStack = (dataStack, prop) => {
|
|
|
3278
2857
|
class EvaluatingVisitor {
|
|
3279
2858
|
#modules;
|
|
3280
2859
|
#dataStack;
|
|
3281
|
-
|
|
2860
|
+
#bound;
|
|
2861
|
+
constructor(modules, dataStack, bound) {
|
|
3282
2862
|
this.#modules = modules;
|
|
3283
2863
|
this.#dataStack = dataStack;
|
|
2864
|
+
this.#bound = bound ?? dataStack.length;
|
|
3284
2865
|
}
|
|
3285
2866
|
#resolve(prop) {
|
|
3286
2867
|
return resolveInStack(this.#dataStack, prop);
|
|
@@ -3304,8 +2885,9 @@ class EvaluatingVisitor {
|
|
|
3304
2885
|
#cached_resolve_proxy;
|
|
3305
2886
|
#resolve_proxy() {
|
|
3306
2887
|
if (!this.#cached_resolve_proxy) {
|
|
2888
|
+
const bound = (name) => resolveInStack(this.#dataStack.slice(0, this.#bound), name);
|
|
3307
2889
|
this.#cached_resolve_proxy = new Proxy(this.#dataStack, {
|
|
3308
|
-
get: (target, prop) => this.#resolve(prop),
|
|
2890
|
+
get: (target, prop) => (prop === BOUND ? bound : this.#resolve(prop)),
|
|
3309
2891
|
});
|
|
3310
2892
|
}
|
|
3311
2893
|
return this.#cached_resolve_proxy;
|
|
@@ -3317,8 +2899,7 @@ class EvaluatingVisitor {
|
|
|
3317
2899
|
return this.visit(node.lhs) || this.visit(node.rhs);
|
|
3318
2900
|
}
|
|
3319
2901
|
[nodes.nullc](node) {
|
|
3320
|
-
|
|
3321
|
-
return lhs !== null && lhs !== undefined ? lhs : this.visit(node.rhs);
|
|
2902
|
+
return this.visit(node.lhs) ?? this.visit(node.rhs);
|
|
3322
2903
|
}
|
|
3323
2904
|
[nodes.not](node) {
|
|
3324
2905
|
return !this.visit(node.expr);
|
|
@@ -3385,8 +2966,7 @@ class EvaluatingVisitor {
|
|
|
3385
2966
|
return this.visit(node.cond) ? this.visit(node.ifTrue) : this.visit(node.ifFalse);
|
|
3386
2967
|
}
|
|
3387
2968
|
[nodes.elv](node) {
|
|
3388
|
-
|
|
3389
|
-
return cond ? cond : this.visit(node.ifFalse);
|
|
2969
|
+
return this.visit(node.cond) || this.visit(node.ifFalse);
|
|
3390
2970
|
}
|
|
3391
2971
|
[nodes.access](node) {
|
|
3392
2972
|
let prev;
|
|
@@ -3452,16 +3032,33 @@ class EvaluatingVisitor {
|
|
|
3452
3032
|
* spliced into the expression text.
|
|
3453
3033
|
*/
|
|
3454
3034
|
class Expressions {
|
|
3035
|
+
/** Parses the whole text as one expression: `user.name`. */
|
|
3455
3036
|
static MODE_EXPRESSION = Symbol('MODE_EXPRESSION');
|
|
3037
|
+
/**
|
|
3038
|
+
* Parses text with interpolations, `Hello {{ user.name }}`, evaluating to
|
|
3039
|
+
* its parts: `{ type, value }` for each literal run and each `{{ }}` (text),
|
|
3040
|
+
* `{{{ }}}` (html) or `{{{{ }}}}` (node) interpolation, in order.
|
|
3041
|
+
*/
|
|
3456
3042
|
static MODE_TEMPLATED = Symbol('MODE_TEMPLATED');
|
|
3043
|
+
/**
|
|
3044
|
+
* The key under which the receiver of a module function answers a lookup
|
|
3045
|
+
* against the bound data alone: `this[Expressions.BOUND](name)` resolves
|
|
3046
|
+
* the name in the layers the scope was created with (a registry's data,
|
|
3047
|
+
* the data handed to a Template factory) and never in an overlay pushed
|
|
3048
|
+
* afterwards, by `data-tpl-each`, `data-tpl-with` or `withOverlay`. It is
|
|
3049
|
+
* how a module reads what the page configured without the rendered data
|
|
3050
|
+
* being able to supply or replace it. No name on the data stack reaches it.
|
|
3051
|
+
*/
|
|
3052
|
+
static BOUND = BOUND;
|
|
3457
3053
|
|
|
3458
3054
|
static #astCache = new BoundedCache(1000);
|
|
3459
3055
|
|
|
3460
3056
|
/**
|
|
3461
|
-
* Parses an expression.
|
|
3057
|
+
* Parses an expression, caching the ast by mode and text.
|
|
3462
3058
|
* @param {string} expression
|
|
3463
|
-
* @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode]
|
|
3464
|
-
* @returns the ast
|
|
3059
|
+
* @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode] MODE_EXPRESSION when omitted
|
|
3060
|
+
* @returns {any} the ast
|
|
3061
|
+
* @throws a peggy syntax error when the text does not parse
|
|
3465
3062
|
*/
|
|
3466
3063
|
static parse(expression, mode) {
|
|
3467
3064
|
const key = mode?.toString() + expression;
|
|
@@ -3472,26 +3069,28 @@ class Expressions {
|
|
|
3472
3069
|
);
|
|
3473
3070
|
}
|
|
3474
3071
|
/**
|
|
3475
|
-
* Evaluates
|
|
3072
|
+
* Evaluates a parsed ast against the modules and the data stack.
|
|
3476
3073
|
* @param {{[k: string]: any } | null | undefined } modules
|
|
3477
|
-
* @param {any[]} dataStack
|
|
3478
|
-
* @param {any} ast
|
|
3479
|
-
* @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode]
|
|
3480
|
-
* @
|
|
3074
|
+
* @param {any[]} dataStack innermost overlay last
|
|
3075
|
+
* @param {any} ast what `parse` answered
|
|
3076
|
+
* @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode] the mode it was parsed in
|
|
3077
|
+
* @param {number} [bound] how many layers, from the outermost, are the bound data; all of them when omitted
|
|
3078
|
+
* @returns {any} the value, or the list of parts in MODE_TEMPLATED
|
|
3481
3079
|
*/
|
|
3482
|
-
static evaluate(modules, dataStack, ast, mode) {
|
|
3483
|
-
return new EvaluatingVisitor(modules, dataStack).visitRoot(ast, mode === Expressions.MODE_TEMPLATED);
|
|
3080
|
+
static evaluate(modules, dataStack, ast, mode, bound) {
|
|
3081
|
+
return new EvaluatingVisitor(modules, dataStack, bound).visitRoot(ast, mode === Expressions.MODE_TEMPLATED);
|
|
3484
3082
|
}
|
|
3485
3083
|
/**
|
|
3486
3084
|
* Parses and evaluates an expression.
|
|
3487
3085
|
* @param {{ [x: string]: any; } | null | undefined} modules
|
|
3488
|
-
* @param {any[]} dataStack
|
|
3086
|
+
* @param {any[]} dataStack innermost overlay last
|
|
3489
3087
|
* @param {string} expression
|
|
3490
|
-
* @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode]
|
|
3491
|
-
* @
|
|
3088
|
+
* @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode] MODE_EXPRESSION when omitted
|
|
3089
|
+
* @param {number} [bound] how many layers, from the outermost, are the bound data; all of them when omitted
|
|
3090
|
+
* @returns {any} the value, or the list of parts in MODE_TEMPLATED
|
|
3492
3091
|
*/
|
|
3493
|
-
static interpret(modules, dataStack, expression, mode) {
|
|
3494
|
-
return Expressions.evaluate(modules, dataStack, Expressions.parse(expression, mode), mode);
|
|
3092
|
+
static interpret(modules, dataStack, expression, mode, bound) {
|
|
3093
|
+
return Expressions.evaluate(modules, dataStack, Expressions.parse(expression, mode), mode, bound);
|
|
3495
3094
|
}
|
|
3496
3095
|
}
|
|
3497
3096
|
|
|
@@ -3500,48 +3099,89 @@ class Expressions {
|
|
|
3500
3099
|
* that a bare identifier resolves against, as a single value. Adding an overlay
|
|
3501
3100
|
* returns a new evaluator instead of modifying this one, so a template can add
|
|
3502
3101
|
* data for one subtree without affecting the rest of the render.
|
|
3102
|
+
*
|
|
3103
|
+
* The layers an evaluator is created with are its bound data; the overlays
|
|
3104
|
+
* pushed afterwards are not. A bare identifier resolves against both, while a
|
|
3105
|
+
* module function can ask for the bound data alone through `Expressions.BOUND`.
|
|
3503
3106
|
*/
|
|
3504
3107
|
class ExpressionEvaluator {
|
|
3505
3108
|
#modules;
|
|
3506
3109
|
#dataStack;
|
|
3507
|
-
|
|
3110
|
+
#bound;
|
|
3111
|
+
/**
|
|
3112
|
+
* @param {{ [k: string]: any } | null | undefined} modules
|
|
3113
|
+
* @param {any[]} dataStack innermost overlay last
|
|
3114
|
+
* @param {number} [bound] how many layers, from the outermost, are the bound data; all of them when omitted
|
|
3115
|
+
*/
|
|
3116
|
+
constructor(modules, dataStack, bound) {
|
|
3508
3117
|
this.#modules = modules;
|
|
3509
3118
|
this.#dataStack = dataStack;
|
|
3119
|
+
this.#bound = bound ?? dataStack.length;
|
|
3510
3120
|
}
|
|
3121
|
+
/**
|
|
3122
|
+
* A new evaluator with one more module: `#name:fn()` resolves into it, and an
|
|
3123
|
+
* empty name merges a whole map whose functions resolve bare, as `#fn()`.
|
|
3124
|
+
* @param {string} name
|
|
3125
|
+
* @param {object} value
|
|
3126
|
+
* @returns {ExpressionEvaluator}
|
|
3127
|
+
*/
|
|
3511
3128
|
withModule(name, value) {
|
|
3512
3129
|
const module = name ? { [name]: value } : value;
|
|
3513
|
-
return new ExpressionEvaluator({ ...this.#modules, ...module }, this.#dataStack);
|
|
3130
|
+
return new ExpressionEvaluator({ ...this.#modules, ...module }, this.#dataStack, this.#bound);
|
|
3514
3131
|
}
|
|
3132
|
+
/**
|
|
3133
|
+
* A new evaluator with the data pushed as the innermost overlays, the last
|
|
3134
|
+
* one winning a shared name. The bound data stays what it was.
|
|
3135
|
+
* @param {...any} data
|
|
3136
|
+
* @returns {ExpressionEvaluator}
|
|
3137
|
+
*/
|
|
3515
3138
|
withOverlay(...data) {
|
|
3516
3139
|
return new ExpressionEvaluator(
|
|
3517
3140
|
this.#modules,
|
|
3518
3141
|
data.length === 0 ? this.#dataStack : [...this.#dataStack, ...data],
|
|
3142
|
+
this.#bound,
|
|
3519
3143
|
);
|
|
3520
3144
|
}
|
|
3521
3145
|
/**
|
|
3522
3146
|
* Resolves a name against the data stack, with no parse round trip: the
|
|
3523
3147
|
* lookup a template's bare identifier makes, for an imperative caller.
|
|
3524
3148
|
* @param {string} name
|
|
3149
|
+
* @returns {any} undefined when no overlay carries the name
|
|
3525
3150
|
*/
|
|
3526
3151
|
resolve(name) {
|
|
3527
3152
|
return resolveInStack(this.#dataStack, name);
|
|
3528
3153
|
}
|
|
3529
|
-
/**
|
|
3154
|
+
/**
|
|
3155
|
+
* Evaluates an expression against this scope.
|
|
3156
|
+
* @param {string} expression
|
|
3157
|
+
* @returns {any}
|
|
3158
|
+
*/
|
|
3530
3159
|
evaluateExpression(expression) {
|
|
3531
|
-
return Expressions.interpret(
|
|
3160
|
+
return Expressions.interpret(
|
|
3161
|
+
this.#modules,
|
|
3162
|
+
this.#dataStack,
|
|
3163
|
+
expression,
|
|
3164
|
+
Expressions.MODE_EXPRESSION,
|
|
3165
|
+
this.#bound,
|
|
3166
|
+
);
|
|
3532
3167
|
}
|
|
3533
|
-
/**
|
|
3168
|
+
/**
|
|
3169
|
+
* Evaluates a templated text, the `{{ }}` form, against this scope.
|
|
3170
|
+
* @param {string} text
|
|
3171
|
+
* @returns {{ type: symbol, value: any }[]} its parts, as MODE_TEMPLATED describes
|
|
3172
|
+
*/
|
|
3534
3173
|
evaluateTemplated(text) {
|
|
3535
|
-
return Expressions.interpret(this.#modules, this.#dataStack, text, Expressions.MODE_TEMPLATED);
|
|
3174
|
+
return Expressions.interpret(this.#modules, this.#dataStack, text, Expressions.MODE_TEMPLATED, this.#bound);
|
|
3536
3175
|
}
|
|
3537
3176
|
}
|
|
3538
3177
|
|
|
3539
|
-
/** Creates and inspects
|
|
3178
|
+
/** Creates, serializes and inspects DocumentFragments. */
|
|
3540
3179
|
class Fragments {
|
|
3541
3180
|
/**
|
|
3542
|
-
*
|
|
3543
|
-
*
|
|
3544
|
-
* @
|
|
3181
|
+
* Parses html into a DocumentFragment adopted by the document, each piece
|
|
3182
|
+
* trimmed and the pieces joined.
|
|
3183
|
+
* @param {...string} html
|
|
3184
|
+
* @returns {DocumentFragment}
|
|
3545
3185
|
*/
|
|
3546
3186
|
static fromHtml(...html) {
|
|
3547
3187
|
const el = document.createElement('template');
|
|
@@ -3549,11 +3189,11 @@ class Fragments {
|
|
|
3549
3189
|
return document.adoptNode(el.content);
|
|
3550
3190
|
}
|
|
3551
3191
|
/**
|
|
3552
|
-
*
|
|
3553
|
-
*
|
|
3192
|
+
* Serializes a fragment, or a single node, to html, consuming it: the nodes
|
|
3193
|
+
* are moved out, not copied, and a fragment is left empty. Pass
|
|
3554
3194
|
* `fragment.cloneNode(true)` to keep the original usable.
|
|
3555
|
-
* @param {DocumentFragment} fragment
|
|
3556
|
-
* @returns {string}
|
|
3195
|
+
* @param {DocumentFragment|Node} fragment
|
|
3196
|
+
* @returns {string}
|
|
3557
3197
|
*/
|
|
3558
3198
|
static toHtml(fragment) {
|
|
3559
3199
|
var el = document.createElement('template');
|
|
@@ -3561,17 +3201,17 @@ class Fragments {
|
|
|
3561
3201
|
return el.innerHTML;
|
|
3562
3202
|
}
|
|
3563
3203
|
/**
|
|
3564
|
-
*
|
|
3204
|
+
* Whether a fragment holds no element and no text but whitespace.
|
|
3565
3205
|
* @param {DocumentFragment} fragment
|
|
3566
|
-
* @returns {boolean}
|
|
3206
|
+
* @returns {boolean}
|
|
3567
3207
|
*/
|
|
3568
3208
|
static isBlank(fragment) {
|
|
3569
3209
|
return fragment.childElementCount === 0 && fragment.textContent.trim() === '';
|
|
3570
3210
|
}
|
|
3571
3211
|
/**
|
|
3572
|
-
*
|
|
3573
|
-
* @param
|
|
3574
|
-
* @returns {DocumentFragment}
|
|
3212
|
+
* Moves the nodes, in order, into a new DocumentFragment.
|
|
3213
|
+
* @param {...Node} nodes
|
|
3214
|
+
* @returns {DocumentFragment}
|
|
3575
3215
|
*/
|
|
3576
3216
|
static from(...nodes) {
|
|
3577
3217
|
const fragment = new DocumentFragment();
|
|
@@ -3581,9 +3221,10 @@ class Fragments {
|
|
|
3581
3221
|
return fragment;
|
|
3582
3222
|
}
|
|
3583
3223
|
/**
|
|
3584
|
-
*
|
|
3224
|
+
* Moves the children of a node into a new DocumentFragment, leaving the
|
|
3225
|
+
* node empty.
|
|
3585
3226
|
* @param {Node} el
|
|
3586
|
-
* @returns {DocumentFragment}
|
|
3227
|
+
* @returns {DocumentFragment}
|
|
3587
3228
|
*/
|
|
3588
3229
|
static fromChildNodes(el) {
|
|
3589
3230
|
const fragment = new DocumentFragment();
|
|
@@ -3594,23 +3235,23 @@ class Fragments {
|
|
|
3594
3235
|
}
|
|
3595
3236
|
}
|
|
3596
3237
|
|
|
3597
|
-
/** Attribute
|
|
3238
|
+
/** Attribute helpers: unique ids, defaults, prefixed forwarding, and writes where a nullish value removes the attribute. */
|
|
3598
3239
|
class Attributes {
|
|
3599
3240
|
static id = 0;
|
|
3600
3241
|
/**
|
|
3601
|
-
*
|
|
3242
|
+
* A page-unique id under the prefix, as `prefix-n`.
|
|
3602
3243
|
* @param {string} prefix
|
|
3603
|
-
* @returns
|
|
3244
|
+
* @returns {string}
|
|
3604
3245
|
*/
|
|
3605
3246
|
static uid(prefix) {
|
|
3606
3247
|
return `${prefix}-${++Attributes.id}`;
|
|
3607
3248
|
}
|
|
3608
3249
|
/**
|
|
3609
|
-
* Sets an attribute
|
|
3250
|
+
* Sets an attribute when the element does not carry it.
|
|
3610
3251
|
* @param {Element} el
|
|
3611
3252
|
* @param {string} k
|
|
3612
3253
|
* @param {string} v
|
|
3613
|
-
* @returns
|
|
3254
|
+
* @returns {string|null} the attribute's value afterwards
|
|
3614
3255
|
*/
|
|
3615
3256
|
static defaultValue(el, k, v) {
|
|
3616
3257
|
if (!el.hasAttribute(k)) {
|
|
@@ -3619,7 +3260,9 @@ class Attributes {
|
|
|
3619
3260
|
return el.getAttribute(k);
|
|
3620
3261
|
}
|
|
3621
3262
|
/**
|
|
3622
|
-
*
|
|
3263
|
+
* Copies every attribute whose name starts with the prefix onto another
|
|
3264
|
+
* element, under the name without the prefix. `<prefix>class` adds its
|
|
3265
|
+
* classes to the target's instead of replacing them.
|
|
3623
3266
|
* @param {string} prefix
|
|
3624
3267
|
* @param {Element} from
|
|
3625
3268
|
* @param {Element} to
|
|
@@ -3642,7 +3285,7 @@ class Attributes {
|
|
|
3642
3285
|
});
|
|
3643
3286
|
}
|
|
3644
3287
|
/**
|
|
3645
|
-
* Sets
|
|
3288
|
+
* Sets an attribute, or removes it when the value is nullish.
|
|
3646
3289
|
* @param {Element} el
|
|
3647
3290
|
* @param {string} attr
|
|
3648
3291
|
* @param {string | null | undefined} value
|
|
@@ -3664,13 +3307,14 @@ class Attributes {
|
|
|
3664
3307
|
*/
|
|
3665
3308
|
class LightSlots {
|
|
3666
3309
|
/**
|
|
3667
|
-
*
|
|
3310
|
+
* Takes an element's children apart into slots, leaving the element empty.
|
|
3311
|
+
* A named child is removed and contributes what `slotFromNode` answers;
|
|
3312
|
+
* children sharing a name are gathered in order. A child with an empty
|
|
3313
|
+
* `slot` belongs to the default slot, in document order among the rest.
|
|
3668
3314
|
* @param {Element} el
|
|
3669
|
-
* @returns the slots
|
|
3315
|
+
* @returns {Record<string, DocumentFragment>} the slots by name, `default` always present
|
|
3670
3316
|
*/
|
|
3671
3317
|
static from(el) {
|
|
3672
|
-
//the platform reads slot="" as the default slot: such children stay in
|
|
3673
|
-
//the document order of the default content, their claim stripped like any named one
|
|
3674
3318
|
for (const child of el.children) {
|
|
3675
3319
|
if (child.matches('[slot=""]')) {
|
|
3676
3320
|
child.removeAttribute('slot');
|
|
@@ -3685,7 +3329,7 @@ class LightSlots {
|
|
|
3685
3329
|
el.removeAttribute('slot');
|
|
3686
3330
|
return [slot, LightSlots.slotFromNode(el)];
|
|
3687
3331
|
});
|
|
3688
|
-
const slots =
|
|
3332
|
+
const slots = Object.create(null);
|
|
3689
3333
|
slots.default = new DocumentFragment();
|
|
3690
3334
|
slots.default.append(...el.childNodes);
|
|
3691
3335
|
for (const [name, el] of namedSlots) {
|
|
@@ -3696,6 +3340,12 @@ class LightSlots {
|
|
|
3696
3340
|
}
|
|
3697
3341
|
return slots;
|
|
3698
3342
|
}
|
|
3343
|
+
/**
|
|
3344
|
+
* What a slotted node contributes: a template's content, the parsed markup
|
|
3345
|
+
* of a `<script type="text/html">`, or the node itself.
|
|
3346
|
+
* @param {Element} el
|
|
3347
|
+
* @returns {Element|DocumentFragment}
|
|
3348
|
+
*/
|
|
3699
3349
|
static slotFromNode(el) {
|
|
3700
3350
|
if (el instanceof HTMLTemplateElement) {
|
|
3701
3351
|
return document.adoptNode(el.content);
|
|
@@ -3707,12 +3357,13 @@ class LightSlots {
|
|
|
3707
3357
|
}
|
|
3708
3358
|
}
|
|
3709
3359
|
|
|
3710
|
-
/** Waits
|
|
3360
|
+
/** Waits on the parser and the document, and queries an element's own children. */
|
|
3711
3361
|
class Nodes {
|
|
3712
3362
|
/**
|
|
3713
|
-
*
|
|
3363
|
+
* Whether the parser has moved past the element: the element, or one of
|
|
3364
|
+
* its ancestors, has a next sibling.
|
|
3714
3365
|
* @param {Element} el
|
|
3715
|
-
* @returns
|
|
3366
|
+
* @returns {boolean}
|
|
3716
3367
|
*/
|
|
3717
3368
|
static isParsed(el) {
|
|
3718
3369
|
for (let c = /** @type {Node | null} */ (el); c; c = c.parentNode) {
|
|
@@ -3724,13 +3375,12 @@ class Nodes {
|
|
|
3724
3375
|
}
|
|
3725
3376
|
|
|
3726
3377
|
/**
|
|
3727
|
-
* Waits for the document's DOMContentLoaded
|
|
3728
|
-
*
|
|
3729
|
-
*
|
|
3730
|
-
*
|
|
3731
|
-
*
|
|
3732
|
-
*
|
|
3733
|
-
* never early.
|
|
3378
|
+
* Waits for the document's DOMContentLoaded. Resolves at once when that
|
|
3379
|
+
* moment already passed, and for a document with no view, which receives
|
|
3380
|
+
* no events. Never resolves early for a document still running its
|
|
3381
|
+
* deferred scripts.
|
|
3382
|
+
* @param {Document} doc
|
|
3383
|
+
* @returns {Promise<void>}
|
|
3734
3384
|
*/
|
|
3735
3385
|
static waitDomContentLoaded(doc) {
|
|
3736
3386
|
if (doc.readyState === 'loading') {
|
|
@@ -3743,7 +3393,13 @@ class Nodes {
|
|
|
3743
3393
|
}
|
|
3744
3394
|
const win = doc.defaultView;
|
|
3745
3395
|
if (win === null) {
|
|
3746
|
-
|
|
3396
|
+
return Promise.resolve();
|
|
3397
|
+
}
|
|
3398
|
+
// interactive spans both the deferred scripts and the time between DOMContentLoaded and load
|
|
3399
|
+
const [navigation] = /** @type {PerformanceNavigationTiming[]} */ (
|
|
3400
|
+
win.performance?.getEntriesByType('navigation') ?? []
|
|
3401
|
+
);
|
|
3402
|
+
if (navigation?.domContentLoadedEventStart > 0) {
|
|
3747
3403
|
return Promise.resolve();
|
|
3748
3404
|
}
|
|
3749
3405
|
return new Promise((resolve) => {
|
|
@@ -3762,22 +3418,12 @@ class Nodes {
|
|
|
3762
3418
|
* resolves once the element, or any of its ancestors, gains a next sibling,
|
|
3763
3419
|
* which is the parser having moved past this subtree. The document's
|
|
3764
3420
|
* DOMContentLoaded is the deadline. Resolves immediately for an element
|
|
3765
|
-
* that is already parsed.
|
|
3766
|
-
*
|
|
3767
|
-
*
|
|
3768
|
-
* predicate reads: an element last among its siblings becomes parsed when
|
|
3769
|
-
* an ancestor gains one, and a childList observer on the parent never sees
|
|
3770
|
-
* that. Formatted markup usually hides the difference, the whitespace
|
|
3771
|
-
* before a closing tag being a text node the parent does gain, so the gap
|
|
3772
|
-
* shows on whitespace-free markup, where the wait fell back to the
|
|
3773
|
-
* deadline. One observer takes many targets, so the cost is one observe()
|
|
3774
|
-
* per level, and it is disconnected at the first checkpoint past the
|
|
3775
|
-
* element either way.
|
|
3776
|
-
* @param {any} el
|
|
3777
|
-
* @returns {Promise<any>}
|
|
3421
|
+
* that is already parsed, or once the parser has finished the document.
|
|
3422
|
+
* @param {Element} el
|
|
3423
|
+
* @returns {Promise<Element>} the element
|
|
3778
3424
|
*/
|
|
3779
3425
|
static waitParsed(el) {
|
|
3780
|
-
if (Nodes.isParsed(el)) {
|
|
3426
|
+
if (Nodes.isParsed(el) || el.ownerDocument.readyState !== 'loading') {
|
|
3781
3427
|
return Promise.resolve(el);
|
|
3782
3428
|
}
|
|
3783
3429
|
return new Promise((resolve) => {
|
|
@@ -3799,10 +3445,10 @@ class Nodes {
|
|
|
3799
3445
|
}
|
|
3800
3446
|
|
|
3801
3447
|
/**
|
|
3802
|
-
*
|
|
3803
|
-
* @param {Element} el
|
|
3448
|
+
* The first child element matching the selector, descendants excluded.
|
|
3449
|
+
* @param {Element|DocumentFragment} el
|
|
3804
3450
|
* @param {string} selector
|
|
3805
|
-
* @returns
|
|
3451
|
+
* @returns {Element|null}
|
|
3806
3452
|
*/
|
|
3807
3453
|
static queryChildren(el, selector) {
|
|
3808
3454
|
for (const c of el.children) {
|
|
@@ -3813,10 +3459,10 @@ class Nodes {
|
|
|
3813
3459
|
return null;
|
|
3814
3460
|
}
|
|
3815
3461
|
/**
|
|
3816
|
-
*
|
|
3817
|
-
* @param {Element} el
|
|
3462
|
+
* Every child element matching the selector, descendants excluded.
|
|
3463
|
+
* @param {Element|DocumentFragment} el
|
|
3818
3464
|
* @param {string} selector
|
|
3819
|
-
* @returns
|
|
3465
|
+
* @returns {Element[]}
|
|
3820
3466
|
*/
|
|
3821
3467
|
static queryChildrenAll(el, selector) {
|
|
3822
3468
|
const r = [];
|
|
@@ -3873,9 +3519,11 @@ class NodeOperations {
|
|
|
3873
3519
|
|
|
3874
3520
|
/** The `data-tpl-*` commands, each taking the node it is written on and the scope it evaluates in. */
|
|
3875
3521
|
class CommandsHandler {
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3522
|
+
/**
|
|
3523
|
+
* The order the commands run in on one element, each seeing the node as the
|
|
3524
|
+
* ones before left it: tplIf gates before tplWith and tplEach push their
|
|
3525
|
+
* overlay, tplWhen gates after, inside the scope that overlay opened.
|
|
3526
|
+
*/
|
|
3879
3527
|
static ORDERED_COMMANDS = [
|
|
3880
3528
|
'tplIf',
|
|
3881
3529
|
'tplWith',
|
|
@@ -3886,8 +3534,7 @@ class CommandsHandler {
|
|
|
3886
3534
|
'tplRemove',
|
|
3887
3535
|
'tplVerbatim',
|
|
3888
3536
|
];
|
|
3889
|
-
|
|
3890
|
-
//if evaluates in the outer scope, before with/each
|
|
3537
|
+
/** Removes the node when the expression is falsy, evaluated before tplWith and tplEach open their overlay. */
|
|
3891
3538
|
static tplIf(node, expression, ops, evaluator) {
|
|
3892
3539
|
const accept = evaluator.evaluateExpression(expression);
|
|
3893
3540
|
if (!accept) {
|
|
@@ -3902,49 +3549,37 @@ class CommandsHandler {
|
|
|
3902
3549
|
.render();
|
|
3903
3550
|
ops.replace(node, newNode);
|
|
3904
3551
|
}
|
|
3552
|
+
/**
|
|
3553
|
+
* Renders the node once per entry, in place of the node. A Map iterates as
|
|
3554
|
+
* `{ key, value }` entries in its own order, a plain object as `{ key, value }`
|
|
3555
|
+
* entries in `Object.entries` order, and any other iterable as itself; a
|
|
3556
|
+
* value that is none of these throws. Each render sees its entry under
|
|
3557
|
+
* `data-tpl-var`, or as the whole overlay without one. `data-tpl-stat` names
|
|
3558
|
+
* an overlay beneath the entry carrying `index`, `count`, `size`, `first`,
|
|
3559
|
+
* `last`, `even` and `odd`, so an entry property of the same name wins; the
|
|
3560
|
+
* size, and so `last`, is null for a collection that knows neither its
|
|
3561
|
+
* `length` nor its `size`, which is never consumed to learn it.
|
|
3562
|
+
*/
|
|
3905
3563
|
static tplEach(node, expression, ops, evaluator) {
|
|
3906
3564
|
const varName = ops.popData(node, 'tplVar');
|
|
3907
3565
|
const statName = ops.popData(node, 'tplStat');
|
|
3908
3566
|
const template = new Template(node, evaluator);
|
|
3909
3567
|
const evaluated = evaluator.evaluateExpression(expression);
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
const proto = evaluated === null || typeof evaluated !== 'object' ? undefined : Object.getPrototypeOf(evaluated);
|
|
3916
|
-
const keyed =
|
|
3917
|
-
evaluated instanceof Map
|
|
3918
|
-
? [...evaluated]
|
|
3919
|
-
: !evaluated?.[Symbol.iterator] && (proto === Object.prototype || proto === null)
|
|
3920
|
-
? Object.entries(evaluated)
|
|
3921
|
-
: null;
|
|
3568
|
+
const proto =
|
|
3569
|
+
evaluated === null || typeof evaluated !== 'object' ? undefined : Object.getPrototypeOf(evaluated);
|
|
3570
|
+
const plain = !evaluated?.[Symbol.iterator] && (proto === Object.prototype || proto === null);
|
|
3571
|
+
const dict = plain ? Object.entries(evaluated) : null;
|
|
3572
|
+
const keyed = evaluated instanceof Map ? [...evaluated] : dict;
|
|
3922
3573
|
const entries = keyed === null ? evaluated : keyed.map(([key, value]) => ({ key, value }));
|
|
3923
3574
|
if (!entries?.[Symbol.iterator]) {
|
|
3924
3575
|
throw new Error(`Expected an iterable got '${evaluated}'`);
|
|
3925
3576
|
}
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
}
|
|
3930
|
-
ops.remove(node);
|
|
3931
|
-
return;
|
|
3932
|
-
}
|
|
3933
|
-
//the stat rides grouped under its declared name, overlaid below the
|
|
3934
|
-
//item, so data can never collide with its fields and an item property
|
|
3935
|
-
//sharing the stat's very name wins, as data always does. The size is
|
|
3936
|
-
//read where the collection already knows it (arrays and keyed entries
|
|
3937
|
-
//by length, sets and the like by size): an arbitrary iterator is never
|
|
3938
|
-
//consumed to learn it, so its size and last read null, the unknown
|
|
3939
|
-
const size = Array.isArray(entries)
|
|
3940
|
-
? entries.length
|
|
3941
|
-
: typeof entries.length === 'number'
|
|
3942
|
-
? entries.length
|
|
3943
|
-
: typeof entries.size === 'number'
|
|
3944
|
-
? entries.size
|
|
3945
|
-
: null;
|
|
3577
|
+
const measured = statName ? entries : null;
|
|
3578
|
+
const counted = typeof measured?.length === 'number' ? measured.length : measured?.size;
|
|
3579
|
+
const size = typeof counted === 'number' ? counted : null;
|
|
3946
3580
|
let index = 0;
|
|
3947
3581
|
for (const v of entries) {
|
|
3582
|
+
const item = varName ? { [varName]: v } : v;
|
|
3948
3583
|
const stat = {
|
|
3949
3584
|
index,
|
|
3950
3585
|
count: index + 1,
|
|
@@ -3954,16 +3589,13 @@ class CommandsHandler {
|
|
|
3954
3589
|
even: index % 2 === 0,
|
|
3955
3590
|
odd: index % 2 === 1,
|
|
3956
3591
|
};
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
template.withOverlay({ [statName]: stat }, varName ? { [varName]: v } : v).render(),
|
|
3960
|
-
);
|
|
3592
|
+
const layers = statName ? [{ [statName]: stat }, item] : [item];
|
|
3593
|
+
ops.prepend(node, template.withOverlay(...layers).render());
|
|
3961
3594
|
++index;
|
|
3962
3595
|
}
|
|
3963
3596
|
ops.remove(node);
|
|
3964
3597
|
}
|
|
3965
|
-
|
|
3966
|
-
//when evaluates after with/each, in the scope their overlay opened
|
|
3598
|
+
/** Removes the node when the expression is falsy, evaluated in the overlay tplWith or tplEach opened. */
|
|
3967
3599
|
static tplWhen(node, expression, ops, evaluator) {
|
|
3968
3600
|
const accept = evaluator.evaluateExpression(expression);
|
|
3969
3601
|
if (!accept) {
|
|
@@ -3977,12 +3609,8 @@ class CommandsHandler {
|
|
|
3977
3609
|
static tplRemove(node, value, ops, evaluator) {
|
|
3978
3610
|
switch (value.toLowerCase()) {
|
|
3979
3611
|
case 'tag': {
|
|
3980
|
-
const fragment =
|
|
3981
|
-
while (node.firstChild) {
|
|
3982
|
-
fragment.appendChild(node.firstChild);
|
|
3983
|
-
}
|
|
3612
|
+
const fragment = Fragments.fromChildNodes(node);
|
|
3984
3613
|
if ('tplVerbatim' in node.dataset) {
|
|
3985
|
-
//we are removing the parent element so we have to handle the lower priority commands
|
|
3986
3614
|
ops.popData(node, 'tplVerbatim');
|
|
3987
3615
|
ops.replace(node, fragment);
|
|
3988
3616
|
} else {
|
|
@@ -4018,6 +3646,9 @@ class CommandsHandler {
|
|
|
4018
3646
|
}
|
|
4019
3647
|
const tuples = Array.isArray(attributesAndValues[0]) ? attributesAndValues : [attributesAndValues];
|
|
4020
3648
|
tuples.forEach(([k, v]) => {
|
|
3649
|
+
if (v === null || v === undefined) {
|
|
3650
|
+
return;
|
|
3651
|
+
}
|
|
4021
3652
|
node.setAttribute(k, v);
|
|
4022
3653
|
});
|
|
4023
3654
|
}
|
|
@@ -4045,12 +3676,10 @@ class CommandsHandler {
|
|
|
4045
3676
|
}
|
|
4046
3677
|
}
|
|
4047
3678
|
|
|
4048
|
-
// Module-isolated string cache for dataset-to-attribute conversions
|
|
4049
3679
|
const attributeCache = new BoundedCache(1000);
|
|
4050
3680
|
|
|
4051
3681
|
/**
|
|
4052
|
-
*
|
|
4053
|
-
* Uses a cache lookup to avoid repetitive regex/string splitting.
|
|
3682
|
+
* The attribute a `tpl` dataset key targets: `tplAriaLabel` is `aria-label`.
|
|
4054
3683
|
* @param {string} dataSetKey
|
|
4055
3684
|
* @returns {string}
|
|
4056
3685
|
*/
|
|
@@ -4072,38 +3701,40 @@ function toAttr(dataSetKey) {
|
|
|
4072
3701
|
*/
|
|
4073
3702
|
class Template {
|
|
4074
3703
|
/**
|
|
4075
|
-
* Creates a template from
|
|
3704
|
+
* Creates a template from html.
|
|
4076
3705
|
* @param {string} html
|
|
4077
3706
|
* @param {{ [k: string] : any }?} [modules]
|
|
4078
|
-
* @param {...*} data
|
|
4079
|
-
* @returns
|
|
3707
|
+
* @param {...*} data the data stack, innermost last
|
|
3708
|
+
* @returns {Template}
|
|
4080
3709
|
*/
|
|
4081
3710
|
static fromHtml(html, modules, ...data) {
|
|
4082
3711
|
return new Template(Fragments.fromHtml(html), new ExpressionEvaluator(modules, data));
|
|
4083
3712
|
}
|
|
4084
3713
|
|
|
4085
3714
|
/**
|
|
4086
|
-
* Creates a template from the content of the first
|
|
3715
|
+
* Creates a template from the content of the first element matching the
|
|
3716
|
+
* selector, which is adopted: the template element is left empty.
|
|
4087
3717
|
* @param {string} selector for an HTMLTemplateElement
|
|
4088
3718
|
* @param {{ [k: string] : any }?} [modules]
|
|
4089
|
-
* @param {...*} data
|
|
4090
|
-
* @returns
|
|
3719
|
+
* @param {...*} data the data stack, innermost last
|
|
3720
|
+
* @returns {Template}
|
|
3721
|
+
* @throws when the selector matches no template element
|
|
4091
3722
|
*/
|
|
4092
3723
|
static fromSelector(selector, modules, ...data) {
|
|
4093
3724
|
const templateEl = document.querySelector(selector);
|
|
4094
3725
|
if (!(templateEl instanceof HTMLTemplateElement)) {
|
|
4095
3726
|
throw new Error('template selector does not match any template tag');
|
|
4096
3727
|
}
|
|
4097
|
-
|
|
4098
|
-
return new Template(fragment, new ExpressionEvaluator(modules, data));
|
|
3728
|
+
return Template.fromTemplate(templateEl, modules, ...data);
|
|
4099
3729
|
}
|
|
4100
3730
|
|
|
4101
3731
|
/**
|
|
4102
|
-
* Creates a template from the content of
|
|
3732
|
+
* Creates a template from the content of a template element, which is
|
|
3733
|
+
* adopted: the template element is left empty.
|
|
4103
3734
|
* @param {HTMLTemplateElement} templateEl
|
|
4104
3735
|
* @param {{ [k: string] : any }?} [modules]
|
|
4105
|
-
* @param {...*} data
|
|
4106
|
-
* @returns
|
|
3736
|
+
* @param {...*} data the data stack, innermost last
|
|
3737
|
+
* @returns {Template}
|
|
4107
3738
|
*/
|
|
4108
3739
|
static fromTemplate(templateEl, modules, ...data) {
|
|
4109
3740
|
const fragment = document.adoptNode(templateEl.content);
|
|
@@ -4111,11 +3742,12 @@ class Template {
|
|
|
4111
3742
|
}
|
|
4112
3743
|
|
|
4113
3744
|
/**
|
|
4114
|
-
* Creates a template
|
|
3745
|
+
* Creates a template over a fragment, which is imported afresh on every
|
|
3746
|
+
* render and never modified.
|
|
4115
3747
|
* @param {DocumentFragment} fragment
|
|
4116
3748
|
* @param { { [k: string] : any }? } [modules]
|
|
4117
|
-
* @param {...*} data
|
|
4118
|
-
* @returns
|
|
3749
|
+
* @param {...*} data the data stack, innermost last
|
|
3750
|
+
* @returns {Template}
|
|
4119
3751
|
*/
|
|
4120
3752
|
static fromFragment(fragment, modules, ...data) {
|
|
4121
3753
|
return new Template(fragment, new ExpressionEvaluator(modules, data));
|
|
@@ -4135,28 +3767,32 @@ class Template {
|
|
|
4135
3767
|
* Creates a new Template rendering in another scope: the one way to rebind
|
|
4136
3768
|
* a compiled template to a registry's modules and data.
|
|
4137
3769
|
* @param {ExpressionEvaluator} evaluator
|
|
3770
|
+
* @returns {Template}
|
|
4138
3771
|
*/
|
|
4139
3772
|
withEvaluator(evaluator) {
|
|
4140
3773
|
return new Template(this.#fragment, evaluator);
|
|
4141
3774
|
}
|
|
4142
3775
|
/**
|
|
4143
|
-
* Creates a new Template
|
|
3776
|
+
* Creates a new Template over another fragment, in the same scope.
|
|
4144
3777
|
* @param {DocumentFragment} fragment
|
|
3778
|
+
* @returns {Template}
|
|
4145
3779
|
*/
|
|
4146
3780
|
withFragment(fragment) {
|
|
4147
3781
|
return new Template(fragment, this.#evaluator);
|
|
4148
3782
|
}
|
|
4149
3783
|
/**
|
|
4150
|
-
* Creates a new Template with a
|
|
3784
|
+
* Creates a new Template with a module added, as `ExpressionEvaluator.withModule` does.
|
|
4151
3785
|
* @param {string?} name
|
|
4152
3786
|
* @param {{[k: string]: any}} value
|
|
3787
|
+
* @returns {Template}
|
|
4153
3788
|
*/
|
|
4154
3789
|
withModule(name, value) {
|
|
4155
3790
|
return new Template(this.#fragment, this.#evaluator.withModule(name, value));
|
|
4156
3791
|
}
|
|
4157
3792
|
/**
|
|
4158
|
-
* Creates a new Template with
|
|
3793
|
+
* Creates a new Template with the data pushed as the innermost overlays.
|
|
4159
3794
|
* @param {...*} data
|
|
3795
|
+
* @returns {Template}
|
|
4160
3796
|
*/
|
|
4161
3797
|
withOverlay(...data) {
|
|
4162
3798
|
return new Template(this.#fragment, this.#evaluator.withOverlay(...data));
|
|
@@ -4165,7 +3801,7 @@ class Template {
|
|
|
4165
3801
|
* Evaluates an expression in this template's scope, widened by the overlays.
|
|
4166
3802
|
* @param {string} expression
|
|
4167
3803
|
* @param {...*} data
|
|
4168
|
-
* @returns
|
|
3804
|
+
* @returns {any}
|
|
4169
3805
|
*/
|
|
4170
3806
|
evaluateExpression(expression, ...data) {
|
|
4171
3807
|
return this.#evaluator.withOverlay(...data).evaluateExpression(expression);
|
|
@@ -4174,33 +3810,29 @@ class Template {
|
|
|
4174
3810
|
* Evaluates a templated text, the `{{ }}` form, in this template's scope.
|
|
4175
3811
|
* @param {string} text
|
|
4176
3812
|
* @param {...*} data
|
|
4177
|
-
* @returns the parts
|
|
3813
|
+
* @returns {{ type: symbol, value: any }[]} the parts, as `Expressions.MODE_TEMPLATED` describes
|
|
4178
3814
|
*/
|
|
4179
3815
|
evaluateTemplated(text, ...data) {
|
|
4180
3816
|
return this.#evaluator.withOverlay(...data).evaluateTemplated(text);
|
|
4181
3817
|
}
|
|
4182
3818
|
/**
|
|
4183
|
-
*
|
|
3819
|
+
* The scope this template renders in.
|
|
3820
|
+
* @returns {ExpressionEvaluator}
|
|
4184
3821
|
*/
|
|
4185
3822
|
evaluator() {
|
|
4186
3823
|
return this.#evaluator;
|
|
4187
3824
|
}
|
|
4188
3825
|
/**
|
|
4189
|
-
* Renders the template
|
|
4190
|
-
*
|
|
3826
|
+
* Renders the template into a new fragment, applying the `data-tpl-*`
|
|
3827
|
+
* commands and bindings and the `{{ }}` interpolations.
|
|
3828
|
+
* @returns {DocumentFragment}
|
|
3829
|
+
* @throws {RenderError} naming the node and the directive that failed
|
|
4191
3830
|
*/
|
|
4192
3831
|
render() {
|
|
4193
3832
|
try {
|
|
4194
3833
|
const ops = new NodeOperations();
|
|
4195
3834
|
const imported = document.importNode(this.#fragment, true);
|
|
4196
|
-
const fragment =
|
|
4197
|
-
imported.nodeType === Node.DOCUMENT_FRAGMENT_NODE
|
|
4198
|
-
? imported
|
|
4199
|
-
: (() => {
|
|
4200
|
-
const d = new DocumentFragment();
|
|
4201
|
-
d.appendChild(imported);
|
|
4202
|
-
return d;
|
|
4203
|
-
})();
|
|
3835
|
+
const fragment = imported.nodeType === Node.DOCUMENT_FRAGMENT_NODE ? imported : Fragments.from(imported);
|
|
4204
3836
|
const iterator = document.createNodeIterator(
|
|
4205
3837
|
fragment,
|
|
4206
3838
|
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
|
|
@@ -4226,8 +3858,6 @@ class Template {
|
|
|
4226
3858
|
try {
|
|
4227
3859
|
CommandsHandler[command](el, value, ops, this.#evaluator);
|
|
4228
3860
|
} catch (ex) {
|
|
4229
|
-
//the directive is popped before it runs, so the open tag no
|
|
4230
|
-
//longer carries it: the frame names it and its expression
|
|
4231
3861
|
throw RenderError.wrap(`Error evaluating data-tpl-${toAttr(command)}="${value}"`, el, ex);
|
|
4232
3862
|
}
|
|
4233
3863
|
if (ops.removed(el)) {
|
|
@@ -4250,17 +3880,13 @@ class Template {
|
|
|
4250
3880
|
el.setAttribute(attributeName, evaluated);
|
|
4251
3881
|
}
|
|
4252
3882
|
} catch (ex) {
|
|
4253
|
-
throw RenderError.wrap(`Error evaluating data-tpl-${
|
|
3883
|
+
throw RenderError.wrap(`Error evaluating data-tpl-${attributeName}="${expression}"`, el, ex);
|
|
4254
3884
|
}
|
|
4255
3885
|
}
|
|
4256
3886
|
}
|
|
4257
3887
|
ops.cleanup();
|
|
4258
3888
|
return fragment;
|
|
4259
3889
|
} catch (ex) {
|
|
4260
|
-
//a command, a text node or a nested render already named the node it
|
|
4261
|
-
//failed on: wrapping again would add a frame for the fragment that
|
|
4262
|
-
//contains it, one per level, serializing the whole template into the
|
|
4263
|
-
//message that survives. Only a failure outside those is framed here
|
|
4264
3890
|
if (ex instanceof RenderError) {
|
|
4265
3891
|
throw ex;
|
|
4266
3892
|
}
|
|
@@ -4268,22 +3894,26 @@ class Template {
|
|
|
4268
3894
|
}
|
|
4269
3895
|
}
|
|
4270
3896
|
/**
|
|
4271
|
-
* Renders this template
|
|
3897
|
+
* Renders this template into the element, replacing its children.
|
|
4272
3898
|
* @param {Element} el
|
|
3899
|
+
* @throws {RenderError}
|
|
4273
3900
|
*/
|
|
4274
3901
|
renderTo(el) {
|
|
4275
3902
|
el.replaceChildren(this.render());
|
|
4276
3903
|
}
|
|
4277
3904
|
/**
|
|
4278
|
-
* Renders this template
|
|
3905
|
+
* Renders this template and appends the result to the element.
|
|
4279
3906
|
* @param {Element} el
|
|
3907
|
+
* @throws {RenderError}
|
|
4280
3908
|
*/
|
|
4281
3909
|
appendTo(el) {
|
|
4282
3910
|
el.appendChild(this.render());
|
|
4283
3911
|
}
|
|
4284
3912
|
/**
|
|
4285
|
-
* Renders this template
|
|
3913
|
+
* Renders this template into the first element matching the selector,
|
|
3914
|
+
* replacing its children; does nothing when none matches.
|
|
4286
3915
|
* @param {string} selector
|
|
3916
|
+
* @throws {RenderError}
|
|
4287
3917
|
*/
|
|
4288
3918
|
renderToSelector(selector) {
|
|
4289
3919
|
const el = document.querySelector(selector);
|
|
@@ -4292,8 +3922,10 @@ class Template {
|
|
|
4292
3922
|
}
|
|
4293
3923
|
}
|
|
4294
3924
|
/**
|
|
4295
|
-
* Renders this template
|
|
3925
|
+
* Renders this template and appends the result to the first element
|
|
3926
|
+
* matching the selector; does nothing when none matches.
|
|
4296
3927
|
* @param {string} selector
|
|
3928
|
+
* @throws {RenderError}
|
|
4297
3929
|
*/
|
|
4298
3930
|
appendToSelector(selector) {
|
|
4299
3931
|
const el = document.querySelector(selector);
|
|
@@ -4317,30 +3949,26 @@ class Template {
|
|
|
4317
3949
|
}
|
|
4318
3950
|
|
|
4319
3951
|
/**
|
|
4320
|
-
* A render failure, one frame per nesting level
|
|
4321
|
-
*
|
|
4322
|
-
*
|
|
4323
|
-
*
|
|
4324
|
-
* the
|
|
4325
|
-
* `node`, so a failure costs no clone and a nested failure does not embed the
|
|
4326
|
-
* page in its own message.
|
|
3952
|
+
* A render failure, one frame per nesting level, chained through `cause` from
|
|
3953
|
+
* the outermost frame down to the error that failed the expression. A frame's
|
|
3954
|
+
* message names what was being evaluated and the node's open tag, with the
|
|
3955
|
+
* attributes it still carries: the directive being evaluated is already off
|
|
3956
|
+
* the tag. The subtree is not in the message; `html` serializes it on demand.
|
|
4327
3957
|
*/
|
|
4328
3958
|
class RenderError extends Error {
|
|
4329
|
-
/**
|
|
4330
|
-
* How many frames a chain keeps. The innermost are the specific ones, so a
|
|
4331
|
-
* deeper nesting drops the outer context rather than the failure site: three
|
|
4332
|
-
* frames name the offending node and the two levels that hold it, which is
|
|
4333
|
-
* the path a reader follows without the page arriving with it.
|
|
4334
|
-
*/
|
|
3959
|
+
/** How many frames a chain keeps: a deeper nesting drops the outermost ones and marks the chain `truncated`. */
|
|
4335
3960
|
static FRAMES = 3;
|
|
4336
3961
|
#node;
|
|
4337
3962
|
#depth;
|
|
4338
|
-
/**
|
|
3963
|
+
/** Whether the chain dropped outer frames to stay within FRAMES. */
|
|
4339
3964
|
truncated = false;
|
|
4340
3965
|
/**
|
|
4341
|
-
* Frames a failure, unless the chain already
|
|
4342
|
-
* cause
|
|
4343
|
-
*
|
|
3966
|
+
* Frames a failure, unless the chain already holds FRAMES frames: then the
|
|
3967
|
+
* cause is returned as it is, marked `truncated`.
|
|
3968
|
+
* @param {string} message
|
|
3969
|
+
* @param {Node} nodeOrFragment the node that failed
|
|
3970
|
+
* @param {any} cause
|
|
3971
|
+
* @returns {RenderError}
|
|
4344
3972
|
*/
|
|
4345
3973
|
static wrap(message, nodeOrFragment, cause) {
|
|
4346
3974
|
if (cause instanceof RenderError && cause.depth >= RenderError.FRAMES) {
|
|
@@ -4349,27 +3977,44 @@ class RenderError extends Error {
|
|
|
4349
3977
|
}
|
|
4350
3978
|
return new RenderError(message, nodeOrFragment, cause);
|
|
4351
3979
|
}
|
|
3980
|
+
/**
|
|
3981
|
+
* @param {string} message
|
|
3982
|
+
* @param {Node} nodeOrFragment the node that failed
|
|
3983
|
+
* @param {any} cause
|
|
3984
|
+
*/
|
|
4352
3985
|
constructor(message, nodeOrFragment, cause) {
|
|
4353
3986
|
super(`${message} in \`${RenderError.describe(nodeOrFragment)}\``, { cause });
|
|
4354
3987
|
this.name = 'RenderError';
|
|
4355
3988
|
this.#node = nodeOrFragment;
|
|
4356
3989
|
this.#depth = (cause instanceof RenderError ? cause.depth : 0) + 1;
|
|
4357
3990
|
}
|
|
4358
|
-
/**
|
|
3991
|
+
/**
|
|
3992
|
+
* How many frames this chain carries, this one included.
|
|
3993
|
+
* @returns {number}
|
|
3994
|
+
*/
|
|
4359
3995
|
get depth() {
|
|
4360
3996
|
return this.#depth;
|
|
4361
3997
|
}
|
|
4362
|
-
/**
|
|
3998
|
+
/**
|
|
3999
|
+
* The node the render failed on, live in the fragment being built.
|
|
4000
|
+
* @returns {Node}
|
|
4001
|
+
*/
|
|
4363
4002
|
get node() {
|
|
4364
4003
|
return this.#node;
|
|
4365
4004
|
}
|
|
4366
|
-
/**
|
|
4005
|
+
/**
|
|
4006
|
+
* The failed node's markup, whitespace trimmed, serialized on each read.
|
|
4007
|
+
* @returns {string}
|
|
4008
|
+
*/
|
|
4367
4009
|
get html() {
|
|
4368
4010
|
return RenderError.stringify(this.#node);
|
|
4369
4011
|
}
|
|
4370
4012
|
/**
|
|
4371
4013
|
* What identifies a node in a frame: an element by its open tag, a text node
|
|
4372
|
-
* by its source, a fragment by the open tags of the elements it holds
|
|
4014
|
+
* by its source, a fragment by the open tags of the elements it holds, cut
|
|
4015
|
+
* at 120 characters.
|
|
4016
|
+
* @param {Node} nodeOrFragment
|
|
4017
|
+
* @returns {string}
|
|
4373
4018
|
*/
|
|
4374
4019
|
static describe(nodeOrFragment) {
|
|
4375
4020
|
if (nodeOrFragment.nodeType === Node.TEXT_NODE) {
|
|
@@ -4388,10 +4033,13 @@ class RenderError extends Error {
|
|
|
4388
4033
|
static #ellipsize(text) {
|
|
4389
4034
|
return text.length > 120 ? `${text.slice(0, 120)}…` : text;
|
|
4390
4035
|
}
|
|
4036
|
+
/**
|
|
4037
|
+
* A node's markup with its whitespace-only text dropped and the rest trimmed.
|
|
4038
|
+
* @param {Node} nodeOrFragment left untouched: a clone is serialized
|
|
4039
|
+
* @returns {string}
|
|
4040
|
+
*/
|
|
4391
4041
|
static stringify(nodeOrFragment) {
|
|
4392
|
-
|
|
4393
|
-
t.content.appendChild(RenderError.#cleanup(nodeOrFragment.cloneNode(true)));
|
|
4394
|
-
return t.innerHTML;
|
|
4042
|
+
return Fragments.toHtml(RenderError.#cleanup(nodeOrFragment.cloneNode(true)));
|
|
4395
4043
|
}
|
|
4396
4044
|
static #cleanup(node) {
|
|
4397
4045
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
@@ -4413,31 +4061,28 @@ class RenderError extends Error {
|
|
|
4413
4061
|
}
|
|
4414
4062
|
}
|
|
4415
4063
|
|
|
4416
|
-
/**
|
|
4417
|
-
* Tracks the elements waiting to render. `ready` resolves once the queue has
|
|
4418
|
-
* drained and never rejects; the promise kept per element resolves when that
|
|
4419
|
-
* element has rendered and rejects with whatever its upgrade threw.
|
|
4420
|
-
*/
|
|
4421
4064
|
class UpgradeQueue {
|
|
4422
4065
|
#q = new Map();
|
|
4423
4066
|
#readyResolve;
|
|
4424
4067
|
#ready = new Promise((resolve) => {
|
|
4425
4068
|
this.#readyResolve = resolve;
|
|
4426
4069
|
});
|
|
4070
|
+
#configured = /** @type {PromiseWithResolvers<void>} */ (Promise.withResolvers());
|
|
4427
4071
|
constructor() {
|
|
4428
|
-
Nodes.waitDomContentLoaded(document).then(() => {
|
|
4072
|
+
Promise.all([Nodes.waitDomContentLoaded(document), this.#configured.promise]).then(() => {
|
|
4429
4073
|
this.#start();
|
|
4430
4074
|
});
|
|
4431
4075
|
}
|
|
4432
|
-
/**
|
|
4433
|
-
* Waits for the page's readiness: the promise resolves right after the
|
|
4434
|
-
* ftl:ready event is dispatched, and immediately when that moment already
|
|
4435
|
-
* passed.
|
|
4436
|
-
* @returns {Promise<void>}
|
|
4437
|
-
*/
|
|
4438
4076
|
ready() {
|
|
4439
4077
|
return this.#ready;
|
|
4440
4078
|
}
|
|
4079
|
+
/**
|
|
4080
|
+
* Marks the registry as configured: until then nothing is defined, so an
|
|
4081
|
+
* empty queue says nothing about the page and readiness is held back.
|
|
4082
|
+
*/
|
|
4083
|
+
configured() {
|
|
4084
|
+
this.#configured.resolve();
|
|
4085
|
+
}
|
|
4441
4086
|
async #start() {
|
|
4442
4087
|
await this.settled();
|
|
4443
4088
|
document.dispatchEvent(
|
|
@@ -4450,13 +4095,8 @@ class UpgradeQueue {
|
|
|
4450
4095
|
}
|
|
4451
4096
|
enqueue(el) {
|
|
4452
4097
|
if (this.#q.has(el)) {
|
|
4453
|
-
//already upgrading, can happen when disconnecting an element
|
|
4454
|
-
//while it's already queued for upgrade (e.g.: ful-form)
|
|
4455
4098
|
return;
|
|
4456
4099
|
}
|
|
4457
|
-
//one entry, two signals: readiness waits on a signal that only ever
|
|
4458
|
-
//resolves, so nothing here attaches a rejection handler to the upgrade
|
|
4459
|
-
//itself and a component that fails is still reported the way it always was
|
|
4460
4100
|
const { promise: finished, resolve: markFinished } = /** @type {PromiseWithResolvers<void>} */ (
|
|
4461
4101
|
Promise.withResolvers()
|
|
4462
4102
|
);
|
|
@@ -4468,11 +4108,6 @@ class UpgradeQueue {
|
|
|
4468
4108
|
});
|
|
4469
4109
|
this.#q.set(el, { upgrade, finished });
|
|
4470
4110
|
}
|
|
4471
|
-
/**
|
|
4472
|
-
* The one fixed-point loop: drains the accepted entries, including the ones
|
|
4473
|
-
* enqueued while waiting, since a component is only queued once its parent
|
|
4474
|
-
* connects it and a single pass would miss everything nested.
|
|
4475
|
-
*/
|
|
4476
4111
|
async #drain(accept, pick) {
|
|
4477
4112
|
for (;;) {
|
|
4478
4113
|
const pending = Array.from(this.#q)
|
|
@@ -4485,22 +4120,28 @@ class UpgradeQueue {
|
|
|
4485
4120
|
}
|
|
4486
4121
|
}
|
|
4487
4122
|
/**
|
|
4488
|
-
* Waits for the whole queue to drain
|
|
4489
|
-
*
|
|
4490
|
-
*
|
|
4123
|
+
* Waits for the whole queue to drain, the upgrades enqueued meanwhile included.
|
|
4124
|
+
* Never rejects: a failed upgrade counts as drained.
|
|
4125
|
+
* @returns {Promise<void>}
|
|
4491
4126
|
*/
|
|
4492
4127
|
settled() {
|
|
4493
|
-
return this.#drain(
|
|
4128
|
+
return this.#drain(
|
|
4129
|
+
() => true,
|
|
4130
|
+
(entry) => entry.finished,
|
|
4131
|
+
);
|
|
4494
4132
|
}
|
|
4495
|
-
/**
|
|
4133
|
+
/**
|
|
4134
|
+
* Waits for the accepted upgrades, the ones enqueued meanwhile included,
|
|
4135
|
+
* rejecting with the first that failed.
|
|
4136
|
+
* @param {(el: Element) => boolean} accept
|
|
4137
|
+
* @returns {Promise<void>}
|
|
4138
|
+
*/
|
|
4496
4139
|
upgraded(accept) {
|
|
4497
4140
|
return this.#drain(accept, (entry) => entry.upgrade);
|
|
4498
4141
|
}
|
|
4499
|
-
/** The pending upgrade of one element, undefined when it is not queued. */
|
|
4500
4142
|
whenUpgraded(el) {
|
|
4501
4143
|
return this.#q.get(el)?.upgrade;
|
|
4502
4144
|
}
|
|
4503
|
-
/** The elements whose upgrade is still pending, in queue order. */
|
|
4504
4145
|
pending() {
|
|
4505
4146
|
return Array.from(this.#q.keys());
|
|
4506
4147
|
}
|
|
@@ -4511,8 +4152,8 @@ class UpgradeQueue {
|
|
|
4511
4152
|
* and attribute mappers. Elements defined before configure() are deferred and
|
|
4512
4153
|
* defined by it; from then on every defineElement takes effect immediately.
|
|
4513
4154
|
* The exported `registry` singleton is the page's own; a `new Registry()` is a
|
|
4514
|
-
* separate instance
|
|
4515
|
-
* singleton alone.
|
|
4155
|
+
* separate instance with its own upgrade queue, which dispatches its own
|
|
4156
|
+
* ftl:ready on the document, while Rendering waits on the singleton alone.
|
|
4516
4157
|
*/
|
|
4517
4158
|
class Registry {
|
|
4518
4159
|
#tagToClass = {};
|
|
@@ -4581,9 +4222,12 @@ class Registry {
|
|
|
4581
4222
|
* Registers a custom element under its tag: the class is augmented with its
|
|
4582
4223
|
* BITS (observed attributes, mappers, templates) and handed to the platform.
|
|
4583
4224
|
* Before configure() the definition is deferred, so import order never
|
|
4584
|
-
* matters.
|
|
4225
|
+
* matters. The element resolves its templates and components through this
|
|
4226
|
+
* registry, whichever one a module imports, and its `static config` is read
|
|
4227
|
+
* here, so a page replacing it does so before the definition.
|
|
4585
4228
|
* @param {string} tag
|
|
4586
4229
|
* @param {*} klass a ParsedElement subclass
|
|
4230
|
+
* @returns {Registry} this registry
|
|
4587
4231
|
*/
|
|
4588
4232
|
defineElement(tag, klass) {
|
|
4589
4233
|
if (!this.#configured) {
|
|
@@ -4601,13 +4245,8 @@ class Registry {
|
|
|
4601
4245
|
* ancestors', so a base class declares what every subclass keeps observing
|
|
4602
4246
|
* (a protocol attribute such as Field's disabled claim) and a leaf refines a
|
|
4603
4247
|
* mapping, or moves a name's position, without repeating the whole list.
|
|
4604
|
-
*
|
|
4605
|
-
*
|
|
4606
|
-
* stop can work, since a registered ancestor carries an own BITS of its own,
|
|
4607
|
-
* and nothing above the elements declares anything.
|
|
4608
|
-
*
|
|
4609
|
-
* Everything deriving a component's attribute vocabulary reads it here, so
|
|
4610
|
-
* the runtime and whatever documents it cannot walk the chain differently.
|
|
4248
|
+
* The observed attributes are applied in the composed order, a re-declared
|
|
4249
|
+
* name taking the position of its last declaration.
|
|
4611
4250
|
* @param {*} klass a ParsedElement subclass
|
|
4612
4251
|
* @returns {{ observed: string[], attributes: string[] }}
|
|
4613
4252
|
*/
|
|
@@ -4623,14 +4262,7 @@ class Registry {
|
|
|
4623
4262
|
* The property an observed attribute drives, by the platform's own
|
|
4624
4263
|
* dash-to-camel rule: the one `dataset` applies, so `clear-invalid-on-change`
|
|
4625
4264
|
* would reach `clearInvalidOnChange` the way `data-clear-invalid-on-change`
|
|
4626
|
-
* reaches `dataset.clearInvalidOnChange`.
|
|
4627
|
-
* unchanged, which is what every observed attribute in the library is.
|
|
4628
|
-
*
|
|
4629
|
-
* It exists because the observed tier is the one that becomes properties:
|
|
4630
|
-
* without it an attribute could only be observed if its name happened to be
|
|
4631
|
-
* a usable identifier, which is why every multiword observed attribute here
|
|
4632
|
-
* used to be squashed into one word while the configuration tier, which
|
|
4633
|
-
* never becomes a property, spelled the same idea with a dash.
|
|
4265
|
+
* reaches `dataset.clearInvalidOnChange`.
|
|
4634
4266
|
*
|
|
4635
4267
|
* Only this direction is mapped. A property never derives its attribute: a
|
|
4636
4268
|
* setter reflects through `reflectTo`, naming the attribute it writes.
|
|
@@ -4643,10 +4275,6 @@ class Registry {
|
|
|
4643
4275
|
#augmentAndDefineElement(tag, klass) {
|
|
4644
4276
|
const { observed, attributes } = Registry.declarationsOf(klass);
|
|
4645
4277
|
const { template, templates, slots, mappers, config } = klass;
|
|
4646
|
-
//a name a subclass re-declares takes the subclass's position, which is
|
|
4647
|
-
//how a field whose value setter reads its own shape attributes declares
|
|
4648
|
-
//that its value lands after them: the order the declarations compose in
|
|
4649
|
-
//is the order the base applies them
|
|
4650
4278
|
const declaredNames = observed.map((a) => a.split(':')[0]);
|
|
4651
4279
|
const observedNames = [...new Set(declaredNames.reverse())].reverse();
|
|
4652
4280
|
const attrToMapper = [...attributes, ...observed].reduce((acc, a) => {
|
|
@@ -4665,20 +4293,13 @@ class Registry {
|
|
|
4665
4293
|
const nameToTemplate = Object.fromEntries(namesAndTemplates);
|
|
4666
4294
|
|
|
4667
4295
|
klass.BITS = {
|
|
4668
|
-
//the defining registry travels with the definition: an element resolves
|
|
4669
|
-
//its templates and its components through the registry that defined it,
|
|
4670
|
-
//not through whichever one a module happened to import
|
|
4671
4296
|
registry: this,
|
|
4672
4297
|
enqueue: (el) => this.#upgradeQueue.enqueue(el),
|
|
4673
4298
|
SLOTS: slots,
|
|
4674
|
-
//the class's own constants, overlaid on its templates as `config`:
|
|
4675
|
-
//read here with the rest of the declaration, so a page replacing them
|
|
4676
|
-
//does it before configure() like every other definition-time choice
|
|
4677
4299
|
CONFIG: config,
|
|
4678
4300
|
OBSERVED: observedNames,
|
|
4679
4301
|
DECLARED: [...new Set([...observedNames, ...attributes.map((a) => a.split(':')[0])])],
|
|
4680
4302
|
ATTR_TO_MAPPER: attrToMapper,
|
|
4681
|
-
//resolved once here rather than at every attribute write
|
|
4682
4303
|
ATTR_TO_PROPERTY: Object.fromEntries(observedNames.map((a) => [a, Registry.propertyOf(a)])),
|
|
4683
4304
|
TEMPLATES: nameToTemplate,
|
|
4684
4305
|
};
|
|
@@ -4690,6 +4311,7 @@ class Registry {
|
|
|
4690
4311
|
* as `#fn`.
|
|
4691
4312
|
* @param {string} name
|
|
4692
4313
|
* @param {object} value
|
|
4314
|
+
* @returns {Registry} this registry
|
|
4693
4315
|
*/
|
|
4694
4316
|
defineModule(name, value) {
|
|
4695
4317
|
const module = name ? { [name]: value } : value;
|
|
@@ -4700,6 +4322,7 @@ class Registry {
|
|
|
4700
4322
|
/**
|
|
4701
4323
|
* Replaces the whole module map.
|
|
4702
4324
|
* @param {object} ms
|
|
4325
|
+
* @returns {Registry} this registry
|
|
4703
4326
|
*/
|
|
4704
4327
|
defineModules(ms) {
|
|
4705
4328
|
this.#modules = ms;
|
|
@@ -4711,6 +4334,7 @@ class Registry {
|
|
|
4711
4334
|
* through component().
|
|
4712
4335
|
* @param {string} name
|
|
4713
4336
|
* @param {*} value
|
|
4337
|
+
* @returns {Registry} this registry
|
|
4714
4338
|
*/
|
|
4715
4339
|
defineComponent(name, value) {
|
|
4716
4340
|
this.#components[name] = value;
|
|
@@ -4719,6 +4343,7 @@ class Registry {
|
|
|
4719
4343
|
/**
|
|
4720
4344
|
* Replaces the data stack the templates evaluate over.
|
|
4721
4345
|
* @param {...any} data
|
|
4346
|
+
* @returns {Registry} this registry
|
|
4722
4347
|
*/
|
|
4723
4348
|
defineData(...data) {
|
|
4724
4349
|
this.#data = data;
|
|
@@ -4728,6 +4353,7 @@ class Registry {
|
|
|
4728
4353
|
/**
|
|
4729
4354
|
* Appends to the data stack, the later entry winning a shared name.
|
|
4730
4355
|
* @param {...any} data
|
|
4356
|
+
* @returns {Registry} this registry
|
|
4731
4357
|
*/
|
|
4732
4358
|
defineOverlay(...data) {
|
|
4733
4359
|
this.#data = [...this.#data, ...data];
|
|
@@ -4739,6 +4365,7 @@ class Registry {
|
|
|
4739
4365
|
* `name:type` declaration.
|
|
4740
4366
|
* @param {string} k
|
|
4741
4367
|
* @param {{ unmarshal(str: string|null, name: string, el: Element): any, marshal(value: any, name: string, el: Element): string|null }} v
|
|
4368
|
+
* @returns {Registry} this registry
|
|
4742
4369
|
*/
|
|
4743
4370
|
defineMapper(k, v) {
|
|
4744
4371
|
this.#mappers[k] = v;
|
|
@@ -4747,6 +4374,7 @@ class Registry {
|
|
|
4747
4374
|
/**
|
|
4748
4375
|
* Hands the registry over to the plugin's configure.
|
|
4749
4376
|
* @param {{ configure(registry: Registry): void }} p
|
|
4377
|
+
* @returns {Registry} this registry
|
|
4750
4378
|
*/
|
|
4751
4379
|
plugin(p) {
|
|
4752
4380
|
p.configure(this);
|
|
@@ -4754,7 +4382,10 @@ class Registry {
|
|
|
4754
4382
|
}
|
|
4755
4383
|
/**
|
|
4756
4384
|
* Defines every element deferred so far; from then on, defineElement takes
|
|
4757
|
-
* effect immediately.
|
|
4385
|
+
* effect immediately. Readiness waits for the first call: a page that
|
|
4386
|
+
* configures late (translations fetched first) gets its ftl:ready once the
|
|
4387
|
+
* elements this defines have upgraded, not at DOMContentLoaded.
|
|
4388
|
+
* @returns {Registry} this registry
|
|
4758
4389
|
*/
|
|
4759
4390
|
configure() {
|
|
4760
4391
|
for (const [tag, klass] of Object.entries(this.#tagToClass)) {
|
|
@@ -4762,27 +4393,43 @@ class Registry {
|
|
|
4762
4393
|
delete this.#tagToClass[tag];
|
|
4763
4394
|
}
|
|
4764
4395
|
this.#configured = true;
|
|
4396
|
+
this.#upgradeQueue.configured();
|
|
4765
4397
|
return this;
|
|
4766
4398
|
}
|
|
4767
4399
|
/**
|
|
4768
4400
|
* Waits for the queued upgrades the filter accepts, rejecting with the first
|
|
4769
|
-
* that failed
|
|
4401
|
+
* that failed, the upgrades enqueued meanwhile included: the rejecting
|
|
4402
|
+
* barrier Rendering is a facade over.
|
|
4770
4403
|
* @param {(el: Element) => boolean} accept
|
|
4404
|
+
* @returns {Promise<void>}
|
|
4771
4405
|
*/
|
|
4772
4406
|
settle(accept) {
|
|
4773
4407
|
return this.#upgradeQueue.upgraded(accept);
|
|
4774
4408
|
}
|
|
4775
|
-
/**
|
|
4409
|
+
/**
|
|
4410
|
+
* The pending upgrade of one element: resolving when it has rendered,
|
|
4411
|
+
* rejecting with what its upgrade threw.
|
|
4412
|
+
* @param {Element} el
|
|
4413
|
+
* @returns {Promise<void>|undefined} undefined when the element is not queued
|
|
4414
|
+
*/
|
|
4776
4415
|
whenUpgraded(el) {
|
|
4777
4416
|
return this.#upgradeQueue.whenUpgraded(el);
|
|
4778
4417
|
}
|
|
4779
|
-
/**
|
|
4418
|
+
/**
|
|
4419
|
+
* The elements whose upgrade is still pending, in queue order.
|
|
4420
|
+
* @returns {Element[]}
|
|
4421
|
+
*/
|
|
4780
4422
|
pending() {
|
|
4781
4423
|
return this.#upgradeQueue.pending();
|
|
4782
4424
|
}
|
|
4783
4425
|
/**
|
|
4784
|
-
* Waits for
|
|
4785
|
-
* dispatched
|
|
4426
|
+
* Waits for this registry's readiness: resolves right after its ftl:ready
|
|
4427
|
+
* event is dispatched on the document, and immediately when that already
|
|
4428
|
+
* happened. The event is dispatched once DOMContentLoaded has passed,
|
|
4429
|
+
* configure() has been called and the queue has drained, so a registry
|
|
4430
|
+
* that is never configured is never ready. Never rejects: a failed
|
|
4431
|
+
* upgrade counts as settled, and its rejection is left unhandled so it
|
|
4432
|
+
* still reaches the console and the page's error reporting.
|
|
4786
4433
|
* @returns {Promise<void>}
|
|
4787
4434
|
*/
|
|
4788
4435
|
ready() {
|
|
@@ -4795,11 +4442,16 @@ class Registry {
|
|
|
4795
4442
|
* The scope every template on this registry renders in: the modules and the
|
|
4796
4443
|
* data stack, as one value. Replaced whenever either is defined, so a holder
|
|
4797
4444
|
* of an older one keeps rendering against what it was handed.
|
|
4445
|
+
* @returns {ExpressionEvaluator}
|
|
4798
4446
|
*/
|
|
4799
4447
|
evaluator() {
|
|
4800
4448
|
return this.#evaluator;
|
|
4801
4449
|
}
|
|
4802
|
-
/**
|
|
4450
|
+
/**
|
|
4451
|
+
* The component registered under the name.
|
|
4452
|
+
* @param {string} name
|
|
4453
|
+
* @returns {any} undefined when none is
|
|
4454
|
+
*/
|
|
4803
4455
|
component(name) {
|
|
4804
4456
|
return this.#components[name];
|
|
4805
4457
|
}
|
|
@@ -4807,33 +4459,59 @@ class Registry {
|
|
|
4807
4459
|
|
|
4808
4460
|
const registry = new Registry();
|
|
4809
4461
|
|
|
4810
|
-
/** The Template factories, each bound to the page registry's scope. */
|
|
4811
4462
|
/** The Template factories bound to the page's own registry: the same four sources, with its modules and data already applied. */
|
|
4812
4463
|
class Templates {
|
|
4464
|
+
/**
|
|
4465
|
+
* @param {string} html
|
|
4466
|
+
* @returns {Template}
|
|
4467
|
+
*/
|
|
4813
4468
|
static fromHtml(html) {
|
|
4814
4469
|
return Template.fromHtml(html).withEvaluator(registry.evaluator());
|
|
4815
4470
|
}
|
|
4471
|
+
/**
|
|
4472
|
+
* @param {string} selector for an HTMLTemplateElement
|
|
4473
|
+
* @returns {Template}
|
|
4474
|
+
*/
|
|
4816
4475
|
static fromSelector(selector) {
|
|
4817
4476
|
return Template.fromSelector(selector).withEvaluator(registry.evaluator());
|
|
4818
4477
|
}
|
|
4478
|
+
/**
|
|
4479
|
+
* @param {HTMLTemplateElement} templateEl consumed: its content is adopted
|
|
4480
|
+
* @returns {Template}
|
|
4481
|
+
*/
|
|
4819
4482
|
static fromTemplate(templateEl) {
|
|
4820
4483
|
return Template.fromTemplate(templateEl).withEvaluator(registry.evaluator());
|
|
4821
4484
|
}
|
|
4485
|
+
/**
|
|
4486
|
+
* @param {DocumentFragment} fragment
|
|
4487
|
+
* @returns {Template}
|
|
4488
|
+
*/
|
|
4822
4489
|
static fromFragment(fragment) {
|
|
4823
4490
|
return Template.fromFragment(fragment).withEvaluator(registry.evaluator());
|
|
4824
4491
|
}
|
|
4825
4492
|
}
|
|
4826
4493
|
|
|
4827
4494
|
/**
|
|
4828
|
-
*
|
|
4829
|
-
*
|
|
4830
|
-
*
|
|
4831
|
-
* leaves a failed component to its own unhandled-rejection report.
|
|
4495
|
+
* Waits for elements queued on the page's `registry` to render. Unlike
|
|
4496
|
+
* `registry.ready()`, these reject when an awaited upgrade fails. An element
|
|
4497
|
+
* defined by another Registry is awaited through that registry instead.
|
|
4832
4498
|
*/
|
|
4833
4499
|
class Rendering {
|
|
4500
|
+
/**
|
|
4501
|
+
* Waits for the element and its descendants, the ones their own renders
|
|
4502
|
+
* enqueue included.
|
|
4503
|
+
* @param {Element} el
|
|
4504
|
+
* @returns {Promise<void>} rejecting with the first upgrade that failed
|
|
4505
|
+
*/
|
|
4834
4506
|
static waitFor(el) {
|
|
4835
4507
|
return registry.settle((child) => el.contains(child));
|
|
4836
4508
|
}
|
|
4509
|
+
/**
|
|
4510
|
+
* Waits for the element's descendants, the element itself excluded: what a
|
|
4511
|
+
* component awaits from inside its own render.
|
|
4512
|
+
* @param {Element} el
|
|
4513
|
+
* @returns {Promise<void>} rejecting with the first upgrade that failed
|
|
4514
|
+
*/
|
|
4837
4515
|
static waitForChildren(el) {
|
|
4838
4516
|
return registry.settle((child) => el !== child && el.contains(child));
|
|
4839
4517
|
}
|
|
@@ -4847,9 +4525,15 @@ class Rendering {
|
|
|
4847
4525
|
* @property {(val: any, name: string, el: Element) => string|null} marshal
|
|
4848
4526
|
*/
|
|
4849
4527
|
|
|
4528
|
+
/**
|
|
4529
|
+
* The base of every fml element. A subclass declares its attributes, slots and
|
|
4530
|
+
* templates in statics and builds its dom in `render`; the base reads the light
|
|
4531
|
+
* slots, reads the declared attributes through their mappers, renders once, and
|
|
4532
|
+
* then forwards observed attribute writes to the matching properties. An element
|
|
4533
|
+
* resolves templates and components through the registry that defined it.
|
|
4534
|
+
*/
|
|
4850
4535
|
class ParsedElement extends HTMLElement {
|
|
4851
4536
|
static BITS = {
|
|
4852
|
-
//an element the registry never defined still answers the page's own
|
|
4853
4537
|
registry,
|
|
4854
4538
|
enqueue: (el) => {},
|
|
4855
4539
|
SLOTS: false,
|
|
@@ -4873,14 +4557,12 @@ class ParsedElement extends HTMLElement {
|
|
|
4873
4557
|
this.internals = this.attachInternals();
|
|
4874
4558
|
}
|
|
4875
4559
|
/**
|
|
4876
|
-
* The
|
|
4877
|
-
*
|
|
4878
|
-
*
|
|
4879
|
-
*
|
|
4880
|
-
*
|
|
4881
|
-
*
|
|
4882
|
-
* A subclass must not redeclare it: a class field with no initializer
|
|
4883
|
-
* assigns undefined after super() returns, which would wipe it.
|
|
4560
|
+
* The element's ElementInternals, attached by the base for every element: a
|
|
4561
|
+
* subclass never calls attachInternals itself, since a second call throws.
|
|
4562
|
+
* A subclass declaring `static formAssociated` gets the form apis here.
|
|
4563
|
+
* A subclass must not redeclare the field: a class field without an
|
|
4564
|
+
* initializer assigns undefined after super() returns, wiping it.
|
|
4565
|
+
* @type {ElementInternals}
|
|
4884
4566
|
*/
|
|
4885
4567
|
internals;
|
|
4886
4568
|
#parsed = false;
|
|
@@ -4897,26 +4579,36 @@ class ParsedElement extends HTMLElement {
|
|
|
4897
4579
|
#mapper(attr) {
|
|
4898
4580
|
const mapper = this.#bits().ATTR_TO_MAPPER[attr];
|
|
4899
4581
|
if (!mapper) {
|
|
4900
|
-
//an attribute the class never declared has no type to read it as:
|
|
4901
|
-
//say so rather than dying on the missing mapper. Content this
|
|
4902
|
-
//element does not own, such as a custom loader's own configuration,
|
|
4903
|
-
//is read with getAttribute, the platform's own answer
|
|
4904
4582
|
throw new Error(
|
|
4905
4583
|
`${this.constructor.name} declares no attribute '${attr}': declare it in static observed or static attributes, or read it with getAttribute`,
|
|
4906
4584
|
);
|
|
4907
4585
|
}
|
|
4908
4586
|
return mapper;
|
|
4909
4587
|
}
|
|
4588
|
+
/**
|
|
4589
|
+
* Reads an attribute value through the mapper it was declared with.
|
|
4590
|
+
* @param {string} attr
|
|
4591
|
+
* @param {string|null} str
|
|
4592
|
+
* @returns {any}
|
|
4593
|
+
* @throws when the class declares no such attribute; read undeclared ones with getAttribute
|
|
4594
|
+
*/
|
|
4910
4595
|
unmarshal(attr, str) {
|
|
4911
4596
|
return this.#mapper(attr).unmarshal(str, attr, this);
|
|
4912
4597
|
}
|
|
4598
|
+
/**
|
|
4599
|
+
* Writes a value as the attribute string its declared mapper produces.
|
|
4600
|
+
* @param {string} attr
|
|
4601
|
+
* @param {any} value
|
|
4602
|
+
* @returns {string|null} null for a value that removes the attribute
|
|
4603
|
+
* @throws when the class declares no such attribute
|
|
4604
|
+
*/
|
|
4913
4605
|
marshal(attr, value) {
|
|
4914
4606
|
return this.#mapper(attr).marshal(value, attr, this);
|
|
4915
4607
|
}
|
|
4916
4608
|
/**
|
|
4917
|
-
*
|
|
4609
|
+
* The registry that defined this element, the page's own for an undefined one.
|
|
4610
|
+
* @returns {typeof registry}
|
|
4918
4611
|
*/
|
|
4919
|
-
/** The registry that defined this element, the page's own for an undefined one. */
|
|
4920
4612
|
get _registry() {
|
|
4921
4613
|
return this.#bits().registry;
|
|
4922
4614
|
}
|
|
@@ -4924,10 +4616,18 @@ class ParsedElement extends HTMLElement {
|
|
|
4924
4616
|
* The component registered under the name on this element's registry, the
|
|
4925
4617
|
* one every ful loader and mapper resolves through.
|
|
4926
4618
|
* @param {string} name
|
|
4619
|
+
* @returns {any} undefined when none is registered
|
|
4927
4620
|
*/
|
|
4928
4621
|
component(name) {
|
|
4929
4622
|
return this.#bits().registry.component(name);
|
|
4930
4623
|
}
|
|
4624
|
+
/**
|
|
4625
|
+
* One of the class's templates, bound to its registry's scope with the
|
|
4626
|
+
* class's `static config` overlaid as `config`.
|
|
4627
|
+
* @param {string} [name] 'default', the `static template`, when omitted
|
|
4628
|
+
* @returns {Template}
|
|
4629
|
+
* @throws when the class declares no template of that name
|
|
4630
|
+
*/
|
|
4931
4631
|
template(name) {
|
|
4932
4632
|
const target = this.#bits().TEMPLATES[name ?? 'default'];
|
|
4933
4633
|
if (!target) {
|
|
@@ -4947,15 +4647,9 @@ class ParsedElement extends HTMLElement {
|
|
|
4947
4647
|
if (oldValue === newValue) {
|
|
4948
4648
|
return;
|
|
4949
4649
|
}
|
|
4950
|
-
//the mute is the attribute being reflected, not the element: a component
|
|
4951
|
-
//that legitimately changes another observed attribute while one reflects
|
|
4952
|
-
//is a change like any other, and used to be swallowed
|
|
4953
4650
|
if (this.#reflecting.has(attr)) {
|
|
4954
4651
|
return;
|
|
4955
4652
|
}
|
|
4956
|
-
//the properties go live only once the render is done: before that, an
|
|
4957
|
-
//attribute write lands in the observed snapshot and the base applies it
|
|
4958
|
-
//with the rest of the declared state
|
|
4959
4653
|
if (!this.#parsed) {
|
|
4960
4654
|
if (this.#pending !== null && attr in this.#pending) {
|
|
4961
4655
|
this.#pending[attr] = this.unmarshal(attr, newValue);
|
|
@@ -4965,14 +4659,18 @@ class ParsedElement extends HTMLElement {
|
|
|
4965
4659
|
this[this.#bits().ATTR_TO_PROPERTY[attr]] = this.unmarshal(attr, newValue);
|
|
4966
4660
|
}
|
|
4967
4661
|
/**
|
|
4968
|
-
* Upgrades once
|
|
4969
|
-
* to writes made while the render is
|
|
4970
|
-
*
|
|
4662
|
+
* Upgrades once, however often the element is connected: reads the declared
|
|
4663
|
+
* attributes, keeps the observed ones open to writes made while the render is
|
|
4664
|
+
* pending, renders, then applies the observed values to their properties in
|
|
4665
|
+
* the order the registry composed them, a base class's before a subclass's.
|
|
4666
|
+
* Only then do attribute writes forward to properties, and the element gains
|
|
4667
|
+
* the `rendered` custom state. A render that throws leaves the forwarding
|
|
4668
|
+
* shut and the state unset.
|
|
4971
4669
|
*
|
|
4972
4670
|
* An observed attribute drives the property the registry's `propertyOf`
|
|
4973
4671
|
* names, so a hyphenated attribute is authored with its dashes and read as
|
|
4974
|
-
* a camelCase property.
|
|
4975
|
-
*
|
|
4672
|
+
* a camelCase property.
|
|
4673
|
+
* @returns {Promise<void>} rejecting with what the render threw
|
|
4976
4674
|
*/
|
|
4977
4675
|
async upgrade() {
|
|
4978
4676
|
if (this.#started) {
|
|
@@ -4980,9 +4678,6 @@ class ParsedElement extends HTMLElement {
|
|
|
4980
4678
|
}
|
|
4981
4679
|
this.#started = true;
|
|
4982
4680
|
const slots = this.#bits().SLOTS ? LightSlots.from(this) : undefined;
|
|
4983
|
-
//both tiers are read here: the observed ones forward to a property once
|
|
4984
|
-
//the render is done, the rest are the element's configuration, read the
|
|
4985
|
-
//one time and never again
|
|
4986
4681
|
const declared = Object.fromEntries(
|
|
4987
4682
|
this.#bits().DECLARED.map((attribute) => [
|
|
4988
4683
|
attribute,
|
|
@@ -4994,21 +4689,11 @@ class ParsedElement extends HTMLElement {
|
|
|
4994
4689
|
this.#pending = declared;
|
|
4995
4690
|
try {
|
|
4996
4691
|
await this.render({ slots });
|
|
4997
|
-
//the declared state reaches the properties once the dom the setters
|
|
4998
|
-
//drive exists, in the order the registry composed the declarations:
|
|
4999
|
-
//a base class's attributes before the subclass's own
|
|
5000
4692
|
const properties = this.#bits().ATTR_TO_PROPERTY;
|
|
5001
4693
|
for (const name of this.#bits().OBSERVED) {
|
|
5002
4694
|
this[properties[name]] = declared[name];
|
|
5003
4695
|
}
|
|
5004
|
-
//the properties go live once the render is done: from here on, an
|
|
5005
|
-
//attribute write forwards to the property. A render that threw
|
|
5006
|
-
//leaves it shut, so a later attribute write cannot reach setters
|
|
5007
|
-
//that assume pieces the failed render never adopted
|
|
5008
4696
|
this.#parsed = true;
|
|
5009
|
-
//the same moment, said to css. :defined is true from the constructor,
|
|
5010
|
-
//which is before the dom exists, so a guard written against it reveals
|
|
5011
|
-
//an element that has nothing in it yet
|
|
5012
4697
|
this.internals.states.add('rendered');
|
|
5013
4698
|
} finally {
|
|
5014
4699
|
this.#pending = null;
|
|
@@ -5019,8 +4704,9 @@ class ParsedElement extends HTMLElement {
|
|
|
5019
4704
|
* drive, and the base applies the declared state onto the properties as
|
|
5020
4705
|
* soon as it returns. A render needing a declared value while it builds
|
|
5021
4706
|
* reads it through `declared(name)`. The slots are undefined for an element
|
|
5022
|
-
* declaring no slots.
|
|
5023
|
-
* @param {{ slots:
|
|
4707
|
+
* declaring no slots. It may be async; the upgrade awaits it.
|
|
4708
|
+
* @param {{ slots: Record<string, DocumentFragment> | undefined }} c
|
|
4709
|
+
* @returns {void | Promise<void>}
|
|
5024
4710
|
*/
|
|
5025
4711
|
render(c) {}
|
|
5026
4712
|
/**
|
|
@@ -5032,12 +4718,9 @@ class ParsedElement extends HTMLElement {
|
|
|
5032
4718
|
* observed name answers the snapshot while the render is pending, kept
|
|
5033
4719
|
* open to attribute writes landing in that window, and the live attribute
|
|
5034
4720
|
* afterwards, the property being live by then.
|
|
5035
|
-
*
|
|
5036
|
-
* The snapshot exists rather than a read of the dom because an element may
|
|
5037
|
-
* write its own observed attributes while it renders, whether a reflection
|
|
5038
|
-
* or a value the platform normalizes on the way in, and what the author
|
|
5039
|
-
* declared is what the base applies, not what the render left behind.
|
|
5040
4721
|
* @param {string} name
|
|
4722
|
+
* @returns {any}
|
|
4723
|
+
* @throws when the class declares no such attribute
|
|
5041
4724
|
*/
|
|
5042
4725
|
declared(name) {
|
|
5043
4726
|
if (name in this.#frozen) {
|
|
@@ -5048,15 +4731,18 @@ class ParsedElement extends HTMLElement {
|
|
|
5048
4731
|
}
|
|
5049
4732
|
return this.unmarshal(name, this.getAttribute(name));
|
|
5050
4733
|
}
|
|
5051
|
-
/**
|
|
4734
|
+
/**
|
|
4735
|
+
* Whether the element's render completed: the moment its properties went
|
|
4736
|
+
* live, which css reads as `:state(rendered)`. Unlike `:defined`, which holds
|
|
4737
|
+
* from the constructor, it is false until the dom exists.
|
|
4738
|
+
* @returns {boolean}
|
|
4739
|
+
*/
|
|
5052
4740
|
get rendered() {
|
|
5053
4741
|
return this.#parsed;
|
|
5054
4742
|
}
|
|
5055
4743
|
/**
|
|
5056
4744
|
* Projects a property back onto its observed attribute, marshalled through
|
|
5057
|
-
* the mapper the attribute was declared with
|
|
5058
|
-
* reaches its attribute, so a setter never has to know how its own type
|
|
5059
|
-
* serializes.
|
|
4745
|
+
* the mapper the attribute was declared with.
|
|
5060
4746
|
*
|
|
5061
4747
|
* A value the attribute already carries is not written at all, so reflecting
|
|
5062
4748
|
* what an attribute write just delivered ends there rather than looping, and
|
|
@@ -5087,18 +4773,37 @@ class ParsedElement extends HTMLElement {
|
|
|
5087
4773
|
*/
|
|
5088
4774
|
|
|
5089
4775
|
/**
|
|
5090
|
-
* The receiver contract of the module functions:
|
|
5091
|
-
*
|
|
4776
|
+
* The receiver contract of the module functions: a plain object carrying the
|
|
4777
|
+
* translations and the locale, as Localization.of() binds them, or the proxy
|
|
4778
|
+
* over the data stack a template calls them with.
|
|
5092
4779
|
*
|
|
5093
4780
|
* @typedef {{ l10n?: Messages, locale?: string }} Receiver
|
|
5094
4781
|
*/
|
|
5095
4782
|
|
|
4783
|
+
/**
|
|
4784
|
+
* The translations or the locale of a receiver. From a template they are read
|
|
4785
|
+
* from the bound data alone (the registry's overlays, or the data handed to
|
|
4786
|
+
* the Template factory): the rows and responses a render pushes on top are
|
|
4787
|
+
* what arrives later, and must not supply or replace the page's own messages.
|
|
4788
|
+
* @param {any} receiver
|
|
4789
|
+
* @param {'l10n' | 'locale'} name
|
|
4790
|
+
* @returns {any}
|
|
4791
|
+
*/
|
|
4792
|
+
const configured = (receiver, name) => {
|
|
4793
|
+
const bound = receiver[Expressions.BOUND];
|
|
4794
|
+
return typeof bound === 'function' ? bound(name) : receiver[name];
|
|
4795
|
+
};
|
|
4796
|
+
|
|
5096
4797
|
const PLACEHOLDER = /\{(\w+)\}/g;
|
|
5097
4798
|
const POSITIONAL = /\{(\d+)\}/g;
|
|
5098
4799
|
/** @param {any} v @returns {v is Record<string, string>} */
|
|
5099
|
-
const isPlainObject = (v) =>
|
|
4800
|
+
const isPlainObject = (v) =>
|
|
4801
|
+
typeof v === 'object' && v !== null && [Object.prototype, null].includes(Object.getPrototypeOf(v));
|
|
4802
|
+
|
|
4803
|
+
/** @param {any} problem @returns {string|undefined} the reason, unless it is missing or blank */
|
|
4804
|
+
const reasonOf = (problem) =>
|
|
4805
|
+
typeof problem?.reason === 'string' && problem.reason.trim() !== '' ? problem.reason : undefined;
|
|
5100
4806
|
|
|
5101
|
-
/** every l10n problem is reported once per session: a missing key in a row template must not flood the console */
|
|
5102
4807
|
const warned = new Set();
|
|
5103
4808
|
const warnOnce = (problem) => {
|
|
5104
4809
|
if (!warned.has(problem)) {
|
|
@@ -5109,11 +4814,7 @@ const warnOnce = (problem) => {
|
|
|
5109
4814
|
|
|
5110
4815
|
const formatters = new BoundedCache(100);
|
|
5111
4816
|
/**
|
|
5112
|
-
* Intl
|
|
5113
|
-
* lookups), while formatting on a built instance is near free: a list rendering
|
|
5114
|
-
* a size per row must not rebuild a NumberFormat per cell, so instances are
|
|
5115
|
-
* memoized by constructor, locale and options.
|
|
5116
|
-
*
|
|
4817
|
+
* An Intl instance, memoized by constructor, locale and options.
|
|
5117
4818
|
* @param {{ new (locale: string | undefined, options: any): any }} ctor
|
|
5118
4819
|
* @param {string | undefined} locale
|
|
5119
4820
|
* @param {any} [options]
|
|
@@ -5127,35 +4828,40 @@ const formatter = (ctor, locale, options) => {
|
|
|
5127
4828
|
* The translations as a template module: `#l10n:t()` for messages, plus date,
|
|
5128
4829
|
* number and bytes formatting in the page's locale. The template form and the
|
|
5129
4830
|
* imperative `of()` facade both resolve through the registry's scope, so they
|
|
5130
|
-
* always produce the same result for the same key.
|
|
4831
|
+
* always produce the same result for the same key. A template reads `l10n`
|
|
4832
|
+
* and `locale` from its bound data only, the most recent layer winning: an
|
|
4833
|
+
* overlay pushed while rendering (a row, a response) never reaches them.
|
|
5131
4834
|
*/
|
|
5132
4835
|
class Localization {
|
|
5133
4836
|
/**
|
|
5134
4837
|
* Resolves a message from the translations and interpolates its arguments.
|
|
5135
|
-
* A single plain object argument
|
|
5136
|
-
* ({name}); anything else interpolates positional ones ({0}).
|
|
4838
|
+
* A single plain object argument (an object literal, or one with a null
|
|
4839
|
+
* prototype) interpolates named placeholders ({name}); anything else interpolates positional ones ({0}).
|
|
5137
4840
|
* A plural leaf selects its form through Intl.PluralRules over the
|
|
5138
4841
|
* numeric {count} named argument.
|
|
5139
4842
|
*
|
|
5140
4843
|
* @param {string} key
|
|
5141
4844
|
* @param {...any} args
|
|
4845
|
+
* Each problem (a missing key, placeholder or plural form) is warned once
|
|
4846
|
+
* per page.
|
|
4847
|
+
*
|
|
5142
4848
|
* @this {Receiver}
|
|
5143
4849
|
* @returns {string} the message, or the key itself when the translations do not carry it
|
|
5144
4850
|
*/
|
|
5145
4851
|
static t(key, ...args) {
|
|
5146
|
-
const messages = this
|
|
5147
|
-
let message = messages[key];
|
|
4852
|
+
const messages = configured(this, 'l10n') ?? {};
|
|
4853
|
+
let message = Object.hasOwn(messages, key) ? messages[key] : undefined;
|
|
5148
4854
|
if (message === undefined) {
|
|
5149
4855
|
warnOnce(`missing message "${key}"`);
|
|
5150
4856
|
return key;
|
|
5151
4857
|
}
|
|
4858
|
+
const named = args.length === 1 && isPlainObject(args[0]) ? args[0] : null;
|
|
5152
4859
|
if (isPlainObject(message)) {
|
|
5153
|
-
|
|
5154
|
-
if (typeof named.count !== 'number') {
|
|
4860
|
+
if (typeof named?.count !== 'number') {
|
|
5155
4861
|
warnOnce(`plural message "${key}" needs a numeric {count}`);
|
|
5156
4862
|
message = message.other;
|
|
5157
4863
|
} else {
|
|
5158
|
-
const locale = this
|
|
4864
|
+
const locale = configured(this, 'locale') ?? navigator?.language ?? 'en';
|
|
5159
4865
|
const category = formatter(Intl.PluralRules, locale).select(named.count);
|
|
5160
4866
|
message = message[category] ?? message.other;
|
|
5161
4867
|
}
|
|
@@ -5164,10 +4870,9 @@ class Localization {
|
|
|
5164
4870
|
warnOnce(`plural message "${key}" has no "other" form`);
|
|
5165
4871
|
return key;
|
|
5166
4872
|
}
|
|
5167
|
-
if (
|
|
5168
|
-
const named = args[0];
|
|
4873
|
+
if (named) {
|
|
5169
4874
|
return message.replace(PLACEHOLDER, (literal, name) => {
|
|
5170
|
-
if (!(name
|
|
4875
|
+
if (!Object.hasOwn(named, name)) {
|
|
5171
4876
|
warnOnce(`message "${key}" wants {${name}}`);
|
|
5172
4877
|
return literal;
|
|
5173
4878
|
}
|
|
@@ -5193,9 +4898,10 @@ class Localization {
|
|
|
5193
4898
|
* @param {Date | number} value
|
|
5194
4899
|
* @param {Intl.DateTimeFormatOptions} [options]
|
|
5195
4900
|
* @this {Receiver}
|
|
4901
|
+
* @returns {string}
|
|
5196
4902
|
*/
|
|
5197
4903
|
static date(value, options) {
|
|
5198
|
-
return formatter(Intl.DateTimeFormat, this
|
|
4904
|
+
return formatter(Intl.DateTimeFormat, configured(this, 'locale'), options).format(value);
|
|
5199
4905
|
}
|
|
5200
4906
|
|
|
5201
4907
|
/**
|
|
@@ -5204,9 +4910,10 @@ class Localization {
|
|
|
5204
4910
|
* @param {number} value
|
|
5205
4911
|
* @param {Intl.NumberFormatOptions} [options]
|
|
5206
4912
|
* @this {Receiver}
|
|
4913
|
+
* @returns {string}
|
|
5207
4914
|
*/
|
|
5208
4915
|
static number(value, options) {
|
|
5209
|
-
return formatter(Intl.NumberFormat, this
|
|
4916
|
+
return formatter(Intl.NumberFormat, configured(this, 'locale'), options).format(value);
|
|
5210
4917
|
}
|
|
5211
4918
|
|
|
5212
4919
|
/**
|
|
@@ -5216,9 +4923,10 @@ class Localization {
|
|
|
5216
4923
|
*
|
|
5217
4924
|
* @param {number} value
|
|
5218
4925
|
* @this {Receiver}
|
|
4926
|
+
* @returns {string}
|
|
5219
4927
|
*/
|
|
5220
4928
|
static bytes(value) {
|
|
5221
|
-
const format = formatter(Intl.NumberFormat, this
|
|
4929
|
+
const format = formatter(Intl.NumberFormat, configured(this, 'locale'), { maximumFractionDigits: 2 }).format;
|
|
5222
4930
|
if (value >= 1024 * 1024 * 1024) {
|
|
5223
4931
|
return `${format(value / 1024 / 1024 / 1024)}GiB`;
|
|
5224
4932
|
}
|
|
@@ -5231,16 +4939,37 @@ class Localization {
|
|
|
5231
4939
|
return `${format(value)}B`;
|
|
5232
4940
|
}
|
|
5233
4941
|
|
|
4942
|
+
/**
|
|
4943
|
+
* What went wrong, as text for the reader. A value carrying a non-empty
|
|
4944
|
+
* `problems` array reads as their reasons, one per line, a blank or missing
|
|
4945
|
+
* reason reading as the `failure.no-reason` message. Any other value with a
|
|
4946
|
+
* `message` reads as that message; anything else as its string form, and
|
|
4947
|
+
* null or undefined as the empty string.
|
|
4948
|
+
*
|
|
4949
|
+
* @param {any} value what was thrown or rejected with
|
|
4950
|
+
* @this {Receiver}
|
|
4951
|
+
* @returns {string}
|
|
4952
|
+
*/
|
|
4953
|
+
static failure(value) {
|
|
4954
|
+
if (Array.isArray(value?.problems) && value.problems.length > 0) {
|
|
4955
|
+
return value.problems.map((p) => reasonOf(p) ?? Localization.t.call(this, 'failure.no-reason')).join('\n');
|
|
4956
|
+
}
|
|
4957
|
+
if (value?.message !== undefined) {
|
|
4958
|
+
return String(value.message);
|
|
4959
|
+
}
|
|
4960
|
+
return value == null ? '' : String(value);
|
|
4961
|
+
}
|
|
4962
|
+
|
|
5234
4963
|
/**
|
|
5235
4964
|
* An imperative facade over the module functions, resolving the translations
|
|
5236
|
-
* and the locale from the registry overlays on every call
|
|
4965
|
+
* and the locale from the registry overlays on every call, the same lookup a
|
|
4966
|
+
* template makes: a facade taken at module scope, before the plugin
|
|
4967
|
+
* configures, still sees the translations.
|
|
5237
4968
|
*
|
|
5238
4969
|
* @param {{ locale?: string }} [overrides] an explicit locale, winning over the registry one
|
|
4970
|
+
* @returns {{ t: (key: string, ...args: any[]) => string, date: (value: Date | number, options?: Intl.DateTimeFormatOptions) => string, number: (value: number, options?: Intl.NumberFormatOptions) => string, bytes: (value: number) => string, failure: (value: any) => string }}
|
|
5239
4971
|
*/
|
|
5240
4972
|
static of(overrides = {}) {
|
|
5241
|
-
//resolved per call, not per facade: a module-scope `Localization.of()` is
|
|
5242
|
-
//bound before the plugin configures. The registry's own evaluator does the
|
|
5243
|
-
//lookup, so this cannot drift from what a template sees
|
|
5244
4973
|
const resolve = (prop) => registry.evaluator().resolve(prop);
|
|
5245
4974
|
/** @param {any} fn */
|
|
5246
4975
|
const bind =
|
|
@@ -5252,6 +4981,7 @@ class Localization {
|
|
|
5252
4981
|
date: bind(Localization.date),
|
|
5253
4982
|
number: bind(Localization.number),
|
|
5254
4983
|
bytes: bind(Localization.bytes),
|
|
4984
|
+
failure: bind(Localization.failure),
|
|
5255
4985
|
};
|
|
5256
4986
|
}
|
|
5257
4987
|
}
|