@hyperfixi/core 3.1.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -36
- package/dist/ast/command-slots.d.ts +1 -1
- package/dist/ast/nodes.d.ts +2 -0
- package/dist/ast-utils/index.cjs +193 -48
- package/dist/ast-utils/index.mjs +193 -48
- package/dist/ast-utils/interchange/types.d.ts +19 -1
- package/dist/behaviors/index.cjs +19 -0
- package/dist/behaviors/index.mjs +19 -0
- package/dist/bundle-generator/index.cjs +36 -17
- package/dist/bundle-generator/index.mjs +36 -17
- package/dist/bundle-generator/parser-templates.d.ts +1 -1
- package/dist/chunks/bridge-CI9SOl8u.js +2 -0
- package/dist/chunks/browser-modular-7CpI5VF2.js +2 -0
- package/dist/chunks/index-BwXDEK0m.js +2 -0
- package/dist/chunks/index-DQi2o8Lk.js +2 -0
- package/dist/chunks/index-uCOQ8Qn0.js +2 -0
- package/dist/commands/animation/transition.d.ts +12 -6
- package/dist/commands/helpers/loop-executor.d.ts +2 -2
- package/dist/commands/helpers/numeric-target-parser.d.ts +1 -1
- package/dist/commands/helpers/variable-access.d.ts +5 -2
- package/dist/commands/helpers/write-target.d.ts +5 -0
- package/dist/commands/index.cjs +938 -425
- package/dist/commands/index.mjs +938 -425
- package/dist/commands/templates/render.d.ts +4 -4
- package/dist/commands/utility/beep.d.ts +0 -3
- package/dist/expressions/index.cjs +7 -13
- package/dist/expressions/index.mjs +7 -13
- package/dist/htmx/i18n-hooks.d.ts +4 -1
- package/dist/hyperfixi-browser-classic-i18n.js +1 -1
- package/dist/hyperfixi-browser.js +1 -1
- package/dist/hyperfixi-classic-i18n.js +1 -1
- package/dist/hyperfixi-hx-v4.js +1 -1
- package/dist/hyperfixi-hx.js +1 -1
- package/dist/hyperfixi-hybrid-complete.js +1 -1
- package/dist/hyperfixi-hybrid-hx.js +1 -1
- package/dist/hyperfixi-multilingual.js +1 -1
- package/dist/hyperfixi.js +1 -1
- package/dist/hyperfixi.mjs +1 -1
- package/dist/index.cjs +1149 -485
- package/dist/index.min.js +1 -1
- package/dist/index.mjs +1149 -485
- package/dist/lib/swap-executor.d.ts +1 -1
- package/dist/lokascript-browser-classic-i18n.js +1 -1
- package/dist/lokascript-browser.js +1 -1
- package/dist/lokascript-hybrid-complete.js +1 -1
- package/dist/lokascript-hybrid-hx.js +1 -1
- package/dist/lokascript-multilingual.js +1 -1
- package/dist/metadata.cjs +9 -9
- package/dist/metadata.d.ts +1 -1
- package/dist/metadata.mjs +9 -9
- package/dist/parser/command-grammar.d.ts +1 -0
- package/dist/parser/full-parser.cjs +396 -153
- package/dist/parser/full-parser.mjs +396 -153
- package/dist/parser/helpers/parsing-helpers.d.ts +3 -0
- package/dist/parser/hybrid/index.cjs +3 -0
- package/dist/parser/hybrid/index.mjs +3 -0
- package/dist/parser/hybrid/parser-core.cjs +3 -0
- package/dist/parser/hybrid/parser-core.mjs +3 -0
- package/dist/parser/hybrid-parser.cjs +3 -0
- package/dist/parser/hybrid-parser.mjs +3 -0
- package/dist/parser/parser-types.d.ts +2 -0
- package/dist/parser/parser.d.ts +9 -0
- package/dist/parser/pratt-parser.d.ts +2 -0
- package/dist/registry/index.cjs +535 -210
- package/dist/registry/index.mjs +535 -210
- package/dist/utils/beep.d.ts +8 -0
- package/dist/version.d.ts +1 -1
- package/package.json +21 -18
- package/vocab/htmx/ar.js +22 -20
- package/vocab/htmx/bn.js +21 -5
- package/vocab/htmx/de.js +33 -10
- package/vocab/htmx/en.js +4 -1
- package/vocab/htmx/es.js +28 -9
- package/vocab/htmx/fr.js +28 -8
- package/vocab/htmx/he.js +22 -1
- package/vocab/htmx/hi.js +28 -7
- package/vocab/htmx/id.js +29 -10
- package/vocab/htmx/it.js +26 -5
- package/vocab/htmx/ja.js +36 -11
- package/vocab/htmx/ko.js +27 -6
- package/vocab/htmx/ms.js +24 -1
- package/vocab/htmx/pl.js +27 -7
- package/vocab/htmx/pt.js +30 -9
- package/vocab/htmx/qu.js +32 -9
- package/vocab/htmx/ru.js +28 -6
- package/vocab/htmx/sw.js +28 -8
- package/vocab/htmx/th.js +19 -1
- package/vocab/htmx/tl.js +24 -1
- package/vocab/htmx/tr.js +34 -8
- package/vocab/htmx/uk.js +28 -6
- package/vocab/htmx/vi.js +27 -19
- package/vocab/htmx/zh.js +28 -9
- package/dist/chunks/bridge-D7qrHhV0.js +0 -2
- package/dist/chunks/browser-modular-C4LFxfoS.js +0 -2
- package/dist/chunks/index-C7lqD4mo.js +0 -2
- package/dist/chunks/index-CAPhqA6k.js +0 -2
- package/dist/chunks/index-CLdRyZaP.js +0 -2
package/dist/index.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')) {
|
|
@@ -3800,6 +3945,15 @@ function parseToggleCommand(ctx, identifierNode) {
|
|
|
3800
3945
|
args.push(classArg.expression);
|
|
3801
3946
|
modifiers['as'] = literalModifier(mode);
|
|
3802
3947
|
}
|
|
3948
|
+
else if (classArg?.type === 'binaryExpression' &&
|
|
3949
|
+
classArg.operator === 'of' &&
|
|
3950
|
+
classArg.left?.type === 'selector' &&
|
|
3951
|
+
typeof classArg.left.value === 'string' &&
|
|
3952
|
+
classArg.left.value.startsWith('*') &&
|
|
3953
|
+
classArg.right) {
|
|
3954
|
+
args.push(classArg.left);
|
|
3955
|
+
modifiers['on'] = classArg.right;
|
|
3956
|
+
}
|
|
3803
3957
|
else if (classArg) {
|
|
3804
3958
|
args.push(classArg);
|
|
3805
3959
|
}
|
|
@@ -3977,11 +4131,17 @@ function parseSwapCommand(ctx, identifierNode) {
|
|
|
3977
4131
|
const targetExpr = isDelete ? ctx.parseExpression() : parseOneArgument(ctx, [KEYWORDS.WITH]);
|
|
3978
4132
|
if (targetExpr)
|
|
3979
4133
|
args.push(targetExpr);
|
|
3980
|
-
|
|
4134
|
+
const isMorph = identifierNode.name === 'morph';
|
|
4135
|
+
if (!isDelete &&
|
|
4136
|
+
(consumeOptionalKeyword(ctx, KEYWORDS.WITH) ||
|
|
4137
|
+
(isMorph && consumeOptionalKeyword(ctx, KEYWORDS.TO)))) {
|
|
3981
4138
|
const contentExpr = parseOneArgument(ctx, ['using']);
|
|
3982
4139
|
if (contentExpr)
|
|
3983
4140
|
modifiers['with'] = contentExpr;
|
|
3984
4141
|
}
|
|
4142
|
+
if (isMorph && modifiers['with'] === undefined) {
|
|
4143
|
+
ctx.addError("Expected 'to' and the content to morph into");
|
|
4144
|
+
}
|
|
3985
4145
|
return CommandNodeBuilder.fromIdentifier(identifierNode)
|
|
3986
4146
|
.withArgs(...args)
|
|
3987
4147
|
.withModifiers({ ...modifiers, ...parseViewTransitionTail(ctx) })
|
|
@@ -4742,6 +4902,8 @@ function atArgumentBoundary(ctx, grammar) {
|
|
|
4742
4902
|
const next = ctx.peek();
|
|
4743
4903
|
if (ctx.checkIsCommand() && ctx.peekAt(1)?.value === '(')
|
|
4744
4904
|
return false;
|
|
4905
|
+
if (isGluedBeepBang(next, ctx.peekAt(1)))
|
|
4906
|
+
return false;
|
|
4745
4907
|
if (isCommandBoundary(ctx, ['catch', 'finally', 'on']))
|
|
4746
4908
|
return true;
|
|
4747
4909
|
return isKeyword(next, [...grammar.markers]);
|
|
@@ -4773,10 +4935,13 @@ function parseDeclaredCommand(ctx, commandToken, name, grammar) {
|
|
|
4773
4935
|
}
|
|
4774
4936
|
}
|
|
4775
4937
|
const commaList = new Set(grammar.commaList ?? []);
|
|
4938
|
+
const namedArgs = new Set(grammar.namedArgs ?? []);
|
|
4776
4939
|
while (!ctx.isAtEnd() && isKeyword(ctx.peek(), [...grammar.markers])) {
|
|
4777
4940
|
const keyword = ctx.advance().value;
|
|
4778
4941
|
const key = ctx.resolveKeyword(keyword).toLowerCase();
|
|
4779
|
-
let value =
|
|
4942
|
+
let value = namedArgs.has(key) && isNakedNamedArgStart(ctx)
|
|
4943
|
+
? parseNakedNamedArgs(ctx, () => ctx.parseExpression(), name)
|
|
4944
|
+
: ctx.parseExpression();
|
|
4780
4945
|
if (value && commaList.has(key) && ctx.check(',')) {
|
|
4781
4946
|
const elements = [value];
|
|
4782
4947
|
while (ctx.match(',')) {
|
|
@@ -4941,8 +5106,8 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
4941
5106
|
}
|
|
4942
5107
|
if (ctx.check('with') && !modifiers['with']) {
|
|
4943
5108
|
ctx.advance();
|
|
4944
|
-
if (
|
|
4945
|
-
modifiers['with'] =
|
|
5109
|
+
if (isNakedNamedArgStart(ctx)) {
|
|
5110
|
+
modifiers['with'] = parseNakedNamedArgs(ctx, () => ctx.parsePrimary(), 'fetch');
|
|
4946
5111
|
}
|
|
4947
5112
|
else {
|
|
4948
5113
|
modifiers['with'] = ctx.parsePrimary();
|
|
@@ -4977,45 +5142,6 @@ function parseFetchCommand(ctx, commandToken) {
|
|
|
4977
5142
|
}
|
|
4978
5143
|
return builder.build();
|
|
4979
5144
|
}
|
|
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
5145
|
function findJsEndBoundary(ctx, startPos) {
|
|
5020
5146
|
const input = ctx.getInputSlice(startPos);
|
|
5021
5147
|
if (!input) {
|
|
@@ -5135,6 +5261,7 @@ function parseTellCommand(ctx, identifierNode) {
|
|
|
5135
5261
|
const commands = [];
|
|
5136
5262
|
while (!ctx.isAtEnd()) {
|
|
5137
5263
|
if (ctx.checkIsCommand()) {
|
|
5264
|
+
const word = ctx.peek().value;
|
|
5138
5265
|
try {
|
|
5139
5266
|
ctx.advance();
|
|
5140
5267
|
const cmd = ctx.parseCommand();
|
|
@@ -5145,7 +5272,8 @@ function parseTellCommand(ctx, identifierNode) {
|
|
|
5145
5272
|
break;
|
|
5146
5273
|
}
|
|
5147
5274
|
}
|
|
5148
|
-
catch {
|
|
5275
|
+
catch (error) {
|
|
5276
|
+
ctx.addError(`'${word}' in a tell body failed to parse: ${error instanceof Error ? error.message : String(error)}`);
|
|
5149
5277
|
break;
|
|
5150
5278
|
}
|
|
5151
5279
|
if (ctx.match(KEYWORDS.AND) || ctx.match(KEYWORDS.THEN)) {
|
|
@@ -5294,7 +5422,6 @@ const ONE_EXPR = (syntax) => ({
|
|
|
5294
5422
|
markers: [],
|
|
5295
5423
|
syntax,
|
|
5296
5424
|
});
|
|
5297
|
-
const BEEP = ONE_EXPR('beep! [<expression>, \u2026]');
|
|
5298
5425
|
const COMMAND_GRAMMAR = {
|
|
5299
5426
|
append: { positional: 'expression', markers: ['to'], syntax: 'append <value> [to <target>]' },
|
|
5300
5427
|
prepend: { positional: 'expression', markers: ['to'], syntax: 'prepend <value> [to <target>]' },
|
|
@@ -5305,8 +5432,7 @@ const COMMAND_GRAMMAR = {
|
|
|
5305
5432
|
syntax: 'make (a|an) <type> [from <args>] [called <name>]',
|
|
5306
5433
|
},
|
|
5307
5434
|
throw: { positional: 'primary', markers: [], syntax: 'throw <error>' },
|
|
5308
|
-
'beep!'
|
|
5309
|
-
beep: BEEP,
|
|
5435
|
+
beep: ONE_EXPR('beep! [<expression>, \u2026]'),
|
|
5310
5436
|
blur: ONE_EXPR('blur [<target>]'),
|
|
5311
5437
|
break: NONE,
|
|
5312
5438
|
breakpoint: NONE,
|
|
@@ -5330,7 +5456,8 @@ const COMMAND_GRAMMAR = {
|
|
|
5330
5456
|
render: {
|
|
5331
5457
|
positional: 'expression',
|
|
5332
5458
|
markers: ['with'],
|
|
5333
|
-
|
|
5459
|
+
namedArgs: ['with'],
|
|
5460
|
+
syntax: 'render <template> [with <name>: <value>, \u2026]',
|
|
5334
5461
|
},
|
|
5335
5462
|
reset: ONE_EXPR('reset [<form>]'),
|
|
5336
5463
|
return: ONE_EXPR('return [<value>]'),
|
|
@@ -5685,7 +5812,10 @@ class Parser {
|
|
|
5685
5812
|
const firstToken = this.peek();
|
|
5686
5813
|
const prefixEntry = Parser.PRATT_TABLE.get(firstToken.value);
|
|
5687
5814
|
let left;
|
|
5688
|
-
if (
|
|
5815
|
+
if (isGluedBeepBang(firstToken, this.tokens[this.current + 1])) {
|
|
5816
|
+
left = this.parseBeepExpression();
|
|
5817
|
+
}
|
|
5818
|
+
else if (prefixEntry?.prefix) {
|
|
5689
5819
|
const token = this.advance();
|
|
5690
5820
|
if (this.isAtEnd()) {
|
|
5691
5821
|
this.addError(`Expected expression after '${token.value}' operator`);
|
|
@@ -5705,6 +5835,9 @@ class Parser {
|
|
|
5705
5835
|
continue;
|
|
5706
5836
|
}
|
|
5707
5837
|
}
|
|
5838
|
+
if (nextToken.value === '*' && this.isSpacedStyleRef(this.current)) {
|
|
5839
|
+
break;
|
|
5840
|
+
}
|
|
5708
5841
|
const infixEntry = Parser.PRATT_TABLE.get(nextToken.value);
|
|
5709
5842
|
if (!infixEntry?.infix) {
|
|
5710
5843
|
if (STOP_TOKENS.has(nextToken.value) || STOP_DELIMITERS.has(nextToken.value)) {
|
|
@@ -5766,6 +5899,35 @@ class Parser {
|
|
|
5766
5899
|
this.prattCache.set(cacheKey, { node: left, endPos: this.current });
|
|
5767
5900
|
return left;
|
|
5768
5901
|
}
|
|
5902
|
+
parseBeepExpression() {
|
|
5903
|
+
const beepToken = this.advance();
|
|
5904
|
+
this.advance();
|
|
5905
|
+
if (this.isAtEnd()) {
|
|
5906
|
+
this.addError("Expected expression after 'beep!'");
|
|
5907
|
+
return this.createErrorNode();
|
|
5908
|
+
}
|
|
5909
|
+
const operand = this.parseExpressionPratt(Parser.BEEP_OPERAND_BP);
|
|
5910
|
+
return {
|
|
5911
|
+
type: 'unaryExpression',
|
|
5912
|
+
operator: 'beep!',
|
|
5913
|
+
operand,
|
|
5914
|
+
argument: operand,
|
|
5915
|
+
prefix: true,
|
|
5916
|
+
start: beepToken.start,
|
|
5917
|
+
end: this.previous().end,
|
|
5918
|
+
line: beepToken.line,
|
|
5919
|
+
column: beepToken.column,
|
|
5920
|
+
};
|
|
5921
|
+
}
|
|
5922
|
+
isStyleRefStar(i) {
|
|
5923
|
+
const star = this.tokens[i];
|
|
5924
|
+
const name = this.tokens[i + 1];
|
|
5925
|
+
return star?.value === '*' && !!name && name.start === star.end && isIdentifierLike(name);
|
|
5926
|
+
}
|
|
5927
|
+
isSpacedStyleRef(i) {
|
|
5928
|
+
const prev = this.tokens[i - 1];
|
|
5929
|
+
return this.isStyleRefStar(i) && !!prev && prev.end < this.tokens[i].start;
|
|
5930
|
+
}
|
|
5769
5931
|
tryParseStringPostfix(left, nextToken) {
|
|
5770
5932
|
const v = nextToken.value;
|
|
5771
5933
|
let unit = null;
|
|
@@ -5968,6 +6130,10 @@ class Parser {
|
|
|
5968
6130
|
const isStop = () => this.check('end') || extraStops.some(s => this.check(s));
|
|
5969
6131
|
debug.parse('\ud83d\udd04 parseCommandListUntilTerminator: Starting (extraStops:', extraStops.join(','), ')');
|
|
5970
6132
|
while (!this.isAtEnd() && !isStop()) {
|
|
6133
|
+
if (this.checkComment()) {
|
|
6134
|
+
this.advance();
|
|
6135
|
+
continue;
|
|
6136
|
+
}
|
|
5971
6137
|
debug.parse('\ud83d\udccd Loop iteration, current token:', this.peek().value, 'kind:', this.peek().kind);
|
|
5972
6138
|
let parsedCommand = false;
|
|
5973
6139
|
const isCommandToken = this.checkIsCommand();
|
|
@@ -6051,7 +6217,7 @@ class Parser {
|
|
|
6051
6217
|
debug.parse('\u2705 Found "end", consuming it');
|
|
6052
6218
|
this.advance();
|
|
6053
6219
|
}
|
|
6054
|
-
else {
|
|
6220
|
+
else if (!this.atEndOfInput()) {
|
|
6055
6221
|
debug.parse('\u274c ERROR: Expected "end" but got:', this.peek().value, 'at position:', this.peek().start);
|
|
6056
6222
|
throw new Error(`Expected "end" to close ${construct} block`);
|
|
6057
6223
|
}
|
|
@@ -6064,7 +6230,7 @@ class Parser {
|
|
|
6064
6230
|
if (this.check('end')) {
|
|
6065
6231
|
this.advance();
|
|
6066
6232
|
}
|
|
6067
|
-
else {
|
|
6233
|
+
else if (!this.atEndOfInput()) {
|
|
6068
6234
|
throw new Error('Expected "end" to close repeat block');
|
|
6069
6235
|
}
|
|
6070
6236
|
}
|
|
@@ -6080,7 +6246,10 @@ class Parser {
|
|
|
6080
6246
|
this.advance();
|
|
6081
6247
|
}
|
|
6082
6248
|
else if (terminator === '') {
|
|
6083
|
-
|
|
6249
|
+
if (!this.atEndOfInput()) {
|
|
6250
|
+
throw new Error('Expected "end", "else", "until", or "while" to close repeat block');
|
|
6251
|
+
}
|
|
6252
|
+
return { commands, terminator: 'end' };
|
|
6084
6253
|
}
|
|
6085
6254
|
return { commands, terminator };
|
|
6086
6255
|
}
|
|
@@ -6664,7 +6833,9 @@ class Parser {
|
|
|
6664
6833
|
parseTopLevelInitBlock() {
|
|
6665
6834
|
const pos = this.getPosition();
|
|
6666
6835
|
const initCommands = this.parseCommandBlock(['end']);
|
|
6667
|
-
this.
|
|
6836
|
+
if (!this.match('end') && !this.atEndOfInput() && !this.checkFeatureStart()) {
|
|
6837
|
+
this.consume('end', "Expected 'end' after init block");
|
|
6838
|
+
}
|
|
6668
6839
|
return {
|
|
6669
6840
|
type: 'initBlock',
|
|
6670
6841
|
commands: initCommands,
|
|
@@ -6840,10 +7011,29 @@ class Parser {
|
|
|
6840
7011
|
...(sourceExpr.column !== undefined && { column: sourceExpr.column }),
|
|
6841
7012
|
};
|
|
6842
7013
|
}
|
|
7014
|
+
isArgumentListOnly(open) {
|
|
7015
|
+
let depth = 0;
|
|
7016
|
+
for (let i = open; i < this.tokens.length; i++) {
|
|
7017
|
+
const token = this.tokens[i];
|
|
7018
|
+
if (token.kind !== 'operator')
|
|
7019
|
+
continue;
|
|
7020
|
+
if (token.value === '(' || token.value === '[' || token.value === '{')
|
|
7021
|
+
depth++;
|
|
7022
|
+
else if (token.value === ')' || token.value === ']' || token.value === '}') {
|
|
7023
|
+
if (--depth === 0)
|
|
7024
|
+
return i === open + 1;
|
|
7025
|
+
}
|
|
7026
|
+
else if (token.value === ',' && depth === 1)
|
|
7027
|
+
return true;
|
|
7028
|
+
}
|
|
7029
|
+
return false;
|
|
7030
|
+
}
|
|
6843
7031
|
tryParsePseudoCommand(callExpr, sourceExpr) {
|
|
6844
7032
|
const methodName = callExpr.callee.name;
|
|
6845
7033
|
const nextToken = this.peek();
|
|
6846
|
-
const
|
|
7034
|
+
const nextIsArticle = nextToken.value.toLowerCase() === 'the';
|
|
7035
|
+
const hasPseudoCommandPattern = nextIsArticle ||
|
|
7036
|
+
Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(nextToken.value.toLowerCase()) ||
|
|
6847
7037
|
(isIdentifier(nextToken) && !this.isCommand(nextToken.value)) ||
|
|
6848
7038
|
isContextVar(nextToken);
|
|
6849
7039
|
if (!hasPseudoCommandPattern) {
|
|
@@ -6869,7 +7059,10 @@ class Parser {
|
|
|
6869
7059
|
});
|
|
6870
7060
|
}
|
|
6871
7061
|
let preposition;
|
|
6872
|
-
if (
|
|
7062
|
+
if (nextIsArticle) {
|
|
7063
|
+
this.advance();
|
|
7064
|
+
}
|
|
7065
|
+
else if (Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(this.peek().value.toLowerCase())) {
|
|
6873
7066
|
preposition = this.advance().value.toLowerCase();
|
|
6874
7067
|
}
|
|
6875
7068
|
let targetExpr;
|
|
@@ -7033,10 +7226,22 @@ class Parser {
|
|
|
7033
7226
|
this.consume(']', "Expected ']' after event condition");
|
|
7034
7227
|
}
|
|
7035
7228
|
let target;
|
|
7229
|
+
let targetExpression;
|
|
7036
7230
|
if (this.match('from')) {
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7231
|
+
if (this.check('the'))
|
|
7232
|
+
this.advance();
|
|
7233
|
+
if (Parser.POSITIONAL_SOURCE_HEADS.has(this.peek().value.toLowerCase())) {
|
|
7234
|
+
const startIndex = this.current;
|
|
7235
|
+
targetExpression = this.parseExpression();
|
|
7236
|
+
target = this.tokens
|
|
7237
|
+
.slice(startIndex, this.current)
|
|
7238
|
+
.map(t => t.value)
|
|
7239
|
+
.join(' ');
|
|
7240
|
+
}
|
|
7241
|
+
else {
|
|
7242
|
+
target = this.advance().value;
|
|
7243
|
+
}
|
|
7244
|
+
debug.parse(`\ud83d\udd27 parseEventHandler: Parsed 'from' target: ${target}`);
|
|
7040
7245
|
this.parseTemporalKeywordModifiers(modifiers);
|
|
7041
7246
|
}
|
|
7042
7247
|
let attributeName;
|
|
@@ -7082,7 +7287,7 @@ class Parser {
|
|
|
7082
7287
|
const nextIsOpenParen = this.tokens[this.current + 1]?.value === '(';
|
|
7083
7288
|
const commandName = this.peek().value.toLowerCase();
|
|
7084
7289
|
const isSpecialBodyCommand = commandName === 'js' || commandName === 'tell';
|
|
7085
|
-
if (nextIsOpenParen && !isSpecialBodyCommand) {
|
|
7290
|
+
if (nextIsOpenParen && !isSpecialBodyCommand && this.isArgumentListOnly(this.current + 1)) {
|
|
7086
7291
|
let expr;
|
|
7087
7292
|
const savedError = this.error;
|
|
7088
7293
|
try {
|
|
@@ -7114,7 +7319,7 @@ class Parser {
|
|
|
7114
7319
|
}
|
|
7115
7320
|
}
|
|
7116
7321
|
}
|
|
7117
|
-
else if (this.checkIdentifier()) {
|
|
7322
|
+
else if (this.checkIdentifier() || this.checkEventNamedCall()) {
|
|
7118
7323
|
const token = this.peek();
|
|
7119
7324
|
if (this.isCommand(token.value)) {
|
|
7120
7325
|
const cmd = this.parseCommandWithErrorRecovery();
|
|
@@ -7223,6 +7428,7 @@ class Parser {
|
|
|
7223
7428
|
...(eventParams.length > 0 && { args: eventParams }),
|
|
7224
7429
|
...(condition && { condition: fromLegacyExpression(condition) }),
|
|
7225
7430
|
...(target && { target }),
|
|
7431
|
+
...(targetExpression && { targetExpression: fromLegacyExpression(targetExpression) }),
|
|
7226
7432
|
...(attributeName && { attributeName }),
|
|
7227
7433
|
...(watchTarget && { watchTarget: fromLegacyExpression(watchTarget) }),
|
|
7228
7434
|
...(customEventSource && { customEventSource }),
|
|
@@ -7478,10 +7684,9 @@ class Parser {
|
|
|
7478
7684
|
}
|
|
7479
7685
|
parseCommandCore() {
|
|
7480
7686
|
const commandToken = this.previous();
|
|
7481
|
-
|
|
7687
|
+
const commandName = commandToken.value;
|
|
7482
7688
|
if (commandName === 'beep' && this.check('!')) {
|
|
7483
7689
|
this.advance();
|
|
7484
|
-
commandName = 'beep!';
|
|
7485
7690
|
}
|
|
7486
7691
|
if (commandName === 'fetch') {
|
|
7487
7692
|
return parseFetchCommand(this.getContext(), commandToken);
|
|
@@ -7580,6 +7785,9 @@ class Parser {
|
|
|
7580
7785
|
args.push(this.parsePrimary());
|
|
7581
7786
|
}
|
|
7582
7787
|
const callNode = this.createCallExpression(createIdentifier(funcName, namePos), args);
|
|
7788
|
+
if (funcName === 'closest' && args.length > 0 && this.match('to')) {
|
|
7789
|
+
callNode.closestTo = this.parseExpression();
|
|
7790
|
+
}
|
|
7583
7791
|
if (funcName === 'next' || funcName === 'previous') {
|
|
7584
7792
|
const relativePositional = this.parseRelativePositionalModifiers();
|
|
7585
7793
|
if (relativePositional) {
|
|
@@ -7619,6 +7827,9 @@ class Parser {
|
|
|
7619
7827
|
}
|
|
7620
7828
|
parseTheXofY() {
|
|
7621
7829
|
const positionalKeywords = ['first', 'last', 'next', 'previous', 'random', 'closest'];
|
|
7830
|
+
if (this.isStyleRefStar(this.current)) {
|
|
7831
|
+
return this.parsePrimary();
|
|
7832
|
+
}
|
|
7622
7833
|
if (!this.checkIdentifier()) {
|
|
7623
7834
|
return this.createIdentifier('the');
|
|
7624
7835
|
}
|
|
@@ -7634,7 +7845,7 @@ class Parser {
|
|
|
7634
7845
|
return this.createIdentifier(propertyName);
|
|
7635
7846
|
}
|
|
7636
7847
|
this.advance();
|
|
7637
|
-
const target = this.
|
|
7848
|
+
const target = parseOfOperand(this.makePrattContext(), THE_OF_TARGET_BP);
|
|
7638
7849
|
return {
|
|
7639
7850
|
type: 'propertyOfExpression',
|
|
7640
7851
|
property: { type: 'identifier', name: propertyName },
|
|
@@ -7832,6 +8043,13 @@ class Parser {
|
|
|
7832
8043
|
checkLiteral() {
|
|
7833
8044
|
return this.checkPredicate(isLiteral);
|
|
7834
8045
|
}
|
|
8046
|
+
checkEventNamedCall() {
|
|
8047
|
+
if (this.isAtEnd() || !this.checkIdentifierLike() || this.checkIsCommand())
|
|
8048
|
+
return false;
|
|
8049
|
+
const token = this.peek();
|
|
8050
|
+
const next = this.tokens[this.current + 1];
|
|
8051
|
+
return next?.value === '(' && next.start === token.end;
|
|
8052
|
+
}
|
|
7835
8053
|
checkIdentifierLike() {
|
|
7836
8054
|
return this.checkPredicate(isIdentifierLike);
|
|
7837
8055
|
}
|
|
@@ -7940,6 +8158,22 @@ class Parser {
|
|
|
7940
8158
|
isAtEnd() {
|
|
7941
8159
|
return this.current >= this.tokens.length;
|
|
7942
8160
|
}
|
|
8161
|
+
atEndOfInput() {
|
|
8162
|
+
for (let i = this.current; i < this.tokens.length; i++) {
|
|
8163
|
+
if (!isComment(this.tokens[i]))
|
|
8164
|
+
return false;
|
|
8165
|
+
}
|
|
8166
|
+
return true;
|
|
8167
|
+
}
|
|
8168
|
+
checkFeatureStart() {
|
|
8169
|
+
if (this.isAtEnd())
|
|
8170
|
+
return false;
|
|
8171
|
+
const value = this.peek().value;
|
|
8172
|
+
return (value === 'init' ||
|
|
8173
|
+
value === 'on' ||
|
|
8174
|
+
value === 'def' ||
|
|
8175
|
+
getRegisteredFeature(value) !== undefined);
|
|
8176
|
+
}
|
|
7943
8177
|
peek() {
|
|
7944
8178
|
if (this.isAtEnd()) {
|
|
7945
8179
|
return { kind: 'unknown', value: '', start: 0, end: 0, line: 1, column: 1 };
|
|
@@ -8151,6 +8385,7 @@ class Parser {
|
|
|
8151
8385
|
match: this.match.bind(this),
|
|
8152
8386
|
matchOperator: this.matchOperator.bind(this),
|
|
8153
8387
|
isAtEnd: this.isAtEnd.bind(this),
|
|
8388
|
+
atEndOfInput: this.atEndOfInput.bind(this),
|
|
8154
8389
|
createIdentifier: this.createIdentifier.bind(this),
|
|
8155
8390
|
parseExpression: this.parseExpression.bind(this),
|
|
8156
8391
|
parsePrimary: this.parsePrimary.bind(this),
|
|
@@ -8201,7 +8436,15 @@ Parser.POSTFIX_UNARY_OPERATORS = new Set([
|
|
|
8201
8436
|
'ignoring case',
|
|
8202
8437
|
]);
|
|
8203
8438
|
Parser.PRATT_TABLE = PARSER_TABLE;
|
|
8439
|
+
Parser.BEEP_OPERAND_BP = 80;
|
|
8204
8440
|
Parser.PSEUDO_COMMAND_PREPOSITIONS = ['from', 'on', 'with', 'into', 'at', 'to'];
|
|
8441
|
+
Parser.POSITIONAL_SOURCE_HEADS = new Set([
|
|
8442
|
+
'closest',
|
|
8443
|
+
'next',
|
|
8444
|
+
'previous',
|
|
8445
|
+
'first',
|
|
8446
|
+
'last',
|
|
8447
|
+
]);
|
|
8205
8448
|
function parse(input, options) {
|
|
8206
8449
|
const tokens = tokenize(input);
|
|
8207
8450
|
const parser = new Parser(tokens, options, input);
|
|
@@ -8406,6 +8649,89 @@ function isDocumentFragment(value) {
|
|
|
8406
8649
|
'childNodes' in value);
|
|
8407
8650
|
}
|
|
8408
8651
|
|
|
8652
|
+
function beepValues(me, values) {
|
|
8653
|
+
const outputs = values.map(describeBeepValue);
|
|
8654
|
+
const shown = outputs.filter(output => announce(me, output.value));
|
|
8655
|
+
if (shown.length === 0)
|
|
8656
|
+
return outputs;
|
|
8657
|
+
console.group('\ud83d\udd14 beep! Debug Output');
|
|
8658
|
+
for (const output of shown) {
|
|
8659
|
+
console.log(`Value:`, output.value);
|
|
8660
|
+
console.log(`Type:`, output.type);
|
|
8661
|
+
console.log(`Representation:`, output.representation);
|
|
8662
|
+
console.log('---');
|
|
8663
|
+
}
|
|
8664
|
+
console.groupEnd();
|
|
8665
|
+
return outputs;
|
|
8666
|
+
}
|
|
8667
|
+
function announce(me, value) {
|
|
8668
|
+
const target = me;
|
|
8669
|
+
if (typeof CustomEvent !== 'function' || typeof target?.dispatchEvent !== 'function')
|
|
8670
|
+
return true;
|
|
8671
|
+
return target.dispatchEvent(new CustomEvent('hyperscript:beep', {
|
|
8672
|
+
bubbles: true,
|
|
8673
|
+
cancelable: true,
|
|
8674
|
+
detail: { element: me, value },
|
|
8675
|
+
}));
|
|
8676
|
+
}
|
|
8677
|
+
function describeBeepValue(value) {
|
|
8678
|
+
return { value, type: typeOf(value), representation: representationOf(value) };
|
|
8679
|
+
}
|
|
8680
|
+
function typeOf(value) {
|
|
8681
|
+
if (value === null)
|
|
8682
|
+
return 'null';
|
|
8683
|
+
if (value === undefined)
|
|
8684
|
+
return 'undefined';
|
|
8685
|
+
if (Array.isArray(value))
|
|
8686
|
+
return 'array';
|
|
8687
|
+
if (isHTMLElement$2(value))
|
|
8688
|
+
return 'HTMLElement';
|
|
8689
|
+
if (typeof Element !== 'undefined' && value instanceof Element)
|
|
8690
|
+
return 'Element';
|
|
8691
|
+
if (typeof Node !== 'undefined' && value instanceof Node)
|
|
8692
|
+
return 'Node';
|
|
8693
|
+
if (value instanceof Error)
|
|
8694
|
+
return 'Error';
|
|
8695
|
+
if (value instanceof Date)
|
|
8696
|
+
return 'Date';
|
|
8697
|
+
if (value instanceof RegExp)
|
|
8698
|
+
return 'RegExp';
|
|
8699
|
+
return typeof value;
|
|
8700
|
+
}
|
|
8701
|
+
function representationOf(value) {
|
|
8702
|
+
if (value === null)
|
|
8703
|
+
return 'null';
|
|
8704
|
+
if (value === undefined)
|
|
8705
|
+
return 'undefined';
|
|
8706
|
+
if (Array.isArray(value)) {
|
|
8707
|
+
return `Array(${value.length}) [${value
|
|
8708
|
+
.slice(0, 3)
|
|
8709
|
+
.map(v => representationOf(v))
|
|
8710
|
+
.join(', ')}${value.length > 3 ? '...' : ''}]`;
|
|
8711
|
+
}
|
|
8712
|
+
if (isHTMLElement$2(value)) {
|
|
8713
|
+
const el = value;
|
|
8714
|
+
const tag = el.tagName.toLowerCase();
|
|
8715
|
+
const id = el.id ? `#${el.id}` : '';
|
|
8716
|
+
const classes = el.className ? `.${el.className.split(' ').join('.')}` : '';
|
|
8717
|
+
return `<${tag}${id}${classes}/>`;
|
|
8718
|
+
}
|
|
8719
|
+
if (value instanceof Error)
|
|
8720
|
+
return `Error: ${value.message}`;
|
|
8721
|
+
if (typeof value === 'string')
|
|
8722
|
+
return value.length > 50 ? `"${value.substring(0, 47)}..."` : `"${value}"`;
|
|
8723
|
+
if (typeof value === 'object') {
|
|
8724
|
+
try {
|
|
8725
|
+
const keys = Object.keys(value);
|
|
8726
|
+
return `Object {${keys.slice(0, 3).join(', ')}${keys.length > 3 ? '...' : ''}}`;
|
|
8727
|
+
}
|
|
8728
|
+
catch {
|
|
8729
|
+
return '[Object]';
|
|
8730
|
+
}
|
|
8731
|
+
}
|
|
8732
|
+
return String(value);
|
|
8733
|
+
}
|
|
8734
|
+
|
|
8409
8735
|
function convertToNumber(value) {
|
|
8410
8736
|
if (value === null || value === undefined) {
|
|
8411
8737
|
return 0;
|
|
@@ -8437,6 +8763,16 @@ function convertToNumber(value) {
|
|
|
8437
8763
|
}
|
|
8438
8764
|
return 0;
|
|
8439
8765
|
}
|
|
8766
|
+
function counterAmount(value) {
|
|
8767
|
+
return typeof value === 'number' ? value : parseFloat(String(value));
|
|
8768
|
+
}
|
|
8769
|
+
function counterValue(value) {
|
|
8770
|
+
if (isHTMLElement$2(value)) {
|
|
8771
|
+
const element = value;
|
|
8772
|
+
return counterValue(element.value || element.textContent);
|
|
8773
|
+
}
|
|
8774
|
+
return value ? counterAmount(value) : 0;
|
|
8775
|
+
}
|
|
8440
8776
|
function getVariableValue(name, context, preferredScope) {
|
|
8441
8777
|
if (preferredScope === 'element') {
|
|
8442
8778
|
return getElementVar(context, name);
|
|
@@ -8541,35 +8877,39 @@ function setElementPropertyValue(propertyPath, value, context) {
|
|
|
8541
8877
|
element[property] = value;
|
|
8542
8878
|
}
|
|
8543
8879
|
}
|
|
8880
|
+
function isObjectOwner(value) {
|
|
8881
|
+
return (typeof value === 'object' && value !== null && !Array.isArray(value) && !isHTMLElement$2(value));
|
|
8882
|
+
}
|
|
8544
8883
|
function getCurrentNumericValue(target, property, scope, context) {
|
|
8545
8884
|
if (typeof target === 'number') {
|
|
8546
8885
|
return target;
|
|
8547
8886
|
}
|
|
8548
8887
|
if (isHTMLElement$2(target)) {
|
|
8549
8888
|
const element = target;
|
|
8889
|
+
if (property?.startsWith('@'))
|
|
8890
|
+
return counterValue(element.getAttribute(property.slice(1)));
|
|
8550
8891
|
if (property) {
|
|
8551
8892
|
if (property.startsWith('data-') ||
|
|
8552
8893
|
['id', 'class', 'title', 'alt', 'src', 'href'].includes(property)) {
|
|
8553
|
-
|
|
8554
|
-
return convertToNumber(value);
|
|
8894
|
+
return counterValue(element.getAttribute(property));
|
|
8555
8895
|
}
|
|
8556
8896
|
else {
|
|
8557
|
-
|
|
8558
|
-
return convertToNumber(value);
|
|
8897
|
+
return counterValue(element[property]);
|
|
8559
8898
|
}
|
|
8560
8899
|
}
|
|
8561
8900
|
else {
|
|
8562
|
-
|
|
8563
|
-
return convertToNumber(value);
|
|
8901
|
+
return counterValue(element);
|
|
8564
8902
|
}
|
|
8565
8903
|
}
|
|
8904
|
+
if (property && isObjectOwner(target)) {
|
|
8905
|
+
return counterValue(target[property]);
|
|
8906
|
+
}
|
|
8566
8907
|
if (typeof target === 'string') {
|
|
8567
8908
|
if (scope === 'element') {
|
|
8568
|
-
return
|
|
8909
|
+
return counterValue(getVariableValue(target, context, 'element'));
|
|
8569
8910
|
}
|
|
8570
8911
|
if (scope === 'global') {
|
|
8571
|
-
|
|
8572
|
-
return convertToNumber(value);
|
|
8912
|
+
return counterValue(getVariableValue(target, context, 'global'));
|
|
8573
8913
|
}
|
|
8574
8914
|
if (target.includes('.')) {
|
|
8575
8915
|
return getElementPropertyValue(target, context);
|
|
@@ -8583,14 +8923,17 @@ function getCurrentNumericValue(target, property, scope, context) {
|
|
|
8583
8923
|
else if (target === 'you' && context.you) {
|
|
8584
8924
|
return convertToNumber(context.you.value || 0);
|
|
8585
8925
|
}
|
|
8586
|
-
|
|
8587
|
-
return convertToNumber(value);
|
|
8926
|
+
return counterValue(getVariableValue(target, context));
|
|
8588
8927
|
}
|
|
8589
|
-
return
|
|
8928
|
+
return counterValue(target);
|
|
8590
8929
|
}
|
|
8591
8930
|
function setTargetValue(target, property, scope, newValue, context) {
|
|
8592
8931
|
if (isHTMLElement$2(target)) {
|
|
8593
8932
|
const element = target;
|
|
8933
|
+
if (property?.startsWith('@')) {
|
|
8934
|
+
element.setAttribute(property.slice(1), String(newValue));
|
|
8935
|
+
return;
|
|
8936
|
+
}
|
|
8594
8937
|
if (property) {
|
|
8595
8938
|
if (property.startsWith('data-') ||
|
|
8596
8939
|
['id', 'class', 'title', 'alt', 'src', 'href'].includes(property)) {
|
|
@@ -8610,6 +8953,10 @@ function setTargetValue(target, property, scope, newValue, context) {
|
|
|
8610
8953
|
}
|
|
8611
8954
|
return;
|
|
8612
8955
|
}
|
|
8956
|
+
if (property && isObjectOwner(target)) {
|
|
8957
|
+
target[property] = newValue;
|
|
8958
|
+
return;
|
|
8959
|
+
}
|
|
8613
8960
|
if (typeof target === 'string') {
|
|
8614
8961
|
if (scope === 'element') {
|
|
8615
8962
|
setVariableValue(target, newValue, context, 'element');
|
|
@@ -9038,13 +9385,6 @@ function isFunction(value) {
|
|
|
9038
9385
|
function isInputElement$1(el) {
|
|
9039
9386
|
return (el !== null && typeof el === 'object' && 'tagName' in el && el.tagName === 'INPUT');
|
|
9040
9387
|
}
|
|
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
9388
|
function isHTMLElement$1(el) {
|
|
9049
9389
|
return (el !== null &&
|
|
9050
9390
|
typeof el === 'object' &&
|
|
@@ -9104,7 +9444,7 @@ const SPECIAL_DOM_PROPERTIES = {
|
|
|
9104
9444
|
innertext: el => el.textContent?.trim(),
|
|
9105
9445
|
innerHTML: el => el.innerHTML,
|
|
9106
9446
|
outerhtml: el => el.outerHTML,
|
|
9107
|
-
value: el => (
|
|
9447
|
+
value: el => ('value' in el ? el.value : undefined),
|
|
9108
9448
|
checked: el => (isInputElement$1(el) ? el.checked : undefined),
|
|
9109
9449
|
disabled: el => ('disabled' in el ? el.disabled : undefined),
|
|
9110
9450
|
selected: el => (isOptionElement$1(el) ? el.selected : undefined),
|
|
@@ -9772,14 +10112,15 @@ const closestExpression = {
|
|
|
9772
10112
|
if (typeof selector !== 'string') {
|
|
9773
10113
|
throw new Error('closest requires a string selector');
|
|
9774
10114
|
}
|
|
9775
|
-
|
|
10115
|
+
const start = args.length > 1 ? args[1] : context.me;
|
|
10116
|
+
if (!(start instanceof Element)) {
|
|
9776
10117
|
return null;
|
|
9777
10118
|
}
|
|
9778
|
-
return
|
|
10119
|
+
return start.closest(selector);
|
|
9779
10120
|
},
|
|
9780
10121
|
validate(args) {
|
|
9781
|
-
if (args.length !== 1) {
|
|
9782
|
-
return 'closest requires
|
|
10122
|
+
if (args.length !== 1 && args.length !== 2) {
|
|
10123
|
+
return 'closest requires a selector and, optionally, the element to start from';
|
|
9783
10124
|
}
|
|
9784
10125
|
const arg = args[0];
|
|
9785
10126
|
if (typeof arg === 'string')
|
|
@@ -11279,7 +11620,7 @@ const isEmptyExpression = {
|
|
|
11279
11620
|
else if (value instanceof Node || value instanceof Element)
|
|
11280
11621
|
result = false;
|
|
11281
11622
|
else if (isObject(value))
|
|
11282
|
-
result =
|
|
11623
|
+
result = value.length === 0;
|
|
11283
11624
|
else
|
|
11284
11625
|
result = false;
|
|
11285
11626
|
return result;
|
|
@@ -12403,11 +12744,16 @@ async function evaluateBinaryExpression(node, context) {
|
|
|
12403
12744
|
}
|
|
12404
12745
|
}
|
|
12405
12746
|
}
|
|
12406
|
-
if (leftNode?.type === 'selector' &&
|
|
12747
|
+
if (leftNode?.type === 'selector' &&
|
|
12748
|
+
typeof leftNode.value === 'string' &&
|
|
12749
|
+
(leftNode.fromQuery || (operator === 'in' && leftNode.value.startsWith('.')))) {
|
|
12407
12750
|
const root = await evaluateAST(node.right, context);
|
|
12408
12751
|
const scope = root && typeof root.querySelectorAll === 'function' ? root : null;
|
|
12409
12752
|
if (scope) {
|
|
12410
|
-
|
|
12753
|
+
const query = leftNode.fromQuery
|
|
12754
|
+
? leftNode.value
|
|
12755
|
+
: escapeSelectorForQuery(leftNode, leftNode.value);
|
|
12756
|
+
return Array.from(scope.querySelectorAll(query));
|
|
12411
12757
|
}
|
|
12412
12758
|
}
|
|
12413
12759
|
}
|
|
@@ -12419,6 +12765,12 @@ async function evaluateBinaryExpression(node, context) {
|
|
|
12419
12765
|
return getExpr(context, 'styleRef').evaluate(context, leftNode.value.slice(1), el);
|
|
12420
12766
|
}
|
|
12421
12767
|
if (operator === 'of') {
|
|
12768
|
+
if (leftNode?.type === 'attributeAccess') {
|
|
12769
|
+
const target = await evaluateAST(node.right, context);
|
|
12770
|
+
if (target == null)
|
|
12771
|
+
return undefined;
|
|
12772
|
+
return getExpr(context, 'possessive').evaluate(context, target, `@${leftNode.attributeName}`);
|
|
12773
|
+
}
|
|
12422
12774
|
const path = propertyPathOf(node.left);
|
|
12423
12775
|
if (path) {
|
|
12424
12776
|
const target = await evaluateAST(node.right, context);
|
|
@@ -12426,6 +12778,10 @@ async function evaluateBinaryExpression(node, context) {
|
|
|
12426
12778
|
for (const seg of path) {
|
|
12427
12779
|
if (cur == null)
|
|
12428
12780
|
return undefined;
|
|
12781
|
+
if (isMappableCollection(cur) && !(seg in cur)) {
|
|
12782
|
+
cur = await getExpr(context, 'possessive').evaluate(context, cur, seg);
|
|
12783
|
+
continue;
|
|
12784
|
+
}
|
|
12429
12785
|
cur = isElement(cur) ? getElementProperty$1(cur, seg) : cur[seg];
|
|
12430
12786
|
}
|
|
12431
12787
|
return typeof cur === 'function' ? cur.bind(target) : cur;
|
|
@@ -12450,26 +12806,21 @@ async function evaluateBinaryExpression(node, context) {
|
|
|
12450
12806
|
const R = node.ignoringCase ? applyCI(right) : right;
|
|
12451
12807
|
switch (operator) {
|
|
12452
12808
|
case '+': {
|
|
12453
|
-
let addLeft = left;
|
|
12454
|
-
let addRight = right;
|
|
12455
12809
|
if (node.coerceNumeric) {
|
|
12456
|
-
|
|
12457
|
-
addLeft = convertToNumber(addLeft);
|
|
12458
|
-
if (typeof addRight === 'string')
|
|
12459
|
-
addRight = convertToNumber(addRight);
|
|
12810
|
+
return counterValue(left) + counterAmount(right);
|
|
12460
12811
|
}
|
|
12461
|
-
if (typeof
|
|
12462
|
-
return String(
|
|
12812
|
+
if (typeof left === 'string' || typeof right === 'string') {
|
|
12813
|
+
return String(left ?? '') + String(right ?? '');
|
|
12463
12814
|
}
|
|
12464
|
-
if (Array.isArray(
|
|
12465
|
-
return Array.isArray(
|
|
12815
|
+
if (Array.isArray(left)) {
|
|
12816
|
+
return Array.isArray(right) ? [...left, ...right] : [...left, right];
|
|
12466
12817
|
}
|
|
12467
|
-
return unwrapTypedResult(await getExpr(context, 'addition').evaluate(context, {
|
|
12468
|
-
left: addLeft,
|
|
12469
|
-
right: addRight,
|
|
12470
|
-
}));
|
|
12818
|
+
return unwrapTypedResult(await getExpr(context, 'addition').evaluate(context, { left, right }));
|
|
12471
12819
|
}
|
|
12472
12820
|
case '-':
|
|
12821
|
+
if (node.coerceNumeric) {
|
|
12822
|
+
return counterValue(left) - counterAmount(right);
|
|
12823
|
+
}
|
|
12473
12824
|
return unwrapTypedResult(await getExpr(context, 'subtraction').evaluate(context, { left, right }));
|
|
12474
12825
|
case '*':
|
|
12475
12826
|
return unwrapTypedResult(await getExpr(context, 'multiplication').evaluate(context, { left, right }));
|
|
@@ -12727,9 +13078,8 @@ async function evaluatePropertyOfExpressionNode(node, context) {
|
|
|
12727
13078
|
}
|
|
12728
13079
|
const propertyName = propertyNode.name;
|
|
12729
13080
|
const target = await evaluateAST(node.target, context);
|
|
12730
|
-
if (target == null)
|
|
12731
|
-
|
|
12732
|
-
}
|
|
13081
|
+
if (target == null)
|
|
13082
|
+
return undefined;
|
|
12733
13083
|
return getExpr(context, 'possessive').evaluate(context, target, propertyName);
|
|
12734
13084
|
}
|
|
12735
13085
|
async function evaluateTemplateLiteralNode(node, context) {
|
|
@@ -12847,6 +13197,9 @@ async function evaluateUnaryExpression(node, context) {
|
|
|
12847
13197
|
case 'not':
|
|
12848
13198
|
case '!':
|
|
12849
13199
|
return logicalExpressions.not.evaluate(context, value);
|
|
13200
|
+
case 'beep!':
|
|
13201
|
+
beepValues(context.me, [value]);
|
|
13202
|
+
return value;
|
|
12850
13203
|
case 'no':
|
|
12851
13204
|
return logicalExpressions.no.evaluate(context, value);
|
|
12852
13205
|
case '-':
|
|
@@ -12936,6 +13289,10 @@ async function evaluateCallExpression(node, context) {
|
|
|
12936
13289
|
const args = await resolveCallArgs(node.arguments, funcName, context);
|
|
12937
13290
|
switch (funcName) {
|
|
12938
13291
|
case 'closest':
|
|
13292
|
+
if (node.closestTo) {
|
|
13293
|
+
const start = unwrapElement(await evaluateAST(node.closestTo, context)) ?? null;
|
|
13294
|
+
return referencesExpressions.closest.evaluate(context, args[0], start);
|
|
13295
|
+
}
|
|
12939
13296
|
return referencesExpressions.closest.evaluate(context, ...args);
|
|
12940
13297
|
case 'previous':
|
|
12941
13298
|
case 'next': {
|
|
@@ -14322,12 +14679,23 @@ class RuntimeBase {
|
|
|
14322
14679
|
debug.runtime(`BEHAVIOR: Finished installing ${behaviorName}`);
|
|
14323
14680
|
}
|
|
14324
14681
|
async executeEventHandler(node, context) {
|
|
14325
|
-
const { event, events, commands, target, args, selector, condition, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler, } = node;
|
|
14682
|
+
const { event, events, commands, target, targetExpression, args, selector, condition, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler, } = node;
|
|
14326
14683
|
const eventNames = events && events.length > 0 ? events : [event];
|
|
14327
14684
|
debug.runtime(`BEHAVIOR: executeEventHandler: event='${event}', target='${target}'`);
|
|
14328
14685
|
let targets = [];
|
|
14329
14686
|
let globalTarget = null;
|
|
14330
|
-
if (
|
|
14687
|
+
if (targetExpression) {
|
|
14688
|
+
const resolved = await this.execute(targetExpression, context);
|
|
14689
|
+
if (resolved === window)
|
|
14690
|
+
globalTarget = window;
|
|
14691
|
+
else if (resolved === document)
|
|
14692
|
+
globalTarget = document;
|
|
14693
|
+
else if (this.isElement(resolved))
|
|
14694
|
+
targets = [resolved];
|
|
14695
|
+
else if (Array.isArray(resolved))
|
|
14696
|
+
targets = resolved.filter(el => this.isElement(el));
|
|
14697
|
+
}
|
|
14698
|
+
else if (target) {
|
|
14331
14699
|
const targetLower = typeof target === 'string' ? target.toLowerCase() : '';
|
|
14332
14700
|
if (targetLower === 'window' || targetLower === 'the window') {
|
|
14333
14701
|
globalTarget = window;
|
|
@@ -16348,14 +16716,83 @@ function parseAttribute(expression) {
|
|
|
16348
16716
|
if (trimmed.startsWith('@')) {
|
|
16349
16717
|
return { name: trimmed.substring(1).trim() };
|
|
16350
16718
|
}
|
|
16351
|
-
throw new Error(`Invalid attribute syntax: ${expression}`);
|
|
16352
|
-
}
|
|
16353
|
-
function parseAttributeName(expression) {
|
|
16354
|
-
return parseAttribute(expression).name;
|
|
16719
|
+
throw new Error(`Invalid attribute syntax: ${expression}`);
|
|
16720
|
+
}
|
|
16721
|
+
function parseAttributeName(expression) {
|
|
16722
|
+
return parseAttribute(expression).name;
|
|
16723
|
+
}
|
|
16724
|
+
function parseAttributeWithValue(expression) {
|
|
16725
|
+
const result = parseAttribute(expression);
|
|
16726
|
+
return { name: result.name, value: result.value ?? '' };
|
|
16727
|
+
}
|
|
16728
|
+
|
|
16729
|
+
function _parseDurationComponents(value) {
|
|
16730
|
+
const trimmed = value.trim();
|
|
16731
|
+
const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*(s|ms|sec|seconds?|milliseconds?)?$/i);
|
|
16732
|
+
if (!match) {
|
|
16733
|
+
return null;
|
|
16734
|
+
}
|
|
16735
|
+
const number = parseFloat(match[1]);
|
|
16736
|
+
const unit = (match[2] || 'ms').toLowerCase();
|
|
16737
|
+
if (unit === 'ms' || unit === 'millisecond' || unit === 'milliseconds') {
|
|
16738
|
+
return { number, milliseconds: Math.floor(number) };
|
|
16739
|
+
}
|
|
16740
|
+
else if (unit === 's' || unit === 'sec' || unit === 'second' || unit === 'seconds') {
|
|
16741
|
+
return { number, milliseconds: Math.floor(number * 1000) };
|
|
16742
|
+
}
|
|
16743
|
+
return null;
|
|
16744
|
+
}
|
|
16745
|
+
function parseDuration(value, defaultMs = 300) {
|
|
16746
|
+
if (typeof value === 'number') {
|
|
16747
|
+
return Math.max(0, Math.floor(value));
|
|
16748
|
+
}
|
|
16749
|
+
if (typeof value === 'string') {
|
|
16750
|
+
const result = _parseDurationComponents(value);
|
|
16751
|
+
return result ? result.milliseconds : defaultMs;
|
|
16752
|
+
}
|
|
16753
|
+
return defaultMs;
|
|
16754
|
+
}
|
|
16755
|
+
function parseDurationStrict(value) {
|
|
16756
|
+
if (typeof value === 'number') {
|
|
16757
|
+
if (value < 0)
|
|
16758
|
+
throw new Error('Duration must be >= 0');
|
|
16759
|
+
return Math.floor(value);
|
|
16760
|
+
}
|
|
16761
|
+
if (typeof value === 'string') {
|
|
16762
|
+
const result = _parseDurationComponents(value);
|
|
16763
|
+
if (!result) {
|
|
16764
|
+
throw new Error(`Invalid duration format: "${value}"`);
|
|
16765
|
+
}
|
|
16766
|
+
return result.milliseconds;
|
|
16767
|
+
}
|
|
16768
|
+
throw new Error(`Invalid duration type: ${typeof value}`);
|
|
16769
|
+
}
|
|
16770
|
+
function parseCSSDurations(durationString) {
|
|
16771
|
+
if (!durationString || durationString === 'none') {
|
|
16772
|
+
return [0];
|
|
16773
|
+
}
|
|
16774
|
+
return durationString.split(',').map(duration => {
|
|
16775
|
+
const value = parseFloat(duration.trim());
|
|
16776
|
+
if (isNaN(value))
|
|
16777
|
+
return 0;
|
|
16778
|
+
if (duration.includes('s') && !duration.includes('ms')) {
|
|
16779
|
+
return value * 1000;
|
|
16780
|
+
}
|
|
16781
|
+
return value;
|
|
16782
|
+
});
|
|
16783
|
+
}
|
|
16784
|
+
function calculateMaxAnimationTime(durations, delays) {
|
|
16785
|
+
let maxTime = 0;
|
|
16786
|
+
for (let i = 0; i < durations.length; i++) {
|
|
16787
|
+
const duration = durations[i] || 0;
|
|
16788
|
+
const delay = delays[i] || 0;
|
|
16789
|
+
const totalTime = duration + delay;
|
|
16790
|
+
maxTime = Math.max(maxTime, totalTime);
|
|
16791
|
+
}
|
|
16792
|
+
return maxTime;
|
|
16355
16793
|
}
|
|
16356
|
-
function
|
|
16357
|
-
|
|
16358
|
-
return { name: result.name, value: result.value ?? '' };
|
|
16794
|
+
function camelToKebab(str) {
|
|
16795
|
+
return str.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
16359
16796
|
}
|
|
16360
16797
|
|
|
16361
16798
|
function batchApply(targets, operation) {
|
|
@@ -16423,15 +16860,18 @@ function toggleAttribute(element, name, value) {
|
|
|
16423
16860
|
}
|
|
16424
16861
|
}
|
|
16425
16862
|
}
|
|
16863
|
+
function toCssPropertyName(property) {
|
|
16864
|
+
return property.startsWith('--') ? property : camelToKebab(property);
|
|
16865
|
+
}
|
|
16426
16866
|
function batchSetStyles(targets, styles) {
|
|
16427
16867
|
const entries = Object.entries(styles);
|
|
16428
16868
|
return batchApplyItems(targets, entries, (element, [property, value]) => {
|
|
16429
|
-
element.style.setProperty(property, value);
|
|
16869
|
+
element.style.setProperty(toCssPropertyName(property), value);
|
|
16430
16870
|
});
|
|
16431
16871
|
}
|
|
16432
16872
|
function batchRemoveStyles(targets, properties) {
|
|
16433
16873
|
return batchApplyItems(targets, properties, (element, property) => {
|
|
16434
|
-
element.style.removeProperty(property);
|
|
16874
|
+
element.style.removeProperty(toCssPropertyName(property));
|
|
16435
16875
|
});
|
|
16436
16876
|
}
|
|
16437
16877
|
|
|
@@ -17097,75 +17537,6 @@ let RemoveCommand = (() => {
|
|
|
17097
17537
|
})();
|
|
17098
17538
|
const createRemoveCommand = createFactory(RemoveCommand);
|
|
17099
17539
|
|
|
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
17540
|
function detectSmartElementType(elements) {
|
|
17170
17541
|
if (elements.length === 0)
|
|
17171
17542
|
return null;
|
|
@@ -18035,7 +18406,11 @@ let PutCommand = (() => {
|
|
|
18035
18406
|
}
|
|
18036
18407
|
if (isInsertableNode(v))
|
|
18037
18408
|
return v;
|
|
18038
|
-
|
|
18409
|
+
if (v == null)
|
|
18410
|
+
return 'null';
|
|
18411
|
+
if (Array.isArray(v))
|
|
18412
|
+
return arrayFragment(v);
|
|
18413
|
+
return String(v);
|
|
18039
18414
|
}
|
|
18040
18415
|
looksLikeCss(s) {
|
|
18041
18416
|
if (!s)
|
|
@@ -18120,6 +18495,19 @@ let PutCommand = (() => {
|
|
|
18120
18495
|
})();
|
|
18121
18496
|
return PutCommand = _classThis;
|
|
18122
18497
|
})();
|
|
18498
|
+
function arrayFragment(items) {
|
|
18499
|
+
const fragment = document.createDocumentFragment();
|
|
18500
|
+
for (const item of items) {
|
|
18501
|
+
if (isInsertableNode(item)) {
|
|
18502
|
+
fragment.append(item);
|
|
18503
|
+
continue;
|
|
18504
|
+
}
|
|
18505
|
+
const template = document.createElement('template');
|
|
18506
|
+
template.innerHTML = item === null ? '' : String(item);
|
|
18507
|
+
fragment.append(template.content);
|
|
18508
|
+
}
|
|
18509
|
+
return fragment;
|
|
18510
|
+
}
|
|
18123
18511
|
const createPutCommand = createFactory(PutCommand);
|
|
18124
18512
|
|
|
18125
18513
|
const HTML_ESCAPE = {
|
|
@@ -19337,10 +19725,29 @@ const STRATEGY_KEYWORDS = {
|
|
|
19337
19725
|
innermorph: 'morph',
|
|
19338
19726
|
outermorph: 'morphOuter',
|
|
19339
19727
|
};
|
|
19728
|
+
function loneRootWithTag(content, tagName) {
|
|
19729
|
+
let root;
|
|
19730
|
+
if (typeof content === 'string') {
|
|
19731
|
+
const template = document.createElement('template');
|
|
19732
|
+
template.innerHTML = content;
|
|
19733
|
+
root = template.content.firstElementChild;
|
|
19734
|
+
if (!root || root.nextElementSibling)
|
|
19735
|
+
return null;
|
|
19736
|
+
}
|
|
19737
|
+
else {
|
|
19738
|
+
root = content.cloneNode(true);
|
|
19739
|
+
}
|
|
19740
|
+
return root.tagName === tagName && isHTMLElement$2(root) ? root : null;
|
|
19741
|
+
}
|
|
19340
19742
|
function executeSwap(target, content, strategy, morphOptions) {
|
|
19341
19743
|
const contentStr = content !== null && !isHTMLElement$2(content) ? content : '';
|
|
19342
19744
|
const contentEl = isHTMLElement$2(content) ? content : null;
|
|
19343
19745
|
switch (strategy) {
|
|
19746
|
+
case 'morphMerge': {
|
|
19747
|
+
const root = content !== null ? loneRootWithTag(content, target.tagName) : null;
|
|
19748
|
+
executeSwap(target, root ?? content, root ? 'morphOuter' : 'morph', morphOptions);
|
|
19749
|
+
break;
|
|
19750
|
+
}
|
|
19344
19751
|
case 'morph':
|
|
19345
19752
|
if (content !== null) {
|
|
19346
19753
|
try {
|
|
@@ -19630,7 +20037,7 @@ let MorphCommand = (() => {
|
|
|
19630
20037
|
if (args.length === 0 && modifiers.with === undefined) {
|
|
19631
20038
|
throw new Error('[HyperFixi] morph: command requires arguments');
|
|
19632
20039
|
}
|
|
19633
|
-
const strategy = literalText(modifiers.strategy)?.toLowerCase() === 'over' ? 'morphOuter' : '
|
|
20040
|
+
const strategy = literalText(modifiers.strategy)?.toLowerCase() === 'over' ? 'morphOuter' : 'morphMerge';
|
|
19634
20041
|
const useViewTransition = modifiers.viewTransition !== undefined;
|
|
19635
20042
|
const targetNode = modifiers.on !== undefined ? modifiers.on : args[0];
|
|
19636
20043
|
const contentNode = modifiers.with !== undefined
|
|
@@ -20936,6 +21343,19 @@ async function resolveWriteTarget(node, evaluator, context, options) {
|
|
|
20936
21343
|
}
|
|
20937
21344
|
return { kind: 'property', target: property };
|
|
20938
21345
|
}
|
|
21346
|
+
if (options.ofAndObjectProperty) {
|
|
21347
|
+
const access = ofOrPossessiveAccess(n);
|
|
21348
|
+
if (access) {
|
|
21349
|
+
const owner = await evaluator.evaluate(access.owner, context);
|
|
21350
|
+
const element = Array.isArray(owner) ? owner[0] : owner;
|
|
21351
|
+
if (access.isOf && isHTMLElement$2(element)) {
|
|
21352
|
+
return { kind: 'property', target: { element, property: access.name } };
|
|
21353
|
+
}
|
|
21354
|
+
if (isObjectOwner(owner)) {
|
|
21355
|
+
return { kind: 'member', container: owner, property: access.name };
|
|
21356
|
+
}
|
|
21357
|
+
}
|
|
21358
|
+
}
|
|
20939
21359
|
if (options.bareReference &&
|
|
20940
21360
|
(nodeType === 'identifier' || nodeType === 'variable' || nodeType === 'symbol') &&
|
|
20941
21361
|
typeof n.name === 'string') {
|
|
@@ -20945,6 +21365,19 @@ async function resolveWriteTarget(node, evaluator, context, options) {
|
|
|
20945
21365
|
}
|
|
20946
21366
|
return null;
|
|
20947
21367
|
}
|
|
21368
|
+
function ofOrPossessiveAccess(n) {
|
|
21369
|
+
const [owner, property, isOf] = n.type === 'possessiveExpression'
|
|
21370
|
+
? [n.object, n.property, false]
|
|
21371
|
+
: n.type === 'propertyOfExpression'
|
|
21372
|
+
? [n.target, n.property, false]
|
|
21373
|
+
: n.type === 'binaryExpression' && n.operator === 'of'
|
|
21374
|
+
? [n.right, n.left, true]
|
|
21375
|
+
: [undefined, undefined, false];
|
|
21376
|
+
const name = property?.name;
|
|
21377
|
+
if (!owner || typeof name !== 'string' || !/^[A-Za-z_$][\w$]*$/.test(name))
|
|
21378
|
+
return null;
|
|
21379
|
+
return { owner, name, isOf };
|
|
21380
|
+
}
|
|
20948
21381
|
|
|
20949
21382
|
function toSetInput(target, value) {
|
|
20950
21383
|
switch (target.kind) {
|
|
@@ -20966,10 +21399,18 @@ function toSetInput(target, value) {
|
|
|
20966
21399
|
property: target.target.property,
|
|
20967
21400
|
value,
|
|
20968
21401
|
};
|
|
21402
|
+
case 'member':
|
|
21403
|
+
return {
|
|
21404
|
+
type: 'member-assignment',
|
|
21405
|
+
container: target.container,
|
|
21406
|
+
property: target.property,
|
|
21407
|
+
value,
|
|
21408
|
+
};
|
|
20969
21409
|
default:
|
|
20970
21410
|
throw new Error(`set: unrequested write-target rung '${target.kind}'`);
|
|
20971
21411
|
}
|
|
20972
21412
|
}
|
|
21413
|
+
const SET_RUNGS = { nodeWriters: true, styleSplit: true, ofAndObjectProperty: true };
|
|
20973
21414
|
let SetCommand = (() => {
|
|
20974
21415
|
let _classDecorators = [command({ name: 'set' })];
|
|
20975
21416
|
let _classDescriptor;
|
|
@@ -20988,8 +21429,7 @@ let SetCommand = (() => {
|
|
|
20988
21429
|
const argScope = firstArg?.scope;
|
|
20989
21430
|
const writeTarget = await resolveWriteTarget(firstArg, evaluator, context, {
|
|
20990
21431
|
scopeElements: () => this.resolveTargets(raw.modifiers.on, evaluator, context),
|
|
20991
|
-
|
|
20992
|
-
styleSplit: true,
|
|
21432
|
+
...SET_RUNGS,
|
|
20993
21433
|
});
|
|
20994
21434
|
if (writeTarget) {
|
|
20995
21435
|
return toSetInput(writeTarget, await this.extractValue(raw, evaluator, context));
|
|
@@ -21315,12 +21755,30 @@ function getNodeType(node) {
|
|
|
21315
21755
|
return 'unknown';
|
|
21316
21756
|
return node.type || 'unknown';
|
|
21317
21757
|
}
|
|
21758
|
+
function propertyTarget(node) {
|
|
21759
|
+
const n = node;
|
|
21760
|
+
const owner = n.type === 'propertyOfExpression'
|
|
21761
|
+
? n.target
|
|
21762
|
+
: n.type === 'possessiveExpression' ||
|
|
21763
|
+
n.type === 'memberExpression' ||
|
|
21764
|
+
n.type === 'propertyAccess'
|
|
21765
|
+
? n.object
|
|
21766
|
+
: undefined;
|
|
21767
|
+
if (!owner || n.computed)
|
|
21768
|
+
return null;
|
|
21769
|
+
const p = n.property;
|
|
21770
|
+
const name = typeof p === 'string' ? p : typeof p?.name === 'string' ? p.name : undefined;
|
|
21771
|
+
if (!name || !/^[A-Za-z_$][\w$-]*$/.test(name))
|
|
21772
|
+
return null;
|
|
21773
|
+
return { owner, name };
|
|
21774
|
+
}
|
|
21318
21775
|
async function parseNumericTargetInput(raw, evaluator, context, commandName) {
|
|
21319
21776
|
if (!raw.args || raw.args.length === 0) {
|
|
21320
21777
|
throw new Error(`${commandName} command requires a target`);
|
|
21321
21778
|
}
|
|
21322
21779
|
const targetArg = raw.args[0];
|
|
21323
21780
|
let target;
|
|
21781
|
+
let property;
|
|
21324
21782
|
let extractedScope;
|
|
21325
21783
|
let customWrite;
|
|
21326
21784
|
const nodeType = getNodeType(targetArg);
|
|
@@ -21335,7 +21793,25 @@ async function parseNumericTargetInput(raw, evaluator, context, commandName) {
|
|
|
21335
21793
|
}
|
|
21336
21794
|
else {
|
|
21337
21795
|
const writer = getRegisteredNodeWriter(nodeType);
|
|
21338
|
-
|
|
21796
|
+
const attribute = writer
|
|
21797
|
+
? null
|
|
21798
|
+
: await resolveAttributeWriteTarget(targetArg, evaluator, context, async () => context.me ? [context.me] : []);
|
|
21799
|
+
const prop = writer || attribute ? null : propertyTarget(targetArg);
|
|
21800
|
+
const owner = prop ? await evaluator.evaluate(prop.owner, context) : undefined;
|
|
21801
|
+
const ownerElement = Array.isArray(owner) ? owner[0] : owner;
|
|
21802
|
+
if (attribute?.elements[0]) {
|
|
21803
|
+
target = attribute.elements[0];
|
|
21804
|
+
property = `@${attribute.name}`;
|
|
21805
|
+
}
|
|
21806
|
+
else if (prop && isHTMLElement$2(ownerElement)) {
|
|
21807
|
+
target = ownerElement;
|
|
21808
|
+
property = prop.name;
|
|
21809
|
+
}
|
|
21810
|
+
else if (prop && isObjectOwner(owner)) {
|
|
21811
|
+
target = owner;
|
|
21812
|
+
property = prop.name;
|
|
21813
|
+
}
|
|
21814
|
+
else if (writer) {
|
|
21339
21815
|
const currentRaw = await evaluator.evaluate(targetArg, context);
|
|
21340
21816
|
const currentValue = typeof currentRaw === 'number'
|
|
21341
21817
|
? currentRaw
|
|
@@ -21373,24 +21849,22 @@ async function parseNumericTargetInput(raw, evaluator, context, commandName) {
|
|
|
21373
21849
|
if (typeof evaluated === 'number') {
|
|
21374
21850
|
amount = evaluated;
|
|
21375
21851
|
}
|
|
21852
|
+
else if (evaluated == null) {
|
|
21853
|
+
amount = NaN;
|
|
21854
|
+
}
|
|
21376
21855
|
}
|
|
21377
21856
|
}
|
|
21378
21857
|
const byModifier = raw.modifiers?.by;
|
|
21379
21858
|
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
|
-
}
|
|
21859
|
+
const value = getNodeType(byModifier) === 'literal'
|
|
21860
|
+
? byModifier.value
|
|
21861
|
+
: await evaluator.evaluate(byModifier, context);
|
|
21862
|
+
amount = counterAmount(value);
|
|
21390
21863
|
}
|
|
21391
21864
|
return {
|
|
21392
21865
|
target,
|
|
21393
21866
|
amount,
|
|
21867
|
+
...(property && { property }),
|
|
21394
21868
|
...(scope && { scope }),
|
|
21395
21869
|
...(customWrite && { customWrite }),
|
|
21396
21870
|
};
|
|
@@ -21413,7 +21887,7 @@ let NumericModifyCommand = (() => {
|
|
|
21413
21887
|
async execute(input, context) {
|
|
21414
21888
|
const { target, property, scope, amount = 1, operation = 'increment', customWrite } = input;
|
|
21415
21889
|
if (customWrite) {
|
|
21416
|
-
const delta =
|
|
21890
|
+
const delta = amount;
|
|
21417
21891
|
const newValue = operation === 'increment'
|
|
21418
21892
|
? customWrite.currentValue + delta
|
|
21419
21893
|
: customWrite.currentValue - delta;
|
|
@@ -21427,8 +21901,7 @@ let NumericModifyCommand = (() => {
|
|
|
21427
21901
|
newValue = NaN;
|
|
21428
21902
|
}
|
|
21429
21903
|
else {
|
|
21430
|
-
|
|
21431
|
-
newValue = operation === 'increment' ? currentValue + delta : currentValue - delta;
|
|
21904
|
+
newValue = operation === 'increment' ? currentValue + amount : currentValue - amount;
|
|
21432
21905
|
}
|
|
21433
21906
|
setTargetValue(target, property, scope, newValue, context);
|
|
21434
21907
|
Object.assign(context, { it: newValue });
|
|
@@ -22536,7 +23009,10 @@ function createUntilLoopConfig(conditionExpr, evaluateCondition, context, indexV
|
|
|
22536
23009
|
};
|
|
22537
23010
|
const config = {
|
|
22538
23011
|
type: 'until',
|
|
22539
|
-
shouldContinue: () =>
|
|
23012
|
+
shouldContinue: () => {
|
|
23013
|
+
const met = evaluateCondition(conditionExpr, context);
|
|
23014
|
+
return met instanceof Promise ? met.then(value => !value) : !met;
|
|
23015
|
+
},
|
|
22540
23016
|
};
|
|
22541
23017
|
return { config, iterCtx };
|
|
22542
23018
|
}
|
|
@@ -22572,7 +23048,7 @@ function createForeverLoopConfig(indexVariable, maxIterations = 10000) {
|
|
|
22572
23048
|
return { config, iterCtx };
|
|
22573
23049
|
}
|
|
22574
23050
|
|
|
22575
|
-
async function slotText(node, evaluator, context) {
|
|
23051
|
+
async function slotText$1(node, evaluator, context) {
|
|
22576
23052
|
if (!node)
|
|
22577
23053
|
return undefined;
|
|
22578
23054
|
const n = node;
|
|
@@ -22585,6 +23061,18 @@ async function slotText(node, evaluator, context) {
|
|
|
22585
23061
|
const v = await evaluator.evaluate(node, context);
|
|
22586
23062
|
return typeof v === 'string' ? v : undefined;
|
|
22587
23063
|
}
|
|
23064
|
+
function isEventTarget(value) {
|
|
23065
|
+
return typeof value?.addEventListener === 'function';
|
|
23066
|
+
}
|
|
23067
|
+
async function evaluateLoopCondition(condition, context) {
|
|
23068
|
+
if (typeof condition === 'function')
|
|
23069
|
+
return Boolean(await condition());
|
|
23070
|
+
return evaluateCondition(condition, context);
|
|
23071
|
+
}
|
|
23072
|
+
function loopCount(value) {
|
|
23073
|
+
const count = Number(value);
|
|
23074
|
+
return Number.isNaN(count) ? 0 : count;
|
|
23075
|
+
}
|
|
22588
23076
|
let RepeatCommand = (() => {
|
|
22589
23077
|
let _classDecorators = [command({ name: 'repeat' })];
|
|
22590
23078
|
let _classDescriptor;
|
|
@@ -22622,7 +23110,7 @@ let RepeatCommand = (() => {
|
|
|
22622
23110
|
}
|
|
22623
23111
|
}
|
|
22624
23112
|
const m = raw.modifiers ?? {};
|
|
22625
|
-
const text = (node) => slotText(node, evaluator, context);
|
|
23113
|
+
const text = (node) => slotText$1(node, evaluator, context);
|
|
22626
23114
|
const loopType = (await text(m.loopType)) ?? null;
|
|
22627
23115
|
const bottomTested = m.bottomTested
|
|
22628
23116
|
? (await evaluator.evaluate(m.bottomTested, context))
|
|
@@ -22642,17 +23130,24 @@ let RepeatCommand = (() => {
|
|
|
22642
23130
|
};
|
|
22643
23131
|
}
|
|
22644
23132
|
if (loopType === 'times' || m.times) {
|
|
22645
|
-
|
|
22646
|
-
const count = typeof countValue === 'number' ? countValue : parseInt(String(countValue), 10);
|
|
22647
|
-
if (isNaN(count))
|
|
23133
|
+
if (!m.times)
|
|
22648
23134
|
throw new Error('times loops require a count number');
|
|
23135
|
+
const count = loopCount(await evaluator.evaluate(m.times, context));
|
|
22649
23136
|
return { type: 'times', count, indexVariable, commands, elseCommands };
|
|
22650
23137
|
}
|
|
22651
23138
|
if (loopType === 'while' || m.while) {
|
|
22652
23139
|
const condition = m.while;
|
|
22653
23140
|
if (!condition)
|
|
22654
23141
|
throw new Error('while loops require a condition');
|
|
22655
|
-
|
|
23142
|
+
const check = () => evaluator.evaluate(condition, context);
|
|
23143
|
+
return {
|
|
23144
|
+
type: 'while',
|
|
23145
|
+
condition: check,
|
|
23146
|
+
indexVariable,
|
|
23147
|
+
commands,
|
|
23148
|
+
elseCommands,
|
|
23149
|
+
bottomTested,
|
|
23150
|
+
};
|
|
22656
23151
|
}
|
|
22657
23152
|
if (loopType === 'until-event' || m.event) {
|
|
22658
23153
|
const eventName = await text(m.event);
|
|
@@ -22670,7 +23165,7 @@ let RepeatCommand = (() => {
|
|
|
22670
23165
|
}
|
|
22671
23166
|
else {
|
|
22672
23167
|
const target = await evaluator.evaluate(m.from, context);
|
|
22673
|
-
if (target
|
|
23168
|
+
if (isEventTarget(target))
|
|
22674
23169
|
eventTarget = target;
|
|
22675
23170
|
else if (target === 'document')
|
|
22676
23171
|
eventTarget = document;
|
|
@@ -22691,7 +23186,15 @@ let RepeatCommand = (() => {
|
|
|
22691
23186
|
const condition = m.until;
|
|
22692
23187
|
if (!condition)
|
|
22693
23188
|
throw new Error('until loops require a condition');
|
|
22694
|
-
|
|
23189
|
+
const check = () => evaluator.evaluate(condition, context);
|
|
23190
|
+
return {
|
|
23191
|
+
type: 'until',
|
|
23192
|
+
condition: check,
|
|
23193
|
+
indexVariable,
|
|
23194
|
+
commands,
|
|
23195
|
+
elseCommands,
|
|
23196
|
+
bottomTested,
|
|
23197
|
+
};
|
|
22695
23198
|
}
|
|
22696
23199
|
if (loopType === 'forever') {
|
|
22697
23200
|
return { type: 'forever', indexVariable, commands, elseCommands };
|
|
@@ -22709,10 +23212,10 @@ let RepeatCommand = (() => {
|
|
|
22709
23212
|
({ config, iterCtx } = createTimesLoopConfig(count, indexVariable));
|
|
22710
23213
|
break;
|
|
22711
23214
|
case 'while':
|
|
22712
|
-
({ config, iterCtx } = createWhileLoopConfig(condition,
|
|
23215
|
+
({ config, iterCtx } = createWhileLoopConfig(condition, evaluateLoopCondition, context, indexVariable));
|
|
22713
23216
|
break;
|
|
22714
23217
|
case 'until':
|
|
22715
|
-
({ config, iterCtx } = createUntilLoopConfig(condition,
|
|
23218
|
+
({ config, iterCtx } = createUntilLoopConfig(condition, evaluateLoopCondition, context, indexVariable));
|
|
22716
23219
|
break;
|
|
22717
23220
|
case 'until-event':
|
|
22718
23221
|
({ config, iterCtx } = createUntilEventLoopConfig(eventName, eventTarget, indexVariable));
|
|
@@ -23743,6 +24246,29 @@ function namedPropertyOf(node) {
|
|
|
23743
24246
|
}
|
|
23744
24247
|
return null;
|
|
23745
24248
|
}
|
|
24249
|
+
const RESTORE_KEYWORDS = new Set(['initial', 'inherit', 'unset', 'revert']);
|
|
24250
|
+
function elementsOf(value) {
|
|
24251
|
+
if (Array.isArray(value) || isNodeList(value)) {
|
|
24252
|
+
return Array.from(value).filter(isHTMLElement$2);
|
|
24253
|
+
}
|
|
24254
|
+
return isHTMLElement$2(value) ? [value] : [];
|
|
24255
|
+
}
|
|
24256
|
+
async function valueOf(node, evaluator, context) {
|
|
24257
|
+
const value = await evaluator.evaluate(node, context);
|
|
24258
|
+
const name = node.name;
|
|
24259
|
+
if (value === undefined && node.type === 'identifier' && typeof name === 'string')
|
|
24260
|
+
return name;
|
|
24261
|
+
return value;
|
|
24262
|
+
}
|
|
24263
|
+
function longestTransitionMs(css) {
|
|
24264
|
+
let longest;
|
|
24265
|
+
for (const item of css.split(',')) {
|
|
24266
|
+
const times = [...item.matchAll(/(\d*\.?\d+)(ms|s)\b/g)].map(m => parseFloat(m[1]) * (m[2] === 's' ? 1000 : 1));
|
|
24267
|
+
if (times.length > 0)
|
|
24268
|
+
longest = Math.max(longest ?? 0, times[0] + (times[1] ?? 0));
|
|
24269
|
+
}
|
|
24270
|
+
return longest;
|
|
24271
|
+
}
|
|
23746
24272
|
let TransitionCommand = (() => {
|
|
23747
24273
|
let _classDecorators = [command({ name: 'transition' })];
|
|
23748
24274
|
let _classDescriptor;
|
|
@@ -23758,11 +24284,10 @@ let TransitionCommand = (() => {
|
|
|
23758
24284
|
let property;
|
|
23759
24285
|
let target;
|
|
23760
24286
|
const firstArg = await evaluator.evaluate(raw.args[0], context);
|
|
23761
|
-
const
|
|
23762
|
-
|
|
23763
|
-
if (isHTMLElement$2(firstAsElement) ||
|
|
24287
|
+
const owners = elementsOf(firstArg);
|
|
24288
|
+
if (owners.length > 0 ||
|
|
23764
24289
|
(typeof firstArg === 'string' && /^[#.]|^(?:me|it|you)$/.test(firstArg))) {
|
|
23765
|
-
target =
|
|
24290
|
+
target = owners.length === 1 ? owners[0] : owners.length > 1 ? owners : firstArg;
|
|
23766
24291
|
property = String(await evaluator.evaluate(raw.args[1], context));
|
|
23767
24292
|
}
|
|
23768
24293
|
else if (raw.args.length >= 2) {
|
|
@@ -23774,9 +24299,9 @@ let TransitionCommand = (() => {
|
|
|
23774
24299
|
throw new Error('transition requires a CSS property');
|
|
23775
24300
|
property = named.property;
|
|
23776
24301
|
if (named.objectNode !== undefined) {
|
|
23777
|
-
const owner =
|
|
23778
|
-
if (
|
|
23779
|
-
target = owner;
|
|
24302
|
+
const owner = elementsOf(await evaluator.evaluate(named.objectNode, context));
|
|
24303
|
+
if (owner.length > 0)
|
|
24304
|
+
target = owner.length === 1 ? owner[0] : owner;
|
|
23780
24305
|
}
|
|
23781
24306
|
}
|
|
23782
24307
|
else {
|
|
@@ -23787,60 +24312,131 @@ let TransitionCommand = (() => {
|
|
|
23787
24312
|
}
|
|
23788
24313
|
if (!raw.modifiers?.to)
|
|
23789
24314
|
throw new Error('transition requires "to <value>"');
|
|
23790
|
-
|
|
23791
|
-
|
|
23792
|
-
value
|
|
23793
|
-
}
|
|
23794
|
-
const result = { property, value: value };
|
|
24315
|
+
const result = {
|
|
24316
|
+
property,
|
|
24317
|
+
value: await valueOf(raw.modifiers.to, evaluator, context),
|
|
24318
|
+
};
|
|
23795
24319
|
if (target !== undefined)
|
|
23796
24320
|
result.target = target;
|
|
24321
|
+
if (raw.modifiers?.from)
|
|
24322
|
+
result.from = await valueOf(raw.modifiers.from, evaluator, context);
|
|
24323
|
+
if (raw.modifiers?.pairs) {
|
|
24324
|
+
result.pairs = await this.laterPairs(raw.modifiers.pairs, evaluator, context);
|
|
24325
|
+
}
|
|
23797
24326
|
if (raw.modifiers?.over)
|
|
23798
24327
|
result.duration = await evaluator.evaluate(raw.modifiers.over, context);
|
|
23799
24328
|
if (raw.modifiers?.with)
|
|
23800
24329
|
result.timingFunction = String(await evaluator.evaluate(raw.modifiers.with, context));
|
|
24330
|
+
if (raw.modifiers?.using)
|
|
24331
|
+
result.using = String(await evaluator.evaluate(raw.modifiers.using, context));
|
|
23801
24332
|
return result;
|
|
23802
24333
|
}
|
|
24334
|
+
async laterPairs(node, evaluator, context) {
|
|
24335
|
+
const pairs = [];
|
|
24336
|
+
for (const pairNode of node.elements ?? []) {
|
|
24337
|
+
const fields = new Map();
|
|
24338
|
+
const properties = pairNode
|
|
24339
|
+
.properties ?? [];
|
|
24340
|
+
for (const { key, value } of properties) {
|
|
24341
|
+
if (typeof key.name === 'string')
|
|
24342
|
+
fields.set(key.name, value);
|
|
24343
|
+
}
|
|
24344
|
+
const propertyNode = fields.get('property');
|
|
24345
|
+
const toNode = fields.get('to');
|
|
24346
|
+
if (!propertyNode || !toNode)
|
|
24347
|
+
throw new Error('transition requires property and value');
|
|
24348
|
+
const pair = {
|
|
24349
|
+
property: String(await evaluator.evaluate(propertyNode, context)),
|
|
24350
|
+
value: await valueOf(toNode, evaluator, context),
|
|
24351
|
+
};
|
|
24352
|
+
const ownerNode = fields.get('owner');
|
|
24353
|
+
if (ownerNode) {
|
|
24354
|
+
const owners = elementsOf(await evaluator.evaluate(ownerNode, context));
|
|
24355
|
+
if (owners.length === 0)
|
|
24356
|
+
throw new Error('transition: target element not found');
|
|
24357
|
+
pair.target = owners.length === 1 ? owners[0] : owners;
|
|
24358
|
+
}
|
|
24359
|
+
const fromNode = fields.get('from');
|
|
24360
|
+
if (fromNode)
|
|
24361
|
+
pair.from = await valueOf(fromNode, evaluator, context);
|
|
24362
|
+
pairs.push(pair);
|
|
24363
|
+
}
|
|
24364
|
+
return pairs;
|
|
24365
|
+
}
|
|
23803
24366
|
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
|
-
|
|
24367
|
+
const duration = parseDuration(input.duration, (input.using !== undefined && longestTransitionMs(input.using)) || 300);
|
|
24368
|
+
const timing = input.timingFunction || 'ease';
|
|
24369
|
+
const jobs = [];
|
|
24370
|
+
for (const pair of [input, ...(input.pairs ?? [])]) {
|
|
24371
|
+
let property = pair.property;
|
|
24372
|
+
if (property.startsWith('*'))
|
|
24373
|
+
property = property.substring(1);
|
|
24374
|
+
property = camelToKebab(property);
|
|
24375
|
+
const elements = Array.isArray(pair.target)
|
|
24376
|
+
? pair.target.filter(isHTMLElement$2)
|
|
24377
|
+
: [resolveElement(pair.target, context, 'transition')];
|
|
24378
|
+
for (const element of elements) {
|
|
24379
|
+
jobs.push({
|
|
24380
|
+
element,
|
|
24381
|
+
property,
|
|
24382
|
+
pair,
|
|
24383
|
+
fromValue: getComputedStyle(element).getPropertyValue(property),
|
|
24384
|
+
toValue: '',
|
|
24385
|
+
removeInlineAfter: false,
|
|
24386
|
+
});
|
|
24387
|
+
}
|
|
24388
|
+
}
|
|
24389
|
+
if (jobs.length === 0)
|
|
24390
|
+
throw new Error('transition: target element not found');
|
|
24391
|
+
for (const job of jobs) {
|
|
24392
|
+
if (job.pair.from !== undefined) {
|
|
24393
|
+
job.element.style.setProperty(job.property, String(job.pair.from));
|
|
24394
|
+
}
|
|
24395
|
+
}
|
|
24396
|
+
for (const job of jobs)
|
|
24397
|
+
if (job.pair.from !== undefined)
|
|
24398
|
+
void job.element.offsetWidth;
|
|
24399
|
+
const originals = new Map();
|
|
24400
|
+
for (const job of jobs) {
|
|
24401
|
+
if (!originals.has(job.element))
|
|
24402
|
+
originals.set(job.element, job.element.style.transition);
|
|
24403
|
+
}
|
|
24404
|
+
for (const [element, original] of originals) {
|
|
24405
|
+
const own = jobs
|
|
24406
|
+
.filter(job => job.element === element)
|
|
24407
|
+
.map(job => `${job.property} ${duration}ms ${timing}`);
|
|
24408
|
+
element.style.transition = input.using ?? [original, ...own].filter(Boolean).join(', ');
|
|
24409
|
+
}
|
|
24410
|
+
for (const job of jobs) {
|
|
24411
|
+
const { element, property } = job;
|
|
24412
|
+
let toValue = String(job.pair.value);
|
|
24413
|
+
if (RESTORE_KEYWORDS.has(toValue)) {
|
|
24414
|
+
const currentInline = element.style.getPropertyValue(property);
|
|
24415
|
+
element.style.removeProperty(property);
|
|
24416
|
+
toValue = getComputedStyle(element).getPropertyValue(property);
|
|
24417
|
+
if (currentInline) {
|
|
24418
|
+
element.style.setProperty(property, currentInline);
|
|
24419
|
+
}
|
|
24420
|
+
job.removeInlineAfter = true;
|
|
24421
|
+
}
|
|
24422
|
+
job.toValue = toValue;
|
|
24423
|
+
element.style.setProperty(property, toValue);
|
|
24424
|
+
}
|
|
24425
|
+
const results = await Promise.all(jobs.map(job => waitForTransitionEnd(job.element, job.property, duration)));
|
|
24426
|
+
for (const [element, original] of originals)
|
|
24427
|
+
element.style.transition = original;
|
|
24428
|
+
for (const job of jobs) {
|
|
24429
|
+
if (job.removeInlineAfter)
|
|
24430
|
+
job.element.style.removeProperty(job.property);
|
|
23836
24431
|
}
|
|
24432
|
+
const first = jobs[0];
|
|
23837
24433
|
return {
|
|
23838
|
-
element:
|
|
23839
|
-
property,
|
|
23840
|
-
fromValue,
|
|
23841
|
-
toValue,
|
|
24434
|
+
element: first.element,
|
|
24435
|
+
property: first.property,
|
|
24436
|
+
fromValue: first.fromValue,
|
|
24437
|
+
toValue: first.toValue,
|
|
23842
24438
|
duration,
|
|
23843
|
-
completed: result.completed,
|
|
24439
|
+
completed: results.every(result => result.completed),
|
|
23844
24440
|
};
|
|
23845
24441
|
}
|
|
23846
24442
|
};
|
|
@@ -23853,13 +24449,16 @@ let TransitionCommand = (() => {
|
|
|
23853
24449
|
})();
|
|
23854
24450
|
_classThis.metadata = commandMeta({
|
|
23855
24451
|
description: 'Animate CSS properties using CSS transitions',
|
|
23856
|
-
syntax: 'transition [<
|
|
24452
|
+
syntax: 'transition <property> [from <value>] to <value> [<property> \u2026 to <value>]\u2026 [over <duration>] [using <css> | with <timing>]',
|
|
23857
24453
|
examples: [
|
|
23858
24454
|
'transition opacity to 0.5',
|
|
23859
24455
|
'transition my *opacity to 0 over 200ms',
|
|
23860
24456
|
"transition #box's *opacity to 0 over 200ms",
|
|
23861
24457
|
'transition left to 100px over 500ms',
|
|
23862
24458
|
'transition background-color to red over 1s with ease-in-out',
|
|
24459
|
+
'transition *width to 100px *height to 50px over 300ms',
|
|
24460
|
+
'transition *opacity of #panel from 0 to 1 over 200ms',
|
|
24461
|
+
'transition my *opacity to 0 using "opacity 1s ease-in"',
|
|
23863
24462
|
],
|
|
23864
24463
|
sideEffects: ['style-change', 'timing'],
|
|
23865
24464
|
category: 'animation',
|
|
@@ -25109,17 +25708,7 @@ let BeepCommand = (() => {
|
|
|
25109
25708
|
this.debugContext(context);
|
|
25110
25709
|
return { expressionCount: 0, debugged: true, outputs: [] };
|
|
25111
25710
|
}
|
|
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();
|
|
25711
|
+
const outputs = beepValues(context.me, expressions);
|
|
25123
25712
|
return { expressionCount: expressions.length, debugged: true, outputs };
|
|
25124
25713
|
}
|
|
25125
25714
|
debugContext(context) {
|
|
@@ -25132,67 +25721,6 @@ let BeepCommand = (() => {
|
|
|
25132
25721
|
console.log('variables:', context.variables);
|
|
25133
25722
|
console.groupEnd();
|
|
25134
25723
|
}
|
|
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
25724
|
};
|
|
25197
25725
|
__setFunctionName(_classThis, "BeepCommand");
|
|
25198
25726
|
(() => {
|
|
@@ -25740,16 +26268,12 @@ class RenderCommand {
|
|
|
25740
26268
|
}
|
|
25741
26269
|
RenderCommand.metadata = commandMeta({
|
|
25742
26270
|
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
|
-
],
|
|
26271
|
+
syntax: ['render <template>', 'render <template> with <name>: <value>, ...'],
|
|
25748
26272
|
examples: [
|
|
25749
26273
|
'render myTemplate',
|
|
25750
|
-
'render myTemplate with
|
|
25751
|
-
'render "<template>Hello ${name}!</template>" with
|
|
25752
|
-
'render template with
|
|
26274
|
+
'render myTemplate with name: "Alice"',
|
|
26275
|
+
'render "<template>Hello ${name}!</template>" with name: "World"',
|
|
26276
|
+
'render template with items: data',
|
|
25753
26277
|
],
|
|
25754
26278
|
category: 'templates',
|
|
25755
26279
|
compatibility: 'standard',
|
|
@@ -25937,7 +26461,7 @@ function updateDebugStats(event) {
|
|
|
25937
26461
|
stats.confidenceHistory.reduce((a, b) => a + b, 0) / stats.confidenceHistory.length;
|
|
25938
26462
|
}
|
|
25939
26463
|
|
|
25940
|
-
const VERSION = '3.
|
|
26464
|
+
const VERSION = '3.3.0';
|
|
25941
26465
|
|
|
25942
26466
|
function createHistorySwap(config) {
|
|
25943
26467
|
const { target, strategy = 'morph', useViewTransition = false, fetchOptions = {}, transformUrl, onBeforeFetch, onAfterSwap, onError, } = config;
|
|
@@ -27483,6 +28007,14 @@ function convertNode(node, infer) {
|
|
|
27483
28007
|
...pos(node),
|
|
27484
28008
|
};
|
|
27485
28009
|
case 'identifier':
|
|
28010
|
+
if (node.scope === 'element' || node.scope === 'local' || node.scope === 'global') {
|
|
28011
|
+
return {
|
|
28012
|
+
type: 'variable',
|
|
28013
|
+
name: (node.name ?? ''),
|
|
28014
|
+
scope: node.scope,
|
|
28015
|
+
...pos(node),
|
|
28016
|
+
};
|
|
28017
|
+
}
|
|
27486
28018
|
return {
|
|
27487
28019
|
type: 'identifier',
|
|
27488
28020
|
value: (node.name ?? node.value ?? ''),
|
|
@@ -27505,6 +28037,15 @@ function convertNode(node, infer) {
|
|
|
27505
28037
|
operand: convertNode(node.argument ?? node.operand, infer),
|
|
27506
28038
|
...pos(node),
|
|
27507
28039
|
};
|
|
28040
|
+
case 'typeCheckExpression':
|
|
28041
|
+
return {
|
|
28042
|
+
type: 'typeCheck',
|
|
28043
|
+
value: convertNode(node.value, infer),
|
|
28044
|
+
typeName: String(node.typeName),
|
|
28045
|
+
nullOk: node.nullOk !== false,
|
|
28046
|
+
negated: node.negated === true,
|
|
28047
|
+
...pos(node),
|
|
28048
|
+
};
|
|
27508
28049
|
case 'timeExpression':
|
|
27509
28050
|
return { type: 'literal', value: node.value, ...pos(node) };
|
|
27510
28051
|
case 'templateLiteral':
|
|
@@ -27516,6 +28057,28 @@ function convertNode(node, infer) {
|
|
|
27516
28057
|
scope: (node.scope ?? 'local'),
|
|
27517
28058
|
...pos(node),
|
|
27518
28059
|
};
|
|
28060
|
+
case 'arrayLiteral':
|
|
28061
|
+
return {
|
|
28062
|
+
type: 'array',
|
|
28063
|
+
elements: (node.elements ?? []).map(element => convertNode(element, infer)),
|
|
28064
|
+
...pos(node),
|
|
28065
|
+
};
|
|
28066
|
+
case 'attributeAccess':
|
|
28067
|
+
return {
|
|
28068
|
+
type: 'possessive',
|
|
28069
|
+
object: { type: 'identifier', value: 'me' },
|
|
28070
|
+
property: `@${node.attributeName}`,
|
|
28071
|
+
...pos(node),
|
|
28072
|
+
};
|
|
28073
|
+
case 'propertyOfExpression': {
|
|
28074
|
+
const property = node.property;
|
|
28075
|
+
return {
|
|
28076
|
+
type: 'possessive',
|
|
28077
|
+
object: convertNode(node.target, infer),
|
|
28078
|
+
property: (property?.name ?? property?.value ?? ''),
|
|
28079
|
+
...pos(node),
|
|
28080
|
+
};
|
|
28081
|
+
}
|
|
27519
28082
|
case 'htmlSelector':
|
|
27520
28083
|
return {
|
|
27521
28084
|
type: 'selector',
|
|
@@ -27567,6 +28130,21 @@ function convertCommand$1(node, infer) {
|
|
|
27567
28130
|
if (name === 'repeat') {
|
|
27568
28131
|
return convertRepeatCommand(node, infer);
|
|
27569
28132
|
}
|
|
28133
|
+
const original = node.originalCommand;
|
|
28134
|
+
const sum = node.modifiers?.to;
|
|
28135
|
+
const counted = node.args?.[0];
|
|
28136
|
+
if (name === 'set' &&
|
|
28137
|
+
(original === 'increment' || original === 'decrement') &&
|
|
28138
|
+
sum?.type === 'binaryExpression' &&
|
|
28139
|
+
counted) {
|
|
28140
|
+
return convertCommand$1({
|
|
28141
|
+
...node,
|
|
28142
|
+
name: original,
|
|
28143
|
+
args: [counted],
|
|
28144
|
+
modifiers: { by: sum.right },
|
|
28145
|
+
originalCommand: undefined,
|
|
28146
|
+
}, infer);
|
|
28147
|
+
}
|
|
27570
28148
|
const args = (node.args ?? []).map(arg => convertNode(arg, infer));
|
|
27571
28149
|
const target = node.target ? convertNode(node.target, infer) : undefined;
|
|
27572
28150
|
const modifiers = node.modifiers
|
|
@@ -27615,6 +28193,64 @@ function convertIfCommand(node, infer) {
|
|
|
27615
28193
|
};
|
|
27616
28194
|
}
|
|
27617
28195
|
function convertRepeatCommand(node, infer) {
|
|
28196
|
+
const slots = (node.modifiers ?? {});
|
|
28197
|
+
const form = slotText(slots.loopType);
|
|
28198
|
+
if (form === undefined)
|
|
28199
|
+
return convertPositionalRepeat(node, infer);
|
|
28200
|
+
const [bodyBlock, elseBlock] = (node.args ?? []);
|
|
28201
|
+
const body = extractBlockCommands(bodyBlock, infer);
|
|
28202
|
+
const indexName = slotText(slots.index);
|
|
28203
|
+
const shared = {
|
|
28204
|
+
...(indexName ? { indexName } : {}),
|
|
28205
|
+
...(elseBlock ? { elseBody: extractBlockCommands(elseBlock, infer) } : {}),
|
|
28206
|
+
...pos(node),
|
|
28207
|
+
};
|
|
28208
|
+
const operand = (slot) => (slot ? convertNode(slot, infer) : undefined);
|
|
28209
|
+
switch (form) {
|
|
28210
|
+
case 'for':
|
|
28211
|
+
return {
|
|
28212
|
+
type: 'foreach',
|
|
28213
|
+
itemName: slotText(slots.for) ?? 'it',
|
|
28214
|
+
collection: operand(slots.in) ?? { type: 'literal', value: null },
|
|
28215
|
+
body,
|
|
28216
|
+
...shared,
|
|
28217
|
+
};
|
|
28218
|
+
case 'times':
|
|
28219
|
+
return { type: 'repeat', count: operand(slots.times), body, ...shared };
|
|
28220
|
+
case 'while':
|
|
28221
|
+
case 'until': {
|
|
28222
|
+
const test = operand(slots[form]) ?? { type: 'literal', value: form === 'until' };
|
|
28223
|
+
return {
|
|
28224
|
+
type: 'while',
|
|
28225
|
+
condition: form === 'until' ? { type: 'unary', operator: 'not', operand: test } : test,
|
|
28226
|
+
...(slots.bottomTested?.value === true ? { bottomTested: true } : {}),
|
|
28227
|
+
body,
|
|
28228
|
+
...shared,
|
|
28229
|
+
};
|
|
28230
|
+
}
|
|
28231
|
+
case 'until-event': {
|
|
28232
|
+
const event = slotText(slots.event);
|
|
28233
|
+
if (!event) {
|
|
28234
|
+
return { type: 'error', message: "'repeat until event' has no event name", ...pos(node) };
|
|
28235
|
+
}
|
|
28236
|
+
const target = operand(slots.from);
|
|
28237
|
+
return {
|
|
28238
|
+
type: 'repeat',
|
|
28239
|
+
untilEvent: event,
|
|
28240
|
+
...(target ? { untilEventTarget: target } : {}),
|
|
28241
|
+
body,
|
|
28242
|
+
...shared,
|
|
28243
|
+
};
|
|
28244
|
+
}
|
|
28245
|
+
default:
|
|
28246
|
+
return { type: 'repeat', body, ...shared };
|
|
28247
|
+
}
|
|
28248
|
+
}
|
|
28249
|
+
function slotText(slot) {
|
|
28250
|
+
const text = slot?.value ?? slot?.name;
|
|
28251
|
+
return typeof text === 'string' ? text : undefined;
|
|
28252
|
+
}
|
|
28253
|
+
function convertPositionalRepeat(node, infer) {
|
|
27618
28254
|
const args = (node.args ?? []);
|
|
27619
28255
|
if (args.length === 0) {
|
|
27620
28256
|
return { type: 'repeat', body: [], ...pos(node) };
|
|
@@ -27931,6 +28567,15 @@ function toCoreAST(node) {
|
|
|
27931
28567
|
prefix: true,
|
|
27932
28568
|
...nodePos(node),
|
|
27933
28569
|
};
|
|
28570
|
+
case 'typeCheck':
|
|
28571
|
+
return {
|
|
28572
|
+
type: 'typeCheckExpression',
|
|
28573
|
+
value: toCoreAST(node.value),
|
|
28574
|
+
typeName: node.typeName,
|
|
28575
|
+
nullOk: node.nullOk,
|
|
28576
|
+
negated: node.negated,
|
|
28577
|
+
...nodePos(node),
|
|
28578
|
+
};
|
|
27934
28579
|
case 'member':
|
|
27935
28580
|
return {
|
|
27936
28581
|
type: 'memberExpression',
|
|
@@ -27941,6 +28586,12 @@ function toCoreAST(node) {
|
|
|
27941
28586
|
computed: node.computed ?? false,
|
|
27942
28587
|
...nodePos(node),
|
|
27943
28588
|
};
|
|
28589
|
+
case 'array':
|
|
28590
|
+
return {
|
|
28591
|
+
type: 'arrayLiteral',
|
|
28592
|
+
elements: node.elements.map(element => toCoreAST(element)),
|
|
28593
|
+
...nodePos(node),
|
|
28594
|
+
};
|
|
27944
28595
|
case 'possessive':
|
|
27945
28596
|
return {
|
|
27946
28597
|
type: 'possessiveExpression',
|
|
@@ -28003,72 +28654,85 @@ function convertCommand(node) {
|
|
|
28003
28654
|
}
|
|
28004
28655
|
return result;
|
|
28005
28656
|
}
|
|
28657
|
+
function block(nodes) {
|
|
28658
|
+
return { type: 'block', commands: nodes.map(n => toCoreAST(n)), ...POS };
|
|
28659
|
+
}
|
|
28006
28660
|
function convertIf(node) {
|
|
28007
|
-
const
|
|
28661
|
+
const [elseIf, ...laterElseIfs] = node.elseIfBranches ?? [];
|
|
28662
|
+
const elseBranch = elseIf
|
|
28663
|
+
? [
|
|
28664
|
+
{
|
|
28665
|
+
type: 'if',
|
|
28666
|
+
condition: elseIf.condition,
|
|
28667
|
+
thenBranch: elseIf.body,
|
|
28668
|
+
elseIfBranches: laterElseIfs,
|
|
28669
|
+
...(node.elseBranch ? { elseBranch: node.elseBranch } : {}),
|
|
28670
|
+
},
|
|
28671
|
+
]
|
|
28672
|
+
: node.elseBranch;
|
|
28673
|
+
return {
|
|
28008
28674
|
type: 'command',
|
|
28009
28675
|
name: 'if',
|
|
28010
28676
|
isBlocking: true,
|
|
28011
|
-
|
|
28012
|
-
|
|
28013
|
-
|
|
28677
|
+
args: [
|
|
28678
|
+
toCoreAST(node.condition),
|
|
28679
|
+
block(node.thenBranch),
|
|
28680
|
+
...(elseBranch ? [block(elseBranch)] : []),
|
|
28681
|
+
],
|
|
28014
28682
|
...nodePos(node),
|
|
28015
28683
|
};
|
|
28016
|
-
if (node.elseBranch) {
|
|
28017
|
-
result.elseBranch = node.elseBranch.map(n => toCoreAST(n));
|
|
28018
|
-
}
|
|
28019
|
-
return result;
|
|
28020
28684
|
}
|
|
28021
|
-
function
|
|
28022
|
-
const
|
|
28685
|
+
function loopCommand(node, form, operands) {
|
|
28686
|
+
const modifiers = { loopType: slotString(form) };
|
|
28687
|
+
for (const [slot, value] of Object.entries(operands)) {
|
|
28688
|
+
if (value)
|
|
28689
|
+
modifiers[slot] = value;
|
|
28690
|
+
}
|
|
28691
|
+
if (node.indexName)
|
|
28692
|
+
modifiers.index = slotString(node.indexName);
|
|
28693
|
+
return {
|
|
28023
28694
|
type: 'command',
|
|
28024
28695
|
name: 'repeat',
|
|
28025
28696
|
isBlocking: true,
|
|
28026
|
-
|
|
28027
|
-
|
|
28697
|
+
args: [block(node.body), ...(node.elseBody ? [block(node.elseBody)] : [])],
|
|
28698
|
+
modifiers,
|
|
28028
28699
|
...nodePos(node),
|
|
28029
28700
|
};
|
|
28701
|
+
}
|
|
28702
|
+
function slotString(value) {
|
|
28703
|
+
return { type: 'string', value, ...POS };
|
|
28704
|
+
}
|
|
28705
|
+
function convertRepeat(node) {
|
|
28030
28706
|
if (node.count !== undefined) {
|
|
28031
|
-
|
|
28032
|
-
|
|
28033
|
-
|
|
28034
|
-
|
|
28035
|
-
|
|
28036
|
-
|
|
28037
|
-
|
|
28038
|
-
|
|
28039
|
-
|
|
28040
|
-
|
|
28041
|
-
|
|
28042
|
-
|
|
28043
|
-
|
|
28707
|
+
const times = typeof node.count === 'number'
|
|
28708
|
+
? { type: 'literal', value: node.count, raw: String(node.count), ...POS }
|
|
28709
|
+
: toCoreAST(node.count);
|
|
28710
|
+
return loopCommand(node, 'times', { times });
|
|
28711
|
+
}
|
|
28712
|
+
if (node.whileCondition !== undefined) {
|
|
28713
|
+
return loopCommand(node, 'while', { while: toCoreAST(node.whileCondition) });
|
|
28714
|
+
}
|
|
28715
|
+
if (node.untilEvent !== undefined) {
|
|
28716
|
+
return loopCommand(node, 'until-event', {
|
|
28717
|
+
event: slotString(node.untilEvent),
|
|
28718
|
+
from: node.untilEventTarget ? toCoreAST(node.untilEventTarget) : undefined,
|
|
28719
|
+
});
|
|
28044
28720
|
}
|
|
28045
|
-
return
|
|
28721
|
+
return loopCommand(node, 'forever', {});
|
|
28046
28722
|
}
|
|
28047
28723
|
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
|
-
};
|
|
28724
|
+
return loopCommand(node, 'for', {
|
|
28725
|
+
for: slotString(node.itemName),
|
|
28726
|
+
in: toCoreAST(node.collection),
|
|
28727
|
+
});
|
|
28060
28728
|
}
|
|
28061
28729
|
function convertWhile(node) {
|
|
28062
|
-
return {
|
|
28063
|
-
|
|
28064
|
-
|
|
28065
|
-
|
|
28066
|
-
|
|
28067
|
-
|
|
28068
|
-
body: node.body.map(n => toCoreAST(n)),
|
|
28069
|
-
args: [],
|
|
28070
|
-
...nodePos(node),
|
|
28071
|
-
};
|
|
28730
|
+
return loopCommand(node, 'while', {
|
|
28731
|
+
while: toCoreAST(node.condition),
|
|
28732
|
+
bottomTested: node.bottomTested
|
|
28733
|
+
? { type: 'literal', value: true, raw: 'true', ...POS }
|
|
28734
|
+
: undefined,
|
|
28735
|
+
});
|
|
28072
28736
|
}
|
|
28073
28737
|
function flattenEventModifiers(modifiers) {
|
|
28074
28738
|
if (!modifiers)
|