@hyperfixi/core 3.1.1 → 3.2.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 +24 -9
- package/dist/bundle-generator/index.mjs +24 -9
- package/dist/bundle-generator/parser-templates.d.ts +1 -1
- package/dist/chunks/bridge-Cnj8dQFV.js +2 -0
- package/dist/chunks/browser-modular-BNDYF6Dq.js +2 -0
- package/dist/chunks/index-DQi2o8Lk.js +2 -0
- package/dist/chunks/index-LLuwEQp0.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 +929 -425
- package/dist/commands/index.mjs +929 -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 +1140 -485
- package/dist/index.min.js +1 -1
- package/dist/index.mjs +1140 -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 +387 -153
- package/dist/parser/full-parser.mjs +387 -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 +526 -210
- package/dist/registry/index.mjs +526 -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')) {
|
|
@@ -3979,11 +4124,17 @@ function parseSwapCommand(ctx, identifierNode) {
|
|
|
3979
4124
|
const targetExpr = isDelete ? ctx.parseExpression() : parseOneArgument(ctx, [KEYWORDS.WITH]);
|
|
3980
4125
|
if (targetExpr)
|
|
3981
4126
|
args.push(targetExpr);
|
|
3982
|
-
|
|
4127
|
+
const isMorph = identifierNode.name === 'morph';
|
|
4128
|
+
if (!isDelete &&
|
|
4129
|
+
(consumeOptionalKeyword(ctx, KEYWORDS.WITH) ||
|
|
4130
|
+
(isMorph && consumeOptionalKeyword(ctx, KEYWORDS.TO)))) {
|
|
3983
4131
|
const contentExpr = parseOneArgument(ctx, ['using']);
|
|
3984
4132
|
if (contentExpr)
|
|
3985
4133
|
modifiers['with'] = contentExpr;
|
|
3986
4134
|
}
|
|
4135
|
+
if (isMorph && modifiers['with'] === undefined) {
|
|
4136
|
+
ctx.addError("Expected 'to' and the content to morph into");
|
|
4137
|
+
}
|
|
3987
4138
|
return CommandNodeBuilder.fromIdentifier(identifierNode)
|
|
3988
4139
|
.withArgs(...args)
|
|
3989
4140
|
.withModifiers({ ...modifiers, ...parseViewTransitionTail(ctx) })
|
|
@@ -4744,6 +4895,8 @@ function atArgumentBoundary(ctx, grammar) {
|
|
|
4744
4895
|
const next = ctx.peek();
|
|
4745
4896
|
if (ctx.checkIsCommand() && ctx.peekAt(1)?.value === '(')
|
|
4746
4897
|
return false;
|
|
4898
|
+
if (isGluedBeepBang(next, ctx.peekAt(1)))
|
|
4899
|
+
return false;
|
|
4747
4900
|
if (isCommandBoundary(ctx, ['catch', 'finally', 'on']))
|
|
4748
4901
|
return true;
|
|
4749
4902
|
return isKeyword(next, [...grammar.markers]);
|
|
@@ -4775,10 +4928,13 @@ function parseDeclaredCommand(ctx, commandToken, name, grammar) {
|
|
|
4775
4928
|
}
|
|
4776
4929
|
}
|
|
4777
4930
|
const commaList = new Set(grammar.commaList ?? []);
|
|
4931
|
+
const namedArgs = new Set(grammar.namedArgs ?? []);
|
|
4778
4932
|
while (!ctx.isAtEnd() && isKeyword(ctx.peek(), [...grammar.markers])) {
|
|
4779
4933
|
const keyword = ctx.advance().value;
|
|
4780
4934
|
const key = ctx.resolveKeyword(keyword).toLowerCase();
|
|
4781
|
-
let value =
|
|
4935
|
+
let value = namedArgs.has(key) && isNakedNamedArgStart(ctx)
|
|
4936
|
+
? parseNakedNamedArgs(ctx, () => ctx.parseExpression(), name)
|
|
4937
|
+
: ctx.parseExpression();
|
|
4782
4938
|
if (value && commaList.has(key) && ctx.check(',')) {
|
|
4783
4939
|
const elements = [value];
|
|
4784
4940
|
while (ctx.match(',')) {
|
|
@@ -4943,8 +5099,8 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
4943
5099
|
}
|
|
4944
5100
|
if (ctx.check('with') && !modifiers['with']) {
|
|
4945
5101
|
ctx.advance();
|
|
4946
|
-
if (
|
|
4947
|
-
modifiers['with'] =
|
|
5102
|
+
if (isNakedNamedArgStart(ctx)) {
|
|
5103
|
+
modifiers['with'] = parseNakedNamedArgs(ctx, () => ctx.parsePrimary(), 'fetch');
|
|
4948
5104
|
}
|
|
4949
5105
|
else {
|
|
4950
5106
|
modifiers['with'] = ctx.parsePrimary();
|
|
@@ -4979,45 +5135,6 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
4979
5135
|
}
|
|
4980
5136
|
return builder.build();
|
|
4981
5137
|
}
|
|
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
5138
|
function findJsEndBoundary(ctx, startPos) {
|
|
5022
5139
|
const input = ctx.getInputSlice(startPos);
|
|
5023
5140
|
if (!input) {
|
|
@@ -5137,6 +5254,7 @@ function parseTellCommand(ctx, identifierNode) {
|
|
|
5137
5254
|
const commands = [];
|
|
5138
5255
|
while (!ctx.isAtEnd()) {
|
|
5139
5256
|
if (ctx.checkIsCommand()) {
|
|
5257
|
+
const word = ctx.peek().value;
|
|
5140
5258
|
try {
|
|
5141
5259
|
ctx.advance();
|
|
5142
5260
|
const cmd = ctx.parseCommand();
|
|
@@ -5147,7 +5265,8 @@ function parseTellCommand(ctx, identifierNode) {
|
|
|
5147
5265
|
break;
|
|
5148
5266
|
}
|
|
5149
5267
|
}
|
|
5150
|
-
catch {
|
|
5268
|
+
catch (error) {
|
|
5269
|
+
ctx.addError(`'${word}' in a tell body failed to parse: ${error instanceof Error ? error.message : String(error)}`);
|
|
5151
5270
|
break;
|
|
5152
5271
|
}
|
|
5153
5272
|
if (ctx.match(KEYWORDS.AND) || ctx.match(KEYWORDS.THEN)) {
|
|
@@ -5296,7 +5415,6 @@ const ONE_EXPR = (syntax) => ({
|
|
|
5296
5415
|
markers: [],
|
|
5297
5416
|
syntax,
|
|
5298
5417
|
});
|
|
5299
|
-
const BEEP = ONE_EXPR('beep! [<expression>, \u2026]');
|
|
5300
5418
|
const COMMAND_GRAMMAR = {
|
|
5301
5419
|
append: { positional: 'expression', markers: ['to'], syntax: 'append <value> [to <target>]' },
|
|
5302
5420
|
prepend: { positional: 'expression', markers: ['to'], syntax: 'prepend <value> [to <target>]' },
|
|
@@ -5307,8 +5425,7 @@ const COMMAND_GRAMMAR = {
|
|
|
5307
5425
|
syntax: 'make (a|an) <type> [from <args>] [called <name>]',
|
|
5308
5426
|
},
|
|
5309
5427
|
throw: { positional: 'primary', markers: [], syntax: 'throw <error>' },
|
|
5310
|
-
'beep!'
|
|
5311
|
-
beep: BEEP,
|
|
5428
|
+
beep: ONE_EXPR('beep! [<expression>, \u2026]'),
|
|
5312
5429
|
blur: ONE_EXPR('blur [<target>]'),
|
|
5313
5430
|
break: NONE,
|
|
5314
5431
|
breakpoint: NONE,
|
|
@@ -5332,7 +5449,8 @@ const COMMAND_GRAMMAR = {
|
|
|
5332
5449
|
render: {
|
|
5333
5450
|
positional: 'expression',
|
|
5334
5451
|
markers: ['with'],
|
|
5335
|
-
|
|
5452
|
+
namedArgs: ['with'],
|
|
5453
|
+
syntax: 'render <template> [with <name>: <value>, \u2026]',
|
|
5336
5454
|
},
|
|
5337
5455
|
reset: ONE_EXPR('reset [<form>]'),
|
|
5338
5456
|
return: ONE_EXPR('return [<value>]'),
|
|
@@ -5687,7 +5805,10 @@ class Parser {
|
|
|
5687
5805
|
const firstToken = this.peek();
|
|
5688
5806
|
const prefixEntry = Parser.PRATT_TABLE.get(firstToken.value);
|
|
5689
5807
|
let left;
|
|
5690
|
-
if (
|
|
5808
|
+
if (isGluedBeepBang(firstToken, this.tokens[this.current + 1])) {
|
|
5809
|
+
left = this.parseBeepExpression();
|
|
5810
|
+
}
|
|
5811
|
+
else if (prefixEntry?.prefix) {
|
|
5691
5812
|
const token = this.advance();
|
|
5692
5813
|
if (this.isAtEnd()) {
|
|
5693
5814
|
this.addError(`Expected expression after '${token.value}' operator`);
|
|
@@ -5707,6 +5828,9 @@ class Parser {
|
|
|
5707
5828
|
continue;
|
|
5708
5829
|
}
|
|
5709
5830
|
}
|
|
5831
|
+
if (nextToken.value === '*' && this.isSpacedStyleRef(this.current)) {
|
|
5832
|
+
break;
|
|
5833
|
+
}
|
|
5710
5834
|
const infixEntry = Parser.PRATT_TABLE.get(nextToken.value);
|
|
5711
5835
|
if (!infixEntry?.infix) {
|
|
5712
5836
|
if (STOP_TOKENS.has(nextToken.value) || STOP_DELIMITERS.has(nextToken.value)) {
|
|
@@ -5768,6 +5892,35 @@ class Parser {
|
|
|
5768
5892
|
this.prattCache.set(cacheKey, { node: left, endPos: this.current });
|
|
5769
5893
|
return left;
|
|
5770
5894
|
}
|
|
5895
|
+
parseBeepExpression() {
|
|
5896
|
+
const beepToken = this.advance();
|
|
5897
|
+
this.advance();
|
|
5898
|
+
if (this.isAtEnd()) {
|
|
5899
|
+
this.addError("Expected expression after 'beep!'");
|
|
5900
|
+
return this.createErrorNode();
|
|
5901
|
+
}
|
|
5902
|
+
const operand = this.parseExpressionPratt(Parser.BEEP_OPERAND_BP);
|
|
5903
|
+
return {
|
|
5904
|
+
type: 'unaryExpression',
|
|
5905
|
+
operator: 'beep!',
|
|
5906
|
+
operand,
|
|
5907
|
+
argument: operand,
|
|
5908
|
+
prefix: true,
|
|
5909
|
+
start: beepToken.start,
|
|
5910
|
+
end: this.previous().end,
|
|
5911
|
+
line: beepToken.line,
|
|
5912
|
+
column: beepToken.column,
|
|
5913
|
+
};
|
|
5914
|
+
}
|
|
5915
|
+
isStyleRefStar(i) {
|
|
5916
|
+
const star = this.tokens[i];
|
|
5917
|
+
const name = this.tokens[i + 1];
|
|
5918
|
+
return star?.value === '*' && !!name && name.start === star.end && isIdentifierLike(name);
|
|
5919
|
+
}
|
|
5920
|
+
isSpacedStyleRef(i) {
|
|
5921
|
+
const prev = this.tokens[i - 1];
|
|
5922
|
+
return this.isStyleRefStar(i) && !!prev && prev.end < this.tokens[i].start;
|
|
5923
|
+
}
|
|
5771
5924
|
tryParseStringPostfix(left, nextToken) {
|
|
5772
5925
|
const v = nextToken.value;
|
|
5773
5926
|
let unit = null;
|
|
@@ -5970,6 +6123,10 @@ class Parser {
|
|
|
5970
6123
|
const isStop = () => this.check('end') || extraStops.some(s => this.check(s));
|
|
5971
6124
|
debug.parse('\ud83d\udd04 parseCommandListUntilTerminator: Starting (extraStops:', extraStops.join(','), ')');
|
|
5972
6125
|
while (!this.isAtEnd() && !isStop()) {
|
|
6126
|
+
if (this.checkComment()) {
|
|
6127
|
+
this.advance();
|
|
6128
|
+
continue;
|
|
6129
|
+
}
|
|
5973
6130
|
debug.parse('\ud83d\udccd Loop iteration, current token:', this.peek().value, 'kind:', this.peek().kind);
|
|
5974
6131
|
let parsedCommand = false;
|
|
5975
6132
|
const isCommandToken = this.checkIsCommand();
|
|
@@ -6053,7 +6210,7 @@ class Parser {
|
|
|
6053
6210
|
debug.parse('\u2705 Found "end", consuming it');
|
|
6054
6211
|
this.advance();
|
|
6055
6212
|
}
|
|
6056
|
-
else {
|
|
6213
|
+
else if (!this.atEndOfInput()) {
|
|
6057
6214
|
debug.parse('\u274c ERROR: Expected "end" but got:', this.peek().value, 'at position:', this.peek().start);
|
|
6058
6215
|
throw new Error(`Expected "end" to close ${construct} block`);
|
|
6059
6216
|
}
|
|
@@ -6066,7 +6223,7 @@ class Parser {
|
|
|
6066
6223
|
if (this.check('end')) {
|
|
6067
6224
|
this.advance();
|
|
6068
6225
|
}
|
|
6069
|
-
else {
|
|
6226
|
+
else if (!this.atEndOfInput()) {
|
|
6070
6227
|
throw new Error('Expected "end" to close repeat block');
|
|
6071
6228
|
}
|
|
6072
6229
|
}
|
|
@@ -6082,7 +6239,10 @@ class Parser {
|
|
|
6082
6239
|
this.advance();
|
|
6083
6240
|
}
|
|
6084
6241
|
else if (terminator === '') {
|
|
6085
|
-
|
|
6242
|
+
if (!this.atEndOfInput()) {
|
|
6243
|
+
throw new Error('Expected "end", "else", "until", or "while" to close repeat block');
|
|
6244
|
+
}
|
|
6245
|
+
return { commands, terminator: 'end' };
|
|
6086
6246
|
}
|
|
6087
6247
|
return { commands, terminator };
|
|
6088
6248
|
}
|
|
@@ -6666,7 +6826,9 @@ class Parser {
|
|
|
6666
6826
|
parseTopLevelInitBlock() {
|
|
6667
6827
|
const pos = this.getPosition();
|
|
6668
6828
|
const initCommands = this.parseCommandBlock(['end']);
|
|
6669
|
-
this.
|
|
6829
|
+
if (!this.match('end') && !this.atEndOfInput() && !this.checkFeatureStart()) {
|
|
6830
|
+
this.consume('end', "Expected 'end' after init block");
|
|
6831
|
+
}
|
|
6670
6832
|
return {
|
|
6671
6833
|
type: 'initBlock',
|
|
6672
6834
|
commands: initCommands,
|
|
@@ -6842,10 +7004,29 @@ class Parser {
|
|
|
6842
7004
|
...(sourceExpr.column !== undefined && { column: sourceExpr.column }),
|
|
6843
7005
|
};
|
|
6844
7006
|
}
|
|
7007
|
+
isArgumentListOnly(open) {
|
|
7008
|
+
let depth = 0;
|
|
7009
|
+
for (let i = open; i < this.tokens.length; i++) {
|
|
7010
|
+
const token = this.tokens[i];
|
|
7011
|
+
if (token.kind !== 'operator')
|
|
7012
|
+
continue;
|
|
7013
|
+
if (token.value === '(' || token.value === '[' || token.value === '{')
|
|
7014
|
+
depth++;
|
|
7015
|
+
else if (token.value === ')' || token.value === ']' || token.value === '}') {
|
|
7016
|
+
if (--depth === 0)
|
|
7017
|
+
return i === open + 1;
|
|
7018
|
+
}
|
|
7019
|
+
else if (token.value === ',' && depth === 1)
|
|
7020
|
+
return true;
|
|
7021
|
+
}
|
|
7022
|
+
return false;
|
|
7023
|
+
}
|
|
6845
7024
|
tryParsePseudoCommand(callExpr, sourceExpr) {
|
|
6846
7025
|
const methodName = callExpr.callee.name;
|
|
6847
7026
|
const nextToken = this.peek();
|
|
6848
|
-
const
|
|
7027
|
+
const nextIsArticle = nextToken.value.toLowerCase() === 'the';
|
|
7028
|
+
const hasPseudoCommandPattern = nextIsArticle ||
|
|
7029
|
+
Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(nextToken.value.toLowerCase()) ||
|
|
6849
7030
|
(isIdentifier(nextToken) && !this.isCommand(nextToken.value)) ||
|
|
6850
7031
|
isContextVar(nextToken);
|
|
6851
7032
|
if (!hasPseudoCommandPattern) {
|
|
@@ -6871,7 +7052,10 @@ class Parser {
|
|
|
6871
7052
|
});
|
|
6872
7053
|
}
|
|
6873
7054
|
let preposition;
|
|
6874
|
-
if (
|
|
7055
|
+
if (nextIsArticle) {
|
|
7056
|
+
this.advance();
|
|
7057
|
+
}
|
|
7058
|
+
else if (Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(this.peek().value.toLowerCase())) {
|
|
6875
7059
|
preposition = this.advance().value.toLowerCase();
|
|
6876
7060
|
}
|
|
6877
7061
|
let targetExpr;
|
|
@@ -7035,10 +7219,22 @@ class Parser {
|
|
|
7035
7219
|
this.consume(']', "Expected ']' after event condition");
|
|
7036
7220
|
}
|
|
7037
7221
|
let target;
|
|
7222
|
+
let targetExpression;
|
|
7038
7223
|
if (this.match('from')) {
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7224
|
+
if (this.check('the'))
|
|
7225
|
+
this.advance();
|
|
7226
|
+
if (Parser.POSITIONAL_SOURCE_HEADS.has(this.peek().value.toLowerCase())) {
|
|
7227
|
+
const startIndex = this.current;
|
|
7228
|
+
targetExpression = this.parseExpression();
|
|
7229
|
+
target = this.tokens
|
|
7230
|
+
.slice(startIndex, this.current)
|
|
7231
|
+
.map(t => t.value)
|
|
7232
|
+
.join(' ');
|
|
7233
|
+
}
|
|
7234
|
+
else {
|
|
7235
|
+
target = this.advance().value;
|
|
7236
|
+
}
|
|
7237
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed 'from' target: ${target}`);
|
|
7042
7238
|
this.parseTemporalKeywordModifiers(modifiers);
|
|
7043
7239
|
}
|
|
7044
7240
|
let attributeName;
|
|
@@ -7084,7 +7280,7 @@ class Parser {
|
|
|
7084
7280
|
const nextIsOpenParen = this.tokens[this.current + 1]?.value === '(';
|
|
7085
7281
|
const commandName = this.peek().value.toLowerCase();
|
|
7086
7282
|
const isSpecialBodyCommand = commandName === 'js' || commandName === 'tell';
|
|
7087
|
-
if (nextIsOpenParen && !isSpecialBodyCommand) {
|
|
7283
|
+
if (nextIsOpenParen && !isSpecialBodyCommand && this.isArgumentListOnly(this.current + 1)) {
|
|
7088
7284
|
let expr;
|
|
7089
7285
|
const savedError = this.error;
|
|
7090
7286
|
try {
|
|
@@ -7116,7 +7312,7 @@ class Parser {
|
|
|
7116
7312
|
}
|
|
7117
7313
|
}
|
|
7118
7314
|
}
|
|
7119
|
-
else if (this.checkIdentifier()) {
|
|
7315
|
+
else if (this.checkIdentifier() || this.checkEventNamedCall()) {
|
|
7120
7316
|
const token = this.peek();
|
|
7121
7317
|
if (this.isCommand(token.value)) {
|
|
7122
7318
|
const cmd = this.parseCommandWithErrorRecovery();
|
|
@@ -7225,6 +7421,7 @@ class Parser {
|
|
|
7225
7421
|
...(eventParams.length > 0 && { args: eventParams }),
|
|
7226
7422
|
...(condition && { condition: fromLegacyExpression(condition) }),
|
|
7227
7423
|
...(target && { target }),
|
|
7424
|
+
...(targetExpression && { targetExpression: fromLegacyExpression(targetExpression) }),
|
|
7228
7425
|
...(attributeName && { attributeName }),
|
|
7229
7426
|
...(watchTarget && { watchTarget: fromLegacyExpression(watchTarget) }),
|
|
7230
7427
|
...(customEventSource && { customEventSource }),
|
|
@@ -7480,10 +7677,9 @@ class Parser {
|
|
|
7480
7677
|
}
|
|
7481
7678
|
parseCommandCore() {
|
|
7482
7679
|
const commandToken = this.previous();
|
|
7483
|
-
|
|
7680
|
+
const commandName = commandToken.value;
|
|
7484
7681
|
if (commandName === 'beep' && this.check('!')) {
|
|
7485
7682
|
this.advance();
|
|
7486
|
-
commandName = 'beep!';
|
|
7487
7683
|
}
|
|
7488
7684
|
if (commandName === 'fetch') {
|
|
7489
7685
|
return parseFetchCommand(this.getContext(), commandToken);
|
|
@@ -7582,6 +7778,9 @@ class Parser {
|
|
|
7582
7778
|
args.push(this.parsePrimary());
|
|
7583
7779
|
}
|
|
7584
7780
|
const callNode = this.createCallExpression(createIdentifier(funcName, namePos), args);
|
|
7781
|
+
if (funcName === 'closest' && args.length > 0 && this.match('to')) {
|
|
7782
|
+
callNode.closestTo = this.parseExpression();
|
|
7783
|
+
}
|
|
7585
7784
|
if (funcName === 'next' || funcName === 'previous') {
|
|
7586
7785
|
const relativePositional = this.parseRelativePositionalModifiers();
|
|
7587
7786
|
if (relativePositional) {
|
|
@@ -7621,6 +7820,9 @@ class Parser {
|
|
|
7621
7820
|
}
|
|
7622
7821
|
parseTheXofY() {
|
|
7623
7822
|
const positionalKeywords = ['first', 'last', 'next', 'previous', 'random', 'closest'];
|
|
7823
|
+
if (this.isStyleRefStar(this.current)) {
|
|
7824
|
+
return this.parsePrimary();
|
|
7825
|
+
}
|
|
7624
7826
|
if (!this.checkIdentifier()) {
|
|
7625
7827
|
return this.createIdentifier('the');
|
|
7626
7828
|
}
|
|
@@ -7636,7 +7838,7 @@ class Parser {
|
|
|
7636
7838
|
return this.createIdentifier(propertyName);
|
|
7637
7839
|
}
|
|
7638
7840
|
this.advance();
|
|
7639
|
-
const target = this.
|
|
7841
|
+
const target = parseOfOperand(this.makePrattContext(), THE_OF_TARGET_BP);
|
|
7640
7842
|
return {
|
|
7641
7843
|
type: 'propertyOfExpression',
|
|
7642
7844
|
property: { type: 'identifier', name: propertyName },
|
|
@@ -7834,6 +8036,13 @@ class Parser {
|
|
|
7834
8036
|
checkLiteral() {
|
|
7835
8037
|
return this.checkPredicate(isLiteral);
|
|
7836
8038
|
}
|
|
8039
|
+
checkEventNamedCall() {
|
|
8040
|
+
if (this.isAtEnd() || !this.checkIdentifierLike() || this.checkIsCommand())
|
|
8041
|
+
return false;
|
|
8042
|
+
const token = this.peek();
|
|
8043
|
+
const next = this.tokens[this.current + 1];
|
|
8044
|
+
return next?.value === '(' && next.start === token.end;
|
|
8045
|
+
}
|
|
7837
8046
|
checkIdentifierLike() {
|
|
7838
8047
|
return this.checkPredicate(isIdentifierLike);
|
|
7839
8048
|
}
|
|
@@ -7942,6 +8151,22 @@ class Parser {
|
|
|
7942
8151
|
isAtEnd() {
|
|
7943
8152
|
return this.current >= this.tokens.length;
|
|
7944
8153
|
}
|
|
8154
|
+
atEndOfInput() {
|
|
8155
|
+
for (let i = this.current; i < this.tokens.length; i++) {
|
|
8156
|
+
if (!isComment(this.tokens[i]))
|
|
8157
|
+
return false;
|
|
8158
|
+
}
|
|
8159
|
+
return true;
|
|
8160
|
+
}
|
|
8161
|
+
checkFeatureStart() {
|
|
8162
|
+
if (this.isAtEnd())
|
|
8163
|
+
return false;
|
|
8164
|
+
const value = this.peek().value;
|
|
8165
|
+
return (value === 'init' ||
|
|
8166
|
+
value === 'on' ||
|
|
8167
|
+
value === 'def' ||
|
|
8168
|
+
getRegisteredFeature(value) !== undefined);
|
|
8169
|
+
}
|
|
7945
8170
|
peek() {
|
|
7946
8171
|
if (this.isAtEnd()) {
|
|
7947
8172
|
return { kind: 'unknown', value: '', start: 0, end: 0, line: 1, column: 1 };
|
|
@@ -8153,6 +8378,7 @@ class Parser {
|
|
|
8153
8378
|
match: this.match.bind(this),
|
|
8154
8379
|
matchOperator: this.matchOperator.bind(this),
|
|
8155
8380
|
isAtEnd: this.isAtEnd.bind(this),
|
|
8381
|
+
atEndOfInput: this.atEndOfInput.bind(this),
|
|
8156
8382
|
createIdentifier: this.createIdentifier.bind(this),
|
|
8157
8383
|
parseExpression: this.parseExpression.bind(this),
|
|
8158
8384
|
parsePrimary: this.parsePrimary.bind(this),
|
|
@@ -8203,7 +8429,15 @@ Parser.POSTFIX_UNARY_OPERATORS = new Set([
|
|
|
8203
8429
|
'ignoring case',
|
|
8204
8430
|
]);
|
|
8205
8431
|
Parser.PRATT_TABLE = PARSER_TABLE;
|
|
8432
|
+
Parser.BEEP_OPERAND_BP = 80;
|
|
8206
8433
|
Parser.PSEUDO_COMMAND_PREPOSITIONS = ['from', 'on', 'with', 'into', 'at', 'to'];
|
|
8434
|
+
Parser.POSITIONAL_SOURCE_HEADS = new Set([
|
|
8435
|
+
'closest',
|
|
8436
|
+
'next',
|
|
8437
|
+
'previous',
|
|
8438
|
+
'first',
|
|
8439
|
+
'last',
|
|
8440
|
+
]);
|
|
8207
8441
|
function parse(input, options) {
|
|
8208
8442
|
const tokens = tokenize(input);
|
|
8209
8443
|
const parser = new Parser(tokens, options, input);
|
|
@@ -8408,6 +8642,89 @@ function isDocumentFragment(value) {
|
|
|
8408
8642
|
'childNodes' in value);
|
|
8409
8643
|
}
|
|
8410
8644
|
|
|
8645
|
+
function beepValues(me, values) {
|
|
8646
|
+
const outputs = values.map(describeBeepValue);
|
|
8647
|
+
const shown = outputs.filter(output => announce(me, output.value));
|
|
8648
|
+
if (shown.length === 0)
|
|
8649
|
+
return outputs;
|
|
8650
|
+
console.group('\ud83d\udd14 beep! Debug Output');
|
|
8651
|
+
for (const output of shown) {
|
|
8652
|
+
console.log(`Value:`, output.value);
|
|
8653
|
+
console.log(`Type:`, output.type);
|
|
8654
|
+
console.log(`Representation:`, output.representation);
|
|
8655
|
+
console.log('---');
|
|
8656
|
+
}
|
|
8657
|
+
console.groupEnd();
|
|
8658
|
+
return outputs;
|
|
8659
|
+
}
|
|
8660
|
+
function announce(me, value) {
|
|
8661
|
+
const target = me;
|
|
8662
|
+
if (typeof CustomEvent !== 'function' || typeof target?.dispatchEvent !== 'function')
|
|
8663
|
+
return true;
|
|
8664
|
+
return target.dispatchEvent(new CustomEvent('hyperscript:beep', {
|
|
8665
|
+
bubbles: true,
|
|
8666
|
+
cancelable: true,
|
|
8667
|
+
detail: { element: me, value },
|
|
8668
|
+
}));
|
|
8669
|
+
}
|
|
8670
|
+
function describeBeepValue(value) {
|
|
8671
|
+
return { value, type: typeOf(value), representation: representationOf(value) };
|
|
8672
|
+
}
|
|
8673
|
+
function typeOf(value) {
|
|
8674
|
+
if (value === null)
|
|
8675
|
+
return 'null';
|
|
8676
|
+
if (value === undefined)
|
|
8677
|
+
return 'undefined';
|
|
8678
|
+
if (Array.isArray(value))
|
|
8679
|
+
return 'array';
|
|
8680
|
+
if (isHTMLElement$2(value))
|
|
8681
|
+
return 'HTMLElement';
|
|
8682
|
+
if (typeof Element !== 'undefined' && value instanceof Element)
|
|
8683
|
+
return 'Element';
|
|
8684
|
+
if (typeof Node !== 'undefined' && value instanceof Node)
|
|
8685
|
+
return 'Node';
|
|
8686
|
+
if (value instanceof Error)
|
|
8687
|
+
return 'Error';
|
|
8688
|
+
if (value instanceof Date)
|
|
8689
|
+
return 'Date';
|
|
8690
|
+
if (value instanceof RegExp)
|
|
8691
|
+
return 'RegExp';
|
|
8692
|
+
return typeof value;
|
|
8693
|
+
}
|
|
8694
|
+
function representationOf(value) {
|
|
8695
|
+
if (value === null)
|
|
8696
|
+
return 'null';
|
|
8697
|
+
if (value === undefined)
|
|
8698
|
+
return 'undefined';
|
|
8699
|
+
if (Array.isArray(value)) {
|
|
8700
|
+
return `Array(${value.length}) [${value
|
|
8701
|
+
.slice(0, 3)
|
|
8702
|
+
.map(v => representationOf(v))
|
|
8703
|
+
.join(', ')}${value.length > 3 ? '...' : ''}]`;
|
|
8704
|
+
}
|
|
8705
|
+
if (isHTMLElement$2(value)) {
|
|
8706
|
+
const el = value;
|
|
8707
|
+
const tag = el.tagName.toLowerCase();
|
|
8708
|
+
const id = el.id ? `#${el.id}` : '';
|
|
8709
|
+
const classes = el.className ? `.${el.className.split(' ').join('.')}` : '';
|
|
8710
|
+
return `<${tag}${id}${classes}/>`;
|
|
8711
|
+
}
|
|
8712
|
+
if (value instanceof Error)
|
|
8713
|
+
return `Error: ${value.message}`;
|
|
8714
|
+
if (typeof value === 'string')
|
|
8715
|
+
return value.length > 50 ? `"${value.substring(0, 47)}..."` : `"${value}"`;
|
|
8716
|
+
if (typeof value === 'object') {
|
|
8717
|
+
try {
|
|
8718
|
+
const keys = Object.keys(value);
|
|
8719
|
+
return `Object {${keys.slice(0, 3).join(', ')}${keys.length > 3 ? '...' : ''}}`;
|
|
8720
|
+
}
|
|
8721
|
+
catch {
|
|
8722
|
+
return '[Object]';
|
|
8723
|
+
}
|
|
8724
|
+
}
|
|
8725
|
+
return String(value);
|
|
8726
|
+
}
|
|
8727
|
+
|
|
8411
8728
|
function convertToNumber(value) {
|
|
8412
8729
|
if (value === null || value === undefined) {
|
|
8413
8730
|
return 0;
|
|
@@ -8439,6 +8756,16 @@ function convertToNumber(value) {
|
|
|
8439
8756
|
}
|
|
8440
8757
|
return 0;
|
|
8441
8758
|
}
|
|
8759
|
+
function counterAmount(value) {
|
|
8760
|
+
return typeof value === 'number' ? value : parseFloat(String(value));
|
|
8761
|
+
}
|
|
8762
|
+
function counterValue(value) {
|
|
8763
|
+
if (isHTMLElement$2(value)) {
|
|
8764
|
+
const element = value;
|
|
8765
|
+
return counterValue(element.value || element.textContent);
|
|
8766
|
+
}
|
|
8767
|
+
return value ? counterAmount(value) : 0;
|
|
8768
|
+
}
|
|
8442
8769
|
function getVariableValue(name, context, preferredScope) {
|
|
8443
8770
|
if (preferredScope === 'element') {
|
|
8444
8771
|
return getElementVar(context, name);
|
|
@@ -8543,35 +8870,39 @@ function setElementPropertyValue(propertyPath, value, context) {
|
|
|
8543
8870
|
element[property] = value;
|
|
8544
8871
|
}
|
|
8545
8872
|
}
|
|
8873
|
+
function isObjectOwner(value) {
|
|
8874
|
+
return (typeof value === 'object' && value !== null && !Array.isArray(value) && !isHTMLElement$2(value));
|
|
8875
|
+
}
|
|
8546
8876
|
function getCurrentNumericValue(target, property, scope, context) {
|
|
8547
8877
|
if (typeof target === 'number') {
|
|
8548
8878
|
return target;
|
|
8549
8879
|
}
|
|
8550
8880
|
if (isHTMLElement$2(target)) {
|
|
8551
8881
|
const element = target;
|
|
8882
|
+
if (property?.startsWith('@'))
|
|
8883
|
+
return counterValue(element.getAttribute(property.slice(1)));
|
|
8552
8884
|
if (property) {
|
|
8553
8885
|
if (property.startsWith('data-') ||
|
|
8554
8886
|
['id', 'class', 'title', 'alt', 'src', 'href'].includes(property)) {
|
|
8555
|
-
|
|
8556
|
-
return convertToNumber(value);
|
|
8887
|
+
return counterValue(element.getAttribute(property));
|
|
8557
8888
|
}
|
|
8558
8889
|
else {
|
|
8559
|
-
|
|
8560
|
-
return convertToNumber(value);
|
|
8890
|
+
return counterValue(element[property]);
|
|
8561
8891
|
}
|
|
8562
8892
|
}
|
|
8563
8893
|
else {
|
|
8564
|
-
|
|
8565
|
-
return convertToNumber(value);
|
|
8894
|
+
return counterValue(element);
|
|
8566
8895
|
}
|
|
8567
8896
|
}
|
|
8897
|
+
if (property && isObjectOwner(target)) {
|
|
8898
|
+
return counterValue(target[property]);
|
|
8899
|
+
}
|
|
8568
8900
|
if (typeof target === 'string') {
|
|
8569
8901
|
if (scope === 'element') {
|
|
8570
|
-
return
|
|
8902
|
+
return counterValue(getVariableValue(target, context, 'element'));
|
|
8571
8903
|
}
|
|
8572
8904
|
if (scope === 'global') {
|
|
8573
|
-
|
|
8574
|
-
return convertToNumber(value);
|
|
8905
|
+
return counterValue(getVariableValue(target, context, 'global'));
|
|
8575
8906
|
}
|
|
8576
8907
|
if (target.includes('.')) {
|
|
8577
8908
|
return getElementPropertyValue(target, context);
|
|
@@ -8585,14 +8916,17 @@ function getCurrentNumericValue(target, property, scope, context) {
|
|
|
8585
8916
|
else if (target === 'you' && context.you) {
|
|
8586
8917
|
return convertToNumber(context.you.value || 0);
|
|
8587
8918
|
}
|
|
8588
|
-
|
|
8589
|
-
return convertToNumber(value);
|
|
8919
|
+
return counterValue(getVariableValue(target, context));
|
|
8590
8920
|
}
|
|
8591
|
-
return
|
|
8921
|
+
return counterValue(target);
|
|
8592
8922
|
}
|
|
8593
8923
|
function setTargetValue(target, property, scope, newValue, context) {
|
|
8594
8924
|
if (isHTMLElement$2(target)) {
|
|
8595
8925
|
const element = target;
|
|
8926
|
+
if (property?.startsWith('@')) {
|
|
8927
|
+
element.setAttribute(property.slice(1), String(newValue));
|
|
8928
|
+
return;
|
|
8929
|
+
}
|
|
8596
8930
|
if (property) {
|
|
8597
8931
|
if (property.startsWith('data-') ||
|
|
8598
8932
|
['id', 'class', 'title', 'alt', 'src', 'href'].includes(property)) {
|
|
@@ -8612,6 +8946,10 @@ function setTargetValue(target, property, scope, newValue, context) {
|
|
|
8612
8946
|
}
|
|
8613
8947
|
return;
|
|
8614
8948
|
}
|
|
8949
|
+
if (property && isObjectOwner(target)) {
|
|
8950
|
+
target[property] = newValue;
|
|
8951
|
+
return;
|
|
8952
|
+
}
|
|
8615
8953
|
if (typeof target === 'string') {
|
|
8616
8954
|
if (scope === 'element') {
|
|
8617
8955
|
setVariableValue(target, newValue, context, 'element');
|
|
@@ -9040,13 +9378,6 @@ function isFunction(value) {
|
|
|
9040
9378
|
function isInputElement$1(el) {
|
|
9041
9379
|
return (el !== null && typeof el === 'object' && 'tagName' in el && el.tagName === 'INPUT');
|
|
9042
9380
|
}
|
|
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
9381
|
function isHTMLElement$1(el) {
|
|
9051
9382
|
return (el !== null &&
|
|
9052
9383
|
typeof el === 'object' &&
|
|
@@ -9106,7 +9437,7 @@ const SPECIAL_DOM_PROPERTIES = {
|
|
|
9106
9437
|
innertext: el => el.textContent?.trim(),
|
|
9107
9438
|
innerHTML: el => el.innerHTML,
|
|
9108
9439
|
outerhtml: el => el.outerHTML,
|
|
9109
|
-
value: el => (
|
|
9440
|
+
value: el => ('value' in el ? el.value : undefined),
|
|
9110
9441
|
checked: el => (isInputElement$1(el) ? el.checked : undefined),
|
|
9111
9442
|
disabled: el => ('disabled' in el ? el.disabled : undefined),
|
|
9112
9443
|
selected: el => (isOptionElement$1(el) ? el.selected : undefined),
|
|
@@ -9774,14 +10105,15 @@ const closestExpression = {
|
|
|
9774
10105
|
if (typeof selector !== 'string') {
|
|
9775
10106
|
throw new Error('closest requires a string selector');
|
|
9776
10107
|
}
|
|
9777
|
-
|
|
10108
|
+
const start = args.length > 1 ? args[1] : context.me;
|
|
10109
|
+
if (!(start instanceof Element)) {
|
|
9778
10110
|
return null;
|
|
9779
10111
|
}
|
|
9780
|
-
return
|
|
10112
|
+
return start.closest(selector);
|
|
9781
10113
|
},
|
|
9782
10114
|
validate(args) {
|
|
9783
|
-
if (args.length !== 1) {
|
|
9784
|
-
return 'closest requires
|
|
10115
|
+
if (args.length !== 1 && args.length !== 2) {
|
|
10116
|
+
return 'closest requires a selector and, optionally, the element to start from';
|
|
9785
10117
|
}
|
|
9786
10118
|
const arg = args[0];
|
|
9787
10119
|
if (typeof arg === 'string')
|
|
@@ -11281,7 +11613,7 @@ const isEmptyExpression = {
|
|
|
11281
11613
|
else if (value instanceof Node || value instanceof Element)
|
|
11282
11614
|
result = false;
|
|
11283
11615
|
else if (isObject(value))
|
|
11284
|
-
result =
|
|
11616
|
+
result = value.length === 0;
|
|
11285
11617
|
else
|
|
11286
11618
|
result = false;
|
|
11287
11619
|
return result;
|
|
@@ -12405,11 +12737,16 @@ async function evaluateBinaryExpression(node, context) {
|
|
|
12405
12737
|
}
|
|
12406
12738
|
}
|
|
12407
12739
|
}
|
|
12408
|
-
if (leftNode?.type === 'selector' &&
|
|
12740
|
+
if (leftNode?.type === 'selector' &&
|
|
12741
|
+
typeof leftNode.value === 'string' &&
|
|
12742
|
+
(leftNode.fromQuery || (operator === 'in' && leftNode.value.startsWith('.')))) {
|
|
12409
12743
|
const root = await evaluateAST(node.right, context);
|
|
12410
12744
|
const scope = root && typeof root.querySelectorAll === 'function' ? root : null;
|
|
12411
12745
|
if (scope) {
|
|
12412
|
-
|
|
12746
|
+
const query = leftNode.fromQuery
|
|
12747
|
+
? leftNode.value
|
|
12748
|
+
: escapeSelectorForQuery(leftNode, leftNode.value);
|
|
12749
|
+
return Array.from(scope.querySelectorAll(query));
|
|
12413
12750
|
}
|
|
12414
12751
|
}
|
|
12415
12752
|
}
|
|
@@ -12421,6 +12758,12 @@ async function evaluateBinaryExpression(node, context) {
|
|
|
12421
12758
|
return getExpr(context, 'styleRef').evaluate(context, leftNode.value.slice(1), el);
|
|
12422
12759
|
}
|
|
12423
12760
|
if (operator === 'of') {
|
|
12761
|
+
if (leftNode?.type === 'attributeAccess') {
|
|
12762
|
+
const target = await evaluateAST(node.right, context);
|
|
12763
|
+
if (target == null)
|
|
12764
|
+
return undefined;
|
|
12765
|
+
return getExpr(context, 'possessive').evaluate(context, target, `@${leftNode.attributeName}`);
|
|
12766
|
+
}
|
|
12424
12767
|
const path = propertyPathOf(node.left);
|
|
12425
12768
|
if (path) {
|
|
12426
12769
|
const target = await evaluateAST(node.right, context);
|
|
@@ -12428,6 +12771,10 @@ async function evaluateBinaryExpression(node, context) {
|
|
|
12428
12771
|
for (const seg of path) {
|
|
12429
12772
|
if (cur == null)
|
|
12430
12773
|
return undefined;
|
|
12774
|
+
if (isMappableCollection(cur) && !(seg in cur)) {
|
|
12775
|
+
cur = await getExpr(context, 'possessive').evaluate(context, cur, seg);
|
|
12776
|
+
continue;
|
|
12777
|
+
}
|
|
12431
12778
|
cur = isElement(cur) ? getElementProperty$1(cur, seg) : cur[seg];
|
|
12432
12779
|
}
|
|
12433
12780
|
return typeof cur === 'function' ? cur.bind(target) : cur;
|
|
@@ -12452,26 +12799,21 @@ async function evaluateBinaryExpression(node, context) {
|
|
|
12452
12799
|
const R = node.ignoringCase ? applyCI(right) : right;
|
|
12453
12800
|
switch (operator) {
|
|
12454
12801
|
case '+': {
|
|
12455
|
-
let addLeft = left;
|
|
12456
|
-
let addRight = right;
|
|
12457
12802
|
if (node.coerceNumeric) {
|
|
12458
|
-
|
|
12459
|
-
addLeft = convertToNumber(addLeft);
|
|
12460
|
-
if (typeof addRight === 'string')
|
|
12461
|
-
addRight = convertToNumber(addRight);
|
|
12803
|
+
return counterValue(left) + counterAmount(right);
|
|
12462
12804
|
}
|
|
12463
|
-
if (typeof
|
|
12464
|
-
return String(
|
|
12805
|
+
if (typeof left === 'string' || typeof right === 'string') {
|
|
12806
|
+
return String(left ?? '') + String(right ?? '');
|
|
12465
12807
|
}
|
|
12466
|
-
if (Array.isArray(
|
|
12467
|
-
return Array.isArray(
|
|
12808
|
+
if (Array.isArray(left)) {
|
|
12809
|
+
return Array.isArray(right) ? [...left, ...right] : [...left, right];
|
|
12468
12810
|
}
|
|
12469
|
-
return unwrapTypedResult(await getExpr(context, 'addition').evaluate(context, {
|
|
12470
|
-
left: addLeft,
|
|
12471
|
-
right: addRight,
|
|
12472
|
-
}));
|
|
12811
|
+
return unwrapTypedResult(await getExpr(context, 'addition').evaluate(context, { left, right }));
|
|
12473
12812
|
}
|
|
12474
12813
|
case '-':
|
|
12814
|
+
if (node.coerceNumeric) {
|
|
12815
|
+
return counterValue(left) - counterAmount(right);
|
|
12816
|
+
}
|
|
12475
12817
|
return unwrapTypedResult(await getExpr(context, 'subtraction').evaluate(context, { left, right }));
|
|
12476
12818
|
case '*':
|
|
12477
12819
|
return unwrapTypedResult(await getExpr(context, 'multiplication').evaluate(context, { left, right }));
|
|
@@ -12729,9 +13071,8 @@ async function evaluatePropertyOfExpressionNode(node, context) {
|
|
|
12729
13071
|
}
|
|
12730
13072
|
const propertyName = propertyNode.name;
|
|
12731
13073
|
const target = await evaluateAST(node.target, context);
|
|
12732
|
-
if (target == null)
|
|
12733
|
-
|
|
12734
|
-
}
|
|
13074
|
+
if (target == null)
|
|
13075
|
+
return undefined;
|
|
12735
13076
|
return getExpr(context, 'possessive').evaluate(context, target, propertyName);
|
|
12736
13077
|
}
|
|
12737
13078
|
async function evaluateTemplateLiteralNode(node, context) {
|
|
@@ -12849,6 +13190,9 @@ async function evaluateUnaryExpression(node, context) {
|
|
|
12849
13190
|
case 'not':
|
|
12850
13191
|
case '!':
|
|
12851
13192
|
return logicalExpressions.not.evaluate(context, value);
|
|
13193
|
+
case 'beep!':
|
|
13194
|
+
beepValues(context.me, [value]);
|
|
13195
|
+
return value;
|
|
12852
13196
|
case 'no':
|
|
12853
13197
|
return logicalExpressions.no.evaluate(context, value);
|
|
12854
13198
|
case '-':
|
|
@@ -12938,6 +13282,10 @@ async function evaluateCallExpression(node, context) {
|
|
|
12938
13282
|
const args = await resolveCallArgs(node.arguments, funcName, context);
|
|
12939
13283
|
switch (funcName) {
|
|
12940
13284
|
case 'closest':
|
|
13285
|
+
if (node.closestTo) {
|
|
13286
|
+
const start = unwrapElement(await evaluateAST(node.closestTo, context)) ?? null;
|
|
13287
|
+
return referencesExpressions.closest.evaluate(context, args[0], start);
|
|
13288
|
+
}
|
|
12941
13289
|
return referencesExpressions.closest.evaluate(context, ...args);
|
|
12942
13290
|
case 'previous':
|
|
12943
13291
|
case 'next': {
|
|
@@ -14324,12 +14672,23 @@ class RuntimeBase {
|
|
|
14324
14672
|
debug.runtime(`BEHAVIOR: Finished installing ${behaviorName}`);
|
|
14325
14673
|
}
|
|
14326
14674
|
async executeEventHandler(node, context) {
|
|
14327
|
-
const { event, events, commands, target, args, selector, condition, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler, } = node;
|
|
14675
|
+
const { event, events, commands, target, targetExpression, args, selector, condition, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler, } = node;
|
|
14328
14676
|
const eventNames = events && events.length > 0 ? events : [event];
|
|
14329
14677
|
debug.runtime(`BEHAVIOR: executeEventHandler: event='${event}', target='${target}'`);
|
|
14330
14678
|
let targets = [];
|
|
14331
14679
|
let globalTarget = null;
|
|
14332
|
-
if (
|
|
14680
|
+
if (targetExpression) {
|
|
14681
|
+
const resolved = await this.execute(targetExpression, context);
|
|
14682
|
+
if (resolved === window)
|
|
14683
|
+
globalTarget = window;
|
|
14684
|
+
else if (resolved === document)
|
|
14685
|
+
globalTarget = document;
|
|
14686
|
+
else if (this.isElement(resolved))
|
|
14687
|
+
targets = [resolved];
|
|
14688
|
+
else if (Array.isArray(resolved))
|
|
14689
|
+
targets = resolved.filter(el => this.isElement(el));
|
|
14690
|
+
}
|
|
14691
|
+
else if (target) {
|
|
14333
14692
|
const targetLower = typeof target === 'string' ? target.toLowerCase() : '';
|
|
14334
14693
|
if (targetLower === 'window' || targetLower === 'the window') {
|
|
14335
14694
|
globalTarget = window;
|
|
@@ -16350,14 +16709,83 @@ function parseAttribute(expression) {
|
|
|
16350
16709
|
if (trimmed.startsWith('@')) {
|
|
16351
16710
|
return { name: trimmed.substring(1).trim() };
|
|
16352
16711
|
}
|
|
16353
|
-
throw new Error(`Invalid attribute syntax: ${expression}`);
|
|
16354
|
-
}
|
|
16355
|
-
function parseAttributeName(expression) {
|
|
16356
|
-
return parseAttribute(expression).name;
|
|
16712
|
+
throw new Error(`Invalid attribute syntax: ${expression}`);
|
|
16713
|
+
}
|
|
16714
|
+
function parseAttributeName(expression) {
|
|
16715
|
+
return parseAttribute(expression).name;
|
|
16716
|
+
}
|
|
16717
|
+
function parseAttributeWithValue(expression) {
|
|
16718
|
+
const result = parseAttribute(expression);
|
|
16719
|
+
return { name: result.name, value: result.value ?? '' };
|
|
16720
|
+
}
|
|
16721
|
+
|
|
16722
|
+
function _parseDurationComponents(value) {
|
|
16723
|
+
const trimmed = value.trim();
|
|
16724
|
+
const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*(s|ms|sec|seconds?|milliseconds?)?$/i);
|
|
16725
|
+
if (!match) {
|
|
16726
|
+
return null;
|
|
16727
|
+
}
|
|
16728
|
+
const number = parseFloat(match[1]);
|
|
16729
|
+
const unit = (match[2] || 'ms').toLowerCase();
|
|
16730
|
+
if (unit === 'ms' || unit === 'millisecond' || unit === 'milliseconds') {
|
|
16731
|
+
return { number, milliseconds: Math.floor(number) };
|
|
16732
|
+
}
|
|
16733
|
+
else if (unit === 's' || unit === 'sec' || unit === 'second' || unit === 'seconds') {
|
|
16734
|
+
return { number, milliseconds: Math.floor(number * 1000) };
|
|
16735
|
+
}
|
|
16736
|
+
return null;
|
|
16737
|
+
}
|
|
16738
|
+
function parseDuration(value, defaultMs = 300) {
|
|
16739
|
+
if (typeof value === 'number') {
|
|
16740
|
+
return Math.max(0, Math.floor(value));
|
|
16741
|
+
}
|
|
16742
|
+
if (typeof value === 'string') {
|
|
16743
|
+
const result = _parseDurationComponents(value);
|
|
16744
|
+
return result ? result.milliseconds : defaultMs;
|
|
16745
|
+
}
|
|
16746
|
+
return defaultMs;
|
|
16747
|
+
}
|
|
16748
|
+
function parseDurationStrict(value) {
|
|
16749
|
+
if (typeof value === 'number') {
|
|
16750
|
+
if (value < 0)
|
|
16751
|
+
throw new Error('Duration must be >= 0');
|
|
16752
|
+
return Math.floor(value);
|
|
16753
|
+
}
|
|
16754
|
+
if (typeof value === 'string') {
|
|
16755
|
+
const result = _parseDurationComponents(value);
|
|
16756
|
+
if (!result) {
|
|
16757
|
+
throw new Error(`Invalid duration format: "${value}"`);
|
|
16758
|
+
}
|
|
16759
|
+
return result.milliseconds;
|
|
16760
|
+
}
|
|
16761
|
+
throw new Error(`Invalid duration type: ${typeof value}`);
|
|
16762
|
+
}
|
|
16763
|
+
function parseCSSDurations(durationString) {
|
|
16764
|
+
if (!durationString || durationString === 'none') {
|
|
16765
|
+
return [0];
|
|
16766
|
+
}
|
|
16767
|
+
return durationString.split(',').map(duration => {
|
|
16768
|
+
const value = parseFloat(duration.trim());
|
|
16769
|
+
if (isNaN(value))
|
|
16770
|
+
return 0;
|
|
16771
|
+
if (duration.includes('s') && !duration.includes('ms')) {
|
|
16772
|
+
return value * 1000;
|
|
16773
|
+
}
|
|
16774
|
+
return value;
|
|
16775
|
+
});
|
|
16776
|
+
}
|
|
16777
|
+
function calculateMaxAnimationTime(durations, delays) {
|
|
16778
|
+
let maxTime = 0;
|
|
16779
|
+
for (let i = 0; i < durations.length; i++) {
|
|
16780
|
+
const duration = durations[i] || 0;
|
|
16781
|
+
const delay = delays[i] || 0;
|
|
16782
|
+
const totalTime = duration + delay;
|
|
16783
|
+
maxTime = Math.max(maxTime, totalTime);
|
|
16784
|
+
}
|
|
16785
|
+
return maxTime;
|
|
16357
16786
|
}
|
|
16358
|
-
function
|
|
16359
|
-
|
|
16360
|
-
return { name: result.name, value: result.value ?? '' };
|
|
16787
|
+
function camelToKebab(str) {
|
|
16788
|
+
return str.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
16361
16789
|
}
|
|
16362
16790
|
|
|
16363
16791
|
function batchApply(targets, operation) {
|
|
@@ -16425,15 +16853,18 @@ function toggleAttribute(element, name, value) {
|
|
|
16425
16853
|
}
|
|
16426
16854
|
}
|
|
16427
16855
|
}
|
|
16856
|
+
function toCssPropertyName(property) {
|
|
16857
|
+
return property.startsWith('--') ? property : camelToKebab(property);
|
|
16858
|
+
}
|
|
16428
16859
|
function batchSetStyles(targets, styles) {
|
|
16429
16860
|
const entries = Object.entries(styles);
|
|
16430
16861
|
return batchApplyItems(targets, entries, (element, [property, value]) => {
|
|
16431
|
-
element.style.setProperty(property, value);
|
|
16862
|
+
element.style.setProperty(toCssPropertyName(property), value);
|
|
16432
16863
|
});
|
|
16433
16864
|
}
|
|
16434
16865
|
function batchRemoveStyles(targets, properties) {
|
|
16435
16866
|
return batchApplyItems(targets, properties, (element, property) => {
|
|
16436
|
-
element.style.removeProperty(property);
|
|
16867
|
+
element.style.removeProperty(toCssPropertyName(property));
|
|
16437
16868
|
});
|
|
16438
16869
|
}
|
|
16439
16870
|
|
|
@@ -17099,75 +17530,6 @@ let RemoveCommand = (() => {
|
|
|
17099
17530
|
})();
|
|
17100
17531
|
const createRemoveCommand = createFactory(RemoveCommand);
|
|
17101
17532
|
|
|
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
17533
|
function detectSmartElementType(elements) {
|
|
17172
17534
|
if (elements.length === 0)
|
|
17173
17535
|
return null;
|
|
@@ -18037,7 +18399,11 @@ let PutCommand = (() => {
|
|
|
18037
18399
|
}
|
|
18038
18400
|
if (isInsertableNode(v))
|
|
18039
18401
|
return v;
|
|
18040
|
-
|
|
18402
|
+
if (v == null)
|
|
18403
|
+
return 'null';
|
|
18404
|
+
if (Array.isArray(v))
|
|
18405
|
+
return arrayFragment(v);
|
|
18406
|
+
return String(v);
|
|
18041
18407
|
}
|
|
18042
18408
|
looksLikeCss(s) {
|
|
18043
18409
|
if (!s)
|
|
@@ -18122,6 +18488,19 @@ let PutCommand = (() => {
|
|
|
18122
18488
|
})();
|
|
18123
18489
|
return PutCommand = _classThis;
|
|
18124
18490
|
})();
|
|
18491
|
+
function arrayFragment(items) {
|
|
18492
|
+
const fragment = document.createDocumentFragment();
|
|
18493
|
+
for (const item of items) {
|
|
18494
|
+
if (isInsertableNode(item)) {
|
|
18495
|
+
fragment.append(item);
|
|
18496
|
+
continue;
|
|
18497
|
+
}
|
|
18498
|
+
const template = document.createElement('template');
|
|
18499
|
+
template.innerHTML = item === null ? '' : String(item);
|
|
18500
|
+
fragment.append(template.content);
|
|
18501
|
+
}
|
|
18502
|
+
return fragment;
|
|
18503
|
+
}
|
|
18125
18504
|
const createPutCommand = createFactory(PutCommand);
|
|
18126
18505
|
|
|
18127
18506
|
const HTML_ESCAPE = {
|
|
@@ -19339,10 +19718,29 @@ const STRATEGY_KEYWORDS = {
|
|
|
19339
19718
|
innermorph: 'morph',
|
|
19340
19719
|
outermorph: 'morphOuter',
|
|
19341
19720
|
};
|
|
19721
|
+
function loneRootWithTag(content, tagName) {
|
|
19722
|
+
let root;
|
|
19723
|
+
if (typeof content === 'string') {
|
|
19724
|
+
const template = document.createElement('template');
|
|
19725
|
+
template.innerHTML = content;
|
|
19726
|
+
root = template.content.firstElementChild;
|
|
19727
|
+
if (!root || root.nextElementSibling)
|
|
19728
|
+
return null;
|
|
19729
|
+
}
|
|
19730
|
+
else {
|
|
19731
|
+
root = content.cloneNode(true);
|
|
19732
|
+
}
|
|
19733
|
+
return root.tagName === tagName && isHTMLElement$2(root) ? root : null;
|
|
19734
|
+
}
|
|
19342
19735
|
function executeSwap(target, content, strategy, morphOptions) {
|
|
19343
19736
|
const contentStr = content !== null && !isHTMLElement$2(content) ? content : '';
|
|
19344
19737
|
const contentEl = isHTMLElement$2(content) ? content : null;
|
|
19345
19738
|
switch (strategy) {
|
|
19739
|
+
case 'morphMerge': {
|
|
19740
|
+
const root = content !== null ? loneRootWithTag(content, target.tagName) : null;
|
|
19741
|
+
executeSwap(target, root ?? content, root ? 'morphOuter' : 'morph', morphOptions);
|
|
19742
|
+
break;
|
|
19743
|
+
}
|
|
19346
19744
|
case 'morph':
|
|
19347
19745
|
if (content !== null) {
|
|
19348
19746
|
try {
|
|
@@ -19632,7 +20030,7 @@ let MorphCommand = (() => {
|
|
|
19632
20030
|
if (args.length === 0 && modifiers.with === undefined) {
|
|
19633
20031
|
throw new Error('[HyperFixi] morph: command requires arguments');
|
|
19634
20032
|
}
|
|
19635
|
-
const strategy = literalText(modifiers.strategy)?.toLowerCase() === 'over' ? 'morphOuter' : '
|
|
20033
|
+
const strategy = literalText(modifiers.strategy)?.toLowerCase() === 'over' ? 'morphOuter' : 'morphMerge';
|
|
19636
20034
|
const useViewTransition = modifiers.viewTransition !== undefined;
|
|
19637
20035
|
const targetNode = modifiers.on !== undefined ? modifiers.on : args[0];
|
|
19638
20036
|
const contentNode = modifiers.with !== undefined
|
|
@@ -20938,6 +21336,19 @@ async function resolveWriteTarget(node, evaluator, context, options) {
|
|
|
20938
21336
|
}
|
|
20939
21337
|
return { kind: 'property', target: property };
|
|
20940
21338
|
}
|
|
21339
|
+
if (options.ofAndObjectProperty) {
|
|
21340
|
+
const access = ofOrPossessiveAccess(n);
|
|
21341
|
+
if (access) {
|
|
21342
|
+
const owner = await evaluator.evaluate(access.owner, context);
|
|
21343
|
+
const element = Array.isArray(owner) ? owner[0] : owner;
|
|
21344
|
+
if (access.isOf && isHTMLElement$2(element)) {
|
|
21345
|
+
return { kind: 'property', target: { element, property: access.name } };
|
|
21346
|
+
}
|
|
21347
|
+
if (isObjectOwner(owner)) {
|
|
21348
|
+
return { kind: 'member', container: owner, property: access.name };
|
|
21349
|
+
}
|
|
21350
|
+
}
|
|
21351
|
+
}
|
|
20941
21352
|
if (options.bareReference &&
|
|
20942
21353
|
(nodeType === 'identifier' || nodeType === 'variable' || nodeType === 'symbol') &&
|
|
20943
21354
|
typeof n.name === 'string') {
|
|
@@ -20947,6 +21358,19 @@ async function resolveWriteTarget(node, evaluator, context, options) {
|
|
|
20947
21358
|
}
|
|
20948
21359
|
return null;
|
|
20949
21360
|
}
|
|
21361
|
+
function ofOrPossessiveAccess(n) {
|
|
21362
|
+
const [owner, property, isOf] = n.type === 'possessiveExpression'
|
|
21363
|
+
? [n.object, n.property, false]
|
|
21364
|
+
: n.type === 'propertyOfExpression'
|
|
21365
|
+
? [n.target, n.property, false]
|
|
21366
|
+
: n.type === 'binaryExpression' && n.operator === 'of'
|
|
21367
|
+
? [n.right, n.left, true]
|
|
21368
|
+
: [undefined, undefined, false];
|
|
21369
|
+
const name = property?.name;
|
|
21370
|
+
if (!owner || typeof name !== 'string' || !/^[A-Za-z_$][\w$]*$/.test(name))
|
|
21371
|
+
return null;
|
|
21372
|
+
return { owner, name, isOf };
|
|
21373
|
+
}
|
|
20950
21374
|
|
|
20951
21375
|
function toSetInput(target, value) {
|
|
20952
21376
|
switch (target.kind) {
|
|
@@ -20968,10 +21392,18 @@ function toSetInput(target, value) {
|
|
|
20968
21392
|
property: target.target.property,
|
|
20969
21393
|
value,
|
|
20970
21394
|
};
|
|
21395
|
+
case 'member':
|
|
21396
|
+
return {
|
|
21397
|
+
type: 'member-assignment',
|
|
21398
|
+
container: target.container,
|
|
21399
|
+
property: target.property,
|
|
21400
|
+
value,
|
|
21401
|
+
};
|
|
20971
21402
|
default:
|
|
20972
21403
|
throw new Error(`set: unrequested write-target rung '${target.kind}'`);
|
|
20973
21404
|
}
|
|
20974
21405
|
}
|
|
21406
|
+
const SET_RUNGS = { nodeWriters: true, styleSplit: true, ofAndObjectProperty: true };
|
|
20975
21407
|
let SetCommand = (() => {
|
|
20976
21408
|
let _classDecorators = [command({ name: 'set' })];
|
|
20977
21409
|
let _classDescriptor;
|
|
@@ -20990,8 +21422,7 @@ let SetCommand = (() => {
|
|
|
20990
21422
|
const argScope = firstArg?.scope;
|
|
20991
21423
|
const writeTarget = await resolveWriteTarget(firstArg, evaluator, context, {
|
|
20992
21424
|
scopeElements: () => this.resolveTargets(raw.modifiers.on, evaluator, context),
|
|
20993
|
-
|
|
20994
|
-
styleSplit: true,
|
|
21425
|
+
...SET_RUNGS,
|
|
20995
21426
|
});
|
|
20996
21427
|
if (writeTarget) {
|
|
20997
21428
|
return toSetInput(writeTarget, await this.extractValue(raw, evaluator, context));
|
|
@@ -21317,12 +21748,30 @@ function getNodeType(node) {
|
|
|
21317
21748
|
return 'unknown';
|
|
21318
21749
|
return node.type || 'unknown';
|
|
21319
21750
|
}
|
|
21751
|
+
function propertyTarget(node) {
|
|
21752
|
+
const n = node;
|
|
21753
|
+
const owner = n.type === 'propertyOfExpression'
|
|
21754
|
+
? n.target
|
|
21755
|
+
: n.type === 'possessiveExpression' ||
|
|
21756
|
+
n.type === 'memberExpression' ||
|
|
21757
|
+
n.type === 'propertyAccess'
|
|
21758
|
+
? n.object
|
|
21759
|
+
: undefined;
|
|
21760
|
+
if (!owner || n.computed)
|
|
21761
|
+
return null;
|
|
21762
|
+
const p = n.property;
|
|
21763
|
+
const name = typeof p === 'string' ? p : typeof p?.name === 'string' ? p.name : undefined;
|
|
21764
|
+
if (!name || !/^[A-Za-z_$][\w$-]*$/.test(name))
|
|
21765
|
+
return null;
|
|
21766
|
+
return { owner, name };
|
|
21767
|
+
}
|
|
21320
21768
|
async function parseNumericTargetInput(raw, evaluator, context, commandName) {
|
|
21321
21769
|
if (!raw.args || raw.args.length === 0) {
|
|
21322
21770
|
throw new Error(`${commandName} command requires a target`);
|
|
21323
21771
|
}
|
|
21324
21772
|
const targetArg = raw.args[0];
|
|
21325
21773
|
let target;
|
|
21774
|
+
let property;
|
|
21326
21775
|
let extractedScope;
|
|
21327
21776
|
let customWrite;
|
|
21328
21777
|
const nodeType = getNodeType(targetArg);
|
|
@@ -21337,7 +21786,25 @@ async function parseNumericTargetInput(raw, evaluator, context, commandName) {
|
|
|
21337
21786
|
}
|
|
21338
21787
|
else {
|
|
21339
21788
|
const writer = getRegisteredNodeWriter(nodeType);
|
|
21340
|
-
|
|
21789
|
+
const attribute = writer
|
|
21790
|
+
? null
|
|
21791
|
+
: await resolveAttributeWriteTarget(targetArg, evaluator, context, async () => context.me ? [context.me] : []);
|
|
21792
|
+
const prop = writer || attribute ? null : propertyTarget(targetArg);
|
|
21793
|
+
const owner = prop ? await evaluator.evaluate(prop.owner, context) : undefined;
|
|
21794
|
+
const ownerElement = Array.isArray(owner) ? owner[0] : owner;
|
|
21795
|
+
if (attribute?.elements[0]) {
|
|
21796
|
+
target = attribute.elements[0];
|
|
21797
|
+
property = `@${attribute.name}`;
|
|
21798
|
+
}
|
|
21799
|
+
else if (prop && isHTMLElement$2(ownerElement)) {
|
|
21800
|
+
target = ownerElement;
|
|
21801
|
+
property = prop.name;
|
|
21802
|
+
}
|
|
21803
|
+
else if (prop && isObjectOwner(owner)) {
|
|
21804
|
+
target = owner;
|
|
21805
|
+
property = prop.name;
|
|
21806
|
+
}
|
|
21807
|
+
else if (writer) {
|
|
21341
21808
|
const currentRaw = await evaluator.evaluate(targetArg, context);
|
|
21342
21809
|
const currentValue = typeof currentRaw === 'number'
|
|
21343
21810
|
? currentRaw
|
|
@@ -21375,24 +21842,22 @@ async function parseNumericTargetInput(raw, evaluator, context, commandName) {
|
|
|
21375
21842
|
if (typeof evaluated === 'number') {
|
|
21376
21843
|
amount = evaluated;
|
|
21377
21844
|
}
|
|
21845
|
+
else if (evaluated == null) {
|
|
21846
|
+
amount = NaN;
|
|
21847
|
+
}
|
|
21378
21848
|
}
|
|
21379
21849
|
}
|
|
21380
21850
|
const byModifier = raw.modifiers?.by;
|
|
21381
21851
|
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
|
-
}
|
|
21852
|
+
const value = getNodeType(byModifier) === 'literal'
|
|
21853
|
+
? byModifier.value
|
|
21854
|
+
: await evaluator.evaluate(byModifier, context);
|
|
21855
|
+
amount = counterAmount(value);
|
|
21392
21856
|
}
|
|
21393
21857
|
return {
|
|
21394
21858
|
target,
|
|
21395
21859
|
amount,
|
|
21860
|
+
...(property && { property }),
|
|
21396
21861
|
...(scope && { scope }),
|
|
21397
21862
|
...(customWrite && { customWrite }),
|
|
21398
21863
|
};
|
|
@@ -21415,7 +21880,7 @@ let NumericModifyCommand = (() => {
|
|
|
21415
21880
|
async execute(input, context) {
|
|
21416
21881
|
const { target, property, scope, amount = 1, operation = 'increment', customWrite } = input;
|
|
21417
21882
|
if (customWrite) {
|
|
21418
|
-
const delta =
|
|
21883
|
+
const delta = amount;
|
|
21419
21884
|
const newValue = operation === 'increment'
|
|
21420
21885
|
? customWrite.currentValue + delta
|
|
21421
21886
|
: customWrite.currentValue - delta;
|
|
@@ -21429,8 +21894,7 @@ let NumericModifyCommand = (() => {
|
|
|
21429
21894
|
newValue = NaN;
|
|
21430
21895
|
}
|
|
21431
21896
|
else {
|
|
21432
|
-
|
|
21433
|
-
newValue = operation === 'increment' ? currentValue + delta : currentValue - delta;
|
|
21897
|
+
newValue = operation === 'increment' ? currentValue + amount : currentValue - amount;
|
|
21434
21898
|
}
|
|
21435
21899
|
setTargetValue(target, property, scope, newValue, context);
|
|
21436
21900
|
Object.assign(context, { it: newValue });
|
|
@@ -22538,7 +23002,10 @@ function createUntilLoopConfig(conditionExpr, evaluateCondition, context, indexV
|
|
|
22538
23002
|
};
|
|
22539
23003
|
const config = {
|
|
22540
23004
|
type: 'until',
|
|
22541
|
-
shouldContinue: () =>
|
|
23005
|
+
shouldContinue: () => {
|
|
23006
|
+
const met = evaluateCondition(conditionExpr, context);
|
|
23007
|
+
return met instanceof Promise ? met.then(value => !value) : !met;
|
|
23008
|
+
},
|
|
22542
23009
|
};
|
|
22543
23010
|
return { config, iterCtx };
|
|
22544
23011
|
}
|
|
@@ -22574,7 +23041,7 @@ function createForeverLoopConfig(indexVariable, maxIterations = 10000) {
|
|
|
22574
23041
|
return { config, iterCtx };
|
|
22575
23042
|
}
|
|
22576
23043
|
|
|
22577
|
-
async function slotText(node, evaluator, context) {
|
|
23044
|
+
async function slotText$1(node, evaluator, context) {
|
|
22578
23045
|
if (!node)
|
|
22579
23046
|
return undefined;
|
|
22580
23047
|
const n = node;
|
|
@@ -22587,6 +23054,18 @@ async function slotText(node, evaluator, context) {
|
|
|
22587
23054
|
const v = await evaluator.evaluate(node, context);
|
|
22588
23055
|
return typeof v === 'string' ? v : undefined;
|
|
22589
23056
|
}
|
|
23057
|
+
function isEventTarget(value) {
|
|
23058
|
+
return typeof value?.addEventListener === 'function';
|
|
23059
|
+
}
|
|
23060
|
+
async function evaluateLoopCondition(condition, context) {
|
|
23061
|
+
if (typeof condition === 'function')
|
|
23062
|
+
return Boolean(await condition());
|
|
23063
|
+
return evaluateCondition(condition, context);
|
|
23064
|
+
}
|
|
23065
|
+
function loopCount(value) {
|
|
23066
|
+
const count = Number(value);
|
|
23067
|
+
return Number.isNaN(count) ? 0 : count;
|
|
23068
|
+
}
|
|
22590
23069
|
let RepeatCommand = (() => {
|
|
22591
23070
|
let _classDecorators = [command({ name: 'repeat' })];
|
|
22592
23071
|
let _classDescriptor;
|
|
@@ -22624,7 +23103,7 @@ let RepeatCommand = (() => {
|
|
|
22624
23103
|
}
|
|
22625
23104
|
}
|
|
22626
23105
|
const m = raw.modifiers ?? {};
|
|
22627
|
-
const text = (node) => slotText(node, evaluator, context);
|
|
23106
|
+
const text = (node) => slotText$1(node, evaluator, context);
|
|
22628
23107
|
const loopType = (await text(m.loopType)) ?? null;
|
|
22629
23108
|
const bottomTested = m.bottomTested
|
|
22630
23109
|
? (await evaluator.evaluate(m.bottomTested, context))
|
|
@@ -22644,17 +23123,24 @@ let RepeatCommand = (() => {
|
|
|
22644
23123
|
};
|
|
22645
23124
|
}
|
|
22646
23125
|
if (loopType === 'times' || m.times) {
|
|
22647
|
-
|
|
22648
|
-
const count = typeof countValue === 'number' ? countValue : parseInt(String(countValue), 10);
|
|
22649
|
-
if (isNaN(count))
|
|
23126
|
+
if (!m.times)
|
|
22650
23127
|
throw new Error('times loops require a count number');
|
|
23128
|
+
const count = loopCount(await evaluator.evaluate(m.times, context));
|
|
22651
23129
|
return { type: 'times', count, indexVariable, commands, elseCommands };
|
|
22652
23130
|
}
|
|
22653
23131
|
if (loopType === 'while' || m.while) {
|
|
22654
23132
|
const condition = m.while;
|
|
22655
23133
|
if (!condition)
|
|
22656
23134
|
throw new Error('while loops require a condition');
|
|
22657
|
-
|
|
23135
|
+
const check = () => evaluator.evaluate(condition, context);
|
|
23136
|
+
return {
|
|
23137
|
+
type: 'while',
|
|
23138
|
+
condition: check,
|
|
23139
|
+
indexVariable,
|
|
23140
|
+
commands,
|
|
23141
|
+
elseCommands,
|
|
23142
|
+
bottomTested,
|
|
23143
|
+
};
|
|
22658
23144
|
}
|
|
22659
23145
|
if (loopType === 'until-event' || m.event) {
|
|
22660
23146
|
const eventName = await text(m.event);
|
|
@@ -22672,7 +23158,7 @@ let RepeatCommand = (() => {
|
|
|
22672
23158
|
}
|
|
22673
23159
|
else {
|
|
22674
23160
|
const target = await evaluator.evaluate(m.from, context);
|
|
22675
|
-
if (target
|
|
23161
|
+
if (isEventTarget(target))
|
|
22676
23162
|
eventTarget = target;
|
|
22677
23163
|
else if (target === 'document')
|
|
22678
23164
|
eventTarget = document;
|
|
@@ -22693,7 +23179,15 @@ let RepeatCommand = (() => {
|
|
|
22693
23179
|
const condition = m.until;
|
|
22694
23180
|
if (!condition)
|
|
22695
23181
|
throw new Error('until loops require a condition');
|
|
22696
|
-
|
|
23182
|
+
const check = () => evaluator.evaluate(condition, context);
|
|
23183
|
+
return {
|
|
23184
|
+
type: 'until',
|
|
23185
|
+
condition: check,
|
|
23186
|
+
indexVariable,
|
|
23187
|
+
commands,
|
|
23188
|
+
elseCommands,
|
|
23189
|
+
bottomTested,
|
|
23190
|
+
};
|
|
22697
23191
|
}
|
|
22698
23192
|
if (loopType === 'forever') {
|
|
22699
23193
|
return { type: 'forever', indexVariable, commands, elseCommands };
|
|
@@ -22711,10 +23205,10 @@ let RepeatCommand = (() => {
|
|
|
22711
23205
|
({ config, iterCtx } = createTimesLoopConfig(count, indexVariable));
|
|
22712
23206
|
break;
|
|
22713
23207
|
case 'while':
|
|
22714
|
-
({ config, iterCtx } = createWhileLoopConfig(condition,
|
|
23208
|
+
({ config, iterCtx } = createWhileLoopConfig(condition, evaluateLoopCondition, context, indexVariable));
|
|
22715
23209
|
break;
|
|
22716
23210
|
case 'until':
|
|
22717
|
-
({ config, iterCtx } = createUntilLoopConfig(condition,
|
|
23211
|
+
({ config, iterCtx } = createUntilLoopConfig(condition, evaluateLoopCondition, context, indexVariable));
|
|
22718
23212
|
break;
|
|
22719
23213
|
case 'until-event':
|
|
22720
23214
|
({ config, iterCtx } = createUntilEventLoopConfig(eventName, eventTarget, indexVariable));
|
|
@@ -23745,6 +24239,29 @@ function namedPropertyOf(node) {
|
|
|
23745
24239
|
}
|
|
23746
24240
|
return null;
|
|
23747
24241
|
}
|
|
24242
|
+
const RESTORE_KEYWORDS = new Set(['initial', 'inherit', 'unset', 'revert']);
|
|
24243
|
+
function elementsOf(value) {
|
|
24244
|
+
if (Array.isArray(value) || isNodeList(value)) {
|
|
24245
|
+
return Array.from(value).filter(isHTMLElement$2);
|
|
24246
|
+
}
|
|
24247
|
+
return isHTMLElement$2(value) ? [value] : [];
|
|
24248
|
+
}
|
|
24249
|
+
async function valueOf(node, evaluator, context) {
|
|
24250
|
+
const value = await evaluator.evaluate(node, context);
|
|
24251
|
+
const name = node.name;
|
|
24252
|
+
if (value === undefined && node.type === 'identifier' && typeof name === 'string')
|
|
24253
|
+
return name;
|
|
24254
|
+
return value;
|
|
24255
|
+
}
|
|
24256
|
+
function longestTransitionMs(css) {
|
|
24257
|
+
let longest;
|
|
24258
|
+
for (const item of css.split(',')) {
|
|
24259
|
+
const times = [...item.matchAll(/(\d*\.?\d+)(ms|s)\b/g)].map(m => parseFloat(m[1]) * (m[2] === 's' ? 1000 : 1));
|
|
24260
|
+
if (times.length > 0)
|
|
24261
|
+
longest = Math.max(longest ?? 0, times[0] + (times[1] ?? 0));
|
|
24262
|
+
}
|
|
24263
|
+
return longest;
|
|
24264
|
+
}
|
|
23748
24265
|
let TransitionCommand = (() => {
|
|
23749
24266
|
let _classDecorators = [command({ name: 'transition' })];
|
|
23750
24267
|
let _classDescriptor;
|
|
@@ -23760,11 +24277,10 @@ let TransitionCommand = (() => {
|
|
|
23760
24277
|
let property;
|
|
23761
24278
|
let target;
|
|
23762
24279
|
const firstArg = await evaluator.evaluate(raw.args[0], context);
|
|
23763
|
-
const
|
|
23764
|
-
|
|
23765
|
-
if (isHTMLElement$2(firstAsElement) ||
|
|
24280
|
+
const owners = elementsOf(firstArg);
|
|
24281
|
+
if (owners.length > 0 ||
|
|
23766
24282
|
(typeof firstArg === 'string' && /^[#.]|^(?:me|it|you)$/.test(firstArg))) {
|
|
23767
|
-
target =
|
|
24283
|
+
target = owners.length === 1 ? owners[0] : owners.length > 1 ? owners : firstArg;
|
|
23768
24284
|
property = String(await evaluator.evaluate(raw.args[1], context));
|
|
23769
24285
|
}
|
|
23770
24286
|
else if (raw.args.length >= 2) {
|
|
@@ -23776,9 +24292,9 @@ let TransitionCommand = (() => {
|
|
|
23776
24292
|
throw new Error('transition requires a CSS property');
|
|
23777
24293
|
property = named.property;
|
|
23778
24294
|
if (named.objectNode !== undefined) {
|
|
23779
|
-
const owner =
|
|
23780
|
-
if (
|
|
23781
|
-
target = owner;
|
|
24295
|
+
const owner = elementsOf(await evaluator.evaluate(named.objectNode, context));
|
|
24296
|
+
if (owner.length > 0)
|
|
24297
|
+
target = owner.length === 1 ? owner[0] : owner;
|
|
23782
24298
|
}
|
|
23783
24299
|
}
|
|
23784
24300
|
else {
|
|
@@ -23789,60 +24305,131 @@ let TransitionCommand = (() => {
|
|
|
23789
24305
|
}
|
|
23790
24306
|
if (!raw.modifiers?.to)
|
|
23791
24307
|
throw new Error('transition requires "to <value>"');
|
|
23792
|
-
|
|
23793
|
-
|
|
23794
|
-
value
|
|
23795
|
-
}
|
|
23796
|
-
const result = { property, value: value };
|
|
24308
|
+
const result = {
|
|
24309
|
+
property,
|
|
24310
|
+
value: await valueOf(raw.modifiers.to, evaluator, context),
|
|
24311
|
+
};
|
|
23797
24312
|
if (target !== undefined)
|
|
23798
24313
|
result.target = target;
|
|
24314
|
+
if (raw.modifiers?.from)
|
|
24315
|
+
result.from = await valueOf(raw.modifiers.from, evaluator, context);
|
|
24316
|
+
if (raw.modifiers?.pairs) {
|
|
24317
|
+
result.pairs = await this.laterPairs(raw.modifiers.pairs, evaluator, context);
|
|
24318
|
+
}
|
|
23799
24319
|
if (raw.modifiers?.over)
|
|
23800
24320
|
result.duration = await evaluator.evaluate(raw.modifiers.over, context);
|
|
23801
24321
|
if (raw.modifiers?.with)
|
|
23802
24322
|
result.timingFunction = String(await evaluator.evaluate(raw.modifiers.with, context));
|
|
24323
|
+
if (raw.modifiers?.using)
|
|
24324
|
+
result.using = String(await evaluator.evaluate(raw.modifiers.using, context));
|
|
23803
24325
|
return result;
|
|
23804
24326
|
}
|
|
24327
|
+
async laterPairs(node, evaluator, context) {
|
|
24328
|
+
const pairs = [];
|
|
24329
|
+
for (const pairNode of node.elements ?? []) {
|
|
24330
|
+
const fields = new Map();
|
|
24331
|
+
const properties = pairNode
|
|
24332
|
+
.properties ?? [];
|
|
24333
|
+
for (const { key, value } of properties) {
|
|
24334
|
+
if (typeof key.name === 'string')
|
|
24335
|
+
fields.set(key.name, value);
|
|
24336
|
+
}
|
|
24337
|
+
const propertyNode = fields.get('property');
|
|
24338
|
+
const toNode = fields.get('to');
|
|
24339
|
+
if (!propertyNode || !toNode)
|
|
24340
|
+
throw new Error('transition requires property and value');
|
|
24341
|
+
const pair = {
|
|
24342
|
+
property: String(await evaluator.evaluate(propertyNode, context)),
|
|
24343
|
+
value: await valueOf(toNode, evaluator, context),
|
|
24344
|
+
};
|
|
24345
|
+
const ownerNode = fields.get('owner');
|
|
24346
|
+
if (ownerNode) {
|
|
24347
|
+
const owners = elementsOf(await evaluator.evaluate(ownerNode, context));
|
|
24348
|
+
if (owners.length === 0)
|
|
24349
|
+
throw new Error('transition: target element not found');
|
|
24350
|
+
pair.target = owners.length === 1 ? owners[0] : owners;
|
|
24351
|
+
}
|
|
24352
|
+
const fromNode = fields.get('from');
|
|
24353
|
+
if (fromNode)
|
|
24354
|
+
pair.from = await valueOf(fromNode, evaluator, context);
|
|
24355
|
+
pairs.push(pair);
|
|
24356
|
+
}
|
|
24357
|
+
return pairs;
|
|
24358
|
+
}
|
|
23805
24359
|
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
|
-
|
|
24360
|
+
const duration = parseDuration(input.duration, (input.using !== undefined && longestTransitionMs(input.using)) || 300);
|
|
24361
|
+
const timing = input.timingFunction || 'ease';
|
|
24362
|
+
const jobs = [];
|
|
24363
|
+
for (const pair of [input, ...(input.pairs ?? [])]) {
|
|
24364
|
+
let property = pair.property;
|
|
24365
|
+
if (property.startsWith('*'))
|
|
24366
|
+
property = property.substring(1);
|
|
24367
|
+
property = camelToKebab(property);
|
|
24368
|
+
const elements = Array.isArray(pair.target)
|
|
24369
|
+
? pair.target.filter(isHTMLElement$2)
|
|
24370
|
+
: [resolveElement(pair.target, context, 'transition')];
|
|
24371
|
+
for (const element of elements) {
|
|
24372
|
+
jobs.push({
|
|
24373
|
+
element,
|
|
24374
|
+
property,
|
|
24375
|
+
pair,
|
|
24376
|
+
fromValue: getComputedStyle(element).getPropertyValue(property),
|
|
24377
|
+
toValue: '',
|
|
24378
|
+
removeInlineAfter: false,
|
|
24379
|
+
});
|
|
24380
|
+
}
|
|
24381
|
+
}
|
|
24382
|
+
if (jobs.length === 0)
|
|
24383
|
+
throw new Error('transition: target element not found');
|
|
24384
|
+
for (const job of jobs) {
|
|
24385
|
+
if (job.pair.from !== undefined) {
|
|
24386
|
+
job.element.style.setProperty(job.property, String(job.pair.from));
|
|
24387
|
+
}
|
|
24388
|
+
}
|
|
24389
|
+
for (const job of jobs)
|
|
24390
|
+
if (job.pair.from !== undefined)
|
|
24391
|
+
void job.element.offsetWidth;
|
|
24392
|
+
const originals = new Map();
|
|
24393
|
+
for (const job of jobs) {
|
|
24394
|
+
if (!originals.has(job.element))
|
|
24395
|
+
originals.set(job.element, job.element.style.transition);
|
|
24396
|
+
}
|
|
24397
|
+
for (const [element, original] of originals) {
|
|
24398
|
+
const own = jobs
|
|
24399
|
+
.filter(job => job.element === element)
|
|
24400
|
+
.map(job => `${job.property} ${duration}ms ${timing}`);
|
|
24401
|
+
element.style.transition = input.using ?? [original, ...own].filter(Boolean).join(', ');
|
|
24402
|
+
}
|
|
24403
|
+
for (const job of jobs) {
|
|
24404
|
+
const { element, property } = job;
|
|
24405
|
+
let toValue = String(job.pair.value);
|
|
24406
|
+
if (RESTORE_KEYWORDS.has(toValue)) {
|
|
24407
|
+
const currentInline = element.style.getPropertyValue(property);
|
|
24408
|
+
element.style.removeProperty(property);
|
|
24409
|
+
toValue = getComputedStyle(element).getPropertyValue(property);
|
|
24410
|
+
if (currentInline) {
|
|
24411
|
+
element.style.setProperty(property, currentInline);
|
|
24412
|
+
}
|
|
24413
|
+
job.removeInlineAfter = true;
|
|
24414
|
+
}
|
|
24415
|
+
job.toValue = toValue;
|
|
24416
|
+
element.style.setProperty(property, toValue);
|
|
24417
|
+
}
|
|
24418
|
+
const results = await Promise.all(jobs.map(job => waitForTransitionEnd(job.element, job.property, duration)));
|
|
24419
|
+
for (const [element, original] of originals)
|
|
24420
|
+
element.style.transition = original;
|
|
24421
|
+
for (const job of jobs) {
|
|
24422
|
+
if (job.removeInlineAfter)
|
|
24423
|
+
job.element.style.removeProperty(job.property);
|
|
23838
24424
|
}
|
|
24425
|
+
const first = jobs[0];
|
|
23839
24426
|
return {
|
|
23840
|
-
element:
|
|
23841
|
-
property,
|
|
23842
|
-
fromValue,
|
|
23843
|
-
toValue,
|
|
24427
|
+
element: first.element,
|
|
24428
|
+
property: first.property,
|
|
24429
|
+
fromValue: first.fromValue,
|
|
24430
|
+
toValue: first.toValue,
|
|
23844
24431
|
duration,
|
|
23845
|
-
completed: result.completed,
|
|
24432
|
+
completed: results.every(result => result.completed),
|
|
23846
24433
|
};
|
|
23847
24434
|
}
|
|
23848
24435
|
};
|
|
@@ -23855,13 +24442,16 @@ let TransitionCommand = (() => {
|
|
|
23855
24442
|
})();
|
|
23856
24443
|
_classThis.metadata = commandMeta({
|
|
23857
24444
|
description: 'Animate CSS properties using CSS transitions',
|
|
23858
|
-
syntax: 'transition [<
|
|
24445
|
+
syntax: 'transition <property> [from <value>] to <value> [<property> \u2026 to <value>]\u2026 [over <duration>] [using <css> | with <timing>]',
|
|
23859
24446
|
examples: [
|
|
23860
24447
|
'transition opacity to 0.5',
|
|
23861
24448
|
'transition my *opacity to 0 over 200ms',
|
|
23862
24449
|
"transition #box's *opacity to 0 over 200ms",
|
|
23863
24450
|
'transition left to 100px over 500ms',
|
|
23864
24451
|
'transition background-color to red over 1s with ease-in-out',
|
|
24452
|
+
'transition *width to 100px *height to 50px over 300ms',
|
|
24453
|
+
'transition *opacity of #panel from 0 to 1 over 200ms',
|
|
24454
|
+
'transition my *opacity to 0 using "opacity 1s ease-in"',
|
|
23865
24455
|
],
|
|
23866
24456
|
sideEffects: ['style-change', 'timing'],
|
|
23867
24457
|
category: 'animation',
|
|
@@ -25111,17 +25701,7 @@ let BeepCommand = (() => {
|
|
|
25111
25701
|
this.debugContext(context);
|
|
25112
25702
|
return { expressionCount: 0, debugged: true, outputs: [] };
|
|
25113
25703
|
}
|
|
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();
|
|
25704
|
+
const outputs = beepValues(context.me, expressions);
|
|
25125
25705
|
return { expressionCount: expressions.length, debugged: true, outputs };
|
|
25126
25706
|
}
|
|
25127
25707
|
debugContext(context) {
|
|
@@ -25134,67 +25714,6 @@ let BeepCommand = (() => {
|
|
|
25134
25714
|
console.log('variables:', context.variables);
|
|
25135
25715
|
console.groupEnd();
|
|
25136
25716
|
}
|
|
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
25717
|
};
|
|
25199
25718
|
__setFunctionName(_classThis, "BeepCommand");
|
|
25200
25719
|
(() => {
|
|
@@ -25742,16 +26261,12 @@ class RenderCommand {
|
|
|
25742
26261
|
}
|
|
25743
26262
|
RenderCommand.metadata = commandMeta({
|
|
25744
26263
|
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
|
-
],
|
|
26264
|
+
syntax: ['render <template>', 'render <template> with <name>: <value>, ...'],
|
|
25750
26265
|
examples: [
|
|
25751
26266
|
'render myTemplate',
|
|
25752
|
-
'render myTemplate with
|
|
25753
|
-
'render "<template>Hello ${name}!</template>" with
|
|
25754
|
-
'render template with
|
|
26267
|
+
'render myTemplate with name: "Alice"',
|
|
26268
|
+
'render "<template>Hello ${name}!</template>" with name: "World"',
|
|
26269
|
+
'render template with items: data',
|
|
25755
26270
|
],
|
|
25756
26271
|
category: 'templates',
|
|
25757
26272
|
compatibility: 'standard',
|
|
@@ -25939,7 +26454,7 @@ function updateDebugStats(event) {
|
|
|
25939
26454
|
stats.confidenceHistory.reduce((a, b) => a + b, 0) / stats.confidenceHistory.length;
|
|
25940
26455
|
}
|
|
25941
26456
|
|
|
25942
|
-
const VERSION = '3.
|
|
26457
|
+
const VERSION = '3.2.0';
|
|
25943
26458
|
|
|
25944
26459
|
function createHistorySwap(config) {
|
|
25945
26460
|
const { target, strategy = 'morph', useViewTransition = false, fetchOptions = {}, transformUrl, onBeforeFetch, onAfterSwap, onError, } = config;
|
|
@@ -27485,6 +28000,14 @@ function convertNode(node, infer) {
|
|
|
27485
28000
|
...pos(node),
|
|
27486
28001
|
};
|
|
27487
28002
|
case 'identifier':
|
|
28003
|
+
if (node.scope === 'element' || node.scope === 'local' || node.scope === 'global') {
|
|
28004
|
+
return {
|
|
28005
|
+
type: 'variable',
|
|
28006
|
+
name: (node.name ?? ''),
|
|
28007
|
+
scope: node.scope,
|
|
28008
|
+
...pos(node),
|
|
28009
|
+
};
|
|
28010
|
+
}
|
|
27488
28011
|
return {
|
|
27489
28012
|
type: 'identifier',
|
|
27490
28013
|
value: (node.name ?? node.value ?? ''),
|
|
@@ -27507,6 +28030,15 @@ function convertNode(node, infer) {
|
|
|
27507
28030
|
operand: convertNode(node.argument ?? node.operand, infer),
|
|
27508
28031
|
...pos(node),
|
|
27509
28032
|
};
|
|
28033
|
+
case 'typeCheckExpression':
|
|
28034
|
+
return {
|
|
28035
|
+
type: 'typeCheck',
|
|
28036
|
+
value: convertNode(node.value, infer),
|
|
28037
|
+
typeName: String(node.typeName),
|
|
28038
|
+
nullOk: node.nullOk !== false,
|
|
28039
|
+
negated: node.negated === true,
|
|
28040
|
+
...pos(node),
|
|
28041
|
+
};
|
|
27510
28042
|
case 'timeExpression':
|
|
27511
28043
|
return { type: 'literal', value: node.value, ...pos(node) };
|
|
27512
28044
|
case 'templateLiteral':
|
|
@@ -27518,6 +28050,28 @@ function convertNode(node, infer) {
|
|
|
27518
28050
|
scope: (node.scope ?? 'local'),
|
|
27519
28051
|
...pos(node),
|
|
27520
28052
|
};
|
|
28053
|
+
case 'arrayLiteral':
|
|
28054
|
+
return {
|
|
28055
|
+
type: 'array',
|
|
28056
|
+
elements: (node.elements ?? []).map(element => convertNode(element, infer)),
|
|
28057
|
+
...pos(node),
|
|
28058
|
+
};
|
|
28059
|
+
case 'attributeAccess':
|
|
28060
|
+
return {
|
|
28061
|
+
type: 'possessive',
|
|
28062
|
+
object: { type: 'identifier', value: 'me' },
|
|
28063
|
+
property: `@${node.attributeName}`,
|
|
28064
|
+
...pos(node),
|
|
28065
|
+
};
|
|
28066
|
+
case 'propertyOfExpression': {
|
|
28067
|
+
const property = node.property;
|
|
28068
|
+
return {
|
|
28069
|
+
type: 'possessive',
|
|
28070
|
+
object: convertNode(node.target, infer),
|
|
28071
|
+
property: (property?.name ?? property?.value ?? ''),
|
|
28072
|
+
...pos(node),
|
|
28073
|
+
};
|
|
28074
|
+
}
|
|
27521
28075
|
case 'htmlSelector':
|
|
27522
28076
|
return {
|
|
27523
28077
|
type: 'selector',
|
|
@@ -27569,6 +28123,21 @@ function convertCommand$1(node, infer) {
|
|
|
27569
28123
|
if (name === 'repeat') {
|
|
27570
28124
|
return convertRepeatCommand(node, infer);
|
|
27571
28125
|
}
|
|
28126
|
+
const original = node.originalCommand;
|
|
28127
|
+
const sum = node.modifiers?.to;
|
|
28128
|
+
const counted = node.args?.[0];
|
|
28129
|
+
if (name === 'set' &&
|
|
28130
|
+
(original === 'increment' || original === 'decrement') &&
|
|
28131
|
+
sum?.type === 'binaryExpression' &&
|
|
28132
|
+
counted) {
|
|
28133
|
+
return convertCommand$1({
|
|
28134
|
+
...node,
|
|
28135
|
+
name: original,
|
|
28136
|
+
args: [counted],
|
|
28137
|
+
modifiers: { by: sum.right },
|
|
28138
|
+
originalCommand: undefined,
|
|
28139
|
+
}, infer);
|
|
28140
|
+
}
|
|
27572
28141
|
const args = (node.args ?? []).map(arg => convertNode(arg, infer));
|
|
27573
28142
|
const target = node.target ? convertNode(node.target, infer) : undefined;
|
|
27574
28143
|
const modifiers = node.modifiers
|
|
@@ -27617,6 +28186,64 @@ function convertIfCommand(node, infer) {
|
|
|
27617
28186
|
};
|
|
27618
28187
|
}
|
|
27619
28188
|
function convertRepeatCommand(node, infer) {
|
|
28189
|
+
const slots = (node.modifiers ?? {});
|
|
28190
|
+
const form = slotText(slots.loopType);
|
|
28191
|
+
if (form === undefined)
|
|
28192
|
+
return convertPositionalRepeat(node, infer);
|
|
28193
|
+
const [bodyBlock, elseBlock] = (node.args ?? []);
|
|
28194
|
+
const body = extractBlockCommands(bodyBlock, infer);
|
|
28195
|
+
const indexName = slotText(slots.index);
|
|
28196
|
+
const shared = {
|
|
28197
|
+
...(indexName ? { indexName } : {}),
|
|
28198
|
+
...(elseBlock ? { elseBody: extractBlockCommands(elseBlock, infer) } : {}),
|
|
28199
|
+
...pos(node),
|
|
28200
|
+
};
|
|
28201
|
+
const operand = (slot) => (slot ? convertNode(slot, infer) : undefined);
|
|
28202
|
+
switch (form) {
|
|
28203
|
+
case 'for':
|
|
28204
|
+
return {
|
|
28205
|
+
type: 'foreach',
|
|
28206
|
+
itemName: slotText(slots.for) ?? 'it',
|
|
28207
|
+
collection: operand(slots.in) ?? { type: 'literal', value: null },
|
|
28208
|
+
body,
|
|
28209
|
+
...shared,
|
|
28210
|
+
};
|
|
28211
|
+
case 'times':
|
|
28212
|
+
return { type: 'repeat', count: operand(slots.times), body, ...shared };
|
|
28213
|
+
case 'while':
|
|
28214
|
+
case 'until': {
|
|
28215
|
+
const test = operand(slots[form]) ?? { type: 'literal', value: form === 'until' };
|
|
28216
|
+
return {
|
|
28217
|
+
type: 'while',
|
|
28218
|
+
condition: form === 'until' ? { type: 'unary', operator: 'not', operand: test } : test,
|
|
28219
|
+
...(slots.bottomTested?.value === true ? { bottomTested: true } : {}),
|
|
28220
|
+
body,
|
|
28221
|
+
...shared,
|
|
28222
|
+
};
|
|
28223
|
+
}
|
|
28224
|
+
case 'until-event': {
|
|
28225
|
+
const event = slotText(slots.event);
|
|
28226
|
+
if (!event) {
|
|
28227
|
+
return { type: 'error', message: "'repeat until event' has no event name", ...pos(node) };
|
|
28228
|
+
}
|
|
28229
|
+
const target = operand(slots.from);
|
|
28230
|
+
return {
|
|
28231
|
+
type: 'repeat',
|
|
28232
|
+
untilEvent: event,
|
|
28233
|
+
...(target ? { untilEventTarget: target } : {}),
|
|
28234
|
+
body,
|
|
28235
|
+
...shared,
|
|
28236
|
+
};
|
|
28237
|
+
}
|
|
28238
|
+
default:
|
|
28239
|
+
return { type: 'repeat', body, ...shared };
|
|
28240
|
+
}
|
|
28241
|
+
}
|
|
28242
|
+
function slotText(slot) {
|
|
28243
|
+
const text = slot?.value ?? slot?.name;
|
|
28244
|
+
return typeof text === 'string' ? text : undefined;
|
|
28245
|
+
}
|
|
28246
|
+
function convertPositionalRepeat(node, infer) {
|
|
27620
28247
|
const args = (node.args ?? []);
|
|
27621
28248
|
if (args.length === 0) {
|
|
27622
28249
|
return { type: 'repeat', body: [], ...pos(node) };
|
|
@@ -27933,6 +28560,15 @@ function toCoreAST(node) {
|
|
|
27933
28560
|
prefix: true,
|
|
27934
28561
|
...nodePos(node),
|
|
27935
28562
|
};
|
|
28563
|
+
case 'typeCheck':
|
|
28564
|
+
return {
|
|
28565
|
+
type: 'typeCheckExpression',
|
|
28566
|
+
value: toCoreAST(node.value),
|
|
28567
|
+
typeName: node.typeName,
|
|
28568
|
+
nullOk: node.nullOk,
|
|
28569
|
+
negated: node.negated,
|
|
28570
|
+
...nodePos(node),
|
|
28571
|
+
};
|
|
27936
28572
|
case 'member':
|
|
27937
28573
|
return {
|
|
27938
28574
|
type: 'memberExpression',
|
|
@@ -27943,6 +28579,12 @@ function toCoreAST(node) {
|
|
|
27943
28579
|
computed: node.computed ?? false,
|
|
27944
28580
|
...nodePos(node),
|
|
27945
28581
|
};
|
|
28582
|
+
case 'array':
|
|
28583
|
+
return {
|
|
28584
|
+
type: 'arrayLiteral',
|
|
28585
|
+
elements: node.elements.map(element => toCoreAST(element)),
|
|
28586
|
+
...nodePos(node),
|
|
28587
|
+
};
|
|
27946
28588
|
case 'possessive':
|
|
27947
28589
|
return {
|
|
27948
28590
|
type: 'possessiveExpression',
|
|
@@ -28005,72 +28647,85 @@ function convertCommand(node) {
|
|
|
28005
28647
|
}
|
|
28006
28648
|
return result;
|
|
28007
28649
|
}
|
|
28650
|
+
function block(nodes) {
|
|
28651
|
+
return { type: 'block', commands: nodes.map(n => toCoreAST(n)), ...POS };
|
|
28652
|
+
}
|
|
28008
28653
|
function convertIf(node) {
|
|
28009
|
-
const
|
|
28654
|
+
const [elseIf, ...laterElseIfs] = node.elseIfBranches ?? [];
|
|
28655
|
+
const elseBranch = elseIf
|
|
28656
|
+
? [
|
|
28657
|
+
{
|
|
28658
|
+
type: 'if',
|
|
28659
|
+
condition: elseIf.condition,
|
|
28660
|
+
thenBranch: elseIf.body,
|
|
28661
|
+
elseIfBranches: laterElseIfs,
|
|
28662
|
+
...(node.elseBranch ? { elseBranch: node.elseBranch } : {}),
|
|
28663
|
+
},
|
|
28664
|
+
]
|
|
28665
|
+
: node.elseBranch;
|
|
28666
|
+
return {
|
|
28010
28667
|
type: 'command',
|
|
28011
28668
|
name: 'if',
|
|
28012
28669
|
isBlocking: true,
|
|
28013
|
-
|
|
28014
|
-
|
|
28015
|
-
|
|
28670
|
+
args: [
|
|
28671
|
+
toCoreAST(node.condition),
|
|
28672
|
+
block(node.thenBranch),
|
|
28673
|
+
...(elseBranch ? [block(elseBranch)] : []),
|
|
28674
|
+
],
|
|
28016
28675
|
...nodePos(node),
|
|
28017
28676
|
};
|
|
28018
|
-
if (node.elseBranch) {
|
|
28019
|
-
result.elseBranch = node.elseBranch.map(n => toCoreAST(n));
|
|
28020
|
-
}
|
|
28021
|
-
return result;
|
|
28022
28677
|
}
|
|
28023
|
-
function
|
|
28024
|
-
const
|
|
28678
|
+
function loopCommand(node, form, operands) {
|
|
28679
|
+
const modifiers = { loopType: slotString(form) };
|
|
28680
|
+
for (const [slot, value] of Object.entries(operands)) {
|
|
28681
|
+
if (value)
|
|
28682
|
+
modifiers[slot] = value;
|
|
28683
|
+
}
|
|
28684
|
+
if (node.indexName)
|
|
28685
|
+
modifiers.index = slotString(node.indexName);
|
|
28686
|
+
return {
|
|
28025
28687
|
type: 'command',
|
|
28026
28688
|
name: 'repeat',
|
|
28027
28689
|
isBlocking: true,
|
|
28028
|
-
|
|
28029
|
-
|
|
28690
|
+
args: [block(node.body), ...(node.elseBody ? [block(node.elseBody)] : [])],
|
|
28691
|
+
modifiers,
|
|
28030
28692
|
...nodePos(node),
|
|
28031
28693
|
};
|
|
28694
|
+
}
|
|
28695
|
+
function slotString(value) {
|
|
28696
|
+
return { type: 'string', value, ...POS };
|
|
28697
|
+
}
|
|
28698
|
+
function convertRepeat(node) {
|
|
28032
28699
|
if (node.count !== undefined) {
|
|
28033
|
-
|
|
28034
|
-
|
|
28035
|
-
|
|
28036
|
-
|
|
28037
|
-
|
|
28038
|
-
|
|
28039
|
-
|
|
28040
|
-
|
|
28041
|
-
|
|
28042
|
-
|
|
28043
|
-
|
|
28044
|
-
|
|
28045
|
-
|
|
28700
|
+
const times = typeof node.count === 'number'
|
|
28701
|
+
? { type: 'literal', value: node.count, raw: String(node.count), ...POS }
|
|
28702
|
+
: toCoreAST(node.count);
|
|
28703
|
+
return loopCommand(node, 'times', { times });
|
|
28704
|
+
}
|
|
28705
|
+
if (node.whileCondition !== undefined) {
|
|
28706
|
+
return loopCommand(node, 'while', { while: toCoreAST(node.whileCondition) });
|
|
28707
|
+
}
|
|
28708
|
+
if (node.untilEvent !== undefined) {
|
|
28709
|
+
return loopCommand(node, 'until-event', {
|
|
28710
|
+
event: slotString(node.untilEvent),
|
|
28711
|
+
from: node.untilEventTarget ? toCoreAST(node.untilEventTarget) : undefined,
|
|
28712
|
+
});
|
|
28046
28713
|
}
|
|
28047
|
-
return
|
|
28714
|
+
return loopCommand(node, 'forever', {});
|
|
28048
28715
|
}
|
|
28049
28716
|
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
|
-
};
|
|
28717
|
+
return loopCommand(node, 'for', {
|
|
28718
|
+
for: slotString(node.itemName),
|
|
28719
|
+
in: toCoreAST(node.collection),
|
|
28720
|
+
});
|
|
28062
28721
|
}
|
|
28063
28722
|
function convertWhile(node) {
|
|
28064
|
-
return {
|
|
28065
|
-
|
|
28066
|
-
|
|
28067
|
-
|
|
28068
|
-
|
|
28069
|
-
|
|
28070
|
-
body: node.body.map(n => toCoreAST(n)),
|
|
28071
|
-
args: [],
|
|
28072
|
-
...nodePos(node),
|
|
28073
|
-
};
|
|
28723
|
+
return loopCommand(node, 'while', {
|
|
28724
|
+
while: toCoreAST(node.condition),
|
|
28725
|
+
bottomTested: node.bottomTested
|
|
28726
|
+
? { type: 'literal', value: true, raw: 'true', ...POS }
|
|
28727
|
+
: undefined,
|
|
28728
|
+
});
|
|
28074
28729
|
}
|
|
28075
28730
|
function flattenEventModifiers(modifiers) {
|
|
28076
28731
|
if (!modifiers)
|