@hyperfixi/core 3.1.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -36
- package/dist/ast/command-slots.d.ts +1 -1
- package/dist/ast/nodes.d.ts +2 -0
- package/dist/ast-utils/index.cjs +193 -48
- package/dist/ast-utils/index.mjs +193 -48
- package/dist/ast-utils/interchange/types.d.ts +19 -1
- package/dist/behaviors/index.cjs +19 -0
- package/dist/behaviors/index.mjs +19 -0
- package/dist/bundle-generator/index.cjs +36 -17
- package/dist/bundle-generator/index.mjs +36 -17
- package/dist/bundle-generator/parser-templates.d.ts +1 -1
- package/dist/chunks/bridge-CI9SOl8u.js +2 -0
- package/dist/chunks/browser-modular-7CpI5VF2.js +2 -0
- package/dist/chunks/index-BwXDEK0m.js +2 -0
- package/dist/chunks/index-DQi2o8Lk.js +2 -0
- package/dist/chunks/index-uCOQ8Qn0.js +2 -0
- package/dist/commands/animation/transition.d.ts +12 -6
- package/dist/commands/helpers/loop-executor.d.ts +2 -2
- package/dist/commands/helpers/numeric-target-parser.d.ts +1 -1
- package/dist/commands/helpers/variable-access.d.ts +5 -2
- package/dist/commands/helpers/write-target.d.ts +5 -0
- package/dist/commands/index.cjs +938 -425
- package/dist/commands/index.mjs +938 -425
- package/dist/commands/templates/render.d.ts +4 -4
- package/dist/commands/utility/beep.d.ts +0 -3
- package/dist/expressions/index.cjs +7 -13
- package/dist/expressions/index.mjs +7 -13
- package/dist/htmx/i18n-hooks.d.ts +4 -1
- package/dist/hyperfixi-browser-classic-i18n.js +1 -1
- package/dist/hyperfixi-browser.js +1 -1
- package/dist/hyperfixi-classic-i18n.js +1 -1
- package/dist/hyperfixi-hx-v4.js +1 -1
- package/dist/hyperfixi-hx.js +1 -1
- package/dist/hyperfixi-hybrid-complete.js +1 -1
- package/dist/hyperfixi-hybrid-hx.js +1 -1
- package/dist/hyperfixi-multilingual.js +1 -1
- package/dist/hyperfixi.js +1 -1
- package/dist/hyperfixi.mjs +1 -1
- package/dist/index.cjs +1149 -485
- package/dist/index.min.js +1 -1
- package/dist/index.mjs +1149 -485
- package/dist/lib/swap-executor.d.ts +1 -1
- package/dist/lokascript-browser-classic-i18n.js +1 -1
- package/dist/lokascript-browser.js +1 -1
- package/dist/lokascript-hybrid-complete.js +1 -1
- package/dist/lokascript-hybrid-hx.js +1 -1
- package/dist/lokascript-multilingual.js +1 -1
- package/dist/metadata.cjs +9 -9
- package/dist/metadata.d.ts +1 -1
- package/dist/metadata.mjs +9 -9
- package/dist/parser/command-grammar.d.ts +1 -0
- package/dist/parser/full-parser.cjs +396 -153
- package/dist/parser/full-parser.mjs +396 -153
- package/dist/parser/helpers/parsing-helpers.d.ts +3 -0
- package/dist/parser/hybrid/index.cjs +3 -0
- package/dist/parser/hybrid/index.mjs +3 -0
- package/dist/parser/hybrid/parser-core.cjs +3 -0
- package/dist/parser/hybrid/parser-core.mjs +3 -0
- package/dist/parser/hybrid-parser.cjs +3 -0
- package/dist/parser/hybrid-parser.mjs +3 -0
- package/dist/parser/parser-types.d.ts +2 -0
- package/dist/parser/parser.d.ts +9 -0
- package/dist/parser/pratt-parser.d.ts +2 -0
- package/dist/registry/index.cjs +535 -210
- package/dist/registry/index.mjs +535 -210
- package/dist/utils/beep.d.ts +8 -0
- package/dist/version.d.ts +1 -1
- package/package.json +21 -18
- package/vocab/htmx/ar.js +22 -20
- package/vocab/htmx/bn.js +21 -5
- package/vocab/htmx/de.js +33 -10
- package/vocab/htmx/en.js +4 -1
- package/vocab/htmx/es.js +28 -9
- package/vocab/htmx/fr.js +28 -8
- package/vocab/htmx/he.js +22 -1
- package/vocab/htmx/hi.js +28 -7
- package/vocab/htmx/id.js +29 -10
- package/vocab/htmx/it.js +26 -5
- package/vocab/htmx/ja.js +36 -11
- package/vocab/htmx/ko.js +27 -6
- package/vocab/htmx/ms.js +24 -1
- package/vocab/htmx/pl.js +27 -7
- package/vocab/htmx/pt.js +30 -9
- package/vocab/htmx/qu.js +32 -9
- package/vocab/htmx/ru.js +28 -6
- package/vocab/htmx/sw.js +28 -8
- package/vocab/htmx/th.js +19 -1
- package/vocab/htmx/tl.js +24 -1
- package/vocab/htmx/tr.js +34 -8
- package/vocab/htmx/uk.js +28 -6
- package/vocab/htmx/vi.js +27 -19
- package/vocab/htmx/zh.js +28 -9
- package/dist/chunks/bridge-D7qrHhV0.js +0 -2
- package/dist/chunks/browser-modular-C4LFxfoS.js +0 -2
- package/dist/chunks/index-C7lqD4mo.js +0 -2
- package/dist/chunks/index-CAPhqA6k.js +0 -2
- package/dist/chunks/index-CLdRyZaP.js +0 -2
package/dist/index.cjs
CHANGED
|
@@ -2075,6 +2075,39 @@ function makeTypeCheckHandler(negated) {
|
|
|
2075
2075
|
};
|
|
2076
2076
|
};
|
|
2077
2077
|
}
|
|
2078
|
+
const OF_OPERAND_BP = 70;
|
|
2079
|
+
function parseOfOperand(ctx, minBp = OF_OPERAND_BP) {
|
|
2080
|
+
let right = ctx.parseExpr(minBp);
|
|
2081
|
+
if (ctx.peek()?.value === 'in') {
|
|
2082
|
+
const inToken = ctx.advance();
|
|
2083
|
+
const scope = ctx.parseExpr(minBp);
|
|
2084
|
+
right = {
|
|
2085
|
+
type: 'binaryExpression',
|
|
2086
|
+
operator: 'in',
|
|
2087
|
+
left: right,
|
|
2088
|
+
right: scope,
|
|
2089
|
+
start: right.start,
|
|
2090
|
+
end: scope.end ?? inToken.end,
|
|
2091
|
+
line: right.line ?? inToken.line,
|
|
2092
|
+
column: right.column ?? inToken.column,
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
2095
|
+
return right;
|
|
2096
|
+
}
|
|
2097
|
+
const THE_OF_TARGET_BP = OF_OPERAND_BP + 1;
|
|
2098
|
+
function ofHandler(left, token, ctx) {
|
|
2099
|
+
const right = parseOfOperand(ctx);
|
|
2100
|
+
return {
|
|
2101
|
+
type: 'binaryExpression',
|
|
2102
|
+
operator: token.value,
|
|
2103
|
+
left,
|
|
2104
|
+
right,
|
|
2105
|
+
start: left.start,
|
|
2106
|
+
end: right.end ?? token.end,
|
|
2107
|
+
line: left.line ?? token.line,
|
|
2108
|
+
column: left.column ?? token.column,
|
|
2109
|
+
};
|
|
2110
|
+
}
|
|
2078
2111
|
const PARSER_COMPARISON_FRAGMENT = new Map([
|
|
2079
2112
|
['===', leftAssoc(30)],
|
|
2080
2113
|
['!==', leftAssoc(30)],
|
|
@@ -2119,7 +2152,7 @@ const PARSER_COMPARISON_FRAGMENT = new Map([
|
|
|
2119
2152
|
['is greater than or equal to', leftAssoc(30)],
|
|
2120
2153
|
['is less than or equal to', leftAssoc(30)],
|
|
2121
2154
|
['in', leftAssoc(30)],
|
|
2122
|
-
['of',
|
|
2155
|
+
['of', { infix: { bp: [90, OF_OPERAND_BP], handler: ofHandler } }],
|
|
2123
2156
|
['really', leftAssoc(30)],
|
|
2124
2157
|
[
|
|
2125
2158
|
'exists',
|
|
@@ -2823,6 +2856,48 @@ function parseMaybeNamedArgument(ctx) {
|
|
|
2823
2856
|
const value = ctx.parseExpression();
|
|
2824
2857
|
return name !== undefined ? { name, value } : { value };
|
|
2825
2858
|
}
|
|
2859
|
+
function isNakedNamedArgStart(ctx) {
|
|
2860
|
+
if (ctx.check('{'))
|
|
2861
|
+
return false;
|
|
2862
|
+
if (!ctx.checkIdentifierLike())
|
|
2863
|
+
return false;
|
|
2864
|
+
const next = ctx.peekAt(1);
|
|
2865
|
+
return next !== null && next.value === ':';
|
|
2866
|
+
}
|
|
2867
|
+
function parseNakedNamedArgs(ctx, parseValue, command) {
|
|
2868
|
+
const properties = [];
|
|
2869
|
+
const startPos = ctx.getPosition();
|
|
2870
|
+
do {
|
|
2871
|
+
if (!ctx.checkIdentifierLike())
|
|
2872
|
+
break;
|
|
2873
|
+
const keyToken = ctx.advance();
|
|
2874
|
+
const key = {
|
|
2875
|
+
type: 'identifier',
|
|
2876
|
+
name: keyToken.value,
|
|
2877
|
+
start: keyToken.start,
|
|
2878
|
+
end: keyToken.end,
|
|
2879
|
+
line: keyToken.line,
|
|
2880
|
+
column: keyToken.column,
|
|
2881
|
+
};
|
|
2882
|
+
ctx.consume(':', `Expected ':' after property name in ${command} named arguments`);
|
|
2883
|
+
const value = parseValue();
|
|
2884
|
+
if (value) {
|
|
2885
|
+
properties.push({ key, value });
|
|
2886
|
+
}
|
|
2887
|
+
} while (ctx.match(',') && !ctx.isAtEnd());
|
|
2888
|
+
const endPos = ctx.getPosition();
|
|
2889
|
+
return {
|
|
2890
|
+
type: 'objectLiteral',
|
|
2891
|
+
properties,
|
|
2892
|
+
start: startPos.start,
|
|
2893
|
+
end: endPos.end,
|
|
2894
|
+
line: startPos.line,
|
|
2895
|
+
column: startPos.column,
|
|
2896
|
+
};
|
|
2897
|
+
}
|
|
2898
|
+
function isGluedBeepBang(beep, bang) {
|
|
2899
|
+
return beep?.value === 'beep' && bang?.value === '!' && bang.start === beep.end;
|
|
2900
|
+
}
|
|
2826
2901
|
|
|
2827
2902
|
class CommandNodeBuilder {
|
|
2828
2903
|
constructor(name) {
|
|
@@ -3024,6 +3099,8 @@ function parseTriggerCommand(ctx, identifierNode) {
|
|
|
3024
3099
|
const modifiers = {};
|
|
3025
3100
|
while (!isCommandBoundary(ctx)) {
|
|
3026
3101
|
if (ctx.check(KEYWORDS.ON) || ctx.check(KEYWORDS.TO)) {
|
|
3102
|
+
if (modifiers['on'])
|
|
3103
|
+
break;
|
|
3027
3104
|
ctx.advance();
|
|
3028
3105
|
const target = parseOneArgument(ctx, [KEYWORDS.WITH]);
|
|
3029
3106
|
if (target)
|
|
@@ -3164,6 +3241,9 @@ function parseRepeatCommand(ctx, commandToken) {
|
|
|
3164
3241
|
ctx.advance();
|
|
3165
3242
|
loopType = KEYWORDS.FOREVER;
|
|
3166
3243
|
}
|
|
3244
|
+
else if (ctx.checkIsCommand() || ctx.isCommand(ctx.peek().value)) {
|
|
3245
|
+
loopType = KEYWORDS.FOREVER;
|
|
3246
|
+
}
|
|
3167
3247
|
else {
|
|
3168
3248
|
times = ctx.parseExpression();
|
|
3169
3249
|
if (ctx.check(KEYWORDS.TIMES)) {
|
|
@@ -3206,10 +3286,12 @@ function parseRepeatCommand(ctx, commandToken) {
|
|
|
3206
3286
|
loopType = result.terminator;
|
|
3207
3287
|
ctx.advance();
|
|
3208
3288
|
condition = ctx.parseExpression();
|
|
3209
|
-
if (
|
|
3289
|
+
if (ctx.check('end')) {
|
|
3290
|
+
ctx.advance();
|
|
3291
|
+
}
|
|
3292
|
+
else if (!ctx.atEndOfInput()) {
|
|
3210
3293
|
throw new Error('Expected "end" to close repeat block');
|
|
3211
3294
|
}
|
|
3212
|
-
ctx.advance();
|
|
3213
3295
|
}
|
|
3214
3296
|
}
|
|
3215
3297
|
else {
|
|
@@ -3455,7 +3537,7 @@ function parseIfCommand(ctx, commandToken) {
|
|
|
3455
3537
|
}));
|
|
3456
3538
|
}
|
|
3457
3539
|
}
|
|
3458
|
-
if (!consumedElseIf && !ctx.
|
|
3540
|
+
if (!consumedElseIf && !ctx.atEndOfInput()) {
|
|
3459
3541
|
ctx.consume(KEYWORDS.END, "Expected 'end' after if block");
|
|
3460
3542
|
}
|
|
3461
3543
|
}
|
|
@@ -3480,7 +3562,6 @@ function parseIfCommand(ctx, commandToken) {
|
|
|
3480
3562
|
.build();
|
|
3481
3563
|
}
|
|
3482
3564
|
function parseForCommand(ctx, commandToken) {
|
|
3483
|
-
const args = [];
|
|
3484
3565
|
let variable = null;
|
|
3485
3566
|
let collection = null;
|
|
3486
3567
|
if (ctx.check(KEYWORDS.EACH)) {
|
|
@@ -3523,31 +3604,22 @@ function parseForCommand(ctx, commandToken) {
|
|
|
3523
3604
|
}
|
|
3524
3605
|
}
|
|
3525
3606
|
const commands = ctx.parseCommandListUntilEnd('for');
|
|
3526
|
-
|
|
3527
|
-
type: 'identifier',
|
|
3528
|
-
name: 'for',
|
|
3529
|
-
start: commandToken.start,
|
|
3530
|
-
end: commandToken.end,
|
|
3531
|
-
line: commandToken.line,
|
|
3532
|
-
column: commandToken.column,
|
|
3533
|
-
});
|
|
3534
|
-
const forPos = {
|
|
3607
|
+
const pos = {
|
|
3535
3608
|
start: commandToken.start,
|
|
3536
3609
|
end: commandToken.end,
|
|
3537
3610
|
line: commandToken.line,
|
|
3538
3611
|
column: commandToken.column,
|
|
3539
3612
|
};
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
}
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
.withArgs(...args)
|
|
3613
|
+
const modifiers = {
|
|
3614
|
+
loopType: toLegacyExpression(createStringLiteral(KEYWORDS.FOR, pos)),
|
|
3615
|
+
for: toLegacyExpression(createStringLiteral(variable, pos)),
|
|
3616
|
+
in: collection,
|
|
3617
|
+
};
|
|
3618
|
+
if (indexVariable)
|
|
3619
|
+
modifiers['index'] = toLegacyExpression(createStringLiteral(indexVariable, pos));
|
|
3620
|
+
return CommandNodeBuilder.from({ ...commandToken, value: 'repeat' })
|
|
3621
|
+
.withArgs(createBlock(commands, { ...pos, end: pos.end || 0 }))
|
|
3622
|
+
.withModifiers(modifiers)
|
|
3551
3623
|
.endingAt(ctx.getPosition())
|
|
3552
3624
|
.build();
|
|
3553
3625
|
}
|
|
@@ -3603,86 +3675,159 @@ function parseMeasureCommand(ctx, identifierNode) {
|
|
|
3603
3675
|
}
|
|
3604
3676
|
return builder.build();
|
|
3605
3677
|
}
|
|
3606
|
-
const
|
|
3678
|
+
const CONTEXT_REFS = new Set(['me', 'it', 'you', 'result']);
|
|
3607
3679
|
function parseTransitionCommand(ctx, commandToken) {
|
|
3608
|
-
const
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
const
|
|
3613
|
-
const
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
if (
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
target = parsed.object;
|
|
3624
|
-
property = {
|
|
3625
|
-
type: 'string',
|
|
3626
|
-
value: parsed.property.name,
|
|
3627
|
-
start: leadToken.start || 0,
|
|
3628
|
-
end: ctx.getPosition().end,
|
|
3629
|
-
line: leadToken.line,
|
|
3630
|
-
column: leadToken.column,
|
|
3631
|
-
};
|
|
3680
|
+
const pairs = [];
|
|
3681
|
+
do {
|
|
3682
|
+
pairs.push(parseTransitionPair(ctx));
|
|
3683
|
+
} while (startsAnotherPair(ctx));
|
|
3684
|
+
const [first, ...rest] = pairs;
|
|
3685
|
+
const args = first.owner ? [first.owner, first.property] : [first.property];
|
|
3686
|
+
const modifiers = { to: first.to };
|
|
3687
|
+
if (first.from)
|
|
3688
|
+
modifiers['from'] = first.from;
|
|
3689
|
+
if (rest.length > 0)
|
|
3690
|
+
modifiers['pairs'] = pairsNode(rest);
|
|
3691
|
+
for (let i = 0; i < 3 && !ctx.isAtEnd(); i++) {
|
|
3692
|
+
if (ctx.check('over') && !modifiers['over']) {
|
|
3693
|
+
ctx.advance();
|
|
3694
|
+
modifiers['over'] = ctx.parseExpression();
|
|
3632
3695
|
}
|
|
3633
|
-
else {
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
ctx.advance();
|
|
3696
|
+
else if (ctx.check('using') && !modifiers['using']) {
|
|
3697
|
+
ctx.advance();
|
|
3698
|
+
modifiers['using'] = ctx.parseExpression();
|
|
3637
3699
|
}
|
|
3638
|
-
|
|
3639
|
-
const firstToken = ctx.peek();
|
|
3640
|
-
if (!property && (isIdentifierLike(firstToken) || firstToken.value === '*')) {
|
|
3641
|
-
let propertyValue = '';
|
|
3642
|
-
if (ctx.check('*')) {
|
|
3643
|
-
propertyValue = '*';
|
|
3700
|
+
else if (ctx.check(KEYWORDS.WITH) && !modifiers['with']) {
|
|
3644
3701
|
ctx.advance();
|
|
3702
|
+
modifiers['with'] = ctx.parsePrimary();
|
|
3645
3703
|
}
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
propertyValue += hyphenatedName;
|
|
3649
|
-
property = {
|
|
3650
|
-
type: 'string',
|
|
3651
|
-
value: propertyValue,
|
|
3652
|
-
start: firstToken.start || 0,
|
|
3653
|
-
end: ctx.getPosition().end,
|
|
3654
|
-
line: firstToken.line,
|
|
3655
|
-
column: firstToken.column,
|
|
3656
|
-
};
|
|
3704
|
+
else {
|
|
3705
|
+
break;
|
|
3657
3706
|
}
|
|
3658
3707
|
}
|
|
3659
|
-
|
|
3660
|
-
|
|
3708
|
+
return CommandNodeBuilder.from(commandToken)
|
|
3709
|
+
.withArgs(...args)
|
|
3710
|
+
.withModifiers(modifiers)
|
|
3711
|
+
.endingAt(ctx.getPosition())
|
|
3712
|
+
.build();
|
|
3713
|
+
}
|
|
3714
|
+
function parseTransitionPair(ctx) {
|
|
3715
|
+
const { owner, property } = parseTransitionProperty(ctx);
|
|
3716
|
+
let from = null;
|
|
3717
|
+
if (ctx.check('from')) {
|
|
3718
|
+
ctx.advance();
|
|
3719
|
+
from = ctx.parseExpression();
|
|
3661
3720
|
}
|
|
3662
|
-
if (target)
|
|
3663
|
-
args.push(target);
|
|
3664
|
-
args.push(property);
|
|
3665
3721
|
if (!ctx.check(KEYWORDS.TO)) {
|
|
3666
3722
|
throw new Error('Expected "to" keyword after property in transition command');
|
|
3667
3723
|
}
|
|
3668
3724
|
ctx.advance();
|
|
3669
|
-
const
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3725
|
+
const to = ctx.parseExpression();
|
|
3726
|
+
return { owner, property, from, to };
|
|
3727
|
+
}
|
|
3728
|
+
function parseTransitionProperty(ctx) {
|
|
3729
|
+
const lead = ctx.peek();
|
|
3730
|
+
if (!lead || isKeyword(lead, [KEYWORDS.TO, 'from'])) {
|
|
3731
|
+
throw new Error('Transition command requires a CSS property');
|
|
3675
3732
|
}
|
|
3676
|
-
|
|
3733
|
+
const expr = ctx.parseExpression();
|
|
3734
|
+
const split = splitStyleExpression(expr);
|
|
3735
|
+
if (split)
|
|
3736
|
+
return { owner: split.owner, property: propertyNode(split.name, lead, ctx) };
|
|
3737
|
+
let name = '';
|
|
3738
|
+
if (ctx.check('*')) {
|
|
3739
|
+
name = '*';
|
|
3677
3740
|
ctx.advance();
|
|
3678
|
-
const timingFunction = ctx.parsePrimary();
|
|
3679
|
-
modifiers['with'] = timingFunction;
|
|
3680
3741
|
}
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3742
|
+
const hyphenated = parseHyphenatedName(ctx);
|
|
3743
|
+
if (!hyphenated)
|
|
3744
|
+
throw new Error('Transition command requires a CSS property');
|
|
3745
|
+
return { owner: expr, property: propertyNode(name + hyphenated, lead, ctx) };
|
|
3746
|
+
}
|
|
3747
|
+
function splitStyleExpression(node) {
|
|
3748
|
+
const n = node;
|
|
3749
|
+
switch (n.type) {
|
|
3750
|
+
case 'selector':
|
|
3751
|
+
return typeof n.value === 'string' && n.value.startsWith('*')
|
|
3752
|
+
? { owner: null, name: n.value }
|
|
3753
|
+
: null;
|
|
3754
|
+
case 'identifier':
|
|
3755
|
+
return typeof n.name === 'string' && !CONTEXT_REFS.has(n.name)
|
|
3756
|
+
? { owner: null, name: n.name }
|
|
3757
|
+
: null;
|
|
3758
|
+
case 'possessiveExpression':
|
|
3759
|
+
case 'memberExpression':
|
|
3760
|
+
return !n.computed && n.object && typeof n.property?.name === 'string'
|
|
3761
|
+
? { owner: n.object, name: n.property.name }
|
|
3762
|
+
: null;
|
|
3763
|
+
case 'binaryExpression': {
|
|
3764
|
+
if (n.operator !== 'of' || !n.left || !n.right)
|
|
3765
|
+
return null;
|
|
3766
|
+
const inner = splitStyleExpression(n.left);
|
|
3767
|
+
return inner && !inner.owner ? { owner: n.right, name: inner.name } : null;
|
|
3768
|
+
}
|
|
3769
|
+
case 'propertyOfExpression':
|
|
3770
|
+
return n.target && typeof n.property?.name === 'string'
|
|
3771
|
+
? { owner: n.target, name: n.property.name }
|
|
3772
|
+
: null;
|
|
3773
|
+
default:
|
|
3774
|
+
return null;
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3777
|
+
function propertyNode(name, lead, ctx) {
|
|
3778
|
+
return {
|
|
3779
|
+
type: 'string',
|
|
3780
|
+
value: name,
|
|
3781
|
+
start: lead.start || 0,
|
|
3782
|
+
end: ctx.getPosition().end,
|
|
3783
|
+
line: lead.line,
|
|
3784
|
+
column: lead.column,
|
|
3785
|
+
};
|
|
3786
|
+
}
|
|
3787
|
+
function startsAnotherPair(ctx) {
|
|
3788
|
+
if (ctx.isAtEnd() || isComment(ctx.peek()))
|
|
3789
|
+
return false;
|
|
3790
|
+
if (isKeyword(ctx.peek(), ['over', 'using', KEYWORDS.WITH]))
|
|
3791
|
+
return false;
|
|
3792
|
+
return !isCommandBoundary(ctx, ['catch', 'finally', 'on']);
|
|
3793
|
+
}
|
|
3794
|
+
function pairsNode(pairs) {
|
|
3795
|
+
const field = (name, value) => ({
|
|
3796
|
+
key: {
|
|
3797
|
+
type: 'identifier',
|
|
3798
|
+
name,
|
|
3799
|
+
start: value.start,
|
|
3800
|
+
end: value.end,
|
|
3801
|
+
line: value.line,
|
|
3802
|
+
column: value.column,
|
|
3803
|
+
},
|
|
3804
|
+
value,
|
|
3805
|
+
});
|
|
3806
|
+
const elements = pairs.map(pair => {
|
|
3807
|
+
const properties = [field('property', pair.property), field('to', pair.to)];
|
|
3808
|
+
if (pair.owner)
|
|
3809
|
+
properties.push(field('owner', pair.owner));
|
|
3810
|
+
if (pair.from)
|
|
3811
|
+
properties.push(field('from', pair.from));
|
|
3812
|
+
return {
|
|
3813
|
+
type: 'objectLiteral',
|
|
3814
|
+
properties,
|
|
3815
|
+
start: (pair.owner ?? pair.property).start,
|
|
3816
|
+
end: pair.to.end,
|
|
3817
|
+
line: (pair.owner ?? pair.property).line,
|
|
3818
|
+
column: (pair.owner ?? pair.property).column,
|
|
3819
|
+
};
|
|
3820
|
+
});
|
|
3821
|
+
const firstEl = elements[0];
|
|
3822
|
+
const lastEl = elements[elements.length - 1];
|
|
3823
|
+
return {
|
|
3824
|
+
type: 'arrayLiteral',
|
|
3825
|
+
elements,
|
|
3826
|
+
start: firstEl.start,
|
|
3827
|
+
end: lastEl.end,
|
|
3828
|
+
line: firstEl.line,
|
|
3829
|
+
column: firstEl.column,
|
|
3830
|
+
};
|
|
3686
3831
|
}
|
|
3687
3832
|
function parseStartCommand(ctx, identifierNode) {
|
|
3688
3833
|
if (!ctx.match('view')) {
|
|
@@ -3802,6 +3947,15 @@ function parseToggleCommand(ctx, identifierNode) {
|
|
|
3802
3947
|
args.push(classArg.expression);
|
|
3803
3948
|
modifiers['as'] = literalModifier(mode);
|
|
3804
3949
|
}
|
|
3950
|
+
else if (classArg?.type === 'binaryExpression' &&
|
|
3951
|
+
classArg.operator === 'of' &&
|
|
3952
|
+
classArg.left?.type === 'selector' &&
|
|
3953
|
+
typeof classArg.left.value === 'string' &&
|
|
3954
|
+
classArg.left.value.startsWith('*') &&
|
|
3955
|
+
classArg.right) {
|
|
3956
|
+
args.push(classArg.left);
|
|
3957
|
+
modifiers['on'] = classArg.right;
|
|
3958
|
+
}
|
|
3805
3959
|
else if (classArg) {
|
|
3806
3960
|
args.push(classArg);
|
|
3807
3961
|
}
|
|
@@ -3979,11 +4133,17 @@ function parseSwapCommand(ctx, identifierNode) {
|
|
|
3979
4133
|
const targetExpr = isDelete ? ctx.parseExpression() : parseOneArgument(ctx, [KEYWORDS.WITH]);
|
|
3980
4134
|
if (targetExpr)
|
|
3981
4135
|
args.push(targetExpr);
|
|
3982
|
-
|
|
4136
|
+
const isMorph = identifierNode.name === 'morph';
|
|
4137
|
+
if (!isDelete &&
|
|
4138
|
+
(consumeOptionalKeyword(ctx, KEYWORDS.WITH) ||
|
|
4139
|
+
(isMorph && consumeOptionalKeyword(ctx, KEYWORDS.TO)))) {
|
|
3983
4140
|
const contentExpr = parseOneArgument(ctx, ['using']);
|
|
3984
4141
|
if (contentExpr)
|
|
3985
4142
|
modifiers['with'] = contentExpr;
|
|
3986
4143
|
}
|
|
4144
|
+
if (isMorph && modifiers['with'] === undefined) {
|
|
4145
|
+
ctx.addError("Expected 'to' and the content to morph into");
|
|
4146
|
+
}
|
|
3987
4147
|
return CommandNodeBuilder.fromIdentifier(identifierNode)
|
|
3988
4148
|
.withArgs(...args)
|
|
3989
4149
|
.withModifiers({ ...modifiers, ...parseViewTransitionTail(ctx) })
|
|
@@ -4744,6 +4904,8 @@ function atArgumentBoundary(ctx, grammar) {
|
|
|
4744
4904
|
const next = ctx.peek();
|
|
4745
4905
|
if (ctx.checkIsCommand() && ctx.peekAt(1)?.value === '(')
|
|
4746
4906
|
return false;
|
|
4907
|
+
if (isGluedBeepBang(next, ctx.peekAt(1)))
|
|
4908
|
+
return false;
|
|
4747
4909
|
if (isCommandBoundary(ctx, ['catch', 'finally', 'on']))
|
|
4748
4910
|
return true;
|
|
4749
4911
|
return isKeyword(next, [...grammar.markers]);
|
|
@@ -4775,10 +4937,13 @@ function parseDeclaredCommand(ctx, commandToken, name, grammar) {
|
|
|
4775
4937
|
}
|
|
4776
4938
|
}
|
|
4777
4939
|
const commaList = new Set(grammar.commaList ?? []);
|
|
4940
|
+
const namedArgs = new Set(grammar.namedArgs ?? []);
|
|
4778
4941
|
while (!ctx.isAtEnd() && isKeyword(ctx.peek(), [...grammar.markers])) {
|
|
4779
4942
|
const keyword = ctx.advance().value;
|
|
4780
4943
|
const key = ctx.resolveKeyword(keyword).toLowerCase();
|
|
4781
|
-
let value =
|
|
4944
|
+
let value = namedArgs.has(key) && isNakedNamedArgStart(ctx)
|
|
4945
|
+
? parseNakedNamedArgs(ctx, () => ctx.parseExpression(), name)
|
|
4946
|
+
: ctx.parseExpression();
|
|
4782
4947
|
if (value && commaList.has(key) && ctx.check(',')) {
|
|
4783
4948
|
const elements = [value];
|
|
4784
4949
|
while (ctx.match(',')) {
|
|
@@ -4943,8 +5108,8 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
4943
5108
|
}
|
|
4944
5109
|
if (ctx.check('with') && !modifiers['with']) {
|
|
4945
5110
|
ctx.advance();
|
|
4946
|
-
if (
|
|
4947
|
-
modifiers['with'] =
|
|
5111
|
+
if (isNakedNamedArgStart(ctx)) {
|
|
5112
|
+
modifiers['with'] = parseNakedNamedArgs(ctx, () => ctx.parsePrimary(), 'fetch');
|
|
4948
5113
|
}
|
|
4949
5114
|
else {
|
|
4950
5115
|
modifiers['with'] = ctx.parsePrimary();
|
|
@@ -4979,45 +5144,6 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
4979
5144
|
}
|
|
4980
5145
|
return builder.build();
|
|
4981
5146
|
}
|
|
4982
|
-
function isFetchNakedNamedArgStart(ctx) {
|
|
4983
|
-
if (ctx.check('{'))
|
|
4984
|
-
return false;
|
|
4985
|
-
if (!ctx.checkIdentifierLike())
|
|
4986
|
-
return false;
|
|
4987
|
-
const next = ctx.peekAt(1);
|
|
4988
|
-
return next !== null && next.value === ':';
|
|
4989
|
-
}
|
|
4990
|
-
function parseFetchNakedNamedArgs(ctx) {
|
|
4991
|
-
const properties = [];
|
|
4992
|
-
const startPos = ctx.getPosition();
|
|
4993
|
-
do {
|
|
4994
|
-
if (!ctx.checkIdentifierLike())
|
|
4995
|
-
break;
|
|
4996
|
-
const keyToken = ctx.advance();
|
|
4997
|
-
const key = {
|
|
4998
|
-
type: 'identifier',
|
|
4999
|
-
name: keyToken.value,
|
|
5000
|
-
start: keyToken.start,
|
|
5001
|
-
end: keyToken.end,
|
|
5002
|
-
line: keyToken.line,
|
|
5003
|
-
column: keyToken.column,
|
|
5004
|
-
};
|
|
5005
|
-
ctx.consume(':', "Expected ':' after property name in fetch named arguments");
|
|
5006
|
-
const value = ctx.parsePrimary();
|
|
5007
|
-
if (value) {
|
|
5008
|
-
properties.push({ key, value });
|
|
5009
|
-
}
|
|
5010
|
-
} while (ctx.match(',') && !ctx.isAtEnd());
|
|
5011
|
-
const endPos = ctx.getPosition();
|
|
5012
|
-
return {
|
|
5013
|
-
type: 'objectLiteral',
|
|
5014
|
-
properties,
|
|
5015
|
-
start: startPos.start,
|
|
5016
|
-
end: endPos.end,
|
|
5017
|
-
line: startPos.line,
|
|
5018
|
-
column: startPos.column,
|
|
5019
|
-
};
|
|
5020
|
-
}
|
|
5021
5147
|
function findJsEndBoundary(ctx, startPos) {
|
|
5022
5148
|
const input = ctx.getInputSlice(startPos);
|
|
5023
5149
|
if (!input) {
|
|
@@ -5137,6 +5263,7 @@ function parseTellCommand(ctx, identifierNode) {
|
|
|
5137
5263
|
const commands = [];
|
|
5138
5264
|
while (!ctx.isAtEnd()) {
|
|
5139
5265
|
if (ctx.checkIsCommand()) {
|
|
5266
|
+
const word = ctx.peek().value;
|
|
5140
5267
|
try {
|
|
5141
5268
|
ctx.advance();
|
|
5142
5269
|
const cmd = ctx.parseCommand();
|
|
@@ -5147,7 +5274,8 @@ function parseTellCommand(ctx, identifierNode) {
|
|
|
5147
5274
|
break;
|
|
5148
5275
|
}
|
|
5149
5276
|
}
|
|
5150
|
-
catch {
|
|
5277
|
+
catch (error) {
|
|
5278
|
+
ctx.addError(`'${word}' in a tell body failed to parse: ${error instanceof Error ? error.message : String(error)}`);
|
|
5151
5279
|
break;
|
|
5152
5280
|
}
|
|
5153
5281
|
if (ctx.match(KEYWORDS.AND) || ctx.match(KEYWORDS.THEN)) {
|
|
@@ -5296,7 +5424,6 @@ const ONE_EXPR = (syntax) => ({
|
|
|
5296
5424
|
markers: [],
|
|
5297
5425
|
syntax,
|
|
5298
5426
|
});
|
|
5299
|
-
const BEEP = ONE_EXPR('beep! [<expression>, \u2026]');
|
|
5300
5427
|
const COMMAND_GRAMMAR = {
|
|
5301
5428
|
append: { positional: 'expression', markers: ['to'], syntax: 'append <value> [to <target>]' },
|
|
5302
5429
|
prepend: { positional: 'expression', markers: ['to'], syntax: 'prepend <value> [to <target>]' },
|
|
@@ -5307,8 +5434,7 @@ const COMMAND_GRAMMAR = {
|
|
|
5307
5434
|
syntax: 'make (a|an) <type> [from <args>] [called <name>]',
|
|
5308
5435
|
},
|
|
5309
5436
|
throw: { positional: 'primary', markers: [], syntax: 'throw <error>' },
|
|
5310
|
-
'beep!'
|
|
5311
|
-
beep: BEEP,
|
|
5437
|
+
beep: ONE_EXPR('beep! [<expression>, \u2026]'),
|
|
5312
5438
|
blur: ONE_EXPR('blur [<target>]'),
|
|
5313
5439
|
break: NONE,
|
|
5314
5440
|
breakpoint: NONE,
|
|
@@ -5332,7 +5458,8 @@ const COMMAND_GRAMMAR = {
|
|
|
5332
5458
|
render: {
|
|
5333
5459
|
positional: 'expression',
|
|
5334
5460
|
markers: ['with'],
|
|
5335
|
-
|
|
5461
|
+
namedArgs: ['with'],
|
|
5462
|
+
syntax: 'render <template> [with <name>: <value>, \u2026]',
|
|
5336
5463
|
},
|
|
5337
5464
|
reset: ONE_EXPR('reset [<form>]'),
|
|
5338
5465
|
return: ONE_EXPR('return [<value>]'),
|
|
@@ -5687,7 +5814,10 @@ class Parser {
|
|
|
5687
5814
|
const firstToken = this.peek();
|
|
5688
5815
|
const prefixEntry = Parser.PRATT_TABLE.get(firstToken.value);
|
|
5689
5816
|
let left;
|
|
5690
|
-
if (
|
|
5817
|
+
if (isGluedBeepBang(firstToken, this.tokens[this.current + 1])) {
|
|
5818
|
+
left = this.parseBeepExpression();
|
|
5819
|
+
}
|
|
5820
|
+
else if (prefixEntry?.prefix) {
|
|
5691
5821
|
const token = this.advance();
|
|
5692
5822
|
if (this.isAtEnd()) {
|
|
5693
5823
|
this.addError(`Expected expression after '${token.value}' operator`);
|
|
@@ -5707,6 +5837,9 @@ class Parser {
|
|
|
5707
5837
|
continue;
|
|
5708
5838
|
}
|
|
5709
5839
|
}
|
|
5840
|
+
if (nextToken.value === '*' && this.isSpacedStyleRef(this.current)) {
|
|
5841
|
+
break;
|
|
5842
|
+
}
|
|
5710
5843
|
const infixEntry = Parser.PRATT_TABLE.get(nextToken.value);
|
|
5711
5844
|
if (!infixEntry?.infix) {
|
|
5712
5845
|
if (STOP_TOKENS.has(nextToken.value) || STOP_DELIMITERS.has(nextToken.value)) {
|
|
@@ -5768,6 +5901,35 @@ class Parser {
|
|
|
5768
5901
|
this.prattCache.set(cacheKey, { node: left, endPos: this.current });
|
|
5769
5902
|
return left;
|
|
5770
5903
|
}
|
|
5904
|
+
parseBeepExpression() {
|
|
5905
|
+
const beepToken = this.advance();
|
|
5906
|
+
this.advance();
|
|
5907
|
+
if (this.isAtEnd()) {
|
|
5908
|
+
this.addError("Expected expression after 'beep!'");
|
|
5909
|
+
return this.createErrorNode();
|
|
5910
|
+
}
|
|
5911
|
+
const operand = this.parseExpressionPratt(Parser.BEEP_OPERAND_BP);
|
|
5912
|
+
return {
|
|
5913
|
+
type: 'unaryExpression',
|
|
5914
|
+
operator: 'beep!',
|
|
5915
|
+
operand,
|
|
5916
|
+
argument: operand,
|
|
5917
|
+
prefix: true,
|
|
5918
|
+
start: beepToken.start,
|
|
5919
|
+
end: this.previous().end,
|
|
5920
|
+
line: beepToken.line,
|
|
5921
|
+
column: beepToken.column,
|
|
5922
|
+
};
|
|
5923
|
+
}
|
|
5924
|
+
isStyleRefStar(i) {
|
|
5925
|
+
const star = this.tokens[i];
|
|
5926
|
+
const name = this.tokens[i + 1];
|
|
5927
|
+
return star?.value === '*' && !!name && name.start === star.end && isIdentifierLike(name);
|
|
5928
|
+
}
|
|
5929
|
+
isSpacedStyleRef(i) {
|
|
5930
|
+
const prev = this.tokens[i - 1];
|
|
5931
|
+
return this.isStyleRefStar(i) && !!prev && prev.end < this.tokens[i].start;
|
|
5932
|
+
}
|
|
5771
5933
|
tryParseStringPostfix(left, nextToken) {
|
|
5772
5934
|
const v = nextToken.value;
|
|
5773
5935
|
let unit = null;
|
|
@@ -5970,6 +6132,10 @@ class Parser {
|
|
|
5970
6132
|
const isStop = () => this.check('end') || extraStops.some(s => this.check(s));
|
|
5971
6133
|
debug.parse('\ud83d\udd04 parseCommandListUntilTerminator: Starting (extraStops:', extraStops.join(','), ')');
|
|
5972
6134
|
while (!this.isAtEnd() && !isStop()) {
|
|
6135
|
+
if (this.checkComment()) {
|
|
6136
|
+
this.advance();
|
|
6137
|
+
continue;
|
|
6138
|
+
}
|
|
5973
6139
|
debug.parse('\ud83d\udccd Loop iteration, current token:', this.peek().value, 'kind:', this.peek().kind);
|
|
5974
6140
|
let parsedCommand = false;
|
|
5975
6141
|
const isCommandToken = this.checkIsCommand();
|
|
@@ -6053,7 +6219,7 @@ class Parser {
|
|
|
6053
6219
|
debug.parse('\u2705 Found "end", consuming it');
|
|
6054
6220
|
this.advance();
|
|
6055
6221
|
}
|
|
6056
|
-
else {
|
|
6222
|
+
else if (!this.atEndOfInput()) {
|
|
6057
6223
|
debug.parse('\u274c ERROR: Expected "end" but got:', this.peek().value, 'at position:', this.peek().start);
|
|
6058
6224
|
throw new Error(`Expected "end" to close ${construct} block`);
|
|
6059
6225
|
}
|
|
@@ -6066,7 +6232,7 @@ class Parser {
|
|
|
6066
6232
|
if (this.check('end')) {
|
|
6067
6233
|
this.advance();
|
|
6068
6234
|
}
|
|
6069
|
-
else {
|
|
6235
|
+
else if (!this.atEndOfInput()) {
|
|
6070
6236
|
throw new Error('Expected "end" to close repeat block');
|
|
6071
6237
|
}
|
|
6072
6238
|
}
|
|
@@ -6082,7 +6248,10 @@ class Parser {
|
|
|
6082
6248
|
this.advance();
|
|
6083
6249
|
}
|
|
6084
6250
|
else if (terminator === '') {
|
|
6085
|
-
|
|
6251
|
+
if (!this.atEndOfInput()) {
|
|
6252
|
+
throw new Error('Expected "end", "else", "until", or "while" to close repeat block');
|
|
6253
|
+
}
|
|
6254
|
+
return { commands, terminator: 'end' };
|
|
6086
6255
|
}
|
|
6087
6256
|
return { commands, terminator };
|
|
6088
6257
|
}
|
|
@@ -6666,7 +6835,9 @@ class Parser {
|
|
|
6666
6835
|
parseTopLevelInitBlock() {
|
|
6667
6836
|
const pos = this.getPosition();
|
|
6668
6837
|
const initCommands = this.parseCommandBlock(['end']);
|
|
6669
|
-
this.
|
|
6838
|
+
if (!this.match('end') && !this.atEndOfInput() && !this.checkFeatureStart()) {
|
|
6839
|
+
this.consume('end', "Expected 'end' after init block");
|
|
6840
|
+
}
|
|
6670
6841
|
return {
|
|
6671
6842
|
type: 'initBlock',
|
|
6672
6843
|
commands: initCommands,
|
|
@@ -6842,10 +7013,29 @@ class Parser {
|
|
|
6842
7013
|
...(sourceExpr.column !== undefined && { column: sourceExpr.column }),
|
|
6843
7014
|
};
|
|
6844
7015
|
}
|
|
7016
|
+
isArgumentListOnly(open) {
|
|
7017
|
+
let depth = 0;
|
|
7018
|
+
for (let i = open; i < this.tokens.length; i++) {
|
|
7019
|
+
const token = this.tokens[i];
|
|
7020
|
+
if (token.kind !== 'operator')
|
|
7021
|
+
continue;
|
|
7022
|
+
if (token.value === '(' || token.value === '[' || token.value === '{')
|
|
7023
|
+
depth++;
|
|
7024
|
+
else if (token.value === ')' || token.value === ']' || token.value === '}') {
|
|
7025
|
+
if (--depth === 0)
|
|
7026
|
+
return i === open + 1;
|
|
7027
|
+
}
|
|
7028
|
+
else if (token.value === ',' && depth === 1)
|
|
7029
|
+
return true;
|
|
7030
|
+
}
|
|
7031
|
+
return false;
|
|
7032
|
+
}
|
|
6845
7033
|
tryParsePseudoCommand(callExpr, sourceExpr) {
|
|
6846
7034
|
const methodName = callExpr.callee.name;
|
|
6847
7035
|
const nextToken = this.peek();
|
|
6848
|
-
const
|
|
7036
|
+
const nextIsArticle = nextToken.value.toLowerCase() === 'the';
|
|
7037
|
+
const hasPseudoCommandPattern = nextIsArticle ||
|
|
7038
|
+
Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(nextToken.value.toLowerCase()) ||
|
|
6849
7039
|
(isIdentifier(nextToken) && !this.isCommand(nextToken.value)) ||
|
|
6850
7040
|
isContextVar(nextToken);
|
|
6851
7041
|
if (!hasPseudoCommandPattern) {
|
|
@@ -6871,7 +7061,10 @@ class Parser {
|
|
|
6871
7061
|
});
|
|
6872
7062
|
}
|
|
6873
7063
|
let preposition;
|
|
6874
|
-
if (
|
|
7064
|
+
if (nextIsArticle) {
|
|
7065
|
+
this.advance();
|
|
7066
|
+
}
|
|
7067
|
+
else if (Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(this.peek().value.toLowerCase())) {
|
|
6875
7068
|
preposition = this.advance().value.toLowerCase();
|
|
6876
7069
|
}
|
|
6877
7070
|
let targetExpr;
|
|
@@ -7035,10 +7228,22 @@ class Parser {
|
|
|
7035
7228
|
this.consume(']', "Expected ']' after event condition");
|
|
7036
7229
|
}
|
|
7037
7230
|
let target;
|
|
7231
|
+
let targetExpression;
|
|
7038
7232
|
if (this.match('from')) {
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7233
|
+
if (this.check('the'))
|
|
7234
|
+
this.advance();
|
|
7235
|
+
if (Parser.POSITIONAL_SOURCE_HEADS.has(this.peek().value.toLowerCase())) {
|
|
7236
|
+
const startIndex = this.current;
|
|
7237
|
+
targetExpression = this.parseExpression();
|
|
7238
|
+
target = this.tokens
|
|
7239
|
+
.slice(startIndex, this.current)
|
|
7240
|
+
.map(t => t.value)
|
|
7241
|
+
.join(' ');
|
|
7242
|
+
}
|
|
7243
|
+
else {
|
|
7244
|
+
target = this.advance().value;
|
|
7245
|
+
}
|
|
7246
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed 'from' target: ${target}`);
|
|
7042
7247
|
this.parseTemporalKeywordModifiers(modifiers);
|
|
7043
7248
|
}
|
|
7044
7249
|
let attributeName;
|
|
@@ -7084,7 +7289,7 @@ class Parser {
|
|
|
7084
7289
|
const nextIsOpenParen = this.tokens[this.current + 1]?.value === '(';
|
|
7085
7290
|
const commandName = this.peek().value.toLowerCase();
|
|
7086
7291
|
const isSpecialBodyCommand = commandName === 'js' || commandName === 'tell';
|
|
7087
|
-
if (nextIsOpenParen && !isSpecialBodyCommand) {
|
|
7292
|
+
if (nextIsOpenParen && !isSpecialBodyCommand && this.isArgumentListOnly(this.current + 1)) {
|
|
7088
7293
|
let expr;
|
|
7089
7294
|
const savedError = this.error;
|
|
7090
7295
|
try {
|
|
@@ -7116,7 +7321,7 @@ class Parser {
|
|
|
7116
7321
|
}
|
|
7117
7322
|
}
|
|
7118
7323
|
}
|
|
7119
|
-
else if (this.checkIdentifier()) {
|
|
7324
|
+
else if (this.checkIdentifier() || this.checkEventNamedCall()) {
|
|
7120
7325
|
const token = this.peek();
|
|
7121
7326
|
if (this.isCommand(token.value)) {
|
|
7122
7327
|
const cmd = this.parseCommandWithErrorRecovery();
|
|
@@ -7225,6 +7430,7 @@ class Parser {
|
|
|
7225
7430
|
...(eventParams.length > 0 && { args: eventParams }),
|
|
7226
7431
|
...(condition && { condition: fromLegacyExpression(condition) }),
|
|
7227
7432
|
...(target && { target }),
|
|
7433
|
+
...(targetExpression && { targetExpression: fromLegacyExpression(targetExpression) }),
|
|
7228
7434
|
...(attributeName && { attributeName }),
|
|
7229
7435
|
...(watchTarget && { watchTarget: fromLegacyExpression(watchTarget) }),
|
|
7230
7436
|
...(customEventSource && { customEventSource }),
|
|
@@ -7480,10 +7686,9 @@ class Parser {
|
|
|
7480
7686
|
}
|
|
7481
7687
|
parseCommandCore() {
|
|
7482
7688
|
const commandToken = this.previous();
|
|
7483
|
-
|
|
7689
|
+
const commandName = commandToken.value;
|
|
7484
7690
|
if (commandName === 'beep' && this.check('!')) {
|
|
7485
7691
|
this.advance();
|
|
7486
|
-
commandName = 'beep!';
|
|
7487
7692
|
}
|
|
7488
7693
|
if (commandName === 'fetch') {
|
|
7489
7694
|
return parseFetchCommand(this.getContext(), commandToken);
|
|
@@ -7582,6 +7787,9 @@ class Parser {
|
|
|
7582
7787
|
args.push(this.parsePrimary());
|
|
7583
7788
|
}
|
|
7584
7789
|
const callNode = this.createCallExpression(createIdentifier(funcName, namePos), args);
|
|
7790
|
+
if (funcName === 'closest' && args.length > 0 && this.match('to')) {
|
|
7791
|
+
callNode.closestTo = this.parseExpression();
|
|
7792
|
+
}
|
|
7585
7793
|
if (funcName === 'next' || funcName === 'previous') {
|
|
7586
7794
|
const relativePositional = this.parseRelativePositionalModifiers();
|
|
7587
7795
|
if (relativePositional) {
|
|
@@ -7621,6 +7829,9 @@ class Parser {
|
|
|
7621
7829
|
}
|
|
7622
7830
|
parseTheXofY() {
|
|
7623
7831
|
const positionalKeywords = ['first', 'last', 'next', 'previous', 'random', 'closest'];
|
|
7832
|
+
if (this.isStyleRefStar(this.current)) {
|
|
7833
|
+
return this.parsePrimary();
|
|
7834
|
+
}
|
|
7624
7835
|
if (!this.checkIdentifier()) {
|
|
7625
7836
|
return this.createIdentifier('the');
|
|
7626
7837
|
}
|
|
@@ -7636,7 +7847,7 @@ class Parser {
|
|
|
7636
7847
|
return this.createIdentifier(propertyName);
|
|
7637
7848
|
}
|
|
7638
7849
|
this.advance();
|
|
7639
|
-
const target = this.
|
|
7850
|
+
const target = parseOfOperand(this.makePrattContext(), THE_OF_TARGET_BP);
|
|
7640
7851
|
return {
|
|
7641
7852
|
type: 'propertyOfExpression',
|
|
7642
7853
|
property: { type: 'identifier', name: propertyName },
|
|
@@ -7834,6 +8045,13 @@ class Parser {
|
|
|
7834
8045
|
checkLiteral() {
|
|
7835
8046
|
return this.checkPredicate(isLiteral);
|
|
7836
8047
|
}
|
|
8048
|
+
checkEventNamedCall() {
|
|
8049
|
+
if (this.isAtEnd() || !this.checkIdentifierLike() || this.checkIsCommand())
|
|
8050
|
+
return false;
|
|
8051
|
+
const token = this.peek();
|
|
8052
|
+
const next = this.tokens[this.current + 1];
|
|
8053
|
+
return next?.value === '(' && next.start === token.end;
|
|
8054
|
+
}
|
|
7837
8055
|
checkIdentifierLike() {
|
|
7838
8056
|
return this.checkPredicate(isIdentifierLike);
|
|
7839
8057
|
}
|
|
@@ -7942,6 +8160,22 @@ class Parser {
|
|
|
7942
8160
|
isAtEnd() {
|
|
7943
8161
|
return this.current >= this.tokens.length;
|
|
7944
8162
|
}
|
|
8163
|
+
atEndOfInput() {
|
|
8164
|
+
for (let i = this.current; i < this.tokens.length; i++) {
|
|
8165
|
+
if (!isComment(this.tokens[i]))
|
|
8166
|
+
return false;
|
|
8167
|
+
}
|
|
8168
|
+
return true;
|
|
8169
|
+
}
|
|
8170
|
+
checkFeatureStart() {
|
|
8171
|
+
if (this.isAtEnd())
|
|
8172
|
+
return false;
|
|
8173
|
+
const value = this.peek().value;
|
|
8174
|
+
return (value === 'init' ||
|
|
8175
|
+
value === 'on' ||
|
|
8176
|
+
value === 'def' ||
|
|
8177
|
+
getRegisteredFeature(value) !== undefined);
|
|
8178
|
+
}
|
|
7945
8179
|
peek() {
|
|
7946
8180
|
if (this.isAtEnd()) {
|
|
7947
8181
|
return { kind: 'unknown', value: '', start: 0, end: 0, line: 1, column: 1 };
|
|
@@ -8153,6 +8387,7 @@ class Parser {
|
|
|
8153
8387
|
match: this.match.bind(this),
|
|
8154
8388
|
matchOperator: this.matchOperator.bind(this),
|
|
8155
8389
|
isAtEnd: this.isAtEnd.bind(this),
|
|
8390
|
+
atEndOfInput: this.atEndOfInput.bind(this),
|
|
8156
8391
|
createIdentifier: this.createIdentifier.bind(this),
|
|
8157
8392
|
parseExpression: this.parseExpression.bind(this),
|
|
8158
8393
|
parsePrimary: this.parsePrimary.bind(this),
|
|
@@ -8203,7 +8438,15 @@ Parser.POSTFIX_UNARY_OPERATORS = new Set([
|
|
|
8203
8438
|
'ignoring case',
|
|
8204
8439
|
]);
|
|
8205
8440
|
Parser.PRATT_TABLE = PARSER_TABLE;
|
|
8441
|
+
Parser.BEEP_OPERAND_BP = 80;
|
|
8206
8442
|
Parser.PSEUDO_COMMAND_PREPOSITIONS = ['from', 'on', 'with', 'into', 'at', 'to'];
|
|
8443
|
+
Parser.POSITIONAL_SOURCE_HEADS = new Set([
|
|
8444
|
+
'closest',
|
|
8445
|
+
'next',
|
|
8446
|
+
'previous',
|
|
8447
|
+
'first',
|
|
8448
|
+
'last',
|
|
8449
|
+
]);
|
|
8207
8450
|
function parse(input, options) {
|
|
8208
8451
|
const tokens = tokenize(input);
|
|
8209
8452
|
const parser = new Parser(tokens, options, input);
|
|
@@ -8408,6 +8651,89 @@ function isDocumentFragment(value) {
|
|
|
8408
8651
|
'childNodes' in value);
|
|
8409
8652
|
}
|
|
8410
8653
|
|
|
8654
|
+
function beepValues(me, values) {
|
|
8655
|
+
const outputs = values.map(describeBeepValue);
|
|
8656
|
+
const shown = outputs.filter(output => announce(me, output.value));
|
|
8657
|
+
if (shown.length === 0)
|
|
8658
|
+
return outputs;
|
|
8659
|
+
console.group('\ud83d\udd14 beep! Debug Output');
|
|
8660
|
+
for (const output of shown) {
|
|
8661
|
+
console.log(`Value:`, output.value);
|
|
8662
|
+
console.log(`Type:`, output.type);
|
|
8663
|
+
console.log(`Representation:`, output.representation);
|
|
8664
|
+
console.log('---');
|
|
8665
|
+
}
|
|
8666
|
+
console.groupEnd();
|
|
8667
|
+
return outputs;
|
|
8668
|
+
}
|
|
8669
|
+
function announce(me, value) {
|
|
8670
|
+
const target = me;
|
|
8671
|
+
if (typeof CustomEvent !== 'function' || typeof target?.dispatchEvent !== 'function')
|
|
8672
|
+
return true;
|
|
8673
|
+
return target.dispatchEvent(new CustomEvent('hyperscript:beep', {
|
|
8674
|
+
bubbles: true,
|
|
8675
|
+
cancelable: true,
|
|
8676
|
+
detail: { element: me, value },
|
|
8677
|
+
}));
|
|
8678
|
+
}
|
|
8679
|
+
function describeBeepValue(value) {
|
|
8680
|
+
return { value, type: typeOf(value), representation: representationOf(value) };
|
|
8681
|
+
}
|
|
8682
|
+
function typeOf(value) {
|
|
8683
|
+
if (value === null)
|
|
8684
|
+
return 'null';
|
|
8685
|
+
if (value === undefined)
|
|
8686
|
+
return 'undefined';
|
|
8687
|
+
if (Array.isArray(value))
|
|
8688
|
+
return 'array';
|
|
8689
|
+
if (isHTMLElement$2(value))
|
|
8690
|
+
return 'HTMLElement';
|
|
8691
|
+
if (typeof Element !== 'undefined' && value instanceof Element)
|
|
8692
|
+
return 'Element';
|
|
8693
|
+
if (typeof Node !== 'undefined' && value instanceof Node)
|
|
8694
|
+
return 'Node';
|
|
8695
|
+
if (value instanceof Error)
|
|
8696
|
+
return 'Error';
|
|
8697
|
+
if (value instanceof Date)
|
|
8698
|
+
return 'Date';
|
|
8699
|
+
if (value instanceof RegExp)
|
|
8700
|
+
return 'RegExp';
|
|
8701
|
+
return typeof value;
|
|
8702
|
+
}
|
|
8703
|
+
function representationOf(value) {
|
|
8704
|
+
if (value === null)
|
|
8705
|
+
return 'null';
|
|
8706
|
+
if (value === undefined)
|
|
8707
|
+
return 'undefined';
|
|
8708
|
+
if (Array.isArray(value)) {
|
|
8709
|
+
return `Array(${value.length}) [${value
|
|
8710
|
+
.slice(0, 3)
|
|
8711
|
+
.map(v => representationOf(v))
|
|
8712
|
+
.join(', ')}${value.length > 3 ? '...' : ''}]`;
|
|
8713
|
+
}
|
|
8714
|
+
if (isHTMLElement$2(value)) {
|
|
8715
|
+
const el = value;
|
|
8716
|
+
const tag = el.tagName.toLowerCase();
|
|
8717
|
+
const id = el.id ? `#${el.id}` : '';
|
|
8718
|
+
const classes = el.className ? `.${el.className.split(' ').join('.')}` : '';
|
|
8719
|
+
return `<${tag}${id}${classes}/>`;
|
|
8720
|
+
}
|
|
8721
|
+
if (value instanceof Error)
|
|
8722
|
+
return `Error: ${value.message}`;
|
|
8723
|
+
if (typeof value === 'string')
|
|
8724
|
+
return value.length > 50 ? `"${value.substring(0, 47)}..."` : `"${value}"`;
|
|
8725
|
+
if (typeof value === 'object') {
|
|
8726
|
+
try {
|
|
8727
|
+
const keys = Object.keys(value);
|
|
8728
|
+
return `Object {${keys.slice(0, 3).join(', ')}${keys.length > 3 ? '...' : ''}}`;
|
|
8729
|
+
}
|
|
8730
|
+
catch {
|
|
8731
|
+
return '[Object]';
|
|
8732
|
+
}
|
|
8733
|
+
}
|
|
8734
|
+
return String(value);
|
|
8735
|
+
}
|
|
8736
|
+
|
|
8411
8737
|
function convertToNumber(value) {
|
|
8412
8738
|
if (value === null || value === undefined) {
|
|
8413
8739
|
return 0;
|
|
@@ -8439,6 +8765,16 @@ function convertToNumber(value) {
|
|
|
8439
8765
|
}
|
|
8440
8766
|
return 0;
|
|
8441
8767
|
}
|
|
8768
|
+
function counterAmount(value) {
|
|
8769
|
+
return typeof value === 'number' ? value : parseFloat(String(value));
|
|
8770
|
+
}
|
|
8771
|
+
function counterValue(value) {
|
|
8772
|
+
if (isHTMLElement$2(value)) {
|
|
8773
|
+
const element = value;
|
|
8774
|
+
return counterValue(element.value || element.textContent);
|
|
8775
|
+
}
|
|
8776
|
+
return value ? counterAmount(value) : 0;
|
|
8777
|
+
}
|
|
8442
8778
|
function getVariableValue(name, context, preferredScope) {
|
|
8443
8779
|
if (preferredScope === 'element') {
|
|
8444
8780
|
return getElementVar(context, name);
|
|
@@ -8543,35 +8879,39 @@ function setElementPropertyValue(propertyPath, value, context) {
|
|
|
8543
8879
|
element[property] = value;
|
|
8544
8880
|
}
|
|
8545
8881
|
}
|
|
8882
|
+
function isObjectOwner(value) {
|
|
8883
|
+
return (typeof value === 'object' && value !== null && !Array.isArray(value) && !isHTMLElement$2(value));
|
|
8884
|
+
}
|
|
8546
8885
|
function getCurrentNumericValue(target, property, scope, context) {
|
|
8547
8886
|
if (typeof target === 'number') {
|
|
8548
8887
|
return target;
|
|
8549
8888
|
}
|
|
8550
8889
|
if (isHTMLElement$2(target)) {
|
|
8551
8890
|
const element = target;
|
|
8891
|
+
if (property?.startsWith('@'))
|
|
8892
|
+
return counterValue(element.getAttribute(property.slice(1)));
|
|
8552
8893
|
if (property) {
|
|
8553
8894
|
if (property.startsWith('data-') ||
|
|
8554
8895
|
['id', 'class', 'title', 'alt', 'src', 'href'].includes(property)) {
|
|
8555
|
-
|
|
8556
|
-
return convertToNumber(value);
|
|
8896
|
+
return counterValue(element.getAttribute(property));
|
|
8557
8897
|
}
|
|
8558
8898
|
else {
|
|
8559
|
-
|
|
8560
|
-
return convertToNumber(value);
|
|
8899
|
+
return counterValue(element[property]);
|
|
8561
8900
|
}
|
|
8562
8901
|
}
|
|
8563
8902
|
else {
|
|
8564
|
-
|
|
8565
|
-
return convertToNumber(value);
|
|
8903
|
+
return counterValue(element);
|
|
8566
8904
|
}
|
|
8567
8905
|
}
|
|
8906
|
+
if (property && isObjectOwner(target)) {
|
|
8907
|
+
return counterValue(target[property]);
|
|
8908
|
+
}
|
|
8568
8909
|
if (typeof target === 'string') {
|
|
8569
8910
|
if (scope === 'element') {
|
|
8570
|
-
return
|
|
8911
|
+
return counterValue(getVariableValue(target, context, 'element'));
|
|
8571
8912
|
}
|
|
8572
8913
|
if (scope === 'global') {
|
|
8573
|
-
|
|
8574
|
-
return convertToNumber(value);
|
|
8914
|
+
return counterValue(getVariableValue(target, context, 'global'));
|
|
8575
8915
|
}
|
|
8576
8916
|
if (target.includes('.')) {
|
|
8577
8917
|
return getElementPropertyValue(target, context);
|
|
@@ -8585,14 +8925,17 @@ function getCurrentNumericValue(target, property, scope, context) {
|
|
|
8585
8925
|
else if (target === 'you' && context.you) {
|
|
8586
8926
|
return convertToNumber(context.you.value || 0);
|
|
8587
8927
|
}
|
|
8588
|
-
|
|
8589
|
-
return convertToNumber(value);
|
|
8928
|
+
return counterValue(getVariableValue(target, context));
|
|
8590
8929
|
}
|
|
8591
|
-
return
|
|
8930
|
+
return counterValue(target);
|
|
8592
8931
|
}
|
|
8593
8932
|
function setTargetValue(target, property, scope, newValue, context) {
|
|
8594
8933
|
if (isHTMLElement$2(target)) {
|
|
8595
8934
|
const element = target;
|
|
8935
|
+
if (property?.startsWith('@')) {
|
|
8936
|
+
element.setAttribute(property.slice(1), String(newValue));
|
|
8937
|
+
return;
|
|
8938
|
+
}
|
|
8596
8939
|
if (property) {
|
|
8597
8940
|
if (property.startsWith('data-') ||
|
|
8598
8941
|
['id', 'class', 'title', 'alt', 'src', 'href'].includes(property)) {
|
|
@@ -8612,6 +8955,10 @@ function setTargetValue(target, property, scope, newValue, context) {
|
|
|
8612
8955
|
}
|
|
8613
8956
|
return;
|
|
8614
8957
|
}
|
|
8958
|
+
if (property && isObjectOwner(target)) {
|
|
8959
|
+
target[property] = newValue;
|
|
8960
|
+
return;
|
|
8961
|
+
}
|
|
8615
8962
|
if (typeof target === 'string') {
|
|
8616
8963
|
if (scope === 'element') {
|
|
8617
8964
|
setVariableValue(target, newValue, context, 'element');
|
|
@@ -9040,13 +9387,6 @@ function isFunction(value) {
|
|
|
9040
9387
|
function isInputElement$1(el) {
|
|
9041
9388
|
return (el !== null && typeof el === 'object' && 'tagName' in el && el.tagName === 'INPUT');
|
|
9042
9389
|
}
|
|
9043
|
-
function isFormElement$1(el) {
|
|
9044
|
-
if (el === null || typeof el !== 'object' || !('tagName' in el)) {
|
|
9045
|
-
return false;
|
|
9046
|
-
}
|
|
9047
|
-
const tag = el.tagName;
|
|
9048
|
-
return tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA';
|
|
9049
|
-
}
|
|
9050
9390
|
function isHTMLElement$1(el) {
|
|
9051
9391
|
return (el !== null &&
|
|
9052
9392
|
typeof el === 'object' &&
|
|
@@ -9106,7 +9446,7 @@ const SPECIAL_DOM_PROPERTIES = {
|
|
|
9106
9446
|
innertext: el => el.textContent?.trim(),
|
|
9107
9447
|
innerHTML: el => el.innerHTML,
|
|
9108
9448
|
outerhtml: el => el.outerHTML,
|
|
9109
|
-
value: el => (
|
|
9449
|
+
value: el => ('value' in el ? el.value : undefined),
|
|
9110
9450
|
checked: el => (isInputElement$1(el) ? el.checked : undefined),
|
|
9111
9451
|
disabled: el => ('disabled' in el ? el.disabled : undefined),
|
|
9112
9452
|
selected: el => (isOptionElement$1(el) ? el.selected : undefined),
|
|
@@ -9774,14 +10114,15 @@ const closestExpression = {
|
|
|
9774
10114
|
if (typeof selector !== 'string') {
|
|
9775
10115
|
throw new Error('closest requires a string selector');
|
|
9776
10116
|
}
|
|
9777
|
-
|
|
10117
|
+
const start = args.length > 1 ? args[1] : context.me;
|
|
10118
|
+
if (!(start instanceof Element)) {
|
|
9778
10119
|
return null;
|
|
9779
10120
|
}
|
|
9780
|
-
return
|
|
10121
|
+
return start.closest(selector);
|
|
9781
10122
|
},
|
|
9782
10123
|
validate(args) {
|
|
9783
|
-
if (args.length !== 1) {
|
|
9784
|
-
return 'closest requires
|
|
10124
|
+
if (args.length !== 1 && args.length !== 2) {
|
|
10125
|
+
return 'closest requires a selector and, optionally, the element to start from';
|
|
9785
10126
|
}
|
|
9786
10127
|
const arg = args[0];
|
|
9787
10128
|
if (typeof arg === 'string')
|
|
@@ -11281,7 +11622,7 @@ const isEmptyExpression = {
|
|
|
11281
11622
|
else if (value instanceof Node || value instanceof Element)
|
|
11282
11623
|
result = false;
|
|
11283
11624
|
else if (isObject(value))
|
|
11284
|
-
result =
|
|
11625
|
+
result = value.length === 0;
|
|
11285
11626
|
else
|
|
11286
11627
|
result = false;
|
|
11287
11628
|
return result;
|
|
@@ -12405,11 +12746,16 @@ async function evaluateBinaryExpression(node, context) {
|
|
|
12405
12746
|
}
|
|
12406
12747
|
}
|
|
12407
12748
|
}
|
|
12408
|
-
if (leftNode?.type === 'selector' &&
|
|
12749
|
+
if (leftNode?.type === 'selector' &&
|
|
12750
|
+
typeof leftNode.value === 'string' &&
|
|
12751
|
+
(leftNode.fromQuery || (operator === 'in' && leftNode.value.startsWith('.')))) {
|
|
12409
12752
|
const root = await evaluateAST(node.right, context);
|
|
12410
12753
|
const scope = root && typeof root.querySelectorAll === 'function' ? root : null;
|
|
12411
12754
|
if (scope) {
|
|
12412
|
-
|
|
12755
|
+
const query = leftNode.fromQuery
|
|
12756
|
+
? leftNode.value
|
|
12757
|
+
: escapeSelectorForQuery(leftNode, leftNode.value);
|
|
12758
|
+
return Array.from(scope.querySelectorAll(query));
|
|
12413
12759
|
}
|
|
12414
12760
|
}
|
|
12415
12761
|
}
|
|
@@ -12421,6 +12767,12 @@ async function evaluateBinaryExpression(node, context) {
|
|
|
12421
12767
|
return getExpr(context, 'styleRef').evaluate(context, leftNode.value.slice(1), el);
|
|
12422
12768
|
}
|
|
12423
12769
|
if (operator === 'of') {
|
|
12770
|
+
if (leftNode?.type === 'attributeAccess') {
|
|
12771
|
+
const target = await evaluateAST(node.right, context);
|
|
12772
|
+
if (target == null)
|
|
12773
|
+
return undefined;
|
|
12774
|
+
return getExpr(context, 'possessive').evaluate(context, target, `@${leftNode.attributeName}`);
|
|
12775
|
+
}
|
|
12424
12776
|
const path = propertyPathOf(node.left);
|
|
12425
12777
|
if (path) {
|
|
12426
12778
|
const target = await evaluateAST(node.right, context);
|
|
@@ -12428,6 +12780,10 @@ async function evaluateBinaryExpression(node, context) {
|
|
|
12428
12780
|
for (const seg of path) {
|
|
12429
12781
|
if (cur == null)
|
|
12430
12782
|
return undefined;
|
|
12783
|
+
if (isMappableCollection(cur) && !(seg in cur)) {
|
|
12784
|
+
cur = await getExpr(context, 'possessive').evaluate(context, cur, seg);
|
|
12785
|
+
continue;
|
|
12786
|
+
}
|
|
12431
12787
|
cur = isElement(cur) ? getElementProperty$1(cur, seg) : cur[seg];
|
|
12432
12788
|
}
|
|
12433
12789
|
return typeof cur === 'function' ? cur.bind(target) : cur;
|
|
@@ -12452,26 +12808,21 @@ async function evaluateBinaryExpression(node, context) {
|
|
|
12452
12808
|
const R = node.ignoringCase ? applyCI(right) : right;
|
|
12453
12809
|
switch (operator) {
|
|
12454
12810
|
case '+': {
|
|
12455
|
-
let addLeft = left;
|
|
12456
|
-
let addRight = right;
|
|
12457
12811
|
if (node.coerceNumeric) {
|
|
12458
|
-
|
|
12459
|
-
addLeft = convertToNumber(addLeft);
|
|
12460
|
-
if (typeof addRight === 'string')
|
|
12461
|
-
addRight = convertToNumber(addRight);
|
|
12812
|
+
return counterValue(left) + counterAmount(right);
|
|
12462
12813
|
}
|
|
12463
|
-
if (typeof
|
|
12464
|
-
return String(
|
|
12814
|
+
if (typeof left === 'string' || typeof right === 'string') {
|
|
12815
|
+
return String(left ?? '') + String(right ?? '');
|
|
12465
12816
|
}
|
|
12466
|
-
if (Array.isArray(
|
|
12467
|
-
return Array.isArray(
|
|
12817
|
+
if (Array.isArray(left)) {
|
|
12818
|
+
return Array.isArray(right) ? [...left, ...right] : [...left, right];
|
|
12468
12819
|
}
|
|
12469
|
-
return unwrapTypedResult(await getExpr(context, 'addition').evaluate(context, {
|
|
12470
|
-
left: addLeft,
|
|
12471
|
-
right: addRight,
|
|
12472
|
-
}));
|
|
12820
|
+
return unwrapTypedResult(await getExpr(context, 'addition').evaluate(context, { left, right }));
|
|
12473
12821
|
}
|
|
12474
12822
|
case '-':
|
|
12823
|
+
if (node.coerceNumeric) {
|
|
12824
|
+
return counterValue(left) - counterAmount(right);
|
|
12825
|
+
}
|
|
12475
12826
|
return unwrapTypedResult(await getExpr(context, 'subtraction').evaluate(context, { left, right }));
|
|
12476
12827
|
case '*':
|
|
12477
12828
|
return unwrapTypedResult(await getExpr(context, 'multiplication').evaluate(context, { left, right }));
|
|
@@ -12729,9 +13080,8 @@ async function evaluatePropertyOfExpressionNode(node, context) {
|
|
|
12729
13080
|
}
|
|
12730
13081
|
const propertyName = propertyNode.name;
|
|
12731
13082
|
const target = await evaluateAST(node.target, context);
|
|
12732
|
-
if (target == null)
|
|
12733
|
-
|
|
12734
|
-
}
|
|
13083
|
+
if (target == null)
|
|
13084
|
+
return undefined;
|
|
12735
13085
|
return getExpr(context, 'possessive').evaluate(context, target, propertyName);
|
|
12736
13086
|
}
|
|
12737
13087
|
async function evaluateTemplateLiteralNode(node, context) {
|
|
@@ -12849,6 +13199,9 @@ async function evaluateUnaryExpression(node, context) {
|
|
|
12849
13199
|
case 'not':
|
|
12850
13200
|
case '!':
|
|
12851
13201
|
return logicalExpressions.not.evaluate(context, value);
|
|
13202
|
+
case 'beep!':
|
|
13203
|
+
beepValues(context.me, [value]);
|
|
13204
|
+
return value;
|
|
12852
13205
|
case 'no':
|
|
12853
13206
|
return logicalExpressions.no.evaluate(context, value);
|
|
12854
13207
|
case '-':
|
|
@@ -12938,6 +13291,10 @@ async function evaluateCallExpression(node, context) {
|
|
|
12938
13291
|
const args = await resolveCallArgs(node.arguments, funcName, context);
|
|
12939
13292
|
switch (funcName) {
|
|
12940
13293
|
case 'closest':
|
|
13294
|
+
if (node.closestTo) {
|
|
13295
|
+
const start = unwrapElement(await evaluateAST(node.closestTo, context)) ?? null;
|
|
13296
|
+
return referencesExpressions.closest.evaluate(context, args[0], start);
|
|
13297
|
+
}
|
|
12941
13298
|
return referencesExpressions.closest.evaluate(context, ...args);
|
|
12942
13299
|
case 'previous':
|
|
12943
13300
|
case 'next': {
|
|
@@ -14324,12 +14681,23 @@ class RuntimeBase {
|
|
|
14324
14681
|
debug.runtime(`BEHAVIOR: Finished installing ${behaviorName}`);
|
|
14325
14682
|
}
|
|
14326
14683
|
async executeEventHandler(node, context) {
|
|
14327
|
-
const { event, events, commands, target, args, selector, condition, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler, } = node;
|
|
14684
|
+
const { event, events, commands, target, targetExpression, args, selector, condition, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler, } = node;
|
|
14328
14685
|
const eventNames = events && events.length > 0 ? events : [event];
|
|
14329
14686
|
debug.runtime(`BEHAVIOR: executeEventHandler: event='${event}', target='${target}'`);
|
|
14330
14687
|
let targets = [];
|
|
14331
14688
|
let globalTarget = null;
|
|
14332
|
-
if (
|
|
14689
|
+
if (targetExpression) {
|
|
14690
|
+
const resolved = await this.execute(targetExpression, context);
|
|
14691
|
+
if (resolved === window)
|
|
14692
|
+
globalTarget = window;
|
|
14693
|
+
else if (resolved === document)
|
|
14694
|
+
globalTarget = document;
|
|
14695
|
+
else if (this.isElement(resolved))
|
|
14696
|
+
targets = [resolved];
|
|
14697
|
+
else if (Array.isArray(resolved))
|
|
14698
|
+
targets = resolved.filter(el => this.isElement(el));
|
|
14699
|
+
}
|
|
14700
|
+
else if (target) {
|
|
14333
14701
|
const targetLower = typeof target === 'string' ? target.toLowerCase() : '';
|
|
14334
14702
|
if (targetLower === 'window' || targetLower === 'the window') {
|
|
14335
14703
|
globalTarget = window;
|
|
@@ -16350,14 +16718,83 @@ function parseAttribute(expression) {
|
|
|
16350
16718
|
if (trimmed.startsWith('@')) {
|
|
16351
16719
|
return { name: trimmed.substring(1).trim() };
|
|
16352
16720
|
}
|
|
16353
|
-
throw new Error(`Invalid attribute syntax: ${expression}`);
|
|
16354
|
-
}
|
|
16355
|
-
function parseAttributeName(expression) {
|
|
16356
|
-
return parseAttribute(expression).name;
|
|
16721
|
+
throw new Error(`Invalid attribute syntax: ${expression}`);
|
|
16722
|
+
}
|
|
16723
|
+
function parseAttributeName(expression) {
|
|
16724
|
+
return parseAttribute(expression).name;
|
|
16725
|
+
}
|
|
16726
|
+
function parseAttributeWithValue(expression) {
|
|
16727
|
+
const result = parseAttribute(expression);
|
|
16728
|
+
return { name: result.name, value: result.value ?? '' };
|
|
16729
|
+
}
|
|
16730
|
+
|
|
16731
|
+
function _parseDurationComponents(value) {
|
|
16732
|
+
const trimmed = value.trim();
|
|
16733
|
+
const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*(s|ms|sec|seconds?|milliseconds?)?$/i);
|
|
16734
|
+
if (!match) {
|
|
16735
|
+
return null;
|
|
16736
|
+
}
|
|
16737
|
+
const number = parseFloat(match[1]);
|
|
16738
|
+
const unit = (match[2] || 'ms').toLowerCase();
|
|
16739
|
+
if (unit === 'ms' || unit === 'millisecond' || unit === 'milliseconds') {
|
|
16740
|
+
return { number, milliseconds: Math.floor(number) };
|
|
16741
|
+
}
|
|
16742
|
+
else if (unit === 's' || unit === 'sec' || unit === 'second' || unit === 'seconds') {
|
|
16743
|
+
return { number, milliseconds: Math.floor(number * 1000) };
|
|
16744
|
+
}
|
|
16745
|
+
return null;
|
|
16746
|
+
}
|
|
16747
|
+
function parseDuration(value, defaultMs = 300) {
|
|
16748
|
+
if (typeof value === 'number') {
|
|
16749
|
+
return Math.max(0, Math.floor(value));
|
|
16750
|
+
}
|
|
16751
|
+
if (typeof value === 'string') {
|
|
16752
|
+
const result = _parseDurationComponents(value);
|
|
16753
|
+
return result ? result.milliseconds : defaultMs;
|
|
16754
|
+
}
|
|
16755
|
+
return defaultMs;
|
|
16756
|
+
}
|
|
16757
|
+
function parseDurationStrict(value) {
|
|
16758
|
+
if (typeof value === 'number') {
|
|
16759
|
+
if (value < 0)
|
|
16760
|
+
throw new Error('Duration must be >= 0');
|
|
16761
|
+
return Math.floor(value);
|
|
16762
|
+
}
|
|
16763
|
+
if (typeof value === 'string') {
|
|
16764
|
+
const result = _parseDurationComponents(value);
|
|
16765
|
+
if (!result) {
|
|
16766
|
+
throw new Error(`Invalid duration format: "${value}"`);
|
|
16767
|
+
}
|
|
16768
|
+
return result.milliseconds;
|
|
16769
|
+
}
|
|
16770
|
+
throw new Error(`Invalid duration type: ${typeof value}`);
|
|
16771
|
+
}
|
|
16772
|
+
function parseCSSDurations(durationString) {
|
|
16773
|
+
if (!durationString || durationString === 'none') {
|
|
16774
|
+
return [0];
|
|
16775
|
+
}
|
|
16776
|
+
return durationString.split(',').map(duration => {
|
|
16777
|
+
const value = parseFloat(duration.trim());
|
|
16778
|
+
if (isNaN(value))
|
|
16779
|
+
return 0;
|
|
16780
|
+
if (duration.includes('s') && !duration.includes('ms')) {
|
|
16781
|
+
return value * 1000;
|
|
16782
|
+
}
|
|
16783
|
+
return value;
|
|
16784
|
+
});
|
|
16785
|
+
}
|
|
16786
|
+
function calculateMaxAnimationTime(durations, delays) {
|
|
16787
|
+
let maxTime = 0;
|
|
16788
|
+
for (let i = 0; i < durations.length; i++) {
|
|
16789
|
+
const duration = durations[i] || 0;
|
|
16790
|
+
const delay = delays[i] || 0;
|
|
16791
|
+
const totalTime = duration + delay;
|
|
16792
|
+
maxTime = Math.max(maxTime, totalTime);
|
|
16793
|
+
}
|
|
16794
|
+
return maxTime;
|
|
16357
16795
|
}
|
|
16358
|
-
function
|
|
16359
|
-
|
|
16360
|
-
return { name: result.name, value: result.value ?? '' };
|
|
16796
|
+
function camelToKebab(str) {
|
|
16797
|
+
return str.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
16361
16798
|
}
|
|
16362
16799
|
|
|
16363
16800
|
function batchApply(targets, operation) {
|
|
@@ -16425,15 +16862,18 @@ function toggleAttribute(element, name, value) {
|
|
|
16425
16862
|
}
|
|
16426
16863
|
}
|
|
16427
16864
|
}
|
|
16865
|
+
function toCssPropertyName(property) {
|
|
16866
|
+
return property.startsWith('--') ? property : camelToKebab(property);
|
|
16867
|
+
}
|
|
16428
16868
|
function batchSetStyles(targets, styles) {
|
|
16429
16869
|
const entries = Object.entries(styles);
|
|
16430
16870
|
return batchApplyItems(targets, entries, (element, [property, value]) => {
|
|
16431
|
-
element.style.setProperty(property, value);
|
|
16871
|
+
element.style.setProperty(toCssPropertyName(property), value);
|
|
16432
16872
|
});
|
|
16433
16873
|
}
|
|
16434
16874
|
function batchRemoveStyles(targets, properties) {
|
|
16435
16875
|
return batchApplyItems(targets, properties, (element, property) => {
|
|
16436
|
-
element.style.removeProperty(property);
|
|
16876
|
+
element.style.removeProperty(toCssPropertyName(property));
|
|
16437
16877
|
});
|
|
16438
16878
|
}
|
|
16439
16879
|
|
|
@@ -17099,75 +17539,6 @@ let RemoveCommand = (() => {
|
|
|
17099
17539
|
})();
|
|
17100
17540
|
const createRemoveCommand = createFactory(RemoveCommand);
|
|
17101
17541
|
|
|
17102
|
-
function _parseDurationComponents(value) {
|
|
17103
|
-
const trimmed = value.trim();
|
|
17104
|
-
const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*(s|ms|sec|seconds?|milliseconds?)?$/i);
|
|
17105
|
-
if (!match) {
|
|
17106
|
-
return null;
|
|
17107
|
-
}
|
|
17108
|
-
const number = parseFloat(match[1]);
|
|
17109
|
-
const unit = (match[2] || 'ms').toLowerCase();
|
|
17110
|
-
if (unit === 'ms' || unit === 'millisecond' || unit === 'milliseconds') {
|
|
17111
|
-
return { number, milliseconds: Math.floor(number) };
|
|
17112
|
-
}
|
|
17113
|
-
else if (unit === 's' || unit === 'sec' || unit === 'second' || unit === 'seconds') {
|
|
17114
|
-
return { number, milliseconds: Math.floor(number * 1000) };
|
|
17115
|
-
}
|
|
17116
|
-
return null;
|
|
17117
|
-
}
|
|
17118
|
-
function parseDuration(value, defaultMs = 300) {
|
|
17119
|
-
if (typeof value === 'number') {
|
|
17120
|
-
return Math.max(0, Math.floor(value));
|
|
17121
|
-
}
|
|
17122
|
-
if (typeof value === 'string') {
|
|
17123
|
-
const result = _parseDurationComponents(value);
|
|
17124
|
-
return result ? result.milliseconds : defaultMs;
|
|
17125
|
-
}
|
|
17126
|
-
return defaultMs;
|
|
17127
|
-
}
|
|
17128
|
-
function parseDurationStrict(value) {
|
|
17129
|
-
if (typeof value === 'number') {
|
|
17130
|
-
if (value < 0)
|
|
17131
|
-
throw new Error('Duration must be >= 0');
|
|
17132
|
-
return Math.floor(value);
|
|
17133
|
-
}
|
|
17134
|
-
if (typeof value === 'string') {
|
|
17135
|
-
const result = _parseDurationComponents(value);
|
|
17136
|
-
if (!result) {
|
|
17137
|
-
throw new Error(`Invalid duration format: "${value}"`);
|
|
17138
|
-
}
|
|
17139
|
-
return result.milliseconds;
|
|
17140
|
-
}
|
|
17141
|
-
throw new Error(`Invalid duration type: ${typeof value}`);
|
|
17142
|
-
}
|
|
17143
|
-
function parseCSSDurations(durationString) {
|
|
17144
|
-
if (!durationString || durationString === 'none') {
|
|
17145
|
-
return [0];
|
|
17146
|
-
}
|
|
17147
|
-
return durationString.split(',').map(duration => {
|
|
17148
|
-
const value = parseFloat(duration.trim());
|
|
17149
|
-
if (isNaN(value))
|
|
17150
|
-
return 0;
|
|
17151
|
-
if (duration.includes('s') && !duration.includes('ms')) {
|
|
17152
|
-
return value * 1000;
|
|
17153
|
-
}
|
|
17154
|
-
return value;
|
|
17155
|
-
});
|
|
17156
|
-
}
|
|
17157
|
-
function calculateMaxAnimationTime(durations, delays) {
|
|
17158
|
-
let maxTime = 0;
|
|
17159
|
-
for (let i = 0; i < durations.length; i++) {
|
|
17160
|
-
const duration = durations[i] || 0;
|
|
17161
|
-
const delay = delays[i] || 0;
|
|
17162
|
-
const totalTime = duration + delay;
|
|
17163
|
-
maxTime = Math.max(maxTime, totalTime);
|
|
17164
|
-
}
|
|
17165
|
-
return maxTime;
|
|
17166
|
-
}
|
|
17167
|
-
function camelToKebab(str) {
|
|
17168
|
-
return str.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
17169
|
-
}
|
|
17170
|
-
|
|
17171
17542
|
function detectSmartElementType(elements) {
|
|
17172
17543
|
if (elements.length === 0)
|
|
17173
17544
|
return null;
|
|
@@ -18037,7 +18408,11 @@ let PutCommand = (() => {
|
|
|
18037
18408
|
}
|
|
18038
18409
|
if (isInsertableNode(v))
|
|
18039
18410
|
return v;
|
|
18040
|
-
|
|
18411
|
+
if (v == null)
|
|
18412
|
+
return 'null';
|
|
18413
|
+
if (Array.isArray(v))
|
|
18414
|
+
return arrayFragment(v);
|
|
18415
|
+
return String(v);
|
|
18041
18416
|
}
|
|
18042
18417
|
looksLikeCss(s) {
|
|
18043
18418
|
if (!s)
|
|
@@ -18122,6 +18497,19 @@ let PutCommand = (() => {
|
|
|
18122
18497
|
})();
|
|
18123
18498
|
return PutCommand = _classThis;
|
|
18124
18499
|
})();
|
|
18500
|
+
function arrayFragment(items) {
|
|
18501
|
+
const fragment = document.createDocumentFragment();
|
|
18502
|
+
for (const item of items) {
|
|
18503
|
+
if (isInsertableNode(item)) {
|
|
18504
|
+
fragment.append(item);
|
|
18505
|
+
continue;
|
|
18506
|
+
}
|
|
18507
|
+
const template = document.createElement('template');
|
|
18508
|
+
template.innerHTML = item === null ? '' : String(item);
|
|
18509
|
+
fragment.append(template.content);
|
|
18510
|
+
}
|
|
18511
|
+
return fragment;
|
|
18512
|
+
}
|
|
18125
18513
|
const createPutCommand = createFactory(PutCommand);
|
|
18126
18514
|
|
|
18127
18515
|
const HTML_ESCAPE = {
|
|
@@ -19339,10 +19727,29 @@ const STRATEGY_KEYWORDS = {
|
|
|
19339
19727
|
innermorph: 'morph',
|
|
19340
19728
|
outermorph: 'morphOuter',
|
|
19341
19729
|
};
|
|
19730
|
+
function loneRootWithTag(content, tagName) {
|
|
19731
|
+
let root;
|
|
19732
|
+
if (typeof content === 'string') {
|
|
19733
|
+
const template = document.createElement('template');
|
|
19734
|
+
template.innerHTML = content;
|
|
19735
|
+
root = template.content.firstElementChild;
|
|
19736
|
+
if (!root || root.nextElementSibling)
|
|
19737
|
+
return null;
|
|
19738
|
+
}
|
|
19739
|
+
else {
|
|
19740
|
+
root = content.cloneNode(true);
|
|
19741
|
+
}
|
|
19742
|
+
return root.tagName === tagName && isHTMLElement$2(root) ? root : null;
|
|
19743
|
+
}
|
|
19342
19744
|
function executeSwap(target, content, strategy, morphOptions) {
|
|
19343
19745
|
const contentStr = content !== null && !isHTMLElement$2(content) ? content : '';
|
|
19344
19746
|
const contentEl = isHTMLElement$2(content) ? content : null;
|
|
19345
19747
|
switch (strategy) {
|
|
19748
|
+
case 'morphMerge': {
|
|
19749
|
+
const root = content !== null ? loneRootWithTag(content, target.tagName) : null;
|
|
19750
|
+
executeSwap(target, root ?? content, root ? 'morphOuter' : 'morph', morphOptions);
|
|
19751
|
+
break;
|
|
19752
|
+
}
|
|
19346
19753
|
case 'morph':
|
|
19347
19754
|
if (content !== null) {
|
|
19348
19755
|
try {
|
|
@@ -19632,7 +20039,7 @@ let MorphCommand = (() => {
|
|
|
19632
20039
|
if (args.length === 0 && modifiers.with === undefined) {
|
|
19633
20040
|
throw new Error('[HyperFixi] morph: command requires arguments');
|
|
19634
20041
|
}
|
|
19635
|
-
const strategy = literalText(modifiers.strategy)?.toLowerCase() === 'over' ? 'morphOuter' : '
|
|
20042
|
+
const strategy = literalText(modifiers.strategy)?.toLowerCase() === 'over' ? 'morphOuter' : 'morphMerge';
|
|
19636
20043
|
const useViewTransition = modifiers.viewTransition !== undefined;
|
|
19637
20044
|
const targetNode = modifiers.on !== undefined ? modifiers.on : args[0];
|
|
19638
20045
|
const contentNode = modifiers.with !== undefined
|
|
@@ -20938,6 +21345,19 @@ async function resolveWriteTarget(node, evaluator, context, options) {
|
|
|
20938
21345
|
}
|
|
20939
21346
|
return { kind: 'property', target: property };
|
|
20940
21347
|
}
|
|
21348
|
+
if (options.ofAndObjectProperty) {
|
|
21349
|
+
const access = ofOrPossessiveAccess(n);
|
|
21350
|
+
if (access) {
|
|
21351
|
+
const owner = await evaluator.evaluate(access.owner, context);
|
|
21352
|
+
const element = Array.isArray(owner) ? owner[0] : owner;
|
|
21353
|
+
if (access.isOf && isHTMLElement$2(element)) {
|
|
21354
|
+
return { kind: 'property', target: { element, property: access.name } };
|
|
21355
|
+
}
|
|
21356
|
+
if (isObjectOwner(owner)) {
|
|
21357
|
+
return { kind: 'member', container: owner, property: access.name };
|
|
21358
|
+
}
|
|
21359
|
+
}
|
|
21360
|
+
}
|
|
20941
21361
|
if (options.bareReference &&
|
|
20942
21362
|
(nodeType === 'identifier' || nodeType === 'variable' || nodeType === 'symbol') &&
|
|
20943
21363
|
typeof n.name === 'string') {
|
|
@@ -20947,6 +21367,19 @@ async function resolveWriteTarget(node, evaluator, context, options) {
|
|
|
20947
21367
|
}
|
|
20948
21368
|
return null;
|
|
20949
21369
|
}
|
|
21370
|
+
function ofOrPossessiveAccess(n) {
|
|
21371
|
+
const [owner, property, isOf] = n.type === 'possessiveExpression'
|
|
21372
|
+
? [n.object, n.property, false]
|
|
21373
|
+
: n.type === 'propertyOfExpression'
|
|
21374
|
+
? [n.target, n.property, false]
|
|
21375
|
+
: n.type === 'binaryExpression' && n.operator === 'of'
|
|
21376
|
+
? [n.right, n.left, true]
|
|
21377
|
+
: [undefined, undefined, false];
|
|
21378
|
+
const name = property?.name;
|
|
21379
|
+
if (!owner || typeof name !== 'string' || !/^[A-Za-z_$][\w$]*$/.test(name))
|
|
21380
|
+
return null;
|
|
21381
|
+
return { owner, name, isOf };
|
|
21382
|
+
}
|
|
20950
21383
|
|
|
20951
21384
|
function toSetInput(target, value) {
|
|
20952
21385
|
switch (target.kind) {
|
|
@@ -20968,10 +21401,18 @@ function toSetInput(target, value) {
|
|
|
20968
21401
|
property: target.target.property,
|
|
20969
21402
|
value,
|
|
20970
21403
|
};
|
|
21404
|
+
case 'member':
|
|
21405
|
+
return {
|
|
21406
|
+
type: 'member-assignment',
|
|
21407
|
+
container: target.container,
|
|
21408
|
+
property: target.property,
|
|
21409
|
+
value,
|
|
21410
|
+
};
|
|
20971
21411
|
default:
|
|
20972
21412
|
throw new Error(`set: unrequested write-target rung '${target.kind}'`);
|
|
20973
21413
|
}
|
|
20974
21414
|
}
|
|
21415
|
+
const SET_RUNGS = { nodeWriters: true, styleSplit: true, ofAndObjectProperty: true };
|
|
20975
21416
|
let SetCommand = (() => {
|
|
20976
21417
|
let _classDecorators = [command({ name: 'set' })];
|
|
20977
21418
|
let _classDescriptor;
|
|
@@ -20990,8 +21431,7 @@ let SetCommand = (() => {
|
|
|
20990
21431
|
const argScope = firstArg?.scope;
|
|
20991
21432
|
const writeTarget = await resolveWriteTarget(firstArg, evaluator, context, {
|
|
20992
21433
|
scopeElements: () => this.resolveTargets(raw.modifiers.on, evaluator, context),
|
|
20993
|
-
|
|
20994
|
-
styleSplit: true,
|
|
21434
|
+
...SET_RUNGS,
|
|
20995
21435
|
});
|
|
20996
21436
|
if (writeTarget) {
|
|
20997
21437
|
return toSetInput(writeTarget, await this.extractValue(raw, evaluator, context));
|
|
@@ -21317,12 +21757,30 @@ function getNodeType(node) {
|
|
|
21317
21757
|
return 'unknown';
|
|
21318
21758
|
return node.type || 'unknown';
|
|
21319
21759
|
}
|
|
21760
|
+
function propertyTarget(node) {
|
|
21761
|
+
const n = node;
|
|
21762
|
+
const owner = n.type === 'propertyOfExpression'
|
|
21763
|
+
? n.target
|
|
21764
|
+
: n.type === 'possessiveExpression' ||
|
|
21765
|
+
n.type === 'memberExpression' ||
|
|
21766
|
+
n.type === 'propertyAccess'
|
|
21767
|
+
? n.object
|
|
21768
|
+
: undefined;
|
|
21769
|
+
if (!owner || n.computed)
|
|
21770
|
+
return null;
|
|
21771
|
+
const p = n.property;
|
|
21772
|
+
const name = typeof p === 'string' ? p : typeof p?.name === 'string' ? p.name : undefined;
|
|
21773
|
+
if (!name || !/^[A-Za-z_$][\w$-]*$/.test(name))
|
|
21774
|
+
return null;
|
|
21775
|
+
return { owner, name };
|
|
21776
|
+
}
|
|
21320
21777
|
async function parseNumericTargetInput(raw, evaluator, context, commandName) {
|
|
21321
21778
|
if (!raw.args || raw.args.length === 0) {
|
|
21322
21779
|
throw new Error(`${commandName} command requires a target`);
|
|
21323
21780
|
}
|
|
21324
21781
|
const targetArg = raw.args[0];
|
|
21325
21782
|
let target;
|
|
21783
|
+
let property;
|
|
21326
21784
|
let extractedScope;
|
|
21327
21785
|
let customWrite;
|
|
21328
21786
|
const nodeType = getNodeType(targetArg);
|
|
@@ -21337,7 +21795,25 @@ async function parseNumericTargetInput(raw, evaluator, context, commandName) {
|
|
|
21337
21795
|
}
|
|
21338
21796
|
else {
|
|
21339
21797
|
const writer = getRegisteredNodeWriter(nodeType);
|
|
21340
|
-
|
|
21798
|
+
const attribute = writer
|
|
21799
|
+
? null
|
|
21800
|
+
: await resolveAttributeWriteTarget(targetArg, evaluator, context, async () => context.me ? [context.me] : []);
|
|
21801
|
+
const prop = writer || attribute ? null : propertyTarget(targetArg);
|
|
21802
|
+
const owner = prop ? await evaluator.evaluate(prop.owner, context) : undefined;
|
|
21803
|
+
const ownerElement = Array.isArray(owner) ? owner[0] : owner;
|
|
21804
|
+
if (attribute?.elements[0]) {
|
|
21805
|
+
target = attribute.elements[0];
|
|
21806
|
+
property = `@${attribute.name}`;
|
|
21807
|
+
}
|
|
21808
|
+
else if (prop && isHTMLElement$2(ownerElement)) {
|
|
21809
|
+
target = ownerElement;
|
|
21810
|
+
property = prop.name;
|
|
21811
|
+
}
|
|
21812
|
+
else if (prop && isObjectOwner(owner)) {
|
|
21813
|
+
target = owner;
|
|
21814
|
+
property = prop.name;
|
|
21815
|
+
}
|
|
21816
|
+
else if (writer) {
|
|
21341
21817
|
const currentRaw = await evaluator.evaluate(targetArg, context);
|
|
21342
21818
|
const currentValue = typeof currentRaw === 'number'
|
|
21343
21819
|
? currentRaw
|
|
@@ -21375,24 +21851,22 @@ async function parseNumericTargetInput(raw, evaluator, context, commandName) {
|
|
|
21375
21851
|
if (typeof evaluated === 'number') {
|
|
21376
21852
|
amount = evaluated;
|
|
21377
21853
|
}
|
|
21854
|
+
else if (evaluated == null) {
|
|
21855
|
+
amount = NaN;
|
|
21856
|
+
}
|
|
21378
21857
|
}
|
|
21379
21858
|
}
|
|
21380
21859
|
const byModifier = raw.modifiers?.by;
|
|
21381
21860
|
if (byModifier) {
|
|
21382
|
-
const
|
|
21383
|
-
|
|
21384
|
-
|
|
21385
|
-
|
|
21386
|
-
else if (getNodeType(byModifier) !== 'literal') {
|
|
21387
|
-
const evaluated = await evaluator.evaluate(byModifier, context);
|
|
21388
|
-
if (typeof evaluated === 'number') {
|
|
21389
|
-
amount = evaluated;
|
|
21390
|
-
}
|
|
21391
|
-
}
|
|
21861
|
+
const value = getNodeType(byModifier) === 'literal'
|
|
21862
|
+
? byModifier.value
|
|
21863
|
+
: await evaluator.evaluate(byModifier, context);
|
|
21864
|
+
amount = counterAmount(value);
|
|
21392
21865
|
}
|
|
21393
21866
|
return {
|
|
21394
21867
|
target,
|
|
21395
21868
|
amount,
|
|
21869
|
+
...(property && { property }),
|
|
21396
21870
|
...(scope && { scope }),
|
|
21397
21871
|
...(customWrite && { customWrite }),
|
|
21398
21872
|
};
|
|
@@ -21415,7 +21889,7 @@ let NumericModifyCommand = (() => {
|
|
|
21415
21889
|
async execute(input, context) {
|
|
21416
21890
|
const { target, property, scope, amount = 1, operation = 'increment', customWrite } = input;
|
|
21417
21891
|
if (customWrite) {
|
|
21418
|
-
const delta =
|
|
21892
|
+
const delta = amount;
|
|
21419
21893
|
const newValue = operation === 'increment'
|
|
21420
21894
|
? customWrite.currentValue + delta
|
|
21421
21895
|
: customWrite.currentValue - delta;
|
|
@@ -21429,8 +21903,7 @@ let NumericModifyCommand = (() => {
|
|
|
21429
21903
|
newValue = NaN;
|
|
21430
21904
|
}
|
|
21431
21905
|
else {
|
|
21432
|
-
|
|
21433
|
-
newValue = operation === 'increment' ? currentValue + delta : currentValue - delta;
|
|
21906
|
+
newValue = operation === 'increment' ? currentValue + amount : currentValue - amount;
|
|
21434
21907
|
}
|
|
21435
21908
|
setTargetValue(target, property, scope, newValue, context);
|
|
21436
21909
|
Object.assign(context, { it: newValue });
|
|
@@ -22538,7 +23011,10 @@ function createUntilLoopConfig(conditionExpr, evaluateCondition, context, indexV
|
|
|
22538
23011
|
};
|
|
22539
23012
|
const config = {
|
|
22540
23013
|
type: 'until',
|
|
22541
|
-
shouldContinue: () =>
|
|
23014
|
+
shouldContinue: () => {
|
|
23015
|
+
const met = evaluateCondition(conditionExpr, context);
|
|
23016
|
+
return met instanceof Promise ? met.then(value => !value) : !met;
|
|
23017
|
+
},
|
|
22542
23018
|
};
|
|
22543
23019
|
return { config, iterCtx };
|
|
22544
23020
|
}
|
|
@@ -22574,7 +23050,7 @@ function createForeverLoopConfig(indexVariable, maxIterations = 10000) {
|
|
|
22574
23050
|
return { config, iterCtx };
|
|
22575
23051
|
}
|
|
22576
23052
|
|
|
22577
|
-
async function slotText(node, evaluator, context) {
|
|
23053
|
+
async function slotText$1(node, evaluator, context) {
|
|
22578
23054
|
if (!node)
|
|
22579
23055
|
return undefined;
|
|
22580
23056
|
const n = node;
|
|
@@ -22587,6 +23063,18 @@ async function slotText(node, evaluator, context) {
|
|
|
22587
23063
|
const v = await evaluator.evaluate(node, context);
|
|
22588
23064
|
return typeof v === 'string' ? v : undefined;
|
|
22589
23065
|
}
|
|
23066
|
+
function isEventTarget(value) {
|
|
23067
|
+
return typeof value?.addEventListener === 'function';
|
|
23068
|
+
}
|
|
23069
|
+
async function evaluateLoopCondition(condition, context) {
|
|
23070
|
+
if (typeof condition === 'function')
|
|
23071
|
+
return Boolean(await condition());
|
|
23072
|
+
return evaluateCondition(condition, context);
|
|
23073
|
+
}
|
|
23074
|
+
function loopCount(value) {
|
|
23075
|
+
const count = Number(value);
|
|
23076
|
+
return Number.isNaN(count) ? 0 : count;
|
|
23077
|
+
}
|
|
22590
23078
|
let RepeatCommand = (() => {
|
|
22591
23079
|
let _classDecorators = [command({ name: 'repeat' })];
|
|
22592
23080
|
let _classDescriptor;
|
|
@@ -22624,7 +23112,7 @@ let RepeatCommand = (() => {
|
|
|
22624
23112
|
}
|
|
22625
23113
|
}
|
|
22626
23114
|
const m = raw.modifiers ?? {};
|
|
22627
|
-
const text = (node) => slotText(node, evaluator, context);
|
|
23115
|
+
const text = (node) => slotText$1(node, evaluator, context);
|
|
22628
23116
|
const loopType = (await text(m.loopType)) ?? null;
|
|
22629
23117
|
const bottomTested = m.bottomTested
|
|
22630
23118
|
? (await evaluator.evaluate(m.bottomTested, context))
|
|
@@ -22644,17 +23132,24 @@ let RepeatCommand = (() => {
|
|
|
22644
23132
|
};
|
|
22645
23133
|
}
|
|
22646
23134
|
if (loopType === 'times' || m.times) {
|
|
22647
|
-
|
|
22648
|
-
const count = typeof countValue === 'number' ? countValue : parseInt(String(countValue), 10);
|
|
22649
|
-
if (isNaN(count))
|
|
23135
|
+
if (!m.times)
|
|
22650
23136
|
throw new Error('times loops require a count number');
|
|
23137
|
+
const count = loopCount(await evaluator.evaluate(m.times, context));
|
|
22651
23138
|
return { type: 'times', count, indexVariable, commands, elseCommands };
|
|
22652
23139
|
}
|
|
22653
23140
|
if (loopType === 'while' || m.while) {
|
|
22654
23141
|
const condition = m.while;
|
|
22655
23142
|
if (!condition)
|
|
22656
23143
|
throw new Error('while loops require a condition');
|
|
22657
|
-
|
|
23144
|
+
const check = () => evaluator.evaluate(condition, context);
|
|
23145
|
+
return {
|
|
23146
|
+
type: 'while',
|
|
23147
|
+
condition: check,
|
|
23148
|
+
indexVariable,
|
|
23149
|
+
commands,
|
|
23150
|
+
elseCommands,
|
|
23151
|
+
bottomTested,
|
|
23152
|
+
};
|
|
22658
23153
|
}
|
|
22659
23154
|
if (loopType === 'until-event' || m.event) {
|
|
22660
23155
|
const eventName = await text(m.event);
|
|
@@ -22672,7 +23167,7 @@ let RepeatCommand = (() => {
|
|
|
22672
23167
|
}
|
|
22673
23168
|
else {
|
|
22674
23169
|
const target = await evaluator.evaluate(m.from, context);
|
|
22675
|
-
if (target
|
|
23170
|
+
if (isEventTarget(target))
|
|
22676
23171
|
eventTarget = target;
|
|
22677
23172
|
else if (target === 'document')
|
|
22678
23173
|
eventTarget = document;
|
|
@@ -22693,7 +23188,15 @@ let RepeatCommand = (() => {
|
|
|
22693
23188
|
const condition = m.until;
|
|
22694
23189
|
if (!condition)
|
|
22695
23190
|
throw new Error('until loops require a condition');
|
|
22696
|
-
|
|
23191
|
+
const check = () => evaluator.evaluate(condition, context);
|
|
23192
|
+
return {
|
|
23193
|
+
type: 'until',
|
|
23194
|
+
condition: check,
|
|
23195
|
+
indexVariable,
|
|
23196
|
+
commands,
|
|
23197
|
+
elseCommands,
|
|
23198
|
+
bottomTested,
|
|
23199
|
+
};
|
|
22697
23200
|
}
|
|
22698
23201
|
if (loopType === 'forever') {
|
|
22699
23202
|
return { type: 'forever', indexVariable, commands, elseCommands };
|
|
@@ -22711,10 +23214,10 @@ let RepeatCommand = (() => {
|
|
|
22711
23214
|
({ config, iterCtx } = createTimesLoopConfig(count, indexVariable));
|
|
22712
23215
|
break;
|
|
22713
23216
|
case 'while':
|
|
22714
|
-
({ config, iterCtx } = createWhileLoopConfig(condition,
|
|
23217
|
+
({ config, iterCtx } = createWhileLoopConfig(condition, evaluateLoopCondition, context, indexVariable));
|
|
22715
23218
|
break;
|
|
22716
23219
|
case 'until':
|
|
22717
|
-
({ config, iterCtx } = createUntilLoopConfig(condition,
|
|
23220
|
+
({ config, iterCtx } = createUntilLoopConfig(condition, evaluateLoopCondition, context, indexVariable));
|
|
22718
23221
|
break;
|
|
22719
23222
|
case 'until-event':
|
|
22720
23223
|
({ config, iterCtx } = createUntilEventLoopConfig(eventName, eventTarget, indexVariable));
|
|
@@ -23745,6 +24248,29 @@ function namedPropertyOf(node) {
|
|
|
23745
24248
|
}
|
|
23746
24249
|
return null;
|
|
23747
24250
|
}
|
|
24251
|
+
const RESTORE_KEYWORDS = new Set(['initial', 'inherit', 'unset', 'revert']);
|
|
24252
|
+
function elementsOf(value) {
|
|
24253
|
+
if (Array.isArray(value) || isNodeList(value)) {
|
|
24254
|
+
return Array.from(value).filter(isHTMLElement$2);
|
|
24255
|
+
}
|
|
24256
|
+
return isHTMLElement$2(value) ? [value] : [];
|
|
24257
|
+
}
|
|
24258
|
+
async function valueOf(node, evaluator, context) {
|
|
24259
|
+
const value = await evaluator.evaluate(node, context);
|
|
24260
|
+
const name = node.name;
|
|
24261
|
+
if (value === undefined && node.type === 'identifier' && typeof name === 'string')
|
|
24262
|
+
return name;
|
|
24263
|
+
return value;
|
|
24264
|
+
}
|
|
24265
|
+
function longestTransitionMs(css) {
|
|
24266
|
+
let longest;
|
|
24267
|
+
for (const item of css.split(',')) {
|
|
24268
|
+
const times = [...item.matchAll(/(\d*\.?\d+)(ms|s)\b/g)].map(m => parseFloat(m[1]) * (m[2] === 's' ? 1000 : 1));
|
|
24269
|
+
if (times.length > 0)
|
|
24270
|
+
longest = Math.max(longest ?? 0, times[0] + (times[1] ?? 0));
|
|
24271
|
+
}
|
|
24272
|
+
return longest;
|
|
24273
|
+
}
|
|
23748
24274
|
let TransitionCommand = (() => {
|
|
23749
24275
|
let _classDecorators = [command({ name: 'transition' })];
|
|
23750
24276
|
let _classDescriptor;
|
|
@@ -23760,11 +24286,10 @@ let TransitionCommand = (() => {
|
|
|
23760
24286
|
let property;
|
|
23761
24287
|
let target;
|
|
23762
24288
|
const firstArg = await evaluator.evaluate(raw.args[0], context);
|
|
23763
|
-
const
|
|
23764
|
-
|
|
23765
|
-
if (isHTMLElement$2(firstAsElement) ||
|
|
24289
|
+
const owners = elementsOf(firstArg);
|
|
24290
|
+
if (owners.length > 0 ||
|
|
23766
24291
|
(typeof firstArg === 'string' && /^[#.]|^(?:me|it|you)$/.test(firstArg))) {
|
|
23767
|
-
target =
|
|
24292
|
+
target = owners.length === 1 ? owners[0] : owners.length > 1 ? owners : firstArg;
|
|
23768
24293
|
property = String(await evaluator.evaluate(raw.args[1], context));
|
|
23769
24294
|
}
|
|
23770
24295
|
else if (raw.args.length >= 2) {
|
|
@@ -23776,9 +24301,9 @@ let TransitionCommand = (() => {
|
|
|
23776
24301
|
throw new Error('transition requires a CSS property');
|
|
23777
24302
|
property = named.property;
|
|
23778
24303
|
if (named.objectNode !== undefined) {
|
|
23779
|
-
const owner =
|
|
23780
|
-
if (
|
|
23781
|
-
target = owner;
|
|
24304
|
+
const owner = elementsOf(await evaluator.evaluate(named.objectNode, context));
|
|
24305
|
+
if (owner.length > 0)
|
|
24306
|
+
target = owner.length === 1 ? owner[0] : owner;
|
|
23782
24307
|
}
|
|
23783
24308
|
}
|
|
23784
24309
|
else {
|
|
@@ -23789,60 +24314,131 @@ let TransitionCommand = (() => {
|
|
|
23789
24314
|
}
|
|
23790
24315
|
if (!raw.modifiers?.to)
|
|
23791
24316
|
throw new Error('transition requires "to <value>"');
|
|
23792
|
-
|
|
23793
|
-
|
|
23794
|
-
value
|
|
23795
|
-
}
|
|
23796
|
-
const result = { property, value: value };
|
|
24317
|
+
const result = {
|
|
24318
|
+
property,
|
|
24319
|
+
value: await valueOf(raw.modifiers.to, evaluator, context),
|
|
24320
|
+
};
|
|
23797
24321
|
if (target !== undefined)
|
|
23798
24322
|
result.target = target;
|
|
24323
|
+
if (raw.modifiers?.from)
|
|
24324
|
+
result.from = await valueOf(raw.modifiers.from, evaluator, context);
|
|
24325
|
+
if (raw.modifiers?.pairs) {
|
|
24326
|
+
result.pairs = await this.laterPairs(raw.modifiers.pairs, evaluator, context);
|
|
24327
|
+
}
|
|
23799
24328
|
if (raw.modifiers?.over)
|
|
23800
24329
|
result.duration = await evaluator.evaluate(raw.modifiers.over, context);
|
|
23801
24330
|
if (raw.modifiers?.with)
|
|
23802
24331
|
result.timingFunction = String(await evaluator.evaluate(raw.modifiers.with, context));
|
|
24332
|
+
if (raw.modifiers?.using)
|
|
24333
|
+
result.using = String(await evaluator.evaluate(raw.modifiers.using, context));
|
|
23803
24334
|
return result;
|
|
23804
24335
|
}
|
|
24336
|
+
async laterPairs(node, evaluator, context) {
|
|
24337
|
+
const pairs = [];
|
|
24338
|
+
for (const pairNode of node.elements ?? []) {
|
|
24339
|
+
const fields = new Map();
|
|
24340
|
+
const properties = pairNode
|
|
24341
|
+
.properties ?? [];
|
|
24342
|
+
for (const { key, value } of properties) {
|
|
24343
|
+
if (typeof key.name === 'string')
|
|
24344
|
+
fields.set(key.name, value);
|
|
24345
|
+
}
|
|
24346
|
+
const propertyNode = fields.get('property');
|
|
24347
|
+
const toNode = fields.get('to');
|
|
24348
|
+
if (!propertyNode || !toNode)
|
|
24349
|
+
throw new Error('transition requires property and value');
|
|
24350
|
+
const pair = {
|
|
24351
|
+
property: String(await evaluator.evaluate(propertyNode, context)),
|
|
24352
|
+
value: await valueOf(toNode, evaluator, context),
|
|
24353
|
+
};
|
|
24354
|
+
const ownerNode = fields.get('owner');
|
|
24355
|
+
if (ownerNode) {
|
|
24356
|
+
const owners = elementsOf(await evaluator.evaluate(ownerNode, context));
|
|
24357
|
+
if (owners.length === 0)
|
|
24358
|
+
throw new Error('transition: target element not found');
|
|
24359
|
+
pair.target = owners.length === 1 ? owners[0] : owners;
|
|
24360
|
+
}
|
|
24361
|
+
const fromNode = fields.get('from');
|
|
24362
|
+
if (fromNode)
|
|
24363
|
+
pair.from = await valueOf(fromNode, evaluator, context);
|
|
24364
|
+
pairs.push(pair);
|
|
24365
|
+
}
|
|
24366
|
+
return pairs;
|
|
24367
|
+
}
|
|
23805
24368
|
async execute(input, context) {
|
|
23806
|
-
|
|
23807
|
-
const
|
|
23808
|
-
|
|
23809
|
-
|
|
23810
|
-
|
|
23811
|
-
|
|
23812
|
-
|
|
23813
|
-
|
|
23814
|
-
|
|
23815
|
-
|
|
23816
|
-
|
|
23817
|
-
|
|
23818
|
-
|
|
23819
|
-
|
|
23820
|
-
|
|
23821
|
-
|
|
23822
|
-
|
|
23823
|
-
|
|
23824
|
-
|
|
23825
|
-
|
|
23826
|
-
|
|
23827
|
-
|
|
23828
|
-
|
|
23829
|
-
|
|
23830
|
-
|
|
23831
|
-
|
|
23832
|
-
|
|
23833
|
-
|
|
23834
|
-
|
|
23835
|
-
|
|
23836
|
-
|
|
23837
|
-
|
|
24369
|
+
const duration = parseDuration(input.duration, (input.using !== undefined && longestTransitionMs(input.using)) || 300);
|
|
24370
|
+
const timing = input.timingFunction || 'ease';
|
|
24371
|
+
const jobs = [];
|
|
24372
|
+
for (const pair of [input, ...(input.pairs ?? [])]) {
|
|
24373
|
+
let property = pair.property;
|
|
24374
|
+
if (property.startsWith('*'))
|
|
24375
|
+
property = property.substring(1);
|
|
24376
|
+
property = camelToKebab(property);
|
|
24377
|
+
const elements = Array.isArray(pair.target)
|
|
24378
|
+
? pair.target.filter(isHTMLElement$2)
|
|
24379
|
+
: [resolveElement(pair.target, context, 'transition')];
|
|
24380
|
+
for (const element of elements) {
|
|
24381
|
+
jobs.push({
|
|
24382
|
+
element,
|
|
24383
|
+
property,
|
|
24384
|
+
pair,
|
|
24385
|
+
fromValue: getComputedStyle(element).getPropertyValue(property),
|
|
24386
|
+
toValue: '',
|
|
24387
|
+
removeInlineAfter: false,
|
|
24388
|
+
});
|
|
24389
|
+
}
|
|
24390
|
+
}
|
|
24391
|
+
if (jobs.length === 0)
|
|
24392
|
+
throw new Error('transition: target element not found');
|
|
24393
|
+
for (const job of jobs) {
|
|
24394
|
+
if (job.pair.from !== undefined) {
|
|
24395
|
+
job.element.style.setProperty(job.property, String(job.pair.from));
|
|
24396
|
+
}
|
|
24397
|
+
}
|
|
24398
|
+
for (const job of jobs)
|
|
24399
|
+
if (job.pair.from !== undefined)
|
|
24400
|
+
void job.element.offsetWidth;
|
|
24401
|
+
const originals = new Map();
|
|
24402
|
+
for (const job of jobs) {
|
|
24403
|
+
if (!originals.has(job.element))
|
|
24404
|
+
originals.set(job.element, job.element.style.transition);
|
|
24405
|
+
}
|
|
24406
|
+
for (const [element, original] of originals) {
|
|
24407
|
+
const own = jobs
|
|
24408
|
+
.filter(job => job.element === element)
|
|
24409
|
+
.map(job => `${job.property} ${duration}ms ${timing}`);
|
|
24410
|
+
element.style.transition = input.using ?? [original, ...own].filter(Boolean).join(', ');
|
|
24411
|
+
}
|
|
24412
|
+
for (const job of jobs) {
|
|
24413
|
+
const { element, property } = job;
|
|
24414
|
+
let toValue = String(job.pair.value);
|
|
24415
|
+
if (RESTORE_KEYWORDS.has(toValue)) {
|
|
24416
|
+
const currentInline = element.style.getPropertyValue(property);
|
|
24417
|
+
element.style.removeProperty(property);
|
|
24418
|
+
toValue = getComputedStyle(element).getPropertyValue(property);
|
|
24419
|
+
if (currentInline) {
|
|
24420
|
+
element.style.setProperty(property, currentInline);
|
|
24421
|
+
}
|
|
24422
|
+
job.removeInlineAfter = true;
|
|
24423
|
+
}
|
|
24424
|
+
job.toValue = toValue;
|
|
24425
|
+
element.style.setProperty(property, toValue);
|
|
24426
|
+
}
|
|
24427
|
+
const results = await Promise.all(jobs.map(job => waitForTransitionEnd(job.element, job.property, duration)));
|
|
24428
|
+
for (const [element, original] of originals)
|
|
24429
|
+
element.style.transition = original;
|
|
24430
|
+
for (const job of jobs) {
|
|
24431
|
+
if (job.removeInlineAfter)
|
|
24432
|
+
job.element.style.removeProperty(job.property);
|
|
23838
24433
|
}
|
|
24434
|
+
const first = jobs[0];
|
|
23839
24435
|
return {
|
|
23840
|
-
element:
|
|
23841
|
-
property,
|
|
23842
|
-
fromValue,
|
|
23843
|
-
toValue,
|
|
24436
|
+
element: first.element,
|
|
24437
|
+
property: first.property,
|
|
24438
|
+
fromValue: first.fromValue,
|
|
24439
|
+
toValue: first.toValue,
|
|
23844
24440
|
duration,
|
|
23845
|
-
completed: result.completed,
|
|
24441
|
+
completed: results.every(result => result.completed),
|
|
23846
24442
|
};
|
|
23847
24443
|
}
|
|
23848
24444
|
};
|
|
@@ -23855,13 +24451,16 @@ let TransitionCommand = (() => {
|
|
|
23855
24451
|
})();
|
|
23856
24452
|
_classThis.metadata = commandMeta({
|
|
23857
24453
|
description: 'Animate CSS properties using CSS transitions',
|
|
23858
|
-
syntax: 'transition [<
|
|
24454
|
+
syntax: 'transition <property> [from <value>] to <value> [<property> \u2026 to <value>]\u2026 [over <duration>] [using <css> | with <timing>]',
|
|
23859
24455
|
examples: [
|
|
23860
24456
|
'transition opacity to 0.5',
|
|
23861
24457
|
'transition my *opacity to 0 over 200ms',
|
|
23862
24458
|
"transition #box's *opacity to 0 over 200ms",
|
|
23863
24459
|
'transition left to 100px over 500ms',
|
|
23864
24460
|
'transition background-color to red over 1s with ease-in-out',
|
|
24461
|
+
'transition *width to 100px *height to 50px over 300ms',
|
|
24462
|
+
'transition *opacity of #panel from 0 to 1 over 200ms',
|
|
24463
|
+
'transition my *opacity to 0 using "opacity 1s ease-in"',
|
|
23865
24464
|
],
|
|
23866
24465
|
sideEffects: ['style-change', 'timing'],
|
|
23867
24466
|
category: 'animation',
|
|
@@ -25111,17 +25710,7 @@ let BeepCommand = (() => {
|
|
|
25111
25710
|
this.debugContext(context);
|
|
25112
25711
|
return { expressionCount: 0, debugged: true, outputs: [] };
|
|
25113
25712
|
}
|
|
25114
|
-
const outputs =
|
|
25115
|
-
console.group('\ud83d\udd14 beep! Debug Output');
|
|
25116
|
-
for (const expression of expressions) {
|
|
25117
|
-
const output = this.debugExpression(expression);
|
|
25118
|
-
outputs.push(output);
|
|
25119
|
-
console.log(`Value:`, expression);
|
|
25120
|
-
console.log(`Type:`, output.type);
|
|
25121
|
-
console.log(`Representation:`, output.representation);
|
|
25122
|
-
console.log('---');
|
|
25123
|
-
}
|
|
25124
|
-
console.groupEnd();
|
|
25713
|
+
const outputs = beepValues(context.me, expressions);
|
|
25125
25714
|
return { expressionCount: expressions.length, debugged: true, outputs };
|
|
25126
25715
|
}
|
|
25127
25716
|
debugContext(context) {
|
|
@@ -25134,67 +25723,6 @@ let BeepCommand = (() => {
|
|
|
25134
25723
|
console.log('variables:', context.variables);
|
|
25135
25724
|
console.groupEnd();
|
|
25136
25725
|
}
|
|
25137
|
-
debugExpression(expression) {
|
|
25138
|
-
return {
|
|
25139
|
-
value: expression,
|
|
25140
|
-
type: this.getType(expression),
|
|
25141
|
-
representation: this.getRepresentation(expression),
|
|
25142
|
-
};
|
|
25143
|
-
}
|
|
25144
|
-
getType(value) {
|
|
25145
|
-
if (value === null)
|
|
25146
|
-
return 'null';
|
|
25147
|
-
if (value === undefined)
|
|
25148
|
-
return 'undefined';
|
|
25149
|
-
if (Array.isArray(value))
|
|
25150
|
-
return 'array';
|
|
25151
|
-
if (isHTMLElement$2(value))
|
|
25152
|
-
return 'HTMLElement';
|
|
25153
|
-
if (value instanceof Element)
|
|
25154
|
-
return 'Element';
|
|
25155
|
-
if (value instanceof Node)
|
|
25156
|
-
return 'Node';
|
|
25157
|
-
if (value instanceof Error)
|
|
25158
|
-
return 'Error';
|
|
25159
|
-
if (value instanceof Date)
|
|
25160
|
-
return 'Date';
|
|
25161
|
-
if (value instanceof RegExp)
|
|
25162
|
-
return 'RegExp';
|
|
25163
|
-
return typeof value;
|
|
25164
|
-
}
|
|
25165
|
-
getRepresentation(value) {
|
|
25166
|
-
if (value === null)
|
|
25167
|
-
return 'null';
|
|
25168
|
-
if (value === undefined)
|
|
25169
|
-
return 'undefined';
|
|
25170
|
-
if (Array.isArray(value)) {
|
|
25171
|
-
return `Array(${value.length}) [${value
|
|
25172
|
-
.slice(0, 3)
|
|
25173
|
-
.map(v => this.getRepresentation(v))
|
|
25174
|
-
.join(', ')}${value.length > 3 ? '...' : ''}]`;
|
|
25175
|
-
}
|
|
25176
|
-
if (isHTMLElement$2(value)) {
|
|
25177
|
-
const el = value;
|
|
25178
|
-
const tag = el.tagName.toLowerCase();
|
|
25179
|
-
const id = el.id ? `#${el.id}` : '';
|
|
25180
|
-
const classes = el.className ? `.${el.className.split(' ').join('.')}` : '';
|
|
25181
|
-
return `<${tag}${id}${classes}/>`;
|
|
25182
|
-
}
|
|
25183
|
-
if (value instanceof Error)
|
|
25184
|
-
return `Error: ${value.message}`;
|
|
25185
|
-
if (typeof value === 'string')
|
|
25186
|
-
return value.length > 50 ? `"${value.substring(0, 47)}..."` : `"${value}"`;
|
|
25187
|
-
if (typeof value === 'object') {
|
|
25188
|
-
try {
|
|
25189
|
-
const keys = Object.keys(value);
|
|
25190
|
-
return `Object {${keys.slice(0, 3).join(', ')}${keys.length > 3 ? '...' : ''}}`;
|
|
25191
|
-
}
|
|
25192
|
-
catch {
|
|
25193
|
-
return '[Object]';
|
|
25194
|
-
}
|
|
25195
|
-
}
|
|
25196
|
-
return String(value);
|
|
25197
|
-
}
|
|
25198
25726
|
};
|
|
25199
25727
|
__setFunctionName(_classThis, "BeepCommand");
|
|
25200
25728
|
(() => {
|
|
@@ -25742,16 +26270,12 @@ class RenderCommand {
|
|
|
25742
26270
|
}
|
|
25743
26271
|
RenderCommand.metadata = commandMeta({
|
|
25744
26272
|
description: 'Render templates with @if, @else, and @repeat directives',
|
|
25745
|
-
syntax: [
|
|
25746
|
-
'render <template>',
|
|
25747
|
-
'render <template> with <variables>',
|
|
25748
|
-
'render <template> with (key: value, ...)',
|
|
25749
|
-
],
|
|
26273
|
+
syntax: ['render <template>', 'render <template> with <name>: <value>, ...'],
|
|
25750
26274
|
examples: [
|
|
25751
26275
|
'render myTemplate',
|
|
25752
|
-
'render myTemplate with
|
|
25753
|
-
'render "<template>Hello ${name}!</template>" with
|
|
25754
|
-
'render template with
|
|
26276
|
+
'render myTemplate with name: "Alice"',
|
|
26277
|
+
'render "<template>Hello ${name}!</template>" with name: "World"',
|
|
26278
|
+
'render template with items: data',
|
|
25755
26279
|
],
|
|
25756
26280
|
category: 'templates',
|
|
25757
26281
|
compatibility: 'standard',
|
|
@@ -25939,7 +26463,7 @@ function updateDebugStats(event) {
|
|
|
25939
26463
|
stats.confidenceHistory.reduce((a, b) => a + b, 0) / stats.confidenceHistory.length;
|
|
25940
26464
|
}
|
|
25941
26465
|
|
|
25942
|
-
const VERSION = '3.
|
|
26466
|
+
const VERSION = '3.3.0';
|
|
25943
26467
|
|
|
25944
26468
|
function createHistorySwap(config) {
|
|
25945
26469
|
const { target, strategy = 'morph', useViewTransition = false, fetchOptions = {}, transformUrl, onBeforeFetch, onAfterSwap, onError, } = config;
|
|
@@ -27485,6 +28009,14 @@ function convertNode(node, infer) {
|
|
|
27485
28009
|
...pos(node),
|
|
27486
28010
|
};
|
|
27487
28011
|
case 'identifier':
|
|
28012
|
+
if (node.scope === 'element' || node.scope === 'local' || node.scope === 'global') {
|
|
28013
|
+
return {
|
|
28014
|
+
type: 'variable',
|
|
28015
|
+
name: (node.name ?? ''),
|
|
28016
|
+
scope: node.scope,
|
|
28017
|
+
...pos(node),
|
|
28018
|
+
};
|
|
28019
|
+
}
|
|
27488
28020
|
return {
|
|
27489
28021
|
type: 'identifier',
|
|
27490
28022
|
value: (node.name ?? node.value ?? ''),
|
|
@@ -27507,6 +28039,15 @@ function convertNode(node, infer) {
|
|
|
27507
28039
|
operand: convertNode(node.argument ?? node.operand, infer),
|
|
27508
28040
|
...pos(node),
|
|
27509
28041
|
};
|
|
28042
|
+
case 'typeCheckExpression':
|
|
28043
|
+
return {
|
|
28044
|
+
type: 'typeCheck',
|
|
28045
|
+
value: convertNode(node.value, infer),
|
|
28046
|
+
typeName: String(node.typeName),
|
|
28047
|
+
nullOk: node.nullOk !== false,
|
|
28048
|
+
negated: node.negated === true,
|
|
28049
|
+
...pos(node),
|
|
28050
|
+
};
|
|
27510
28051
|
case 'timeExpression':
|
|
27511
28052
|
return { type: 'literal', value: node.value, ...pos(node) };
|
|
27512
28053
|
case 'templateLiteral':
|
|
@@ -27518,6 +28059,28 @@ function convertNode(node, infer) {
|
|
|
27518
28059
|
scope: (node.scope ?? 'local'),
|
|
27519
28060
|
...pos(node),
|
|
27520
28061
|
};
|
|
28062
|
+
case 'arrayLiteral':
|
|
28063
|
+
return {
|
|
28064
|
+
type: 'array',
|
|
28065
|
+
elements: (node.elements ?? []).map(element => convertNode(element, infer)),
|
|
28066
|
+
...pos(node),
|
|
28067
|
+
};
|
|
28068
|
+
case 'attributeAccess':
|
|
28069
|
+
return {
|
|
28070
|
+
type: 'possessive',
|
|
28071
|
+
object: { type: 'identifier', value: 'me' },
|
|
28072
|
+
property: `@${node.attributeName}`,
|
|
28073
|
+
...pos(node),
|
|
28074
|
+
};
|
|
28075
|
+
case 'propertyOfExpression': {
|
|
28076
|
+
const property = node.property;
|
|
28077
|
+
return {
|
|
28078
|
+
type: 'possessive',
|
|
28079
|
+
object: convertNode(node.target, infer),
|
|
28080
|
+
property: (property?.name ?? property?.value ?? ''),
|
|
28081
|
+
...pos(node),
|
|
28082
|
+
};
|
|
28083
|
+
}
|
|
27521
28084
|
case 'htmlSelector':
|
|
27522
28085
|
return {
|
|
27523
28086
|
type: 'selector',
|
|
@@ -27569,6 +28132,21 @@ function convertCommand$1(node, infer) {
|
|
|
27569
28132
|
if (name === 'repeat') {
|
|
27570
28133
|
return convertRepeatCommand(node, infer);
|
|
27571
28134
|
}
|
|
28135
|
+
const original = node.originalCommand;
|
|
28136
|
+
const sum = node.modifiers?.to;
|
|
28137
|
+
const counted = node.args?.[0];
|
|
28138
|
+
if (name === 'set' &&
|
|
28139
|
+
(original === 'increment' || original === 'decrement') &&
|
|
28140
|
+
sum?.type === 'binaryExpression' &&
|
|
28141
|
+
counted) {
|
|
28142
|
+
return convertCommand$1({
|
|
28143
|
+
...node,
|
|
28144
|
+
name: original,
|
|
28145
|
+
args: [counted],
|
|
28146
|
+
modifiers: { by: sum.right },
|
|
28147
|
+
originalCommand: undefined,
|
|
28148
|
+
}, infer);
|
|
28149
|
+
}
|
|
27572
28150
|
const args = (node.args ?? []).map(arg => convertNode(arg, infer));
|
|
27573
28151
|
const target = node.target ? convertNode(node.target, infer) : undefined;
|
|
27574
28152
|
const modifiers = node.modifiers
|
|
@@ -27617,6 +28195,64 @@ function convertIfCommand(node, infer) {
|
|
|
27617
28195
|
};
|
|
27618
28196
|
}
|
|
27619
28197
|
function convertRepeatCommand(node, infer) {
|
|
28198
|
+
const slots = (node.modifiers ?? {});
|
|
28199
|
+
const form = slotText(slots.loopType);
|
|
28200
|
+
if (form === undefined)
|
|
28201
|
+
return convertPositionalRepeat(node, infer);
|
|
28202
|
+
const [bodyBlock, elseBlock] = (node.args ?? []);
|
|
28203
|
+
const body = extractBlockCommands(bodyBlock, infer);
|
|
28204
|
+
const indexName = slotText(slots.index);
|
|
28205
|
+
const shared = {
|
|
28206
|
+
...(indexName ? { indexName } : {}),
|
|
28207
|
+
...(elseBlock ? { elseBody: extractBlockCommands(elseBlock, infer) } : {}),
|
|
28208
|
+
...pos(node),
|
|
28209
|
+
};
|
|
28210
|
+
const operand = (slot) => (slot ? convertNode(slot, infer) : undefined);
|
|
28211
|
+
switch (form) {
|
|
28212
|
+
case 'for':
|
|
28213
|
+
return {
|
|
28214
|
+
type: 'foreach',
|
|
28215
|
+
itemName: slotText(slots.for) ?? 'it',
|
|
28216
|
+
collection: operand(slots.in) ?? { type: 'literal', value: null },
|
|
28217
|
+
body,
|
|
28218
|
+
...shared,
|
|
28219
|
+
};
|
|
28220
|
+
case 'times':
|
|
28221
|
+
return { type: 'repeat', count: operand(slots.times), body, ...shared };
|
|
28222
|
+
case 'while':
|
|
28223
|
+
case 'until': {
|
|
28224
|
+
const test = operand(slots[form]) ?? { type: 'literal', value: form === 'until' };
|
|
28225
|
+
return {
|
|
28226
|
+
type: 'while',
|
|
28227
|
+
condition: form === 'until' ? { type: 'unary', operator: 'not', operand: test } : test,
|
|
28228
|
+
...(slots.bottomTested?.value === true ? { bottomTested: true } : {}),
|
|
28229
|
+
body,
|
|
28230
|
+
...shared,
|
|
28231
|
+
};
|
|
28232
|
+
}
|
|
28233
|
+
case 'until-event': {
|
|
28234
|
+
const event = slotText(slots.event);
|
|
28235
|
+
if (!event) {
|
|
28236
|
+
return { type: 'error', message: "'repeat until event' has no event name", ...pos(node) };
|
|
28237
|
+
}
|
|
28238
|
+
const target = operand(slots.from);
|
|
28239
|
+
return {
|
|
28240
|
+
type: 'repeat',
|
|
28241
|
+
untilEvent: event,
|
|
28242
|
+
...(target ? { untilEventTarget: target } : {}),
|
|
28243
|
+
body,
|
|
28244
|
+
...shared,
|
|
28245
|
+
};
|
|
28246
|
+
}
|
|
28247
|
+
default:
|
|
28248
|
+
return { type: 'repeat', body, ...shared };
|
|
28249
|
+
}
|
|
28250
|
+
}
|
|
28251
|
+
function slotText(slot) {
|
|
28252
|
+
const text = slot?.value ?? slot?.name;
|
|
28253
|
+
return typeof text === 'string' ? text : undefined;
|
|
28254
|
+
}
|
|
28255
|
+
function convertPositionalRepeat(node, infer) {
|
|
27620
28256
|
const args = (node.args ?? []);
|
|
27621
28257
|
if (args.length === 0) {
|
|
27622
28258
|
return { type: 'repeat', body: [], ...pos(node) };
|
|
@@ -27933,6 +28569,15 @@ function toCoreAST(node) {
|
|
|
27933
28569
|
prefix: true,
|
|
27934
28570
|
...nodePos(node),
|
|
27935
28571
|
};
|
|
28572
|
+
case 'typeCheck':
|
|
28573
|
+
return {
|
|
28574
|
+
type: 'typeCheckExpression',
|
|
28575
|
+
value: toCoreAST(node.value),
|
|
28576
|
+
typeName: node.typeName,
|
|
28577
|
+
nullOk: node.nullOk,
|
|
28578
|
+
negated: node.negated,
|
|
28579
|
+
...nodePos(node),
|
|
28580
|
+
};
|
|
27936
28581
|
case 'member':
|
|
27937
28582
|
return {
|
|
27938
28583
|
type: 'memberExpression',
|
|
@@ -27943,6 +28588,12 @@ function toCoreAST(node) {
|
|
|
27943
28588
|
computed: node.computed ?? false,
|
|
27944
28589
|
...nodePos(node),
|
|
27945
28590
|
};
|
|
28591
|
+
case 'array':
|
|
28592
|
+
return {
|
|
28593
|
+
type: 'arrayLiteral',
|
|
28594
|
+
elements: node.elements.map(element => toCoreAST(element)),
|
|
28595
|
+
...nodePos(node),
|
|
28596
|
+
};
|
|
27946
28597
|
case 'possessive':
|
|
27947
28598
|
return {
|
|
27948
28599
|
type: 'possessiveExpression',
|
|
@@ -28005,72 +28656,85 @@ function convertCommand(node) {
|
|
|
28005
28656
|
}
|
|
28006
28657
|
return result;
|
|
28007
28658
|
}
|
|
28659
|
+
function block(nodes) {
|
|
28660
|
+
return { type: 'block', commands: nodes.map(n => toCoreAST(n)), ...POS };
|
|
28661
|
+
}
|
|
28008
28662
|
function convertIf(node) {
|
|
28009
|
-
const
|
|
28663
|
+
const [elseIf, ...laterElseIfs] = node.elseIfBranches ?? [];
|
|
28664
|
+
const elseBranch = elseIf
|
|
28665
|
+
? [
|
|
28666
|
+
{
|
|
28667
|
+
type: 'if',
|
|
28668
|
+
condition: elseIf.condition,
|
|
28669
|
+
thenBranch: elseIf.body,
|
|
28670
|
+
elseIfBranches: laterElseIfs,
|
|
28671
|
+
...(node.elseBranch ? { elseBranch: node.elseBranch } : {}),
|
|
28672
|
+
},
|
|
28673
|
+
]
|
|
28674
|
+
: node.elseBranch;
|
|
28675
|
+
return {
|
|
28010
28676
|
type: 'command',
|
|
28011
28677
|
name: 'if',
|
|
28012
28678
|
isBlocking: true,
|
|
28013
|
-
|
|
28014
|
-
|
|
28015
|
-
|
|
28679
|
+
args: [
|
|
28680
|
+
toCoreAST(node.condition),
|
|
28681
|
+
block(node.thenBranch),
|
|
28682
|
+
...(elseBranch ? [block(elseBranch)] : []),
|
|
28683
|
+
],
|
|
28016
28684
|
...nodePos(node),
|
|
28017
28685
|
};
|
|
28018
|
-
if (node.elseBranch) {
|
|
28019
|
-
result.elseBranch = node.elseBranch.map(n => toCoreAST(n));
|
|
28020
|
-
}
|
|
28021
|
-
return result;
|
|
28022
28686
|
}
|
|
28023
|
-
function
|
|
28024
|
-
const
|
|
28687
|
+
function loopCommand(node, form, operands) {
|
|
28688
|
+
const modifiers = { loopType: slotString(form) };
|
|
28689
|
+
for (const [slot, value] of Object.entries(operands)) {
|
|
28690
|
+
if (value)
|
|
28691
|
+
modifiers[slot] = value;
|
|
28692
|
+
}
|
|
28693
|
+
if (node.indexName)
|
|
28694
|
+
modifiers.index = slotString(node.indexName);
|
|
28695
|
+
return {
|
|
28025
28696
|
type: 'command',
|
|
28026
28697
|
name: 'repeat',
|
|
28027
28698
|
isBlocking: true,
|
|
28028
|
-
|
|
28029
|
-
|
|
28699
|
+
args: [block(node.body), ...(node.elseBody ? [block(node.elseBody)] : [])],
|
|
28700
|
+
modifiers,
|
|
28030
28701
|
...nodePos(node),
|
|
28031
28702
|
};
|
|
28703
|
+
}
|
|
28704
|
+
function slotString(value) {
|
|
28705
|
+
return { type: 'string', value, ...POS };
|
|
28706
|
+
}
|
|
28707
|
+
function convertRepeat(node) {
|
|
28032
28708
|
if (node.count !== undefined) {
|
|
28033
|
-
|
|
28034
|
-
|
|
28035
|
-
|
|
28036
|
-
|
|
28037
|
-
|
|
28038
|
-
|
|
28039
|
-
|
|
28040
|
-
|
|
28041
|
-
|
|
28042
|
-
|
|
28043
|
-
|
|
28044
|
-
|
|
28045
|
-
|
|
28709
|
+
const times = typeof node.count === 'number'
|
|
28710
|
+
? { type: 'literal', value: node.count, raw: String(node.count), ...POS }
|
|
28711
|
+
: toCoreAST(node.count);
|
|
28712
|
+
return loopCommand(node, 'times', { times });
|
|
28713
|
+
}
|
|
28714
|
+
if (node.whileCondition !== undefined) {
|
|
28715
|
+
return loopCommand(node, 'while', { while: toCoreAST(node.whileCondition) });
|
|
28716
|
+
}
|
|
28717
|
+
if (node.untilEvent !== undefined) {
|
|
28718
|
+
return loopCommand(node, 'until-event', {
|
|
28719
|
+
event: slotString(node.untilEvent),
|
|
28720
|
+
from: node.untilEventTarget ? toCoreAST(node.untilEventTarget) : undefined,
|
|
28721
|
+
});
|
|
28046
28722
|
}
|
|
28047
|
-
return
|
|
28723
|
+
return loopCommand(node, 'forever', {});
|
|
28048
28724
|
}
|
|
28049
28725
|
function convertForEach(node) {
|
|
28050
|
-
return {
|
|
28051
|
-
|
|
28052
|
-
|
|
28053
|
-
|
|
28054
|
-
loopVariant: 'for',
|
|
28055
|
-
itemName: node.itemName,
|
|
28056
|
-
...(node.indexName ? { indexName: node.indexName } : {}),
|
|
28057
|
-
collection: toCoreAST(node.collection),
|
|
28058
|
-
body: node.body.map(n => toCoreAST(n)),
|
|
28059
|
-
args: [],
|
|
28060
|
-
...nodePos(node),
|
|
28061
|
-
};
|
|
28726
|
+
return loopCommand(node, 'for', {
|
|
28727
|
+
for: slotString(node.itemName),
|
|
28728
|
+
in: toCoreAST(node.collection),
|
|
28729
|
+
});
|
|
28062
28730
|
}
|
|
28063
28731
|
function convertWhile(node) {
|
|
28064
|
-
return {
|
|
28065
|
-
|
|
28066
|
-
|
|
28067
|
-
|
|
28068
|
-
|
|
28069
|
-
|
|
28070
|
-
body: node.body.map(n => toCoreAST(n)),
|
|
28071
|
-
args: [],
|
|
28072
|
-
...nodePos(node),
|
|
28073
|
-
};
|
|
28732
|
+
return loopCommand(node, 'while', {
|
|
28733
|
+
while: toCoreAST(node.condition),
|
|
28734
|
+
bottomTested: node.bottomTested
|
|
28735
|
+
? { type: 'literal', value: true, raw: 'true', ...POS }
|
|
28736
|
+
: undefined,
|
|
28737
|
+
});
|
|
28074
28738
|
}
|
|
28075
28739
|
function flattenEventModifiers(modifiers) {
|
|
28076
28740
|
if (!modifiers)
|