@hyperfixi/core 3.1.0 → 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-DylVo1oZ.js +0 -2
- package/dist/chunks/browser-modular-sLPMdxR7.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.mjs
CHANGED
|
@@ -2073,6 +2073,39 @@ function makeTypeCheckHandler(negated) {
|
|
|
2073
2073
|
};
|
|
2074
2074
|
};
|
|
2075
2075
|
}
|
|
2076
|
+
const OF_OPERAND_BP = 70;
|
|
2077
|
+
function parseOfOperand(ctx, minBp = OF_OPERAND_BP) {
|
|
2078
|
+
let right = ctx.parseExpr(minBp);
|
|
2079
|
+
if (ctx.peek()?.value === 'in') {
|
|
2080
|
+
const inToken = ctx.advance();
|
|
2081
|
+
const scope = ctx.parseExpr(minBp);
|
|
2082
|
+
right = {
|
|
2083
|
+
type: 'binaryExpression',
|
|
2084
|
+
operator: 'in',
|
|
2085
|
+
left: right,
|
|
2086
|
+
right: scope,
|
|
2087
|
+
start: right.start,
|
|
2088
|
+
end: scope.end ?? inToken.end,
|
|
2089
|
+
line: right.line ?? inToken.line,
|
|
2090
|
+
column: right.column ?? inToken.column,
|
|
2091
|
+
};
|
|
2092
|
+
}
|
|
2093
|
+
return right;
|
|
2094
|
+
}
|
|
2095
|
+
const THE_OF_TARGET_BP = OF_OPERAND_BP + 1;
|
|
2096
|
+
function ofHandler(left, token, ctx) {
|
|
2097
|
+
const right = parseOfOperand(ctx);
|
|
2098
|
+
return {
|
|
2099
|
+
type: 'binaryExpression',
|
|
2100
|
+
operator: token.value,
|
|
2101
|
+
left,
|
|
2102
|
+
right,
|
|
2103
|
+
start: left.start,
|
|
2104
|
+
end: right.end ?? token.end,
|
|
2105
|
+
line: left.line ?? token.line,
|
|
2106
|
+
column: left.column ?? token.column,
|
|
2107
|
+
};
|
|
2108
|
+
}
|
|
2076
2109
|
const PARSER_COMPARISON_FRAGMENT = new Map([
|
|
2077
2110
|
['===', leftAssoc(30)],
|
|
2078
2111
|
['!==', leftAssoc(30)],
|
|
@@ -2117,7 +2150,7 @@ const PARSER_COMPARISON_FRAGMENT = new Map([
|
|
|
2117
2150
|
['is greater than or equal to', leftAssoc(30)],
|
|
2118
2151
|
['is less than or equal to', leftAssoc(30)],
|
|
2119
2152
|
['in', leftAssoc(30)],
|
|
2120
|
-
['of',
|
|
2153
|
+
['of', { infix: { bp: [90, OF_OPERAND_BP], handler: ofHandler } }],
|
|
2121
2154
|
['really', leftAssoc(30)],
|
|
2122
2155
|
[
|
|
2123
2156
|
'exists',
|
|
@@ -2821,6 +2854,48 @@ function parseMaybeNamedArgument(ctx) {
|
|
|
2821
2854
|
const value = ctx.parseExpression();
|
|
2822
2855
|
return name !== undefined ? { name, value } : { value };
|
|
2823
2856
|
}
|
|
2857
|
+
function isNakedNamedArgStart(ctx) {
|
|
2858
|
+
if (ctx.check('{'))
|
|
2859
|
+
return false;
|
|
2860
|
+
if (!ctx.checkIdentifierLike())
|
|
2861
|
+
return false;
|
|
2862
|
+
const next = ctx.peekAt(1);
|
|
2863
|
+
return next !== null && next.value === ':';
|
|
2864
|
+
}
|
|
2865
|
+
function parseNakedNamedArgs(ctx, parseValue, command) {
|
|
2866
|
+
const properties = [];
|
|
2867
|
+
const startPos = ctx.getPosition();
|
|
2868
|
+
do {
|
|
2869
|
+
if (!ctx.checkIdentifierLike())
|
|
2870
|
+
break;
|
|
2871
|
+
const keyToken = ctx.advance();
|
|
2872
|
+
const key = {
|
|
2873
|
+
type: 'identifier',
|
|
2874
|
+
name: keyToken.value,
|
|
2875
|
+
start: keyToken.start,
|
|
2876
|
+
end: keyToken.end,
|
|
2877
|
+
line: keyToken.line,
|
|
2878
|
+
column: keyToken.column,
|
|
2879
|
+
};
|
|
2880
|
+
ctx.consume(':', `Expected ':' after property name in ${command} named arguments`);
|
|
2881
|
+
const value = parseValue();
|
|
2882
|
+
if (value) {
|
|
2883
|
+
properties.push({ key, value });
|
|
2884
|
+
}
|
|
2885
|
+
} while (ctx.match(',') && !ctx.isAtEnd());
|
|
2886
|
+
const endPos = ctx.getPosition();
|
|
2887
|
+
return {
|
|
2888
|
+
type: 'objectLiteral',
|
|
2889
|
+
properties,
|
|
2890
|
+
start: startPos.start,
|
|
2891
|
+
end: endPos.end,
|
|
2892
|
+
line: startPos.line,
|
|
2893
|
+
column: startPos.column,
|
|
2894
|
+
};
|
|
2895
|
+
}
|
|
2896
|
+
function isGluedBeepBang(beep, bang) {
|
|
2897
|
+
return beep?.value === 'beep' && bang?.value === '!' && bang.start === beep.end;
|
|
2898
|
+
}
|
|
2824
2899
|
|
|
2825
2900
|
class CommandNodeBuilder {
|
|
2826
2901
|
constructor(name) {
|
|
@@ -3022,6 +3097,8 @@ function parseTriggerCommand(ctx, identifierNode) {
|
|
|
3022
3097
|
const modifiers = {};
|
|
3023
3098
|
while (!isCommandBoundary(ctx)) {
|
|
3024
3099
|
if (ctx.check(KEYWORDS.ON) || ctx.check(KEYWORDS.TO)) {
|
|
3100
|
+
if (modifiers['on'])
|
|
3101
|
+
break;
|
|
3025
3102
|
ctx.advance();
|
|
3026
3103
|
const target = parseOneArgument(ctx, [KEYWORDS.WITH]);
|
|
3027
3104
|
if (target)
|
|
@@ -3162,6 +3239,9 @@ function parseRepeatCommand(ctx, commandToken) {
|
|
|
3162
3239
|
ctx.advance();
|
|
3163
3240
|
loopType = KEYWORDS.FOREVER;
|
|
3164
3241
|
}
|
|
3242
|
+
else if (ctx.checkIsCommand() || ctx.isCommand(ctx.peek().value)) {
|
|
3243
|
+
loopType = KEYWORDS.FOREVER;
|
|
3244
|
+
}
|
|
3165
3245
|
else {
|
|
3166
3246
|
times = ctx.parseExpression();
|
|
3167
3247
|
if (ctx.check(KEYWORDS.TIMES)) {
|
|
@@ -3204,10 +3284,12 @@ function parseRepeatCommand(ctx, commandToken) {
|
|
|
3204
3284
|
loopType = result.terminator;
|
|
3205
3285
|
ctx.advance();
|
|
3206
3286
|
condition = ctx.parseExpression();
|
|
3207
|
-
if (
|
|
3287
|
+
if (ctx.check('end')) {
|
|
3288
|
+
ctx.advance();
|
|
3289
|
+
}
|
|
3290
|
+
else if (!ctx.atEndOfInput()) {
|
|
3208
3291
|
throw new Error('Expected "end" to close repeat block');
|
|
3209
3292
|
}
|
|
3210
|
-
ctx.advance();
|
|
3211
3293
|
}
|
|
3212
3294
|
}
|
|
3213
3295
|
else {
|
|
@@ -3453,7 +3535,7 @@ function parseIfCommand(ctx, commandToken) {
|
|
|
3453
3535
|
}));
|
|
3454
3536
|
}
|
|
3455
3537
|
}
|
|
3456
|
-
if (!consumedElseIf && !ctx.
|
|
3538
|
+
if (!consumedElseIf && !ctx.atEndOfInput()) {
|
|
3457
3539
|
ctx.consume(KEYWORDS.END, "Expected 'end' after if block");
|
|
3458
3540
|
}
|
|
3459
3541
|
}
|
|
@@ -3478,7 +3560,6 @@ function parseIfCommand(ctx, commandToken) {
|
|
|
3478
3560
|
.build();
|
|
3479
3561
|
}
|
|
3480
3562
|
function parseForCommand(ctx, commandToken) {
|
|
3481
|
-
const args = [];
|
|
3482
3563
|
let variable = null;
|
|
3483
3564
|
let collection = null;
|
|
3484
3565
|
if (ctx.check(KEYWORDS.EACH)) {
|
|
@@ -3521,31 +3602,22 @@ function parseForCommand(ctx, commandToken) {
|
|
|
3521
3602
|
}
|
|
3522
3603
|
}
|
|
3523
3604
|
const commands = ctx.parseCommandListUntilEnd('for');
|
|
3524
|
-
|
|
3525
|
-
type: 'identifier',
|
|
3526
|
-
name: 'for',
|
|
3527
|
-
start: commandToken.start,
|
|
3528
|
-
end: commandToken.end,
|
|
3529
|
-
line: commandToken.line,
|
|
3530
|
-
column: commandToken.column,
|
|
3531
|
-
});
|
|
3532
|
-
const forPos = {
|
|
3605
|
+
const pos = {
|
|
3533
3606
|
start: commandToken.start,
|
|
3534
3607
|
end: commandToken.end,
|
|
3535
3608
|
line: commandToken.line,
|
|
3536
3609
|
column: commandToken.column,
|
|
3537
3610
|
};
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
}
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
.withArgs(...args)
|
|
3611
|
+
const modifiers = {
|
|
3612
|
+
loopType: toLegacyExpression(createStringLiteral(KEYWORDS.FOR, pos)),
|
|
3613
|
+
for: toLegacyExpression(createStringLiteral(variable, pos)),
|
|
3614
|
+
in: collection,
|
|
3615
|
+
};
|
|
3616
|
+
if (indexVariable)
|
|
3617
|
+
modifiers['index'] = toLegacyExpression(createStringLiteral(indexVariable, pos));
|
|
3618
|
+
return CommandNodeBuilder.from({ ...commandToken, value: 'repeat' })
|
|
3619
|
+
.withArgs(createBlock(commands, { ...pos, end: pos.end || 0 }))
|
|
3620
|
+
.withModifiers(modifiers)
|
|
3549
3621
|
.endingAt(ctx.getPosition())
|
|
3550
3622
|
.build();
|
|
3551
3623
|
}
|
|
@@ -3601,86 +3673,159 @@ function parseMeasureCommand(ctx, identifierNode) {
|
|
|
3601
3673
|
}
|
|
3602
3674
|
return builder.build();
|
|
3603
3675
|
}
|
|
3604
|
-
const
|
|
3676
|
+
const CONTEXT_REFS = new Set(['me', 'it', 'you', 'result']);
|
|
3605
3677
|
function parseTransitionCommand(ctx, commandToken) {
|
|
3606
|
-
const
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
const
|
|
3611
|
-
const
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
if (
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
target = parsed.object;
|
|
3622
|
-
property = {
|
|
3623
|
-
type: 'string',
|
|
3624
|
-
value: parsed.property.name,
|
|
3625
|
-
start: leadToken.start || 0,
|
|
3626
|
-
end: ctx.getPosition().end,
|
|
3627
|
-
line: leadToken.line,
|
|
3628
|
-
column: leadToken.column,
|
|
3629
|
-
};
|
|
3678
|
+
const pairs = [];
|
|
3679
|
+
do {
|
|
3680
|
+
pairs.push(parseTransitionPair(ctx));
|
|
3681
|
+
} while (startsAnotherPair(ctx));
|
|
3682
|
+
const [first, ...rest] = pairs;
|
|
3683
|
+
const args = first.owner ? [first.owner, first.property] : [first.property];
|
|
3684
|
+
const modifiers = { to: first.to };
|
|
3685
|
+
if (first.from)
|
|
3686
|
+
modifiers['from'] = first.from;
|
|
3687
|
+
if (rest.length > 0)
|
|
3688
|
+
modifiers['pairs'] = pairsNode(rest);
|
|
3689
|
+
for (let i = 0; i < 3 && !ctx.isAtEnd(); i++) {
|
|
3690
|
+
if (ctx.check('over') && !modifiers['over']) {
|
|
3691
|
+
ctx.advance();
|
|
3692
|
+
modifiers['over'] = ctx.parseExpression();
|
|
3630
3693
|
}
|
|
3631
|
-
else {
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
ctx.advance();
|
|
3694
|
+
else if (ctx.check('using') && !modifiers['using']) {
|
|
3695
|
+
ctx.advance();
|
|
3696
|
+
modifiers['using'] = ctx.parseExpression();
|
|
3635
3697
|
}
|
|
3636
|
-
|
|
3637
|
-
const firstToken = ctx.peek();
|
|
3638
|
-
if (!property && (isIdentifierLike(firstToken) || firstToken.value === '*')) {
|
|
3639
|
-
let propertyValue = '';
|
|
3640
|
-
if (ctx.check('*')) {
|
|
3641
|
-
propertyValue = '*';
|
|
3698
|
+
else if (ctx.check(KEYWORDS.WITH) && !modifiers['with']) {
|
|
3642
3699
|
ctx.advance();
|
|
3700
|
+
modifiers['with'] = ctx.parsePrimary();
|
|
3643
3701
|
}
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
propertyValue += hyphenatedName;
|
|
3647
|
-
property = {
|
|
3648
|
-
type: 'string',
|
|
3649
|
-
value: propertyValue,
|
|
3650
|
-
start: firstToken.start || 0,
|
|
3651
|
-
end: ctx.getPosition().end,
|
|
3652
|
-
line: firstToken.line,
|
|
3653
|
-
column: firstToken.column,
|
|
3654
|
-
};
|
|
3702
|
+
else {
|
|
3703
|
+
break;
|
|
3655
3704
|
}
|
|
3656
3705
|
}
|
|
3657
|
-
|
|
3658
|
-
|
|
3706
|
+
return CommandNodeBuilder.from(commandToken)
|
|
3707
|
+
.withArgs(...args)
|
|
3708
|
+
.withModifiers(modifiers)
|
|
3709
|
+
.endingAt(ctx.getPosition())
|
|
3710
|
+
.build();
|
|
3711
|
+
}
|
|
3712
|
+
function parseTransitionPair(ctx) {
|
|
3713
|
+
const { owner, property } = parseTransitionProperty(ctx);
|
|
3714
|
+
let from = null;
|
|
3715
|
+
if (ctx.check('from')) {
|
|
3716
|
+
ctx.advance();
|
|
3717
|
+
from = ctx.parseExpression();
|
|
3659
3718
|
}
|
|
3660
|
-
if (target)
|
|
3661
|
-
args.push(target);
|
|
3662
|
-
args.push(property);
|
|
3663
3719
|
if (!ctx.check(KEYWORDS.TO)) {
|
|
3664
3720
|
throw new Error('Expected "to" keyword after property in transition command');
|
|
3665
3721
|
}
|
|
3666
3722
|
ctx.advance();
|
|
3667
|
-
const
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3723
|
+
const to = ctx.parseExpression();
|
|
3724
|
+
return { owner, property, from, to };
|
|
3725
|
+
}
|
|
3726
|
+
function parseTransitionProperty(ctx) {
|
|
3727
|
+
const lead = ctx.peek();
|
|
3728
|
+
if (!lead || isKeyword(lead, [KEYWORDS.TO, 'from'])) {
|
|
3729
|
+
throw new Error('Transition command requires a CSS property');
|
|
3673
3730
|
}
|
|
3674
|
-
|
|
3731
|
+
const expr = ctx.parseExpression();
|
|
3732
|
+
const split = splitStyleExpression(expr);
|
|
3733
|
+
if (split)
|
|
3734
|
+
return { owner: split.owner, property: propertyNode(split.name, lead, ctx) };
|
|
3735
|
+
let name = '';
|
|
3736
|
+
if (ctx.check('*')) {
|
|
3737
|
+
name = '*';
|
|
3675
3738
|
ctx.advance();
|
|
3676
|
-
const timingFunction = ctx.parsePrimary();
|
|
3677
|
-
modifiers['with'] = timingFunction;
|
|
3678
3739
|
}
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3740
|
+
const hyphenated = parseHyphenatedName(ctx);
|
|
3741
|
+
if (!hyphenated)
|
|
3742
|
+
throw new Error('Transition command requires a CSS property');
|
|
3743
|
+
return { owner: expr, property: propertyNode(name + hyphenated, lead, ctx) };
|
|
3744
|
+
}
|
|
3745
|
+
function splitStyleExpression(node) {
|
|
3746
|
+
const n = node;
|
|
3747
|
+
switch (n.type) {
|
|
3748
|
+
case 'selector':
|
|
3749
|
+
return typeof n.value === 'string' && n.value.startsWith('*')
|
|
3750
|
+
? { owner: null, name: n.value }
|
|
3751
|
+
: null;
|
|
3752
|
+
case 'identifier':
|
|
3753
|
+
return typeof n.name === 'string' && !CONTEXT_REFS.has(n.name)
|
|
3754
|
+
? { owner: null, name: n.name }
|
|
3755
|
+
: null;
|
|
3756
|
+
case 'possessiveExpression':
|
|
3757
|
+
case 'memberExpression':
|
|
3758
|
+
return !n.computed && n.object && typeof n.property?.name === 'string'
|
|
3759
|
+
? { owner: n.object, name: n.property.name }
|
|
3760
|
+
: null;
|
|
3761
|
+
case 'binaryExpression': {
|
|
3762
|
+
if (n.operator !== 'of' || !n.left || !n.right)
|
|
3763
|
+
return null;
|
|
3764
|
+
const inner = splitStyleExpression(n.left);
|
|
3765
|
+
return inner && !inner.owner ? { owner: n.right, name: inner.name } : null;
|
|
3766
|
+
}
|
|
3767
|
+
case 'propertyOfExpression':
|
|
3768
|
+
return n.target && typeof n.property?.name === 'string'
|
|
3769
|
+
? { owner: n.target, name: n.property.name }
|
|
3770
|
+
: null;
|
|
3771
|
+
default:
|
|
3772
|
+
return null;
|
|
3773
|
+
}
|
|
3774
|
+
}
|
|
3775
|
+
function propertyNode(name, lead, ctx) {
|
|
3776
|
+
return {
|
|
3777
|
+
type: 'string',
|
|
3778
|
+
value: name,
|
|
3779
|
+
start: lead.start || 0,
|
|
3780
|
+
end: ctx.getPosition().end,
|
|
3781
|
+
line: lead.line,
|
|
3782
|
+
column: lead.column,
|
|
3783
|
+
};
|
|
3784
|
+
}
|
|
3785
|
+
function startsAnotherPair(ctx) {
|
|
3786
|
+
if (ctx.isAtEnd() || isComment(ctx.peek()))
|
|
3787
|
+
return false;
|
|
3788
|
+
if (isKeyword(ctx.peek(), ['over', 'using', KEYWORDS.WITH]))
|
|
3789
|
+
return false;
|
|
3790
|
+
return !isCommandBoundary(ctx, ['catch', 'finally', 'on']);
|
|
3791
|
+
}
|
|
3792
|
+
function pairsNode(pairs) {
|
|
3793
|
+
const field = (name, value) => ({
|
|
3794
|
+
key: {
|
|
3795
|
+
type: 'identifier',
|
|
3796
|
+
name,
|
|
3797
|
+
start: value.start,
|
|
3798
|
+
end: value.end,
|
|
3799
|
+
line: value.line,
|
|
3800
|
+
column: value.column,
|
|
3801
|
+
},
|
|
3802
|
+
value,
|
|
3803
|
+
});
|
|
3804
|
+
const elements = pairs.map(pair => {
|
|
3805
|
+
const properties = [field('property', pair.property), field('to', pair.to)];
|
|
3806
|
+
if (pair.owner)
|
|
3807
|
+
properties.push(field('owner', pair.owner));
|
|
3808
|
+
if (pair.from)
|
|
3809
|
+
properties.push(field('from', pair.from));
|
|
3810
|
+
return {
|
|
3811
|
+
type: 'objectLiteral',
|
|
3812
|
+
properties,
|
|
3813
|
+
start: (pair.owner ?? pair.property).start,
|
|
3814
|
+
end: pair.to.end,
|
|
3815
|
+
line: (pair.owner ?? pair.property).line,
|
|
3816
|
+
column: (pair.owner ?? pair.property).column,
|
|
3817
|
+
};
|
|
3818
|
+
});
|
|
3819
|
+
const firstEl = elements[0];
|
|
3820
|
+
const lastEl = elements[elements.length - 1];
|
|
3821
|
+
return {
|
|
3822
|
+
type: 'arrayLiteral',
|
|
3823
|
+
elements,
|
|
3824
|
+
start: firstEl.start,
|
|
3825
|
+
end: lastEl.end,
|
|
3826
|
+
line: firstEl.line,
|
|
3827
|
+
column: firstEl.column,
|
|
3828
|
+
};
|
|
3684
3829
|
}
|
|
3685
3830
|
function parseStartCommand(ctx, identifierNode) {
|
|
3686
3831
|
if (!ctx.match('view')) {
|
|
@@ -3977,11 +4122,17 @@ function parseSwapCommand(ctx, identifierNode) {
|
|
|
3977
4122
|
const targetExpr = isDelete ? ctx.parseExpression() : parseOneArgument(ctx, [KEYWORDS.WITH]);
|
|
3978
4123
|
if (targetExpr)
|
|
3979
4124
|
args.push(targetExpr);
|
|
3980
|
-
|
|
4125
|
+
const isMorph = identifierNode.name === 'morph';
|
|
4126
|
+
if (!isDelete &&
|
|
4127
|
+
(consumeOptionalKeyword(ctx, KEYWORDS.WITH) ||
|
|
4128
|
+
(isMorph && consumeOptionalKeyword(ctx, KEYWORDS.TO)))) {
|
|
3981
4129
|
const contentExpr = parseOneArgument(ctx, ['using']);
|
|
3982
4130
|
if (contentExpr)
|
|
3983
4131
|
modifiers['with'] = contentExpr;
|
|
3984
4132
|
}
|
|
4133
|
+
if (isMorph && modifiers['with'] === undefined) {
|
|
4134
|
+
ctx.addError("Expected 'to' and the content to morph into");
|
|
4135
|
+
}
|
|
3985
4136
|
return CommandNodeBuilder.fromIdentifier(identifierNode)
|
|
3986
4137
|
.withArgs(...args)
|
|
3987
4138
|
.withModifiers({ ...modifiers, ...parseViewTransitionTail(ctx) })
|
|
@@ -4742,6 +4893,8 @@ function atArgumentBoundary(ctx, grammar) {
|
|
|
4742
4893
|
const next = ctx.peek();
|
|
4743
4894
|
if (ctx.checkIsCommand() && ctx.peekAt(1)?.value === '(')
|
|
4744
4895
|
return false;
|
|
4896
|
+
if (isGluedBeepBang(next, ctx.peekAt(1)))
|
|
4897
|
+
return false;
|
|
4745
4898
|
if (isCommandBoundary(ctx, ['catch', 'finally', 'on']))
|
|
4746
4899
|
return true;
|
|
4747
4900
|
return isKeyword(next, [...grammar.markers]);
|
|
@@ -4773,10 +4926,13 @@ function parseDeclaredCommand(ctx, commandToken, name, grammar) {
|
|
|
4773
4926
|
}
|
|
4774
4927
|
}
|
|
4775
4928
|
const commaList = new Set(grammar.commaList ?? []);
|
|
4929
|
+
const namedArgs = new Set(grammar.namedArgs ?? []);
|
|
4776
4930
|
while (!ctx.isAtEnd() && isKeyword(ctx.peek(), [...grammar.markers])) {
|
|
4777
4931
|
const keyword = ctx.advance().value;
|
|
4778
4932
|
const key = ctx.resolveKeyword(keyword).toLowerCase();
|
|
4779
|
-
let value =
|
|
4933
|
+
let value = namedArgs.has(key) && isNakedNamedArgStart(ctx)
|
|
4934
|
+
? parseNakedNamedArgs(ctx, () => ctx.parseExpression(), name)
|
|
4935
|
+
: ctx.parseExpression();
|
|
4780
4936
|
if (value && commaList.has(key) && ctx.check(',')) {
|
|
4781
4937
|
const elements = [value];
|
|
4782
4938
|
while (ctx.match(',')) {
|
|
@@ -4941,8 +5097,8 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
4941
5097
|
}
|
|
4942
5098
|
if (ctx.check('with') && !modifiers['with']) {
|
|
4943
5099
|
ctx.advance();
|
|
4944
|
-
if (
|
|
4945
|
-
modifiers['with'] =
|
|
5100
|
+
if (isNakedNamedArgStart(ctx)) {
|
|
5101
|
+
modifiers['with'] = parseNakedNamedArgs(ctx, () => ctx.parsePrimary(), 'fetch');
|
|
4946
5102
|
}
|
|
4947
5103
|
else {
|
|
4948
5104
|
modifiers['with'] = ctx.parsePrimary();
|
|
@@ -4977,45 +5133,6 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
4977
5133
|
}
|
|
4978
5134
|
return builder.build();
|
|
4979
5135
|
}
|
|
4980
|
-
function isFetchNakedNamedArgStart(ctx) {
|
|
4981
|
-
if (ctx.check('{'))
|
|
4982
|
-
return false;
|
|
4983
|
-
if (!ctx.checkIdentifierLike())
|
|
4984
|
-
return false;
|
|
4985
|
-
const next = ctx.peekAt(1);
|
|
4986
|
-
return next !== null && next.value === ':';
|
|
4987
|
-
}
|
|
4988
|
-
function parseFetchNakedNamedArgs(ctx) {
|
|
4989
|
-
const properties = [];
|
|
4990
|
-
const startPos = ctx.getPosition();
|
|
4991
|
-
do {
|
|
4992
|
-
if (!ctx.checkIdentifierLike())
|
|
4993
|
-
break;
|
|
4994
|
-
const keyToken = ctx.advance();
|
|
4995
|
-
const key = {
|
|
4996
|
-
type: 'identifier',
|
|
4997
|
-
name: keyToken.value,
|
|
4998
|
-
start: keyToken.start,
|
|
4999
|
-
end: keyToken.end,
|
|
5000
|
-
line: keyToken.line,
|
|
5001
|
-
column: keyToken.column,
|
|
5002
|
-
};
|
|
5003
|
-
ctx.consume(':', "Expected ':' after property name in fetch named arguments");
|
|
5004
|
-
const value = ctx.parsePrimary();
|
|
5005
|
-
if (value) {
|
|
5006
|
-
properties.push({ key, value });
|
|
5007
|
-
}
|
|
5008
|
-
} while (ctx.match(',') && !ctx.isAtEnd());
|
|
5009
|
-
const endPos = ctx.getPosition();
|
|
5010
|
-
return {
|
|
5011
|
-
type: 'objectLiteral',
|
|
5012
|
-
properties,
|
|
5013
|
-
start: startPos.start,
|
|
5014
|
-
end: endPos.end,
|
|
5015
|
-
line: startPos.line,
|
|
5016
|
-
column: startPos.column,
|
|
5017
|
-
};
|
|
5018
|
-
}
|
|
5019
5136
|
function findJsEndBoundary(ctx, startPos) {
|
|
5020
5137
|
const input = ctx.getInputSlice(startPos);
|
|
5021
5138
|
if (!input) {
|
|
@@ -5135,6 +5252,7 @@ function parseTellCommand(ctx, identifierNode) {
|
|
|
5135
5252
|
const commands = [];
|
|
5136
5253
|
while (!ctx.isAtEnd()) {
|
|
5137
5254
|
if (ctx.checkIsCommand()) {
|
|
5255
|
+
const word = ctx.peek().value;
|
|
5138
5256
|
try {
|
|
5139
5257
|
ctx.advance();
|
|
5140
5258
|
const cmd = ctx.parseCommand();
|
|
@@ -5145,7 +5263,8 @@ function parseTellCommand(ctx, identifierNode) {
|
|
|
5145
5263
|
break;
|
|
5146
5264
|
}
|
|
5147
5265
|
}
|
|
5148
|
-
catch {
|
|
5266
|
+
catch (error) {
|
|
5267
|
+
ctx.addError(`'${word}' in a tell body failed to parse: ${error instanceof Error ? error.message : String(error)}`);
|
|
5149
5268
|
break;
|
|
5150
5269
|
}
|
|
5151
5270
|
if (ctx.match(KEYWORDS.AND) || ctx.match(KEYWORDS.THEN)) {
|
|
@@ -5294,7 +5413,6 @@ const ONE_EXPR = (syntax) => ({
|
|
|
5294
5413
|
markers: [],
|
|
5295
5414
|
syntax,
|
|
5296
5415
|
});
|
|
5297
|
-
const BEEP = ONE_EXPR('beep! [<expression>, \u2026]');
|
|
5298
5416
|
const COMMAND_GRAMMAR = {
|
|
5299
5417
|
append: { positional: 'expression', markers: ['to'], syntax: 'append <value> [to <target>]' },
|
|
5300
5418
|
prepend: { positional: 'expression', markers: ['to'], syntax: 'prepend <value> [to <target>]' },
|
|
@@ -5305,8 +5423,7 @@ const COMMAND_GRAMMAR = {
|
|
|
5305
5423
|
syntax: 'make (a|an) <type> [from <args>] [called <name>]',
|
|
5306
5424
|
},
|
|
5307
5425
|
throw: { positional: 'primary', markers: [], syntax: 'throw <error>' },
|
|
5308
|
-
'beep!'
|
|
5309
|
-
beep: BEEP,
|
|
5426
|
+
beep: ONE_EXPR('beep! [<expression>, \u2026]'),
|
|
5310
5427
|
blur: ONE_EXPR('blur [<target>]'),
|
|
5311
5428
|
break: NONE,
|
|
5312
5429
|
breakpoint: NONE,
|
|
@@ -5330,7 +5447,8 @@ const COMMAND_GRAMMAR = {
|
|
|
5330
5447
|
render: {
|
|
5331
5448
|
positional: 'expression',
|
|
5332
5449
|
markers: ['with'],
|
|
5333
|
-
|
|
5450
|
+
namedArgs: ['with'],
|
|
5451
|
+
syntax: 'render <template> [with <name>: <value>, \u2026]',
|
|
5334
5452
|
},
|
|
5335
5453
|
reset: ONE_EXPR('reset [<form>]'),
|
|
5336
5454
|
return: ONE_EXPR('return [<value>]'),
|
|
@@ -5685,7 +5803,10 @@ class Parser {
|
|
|
5685
5803
|
const firstToken = this.peek();
|
|
5686
5804
|
const prefixEntry = Parser.PRATT_TABLE.get(firstToken.value);
|
|
5687
5805
|
let left;
|
|
5688
|
-
if (
|
|
5806
|
+
if (isGluedBeepBang(firstToken, this.tokens[this.current + 1])) {
|
|
5807
|
+
left = this.parseBeepExpression();
|
|
5808
|
+
}
|
|
5809
|
+
else if (prefixEntry?.prefix) {
|
|
5689
5810
|
const token = this.advance();
|
|
5690
5811
|
if (this.isAtEnd()) {
|
|
5691
5812
|
this.addError(`Expected expression after '${token.value}' operator`);
|
|
@@ -5705,6 +5826,9 @@ class Parser {
|
|
|
5705
5826
|
continue;
|
|
5706
5827
|
}
|
|
5707
5828
|
}
|
|
5829
|
+
if (nextToken.value === '*' && this.isSpacedStyleRef(this.current)) {
|
|
5830
|
+
break;
|
|
5831
|
+
}
|
|
5708
5832
|
const infixEntry = Parser.PRATT_TABLE.get(nextToken.value);
|
|
5709
5833
|
if (!infixEntry?.infix) {
|
|
5710
5834
|
if (STOP_TOKENS.has(nextToken.value) || STOP_DELIMITERS.has(nextToken.value)) {
|
|
@@ -5766,6 +5890,35 @@ class Parser {
|
|
|
5766
5890
|
this.prattCache.set(cacheKey, { node: left, endPos: this.current });
|
|
5767
5891
|
return left;
|
|
5768
5892
|
}
|
|
5893
|
+
parseBeepExpression() {
|
|
5894
|
+
const beepToken = this.advance();
|
|
5895
|
+
this.advance();
|
|
5896
|
+
if (this.isAtEnd()) {
|
|
5897
|
+
this.addError("Expected expression after 'beep!'");
|
|
5898
|
+
return this.createErrorNode();
|
|
5899
|
+
}
|
|
5900
|
+
const operand = this.parseExpressionPratt(Parser.BEEP_OPERAND_BP);
|
|
5901
|
+
return {
|
|
5902
|
+
type: 'unaryExpression',
|
|
5903
|
+
operator: 'beep!',
|
|
5904
|
+
operand,
|
|
5905
|
+
argument: operand,
|
|
5906
|
+
prefix: true,
|
|
5907
|
+
start: beepToken.start,
|
|
5908
|
+
end: this.previous().end,
|
|
5909
|
+
line: beepToken.line,
|
|
5910
|
+
column: beepToken.column,
|
|
5911
|
+
};
|
|
5912
|
+
}
|
|
5913
|
+
isStyleRefStar(i) {
|
|
5914
|
+
const star = this.tokens[i];
|
|
5915
|
+
const name = this.tokens[i + 1];
|
|
5916
|
+
return star?.value === '*' && !!name && name.start === star.end && isIdentifierLike(name);
|
|
5917
|
+
}
|
|
5918
|
+
isSpacedStyleRef(i) {
|
|
5919
|
+
const prev = this.tokens[i - 1];
|
|
5920
|
+
return this.isStyleRefStar(i) && !!prev && prev.end < this.tokens[i].start;
|
|
5921
|
+
}
|
|
5769
5922
|
tryParseStringPostfix(left, nextToken) {
|
|
5770
5923
|
const v = nextToken.value;
|
|
5771
5924
|
let unit = null;
|
|
@@ -5968,6 +6121,10 @@ class Parser {
|
|
|
5968
6121
|
const isStop = () => this.check('end') || extraStops.some(s => this.check(s));
|
|
5969
6122
|
debug.parse('\ud83d\udd04 parseCommandListUntilTerminator: Starting (extraStops:', extraStops.join(','), ')');
|
|
5970
6123
|
while (!this.isAtEnd() && !isStop()) {
|
|
6124
|
+
if (this.checkComment()) {
|
|
6125
|
+
this.advance();
|
|
6126
|
+
continue;
|
|
6127
|
+
}
|
|
5971
6128
|
debug.parse('\ud83d\udccd Loop iteration, current token:', this.peek().value, 'kind:', this.peek().kind);
|
|
5972
6129
|
let parsedCommand = false;
|
|
5973
6130
|
const isCommandToken = this.checkIsCommand();
|
|
@@ -6051,7 +6208,7 @@ class Parser {
|
|
|
6051
6208
|
debug.parse('\u2705 Found "end", consuming it');
|
|
6052
6209
|
this.advance();
|
|
6053
6210
|
}
|
|
6054
|
-
else {
|
|
6211
|
+
else if (!this.atEndOfInput()) {
|
|
6055
6212
|
debug.parse('\u274c ERROR: Expected "end" but got:', this.peek().value, 'at position:', this.peek().start);
|
|
6056
6213
|
throw new Error(`Expected "end" to close ${construct} block`);
|
|
6057
6214
|
}
|
|
@@ -6064,7 +6221,7 @@ class Parser {
|
|
|
6064
6221
|
if (this.check('end')) {
|
|
6065
6222
|
this.advance();
|
|
6066
6223
|
}
|
|
6067
|
-
else {
|
|
6224
|
+
else if (!this.atEndOfInput()) {
|
|
6068
6225
|
throw new Error('Expected "end" to close repeat block');
|
|
6069
6226
|
}
|
|
6070
6227
|
}
|
|
@@ -6080,7 +6237,10 @@ class Parser {
|
|
|
6080
6237
|
this.advance();
|
|
6081
6238
|
}
|
|
6082
6239
|
else if (terminator === '') {
|
|
6083
|
-
|
|
6240
|
+
if (!this.atEndOfInput()) {
|
|
6241
|
+
throw new Error('Expected "end", "else", "until", or "while" to close repeat block');
|
|
6242
|
+
}
|
|
6243
|
+
return { commands, terminator: 'end' };
|
|
6084
6244
|
}
|
|
6085
6245
|
return { commands, terminator };
|
|
6086
6246
|
}
|
|
@@ -6664,7 +6824,9 @@ class Parser {
|
|
|
6664
6824
|
parseTopLevelInitBlock() {
|
|
6665
6825
|
const pos = this.getPosition();
|
|
6666
6826
|
const initCommands = this.parseCommandBlock(['end']);
|
|
6667
|
-
this.
|
|
6827
|
+
if (!this.match('end') && !this.atEndOfInput() && !this.checkFeatureStart()) {
|
|
6828
|
+
this.consume('end', "Expected 'end' after init block");
|
|
6829
|
+
}
|
|
6668
6830
|
return {
|
|
6669
6831
|
type: 'initBlock',
|
|
6670
6832
|
commands: initCommands,
|
|
@@ -6840,10 +7002,29 @@ class Parser {
|
|
|
6840
7002
|
...(sourceExpr.column !== undefined && { column: sourceExpr.column }),
|
|
6841
7003
|
};
|
|
6842
7004
|
}
|
|
7005
|
+
isArgumentListOnly(open) {
|
|
7006
|
+
let depth = 0;
|
|
7007
|
+
for (let i = open; i < this.tokens.length; i++) {
|
|
7008
|
+
const token = this.tokens[i];
|
|
7009
|
+
if (token.kind !== 'operator')
|
|
7010
|
+
continue;
|
|
7011
|
+
if (token.value === '(' || token.value === '[' || token.value === '{')
|
|
7012
|
+
depth++;
|
|
7013
|
+
else if (token.value === ')' || token.value === ']' || token.value === '}') {
|
|
7014
|
+
if (--depth === 0)
|
|
7015
|
+
return i === open + 1;
|
|
7016
|
+
}
|
|
7017
|
+
else if (token.value === ',' && depth === 1)
|
|
7018
|
+
return true;
|
|
7019
|
+
}
|
|
7020
|
+
return false;
|
|
7021
|
+
}
|
|
6843
7022
|
tryParsePseudoCommand(callExpr, sourceExpr) {
|
|
6844
7023
|
const methodName = callExpr.callee.name;
|
|
6845
7024
|
const nextToken = this.peek();
|
|
6846
|
-
const
|
|
7025
|
+
const nextIsArticle = nextToken.value.toLowerCase() === 'the';
|
|
7026
|
+
const hasPseudoCommandPattern = nextIsArticle ||
|
|
7027
|
+
Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(nextToken.value.toLowerCase()) ||
|
|
6847
7028
|
(isIdentifier(nextToken) && !this.isCommand(nextToken.value)) ||
|
|
6848
7029
|
isContextVar(nextToken);
|
|
6849
7030
|
if (!hasPseudoCommandPattern) {
|
|
@@ -6869,7 +7050,10 @@ class Parser {
|
|
|
6869
7050
|
});
|
|
6870
7051
|
}
|
|
6871
7052
|
let preposition;
|
|
6872
|
-
if (
|
|
7053
|
+
if (nextIsArticle) {
|
|
7054
|
+
this.advance();
|
|
7055
|
+
}
|
|
7056
|
+
else if (Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(this.peek().value.toLowerCase())) {
|
|
6873
7057
|
preposition = this.advance().value.toLowerCase();
|
|
6874
7058
|
}
|
|
6875
7059
|
let targetExpr;
|
|
@@ -7033,10 +7217,22 @@ class Parser {
|
|
|
7033
7217
|
this.consume(']', "Expected ']' after event condition");
|
|
7034
7218
|
}
|
|
7035
7219
|
let target;
|
|
7220
|
+
let targetExpression;
|
|
7036
7221
|
if (this.match('from')) {
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7222
|
+
if (this.check('the'))
|
|
7223
|
+
this.advance();
|
|
7224
|
+
if (Parser.POSITIONAL_SOURCE_HEADS.has(this.peek().value.toLowerCase())) {
|
|
7225
|
+
const startIndex = this.current;
|
|
7226
|
+
targetExpression = this.parseExpression();
|
|
7227
|
+
target = this.tokens
|
|
7228
|
+
.slice(startIndex, this.current)
|
|
7229
|
+
.map(t => t.value)
|
|
7230
|
+
.join(' ');
|
|
7231
|
+
}
|
|
7232
|
+
else {
|
|
7233
|
+
target = this.advance().value;
|
|
7234
|
+
}
|
|
7235
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed 'from' target: ${target}`);
|
|
7040
7236
|
this.parseTemporalKeywordModifiers(modifiers);
|
|
7041
7237
|
}
|
|
7042
7238
|
let attributeName;
|
|
@@ -7082,7 +7278,7 @@ class Parser {
|
|
|
7082
7278
|
const nextIsOpenParen = this.tokens[this.current + 1]?.value === '(';
|
|
7083
7279
|
const commandName = this.peek().value.toLowerCase();
|
|
7084
7280
|
const isSpecialBodyCommand = commandName === 'js' || commandName === 'tell';
|
|
7085
|
-
if (nextIsOpenParen && !isSpecialBodyCommand) {
|
|
7281
|
+
if (nextIsOpenParen && !isSpecialBodyCommand && this.isArgumentListOnly(this.current + 1)) {
|
|
7086
7282
|
let expr;
|
|
7087
7283
|
const savedError = this.error;
|
|
7088
7284
|
try {
|
|
@@ -7114,7 +7310,7 @@ class Parser {
|
|
|
7114
7310
|
}
|
|
7115
7311
|
}
|
|
7116
7312
|
}
|
|
7117
|
-
else if (this.checkIdentifier()) {
|
|
7313
|
+
else if (this.checkIdentifier() || this.checkEventNamedCall()) {
|
|
7118
7314
|
const token = this.peek();
|
|
7119
7315
|
if (this.isCommand(token.value)) {
|
|
7120
7316
|
const cmd = this.parseCommandWithErrorRecovery();
|
|
@@ -7223,6 +7419,7 @@ class Parser {
|
|
|
7223
7419
|
...(eventParams.length > 0 && { args: eventParams }),
|
|
7224
7420
|
...(condition && { condition: fromLegacyExpression(condition) }),
|
|
7225
7421
|
...(target && { target }),
|
|
7422
|
+
...(targetExpression && { targetExpression: fromLegacyExpression(targetExpression) }),
|
|
7226
7423
|
...(attributeName && { attributeName }),
|
|
7227
7424
|
...(watchTarget && { watchTarget: fromLegacyExpression(watchTarget) }),
|
|
7228
7425
|
...(customEventSource && { customEventSource }),
|
|
@@ -7478,10 +7675,9 @@ class Parser {
|
|
|
7478
7675
|
}
|
|
7479
7676
|
parseCommandCore() {
|
|
7480
7677
|
const commandToken = this.previous();
|
|
7481
|
-
|
|
7678
|
+
const commandName = commandToken.value;
|
|
7482
7679
|
if (commandName === 'beep' && this.check('!')) {
|
|
7483
7680
|
this.advance();
|
|
7484
|
-
commandName = 'beep!';
|
|
7485
7681
|
}
|
|
7486
7682
|
if (commandName === 'fetch') {
|
|
7487
7683
|
return parseFetchCommand(this.getContext(), commandToken);
|
|
@@ -7580,6 +7776,9 @@ class Parser {
|
|
|
7580
7776
|
args.push(this.parsePrimary());
|
|
7581
7777
|
}
|
|
7582
7778
|
const callNode = this.createCallExpression(createIdentifier(funcName, namePos), args);
|
|
7779
|
+
if (funcName === 'closest' && args.length > 0 && this.match('to')) {
|
|
7780
|
+
callNode.closestTo = this.parseExpression();
|
|
7781
|
+
}
|
|
7583
7782
|
if (funcName === 'next' || funcName === 'previous') {
|
|
7584
7783
|
const relativePositional = this.parseRelativePositionalModifiers();
|
|
7585
7784
|
if (relativePositional) {
|
|
@@ -7619,6 +7818,9 @@ class Parser {
|
|
|
7619
7818
|
}
|
|
7620
7819
|
parseTheXofY() {
|
|
7621
7820
|
const positionalKeywords = ['first', 'last', 'next', 'previous', 'random', 'closest'];
|
|
7821
|
+
if (this.isStyleRefStar(this.current)) {
|
|
7822
|
+
return this.parsePrimary();
|
|
7823
|
+
}
|
|
7622
7824
|
if (!this.checkIdentifier()) {
|
|
7623
7825
|
return this.createIdentifier('the');
|
|
7624
7826
|
}
|
|
@@ -7634,7 +7836,7 @@ class Parser {
|
|
|
7634
7836
|
return this.createIdentifier(propertyName);
|
|
7635
7837
|
}
|
|
7636
7838
|
this.advance();
|
|
7637
|
-
const target = this.
|
|
7839
|
+
const target = parseOfOperand(this.makePrattContext(), THE_OF_TARGET_BP);
|
|
7638
7840
|
return {
|
|
7639
7841
|
type: 'propertyOfExpression',
|
|
7640
7842
|
property: { type: 'identifier', name: propertyName },
|
|
@@ -7832,6 +8034,13 @@ class Parser {
|
|
|
7832
8034
|
checkLiteral() {
|
|
7833
8035
|
return this.checkPredicate(isLiteral);
|
|
7834
8036
|
}
|
|
8037
|
+
checkEventNamedCall() {
|
|
8038
|
+
if (this.isAtEnd() || !this.checkIdentifierLike() || this.checkIsCommand())
|
|
8039
|
+
return false;
|
|
8040
|
+
const token = this.peek();
|
|
8041
|
+
const next = this.tokens[this.current + 1];
|
|
8042
|
+
return next?.value === '(' && next.start === token.end;
|
|
8043
|
+
}
|
|
7835
8044
|
checkIdentifierLike() {
|
|
7836
8045
|
return this.checkPredicate(isIdentifierLike);
|
|
7837
8046
|
}
|
|
@@ -7940,6 +8149,22 @@ class Parser {
|
|
|
7940
8149
|
isAtEnd() {
|
|
7941
8150
|
return this.current >= this.tokens.length;
|
|
7942
8151
|
}
|
|
8152
|
+
atEndOfInput() {
|
|
8153
|
+
for (let i = this.current; i < this.tokens.length; i++) {
|
|
8154
|
+
if (!isComment(this.tokens[i]))
|
|
8155
|
+
return false;
|
|
8156
|
+
}
|
|
8157
|
+
return true;
|
|
8158
|
+
}
|
|
8159
|
+
checkFeatureStart() {
|
|
8160
|
+
if (this.isAtEnd())
|
|
8161
|
+
return false;
|
|
8162
|
+
const value = this.peek().value;
|
|
8163
|
+
return (value === 'init' ||
|
|
8164
|
+
value === 'on' ||
|
|
8165
|
+
value === 'def' ||
|
|
8166
|
+
getRegisteredFeature(value) !== undefined);
|
|
8167
|
+
}
|
|
7943
8168
|
peek() {
|
|
7944
8169
|
if (this.isAtEnd()) {
|
|
7945
8170
|
return { kind: 'unknown', value: '', start: 0, end: 0, line: 1, column: 1 };
|
|
@@ -8151,6 +8376,7 @@ class Parser {
|
|
|
8151
8376
|
match: this.match.bind(this),
|
|
8152
8377
|
matchOperator: this.matchOperator.bind(this),
|
|
8153
8378
|
isAtEnd: this.isAtEnd.bind(this),
|
|
8379
|
+
atEndOfInput: this.atEndOfInput.bind(this),
|
|
8154
8380
|
createIdentifier: this.createIdentifier.bind(this),
|
|
8155
8381
|
parseExpression: this.parseExpression.bind(this),
|
|
8156
8382
|
parsePrimary: this.parsePrimary.bind(this),
|
|
@@ -8201,7 +8427,15 @@ Parser.POSTFIX_UNARY_OPERATORS = new Set([
|
|
|
8201
8427
|
'ignoring case',
|
|
8202
8428
|
]);
|
|
8203
8429
|
Parser.PRATT_TABLE = PARSER_TABLE;
|
|
8430
|
+
Parser.BEEP_OPERAND_BP = 80;
|
|
8204
8431
|
Parser.PSEUDO_COMMAND_PREPOSITIONS = ['from', 'on', 'with', 'into', 'at', 'to'];
|
|
8432
|
+
Parser.POSITIONAL_SOURCE_HEADS = new Set([
|
|
8433
|
+
'closest',
|
|
8434
|
+
'next',
|
|
8435
|
+
'previous',
|
|
8436
|
+
'first',
|
|
8437
|
+
'last',
|
|
8438
|
+
]);
|
|
8205
8439
|
function parse(input, options) {
|
|
8206
8440
|
const tokens = tokenize(input);
|
|
8207
8441
|
const parser = new Parser(tokens, options, input);
|
|
@@ -8406,6 +8640,89 @@ function isDocumentFragment(value) {
|
|
|
8406
8640
|
'childNodes' in value);
|
|
8407
8641
|
}
|
|
8408
8642
|
|
|
8643
|
+
function beepValues(me, values) {
|
|
8644
|
+
const outputs = values.map(describeBeepValue);
|
|
8645
|
+
const shown = outputs.filter(output => announce(me, output.value));
|
|
8646
|
+
if (shown.length === 0)
|
|
8647
|
+
return outputs;
|
|
8648
|
+
console.group('\ud83d\udd14 beep! Debug Output');
|
|
8649
|
+
for (const output of shown) {
|
|
8650
|
+
console.log(`Value:`, output.value);
|
|
8651
|
+
console.log(`Type:`, output.type);
|
|
8652
|
+
console.log(`Representation:`, output.representation);
|
|
8653
|
+
console.log('---');
|
|
8654
|
+
}
|
|
8655
|
+
console.groupEnd();
|
|
8656
|
+
return outputs;
|
|
8657
|
+
}
|
|
8658
|
+
function announce(me, value) {
|
|
8659
|
+
const target = me;
|
|
8660
|
+
if (typeof CustomEvent !== 'function' || typeof target?.dispatchEvent !== 'function')
|
|
8661
|
+
return true;
|
|
8662
|
+
return target.dispatchEvent(new CustomEvent('hyperscript:beep', {
|
|
8663
|
+
bubbles: true,
|
|
8664
|
+
cancelable: true,
|
|
8665
|
+
detail: { element: me, value },
|
|
8666
|
+
}));
|
|
8667
|
+
}
|
|
8668
|
+
function describeBeepValue(value) {
|
|
8669
|
+
return { value, type: typeOf(value), representation: representationOf(value) };
|
|
8670
|
+
}
|
|
8671
|
+
function typeOf(value) {
|
|
8672
|
+
if (value === null)
|
|
8673
|
+
return 'null';
|
|
8674
|
+
if (value === undefined)
|
|
8675
|
+
return 'undefined';
|
|
8676
|
+
if (Array.isArray(value))
|
|
8677
|
+
return 'array';
|
|
8678
|
+
if (isHTMLElement$2(value))
|
|
8679
|
+
return 'HTMLElement';
|
|
8680
|
+
if (typeof Element !== 'undefined' && value instanceof Element)
|
|
8681
|
+
return 'Element';
|
|
8682
|
+
if (typeof Node !== 'undefined' && value instanceof Node)
|
|
8683
|
+
return 'Node';
|
|
8684
|
+
if (value instanceof Error)
|
|
8685
|
+
return 'Error';
|
|
8686
|
+
if (value instanceof Date)
|
|
8687
|
+
return 'Date';
|
|
8688
|
+
if (value instanceof RegExp)
|
|
8689
|
+
return 'RegExp';
|
|
8690
|
+
return typeof value;
|
|
8691
|
+
}
|
|
8692
|
+
function representationOf(value) {
|
|
8693
|
+
if (value === null)
|
|
8694
|
+
return 'null';
|
|
8695
|
+
if (value === undefined)
|
|
8696
|
+
return 'undefined';
|
|
8697
|
+
if (Array.isArray(value)) {
|
|
8698
|
+
return `Array(${value.length}) [${value
|
|
8699
|
+
.slice(0, 3)
|
|
8700
|
+
.map(v => representationOf(v))
|
|
8701
|
+
.join(', ')}${value.length > 3 ? '...' : ''}]`;
|
|
8702
|
+
}
|
|
8703
|
+
if (isHTMLElement$2(value)) {
|
|
8704
|
+
const el = value;
|
|
8705
|
+
const tag = el.tagName.toLowerCase();
|
|
8706
|
+
const id = el.id ? `#${el.id}` : '';
|
|
8707
|
+
const classes = el.className ? `.${el.className.split(' ').join('.')}` : '';
|
|
8708
|
+
return `<${tag}${id}${classes}/>`;
|
|
8709
|
+
}
|
|
8710
|
+
if (value instanceof Error)
|
|
8711
|
+
return `Error: ${value.message}`;
|
|
8712
|
+
if (typeof value === 'string')
|
|
8713
|
+
return value.length > 50 ? `"${value.substring(0, 47)}..."` : `"${value}"`;
|
|
8714
|
+
if (typeof value === 'object') {
|
|
8715
|
+
try {
|
|
8716
|
+
const keys = Object.keys(value);
|
|
8717
|
+
return `Object {${keys.slice(0, 3).join(', ')}${keys.length > 3 ? '...' : ''}}`;
|
|
8718
|
+
}
|
|
8719
|
+
catch {
|
|
8720
|
+
return '[Object]';
|
|
8721
|
+
}
|
|
8722
|
+
}
|
|
8723
|
+
return String(value);
|
|
8724
|
+
}
|
|
8725
|
+
|
|
8409
8726
|
function convertToNumber(value) {
|
|
8410
8727
|
if (value === null || value === undefined) {
|
|
8411
8728
|
return 0;
|
|
@@ -8437,6 +8754,16 @@ function convertToNumber(value) {
|
|
|
8437
8754
|
}
|
|
8438
8755
|
return 0;
|
|
8439
8756
|
}
|
|
8757
|
+
function counterAmount(value) {
|
|
8758
|
+
return typeof value === 'number' ? value : parseFloat(String(value));
|
|
8759
|
+
}
|
|
8760
|
+
function counterValue(value) {
|
|
8761
|
+
if (isHTMLElement$2(value)) {
|
|
8762
|
+
const element = value;
|
|
8763
|
+
return counterValue(element.value || element.textContent);
|
|
8764
|
+
}
|
|
8765
|
+
return value ? counterAmount(value) : 0;
|
|
8766
|
+
}
|
|
8440
8767
|
function getVariableValue(name, context, preferredScope) {
|
|
8441
8768
|
if (preferredScope === 'element') {
|
|
8442
8769
|
return getElementVar(context, name);
|
|
@@ -8541,35 +8868,39 @@ function setElementPropertyValue(propertyPath, value, context) {
|
|
|
8541
8868
|
element[property] = value;
|
|
8542
8869
|
}
|
|
8543
8870
|
}
|
|
8871
|
+
function isObjectOwner(value) {
|
|
8872
|
+
return (typeof value === 'object' && value !== null && !Array.isArray(value) && !isHTMLElement$2(value));
|
|
8873
|
+
}
|
|
8544
8874
|
function getCurrentNumericValue(target, property, scope, context) {
|
|
8545
8875
|
if (typeof target === 'number') {
|
|
8546
8876
|
return target;
|
|
8547
8877
|
}
|
|
8548
8878
|
if (isHTMLElement$2(target)) {
|
|
8549
8879
|
const element = target;
|
|
8880
|
+
if (property?.startsWith('@'))
|
|
8881
|
+
return counterValue(element.getAttribute(property.slice(1)));
|
|
8550
8882
|
if (property) {
|
|
8551
8883
|
if (property.startsWith('data-') ||
|
|
8552
8884
|
['id', 'class', 'title', 'alt', 'src', 'href'].includes(property)) {
|
|
8553
|
-
|
|
8554
|
-
return convertToNumber(value);
|
|
8885
|
+
return counterValue(element.getAttribute(property));
|
|
8555
8886
|
}
|
|
8556
8887
|
else {
|
|
8557
|
-
|
|
8558
|
-
return convertToNumber(value);
|
|
8888
|
+
return counterValue(element[property]);
|
|
8559
8889
|
}
|
|
8560
8890
|
}
|
|
8561
8891
|
else {
|
|
8562
|
-
|
|
8563
|
-
return convertToNumber(value);
|
|
8892
|
+
return counterValue(element);
|
|
8564
8893
|
}
|
|
8565
8894
|
}
|
|
8895
|
+
if (property && isObjectOwner(target)) {
|
|
8896
|
+
return counterValue(target[property]);
|
|
8897
|
+
}
|
|
8566
8898
|
if (typeof target === 'string') {
|
|
8567
8899
|
if (scope === 'element') {
|
|
8568
|
-
return
|
|
8900
|
+
return counterValue(getVariableValue(target, context, 'element'));
|
|
8569
8901
|
}
|
|
8570
8902
|
if (scope === 'global') {
|
|
8571
|
-
|
|
8572
|
-
return convertToNumber(value);
|
|
8903
|
+
return counterValue(getVariableValue(target, context, 'global'));
|
|
8573
8904
|
}
|
|
8574
8905
|
if (target.includes('.')) {
|
|
8575
8906
|
return getElementPropertyValue(target, context);
|
|
@@ -8583,14 +8914,17 @@ function getCurrentNumericValue(target, property, scope, context) {
|
|
|
8583
8914
|
else if (target === 'you' && context.you) {
|
|
8584
8915
|
return convertToNumber(context.you.value || 0);
|
|
8585
8916
|
}
|
|
8586
|
-
|
|
8587
|
-
return convertToNumber(value);
|
|
8917
|
+
return counterValue(getVariableValue(target, context));
|
|
8588
8918
|
}
|
|
8589
|
-
return
|
|
8919
|
+
return counterValue(target);
|
|
8590
8920
|
}
|
|
8591
8921
|
function setTargetValue(target, property, scope, newValue, context) {
|
|
8592
8922
|
if (isHTMLElement$2(target)) {
|
|
8593
8923
|
const element = target;
|
|
8924
|
+
if (property?.startsWith('@')) {
|
|
8925
|
+
element.setAttribute(property.slice(1), String(newValue));
|
|
8926
|
+
return;
|
|
8927
|
+
}
|
|
8594
8928
|
if (property) {
|
|
8595
8929
|
if (property.startsWith('data-') ||
|
|
8596
8930
|
['id', 'class', 'title', 'alt', 'src', 'href'].includes(property)) {
|
|
@@ -8610,6 +8944,10 @@ function setTargetValue(target, property, scope, newValue, context) {
|
|
|
8610
8944
|
}
|
|
8611
8945
|
return;
|
|
8612
8946
|
}
|
|
8947
|
+
if (property && isObjectOwner(target)) {
|
|
8948
|
+
target[property] = newValue;
|
|
8949
|
+
return;
|
|
8950
|
+
}
|
|
8613
8951
|
if (typeof target === 'string') {
|
|
8614
8952
|
if (scope === 'element') {
|
|
8615
8953
|
setVariableValue(target, newValue, context, 'element');
|
|
@@ -9038,13 +9376,6 @@ function isFunction(value) {
|
|
|
9038
9376
|
function isInputElement$1(el) {
|
|
9039
9377
|
return (el !== null && typeof el === 'object' && 'tagName' in el && el.tagName === 'INPUT');
|
|
9040
9378
|
}
|
|
9041
|
-
function isFormElement$1(el) {
|
|
9042
|
-
if (el === null || typeof el !== 'object' || !('tagName' in el)) {
|
|
9043
|
-
return false;
|
|
9044
|
-
}
|
|
9045
|
-
const tag = el.tagName;
|
|
9046
|
-
return tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA';
|
|
9047
|
-
}
|
|
9048
9379
|
function isHTMLElement$1(el) {
|
|
9049
9380
|
return (el !== null &&
|
|
9050
9381
|
typeof el === 'object' &&
|
|
@@ -9104,7 +9435,7 @@ const SPECIAL_DOM_PROPERTIES = {
|
|
|
9104
9435
|
innertext: el => el.textContent?.trim(),
|
|
9105
9436
|
innerHTML: el => el.innerHTML,
|
|
9106
9437
|
outerhtml: el => el.outerHTML,
|
|
9107
|
-
value: el => (
|
|
9438
|
+
value: el => ('value' in el ? el.value : undefined),
|
|
9108
9439
|
checked: el => (isInputElement$1(el) ? el.checked : undefined),
|
|
9109
9440
|
disabled: el => ('disabled' in el ? el.disabled : undefined),
|
|
9110
9441
|
selected: el => (isOptionElement$1(el) ? el.selected : undefined),
|
|
@@ -9772,14 +10103,15 @@ const closestExpression = {
|
|
|
9772
10103
|
if (typeof selector !== 'string') {
|
|
9773
10104
|
throw new Error('closest requires a string selector');
|
|
9774
10105
|
}
|
|
9775
|
-
|
|
10106
|
+
const start = args.length > 1 ? args[1] : context.me;
|
|
10107
|
+
if (!(start instanceof Element)) {
|
|
9776
10108
|
return null;
|
|
9777
10109
|
}
|
|
9778
|
-
return
|
|
10110
|
+
return start.closest(selector);
|
|
9779
10111
|
},
|
|
9780
10112
|
validate(args) {
|
|
9781
|
-
if (args.length !== 1) {
|
|
9782
|
-
return 'closest requires
|
|
10113
|
+
if (args.length !== 1 && args.length !== 2) {
|
|
10114
|
+
return 'closest requires a selector and, optionally, the element to start from';
|
|
9783
10115
|
}
|
|
9784
10116
|
const arg = args[0];
|
|
9785
10117
|
if (typeof arg === 'string')
|
|
@@ -11279,7 +11611,7 @@ const isEmptyExpression = {
|
|
|
11279
11611
|
else if (value instanceof Node || value instanceof Element)
|
|
11280
11612
|
result = false;
|
|
11281
11613
|
else if (isObject(value))
|
|
11282
|
-
result =
|
|
11614
|
+
result = value.length === 0;
|
|
11283
11615
|
else
|
|
11284
11616
|
result = false;
|
|
11285
11617
|
return result;
|
|
@@ -12403,11 +12735,16 @@ async function evaluateBinaryExpression(node, context) {
|
|
|
12403
12735
|
}
|
|
12404
12736
|
}
|
|
12405
12737
|
}
|
|
12406
|
-
if (leftNode?.type === 'selector' &&
|
|
12738
|
+
if (leftNode?.type === 'selector' &&
|
|
12739
|
+
typeof leftNode.value === 'string' &&
|
|
12740
|
+
(leftNode.fromQuery || (operator === 'in' && leftNode.value.startsWith('.')))) {
|
|
12407
12741
|
const root = await evaluateAST(node.right, context);
|
|
12408
12742
|
const scope = root && typeof root.querySelectorAll === 'function' ? root : null;
|
|
12409
12743
|
if (scope) {
|
|
12410
|
-
|
|
12744
|
+
const query = leftNode.fromQuery
|
|
12745
|
+
? leftNode.value
|
|
12746
|
+
: escapeSelectorForQuery(leftNode, leftNode.value);
|
|
12747
|
+
return Array.from(scope.querySelectorAll(query));
|
|
12411
12748
|
}
|
|
12412
12749
|
}
|
|
12413
12750
|
}
|
|
@@ -12419,6 +12756,12 @@ async function evaluateBinaryExpression(node, context) {
|
|
|
12419
12756
|
return getExpr(context, 'styleRef').evaluate(context, leftNode.value.slice(1), el);
|
|
12420
12757
|
}
|
|
12421
12758
|
if (operator === 'of') {
|
|
12759
|
+
if (leftNode?.type === 'attributeAccess') {
|
|
12760
|
+
const target = await evaluateAST(node.right, context);
|
|
12761
|
+
if (target == null)
|
|
12762
|
+
return undefined;
|
|
12763
|
+
return getExpr(context, 'possessive').evaluate(context, target, `@${leftNode.attributeName}`);
|
|
12764
|
+
}
|
|
12422
12765
|
const path = propertyPathOf(node.left);
|
|
12423
12766
|
if (path) {
|
|
12424
12767
|
const target = await evaluateAST(node.right, context);
|
|
@@ -12426,6 +12769,10 @@ async function evaluateBinaryExpression(node, context) {
|
|
|
12426
12769
|
for (const seg of path) {
|
|
12427
12770
|
if (cur == null)
|
|
12428
12771
|
return undefined;
|
|
12772
|
+
if (isMappableCollection(cur) && !(seg in cur)) {
|
|
12773
|
+
cur = await getExpr(context, 'possessive').evaluate(context, cur, seg);
|
|
12774
|
+
continue;
|
|
12775
|
+
}
|
|
12429
12776
|
cur = isElement(cur) ? getElementProperty$1(cur, seg) : cur[seg];
|
|
12430
12777
|
}
|
|
12431
12778
|
return typeof cur === 'function' ? cur.bind(target) : cur;
|
|
@@ -12450,26 +12797,21 @@ async function evaluateBinaryExpression(node, context) {
|
|
|
12450
12797
|
const R = node.ignoringCase ? applyCI(right) : right;
|
|
12451
12798
|
switch (operator) {
|
|
12452
12799
|
case '+': {
|
|
12453
|
-
let addLeft = left;
|
|
12454
|
-
let addRight = right;
|
|
12455
12800
|
if (node.coerceNumeric) {
|
|
12456
|
-
|
|
12457
|
-
addLeft = convertToNumber(addLeft);
|
|
12458
|
-
if (typeof addRight === 'string')
|
|
12459
|
-
addRight = convertToNumber(addRight);
|
|
12801
|
+
return counterValue(left) + counterAmount(right);
|
|
12460
12802
|
}
|
|
12461
|
-
if (typeof
|
|
12462
|
-
return String(
|
|
12803
|
+
if (typeof left === 'string' || typeof right === 'string') {
|
|
12804
|
+
return String(left ?? '') + String(right ?? '');
|
|
12463
12805
|
}
|
|
12464
|
-
if (Array.isArray(
|
|
12465
|
-
return Array.isArray(
|
|
12806
|
+
if (Array.isArray(left)) {
|
|
12807
|
+
return Array.isArray(right) ? [...left, ...right] : [...left, right];
|
|
12466
12808
|
}
|
|
12467
|
-
return unwrapTypedResult(await getExpr(context, 'addition').evaluate(context, {
|
|
12468
|
-
left: addLeft,
|
|
12469
|
-
right: addRight,
|
|
12470
|
-
}));
|
|
12809
|
+
return unwrapTypedResult(await getExpr(context, 'addition').evaluate(context, { left, right }));
|
|
12471
12810
|
}
|
|
12472
12811
|
case '-':
|
|
12812
|
+
if (node.coerceNumeric) {
|
|
12813
|
+
return counterValue(left) - counterAmount(right);
|
|
12814
|
+
}
|
|
12473
12815
|
return unwrapTypedResult(await getExpr(context, 'subtraction').evaluate(context, { left, right }));
|
|
12474
12816
|
case '*':
|
|
12475
12817
|
return unwrapTypedResult(await getExpr(context, 'multiplication').evaluate(context, { left, right }));
|
|
@@ -12727,9 +13069,8 @@ async function evaluatePropertyOfExpressionNode(node, context) {
|
|
|
12727
13069
|
}
|
|
12728
13070
|
const propertyName = propertyNode.name;
|
|
12729
13071
|
const target = await evaluateAST(node.target, context);
|
|
12730
|
-
if (target == null)
|
|
12731
|
-
|
|
12732
|
-
}
|
|
13072
|
+
if (target == null)
|
|
13073
|
+
return undefined;
|
|
12733
13074
|
return getExpr(context, 'possessive').evaluate(context, target, propertyName);
|
|
12734
13075
|
}
|
|
12735
13076
|
async function evaluateTemplateLiteralNode(node, context) {
|
|
@@ -12847,6 +13188,9 @@ async function evaluateUnaryExpression(node, context) {
|
|
|
12847
13188
|
case 'not':
|
|
12848
13189
|
case '!':
|
|
12849
13190
|
return logicalExpressions.not.evaluate(context, value);
|
|
13191
|
+
case 'beep!':
|
|
13192
|
+
beepValues(context.me, [value]);
|
|
13193
|
+
return value;
|
|
12850
13194
|
case 'no':
|
|
12851
13195
|
return logicalExpressions.no.evaluate(context, value);
|
|
12852
13196
|
case '-':
|
|
@@ -12936,6 +13280,10 @@ async function evaluateCallExpression(node, context) {
|
|
|
12936
13280
|
const args = await resolveCallArgs(node.arguments, funcName, context);
|
|
12937
13281
|
switch (funcName) {
|
|
12938
13282
|
case 'closest':
|
|
13283
|
+
if (node.closestTo) {
|
|
13284
|
+
const start = unwrapElement(await evaluateAST(node.closestTo, context)) ?? null;
|
|
13285
|
+
return referencesExpressions.closest.evaluate(context, args[0], start);
|
|
13286
|
+
}
|
|
12939
13287
|
return referencesExpressions.closest.evaluate(context, ...args);
|
|
12940
13288
|
case 'previous':
|
|
12941
13289
|
case 'next': {
|
|
@@ -14322,12 +14670,23 @@ class RuntimeBase {
|
|
|
14322
14670
|
debug.runtime(`BEHAVIOR: Finished installing ${behaviorName}`);
|
|
14323
14671
|
}
|
|
14324
14672
|
async executeEventHandler(node, context) {
|
|
14325
|
-
const { event, events, commands, target, args, selector, condition, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler, } = node;
|
|
14673
|
+
const { event, events, commands, target, targetExpression, args, selector, condition, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler, } = node;
|
|
14326
14674
|
const eventNames = events && events.length > 0 ? events : [event];
|
|
14327
14675
|
debug.runtime(`BEHAVIOR: executeEventHandler: event='${event}', target='${target}'`);
|
|
14328
14676
|
let targets = [];
|
|
14329
14677
|
let globalTarget = null;
|
|
14330
|
-
if (
|
|
14678
|
+
if (targetExpression) {
|
|
14679
|
+
const resolved = await this.execute(targetExpression, context);
|
|
14680
|
+
if (resolved === window)
|
|
14681
|
+
globalTarget = window;
|
|
14682
|
+
else if (resolved === document)
|
|
14683
|
+
globalTarget = document;
|
|
14684
|
+
else if (this.isElement(resolved))
|
|
14685
|
+
targets = [resolved];
|
|
14686
|
+
else if (Array.isArray(resolved))
|
|
14687
|
+
targets = resolved.filter(el => this.isElement(el));
|
|
14688
|
+
}
|
|
14689
|
+
else if (target) {
|
|
14331
14690
|
const targetLower = typeof target === 'string' ? target.toLowerCase() : '';
|
|
14332
14691
|
if (targetLower === 'window' || targetLower === 'the window') {
|
|
14333
14692
|
globalTarget = window;
|
|
@@ -16348,14 +16707,83 @@ function parseAttribute(expression) {
|
|
|
16348
16707
|
if (trimmed.startsWith('@')) {
|
|
16349
16708
|
return { name: trimmed.substring(1).trim() };
|
|
16350
16709
|
}
|
|
16351
|
-
throw new Error(`Invalid attribute syntax: ${expression}`);
|
|
16352
|
-
}
|
|
16353
|
-
function parseAttributeName(expression) {
|
|
16354
|
-
return parseAttribute(expression).name;
|
|
16710
|
+
throw new Error(`Invalid attribute syntax: ${expression}`);
|
|
16711
|
+
}
|
|
16712
|
+
function parseAttributeName(expression) {
|
|
16713
|
+
return parseAttribute(expression).name;
|
|
16714
|
+
}
|
|
16715
|
+
function parseAttributeWithValue(expression) {
|
|
16716
|
+
const result = parseAttribute(expression);
|
|
16717
|
+
return { name: result.name, value: result.value ?? '' };
|
|
16718
|
+
}
|
|
16719
|
+
|
|
16720
|
+
function _parseDurationComponents(value) {
|
|
16721
|
+
const trimmed = value.trim();
|
|
16722
|
+
const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*(s|ms|sec|seconds?|milliseconds?)?$/i);
|
|
16723
|
+
if (!match) {
|
|
16724
|
+
return null;
|
|
16725
|
+
}
|
|
16726
|
+
const number = parseFloat(match[1]);
|
|
16727
|
+
const unit = (match[2] || 'ms').toLowerCase();
|
|
16728
|
+
if (unit === 'ms' || unit === 'millisecond' || unit === 'milliseconds') {
|
|
16729
|
+
return { number, milliseconds: Math.floor(number) };
|
|
16730
|
+
}
|
|
16731
|
+
else if (unit === 's' || unit === 'sec' || unit === 'second' || unit === 'seconds') {
|
|
16732
|
+
return { number, milliseconds: Math.floor(number * 1000) };
|
|
16733
|
+
}
|
|
16734
|
+
return null;
|
|
16735
|
+
}
|
|
16736
|
+
function parseDuration(value, defaultMs = 300) {
|
|
16737
|
+
if (typeof value === 'number') {
|
|
16738
|
+
return Math.max(0, Math.floor(value));
|
|
16739
|
+
}
|
|
16740
|
+
if (typeof value === 'string') {
|
|
16741
|
+
const result = _parseDurationComponents(value);
|
|
16742
|
+
return result ? result.milliseconds : defaultMs;
|
|
16743
|
+
}
|
|
16744
|
+
return defaultMs;
|
|
16745
|
+
}
|
|
16746
|
+
function parseDurationStrict(value) {
|
|
16747
|
+
if (typeof value === 'number') {
|
|
16748
|
+
if (value < 0)
|
|
16749
|
+
throw new Error('Duration must be >= 0');
|
|
16750
|
+
return Math.floor(value);
|
|
16751
|
+
}
|
|
16752
|
+
if (typeof value === 'string') {
|
|
16753
|
+
const result = _parseDurationComponents(value);
|
|
16754
|
+
if (!result) {
|
|
16755
|
+
throw new Error(`Invalid duration format: "${value}"`);
|
|
16756
|
+
}
|
|
16757
|
+
return result.milliseconds;
|
|
16758
|
+
}
|
|
16759
|
+
throw new Error(`Invalid duration type: ${typeof value}`);
|
|
16760
|
+
}
|
|
16761
|
+
function parseCSSDurations(durationString) {
|
|
16762
|
+
if (!durationString || durationString === 'none') {
|
|
16763
|
+
return [0];
|
|
16764
|
+
}
|
|
16765
|
+
return durationString.split(',').map(duration => {
|
|
16766
|
+
const value = parseFloat(duration.trim());
|
|
16767
|
+
if (isNaN(value))
|
|
16768
|
+
return 0;
|
|
16769
|
+
if (duration.includes('s') && !duration.includes('ms')) {
|
|
16770
|
+
return value * 1000;
|
|
16771
|
+
}
|
|
16772
|
+
return value;
|
|
16773
|
+
});
|
|
16774
|
+
}
|
|
16775
|
+
function calculateMaxAnimationTime(durations, delays) {
|
|
16776
|
+
let maxTime = 0;
|
|
16777
|
+
for (let i = 0; i < durations.length; i++) {
|
|
16778
|
+
const duration = durations[i] || 0;
|
|
16779
|
+
const delay = delays[i] || 0;
|
|
16780
|
+
const totalTime = duration + delay;
|
|
16781
|
+
maxTime = Math.max(maxTime, totalTime);
|
|
16782
|
+
}
|
|
16783
|
+
return maxTime;
|
|
16355
16784
|
}
|
|
16356
|
-
function
|
|
16357
|
-
|
|
16358
|
-
return { name: result.name, value: result.value ?? '' };
|
|
16785
|
+
function camelToKebab(str) {
|
|
16786
|
+
return str.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
16359
16787
|
}
|
|
16360
16788
|
|
|
16361
16789
|
function batchApply(targets, operation) {
|
|
@@ -16423,15 +16851,18 @@ function toggleAttribute(element, name, value) {
|
|
|
16423
16851
|
}
|
|
16424
16852
|
}
|
|
16425
16853
|
}
|
|
16854
|
+
function toCssPropertyName(property) {
|
|
16855
|
+
return property.startsWith('--') ? property : camelToKebab(property);
|
|
16856
|
+
}
|
|
16426
16857
|
function batchSetStyles(targets, styles) {
|
|
16427
16858
|
const entries = Object.entries(styles);
|
|
16428
16859
|
return batchApplyItems(targets, entries, (element, [property, value]) => {
|
|
16429
|
-
element.style.setProperty(property, value);
|
|
16860
|
+
element.style.setProperty(toCssPropertyName(property), value);
|
|
16430
16861
|
});
|
|
16431
16862
|
}
|
|
16432
16863
|
function batchRemoveStyles(targets, properties) {
|
|
16433
16864
|
return batchApplyItems(targets, properties, (element, property) => {
|
|
16434
|
-
element.style.removeProperty(property);
|
|
16865
|
+
element.style.removeProperty(toCssPropertyName(property));
|
|
16435
16866
|
});
|
|
16436
16867
|
}
|
|
16437
16868
|
|
|
@@ -17097,75 +17528,6 @@ let RemoveCommand = (() => {
|
|
|
17097
17528
|
})();
|
|
17098
17529
|
const createRemoveCommand = createFactory(RemoveCommand);
|
|
17099
17530
|
|
|
17100
|
-
function _parseDurationComponents(value) {
|
|
17101
|
-
const trimmed = value.trim();
|
|
17102
|
-
const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*(s|ms|sec|seconds?|milliseconds?)?$/i);
|
|
17103
|
-
if (!match) {
|
|
17104
|
-
return null;
|
|
17105
|
-
}
|
|
17106
|
-
const number = parseFloat(match[1]);
|
|
17107
|
-
const unit = (match[2] || 'ms').toLowerCase();
|
|
17108
|
-
if (unit === 'ms' || unit === 'millisecond' || unit === 'milliseconds') {
|
|
17109
|
-
return { number, milliseconds: Math.floor(number) };
|
|
17110
|
-
}
|
|
17111
|
-
else if (unit === 's' || unit === 'sec' || unit === 'second' || unit === 'seconds') {
|
|
17112
|
-
return { number, milliseconds: Math.floor(number * 1000) };
|
|
17113
|
-
}
|
|
17114
|
-
return null;
|
|
17115
|
-
}
|
|
17116
|
-
function parseDuration(value, defaultMs = 300) {
|
|
17117
|
-
if (typeof value === 'number') {
|
|
17118
|
-
return Math.max(0, Math.floor(value));
|
|
17119
|
-
}
|
|
17120
|
-
if (typeof value === 'string') {
|
|
17121
|
-
const result = _parseDurationComponents(value);
|
|
17122
|
-
return result ? result.milliseconds : defaultMs;
|
|
17123
|
-
}
|
|
17124
|
-
return defaultMs;
|
|
17125
|
-
}
|
|
17126
|
-
function parseDurationStrict(value) {
|
|
17127
|
-
if (typeof value === 'number') {
|
|
17128
|
-
if (value < 0)
|
|
17129
|
-
throw new Error('Duration must be >= 0');
|
|
17130
|
-
return Math.floor(value);
|
|
17131
|
-
}
|
|
17132
|
-
if (typeof value === 'string') {
|
|
17133
|
-
const result = _parseDurationComponents(value);
|
|
17134
|
-
if (!result) {
|
|
17135
|
-
throw new Error(`Invalid duration format: "${value}"`);
|
|
17136
|
-
}
|
|
17137
|
-
return result.milliseconds;
|
|
17138
|
-
}
|
|
17139
|
-
throw new Error(`Invalid duration type: ${typeof value}`);
|
|
17140
|
-
}
|
|
17141
|
-
function parseCSSDurations(durationString) {
|
|
17142
|
-
if (!durationString || durationString === 'none') {
|
|
17143
|
-
return [0];
|
|
17144
|
-
}
|
|
17145
|
-
return durationString.split(',').map(duration => {
|
|
17146
|
-
const value = parseFloat(duration.trim());
|
|
17147
|
-
if (isNaN(value))
|
|
17148
|
-
return 0;
|
|
17149
|
-
if (duration.includes('s') && !duration.includes('ms')) {
|
|
17150
|
-
return value * 1000;
|
|
17151
|
-
}
|
|
17152
|
-
return value;
|
|
17153
|
-
});
|
|
17154
|
-
}
|
|
17155
|
-
function calculateMaxAnimationTime(durations, delays) {
|
|
17156
|
-
let maxTime = 0;
|
|
17157
|
-
for (let i = 0; i < durations.length; i++) {
|
|
17158
|
-
const duration = durations[i] || 0;
|
|
17159
|
-
const delay = delays[i] || 0;
|
|
17160
|
-
const totalTime = duration + delay;
|
|
17161
|
-
maxTime = Math.max(maxTime, totalTime);
|
|
17162
|
-
}
|
|
17163
|
-
return maxTime;
|
|
17164
|
-
}
|
|
17165
|
-
function camelToKebab(str) {
|
|
17166
|
-
return str.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
17167
|
-
}
|
|
17168
|
-
|
|
17169
17531
|
function detectSmartElementType(elements) {
|
|
17170
17532
|
if (elements.length === 0)
|
|
17171
17533
|
return null;
|
|
@@ -18035,7 +18397,11 @@ let PutCommand = (() => {
|
|
|
18035
18397
|
}
|
|
18036
18398
|
if (isInsertableNode(v))
|
|
18037
18399
|
return v;
|
|
18038
|
-
|
|
18400
|
+
if (v == null)
|
|
18401
|
+
return 'null';
|
|
18402
|
+
if (Array.isArray(v))
|
|
18403
|
+
return arrayFragment(v);
|
|
18404
|
+
return String(v);
|
|
18039
18405
|
}
|
|
18040
18406
|
looksLikeCss(s) {
|
|
18041
18407
|
if (!s)
|
|
@@ -18120,6 +18486,19 @@ let PutCommand = (() => {
|
|
|
18120
18486
|
})();
|
|
18121
18487
|
return PutCommand = _classThis;
|
|
18122
18488
|
})();
|
|
18489
|
+
function arrayFragment(items) {
|
|
18490
|
+
const fragment = document.createDocumentFragment();
|
|
18491
|
+
for (const item of items) {
|
|
18492
|
+
if (isInsertableNode(item)) {
|
|
18493
|
+
fragment.append(item);
|
|
18494
|
+
continue;
|
|
18495
|
+
}
|
|
18496
|
+
const template = document.createElement('template');
|
|
18497
|
+
template.innerHTML = item === null ? '' : String(item);
|
|
18498
|
+
fragment.append(template.content);
|
|
18499
|
+
}
|
|
18500
|
+
return fragment;
|
|
18501
|
+
}
|
|
18123
18502
|
const createPutCommand = createFactory(PutCommand);
|
|
18124
18503
|
|
|
18125
18504
|
const HTML_ESCAPE = {
|
|
@@ -19337,10 +19716,29 @@ const STRATEGY_KEYWORDS = {
|
|
|
19337
19716
|
innermorph: 'morph',
|
|
19338
19717
|
outermorph: 'morphOuter',
|
|
19339
19718
|
};
|
|
19719
|
+
function loneRootWithTag(content, tagName) {
|
|
19720
|
+
let root;
|
|
19721
|
+
if (typeof content === 'string') {
|
|
19722
|
+
const template = document.createElement('template');
|
|
19723
|
+
template.innerHTML = content;
|
|
19724
|
+
root = template.content.firstElementChild;
|
|
19725
|
+
if (!root || root.nextElementSibling)
|
|
19726
|
+
return null;
|
|
19727
|
+
}
|
|
19728
|
+
else {
|
|
19729
|
+
root = content.cloneNode(true);
|
|
19730
|
+
}
|
|
19731
|
+
return root.tagName === tagName && isHTMLElement$2(root) ? root : null;
|
|
19732
|
+
}
|
|
19340
19733
|
function executeSwap(target, content, strategy, morphOptions) {
|
|
19341
19734
|
const contentStr = content !== null && !isHTMLElement$2(content) ? content : '';
|
|
19342
19735
|
const contentEl = isHTMLElement$2(content) ? content : null;
|
|
19343
19736
|
switch (strategy) {
|
|
19737
|
+
case 'morphMerge': {
|
|
19738
|
+
const root = content !== null ? loneRootWithTag(content, target.tagName) : null;
|
|
19739
|
+
executeSwap(target, root ?? content, root ? 'morphOuter' : 'morph', morphOptions);
|
|
19740
|
+
break;
|
|
19741
|
+
}
|
|
19344
19742
|
case 'morph':
|
|
19345
19743
|
if (content !== null) {
|
|
19346
19744
|
try {
|
|
@@ -19630,7 +20028,7 @@ let MorphCommand = (() => {
|
|
|
19630
20028
|
if (args.length === 0 && modifiers.with === undefined) {
|
|
19631
20029
|
throw new Error('[HyperFixi] morph: command requires arguments');
|
|
19632
20030
|
}
|
|
19633
|
-
const strategy = literalText(modifiers.strategy)?.toLowerCase() === 'over' ? 'morphOuter' : '
|
|
20031
|
+
const strategy = literalText(modifiers.strategy)?.toLowerCase() === 'over' ? 'morphOuter' : 'morphMerge';
|
|
19634
20032
|
const useViewTransition = modifiers.viewTransition !== undefined;
|
|
19635
20033
|
const targetNode = modifiers.on !== undefined ? modifiers.on : args[0];
|
|
19636
20034
|
const contentNode = modifiers.with !== undefined
|
|
@@ -20936,6 +21334,19 @@ async function resolveWriteTarget(node, evaluator, context, options) {
|
|
|
20936
21334
|
}
|
|
20937
21335
|
return { kind: 'property', target: property };
|
|
20938
21336
|
}
|
|
21337
|
+
if (options.ofAndObjectProperty) {
|
|
21338
|
+
const access = ofOrPossessiveAccess(n);
|
|
21339
|
+
if (access) {
|
|
21340
|
+
const owner = await evaluator.evaluate(access.owner, context);
|
|
21341
|
+
const element = Array.isArray(owner) ? owner[0] : owner;
|
|
21342
|
+
if (access.isOf && isHTMLElement$2(element)) {
|
|
21343
|
+
return { kind: 'property', target: { element, property: access.name } };
|
|
21344
|
+
}
|
|
21345
|
+
if (isObjectOwner(owner)) {
|
|
21346
|
+
return { kind: 'member', container: owner, property: access.name };
|
|
21347
|
+
}
|
|
21348
|
+
}
|
|
21349
|
+
}
|
|
20939
21350
|
if (options.bareReference &&
|
|
20940
21351
|
(nodeType === 'identifier' || nodeType === 'variable' || nodeType === 'symbol') &&
|
|
20941
21352
|
typeof n.name === 'string') {
|
|
@@ -20945,6 +21356,19 @@ async function resolveWriteTarget(node, evaluator, context, options) {
|
|
|
20945
21356
|
}
|
|
20946
21357
|
return null;
|
|
20947
21358
|
}
|
|
21359
|
+
function ofOrPossessiveAccess(n) {
|
|
21360
|
+
const [owner, property, isOf] = n.type === 'possessiveExpression'
|
|
21361
|
+
? [n.object, n.property, false]
|
|
21362
|
+
: n.type === 'propertyOfExpression'
|
|
21363
|
+
? [n.target, n.property, false]
|
|
21364
|
+
: n.type === 'binaryExpression' && n.operator === 'of'
|
|
21365
|
+
? [n.right, n.left, true]
|
|
21366
|
+
: [undefined, undefined, false];
|
|
21367
|
+
const name = property?.name;
|
|
21368
|
+
if (!owner || typeof name !== 'string' || !/^[A-Za-z_$][\w$]*$/.test(name))
|
|
21369
|
+
return null;
|
|
21370
|
+
return { owner, name, isOf };
|
|
21371
|
+
}
|
|
20948
21372
|
|
|
20949
21373
|
function toSetInput(target, value) {
|
|
20950
21374
|
switch (target.kind) {
|
|
@@ -20966,10 +21390,18 @@ function toSetInput(target, value) {
|
|
|
20966
21390
|
property: target.target.property,
|
|
20967
21391
|
value,
|
|
20968
21392
|
};
|
|
21393
|
+
case 'member':
|
|
21394
|
+
return {
|
|
21395
|
+
type: 'member-assignment',
|
|
21396
|
+
container: target.container,
|
|
21397
|
+
property: target.property,
|
|
21398
|
+
value,
|
|
21399
|
+
};
|
|
20969
21400
|
default:
|
|
20970
21401
|
throw new Error(`set: unrequested write-target rung '${target.kind}'`);
|
|
20971
21402
|
}
|
|
20972
21403
|
}
|
|
21404
|
+
const SET_RUNGS = { nodeWriters: true, styleSplit: true, ofAndObjectProperty: true };
|
|
20973
21405
|
let SetCommand = (() => {
|
|
20974
21406
|
let _classDecorators = [command({ name: 'set' })];
|
|
20975
21407
|
let _classDescriptor;
|
|
@@ -20988,8 +21420,7 @@ let SetCommand = (() => {
|
|
|
20988
21420
|
const argScope = firstArg?.scope;
|
|
20989
21421
|
const writeTarget = await resolveWriteTarget(firstArg, evaluator, context, {
|
|
20990
21422
|
scopeElements: () => this.resolveTargets(raw.modifiers.on, evaluator, context),
|
|
20991
|
-
|
|
20992
|
-
styleSplit: true,
|
|
21423
|
+
...SET_RUNGS,
|
|
20993
21424
|
});
|
|
20994
21425
|
if (writeTarget) {
|
|
20995
21426
|
return toSetInput(writeTarget, await this.extractValue(raw, evaluator, context));
|
|
@@ -21315,12 +21746,30 @@ function getNodeType(node) {
|
|
|
21315
21746
|
return 'unknown';
|
|
21316
21747
|
return node.type || 'unknown';
|
|
21317
21748
|
}
|
|
21749
|
+
function propertyTarget(node) {
|
|
21750
|
+
const n = node;
|
|
21751
|
+
const owner = n.type === 'propertyOfExpression'
|
|
21752
|
+
? n.target
|
|
21753
|
+
: n.type === 'possessiveExpression' ||
|
|
21754
|
+
n.type === 'memberExpression' ||
|
|
21755
|
+
n.type === 'propertyAccess'
|
|
21756
|
+
? n.object
|
|
21757
|
+
: undefined;
|
|
21758
|
+
if (!owner || n.computed)
|
|
21759
|
+
return null;
|
|
21760
|
+
const p = n.property;
|
|
21761
|
+
const name = typeof p === 'string' ? p : typeof p?.name === 'string' ? p.name : undefined;
|
|
21762
|
+
if (!name || !/^[A-Za-z_$][\w$-]*$/.test(name))
|
|
21763
|
+
return null;
|
|
21764
|
+
return { owner, name };
|
|
21765
|
+
}
|
|
21318
21766
|
async function parseNumericTargetInput(raw, evaluator, context, commandName) {
|
|
21319
21767
|
if (!raw.args || raw.args.length === 0) {
|
|
21320
21768
|
throw new Error(`${commandName} command requires a target`);
|
|
21321
21769
|
}
|
|
21322
21770
|
const targetArg = raw.args[0];
|
|
21323
21771
|
let target;
|
|
21772
|
+
let property;
|
|
21324
21773
|
let extractedScope;
|
|
21325
21774
|
let customWrite;
|
|
21326
21775
|
const nodeType = getNodeType(targetArg);
|
|
@@ -21335,7 +21784,25 @@ async function parseNumericTargetInput(raw, evaluator, context, commandName) {
|
|
|
21335
21784
|
}
|
|
21336
21785
|
else {
|
|
21337
21786
|
const writer = getRegisteredNodeWriter(nodeType);
|
|
21338
|
-
|
|
21787
|
+
const attribute = writer
|
|
21788
|
+
? null
|
|
21789
|
+
: await resolveAttributeWriteTarget(targetArg, evaluator, context, async () => context.me ? [context.me] : []);
|
|
21790
|
+
const prop = writer || attribute ? null : propertyTarget(targetArg);
|
|
21791
|
+
const owner = prop ? await evaluator.evaluate(prop.owner, context) : undefined;
|
|
21792
|
+
const ownerElement = Array.isArray(owner) ? owner[0] : owner;
|
|
21793
|
+
if (attribute?.elements[0]) {
|
|
21794
|
+
target = attribute.elements[0];
|
|
21795
|
+
property = `@${attribute.name}`;
|
|
21796
|
+
}
|
|
21797
|
+
else if (prop && isHTMLElement$2(ownerElement)) {
|
|
21798
|
+
target = ownerElement;
|
|
21799
|
+
property = prop.name;
|
|
21800
|
+
}
|
|
21801
|
+
else if (prop && isObjectOwner(owner)) {
|
|
21802
|
+
target = owner;
|
|
21803
|
+
property = prop.name;
|
|
21804
|
+
}
|
|
21805
|
+
else if (writer) {
|
|
21339
21806
|
const currentRaw = await evaluator.evaluate(targetArg, context);
|
|
21340
21807
|
const currentValue = typeof currentRaw === 'number'
|
|
21341
21808
|
? currentRaw
|
|
@@ -21373,24 +21840,22 @@ async function parseNumericTargetInput(raw, evaluator, context, commandName) {
|
|
|
21373
21840
|
if (typeof evaluated === 'number') {
|
|
21374
21841
|
amount = evaluated;
|
|
21375
21842
|
}
|
|
21843
|
+
else if (evaluated == null) {
|
|
21844
|
+
amount = NaN;
|
|
21845
|
+
}
|
|
21376
21846
|
}
|
|
21377
21847
|
}
|
|
21378
21848
|
const byModifier = raw.modifiers?.by;
|
|
21379
21849
|
if (byModifier) {
|
|
21380
|
-
const
|
|
21381
|
-
|
|
21382
|
-
|
|
21383
|
-
|
|
21384
|
-
else if (getNodeType(byModifier) !== 'literal') {
|
|
21385
|
-
const evaluated = await evaluator.evaluate(byModifier, context);
|
|
21386
|
-
if (typeof evaluated === 'number') {
|
|
21387
|
-
amount = evaluated;
|
|
21388
|
-
}
|
|
21389
|
-
}
|
|
21850
|
+
const value = getNodeType(byModifier) === 'literal'
|
|
21851
|
+
? byModifier.value
|
|
21852
|
+
: await evaluator.evaluate(byModifier, context);
|
|
21853
|
+
amount = counterAmount(value);
|
|
21390
21854
|
}
|
|
21391
21855
|
return {
|
|
21392
21856
|
target,
|
|
21393
21857
|
amount,
|
|
21858
|
+
...(property && { property }),
|
|
21394
21859
|
...(scope && { scope }),
|
|
21395
21860
|
...(customWrite && { customWrite }),
|
|
21396
21861
|
};
|
|
@@ -21413,7 +21878,7 @@ let NumericModifyCommand = (() => {
|
|
|
21413
21878
|
async execute(input, context) {
|
|
21414
21879
|
const { target, property, scope, amount = 1, operation = 'increment', customWrite } = input;
|
|
21415
21880
|
if (customWrite) {
|
|
21416
|
-
const delta =
|
|
21881
|
+
const delta = amount;
|
|
21417
21882
|
const newValue = operation === 'increment'
|
|
21418
21883
|
? customWrite.currentValue + delta
|
|
21419
21884
|
: customWrite.currentValue - delta;
|
|
@@ -21427,8 +21892,7 @@ let NumericModifyCommand = (() => {
|
|
|
21427
21892
|
newValue = NaN;
|
|
21428
21893
|
}
|
|
21429
21894
|
else {
|
|
21430
|
-
|
|
21431
|
-
newValue = operation === 'increment' ? currentValue + delta : currentValue - delta;
|
|
21895
|
+
newValue = operation === 'increment' ? currentValue + amount : currentValue - amount;
|
|
21432
21896
|
}
|
|
21433
21897
|
setTargetValue(target, property, scope, newValue, context);
|
|
21434
21898
|
Object.assign(context, { it: newValue });
|
|
@@ -22536,7 +23000,10 @@ function createUntilLoopConfig(conditionExpr, evaluateCondition, context, indexV
|
|
|
22536
23000
|
};
|
|
22537
23001
|
const config = {
|
|
22538
23002
|
type: 'until',
|
|
22539
|
-
shouldContinue: () =>
|
|
23003
|
+
shouldContinue: () => {
|
|
23004
|
+
const met = evaluateCondition(conditionExpr, context);
|
|
23005
|
+
return met instanceof Promise ? met.then(value => !value) : !met;
|
|
23006
|
+
},
|
|
22540
23007
|
};
|
|
22541
23008
|
return { config, iterCtx };
|
|
22542
23009
|
}
|
|
@@ -22572,7 +23039,7 @@ function createForeverLoopConfig(indexVariable, maxIterations = 10000) {
|
|
|
22572
23039
|
return { config, iterCtx };
|
|
22573
23040
|
}
|
|
22574
23041
|
|
|
22575
|
-
async function slotText(node, evaluator, context) {
|
|
23042
|
+
async function slotText$1(node, evaluator, context) {
|
|
22576
23043
|
if (!node)
|
|
22577
23044
|
return undefined;
|
|
22578
23045
|
const n = node;
|
|
@@ -22585,6 +23052,18 @@ async function slotText(node, evaluator, context) {
|
|
|
22585
23052
|
const v = await evaluator.evaluate(node, context);
|
|
22586
23053
|
return typeof v === 'string' ? v : undefined;
|
|
22587
23054
|
}
|
|
23055
|
+
function isEventTarget(value) {
|
|
23056
|
+
return typeof value?.addEventListener === 'function';
|
|
23057
|
+
}
|
|
23058
|
+
async function evaluateLoopCondition(condition, context) {
|
|
23059
|
+
if (typeof condition === 'function')
|
|
23060
|
+
return Boolean(await condition());
|
|
23061
|
+
return evaluateCondition(condition, context);
|
|
23062
|
+
}
|
|
23063
|
+
function loopCount(value) {
|
|
23064
|
+
const count = Number(value);
|
|
23065
|
+
return Number.isNaN(count) ? 0 : count;
|
|
23066
|
+
}
|
|
22588
23067
|
let RepeatCommand = (() => {
|
|
22589
23068
|
let _classDecorators = [command({ name: 'repeat' })];
|
|
22590
23069
|
let _classDescriptor;
|
|
@@ -22622,7 +23101,7 @@ let RepeatCommand = (() => {
|
|
|
22622
23101
|
}
|
|
22623
23102
|
}
|
|
22624
23103
|
const m = raw.modifiers ?? {};
|
|
22625
|
-
const text = (node) => slotText(node, evaluator, context);
|
|
23104
|
+
const text = (node) => slotText$1(node, evaluator, context);
|
|
22626
23105
|
const loopType = (await text(m.loopType)) ?? null;
|
|
22627
23106
|
const bottomTested = m.bottomTested
|
|
22628
23107
|
? (await evaluator.evaluate(m.bottomTested, context))
|
|
@@ -22642,17 +23121,24 @@ let RepeatCommand = (() => {
|
|
|
22642
23121
|
};
|
|
22643
23122
|
}
|
|
22644
23123
|
if (loopType === 'times' || m.times) {
|
|
22645
|
-
|
|
22646
|
-
const count = typeof countValue === 'number' ? countValue : parseInt(String(countValue), 10);
|
|
22647
|
-
if (isNaN(count))
|
|
23124
|
+
if (!m.times)
|
|
22648
23125
|
throw new Error('times loops require a count number');
|
|
23126
|
+
const count = loopCount(await evaluator.evaluate(m.times, context));
|
|
22649
23127
|
return { type: 'times', count, indexVariable, commands, elseCommands };
|
|
22650
23128
|
}
|
|
22651
23129
|
if (loopType === 'while' || m.while) {
|
|
22652
23130
|
const condition = m.while;
|
|
22653
23131
|
if (!condition)
|
|
22654
23132
|
throw new Error('while loops require a condition');
|
|
22655
|
-
|
|
23133
|
+
const check = () => evaluator.evaluate(condition, context);
|
|
23134
|
+
return {
|
|
23135
|
+
type: 'while',
|
|
23136
|
+
condition: check,
|
|
23137
|
+
indexVariable,
|
|
23138
|
+
commands,
|
|
23139
|
+
elseCommands,
|
|
23140
|
+
bottomTested,
|
|
23141
|
+
};
|
|
22656
23142
|
}
|
|
22657
23143
|
if (loopType === 'until-event' || m.event) {
|
|
22658
23144
|
const eventName = await text(m.event);
|
|
@@ -22670,7 +23156,7 @@ let RepeatCommand = (() => {
|
|
|
22670
23156
|
}
|
|
22671
23157
|
else {
|
|
22672
23158
|
const target = await evaluator.evaluate(m.from, context);
|
|
22673
|
-
if (target
|
|
23159
|
+
if (isEventTarget(target))
|
|
22674
23160
|
eventTarget = target;
|
|
22675
23161
|
else if (target === 'document')
|
|
22676
23162
|
eventTarget = document;
|
|
@@ -22691,7 +23177,15 @@ let RepeatCommand = (() => {
|
|
|
22691
23177
|
const condition = m.until;
|
|
22692
23178
|
if (!condition)
|
|
22693
23179
|
throw new Error('until loops require a condition');
|
|
22694
|
-
|
|
23180
|
+
const check = () => evaluator.evaluate(condition, context);
|
|
23181
|
+
return {
|
|
23182
|
+
type: 'until',
|
|
23183
|
+
condition: check,
|
|
23184
|
+
indexVariable,
|
|
23185
|
+
commands,
|
|
23186
|
+
elseCommands,
|
|
23187
|
+
bottomTested,
|
|
23188
|
+
};
|
|
22695
23189
|
}
|
|
22696
23190
|
if (loopType === 'forever') {
|
|
22697
23191
|
return { type: 'forever', indexVariable, commands, elseCommands };
|
|
@@ -22709,10 +23203,10 @@ let RepeatCommand = (() => {
|
|
|
22709
23203
|
({ config, iterCtx } = createTimesLoopConfig(count, indexVariable));
|
|
22710
23204
|
break;
|
|
22711
23205
|
case 'while':
|
|
22712
|
-
({ config, iterCtx } = createWhileLoopConfig(condition,
|
|
23206
|
+
({ config, iterCtx } = createWhileLoopConfig(condition, evaluateLoopCondition, context, indexVariable));
|
|
22713
23207
|
break;
|
|
22714
23208
|
case 'until':
|
|
22715
|
-
({ config, iterCtx } = createUntilLoopConfig(condition,
|
|
23209
|
+
({ config, iterCtx } = createUntilLoopConfig(condition, evaluateLoopCondition, context, indexVariable));
|
|
22716
23210
|
break;
|
|
22717
23211
|
case 'until-event':
|
|
22718
23212
|
({ config, iterCtx } = createUntilEventLoopConfig(eventName, eventTarget, indexVariable));
|
|
@@ -23743,6 +24237,29 @@ function namedPropertyOf(node) {
|
|
|
23743
24237
|
}
|
|
23744
24238
|
return null;
|
|
23745
24239
|
}
|
|
24240
|
+
const RESTORE_KEYWORDS = new Set(['initial', 'inherit', 'unset', 'revert']);
|
|
24241
|
+
function elementsOf(value) {
|
|
24242
|
+
if (Array.isArray(value) || isNodeList(value)) {
|
|
24243
|
+
return Array.from(value).filter(isHTMLElement$2);
|
|
24244
|
+
}
|
|
24245
|
+
return isHTMLElement$2(value) ? [value] : [];
|
|
24246
|
+
}
|
|
24247
|
+
async function valueOf(node, evaluator, context) {
|
|
24248
|
+
const value = await evaluator.evaluate(node, context);
|
|
24249
|
+
const name = node.name;
|
|
24250
|
+
if (value === undefined && node.type === 'identifier' && typeof name === 'string')
|
|
24251
|
+
return name;
|
|
24252
|
+
return value;
|
|
24253
|
+
}
|
|
24254
|
+
function longestTransitionMs(css) {
|
|
24255
|
+
let longest;
|
|
24256
|
+
for (const item of css.split(',')) {
|
|
24257
|
+
const times = [...item.matchAll(/(\d*\.?\d+)(ms|s)\b/g)].map(m => parseFloat(m[1]) * (m[2] === 's' ? 1000 : 1));
|
|
24258
|
+
if (times.length > 0)
|
|
24259
|
+
longest = Math.max(longest ?? 0, times[0] + (times[1] ?? 0));
|
|
24260
|
+
}
|
|
24261
|
+
return longest;
|
|
24262
|
+
}
|
|
23746
24263
|
let TransitionCommand = (() => {
|
|
23747
24264
|
let _classDecorators = [command({ name: 'transition' })];
|
|
23748
24265
|
let _classDescriptor;
|
|
@@ -23758,11 +24275,10 @@ let TransitionCommand = (() => {
|
|
|
23758
24275
|
let property;
|
|
23759
24276
|
let target;
|
|
23760
24277
|
const firstArg = await evaluator.evaluate(raw.args[0], context);
|
|
23761
|
-
const
|
|
23762
|
-
|
|
23763
|
-
if (isHTMLElement$2(firstAsElement) ||
|
|
24278
|
+
const owners = elementsOf(firstArg);
|
|
24279
|
+
if (owners.length > 0 ||
|
|
23764
24280
|
(typeof firstArg === 'string' && /^[#.]|^(?:me|it|you)$/.test(firstArg))) {
|
|
23765
|
-
target =
|
|
24281
|
+
target = owners.length === 1 ? owners[0] : owners.length > 1 ? owners : firstArg;
|
|
23766
24282
|
property = String(await evaluator.evaluate(raw.args[1], context));
|
|
23767
24283
|
}
|
|
23768
24284
|
else if (raw.args.length >= 2) {
|
|
@@ -23774,9 +24290,9 @@ let TransitionCommand = (() => {
|
|
|
23774
24290
|
throw new Error('transition requires a CSS property');
|
|
23775
24291
|
property = named.property;
|
|
23776
24292
|
if (named.objectNode !== undefined) {
|
|
23777
|
-
const owner =
|
|
23778
|
-
if (
|
|
23779
|
-
target = owner;
|
|
24293
|
+
const owner = elementsOf(await evaluator.evaluate(named.objectNode, context));
|
|
24294
|
+
if (owner.length > 0)
|
|
24295
|
+
target = owner.length === 1 ? owner[0] : owner;
|
|
23780
24296
|
}
|
|
23781
24297
|
}
|
|
23782
24298
|
else {
|
|
@@ -23787,60 +24303,131 @@ let TransitionCommand = (() => {
|
|
|
23787
24303
|
}
|
|
23788
24304
|
if (!raw.modifiers?.to)
|
|
23789
24305
|
throw new Error('transition requires "to <value>"');
|
|
23790
|
-
|
|
23791
|
-
|
|
23792
|
-
value
|
|
23793
|
-
}
|
|
23794
|
-
const result = { property, value: value };
|
|
24306
|
+
const result = {
|
|
24307
|
+
property,
|
|
24308
|
+
value: await valueOf(raw.modifiers.to, evaluator, context),
|
|
24309
|
+
};
|
|
23795
24310
|
if (target !== undefined)
|
|
23796
24311
|
result.target = target;
|
|
24312
|
+
if (raw.modifiers?.from)
|
|
24313
|
+
result.from = await valueOf(raw.modifiers.from, evaluator, context);
|
|
24314
|
+
if (raw.modifiers?.pairs) {
|
|
24315
|
+
result.pairs = await this.laterPairs(raw.modifiers.pairs, evaluator, context);
|
|
24316
|
+
}
|
|
23797
24317
|
if (raw.modifiers?.over)
|
|
23798
24318
|
result.duration = await evaluator.evaluate(raw.modifiers.over, context);
|
|
23799
24319
|
if (raw.modifiers?.with)
|
|
23800
24320
|
result.timingFunction = String(await evaluator.evaluate(raw.modifiers.with, context));
|
|
24321
|
+
if (raw.modifiers?.using)
|
|
24322
|
+
result.using = String(await evaluator.evaluate(raw.modifiers.using, context));
|
|
23801
24323
|
return result;
|
|
23802
24324
|
}
|
|
24325
|
+
async laterPairs(node, evaluator, context) {
|
|
24326
|
+
const pairs = [];
|
|
24327
|
+
for (const pairNode of node.elements ?? []) {
|
|
24328
|
+
const fields = new Map();
|
|
24329
|
+
const properties = pairNode
|
|
24330
|
+
.properties ?? [];
|
|
24331
|
+
for (const { key, value } of properties) {
|
|
24332
|
+
if (typeof key.name === 'string')
|
|
24333
|
+
fields.set(key.name, value);
|
|
24334
|
+
}
|
|
24335
|
+
const propertyNode = fields.get('property');
|
|
24336
|
+
const toNode = fields.get('to');
|
|
24337
|
+
if (!propertyNode || !toNode)
|
|
24338
|
+
throw new Error('transition requires property and value');
|
|
24339
|
+
const pair = {
|
|
24340
|
+
property: String(await evaluator.evaluate(propertyNode, context)),
|
|
24341
|
+
value: await valueOf(toNode, evaluator, context),
|
|
24342
|
+
};
|
|
24343
|
+
const ownerNode = fields.get('owner');
|
|
24344
|
+
if (ownerNode) {
|
|
24345
|
+
const owners = elementsOf(await evaluator.evaluate(ownerNode, context));
|
|
24346
|
+
if (owners.length === 0)
|
|
24347
|
+
throw new Error('transition: target element not found');
|
|
24348
|
+
pair.target = owners.length === 1 ? owners[0] : owners;
|
|
24349
|
+
}
|
|
24350
|
+
const fromNode = fields.get('from');
|
|
24351
|
+
if (fromNode)
|
|
24352
|
+
pair.from = await valueOf(fromNode, evaluator, context);
|
|
24353
|
+
pairs.push(pair);
|
|
24354
|
+
}
|
|
24355
|
+
return pairs;
|
|
24356
|
+
}
|
|
23803
24357
|
async execute(input, context) {
|
|
23804
|
-
|
|
23805
|
-
const
|
|
23806
|
-
|
|
23807
|
-
|
|
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
|
-
|
|
24358
|
+
const duration = parseDuration(input.duration, (input.using !== undefined && longestTransitionMs(input.using)) || 300);
|
|
24359
|
+
const timing = input.timingFunction || 'ease';
|
|
24360
|
+
const jobs = [];
|
|
24361
|
+
for (const pair of [input, ...(input.pairs ?? [])]) {
|
|
24362
|
+
let property = pair.property;
|
|
24363
|
+
if (property.startsWith('*'))
|
|
24364
|
+
property = property.substring(1);
|
|
24365
|
+
property = camelToKebab(property);
|
|
24366
|
+
const elements = Array.isArray(pair.target)
|
|
24367
|
+
? pair.target.filter(isHTMLElement$2)
|
|
24368
|
+
: [resolveElement(pair.target, context, 'transition')];
|
|
24369
|
+
for (const element of elements) {
|
|
24370
|
+
jobs.push({
|
|
24371
|
+
element,
|
|
24372
|
+
property,
|
|
24373
|
+
pair,
|
|
24374
|
+
fromValue: getComputedStyle(element).getPropertyValue(property),
|
|
24375
|
+
toValue: '',
|
|
24376
|
+
removeInlineAfter: false,
|
|
24377
|
+
});
|
|
24378
|
+
}
|
|
24379
|
+
}
|
|
24380
|
+
if (jobs.length === 0)
|
|
24381
|
+
throw new Error('transition: target element not found');
|
|
24382
|
+
for (const job of jobs) {
|
|
24383
|
+
if (job.pair.from !== undefined) {
|
|
24384
|
+
job.element.style.setProperty(job.property, String(job.pair.from));
|
|
24385
|
+
}
|
|
24386
|
+
}
|
|
24387
|
+
for (const job of jobs)
|
|
24388
|
+
if (job.pair.from !== undefined)
|
|
24389
|
+
void job.element.offsetWidth;
|
|
24390
|
+
const originals = new Map();
|
|
24391
|
+
for (const job of jobs) {
|
|
24392
|
+
if (!originals.has(job.element))
|
|
24393
|
+
originals.set(job.element, job.element.style.transition);
|
|
24394
|
+
}
|
|
24395
|
+
for (const [element, original] of originals) {
|
|
24396
|
+
const own = jobs
|
|
24397
|
+
.filter(job => job.element === element)
|
|
24398
|
+
.map(job => `${job.property} ${duration}ms ${timing}`);
|
|
24399
|
+
element.style.transition = input.using ?? [original, ...own].filter(Boolean).join(', ');
|
|
24400
|
+
}
|
|
24401
|
+
for (const job of jobs) {
|
|
24402
|
+
const { element, property } = job;
|
|
24403
|
+
let toValue = String(job.pair.value);
|
|
24404
|
+
if (RESTORE_KEYWORDS.has(toValue)) {
|
|
24405
|
+
const currentInline = element.style.getPropertyValue(property);
|
|
24406
|
+
element.style.removeProperty(property);
|
|
24407
|
+
toValue = getComputedStyle(element).getPropertyValue(property);
|
|
24408
|
+
if (currentInline) {
|
|
24409
|
+
element.style.setProperty(property, currentInline);
|
|
24410
|
+
}
|
|
24411
|
+
job.removeInlineAfter = true;
|
|
24412
|
+
}
|
|
24413
|
+
job.toValue = toValue;
|
|
24414
|
+
element.style.setProperty(property, toValue);
|
|
24415
|
+
}
|
|
24416
|
+
const results = await Promise.all(jobs.map(job => waitForTransitionEnd(job.element, job.property, duration)));
|
|
24417
|
+
for (const [element, original] of originals)
|
|
24418
|
+
element.style.transition = original;
|
|
24419
|
+
for (const job of jobs) {
|
|
24420
|
+
if (job.removeInlineAfter)
|
|
24421
|
+
job.element.style.removeProperty(job.property);
|
|
23836
24422
|
}
|
|
24423
|
+
const first = jobs[0];
|
|
23837
24424
|
return {
|
|
23838
|
-
element:
|
|
23839
|
-
property,
|
|
23840
|
-
fromValue,
|
|
23841
|
-
toValue,
|
|
24425
|
+
element: first.element,
|
|
24426
|
+
property: first.property,
|
|
24427
|
+
fromValue: first.fromValue,
|
|
24428
|
+
toValue: first.toValue,
|
|
23842
24429
|
duration,
|
|
23843
|
-
completed: result.completed,
|
|
24430
|
+
completed: results.every(result => result.completed),
|
|
23844
24431
|
};
|
|
23845
24432
|
}
|
|
23846
24433
|
};
|
|
@@ -23853,13 +24440,16 @@ let TransitionCommand = (() => {
|
|
|
23853
24440
|
})();
|
|
23854
24441
|
_classThis.metadata = commandMeta({
|
|
23855
24442
|
description: 'Animate CSS properties using CSS transitions',
|
|
23856
|
-
syntax: 'transition [<
|
|
24443
|
+
syntax: 'transition <property> [from <value>] to <value> [<property> \u2026 to <value>]\u2026 [over <duration>] [using <css> | with <timing>]',
|
|
23857
24444
|
examples: [
|
|
23858
24445
|
'transition opacity to 0.5',
|
|
23859
24446
|
'transition my *opacity to 0 over 200ms',
|
|
23860
24447
|
"transition #box's *opacity to 0 over 200ms",
|
|
23861
24448
|
'transition left to 100px over 500ms',
|
|
23862
24449
|
'transition background-color to red over 1s with ease-in-out',
|
|
24450
|
+
'transition *width to 100px *height to 50px over 300ms',
|
|
24451
|
+
'transition *opacity of #panel from 0 to 1 over 200ms',
|
|
24452
|
+
'transition my *opacity to 0 using "opacity 1s ease-in"',
|
|
23863
24453
|
],
|
|
23864
24454
|
sideEffects: ['style-change', 'timing'],
|
|
23865
24455
|
category: 'animation',
|
|
@@ -25109,17 +25699,7 @@ let BeepCommand = (() => {
|
|
|
25109
25699
|
this.debugContext(context);
|
|
25110
25700
|
return { expressionCount: 0, debugged: true, outputs: [] };
|
|
25111
25701
|
}
|
|
25112
|
-
const outputs =
|
|
25113
|
-
console.group('\ud83d\udd14 beep! Debug Output');
|
|
25114
|
-
for (const expression of expressions) {
|
|
25115
|
-
const output = this.debugExpression(expression);
|
|
25116
|
-
outputs.push(output);
|
|
25117
|
-
console.log(`Value:`, expression);
|
|
25118
|
-
console.log(`Type:`, output.type);
|
|
25119
|
-
console.log(`Representation:`, output.representation);
|
|
25120
|
-
console.log('---');
|
|
25121
|
-
}
|
|
25122
|
-
console.groupEnd();
|
|
25702
|
+
const outputs = beepValues(context.me, expressions);
|
|
25123
25703
|
return { expressionCount: expressions.length, debugged: true, outputs };
|
|
25124
25704
|
}
|
|
25125
25705
|
debugContext(context) {
|
|
@@ -25132,67 +25712,6 @@ let BeepCommand = (() => {
|
|
|
25132
25712
|
console.log('variables:', context.variables);
|
|
25133
25713
|
console.groupEnd();
|
|
25134
25714
|
}
|
|
25135
|
-
debugExpression(expression) {
|
|
25136
|
-
return {
|
|
25137
|
-
value: expression,
|
|
25138
|
-
type: this.getType(expression),
|
|
25139
|
-
representation: this.getRepresentation(expression),
|
|
25140
|
-
};
|
|
25141
|
-
}
|
|
25142
|
-
getType(value) {
|
|
25143
|
-
if (value === null)
|
|
25144
|
-
return 'null';
|
|
25145
|
-
if (value === undefined)
|
|
25146
|
-
return 'undefined';
|
|
25147
|
-
if (Array.isArray(value))
|
|
25148
|
-
return 'array';
|
|
25149
|
-
if (isHTMLElement$2(value))
|
|
25150
|
-
return 'HTMLElement';
|
|
25151
|
-
if (value instanceof Element)
|
|
25152
|
-
return 'Element';
|
|
25153
|
-
if (value instanceof Node)
|
|
25154
|
-
return 'Node';
|
|
25155
|
-
if (value instanceof Error)
|
|
25156
|
-
return 'Error';
|
|
25157
|
-
if (value instanceof Date)
|
|
25158
|
-
return 'Date';
|
|
25159
|
-
if (value instanceof RegExp)
|
|
25160
|
-
return 'RegExp';
|
|
25161
|
-
return typeof value;
|
|
25162
|
-
}
|
|
25163
|
-
getRepresentation(value) {
|
|
25164
|
-
if (value === null)
|
|
25165
|
-
return 'null';
|
|
25166
|
-
if (value === undefined)
|
|
25167
|
-
return 'undefined';
|
|
25168
|
-
if (Array.isArray(value)) {
|
|
25169
|
-
return `Array(${value.length}) [${value
|
|
25170
|
-
.slice(0, 3)
|
|
25171
|
-
.map(v => this.getRepresentation(v))
|
|
25172
|
-
.join(', ')}${value.length > 3 ? '...' : ''}]`;
|
|
25173
|
-
}
|
|
25174
|
-
if (isHTMLElement$2(value)) {
|
|
25175
|
-
const el = value;
|
|
25176
|
-
const tag = el.tagName.toLowerCase();
|
|
25177
|
-
const id = el.id ? `#${el.id}` : '';
|
|
25178
|
-
const classes = el.className ? `.${el.className.split(' ').join('.')}` : '';
|
|
25179
|
-
return `<${tag}${id}${classes}/>`;
|
|
25180
|
-
}
|
|
25181
|
-
if (value instanceof Error)
|
|
25182
|
-
return `Error: ${value.message}`;
|
|
25183
|
-
if (typeof value === 'string')
|
|
25184
|
-
return value.length > 50 ? `"${value.substring(0, 47)}..."` : `"${value}"`;
|
|
25185
|
-
if (typeof value === 'object') {
|
|
25186
|
-
try {
|
|
25187
|
-
const keys = Object.keys(value);
|
|
25188
|
-
return `Object {${keys.slice(0, 3).join(', ')}${keys.length > 3 ? '...' : ''}}`;
|
|
25189
|
-
}
|
|
25190
|
-
catch {
|
|
25191
|
-
return '[Object]';
|
|
25192
|
-
}
|
|
25193
|
-
}
|
|
25194
|
-
return String(value);
|
|
25195
|
-
}
|
|
25196
25715
|
};
|
|
25197
25716
|
__setFunctionName(_classThis, "BeepCommand");
|
|
25198
25717
|
(() => {
|
|
@@ -25740,16 +26259,12 @@ class RenderCommand {
|
|
|
25740
26259
|
}
|
|
25741
26260
|
RenderCommand.metadata = commandMeta({
|
|
25742
26261
|
description: 'Render templates with @if, @else, and @repeat directives',
|
|
25743
|
-
syntax: [
|
|
25744
|
-
'render <template>',
|
|
25745
|
-
'render <template> with <variables>',
|
|
25746
|
-
'render <template> with (key: value, ...)',
|
|
25747
|
-
],
|
|
26262
|
+
syntax: ['render <template>', 'render <template> with <name>: <value>, ...'],
|
|
25748
26263
|
examples: [
|
|
25749
26264
|
'render myTemplate',
|
|
25750
|
-
'render myTemplate with
|
|
25751
|
-
'render "<template>Hello ${name}!</template>" with
|
|
25752
|
-
'render template with
|
|
26265
|
+
'render myTemplate with name: "Alice"',
|
|
26266
|
+
'render "<template>Hello ${name}!</template>" with name: "World"',
|
|
26267
|
+
'render template with items: data',
|
|
25753
26268
|
],
|
|
25754
26269
|
category: 'templates',
|
|
25755
26270
|
compatibility: 'standard',
|
|
@@ -25937,7 +26452,7 @@ function updateDebugStats(event) {
|
|
|
25937
26452
|
stats.confidenceHistory.reduce((a, b) => a + b, 0) / stats.confidenceHistory.length;
|
|
25938
26453
|
}
|
|
25939
26454
|
|
|
25940
|
-
const VERSION = '3.
|
|
26455
|
+
const VERSION = '3.2.0';
|
|
25941
26456
|
|
|
25942
26457
|
function createHistorySwap(config) {
|
|
25943
26458
|
const { target, strategy = 'morph', useViewTransition = false, fetchOptions = {}, transformUrl, onBeforeFetch, onAfterSwap, onError, } = config;
|
|
@@ -27483,6 +27998,14 @@ function convertNode(node, infer) {
|
|
|
27483
27998
|
...pos(node),
|
|
27484
27999
|
};
|
|
27485
28000
|
case 'identifier':
|
|
28001
|
+
if (node.scope === 'element' || node.scope === 'local' || node.scope === 'global') {
|
|
28002
|
+
return {
|
|
28003
|
+
type: 'variable',
|
|
28004
|
+
name: (node.name ?? ''),
|
|
28005
|
+
scope: node.scope,
|
|
28006
|
+
...pos(node),
|
|
28007
|
+
};
|
|
28008
|
+
}
|
|
27486
28009
|
return {
|
|
27487
28010
|
type: 'identifier',
|
|
27488
28011
|
value: (node.name ?? node.value ?? ''),
|
|
@@ -27505,6 +28028,15 @@ function convertNode(node, infer) {
|
|
|
27505
28028
|
operand: convertNode(node.argument ?? node.operand, infer),
|
|
27506
28029
|
...pos(node),
|
|
27507
28030
|
};
|
|
28031
|
+
case 'typeCheckExpression':
|
|
28032
|
+
return {
|
|
28033
|
+
type: 'typeCheck',
|
|
28034
|
+
value: convertNode(node.value, infer),
|
|
28035
|
+
typeName: String(node.typeName),
|
|
28036
|
+
nullOk: node.nullOk !== false,
|
|
28037
|
+
negated: node.negated === true,
|
|
28038
|
+
...pos(node),
|
|
28039
|
+
};
|
|
27508
28040
|
case 'timeExpression':
|
|
27509
28041
|
return { type: 'literal', value: node.value, ...pos(node) };
|
|
27510
28042
|
case 'templateLiteral':
|
|
@@ -27516,6 +28048,28 @@ function convertNode(node, infer) {
|
|
|
27516
28048
|
scope: (node.scope ?? 'local'),
|
|
27517
28049
|
...pos(node),
|
|
27518
28050
|
};
|
|
28051
|
+
case 'arrayLiteral':
|
|
28052
|
+
return {
|
|
28053
|
+
type: 'array',
|
|
28054
|
+
elements: (node.elements ?? []).map(element => convertNode(element, infer)),
|
|
28055
|
+
...pos(node),
|
|
28056
|
+
};
|
|
28057
|
+
case 'attributeAccess':
|
|
28058
|
+
return {
|
|
28059
|
+
type: 'possessive',
|
|
28060
|
+
object: { type: 'identifier', value: 'me' },
|
|
28061
|
+
property: `@${node.attributeName}`,
|
|
28062
|
+
...pos(node),
|
|
28063
|
+
};
|
|
28064
|
+
case 'propertyOfExpression': {
|
|
28065
|
+
const property = node.property;
|
|
28066
|
+
return {
|
|
28067
|
+
type: 'possessive',
|
|
28068
|
+
object: convertNode(node.target, infer),
|
|
28069
|
+
property: (property?.name ?? property?.value ?? ''),
|
|
28070
|
+
...pos(node),
|
|
28071
|
+
};
|
|
28072
|
+
}
|
|
27519
28073
|
case 'htmlSelector':
|
|
27520
28074
|
return {
|
|
27521
28075
|
type: 'selector',
|
|
@@ -27567,6 +28121,21 @@ function convertCommand$1(node, infer) {
|
|
|
27567
28121
|
if (name === 'repeat') {
|
|
27568
28122
|
return convertRepeatCommand(node, infer);
|
|
27569
28123
|
}
|
|
28124
|
+
const original = node.originalCommand;
|
|
28125
|
+
const sum = node.modifiers?.to;
|
|
28126
|
+
const counted = node.args?.[0];
|
|
28127
|
+
if (name === 'set' &&
|
|
28128
|
+
(original === 'increment' || original === 'decrement') &&
|
|
28129
|
+
sum?.type === 'binaryExpression' &&
|
|
28130
|
+
counted) {
|
|
28131
|
+
return convertCommand$1({
|
|
28132
|
+
...node,
|
|
28133
|
+
name: original,
|
|
28134
|
+
args: [counted],
|
|
28135
|
+
modifiers: { by: sum.right },
|
|
28136
|
+
originalCommand: undefined,
|
|
28137
|
+
}, infer);
|
|
28138
|
+
}
|
|
27570
28139
|
const args = (node.args ?? []).map(arg => convertNode(arg, infer));
|
|
27571
28140
|
const target = node.target ? convertNode(node.target, infer) : undefined;
|
|
27572
28141
|
const modifiers = node.modifiers
|
|
@@ -27615,6 +28184,64 @@ function convertIfCommand(node, infer) {
|
|
|
27615
28184
|
};
|
|
27616
28185
|
}
|
|
27617
28186
|
function convertRepeatCommand(node, infer) {
|
|
28187
|
+
const slots = (node.modifiers ?? {});
|
|
28188
|
+
const form = slotText(slots.loopType);
|
|
28189
|
+
if (form === undefined)
|
|
28190
|
+
return convertPositionalRepeat(node, infer);
|
|
28191
|
+
const [bodyBlock, elseBlock] = (node.args ?? []);
|
|
28192
|
+
const body = extractBlockCommands(bodyBlock, infer);
|
|
28193
|
+
const indexName = slotText(slots.index);
|
|
28194
|
+
const shared = {
|
|
28195
|
+
...(indexName ? { indexName } : {}),
|
|
28196
|
+
...(elseBlock ? { elseBody: extractBlockCommands(elseBlock, infer) } : {}),
|
|
28197
|
+
...pos(node),
|
|
28198
|
+
};
|
|
28199
|
+
const operand = (slot) => (slot ? convertNode(slot, infer) : undefined);
|
|
28200
|
+
switch (form) {
|
|
28201
|
+
case 'for':
|
|
28202
|
+
return {
|
|
28203
|
+
type: 'foreach',
|
|
28204
|
+
itemName: slotText(slots.for) ?? 'it',
|
|
28205
|
+
collection: operand(slots.in) ?? { type: 'literal', value: null },
|
|
28206
|
+
body,
|
|
28207
|
+
...shared,
|
|
28208
|
+
};
|
|
28209
|
+
case 'times':
|
|
28210
|
+
return { type: 'repeat', count: operand(slots.times), body, ...shared };
|
|
28211
|
+
case 'while':
|
|
28212
|
+
case 'until': {
|
|
28213
|
+
const test = operand(slots[form]) ?? { type: 'literal', value: form === 'until' };
|
|
28214
|
+
return {
|
|
28215
|
+
type: 'while',
|
|
28216
|
+
condition: form === 'until' ? { type: 'unary', operator: 'not', operand: test } : test,
|
|
28217
|
+
...(slots.bottomTested?.value === true ? { bottomTested: true } : {}),
|
|
28218
|
+
body,
|
|
28219
|
+
...shared,
|
|
28220
|
+
};
|
|
28221
|
+
}
|
|
28222
|
+
case 'until-event': {
|
|
28223
|
+
const event = slotText(slots.event);
|
|
28224
|
+
if (!event) {
|
|
28225
|
+
return { type: 'error', message: "'repeat until event' has no event name", ...pos(node) };
|
|
28226
|
+
}
|
|
28227
|
+
const target = operand(slots.from);
|
|
28228
|
+
return {
|
|
28229
|
+
type: 'repeat',
|
|
28230
|
+
untilEvent: event,
|
|
28231
|
+
...(target ? { untilEventTarget: target } : {}),
|
|
28232
|
+
body,
|
|
28233
|
+
...shared,
|
|
28234
|
+
};
|
|
28235
|
+
}
|
|
28236
|
+
default:
|
|
28237
|
+
return { type: 'repeat', body, ...shared };
|
|
28238
|
+
}
|
|
28239
|
+
}
|
|
28240
|
+
function slotText(slot) {
|
|
28241
|
+
const text = slot?.value ?? slot?.name;
|
|
28242
|
+
return typeof text === 'string' ? text : undefined;
|
|
28243
|
+
}
|
|
28244
|
+
function convertPositionalRepeat(node, infer) {
|
|
27618
28245
|
const args = (node.args ?? []);
|
|
27619
28246
|
if (args.length === 0) {
|
|
27620
28247
|
return { type: 'repeat', body: [], ...pos(node) };
|
|
@@ -27931,6 +28558,15 @@ function toCoreAST(node) {
|
|
|
27931
28558
|
prefix: true,
|
|
27932
28559
|
...nodePos(node),
|
|
27933
28560
|
};
|
|
28561
|
+
case 'typeCheck':
|
|
28562
|
+
return {
|
|
28563
|
+
type: 'typeCheckExpression',
|
|
28564
|
+
value: toCoreAST(node.value),
|
|
28565
|
+
typeName: node.typeName,
|
|
28566
|
+
nullOk: node.nullOk,
|
|
28567
|
+
negated: node.negated,
|
|
28568
|
+
...nodePos(node),
|
|
28569
|
+
};
|
|
27934
28570
|
case 'member':
|
|
27935
28571
|
return {
|
|
27936
28572
|
type: 'memberExpression',
|
|
@@ -27941,6 +28577,12 @@ function toCoreAST(node) {
|
|
|
27941
28577
|
computed: node.computed ?? false,
|
|
27942
28578
|
...nodePos(node),
|
|
27943
28579
|
};
|
|
28580
|
+
case 'array':
|
|
28581
|
+
return {
|
|
28582
|
+
type: 'arrayLiteral',
|
|
28583
|
+
elements: node.elements.map(element => toCoreAST(element)),
|
|
28584
|
+
...nodePos(node),
|
|
28585
|
+
};
|
|
27944
28586
|
case 'possessive':
|
|
27945
28587
|
return {
|
|
27946
28588
|
type: 'possessiveExpression',
|
|
@@ -28003,72 +28645,85 @@ function convertCommand(node) {
|
|
|
28003
28645
|
}
|
|
28004
28646
|
return result;
|
|
28005
28647
|
}
|
|
28648
|
+
function block(nodes) {
|
|
28649
|
+
return { type: 'block', commands: nodes.map(n => toCoreAST(n)), ...POS };
|
|
28650
|
+
}
|
|
28006
28651
|
function convertIf(node) {
|
|
28007
|
-
const
|
|
28652
|
+
const [elseIf, ...laterElseIfs] = node.elseIfBranches ?? [];
|
|
28653
|
+
const elseBranch = elseIf
|
|
28654
|
+
? [
|
|
28655
|
+
{
|
|
28656
|
+
type: 'if',
|
|
28657
|
+
condition: elseIf.condition,
|
|
28658
|
+
thenBranch: elseIf.body,
|
|
28659
|
+
elseIfBranches: laterElseIfs,
|
|
28660
|
+
...(node.elseBranch ? { elseBranch: node.elseBranch } : {}),
|
|
28661
|
+
},
|
|
28662
|
+
]
|
|
28663
|
+
: node.elseBranch;
|
|
28664
|
+
return {
|
|
28008
28665
|
type: 'command',
|
|
28009
28666
|
name: 'if',
|
|
28010
28667
|
isBlocking: true,
|
|
28011
|
-
|
|
28012
|
-
|
|
28013
|
-
|
|
28668
|
+
args: [
|
|
28669
|
+
toCoreAST(node.condition),
|
|
28670
|
+
block(node.thenBranch),
|
|
28671
|
+
...(elseBranch ? [block(elseBranch)] : []),
|
|
28672
|
+
],
|
|
28014
28673
|
...nodePos(node),
|
|
28015
28674
|
};
|
|
28016
|
-
if (node.elseBranch) {
|
|
28017
|
-
result.elseBranch = node.elseBranch.map(n => toCoreAST(n));
|
|
28018
|
-
}
|
|
28019
|
-
return result;
|
|
28020
28675
|
}
|
|
28021
|
-
function
|
|
28022
|
-
const
|
|
28676
|
+
function loopCommand(node, form, operands) {
|
|
28677
|
+
const modifiers = { loopType: slotString(form) };
|
|
28678
|
+
for (const [slot, value] of Object.entries(operands)) {
|
|
28679
|
+
if (value)
|
|
28680
|
+
modifiers[slot] = value;
|
|
28681
|
+
}
|
|
28682
|
+
if (node.indexName)
|
|
28683
|
+
modifiers.index = slotString(node.indexName);
|
|
28684
|
+
return {
|
|
28023
28685
|
type: 'command',
|
|
28024
28686
|
name: 'repeat',
|
|
28025
28687
|
isBlocking: true,
|
|
28026
|
-
|
|
28027
|
-
|
|
28688
|
+
args: [block(node.body), ...(node.elseBody ? [block(node.elseBody)] : [])],
|
|
28689
|
+
modifiers,
|
|
28028
28690
|
...nodePos(node),
|
|
28029
28691
|
};
|
|
28692
|
+
}
|
|
28693
|
+
function slotString(value) {
|
|
28694
|
+
return { type: 'string', value, ...POS };
|
|
28695
|
+
}
|
|
28696
|
+
function convertRepeat(node) {
|
|
28030
28697
|
if (node.count !== undefined) {
|
|
28031
|
-
|
|
28032
|
-
|
|
28033
|
-
|
|
28034
|
-
|
|
28035
|
-
|
|
28036
|
-
|
|
28037
|
-
|
|
28038
|
-
|
|
28039
|
-
|
|
28040
|
-
|
|
28041
|
-
|
|
28042
|
-
|
|
28043
|
-
|
|
28698
|
+
const times = typeof node.count === 'number'
|
|
28699
|
+
? { type: 'literal', value: node.count, raw: String(node.count), ...POS }
|
|
28700
|
+
: toCoreAST(node.count);
|
|
28701
|
+
return loopCommand(node, 'times', { times });
|
|
28702
|
+
}
|
|
28703
|
+
if (node.whileCondition !== undefined) {
|
|
28704
|
+
return loopCommand(node, 'while', { while: toCoreAST(node.whileCondition) });
|
|
28705
|
+
}
|
|
28706
|
+
if (node.untilEvent !== undefined) {
|
|
28707
|
+
return loopCommand(node, 'until-event', {
|
|
28708
|
+
event: slotString(node.untilEvent),
|
|
28709
|
+
from: node.untilEventTarget ? toCoreAST(node.untilEventTarget) : undefined,
|
|
28710
|
+
});
|
|
28044
28711
|
}
|
|
28045
|
-
return
|
|
28712
|
+
return loopCommand(node, 'forever', {});
|
|
28046
28713
|
}
|
|
28047
28714
|
function convertForEach(node) {
|
|
28048
|
-
return {
|
|
28049
|
-
|
|
28050
|
-
|
|
28051
|
-
|
|
28052
|
-
loopVariant: 'for',
|
|
28053
|
-
itemName: node.itemName,
|
|
28054
|
-
...(node.indexName ? { indexName: node.indexName } : {}),
|
|
28055
|
-
collection: toCoreAST(node.collection),
|
|
28056
|
-
body: node.body.map(n => toCoreAST(n)),
|
|
28057
|
-
args: [],
|
|
28058
|
-
...nodePos(node),
|
|
28059
|
-
};
|
|
28715
|
+
return loopCommand(node, 'for', {
|
|
28716
|
+
for: slotString(node.itemName),
|
|
28717
|
+
in: toCoreAST(node.collection),
|
|
28718
|
+
});
|
|
28060
28719
|
}
|
|
28061
28720
|
function convertWhile(node) {
|
|
28062
|
-
return {
|
|
28063
|
-
|
|
28064
|
-
|
|
28065
|
-
|
|
28066
|
-
|
|
28067
|
-
|
|
28068
|
-
body: node.body.map(n => toCoreAST(n)),
|
|
28069
|
-
args: [],
|
|
28070
|
-
...nodePos(node),
|
|
28071
|
-
};
|
|
28721
|
+
return loopCommand(node, 'while', {
|
|
28722
|
+
while: toCoreAST(node.condition),
|
|
28723
|
+
bottomTested: node.bottomTested
|
|
28724
|
+
? { type: 'literal', value: true, raw: 'true', ...POS }
|
|
28725
|
+
: undefined,
|
|
28726
|
+
});
|
|
28072
28727
|
}
|
|
28073
28728
|
function flattenEventModifiers(modifiers) {
|
|
28074
28729
|
if (!modifiers)
|