@optionfactory/fml 8.0.2 → 9.0.0-rc1
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/LICENSE.md +7 -0
- package/README.md +88 -0
- package/dist/client-errors.iife.js +30 -9
- 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 +1502 -408
- package/dist/fml.css +21 -10
- package/dist/fml.css.map +1 -1
- package/dist/fml.d.mts +3 -1299
- package/dist/fml.iife.js +5304 -2270
- 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/fml.min.mjs +1 -1
- package/dist/fml.min.mjs.map +1 -1
- package/dist/fml.mjs +6 -8694
- package/dist/fml.mjs.map +1 -1
- package/dist/ftl.d.mts +433 -72
- package/dist/ftl.iife.js +1313 -807
- 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 +1312 -808
- package/dist/ftl.mjs.map +1 -1
- package/dist/ful.css +21 -10
- package/dist/ful.css.map +1 -1
- package/dist/ful.d.mts +806 -257
- package/dist/ful.iife.js +3697 -1356
- 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 +3686 -1356
- package/dist/ful.mjs.map +1 -1
- package/dist/httpc.d.mts +114 -19
- package/dist/httpc.iife.js +253 -83
- 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 +250 -84
- package/dist/httpc.mjs.map +1 -1
- package/dist/vscode.html-custom-data.json +607 -65
- package/dist/web-types.json +1471 -376
- package/package.json +16 -8
package/dist/ftl.iife.js
CHANGED
|
@@ -1,6 +1,46 @@
|
|
|
1
1
|
var ftl = (function (exports) {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* A Map bounded by entry count, evicting in insertion order: not an LRU (a hit
|
|
6
|
+
* does not refresh an entry), just a cap keeping unbounded key spaces (parsed
|
|
7
|
+
* expressions, compiled masks, formatter instances) from growing forever. The
|
|
8
|
+
* capacity is sized so eviction never happens on a sane page: hitting it means
|
|
9
|
+
* dynamically generated keys, where FIFO's worst case (evicting a hot entry)
|
|
10
|
+
* costs one recomputation.
|
|
11
|
+
*/
|
|
12
|
+
class BoundedCache {
|
|
13
|
+
#max;
|
|
14
|
+
#entries = new Map();
|
|
15
|
+
/** @param {number} max */
|
|
16
|
+
constructor(max) {
|
|
17
|
+
this.#max = max;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The cached value for the key, computing and caching it on a miss. A
|
|
21
|
+
* computed null or undefined is cached like any value; a throwing compute
|
|
22
|
+
* caches nothing.
|
|
23
|
+
* @template K, V
|
|
24
|
+
* @param {K} key
|
|
25
|
+
* @param {(key: K) => V} compute
|
|
26
|
+
* @returns {V}
|
|
27
|
+
*/
|
|
28
|
+
getOrCompute(key, compute) {
|
|
29
|
+
if (this.#entries.has(key)) {
|
|
30
|
+
return this.#entries.get(key);
|
|
31
|
+
}
|
|
32
|
+
const value = compute(key);
|
|
33
|
+
if (this.#entries.size >= this.#max) {
|
|
34
|
+
this.#entries.delete(this.#entries.keys().next().value);
|
|
35
|
+
}
|
|
36
|
+
this.#entries.set(key, value);
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
get size() {
|
|
40
|
+
return this.#entries.size;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
4
44
|
const nodes = {
|
|
5
45
|
ter: Symbol('ternary'),
|
|
6
46
|
elv: Symbol('elvis'),
|
|
@@ -38,6 +78,16 @@ var ftl = (function (exports) {
|
|
|
38
78
|
//
|
|
39
79
|
// https://peggyjs.org/
|
|
40
80
|
|
|
81
|
+
//keyword classification lives here, not in the grammar's ordered choice: the
|
|
82
|
+
//identifier rule consumes the whole word and the action decides, so no keyword
|
|
83
|
+
//can grab a longer identifier's prefix. A Map, not a plain object: `in` on the
|
|
84
|
+
//latter would classify inherited names such as `constructor` as keywords
|
|
85
|
+
const keywords = new Map([
|
|
86
|
+
['true', true],
|
|
87
|
+
['false', false],
|
|
88
|
+
['null', null],
|
|
89
|
+
['undefined', undefined],
|
|
90
|
+
]);
|
|
41
91
|
|
|
42
92
|
class peg$SyntaxError extends SyntaxError {
|
|
43
93
|
constructor(message, expected, found, location) {
|
|
@@ -222,22 +272,18 @@ var ftl = (function (exports) {
|
|
|
222
272
|
const peg$c32 = "(";
|
|
223
273
|
const peg$c34 = ",";
|
|
224
274
|
const peg$c36 = ")";
|
|
225
|
-
const peg$c39 = "
|
|
226
|
-
const peg$c40 = "
|
|
227
|
-
const peg$c41 = "
|
|
228
|
-
const peg$c42 = "
|
|
229
|
-
const peg$c43 = "
|
|
230
|
-
const peg$
|
|
231
|
-
const peg$
|
|
232
|
-
const peg$
|
|
233
|
-
const peg$
|
|
234
|
-
const peg$
|
|
235
|
-
const peg$c53 = "
|
|
236
|
-
const peg$c54 = "
|
|
237
|
-
const peg$c55 = ">=";
|
|
238
|
-
const peg$c56 = ">";
|
|
239
|
-
const peg$c57 = "<=";
|
|
240
|
-
const peg$c58 = "<";
|
|
275
|
+
const peg$c39 = "-";
|
|
276
|
+
const peg$c40 = "'";
|
|
277
|
+
const peg$c41 = "\"";
|
|
278
|
+
const peg$c42 = "`";
|
|
279
|
+
const peg$c43 = "}";
|
|
280
|
+
const peg$c45 = "#";
|
|
281
|
+
const peg$c49 = "==";
|
|
282
|
+
const peg$c50 = "!=";
|
|
283
|
+
const peg$c51 = ">=";
|
|
284
|
+
const peg$c52 = ">";
|
|
285
|
+
const peg$c53 = "<=";
|
|
286
|
+
const peg$c54 = "<";
|
|
241
287
|
|
|
242
288
|
const peg$r0 = /^[^\\{]/;
|
|
243
289
|
const peg$r1 = /^[0-9]/;
|
|
@@ -245,7 +291,7 @@ var ftl = (function (exports) {
|
|
|
245
291
|
const peg$r3 = /^[^"]/;
|
|
246
292
|
const peg$r4 = /^[^\\`{]/;
|
|
247
293
|
const peg$r5 = /^[a-zA-Z$_]/;
|
|
248
|
-
const peg$r6 = /^[a-zA-Z$_0-
|
|
294
|
+
const peg$r6 = /^[a-zA-Z$_0-9]/;
|
|
249
295
|
const peg$r7 = /^[ \t\n\r]/;
|
|
250
296
|
|
|
251
297
|
const peg$e0 = peg$literalExpectation("{{{{", false);
|
|
@@ -276,40 +322,33 @@ var ftl = (function (exports) {
|
|
|
276
322
|
const peg$e25 = peg$literalExpectation(",", false);
|
|
277
323
|
const peg$e26 = peg$literalExpectation(")", false);
|
|
278
324
|
const peg$e27 = peg$otherExpectation("module-function-call");
|
|
279
|
-
const peg$e28 = peg$otherExpectation("
|
|
280
|
-
const peg$e29 = peg$literalExpectation("
|
|
281
|
-
const peg$e30 = peg$
|
|
282
|
-
const peg$e31 = peg$otherExpectation("
|
|
283
|
-
const peg$e32 = peg$literalExpectation("
|
|
284
|
-
const peg$e33 = peg$
|
|
285
|
-
const peg$e34 = peg$literalExpectation("
|
|
286
|
-
const peg$e35 = peg$
|
|
287
|
-
const peg$e36 = peg$literalExpectation("
|
|
288
|
-
const peg$e37 = peg$
|
|
289
|
-
const peg$e38 = peg$
|
|
290
|
-
const peg$e39 = peg$
|
|
291
|
-
const peg$e40 = peg$classExpectation(["
|
|
292
|
-
const peg$e41 = peg$
|
|
293
|
-
const peg$e42 = peg$
|
|
294
|
-
const peg$e43 = peg$
|
|
295
|
-
const peg$e44 = peg$
|
|
296
|
-
const peg$e45 = peg$
|
|
297
|
-
const peg$e46 = peg$
|
|
298
|
-
const peg$e47 = peg$
|
|
299
|
-
const peg$e48 = peg$
|
|
300
|
-
const peg$e49 = peg$
|
|
301
|
-
const peg$e50 = peg$
|
|
302
|
-
const peg$e51 = peg$literalExpectation("
|
|
303
|
-
const peg$e52 = peg$
|
|
304
|
-
const peg$e53 = peg$
|
|
305
|
-
const peg$e54 = peg$
|
|
306
|
-
const peg$e55 = peg$literalExpectation("==", false);
|
|
307
|
-
const peg$e56 = peg$literalExpectation("!=", false);
|
|
308
|
-
const peg$e57 = peg$literalExpectation(">=", false);
|
|
309
|
-
const peg$e58 = peg$literalExpectation(">", false);
|
|
310
|
-
const peg$e59 = peg$literalExpectation("<=", false);
|
|
311
|
-
const peg$e60 = peg$literalExpectation("<", false);
|
|
312
|
-
const peg$e61 = peg$classExpectation([" ", "\t", "\n", "\r"], false, false, false);
|
|
325
|
+
const peg$e28 = peg$otherExpectation("number-literal");
|
|
326
|
+
const peg$e29 = peg$literalExpectation("-", false);
|
|
327
|
+
const peg$e30 = peg$classExpectation([["0", "9"]], false, false, false);
|
|
328
|
+
const peg$e31 = peg$otherExpectation("string-literal");
|
|
329
|
+
const peg$e32 = peg$literalExpectation("'", false);
|
|
330
|
+
const peg$e33 = peg$classExpectation(["'"], true, false, false);
|
|
331
|
+
const peg$e34 = peg$literalExpectation("\"", false);
|
|
332
|
+
const peg$e35 = peg$classExpectation(["\""], true, false, false);
|
|
333
|
+
const peg$e36 = peg$literalExpectation("`", false);
|
|
334
|
+
const peg$e37 = peg$otherExpectation("tstring-expression");
|
|
335
|
+
const peg$e38 = peg$literalExpectation("}", false);
|
|
336
|
+
const peg$e39 = peg$otherExpectation("tstring-literal");
|
|
337
|
+
const peg$e40 = peg$classExpectation(["\\", "`", "{"], true, false, false);
|
|
338
|
+
const peg$e41 = peg$otherExpectation("array-literal");
|
|
339
|
+
const peg$e42 = peg$otherExpectation("dict-literal");
|
|
340
|
+
const peg$e43 = peg$otherExpectation("module-function");
|
|
341
|
+
const peg$e44 = peg$literalExpectation("#", false);
|
|
342
|
+
const peg$e45 = peg$classExpectation([["a", "z"], ["A", "Z"], "$", "_"], false, false, false);
|
|
343
|
+
const peg$e46 = peg$classExpectation([["a", "z"], ["A", "Z"], "$", "_", ["0", "9"]], false, false, false);
|
|
344
|
+
const peg$e47 = peg$otherExpectation("identifier-or-keyword");
|
|
345
|
+
const peg$e48 = peg$literalExpectation("==", false);
|
|
346
|
+
const peg$e49 = peg$literalExpectation("!=", false);
|
|
347
|
+
const peg$e50 = peg$literalExpectation(">=", false);
|
|
348
|
+
const peg$e51 = peg$literalExpectation(">", false);
|
|
349
|
+
const peg$e52 = peg$literalExpectation("<=", false);
|
|
350
|
+
const peg$e53 = peg$literalExpectation("<", false);
|
|
351
|
+
const peg$e54 = peg$classExpectation([" ", "\t", "\n", "\r"], false, false, false);
|
|
313
352
|
|
|
314
353
|
function peg$f0(value) { return {type:nodes.templated.ten, value} }
|
|
315
354
|
function peg$f1(value) { return {type:nodes.templated.teh, value} }
|
|
@@ -353,8 +392,8 @@ var ftl = (function (exports) {
|
|
|
353
392
|
}
|
|
354
393
|
function peg$f16(expr) { return {type:nodes.not, expr} }
|
|
355
394
|
function peg$f17(lhs, rhs) { return {type: nodes.access, lhs, rhs} }
|
|
356
|
-
function peg$f18(op, rhs) {
|
|
357
|
-
|
|
395
|
+
function peg$f18(op, rhs) {
|
|
396
|
+
return {type: nodes.member, ns: op === '?.', rhs: rhs};
|
|
358
397
|
}
|
|
359
398
|
function peg$f19(op, rhs) {
|
|
360
399
|
return {type: nodes.subscript, ns: op !== null, rhs};
|
|
@@ -367,41 +406,29 @@ var ftl = (function (exports) {
|
|
|
367
406
|
const args = h === null ? [] : [h, ...t];
|
|
368
407
|
return {type: nodes.call, value: fn, args};
|
|
369
408
|
}
|
|
370
|
-
function peg$f23() {
|
|
371
|
-
return {type: nodes.literal, value: true};
|
|
372
|
-
}
|
|
373
|
-
function peg$f24() {
|
|
374
|
-
return {type: nodes.literal, value: false};
|
|
375
|
-
}
|
|
376
|
-
function peg$f25() {
|
|
377
|
-
return {type: nodes.literal, value: null};
|
|
378
|
-
}
|
|
379
|
-
function peg$f26() {
|
|
380
|
-
return {type: nodes.literal, value: undefined};
|
|
381
|
-
}
|
|
382
|
-
function peg$f27(value) {
|
|
409
|
+
function peg$f23(value) {
|
|
383
410
|
return { type: nodes.literal, value: parseFloat(value) };
|
|
384
411
|
}
|
|
385
|
-
function peg$
|
|
386
|
-
function peg$
|
|
387
|
-
function peg$
|
|
412
|
+
function peg$f24(value) { return {type: nodes.literal, value}; }
|
|
413
|
+
function peg$f25(value) { return {type: nodes.literal, value}; }
|
|
414
|
+
function peg$f26(parts) {
|
|
388
415
|
return {type: nodes.tstring, parts};
|
|
389
416
|
}
|
|
390
|
-
function peg$
|
|
417
|
+
function peg$f27(expr) {
|
|
391
418
|
return expr;
|
|
392
419
|
}
|
|
393
|
-
function peg$
|
|
394
|
-
function peg$
|
|
420
|
+
function peg$f28(c) { return c; }
|
|
421
|
+
function peg$f29(parts) {
|
|
395
422
|
return { type: nodes.literal, value: parts.join('') };
|
|
396
423
|
}
|
|
397
|
-
function peg$
|
|
424
|
+
function peg$f30(char) {
|
|
398
425
|
return { type: nodes.literal, value: char };
|
|
399
426
|
}
|
|
400
|
-
function peg$
|
|
427
|
+
function peg$f31(h, t) {
|
|
401
428
|
const value = h === null ? [] : [h, ...t];
|
|
402
429
|
return {type: nodes.array, value};
|
|
403
430
|
}
|
|
404
|
-
function peg$
|
|
431
|
+
function peg$f32(h, t) {
|
|
405
432
|
var value = [];
|
|
406
433
|
if (h !== null){
|
|
407
434
|
value.push(h);
|
|
@@ -409,11 +436,13 @@ var ftl = (function (exports) {
|
|
|
409
436
|
value.push.apply(value, t);
|
|
410
437
|
return {type: nodes.dict, value};
|
|
411
438
|
}
|
|
412
|
-
function peg$
|
|
439
|
+
function peg$f33(module, v) {
|
|
413
440
|
return {type: nodes.function, module, value: v}
|
|
414
441
|
}
|
|
415
|
-
function peg$
|
|
416
|
-
|
|
442
|
+
function peg$f34(s) {
|
|
443
|
+
return keywords.has(s)
|
|
444
|
+
? {type: nodes.literal, value: keywords.get(s)}
|
|
445
|
+
: {type: nodes.symbol, value: s};
|
|
417
446
|
}
|
|
418
447
|
let peg$currPos = options.peg$currPos | 0;
|
|
419
448
|
const peg$posDetailsCache = [{ line: 1, column: 1 }];
|
|
@@ -541,7 +570,7 @@ var ftl = (function (exports) {
|
|
|
541
570
|
function peg$parseTemplatedRoot() {
|
|
542
571
|
let s0, s1;
|
|
543
572
|
|
|
544
|
-
const key = peg$currPos *
|
|
573
|
+
const key = peg$currPos * 38 + 0;
|
|
545
574
|
const cached = peg$resultsCache[key];
|
|
546
575
|
|
|
547
576
|
if (cached) {
|
|
@@ -565,7 +594,7 @@ var ftl = (function (exports) {
|
|
|
565
594
|
function peg$parseTemplatedExpression() {
|
|
566
595
|
let s0, s1, s2, s3, s4;
|
|
567
596
|
|
|
568
|
-
const key = peg$currPos *
|
|
597
|
+
const key = peg$currPos * 38 + 1;
|
|
569
598
|
const cached = peg$resultsCache[key];
|
|
570
599
|
|
|
571
600
|
if (cached) {
|
|
@@ -670,7 +699,7 @@ var ftl = (function (exports) {
|
|
|
670
699
|
function peg$parseBeginNode() {
|
|
671
700
|
let s0;
|
|
672
701
|
|
|
673
|
-
const key = peg$currPos *
|
|
702
|
+
const key = peg$currPos * 38 + 2;
|
|
674
703
|
const cached = peg$resultsCache[key];
|
|
675
704
|
|
|
676
705
|
if (cached) {
|
|
@@ -695,7 +724,7 @@ var ftl = (function (exports) {
|
|
|
695
724
|
function peg$parseEndNode() {
|
|
696
725
|
let s0;
|
|
697
726
|
|
|
698
|
-
const key = peg$currPos *
|
|
727
|
+
const key = peg$currPos * 38 + 3;
|
|
699
728
|
const cached = peg$resultsCache[key];
|
|
700
729
|
|
|
701
730
|
if (cached) {
|
|
@@ -720,7 +749,7 @@ var ftl = (function (exports) {
|
|
|
720
749
|
function peg$parseBeginHtml() {
|
|
721
750
|
let s0;
|
|
722
751
|
|
|
723
|
-
const key = peg$currPos *
|
|
752
|
+
const key = peg$currPos * 38 + 4;
|
|
724
753
|
const cached = peg$resultsCache[key];
|
|
725
754
|
|
|
726
755
|
if (cached) {
|
|
@@ -745,7 +774,7 @@ var ftl = (function (exports) {
|
|
|
745
774
|
function peg$parseEndHtml() {
|
|
746
775
|
let s0;
|
|
747
776
|
|
|
748
|
-
const key = peg$currPos *
|
|
777
|
+
const key = peg$currPos * 38 + 5;
|
|
749
778
|
const cached = peg$resultsCache[key];
|
|
750
779
|
|
|
751
780
|
if (cached) {
|
|
@@ -770,7 +799,7 @@ var ftl = (function (exports) {
|
|
|
770
799
|
function peg$parseBeginText() {
|
|
771
800
|
let s0;
|
|
772
801
|
|
|
773
|
-
const key = peg$currPos *
|
|
802
|
+
const key = peg$currPos * 38 + 6;
|
|
774
803
|
const cached = peg$resultsCache[key];
|
|
775
804
|
|
|
776
805
|
if (cached) {
|
|
@@ -795,7 +824,7 @@ var ftl = (function (exports) {
|
|
|
795
824
|
function peg$parseEndText() {
|
|
796
825
|
let s0;
|
|
797
826
|
|
|
798
|
-
const key = peg$currPos *
|
|
827
|
+
const key = peg$currPos * 38 + 7;
|
|
799
828
|
const cached = peg$resultsCache[key];
|
|
800
829
|
|
|
801
830
|
if (cached) {
|
|
@@ -818,9 +847,9 @@ var ftl = (function (exports) {
|
|
|
818
847
|
}
|
|
819
848
|
|
|
820
849
|
function peg$parseTemplatedText() {
|
|
821
|
-
let s0, s1, s2, s3, s4, s5;
|
|
850
|
+
let s0, s1, s2, s3, s4, s5, s6;
|
|
822
851
|
|
|
823
|
-
const key = peg$currPos *
|
|
852
|
+
const key = peg$currPos * 38 + 8;
|
|
824
853
|
const cached = peg$resultsCache[key];
|
|
825
854
|
|
|
826
855
|
if (cached) {
|
|
@@ -888,40 +917,46 @@ var ftl = (function (exports) {
|
|
|
888
917
|
}
|
|
889
918
|
if (s2 === peg$FAILED) {
|
|
890
919
|
s2 = peg$currPos;
|
|
920
|
+
s3 = peg$currPos;
|
|
891
921
|
if (input.charCodeAt(peg$currPos) === 123) {
|
|
892
|
-
|
|
922
|
+
s4 = peg$c9;
|
|
893
923
|
peg$currPos++;
|
|
894
924
|
} else {
|
|
895
|
-
|
|
925
|
+
s4 = peg$FAILED;
|
|
896
926
|
if (peg$silentFails === 0) { peg$fail(peg$e9); }
|
|
897
927
|
}
|
|
898
|
-
if (
|
|
899
|
-
|
|
928
|
+
if (s4 !== peg$FAILED) {
|
|
929
|
+
s5 = peg$currPos;
|
|
900
930
|
peg$silentFails++;
|
|
901
931
|
if (input.charCodeAt(peg$currPos) === 123) {
|
|
902
|
-
|
|
932
|
+
s6 = peg$c9;
|
|
903
933
|
peg$currPos++;
|
|
904
934
|
} else {
|
|
905
|
-
|
|
935
|
+
s6 = peg$FAILED;
|
|
906
936
|
if (peg$silentFails === 0) { peg$fail(peg$e9); }
|
|
907
937
|
}
|
|
908
938
|
peg$silentFails--;
|
|
909
|
-
if (
|
|
910
|
-
|
|
939
|
+
if (s6 === peg$FAILED) {
|
|
940
|
+
s5 = undefined;
|
|
911
941
|
} else {
|
|
912
|
-
peg$currPos =
|
|
913
|
-
|
|
942
|
+
peg$currPos = s5;
|
|
943
|
+
s5 = peg$FAILED;
|
|
914
944
|
}
|
|
915
|
-
if (
|
|
916
|
-
|
|
917
|
-
|
|
945
|
+
if (s5 !== peg$FAILED) {
|
|
946
|
+
s4 = [s4, s5];
|
|
947
|
+
s3 = s4;
|
|
918
948
|
} else {
|
|
919
|
-
peg$currPos =
|
|
920
|
-
|
|
949
|
+
peg$currPos = s3;
|
|
950
|
+
s3 = peg$FAILED;
|
|
921
951
|
}
|
|
922
952
|
} else {
|
|
923
|
-
peg$currPos =
|
|
924
|
-
|
|
953
|
+
peg$currPos = s3;
|
|
954
|
+
s3 = peg$FAILED;
|
|
955
|
+
}
|
|
956
|
+
if (s3 !== peg$FAILED) {
|
|
957
|
+
s2 = input.substring(s2, peg$currPos);
|
|
958
|
+
} else {
|
|
959
|
+
s2 = s3;
|
|
925
960
|
}
|
|
926
961
|
}
|
|
927
962
|
}
|
|
@@ -985,40 +1020,46 @@ var ftl = (function (exports) {
|
|
|
985
1020
|
}
|
|
986
1021
|
if (s2 === peg$FAILED) {
|
|
987
1022
|
s2 = peg$currPos;
|
|
1023
|
+
s3 = peg$currPos;
|
|
988
1024
|
if (input.charCodeAt(peg$currPos) === 123) {
|
|
989
|
-
|
|
1025
|
+
s4 = peg$c9;
|
|
990
1026
|
peg$currPos++;
|
|
991
1027
|
} else {
|
|
992
|
-
|
|
1028
|
+
s4 = peg$FAILED;
|
|
993
1029
|
if (peg$silentFails === 0) { peg$fail(peg$e9); }
|
|
994
1030
|
}
|
|
995
|
-
if (
|
|
996
|
-
|
|
1031
|
+
if (s4 !== peg$FAILED) {
|
|
1032
|
+
s5 = peg$currPos;
|
|
997
1033
|
peg$silentFails++;
|
|
998
1034
|
if (input.charCodeAt(peg$currPos) === 123) {
|
|
999
|
-
|
|
1035
|
+
s6 = peg$c9;
|
|
1000
1036
|
peg$currPos++;
|
|
1001
1037
|
} else {
|
|
1002
|
-
|
|
1038
|
+
s6 = peg$FAILED;
|
|
1003
1039
|
if (peg$silentFails === 0) { peg$fail(peg$e9); }
|
|
1004
1040
|
}
|
|
1005
1041
|
peg$silentFails--;
|
|
1006
|
-
if (
|
|
1007
|
-
|
|
1042
|
+
if (s6 === peg$FAILED) {
|
|
1043
|
+
s5 = undefined;
|
|
1008
1044
|
} else {
|
|
1009
|
-
peg$currPos =
|
|
1010
|
-
|
|
1045
|
+
peg$currPos = s5;
|
|
1046
|
+
s5 = peg$FAILED;
|
|
1011
1047
|
}
|
|
1012
|
-
if (
|
|
1013
|
-
|
|
1014
|
-
|
|
1048
|
+
if (s5 !== peg$FAILED) {
|
|
1049
|
+
s4 = [s4, s5];
|
|
1050
|
+
s3 = s4;
|
|
1015
1051
|
} else {
|
|
1016
|
-
peg$currPos =
|
|
1017
|
-
|
|
1052
|
+
peg$currPos = s3;
|
|
1053
|
+
s3 = peg$FAILED;
|
|
1018
1054
|
}
|
|
1019
1055
|
} else {
|
|
1020
|
-
peg$currPos =
|
|
1021
|
-
|
|
1056
|
+
peg$currPos = s3;
|
|
1057
|
+
s3 = peg$FAILED;
|
|
1058
|
+
}
|
|
1059
|
+
if (s3 !== peg$FAILED) {
|
|
1060
|
+
s2 = input.substring(s2, peg$currPos);
|
|
1061
|
+
} else {
|
|
1062
|
+
s2 = s3;
|
|
1022
1063
|
}
|
|
1023
1064
|
}
|
|
1024
1065
|
}
|
|
@@ -1039,7 +1080,7 @@ var ftl = (function (exports) {
|
|
|
1039
1080
|
function peg$parseExpressionRoot() {
|
|
1040
1081
|
let s0, s2;
|
|
1041
1082
|
|
|
1042
|
-
const key = peg$currPos *
|
|
1083
|
+
const key = peg$currPos * 38 + 9;
|
|
1043
1084
|
const cached = peg$resultsCache[key];
|
|
1044
1085
|
|
|
1045
1086
|
if (cached) {
|
|
@@ -1067,7 +1108,7 @@ var ftl = (function (exports) {
|
|
|
1067
1108
|
function peg$parseTernaryExpression() {
|
|
1068
1109
|
let s0, s1, s3, s5, s7, s9;
|
|
1069
1110
|
|
|
1070
|
-
const key = peg$currPos *
|
|
1111
|
+
const key = peg$currPos * 38 + 10;
|
|
1071
1112
|
const cached = peg$resultsCache[key];
|
|
1072
1113
|
|
|
1073
1114
|
if (cached) {
|
|
@@ -1136,7 +1177,7 @@ var ftl = (function (exports) {
|
|
|
1136
1177
|
function peg$parseElvisExpression() {
|
|
1137
1178
|
let s0, s1, s3, s5;
|
|
1138
1179
|
|
|
1139
|
-
const key = peg$currPos *
|
|
1180
|
+
const key = peg$currPos * 38 + 11;
|
|
1140
1181
|
const cached = peg$resultsCache[key];
|
|
1141
1182
|
|
|
1142
1183
|
if (cached) {
|
|
@@ -1185,7 +1226,7 @@ var ftl = (function (exports) {
|
|
|
1185
1226
|
function peg$parseNullCoalescingExpression() {
|
|
1186
1227
|
let s0, s1, s3, s5;
|
|
1187
1228
|
|
|
1188
|
-
const key = peg$currPos *
|
|
1229
|
+
const key = peg$currPos * 38 + 12;
|
|
1189
1230
|
const cached = peg$resultsCache[key];
|
|
1190
1231
|
|
|
1191
1232
|
if (cached) {
|
|
@@ -1234,7 +1275,7 @@ var ftl = (function (exports) {
|
|
|
1234
1275
|
function peg$parseOrExpression() {
|
|
1235
1276
|
let s0, s1, s2, s3, s5, s7;
|
|
1236
1277
|
|
|
1237
|
-
const key = peg$currPos *
|
|
1278
|
+
const key = peg$currPos * 38 + 13;
|
|
1238
1279
|
const cached = peg$resultsCache[key];
|
|
1239
1280
|
|
|
1240
1281
|
if (cached) {
|
|
@@ -1308,7 +1349,7 @@ var ftl = (function (exports) {
|
|
|
1308
1349
|
function peg$parseAndExpression() {
|
|
1309
1350
|
let s0, s1, s2, s3, s5, s7;
|
|
1310
1351
|
|
|
1311
|
-
const key = peg$currPos *
|
|
1352
|
+
const key = peg$currPos * 38 + 14;
|
|
1312
1353
|
const cached = peg$resultsCache[key];
|
|
1313
1354
|
|
|
1314
1355
|
if (cached) {
|
|
@@ -1382,7 +1423,7 @@ var ftl = (function (exports) {
|
|
|
1382
1423
|
function peg$parseEqExpression() {
|
|
1383
1424
|
let s0, s1, s2, s3, s5, s7;
|
|
1384
1425
|
|
|
1385
|
-
const key = peg$currPos *
|
|
1426
|
+
const key = peg$currPos * 38 + 15;
|
|
1386
1427
|
const cached = peg$resultsCache[key];
|
|
1387
1428
|
|
|
1388
1429
|
if (cached) {
|
|
@@ -1444,7 +1485,7 @@ var ftl = (function (exports) {
|
|
|
1444
1485
|
function peg$parseRelExpression() {
|
|
1445
1486
|
let s0, s1, s2, s3, s5, s7;
|
|
1446
1487
|
|
|
1447
|
-
const key = peg$currPos *
|
|
1488
|
+
const key = peg$currPos * 38 + 16;
|
|
1448
1489
|
const cached = peg$resultsCache[key];
|
|
1449
1490
|
|
|
1450
1491
|
if (cached) {
|
|
@@ -1506,7 +1547,7 @@ var ftl = (function (exports) {
|
|
|
1506
1547
|
function peg$parseNotExpression() {
|
|
1507
1548
|
let s0, s1, s3;
|
|
1508
1549
|
|
|
1509
|
-
const key = peg$currPos *
|
|
1550
|
+
const key = peg$currPos * 38 + 17;
|
|
1510
1551
|
const cached = peg$resultsCache[key];
|
|
1511
1552
|
|
|
1512
1553
|
if (cached) {
|
|
@@ -1548,7 +1589,7 @@ var ftl = (function (exports) {
|
|
|
1548
1589
|
function peg$parseAccess() {
|
|
1549
1590
|
let s0, s1, s3, s4, s5;
|
|
1550
1591
|
|
|
1551
|
-
const key = peg$currPos *
|
|
1592
|
+
const key = peg$currPos * 38 + 18;
|
|
1552
1593
|
const cached = peg$resultsCache[key];
|
|
1553
1594
|
|
|
1554
1595
|
if (cached) {
|
|
@@ -1609,7 +1650,7 @@ var ftl = (function (exports) {
|
|
|
1609
1650
|
function peg$parseAccessExpression() {
|
|
1610
1651
|
let s0;
|
|
1611
1652
|
|
|
1612
|
-
const key = peg$currPos *
|
|
1653
|
+
const key = peg$currPos * 38 + 19;
|
|
1613
1654
|
const cached = peg$resultsCache[key];
|
|
1614
1655
|
|
|
1615
1656
|
if (cached) {
|
|
@@ -1634,7 +1675,7 @@ var ftl = (function (exports) {
|
|
|
1634
1675
|
function peg$parseAccessMember() {
|
|
1635
1676
|
let s0, s1, s3;
|
|
1636
1677
|
|
|
1637
|
-
const key = peg$currPos *
|
|
1678
|
+
const key = peg$currPos * 38 + 20;
|
|
1638
1679
|
const cached = peg$resultsCache[key];
|
|
1639
1680
|
|
|
1640
1681
|
if (cached) {
|
|
@@ -1663,7 +1704,7 @@ var ftl = (function (exports) {
|
|
|
1663
1704
|
}
|
|
1664
1705
|
if (s1 !== peg$FAILED) {
|
|
1665
1706
|
peg$parse_();
|
|
1666
|
-
s3 = peg$
|
|
1707
|
+
s3 = peg$parseWord();
|
|
1667
1708
|
if (s3 !== peg$FAILED) {
|
|
1668
1709
|
s0 = peg$f18(s1, s3);
|
|
1669
1710
|
} else {
|
|
@@ -1688,7 +1729,7 @@ var ftl = (function (exports) {
|
|
|
1688
1729
|
function peg$parseAccessSubscript() {
|
|
1689
1730
|
let s0, s1, s2, s4, s6;
|
|
1690
1731
|
|
|
1691
|
-
const key = peg$currPos *
|
|
1732
|
+
const key = peg$currPos * 38 + 21;
|
|
1692
1733
|
const cached = peg$resultsCache[key];
|
|
1693
1734
|
|
|
1694
1735
|
if (cached) {
|
|
@@ -1756,7 +1797,7 @@ var ftl = (function (exports) {
|
|
|
1756
1797
|
function peg$parseAccessMethodCall() {
|
|
1757
1798
|
let s0, s1, s2, s4, s5, s6, s7, s9;
|
|
1758
1799
|
|
|
1759
|
-
const key = peg$currPos *
|
|
1800
|
+
const key = peg$currPos * 38 + 22;
|
|
1760
1801
|
const cached = peg$resultsCache[key];
|
|
1761
1802
|
|
|
1762
1803
|
if (cached) {
|
|
@@ -1877,7 +1918,7 @@ var ftl = (function (exports) {
|
|
|
1877
1918
|
function peg$parseGroupingExpression() {
|
|
1878
1919
|
let s0, s1, s3, s5;
|
|
1879
1920
|
|
|
1880
|
-
const key = peg$currPos *
|
|
1921
|
+
const key = peg$currPos * 38 + 23;
|
|
1881
1922
|
const cached = peg$resultsCache[key];
|
|
1882
1923
|
|
|
1883
1924
|
if (cached) {
|
|
@@ -1925,7 +1966,7 @@ var ftl = (function (exports) {
|
|
|
1925
1966
|
if (s0 === peg$FAILED) {
|
|
1926
1967
|
s0 = peg$parseAnyLiteral();
|
|
1927
1968
|
if (s0 === peg$FAILED) {
|
|
1928
|
-
s0 = peg$
|
|
1969
|
+
s0 = peg$parseIdentifier();
|
|
1929
1970
|
}
|
|
1930
1971
|
}
|
|
1931
1972
|
}
|
|
@@ -1938,7 +1979,7 @@ var ftl = (function (exports) {
|
|
|
1938
1979
|
function peg$parseModuleFunctionCall() {
|
|
1939
1980
|
let s0, s1, s2, s4, s5, s6, s7, s9;
|
|
1940
1981
|
|
|
1941
|
-
const key = peg$currPos *
|
|
1982
|
+
const key = peg$currPos * 38 + 24;
|
|
1942
1983
|
const cached = peg$resultsCache[key];
|
|
1943
1984
|
|
|
1944
1985
|
if (cached) {
|
|
@@ -2055,7 +2096,7 @@ var ftl = (function (exports) {
|
|
|
2055
2096
|
function peg$parseAnyLiteral() {
|
|
2056
2097
|
let s0;
|
|
2057
2098
|
|
|
2058
|
-
const key = peg$currPos *
|
|
2099
|
+
const key = peg$currPos * 38 + 25;
|
|
2059
2100
|
const cached = peg$resultsCache[key];
|
|
2060
2101
|
|
|
2061
2102
|
if (cached) {
|
|
@@ -2066,20 +2107,11 @@ var ftl = (function (exports) {
|
|
|
2066
2107
|
|
|
2067
2108
|
s0 = peg$parseNumberLiteral();
|
|
2068
2109
|
if (s0 === peg$FAILED) {
|
|
2069
|
-
s0 = peg$
|
|
2110
|
+
s0 = peg$parseStringLiteral();
|
|
2070
2111
|
if (s0 === peg$FAILED) {
|
|
2071
|
-
s0 = peg$
|
|
2112
|
+
s0 = peg$parseArrayLiteral();
|
|
2072
2113
|
if (s0 === peg$FAILED) {
|
|
2073
|
-
s0 = peg$
|
|
2074
|
-
if (s0 === peg$FAILED) {
|
|
2075
|
-
s0 = peg$parseStringLiteral();
|
|
2076
|
-
if (s0 === peg$FAILED) {
|
|
2077
|
-
s0 = peg$parseArrayLiteral();
|
|
2078
|
-
if (s0 === peg$FAILED) {
|
|
2079
|
-
s0 = peg$parseDictLiteral();
|
|
2080
|
-
}
|
|
2081
|
-
}
|
|
2082
|
-
}
|
|
2114
|
+
s0 = peg$parseDictLiteral();
|
|
2083
2115
|
}
|
|
2084
2116
|
}
|
|
2085
2117
|
}
|
|
@@ -2089,132 +2121,10 @@ var ftl = (function (exports) {
|
|
|
2089
2121
|
return s0;
|
|
2090
2122
|
}
|
|
2091
2123
|
|
|
2092
|
-
function peg$parseBooleanLiteral() {
|
|
2093
|
-
let s0, s1;
|
|
2094
|
-
|
|
2095
|
-
const key = peg$currPos * 40 + 26;
|
|
2096
|
-
const cached = peg$resultsCache[key];
|
|
2097
|
-
|
|
2098
|
-
if (cached) {
|
|
2099
|
-
peg$currPos = cached.nextPos;
|
|
2100
|
-
|
|
2101
|
-
return cached.result;
|
|
2102
|
-
}
|
|
2103
|
-
|
|
2104
|
-
peg$silentFails++;
|
|
2105
|
-
s0 = peg$currPos;
|
|
2106
|
-
if (input.substr(peg$currPos, 4) === peg$c39) {
|
|
2107
|
-
s1 = peg$c39;
|
|
2108
|
-
peg$currPos += 4;
|
|
2109
|
-
} else {
|
|
2110
|
-
s1 = peg$FAILED;
|
|
2111
|
-
if (peg$silentFails === 0) { peg$fail(peg$e29); }
|
|
2112
|
-
}
|
|
2113
|
-
if (s1 !== peg$FAILED) {
|
|
2114
|
-
s1 = peg$f23();
|
|
2115
|
-
}
|
|
2116
|
-
s0 = s1;
|
|
2117
|
-
if (s0 === peg$FAILED) {
|
|
2118
|
-
s0 = peg$currPos;
|
|
2119
|
-
if (input.substr(peg$currPos, 5) === peg$c40) {
|
|
2120
|
-
s1 = peg$c40;
|
|
2121
|
-
peg$currPos += 5;
|
|
2122
|
-
} else {
|
|
2123
|
-
s1 = peg$FAILED;
|
|
2124
|
-
if (peg$silentFails === 0) { peg$fail(peg$e30); }
|
|
2125
|
-
}
|
|
2126
|
-
if (s1 !== peg$FAILED) {
|
|
2127
|
-
s1 = peg$f24();
|
|
2128
|
-
}
|
|
2129
|
-
s0 = s1;
|
|
2130
|
-
}
|
|
2131
|
-
peg$silentFails--;
|
|
2132
|
-
if (s0 === peg$FAILED) {
|
|
2133
|
-
s1 = peg$FAILED;
|
|
2134
|
-
if (peg$silentFails === 0) { peg$fail(peg$e28); }
|
|
2135
|
-
}
|
|
2136
|
-
|
|
2137
|
-
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
|
2138
|
-
|
|
2139
|
-
return s0;
|
|
2140
|
-
}
|
|
2141
|
-
|
|
2142
|
-
function peg$parseNullLiteral() {
|
|
2143
|
-
let s0, s1;
|
|
2144
|
-
|
|
2145
|
-
const key = peg$currPos * 40 + 27;
|
|
2146
|
-
const cached = peg$resultsCache[key];
|
|
2147
|
-
|
|
2148
|
-
if (cached) {
|
|
2149
|
-
peg$currPos = cached.nextPos;
|
|
2150
|
-
|
|
2151
|
-
return cached.result;
|
|
2152
|
-
}
|
|
2153
|
-
|
|
2154
|
-
peg$silentFails++;
|
|
2155
|
-
s0 = peg$currPos;
|
|
2156
|
-
if (input.substr(peg$currPos, 4) === peg$c41) {
|
|
2157
|
-
s1 = peg$c41;
|
|
2158
|
-
peg$currPos += 4;
|
|
2159
|
-
} else {
|
|
2160
|
-
s1 = peg$FAILED;
|
|
2161
|
-
if (peg$silentFails === 0) { peg$fail(peg$e32); }
|
|
2162
|
-
}
|
|
2163
|
-
if (s1 !== peg$FAILED) {
|
|
2164
|
-
s1 = peg$f25();
|
|
2165
|
-
}
|
|
2166
|
-
s0 = s1;
|
|
2167
|
-
peg$silentFails--;
|
|
2168
|
-
if (s0 === peg$FAILED) {
|
|
2169
|
-
s1 = peg$FAILED;
|
|
2170
|
-
if (peg$silentFails === 0) { peg$fail(peg$e31); }
|
|
2171
|
-
}
|
|
2172
|
-
|
|
2173
|
-
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
|
2174
|
-
|
|
2175
|
-
return s0;
|
|
2176
|
-
}
|
|
2177
|
-
|
|
2178
|
-
function peg$parseUndefinedLiteral() {
|
|
2179
|
-
let s0, s1;
|
|
2180
|
-
|
|
2181
|
-
const key = peg$currPos * 40 + 28;
|
|
2182
|
-
const cached = peg$resultsCache[key];
|
|
2183
|
-
|
|
2184
|
-
if (cached) {
|
|
2185
|
-
peg$currPos = cached.nextPos;
|
|
2186
|
-
|
|
2187
|
-
return cached.result;
|
|
2188
|
-
}
|
|
2189
|
-
|
|
2190
|
-
peg$silentFails++;
|
|
2191
|
-
s0 = peg$currPos;
|
|
2192
|
-
if (input.substr(peg$currPos, 9) === peg$c42) {
|
|
2193
|
-
s1 = peg$c42;
|
|
2194
|
-
peg$currPos += 9;
|
|
2195
|
-
} else {
|
|
2196
|
-
s1 = peg$FAILED;
|
|
2197
|
-
if (peg$silentFails === 0) { peg$fail(peg$e34); }
|
|
2198
|
-
}
|
|
2199
|
-
if (s1 !== peg$FAILED) {
|
|
2200
|
-
s1 = peg$f26();
|
|
2201
|
-
}
|
|
2202
|
-
s0 = s1;
|
|
2203
|
-
peg$silentFails--;
|
|
2204
|
-
if (s0 === peg$FAILED) {
|
|
2205
|
-
s1 = peg$FAILED;
|
|
2206
|
-
if (peg$silentFails === 0) { peg$fail(peg$e33); }
|
|
2207
|
-
}
|
|
2208
|
-
|
|
2209
|
-
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
|
2210
|
-
|
|
2211
|
-
return s0;
|
|
2212
|
-
}
|
|
2213
|
-
|
|
2214
2124
|
function peg$parseNumberLiteral() {
|
|
2215
2125
|
let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
|
|
2216
2126
|
|
|
2217
|
-
const key = peg$currPos *
|
|
2127
|
+
const key = peg$currPos * 38 + 26;
|
|
2218
2128
|
const cached = peg$resultsCache[key];
|
|
2219
2129
|
|
|
2220
2130
|
if (cached) {
|
|
@@ -2228,11 +2138,11 @@ var ftl = (function (exports) {
|
|
|
2228
2138
|
s1 = peg$currPos;
|
|
2229
2139
|
s2 = peg$currPos;
|
|
2230
2140
|
if (input.charCodeAt(peg$currPos) === 45) {
|
|
2231
|
-
s3 = peg$
|
|
2141
|
+
s3 = peg$c39;
|
|
2232
2142
|
peg$currPos++;
|
|
2233
2143
|
} else {
|
|
2234
2144
|
s3 = peg$FAILED;
|
|
2235
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2145
|
+
if (peg$silentFails === 0) { peg$fail(peg$e29); }
|
|
2236
2146
|
}
|
|
2237
2147
|
if (s3 === peg$FAILED) {
|
|
2238
2148
|
s3 = null;
|
|
@@ -2244,7 +2154,7 @@ var ftl = (function (exports) {
|
|
|
2244
2154
|
peg$currPos++;
|
|
2245
2155
|
} else {
|
|
2246
2156
|
s6 = peg$FAILED;
|
|
2247
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2157
|
+
if (peg$silentFails === 0) { peg$fail(peg$e30); }
|
|
2248
2158
|
}
|
|
2249
2159
|
if (s6 !== peg$FAILED) {
|
|
2250
2160
|
while (s6 !== peg$FAILED) {
|
|
@@ -2254,7 +2164,7 @@ var ftl = (function (exports) {
|
|
|
2254
2164
|
peg$currPos++;
|
|
2255
2165
|
} else {
|
|
2256
2166
|
s6 = peg$FAILED;
|
|
2257
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2167
|
+
if (peg$silentFails === 0) { peg$fail(peg$e30); }
|
|
2258
2168
|
}
|
|
2259
2169
|
}
|
|
2260
2170
|
} else {
|
|
@@ -2276,7 +2186,7 @@ var ftl = (function (exports) {
|
|
|
2276
2186
|
peg$currPos++;
|
|
2277
2187
|
} else {
|
|
2278
2188
|
s9 = peg$FAILED;
|
|
2279
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2189
|
+
if (peg$silentFails === 0) { peg$fail(peg$e30); }
|
|
2280
2190
|
}
|
|
2281
2191
|
while (s9 !== peg$FAILED) {
|
|
2282
2192
|
s8.push(s9);
|
|
@@ -2285,7 +2195,7 @@ var ftl = (function (exports) {
|
|
|
2285
2195
|
peg$currPos++;
|
|
2286
2196
|
} else {
|
|
2287
2197
|
s9 = peg$FAILED;
|
|
2288
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2198
|
+
if (peg$silentFails === 0) { peg$fail(peg$e30); }
|
|
2289
2199
|
}
|
|
2290
2200
|
}
|
|
2291
2201
|
s7 = [s7, s8];
|
|
@@ -2319,7 +2229,7 @@ var ftl = (function (exports) {
|
|
|
2319
2229
|
peg$currPos++;
|
|
2320
2230
|
} else {
|
|
2321
2231
|
s7 = peg$FAILED;
|
|
2322
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2232
|
+
if (peg$silentFails === 0) { peg$fail(peg$e30); }
|
|
2323
2233
|
}
|
|
2324
2234
|
if (s7 !== peg$FAILED) {
|
|
2325
2235
|
while (s7 !== peg$FAILED) {
|
|
@@ -2329,7 +2239,7 @@ var ftl = (function (exports) {
|
|
|
2329
2239
|
peg$currPos++;
|
|
2330
2240
|
} else {
|
|
2331
2241
|
s7 = peg$FAILED;
|
|
2332
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2242
|
+
if (peg$silentFails === 0) { peg$fail(peg$e30); }
|
|
2333
2243
|
}
|
|
2334
2244
|
}
|
|
2335
2245
|
} else {
|
|
@@ -2360,13 +2270,13 @@ var ftl = (function (exports) {
|
|
|
2360
2270
|
s1 = s2;
|
|
2361
2271
|
}
|
|
2362
2272
|
if (s1 !== peg$FAILED) {
|
|
2363
|
-
s1 = peg$
|
|
2273
|
+
s1 = peg$f23(s1);
|
|
2364
2274
|
}
|
|
2365
2275
|
s0 = s1;
|
|
2366
2276
|
peg$silentFails--;
|
|
2367
2277
|
if (s0 === peg$FAILED) {
|
|
2368
2278
|
s1 = peg$FAILED;
|
|
2369
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2279
|
+
if (peg$silentFails === 0) { peg$fail(peg$e28); }
|
|
2370
2280
|
}
|
|
2371
2281
|
|
|
2372
2282
|
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
|
@@ -2377,7 +2287,7 @@ var ftl = (function (exports) {
|
|
|
2377
2287
|
function peg$parseStringLiteral() {
|
|
2378
2288
|
let s0, s1, s2, s3, s4;
|
|
2379
2289
|
|
|
2380
|
-
const key = peg$currPos *
|
|
2290
|
+
const key = peg$currPos * 38 + 27;
|
|
2381
2291
|
const cached = peg$resultsCache[key];
|
|
2382
2292
|
|
|
2383
2293
|
if (cached) {
|
|
@@ -2389,11 +2299,11 @@ var ftl = (function (exports) {
|
|
|
2389
2299
|
peg$silentFails++;
|
|
2390
2300
|
s0 = peg$currPos;
|
|
2391
2301
|
if (input.charCodeAt(peg$currPos) === 39) {
|
|
2392
|
-
s1 = peg$
|
|
2302
|
+
s1 = peg$c40;
|
|
2393
2303
|
peg$currPos++;
|
|
2394
2304
|
} else {
|
|
2395
2305
|
s1 = peg$FAILED;
|
|
2396
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2306
|
+
if (peg$silentFails === 0) { peg$fail(peg$e32); }
|
|
2397
2307
|
}
|
|
2398
2308
|
if (s1 !== peg$FAILED) {
|
|
2399
2309
|
s2 = peg$currPos;
|
|
@@ -2403,7 +2313,7 @@ var ftl = (function (exports) {
|
|
|
2403
2313
|
peg$currPos++;
|
|
2404
2314
|
} else {
|
|
2405
2315
|
s4 = peg$FAILED;
|
|
2406
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2316
|
+
if (peg$silentFails === 0) { peg$fail(peg$e33); }
|
|
2407
2317
|
}
|
|
2408
2318
|
while (s4 !== peg$FAILED) {
|
|
2409
2319
|
s3.push(s4);
|
|
@@ -2412,19 +2322,19 @@ var ftl = (function (exports) {
|
|
|
2412
2322
|
peg$currPos++;
|
|
2413
2323
|
} else {
|
|
2414
2324
|
s4 = peg$FAILED;
|
|
2415
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2325
|
+
if (peg$silentFails === 0) { peg$fail(peg$e33); }
|
|
2416
2326
|
}
|
|
2417
2327
|
}
|
|
2418
2328
|
s2 = input.substring(s2, peg$currPos);
|
|
2419
2329
|
if (input.charCodeAt(peg$currPos) === 39) {
|
|
2420
|
-
s3 = peg$
|
|
2330
|
+
s3 = peg$c40;
|
|
2421
2331
|
peg$currPos++;
|
|
2422
2332
|
} else {
|
|
2423
2333
|
s3 = peg$FAILED;
|
|
2424
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2334
|
+
if (peg$silentFails === 0) { peg$fail(peg$e32); }
|
|
2425
2335
|
}
|
|
2426
2336
|
if (s3 !== peg$FAILED) {
|
|
2427
|
-
s0 = peg$
|
|
2337
|
+
s0 = peg$f24(s2);
|
|
2428
2338
|
} else {
|
|
2429
2339
|
peg$currPos = s0;
|
|
2430
2340
|
s0 = peg$FAILED;
|
|
@@ -2436,11 +2346,11 @@ var ftl = (function (exports) {
|
|
|
2436
2346
|
if (s0 === peg$FAILED) {
|
|
2437
2347
|
s0 = peg$currPos;
|
|
2438
2348
|
if (input.charCodeAt(peg$currPos) === 34) {
|
|
2439
|
-
s1 = peg$
|
|
2349
|
+
s1 = peg$c41;
|
|
2440
2350
|
peg$currPos++;
|
|
2441
2351
|
} else {
|
|
2442
2352
|
s1 = peg$FAILED;
|
|
2443
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2353
|
+
if (peg$silentFails === 0) { peg$fail(peg$e34); }
|
|
2444
2354
|
}
|
|
2445
2355
|
if (s1 !== peg$FAILED) {
|
|
2446
2356
|
s2 = peg$currPos;
|
|
@@ -2450,7 +2360,7 @@ var ftl = (function (exports) {
|
|
|
2450
2360
|
peg$currPos++;
|
|
2451
2361
|
} else {
|
|
2452
2362
|
s4 = peg$FAILED;
|
|
2453
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2363
|
+
if (peg$silentFails === 0) { peg$fail(peg$e35); }
|
|
2454
2364
|
}
|
|
2455
2365
|
while (s4 !== peg$FAILED) {
|
|
2456
2366
|
s3.push(s4);
|
|
@@ -2459,19 +2369,19 @@ var ftl = (function (exports) {
|
|
|
2459
2369
|
peg$currPos++;
|
|
2460
2370
|
} else {
|
|
2461
2371
|
s4 = peg$FAILED;
|
|
2462
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2372
|
+
if (peg$silentFails === 0) { peg$fail(peg$e35); }
|
|
2463
2373
|
}
|
|
2464
2374
|
}
|
|
2465
2375
|
s2 = input.substring(s2, peg$currPos);
|
|
2466
2376
|
if (input.charCodeAt(peg$currPos) === 34) {
|
|
2467
|
-
s3 = peg$
|
|
2377
|
+
s3 = peg$c41;
|
|
2468
2378
|
peg$currPos++;
|
|
2469
2379
|
} else {
|
|
2470
2380
|
s3 = peg$FAILED;
|
|
2471
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2381
|
+
if (peg$silentFails === 0) { peg$fail(peg$e34); }
|
|
2472
2382
|
}
|
|
2473
2383
|
if (s3 !== peg$FAILED) {
|
|
2474
|
-
s0 = peg$
|
|
2384
|
+
s0 = peg$f25(s2);
|
|
2475
2385
|
} else {
|
|
2476
2386
|
peg$currPos = s0;
|
|
2477
2387
|
s0 = peg$FAILED;
|
|
@@ -2483,11 +2393,11 @@ var ftl = (function (exports) {
|
|
|
2483
2393
|
if (s0 === peg$FAILED) {
|
|
2484
2394
|
s0 = peg$currPos;
|
|
2485
2395
|
if (input.charCodeAt(peg$currPos) === 96) {
|
|
2486
|
-
s1 = peg$
|
|
2396
|
+
s1 = peg$c42;
|
|
2487
2397
|
peg$currPos++;
|
|
2488
2398
|
} else {
|
|
2489
2399
|
s1 = peg$FAILED;
|
|
2490
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2400
|
+
if (peg$silentFails === 0) { peg$fail(peg$e36); }
|
|
2491
2401
|
}
|
|
2492
2402
|
if (s1 !== peg$FAILED) {
|
|
2493
2403
|
s2 = [];
|
|
@@ -2503,14 +2413,14 @@ var ftl = (function (exports) {
|
|
|
2503
2413
|
}
|
|
2504
2414
|
}
|
|
2505
2415
|
if (input.charCodeAt(peg$currPos) === 96) {
|
|
2506
|
-
s3 = peg$
|
|
2416
|
+
s3 = peg$c42;
|
|
2507
2417
|
peg$currPos++;
|
|
2508
2418
|
} else {
|
|
2509
2419
|
s3 = peg$FAILED;
|
|
2510
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2420
|
+
if (peg$silentFails === 0) { peg$fail(peg$e36); }
|
|
2511
2421
|
}
|
|
2512
2422
|
if (s3 !== peg$FAILED) {
|
|
2513
|
-
s0 = peg$
|
|
2423
|
+
s0 = peg$f26(s2);
|
|
2514
2424
|
} else {
|
|
2515
2425
|
peg$currPos = s0;
|
|
2516
2426
|
s0 = peg$FAILED;
|
|
@@ -2524,7 +2434,7 @@ var ftl = (function (exports) {
|
|
|
2524
2434
|
peg$silentFails--;
|
|
2525
2435
|
if (s0 === peg$FAILED) {
|
|
2526
2436
|
s1 = peg$FAILED;
|
|
2527
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2437
|
+
if (peg$silentFails === 0) { peg$fail(peg$e31); }
|
|
2528
2438
|
}
|
|
2529
2439
|
|
|
2530
2440
|
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
|
@@ -2535,7 +2445,7 @@ var ftl = (function (exports) {
|
|
|
2535
2445
|
function peg$parseTemplateStringExpression() {
|
|
2536
2446
|
let s0, s1, s3, s5;
|
|
2537
2447
|
|
|
2538
|
-
const key = peg$currPos *
|
|
2448
|
+
const key = peg$currPos * 38 + 28;
|
|
2539
2449
|
const cached = peg$resultsCache[key];
|
|
2540
2450
|
|
|
2541
2451
|
if (cached) {
|
|
@@ -2559,14 +2469,14 @@ var ftl = (function (exports) {
|
|
|
2559
2469
|
if (s3 !== peg$FAILED) {
|
|
2560
2470
|
peg$parse_();
|
|
2561
2471
|
if (input.charCodeAt(peg$currPos) === 125) {
|
|
2562
|
-
s5 = peg$
|
|
2472
|
+
s5 = peg$c43;
|
|
2563
2473
|
peg$currPos++;
|
|
2564
2474
|
} else {
|
|
2565
2475
|
s5 = peg$FAILED;
|
|
2566
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2476
|
+
if (peg$silentFails === 0) { peg$fail(peg$e38); }
|
|
2567
2477
|
}
|
|
2568
2478
|
if (s5 !== peg$FAILED) {
|
|
2569
|
-
s0 = peg$
|
|
2479
|
+
s0 = peg$f27(s3);
|
|
2570
2480
|
} else {
|
|
2571
2481
|
peg$currPos = s0;
|
|
2572
2482
|
s0 = peg$FAILED;
|
|
@@ -2582,7 +2492,7 @@ var ftl = (function (exports) {
|
|
|
2582
2492
|
peg$silentFails--;
|
|
2583
2493
|
if (s0 === peg$FAILED) {
|
|
2584
2494
|
s1 = peg$FAILED;
|
|
2585
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2495
|
+
if (peg$silentFails === 0) { peg$fail(peg$e37); }
|
|
2586
2496
|
}
|
|
2587
2497
|
|
|
2588
2498
|
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
|
@@ -2593,7 +2503,7 @@ var ftl = (function (exports) {
|
|
|
2593
2503
|
function peg$parseTemplateStringLiteral() {
|
|
2594
2504
|
let s0, s1, s2, s3, s4;
|
|
2595
2505
|
|
|
2596
|
-
const key = peg$currPos *
|
|
2506
|
+
const key = peg$currPos * 38 + 29;
|
|
2597
2507
|
const cached = peg$resultsCache[key];
|
|
2598
2508
|
|
|
2599
2509
|
if (cached) {
|
|
@@ -2612,7 +2522,7 @@ var ftl = (function (exports) {
|
|
|
2612
2522
|
peg$currPos++;
|
|
2613
2523
|
} else {
|
|
2614
2524
|
s4 = peg$FAILED;
|
|
2615
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2525
|
+
if (peg$silentFails === 0) { peg$fail(peg$e40); }
|
|
2616
2526
|
}
|
|
2617
2527
|
if (s4 !== peg$FAILED) {
|
|
2618
2528
|
while (s4 !== peg$FAILED) {
|
|
@@ -2622,7 +2532,7 @@ var ftl = (function (exports) {
|
|
|
2622
2532
|
peg$currPos++;
|
|
2623
2533
|
} else {
|
|
2624
2534
|
s4 = peg$FAILED;
|
|
2625
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2535
|
+
if (peg$silentFails === 0) { peg$fail(peg$e40); }
|
|
2626
2536
|
}
|
|
2627
2537
|
}
|
|
2628
2538
|
} else {
|
|
@@ -2651,7 +2561,7 @@ var ftl = (function (exports) {
|
|
|
2651
2561
|
if (peg$silentFails === 0) { peg$fail(peg$e8); }
|
|
2652
2562
|
}
|
|
2653
2563
|
if (s4 !== peg$FAILED) {
|
|
2654
|
-
s2 = peg$
|
|
2564
|
+
s2 = peg$f28(s4);
|
|
2655
2565
|
} else {
|
|
2656
2566
|
peg$currPos = s2;
|
|
2657
2567
|
s2 = peg$FAILED;
|
|
@@ -2671,7 +2581,7 @@ var ftl = (function (exports) {
|
|
|
2671
2581
|
peg$currPos++;
|
|
2672
2582
|
} else {
|
|
2673
2583
|
s4 = peg$FAILED;
|
|
2674
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2584
|
+
if (peg$silentFails === 0) { peg$fail(peg$e40); }
|
|
2675
2585
|
}
|
|
2676
2586
|
if (s4 !== peg$FAILED) {
|
|
2677
2587
|
while (s4 !== peg$FAILED) {
|
|
@@ -2681,7 +2591,7 @@ var ftl = (function (exports) {
|
|
|
2681
2591
|
peg$currPos++;
|
|
2682
2592
|
} else {
|
|
2683
2593
|
s4 = peg$FAILED;
|
|
2684
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2594
|
+
if (peg$silentFails === 0) { peg$fail(peg$e40); }
|
|
2685
2595
|
}
|
|
2686
2596
|
}
|
|
2687
2597
|
} else {
|
|
@@ -2710,7 +2620,7 @@ var ftl = (function (exports) {
|
|
|
2710
2620
|
if (peg$silentFails === 0) { peg$fail(peg$e8); }
|
|
2711
2621
|
}
|
|
2712
2622
|
if (s4 !== peg$FAILED) {
|
|
2713
|
-
s2 = peg$
|
|
2623
|
+
s2 = peg$f28(s4);
|
|
2714
2624
|
} else {
|
|
2715
2625
|
peg$currPos = s2;
|
|
2716
2626
|
s2 = peg$FAILED;
|
|
@@ -2725,7 +2635,7 @@ var ftl = (function (exports) {
|
|
|
2725
2635
|
s1 = peg$FAILED;
|
|
2726
2636
|
}
|
|
2727
2637
|
if (s1 !== peg$FAILED) {
|
|
2728
|
-
s1 = peg$
|
|
2638
|
+
s1 = peg$f29(s1);
|
|
2729
2639
|
}
|
|
2730
2640
|
s0 = s1;
|
|
2731
2641
|
if (s0 === peg$FAILED) {
|
|
@@ -2738,14 +2648,14 @@ var ftl = (function (exports) {
|
|
|
2738
2648
|
if (peg$silentFails === 0) { peg$fail(peg$e9); }
|
|
2739
2649
|
}
|
|
2740
2650
|
if (s1 !== peg$FAILED) {
|
|
2741
|
-
s1 = peg$
|
|
2651
|
+
s1 = peg$f30(s1);
|
|
2742
2652
|
}
|
|
2743
2653
|
s0 = s1;
|
|
2744
2654
|
}
|
|
2745
2655
|
peg$silentFails--;
|
|
2746
2656
|
if (s0 === peg$FAILED) {
|
|
2747
2657
|
s1 = peg$FAILED;
|
|
2748
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2658
|
+
if (peg$silentFails === 0) { peg$fail(peg$e39); }
|
|
2749
2659
|
}
|
|
2750
2660
|
|
|
2751
2661
|
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
|
@@ -2756,7 +2666,7 @@ var ftl = (function (exports) {
|
|
|
2756
2666
|
function peg$parseArrayLiteral() {
|
|
2757
2667
|
let s0, s1, s3, s5, s6, s7, s9;
|
|
2758
2668
|
|
|
2759
|
-
const key = peg$currPos *
|
|
2669
|
+
const key = peg$currPos * 38 + 30;
|
|
2760
2670
|
const cached = peg$resultsCache[key];
|
|
2761
2671
|
|
|
2762
2672
|
if (cached) {
|
|
@@ -2837,7 +2747,7 @@ var ftl = (function (exports) {
|
|
|
2837
2747
|
if (peg$silentFails === 0) { peg$fail(peg$e22); }
|
|
2838
2748
|
}
|
|
2839
2749
|
if (s6 !== peg$FAILED) {
|
|
2840
|
-
s0 = peg$
|
|
2750
|
+
s0 = peg$f31(s3, s5);
|
|
2841
2751
|
} else {
|
|
2842
2752
|
peg$currPos = s0;
|
|
2843
2753
|
s0 = peg$FAILED;
|
|
@@ -2849,7 +2759,7 @@ var ftl = (function (exports) {
|
|
|
2849
2759
|
peg$silentFails--;
|
|
2850
2760
|
if (s0 === peg$FAILED) {
|
|
2851
2761
|
s1 = peg$FAILED;
|
|
2852
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2762
|
+
if (peg$silentFails === 0) { peg$fail(peg$e41); }
|
|
2853
2763
|
}
|
|
2854
2764
|
|
|
2855
2765
|
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
|
@@ -2860,7 +2770,7 @@ var ftl = (function (exports) {
|
|
|
2860
2770
|
function peg$parseDictLiteral() {
|
|
2861
2771
|
let s0, s1, s3, s4, s5, s6, s7, s8, s9, s11, s13;
|
|
2862
2772
|
|
|
2863
|
-
const key = peg$currPos *
|
|
2773
|
+
const key = peg$currPos * 38 + 31;
|
|
2864
2774
|
const cached = peg$resultsCache[key];
|
|
2865
2775
|
|
|
2866
2776
|
if (cached) {
|
|
@@ -3001,14 +2911,14 @@ var ftl = (function (exports) {
|
|
|
3001
2911
|
}
|
|
3002
2912
|
}
|
|
3003
2913
|
if (input.charCodeAt(peg$currPos) === 125) {
|
|
3004
|
-
s6 = peg$
|
|
2914
|
+
s6 = peg$c43;
|
|
3005
2915
|
peg$currPos++;
|
|
3006
2916
|
} else {
|
|
3007
2917
|
s6 = peg$FAILED;
|
|
3008
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2918
|
+
if (peg$silentFails === 0) { peg$fail(peg$e38); }
|
|
3009
2919
|
}
|
|
3010
2920
|
if (s6 !== peg$FAILED) {
|
|
3011
|
-
s0 = peg$
|
|
2921
|
+
s0 = peg$f32(s3, s5);
|
|
3012
2922
|
} else {
|
|
3013
2923
|
peg$currPos = s0;
|
|
3014
2924
|
s0 = peg$FAILED;
|
|
@@ -3020,7 +2930,7 @@ var ftl = (function (exports) {
|
|
|
3020
2930
|
peg$silentFails--;
|
|
3021
2931
|
if (s0 === peg$FAILED) {
|
|
3022
2932
|
s1 = peg$FAILED;
|
|
3023
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2933
|
+
if (peg$silentFails === 0) { peg$fail(peg$e42); }
|
|
3024
2934
|
}
|
|
3025
2935
|
|
|
3026
2936
|
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
|
@@ -3029,9 +2939,9 @@ var ftl = (function (exports) {
|
|
|
3029
2939
|
}
|
|
3030
2940
|
|
|
3031
2941
|
function peg$parseModuleFunction() {
|
|
3032
|
-
let s0, s1, s2, s3, s4
|
|
2942
|
+
let s0, s1, s2, s3, s4;
|
|
3033
2943
|
|
|
3034
|
-
const key = peg$currPos *
|
|
2944
|
+
const key = peg$currPos * 38 + 32;
|
|
3035
2945
|
const cached = peg$resultsCache[key];
|
|
3036
2946
|
|
|
3037
2947
|
if (cached) {
|
|
@@ -3043,53 +2953,15 @@ var ftl = (function (exports) {
|
|
|
3043
2953
|
peg$silentFails++;
|
|
3044
2954
|
s0 = peg$currPos;
|
|
3045
2955
|
if (input.charCodeAt(peg$currPos) === 35) {
|
|
3046
|
-
s1 = peg$
|
|
2956
|
+
s1 = peg$c45;
|
|
3047
2957
|
peg$currPos++;
|
|
3048
2958
|
} else {
|
|
3049
2959
|
s1 = peg$FAILED;
|
|
3050
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
2960
|
+
if (peg$silentFails === 0) { peg$fail(peg$e44); }
|
|
3051
2961
|
}
|
|
3052
2962
|
if (s1 !== peg$FAILED) {
|
|
3053
2963
|
s2 = peg$currPos;
|
|
3054
|
-
s3 = peg$
|
|
3055
|
-
s4 = peg$currPos;
|
|
3056
|
-
s5 = input.charAt(peg$currPos);
|
|
3057
|
-
if (peg$r5.test(s5)) {
|
|
3058
|
-
peg$currPos++;
|
|
3059
|
-
} else {
|
|
3060
|
-
s5 = peg$FAILED;
|
|
3061
|
-
if (peg$silentFails === 0) { peg$fail(peg$e52); }
|
|
3062
|
-
}
|
|
3063
|
-
if (s5 !== peg$FAILED) {
|
|
3064
|
-
s6 = [];
|
|
3065
|
-
s7 = input.charAt(peg$currPos);
|
|
3066
|
-
if (peg$r6.test(s7)) {
|
|
3067
|
-
peg$currPos++;
|
|
3068
|
-
} else {
|
|
3069
|
-
s7 = peg$FAILED;
|
|
3070
|
-
if (peg$silentFails === 0) { peg$fail(peg$e53); }
|
|
3071
|
-
}
|
|
3072
|
-
while (s7 !== peg$FAILED) {
|
|
3073
|
-
s6.push(s7);
|
|
3074
|
-
s7 = input.charAt(peg$currPos);
|
|
3075
|
-
if (peg$r6.test(s7)) {
|
|
3076
|
-
peg$currPos++;
|
|
3077
|
-
} else {
|
|
3078
|
-
s7 = peg$FAILED;
|
|
3079
|
-
if (peg$silentFails === 0) { peg$fail(peg$e53); }
|
|
3080
|
-
}
|
|
3081
|
-
}
|
|
3082
|
-
s5 = [s5, s6];
|
|
3083
|
-
s4 = s5;
|
|
3084
|
-
} else {
|
|
3085
|
-
peg$currPos = s4;
|
|
3086
|
-
s4 = peg$FAILED;
|
|
3087
|
-
}
|
|
3088
|
-
if (s4 !== peg$FAILED) {
|
|
3089
|
-
s3 = input.substring(s3, peg$currPos);
|
|
3090
|
-
} else {
|
|
3091
|
-
s3 = s4;
|
|
3092
|
-
}
|
|
2964
|
+
s3 = peg$parseWord();
|
|
3093
2965
|
if (s3 !== peg$FAILED) {
|
|
3094
2966
|
if (input.charCodeAt(peg$currPos) === 58) {
|
|
3095
2967
|
s4 = peg$c15;
|
|
@@ -3111,47 +2983,9 @@ var ftl = (function (exports) {
|
|
|
3111
2983
|
if (s2 === peg$FAILED) {
|
|
3112
2984
|
s2 = null;
|
|
3113
2985
|
}
|
|
3114
|
-
s3 = peg$
|
|
3115
|
-
s4 = peg$currPos;
|
|
3116
|
-
s5 = input.charAt(peg$currPos);
|
|
3117
|
-
if (peg$r5.test(s5)) {
|
|
3118
|
-
peg$currPos++;
|
|
3119
|
-
} else {
|
|
3120
|
-
s5 = peg$FAILED;
|
|
3121
|
-
if (peg$silentFails === 0) { peg$fail(peg$e52); }
|
|
3122
|
-
}
|
|
3123
|
-
if (s5 !== peg$FAILED) {
|
|
3124
|
-
s6 = [];
|
|
3125
|
-
s7 = input.charAt(peg$currPos);
|
|
3126
|
-
if (peg$r6.test(s7)) {
|
|
3127
|
-
peg$currPos++;
|
|
3128
|
-
} else {
|
|
3129
|
-
s7 = peg$FAILED;
|
|
3130
|
-
if (peg$silentFails === 0) { peg$fail(peg$e53); }
|
|
3131
|
-
}
|
|
3132
|
-
while (s7 !== peg$FAILED) {
|
|
3133
|
-
s6.push(s7);
|
|
3134
|
-
s7 = input.charAt(peg$currPos);
|
|
3135
|
-
if (peg$r6.test(s7)) {
|
|
3136
|
-
peg$currPos++;
|
|
3137
|
-
} else {
|
|
3138
|
-
s7 = peg$FAILED;
|
|
3139
|
-
if (peg$silentFails === 0) { peg$fail(peg$e53); }
|
|
3140
|
-
}
|
|
3141
|
-
}
|
|
3142
|
-
s5 = [s5, s6];
|
|
3143
|
-
s4 = s5;
|
|
3144
|
-
} else {
|
|
3145
|
-
peg$currPos = s4;
|
|
3146
|
-
s4 = peg$FAILED;
|
|
3147
|
-
}
|
|
3148
|
-
if (s4 !== peg$FAILED) {
|
|
3149
|
-
s3 = input.substring(s3, peg$currPos);
|
|
3150
|
-
} else {
|
|
3151
|
-
s3 = s4;
|
|
3152
|
-
}
|
|
2986
|
+
s3 = peg$parseWord();
|
|
3153
2987
|
if (s3 !== peg$FAILED) {
|
|
3154
|
-
s0 = peg$
|
|
2988
|
+
s0 = peg$f33(s2, s3);
|
|
3155
2989
|
} else {
|
|
3156
2990
|
peg$currPos = s0;
|
|
3157
2991
|
s0 = peg$FAILED;
|
|
@@ -3163,7 +2997,7 @@ var ftl = (function (exports) {
|
|
|
3163
2997
|
peg$silentFails--;
|
|
3164
2998
|
if (s0 === peg$FAILED) {
|
|
3165
2999
|
s1 = peg$FAILED;
|
|
3166
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
3000
|
+
if (peg$silentFails === 0) { peg$fail(peg$e43); }
|
|
3167
3001
|
}
|
|
3168
3002
|
|
|
3169
3003
|
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
|
@@ -3171,10 +3005,10 @@ var ftl = (function (exports) {
|
|
|
3171
3005
|
return s0;
|
|
3172
3006
|
}
|
|
3173
3007
|
|
|
3174
|
-
function peg$
|
|
3175
|
-
let s0, s1, s2, s3, s4
|
|
3008
|
+
function peg$parseWord() {
|
|
3009
|
+
let s0, s1, s2, s3, s4;
|
|
3176
3010
|
|
|
3177
|
-
const key = peg$currPos *
|
|
3011
|
+
const key = peg$currPos * 38 + 33;
|
|
3178
3012
|
const cached = peg$resultsCache[key];
|
|
3179
3013
|
|
|
3180
3014
|
if (cached) {
|
|
@@ -3183,55 +3017,74 @@ var ftl = (function (exports) {
|
|
|
3183
3017
|
return cached.result;
|
|
3184
3018
|
}
|
|
3185
3019
|
|
|
3186
|
-
peg$silentFails++;
|
|
3187
3020
|
s0 = peg$currPos;
|
|
3188
3021
|
s1 = peg$currPos;
|
|
3189
|
-
s2 = peg$currPos;
|
|
3190
|
-
|
|
3191
|
-
if (peg$r5.test(s3)) {
|
|
3022
|
+
s2 = input.charAt(peg$currPos);
|
|
3023
|
+
if (peg$r5.test(s2)) {
|
|
3192
3024
|
peg$currPos++;
|
|
3193
3025
|
} else {
|
|
3194
|
-
|
|
3195
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
3026
|
+
s2 = peg$FAILED;
|
|
3027
|
+
if (peg$silentFails === 0) { peg$fail(peg$e45); }
|
|
3196
3028
|
}
|
|
3197
|
-
if (
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
if (peg$r6.test(
|
|
3029
|
+
if (s2 !== peg$FAILED) {
|
|
3030
|
+
s3 = [];
|
|
3031
|
+
s4 = input.charAt(peg$currPos);
|
|
3032
|
+
if (peg$r6.test(s4)) {
|
|
3201
3033
|
peg$currPos++;
|
|
3202
3034
|
} else {
|
|
3203
|
-
|
|
3204
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
3035
|
+
s4 = peg$FAILED;
|
|
3036
|
+
if (peg$silentFails === 0) { peg$fail(peg$e46); }
|
|
3205
3037
|
}
|
|
3206
|
-
while (
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
if (peg$r6.test(
|
|
3038
|
+
while (s4 !== peg$FAILED) {
|
|
3039
|
+
s3.push(s4);
|
|
3040
|
+
s4 = input.charAt(peg$currPos);
|
|
3041
|
+
if (peg$r6.test(s4)) {
|
|
3210
3042
|
peg$currPos++;
|
|
3211
3043
|
} else {
|
|
3212
|
-
|
|
3213
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
3044
|
+
s4 = peg$FAILED;
|
|
3045
|
+
if (peg$silentFails === 0) { peg$fail(peg$e46); }
|
|
3214
3046
|
}
|
|
3215
3047
|
}
|
|
3216
|
-
|
|
3217
|
-
|
|
3048
|
+
s2 = [s2, s3];
|
|
3049
|
+
s1 = s2;
|
|
3218
3050
|
} else {
|
|
3219
|
-
peg$currPos =
|
|
3220
|
-
|
|
3051
|
+
peg$currPos = s1;
|
|
3052
|
+
s1 = peg$FAILED;
|
|
3221
3053
|
}
|
|
3222
|
-
if (
|
|
3223
|
-
|
|
3054
|
+
if (s1 !== peg$FAILED) {
|
|
3055
|
+
s0 = input.substring(s0, peg$currPos);
|
|
3224
3056
|
} else {
|
|
3225
|
-
|
|
3057
|
+
s0 = s1;
|
|
3226
3058
|
}
|
|
3227
|
-
|
|
3228
|
-
|
|
3059
|
+
|
|
3060
|
+
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
|
3061
|
+
|
|
3062
|
+
return s0;
|
|
3063
|
+
}
|
|
3064
|
+
|
|
3065
|
+
function peg$parseIdentifier() {
|
|
3066
|
+
let s0, s1;
|
|
3067
|
+
|
|
3068
|
+
const key = peg$currPos * 38 + 34;
|
|
3069
|
+
const cached = peg$resultsCache[key];
|
|
3070
|
+
|
|
3071
|
+
if (cached) {
|
|
3072
|
+
peg$currPos = cached.nextPos;
|
|
3073
|
+
|
|
3074
|
+
return cached.result;
|
|
3075
|
+
}
|
|
3076
|
+
|
|
3077
|
+
peg$silentFails++;
|
|
3078
|
+
s0 = peg$currPos;
|
|
3079
|
+
s1 = peg$parseWord();
|
|
3080
|
+
if (s1 !== peg$FAILED) {
|
|
3081
|
+
s1 = peg$f34(s1);
|
|
3229
3082
|
}
|
|
3230
3083
|
s0 = s1;
|
|
3231
3084
|
peg$silentFails--;
|
|
3232
3085
|
if (s0 === peg$FAILED) {
|
|
3233
3086
|
s1 = peg$FAILED;
|
|
3234
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
3087
|
+
if (peg$silentFails === 0) { peg$fail(peg$e47); }
|
|
3235
3088
|
}
|
|
3236
3089
|
|
|
3237
3090
|
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
|
@@ -3242,7 +3095,7 @@ var ftl = (function (exports) {
|
|
|
3242
3095
|
function peg$parseEqualityOp() {
|
|
3243
3096
|
let s0;
|
|
3244
3097
|
|
|
3245
|
-
const key = peg$currPos *
|
|
3098
|
+
const key = peg$currPos * 38 + 35;
|
|
3246
3099
|
const cached = peg$resultsCache[key];
|
|
3247
3100
|
|
|
3248
3101
|
if (cached) {
|
|
@@ -3251,20 +3104,20 @@ var ftl = (function (exports) {
|
|
|
3251
3104
|
return cached.result;
|
|
3252
3105
|
}
|
|
3253
3106
|
|
|
3254
|
-
if (input.substr(peg$currPos, 2) === peg$
|
|
3255
|
-
s0 = peg$
|
|
3107
|
+
if (input.substr(peg$currPos, 2) === peg$c49) {
|
|
3108
|
+
s0 = peg$c49;
|
|
3256
3109
|
peg$currPos += 2;
|
|
3257
3110
|
} else {
|
|
3258
3111
|
s0 = peg$FAILED;
|
|
3259
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
3112
|
+
if (peg$silentFails === 0) { peg$fail(peg$e48); }
|
|
3260
3113
|
}
|
|
3261
3114
|
if (s0 === peg$FAILED) {
|
|
3262
|
-
if (input.substr(peg$currPos, 2) === peg$
|
|
3263
|
-
s0 = peg$
|
|
3115
|
+
if (input.substr(peg$currPos, 2) === peg$c50) {
|
|
3116
|
+
s0 = peg$c50;
|
|
3264
3117
|
peg$currPos += 2;
|
|
3265
3118
|
} else {
|
|
3266
3119
|
s0 = peg$FAILED;
|
|
3267
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
3120
|
+
if (peg$silentFails === 0) { peg$fail(peg$e49); }
|
|
3268
3121
|
}
|
|
3269
3122
|
}
|
|
3270
3123
|
|
|
@@ -3276,7 +3129,7 @@ var ftl = (function (exports) {
|
|
|
3276
3129
|
function peg$parseRelationalOp() {
|
|
3277
3130
|
let s0;
|
|
3278
3131
|
|
|
3279
|
-
const key = peg$currPos *
|
|
3132
|
+
const key = peg$currPos * 38 + 36;
|
|
3280
3133
|
const cached = peg$resultsCache[key];
|
|
3281
3134
|
|
|
3282
3135
|
if (cached) {
|
|
@@ -3285,36 +3138,36 @@ var ftl = (function (exports) {
|
|
|
3285
3138
|
return cached.result;
|
|
3286
3139
|
}
|
|
3287
3140
|
|
|
3288
|
-
if (input.substr(peg$currPos, 2) === peg$
|
|
3289
|
-
s0 = peg$
|
|
3141
|
+
if (input.substr(peg$currPos, 2) === peg$c51) {
|
|
3142
|
+
s0 = peg$c51;
|
|
3290
3143
|
peg$currPos += 2;
|
|
3291
3144
|
} else {
|
|
3292
3145
|
s0 = peg$FAILED;
|
|
3293
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
3146
|
+
if (peg$silentFails === 0) { peg$fail(peg$e50); }
|
|
3294
3147
|
}
|
|
3295
3148
|
if (s0 === peg$FAILED) {
|
|
3296
3149
|
if (input.charCodeAt(peg$currPos) === 62) {
|
|
3297
|
-
s0 = peg$
|
|
3150
|
+
s0 = peg$c52;
|
|
3298
3151
|
peg$currPos++;
|
|
3299
3152
|
} else {
|
|
3300
3153
|
s0 = peg$FAILED;
|
|
3301
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
3154
|
+
if (peg$silentFails === 0) { peg$fail(peg$e51); }
|
|
3302
3155
|
}
|
|
3303
3156
|
if (s0 === peg$FAILED) {
|
|
3304
|
-
if (input.substr(peg$currPos, 2) === peg$
|
|
3305
|
-
s0 = peg$
|
|
3157
|
+
if (input.substr(peg$currPos, 2) === peg$c53) {
|
|
3158
|
+
s0 = peg$c53;
|
|
3306
3159
|
peg$currPos += 2;
|
|
3307
3160
|
} else {
|
|
3308
3161
|
s0 = peg$FAILED;
|
|
3309
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
3162
|
+
if (peg$silentFails === 0) { peg$fail(peg$e52); }
|
|
3310
3163
|
}
|
|
3311
3164
|
if (s0 === peg$FAILED) {
|
|
3312
3165
|
if (input.charCodeAt(peg$currPos) === 60) {
|
|
3313
|
-
s0 = peg$
|
|
3166
|
+
s0 = peg$c54;
|
|
3314
3167
|
peg$currPos++;
|
|
3315
3168
|
} else {
|
|
3316
3169
|
s0 = peg$FAILED;
|
|
3317
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
3170
|
+
if (peg$silentFails === 0) { peg$fail(peg$e53); }
|
|
3318
3171
|
}
|
|
3319
3172
|
}
|
|
3320
3173
|
}
|
|
@@ -3328,7 +3181,7 @@ var ftl = (function (exports) {
|
|
|
3328
3181
|
function peg$parse_() {
|
|
3329
3182
|
let s0, s1;
|
|
3330
3183
|
|
|
3331
|
-
const key = peg$currPos *
|
|
3184
|
+
const key = peg$currPos * 38 + 37;
|
|
3332
3185
|
const cached = peg$resultsCache[key];
|
|
3333
3186
|
|
|
3334
3187
|
if (cached) {
|
|
@@ -3344,7 +3197,7 @@ var ftl = (function (exports) {
|
|
|
3344
3197
|
peg$currPos++;
|
|
3345
3198
|
} else {
|
|
3346
3199
|
s1 = peg$FAILED;
|
|
3347
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
3200
|
+
if (peg$silentFails === 0) { peg$fail(peg$e54); }
|
|
3348
3201
|
}
|
|
3349
3202
|
while (s1 !== peg$FAILED) {
|
|
3350
3203
|
s0.push(s1);
|
|
@@ -3353,7 +3206,7 @@ var ftl = (function (exports) {
|
|
|
3353
3206
|
peg$currPos++;
|
|
3354
3207
|
} else {
|
|
3355
3208
|
s1 = peg$FAILED;
|
|
3356
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
3209
|
+
if (peg$silentFails === 0) { peg$fail(peg$e54); }
|
|
3357
3210
|
}
|
|
3358
3211
|
}
|
|
3359
3212
|
peg$silentFails--;
|
|
@@ -3397,6 +3250,34 @@ var ftl = (function (exports) {
|
|
|
3397
3250
|
}
|
|
3398
3251
|
}
|
|
3399
3252
|
|
|
3253
|
+
/**
|
|
3254
|
+
* The one lookup against a data stack: the innermost overlay carrying the name
|
|
3255
|
+
* wins, `self` is the innermost overlay itself, and a function overlay counts
|
|
3256
|
+
* like an object one. Every lookup that resolves a name goes through this, so the
|
|
3257
|
+
* imperative facades cannot drift from what a template sees.
|
|
3258
|
+
* @param {any[]} dataStack
|
|
3259
|
+
* @param {string|symbol} prop
|
|
3260
|
+
*/
|
|
3261
|
+
const resolveInStack = (dataStack, prop) => {
|
|
3262
|
+
if (prop === 'self') {
|
|
3263
|
+
return dataStack[dataStack.length - 1];
|
|
3264
|
+
}
|
|
3265
|
+
for (let i = dataStack.length - 1; i >= 0; i--) {
|
|
3266
|
+
const overlay = dataStack[i];
|
|
3267
|
+
if (overlay != null && (typeof overlay === 'object' || typeof overlay === 'function')) {
|
|
3268
|
+
if (prop in overlay) {
|
|
3269
|
+
return overlay[prop];
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
return undefined;
|
|
3274
|
+
};
|
|
3275
|
+
|
|
3276
|
+
/**
|
|
3277
|
+
* Evaluates a parsed expression against a scope. One instance per evaluation:
|
|
3278
|
+
* it holds the modules and the data stack that names resolve against, and keeps
|
|
3279
|
+
* no state between visits.
|
|
3280
|
+
*/
|
|
3400
3281
|
class EvaluatingVisitor {
|
|
3401
3282
|
#modules;
|
|
3402
3283
|
#dataStack;
|
|
@@ -3405,25 +3286,29 @@ var ftl = (function (exports) {
|
|
|
3405
3286
|
this.#dataStack = dataStack;
|
|
3406
3287
|
}
|
|
3407
3288
|
#resolve(prop) {
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3289
|
+
return resolveInStack(this.#dataStack, prop);
|
|
3290
|
+
}
|
|
3291
|
+
/**
|
|
3292
|
+
* The name a missing-method report can carry: the called symbol for a bare
|
|
3293
|
+
* `boom()`, the member for a dotted `a.boom()`, nothing for anything else
|
|
3294
|
+
* (a subscript or a grouped left-hand side has no static name).
|
|
3295
|
+
*/
|
|
3296
|
+
#reportableName(node, index) {
|
|
3297
|
+
const source = index === 0 ? node.lhs : node.rhs[index - 1];
|
|
3298
|
+
if (source.type === nodes.symbol) {
|
|
3299
|
+
return source.value;
|
|
3418
3300
|
}
|
|
3419
|
-
|
|
3301
|
+
if (source.type === nodes.member) {
|
|
3302
|
+
return source.rhs;
|
|
3303
|
+
}
|
|
3304
|
+
return null;
|
|
3420
3305
|
}
|
|
3421
3306
|
|
|
3422
3307
|
#cached_resolve_proxy;
|
|
3423
3308
|
#resolve_proxy() {
|
|
3424
3309
|
if (!this.#cached_resolve_proxy) {
|
|
3425
3310
|
this.#cached_resolve_proxy = new Proxy(this.#dataStack, {
|
|
3426
|
-
get: (target, prop) => this.#resolve(prop)
|
|
3311
|
+
get: (target, prop) => this.#resolve(prop),
|
|
3427
3312
|
});
|
|
3428
3313
|
}
|
|
3429
3314
|
return this.#cached_resolve_proxy;
|
|
@@ -3494,7 +3379,7 @@ var ftl = (function (exports) {
|
|
|
3494
3379
|
return this.#resolve(node.value);
|
|
3495
3380
|
}
|
|
3496
3381
|
[nodes.dict](node) {
|
|
3497
|
-
return Object.fromEntries(node.value.map((entry) => [entry[0]
|
|
3382
|
+
return Object.fromEntries(node.value.map((entry) => [this.visit(entry[0]), this.visit(entry[1])]));
|
|
3498
3383
|
}
|
|
3499
3384
|
[nodes.array](node) {
|
|
3500
3385
|
return node.value.map((v) => this.visit(v));
|
|
@@ -3507,14 +3392,14 @@ var ftl = (function (exports) {
|
|
|
3507
3392
|
return cond ? cond : this.visit(node.ifFalse);
|
|
3508
3393
|
}
|
|
3509
3394
|
[nodes.access](node) {
|
|
3510
|
-
let prev
|
|
3395
|
+
let prev;
|
|
3511
3396
|
let cur = this.visit(node.lhs);
|
|
3512
3397
|
for (let i = 0; i !== node.rhs.length; ++i) {
|
|
3513
3398
|
const rhs = node.rhs[i];
|
|
3514
3399
|
if (rhs.ns && cur == null) {
|
|
3515
3400
|
return undefined;
|
|
3516
3401
|
}
|
|
3517
|
-
let value
|
|
3402
|
+
let value;
|
|
3518
3403
|
switch (rhs.type) {
|
|
3519
3404
|
case nodes.member: {
|
|
3520
3405
|
value = cur[rhs.rhs];
|
|
@@ -3526,7 +3411,8 @@ var ftl = (function (exports) {
|
|
|
3526
3411
|
}
|
|
3527
3412
|
case nodes.method: {
|
|
3528
3413
|
if (!cur) {
|
|
3529
|
-
|
|
3414
|
+
const name = this.#reportableName(node, i);
|
|
3415
|
+
throw new Error(name === null ? 'Method missing' : `Method missing "${name}"`);
|
|
3530
3416
|
}
|
|
3531
3417
|
const args = rhs.args.map((arg) => this.visit(arg));
|
|
3532
3418
|
value = cur.apply(prev, args);
|
|
@@ -3545,28 +3431,34 @@ var ftl = (function (exports) {
|
|
|
3545
3431
|
return !templated
|
|
3546
3432
|
? this.visit(ast)
|
|
3547
3433
|
: ast.map((node) => {
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3434
|
+
switch (node.type) {
|
|
3435
|
+
case nodes.templated.tel:
|
|
3436
|
+
return { type: nodes.dom.t, value: node.value };
|
|
3437
|
+
case nodes.templated.tet:
|
|
3438
|
+
return { type: nodes.dom.t, value: this.visit(node.value) };
|
|
3439
|
+
case nodes.templated.teh:
|
|
3440
|
+
return { type: nodes.dom.h, value: this.visit(node.value) };
|
|
3441
|
+
case nodes.templated.ten:
|
|
3442
|
+
return { type: nodes.dom.n, value: this.visit(node.value) };
|
|
3443
|
+
default:
|
|
3444
|
+
throw new Error(`unknown node type ${node.type.toString()}`);
|
|
3445
|
+
}
|
|
3446
|
+
});
|
|
3561
3447
|
}
|
|
3562
3448
|
}
|
|
3563
3449
|
|
|
3450
|
+
/**
|
|
3451
|
+
* The expression language: parsing to an ast, caching the parses, and
|
|
3452
|
+
* interpreting one against a scope. Member access and calls are unfiltered, so
|
|
3453
|
+
* an expression can do whatever the page's own javascript can. Expressions are
|
|
3454
|
+
* written by the page author; untrusted data is passed in as data and never
|
|
3455
|
+
* spliced into the expression text.
|
|
3456
|
+
*/
|
|
3564
3457
|
class Expressions {
|
|
3565
3458
|
static MODE_EXPRESSION = Symbol('MODE_EXPRESSION');
|
|
3566
3459
|
static MODE_TEMPLATED = Symbol('MODE_TEMPLATED');
|
|
3567
3460
|
|
|
3568
|
-
static #astCache = new
|
|
3569
|
-
static #MAX_CACHE_SIZE = 1000;
|
|
3461
|
+
static #astCache = new BoundedCache(1000);
|
|
3570
3462
|
|
|
3571
3463
|
/**
|
|
3572
3464
|
* Parses an expression.
|
|
@@ -3576,18 +3468,11 @@ var ftl = (function (exports) {
|
|
|
3576
3468
|
*/
|
|
3577
3469
|
static parse(expression, mode) {
|
|
3578
3470
|
const key = mode?.toString() + expression;
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
if (this.#astCache.size >= this.#MAX_CACHE_SIZE) {
|
|
3582
|
-
const oldestKey = this.#astCache.keys().next().value;
|
|
3583
|
-
this.#astCache.delete(oldestKey);
|
|
3584
|
-
}
|
|
3585
|
-
this.#astCache.set(key, peg$parse(expression, {
|
|
3471
|
+
return this.#astCache.getOrCompute(key, () =>
|
|
3472
|
+
peg$parse(expression, {
|
|
3586
3473
|
startRule: mode === Expressions.MODE_TEMPLATED ? 'TemplatedRoot' : 'ExpressionRoot',
|
|
3587
|
-
})
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
return this.#astCache.get(key);
|
|
3474
|
+
}),
|
|
3475
|
+
);
|
|
3591
3476
|
}
|
|
3592
3477
|
/**
|
|
3593
3478
|
* Evaluates an expression.
|
|
@@ -3613,6 +3498,12 @@ var ftl = (function (exports) {
|
|
|
3613
3498
|
}
|
|
3614
3499
|
}
|
|
3615
3500
|
|
|
3501
|
+
/**
|
|
3502
|
+
* A scope: the modules that `#name:fn()` resolves against and the data stack
|
|
3503
|
+
* that a bare identifier resolves against, as a single value. Adding an overlay
|
|
3504
|
+
* returns a new evaluator instead of modifying this one, so a template can add
|
|
3505
|
+
* data for one subtree without affecting the rest of the render.
|
|
3506
|
+
*/
|
|
3616
3507
|
class ExpressionEvaluator {
|
|
3617
3508
|
#modules;
|
|
3618
3509
|
#dataStack;
|
|
@@ -3630,17 +3521,25 @@ var ftl = (function (exports) {
|
|
|
3630
3521
|
data.length === 0 ? this.#dataStack : [...this.#dataStack, ...data],
|
|
3631
3522
|
);
|
|
3632
3523
|
}
|
|
3633
|
-
|
|
3634
|
-
|
|
3524
|
+
/**
|
|
3525
|
+
* Resolves a name against the data stack, with no parse round trip: the
|
|
3526
|
+
* lookup a template's bare identifier makes, for an imperative caller.
|
|
3527
|
+
* @param {string} name
|
|
3528
|
+
*/
|
|
3529
|
+
resolve(name) {
|
|
3530
|
+
return resolveInStack(this.#dataStack, name);
|
|
3635
3531
|
}
|
|
3532
|
+
/** Evaluates an expression against this scope. */
|
|
3636
3533
|
evaluateExpression(expression) {
|
|
3637
|
-
return
|
|
3534
|
+
return Expressions.interpret(this.#modules, this.#dataStack, expression, Expressions.MODE_EXPRESSION);
|
|
3638
3535
|
}
|
|
3639
|
-
|
|
3640
|
-
|
|
3536
|
+
/** Evaluates a templated text, the `{{ }}` form, against this scope. */
|
|
3537
|
+
evaluateTemplated(text) {
|
|
3538
|
+
return Expressions.interpret(this.#modules, this.#dataStack, text, Expressions.MODE_TEMPLATED);
|
|
3641
3539
|
}
|
|
3642
3540
|
}
|
|
3643
3541
|
|
|
3542
|
+
/** Creates and inspects the DocumentFragments a Template renders into. */
|
|
3644
3543
|
class Fragments {
|
|
3645
3544
|
/**
|
|
3646
3545
|
* Creates a DocumentFragment from an string.
|
|
@@ -3653,7 +3552,9 @@ var ftl = (function (exports) {
|
|
|
3653
3552
|
return document.adoptNode(el.content);
|
|
3654
3553
|
}
|
|
3655
3554
|
/**
|
|
3656
|
-
* Creates a string representation (HTML) of a DocumentFragment
|
|
3555
|
+
* Creates a string representation (HTML) of a DocumentFragment, consuming it:
|
|
3556
|
+
* the nodes are moved out, not copied, and the fragment is left empty. Pass
|
|
3557
|
+
* `fragment.cloneNode(true)` to keep the original usable.
|
|
3657
3558
|
* @param {DocumentFragment} fragment
|
|
3658
3559
|
* @returns {string} the html
|
|
3659
3560
|
*/
|
|
@@ -3696,6 +3597,7 @@ var ftl = (function (exports) {
|
|
|
3696
3597
|
}
|
|
3697
3598
|
}
|
|
3698
3599
|
|
|
3600
|
+
/** Attribute reads and writes where a nullish value removes the attribute instead of setting it to the string 'null'. */
|
|
3699
3601
|
class Attributes {
|
|
3700
3602
|
static id = 0;
|
|
3701
3603
|
/**
|
|
@@ -3731,38 +3633,17 @@ var ftl = (function (exports) {
|
|
|
3731
3633
|
.forEach((a) => {
|
|
3732
3634
|
const target = a.substring(prefix.length);
|
|
3733
3635
|
if (target === 'class') {
|
|
3734
|
-
const classes =
|
|
3636
|
+
const classes =
|
|
3637
|
+
from
|
|
3638
|
+
.getAttribute(`${prefix}class`)
|
|
3639
|
+
?.split(/\s+/)
|
|
3640
|
+
.filter((a) => a.length) ?? [];
|
|
3735
3641
|
to.classList.add(...classes);
|
|
3736
3642
|
return;
|
|
3737
3643
|
}
|
|
3738
|
-
to.setAttribute(target, /** @type {string} */(from.getAttribute(a)));
|
|
3644
|
+
to.setAttribute(target, /** @type {string} */ (from.getAttribute(a)));
|
|
3739
3645
|
});
|
|
3740
3646
|
}
|
|
3741
|
-
/**
|
|
3742
|
-
* Changes the presence of an attribute.
|
|
3743
|
-
* @param {Element} el
|
|
3744
|
-
* @param {string} attr
|
|
3745
|
-
* @param {boolean} value
|
|
3746
|
-
*/
|
|
3747
|
-
static toggle(el, attr, value) {
|
|
3748
|
-
if (value) {
|
|
3749
|
-
el.setAttribute(attr, '');
|
|
3750
|
-
} else {
|
|
3751
|
-
el.removeAttribute(attr);
|
|
3752
|
-
}
|
|
3753
|
-
}
|
|
3754
|
-
/**
|
|
3755
|
-
* Changes the presence of an attribute based on its current state.
|
|
3756
|
-
* @param {Element} el
|
|
3757
|
-
* @param {string} attr
|
|
3758
|
-
*/
|
|
3759
|
-
static flip(el, attr) {
|
|
3760
|
-
if (el.hasAttribute(attr)) {
|
|
3761
|
-
el.removeAttribute(attr);
|
|
3762
|
-
} else {
|
|
3763
|
-
el.setAttribute(attr, '');
|
|
3764
|
-
}
|
|
3765
|
-
}
|
|
3766
3647
|
/**
|
|
3767
3648
|
* Sets the value of an attribute. nullish values remove the attribute.
|
|
3768
3649
|
* @param {Element} el
|
|
@@ -3778,6 +3659,12 @@ var ftl = (function (exports) {
|
|
|
3778
3659
|
}
|
|
3779
3660
|
}
|
|
3780
3661
|
|
|
3662
|
+
/**
|
|
3663
|
+
* Reads an element's light-dom slots: the named `<template slot=…>` and
|
|
3664
|
+
* `[slot=…]` children are removed and collected by name, and the remaining
|
|
3665
|
+
* children become the default slot. fml renders into the light dom, so this
|
|
3666
|
+
* replaces the slotting a shadow root would do.
|
|
3667
|
+
*/
|
|
3781
3668
|
class LightSlots {
|
|
3782
3669
|
/**
|
|
3783
3670
|
* Extracts light slots from an element. For non default slots in a template tag, the content is extracted.
|
|
@@ -3785,12 +3672,19 @@ var ftl = (function (exports) {
|
|
|
3785
3672
|
* @returns the slots
|
|
3786
3673
|
*/
|
|
3787
3674
|
static from(el) {
|
|
3675
|
+
//the platform reads slot="" as the default slot: such children stay in
|
|
3676
|
+
//the document order of the default content, their claim stripped like any named one
|
|
3677
|
+
for (const child of el.children) {
|
|
3678
|
+
if (child.matches('[slot=""]')) {
|
|
3679
|
+
child.removeAttribute('slot');
|
|
3680
|
+
}
|
|
3681
|
+
}
|
|
3788
3682
|
/** @type [string, Element|DocumentFragment][] */
|
|
3789
3683
|
const namedSlots = Array.from(el.children)
|
|
3790
3684
|
.filter((el) => el.matches('[slot]'))
|
|
3791
3685
|
.map((el) => {
|
|
3792
3686
|
el.remove();
|
|
3793
|
-
const slot = el.getAttribute('slot')
|
|
3687
|
+
const slot = /** @type {string} */ (el.getAttribute('slot'));
|
|
3794
3688
|
el.removeAttribute('slot');
|
|
3795
3689
|
return [slot, LightSlots.slotFromNode(el)];
|
|
3796
3690
|
});
|
|
@@ -3809,13 +3703,14 @@ var ftl = (function (exports) {
|
|
|
3809
3703
|
if (el instanceof HTMLTemplateElement) {
|
|
3810
3704
|
return document.adoptNode(el.content);
|
|
3811
3705
|
}
|
|
3812
|
-
if (el instanceof HTMLScriptElement && el.type
|
|
3706
|
+
if (el instanceof HTMLScriptElement && el.type === 'text/html') {
|
|
3813
3707
|
return Fragments.fromHtml(el.innerHTML);
|
|
3814
3708
|
}
|
|
3815
3709
|
return el;
|
|
3816
3710
|
}
|
|
3817
3711
|
}
|
|
3818
3712
|
|
|
3713
|
+
/** Waits for the parser and for the document, for elements that upgrade before their own markup is complete. */
|
|
3819
3714
|
class Nodes {
|
|
3820
3715
|
/**
|
|
3821
3716
|
* Checks if an element is already parsed.
|
|
@@ -3831,26 +3726,78 @@ var ftl = (function (exports) {
|
|
|
3831
3726
|
return false;
|
|
3832
3727
|
}
|
|
3833
3728
|
|
|
3729
|
+
/**
|
|
3730
|
+
* Waits for the document's DOMContentLoaded, resolving immediately when
|
|
3731
|
+
* that moment already passed. 'interactive' alone cannot tell a document
|
|
3732
|
+
* still waiting for deferred scripts (the event comes, however late) from
|
|
3733
|
+
* a module imported past it (the event is gone): the two events order
|
|
3734
|
+
* themselves, DOMContentLoaded always precedes load, so racing the two
|
|
3735
|
+
* resolves with DCL whenever it is still coming and with load otherwise,
|
|
3736
|
+
* never early.
|
|
3737
|
+
*/
|
|
3738
|
+
static waitDomContentLoaded(doc) {
|
|
3739
|
+
if (doc.readyState === 'loading') {
|
|
3740
|
+
return new Promise((resolve) => {
|
|
3741
|
+
doc.addEventListener('DOMContentLoaded', () => resolve(undefined), { once: true });
|
|
3742
|
+
});
|
|
3743
|
+
}
|
|
3744
|
+
if (doc.readyState === 'complete') {
|
|
3745
|
+
return Promise.resolve();
|
|
3746
|
+
}
|
|
3747
|
+
const win = doc.defaultView;
|
|
3748
|
+
if (win === null) {
|
|
3749
|
+
//a viewless document can receive no event at all
|
|
3750
|
+
return Promise.resolve();
|
|
3751
|
+
}
|
|
3752
|
+
return new Promise((resolve) => {
|
|
3753
|
+
const done = () => {
|
|
3754
|
+
win.removeEventListener('DOMContentLoaded', done);
|
|
3755
|
+
win.removeEventListener('load', done);
|
|
3756
|
+
resolve(undefined);
|
|
3757
|
+
};
|
|
3758
|
+
win.addEventListener('DOMContentLoaded', done);
|
|
3759
|
+
win.addEventListener('load', done);
|
|
3760
|
+
});
|
|
3761
|
+
}
|
|
3762
|
+
|
|
3763
|
+
/**
|
|
3764
|
+
* Waits for the element's closing tag to be parsed: one MutationObserver
|
|
3765
|
+
* resolves once the element, or any of its ancestors, gains a next sibling,
|
|
3766
|
+
* which is the parser having moved past this subtree. The document's
|
|
3767
|
+
* DOMContentLoaded is the deadline. Resolves immediately for an element
|
|
3768
|
+
* that is already parsed.
|
|
3769
|
+
*
|
|
3770
|
+
* Every ancestor is watched, not only the parent, because that is what the
|
|
3771
|
+
* predicate reads: an element last among its siblings becomes parsed when
|
|
3772
|
+
* an ancestor gains one, and a childList observer on the parent never sees
|
|
3773
|
+
* that. Formatted markup usually hides the difference, the whitespace
|
|
3774
|
+
* before a closing tag being a text node the parent does gain, so the gap
|
|
3775
|
+
* shows on whitespace-free markup, where the wait fell back to the
|
|
3776
|
+
* deadline. One observer takes many targets, so the cost is one observe()
|
|
3777
|
+
* per level, and it is disconnected at the first checkpoint past the
|
|
3778
|
+
* element either way.
|
|
3779
|
+
* @param {any} el
|
|
3780
|
+
* @returns {Promise<any>}
|
|
3781
|
+
*/
|
|
3834
3782
|
static waitParsed(el) {
|
|
3835
|
-
if (
|
|
3783
|
+
if (Nodes.isParsed(el)) {
|
|
3836
3784
|
return Promise.resolve(el);
|
|
3837
3785
|
}
|
|
3838
3786
|
return new Promise((resolve) => {
|
|
3839
|
-
const ac = new AbortController();
|
|
3840
|
-
const clearAndQueue = () => {
|
|
3841
|
-
ac.abort();
|
|
3842
|
-
observer.disconnect();
|
|
3843
|
-
resolve(el);
|
|
3844
|
-
};
|
|
3845
|
-
el.ownerDocument.addEventListener('DOMContentLoaded', clearAndQueue, { signal: ac.signal });
|
|
3846
3787
|
const observer = new MutationObserver(() => {
|
|
3847
3788
|
if (!Nodes.isParsed(el)) {
|
|
3848
3789
|
return;
|
|
3849
3790
|
}
|
|
3850
|
-
|
|
3791
|
+
observer.disconnect();
|
|
3792
|
+
resolve(el);
|
|
3793
|
+
});
|
|
3794
|
+
for (let c = el.parentNode; c; c = c.parentNode) {
|
|
3795
|
+
observer.observe(c, { childList: true });
|
|
3796
|
+
}
|
|
3797
|
+
Nodes.waitDomContentLoaded(el.ownerDocument).then(() => {
|
|
3798
|
+
observer.disconnect();
|
|
3799
|
+
resolve(el);
|
|
3851
3800
|
});
|
|
3852
|
-
const parent = /** @type {Node} */ (el.parentNode);
|
|
3853
|
-
observer.observe(parent, { childList: true });
|
|
3854
3801
|
});
|
|
3855
3802
|
}
|
|
3856
3803
|
|
|
@@ -3885,6 +3832,11 @@ var ftl = (function (exports) {
|
|
|
3885
3832
|
}
|
|
3886
3833
|
}
|
|
3887
3834
|
|
|
3835
|
+
/**
|
|
3836
|
+
* Collects the changes a render makes to the tree it is traversing, deferring
|
|
3837
|
+
* those that would break the traversal: a node marked for removal stays in
|
|
3838
|
+
* place until the render finishes, so the iteration containing it completes.
|
|
3839
|
+
*/
|
|
3888
3840
|
class NodeOperations {
|
|
3889
3841
|
#forRemoval = new Set();
|
|
3890
3842
|
removed(node) {
|
|
@@ -3922,7 +3874,11 @@ var ftl = (function (exports) {
|
|
|
3922
3874
|
}
|
|
3923
3875
|
}
|
|
3924
3876
|
|
|
3877
|
+
/** The `data-tpl-*` commands, each taking the node it is written on and the scope it evaluates in. */
|
|
3925
3878
|
class CommandsHandler {
|
|
3879
|
+
//the order is the semantics: each command sees the node as the ones before
|
|
3880
|
+
//it left it, so tplIf gates before tplWith/tplEach push their overlay and
|
|
3881
|
+
//tplWhen gates after (inside the sub-render, where the overlay is in scope)
|
|
3926
3882
|
static ORDERED_COMMANDS = [
|
|
3927
3883
|
'tplIf',
|
|
3928
3884
|
'tplWith',
|
|
@@ -3930,48 +3886,98 @@ var ftl = (function (exports) {
|
|
|
3930
3886
|
'tplWhen',
|
|
3931
3887
|
'tplClassAppend',
|
|
3932
3888
|
'tplAttrAppend',
|
|
3933
|
-
'tplText',
|
|
3934
|
-
'tplHtml',
|
|
3935
3889
|
'tplRemove',
|
|
3936
3890
|
'tplVerbatim',
|
|
3937
3891
|
];
|
|
3938
|
-
|
|
3939
|
-
|
|
3892
|
+
//same body as tplWhen: the two differ only in ORDERED_COMMANDS position,
|
|
3893
|
+
//if evaluates in the outer scope, before with/each
|
|
3894
|
+
static tplIf(node, expression, ops, evaluator) {
|
|
3895
|
+
const accept = evaluator.evaluateExpression(expression);
|
|
3940
3896
|
if (!accept) {
|
|
3941
3897
|
ops.remove(node);
|
|
3942
3898
|
}
|
|
3943
3899
|
}
|
|
3944
|
-
static tplWith(node, expression, ops,
|
|
3945
|
-
const evaluated =
|
|
3900
|
+
static tplWith(node, expression, ops, evaluator) {
|
|
3901
|
+
const evaluated = evaluator.evaluateExpression(expression);
|
|
3946
3902
|
const varName = ops.popData(node, 'tplVar');
|
|
3947
|
-
const newNode = new Template(node,
|
|
3903
|
+
const newNode = new Template(node, evaluator)
|
|
3948
3904
|
.withOverlay(varName ? { [varName]: evaluated } : evaluated)
|
|
3949
3905
|
.render();
|
|
3950
3906
|
ops.replace(node, newNode);
|
|
3951
3907
|
}
|
|
3952
|
-
static tplEach(node, expression, ops,
|
|
3908
|
+
static tplEach(node, expression, ops, evaluator) {
|
|
3953
3909
|
const varName = ops.popData(node, 'tplVar');
|
|
3954
|
-
const
|
|
3955
|
-
const
|
|
3956
|
-
|
|
3910
|
+
const statName = ops.popData(node, 'tplStat');
|
|
3911
|
+
const template = new Template(node, evaluator);
|
|
3912
|
+
const evaluated = evaluator.evaluateExpression(expression);
|
|
3913
|
+
//keyed collections iterate as {key, value} entries: a Map in its own
|
|
3914
|
+
//order, a non-iterable plain dict in Object.entries order. Plain alone:
|
|
3915
|
+
//a class instance or a response wrapper standing where an array was
|
|
3916
|
+
//expected still fails loudly below, and any other iterable (a Set, a
|
|
3917
|
+
//generator, an entries() iterator) iterates as itself
|
|
3918
|
+
const proto = evaluated === null || typeof evaluated !== 'object' ? undefined : Object.getPrototypeOf(evaluated);
|
|
3919
|
+
const keyed =
|
|
3920
|
+
evaluated instanceof Map
|
|
3921
|
+
? [...evaluated]
|
|
3922
|
+
: !evaluated?.[Symbol.iterator] && (proto === Object.prototype || proto === null)
|
|
3923
|
+
? Object.entries(evaluated)
|
|
3924
|
+
: null;
|
|
3925
|
+
const entries = keyed === null ? evaluated : keyed.map(([key, value]) => ({ key, value }));
|
|
3926
|
+
if (!entries?.[Symbol.iterator]) {
|
|
3957
3927
|
throw new Error(`Expected an iterable got '${evaluated}'`);
|
|
3958
3928
|
}
|
|
3959
|
-
|
|
3960
|
-
|
|
3929
|
+
if (!statName) {
|
|
3930
|
+
for (const v of entries) {
|
|
3931
|
+
ops.prepend(node, template.withOverlay(varName ? { [varName]: v } : v).render());
|
|
3932
|
+
}
|
|
3933
|
+
ops.remove(node);
|
|
3934
|
+
return;
|
|
3935
|
+
}
|
|
3936
|
+
//the stat rides grouped under its declared name, overlaid below the
|
|
3937
|
+
//item, so data can never collide with its fields and an item property
|
|
3938
|
+
//sharing the stat's very name wins, as data always does. The size is
|
|
3939
|
+
//read where the collection already knows it (arrays and keyed entries
|
|
3940
|
+
//by length, sets and the like by size): an arbitrary iterator is never
|
|
3941
|
+
//consumed to learn it, so its size and last read null, the unknown
|
|
3942
|
+
const size = Array.isArray(entries)
|
|
3943
|
+
? entries.length
|
|
3944
|
+
: typeof entries.length === 'number'
|
|
3945
|
+
? entries.length
|
|
3946
|
+
: typeof entries.size === 'number'
|
|
3947
|
+
? entries.size
|
|
3948
|
+
: null;
|
|
3949
|
+
let index = 0;
|
|
3950
|
+
for (const v of entries) {
|
|
3951
|
+
const stat = {
|
|
3952
|
+
index,
|
|
3953
|
+
count: index + 1,
|
|
3954
|
+
size,
|
|
3955
|
+
first: index === 0,
|
|
3956
|
+
last: size === null ? null : index === size - 1,
|
|
3957
|
+
even: index % 2 === 0,
|
|
3958
|
+
odd: index % 2 === 1,
|
|
3959
|
+
};
|
|
3960
|
+
ops.prepend(
|
|
3961
|
+
node,
|
|
3962
|
+
template.withOverlay({ [statName]: stat }, varName ? { [varName]: v } : v).render(),
|
|
3963
|
+
);
|
|
3964
|
+
++index;
|
|
3961
3965
|
}
|
|
3962
3966
|
ops.remove(node);
|
|
3963
3967
|
}
|
|
3964
|
-
|
|
3965
|
-
|
|
3968
|
+
//same body as tplIf: the two differ only in ORDERED_COMMANDS position,
|
|
3969
|
+
//when evaluates after with/each, in the scope their overlay opened
|
|
3970
|
+
static tplWhen(node, expression, ops, evaluator) {
|
|
3971
|
+
const accept = evaluator.evaluateExpression(expression);
|
|
3966
3972
|
if (!accept) {
|
|
3967
3973
|
ops.remove(node);
|
|
3968
3974
|
}
|
|
3969
3975
|
}
|
|
3970
|
-
static tplVerbatim(node, expression, ops,
|
|
3976
|
+
static tplVerbatim(node, expression, ops, evaluator) {
|
|
3971
3977
|
const newNode = node.cloneNode(true);
|
|
3972
3978
|
ops.replace(node, newNode);
|
|
3973
3979
|
}
|
|
3974
|
-
static tplRemove(node, value, ops,
|
|
3980
|
+
static tplRemove(node, value, ops, evaluator) {
|
|
3975
3981
|
switch (value.toLowerCase()) {
|
|
3976
3982
|
case 'tag': {
|
|
3977
3983
|
const fragment = new DocumentFragment();
|
|
@@ -3996,34 +4002,20 @@ var ftl = (function (exports) {
|
|
|
3996
4002
|
break;
|
|
3997
4003
|
}
|
|
3998
4004
|
}
|
|
3999
|
-
static
|
|
4000
|
-
const
|
|
4001
|
-
const newNode = node.cloneNode();
|
|
4002
|
-
newNode.replaceChildren(text == null ? '' : text);
|
|
4003
|
-
ops.replace(node, newNode);
|
|
4004
|
-
}
|
|
4005
|
-
static tplHtml(node, expression, ops, modules, dataStack) {
|
|
4006
|
-
const html = Expressions.interpret(modules, dataStack, expression);
|
|
4007
|
-
const newNode = node.cloneNode();
|
|
4008
|
-
newNode.innerHTML = html == null ? '' : html;
|
|
4009
|
-
ops.replace(node, newNode);
|
|
4010
|
-
}
|
|
4011
|
-
static tplClassAppend(node, expression, ops, modules, dataStack) {
|
|
4012
|
-
const classes = Expressions.interpret(modules, dataStack, expression);
|
|
4005
|
+
static tplClassAppend(node, expression, ops, evaluator) {
|
|
4006
|
+
const classes = evaluator.evaluateExpression(expression);
|
|
4013
4007
|
if (!classes) {
|
|
4014
4008
|
return;
|
|
4015
4009
|
}
|
|
4016
4010
|
const classesAsArray = Array.isArray(classes) ? classes : [classes];
|
|
4017
|
-
const cleanClasses = classesAsArray
|
|
4018
|
-
.flatMap(c => typeof c === 'string' ? c.split(' ') : c)
|
|
4019
|
-
.filter(Boolean);
|
|
4011
|
+
const cleanClasses = classesAsArray.flatMap((c) => (typeof c === 'string' ? c.split(' ') : c)).filter(Boolean);
|
|
4020
4012
|
if (cleanClasses.length === 0) {
|
|
4021
4013
|
return;
|
|
4022
4014
|
}
|
|
4023
4015
|
node.classList.add(...cleanClasses);
|
|
4024
4016
|
}
|
|
4025
|
-
static tplAttrAppend(node, expression, ops,
|
|
4026
|
-
const attributesAndValues =
|
|
4017
|
+
static tplAttrAppend(node, expression, ops, evaluator) {
|
|
4018
|
+
const attributesAndValues = evaluator.evaluateExpression(expression);
|
|
4027
4019
|
if (!attributesAndValues || attributesAndValues.length === 0) {
|
|
4028
4020
|
return;
|
|
4029
4021
|
}
|
|
@@ -4032,8 +4024,8 @@ var ftl = (function (exports) {
|
|
|
4032
4024
|
node.setAttribute(k, v);
|
|
4033
4025
|
});
|
|
4034
4026
|
}
|
|
4035
|
-
static textNode(node, expression, ops,
|
|
4036
|
-
for (const v of
|
|
4027
|
+
static textNode(node, expression, ops, evaluator) {
|
|
4028
|
+
for (const v of evaluator.evaluateTemplated(expression)) {
|
|
4037
4029
|
if (v.value == null) {
|
|
4038
4030
|
continue;
|
|
4039
4031
|
}
|
|
@@ -4042,9 +4034,12 @@ var ftl = (function (exports) {
|
|
|
4042
4034
|
ops.prepend(node, document.createTextNode(v.value));
|
|
4043
4035
|
break;
|
|
4044
4036
|
case nodes.dom.h:
|
|
4045
|
-
ops.prepend(node, Fragments.fromHtml(v.value));
|
|
4037
|
+
ops.prepend(node, Fragments.fromHtml(typeof v.value === 'string' ? v.value : String(v.value)));
|
|
4046
4038
|
break;
|
|
4047
4039
|
case nodes.dom.n:
|
|
4040
|
+
if (!(v.value instanceof Node)) {
|
|
4041
|
+
throw new TypeError(`Expected a Node, got '${typeof v.value}'`);
|
|
4042
|
+
}
|
|
4048
4043
|
ops.prepend(node, v.value);
|
|
4049
4044
|
break;
|
|
4050
4045
|
}
|
|
@@ -4053,10 +4048,8 @@ var ftl = (function (exports) {
|
|
|
4053
4048
|
}
|
|
4054
4049
|
}
|
|
4055
4050
|
|
|
4056
|
-
// Module-isolated string cache for dataset-to-attribute conversions
|
|
4057
|
-
|
|
4058
|
-
const attributeCache = new Map();
|
|
4059
|
-
const ATTRIBUTE_CACHE_MAX_SIZE = 1000;
|
|
4051
|
+
// Module-isolated string cache for dataset-to-attribute conversions
|
|
4052
|
+
const attributeCache = new BoundedCache(1000);
|
|
4060
4053
|
|
|
4061
4054
|
/**
|
|
4062
4055
|
* Converts a tpl camelCase dataset key into a kebab-case attribute name.
|
|
@@ -4065,21 +4058,21 @@ var ftl = (function (exports) {
|
|
|
4065
4058
|
* @returns {string}
|
|
4066
4059
|
*/
|
|
4067
4060
|
function toAttr(dataSetKey) {
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
if (attributeCache.size >= ATTRIBUTE_CACHE_MAX_SIZE) {
|
|
4071
|
-
attributeCache.delete(attributeCache.keys().next().value);
|
|
4072
|
-
}
|
|
4073
|
-
cached = dataSetKey
|
|
4061
|
+
return attributeCache.getOrCompute(dataSetKey, (k) =>
|
|
4062
|
+
k
|
|
4074
4063
|
.substring(3)
|
|
4075
4064
|
.split(/(?=[A-Z])/)
|
|
4076
4065
|
.join('-')
|
|
4077
|
-
.toLowerCase()
|
|
4078
|
-
|
|
4079
|
-
}
|
|
4080
|
-
return cached;
|
|
4066
|
+
.toLowerCase(),
|
|
4067
|
+
);
|
|
4081
4068
|
}
|
|
4082
4069
|
|
|
4070
|
+
/**
|
|
4071
|
+
* A compiled fragment together with the scope it renders in. Both are
|
|
4072
|
+
* immutable: adding data or replacing the scope returns a new Template, so one
|
|
4073
|
+
* piece of compiled markup can be rendered against any number of scopes and a
|
|
4074
|
+
* registry can reuse a single template for every element that requests it.
|
|
4075
|
+
*/
|
|
4083
4076
|
class Template {
|
|
4084
4077
|
/**
|
|
4085
4078
|
* Creates a template from a string.
|
|
@@ -4089,7 +4082,7 @@ var ftl = (function (exports) {
|
|
|
4089
4082
|
* @returns the template
|
|
4090
4083
|
*/
|
|
4091
4084
|
static fromHtml(html, modules, ...data) {
|
|
4092
|
-
return new Template(Fragments.fromHtml(html), modules, data);
|
|
4085
|
+
return new Template(Fragments.fromHtml(html), new ExpressionEvaluator(modules, data));
|
|
4093
4086
|
}
|
|
4094
4087
|
|
|
4095
4088
|
/**
|
|
@@ -4105,7 +4098,7 @@ var ftl = (function (exports) {
|
|
|
4105
4098
|
throw new Error('template selector does not match any template tag');
|
|
4106
4099
|
}
|
|
4107
4100
|
const fragment = document.adoptNode(templateEl.content);
|
|
4108
|
-
return new Template(fragment, modules, data);
|
|
4101
|
+
return new Template(fragment, new ExpressionEvaluator(modules, data));
|
|
4109
4102
|
}
|
|
4110
4103
|
|
|
4111
4104
|
/**
|
|
@@ -4117,7 +4110,7 @@ var ftl = (function (exports) {
|
|
|
4117
4110
|
*/
|
|
4118
4111
|
static fromTemplate(templateEl, modules, ...data) {
|
|
4119
4112
|
const fragment = document.adoptNode(templateEl.content);
|
|
4120
|
-
return new Template(fragment, modules, data);
|
|
4113
|
+
return new Template(fragment, new ExpressionEvaluator(modules, data));
|
|
4121
4114
|
}
|
|
4122
4115
|
|
|
4123
4116
|
/**
|
|
@@ -4128,42 +4121,33 @@ var ftl = (function (exports) {
|
|
|
4128
4121
|
* @returns the template
|
|
4129
4122
|
*/
|
|
4130
4123
|
static fromFragment(fragment, modules, ...data) {
|
|
4131
|
-
return new Template(fragment, modules, data);
|
|
4124
|
+
return new Template(fragment, new ExpressionEvaluator(modules, data));
|
|
4132
4125
|
}
|
|
4133
4126
|
#fragment;
|
|
4134
|
-
#
|
|
4135
|
-
#dataStack;
|
|
4127
|
+
#evaluator;
|
|
4136
4128
|
/**
|
|
4137
|
-
* Creates a template.
|
|
4129
|
+
* Creates a template: a fragment plus the scope it renders in.
|
|
4138
4130
|
* @param {DocumentFragment} fragment
|
|
4139
|
-
* @param {
|
|
4140
|
-
* @param {any[]} dataStack
|
|
4131
|
+
* @param {ExpressionEvaluator} evaluator the modules and data stack the expressions resolve against
|
|
4141
4132
|
*/
|
|
4142
|
-
constructor(fragment,
|
|
4133
|
+
constructor(fragment, evaluator) {
|
|
4143
4134
|
this.#fragment = fragment;
|
|
4144
|
-
this.#
|
|
4145
|
-
this.#dataStack = dataStack;
|
|
4146
|
-
}
|
|
4147
|
-
/**
|
|
4148
|
-
* Creates a new Template replacing the modules and dataStack from a context.
|
|
4149
|
-
* @param {{modules: { [x: string]: any; } | null | undefined, data: any[]}} context
|
|
4150
|
-
*/
|
|
4151
|
-
withContext({ modules, data }) {
|
|
4152
|
-
return new Template(this.#fragment, modules, data);
|
|
4135
|
+
this.#evaluator = evaluator;
|
|
4153
4136
|
}
|
|
4154
4137
|
/**
|
|
4155
|
-
* Creates a new Template
|
|
4156
|
-
*
|
|
4138
|
+
* Creates a new Template rendering in another scope: the one way to rebind
|
|
4139
|
+
* a compiled template to a registry's modules and data.
|
|
4140
|
+
* @param {ExpressionEvaluator} evaluator
|
|
4157
4141
|
*/
|
|
4158
|
-
|
|
4159
|
-
return this
|
|
4142
|
+
withEvaluator(evaluator) {
|
|
4143
|
+
return new Template(this.#fragment, evaluator);
|
|
4160
4144
|
}
|
|
4161
4145
|
/**
|
|
4162
4146
|
* Creates a new Template replacing the fragment.
|
|
4163
4147
|
* @param {DocumentFragment} fragment
|
|
4164
4148
|
*/
|
|
4165
4149
|
withFragment(fragment) {
|
|
4166
|
-
return new Template(fragment, this.#
|
|
4150
|
+
return new Template(fragment, this.#evaluator);
|
|
4167
4151
|
}
|
|
4168
4152
|
/**
|
|
4169
4153
|
* Creates a new Template with a new module added.
|
|
@@ -4171,49 +4155,38 @@ var ftl = (function (exports) {
|
|
|
4171
4155
|
* @param {{[k: string]: any}} value
|
|
4172
4156
|
*/
|
|
4173
4157
|
withModule(name, value) {
|
|
4174
|
-
|
|
4175
|
-
return new Template(this.#fragment, { ...this.#modules, ...module }, this.#dataStack);
|
|
4176
|
-
}
|
|
4177
|
-
/**
|
|
4178
|
-
* Creates a new Template replacing the modules.
|
|
4179
|
-
* @param {{ [x: string]: any; }?} modules
|
|
4180
|
-
*/
|
|
4181
|
-
withModules(modules) {
|
|
4182
|
-
return new Template(this.#fragment, modules, this.#dataStack);
|
|
4183
|
-
}
|
|
4184
|
-
/**
|
|
4185
|
-
* Creates a new Template replacing the data stack.
|
|
4186
|
-
* @param {any[]} dataStack the dataStack
|
|
4187
|
-
*/
|
|
4188
|
-
withData(dataStack) {
|
|
4189
|
-
return new Template(this.#fragment, this.#modules, dataStack);
|
|
4158
|
+
return new Template(this.#fragment, this.#evaluator.withModule(name, value));
|
|
4190
4159
|
}
|
|
4191
4160
|
/**
|
|
4192
4161
|
* Creates a new Template with new a data overlay added to the stack.
|
|
4193
4162
|
* @param {...*} data
|
|
4194
4163
|
*/
|
|
4195
4164
|
withOverlay(...data) {
|
|
4196
|
-
return new Template(
|
|
4197
|
-
this.#fragment,
|
|
4198
|
-
this.#modules,
|
|
4199
|
-
data.length === 0 ? this.#dataStack : [...this.#dataStack, ...data],
|
|
4200
|
-
);
|
|
4165
|
+
return new Template(this.#fragment, this.#evaluator.withOverlay(...data));
|
|
4201
4166
|
}
|
|
4202
4167
|
/**
|
|
4203
|
-
* Evaluates an expression
|
|
4168
|
+
* Evaluates an expression in this template's scope, widened by the overlays.
|
|
4204
4169
|
* @param {string} expression
|
|
4205
|
-
* @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode]
|
|
4206
4170
|
* @param {...*} data
|
|
4207
4171
|
* @returns the evaluated expression result
|
|
4208
4172
|
*/
|
|
4209
|
-
|
|
4210
|
-
return
|
|
4173
|
+
evaluateExpression(expression, ...data) {
|
|
4174
|
+
return this.#evaluator.withOverlay(...data).evaluateExpression(expression);
|
|
4175
|
+
}
|
|
4176
|
+
/**
|
|
4177
|
+
* Evaluates a templated text, the `{{ }}` form, in this template's scope.
|
|
4178
|
+
* @param {string} text
|
|
4179
|
+
* @param {...*} data
|
|
4180
|
+
* @returns the parts the text evaluates to
|
|
4181
|
+
*/
|
|
4182
|
+
evaluateTemplated(text, ...data) {
|
|
4183
|
+
return this.#evaluator.withOverlay(...data).evaluateTemplated(text);
|
|
4211
4184
|
}
|
|
4212
4185
|
/**
|
|
4213
4186
|
* Returns an expression evaluator with bound modules and dataStack.
|
|
4214
4187
|
*/
|
|
4215
4188
|
evaluator() {
|
|
4216
|
-
return
|
|
4189
|
+
return this.#evaluator;
|
|
4217
4190
|
}
|
|
4218
4191
|
/**
|
|
4219
4192
|
* Renders the template.
|
|
@@ -4227,10 +4200,10 @@ var ftl = (function (exports) {
|
|
|
4227
4200
|
imported.nodeType === Node.DOCUMENT_FRAGMENT_NODE
|
|
4228
4201
|
? imported
|
|
4229
4202
|
: (() => {
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4203
|
+
const d = new DocumentFragment();
|
|
4204
|
+
d.appendChild(imported);
|
|
4205
|
+
return d;
|
|
4206
|
+
})();
|
|
4234
4207
|
const iterator = document.createNodeIterator(
|
|
4235
4208
|
fragment,
|
|
4236
4209
|
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
|
|
@@ -4241,9 +4214,9 @@ var ftl = (function (exports) {
|
|
|
4241
4214
|
ops.cleanup();
|
|
4242
4215
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
4243
4216
|
try {
|
|
4244
|
-
CommandsHandler.textNode(node, node.nodeValue, ops, this.#
|
|
4217
|
+
CommandsHandler.textNode(node, node.nodeValue, ops, this.#evaluator);
|
|
4245
4218
|
} catch (ex) {
|
|
4246
|
-
throw
|
|
4219
|
+
throw RenderError.wrap('Error evaluating text node', node, ex);
|
|
4247
4220
|
}
|
|
4248
4221
|
continue;
|
|
4249
4222
|
}
|
|
@@ -4254,9 +4227,11 @@ var ftl = (function (exports) {
|
|
|
4254
4227
|
}
|
|
4255
4228
|
const value = ops.popData(el, command);
|
|
4256
4229
|
try {
|
|
4257
|
-
CommandsHandler[command](el, value, ops, this.#
|
|
4230
|
+
CommandsHandler[command](el, value, ops, this.#evaluator);
|
|
4258
4231
|
} catch (ex) {
|
|
4259
|
-
|
|
4232
|
+
//the directive is popped before it runs, so the open tag no
|
|
4233
|
+
//longer carries it: the frame names it and its expression
|
|
4234
|
+
throw RenderError.wrap(`Error evaluating data-tpl-${toAttr(command)}="${value}"`, el, ex);
|
|
4260
4235
|
}
|
|
4261
4236
|
if (ops.removed(el)) {
|
|
4262
4237
|
break;
|
|
@@ -4267,24 +4242,31 @@ var ftl = (function (exports) {
|
|
|
4267
4242
|
continue;
|
|
4268
4243
|
}
|
|
4269
4244
|
const attributeName = toAttr(dataSetKey);
|
|
4245
|
+
const expression = ops.popData(el, dataSetKey);
|
|
4270
4246
|
try {
|
|
4271
|
-
const
|
|
4272
|
-
const evaluated = Expressions.interpret(this.#modules, this.#dataStack, expression);
|
|
4247
|
+
const evaluated = this.#evaluator.evaluateExpression(expression);
|
|
4273
4248
|
if (typeof evaluated === 'boolean') {
|
|
4274
|
-
|
|
4249
|
+
el.toggleAttribute(attributeName, evaluated);
|
|
4275
4250
|
continue;
|
|
4276
4251
|
}
|
|
4277
4252
|
if (evaluated !== null && evaluated !== undefined) {
|
|
4278
4253
|
el.setAttribute(attributeName, evaluated);
|
|
4279
4254
|
}
|
|
4280
4255
|
} catch (ex) {
|
|
4281
|
-
throw
|
|
4256
|
+
throw RenderError.wrap(`Error evaluating data-tpl-${toAttr(dataSetKey)}="${expression}"`, el, ex);
|
|
4282
4257
|
}
|
|
4283
4258
|
}
|
|
4284
4259
|
}
|
|
4285
4260
|
ops.cleanup();
|
|
4286
4261
|
return fragment;
|
|
4287
4262
|
} catch (ex) {
|
|
4263
|
+
//a command, a text node or a nested render already named the node it
|
|
4264
|
+
//failed on: wrapping again would add a frame for the fragment that
|
|
4265
|
+
//contains it, one per level, serializing the whole template into the
|
|
4266
|
+
//message that survives. Only a failure outside those is framed here
|
|
4267
|
+
if (ex instanceof RenderError) {
|
|
4268
|
+
throw ex;
|
|
4269
|
+
}
|
|
4288
4270
|
throw new RenderError('Error rendering template', this.#fragment, ex);
|
|
4289
4271
|
}
|
|
4290
4272
|
}
|
|
@@ -4303,7 +4285,7 @@ var ftl = (function (exports) {
|
|
|
4303
4285
|
el.appendChild(this.render());
|
|
4304
4286
|
}
|
|
4305
4287
|
/**
|
|
4306
|
-
* Renders this template
|
|
4288
|
+
* Renders this template on the first Element matching the selector (replacing children), if exists.
|
|
4307
4289
|
* @param {string} selector
|
|
4308
4290
|
*/
|
|
4309
4291
|
renderToSelector(selector) {
|
|
@@ -4313,7 +4295,7 @@ var ftl = (function (exports) {
|
|
|
4313
4295
|
}
|
|
4314
4296
|
}
|
|
4315
4297
|
/**
|
|
4316
|
-
* Renders this template appending the resulting fragment to the Element
|
|
4298
|
+
* Renders this template appending the resulting fragment to the Element matching the selector, if exists.
|
|
4317
4299
|
* @param {string} selector
|
|
4318
4300
|
*/
|
|
4319
4301
|
appendToSelector(selector) {
|
|
@@ -4337,11 +4319,77 @@ var ftl = (function (exports) {
|
|
|
4337
4319
|
}
|
|
4338
4320
|
}
|
|
4339
4321
|
|
|
4322
|
+
/**
|
|
4323
|
+
* A render failure, one frame per nesting level. Each frame names the node it
|
|
4324
|
+
* failed on and what was being evaluated there, so the chain reads as the path
|
|
4325
|
+
* from the template's root down to the offending expression. The frame carries
|
|
4326
|
+
* the node's identification only, an open tag rather than its whole subtree:
|
|
4327
|
+
* the markup is serialized on demand through `html`, and the live node stays on
|
|
4328
|
+
* `node`, so a failure costs no clone and a nested failure does not embed the
|
|
4329
|
+
* page in its own message.
|
|
4330
|
+
*/
|
|
4340
4331
|
class RenderError extends Error {
|
|
4332
|
+
/**
|
|
4333
|
+
* How many frames a chain keeps. The innermost are the specific ones, so a
|
|
4334
|
+
* deeper nesting drops the outer context rather than the failure site: three
|
|
4335
|
+
* frames name the offending node and the two levels that hold it, which is
|
|
4336
|
+
* the path a reader follows without the page arriving with it.
|
|
4337
|
+
*/
|
|
4338
|
+
static FRAMES = 3;
|
|
4339
|
+
#node;
|
|
4340
|
+
#depth;
|
|
4341
|
+
/** true when the budget dropped the outer frames of this chain */
|
|
4342
|
+
truncated = false;
|
|
4343
|
+
/**
|
|
4344
|
+
* Frames a failure, unless the chain already spent its budget: then the
|
|
4345
|
+
* cause travels on, marked so a report can say the outer context was
|
|
4346
|
+
* dropped.
|
|
4347
|
+
*/
|
|
4348
|
+
static wrap(message, nodeOrFragment, cause) {
|
|
4349
|
+
if (cause instanceof RenderError && cause.depth >= RenderError.FRAMES) {
|
|
4350
|
+
cause.truncated = true;
|
|
4351
|
+
return cause;
|
|
4352
|
+
}
|
|
4353
|
+
return new RenderError(message, nodeOrFragment, cause);
|
|
4354
|
+
}
|
|
4341
4355
|
constructor(message, nodeOrFragment, cause) {
|
|
4342
|
-
super(`${message} in \`${RenderError.
|
|
4356
|
+
super(`${message} in \`${RenderError.describe(nodeOrFragment)}\``, { cause });
|
|
4343
4357
|
this.name = 'RenderError';
|
|
4344
|
-
this
|
|
4358
|
+
this.#node = nodeOrFragment;
|
|
4359
|
+
this.#depth = (cause instanceof RenderError ? cause.depth : 0) + 1;
|
|
4360
|
+
}
|
|
4361
|
+
/** How many frames this chain carries, this one included. */
|
|
4362
|
+
get depth() {
|
|
4363
|
+
return this.#depth;
|
|
4364
|
+
}
|
|
4365
|
+
/** The node the render failed on, live: it keeps its place in the fragment being built. */
|
|
4366
|
+
get node() {
|
|
4367
|
+
return this.#node;
|
|
4368
|
+
}
|
|
4369
|
+
/** The node's markup, serialized when asked for rather than on every failure. */
|
|
4370
|
+
get html() {
|
|
4371
|
+
return RenderError.stringify(this.#node);
|
|
4372
|
+
}
|
|
4373
|
+
/**
|
|
4374
|
+
* What identifies a node in a frame: an element by its open tag, a text node
|
|
4375
|
+
* by its source, a fragment by the open tags of the elements it holds.
|
|
4376
|
+
*/
|
|
4377
|
+
static describe(nodeOrFragment) {
|
|
4378
|
+
if (nodeOrFragment.nodeType === Node.TEXT_NODE) {
|
|
4379
|
+
return RenderError.#ellipsize(String(nodeOrFragment.nodeValue).trim());
|
|
4380
|
+
}
|
|
4381
|
+
if (nodeOrFragment.nodeType === Node.ELEMENT_NODE) {
|
|
4382
|
+
const el = /** @type Element */ (nodeOrFragment);
|
|
4383
|
+
const attrs = Array.from(el.attributes, (a) => ` ${a.name}="${a.value}"`).join('');
|
|
4384
|
+
return RenderError.#ellipsize(`<${el.localName}${attrs}>`);
|
|
4385
|
+
}
|
|
4386
|
+
const children = Array.from(nodeOrFragment.childNodes)
|
|
4387
|
+
.filter((n) => n.nodeType === Node.ELEMENT_NODE || String(n.nodeValue ?? '').trim().length > 0)
|
|
4388
|
+
.map((n) => RenderError.describe(n));
|
|
4389
|
+
return RenderError.#ellipsize(children.join(''));
|
|
4390
|
+
}
|
|
4391
|
+
static #ellipsize(text) {
|
|
4392
|
+
return text.length > 120 ? `${text.slice(0, 120)}…` : text;
|
|
4345
4393
|
}
|
|
4346
4394
|
static stringify(nodeOrFragment) {
|
|
4347
4395
|
const t = document.createElement('template');
|
|
@@ -4368,59 +4416,107 @@ var ftl = (function (exports) {
|
|
|
4368
4416
|
}
|
|
4369
4417
|
}
|
|
4370
4418
|
|
|
4419
|
+
/**
|
|
4420
|
+
* Tracks the elements waiting to render. `ready` resolves once the queue has
|
|
4421
|
+
* drained and never rejects; the promise kept per element resolves when that
|
|
4422
|
+
* element has rendered and rejects with whatever its upgrade threw.
|
|
4423
|
+
*/
|
|
4371
4424
|
class UpgradeQueue {
|
|
4372
4425
|
#q = new Map();
|
|
4426
|
+
#readyResolve;
|
|
4427
|
+
#ready = new Promise((resolve) => {
|
|
4428
|
+
this.#readyResolve = resolve;
|
|
4429
|
+
});
|
|
4373
4430
|
constructor() {
|
|
4374
|
-
document.
|
|
4375
|
-
|
|
4376
|
-
document.dispatchEvent(
|
|
4377
|
-
new CustomEvent('ftl:ready', {
|
|
4378
|
-
bubbles: false,
|
|
4379
|
-
cancelable: false,
|
|
4380
|
-
}),
|
|
4381
|
-
);
|
|
4431
|
+
Nodes.waitDomContentLoaded(document).then(() => {
|
|
4432
|
+
this.#start();
|
|
4382
4433
|
});
|
|
4383
4434
|
}
|
|
4384
|
-
|
|
4435
|
+
/**
|
|
4436
|
+
* Waits for the page's readiness: the promise resolves right after the
|
|
4437
|
+
* ftl:ready event is dispatched, and immediately when that moment already
|
|
4438
|
+
* passed.
|
|
4439
|
+
* @returns {Promise<void>}
|
|
4440
|
+
*/
|
|
4441
|
+
ready() {
|
|
4442
|
+
return this.#ready;
|
|
4443
|
+
}
|
|
4444
|
+
async #start() {
|
|
4445
|
+
await this.settled();
|
|
4446
|
+
document.dispatchEvent(
|
|
4447
|
+
new CustomEvent('ftl:ready', {
|
|
4448
|
+
bubbles: false,
|
|
4449
|
+
cancelable: false,
|
|
4450
|
+
}),
|
|
4451
|
+
);
|
|
4452
|
+
this.#readyResolve();
|
|
4453
|
+
}
|
|
4385
4454
|
enqueue(el) {
|
|
4386
4455
|
if (this.#q.has(el)) {
|
|
4387
4456
|
//already upgrading, can happen when disconnecting an element
|
|
4388
4457
|
//while it's already queued for upgrade (e.g.: ful-form)
|
|
4389
4458
|
return;
|
|
4390
4459
|
}
|
|
4391
|
-
//
|
|
4392
|
-
//
|
|
4393
|
-
//reported the way it always was
|
|
4460
|
+
//one entry, two signals: readiness waits on a signal that only ever
|
|
4461
|
+
//resolves, so nothing here attaches a rejection handler to the upgrade
|
|
4462
|
+
//itself and a component that fails is still reported the way it always was
|
|
4394
4463
|
const { promise: finished, resolve: markFinished } = /** @type {PromiseWithResolvers<void>} */ (
|
|
4395
4464
|
Promise.withResolvers()
|
|
4396
4465
|
);
|
|
4397
|
-
const
|
|
4466
|
+
const upgrade = Nodes.waitParsed(el)
|
|
4398
4467
|
.then(() => el.upgrade())
|
|
4399
4468
|
.finally(() => {
|
|
4400
4469
|
this.#q.delete(el);
|
|
4401
|
-
this.#finished.delete(el);
|
|
4402
4470
|
markFinished();
|
|
4403
4471
|
});
|
|
4404
|
-
this.#q.set(el,
|
|
4405
|
-
this.#finished.set(el, finished);
|
|
4472
|
+
this.#q.set(el, { upgrade, finished });
|
|
4406
4473
|
}
|
|
4407
4474
|
/**
|
|
4408
|
-
*
|
|
4409
|
-
* waiting
|
|
4410
|
-
*
|
|
4411
|
-
* others back, readiness means the queue drained rather than that everything worked.
|
|
4475
|
+
* The one fixed-point loop: drains the accepted entries, including the ones
|
|
4476
|
+
* enqueued while waiting, since a component is only queued once its parent
|
|
4477
|
+
* connects it and a single pass would miss everything nested.
|
|
4412
4478
|
*/
|
|
4413
|
-
async
|
|
4414
|
-
|
|
4415
|
-
|
|
4479
|
+
async #drain(accept, pick) {
|
|
4480
|
+
for (;;) {
|
|
4481
|
+
const pending = Array.from(this.#q)
|
|
4482
|
+
.filter(([el]) => accept(el))
|
|
4483
|
+
.map(([, entry]) => pick(entry));
|
|
4484
|
+
if (pending.length === 0) {
|
|
4485
|
+
return;
|
|
4486
|
+
}
|
|
4487
|
+
await Promise.all(pending);
|
|
4416
4488
|
}
|
|
4417
4489
|
}
|
|
4418
|
-
|
|
4419
|
-
|
|
4490
|
+
/**
|
|
4491
|
+
* Waits for the whole queue to drain. Never rejects: a component that fails
|
|
4492
|
+
* does not hold the others back, and readiness means the queue drained rather
|
|
4493
|
+
* than that everything worked.
|
|
4494
|
+
*/
|
|
4495
|
+
settled() {
|
|
4496
|
+
return this.#drain(() => true, (entry) => entry.finished);
|
|
4497
|
+
}
|
|
4498
|
+
/** Waits for the accepted upgrades, rejecting with the first that failed. */
|
|
4499
|
+
upgraded(accept) {
|
|
4500
|
+
return this.#drain(accept, (entry) => entry.upgrade);
|
|
4501
|
+
}
|
|
4502
|
+
/** The pending upgrade of one element, undefined when it is not queued. */
|
|
4503
|
+
whenUpgraded(el) {
|
|
4504
|
+
return this.#q.get(el)?.upgrade;
|
|
4505
|
+
}
|
|
4506
|
+
/** The elements whose upgrade is still pending, in queue order. */
|
|
4507
|
+
pending() {
|
|
4508
|
+
return Array.from(this.#q.keys());
|
|
4420
4509
|
}
|
|
4421
4510
|
}
|
|
4422
4511
|
|
|
4423
|
-
|
|
4512
|
+
/**
|
|
4513
|
+
* The page's single source of truth for elements, modules, data, components
|
|
4514
|
+
* and attribute mappers. Elements defined before configure() are deferred and
|
|
4515
|
+
* defined by it; from then on every defineElement takes effect immediately.
|
|
4516
|
+
* The exported `registry` singleton is the page's own; a `new Registry()` is a
|
|
4517
|
+
* separate instance, and Rendering and the ftl:ready machinery wait on the
|
|
4518
|
+
* singleton alone.
|
|
4519
|
+
*/
|
|
4424
4520
|
class Registry {
|
|
4425
4521
|
#tagToClass = {};
|
|
4426
4522
|
#configured = false;
|
|
@@ -4478,30 +4574,20 @@ var ftl = (function (exports) {
|
|
|
4478
4574
|
return value == null ? null : value.join(',');
|
|
4479
4575
|
},
|
|
4480
4576
|
},
|
|
4481
|
-
csvm: {
|
|
4482
|
-
unmarshal(str, name, el) {
|
|
4483
|
-
if (el.hasAttribute('multiple')) {
|
|
4484
|
-
return str === null
|
|
4485
|
-
? []
|
|
4486
|
-
: str
|
|
4487
|
-
.split(',')
|
|
4488
|
-
.map((e) => e.trim())
|
|
4489
|
-
.filter((e) => e);
|
|
4490
|
-
}
|
|
4491
|
-
return str === null || str === '' ? null : str;
|
|
4492
|
-
},
|
|
4493
|
-
marshal(value, name, el) {
|
|
4494
|
-
if (el.hasAttribute('multiple')) {
|
|
4495
|
-
return value === null ? null : value.join(',');
|
|
4496
|
-
}
|
|
4497
|
-
return value == null ? null : String(value);
|
|
4498
|
-
},
|
|
4499
|
-
},
|
|
4500
4577
|
};
|
|
4501
4578
|
#components = {};
|
|
4502
4579
|
#modules;
|
|
4503
4580
|
#data = [];
|
|
4581
|
+
#evaluator = new ExpressionEvaluator(undefined, []);
|
|
4504
4582
|
#upgradeQueue = new UpgradeQueue();
|
|
4583
|
+
/**
|
|
4584
|
+
* Registers a custom element under its tag: the class is augmented with its
|
|
4585
|
+
* BITS (observed attributes, mappers, templates) and handed to the platform.
|
|
4586
|
+
* Before configure() the definition is deferred, so import order never
|
|
4587
|
+
* matters.
|
|
4588
|
+
* @param {string} tag
|
|
4589
|
+
* @param {*} klass a ParsedElement subclass
|
|
4590
|
+
*/
|
|
4505
4591
|
defineElement(tag, klass) {
|
|
4506
4592
|
if (!this.#configured) {
|
|
4507
4593
|
this.#tagToClass[tag] = klass;
|
|
@@ -4510,16 +4596,69 @@ var ftl = (function (exports) {
|
|
|
4510
4596
|
this.#augmentAndDefineElement(tag, klass);
|
|
4511
4597
|
return this;
|
|
4512
4598
|
}
|
|
4599
|
+
/**
|
|
4600
|
+
* The attribute declarations a class composes along its inheritance chain,
|
|
4601
|
+
* base first: `observed` stay live after the upgrade and drive the property
|
|
4602
|
+
* `propertyOf` names, `attributes` are the configuration read once at it and
|
|
4603
|
+
* drive no property at all. A subclass's entry for a name overrides its
|
|
4604
|
+
* ancestors', so a base class declares what every subclass keeps observing
|
|
4605
|
+
* (a protocol attribute such as Field's disabled claim) and a leaf refines a
|
|
4606
|
+
* mapping, or moves a name's position, without repeating the whole list.
|
|
4607
|
+
*
|
|
4608
|
+
* The walk stops where the platform's own class hierarchy begins: no earlier
|
|
4609
|
+
* stop can work, since a registered ancestor carries an own BITS of its own,
|
|
4610
|
+
* and nothing above the elements declares anything.
|
|
4611
|
+
*
|
|
4612
|
+
* Everything deriving a component's attribute vocabulary reads it here, so
|
|
4613
|
+
* the runtime and whatever documents it cannot walk the chain differently.
|
|
4614
|
+
* @param {*} klass a ParsedElement subclass
|
|
4615
|
+
* @returns {{ observed: string[], attributes: string[] }}
|
|
4616
|
+
*/
|
|
4617
|
+
static declarationsOf(klass) {
|
|
4618
|
+
const chain = [];
|
|
4619
|
+
for (let c = klass; c !== null && c !== HTMLElement; c = Object.getPrototypeOf(c)) {
|
|
4620
|
+
chain.unshift(c);
|
|
4621
|
+
}
|
|
4622
|
+
const own = (name) => chain.flatMap((c) => Object.getOwnPropertyDescriptor(c, name)?.value ?? []);
|
|
4623
|
+
return { observed: own('observed'), attributes: own('attributes') };
|
|
4624
|
+
}
|
|
4625
|
+
/**
|
|
4626
|
+
* The property an observed attribute drives, by the platform's own
|
|
4627
|
+
* dash-to-camel rule: the one `dataset` applies, so `clear-invalid-on-change`
|
|
4628
|
+
* would reach `clearInvalidOnChange` the way `data-clear-invalid-on-change`
|
|
4629
|
+
* reaches `dataset.clearInvalidOnChange`. A single-word name is returned
|
|
4630
|
+
* unchanged, which is what every observed attribute in the library is.
|
|
4631
|
+
*
|
|
4632
|
+
* It exists because the observed tier is the one that becomes properties:
|
|
4633
|
+
* without it an attribute could only be observed if its name happened to be
|
|
4634
|
+
* a usable identifier, which is why every multiword observed attribute here
|
|
4635
|
+
* used to be squashed into one word while the configuration tier, which
|
|
4636
|
+
* never becomes a property, spelled the same idea with a dash.
|
|
4637
|
+
*
|
|
4638
|
+
* Only this direction is mapped. A property never derives its attribute: a
|
|
4639
|
+
* setter reflects through `reflectTo`, naming the attribute it writes.
|
|
4640
|
+
* @param {string} attribute
|
|
4641
|
+
* @returns {string}
|
|
4642
|
+
*/
|
|
4643
|
+
static propertyOf(attribute) {
|
|
4644
|
+
return attribute.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
4645
|
+
}
|
|
4513
4646
|
#augmentAndDefineElement(tag, klass) {
|
|
4514
|
-
const { observed, attributes
|
|
4515
|
-
const
|
|
4516
|
-
|
|
4647
|
+
const { observed, attributes } = Registry.declarationsOf(klass);
|
|
4648
|
+
const { template, templates, slots, mappers, config } = klass;
|
|
4649
|
+
//a name a subclass re-declares takes the subclass's position, which is
|
|
4650
|
+
//how a field whose value setter reads its own shape attributes declares
|
|
4651
|
+
//that its value lands after them: the order the declarations compose in
|
|
4652
|
+
//is the order the base applies them
|
|
4653
|
+
const declaredNames = observed.map((a) => a.split(':')[0]);
|
|
4654
|
+
const observedNames = [...new Set(declaredNames.reverse())].reverse();
|
|
4655
|
+
const attrToMapper = [...attributes, ...observed].reduce((acc, a) => {
|
|
4517
4656
|
const [attr, maybeType] = a.split(':');
|
|
4518
|
-
const type = maybeType
|
|
4657
|
+
const type = maybeType ?? 'string';
|
|
4519
4658
|
if (!(type in this.#mappers) && !(type in (mappers ?? {}))) {
|
|
4520
4659
|
throw new Error(`unsupported attribute type: ${type}`);
|
|
4521
4660
|
}
|
|
4522
|
-
acc[attr
|
|
4661
|
+
acc[attr] = mappers?.[type] ?? this.#mappers[type];
|
|
4523
4662
|
return acc;
|
|
4524
4663
|
}, {});
|
|
4525
4664
|
|
|
@@ -4529,43 +4668,97 @@ var ftl = (function (exports) {
|
|
|
4529
4668
|
const nameToTemplate = Object.fromEntries(namesAndTemplates);
|
|
4530
4669
|
|
|
4531
4670
|
klass.BITS = {
|
|
4671
|
+
//the defining registry travels with the definition: an element resolves
|
|
4672
|
+
//its templates and its components through the registry that defined it,
|
|
4673
|
+
//not through whichever one a module happened to import
|
|
4674
|
+
registry: this,
|
|
4532
4675
|
enqueue: (el) => this.#upgradeQueue.enqueue(el),
|
|
4533
4676
|
SLOTS: slots,
|
|
4677
|
+
//the class's own constants, overlaid on its templates as `config`:
|
|
4678
|
+
//read here with the rest of the declaration, so a page replacing them
|
|
4679
|
+
//does it before configure() like every other definition-time choice
|
|
4680
|
+
CONFIG: config,
|
|
4534
4681
|
OBSERVED: observedNames,
|
|
4682
|
+
DECLARED: [...new Set([...observedNames, ...attributes.map((a) => a.split(':')[0])])],
|
|
4535
4683
|
ATTR_TO_MAPPER: attrToMapper,
|
|
4684
|
+
//resolved once here rather than at every attribute write
|
|
4685
|
+
ATTR_TO_PROPERTY: Object.fromEntries(observedNames.map((a) => [a, Registry.propertyOf(a)])),
|
|
4536
4686
|
TEMPLATES: nameToTemplate,
|
|
4537
4687
|
};
|
|
4538
4688
|
customElements.define(tag, klass);
|
|
4539
4689
|
}
|
|
4690
|
+
/**
|
|
4691
|
+
* Merges one module under its name, its functions resolving as
|
|
4692
|
+
* `#name:fn`; an empty name merges a whole map, whose keys resolve bare,
|
|
4693
|
+
* as `#fn`.
|
|
4694
|
+
* @param {string} name
|
|
4695
|
+
* @param {object} value
|
|
4696
|
+
*/
|
|
4540
4697
|
defineModule(name, value) {
|
|
4541
4698
|
const module = name ? { [name]: value } : value;
|
|
4542
4699
|
this.#modules = { ...this.#modules, ...module };
|
|
4700
|
+
this.#rebind();
|
|
4543
4701
|
return this;
|
|
4544
4702
|
}
|
|
4703
|
+
/**
|
|
4704
|
+
* Replaces the whole module map.
|
|
4705
|
+
* @param {object} ms
|
|
4706
|
+
*/
|
|
4545
4707
|
defineModules(ms) {
|
|
4546
4708
|
this.#modules = ms;
|
|
4709
|
+
this.#rebind();
|
|
4547
4710
|
return this;
|
|
4548
4711
|
}
|
|
4712
|
+
/**
|
|
4713
|
+
* Registers a named component (a loader, a response mapper), fetched back
|
|
4714
|
+
* through component().
|
|
4715
|
+
* @param {string} name
|
|
4716
|
+
* @param {*} value
|
|
4717
|
+
*/
|
|
4549
4718
|
defineComponent(name, value) {
|
|
4550
4719
|
this.#components[name] = value;
|
|
4551
4720
|
return this;
|
|
4552
4721
|
}
|
|
4722
|
+
/**
|
|
4723
|
+
* Replaces the data stack the templates evaluate over.
|
|
4724
|
+
* @param {...any} data
|
|
4725
|
+
*/
|
|
4553
4726
|
defineData(...data) {
|
|
4554
4727
|
this.#data = data;
|
|
4728
|
+
this.#rebind();
|
|
4555
4729
|
return this;
|
|
4556
4730
|
}
|
|
4731
|
+
/**
|
|
4732
|
+
* Appends to the data stack, the later entry winning a shared name.
|
|
4733
|
+
* @param {...any} data
|
|
4734
|
+
*/
|
|
4557
4735
|
defineOverlay(...data) {
|
|
4558
4736
|
this.#data = [...this.#data, ...data];
|
|
4737
|
+
this.#rebind();
|
|
4559
4738
|
return this;
|
|
4560
4739
|
}
|
|
4740
|
+
/**
|
|
4741
|
+
* Registers a custom attribute mapper type, available to every later
|
|
4742
|
+
* `name:type` declaration.
|
|
4743
|
+
* @param {string} k
|
|
4744
|
+
* @param {{ unmarshal(str: string|null, name: string, el: Element): any, marshal(value: any, name: string, el: Element): string|null }} v
|
|
4745
|
+
*/
|
|
4561
4746
|
defineMapper(k, v) {
|
|
4562
4747
|
this.#mappers[k] = v;
|
|
4563
4748
|
return this;
|
|
4564
4749
|
}
|
|
4750
|
+
/**
|
|
4751
|
+
* Hands the registry over to the plugin's configure.
|
|
4752
|
+
* @param {{ configure(registry: Registry): void }} p
|
|
4753
|
+
*/
|
|
4565
4754
|
plugin(p) {
|
|
4566
4755
|
p.configure(this);
|
|
4567
4756
|
return this;
|
|
4568
4757
|
}
|
|
4758
|
+
/**
|
|
4759
|
+
* Defines every element deferred so far; from then on, defineElement takes
|
|
4760
|
+
* effect immediately.
|
|
4761
|
+
*/
|
|
4569
4762
|
configure() {
|
|
4570
4763
|
for (const [tag, klass] of Object.entries(this.#tagToClass)) {
|
|
4571
4764
|
this.#augmentAndDefineElement(tag, klass);
|
|
@@ -4574,15 +4767,42 @@ var ftl = (function (exports) {
|
|
|
4574
4767
|
this.#configured = true;
|
|
4575
4768
|
return this;
|
|
4576
4769
|
}
|
|
4577
|
-
|
|
4578
|
-
|
|
4770
|
+
/**
|
|
4771
|
+
* Waits for the queued upgrades the filter accepts, rejecting with the first
|
|
4772
|
+
* that failed: the rejecting barrier Rendering is a facade over.
|
|
4773
|
+
* @param {(el: Element) => boolean} accept
|
|
4774
|
+
*/
|
|
4775
|
+
settle(accept) {
|
|
4776
|
+
return this.#upgradeQueue.upgraded(accept);
|
|
4777
|
+
}
|
|
4778
|
+
/** The pending upgrade of one element, undefined when it is not queued. */
|
|
4779
|
+
whenUpgraded(el) {
|
|
4780
|
+
return this.#upgradeQueue.whenUpgraded(el);
|
|
4579
4781
|
}
|
|
4580
|
-
|
|
4581
|
-
|
|
4782
|
+
/** The elements whose upgrade is still pending, in queue order. */
|
|
4783
|
+
pending() {
|
|
4784
|
+
return this.#upgradeQueue.pending();
|
|
4582
4785
|
}
|
|
4786
|
+
/**
|
|
4787
|
+
* Waits for the page's readiness: the same moment the ftl:ready event is
|
|
4788
|
+
* dispatched at, resolving immediately when that moment already passed.
|
|
4789
|
+
* @returns {Promise<void>}
|
|
4790
|
+
*/
|
|
4791
|
+
ready() {
|
|
4792
|
+
return this.#upgradeQueue.ready();
|
|
4793
|
+
}
|
|
4794
|
+
#rebind() {
|
|
4795
|
+
this.#evaluator = new ExpressionEvaluator(this.#modules, this.#data);
|
|
4796
|
+
}
|
|
4797
|
+
/**
|
|
4798
|
+
* The scope every template on this registry renders in: the modules and the
|
|
4799
|
+
* data stack, as one value. Replaced whenever either is defined, so a holder
|
|
4800
|
+
* of an older one keeps rendering against what it was handed.
|
|
4801
|
+
*/
|
|
4583
4802
|
evaluator() {
|
|
4584
|
-
return
|
|
4803
|
+
return this.#evaluator;
|
|
4585
4804
|
}
|
|
4805
|
+
/** The component registered under the name, undefined when none is. */
|
|
4586
4806
|
component(name) {
|
|
4587
4807
|
return this.#components[name];
|
|
4588
4808
|
}
|
|
@@ -4590,49 +4810,35 @@ var ftl = (function (exports) {
|
|
|
4590
4810
|
|
|
4591
4811
|
const registry = new Registry();
|
|
4592
4812
|
|
|
4813
|
+
/** The Template factories, each bound to the page registry's scope. */
|
|
4814
|
+
/** The Template factories bound to the page's own registry: the same four sources, with its modules and data already applied. */
|
|
4593
4815
|
class Templates {
|
|
4594
4816
|
static fromHtml(html) {
|
|
4595
|
-
|
|
4596
|
-
return Template.fromHtml(html, modules, ...data);
|
|
4817
|
+
return Template.fromHtml(html).withEvaluator(registry.evaluator());
|
|
4597
4818
|
}
|
|
4598
4819
|
static fromSelector(selector) {
|
|
4599
|
-
|
|
4600
|
-
return Template.fromSelector(selector, modules, ...data);
|
|
4820
|
+
return Template.fromSelector(selector).withEvaluator(registry.evaluator());
|
|
4601
4821
|
}
|
|
4602
4822
|
static fromTemplate(templateEl) {
|
|
4603
|
-
|
|
4604
|
-
return Template.fromTemplate(templateEl, modules, ...data);
|
|
4823
|
+
return Template.fromTemplate(templateEl).withEvaluator(registry.evaluator());
|
|
4605
4824
|
}
|
|
4606
4825
|
static fromFragment(fragment) {
|
|
4607
|
-
|
|
4608
|
-
return Template.fromFragment(fragment, modules, ...data);
|
|
4826
|
+
return Template.fromFragment(fragment).withEvaluator(registry.evaluator());
|
|
4609
4827
|
}
|
|
4610
4828
|
}
|
|
4611
4829
|
|
|
4612
4830
|
/**
|
|
4613
|
-
*
|
|
4614
|
-
*
|
|
4615
|
-
*
|
|
4616
|
-
*
|
|
4831
|
+
* Awaitable rendering barriers over the registry's upgrade queue. These are
|
|
4832
|
+
* the rejecting waits: a failed upgrade among the awaited components rejects
|
|
4833
|
+
* the wait, where registry.ready() only ever means the queue drained and
|
|
4834
|
+
* leaves a failed component to its own unhandled-rejection report.
|
|
4617
4835
|
*/
|
|
4618
|
-
const settle = async (accept) => {
|
|
4619
|
-
for (;;) {
|
|
4620
|
-
const pending = Array.from(registry.upgrades)
|
|
4621
|
-
.filter(([child]) => accept(child))
|
|
4622
|
-
.map(([, promise]) => promise);
|
|
4623
|
-
if (pending.length === 0) {
|
|
4624
|
-
return;
|
|
4625
|
-
}
|
|
4626
|
-
await Promise.all(pending);
|
|
4627
|
-
}
|
|
4628
|
-
};
|
|
4629
|
-
|
|
4630
4836
|
class Rendering {
|
|
4631
|
-
static
|
|
4632
|
-
|
|
4837
|
+
static waitFor(el) {
|
|
4838
|
+
return registry.settle((child) => el.contains(child));
|
|
4633
4839
|
}
|
|
4634
|
-
static
|
|
4635
|
-
|
|
4840
|
+
static waitForChildren(el) {
|
|
4841
|
+
return registry.settle((child) => el !== child && el.contains(child));
|
|
4636
4842
|
}
|
|
4637
4843
|
}
|
|
4638
4844
|
|
|
@@ -4644,122 +4850,422 @@ var ftl = (function (exports) {
|
|
|
4644
4850
|
* @property {(val: any, name: string, el: Element) => string|null} marshal
|
|
4645
4851
|
*/
|
|
4646
4852
|
|
|
4647
|
-
|
|
4648
4853
|
class ParsedElement extends HTMLElement {
|
|
4649
4854
|
static BITS = {
|
|
4855
|
+
//an element the registry never defined still answers the page's own
|
|
4856
|
+
registry,
|
|
4650
4857
|
enqueue: (el) => {},
|
|
4651
4858
|
SLOTS: false,
|
|
4859
|
+
/** @type {object|undefined} */
|
|
4860
|
+
CONFIG: undefined,
|
|
4861
|
+
/** @type {string[]} */
|
|
4652
4862
|
OBSERVED: [],
|
|
4863
|
+
/** @type {string[]} */
|
|
4864
|
+
DECLARED: [],
|
|
4653
4865
|
/** @type {Record<string, Mapper>} */
|
|
4654
4866
|
ATTR_TO_MAPPER: {},
|
|
4867
|
+
/** @type {Record<string, string>} */
|
|
4868
|
+
ATTR_TO_PROPERTY: {},
|
|
4655
4869
|
TEMPLATES: {},
|
|
4656
4870
|
};
|
|
4657
4871
|
static get observedAttributes() {
|
|
4658
4872
|
return this.BITS.OBSERVED;
|
|
4659
4873
|
}
|
|
4874
|
+
constructor() {
|
|
4875
|
+
super();
|
|
4876
|
+
this.internals = this.attachInternals();
|
|
4877
|
+
}
|
|
4878
|
+
/**
|
|
4879
|
+
* The platform's window into the element's own state. The base attaches it
|
|
4880
|
+
* for every element, so a subclass never calls attachInternals itself: the
|
|
4881
|
+
* platform allows it once, and a second call throws. Form association is a
|
|
4882
|
+
* property of the definition rather than of who attached, so a subclass
|
|
4883
|
+
* declaring `static formAssociated` still gets the form apis here.
|
|
4884
|
+
*
|
|
4885
|
+
* A subclass must not redeclare it: a class field with no initializer
|
|
4886
|
+
* assigns undefined after super() returns, which would wipe it.
|
|
4887
|
+
*/
|
|
4888
|
+
internals;
|
|
4660
4889
|
#parsed = false;
|
|
4661
|
-
#
|
|
4890
|
+
#started = false;
|
|
4891
|
+
/** the attributes whose own reflection is in flight */
|
|
4892
|
+
#reflecting = new Set();
|
|
4893
|
+
/** the observed snapshot between the upgrade's start and its render's end */
|
|
4894
|
+
#pending = /** @type {{ [k: string]: any } | null} */ (null);
|
|
4895
|
+
/** the configuration tier, read once at the upgrade and kept for the element's life */
|
|
4896
|
+
#frozen = /** @type {{ [k: string]: any }} */ ({});
|
|
4662
4897
|
#bits() {
|
|
4663
4898
|
return /** @type {typeof ParsedElement} */ (this.constructor).BITS;
|
|
4664
4899
|
}
|
|
4900
|
+
#mapper(attr) {
|
|
4901
|
+
const mapper = this.#bits().ATTR_TO_MAPPER[attr];
|
|
4902
|
+
if (!mapper) {
|
|
4903
|
+
//an attribute the class never declared has no type to read it as:
|
|
4904
|
+
//say so rather than dying on the missing mapper. Content this
|
|
4905
|
+
//element does not own, such as a custom loader's own configuration,
|
|
4906
|
+
//is read with getAttribute, the platform's own answer
|
|
4907
|
+
throw new Error(
|
|
4908
|
+
`${this.constructor.name} declares no attribute '${attr}': declare it in static observed or static attributes, or read it with getAttribute`,
|
|
4909
|
+
);
|
|
4910
|
+
}
|
|
4911
|
+
return mapper;
|
|
4912
|
+
}
|
|
4665
4913
|
unmarshal(attr, str) {
|
|
4666
|
-
return this.#
|
|
4914
|
+
return this.#mapper(attr).unmarshal(str, attr, this);
|
|
4667
4915
|
}
|
|
4668
4916
|
marshal(attr, value) {
|
|
4669
|
-
return this.#
|
|
4917
|
+
return this.#mapper(attr).marshal(value, attr, this);
|
|
4670
4918
|
}
|
|
4671
4919
|
/**
|
|
4672
4920
|
* @param {string} [name] - The name of the template target, defaults to 'default'
|
|
4673
4921
|
*/
|
|
4922
|
+
/** The registry that defined this element, the page's own for an undefined one. */
|
|
4923
|
+
get _registry() {
|
|
4924
|
+
return this.#bits().registry;
|
|
4925
|
+
}
|
|
4926
|
+
/**
|
|
4927
|
+
* The component registered under the name on this element's registry, the
|
|
4928
|
+
* one every ful loader and mapper resolves through.
|
|
4929
|
+
* @param {string} name
|
|
4930
|
+
*/
|
|
4931
|
+
component(name) {
|
|
4932
|
+
return this.#bits().registry.component(name);
|
|
4933
|
+
}
|
|
4674
4934
|
template(name) {
|
|
4675
|
-
const
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
const v = this.constructor[k];
|
|
4679
|
-
if (v) {
|
|
4680
|
-
t = t.withOverlay({ [k]: v });
|
|
4681
|
-
}
|
|
4935
|
+
const target = this.#bits().TEMPLATES[name ?? 'default'];
|
|
4936
|
+
if (!target) {
|
|
4937
|
+
throw new Error(`no template named '${name ?? 'default'}' on ${this.constructor.name}`);
|
|
4682
4938
|
}
|
|
4683
|
-
|
|
4939
|
+
const t = target.withEvaluator(this.#bits().registry.evaluator());
|
|
4940
|
+
const config = this.#bits().CONFIG;
|
|
4941
|
+
return config ? t.withOverlay({ config }) : t;
|
|
4684
4942
|
}
|
|
4685
4943
|
connectedCallback() {
|
|
4686
|
-
if (this.#
|
|
4944
|
+
if (this.#started) {
|
|
4687
4945
|
return;
|
|
4688
4946
|
}
|
|
4689
4947
|
this.#bits().enqueue(this);
|
|
4690
4948
|
}
|
|
4691
4949
|
attributeChangedCallback(attr, oldValue, newValue) {
|
|
4692
|
-
if (
|
|
4950
|
+
if (oldValue === newValue) {
|
|
4693
4951
|
return;
|
|
4694
4952
|
}
|
|
4695
|
-
|
|
4953
|
+
//the mute is the attribute being reflected, not the element: a component
|
|
4954
|
+
//that legitimately changes another observed attribute while one reflects
|
|
4955
|
+
//is a change like any other, and used to be swallowed
|
|
4956
|
+
if (this.#reflecting.has(attr)) {
|
|
4696
4957
|
return;
|
|
4697
4958
|
}
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
formDisabledCallback(disabled) {
|
|
4959
|
+
//the properties go live only once the render is done: before that, an
|
|
4960
|
+
//attribute write lands in the observed snapshot and the base applies it
|
|
4961
|
+
//with the rest of the declared state
|
|
4702
4962
|
if (!this.#parsed) {
|
|
4703
|
-
this.#
|
|
4963
|
+
if (this.#pending !== null && attr in this.#pending) {
|
|
4964
|
+
this.#pending[attr] = this.unmarshal(attr, newValue);
|
|
4965
|
+
}
|
|
4704
4966
|
return;
|
|
4705
4967
|
}
|
|
4706
|
-
|
|
4707
|
-
if (disabled) {
|
|
4708
|
-
this.#unclaimFormDisabled();
|
|
4709
|
-
}
|
|
4710
|
-
}
|
|
4711
|
-
//a disabled ancestor fieldset already matches the element through :disabled, and
|
|
4712
|
-
//an attribute of its own would keep the element disabled once the fieldset is
|
|
4713
|
-
//re-enabled, as formDisabledCallback(false) is only delivered on an actual state
|
|
4714
|
-
//change. a claim of its own made while already disabled by ancestry is safe: the
|
|
4715
|
-
//platform fires no callback for it, so it survives the fieldset being re-enabled
|
|
4716
|
-
#unclaimFormDisabled() {
|
|
4717
|
-
if (this.closest('fieldset:disabled')) {
|
|
4718
|
-
this.removeAttribute('disabled');
|
|
4719
|
-
}
|
|
4968
|
+
this[this.#bits().ATTR_TO_PROPERTY[attr]] = this.unmarshal(attr, newValue);
|
|
4720
4969
|
}
|
|
4970
|
+
/**
|
|
4971
|
+
* Upgrades once: reads the declared attributes, keeps the observed half open
|
|
4972
|
+
* to writes made while the render is pending, applies them to the properties
|
|
4973
|
+
* when the render returns, and only then lets an attribute write forward.
|
|
4974
|
+
*
|
|
4975
|
+
* An observed attribute drives the property the registry's `propertyOf`
|
|
4976
|
+
* names, so a hyphenated attribute is authored with its dashes and read as
|
|
4977
|
+
* a camelCase property. A single-word attribute is its own property name,
|
|
4978
|
+
* which is what every observed attribute in the library is.
|
|
4979
|
+
*/
|
|
4721
4980
|
async upgrade() {
|
|
4722
|
-
if (this.#
|
|
4981
|
+
if (this.#started) {
|
|
4723
4982
|
return;
|
|
4724
4983
|
}
|
|
4725
|
-
this.#
|
|
4984
|
+
this.#started = true;
|
|
4726
4985
|
const slots = this.#bits().SLOTS ? LightSlots.from(this) : undefined;
|
|
4727
|
-
|
|
4728
|
-
|
|
4986
|
+
//both tiers are read here: the observed ones forward to a property once
|
|
4987
|
+
//the render is done, the rest are the element's configuration, read the
|
|
4988
|
+
//one time and never again
|
|
4989
|
+
const declared = Object.fromEntries(
|
|
4990
|
+
this.#bits().DECLARED.map((attribute) => [
|
|
4729
4991
|
attribute,
|
|
4730
4992
|
this.unmarshal(attribute, this.getAttribute(attribute)),
|
|
4731
4993
|
]),
|
|
4732
4994
|
);
|
|
4733
|
-
const
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4995
|
+
const observedNames = new Set(this.#bits().OBSERVED);
|
|
4996
|
+
this.#frozen = Object.fromEntries(Object.entries(declared).filter(([name]) => !observedNames.has(name)));
|
|
4997
|
+
this.#pending = declared;
|
|
4998
|
+
try {
|
|
4999
|
+
await this.render({ slots });
|
|
5000
|
+
//the declared state reaches the properties once the dom the setters
|
|
5001
|
+
//drive exists, in the order the registry composed the declarations:
|
|
5002
|
+
//a base class's attributes before the subclass's own
|
|
5003
|
+
const properties = this.#bits().ATTR_TO_PROPERTY;
|
|
5004
|
+
for (const name of this.#bits().OBSERVED) {
|
|
5005
|
+
this[properties[name]] = declared[name];
|
|
5006
|
+
}
|
|
5007
|
+
//the properties go live once the render is done: from here on, an
|
|
5008
|
+
//attribute write forwards to the property. A render that threw
|
|
5009
|
+
//leaves it shut, so a later attribute write cannot reach setters
|
|
5010
|
+
//that assume pieces the failed render never adopted
|
|
5011
|
+
this.#parsed = true;
|
|
5012
|
+
//the same moment, said to css. :defined is true from the constructor,
|
|
5013
|
+
//which is before the dom exists, so a guard written against it reveals
|
|
5014
|
+
//an element that has nothing in it yet
|
|
5015
|
+
this.internals.states.add('rendered');
|
|
5016
|
+
} finally {
|
|
5017
|
+
this.#pending = null;
|
|
4737
5018
|
}
|
|
4738
5019
|
}
|
|
5020
|
+
/**
|
|
5021
|
+
* Renders the element from its slots alone: it builds the dom its setters
|
|
5022
|
+
* drive, and the base applies the declared state onto the properties as
|
|
5023
|
+
* soon as it returns. A render needing a declared value while it builds
|
|
5024
|
+
* reads it through `declared(name)`. The slots are undefined for an element
|
|
5025
|
+
* declaring no slots.
|
|
5026
|
+
* @param {{ slots: any }} c
|
|
5027
|
+
*/
|
|
4739
5028
|
render(c) {}
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
5029
|
+
/**
|
|
5030
|
+
* The declared value of an attribute, unmarshalled through its mapper.
|
|
5031
|
+
*
|
|
5032
|
+
* A `static attributes` name is the configuration tier: read once when the
|
|
5033
|
+
* upgrade starts and answered unchanged for the element's life, so a later
|
|
5034
|
+
* attribute write does not quietly change how the element behaves. An
|
|
5035
|
+
* observed name answers the snapshot while the render is pending, kept
|
|
5036
|
+
* open to attribute writes landing in that window, and the live attribute
|
|
5037
|
+
* afterwards, the property being live by then.
|
|
5038
|
+
*
|
|
5039
|
+
* The snapshot exists rather than a read of the dom because an element may
|
|
5040
|
+
* write its own observed attributes while it renders, whether a reflection
|
|
5041
|
+
* or a value the platform normalizes on the way in, and what the author
|
|
5042
|
+
* declared is what the base applies, not what the render left behind.
|
|
5043
|
+
* @param {string} name
|
|
5044
|
+
*/
|
|
5045
|
+
declared(name) {
|
|
5046
|
+
if (name in this.#frozen) {
|
|
5047
|
+
return this.#frozen[name];
|
|
5048
|
+
}
|
|
5049
|
+
if (this.#pending !== null && name in this.#pending) {
|
|
5050
|
+
return this.#pending[name];
|
|
4746
5051
|
}
|
|
5052
|
+
return this.unmarshal(name, this.getAttribute(name));
|
|
4747
5053
|
}
|
|
5054
|
+
/** Whether the element's render completed: the moment its properties went live. */
|
|
5055
|
+
get rendered() {
|
|
5056
|
+
return this.#parsed;
|
|
5057
|
+
}
|
|
5058
|
+
/**
|
|
5059
|
+
* Projects a property back onto its observed attribute, marshalled through
|
|
5060
|
+
* the mapper the attribute was declared with: the one place a property
|
|
5061
|
+
* reaches its attribute, so a setter never has to know how its own type
|
|
5062
|
+
* serializes.
|
|
5063
|
+
*
|
|
5064
|
+
* A value the attribute already carries is not written at all, so reflecting
|
|
5065
|
+
* what an attribute write just delivered ends there rather than looping, and
|
|
5066
|
+
* only the attribute being written is muted while it happens.
|
|
5067
|
+
* @param {string} attr
|
|
5068
|
+
* @param {any} value
|
|
5069
|
+
*/
|
|
4748
5070
|
reflectTo(attr, value) {
|
|
4749
|
-
|
|
5071
|
+
const marshalled = this.marshal(attr, value);
|
|
5072
|
+
if (marshalled === this.getAttribute(attr)) {
|
|
5073
|
+
return;
|
|
5074
|
+
}
|
|
5075
|
+
this.#reflecting.add(attr);
|
|
4750
5076
|
try {
|
|
4751
|
-
Attributes.set(this, attr,
|
|
5077
|
+
Attributes.set(this, attr, marshalled);
|
|
4752
5078
|
} finally {
|
|
4753
|
-
|
|
5079
|
+
this.#reflecting.delete(attr);
|
|
5080
|
+
}
|
|
5081
|
+
}
|
|
5082
|
+
}
|
|
5083
|
+
|
|
5084
|
+
/**
|
|
5085
|
+
* A flat translations map: dotted keys pointing to a message, or to a plural
|
|
5086
|
+
* leaf carrying a CLDR plural category for each of its forms ('other' is
|
|
5087
|
+
* required, the missing categories of a language fall back to it).
|
|
5088
|
+
*
|
|
5089
|
+
* @typedef {Record<string, string | Record<string, string>>} Messages
|
|
5090
|
+
*/
|
|
5091
|
+
|
|
5092
|
+
/**
|
|
5093
|
+
* The receiver contract of the module functions: bound to a proxy over the
|
|
5094
|
+
* data stack when called from a template, or to a plain object by Localization.of().
|
|
5095
|
+
*
|
|
5096
|
+
* @typedef {{ l10n?: Messages, locale?: string }} Receiver
|
|
5097
|
+
*/
|
|
5098
|
+
|
|
5099
|
+
const PLACEHOLDER = /\{(\w+)\}/g;
|
|
5100
|
+
const POSITIONAL = /\{(\d+)\}/g;
|
|
5101
|
+
/** @param {any} v @returns {v is Record<string, string>} */
|
|
5102
|
+
const isPlainObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
5103
|
+
|
|
5104
|
+
/** every l10n problem is reported once per session: a missing key in a row template must not flood the console */
|
|
5105
|
+
const warned = new Set();
|
|
5106
|
+
const warnOnce = (problem) => {
|
|
5107
|
+
if (!warned.has(problem)) {
|
|
5108
|
+
warned.add(problem);
|
|
5109
|
+
console.warn(`l10n: ${problem}`);
|
|
5110
|
+
}
|
|
5111
|
+
};
|
|
5112
|
+
|
|
5113
|
+
const formatters = new BoundedCache(100);
|
|
5114
|
+
/**
|
|
5115
|
+
* Intl construction is the expensive part of formatting (locale parsing, CLDR
|
|
5116
|
+
* lookups), while formatting on a built instance is near free: a list rendering
|
|
5117
|
+
* a size per row must not rebuild a NumberFormat per cell, so instances are
|
|
5118
|
+
* memoized by constructor, locale and options.
|
|
5119
|
+
*
|
|
5120
|
+
* @param {{ new (locale: string | undefined, options: any): any }} ctor
|
|
5121
|
+
* @param {string | undefined} locale
|
|
5122
|
+
* @param {any} [options]
|
|
5123
|
+
*/
|
|
5124
|
+
const formatter = (ctor, locale, options) => {
|
|
5125
|
+
const key = `${ctor.name}|${locale ?? ''}|${options === undefined ? '' : JSON.stringify(options)}`;
|
|
5126
|
+
return formatters.getOrCompute(key, () => new ctor(locale, options));
|
|
5127
|
+
};
|
|
5128
|
+
|
|
5129
|
+
/**
|
|
5130
|
+
* The translations as a template module: `#l10n:t()` for messages, plus date,
|
|
5131
|
+
* number and bytes formatting in the page's locale. The template form and the
|
|
5132
|
+
* imperative `of()` facade both resolve through the registry's scope, so they
|
|
5133
|
+
* always produce the same result for the same key.
|
|
5134
|
+
*/
|
|
5135
|
+
class Localization {
|
|
5136
|
+
/**
|
|
5137
|
+
* Resolves a message from the translations and interpolates its arguments.
|
|
5138
|
+
* A single plain object argument interpolates named placeholders
|
|
5139
|
+
* ({name}); anything else interpolates positional ones ({0}).
|
|
5140
|
+
* A plural leaf selects its form through Intl.PluralRules over the
|
|
5141
|
+
* numeric {count} named argument.
|
|
5142
|
+
*
|
|
5143
|
+
* @param {string} key
|
|
5144
|
+
* @param {...any} args
|
|
5145
|
+
* @this {Receiver}
|
|
5146
|
+
* @returns {string} the message, or the key itself when the translations do not carry it
|
|
5147
|
+
*/
|
|
5148
|
+
static t(key, ...args) {
|
|
5149
|
+
const messages = this.l10n ?? {};
|
|
5150
|
+
let message = messages[key];
|
|
5151
|
+
if (message === undefined) {
|
|
5152
|
+
warnOnce(`missing message "${key}"`);
|
|
5153
|
+
return key;
|
|
5154
|
+
}
|
|
5155
|
+
if (isPlainObject(message)) {
|
|
5156
|
+
const named = args.length === 1 && isPlainObject(args[0]) ? args[0] : {};
|
|
5157
|
+
if (typeof named.count !== 'number') {
|
|
5158
|
+
warnOnce(`plural message "${key}" needs a numeric {count}`);
|
|
5159
|
+
message = message.other;
|
|
5160
|
+
} else {
|
|
5161
|
+
const locale = this.locale ?? navigator?.language ?? 'en';
|
|
5162
|
+
const category = formatter(Intl.PluralRules, locale).select(named.count);
|
|
5163
|
+
message = message[category] ?? message.other;
|
|
5164
|
+
}
|
|
5165
|
+
}
|
|
5166
|
+
if (message === undefined || typeof message === 'object') {
|
|
5167
|
+
warnOnce(`plural message "${key}" has no "other" form`);
|
|
5168
|
+
return key;
|
|
5169
|
+
}
|
|
5170
|
+
if (args.length === 1 && isPlainObject(args[0])) {
|
|
5171
|
+
const named = args[0];
|
|
5172
|
+
return message.replace(PLACEHOLDER, (literal, name) => {
|
|
5173
|
+
if (!(name in named)) {
|
|
5174
|
+
warnOnce(`message "${key}" wants {${name}}`);
|
|
5175
|
+
return literal;
|
|
5176
|
+
}
|
|
5177
|
+
return String(named[name]);
|
|
5178
|
+
});
|
|
4754
5179
|
}
|
|
5180
|
+
if (args.length > 0) {
|
|
5181
|
+
return message.replace(POSITIONAL, (literal, index) => {
|
|
5182
|
+
const i = Number(index);
|
|
5183
|
+
if (i >= args.length) {
|
|
5184
|
+
warnOnce(`message "${key}" wants {${index}}`);
|
|
5185
|
+
return literal;
|
|
5186
|
+
}
|
|
5187
|
+
return String(args[i]);
|
|
5188
|
+
});
|
|
5189
|
+
}
|
|
5190
|
+
return message;
|
|
5191
|
+
}
|
|
5192
|
+
|
|
5193
|
+
/**
|
|
5194
|
+
* Formats a date through Intl.DateTimeFormat in the receiver's locale.
|
|
5195
|
+
*
|
|
5196
|
+
* @param {Date | number} value
|
|
5197
|
+
* @param {Intl.DateTimeFormatOptions} [options]
|
|
5198
|
+
* @this {Receiver}
|
|
5199
|
+
*/
|
|
5200
|
+
static date(value, options) {
|
|
5201
|
+
return formatter(Intl.DateTimeFormat, this.locale, options).format(value);
|
|
5202
|
+
}
|
|
5203
|
+
|
|
5204
|
+
/**
|
|
5205
|
+
* Formats a number through Intl.NumberFormat in the receiver's locale.
|
|
5206
|
+
*
|
|
5207
|
+
* @param {number} value
|
|
5208
|
+
* @param {Intl.NumberFormatOptions} [options]
|
|
5209
|
+
* @this {Receiver}
|
|
5210
|
+
*/
|
|
5211
|
+
static number(value, options) {
|
|
5212
|
+
return formatter(Intl.NumberFormat, this.locale, options).format(value);
|
|
5213
|
+
}
|
|
5214
|
+
|
|
5215
|
+
/**
|
|
5216
|
+
* Formats a byte size with binary thresholds and literal unit suffixes:
|
|
5217
|
+
* the digits honor the locale, the units are the near-universal KiB/MiB/GiB.
|
|
5218
|
+
* A size exactly on a threshold takes the larger unit: 1024 is 1KiB.
|
|
5219
|
+
*
|
|
5220
|
+
* @param {number} value
|
|
5221
|
+
* @this {Receiver}
|
|
5222
|
+
*/
|
|
5223
|
+
static bytes(value) {
|
|
5224
|
+
const format = formatter(Intl.NumberFormat, this.locale, { maximumFractionDigits: 2 }).format;
|
|
5225
|
+
if (value >= 1024 * 1024 * 1024) {
|
|
5226
|
+
return `${format(value / 1024 / 1024 / 1024)}GiB`;
|
|
5227
|
+
}
|
|
5228
|
+
if (value >= 1024 * 1024) {
|
|
5229
|
+
return `${format(value / 1024 / 1024)}MiB`;
|
|
5230
|
+
}
|
|
5231
|
+
if (value >= 1024) {
|
|
5232
|
+
return `${format(value / 1024)}KiB`;
|
|
5233
|
+
}
|
|
5234
|
+
return `${format(value)}B`;
|
|
5235
|
+
}
|
|
5236
|
+
|
|
5237
|
+
/**
|
|
5238
|
+
* An imperative facade over the module functions, resolving the translations
|
|
5239
|
+
* and the locale from the registry overlays on every call.
|
|
5240
|
+
*
|
|
5241
|
+
* @param {{ locale?: string }} [overrides] an explicit locale, winning over the registry one
|
|
5242
|
+
*/
|
|
5243
|
+
static of(overrides = {}) {
|
|
5244
|
+
//resolved per call, not per facade: a module-scope `Localization.of()` is
|
|
5245
|
+
//bound before the plugin configures. The registry's own evaluator does the
|
|
5246
|
+
//lookup, so this cannot drift from what a template sees
|
|
5247
|
+
const resolve = (prop) => registry.evaluator().resolve(prop);
|
|
5248
|
+
/** @param {any} fn */
|
|
5249
|
+
const bind =
|
|
5250
|
+
(fn) =>
|
|
5251
|
+
(...args) =>
|
|
5252
|
+
fn.apply({ l10n: resolve('l10n'), locale: overrides.locale ?? resolve('locale') }, args);
|
|
5253
|
+
return {
|
|
5254
|
+
t: bind(Localization.t),
|
|
5255
|
+
date: bind(Localization.date),
|
|
5256
|
+
number: bind(Localization.number),
|
|
5257
|
+
bytes: bind(Localization.bytes),
|
|
5258
|
+
};
|
|
4755
5259
|
}
|
|
4756
5260
|
}
|
|
4757
5261
|
|
|
4758
5262
|
exports.Attributes = Attributes;
|
|
5263
|
+
exports.BoundedCache = BoundedCache;
|
|
4759
5264
|
exports.ExpressionEvaluator = ExpressionEvaluator;
|
|
4760
5265
|
exports.Expressions = Expressions;
|
|
4761
5266
|
exports.Fragments = Fragments;
|
|
4762
5267
|
exports.LightSlots = LightSlots;
|
|
5268
|
+
exports.Localization = Localization;
|
|
4763
5269
|
exports.Nodes = Nodes;
|
|
4764
5270
|
exports.ParsedElement = ParsedElement;
|
|
4765
5271
|
exports.Registry = Registry;
|