UNPKG

293 kBJavaScriptView Raw
1
2'use strict';
3
4/**
5 * @typedef {Object} Hyperscript
6 */
7
8(function (self, factory) {
9 const _hyperscript = factory(self)
10
11 if (typeof exports === 'object' && typeof exports['nodeName'] !== 'string') {
12 module.exports = _hyperscript
13 } else {
14 self['_hyperscript'] = _hyperscript
15 if ('document' in self) self['_hyperscript'].browserInit()
16 }
17})(typeof self !== 'undefined' ? self : this, (globalScope) => {
18
19 /**
20 * @type {Object}
21 * @property {DynamicConverter[]} dynamicResolvers
22 *
23 * @callback DynamicConverter
24 * @param {String} str
25 * @param {*} value
26 * @returns {*}
27 */
28 const conversions = {
29 dynamicResolvers: [
30 function(str, value){
31 if (str === "Fixed") {
32 return Number(value).toFixed();
33 } else if (str.indexOf("Fixed:") === 0) {
34 let num = str.split(":")[1];
35 return Number(value).toFixed(parseInt(num));
36 }
37 }
38 ],
39 String: function (val) {
40 if (val.toString) {
41 return val.toString();
42 } else {
43 return "" + val;
44 }
45 },
46 Int: function (val) {
47 return parseInt(val);
48 },
49 Float: function (val) {
50 return parseFloat(val);
51 },
52 Number: function (val) {
53 return Number(val);
54 },
55 Date: function (val) {
56 return new Date(val);
57 },
58 Array: function (val) {
59 return Array.from(val);
60 },
61 JSON: function (val) {
62 return JSON.stringify(val);
63 },
64 Object: function (val) {
65 if (val instanceof String) {
66 val = val.toString();
67 }
68 if (typeof val === "string") {
69 return JSON.parse(val);
70 } else {
71 return Object.assign({}, val);
72 }
73 },
74 }
75
76 const config = {
77 attributes: "_, script, data-script",
78 defaultTransition: "all 500ms ease-in",
79 disableSelector: "[disable-scripting], [data-disable-scripting]",
80 conversions,
81 }
82
83 class Lexer {
84 static OP_TABLE = {
85 "+": "PLUS",
86 "-": "MINUS",
87 "*": "MULTIPLY",
88 "/": "DIVIDE",
89 ".": "PERIOD",
90 "..": "ELLIPSIS",
91 "\\": "BACKSLASH",
92 ":": "COLON",
93 "%": "PERCENT",
94 "|": "PIPE",
95 "!": "EXCLAMATION",
96 "?": "QUESTION",
97 "#": "POUND",
98 "&": "AMPERSAND",
99 $: "DOLLAR",
100 ";": "SEMI",
101 ",": "COMMA",
102 "(": "L_PAREN",
103 ")": "R_PAREN",
104 "<": "L_ANG",
105 ">": "R_ANG",
106 "<=": "LTE_ANG",
107 ">=": "GTE_ANG",
108 "==": "EQ",
109 "===": "EQQ",
110 "!=": "NEQ",
111 "!==": "NEQQ",
112 "{": "L_BRACE",
113 "}": "R_BRACE",
114 "[": "L_BRACKET",
115 "]": "R_BRACKET",
116 "=": "EQUALS",
117 };
118
119 /**
120 * isValidCSSClassChar returns `true` if the provided character is valid in a CSS class.
121 * @param {string} c
122 * @returns boolean
123 */
124 static isValidCSSClassChar(c) {
125 return Lexer.isAlpha(c) || Lexer.isNumeric(c) || c === "-" || c === "_" || c === ":";
126 }
127
128 /**
129 * isValidCSSIDChar returns `true` if the provided character is valid in a CSS ID
130 * @param {string} c
131 * @returns boolean
132 */
133 static isValidCSSIDChar(c) {
134 return Lexer.isAlpha(c) || Lexer.isNumeric(c) || c === "-" || c === "_" || c === ":";
135 }
136
137 /**
138 * isWhitespace returns `true` if the provided character is whitespace.
139 * @param {string} c
140 * @returns boolean
141 */
142 static isWhitespace(c) {
143 return c === " " || c === "\t" || Lexer.isNewline(c);
144 }
145
146 /**
147 * positionString returns a string representation of a Token's line and column details.
148 * @param {Token} token
149 * @returns string
150 */
151 static positionString(token) {
152 return "[Line: " + token.line + ", Column: " + token.column + "]";
153 }
154
155 /**
156 * isNewline returns `true` if the provided character is a carrage return or newline
157 * @param {string} c
158 * @returns boolean
159 */
160 static isNewline(c) {
161 return c === "\r" || c === "\n";
162 }
163
164 /**
165 * isNumeric returns `true` if the provided character is a number (0-9)
166 * @param {string} c
167 * @returns boolean
168 */
169 static isNumeric(c) {
170 return c >= "0" && c <= "9";
171 }
172
173 /**
174 * isAlpha returns `true` if the provided character is a letter in the alphabet
175 * @param {string} c
176 * @returns boolean
177 */
178 static isAlpha(c) {
179 return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z");
180 }
181
182 /**
183 * @param {string} c
184 * @param {boolean} [dollarIsOp]
185 * @returns boolean
186 */
187 static isIdentifierChar(c, dollarIsOp) {
188 return c === "_" || c === "$";
189 }
190
191 /**
192 * @param {string} c
193 * @returns boolean
194 */
195 static isReservedChar(c) {
196 return c === "`" || c === "^";
197 }
198
199 /**
200 * @param {Token[]} tokens
201 * @returns {boolean}
202 */
203 static isValidSingleQuoteStringStart(tokens) {
204 if (tokens.length > 0) {
205 var previousToken = tokens[tokens.length - 1];
206 if (
207 previousToken.type === "IDENTIFIER" ||
208 previousToken.type === "CLASS_REF" ||
209 previousToken.type === "ID_REF"
210 ) {
211 return false;
212 }
213 if (previousToken.op && (previousToken.value === ">" || previousToken.value === ")")) {
214 return false;
215 }
216 }
217 return true;
218 }
219
220 /**
221 * @param {string} string
222 * @param {boolean} [template]
223 * @returns {Tokens}
224 */
225 static tokenize(string, template) {
226 var tokens = /** @type {Token[]}*/ [];
227 var source = string;
228 var position = 0;
229 var column = 0;
230 var line = 1;
231 var lastToken = "<START>";
232 var templateBraceCount = 0;
233
234 function inTemplate() {
235 return template && templateBraceCount === 0;
236 }
237
238 while (position < source.length) {
239 if (currentChar() === "-" && nextChar() === "-" && (Lexer.isWhitespace(charAfterThat()) || charAfterThat() === "")) {
240 consumeComment();
241 } else {
242 if (Lexer.isWhitespace(currentChar())) {
243 tokens.push(consumeWhitespace());
244 } else if (
245 !possiblePrecedingSymbol() &&
246 currentChar() === "." &&
247 (Lexer.isAlpha(nextChar()) || nextChar() === "{")
248 ) {
249 tokens.push(consumeClassReference());
250 } else if (
251 !possiblePrecedingSymbol() &&
252 currentChar() === "#" &&
253 (Lexer.isAlpha(nextChar()) || nextChar() === "{")
254 ) {
255 tokens.push(consumeIdReference());
256 } else if (currentChar() === "[" && nextChar() === "@") {
257 tokens.push(consumeAttributeReference());
258 } else if (currentChar() === "@") {
259 tokens.push(consumeShortAttributeReference());
260 } else if (currentChar() === "*" && Lexer.isAlpha(nextChar())) {
261 tokens.push(consumeStyleReference());
262 } else if (Lexer.isAlpha(currentChar()) || (!inTemplate() && Lexer.isIdentifierChar(currentChar()))) {
263 tokens.push(consumeIdentifier());
264 } else if (Lexer.isNumeric(currentChar())) {
265 tokens.push(consumeNumber());
266 } else if (!inTemplate() && (currentChar() === '"' || currentChar() === "`")) {
267 tokens.push(consumeString());
268 } else if (!inTemplate() && currentChar() === "'") {
269 if (Lexer.isValidSingleQuoteStringStart(tokens)) {
270 tokens.push(consumeString());
271 } else {
272 tokens.push(consumeOp());
273 }
274 } else if (Lexer.OP_TABLE[currentChar()]) {
275 if (lastToken === "$" && currentChar() === "{") {
276 templateBraceCount++;
277 }
278 if (currentChar() === "}") {
279 templateBraceCount--;
280 }
281 tokens.push(consumeOp());
282 } else if (inTemplate() || Lexer.isReservedChar(currentChar())) {
283 tokens.push(makeToken("RESERVED", consumeChar()));
284 } else {
285 if (position < source.length) {
286 throw Error("Unknown token: " + currentChar() + " ");
287 }
288 }
289 }
290 }
291
292 return new Tokens(tokens, [], source);
293
294 /**
295 * @param {string} [type]
296 * @param {string} [value]
297 * @returns {Token}
298 */
299 function makeOpToken(type, value) {
300 var token = makeToken(type, value);
301 token.op = true;
302 return token;
303 }
304
305 /**
306 * @param {string} [type]
307 * @param {string} [value]
308 * @returns {Token}
309 */
310 function makeToken(type, value) {
311 return {
312 type: type,
313 value: value,
314 start: position,
315 end: position + 1,
316 column: column,
317 line: line,
318 };
319 }
320
321 function consumeComment() {
322 while (currentChar() && !Lexer.isNewline(currentChar())) {
323 consumeChar();
324 }
325 consumeChar();
326 }
327
328 /**
329 * @returns Token
330 */
331 function consumeClassReference() {
332 var classRef = makeToken("CLASS_REF");
333 var value = consumeChar();
334 if (currentChar() === "{") {
335 classRef.template = true;
336 value += consumeChar();
337 while (currentChar() && currentChar() !== "}") {
338 value += consumeChar();
339 }
340 if (currentChar() !== "}") {
341 throw Error("Unterminated class reference");
342 } else {
343 value += consumeChar(); // consume final curly
344 }
345 } else {
346 while (Lexer.isValidCSSClassChar(currentChar())) {
347 value += consumeChar();
348 }
349 }
350 classRef.value = value;
351 classRef.end = position;
352 return classRef;
353 }
354
355 /**
356 * @returns Token
357 */
358 function consumeAttributeReference() {
359 var attributeRef = makeToken("ATTRIBUTE_REF");
360 var value = consumeChar();
361 while (position < source.length && currentChar() !== "]") {
362 value += consumeChar();
363 }
364 if (currentChar() === "]") {
365 value += consumeChar();
366 }
367 attributeRef.value = value;
368 attributeRef.end = position;
369 return attributeRef;
370 }
371
372 function consumeShortAttributeReference() {
373 var attributeRef = makeToken("ATTRIBUTE_REF");
374 var value = consumeChar();
375 while (Lexer.isValidCSSIDChar(currentChar())) {
376 value += consumeChar();
377 }
378 attributeRef.value = value;
379 attributeRef.end = position;
380 return attributeRef;
381 }
382
383 function consumeStyleReference() {
384 var styleRef = makeToken("STYLE_REF");
385 var value = consumeChar();
386 while (Lexer.isAlpha(currentChar()) || currentChar() === "-") {
387 value += consumeChar();
388 }
389 styleRef.value = value;
390 styleRef.end = position;
391 return styleRef;
392 }
393
394 /**
395 * @returns Token
396 */
397 function consumeIdReference() {
398 var idRef = makeToken("ID_REF");
399 var value = consumeChar();
400 if (currentChar() === "{") {
401 idRef.template = true;
402 value += consumeChar();
403 while (currentChar() && currentChar() !== "}") {
404 value += consumeChar();
405 }
406 if (currentChar() !== "}") {
407 throw Error("Unterminated id reference");
408 } else {
409 consumeChar(); // consume final quote
410 }
411 } else {
412 while (Lexer.isValidCSSIDChar(currentChar())) {
413 value += consumeChar();
414 }
415 }
416 idRef.value = value;
417 idRef.end = position;
418 return idRef;
419 }
420
421 /**
422 * @returns Token
423 */
424 function consumeIdentifier() {
425 var identifier = makeToken("IDENTIFIER");
426 var value = consumeChar();
427 while (Lexer.isAlpha(currentChar()) ||
428 Lexer.isNumeric(currentChar()) ||
429 Lexer.isIdentifierChar(currentChar())) {
430 value += consumeChar();
431 }
432 if (currentChar() === "!" && value === "beep") {
433 value += consumeChar();
434 }
435 identifier.value = value;
436 identifier.end = position;
437 return identifier;
438 }
439
440 /**
441 * @returns Token
442 */
443 function consumeNumber() {
444 var number = makeToken("NUMBER");
445 var value = consumeChar();
446 while (Lexer.isNumeric(currentChar())) {
447 value += consumeChar();
448 }
449 if (currentChar() === "." && Lexer.isNumeric(nextChar())) {
450 value += consumeChar();
451 }
452 while (Lexer.isNumeric(currentChar())) {
453 value += consumeChar();
454 }
455 number.value = value;
456 number.end = position;
457 return number;
458 }
459
460 /**
461 * @returns Token
462 */
463 function consumeOp() {
464 var op = makeOpToken();
465 var value = consumeChar(); // consume leading char
466 while (currentChar() && Lexer.OP_TABLE[value + currentChar()]) {
467 value += consumeChar();
468 }
469 op.type = Lexer.OP_TABLE[value];
470 op.value = value;
471 op.end = position;
472 return op;
473 }
474
475 /**
476 * @returns Token
477 */
478 function consumeString() {
479 var string = makeToken("STRING");
480 var startChar = consumeChar(); // consume leading quote
481 var value = "";
482 while (currentChar() && currentChar() !== startChar) {
483 if (currentChar() === "\\") {
484 consumeChar(); // consume escape char and get the next one
485 let nextChar = consumeChar();
486 if (nextChar === "b") {
487 value += "\b";
488 } else if (nextChar === "f") {
489 value += "\f";
490 } else if (nextChar === "n") {
491 value += "\n";
492 } else if (nextChar === "r") {
493 value += "\r";
494 } else if (nextChar === "t") {
495 value += "\t";
496 } else if (nextChar === "v") {
497 value += "\v";
498 } else {
499 value += nextChar;
500 }
501 } else {
502 value += consumeChar();
503 }
504 }
505 if (currentChar() !== startChar) {
506 throw Error("Unterminated string at " + Lexer.positionString(string));
507 } else {
508 consumeChar(); // consume final quote
509 }
510 string.value = value;
511 string.end = position;
512 string.template = startChar === "`";
513 return string;
514 }
515
516 /**
517 * @returns string
518 */
519 function currentChar() {
520 return source.charAt(position);
521 }
522
523 /**
524 * @returns string
525 */
526 function nextChar() {
527 return source.charAt(position + 1);
528 }
529
530 function charAfterThat() {
531 return source.charAt(position + 2);
532 }
533
534 /**
535 * @returns string
536 */
537 function consumeChar() {
538 lastToken = currentChar();
539 position++;
540 column++;
541 return lastToken;
542 }
543
544 /**
545 * @returns boolean
546 */
547 function possiblePrecedingSymbol() {
548 return (
549 Lexer.isAlpha(lastToken) ||
550 Lexer.isNumeric(lastToken) ||
551 lastToken === ")" ||
552 lastToken === "\"" ||
553 lastToken === "'" ||
554 lastToken === "`" ||
555 lastToken === "}" ||
556 lastToken === "]"
557 );
558 }
559
560 /**
561 * @returns Token
562 */
563 function consumeWhitespace() {
564 var whitespace = makeToken("WHITESPACE");
565 var value = "";
566 while (currentChar() && Lexer.isWhitespace(currentChar())) {
567 if (Lexer.isNewline(currentChar())) {
568 column = 0;
569 line++;
570 }
571 value += consumeChar();
572 }
573 whitespace.value = value;
574 whitespace.end = position;
575 return whitespace;
576 }
577 }
578
579 /**
580 * @param {string} string
581 * @param {boolean} [template]
582 * @returns {Tokens}
583 */
584 tokenize(string, template) {
585 return Lexer.tokenize(string, template)
586 }
587 }
588
589 /**
590 * @typedef {Object} Token
591 * @property {string} [type]
592 * @property {string} [value]
593 * @property {number} [start]
594 * @property {number} [end]
595 * @property {number} [column]
596 * @property {number} [line]
597 * @property {boolean} [op] `true` if this token represents an operator
598 * @property {boolean} [template] `true` if this token is a template, for class refs, id refs, strings
599 */
600
601 class Tokens {
602 constructor(tokens, consumed, source) {
603 this.tokens = tokens
604 this.consumed = consumed
605 this.source = source
606
607 this.consumeWhitespace(); // consume initial whitespace
608 }
609
610 get list() {
611 return this.tokens
612 }
613
614 /** @type Token | null */
615 _lastConsumed = null;
616
617 consumeWhitespace() {
618 while (this.token(0, true).type === "WHITESPACE") {
619 this.consumed.push(this.tokens.shift());
620 }
621 }
622
623 /**
624 * @param {Tokens} tokens
625 * @param {*} error
626 */
627 raiseError(tokens, error) {
628 Parser.raiseParseError(tokens, error);
629 }
630
631 /**
632 * @param {string} value
633 * @returns {Token}
634 */
635 requireOpToken(value) {
636 var token = this.matchOpToken(value);
637 if (token) {
638 return token;
639 } else {
640 this.raiseError(this, "Expected '" + value + "' but found '" + this.currentToken().value + "'");
641 }
642 }
643
644 /**
645 * @param {string} op1
646 * @param {string} [op2]
647 * @param {string} [op3]
648 * @returns {Token | void}
649 */
650 matchAnyOpToken(op1, op2, op3) {
651 for (var i = 0; i < arguments.length; i++) {
652 var opToken = arguments[i];
653 var match = this.matchOpToken(opToken);
654 if (match) {
655 return match;
656 }
657 }
658 }
659
660 /**
661 * @param {string} op1
662 * @param {string} [op2]
663 * @param {string} [op3]
664 * @returns {Token | void}
665 */
666 matchAnyToken(op1, op2, op3) {
667 for (var i = 0; i < arguments.length; i++) {
668 var opToken = arguments[i];
669 var match = this.matchToken(opToken);
670 if (match) {
671 return match;
672 }
673 }
674 }
675
676 /**
677 * @param {string} value
678 * @returns {Token | void}
679 */
680 matchOpToken(value) {
681 if (this.currentToken() && this.currentToken().op && this.currentToken().value === value) {
682 return this.consumeToken();
683 }
684 }
685
686 /**
687 * @param {string} type1
688 * @param {string} [type2]
689 * @param {string} [type3]
690 * @param {string} [type4]
691 * @returns {Token}
692 */
693 requireTokenType(type1, type2, type3, type4) {
694 var token = this.matchTokenType(type1, type2, type3, type4);
695 if (token) {
696 return token;
697 } else {
698 this.raiseError(this, "Expected one of " + JSON.stringify([type1, type2, type3]));
699 }
700 }
701
702 /**
703 * @param {string} type1
704 * @param {string} [type2]
705 * @param {string} [type3]
706 * @param {string} [type4]
707 * @returns {Token | void}
708 */
709 matchTokenType(type1, type2, type3, type4) {
710 if (
711 this.currentToken() &&
712 this.currentToken().type &&
713 [type1, type2, type3, type4].indexOf(this.currentToken().type) >= 0
714 ) {
715 return this.consumeToken();
716 }
717 }
718
719 /**
720 * @param {string} value
721 * @param {string} [type]
722 * @returns {Token}
723 */
724 requireToken(value, type) {
725 var token = this.matchToken(value, type);
726 if (token) {
727 return token;
728 } else {
729 this.raiseError(this, "Expected '" + value + "' but found '" + this.currentToken().value + "'");
730 }
731 }
732
733 peekToken(value, peek, type) {
734 return this.tokens[peek] && this.tokens[peek].value === value && this.tokens[peek].type === type
735 }
736
737 /**
738 * @param {string} value
739 * @param {string} [type]
740 * @returns {Token | void}
741 */
742 matchToken(value, type) {
743 if (this.follows.indexOf(value) !== -1) {
744 return; // disallowed token here
745 }
746 var type = type || "IDENTIFIER";
747 if (this.currentToken() && this.currentToken().value === value && this.currentToken().type === type) {
748 return this.consumeToken();
749 }
750 }
751
752 /**
753 * @returns {Token}
754 */
755 consumeToken() {
756 var match = this.tokens.shift();
757 this.consumed.push(match);
758 this._lastConsumed = match;
759 this.consumeWhitespace(); // consume any whitespace
760 return match;
761 }
762
763 /**
764 * @param {string} value
765 * @param {string} [type]
766 * @returns {Token[]}
767 */
768 consumeUntil(value, type) {
769 /** @type Token[] */
770 var tokenList = [];
771 var currentToken = this.token(0, true);
772
773 while (
774 (type == null || currentToken.type !== type) &&
775 (value == null || currentToken.value !== value) &&
776 currentToken.type !== "EOF"
777 ) {
778 var match = this.tokens.shift();
779 this.consumed.push(match);
780 tokenList.push(currentToken);
781 currentToken = this.token(0, true);
782 }
783 this.consumeWhitespace(); // consume any whitespace
784 return tokenList;
785 }
786
787 /**
788 * @returns {string}
789 */
790 lastWhitespace() {
791 if (this.consumed[this.consumed.length - 1] && this.consumed[this.consumed.length - 1].type === "WHITESPACE") {
792 return this.consumed[this.consumed.length - 1].value;
793 } else {
794 return "";
795 }
796 }
797
798 consumeUntilWhitespace() {
799 return this.consumeUntil(null, "WHITESPACE");
800 }
801
802 /**
803 * @returns {boolean}
804 */
805 hasMore() {
806 return this.tokens.length > 0;
807 }
808
809 /**
810 * @param {number} n
811 * @param {boolean} [dontIgnoreWhitespace]
812 * @returns {Token}
813 */
814 token(n, dontIgnoreWhitespace) {
815 var /**@type {Token}*/ token;
816 var i = 0;
817 do {
818 if (!dontIgnoreWhitespace) {
819 while (this.tokens[i] && this.tokens[i].type === "WHITESPACE") {
820 i++;
821 }
822 }
823 token = this.tokens[i];
824 n--;
825 i++;
826 } while (n > -1);
827 if (token) {
828 return token;
829 } else {
830 return {
831 type: "EOF",
832 value: "<<<EOF>>>",
833 };
834 }
835 }
836
837 /**
838 * @returns {Token}
839 */
840 currentToken() {
841 return this.token(0);
842 }
843
844 /**
845 * @returns {Token | null}
846 */
847 lastMatch() {
848 return this._lastConsumed;
849 }
850
851 /**
852 * @returns {string}
853 */
854 static sourceFor = function () {
855 return this.programSource.substring(this.startToken.start, this.endToken.end);
856 }
857
858 /**
859 * @returns {string}
860 */
861 static lineFor = function () {
862 return this.programSource.split("\n")[this.startToken.line - 1];
863 }
864
865 follows = [];
866
867 pushFollow(str) {
868 this.follows.push(str);
869 }
870
871 popFollow() {
872 this.follows.pop();
873 }
874
875 clearFollows() {
876 var tmp = this.follows;
877 this.follows = [];
878 return tmp;
879 }
880
881 restoreFollows(f) {
882 this.follows = f;
883 }
884 }
885
886 /**
887 * @callback ParseRule
888 * @param {Parser} parser
889 * @param {Runtime} runtime
890 * @param {Tokens} tokens
891 * @param {*} root
892 * @returns {ASTNode | undefined}
893 *
894 * @typedef {Object} ASTNode
895 * @member {boolean} isFeature
896 * @member {string} type
897 * @member {any[]} args
898 * @member {(this: ASTNode, ctx:Context, root:any, ...args:any) => any} op
899 * @member {(this: ASTNode, context?:Context) => any} evaluate
900 * @member {ASTNode} parent
901 * @member {Set<ASTNode>} children
902 * @member {ASTNode} root
903 * @member {String} keyword
904 * @member {Token} endToken
905 * @member {ASTNode} next
906 * @member {(context:Context) => ASTNode} resolveNext
907 * @member {EventSource} eventSource
908 * @member {(this: ASTNode) => void} install
909 * @member {(this: ASTNode, context:Context) => void} execute
910 * @member {(this: ASTNode, target: object, source: object, args?: Object) => void} apply
911 *
912 *
913 */
914
915 class Parser {
916 /**
917 *
918 * @param {Runtime} runtime
919 */
920 constructor(runtime) {
921 this.runtime = runtime
922
923 this.possessivesDisabled = false
924
925 /* ============================================================================================ */
926 /* Core hyperscript Grammar Elements */
927 /* ============================================================================================ */
928 this.addGrammarElement("feature", function (parser, runtime, tokens) {
929 if (tokens.matchOpToken("(")) {
930 var featureElement = parser.requireElement("feature", tokens);
931 tokens.requireOpToken(")");
932 return featureElement;
933 }
934
935 var featureDefinition = parser.FEATURES[tokens.currentToken().value];
936 if (featureDefinition) {
937 return featureDefinition(parser, runtime, tokens);
938 }
939 });
940
941 this.addGrammarElement("command", function (parser, runtime, tokens) {
942 if (tokens.matchOpToken("(")) {
943 const commandElement = parser.requireElement("command", tokens);
944 tokens.requireOpToken(")");
945 return commandElement;
946 }
947
948 var commandDefinition = parser.COMMANDS[tokens.currentToken().value];
949 let commandElement;
950 if (commandDefinition) {
951 commandElement = commandDefinition(parser, runtime, tokens);
952 } else if (tokens.currentToken().type === "IDENTIFIER") {
953 commandElement = parser.parseElement("pseudoCommand", tokens);
954 }
955 if (commandElement) {
956 return parser.parseElement("indirectStatement", tokens, commandElement);
957 }
958
959 return commandElement;
960 });
961
962 this.addGrammarElement("commandList", function (parser, runtime, tokens) {
963 var cmd = parser.parseElement("command", tokens);
964 if (cmd) {
965 tokens.matchToken("then");
966 const next = parser.parseElement("commandList", tokens);
967 if (next) cmd.next = next;
968 return cmd;
969 }
970 });
971
972 this.addGrammarElement("leaf", function (parser, runtime, tokens) {
973 var result = parser.parseAnyOf(parser.LEAF_EXPRESSIONS, tokens);
974 // symbol is last so it doesn't consume any constants
975 if (result == null) {
976 return parser.parseElement("symbol", tokens);
977 }
978
979 return result;
980 });
981
982 this.addGrammarElement("indirectExpression", function (parser, runtime, tokens, root) {
983 for (var i = 0; i < parser.INDIRECT_EXPRESSIONS.length; i++) {
984 var indirect = parser.INDIRECT_EXPRESSIONS[i];
985 root.endToken = tokens.lastMatch();
986 var result = parser.parseElement(indirect, tokens, root);
987 if (result) {
988 return result;
989 }
990 }
991 return root;
992 });
993
994 this.addGrammarElement("indirectStatement", function (parser, runtime, tokens, root) {
995 if (tokens.matchToken("unless")) {
996 root.endToken = tokens.lastMatch();
997 var conditional = parser.requireElement("expression", tokens);
998 var unless = {
999 type: "unlessStatementModifier",
1000 args: [conditional],
1001 op: function (context, conditional) {
1002 if (conditional) {
1003 return this.next;
1004 } else {
1005 return root;
1006 }
1007 },
1008 execute: function (context) {
1009 return runtime.unifiedExec(this, context);
1010 },
1011 };
1012 root.parent = unless;
1013 return unless;
1014 }
1015 return root;
1016 });
1017
1018 this.addGrammarElement("primaryExpression", function (parser, runtime, tokens) {
1019 var leaf = parser.parseElement("leaf", tokens);
1020 if (leaf) {
1021 return parser.parseElement("indirectExpression", tokens, leaf);
1022 }
1023 parser.raiseParseError(tokens, "Unexpected value: " + tokens.currentToken().value);
1024 });
1025 }
1026
1027 use(plugin) {
1028 plugin(this)
1029 return this
1030 }
1031
1032 /** @type {Object<string,ParseRule>} */
1033 GRAMMAR = {};
1034
1035 /** @type {Object<string,ParseRule>} */
1036 COMMANDS = {};
1037
1038 /** @type {Object<string,ParseRule>} */
1039 FEATURES = {};
1040
1041 /** @type {string[]} */
1042 LEAF_EXPRESSIONS = [];
1043 /** @type {string[]} */
1044 INDIRECT_EXPRESSIONS = [];
1045
1046 /**
1047 * @param {*} parseElement
1048 * @param {*} start
1049 * @param {Tokens} tokens
1050 */
1051 initElt(parseElement, start, tokens) {
1052 parseElement.startToken = start;
1053 parseElement.sourceFor = Tokens.sourceFor;
1054 parseElement.lineFor = Tokens.lineFor;
1055 parseElement.programSource = tokens.source;
1056 }
1057
1058 /**
1059 * @param {string} type
1060 * @param {Tokens} tokens
1061 * @param {ASTNode?} root
1062 * @returns {ASTNode}
1063 */
1064 parseElement(type, tokens, root = undefined) {
1065 var elementDefinition = this.GRAMMAR[type];
1066 if (elementDefinition) {
1067 var start = tokens.currentToken();
1068 var parseElement = elementDefinition(this, this.runtime, tokens, root);
1069 if (parseElement) {
1070 this.initElt(parseElement, start, tokens);
1071 parseElement.endToken = parseElement.endToken || tokens.lastMatch();
1072 var root = parseElement.root;
1073 while (root != null) {
1074 this.initElt(root, start, tokens);
1075 root = root.root;
1076 }
1077 }
1078 return parseElement;
1079 }
1080 }
1081
1082 /**
1083 * @param {string} type
1084 * @param {Tokens} tokens
1085 * @param {string} [message]
1086 * @param {*} [root]
1087 * @returns {ASTNode}
1088 */
1089 requireElement(type, tokens, message, root) {
1090 var result = this.parseElement(type, tokens, root);
1091 if (!result) Parser.raiseParseError(tokens, message || "Expected " + type);
1092 // @ts-ignore
1093 return result;
1094 }
1095
1096 /**
1097 * @param {string[]} types
1098 * @param {Tokens} tokens
1099 * @returns {ASTNode}
1100 */
1101 parseAnyOf(types, tokens) {
1102 for (var i = 0; i < types.length; i++) {
1103 var type = types[i];
1104 var expression = this.parseElement(type, tokens);
1105 if (expression) {
1106 return expression;
1107 }
1108 }
1109 }
1110
1111 /**
1112 * @param {string} name
1113 * @param {ParseRule} definition
1114 */
1115 addGrammarElement(name, definition) {
1116 this.GRAMMAR[name] = definition;
1117 }
1118
1119 /**
1120 * @param {string} keyword
1121 * @param {ParseRule} definition
1122 */
1123 addCommand(keyword, definition) {
1124 var commandGrammarType = keyword + "Command";
1125 var commandDefinitionWrapper = function (parser, runtime, tokens) {
1126 const commandElement = definition(parser, runtime, tokens);
1127 if (commandElement) {
1128 commandElement.type = commandGrammarType;
1129 commandElement.execute = function (context) {
1130 context.meta.command = commandElement;
1131 return runtime.unifiedExec(this, context);
1132 };
1133 return commandElement;
1134 }
1135 };
1136 this.GRAMMAR[commandGrammarType] = commandDefinitionWrapper;
1137 this.COMMANDS[keyword] = commandDefinitionWrapper;
1138 }
1139
1140 /**
1141 * @param {string} keyword
1142 * @param {ParseRule} definition
1143 */
1144 addFeature(keyword, definition) {
1145 var featureGrammarType = keyword + "Feature";
1146
1147 /** @type {ParseRule} */
1148 var featureDefinitionWrapper = function (parser, runtime, tokens) {
1149 var featureElement = definition(parser, runtime, tokens);
1150 if (featureElement) {
1151 featureElement.isFeature = true;
1152 featureElement.keyword = keyword;
1153 featureElement.type = featureGrammarType;
1154 return featureElement;
1155 }
1156 };
1157 this.GRAMMAR[featureGrammarType] = featureDefinitionWrapper;
1158 this.FEATURES[keyword] = featureDefinitionWrapper;
1159 }
1160
1161 /**
1162 * @param {string} name
1163 * @param {ParseRule} definition
1164 */
1165 addLeafExpression(name, definition) {
1166 this.LEAF_EXPRESSIONS.push(name);
1167 this.addGrammarElement(name, definition);
1168 }
1169
1170 /**
1171 * @param {string} name
1172 * @param {ParseRule} definition
1173 */
1174 addIndirectExpression(name, definition) {
1175 this.INDIRECT_EXPRESSIONS.push(name);
1176 this.addGrammarElement(name, definition);
1177 }
1178
1179 /**
1180 *
1181 * @param {Tokens} tokens
1182 * @returns string
1183 */
1184 static createParserContext(tokens) {
1185 var currentToken = tokens.currentToken();
1186 var source = tokens.source;
1187 var lines = source.split("\n");
1188 var line = currentToken && currentToken.line ? currentToken.line - 1 : lines.length - 1;
1189 var contextLine = lines[line];
1190 var offset = currentToken && currentToken.line ? currentToken.column : contextLine.length - 1;
1191 return contextLine + "\n" + " ".repeat(offset) + "^^\n\n";
1192 }
1193
1194 /**
1195 * @param {Tokens} tokens
1196 * @param {string} [message]
1197 */
1198 static raiseParseError(tokens, message) {
1199 message =
1200 (message || "Unexpected Token : " + tokens.currentToken().value) + "\n\n" + Parser.createParserContext(tokens);
1201 var error = new Error(message);
1202 error["tokens"] = tokens;
1203 throw error;
1204 }
1205
1206 /**
1207 * @param {Tokens} tokens
1208 * @param {string} [message]
1209 */
1210 raiseParseError(tokens, message) {
1211 Parser.raiseParseError(tokens, message)
1212 }
1213
1214 /**
1215 * @param {Tokens} tokens
1216 * @returns {ASTNode}
1217 */
1218 parseHyperScript(tokens) {
1219 var result = this.parseElement("hyperscript", tokens);
1220 if (tokens.hasMore()) this.raiseParseError(tokens);
1221 if (result) return result;
1222 }
1223
1224 /**
1225 * @param {ASTNode | undefined} elt
1226 * @param {ASTNode} parent
1227 */
1228 setParent(elt, parent) {
1229 if (typeof elt === 'object') {
1230 elt.parent = parent;
1231 if (typeof parent === 'object') {
1232 parent.children = (parent.children || new Set());
1233 parent.children.add(elt)
1234 }
1235 this.setParent(elt.next, parent);
1236 }
1237 }
1238
1239 /**
1240 * @param {Token} token
1241 * @returns {ParseRule}
1242 */
1243 commandStart(token) {
1244 return this.COMMANDS[token.value];
1245 }
1246
1247 /**
1248 * @param {Token} token
1249 * @returns {ParseRule}
1250 */
1251 featureStart(token) {
1252 return this.FEATURES[token.value];
1253 }
1254
1255 /**
1256 * @param {Token} token
1257 * @returns {boolean}
1258 */
1259 commandBoundary(token) {
1260 if (
1261 token.value == "end" ||
1262 token.value == "then" ||
1263 token.value == "else" ||
1264 token.value == "otherwise" ||
1265 token.value == ")" ||
1266 this.commandStart(token) ||
1267 this.featureStart(token) ||
1268 token.type == "EOF"
1269 ) {
1270 return true;
1271 }
1272 return false;
1273 }
1274
1275 /**
1276 * @param {Tokens} tokens
1277 * @returns {(string | ASTNode)[]}
1278 */
1279 parseStringTemplate(tokens) {
1280 /** @type {(string | ASTNode)[]} */
1281 var returnArr = [""];
1282 do {
1283 returnArr.push(tokens.lastWhitespace());
1284 if (tokens.currentToken().value === "$") {
1285 tokens.consumeToken();
1286 var startingBrace = tokens.matchOpToken("{");
1287 returnArr.push(this.requireElement("expression", tokens));
1288 if (startingBrace) {
1289 tokens.requireOpToken("}");
1290 }
1291 returnArr.push("");
1292 } else if (tokens.currentToken().value === "\\") {
1293 tokens.consumeToken(); // skip next
1294 tokens.consumeToken();
1295 } else {
1296 var token = tokens.consumeToken();
1297 returnArr[returnArr.length - 1] += token ? token.value : "";
1298 }
1299 } while (tokens.hasMore());
1300 returnArr.push(tokens.lastWhitespace());
1301 return returnArr;
1302 }
1303
1304 /**
1305 * @param {ASTNode} commandList
1306 */
1307 ensureTerminated(commandList) {
1308 const runtime = this.runtime
1309 var implicitReturn = {
1310 type: "implicitReturn",
1311 op: function (context) {
1312 context.meta.returned = true;
1313 if (context.meta.resolve) {
1314 context.meta.resolve();
1315 }
1316 return runtime.HALT;
1317 },
1318 execute: function (ctx) {
1319 // do nothing
1320 },
1321 };
1322
1323 var end = commandList;
1324 while (end.next) {
1325 end = end.next;
1326 }
1327 end.next = implicitReturn;
1328 }
1329 }
1330
1331 class Runtime {
1332 /**
1333 *
1334 * @param {Lexer} [lexer]
1335 * @param {Parser} [parser]
1336 */
1337 constructor(lexer, parser) {
1338 this.lexer = lexer ?? new Lexer;
1339 this.parser = parser ?? new Parser(this)
1340 .use(hyperscriptCoreGrammar)
1341 .use(hyperscriptWebGrammar);
1342 this.parser.runtime = this
1343 }
1344
1345 /**
1346 * @param {HTMLElement} elt
1347 * @param {string} selector
1348 * @returns boolean
1349 */
1350 matchesSelector(elt, selector) {
1351 // noinspection JSUnresolvedVariable
1352 var matchesFunction =
1353 // @ts-ignore
1354 elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector;
1355 return matchesFunction && matchesFunction.call(elt, selector);
1356 }
1357
1358 /**
1359 * @param {string} eventName
1360 * @param {Object} [detail]
1361 * @returns {Event}
1362 */
1363 makeEvent(eventName, detail) {
1364 var evt;
1365 if (globalScope.Event && typeof globalScope.Event === "function") {
1366 evt = new Event(eventName, {
1367 bubbles: true,
1368 cancelable: true,
1369 });
1370 evt['detail'] = detail;
1371 } else {
1372 evt = document.createEvent("CustomEvent");
1373 evt.initCustomEvent(eventName, true, true, detail);
1374 }
1375 return evt;
1376 }
1377
1378 /**
1379 * @param {Element} elt
1380 * @param {string} eventName
1381 * @param {Object} [detail]
1382 * @param {Element} [sender]
1383 * @returns {boolean}
1384 */
1385 triggerEvent(elt, eventName, detail, sender) {
1386 detail = detail || {};
1387 detail["sender"] = sender;
1388 var event = this.makeEvent(eventName, detail);
1389 var eventResult = elt.dispatchEvent(event);
1390 return eventResult;
1391 }
1392
1393 /**
1394 * isArrayLike returns `true` if the provided value is an array or
1395 * a NodeList (which is close enough to being an array for our purposes).
1396 *
1397 * @param {any} value
1398 * @returns {value is Array | NodeList}
1399 */
1400 isArrayLike(value) {
1401 return Array.isArray(value) ||
1402 (typeof NodeList !== 'undefined' && (value instanceof NodeList || value instanceof HTMLCollection));
1403 }
1404
1405 /**
1406 * isIterable returns `true` if the provided value supports the
1407 * iterator protocol.
1408 *
1409 * @param {any} value
1410 * @returns {value is Iterable}
1411 */
1412 isIterable(value) {
1413 return typeof value === 'object'
1414 && Symbol.iterator in value
1415 && typeof value[Symbol.iterator] === 'function';
1416 }
1417
1418 /**
1419 * shouldAutoIterate returns `true` if the provided value
1420 * should be implicitly iterated over when accessing properties,
1421 * and as the target of some commands.
1422 *
1423 * Currently, this is when the value is an {ElementCollection}
1424 * or {isArrayLike} returns true.
1425 *
1426 * @param {any} value
1427 * @returns {value is (any[] | ElementCollection)}
1428 */
1429 shouldAutoIterate(value) {
1430 return value[shouldAutoIterateSymbol] ||
1431 this.isArrayLike(value);
1432 }
1433
1434 /**
1435 * forEach executes the provided `func` on every item in the `value` array.
1436 * if `value` is a single item (and not an array) then `func` is simply called
1437 * once. If `value` is null, then no further actions are taken.
1438 *
1439 * @template T
1440 * @param {T | Iterable<T>} value
1441 * @param {(item: T) => void} func
1442 */
1443 forEach(value, func) {
1444 if (value == null) {
1445 // do nothing
1446 } else if (this.isIterable(value)) {
1447 for (const nth of value) {
1448 func(nth);
1449 }
1450 } else if (this.isArrayLike(value)) {
1451 for (var i = 0; i < value.length; i++) {
1452 func(value[i]);
1453 }
1454 } else {
1455 func(value);
1456 }
1457 }
1458
1459 /**
1460 * implicitLoop executes the provided `func` on:
1461 * - every item of {value}, if {value} should be auto-iterated
1462 * (see {shouldAutoIterate})
1463 * - {value} otherwise
1464 *
1465 * @template T
1466 * @param {ElementCollection | T | T[]} value
1467 * @param {(item: T) => void} func
1468 */
1469 implicitLoop(value, func) {
1470 if (this.shouldAutoIterate(value)) {
1471 for (const x of value) func(x);
1472 } else {
1473 func(value);
1474 }
1475 }
1476
1477 wrapArrays(args) {
1478 var arr = [];
1479 for (var i = 0; i < args.length; i++) {
1480 var arg = args[i];
1481 if (Array.isArray(arg)) {
1482 arr.push(Promise.all(arg));
1483 } else {
1484 arr.push(arg);
1485 }
1486 }
1487 return arr;
1488 }
1489
1490 unwrapAsyncs(values) {
1491 for (var i = 0; i < values.length; i++) {
1492 var value = values[i];
1493 if (value.asyncWrapper) {
1494 values[i] = value.value;
1495 }
1496 if (Array.isArray(value)) {
1497 for (var j = 0; j < value.length; j++) {
1498 var valueElement = value[j];
1499 if (valueElement.asyncWrapper) {
1500 value[j] = valueElement.value;
1501 }
1502 }
1503 }
1504 }
1505 }
1506
1507 static HALT = {};
1508 HALT = Runtime.HALT;
1509
1510 /**
1511 * @param {ASTNode} command
1512 * @param {Context} ctx
1513 */
1514 unifiedExec(command, ctx) {
1515 while (true) {
1516 try {
1517 var next = this.unifiedEval(command, ctx);
1518 } catch (e) {
1519 if (ctx.meta.handlingFinally) {
1520 console.error(" Exception in finally block: ", e);
1521 next = Runtime.HALT;
1522 } else {
1523 this.registerHyperTrace(ctx, e);
1524 if (ctx.meta.errorHandler && !ctx.meta.handlingError) {
1525 ctx.meta.handlingError = true;
1526 ctx.locals[ctx.meta.errorSymbol] = e;
1527 command = ctx.meta.errorHandler;
1528 continue;
1529 } else {
1530 ctx.meta.currentException = e;
1531 next = Runtime.HALT;
1532 }
1533 }
1534 }
1535 if (next == null) {
1536 console.error(command, " did not return a next element to execute! context: ", ctx);
1537 return;
1538 } else if (next.then) {
1539 next.then(resolvedNext => {
1540 this.unifiedExec(resolvedNext, ctx);
1541 }).catch(reason => {
1542 this.unifiedExec({ // Anonymous command to simply throw the exception
1543 op: function(){
1544 throw reason;
1545 }
1546 }, ctx);
1547 });
1548 return;
1549 } else if (next === Runtime.HALT) {
1550 if (ctx.meta.finallyHandler && !ctx.meta.handlingFinally) {
1551 ctx.meta.handlingFinally = true;
1552 command = ctx.meta.finallyHandler;
1553 } else {
1554 if (ctx.meta.onHalt) {
1555 ctx.meta.onHalt();
1556 }
1557 if (ctx.meta.currentException) {
1558 if (ctx.meta.reject) {
1559 ctx.meta.reject(ctx.meta.currentException);
1560 return;
1561 } else {
1562 throw ctx.meta.currentException;
1563 }
1564 } else {
1565 return;
1566 }
1567 }
1568 } else {
1569 command = next; // move to the next command
1570 }
1571 }
1572 }
1573
1574 /**
1575 * @param {*} parseElement
1576 * @param {Context} ctx
1577 * @returns {*}
1578 */
1579 unifiedEval(parseElement, ctx) {
1580 /** @type any[] */
1581 var args = [ctx];
1582 var async = false;
1583 var wrappedAsyncs = false;
1584
1585 if (parseElement.args) {
1586 for (var i = 0; i < parseElement.args.length; i++) {
1587 var argument = parseElement.args[i];
1588 if (argument == null) {
1589 args.push(null);
1590 } else if (Array.isArray(argument)) {
1591 var arr = [];
1592 for (var j = 0; j < argument.length; j++) {
1593 var element = argument[j];
1594 var value = element ? element.evaluate(ctx) : null; // OK
1595 if (value) {
1596 if (value.then) {
1597 async = true;
1598 } else if (value.asyncWrapper) {
1599 wrappedAsyncs = true;
1600 }
1601 }
1602 arr.push(value);
1603 }
1604 args.push(arr);
1605 } else if (argument.evaluate) {
1606 var value = argument.evaluate(ctx); // OK
1607 if (value) {
1608 if (value.then) {
1609 async = true;
1610 } else if (value.asyncWrapper) {
1611 wrappedAsyncs = true;
1612 }
1613 }
1614 args.push(value);
1615 } else {
1616 args.push(argument);
1617 }
1618 }
1619 }
1620 if (async) {
1621 return new Promise((resolve, reject) => {
1622 args = this.wrapArrays(args);
1623 Promise.all(args)
1624 .then(function (values) {
1625 if (wrappedAsyncs) {
1626 this.unwrapAsyncs(values);
1627 }
1628 try {
1629 var apply = parseElement.op.apply(parseElement, values);
1630 resolve(apply);
1631 } catch (e) {
1632 reject(e);
1633 }
1634 })
1635 .catch(function (reason) {
1636 reject(reason);
1637 });
1638 });
1639 } else {
1640 if (wrappedAsyncs) {
1641 this.unwrapAsyncs(args);
1642 }
1643 return parseElement.op.apply(parseElement, args);
1644 }
1645 }
1646
1647 _scriptAttrs = null;
1648
1649 /**
1650 * getAttributes returns the attribute name(s) to use when
1651 * locating hyperscript scripts in a DOM element. If no value
1652 * has been configured, it defaults to config.attributes
1653 * @returns string[]
1654 */
1655 getScriptAttributes() {
1656 if (this._scriptAttrs == null) {
1657 this._scriptAttrs = config.attributes.replace(/ /g, "").split(",");
1658 }
1659 return this._scriptAttrs;
1660 }
1661
1662 /**
1663 * @param {Element} elt
1664 * @returns {string | null}
1665 */
1666 getScript(elt) {
1667 for (var i = 0; i < this.getScriptAttributes().length; i++) {
1668 var scriptAttribute = this.getScriptAttributes()[i];
1669 if (elt.hasAttribute && elt.hasAttribute(scriptAttribute)) {
1670 return elt.getAttribute(scriptAttribute);
1671 }
1672 }
1673 if (elt instanceof HTMLScriptElement && elt.type === "text/hyperscript") {
1674 return elt.innerText;
1675 }
1676 return null;
1677 }
1678
1679 hyperscriptFeaturesMap = new WeakMap
1680
1681 /**
1682 * @param {*} elt
1683 * @returns {Object}
1684 */
1685 getHyperscriptFeatures(elt) {
1686 var hyperscriptFeatures = this.hyperscriptFeaturesMap.get(elt);
1687 if (typeof hyperscriptFeatures === 'undefined') {
1688 if (elt) {
1689 // in some rare cases, elt is null and this line crashes
1690 this.hyperscriptFeaturesMap.set(elt, hyperscriptFeatures = {});
1691 }
1692 }
1693 return hyperscriptFeatures;
1694 }
1695
1696 /**
1697 * @param {Object} owner
1698 * @param {Context} ctx
1699 */
1700 addFeatures(owner, ctx) {
1701 if (owner) {
1702 Object.assign(ctx.locals, this.getHyperscriptFeatures(owner));
1703 this.addFeatures(owner.parentElement, ctx);
1704 }
1705 }
1706
1707 /**
1708 * @param {*} owner
1709 * @param {*} feature
1710 * @param {*} hyperscriptTarget
1711 * @param {*} event
1712 * @returns {Context}
1713 */
1714 makeContext(owner, feature, hyperscriptTarget, event) {
1715 return new Context(owner, feature, hyperscriptTarget, event, this)
1716 }
1717
1718 /**
1719 * @returns string
1720 */
1721 getScriptSelector() {
1722 return this.getScriptAttributes()
1723 .map(function (attribute) {
1724 return "[" + attribute + "]";
1725 })
1726 .join(", ");
1727 }
1728
1729 /**
1730 * @param {any} value
1731 * @param {string} type
1732 * @returns {any}
1733 */
1734 convertValue(value, type) {
1735 var dynamicResolvers = conversions.dynamicResolvers;
1736 for (var i = 0; i < dynamicResolvers.length; i++) {
1737 var dynamicResolver = dynamicResolvers[i];
1738 var converted = dynamicResolver(type, value);
1739 if (converted !== undefined) {
1740 return converted;
1741 }
1742 }
1743
1744 if (value == null) {
1745 return null;
1746 }
1747 var converter = conversions[type];
1748 if (converter) {
1749 return converter(value);
1750 }
1751
1752 throw "Unknown conversion : " + type;
1753 }
1754
1755 /**
1756 * @param {string} src
1757 * @returns {ASTNode}
1758 */
1759 parse(src) {
1760 const lexer = this.lexer, parser = this.parser
1761 var tokens = lexer.tokenize(src);
1762 if (this.parser.commandStart(tokens.currentToken())) {
1763 var commandList = parser.requireElement("commandList", tokens);
1764 if (tokens.hasMore()) parser.raiseParseError(tokens);
1765 parser.ensureTerminated(commandList);
1766 return commandList;
1767 } else if (parser.featureStart(tokens.currentToken())) {
1768 var hyperscript = parser.requireElement("hyperscript", tokens);
1769 if (tokens.hasMore()) parser.raiseParseError(tokens);
1770 return hyperscript;
1771 } else {
1772 var expression = parser.requireElement("expression", tokens);
1773 if (tokens.hasMore()) parser.raiseParseError(tokens);
1774 return expression;
1775 }
1776 }
1777
1778 /**
1779 *
1780 * @param {ASTNode} elt
1781 * @param {Context} ctx
1782 * @returns {any}
1783 */
1784 evaluateNoPromise(elt, ctx) {
1785 let result = elt.evaluate(ctx);
1786 if (result.next) {
1787 throw new Error(Tokens.sourceFor.call(elt) + " returned a Promise in a context that they are not allowed.");
1788 }
1789 return result;
1790 }
1791
1792 /**
1793 * @param {string} src
1794 * @param {Partial<Context>} [ctx]
1795 * @param {Object} [args]
1796 * @returns {any}
1797 */
1798 evaluate(src, ctx, args) {
1799 class HyperscriptModule extends EventTarget {
1800 constructor(mod) {
1801 super();
1802 this.module = mod;
1803 }
1804 toString() {
1805 return this.module.id;
1806 }
1807 }
1808
1809 var body = 'document' in globalScope
1810 ? globalScope.document.body
1811 : new HyperscriptModule(args && args.module);
1812 ctx = Object.assign(this.makeContext(body, null, body, null), ctx || {});
1813 var element = this.parse(src);
1814 if (element.execute) {
1815 element.execute(ctx);
1816 return ctx.result;
1817 } else if (element.apply) {
1818 element.apply(body, body, args);
1819 return this.getHyperscriptFeatures(body);
1820 } else {
1821 return element.evaluate(ctx);
1822 }
1823
1824 function makeModule() {
1825 return {}
1826 }
1827 }
1828
1829 /**
1830 * @param {HTMLElement} elt
1831 */
1832 processNode(elt) {
1833 var selector = this.getScriptSelector();
1834 if (this.matchesSelector(elt, selector)) {
1835 this.initElement(elt, elt);
1836 }
1837 if (elt instanceof HTMLScriptElement && elt.type === "text/hyperscript") {
1838 this.initElement(elt, document.body);
1839 }
1840 if (elt.querySelectorAll) {
1841 this.forEach(elt.querySelectorAll(selector + ", [type='text/hyperscript']"), elt => {
1842 this.initElement(elt, elt instanceof HTMLScriptElement && elt.type === "text/hyperscript" ? document.body : elt);
1843 });
1844 }
1845 }
1846
1847 /**
1848 * @param {Element} elt
1849 * @param {Element} [target]
1850 */
1851 initElement(elt, target) {
1852 if (elt.closest && elt.closest(config.disableSelector)) {
1853 return;
1854 }
1855 var internalData = this.getInternalData(elt);
1856 if (!internalData.initialized) {
1857 var src = this.getScript(elt);
1858 if (src) {
1859 try {
1860 internalData.initialized = true;
1861 internalData.script = src;
1862 const lexer = this.lexer, parser = this.parser
1863 var tokens = lexer.tokenize(src);
1864 var hyperScript = parser.parseHyperScript(tokens);
1865 if (!hyperScript) return;
1866 hyperScript.apply(target || elt, elt);
1867 setTimeout(() => {
1868 this.triggerEvent(target || elt, "load", {
1869 hyperscript: true,
1870 });
1871 }, 1);
1872 } catch (e) {
1873 this.triggerEvent(elt, "exception", {
1874 error: e,
1875 });
1876 console.error(
1877 "hyperscript errors were found on the following element:",
1878 elt,
1879 "\n\n",
1880 e.message,
1881 e.stack
1882 );
1883 }
1884 }
1885 }
1886 }
1887
1888 internalDataMap = new WeakMap
1889
1890 /**
1891 * @param {Element} elt
1892 * @returns {Object}
1893 */
1894 getInternalData(elt) {
1895 var internalData = this.internalDataMap.get(elt);
1896 if (typeof internalData === 'undefined') {
1897 this.internalDataMap.set(elt, internalData = {});
1898 }
1899 return internalData;
1900 }
1901
1902 /**
1903 * @param {any} value
1904 * @param {string} typeString
1905 * @param {boolean} [nullOk]
1906 * @returns {boolean}
1907 */
1908 typeCheck(value, typeString, nullOk) {
1909 if (value == null && nullOk) {
1910 return true;
1911 }
1912 var typeName = Object.prototype.toString.call(value).slice(8, -1);
1913 return typeName === typeString;
1914 }
1915
1916 getElementScope(context) {
1917 var elt = context.meta && context.meta.owner;
1918 if (elt) {
1919 var internalData = this.getInternalData(elt);
1920 var scopeName = "elementScope";
1921 if (context.meta.feature && context.meta.feature.behavior) {
1922 scopeName = context.meta.feature.behavior + "Scope";
1923 }
1924 var elementScope = getOrInitObject(internalData, scopeName);
1925 return elementScope;
1926 } else {
1927 return {}; // no element, return empty scope
1928 }
1929 }
1930
1931 /**
1932 * @param {string} str
1933 * @returns {boolean}
1934 */
1935 isReservedWord(str) {
1936 return ["meta", "it", "result", "locals", "event", "target", "detail", "sender", "body"].includes(str)
1937 }
1938
1939 /**
1940 * @param {any} context
1941 * @returns {boolean}
1942 */
1943 isHyperscriptContext(context) {
1944 return context instanceof Context;
1945 }
1946
1947 /**
1948 * @param {string} str
1949 * @param {Context} context
1950 * @returns {any}
1951 */
1952 resolveSymbol(str, context, type) {
1953 if (str === "me" || str === "my" || str === "I") {
1954 return context.me;
1955 }
1956 if (str === "it" || str === "its" || str === "result") {
1957 return context.result;
1958 }
1959 if (str === "you" || str === "your" || str === "yourself") {
1960 return context.you;
1961 } else {
1962 if (type === "global") {
1963 return globalScope[str];
1964 } else if (type === "element") {
1965 var elementScope = this.getElementScope(context);
1966 return elementScope[str];
1967 } else if (type === "local") {
1968 return context.locals[str];
1969 } else {
1970 // meta scope (used for event conditionals)
1971 if (context.meta && context.meta.context) {
1972 var fromMetaContext = context.meta.context[str];
1973 if (typeof fromMetaContext !== "undefined") {
1974 return fromMetaContext;
1975 }
1976 }
1977 if (this.isHyperscriptContext(context) && !this.isReservedWord(str)) {
1978 // local scope
1979 var fromContext = context.locals[str];
1980 } else {
1981 // direct get from normal JS object or top-level of context
1982 var fromContext = context[str];
1983 }
1984 if (typeof fromContext !== "undefined") {
1985 return fromContext;
1986 } else {
1987 // element scope
1988 var elementScope = this.getElementScope(context);
1989 fromContext = elementScope[str];
1990 if (typeof fromContext !== "undefined") {
1991 return fromContext;
1992 } else {
1993 // global scope
1994 return globalScope[str];
1995 }
1996 }
1997 }
1998 }
1999 }
2000
2001 setSymbol(str, context, type, value) {
2002 if (type === "global") {
2003 globalScope[str] = value;
2004 } else if (type === "element") {
2005 var elementScope = this.getElementScope(context);
2006 elementScope[str] = value;
2007 } else if (type === "local") {
2008 context.locals[str] = value;
2009 } else {
2010 if (this.isHyperscriptContext(context) && !this.isReservedWord(str) && typeof context.locals[str] !== "undefined") {
2011 // local scope
2012 context.locals[str] = value;
2013 } else {
2014 // element scope
2015 var elementScope = this.getElementScope(context);
2016 var fromContext = elementScope[str];
2017 if (typeof fromContext !== "undefined") {
2018 elementScope[str] = value;
2019 } else {
2020 if (this.isHyperscriptContext(context) && !this.isReservedWord(str)) {
2021 // local scope
2022 context.locals[str] = value;
2023 } else {
2024 // direct set on normal JS object or top-level of context
2025 context[str] = value;
2026 }
2027 }
2028 }
2029 }
2030 }
2031
2032 /**
2033 * @param {ASTNode} command
2034 * @param {Context} context
2035 * @returns {undefined | ASTNode}
2036 */
2037 findNext(command, context) {
2038 if (command) {
2039 if (command.resolveNext) {
2040 return command.resolveNext(context);
2041 } else if (command.next) {
2042 return command.next;
2043 } else {
2044 return this.findNext(command.parent, context);
2045 }
2046 }
2047 }
2048
2049 /**
2050 * @param {Object<string,any>} root
2051 * @param {string} property
2052 * @param {Getter} getter
2053 * @returns {any}
2054 *
2055 * @callback Getter
2056 * @param {Object<string,any>} root
2057 * @param {string} property
2058 */
2059 flatGet(root, property, getter) {
2060 if (root != null) {
2061 var val = getter(root, property);
2062 if (typeof val !== "undefined") {
2063 return val;
2064 }
2065
2066 if (this.shouldAutoIterate(root)) {
2067 // flat map
2068 var result = [];
2069 for (var component of root) {
2070 var componentValue = getter(component, property);
2071 result.push(componentValue);
2072 }
2073 return result;
2074 }
2075 }
2076 }
2077
2078 resolveProperty(root, property) {
2079 return this.flatGet(root, property, (root, property) => root[property] )
2080 }
2081
2082 resolveAttribute(root, property) {
2083 return this.flatGet(root, property, (root, property) => root.getAttribute && root.getAttribute(property) )
2084 }
2085
2086 /**
2087 *
2088 * @param {Object<string, any>} root
2089 * @param {string} property
2090 * @returns {string}
2091 */
2092 resolveStyle(root, property) {
2093 return this.flatGet(root, property, (root, property) => root.style && root.style[property] )
2094 }
2095
2096 /**
2097 *
2098 * @param {Object<string, any>} root
2099 * @param {string} property
2100 * @returns {string}
2101 */
2102 resolveComputedStyle(root, property) {
2103 return this.flatGet(root, property, (root, property) => getComputedStyle(
2104 /** @type {Element} */ (root)).getPropertyValue(property) )
2105 }
2106
2107 /**
2108 * @param {Element} elt
2109 * @param {string[]} nameSpace
2110 * @param {string} name
2111 * @param {any} value
2112 */
2113 assignToNamespace(elt, nameSpace, name, value) {
2114 let root
2115 if (typeof document !== "undefined" && elt === document.body) {
2116 root = globalScope;
2117 } else {
2118 root = this.getHyperscriptFeatures(elt);
2119 }
2120 while (nameSpace.length > 0) {
2121 var propertyName = nameSpace.shift();
2122 var newRoot = root[propertyName];
2123 if (newRoot == null) {
2124 newRoot = {};
2125 root[propertyName] = newRoot;
2126 }
2127 root = newRoot;
2128 }
2129
2130 root[name] = value;
2131 }
2132
2133 getHyperTrace(ctx, thrown) {
2134 var trace = [];
2135 var root = ctx;
2136 while (root.meta.caller) {
2137 root = root.meta.caller;
2138 }
2139 if (root.meta.traceMap) {
2140 return root.meta.traceMap.get(thrown, trace);
2141 }
2142 }
2143
2144 registerHyperTrace(ctx, thrown) {
2145 var trace = [];
2146 var root = null;
2147 while (ctx != null) {
2148 trace.push(ctx);
2149 root = ctx;
2150 ctx = ctx.meta.caller;
2151 }
2152 if (root.meta.traceMap == null) {
2153 root.meta.traceMap = new Map(); // TODO - WeakMap?
2154 }
2155 if (!root.meta.traceMap.get(thrown)) {
2156 var traceEntry = {
2157 trace: trace,
2158 print: function (logger) {
2159 logger = logger || console.error;
2160 logger("hypertrace /// ");
2161 var maxLen = 0;
2162 for (var i = 0; i < trace.length; i++) {
2163 maxLen = Math.max(maxLen, trace[i].meta.feature.displayName.length);
2164 }
2165 for (var i = 0; i < trace.length; i++) {
2166 var traceElt = trace[i];
2167 logger(
2168 " ->",
2169 traceElt.meta.feature.displayName.padEnd(maxLen + 2),
2170 "-",
2171 traceElt.meta.owner
2172 );
2173 }
2174 },
2175 };
2176 root.meta.traceMap.set(thrown, traceEntry);
2177 }
2178 }
2179
2180 /**
2181 * @param {string} str
2182 * @returns {string}
2183 */
2184 escapeSelector(str) {
2185 return str.replace(/:/g, function (str) {
2186 return "\\" + str;
2187 });
2188 }
2189
2190 /**
2191 * @param {any} value
2192 * @param {*} elt
2193 */
2194 nullCheck(value, elt) {
2195 if (value == null) {
2196 throw new Error("'" + elt.sourceFor() + "' is null");
2197 }
2198 }
2199
2200 /**
2201 * @param {any} value
2202 * @returns {boolean}
2203 */
2204 isEmpty(value) {
2205 return value == undefined || value.length === 0;
2206 }
2207
2208 /**
2209 * @param {any} value
2210 * @returns {boolean}
2211 */
2212 doesExist(value) {
2213 if(value == null){
2214 return false;
2215 }
2216 if (this.shouldAutoIterate(value)) {
2217 for (const elt of value) {
2218 return true;
2219 }
2220 }
2221 return false;
2222 }
2223
2224 /**
2225 * @param {Node} node
2226 * @returns {Document|ShadowRoot}
2227 */
2228 getRootNode(node) {
2229 if (node && node instanceof Node) {
2230 var rv = node.getRootNode();
2231 if (rv instanceof Document || rv instanceof ShadowRoot) return rv;
2232 }
2233 return document;
2234 }
2235
2236 /**
2237 *
2238 * @param {Element} elt
2239 * @param {ASTNode} onFeature
2240 * @returns {EventQueue}
2241 *
2242 * @typedef {{queue:Array, executing:boolean}} EventQueue
2243 */
2244 getEventQueueFor(elt, onFeature) {
2245 let internalData = this.getInternalData(elt);
2246 var eventQueuesForElt = internalData.eventQueues;
2247 if (eventQueuesForElt == null) {
2248 eventQueuesForElt = new Map();
2249 internalData.eventQueues = eventQueuesForElt;
2250 }
2251 var eventQueueForFeature = eventQueuesForElt.get(onFeature);
2252 if (eventQueueForFeature == null) {
2253 eventQueueForFeature = {queue:[], executing:false};
2254 eventQueuesForElt.set(onFeature, eventQueueForFeature);
2255 }
2256 return eventQueueForFeature;
2257 }
2258
2259 /** @type string | null */
2260 // @ts-ignore
2261 hyperscriptUrl = "document" in globalScope ? document.currentScript.src : null;
2262 }
2263
2264 class Context {
2265 /**
2266 * @param {*} owner
2267 * @param {*} feature
2268 * @param {*} hyperscriptTarget
2269 * @param {*} event
2270 */
2271 constructor(owner, feature, hyperscriptTarget, event, runtime) {
2272 this.meta = {
2273 parser: runtime.parser,
2274 lexer: runtime.lexer,
2275 runtime,
2276 owner: owner,
2277 feature: feature,
2278 iterators: {},
2279 ctx: this
2280 }
2281 this.locals = {};
2282 this.me = hyperscriptTarget,
2283 this.you = undefined
2284 this.result = undefined
2285 this.event = event;
2286 this.target = event ? event.target : null;
2287 this.detail = event ? event.detail : null;
2288 this.sender = event ? event.detail ? event.detail.sender : null : null;
2289 this.body = "document" in globalScope ? document.body : null;
2290 runtime.addFeatures(owner, this);
2291 }
2292 }
2293
2294 class ElementCollection {
2295 constructor(css, relativeToElement, escape) {
2296 this._css = css;
2297 this.relativeToElement = relativeToElement;
2298 this.escape = escape;
2299 this[shouldAutoIterateSymbol] = true;
2300 }
2301
2302 get css() {
2303 if (this.escape) {
2304 return Runtime.prototype.escapeSelector(this._css);
2305 } else {
2306 return this._css;
2307 }
2308 }
2309
2310 get className() {
2311 return this._css.substr(1);
2312 }
2313
2314 get id() {
2315 return this.className();
2316 }
2317
2318 contains(elt) {
2319 for (let element of this) {
2320 if (element.contains(elt)) {
2321 return true;
2322 }
2323 }
2324 return false;
2325 }
2326
2327 get length() {
2328 return this.selectMatches().length;
2329 }
2330
2331 [Symbol.iterator]() {
2332 let query = this.selectMatches();
2333 return query [Symbol.iterator]();
2334 }
2335
2336 selectMatches() {
2337 let query = Runtime.prototype.getRootNode(this.relativeToElement).querySelectorAll(this.css);
2338 return query;
2339 }
2340 }
2341
2342 const shouldAutoIterateSymbol = Symbol()
2343
2344 function getOrInitObject(root, prop) {
2345 var value = root[prop];
2346 if (value) {
2347 return value;
2348 } else {
2349 var newObj = {};
2350 root[prop] = newObj;
2351 return newObj;
2352 }
2353 }
2354
2355 /**
2356 * parseJSON parses a JSON string into a corresponding value. If the
2357 * value passed in is not valid JSON, then it logs an error and returns `null`.
2358 *
2359 * @param {string} jString
2360 * @returns any
2361 */
2362 function parseJSON(jString) {
2363 try {
2364 return JSON.parse(jString);
2365 } catch (error) {
2366 logError(error);
2367 return null;
2368 }
2369 }
2370
2371 /**
2372 * logError writes an error message to the Javascript console. It can take any
2373 * value, but msg should commonly be a simple string.
2374 * @param {*} msg
2375 */
2376 function logError(msg) {
2377 if (console.error) {
2378 console.error(msg);
2379 } else if (console.log) {
2380 console.log("ERROR: ", msg);
2381 }
2382 }
2383
2384 // TODO: JSDoc description of what's happening here
2385 function varargConstructor(Cls, args) {
2386 return new (Cls.bind.apply(Cls, [Cls].concat(args)))();
2387 }
2388
2389 // Grammar
2390
2391 /**
2392 * @param {Parser} parser
2393 */
2394 function hyperscriptCoreGrammar(parser) {
2395 parser.addLeafExpression("parenthesized", function (parser, _runtime, tokens) {
2396 if (tokens.matchOpToken("(")) {
2397 var follows = tokens.clearFollows();
2398 try {
2399 var expr = parser.requireElement("expression", tokens);
2400 } finally {
2401 tokens.restoreFollows(follows);
2402 }
2403 tokens.requireOpToken(")");
2404 return expr;
2405 }
2406 });
2407
2408 parser.addLeafExpression("string", function (parser, runtime, tokens) {
2409 var stringToken = tokens.matchTokenType("STRING");
2410 if (!stringToken) return;
2411 var rawValue = stringToken.value;
2412 /** @type {any[]} */
2413 var args;
2414 if (stringToken.template) {
2415 var innerTokens = Lexer.tokenize(rawValue, true);
2416 args = parser.parseStringTemplate(innerTokens);
2417 } else {
2418 args = [];
2419 }
2420 return {
2421 type: "string",
2422 token: stringToken,
2423 args: args,
2424 op: function (context) {
2425 var returnStr = "";
2426 for (var i = 1; i < arguments.length; i++) {
2427 var val = arguments[i];
2428 if (val !== undefined) {
2429 returnStr += val;
2430 }
2431 }
2432 return returnStr;
2433 },
2434 evaluate: function (context) {
2435 if (args.length === 0) {
2436 return rawValue;
2437 } else {
2438 return runtime.unifiedEval(this, context);
2439 }
2440 },
2441 };
2442 });
2443
2444 parser.addGrammarElement("nakedString", function (parser, runtime, tokens) {
2445 if (tokens.hasMore()) {
2446 var tokenArr = tokens.consumeUntilWhitespace();
2447 tokens.matchTokenType("WHITESPACE");
2448 return {
2449 type: "nakedString",
2450 tokens: tokenArr,
2451 evaluate: function (context) {
2452 return tokenArr
2453 .map(function (t) {
2454 return t.value;
2455 })
2456 .join("");
2457 },
2458 };
2459 }
2460 });
2461
2462 parser.addLeafExpression("number", function (parser, runtime, tokens) {
2463 var number = tokens.matchTokenType("NUMBER");
2464 if (!number) return;
2465 var numberToken = number;
2466 var value = parseFloat(number.value);
2467 return {
2468 type: "number",
2469 value: value,
2470 numberToken: numberToken,
2471 evaluate: function () {
2472 return value;
2473 },
2474 };
2475 });
2476
2477 parser.addLeafExpression("idRef", function (parser, runtime, tokens) {
2478 var elementId = tokens.matchTokenType("ID_REF");
2479 if (!elementId) return;
2480 // TODO - unify these two expression types
2481 if (elementId.template) {
2482 var templateValue = elementId.value.substr(2, elementId.value.length - 2);
2483 var innerTokens = Lexer.tokenize(templateValue);
2484 var innerExpression = parser.requireElement("expression", innerTokens);
2485 return {
2486 type: "idRefTemplate",
2487 args: [innerExpression],
2488 op: function (context, arg) {
2489 return runtime.getRootNode(context.me).getElementById(arg);
2490 },
2491 evaluate: function (context) {
2492 return runtime.unifiedEval(this, context);
2493 },
2494 };
2495 } else {
2496 const value = elementId.value.substr(1);
2497 return {
2498 type: "idRef",
2499 css: elementId.value,
2500 value: value,
2501 evaluate: function (context) {
2502 return (
2503 runtime.getRootNode(context.me).getElementById(value)
2504 );
2505 },
2506 };
2507 }
2508 });
2509
2510 parser.addLeafExpression("classRef", function (parser, runtime, tokens) {
2511 var classRef = tokens.matchTokenType("CLASS_REF");
2512
2513 if (!classRef) return;
2514
2515 // TODO - unify these two expression types
2516 if (classRef.template) {
2517 var templateValue = classRef.value.substr(2, classRef.value.length - 2);
2518 var innerTokens = Lexer.tokenize(templateValue);
2519 var innerExpression = parser.requireElement("expression", innerTokens);
2520 return {
2521 type: "classRefTemplate",
2522 args: [innerExpression],
2523 op: function (context, arg) {
2524 return new ElementCollection("." + arg, context.me, true)
2525 },
2526 evaluate: function (context) {
2527 return runtime.unifiedEval(this, context);
2528 },
2529 };
2530 } else {
2531 const css = classRef.value;
2532 return {
2533 type: "classRef",
2534 css: css,
2535 evaluate: function (context) {
2536 return new ElementCollection(css, context.me, true)
2537 },
2538 };
2539 }
2540 });
2541
2542 class TemplatedQueryElementCollection extends ElementCollection {
2543 constructor(css, relativeToElement, templateParts) {
2544 super(css, relativeToElement);
2545 this.templateParts = templateParts;
2546 this.elements = templateParts.filter(elt => elt instanceof Element);
2547 }
2548
2549 get css() {
2550 let rv = "", i = 0
2551 for (const val of this.templateParts) {
2552 if (val instanceof Element) {
2553 rv += "[data-hs-query-id='" + i++ + "']";
2554 } else rv += val;
2555 }
2556 return rv;
2557 }
2558
2559 [Symbol.iterator]() {
2560 this.elements.forEach((el, i) => el.dataset.hsQueryId = i);
2561 const rv = super[Symbol.iterator]();
2562 this.elements.forEach(el => el.removeAttribute('data-hs-query-id'));
2563 return rv;
2564 }
2565 }
2566
2567 parser.addLeafExpression("queryRef", function (parser, runtime, tokens) {
2568 var queryStart = tokens.matchOpToken("<");
2569 if (!queryStart) return;
2570 var queryTokens = tokens.consumeUntil("/");
2571 tokens.requireOpToken("/");
2572 tokens.requireOpToken(">");
2573 var queryValue = queryTokens
2574 .map(function (t) {
2575 if (t.type === "STRING") {
2576 return '"' + t.value + '"';
2577 } else {
2578 return t.value;
2579 }
2580 })
2581 .join("");
2582
2583 if (queryValue.indexOf("$") >= 0) {
2584 var template = true;
2585 var innerTokens = Lexer.tokenize(queryValue, true);
2586 var args = parser.parseStringTemplate(innerTokens);
2587 }
2588
2589 return {
2590 type: "queryRef",
2591 css: queryValue,
2592 args: args,
2593 op: function (context, ...args) {
2594 if (template) {
2595 return new TemplatedQueryElementCollection(queryValue, context.me, args)
2596 } else {
2597 return new ElementCollection(queryValue, context.me)
2598 }
2599 },
2600 evaluate: function (context) {
2601 return runtime.unifiedEval(this, context);
2602 },
2603 };
2604 });
2605
2606 parser.addLeafExpression("attributeRef", function (parser, runtime, tokens) {
2607 var attributeRef = tokens.matchTokenType("ATTRIBUTE_REF");
2608 if (!attributeRef) return;
2609 var outerVal = attributeRef.value;
2610 if (outerVal.indexOf("[") === 0) {
2611 var innerValue = outerVal.substring(2, outerVal.length - 1);
2612 } else {
2613 var innerValue = outerVal.substring(1);
2614 }
2615 var css = "[" + innerValue + "]";
2616 var split = innerValue.split("=");
2617 var name = split[0];
2618 var value = split[1];
2619 if (value) {
2620 // strip quotes
2621 if (value.indexOf('"') === 0) {
2622 value = value.substring(1, value.length - 1);
2623 }
2624 }
2625 return {
2626 type: "attributeRef",
2627 name: name,
2628 css: css,
2629 value: value,
2630 op: function (context) {
2631 var target = context.you || context.me;
2632 if (target) {
2633 return target.getAttribute(name);
2634 }
2635 },
2636 evaluate: function (context) {
2637 return runtime.unifiedEval(this, context);
2638 },
2639 };
2640 });
2641
2642 parser.addLeafExpression("styleRef", function (parser, runtime, tokens) {
2643 var styleRef = tokens.matchTokenType("STYLE_REF");
2644 if (!styleRef) return;
2645 var styleProp = styleRef.value.substr(1);
2646 if (styleProp.startsWith("computed-")) {
2647 styleProp = styleProp.substr("computed-".length);
2648 return {
2649 type: "computedStyleRef",
2650 name: styleProp,
2651 op: function (context) {
2652 var target = context.you || context.me;
2653 if (target) {
2654 return runtime.resolveComputedStyle(target, styleProp);
2655 }
2656 },
2657 evaluate: function (context) {
2658 return runtime.unifiedEval(this, context);
2659 },
2660 };
2661 } else {
2662 return {
2663 type: "styleRef",
2664 name: styleProp,
2665 op: function (context) {
2666 var target = context.you || context.me;
2667 if (target) {
2668 return runtime.resolveStyle(target, styleProp);
2669 }
2670 },
2671 evaluate: function (context) {
2672 return runtime.unifiedEval(this, context);
2673 },
2674 };
2675 }
2676 });
2677
2678 parser.addGrammarElement("objectKey", function (parser, runtime, tokens) {
2679 var token;
2680 if ((token = tokens.matchTokenType("STRING"))) {
2681 return {
2682 type: "objectKey",
2683 key: token.value,
2684 evaluate: function () {
2685 return token.value;
2686 },
2687 };
2688 } else if (tokens.matchOpToken("[")) {
2689 var expr = parser.parseElement("expression", tokens);
2690 tokens.requireOpToken("]");
2691 return {
2692 type: "objectKey",
2693 expr: expr,
2694 args: [expr],
2695 op: function (ctx, expr) {
2696 return expr;
2697 },
2698 evaluate: function (context) {
2699 return runtime.unifiedEval(this, context);
2700 },
2701 };
2702 } else {
2703 var key = "";
2704 do {
2705 token = tokens.matchTokenType("IDENTIFIER") || tokens.matchOpToken("-");
2706 if (token) key += token.value;
2707 } while (token);
2708 return {
2709 type: "objectKey",
2710 key: key,
2711 evaluate: function () {
2712 return key;
2713 },
2714 };
2715 }
2716 });
2717
2718 parser.addLeafExpression("objectLiteral", function (parser, runtime, tokens) {
2719 if (!tokens.matchOpToken("{")) return;
2720 var keyExpressions = [];
2721 var valueExpressions = [];
2722 if (!tokens.matchOpToken("}")) {
2723 do {
2724 var name = parser.requireElement("objectKey", tokens);
2725 tokens.requireOpToken(":");
2726 var value = parser.requireElement("expression", tokens);
2727 valueExpressions.push(value);
2728 keyExpressions.push(name);
2729 } while (tokens.matchOpToken(","));
2730 tokens.requireOpToken("}");
2731 }
2732 return {
2733 type: "objectLiteral",
2734 args: [keyExpressions, valueExpressions],
2735 op: function (context, keys, values) {
2736 var returnVal = {};
2737 for (var i = 0; i < keys.length; i++) {
2738 returnVal[keys[i]] = values[i];
2739 }
2740 return returnVal;
2741 },
2742 evaluate: function (context) {
2743 return runtime.unifiedEval(this, context);
2744 },
2745 };
2746 });
2747
2748 parser.addGrammarElement("nakedNamedArgumentList", function (parser, runtime, tokens) {
2749 var fields = [];
2750 var valueExpressions = [];
2751 if (tokens.currentToken().type === "IDENTIFIER") {
2752 do {
2753 var name = tokens.requireTokenType("IDENTIFIER");
2754 tokens.requireOpToken(":");
2755 var value = parser.requireElement("expression", tokens);
2756 valueExpressions.push(value);
2757 fields.push({ name: name, value: value });
2758 } while (tokens.matchOpToken(","));
2759 }
2760 return {
2761 type: "namedArgumentList",
2762 fields: fields,
2763 args: [valueExpressions],
2764 op: function (context, values) {
2765 var returnVal = { _namedArgList_: true };
2766 for (var i = 0; i < values.length; i++) {
2767 var field = fields[i];
2768 returnVal[field.name.value] = values[i];
2769 }
2770 return returnVal;
2771 },
2772 evaluate: function (context) {
2773 return runtime.unifiedEval(this, context);
2774 },
2775 };
2776 });
2777
2778 parser.addGrammarElement("namedArgumentList", function (parser, runtime, tokens) {
2779 if (!tokens.matchOpToken("(")) return;
2780 var elt = parser.requireElement("nakedNamedArgumentList", tokens);
2781 tokens.requireOpToken(")");
2782 return elt;
2783 });
2784
2785 parser.addGrammarElement("symbol", function (parser, runtime, tokens) {
2786 /** @scope {SymbolScope} */
2787 var scope = "default";
2788 if (tokens.matchToken("global")) {
2789 scope = "global";
2790 } else if (tokens.matchToken("element") || tokens.matchToken("module")) {
2791 scope = "element";
2792 // optional possessive
2793 if (tokens.matchOpToken("'")) {
2794 tokens.requireToken("s");
2795 }
2796 } else if (tokens.matchToken("local")) {
2797 scope = "local";
2798 }
2799
2800 // TODO better look ahead here
2801 let eltPrefix = tokens.matchOpToken(":");
2802 let identifier = tokens.matchTokenType("IDENTIFIER");
2803 if (identifier) {
2804 var name = identifier.value;
2805 if (eltPrefix) {
2806 name = ":" + name;
2807 }
2808 if (scope === "default") {
2809 if (name.indexOf("$") === 0) {
2810 scope = "global";
2811 }
2812 if (name.indexOf(":") === 0) {
2813 scope = "element";
2814 }
2815 }
2816 return {
2817 type: "symbol",
2818 token: identifier,
2819 scope: scope,
2820 name: name,
2821 evaluate: function (context) {
2822 return runtime.resolveSymbol(name, context, scope);
2823 },
2824 };
2825 }
2826 });
2827
2828 parser.addGrammarElement("implicitMeTarget", function (parser, runtime, tokens) {
2829 return {
2830 type: "implicitMeTarget",
2831 evaluate: function (context) {
2832 return context.you || context.me;
2833 },
2834 };
2835 });
2836
2837 parser.addLeafExpression("boolean", function (parser, runtime, tokens) {
2838 var booleanLiteral = tokens.matchToken("true") || tokens.matchToken("false");
2839 if (!booleanLiteral) return;
2840 const value = booleanLiteral.value === "true";
2841 return {
2842 type: "boolean",
2843 evaluate: function (context) {
2844 return value;
2845 },
2846 };
2847 });
2848
2849 parser.addLeafExpression("null", function (parser, runtime, tokens) {
2850 if (tokens.matchToken("null")) {
2851 return {
2852 type: "null",
2853 evaluate: function (context) {
2854 return null;
2855 },
2856 };
2857 }
2858 });
2859
2860 parser.addLeafExpression("arrayLiteral", function (parser, runtime, tokens) {
2861 if (!tokens.matchOpToken("[")) return;
2862 var values = [];
2863 if (!tokens.matchOpToken("]")) {
2864 do {
2865 var expr = parser.requireElement("expression", tokens);
2866 values.push(expr);
2867 } while (tokens.matchOpToken(","));
2868 tokens.requireOpToken("]");
2869 }
2870 return {
2871 type: "arrayLiteral",
2872 values: values,
2873 args: [values],
2874 op: function (context, values) {
2875 return values;
2876 },
2877 evaluate: function (context) {
2878 return runtime.unifiedEval(this, context);
2879 },
2880 };
2881 });
2882
2883 parser.addLeafExpression("blockLiteral", function (parser, runtime, tokens) {
2884 if (!tokens.matchOpToken("\\")) return;
2885 var args = [];
2886 var arg1 = tokens.matchTokenType("IDENTIFIER");
2887 if (arg1) {
2888 args.push(arg1);
2889 while (tokens.matchOpToken(",")) {
2890 args.push(tokens.requireTokenType("IDENTIFIER"));
2891 }
2892 }
2893 // TODO compound op token
2894 tokens.requireOpToken("-");
2895 tokens.requireOpToken(">");
2896 var expr = parser.requireElement("expression", tokens);
2897 return {
2898 type: "blockLiteral",
2899 args: args,
2900 expr: expr,
2901 evaluate: function (ctx) {
2902 var returnFunc = function () {
2903 //TODO - push scope
2904 for (var i = 0; i < args.length; i++) {
2905 ctx.locals[args[i].value] = arguments[i];
2906 }
2907 return expr.evaluate(ctx); //OK
2908 };
2909 return returnFunc;
2910 },
2911 };
2912 });
2913
2914 parser.addIndirectExpression("propertyAccess", function (parser, runtime, tokens, root) {
2915 if (!tokens.matchOpToken(".")) return;
2916 var prop = tokens.requireTokenType("IDENTIFIER");
2917 var propertyAccess = {
2918 type: "propertyAccess",
2919 root: root,
2920 prop: prop,
2921 args: [root],
2922 op: function (_context, rootVal) {
2923 var value = runtime.resolveProperty(rootVal, prop.value);
2924 return value;
2925 },
2926 evaluate: function (context) {
2927 return runtime.unifiedEval(this, context);
2928 },
2929 };
2930 return parser.parseElement("indirectExpression", tokens, propertyAccess);
2931 });
2932
2933 parser.addIndirectExpression("of", function (parser, runtime, tokens, root) {
2934 if (!tokens.matchToken("of")) return;
2935 var newRoot = parser.requireElement("unaryExpression", tokens);
2936 // find the urroot
2937 var childOfUrRoot = null;
2938 var urRoot = root;
2939 while (urRoot.root) {
2940 childOfUrRoot = urRoot;
2941 urRoot = urRoot.root;
2942 }
2943 if (urRoot.type !== "symbol" && urRoot.type !== "attributeRef" && urRoot.type !== "styleRef" && urRoot.type !== "computedStyleRef") {
2944 parser.raiseParseError(tokens, "Cannot take a property of a non-symbol: " + urRoot.type);
2945 }
2946 var attribute = urRoot.type === "attributeRef";
2947 var style = urRoot.type === "styleRef" || urRoot.type === "computedStyleRef";
2948 if (attribute || style) {
2949 var attributeElt = urRoot
2950 }
2951 var prop = urRoot.name;
2952
2953 var propertyAccess = {
2954 type: "ofExpression",
2955 prop: urRoot.token,
2956 root: newRoot,
2957 attribute: attributeElt,
2958 expression: root,
2959 args: [newRoot],
2960 op: function (context, rootVal) {
2961 if (attribute) {
2962 return runtime.resolveAttribute(rootVal, prop);
2963 } else if (style) {
2964 if (urRoot.type === "computedStyleRef") {
2965 return runtime.resolveComputedStyle(rootVal, prop);
2966 } else {
2967 return runtime.resolveStyle(rootVal, prop);
2968 }
2969 } else {
2970 return runtime.resolveProperty(rootVal, prop);
2971 }
2972 },
2973 evaluate: function (context) {
2974 return runtime.unifiedEval(this, context);
2975 },
2976 };
2977
2978 if (urRoot.type === "attributeRef") {
2979 propertyAccess.attribute = urRoot;
2980 }
2981 if (childOfUrRoot) {
2982 childOfUrRoot.root = propertyAccess;
2983 childOfUrRoot.args = [propertyAccess];
2984 } else {
2985 root = propertyAccess;
2986 }
2987
2988 return parser.parseElement("indirectExpression", tokens, root);
2989 });
2990
2991 parser.addIndirectExpression("possessive", function (parser, runtime, tokens, root) {
2992 if (parser.possessivesDisabled) {
2993 return;
2994 }
2995 var apostrophe = tokens.matchOpToken("'");
2996 if (
2997 apostrophe ||
2998 (root.type === "symbol" &&
2999 (root.name === "my" || root.name === "its" || root.name === "your") &&
3000 (tokens.currentToken().type === "IDENTIFIER" || tokens.currentToken().type === "ATTRIBUTE_REF" || tokens.currentToken().type === "STYLE_REF"))
3001 ) {
3002 if (apostrophe) {
3003 tokens.requireToken("s");
3004 }
3005 var attribute = parser.parseElement("attributeRef", tokens);
3006 if (attribute == null) {
3007 var style = parser.parseElement("styleRef", tokens);
3008 if (style == null) {
3009 var prop = tokens.requireTokenType("IDENTIFIER");
3010 }
3011 }
3012 var propertyAccess = {
3013 type: "possessive",
3014 root: root,
3015 attribute: attribute || style,
3016 prop: prop,
3017 args: [root],
3018 op: function (context, rootVal) {
3019 if (attribute) {
3020 // @ts-ignore
3021 var value = runtime.resolveAttribute(rootVal, attribute.name);
3022 } else if (style) {
3023 var value
3024 if (style.type === 'computedStyleRef') {
3025 value = runtime.resolveComputedStyle(rootVal, style['name']);
3026 } else {
3027 value = runtime.resolveStyle(rootVal, style['name']);
3028 }
3029 } else {
3030 var value = runtime.resolveProperty(rootVal, prop.value);
3031 }
3032 return value;
3033 },
3034 evaluate: function (context) {
3035 return runtime.unifiedEval(this, context);
3036 },
3037 };
3038 return parser.parseElement("indirectExpression", tokens, propertyAccess);
3039 }
3040 });
3041
3042 parser.addIndirectExpression("inExpression", function (parser, runtime, tokens, root) {
3043 if (!tokens.matchToken("in")) return;
3044 var target = parser.requireElement("unaryExpression", tokens);
3045 var propertyAccess = {
3046 type: "inExpression",
3047 root: root,
3048 args: [root, target],
3049 op: function (context, rootVal, target) {
3050 var returnArr = [];
3051 if (rootVal.css) {
3052 runtime.implicitLoop(target, function (targetElt) {
3053 var results = targetElt.querySelectorAll(rootVal.css);
3054 for (var i = 0; i < results.length; i++) {
3055 returnArr.push(results[i]);
3056 }
3057 });
3058 } else if (rootVal instanceof Element) {
3059 var within = false;
3060 runtime.implicitLoop(target, function (targetElt) {
3061 if (targetElt.contains(rootVal)) {
3062 within = true;
3063 }
3064 });
3065 if(within) {
3066 return rootVal;
3067 }
3068 } else {
3069 runtime.implicitLoop(rootVal, function (rootElt) {
3070 runtime.implicitLoop(target, function (targetElt) {
3071 if (rootElt === targetElt) {
3072 returnArr.push(rootElt);
3073 }
3074 });
3075 });
3076 }
3077 return returnArr;
3078 },
3079 evaluate: function (context) {
3080 return runtime.unifiedEval(this, context);
3081 },
3082 };
3083 return parser.parseElement("indirectExpression", tokens, propertyAccess);
3084 });
3085
3086 parser.addIndirectExpression("asExpression", function (parser, runtime, tokens, root) {
3087 if (!tokens.matchToken("as")) return;
3088 tokens.matchToken("a") || tokens.matchToken("an");
3089 var conversion = parser.requireElement("dotOrColonPath", tokens).evaluate(); // OK No promise
3090 var propertyAccess = {
3091 type: "asExpression",
3092 root: root,
3093 args: [root],
3094 op: function (context, rootVal) {
3095 return runtime.convertValue(rootVal, conversion);
3096 },
3097 evaluate: function (context) {
3098 return runtime.unifiedEval(this, context);
3099 },
3100 };
3101 return parser.parseElement("indirectExpression", tokens, propertyAccess);
3102 });
3103
3104 parser.addIndirectExpression("functionCall", function (parser, runtime, tokens, root) {
3105 if (!tokens.matchOpToken("(")) return;
3106 var args = [];
3107 if (!tokens.matchOpToken(")")) {
3108 do {
3109 args.push(parser.requireElement("expression", tokens));
3110 } while (tokens.matchOpToken(","));
3111 tokens.requireOpToken(")");
3112 }
3113
3114 if (root.root) {
3115 var functionCall = {
3116 type: "functionCall",
3117 root: root,
3118 argExressions: args,
3119 args: [root.root, args],
3120 op: function (context, rootRoot, args) {
3121 runtime.nullCheck(rootRoot, root.root);
3122 var func = rootRoot[root.prop.value];
3123 runtime.nullCheck(func, root);
3124 if (func.hyperfunc) {
3125 args.push(context);
3126 }
3127 return func.apply(rootRoot, args);
3128 },
3129 evaluate: function (context) {
3130 return runtime.unifiedEval(this, context);
3131 },
3132 };
3133 } else {
3134 var functionCall = {
3135 type: "functionCall",
3136 root: root,
3137 argExressions: args,
3138 args: [root, args],
3139 op: function (context, func, argVals) {
3140 runtime.nullCheck(func, root);
3141 if (func.hyperfunc) {
3142 argVals.push(context);
3143 }
3144 var apply = func.apply(null, argVals);
3145 return apply;
3146 },
3147 evaluate: function (context) {
3148 return runtime.unifiedEval(this, context);
3149 },
3150 };
3151 }
3152 return parser.parseElement("indirectExpression", tokens, functionCall);
3153 });
3154
3155 parser.addIndirectExpression("attributeRefAccess", function (parser, runtime, tokens, root) {
3156 var attribute = parser.parseElement("attributeRef", tokens);
3157 if (!attribute) return;
3158 var attributeAccess = {
3159 type: "attributeRefAccess",
3160 root: root,
3161 attribute: attribute,
3162 args: [root],
3163 op: function (_ctx, rootVal) {
3164 // @ts-ignore
3165 var value = runtime.resolveAttribute(rootVal, attribute.name);
3166 return value;
3167 },
3168 evaluate: function (context) {
3169 return runtime.unifiedEval(this, context);
3170 },
3171 };
3172 return attributeAccess;
3173 });
3174
3175 parser.addIndirectExpression("arrayIndex", function (parser, runtime, tokens, root) {
3176 if (!tokens.matchOpToken("[")) return;
3177 var andBefore = false;
3178 var andAfter = false;
3179 var firstIndex = null;
3180 var secondIndex = null;
3181
3182 if (tokens.matchOpToken("..")) {
3183 andBefore = true;
3184 firstIndex = parser.requireElement("expression", tokens);
3185 } else {
3186 firstIndex = parser.requireElement("expression", tokens);
3187
3188 if (tokens.matchOpToken("..")) {
3189 andAfter = true;
3190 var current = tokens.currentToken();
3191 if (current.type !== "R_BRACKET") {
3192 secondIndex = parser.parseElement("expression", tokens);
3193 }
3194 }
3195 }
3196 tokens.requireOpToken("]");
3197
3198 var arrayIndex = {
3199 type: "arrayIndex",
3200 root: root,
3201 prop: firstIndex,
3202 firstIndex: firstIndex,
3203 secondIndex: secondIndex,
3204 args: [root, firstIndex, secondIndex],
3205 op: function (_ctx, root, firstIndex, secondIndex) {
3206 if (root == null) {
3207 return null;
3208 }
3209 if (andBefore) {
3210 if (firstIndex < 0) {
3211 firstIndex = root.length + firstIndex;
3212 }
3213 return root.slice(0, firstIndex + 1); // returns all items from beginning to firstIndex (inclusive)
3214 } else if (andAfter) {
3215 if (secondIndex != null) {
3216 if (secondIndex < 0) {
3217 secondIndex = root.length + secondIndex;
3218 }
3219 return root.slice(firstIndex, secondIndex + 1); // returns all items from firstIndex to secondIndex (inclusive)
3220 } else {
3221 return root.slice(firstIndex); // returns from firstIndex to end of array
3222 }
3223 } else {
3224 return root[firstIndex];
3225 }
3226 },
3227 evaluate: function (context) {
3228 return runtime.unifiedEval(this, context);
3229 },
3230 };
3231
3232 return parser.parseElement("indirectExpression", tokens, arrayIndex);
3233 });
3234
3235 // taken from https://drafts.csswg.org/css-values-4/#relative-length
3236 // and https://drafts.csswg.org/css-values-4/#absolute-length
3237 // (NB: we do not support `in` dues to conflicts w/ the hyperscript grammar)
3238 var STRING_POSTFIXES = [
3239 'em', 'ex', 'cap', 'ch', 'ic', 'rem', 'lh', 'rlh', 'vw', 'vh', 'vi', 'vb', 'vmin', 'vmax',
3240 'cm', 'mm', 'Q', 'pc', 'pt', 'px'
3241 ];
3242 parser.addGrammarElement("postfixExpression", function (parser, runtime, tokens) {
3243 var root = parser.parseElement("primaryExpression", tokens);
3244
3245 let stringPosfix = tokens.matchAnyToken.apply(tokens, STRING_POSTFIXES) || tokens.matchOpToken("%");
3246 if (stringPosfix) {
3247 return {
3248 type: "stringPostfix",
3249 postfix: stringPosfix.value,
3250 args: [root],
3251 op: function (context, val) {
3252 return "" + val + stringPosfix.value;
3253 },
3254 evaluate: function (context) {
3255 return runtime.unifiedEval(this, context);
3256 },
3257 };
3258 }
3259
3260 var timeFactor = null;
3261 if (tokens.matchToken("s") || tokens.matchToken("seconds")) {
3262 timeFactor = 1000;
3263 } else if (tokens.matchToken("ms") || tokens.matchToken("milliseconds")) {
3264 timeFactor = 1;
3265 }
3266 if (timeFactor) {
3267 return {
3268 type: "timeExpression",
3269 time: root,
3270 factor: timeFactor,
3271 args: [root],
3272 op: function (_context, val) {
3273 return val * timeFactor;
3274 },
3275 evaluate: function (context) {
3276 return runtime.unifiedEval(this, context);
3277 },
3278 };
3279 }
3280
3281 if (tokens.matchOpToken(":")) {
3282 var typeName = tokens.requireTokenType("IDENTIFIER");
3283 var nullOk = !tokens.matchOpToken("!");
3284 return {
3285 type: "typeCheck",
3286 typeName: typeName,
3287 nullOk: nullOk,
3288 args: [root],
3289 op: function (context, val) {
3290 var passed = runtime.typeCheck(val, typeName.value, nullOk);
3291 if (passed) {
3292 return val;
3293 } else {
3294 throw new Error("Typecheck failed! Expected: " + typeName.value);
3295 }
3296 },
3297 evaluate: function (context) {
3298 return runtime.unifiedEval(this, context);
3299 },
3300 };
3301 } else {
3302 return root;
3303 }
3304 });
3305
3306 parser.addGrammarElement("logicalNot", function (parser, runtime, tokens) {
3307 if (!tokens.matchToken("not")) return;
3308 var root = parser.requireElement("unaryExpression", tokens);
3309 return {
3310 type: "logicalNot",
3311 root: root,
3312 args: [root],
3313 op: function (context, val) {
3314 return !val;
3315 },
3316 evaluate: function (context) {
3317 return runtime.unifiedEval(this, context);
3318 },
3319 };
3320 });
3321
3322 parser.addGrammarElement("noExpression", function (parser, runtime, tokens) {
3323 if (!tokens.matchToken("no")) return;
3324 var root = parser.requireElement("unaryExpression", tokens);
3325 return {
3326 type: "noExpression",
3327 root: root,
3328 args: [root],
3329 op: function (_context, val) {
3330 return runtime.isEmpty(val);
3331 },
3332 evaluate: function (context) {
3333 return runtime.unifiedEval(this, context);
3334 },
3335 };
3336 });
3337
3338 parser.addLeafExpression("some", function (parser, runtime, tokens) {
3339 if (!tokens.matchToken("some")) return;
3340 var root = parser.requireElement("expression", tokens);
3341 return {
3342 type: "noExpression",
3343 root: root,
3344 args: [root],
3345 op: function (_context, val) {
3346 return !runtime.isEmpty(val);
3347 },
3348 evaluate(context) {
3349 return runtime.unifiedEval(this, context);
3350 },
3351 };
3352 });
3353
3354 parser.addGrammarElement("negativeNumber", function (parser, runtime, tokens) {
3355 if (!tokens.matchOpToken("-")) return;
3356 var root = parser.requireElement("unaryExpression", tokens);
3357 return {
3358 type: "negativeNumber",
3359 root: root,
3360 args: [root],
3361 op: function (context, value) {
3362 return -1 * value;
3363 },
3364 evaluate: function (context) {
3365 return runtime.unifiedEval(this, context);
3366 },
3367 };
3368 });
3369
3370 parser.addGrammarElement("unaryExpression", function (parser, runtime, tokens) {
3371 tokens.matchToken("the"); // optional "the"
3372 return parser.parseAnyOf(
3373 ["beepExpression", "logicalNot", "relativePositionalExpression", "positionalExpression", "noExpression", "negativeNumber", "postfixExpression"],
3374 tokens
3375 );
3376 });
3377
3378 parser.addGrammarElement("beepExpression", function (parser, runtime, tokens) {
3379 if (!tokens.matchToken("beep!")) return;
3380 var expression = parser.parseElement("unaryExpression", tokens);
3381 if (expression) {
3382 expression['booped'] = true;
3383 var originalEvaluate = expression.evaluate;
3384 expression.evaluate = function(ctx){
3385 let value = originalEvaluate.apply(expression, arguments);
3386 let element = ctx.me;
3387 if (runtime.triggerEvent(element, "hyperscript:beep", {element, expression, value})) {
3388 var typeName;
3389 if (value) {
3390 if (value instanceof ElementCollection){
3391 typeName = "ElementCollection";
3392 } else if (value.constructor) {
3393 typeName = value.constructor.name;
3394 } else {
3395 typeName = "unknown";
3396 }
3397 } else {
3398 typeName = "object (null)"
3399 }
3400 var logValue = value;
3401 if (typeName === "String") {
3402 logValue = '"' + logValue + '"';
3403 } else if (value instanceof ElementCollection) {
3404 logValue = Array.from(value);
3405 }
3406 console.log("///_ BEEP! The expression (" + Tokens.sourceFor.call(expression).substr(6) + ") evaluates to:", logValue, "of type " + typeName);
3407 }
3408 return value;
3409 }
3410 return expression;
3411 }
3412 });
3413
3414 var scanForwardQuery = function(start, root, match, wrap) {
3415 var results = root.querySelectorAll(match);
3416 for (var i = 0; i < results.length; i++) {
3417 var elt = results[i];
3418 if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_PRECEDING) {
3419 return elt;
3420 }
3421 }
3422 if (wrap) {
3423 return results[0];
3424 }
3425 }
3426
3427 var scanBackwardsQuery = function(start, root, match, wrap) {
3428 var results = root.querySelectorAll(match);
3429 for (var i = results.length - 1; i >= 0; i--) {
3430 var elt = results[i];
3431 if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_FOLLOWING) {
3432 return elt;
3433 }
3434 }
3435 if (wrap) {
3436 return results[results.length - 1];
3437 }
3438 }
3439
3440 var scanForwardArray = function(start, array, match, wrap) {
3441 var matches = [];
3442 Runtime.prototype.forEach(array, function(elt){
3443 if (elt.matches(match) || elt === start) {
3444 matches.push(elt);
3445 }
3446 })
3447 for (var i = 0; i < matches.length - 1; i++) {
3448 var elt = matches[i];
3449 if (elt === start) {
3450 return matches[i + 1];
3451 }
3452 }
3453 if (wrap) {
3454 var first = matches[0];
3455 if (first && first.matches(match)) {
3456 return first;
3457 }
3458 }
3459 }
3460
3461 var scanBackwardsArray = function(start, array, match, wrap) {
3462 return scanForwardArray(start, Array.from(array).reverse(), match, wrap);
3463 }
3464
3465 parser.addGrammarElement("relativePositionalExpression", function (parser, runtime, tokens) {
3466 var op = tokens.matchAnyToken("next", "previous");
3467 if (!op) return;
3468 if (op.value === "next") {
3469 var forwardSearch = true;
3470 }
3471
3472 var thing = parser.parseElement("expression", tokens);
3473
3474 if (tokens.matchToken("from")) {
3475 tokens.pushFollow("in");
3476 try {
3477 var from = parser.requireElement("unaryExpression", tokens);
3478 } finally {
3479 tokens.popFollow();
3480 }
3481 } else {
3482 var from = parser.requireElement("implicitMeTarget", tokens);
3483 }
3484
3485 var inSearch = false;
3486 var withinElt;
3487 if (tokens.matchToken("in")) {
3488 inSearch = true;
3489 var inElt = parser.requireElement("unaryExpression", tokens);
3490 } else if (tokens.matchToken("within")) {
3491 withinElt = parser.requireElement("unaryExpression", tokens);
3492 } else {
3493 withinElt = document.body;
3494 }
3495
3496 var wrapping = false;
3497 if (tokens.matchToken("with")) {
3498 tokens.requireToken("wrapping")
3499 wrapping = true;
3500 }
3501
3502 return {
3503 type: "relativePositionalExpression",
3504 from: from,
3505 forwardSearch: forwardSearch,
3506 inSearch: inSearch,
3507 wrapping: wrapping,
3508 inElt: inElt,
3509 withinElt: withinElt,
3510 operator: op.value,
3511 args: [thing, from, inElt, withinElt],
3512 op: function (context, thing, from, inElt, withinElt) {
3513
3514 var css = thing.css;
3515 if (css == null) {
3516 throw "Expected a CSS value";
3517 }
3518
3519 if(inSearch) {
3520 if (inElt) {
3521 if (forwardSearch) {
3522 return scanForwardArray(from, inElt, css, wrapping);
3523 } else {
3524 return scanBackwardsArray(from, inElt, css, wrapping);
3525 }
3526 }
3527 } else {
3528 if (withinElt) {
3529 if (forwardSearch) {
3530 return scanForwardQuery(from, withinElt, css, wrapping);
3531 } else {
3532 return scanBackwardsQuery(from, withinElt, css, wrapping);
3533 }
3534 }
3535 }
3536 },
3537 evaluate: function (context) {
3538 return runtime.unifiedEval(this, context);
3539 },
3540 }
3541
3542 });
3543
3544 parser.addGrammarElement("positionalExpression", function (parser, runtime, tokens) {
3545 var op = tokens.matchAnyToken("first", "last", "random");
3546 if (!op) return;
3547 tokens.matchAnyToken("in", "from", "of");
3548 var rhs = parser.requireElement("unaryExpression", tokens);
3549 const operator = op.value;
3550 return {
3551 type: "positionalExpression",
3552 rhs: rhs,
3553 operator: op.value,
3554 args: [rhs],
3555 op: function (context, rhsVal) {
3556 if (rhsVal && !Array.isArray(rhsVal)) {
3557 if (rhsVal.children) {
3558 rhsVal = rhsVal.children;
3559 } else {
3560 rhsVal = Array.from(rhsVal);
3561 }
3562 }
3563 if (rhsVal) {
3564 if (operator === "first") {
3565 return rhsVal[0];
3566 } else if (operator === "last") {
3567 return rhsVal[rhsVal.length - 1];
3568 } else if (operator === "random") {
3569 return rhsVal[Math.floor(Math.random() * rhsVal.length)];
3570 }
3571 }
3572 },
3573 evaluate: function (context) {
3574 return runtime.unifiedEval(this, context);
3575 },
3576 };
3577 });
3578
3579 parser.addGrammarElement("mathOperator", function (parser, runtime, tokens) {
3580 var expr = parser.parseElement("unaryExpression", tokens);
3581 var mathOp,
3582 initialMathOp = null;
3583 mathOp = tokens.matchAnyOpToken("+", "-", "*", "/", "%");
3584 while (mathOp) {
3585 initialMathOp = initialMathOp || mathOp;
3586 var operator = mathOp.value;
3587 if (initialMathOp.value !== operator) {
3588 parser.raiseParseError(tokens, "You must parenthesize math operations with different operators");
3589 }
3590 var rhs = parser.parseElement("unaryExpression", tokens);
3591 expr = {
3592 type: "mathOperator",
3593 lhs: expr,
3594 rhs: rhs,
3595 operator: operator,
3596 args: [expr, rhs],
3597 op: function (context, lhsVal, rhsVal) {
3598 if (operator === "+") {
3599 return lhsVal + rhsVal;
3600 } else if (operator === "-") {
3601 return lhsVal - rhsVal;
3602 } else if (operator === "*") {
3603 return lhsVal * rhsVal;
3604 } else if (operator === "/") {
3605 return lhsVal / rhsVal;
3606 } else if (operator === "%") {
3607 return lhsVal % rhsVal;
3608 }
3609 },
3610 evaluate: function (context) {
3611 return runtime.unifiedEval(this, context);
3612 },
3613 };
3614 mathOp = tokens.matchAnyOpToken("+", "-", "*", "/", "%");
3615 }
3616 return expr;
3617 });
3618
3619 parser.addGrammarElement("mathExpression", function (parser, runtime, tokens) {
3620 return parser.parseAnyOf(["mathOperator", "unaryExpression"], tokens);
3621 });
3622
3623 function sloppyContains(src, container, value){
3624 if (container['contains']) {
3625 return container.contains(value);
3626 } else if (container['includes']) {
3627 return container.includes(value);
3628 } else {
3629 throw Error("The value of " + src.sourceFor() + " does not have a contains or includes method on it");
3630 }
3631 }
3632 function sloppyMatches(src, target, toMatch){
3633 if (target['match']) {
3634 return !!target.match(toMatch);
3635 } else if (target['matches']) {
3636 return target.matches(toMatch);
3637 } else {
3638 throw Error("The value of " + src.sourceFor() + " does not have a match or matches method on it");
3639 }
3640 }
3641
3642 parser.addGrammarElement("comparisonOperator", function (parser, runtime, tokens) {
3643 var expr = parser.parseElement("mathExpression", tokens);
3644 var comparisonToken = tokens.matchAnyOpToken("<", ">", "<=", ">=", "==", "===", "!=", "!==");
3645 var operator = comparisonToken ? comparisonToken.value : null;
3646 var hasRightValue = true; // By default, most comparisons require two values, but there are some exceptions.
3647 var typeCheck = false;
3648
3649 if (operator == null) {
3650 if (tokens.matchToken("is") || tokens.matchToken("am")) {
3651 if (tokens.matchToken("not")) {
3652 if (tokens.matchToken("in")) {
3653 operator = "not in";
3654 } else if (tokens.matchToken("a")) {
3655 operator = "not a";
3656 typeCheck = true;
3657 } else if (tokens.matchToken("empty")) {
3658 operator = "not empty";
3659 hasRightValue = false;
3660 } else {
3661 operator = "!=";
3662 }
3663 } else if (tokens.matchToken("in")) {
3664 operator = "in";
3665 } else if (tokens.matchToken("a")) {
3666 operator = "a";
3667 typeCheck = true;
3668 } else if (tokens.matchToken("empty")) {
3669 operator = "empty";
3670 hasRightValue = false;
3671 } else if (tokens.matchToken("less")) {
3672 tokens.requireToken("than");
3673 if (tokens.matchToken("or")) {
3674 tokens.requireToken("equal");
3675 tokens.requireToken("to");
3676 operator = "<=";
3677 } else {
3678 operator = "<";
3679 }
3680 } else if (tokens.matchToken("greater")) {
3681 tokens.requireToken("than");
3682 if (tokens.matchToken("or")) {
3683 tokens.requireToken("equal");
3684 tokens.requireToken("to");
3685 operator = ">=";
3686 } else {
3687 operator = ">";
3688 }
3689 } else {
3690 operator = "==";
3691 }
3692 } else if (tokens.matchToken("exist") || tokens.matchToken("exists")) {
3693 operator = "exist";
3694 hasRightValue = false;
3695 } else if (tokens.matchToken("matches") || tokens.matchToken("match")) {
3696 operator = "match";
3697 } else if (tokens.matchToken("contains") || tokens.matchToken("contain")) {
3698 operator = "contain";
3699 } else if (tokens.matchToken("includes") || tokens.matchToken("include")) {
3700 operator = "include";
3701 } else if (tokens.matchToken("do") || tokens.matchToken("does")) {
3702 tokens.requireToken("not");
3703 if (tokens.matchToken("matches") || tokens.matchToken("match")) {
3704 operator = "not match";
3705 } else if (tokens.matchToken("contains") || tokens.matchToken("contain")) {
3706 operator = "not contain";
3707 } else if (tokens.matchToken("exist") || tokens.matchToken("exist")) {
3708 operator = "not exist";
3709 hasRightValue = false;
3710 } else if (tokens.matchToken("include")) {
3711 operator = "not include";
3712 } else {
3713 parser.raiseParseError(tokens, "Expected matches or contains");
3714 }
3715 }
3716 }
3717
3718 if (operator) {
3719 // Do not allow chained comparisons, which is dumb
3720 if (typeCheck) {
3721 var typeName = tokens.requireTokenType("IDENTIFIER");
3722 var nullOk = !tokens.matchOpToken("!");
3723 } else if (hasRightValue) {
3724 var rhs = parser.requireElement("mathExpression", tokens);
3725 if (operator === "match" || operator === "not match") {
3726 rhs = rhs.css ? rhs.css : rhs;
3727 }
3728 }
3729 var lhs = expr;
3730 expr = {
3731 type: "comparisonOperator",
3732 operator: operator,
3733 typeName: typeName,
3734 nullOk: nullOk,
3735 lhs: expr,
3736 rhs: rhs,
3737 args: [expr, rhs],
3738 op: function (context, lhsVal, rhsVal) {
3739 if (operator === "==") {
3740 return lhsVal == rhsVal;
3741 } else if (operator === "!=") {
3742 return lhsVal != rhsVal;
3743 }
3744 if (operator === "match") {
3745 return lhsVal != null && sloppyMatches(lhs, lhsVal, rhsVal);
3746 }
3747 if (operator === "not match") {
3748 return lhsVal == null || !sloppyMatches(lhs, lhsVal, rhsVal);
3749 }
3750 if (operator === "in") {
3751 return rhsVal != null && sloppyContains(rhs, rhsVal, lhsVal);
3752 }
3753 if (operator === "not in") {
3754 return rhsVal == null || !sloppyContains(rhs, rhsVal, lhsVal);
3755 }
3756 if (operator === "contain") {
3757 return lhsVal != null && sloppyContains(lhs, lhsVal, rhsVal);
3758 }
3759 if (operator === "not contain") {
3760 return lhsVal == null || !sloppyContains(lhs, lhsVal, rhsVal);
3761 }
3762 if (operator === "include") {
3763 return lhsVal != null && sloppyContains(lhs, lhsVal, rhsVal);
3764 }
3765 if (operator === "not include") {
3766 return lhsVal == null || !sloppyContains(lhs, lhsVal, rhsVal);
3767 }
3768 if (operator === "===") {
3769 return lhsVal === rhsVal;
3770 } else if (operator === "!==") {
3771 return lhsVal !== rhsVal;
3772 } else if (operator === "<") {
3773 return lhsVal < rhsVal;
3774 } else if (operator === ">") {
3775 return lhsVal > rhsVal;
3776 } else if (operator === "<=") {
3777 return lhsVal <= rhsVal;
3778 } else if (operator === ">=") {
3779 return lhsVal >= rhsVal;
3780 } else if (operator === "empty") {
3781 return runtime.isEmpty(lhsVal);
3782 } else if (operator === "not empty") {
3783 return !runtime.isEmpty(lhsVal);
3784 } else if (operator === "exist") {
3785 return runtime.doesExist(lhsVal);
3786 } else if (operator === "not exist") {
3787 return !runtime.doesExist(lhsVal);
3788 } else if (operator === "a") {
3789 return runtime.typeCheck(lhsVal, typeName.value, nullOk);
3790 } else if (operator === "not a") {
3791 return !runtime.typeCheck(lhsVal, typeName.value, nullOk);
3792 } else {
3793 throw "Unknown comparison : " + operator;
3794 }
3795 },
3796 evaluate: function (context) {
3797 return runtime.unifiedEval(this, context);
3798 },
3799 };
3800 }
3801 return expr;
3802 });
3803
3804 parser.addGrammarElement("comparisonExpression", function (parser, runtime, tokens) {
3805 return parser.parseAnyOf(["comparisonOperator", "mathExpression"], tokens);
3806 });
3807
3808 parser.addGrammarElement("logicalOperator", function (parser, runtime, tokens) {
3809 var expr = parser.parseElement("comparisonExpression", tokens);
3810 var logicalOp,
3811 initialLogicalOp = null;
3812 logicalOp = tokens.matchToken("and") || tokens.matchToken("or");
3813 while (logicalOp) {
3814 initialLogicalOp = initialLogicalOp || logicalOp;
3815 if (initialLogicalOp.value !== logicalOp.value) {
3816 parser.raiseParseError(tokens, "You must parenthesize logical operations with different operators");
3817 }
3818 var rhs = parser.requireElement("comparisonExpression", tokens);
3819 const operator = logicalOp.value;
3820 expr = {
3821 type: "logicalOperator",
3822 operator: operator,
3823 lhs: expr,
3824 rhs: rhs,
3825 args: [expr, rhs],
3826 op: function (context, lhsVal, rhsVal) {
3827 if (operator === "and") {
3828 return lhsVal && rhsVal;
3829 } else {
3830 return lhsVal || rhsVal;
3831 }
3832 },
3833 evaluate: function (context) {
3834 return runtime.unifiedEval(this, context);
3835 },
3836 };
3837 logicalOp = tokens.matchToken("and") || tokens.matchToken("or");
3838 }
3839 return expr;
3840 });
3841
3842 parser.addGrammarElement("logicalExpression", function (parser, runtime, tokens) {
3843 return parser.parseAnyOf(["logicalOperator", "mathExpression"], tokens);
3844 });
3845
3846 parser.addGrammarElement("asyncExpression", function (parser, runtime, tokens) {
3847 if (tokens.matchToken("async")) {
3848 var value = parser.requireElement("logicalExpression", tokens);
3849 var expr = {
3850 type: "asyncExpression",
3851 value: value,
3852 evaluate: function (context) {
3853 return {
3854 asyncWrapper: true,
3855 value: this.value.evaluate(context), //OK
3856 };
3857 },
3858 };
3859 return expr;
3860 } else {
3861 return parser.parseElement("logicalExpression", tokens);
3862 }
3863 });
3864
3865 parser.addGrammarElement("expression", function (parser, runtime, tokens) {
3866 tokens.matchToken("the"); // optional the
3867 return parser.parseElement("asyncExpression", tokens);
3868 });
3869
3870 parser.addGrammarElement("assignableExpression", function (parser, runtime, tokens) {
3871 tokens.matchToken("the"); // optional the
3872
3873 // TODO obviously we need to generalize this as a left hand side / targetable concept
3874 var expr = parser.parseElement("primaryExpression", tokens);
3875 if (expr && (
3876 expr.type === "symbol" ||
3877 expr.type === "ofExpression" ||
3878 expr.type === "propertyAccess" ||
3879 expr.type === "attributeRefAccess" ||
3880 expr.type === "attributeRef" ||
3881 expr.type === "styleRef" ||
3882 expr.type === "arrayIndex" ||
3883 expr.type === "possessive")
3884 ) {
3885 return expr;
3886 } else {
3887 parser.raiseParseError(
3888 tokens,
3889 "A target expression must be writable. The expression type '" + (expr && expr.type) + "' is not."
3890 );
3891 }
3892 return expr;
3893 });
3894
3895 parser.addGrammarElement("hyperscript", function (parser, runtime, tokens) {
3896 var features = [];
3897
3898 if (tokens.hasMore()) {
3899 while (parser.featureStart(tokens.currentToken()) || tokens.currentToken().value === "(") {
3900 var feature = parser.requireElement("feature", tokens);
3901 features.push(feature);
3902 tokens.matchToken("end"); // optional end
3903 }
3904 }
3905 return {
3906 type: "hyperscript",
3907 features: features,
3908 apply: function (target, source, args) {
3909 // no op
3910 for (const feature of features) {
3911 feature.install(target, source, args);
3912 }
3913 },
3914 };
3915 });
3916
3917 var parseEventArgs = function (tokens) {
3918 var args = [];
3919 // handle argument list (look ahead 3)
3920 if (
3921 tokens.token(0).value === "(" &&
3922 (tokens.token(1).value === ")" || tokens.token(2).value === "," || tokens.token(2).value === ")")
3923 ) {
3924 tokens.matchOpToken("(");
3925 do {
3926 args.push(tokens.requireTokenType("IDENTIFIER"));
3927 } while (tokens.matchOpToken(","));
3928 tokens.requireOpToken(")");
3929 }
3930 return args;
3931 };
3932
3933 parser.addFeature("on", function (parser, runtime, tokens) {
3934 if (!tokens.matchToken("on")) return;
3935 var every = false;
3936 if (tokens.matchToken("every")) {
3937 every = true;
3938 }
3939 var events = [];
3940 var displayName = null;
3941 do {
3942 var on = parser.requireElement("eventName", tokens, "Expected event name");
3943
3944 var eventName = on.evaluate(); // OK No Promise
3945
3946 if (displayName) {
3947 displayName = displayName + " or " + eventName;
3948 } else {
3949 displayName = "on " + eventName;
3950 }
3951 var args = parseEventArgs(tokens);
3952
3953 var filter = null;
3954 if (tokens.matchOpToken("[")) {
3955 filter = parser.requireElement("expression", tokens);
3956 tokens.requireOpToken("]");
3957 }
3958
3959 if (tokens.currentToken().type === "NUMBER") {
3960 var startCountToken = tokens.consumeToken();
3961 var startCount = parseInt(startCountToken.value);
3962 if (tokens.matchToken("to")) {
3963 var endCountToken = tokens.consumeToken();
3964 var endCount = parseInt(endCountToken.value);
3965 } else if (tokens.matchToken("and")) {
3966 var unbounded = true;
3967 tokens.requireToken("on");
3968 }
3969 }
3970
3971 if (eventName === "intersection") {
3972 var intersectionSpec = {};
3973 if (tokens.matchToken("with")) {
3974 intersectionSpec["with"] = parser.requireElement("expression", tokens).evaluate();
3975 }
3976 if (tokens.matchToken("having")) {
3977 do {
3978 if (tokens.matchToken("margin")) {
3979 intersectionSpec["rootMargin"] = parser.requireElement("stringLike", tokens).evaluate();
3980 } else if (tokens.matchToken("threshold")) {
3981 intersectionSpec["threshold"] = parser.requireElement("expression", tokens).evaluate();
3982 } else {
3983 parser.raiseParseError(tokens, "Unknown intersection config specification");
3984 }
3985 } while (tokens.matchToken("and"));
3986 }
3987 } else if (eventName === "mutation") {
3988 var mutationSpec = {};
3989 if (tokens.matchToken("of")) {
3990 do {
3991 if (tokens.matchToken("anything")) {
3992 mutationSpec["attributes"] = true;
3993 mutationSpec["subtree"] = true;
3994 mutationSpec["characterData"] = true;
3995 mutationSpec["childList"] = true;
3996 } else if (tokens.matchToken("childList")) {
3997 mutationSpec["childList"] = true;
3998 } else if (tokens.matchToken("attributes")) {
3999 mutationSpec["attributes"] = true;
4000 mutationSpec["attributeOldValue"] = true;
4001 } else if (tokens.matchToken("subtree")) {
4002 mutationSpec["subtree"] = true;
4003 } else if (tokens.matchToken("characterData")) {
4004 mutationSpec["characterData"] = true;
4005 mutationSpec["characterDataOldValue"] = true;
4006 } else if (tokens.currentToken().type === "ATTRIBUTE_REF") {
4007 var attribute = tokens.consumeToken();
4008 if (mutationSpec["attributeFilter"] == null) {
4009 mutationSpec["attributeFilter"] = [];
4010 }
4011 if (attribute.value.indexOf("@") == 0) {
4012 mutationSpec["attributeFilter"].push(attribute.value.substring(1));
4013 } else {
4014 parser.raiseParseError(
4015 tokens,
4016 "Only shorthand attribute references are allowed here"
4017 );
4018 }
4019 } else {
4020 parser.raiseParseError(tokens, "Unknown mutation config specification");
4021 }
4022 } while (tokens.matchToken("or"));
4023 } else {
4024 mutationSpec["attributes"] = true;
4025 mutationSpec["characterData"] = true;
4026 mutationSpec["childList"] = true;
4027 }
4028 }
4029
4030 var from = null;
4031 var elsewhere = false;
4032 if (tokens.matchToken("from")) {
4033 if (tokens.matchToken("elsewhere")) {
4034 elsewhere = true;
4035 } else {
4036 from = parser.parseElement("expression", tokens);
4037 if (!from) {
4038 parser.raiseParseError(tokens, 'Expected either target value or "elsewhere".');
4039 }
4040 }
4041 }
4042 // support both "elsewhere" and "from elsewhere"
4043 if (from === null && elsewhere === false && tokens.matchToken("elsewhere")) {
4044 elsewhere = true;
4045 }
4046
4047 if (tokens.matchToken("in")) {
4048 var inExpr = parser.parseElement('unaryExpression', tokens);
4049 }
4050
4051 if (tokens.matchToken("debounced")) {
4052 tokens.requireToken("at");
4053 var timeExpr = parser.requireElement("expression", tokens);
4054 // @ts-ignore
4055 var debounceTime = timeExpr.evaluate({}); // OK No promise TODO make a literal time expr
4056 } else if (tokens.matchToken("throttled")) {
4057 tokens.requireToken("at");
4058 var timeExpr = parser.requireElement("expression", tokens);
4059 // @ts-ignore
4060 var throttleTime = timeExpr.evaluate({}); // OK No promise TODO make a literal time expr
4061 }
4062
4063 events.push({
4064 execCount: 0,
4065 every: every,
4066 on: eventName,
4067 args: args,
4068 filter: filter,
4069 from: from,
4070 inExpr: inExpr,
4071 elsewhere: elsewhere,
4072 startCount: startCount,
4073 endCount: endCount,
4074 unbounded: unbounded,
4075 debounceTime: debounceTime,
4076 throttleTime: throttleTime,
4077 mutationSpec: mutationSpec,
4078 intersectionSpec: intersectionSpec,
4079 debounced: undefined,
4080 lastExec: undefined,
4081 });
4082 } while (tokens.matchToken("or"));
4083
4084 var queueLast = true;
4085 if (!every) {
4086 if (tokens.matchToken("queue")) {
4087 if (tokens.matchToken("all")) {
4088 var queueAll = true;
4089 var queueLast = false;
4090 } else if (tokens.matchToken("first")) {
4091 var queueFirst = true;
4092 } else if (tokens.matchToken("none")) {
4093 var queueNone = true;
4094 } else {
4095 tokens.requireToken("last");
4096 }
4097 }
4098 }
4099
4100 var start = parser.requireElement("commandList", tokens);
4101 parser.ensureTerminated(start);
4102
4103 if (tokens.matchToken("catch")) {
4104 var errorSymbol = tokens.requireTokenType("IDENTIFIER").value;
4105 var errorHandler = parser.requireElement("commandList", tokens);
4106 parser.ensureTerminated(errorHandler);
4107 }
4108
4109 if (tokens.matchToken("finally")) {
4110 var finallyHandler = parser.requireElement("commandList", tokens);
4111 parser.ensureTerminated(finallyHandler);
4112 }
4113
4114 var onFeature = {
4115 displayName: displayName,
4116 events: events,
4117 start: start,
4118 every: every,
4119 execCount: 0,
4120 errorHandler: errorHandler,
4121 errorSymbol: errorSymbol,
4122 execute: function (/** @type {Context} */ ctx) {
4123 let eventQueueInfo = runtime.getEventQueueFor(ctx.me, onFeature);
4124 if (eventQueueInfo.executing && every === false) {
4125 if (queueNone || (queueFirst && eventQueueInfo.queue.length > 0)) {
4126 return;
4127 }
4128 if (queueLast) {
4129 eventQueueInfo.queue.length = 0;
4130 }
4131 eventQueueInfo.queue.push(ctx);
4132 return;
4133 }
4134 onFeature.execCount++;
4135 eventQueueInfo.executing = true;
4136 ctx.meta.onHalt = function () {
4137 eventQueueInfo.executing = false;
4138 var queued = eventQueueInfo.queue.shift();
4139 if (queued) {
4140 setTimeout(function () {
4141 onFeature.execute(queued);
4142 }, 1);
4143 }
4144 };
4145 ctx.meta.reject = function (err) {
4146 console.error(err.message ? err.message : err);
4147 var hypertrace = runtime.getHyperTrace(ctx, err);
4148 if (hypertrace) {
4149 hypertrace.print();
4150 }
4151 runtime.triggerEvent(ctx.me, "exception", {
4152 error: err,
4153 });
4154 };
4155 start.execute(ctx);
4156 },
4157 install: function (elt, source) {
4158 for (const eventSpec of onFeature.events) {
4159 var targets;
4160 if (eventSpec.elsewhere) {
4161 targets = [document];
4162 } else if (eventSpec.from) {
4163 targets = eventSpec.from.evaluate(runtime.makeContext(elt, onFeature, elt, null));
4164 } else {
4165 targets = [elt];
4166 }
4167 runtime.implicitLoop(targets, function (target) {
4168 // OK NO PROMISE
4169
4170 var eventName = eventSpec.on;
4171 if (eventSpec.mutationSpec) {
4172 eventName = "hyperscript:mutation";
4173 const observer = new MutationObserver(function (mutationList, observer) {
4174 if (!onFeature.executing) {
4175 runtime.triggerEvent(target, eventName, {
4176 mutationList: mutationList,
4177 observer: observer,
4178 });
4179 }
4180 });
4181 observer.observe(target, eventSpec.mutationSpec);
4182 }
4183
4184 if (eventSpec.intersectionSpec) {
4185 eventName = "hyperscript:insersection";
4186 const observer = new IntersectionObserver(function (entries) {
4187 for (const entry of entries) {
4188 var detail = {
4189 observer: observer,
4190 };
4191 detail = Object.assign(detail, entry);
4192 detail["intersecting"] = entry.isIntersecting;
4193 runtime.triggerEvent(target, eventName, detail);
4194 }
4195 }, eventSpec.intersectionSpec);
4196 observer.observe(target);
4197 }
4198
4199 var addEventListener = target.addEventListener || target.on;
4200 addEventListener.call(target, eventName, function listener(evt) {
4201 // OK NO PROMISE
4202 if (typeof Node !== 'undefined' && elt instanceof Node && target !== elt && !elt.isConnected) {
4203 target.removeEventListener(eventName, listener);
4204 return;
4205 }
4206
4207 var ctx = runtime.makeContext(elt, onFeature, elt, evt);
4208 if (eventSpec.elsewhere && elt.contains(evt.target)) {
4209 return;
4210 }
4211 if (eventSpec.from) {
4212 ctx.result = target;
4213 }
4214
4215 // establish context
4216 for (const arg of eventSpec.args) {
4217 let eventValue = ctx.event[arg.value];
4218 if (eventValue !== undefined) {
4219 ctx.locals[arg.value] = eventValue;
4220 } else if ('detail' in ctx.event) {
4221 ctx.locals[arg.value] = ctx.event['detail'][arg.value];
4222 }
4223 }
4224
4225 // install error handler if any
4226 ctx.meta.errorHandler = errorHandler;
4227 ctx.meta.errorSymbol = errorSymbol;
4228 ctx.meta.finallyHandler = finallyHandler;
4229
4230 // apply filter
4231 if (eventSpec.filter) {
4232 var initialCtx = ctx.meta.context;
4233 ctx.meta.context = ctx.event;
4234 try {
4235 var value = eventSpec.filter.evaluate(ctx); //OK NO PROMISE
4236 if (value) {
4237 // match the javascript semantics for if statements
4238 } else {
4239 return;
4240 }
4241 } finally {
4242 ctx.meta.context = initialCtx;
4243 }
4244 }
4245
4246 if (eventSpec.inExpr) {
4247 var inElement = evt.target;
4248 while (true) {
4249 if (inElement.matches && inElement.matches(eventSpec.inExpr.css)) {
4250 ctx.result = inElement;
4251 break;
4252 } else {
4253 inElement = inElement.parentElement;
4254 if (inElement == null) {
4255 return; // no match found
4256 }
4257 }
4258 }
4259 }
4260
4261 // verify counts
4262 eventSpec.execCount++;
4263 if (eventSpec.startCount) {
4264 if (eventSpec.endCount) {
4265 if (
4266 eventSpec.execCount < eventSpec.startCount ||
4267 eventSpec.execCount > eventSpec.endCount
4268 ) {
4269 return;
4270 }
4271 } else if (eventSpec.unbounded) {
4272 if (eventSpec.execCount < eventSpec.startCount) {
4273 return;
4274 }
4275 } else if (eventSpec.execCount !== eventSpec.startCount) {
4276 return;
4277 }
4278 }
4279
4280 //debounce
4281 if (eventSpec.debounceTime) {
4282 if (eventSpec.debounced) {
4283 clearTimeout(eventSpec.debounced);
4284 }
4285 eventSpec.debounced = setTimeout(function () {
4286 onFeature.execute(ctx);
4287 }, eventSpec.debounceTime);
4288 return;
4289 }
4290
4291 // throttle
4292 if (eventSpec.throttleTime) {
4293 if (
4294 eventSpec.lastExec &&
4295 Date.now() < eventSpec.lastExec + eventSpec.throttleTime
4296 ) {
4297 return;
4298 } else {
4299 eventSpec.lastExec = Date.now();
4300 }
4301 }
4302
4303 // apply execute
4304 onFeature.execute(ctx);
4305 });
4306 });
4307 }
4308 },
4309 };
4310 parser.setParent(start, onFeature);
4311 return onFeature;
4312 });
4313
4314 parser.addFeature("def", function (parser, runtime, tokens) {
4315 if (!tokens.matchToken("def")) return;
4316 var functionName = parser.requireElement("dotOrColonPath", tokens);
4317 var nameVal = functionName.evaluate(); // OK
4318 var nameSpace = nameVal.split(".");
4319 var funcName = nameSpace.pop();
4320
4321 var args = [];
4322 if (tokens.matchOpToken("(")) {
4323 if (tokens.matchOpToken(")")) {
4324 // emtpy args list
4325 } else {
4326 do {
4327 args.push(tokens.requireTokenType("IDENTIFIER"));
4328 } while (tokens.matchOpToken(","));
4329 tokens.requireOpToken(")");
4330 }
4331 }
4332
4333 var start = parser.requireElement("commandList", tokens);
4334
4335 if (tokens.matchToken("catch")) {
4336 var errorSymbol = tokens.requireTokenType("IDENTIFIER").value;
4337 var errorHandler = parser.parseElement("commandList", tokens);
4338 }
4339
4340 if (tokens.matchToken("finally")) {
4341 var finallyHandler = parser.requireElement("commandList", tokens);
4342 parser.ensureTerminated(finallyHandler);
4343 }
4344
4345 var functionFeature = {
4346 displayName:
4347 funcName +
4348 "(" +
4349 args
4350 .map(function (arg) {
4351 return arg.value;
4352 })
4353 .join(", ") +
4354 ")",
4355 name: funcName,
4356 args: args,
4357 start: start,
4358 errorHandler: errorHandler,
4359 errorSymbol: errorSymbol,
4360 finallyHandler: finallyHandler,
4361 install: function (target, source) {
4362 var func = function () {
4363 // null, worker
4364 var ctx = runtime.makeContext(source, functionFeature, target, null);
4365
4366 // install error handler if any
4367 ctx.meta.errorHandler = errorHandler;
4368 ctx.meta.errorSymbol = errorSymbol;
4369 ctx.meta.finallyHandler = finallyHandler;
4370
4371 for (var i = 0; i < args.length; i++) {
4372 var name = args[i];
4373 var argumentVal = arguments[i];
4374 if (name) {
4375 ctx.locals[name.value] = argumentVal;
4376 }
4377 }
4378 ctx.meta.caller = arguments[args.length];
4379 if (ctx.meta.caller) {
4380 ctx.meta.callingCommand = ctx.meta.caller.meta.command;
4381 }
4382 var resolve,
4383 reject = null;
4384 var promise = new Promise(function (theResolve, theReject) {
4385 resolve = theResolve;
4386 reject = theReject;
4387 });
4388 start.execute(ctx);
4389 if (ctx.meta.returned) {
4390 return ctx.meta.returnValue;
4391 } else {
4392 ctx.meta.resolve = resolve;
4393 ctx.meta.reject = reject;
4394 return promise;
4395 }
4396 };
4397 func.hyperfunc = true;
4398 func.hypername = nameVal;
4399 runtime.assignToNamespace(target, nameSpace, funcName, func);
4400 },
4401 };
4402
4403 parser.ensureTerminated(start);
4404
4405 // terminate error handler if any
4406 if (errorHandler) {
4407 parser.ensureTerminated(errorHandler);
4408 }
4409
4410 parser.setParent(start, functionFeature);
4411 return functionFeature;
4412 });
4413
4414 parser.addFeature("set", function (parser, runtime, tokens) {
4415 let setCmd = parser.parseElement("setCommand", tokens);
4416 if (setCmd) {
4417 if (setCmd.target.scope !== "element") {
4418 parser.raiseParseError(tokens, "variables declared at the feature level must be element scoped.");
4419 }
4420 let setFeature = {
4421 start: setCmd,
4422 install: function (target, source) {
4423 setCmd && setCmd.execute(runtime.makeContext(target, setFeature, target, null));
4424 },
4425 };
4426 parser.ensureTerminated(setCmd);
4427 return setFeature;
4428 }
4429 });
4430
4431 parser.addFeature("init", function (parser, runtime, tokens) {
4432 if (!tokens.matchToken("init")) return;
4433
4434 var immediately = tokens.matchToken("immediately");
4435
4436 var start = parser.requireElement("commandList", tokens);
4437 var initFeature = {
4438 start: start,
4439 install: function (target, source) {
4440 let handler = function () {
4441 start && start.execute(runtime.makeContext(target, initFeature, target, null));
4442 };
4443 if (immediately) {
4444 handler();
4445 } else {
4446 setTimeout(handler, 0);
4447 }
4448 },
4449 };
4450
4451 // terminate body
4452 parser.ensureTerminated(start);
4453 parser.setParent(start, initFeature);
4454 return initFeature;
4455 });
4456
4457 parser.addFeature("worker", function (parser, runtime, tokens) {
4458 if (tokens.matchToken("worker")) {
4459 parser.raiseParseError(
4460 tokens,
4461 "In order to use the 'worker' feature, include " +
4462 "the _hyperscript worker plugin. See " +
4463 "https://hyperscript.org/features/worker/ for " +
4464 "more info."
4465 );
4466 return undefined
4467 }
4468 });
4469
4470 parser.addFeature("behavior", function (parser, runtime, tokens) {
4471 if (!tokens.matchToken("behavior")) return;
4472 var path = parser.requireElement("dotOrColonPath", tokens).evaluate();
4473 var nameSpace = path.split(".");
4474 var name = nameSpace.pop();
4475
4476 var formalParams = [];
4477 if (tokens.matchOpToken("(") && !tokens.matchOpToken(")")) {
4478 do {
4479 formalParams.push(tokens.requireTokenType("IDENTIFIER").value);
4480 } while (tokens.matchOpToken(","));
4481 tokens.requireOpToken(")");
4482 }
4483 var hs = parser.requireElement("hyperscript", tokens);
4484 for (var i = 0; i < hs.features.length; i++) {
4485 var feature = hs.features[i];
4486 feature.behavior = path;
4487 }
4488
4489 return {
4490 install: function (target, source) {
4491 runtime.assignToNamespace(
4492 globalScope.document && globalScope.document.body,
4493 nameSpace,
4494 name,
4495 function (target, source, innerArgs) {
4496 var internalData = runtime.getInternalData(target);
4497 var elementScope = getOrInitObject(internalData, path + "Scope");
4498 for (var i = 0; i < formalParams.length; i++) {
4499 elementScope[formalParams[i]] = innerArgs[formalParams[i]];
4500 }
4501 hs.apply(target, source);
4502 }
4503 );
4504 },
4505 };
4506 });
4507
4508 parser.addFeature("install", function (parser, runtime, tokens) {
4509 if (!tokens.matchToken("install")) return;
4510 var behaviorPath = parser.requireElement("dotOrColonPath", tokens).evaluate();
4511 var behaviorNamespace = behaviorPath.split(".");
4512 var args = parser.parseElement("namedArgumentList", tokens);
4513
4514 var installFeature;
4515 return (installFeature = {
4516 install: function (target, source) {
4517 runtime.unifiedEval(
4518 {
4519 args: [args],
4520 op: function (ctx, args) {
4521 var behavior = globalScope;
4522 for (var i = 0; i < behaviorNamespace.length; i++) {
4523 behavior = behavior[behaviorNamespace[i]];
4524 if (typeof behavior !== "object" && typeof behavior !== "function")
4525 throw new Error("No such behavior defined as " + behaviorPath);
4526 }
4527
4528 if (!(behavior instanceof Function))
4529 throw new Error(behaviorPath + " is not a behavior");
4530
4531 behavior(target, source, args);
4532 },
4533 },
4534 runtime.makeContext(target, installFeature, target)
4535 );
4536 },
4537 });
4538 });
4539
4540 parser.addGrammarElement("jsBody", function (parser, runtime, tokens) {
4541 var jsSourceStart = tokens.currentToken().start;
4542 var jsLastToken = tokens.currentToken();
4543
4544 var funcNames = [];
4545 var funcName = "";
4546 var expectFunctionDeclaration = false;
4547 while (tokens.hasMore()) {
4548 jsLastToken = tokens.consumeToken();
4549 var peek = tokens.token(0, true);
4550 if (peek.type === "IDENTIFIER" && peek.value === "end") {
4551 break;
4552 }
4553 if (expectFunctionDeclaration) {
4554 if (jsLastToken.type === "IDENTIFIER" || jsLastToken.type === "NUMBER") {
4555 funcName += jsLastToken.value;
4556 } else {
4557 if (funcName !== "") funcNames.push(funcName);
4558 funcName = "";
4559 expectFunctionDeclaration = false;
4560 }
4561 } else if (jsLastToken.type === "IDENTIFIER" && jsLastToken.value === "function") {
4562 expectFunctionDeclaration = true;
4563 }
4564 }
4565 var jsSourceEnd = jsLastToken.end + 1;
4566
4567 return {
4568 type: "jsBody",
4569 exposedFunctionNames: funcNames,
4570 jsSource: tokens.source.substring(jsSourceStart, jsSourceEnd),
4571 };
4572 });
4573
4574 parser.addFeature("js", function (parser, runtime, tokens) {
4575 if (!tokens.matchToken("js")) return;
4576 var jsBody = parser.requireElement("jsBody", tokens);
4577
4578 var jsSource =
4579 jsBody.jsSource +
4580 "\nreturn { " +
4581 jsBody.exposedFunctionNames
4582 .map(function (name) {
4583 return name + ":" + name;
4584 })
4585 .join(",") +
4586 " } ";
4587 var func = new Function(jsSource);
4588
4589 return {
4590 jsSource: jsSource,
4591 function: func,
4592 exposedFunctionNames: jsBody.exposedFunctionNames,
4593 install: function () {
4594 Object.assign(globalScope, func());
4595 },
4596 };
4597 });
4598
4599 parser.addCommand("js", function (parser, runtime, tokens) {
4600 if (!tokens.matchToken("js")) return;
4601 // Parse inputs
4602 var inputs = [];
4603 if (tokens.matchOpToken("(")) {
4604 if (tokens.matchOpToken(")")) {
4605 // empty input list
4606 } else {
4607 do {
4608 var inp = tokens.requireTokenType("IDENTIFIER");
4609 inputs.push(inp.value);
4610 } while (tokens.matchOpToken(","));
4611 tokens.requireOpToken(")");
4612 }
4613 }
4614
4615 var jsBody = parser.requireElement("jsBody", tokens);
4616 tokens.matchToken("end");
4617
4618 var func = varargConstructor(Function, inputs.concat([jsBody.jsSource]));
4619
4620 var command = {
4621 jsSource: jsBody.jsSource,
4622 function: func,
4623 inputs: inputs,
4624 op: function (context) {
4625 var args = [];
4626 inputs.forEach(function (input) {
4627 args.push(runtime.resolveSymbol(input, context, 'default'));
4628 });
4629 var result = func.apply(globalScope, args);
4630 if (result && typeof result.then === "function") {
4631 return new Promise(function (resolve) {
4632 result.then(function (actualResult) {
4633 context.result = actualResult;
4634 resolve(runtime.findNext(this, context));
4635 });
4636 });
4637 } else {
4638 context.result = result;
4639 return runtime.findNext(this, context);
4640 }
4641 },
4642 };
4643 return command;
4644 });
4645
4646 parser.addCommand("async", function (parser, runtime, tokens) {
4647 if (!tokens.matchToken("async")) return;
4648 if (tokens.matchToken("do")) {
4649 var body = parser.requireElement("commandList", tokens);
4650
4651 // Append halt
4652 var end = body;
4653 while (end.next) end = end.next;
4654 end.next = runtime.HALT;
4655
4656 tokens.requireToken("end");
4657 } else {
4658 var body = parser.requireElement("command", tokens);
4659 }
4660 var command = {
4661 body: body,
4662 op: function (context) {
4663 setTimeout(function () {
4664 body.execute(context);
4665 });
4666 return runtime.findNext(this, context);
4667 },
4668 };
4669 parser.setParent(body, command);
4670 return command;
4671 });
4672
4673 parser.addCommand("tell", function (parser, runtime, tokens) {
4674 var startToken = tokens.currentToken();
4675 if (!tokens.matchToken("tell")) return;
4676 var value = parser.requireElement("expression", tokens);
4677 var body = parser.requireElement("commandList", tokens);
4678 if (tokens.hasMore() && !parser.featureStart(tokens.currentToken())) {
4679 tokens.requireToken("end");
4680 }
4681 var slot = "tell_" + startToken.start;
4682 var tellCmd = {
4683 value: value,
4684 body: body,
4685 args: [value],
4686 resolveNext: function (context) {
4687 var iterator = context.meta.iterators[slot];
4688 if (iterator.index < iterator.value.length) {
4689 context.you = iterator.value[iterator.index++];
4690 return body;
4691 } else {
4692 // restore original me
4693 context.you = iterator.originalYou;
4694 if (this.next) {
4695 return this.next;
4696 } else {
4697 return runtime.findNext(this.parent, context);
4698 }
4699 }
4700 },
4701 op: function (context, value) {
4702 if (value == null) {
4703 value = [];
4704 } else if (!(Array.isArray(value) || value instanceof NodeList)) {
4705 value = [value];
4706 }
4707 context.meta.iterators[slot] = {
4708 originalYou: context.you,
4709 index: 0,
4710 value: value,
4711 };
4712 return this.resolveNext(context);
4713 },
4714 };
4715 parser.setParent(body, tellCmd);
4716 return tellCmd;
4717 });
4718
4719 parser.addCommand("wait", function (parser, runtime, tokens) {
4720 if (!tokens.matchToken("wait")) return;
4721 var command;
4722
4723 // wait on event
4724 if (tokens.matchToken("for")) {
4725 tokens.matchToken("a"); // optional "a"
4726 var events = [];
4727 do {
4728 var lookahead = tokens.token(0);
4729 if (lookahead.type === 'NUMBER' || lookahead.type === 'L_PAREN') {
4730 events.push({
4731 time: parser.requireElement('expression', tokens).evaluate() // TODO: do we want to allow async here?
4732 })
4733 } else {
4734 events.push({
4735 name: parser.requireElement("dotOrColonPath", tokens, "Expected event name").evaluate(),
4736 args: parseEventArgs(tokens),
4737 });
4738 }
4739 } while (tokens.matchToken("or"));
4740
4741 if (tokens.matchToken("from")) {
4742 var on = parser.requireElement("expression", tokens);
4743 }
4744
4745 // wait on event
4746 command = {
4747 event: events,
4748 on: on,
4749 args: [on],
4750 op: function (context, on) {
4751 var target = on ? on : context.me;
4752 if (!(target instanceof EventTarget))
4753 throw new Error("Not a valid event target: " + this.on.sourceFor());
4754 return new Promise((resolve) => {
4755 var resolved = false;
4756 for (const eventInfo of events) {
4757 var listener = (event) => {
4758 context.result = event;
4759 if (eventInfo.args) {
4760 for (const arg of eventInfo.args) {
4761 context.locals[arg.value] =
4762 event[arg.value] || (event.detail ? event.detail[arg.value] : null);
4763 }
4764 }
4765 if (!resolved) {
4766 resolved = true;
4767 resolve(runtime.findNext(this, context));
4768 }
4769 };
4770 if (eventInfo.name){
4771 target.addEventListener(eventInfo.name, listener, {once: true});
4772 } else if (eventInfo.time != null) {
4773 setTimeout(listener, eventInfo.time, eventInfo.time)
4774 }
4775 }
4776 });
4777 },
4778 };
4779 return command;
4780 } else {
4781 var time;
4782 if (tokens.matchToken("a")) {
4783 tokens.requireToken("tick");
4784 time = 0;
4785 } else {
4786 time = parser.requireElement("expression", tokens);
4787 }
4788
4789 command = {
4790 type: "waitCmd",
4791 time: time,
4792 args: [time],
4793 op: function (context, timeValue) {
4794 return new Promise((resolve) => {
4795 setTimeout(() => {
4796 resolve(runtime.findNext(this, context));
4797 }, timeValue);
4798 });
4799 },
4800 execute: function (context) {
4801 return runtime.unifiedExec(this, context);
4802 },
4803 };
4804 return command;
4805 }
4806 });
4807
4808 // TODO - colon path needs to eventually become part of ruby-style symbols
4809 parser.addGrammarElement("dotOrColonPath", function (parser, runtime, tokens) {
4810 var root = tokens.matchTokenType("IDENTIFIER");
4811 if (root) {
4812 var path = [root.value];
4813
4814 var separator = tokens.matchOpToken(".") || tokens.matchOpToken(":");
4815 if (separator) {
4816 do {
4817 path.push(tokens.requireTokenType("IDENTIFIER", "NUMBER").value);
4818 } while (tokens.matchOpToken(separator.value));
4819 }
4820
4821 return {
4822 type: "dotOrColonPath",
4823 path: path,
4824 evaluate: function () {
4825 return path.join(separator ? separator.value : "");
4826 },
4827 };
4828 }
4829 });
4830
4831
4832 parser.addGrammarElement("eventName", function (parser, runtime, tokens) {
4833 var token;
4834 if ((token = tokens.matchTokenType("STRING"))) {
4835 return {
4836 evaluate: function() {
4837 return token.value;
4838 },
4839 };
4840 }
4841
4842 return parser.parseElement("dotOrColonPath", tokens);
4843 });
4844
4845 function parseSendCmd(cmdType, parser, runtime, tokens) {
4846 var eventName = parser.requireElement("eventName", tokens);
4847
4848 var details = parser.parseElement("namedArgumentList", tokens);
4849 if ((cmdType === "send" && tokens.matchToken("to")) ||
4850 (cmdType === "trigger" && tokens.matchToken("on"))) {
4851 var toExpr = parser.requireElement("expression", tokens);
4852 } else {
4853 var toExpr = parser.requireElement("implicitMeTarget", tokens);
4854 }
4855
4856 var sendCmd = {
4857 eventName: eventName,
4858 details: details,
4859 to: toExpr,
4860 args: [toExpr, eventName, details],
4861 op: function (context, to, eventName, details) {
4862 runtime.nullCheck(to, toExpr);
4863 runtime.forEach(to, function (target) {
4864 runtime.triggerEvent(target, eventName, details, context.me);
4865 });
4866 return runtime.findNext(sendCmd, context);
4867 },
4868 };
4869 return sendCmd;
4870 }
4871
4872 parser.addCommand("trigger", function (parser, runtime, tokens) {
4873 if (tokens.matchToken("trigger")) {
4874 return parseSendCmd("trigger", parser, runtime, tokens);
4875 }
4876 });
4877
4878 parser.addCommand("send", function (parser, runtime, tokens) {
4879 if (tokens.matchToken("send")) {
4880 return parseSendCmd("send", parser, runtime, tokens);
4881 }
4882 });
4883
4884 var parseReturnFunction = function (parser, runtime, tokens, returnAValue) {
4885 if (returnAValue) {
4886 if (parser.commandBoundary(tokens.currentToken())) {
4887 parser.raiseParseError(tokens, "'return' commands must return a value. If you do not wish to return a value, use 'exit' instead.");
4888 } else {
4889 var value = parser.requireElement("expression", tokens);
4890 }
4891 }
4892
4893 var returnCmd = {
4894 value: value,
4895 args: [value],
4896 op: function (context, value) {
4897 var resolve = context.meta.resolve;
4898 context.meta.returned = true;
4899 context.meta.returnValue = value;
4900 if (resolve) {
4901 if (value) {
4902 resolve(value);
4903 } else {
4904 resolve();
4905 }
4906 }
4907 return runtime.HALT;
4908 },
4909 };
4910 return returnCmd;
4911 };
4912
4913 parser.addCommand("return", function (parser, runtime, tokens) {
4914 if (tokens.matchToken("return")) {
4915 return parseReturnFunction(parser, runtime, tokens, true);
4916 }
4917 });
4918
4919 parser.addCommand("exit", function (parser, runtime, tokens) {
4920 if (tokens.matchToken("exit")) {
4921 return parseReturnFunction(parser, runtime, tokens, false);
4922 }
4923 });
4924
4925 parser.addCommand("halt", function (parser, runtime, tokens) {
4926 if (tokens.matchToken("halt")) {
4927 if (tokens.matchToken("the")) {
4928 tokens.requireToken("event");
4929 // optional possessive
4930 if (tokens.matchOpToken("'")) {
4931 tokens.requireToken("s");
4932 }
4933 var keepExecuting = true;
4934 }
4935 if (tokens.matchToken("bubbling")) {
4936 var bubbling = true;
4937 } else if (tokens.matchToken("default")) {
4938 var haltDefault = true;
4939 }
4940 var exit = parseReturnFunction(parser, runtime, tokens, false);
4941
4942 var haltCmd = {
4943 keepExecuting: true,
4944 bubbling: bubbling,
4945 haltDefault: haltDefault,
4946 exit: exit,
4947 op: function (ctx) {
4948 if (ctx.event) {
4949 if (bubbling) {
4950 ctx.event.stopPropagation();
4951 } else if (haltDefault) {
4952 ctx.event.preventDefault();
4953 } else {
4954 ctx.event.stopPropagation();
4955 ctx.event.preventDefault();
4956 }
4957 if (keepExecuting) {
4958 return runtime.findNext(this, ctx);
4959 } else {
4960 return exit;
4961 }
4962 }
4963 },
4964 };
4965 return haltCmd;
4966 }
4967 });
4968
4969 parser.addCommand("log", function (parser, runtime, tokens) {
4970 if (!tokens.matchToken("log")) return;
4971 var exprs = [parser.parseElement("expression", tokens)];
4972 while (tokens.matchOpToken(",")) {
4973 exprs.push(parser.requireElement("expression", tokens));
4974 }
4975 if (tokens.matchToken("with")) {
4976 var withExpr = parser.requireElement("expression", tokens);
4977 }
4978 var logCmd = {
4979 exprs: exprs,
4980 withExpr: withExpr,
4981 args: [withExpr, exprs],
4982 op: function (ctx, withExpr, values) {
4983 if (withExpr) {
4984 withExpr.apply(null, values);
4985 } else {
4986 console.log.apply(null, values);
4987 }
4988 return runtime.findNext(this, ctx);
4989 },
4990 };
4991 return logCmd;
4992 });
4993
4994 parser.addCommand("throw", function (parser, runtime, tokens) {
4995 if (!tokens.matchToken("throw")) return;
4996 var expr = parser.requireElement("expression", tokens);
4997 var throwCmd = {
4998 expr: expr,
4999 args: [expr],
5000 op: function (ctx, expr) {
5001 runtime.registerHyperTrace(ctx, expr);
5002 throw expr;
5003 },
5004 };
5005 return throwCmd;
5006 });
5007
5008 var parseCallOrGet = function (parser, runtime, tokens) {
5009 var expr = parser.requireElement("expression", tokens);
5010 var callCmd = {
5011 expr: expr,
5012 args: [expr],
5013 op: function (context, result) {
5014 context.result = result;
5015 return runtime.findNext(callCmd, context);
5016 },
5017 };
5018 return callCmd;
5019 };
5020 parser.addCommand("call", function (parser, runtime, tokens) {
5021 if (!tokens.matchToken("call")) return;
5022 var call = parseCallOrGet(parser, runtime, tokens);
5023 if (call.expr && call.expr.type !== "functionCall") {
5024 parser.raiseParseError(tokens, "Must be a function invocation");
5025 }
5026 return call;
5027 });
5028 parser.addCommand("get", function (parser, runtime, tokens) {
5029 if (tokens.matchToken("get")) {
5030 return parseCallOrGet(parser, runtime, tokens);
5031 }
5032 });
5033
5034 parser.addCommand("make", function (parser, runtime, tokens) {
5035 if (!tokens.matchToken("make")) return;
5036 tokens.matchToken("a") || tokens.matchToken("an");
5037
5038 var expr = parser.requireElement("expression", tokens);
5039
5040 var args = [];
5041 if (expr.type !== "queryRef" && tokens.matchToken("from")) {
5042 do {
5043 args.push(parser.requireElement("expression", tokens));
5044 } while (tokens.matchOpToken(","));
5045 }
5046
5047 if (tokens.matchToken("called")) {
5048 var target = parser.requireElement("symbol", tokens);
5049 }
5050
5051 var command;
5052 if (expr.type === "queryRef") {
5053 command = {
5054 op: function (ctx) {
5055 var match,
5056 tagname = "div",
5057 id,
5058 classes = [];
5059 var re = /(?:(^|#|\.)([^#\. ]+))/g;
5060 while ((match = re.exec(expr.css))) {
5061 if (match[1] === "") tagname = match[2].trim();
5062 else if (match[1] === "#") id = match[2].trim();
5063 else classes.push(match[2].trim());
5064 }
5065
5066 var result = document.createElement(tagname);
5067 if (id !== undefined) result.id = id;
5068 for (var i = 0; i < classes.length; i++) {
5069 var cls = classes[i];
5070 result.classList.add(cls)
5071 }
5072
5073 ctx.result = result;
5074 if (target){
5075 runtime.setSymbol(target.name, ctx, target.scope, result);
5076 }
5077
5078 return runtime.findNext(this, ctx);
5079 },
5080 };
5081 return command;
5082 } else {
5083 command = {
5084 args: [expr, args],
5085 op: function (ctx, expr, args) {
5086 ctx.result = varargConstructor(expr, args);
5087 if (target){
5088 runtime.setSymbol(target.name, ctx, target.scope, ctx.result);
5089 }
5090
5091 return runtime.findNext(this, ctx);
5092 },
5093 };
5094 return command;
5095 }
5096 });
5097
5098 parser.addGrammarElement("pseudoCommand", function (parser, runtime, tokens) {
5099
5100 let lookAhead = tokens.token(1);
5101 if (!(lookAhead && lookAhead.op && (lookAhead.value === '.' || lookAhead.value === "("))) {
5102 return null;
5103 }
5104
5105 var expr = parser.requireElement("primaryExpression", tokens);
5106
5107 var rootRoot = expr.root;
5108 var root = expr;
5109 while (rootRoot.root != null) {
5110 root = root.root;
5111 rootRoot = rootRoot.root;
5112 }
5113
5114 if (expr.type !== "functionCall") {
5115 parser.raiseParseError(tokens, "Pseudo-commands must be function calls");
5116 }
5117
5118 if (root.type === "functionCall" && root.root.root == null) {
5119 if (tokens.matchAnyToken("the", "to", "on", "with", "into", "from", "at")) {
5120 var realRoot = parser.requireElement("expression", tokens);
5121 } else if (tokens.matchToken("me")) {
5122 var realRoot = parser.requireElement("implicitMeTarget", tokens);
5123 }
5124 }
5125
5126 /** @type {ASTNode} */
5127
5128 var pseudoCommand
5129 if(realRoot){
5130 pseudoCommand = {
5131 type: "pseudoCommand",
5132 root: realRoot,
5133 argExressions: root.argExressions,
5134 args: [realRoot, root.argExressions],
5135 op: function (context, rootRoot, args) {
5136 runtime.nullCheck(rootRoot, realRoot);
5137 var func = rootRoot[root.root.name];
5138 runtime.nullCheck(func, root);
5139 if (func.hyperfunc) {
5140 args.push(context);
5141 }
5142 context.result = func.apply(rootRoot, args);
5143 return runtime.findNext(pseudoCommand, context);
5144 },
5145 execute: function (context) {
5146 return runtime.unifiedExec(this, context);
5147 },
5148 }
5149 } else {
5150 pseudoCommand = {
5151 type: "pseudoCommand",
5152 expr: expr,
5153 args: [expr],
5154 op: function (context, result) {
5155 context.result = result;
5156 return runtime.findNext(pseudoCommand, context);
5157 },
5158 execute: function (context) {
5159 return runtime.unifiedExec(this, context);
5160 },
5161 };
5162 }
5163
5164 return pseudoCommand;
5165 });
5166
5167 /**
5168 * @param {Parser} parser
5169 * @param {Runtime} runtime
5170 * @param {Tokens} tokens
5171 * @param {*} target
5172 * @param {*} value
5173 * @returns
5174 */
5175 var makeSetter = function (parser, runtime, tokens, target, value) {
5176
5177 var symbolWrite = target.type === "symbol";
5178 var attributeWrite = target.type === "attributeRef";
5179 var styleWrite = target.type === "styleRef";
5180 var arrayWrite = target.type === "arrayIndex";
5181
5182 if (!(attributeWrite || styleWrite || symbolWrite) && target.root == null) {
5183 parser.raiseParseError(tokens, "Can only put directly into symbols, not references");
5184 }
5185
5186 var rootElt = null;
5187 var prop = null;
5188 if (symbolWrite) {
5189 // rootElt is null
5190 } else if (attributeWrite || styleWrite) {
5191 rootElt = parser.requireElement("implicitMeTarget", tokens);
5192 var attribute = target;
5193 } else if(arrayWrite) {
5194 prop = target.firstIndex;
5195 rootElt = target.root;
5196 } else {
5197 prop = target.prop ? target.prop.value : null;
5198 var attribute = target.attribute;
5199 rootElt = target.root;
5200 }
5201
5202 /** @type {ASTNode} */
5203 var setCmd = {
5204 target: target,
5205 symbolWrite: symbolWrite,
5206 value: value,
5207 args: [rootElt, prop, value],
5208 op: function (context, root, prop, valueToSet) {
5209 if (symbolWrite) {
5210 runtime.setSymbol(target.name, context, target.scope, valueToSet);
5211 } else {
5212 runtime.nullCheck(root, rootElt);
5213 if (arrayWrite) {
5214 root[prop] = valueToSet;
5215 } else {
5216 runtime.implicitLoop(root, function (elt) {
5217 if (attribute) {
5218 if (attribute.type === "attributeRef") {
5219 if (valueToSet == null) {
5220 elt.removeAttribute(attribute.name);
5221 } else {
5222 elt.setAttribute(attribute.name, valueToSet);
5223 }
5224 } else {
5225 elt.style[attribute.name] = valueToSet;
5226 }
5227 } else {
5228 elt[prop] = valueToSet;
5229 }
5230 });
5231 }
5232 }
5233 return runtime.findNext(this, context);
5234 },
5235 };
5236 return setCmd;
5237 };
5238
5239 parser.addCommand("default", function (parser, runtime, tokens) {
5240 if (!tokens.matchToken("default")) return;
5241 var target = parser.requireElement("assignableExpression", tokens);
5242 tokens.requireToken("to");
5243
5244 var value = parser.requireElement("expression", tokens);
5245
5246 /** @type {ASTNode} */
5247 var setter = makeSetter(parser, runtime, tokens, target, value);
5248 var defaultCmd = {
5249 target: target,
5250 value: value,
5251 setter: setter,
5252 args: [target],
5253 op: function (context, target) {
5254 if (target) {
5255 return runtime.findNext(this, context);
5256 } else {
5257 return setter;
5258 }
5259 },
5260 };
5261 setter.parent = defaultCmd;
5262 return defaultCmd;
5263 });
5264
5265 parser.addCommand("set", function (parser, runtime, tokens) {
5266 if (!tokens.matchToken("set")) return;
5267 if (tokens.currentToken().type === "L_BRACE") {
5268 var obj = parser.requireElement("objectLiteral", tokens);
5269 tokens.requireToken("on");
5270 var target = parser.requireElement("expression", tokens);
5271
5272 var command = {
5273 objectLiteral: obj,
5274 target: target,
5275 args: [obj, target],
5276 op: function (ctx, obj, target) {
5277 Object.assign(target, obj);
5278 return runtime.findNext(this, ctx);
5279 },
5280 };
5281 return command;
5282 }
5283
5284 try {
5285 tokens.pushFollow("to");
5286 var target = parser.requireElement("assignableExpression", tokens);
5287 } finally {
5288 tokens.popFollow();
5289 }
5290 tokens.requireToken("to");
5291 var value = parser.requireElement("expression", tokens);
5292 return makeSetter(parser, runtime, tokens, target, value);
5293 });
5294
5295 parser.addCommand("if", function (parser, runtime, tokens) {
5296 if (!tokens.matchToken("if")) return;
5297 var expr = parser.requireElement("expression", tokens);
5298 tokens.matchToken("then"); // optional 'then'
5299 var trueBranch = parser.parseElement("commandList", tokens);
5300 if (tokens.matchToken("else") || tokens.matchToken("otherwise")) {
5301 var falseBranch = parser.parseElement("commandList", tokens);
5302 }
5303 if (tokens.hasMore()) {
5304 tokens.requireToken("end");
5305 }
5306
5307 /** @type {ASTNode} */
5308 var ifCmd = {
5309 expr: expr,
5310 trueBranch: trueBranch,
5311 falseBranch: falseBranch,
5312 args: [expr],
5313 op: function (context, exprValue) {
5314 if (exprValue) {
5315 return trueBranch;
5316 } else if (falseBranch) {
5317 return falseBranch;
5318 } else {
5319 return runtime.findNext(this, context);
5320 }
5321 },
5322 };
5323 parser.setParent(trueBranch, ifCmd);
5324 parser.setParent(falseBranch, ifCmd);
5325 return ifCmd;
5326 });
5327
5328 var parseRepeatExpression = function (parser, tokens, runtime, startedWithForToken) {
5329 var innerStartToken = tokens.currentToken();
5330 var identifier;
5331 if (tokens.matchToken("for") || startedWithForToken) {
5332 var identifierToken = tokens.requireTokenType("IDENTIFIER");
5333 identifier = identifierToken.value;
5334 tokens.requireToken("in");
5335 var expression = parser.requireElement("expression", tokens);
5336 } else if (tokens.matchToken("in")) {
5337 identifier = "it";
5338 var expression = parser.requireElement("expression", tokens);
5339 } else if (tokens.matchToken("while")) {
5340 var whileExpr = parser.requireElement("expression", tokens);
5341 } else if (tokens.matchToken("until")) {
5342 var isUntil = true;
5343 if (tokens.matchToken("event")) {
5344 var evt = parser.requireElement("dotOrColonPath", tokens, "Expected event name");
5345 if (tokens.matchToken("from")) {
5346 var on = parser.requireElement("expression", tokens);
5347 }
5348 } else {
5349 var whileExpr = parser.requireElement("expression", tokens);
5350 }
5351 } else {
5352 if (!parser.commandBoundary(tokens.currentToken()) &&
5353 tokens.currentToken().value !== 'forever') {
5354 var times = parser.requireElement("expression", tokens);
5355 tokens.requireToken("times");
5356 } else {
5357 tokens.matchToken("forever"); // consume optional forever
5358 var forever = true;
5359 }
5360 }
5361
5362 if (tokens.matchToken("index")) {
5363 var identifierToken = tokens.requireTokenType("IDENTIFIER");
5364 var indexIdentifier = identifierToken.value;
5365 }
5366
5367 var loop = parser.parseElement("commandList", tokens);
5368 if (loop && evt) {
5369 // if this is an event based loop, wait a tick at the end of the loop so that
5370 // events have a chance to trigger in the loop condition o_O)))
5371 var last = loop;
5372 while (last.next) {
5373 last = last.next;
5374 }
5375 var waitATick = {
5376 type: "waitATick",
5377 op: function () {
5378 return new Promise(function (resolve) {
5379 setTimeout(function () {
5380 resolve(runtime.findNext(waitATick));
5381 }, 0);
5382 });
5383 },
5384 };
5385 last.next = waitATick;
5386 }
5387 if (tokens.hasMore()) {
5388 tokens.requireToken("end");
5389 }
5390
5391 if (identifier == null) {
5392 identifier = "_implicit_repeat_" + innerStartToken.start;
5393 var slot = identifier;
5394 } else {
5395 var slot = identifier + "_" + innerStartToken.start;
5396 }
5397
5398 var repeatCmd = {
5399 identifier: identifier,
5400 indexIdentifier: indexIdentifier,
5401 slot: slot,
5402 expression: expression,
5403 forever: forever,
5404 times: times,
5405 until: isUntil,
5406 event: evt,
5407 on: on,
5408 whileExpr: whileExpr,
5409 resolveNext: function () {
5410 return this;
5411 },
5412 loop: loop,
5413 args: [whileExpr, times],
5414 op: function (context, whileValue, times) {
5415 var iteratorInfo = context.meta.iterators[slot];
5416 var keepLooping = false;
5417 var loopVal = null;
5418 if (this.forever) {
5419 keepLooping = true;
5420 } else if (this.until) {
5421 if (evt) {
5422 keepLooping = context.meta.iterators[slot].eventFired === false;
5423 } else {
5424 keepLooping = whileValue !== true;
5425 }
5426 } else if (whileExpr) {
5427 keepLooping = whileValue;
5428 } else if (times) {
5429 keepLooping = iteratorInfo.index < times;
5430 } else {
5431 var nextValFromIterator = iteratorInfo.iterator.next();
5432 keepLooping = !nextValFromIterator.done;
5433 loopVal = nextValFromIterator.value;
5434 }
5435
5436 if (keepLooping) {
5437 if (iteratorInfo.value) {
5438 context.result = context.locals[identifier] = loopVal;
5439 } else {
5440 context.result = iteratorInfo.index;
5441 }
5442 if (indexIdentifier) {
5443 context.locals[indexIdentifier] = iteratorInfo.index;
5444 }
5445 iteratorInfo.index++;
5446 return loop;
5447 } else {
5448 context.meta.iterators[slot] = null;
5449 return runtime.findNext(this.parent, context);
5450 }
5451 },
5452 };
5453 parser.setParent(loop, repeatCmd);
5454 var repeatInit = {
5455 name: "repeatInit",
5456 args: [expression, evt, on],
5457 op: function (context, value, event, on) {
5458 var iteratorInfo = {
5459 index: 0,
5460 value: value,
5461 eventFired: false,
5462 };
5463 context.meta.iterators[slot] = iteratorInfo;
5464 if (value && value[Symbol.iterator]) {
5465 iteratorInfo.iterator = value[Symbol.iterator]();
5466 }
5467 if (evt) {
5468 var target = on || context.me;
5469 target.addEventListener(
5470 event,
5471 function (e) {
5472 context.meta.iterators[slot].eventFired = true;
5473 },
5474 { once: true }
5475 );
5476 }
5477 return repeatCmd; // continue to loop
5478 },
5479 execute: function (context) {
5480 return runtime.unifiedExec(this, context);
5481 },
5482 };
5483 parser.setParent(repeatCmd, repeatInit);
5484 return repeatInit;
5485 };
5486
5487 parser.addCommand("repeat", function (parser, runtime, tokens) {
5488 if (tokens.matchToken("repeat")) {
5489 return parseRepeatExpression(parser, tokens, runtime, false);
5490 }
5491 });
5492
5493 parser.addCommand("for", function (parser, runtime, tokens) {
5494 if (tokens.matchToken("for")) {
5495 return parseRepeatExpression(parser, tokens, runtime, true);
5496 }
5497 });
5498
5499 parser.addCommand("continue", function (parser, runtime, tokens) {
5500
5501 if (!tokens.matchToken("continue")) return;
5502
5503 var command = {
5504 op: function (context) {
5505
5506 // scan for the closest repeat statement
5507 for (var parent = this.parent ; true ; parent = parent.parent) {
5508
5509 if (parent == undefined) {
5510 parser.raiseParseError(tokens, "Command `continue` cannot be used outside of a `repeat` loop.")
5511 }
5512 if (parent.loop != undefined) {
5513 return parent.resolveNext(context)
5514 }
5515 }
5516 }
5517 };
5518 return command;
5519 });
5520
5521 parser.addCommand("break", function (parser, runtime, tokens) {
5522
5523 if (!tokens.matchToken("break")) return;
5524
5525 var command = {
5526 op: function (context) {
5527
5528 // scan for the closest repeat statement
5529 for (var parent = this.parent ; true ; parent = parent.parent) {
5530
5531 if (parent == undefined) {
5532 parser.raiseParseError(tokens, "Command `continue` cannot be used outside of a `repeat` loop.")
5533 }
5534 if (parent.loop != undefined) {
5535 return runtime.findNext(parent.parent, context);
5536 }
5537 }
5538 }
5539 };
5540 return command;
5541 });
5542
5543 parser.addGrammarElement("stringLike", function (parser, runtime, tokens) {
5544 return parser.parseAnyOf(["string", "nakedString"], tokens);
5545 });
5546
5547 parser.addCommand("append", function (parser, runtime, tokens) {
5548 if (!tokens.matchToken("append")) return;
5549 var targetExpr = null;
5550
5551 var value = parser.requireElement("expression", tokens);
5552
5553 /** @type {ASTNode} */
5554 var implicitResultSymbol = {
5555 type: "symbol",
5556 evaluate: function (context) {
5557 return runtime.resolveSymbol("result", context);
5558 },
5559 };
5560
5561 if (tokens.matchToken("to")) {
5562 targetExpr = parser.requireElement("expression", tokens);
5563 } else {
5564 targetExpr = implicitResultSymbol;
5565 }
5566
5567 var setter = null;
5568 if (targetExpr.type === "symbol" || targetExpr.type === "attributeRef" || targetExpr.root != null) {
5569 setter = makeSetter(parser, runtime, tokens, targetExpr, implicitResultSymbol);
5570 }
5571
5572 var command = {
5573 value: value,
5574 target: targetExpr,
5575 args: [targetExpr, value],
5576 op: function (context, target, value) {
5577 if (Array.isArray(target)) {
5578 target.push(value);
5579 return runtime.findNext(this, context);
5580 } else if (target instanceof Element) {
5581 target.innerHTML += value;
5582 return runtime.findNext(this, context);
5583 } else if(setter) {
5584 context.result = (target || "") + value;
5585 return setter;
5586 } else {
5587 throw Error("Unable to append a value!")
5588 }
5589 },
5590 execute: function (context) {
5591 return runtime.unifiedExec(this, context/*, value, target*/);
5592 },
5593 };
5594
5595 if (setter != null) {
5596 setter.parent = command;
5597 }
5598
5599 return command;
5600 });
5601
5602 function parsePickRange(parser, runtime, tokens) {
5603 tokens.matchToken("at") || tokens.matchToken("from");
5604 const rv = { includeStart: true, includeEnd: false }
5605
5606 rv.from = tokens.matchToken("start") ? 0 : parser.requireElement("expression", tokens)
5607
5608 if (tokens.matchToken("to") || tokens.matchOpToken("..")) {
5609 if (tokens.matchToken("end")) {
5610 rv.toEnd = true;
5611 } else {
5612 rv.to = parser.requireElement("expression", tokens);
5613 }
5614 }
5615
5616 if (tokens.matchToken("inclusive")) rv.includeEnd = true;
5617 else if (tokens.matchToken("exclusive")) rv.includeStart = false;
5618
5619 return rv;
5620 }
5621
5622 class RegExpIterator {
5623 constructor(re, str) {
5624 this.re = re;
5625 this.str = str;
5626 }
5627
5628 next() {
5629 const match = this.re.exec(this.str);
5630 if (match === null) return { done: true };
5631 else return { value: match };
5632 }
5633 }
5634
5635 class RegExpIterable {
5636 constructor(re, flags, str) {
5637 this.re = re;
5638 this.flags = flags;
5639 this.str = str;
5640 }
5641
5642 [Symbol.iterator]() {
5643 return new RegExpIterator(new RegExp(this.re, this.flags), this.str);
5644 }
5645 }
5646
5647 parser.addCommand("pick", (parser, runtime, tokens) => {
5648 if (!tokens.matchToken("pick")) return;
5649
5650 tokens.matchToken("the");
5651
5652 if (tokens.matchToken("item") || tokens.matchToken("items")
5653 || tokens.matchToken("character") || tokens.matchToken("characters")) {
5654 const range = parsePickRange(parser, runtime, tokens);
5655
5656 tokens.requireToken("from");
5657 const root = parser.requireElement("expression", tokens);
5658
5659 return {
5660 args: [root, range.from, range.to],
5661 op(ctx, root, from, to) {
5662 if (range.toEnd) to = root.length;
5663 if (!range.includeStart) from++;
5664 if (range.includeEnd) to++;
5665 if (to == null || to == undefined) to = from + 1;
5666 ctx.result = root.slice(from, to);
5667 return runtime.findNext(this, ctx);
5668 }
5669 }
5670 }
5671
5672 if (tokens.matchToken("match")) {
5673 tokens.matchToken("of");
5674 const re = parser.parseElement("expression", tokens);
5675 let flags = ""
5676 if (tokens.matchOpToken("|")) {
5677 flags = tokens.requireToken("identifier").value;
5678 }
5679
5680 tokens.requireToken("from");
5681 const root = parser.parseElement("expression", tokens);
5682
5683 return {
5684 args: [root, re],
5685 op(ctx, root, re) {
5686 ctx.result = new RegExp(re, flags).exec(root);
5687 return runtime.findNext(this, ctx);
5688 }
5689 }
5690 }
5691
5692 if (tokens.matchToken("matches")) {
5693 tokens.matchToken("of");
5694 const re = parser.parseElement("expression", tokens);
5695 let flags = "gu"
5696 if (tokens.matchOpToken("|")) {
5697 flags = 'g' + tokens.requireToken("identifier").value.replace('g', '');
5698 }
5699 console.log('flags', flags)
5700
5701 tokens.requireToken("from");
5702 const root = parser.parseElement("expression", tokens);
5703
5704 return {
5705 args: [root, re],
5706 op(ctx, root, re) {
5707 ctx.result = new RegExpIterable(re, flags, root);
5708 return runtime.findNext(this, ctx);
5709 }
5710 }
5711 }
5712 });
5713
5714 parser.addCommand("increment", function (parser, runtime, tokens) {
5715 if (!tokens.matchToken("increment")) return;
5716 var amountExpr;
5717
5718 // This is optional. Defaults to "result"
5719 var target = parser.parseElement("assignableExpression", tokens);
5720
5721 // This is optional. Defaults to 1.
5722 if (tokens.matchToken("by")) {
5723 amountExpr = parser.requireElement("expression", tokens);
5724 }
5725
5726 var implicitIncrementOp = {
5727 type: "implicitIncrementOp",
5728 target: target,
5729 args: [target, amountExpr],
5730 op: function (context, targetValue, amount) {
5731 targetValue = targetValue ? parseFloat(targetValue) : 0;
5732 amount = amountExpr ? parseFloat(amount) : 1;
5733 var newValue = targetValue + amount;
5734 context.result = newValue;
5735 return newValue;
5736 },
5737 evaluate: function (context) {
5738 return runtime.unifiedEval(this, context);
5739 }
5740 };
5741
5742 return makeSetter(parser, runtime, tokens, target, implicitIncrementOp);
5743 });
5744
5745 parser.addCommand("decrement", function (parser, runtime, tokens) {
5746 if (!tokens.matchToken("decrement")) return;
5747 var amountExpr;
5748
5749 // This is optional. Defaults to "result"
5750 var target = parser.parseElement("assignableExpression", tokens);
5751
5752 // This is optional. Defaults to 1.
5753 if (tokens.matchToken("by")) {
5754 amountExpr = parser.requireElement("expression", tokens);
5755 }
5756
5757 var implicitDecrementOp = {
5758 type: "implicitDecrementOp",
5759 target: target,
5760 args: [target, amountExpr],
5761 op: function (context, targetValue, amount) {
5762 targetValue = targetValue ? parseFloat(targetValue) : 0;
5763 amount = amountExpr ? parseFloat(amount) : 1;
5764 var newValue = targetValue - amount;
5765 context.result = newValue;
5766 return newValue;
5767 },
5768 evaluate: function (context) {
5769 return runtime.unifiedEval(this, context);
5770 }
5771 };
5772
5773 return makeSetter(parser, runtime, tokens, target, implicitDecrementOp);
5774 });
5775
5776 function parseConversionInfo(tokens, parser) {
5777 var type = "text";
5778 var conversion;
5779 tokens.matchToken("a") || tokens.matchToken("an");
5780 if (tokens.matchToken("json") || tokens.matchToken("Object")) {
5781 type = "json";
5782 } else if (tokens.matchToken("response")) {
5783 type = "response";
5784 } else if (tokens.matchToken("html")) {
5785 type = "html";
5786 } else if (tokens.matchToken("text")) {
5787 // default, ignore
5788 } else {
5789 conversion = parser.requireElement("dotOrColonPath", tokens).evaluate();
5790 }
5791 return {type, conversion};
5792 }
5793
5794 parser.addCommand("fetch", function (parser, runtime, tokens) {
5795 if (!tokens.matchToken("fetch")) return;
5796 var url = parser.requireElement("stringLike", tokens);
5797
5798 if (tokens.matchToken("as")) {
5799 var conversionInfo = parseConversionInfo(tokens, parser);
5800 }
5801
5802 if (tokens.matchToken("with") && tokens.currentToken().value !== "{") {
5803 var args = parser.parseElement("nakedNamedArgumentList", tokens);
5804 } else {
5805 var args = parser.parseElement("objectLiteral", tokens);
5806 }
5807
5808 if (conversionInfo == null && tokens.matchToken("as")) {
5809 conversionInfo = parseConversionInfo(tokens, parser);
5810 }
5811
5812 var type = conversionInfo ? conversionInfo.type : "text";
5813 var conversion = conversionInfo ? conversionInfo.conversion : null
5814
5815 /** @type {ASTNode} */
5816 var fetchCmd = {
5817 url: url,
5818 argExpressions: args,
5819 args: [url, args],
5820 op: function (context, url, args) {
5821 var detail = args || {};
5822 detail["sender"] = context.me;
5823 detail["headers"] = detail["headers"] || {}
5824 var abortController = new AbortController();
5825 let abortListener = context.me.addEventListener('fetch:abort', function(){
5826 abortController.abort();
5827 }, {once: true});
5828 detail['signal'] = abortController.signal;
5829 runtime.triggerEvent(context.me, "hyperscript:beforeFetch", detail);
5830 runtime.triggerEvent(context.me, "fetch:beforeRequest", detail);
5831 args = detail;
5832 var finished = false;
5833 if (args.timeout) {
5834 setTimeout(function () {
5835 if (!finished) {
5836 abortController.abort();
5837 }
5838 }, args.timeout);
5839 }
5840 return fetch(url, args)
5841 .then(function (resp) {
5842 let resultDetails = {response:resp};
5843 runtime.triggerEvent(context.me, "fetch:afterResponse", resultDetails);
5844 resp = resultDetails.response;
5845
5846 if (type === "response") {
5847 context.result = resp;
5848 runtime.triggerEvent(context.me, "fetch:afterRequest", {result:resp});
5849 finished = true;
5850 return runtime.findNext(fetchCmd, context);
5851 }
5852 if (type === "json") {
5853 return resp.json().then(function (result) {
5854 context.result = result;
5855 runtime.triggerEvent(context.me, "fetch:afterRequest", {result});
5856 finished = true;
5857 return runtime.findNext(fetchCmd, context);
5858 });
5859 }
5860 return resp.text().then(function (result) {
5861 if (conversion) result = runtime.convertValue(result, conversion);
5862
5863 if (type === "html") result = runtime.convertValue(result, "Fragment");
5864
5865 context.result = result;
5866 runtime.triggerEvent(context.me, "fetch:afterRequest", {result});
5867 finished = true;
5868 return runtime.findNext(fetchCmd, context);
5869 });
5870 })
5871 .catch(function (reason) {
5872 runtime.triggerEvent(context.me, "fetch:error", {
5873 reason: reason,
5874 });
5875 throw reason;
5876 }).finally(function(){
5877 context.me.removeEventListener('fetch:abort', abortListener);
5878 });
5879 },
5880 };
5881 return fetchCmd;
5882 });
5883 }
5884
5885 function hyperscriptWebGrammar(parser) {
5886 parser.addCommand("settle", function (parser, runtime, tokens) {
5887 if (tokens.matchToken("settle")) {
5888 if (!parser.commandBoundary(tokens.currentToken())) {
5889 var onExpr = parser.requireElement("expression", tokens);
5890 } else {
5891 var onExpr = parser.requireElement("implicitMeTarget", tokens);
5892 }
5893
5894 var settleCommand = {
5895 type: "settleCmd",
5896 args: [onExpr],
5897 op: function (context, on) {
5898 runtime.nullCheck(on, onExpr);
5899 var resolve = null;
5900 var resolved = false;
5901 var transitionStarted = false;
5902
5903 var promise = new Promise(function (r) {
5904 resolve = r;
5905 });
5906
5907 // listen for a transition begin
5908 on.addEventListener(
5909 "transitionstart",
5910 function () {
5911 transitionStarted = true;
5912 },
5913 { once: true }
5914 );
5915
5916 // if no transition begins in 500ms, cancel
5917 setTimeout(function () {
5918 if (!transitionStarted && !resolved) {
5919 resolve(runtime.findNext(settleCommand, context));
5920 }
5921 }, 500);
5922
5923 // continue on a transition emd
5924 on.addEventListener(
5925 "transitionend",
5926 function () {
5927 if (!resolved) {
5928 resolve(runtime.findNext(settleCommand, context));
5929 }
5930 },
5931 { once: true }
5932 );
5933 return promise;
5934 },
5935 execute: function (context) {
5936 return runtime.unifiedExec(this, context);
5937 },
5938 };
5939 return settleCommand;
5940 }
5941 });
5942
5943 parser.addCommand("add", function (parser, runtime, tokens) {
5944 if (tokens.matchToken("add")) {
5945 var classRef = parser.parseElement("classRef", tokens);
5946 var attributeRef = null;
5947 var cssDeclaration = null;
5948 if (classRef == null) {
5949 attributeRef = parser.parseElement("attributeRef", tokens);
5950 if (attributeRef == null) {
5951 cssDeclaration = parser.parseElement("styleLiteral", tokens);
5952 if (cssDeclaration == null) {
5953 parser.raiseParseError(tokens, "Expected either a class reference or attribute expression");
5954 }
5955 }
5956 } else {
5957 var classRefs = [classRef];
5958 while ((classRef = parser.parseElement("classRef", tokens))) {
5959 classRefs.push(classRef);
5960 }
5961 }
5962
5963 if (tokens.matchToken("to")) {
5964 var toExpr = parser.requireElement("expression", tokens);
5965 } else {
5966 var toExpr = parser.requireElement("implicitMeTarget", tokens);
5967 }
5968
5969 if (tokens.matchToken("when")) {
5970 if (cssDeclaration) {
5971 parser.raiseParseError(tokens, "Only class and properties are supported with a when clause")
5972 }
5973 var when = parser.requireElement("expression", tokens);
5974 }
5975
5976 if (classRefs) {
5977 return {
5978 classRefs: classRefs,
5979 to: toExpr,
5980 args: [toExpr, classRefs],
5981 op: function (context, to, classRefs) {
5982 runtime.nullCheck(to, toExpr);
5983 runtime.forEach(classRefs, function (classRef) {
5984 runtime.implicitLoop(to, function (target) {
5985 if (when) {
5986 context.result = target;
5987 let whenResult = runtime.evaluateNoPromise(when, context);
5988 if (whenResult) {
5989 if (target instanceof Element) target.classList.add(classRef.className);
5990 } else {
5991 if (target instanceof Element) target.classList.remove(classRef.className);
5992 }
5993 context.result = null;
5994 } else {
5995 if (target instanceof Element) target.classList.add(classRef.className);
5996 }
5997 });
5998 });
5999 return runtime.findNext(this, context);
6000 },
6001 };
6002 } else if (attributeRef) {
6003 return {
6004 type: "addCmd",
6005 attributeRef: attributeRef,
6006 to: toExpr,
6007 args: [toExpr],
6008 op: function (context, to, attrRef) {
6009 runtime.nullCheck(to, toExpr);
6010 runtime.implicitLoop(to, function (target) {
6011 if (when) {
6012 context.result = target;
6013 let whenResult = runtime.evaluateNoPromise(when, context);
6014 if (whenResult) {
6015 target.setAttribute(attributeRef.name, attributeRef.value);
6016 } else {
6017 target.removeAttribute(attributeRef.name);
6018 }
6019 context.result = null;
6020 } else {
6021 target.setAttribute(attributeRef.name, attributeRef.value);
6022 }
6023 });
6024 return runtime.findNext(this, context);
6025 },
6026 execute: function (ctx) {
6027 return runtime.unifiedExec(this, ctx);
6028 },
6029 };
6030 } else {
6031 return {
6032 type: "addCmd",
6033 cssDeclaration: cssDeclaration,
6034 to: toExpr,
6035 args: [toExpr, cssDeclaration],
6036 op: function (context, to, css) {
6037 runtime.nullCheck(to, toExpr);
6038 runtime.implicitLoop(to, function (target) {
6039 target.style.cssText += css;
6040 });
6041 return runtime.findNext(this, context);
6042 },
6043 execute: function (ctx) {
6044 return runtime.unifiedExec(this, ctx);
6045 },
6046 };
6047 }
6048 }
6049 });
6050
6051 parser.addGrammarElement("styleLiteral", function (parser, runtime, tokens) {
6052 if (!tokens.matchOpToken("{")) return;
6053
6054 var stringParts = [""]
6055 var exprs = []
6056
6057 while (tokens.hasMore()) {
6058 if (tokens.matchOpToken("\\")) {
6059 tokens.consumeToken();
6060 } else if (tokens.matchOpToken("}")) {
6061 break;
6062 } else if (tokens.matchToken("$")) {
6063 var opencurly = tokens.matchOpToken("{");
6064 var expr = parser.parseElement("expression", tokens);
6065 if (opencurly) tokens.requireOpToken("}");
6066
6067 exprs.push(expr)
6068 stringParts.push("")
6069 } else {
6070 var tok = tokens.consumeToken();
6071 stringParts[stringParts.length-1] += tokens.source.substring(tok.start, tok.end);
6072 }
6073
6074 stringParts[stringParts.length-1] += tokens.lastWhitespace();
6075 }
6076
6077 return {
6078 type: "styleLiteral",
6079 args: [exprs],
6080 op: function (ctx, exprs) {
6081 var rv = "";
6082
6083 stringParts.forEach(function (part, idx) {
6084 rv += part;
6085 if (idx in exprs) rv += exprs[idx];
6086 });
6087
6088 return rv;
6089 },
6090 evaluate: function(ctx) {
6091 return runtime.unifiedEval(this, ctx);
6092 }
6093 }
6094 })
6095
6096 parser.addCommand("remove", function (parser, runtime, tokens) {
6097 if (tokens.matchToken("remove")) {
6098 var classRef = parser.parseElement("classRef", tokens);
6099 var attributeRef = null;
6100 var elementExpr = null;
6101 if (classRef == null) {
6102 attributeRef = parser.parseElement("attributeRef", tokens);
6103 if (attributeRef == null) {
6104 elementExpr = parser.parseElement("expression", tokens);
6105 if (elementExpr == null) {
6106 parser.raiseParseError(
6107 tokens,
6108 "Expected either a class reference, attribute expression or value expression"
6109 );
6110 }
6111 }
6112 } else {
6113 var classRefs = [classRef];
6114 while ((classRef = parser.parseElement("classRef", tokens))) {
6115 classRefs.push(classRef);
6116 }
6117 }
6118
6119 if (tokens.matchToken("from")) {
6120 var fromExpr = parser.requireElement("expression", tokens);
6121 } else {
6122 if (elementExpr == null) {
6123 var fromExpr = parser.requireElement("implicitMeTarget", tokens);
6124 }
6125 }
6126
6127 if (elementExpr) {
6128 return {
6129 elementExpr: elementExpr,
6130 from: fromExpr,
6131 args: [elementExpr, fromExpr],
6132 op: function (context, element, from) {
6133 runtime.nullCheck(element, elementExpr);
6134 runtime.implicitLoop(element, function (target) {
6135 if (target.parentElement && (from == null || from.contains(target))) {
6136 target.parentElement.removeChild(target);
6137 }
6138 });
6139 return runtime.findNext(this, context);
6140 },
6141 };
6142 } else {
6143 return {
6144 classRefs: classRefs,
6145 attributeRef: attributeRef,
6146 elementExpr: elementExpr,
6147 from: fromExpr,
6148 args: [classRefs, fromExpr],
6149 op: function (context, classRefs, from) {
6150 runtime.nullCheck(from, fromExpr);
6151 if (classRefs) {
6152 runtime.forEach(classRefs, function (classRef) {
6153 runtime.implicitLoop(from, function (target) {
6154 target.classList.remove(classRef.className);
6155 });
6156 });
6157 } else {
6158 runtime.implicitLoop(from, function (target) {
6159 target.removeAttribute(attributeRef.name);
6160 });
6161 }
6162 return runtime.findNext(this, context);
6163 },
6164 };
6165 }
6166 }
6167 });
6168
6169 parser.addCommand("toggle", function (parser, runtime, tokens) {
6170 if (tokens.matchToken("toggle")) {
6171 tokens.matchAnyToken("the", "my");
6172 if (tokens.currentToken().type === "STYLE_REF") {
6173 let styleRef = tokens.consumeToken();
6174 var name = styleRef.value.substr(1);
6175 var visibility = true;
6176 var hideShowStrategy = resolveStrategy(parser, tokens, name);
6177 if (tokens.matchToken("of")) {
6178 tokens.pushFollow("with");
6179 try {
6180 var onExpr = parser.requireElement("expression", tokens);
6181 } finally {
6182 tokens.popFollow();
6183 }
6184 } else {
6185 var onExpr = parser.requireElement("implicitMeTarget", tokens);
6186 }
6187 } else if (tokens.matchToken("between")) {
6188 var between = true;
6189 var classRef = parser.parseElement("classRef", tokens);
6190 tokens.requireToken("and");
6191 var classRef2 = parser.requireElement("classRef", tokens);
6192 } else {
6193 var classRef = parser.parseElement("classRef", tokens);
6194 var attributeRef = null;
6195 if (classRef == null) {
6196 attributeRef = parser.parseElement("attributeRef", tokens);
6197 if (attributeRef == null) {
6198 parser.raiseParseError(tokens, "Expected either a class reference or attribute expression");
6199 }
6200 } else {
6201 var classRefs = [classRef];
6202 while ((classRef = parser.parseElement("classRef", tokens))) {
6203 classRefs.push(classRef);
6204 }
6205 }
6206 }
6207
6208 if (visibility !== true) {
6209 if (tokens.matchToken("on")) {
6210 var onExpr = parser.requireElement("expression", tokens);
6211 } else {
6212 var onExpr = parser.requireElement("implicitMeTarget", tokens);
6213 }
6214 }
6215
6216 if (tokens.matchToken("for")) {
6217 var time = parser.requireElement("expression", tokens);
6218 } else if (tokens.matchToken("until")) {
6219 var evt = parser.requireElement("dotOrColonPath", tokens, "Expected event name");
6220 if (tokens.matchToken("from")) {
6221 var from = parser.requireElement("expression", tokens);
6222 }
6223 }
6224
6225 var toggleCmd = {
6226 classRef: classRef,
6227 classRef2: classRef2,
6228 classRefs: classRefs,
6229 attributeRef: attributeRef,
6230 on: onExpr,
6231 time: time,
6232 evt: evt,
6233 from: from,
6234 toggle: function (on, classRef, classRef2, classRefs) {
6235 runtime.nullCheck(on, onExpr);
6236 if (visibility) {
6237 runtime.implicitLoop(on, function (target) {
6238 hideShowStrategy("toggle", target);
6239 });
6240 } else if (between) {
6241 runtime.implicitLoop(on, function (target) {
6242 if (target.classList.contains(classRef.className)) {
6243 target.classList.remove(classRef.className);
6244 target.classList.add(classRef2.className);
6245 } else {
6246 target.classList.add(classRef.className);
6247 target.classList.remove(classRef2.className);
6248 }
6249 });
6250 } else if (classRefs) {
6251 runtime.forEach(classRefs, function (classRef) {
6252 runtime.implicitLoop(on, function (target) {
6253 target.classList.toggle(classRef.className);
6254 });
6255 });
6256 } else {
6257 runtime.forEach(on, function (target) {
6258 if (target.hasAttribute(attributeRef.name)) {
6259 target.removeAttribute(attributeRef.name);
6260 } else {
6261 target.setAttribute(attributeRef.name, attributeRef.value);
6262 }
6263 });
6264 }
6265 },
6266 args: [onExpr, time, evt, from, classRef, classRef2, classRefs],
6267 op: function (context, on, time, evt, from, classRef, classRef2, classRefs) {
6268 if (time) {
6269 return new Promise(function (resolve) {
6270 toggleCmd.toggle(on, classRef, classRef2, classRefs);
6271 setTimeout(function () {
6272 toggleCmd.toggle(on, classRef, classRef2, classRefs);
6273 resolve(runtime.findNext(toggleCmd, context));
6274 }, time);
6275 });
6276 } else if (evt) {
6277 return new Promise(function (resolve) {
6278 var target = from || context.me;
6279 target.addEventListener(
6280 evt,
6281 function () {
6282 toggleCmd.toggle(on, classRef, classRef2, classRefs);
6283 resolve(runtime.findNext(toggleCmd, context));
6284 },
6285 { once: true }
6286 );
6287 toggleCmd.toggle(on, classRef, classRef2, classRefs);
6288 });
6289 } else {
6290 this.toggle(on, classRef, classRef2, classRefs);
6291 return runtime.findNext(toggleCmd, context);
6292 }
6293 },
6294 };
6295 return toggleCmd;
6296 }
6297 });
6298
6299 var HIDE_SHOW_STRATEGIES = {
6300 display: function (op, element, arg) {
6301 if (arg) {
6302 element.style.display = arg;
6303 } else if (op === "toggle") {
6304 if (getComputedStyle(element).display === "none") {
6305 HIDE_SHOW_STRATEGIES.display("show", element, arg);
6306 } else {
6307 HIDE_SHOW_STRATEGIES.display("hide", element, arg);
6308 }
6309 } else if (op === "hide") {
6310 const internalData = parser.runtime.getInternalData(element);
6311 if (internalData.originalDisplay == null) {
6312 internalData.originalDisplay = element.style.display;
6313 }
6314 element.style.display = "none";
6315 } else {
6316 const internalData = parser.runtime.getInternalData(element);
6317 if (internalData.originalDisplay && internalData.originalDisplay !== 'none') {
6318 element.style.display = internalData.originalDisplay;
6319 } else {
6320 element.style.removeProperty('display');
6321 }
6322 }
6323 },
6324 visibility: function (op, element, arg) {
6325 if (arg) {
6326 element.style.visibility = arg;
6327 } else if (op === "toggle") {
6328 if (getComputedStyle(element).visibility === "hidden") {
6329 HIDE_SHOW_STRATEGIES.visibility("show", element, arg);
6330 } else {
6331 HIDE_SHOW_STRATEGIES.visibility("hide", element, arg);
6332 }
6333 } else if (op === "hide") {
6334 element.style.visibility = "hidden";
6335 } else {
6336 element.style.visibility = "visible";
6337 }
6338 },
6339 opacity: function (op, element, arg) {
6340 if (arg) {
6341 element.style.opacity = arg;
6342 } else if (op === "toggle") {
6343 if (getComputedStyle(element).opacity === "0") {
6344 HIDE_SHOW_STRATEGIES.opacity("show", element, arg);
6345 } else {
6346 HIDE_SHOW_STRATEGIES.opacity("hide", element, arg);
6347 }
6348 } else if (op === "hide") {
6349 element.style.opacity = "0";
6350 } else {
6351 element.style.opacity = "1";
6352 }
6353 },
6354 };
6355
6356 var parseShowHideTarget = function (parser, runtime, tokens) {
6357 var target;
6358 var currentTokenValue = tokens.currentToken();
6359 if (currentTokenValue.value === "when" || currentTokenValue.value === "with" || parser.commandBoundary(currentTokenValue)) {
6360 target = parser.parseElement("implicitMeTarget", tokens);
6361 } else {
6362 target = parser.parseElement("expression", tokens);
6363 }
6364 return target;
6365 };
6366
6367 var resolveStrategy = function (parser, tokens, name) {
6368 var configDefault = config.defaultHideShowStrategy;
6369 var strategies = HIDE_SHOW_STRATEGIES;
6370 if (config.hideShowStrategies) {
6371 strategies = Object.assign(strategies, config.hideShowStrategies); // merge in user provided strategies
6372 }
6373 name = name || configDefault || "display";
6374 var value = strategies[name];
6375 if (value == null) {
6376 parser.raiseParseError(tokens, "Unknown show/hide strategy : " + name);
6377 }
6378 return value;
6379 };
6380
6381 parser.addCommand("hide", function (parser, runtime, tokens) {
6382 if (tokens.matchToken("hide")) {
6383 var targetExpr = parseShowHideTarget(parser, runtime, tokens);
6384
6385 var name = null;
6386 if (tokens.matchToken("with")) {
6387 name = tokens.requireTokenType("IDENTIFIER", "STYLE_REF").value;
6388 if (name.indexOf("*") === 0) {
6389 name = name.substr(1);
6390 }
6391 }
6392 var hideShowStrategy = resolveStrategy(parser, tokens, name);
6393
6394 return {
6395 target: targetExpr,
6396 args: [targetExpr],
6397 op: function (ctx, target) {
6398 runtime.nullCheck(target, targetExpr);
6399 runtime.implicitLoop(target, function (elt) {
6400 hideShowStrategy("hide", elt);
6401 });
6402 return runtime.findNext(this, ctx);
6403 },
6404 };
6405 }
6406 });
6407
6408 parser.addCommand("show", function (parser, runtime, tokens) {
6409 if (tokens.matchToken("show")) {
6410 var targetExpr = parseShowHideTarget(parser, runtime, tokens);
6411
6412 var name = null;
6413 if (tokens.matchToken("with")) {
6414 name = tokens.requireTokenType("IDENTIFIER", "STYLE_REF").value;
6415 if (name.indexOf("*") === 0) {
6416 name = name.substr(1);
6417 }
6418 }
6419 var arg = null;
6420 if (tokens.matchOpToken(":")) {
6421 var tokenArr = tokens.consumeUntilWhitespace();
6422 tokens.matchTokenType("WHITESPACE");
6423 arg = tokenArr
6424 .map(function (t) {
6425 return t.value;
6426 })
6427 .join("");
6428 }
6429
6430 if (tokens.matchToken("when")) {
6431 var when = parser.requireElement("expression", tokens);
6432 }
6433
6434 var hideShowStrategy = resolveStrategy(parser, tokens, name);
6435
6436 return {
6437 target: targetExpr,
6438 when: when,
6439 args: [targetExpr],
6440 op: function (ctx, target) {
6441 runtime.nullCheck(target, targetExpr);
6442 runtime.implicitLoop(target, function (elt) {
6443 if (when) {
6444 ctx.result = elt;
6445 let whenResult = runtime.evaluateNoPromise(when, ctx);
6446 if (whenResult) {
6447 hideShowStrategy("show", elt, arg);
6448 } else {
6449 hideShowStrategy("hide", elt);
6450 }
6451 ctx.result = null;
6452 } else {
6453 hideShowStrategy("show", elt, arg);
6454 }
6455 });
6456 return runtime.findNext(this, ctx);
6457 },
6458 };
6459 }
6460 });
6461
6462 parser.addCommand("take", function (parser, runtime, tokens) {
6463 if (tokens.matchToken("take")) {
6464 var classRef = parser.requireElement("classRef", tokens);
6465
6466 if (tokens.matchToken("from")) {
6467 var fromExpr = parser.requireElement("expression", tokens);
6468 } else {
6469 var fromExpr = classRef;
6470 }
6471
6472 if (tokens.matchToken("for")) {
6473 var forExpr = parser.requireElement("expression", tokens);
6474 } else {
6475 var forExpr = parser.requireElement("implicitMeTarget", tokens);
6476 }
6477
6478 var takeCmd = {
6479 classRef: classRef,
6480 from: fromExpr,
6481 forElt: forExpr,
6482 args: [classRef, fromExpr, forExpr],
6483 op: function (context, eltColl, from, forElt) {
6484 runtime.nullCheck(from, fromExpr);
6485 runtime.nullCheck(forElt, forExpr);
6486 var clazz = eltColl.className;
6487 runtime.implicitLoop(from, function (target) {
6488 target.classList.remove(clazz);
6489 });
6490 runtime.implicitLoop(forElt, function (target) {
6491 target.classList.add(clazz);
6492 });
6493 return runtime.findNext(this, context);
6494 },
6495 };
6496 return takeCmd;
6497 }
6498 });
6499
6500 function putInto(runtime, context, prop, valueToPut) {
6501 if (prop != null) {
6502 var value = runtime.resolveSymbol(prop, context);
6503 } else {
6504 var value = context;
6505 }
6506 if (value instanceof Element || value instanceof HTMLDocument) {
6507 while (value.firstChild) value.removeChild(value.firstChild);
6508 value.append(parser.runtime.convertValue(valueToPut, "Fragment"));
6509 runtime.processNode(value);
6510 } else {
6511 if (prop != null) {
6512 runtime.setSymbol(prop, context, null, valueToPut);
6513 } else {
6514 throw "Don't know how to put a value into " + typeof context;
6515 }
6516 }
6517 }
6518
6519 parser.addCommand("put", function (parser, runtime, tokens) {
6520 if (tokens.matchToken("put")) {
6521 var value = parser.requireElement("expression", tokens);
6522
6523 var operationToken = tokens.matchAnyToken("into", "before", "after");
6524
6525 if (operationToken == null && tokens.matchToken("at")) {
6526 tokens.matchToken("the"); // optional "the"
6527 operationToken = tokens.matchAnyToken("start", "end");
6528 tokens.requireToken("of");
6529 }
6530
6531 if (operationToken == null) {
6532 parser.raiseParseError(tokens, "Expected one of 'into', 'before', 'at start of', 'at end of', 'after'");
6533 }
6534 var target = parser.requireElement("expression", tokens);
6535
6536 var operation = operationToken.value;
6537
6538 var arrayIndex = false;
6539 var symbolWrite = false;
6540 var rootExpr = null;
6541 var prop = null;
6542
6543 if (target.type === "arrayIndex" && operation === "into") {
6544 arrayIndex = true;
6545 prop = target.prop;
6546 rootExpr = target.root;
6547 } else if (target.prop && target.root && operation === "into") {
6548 prop = target.prop.value;
6549 rootExpr = target.root;
6550 } else if (target.type === "symbol" && operation === "into") {
6551 symbolWrite = true;
6552 prop = target.name;
6553 } else if (target.type === "attributeRef" && operation === "into") {
6554 var attributeWrite = true;
6555 prop = target.name;
6556 rootExpr = parser.requireElement("implicitMeTarget", tokens);
6557 } else if (target.type === "styleRef" && operation === "into") {
6558 var styleWrite = true;
6559 prop = target.name;
6560 rootExpr = parser.requireElement("implicitMeTarget", tokens);
6561 } else if (target.attribute && operation === "into") {
6562 var attributeWrite = target.attribute.type === "attributeRef";
6563 var styleWrite = target.attribute.type === "styleRef";
6564 prop = target.attribute.name;
6565 rootExpr = target.root;
6566 } else {
6567 rootExpr = target;
6568 }
6569
6570 var putCmd = {
6571 target: target,
6572 operation: operation,
6573 symbolWrite: symbolWrite,
6574 value: value,
6575 args: [rootExpr, prop, value],
6576 op: function (context, root, prop, valueToPut) {
6577 if (symbolWrite) {
6578 putInto(runtime, context, prop, valueToPut);
6579 } else {
6580 runtime.nullCheck(root, rootExpr);
6581 if (operation === "into") {
6582 if (attributeWrite) {
6583 runtime.implicitLoop(root, function (elt) {
6584 elt.setAttribute(prop, valueToPut);
6585 });
6586 } else if (styleWrite) {
6587 runtime.implicitLoop(root, function (elt) {
6588 elt.style[prop] = valueToPut;
6589 });
6590 } else if (arrayIndex) {
6591 root[prop] = valueToPut;
6592 } else {
6593 runtime.implicitLoop(root, function (elt) {
6594 putInto(runtime, elt, prop, valueToPut);
6595 });
6596 }
6597 } else {
6598 var op =
6599 operation === "before"
6600 ? Element.prototype.before
6601 : operation === "after"
6602 ? Element.prototype.after
6603 : operation === "start"
6604 ? Element.prototype.prepend
6605 : operation === "end"
6606 ? Element.prototype.append
6607 : Element.prototype.append; // unreachable
6608
6609 runtime.implicitLoop(root, function (elt) {
6610 op.call(
6611 elt,
6612 valueToPut instanceof Node
6613 ? valueToPut
6614 : runtime.convertValue(valueToPut, "Fragment")
6615 );
6616 // process any new content
6617 if (elt.parentElement) {
6618 runtime.processNode(elt.parentElement);
6619 } else {
6620 runtime.processNode(elt);
6621 }
6622 });
6623 }
6624 }
6625 return runtime.findNext(this, context);
6626 },
6627 };
6628 return putCmd;
6629 }
6630 });
6631
6632 function parsePseudopossessiveTarget(parser, runtime, tokens) {
6633 var targets;
6634 if (
6635 tokens.matchToken("the") ||
6636 tokens.matchToken("element") ||
6637 tokens.matchToken("elements") ||
6638 tokens.currentToken().type === "CLASS_REF" ||
6639 tokens.currentToken().type === "ID_REF" ||
6640 (tokens.currentToken().op && tokens.currentToken().value === "<")
6641 ) {
6642 parser.possessivesDisabled = true;
6643 try {
6644 targets = parser.parseElement("expression", tokens);
6645 } finally {
6646 delete parser.possessivesDisabled;
6647 }
6648 // optional possessive
6649 if (tokens.matchOpToken("'")) {
6650 tokens.requireToken("s");
6651 }
6652 } else if (tokens.currentToken().type === "IDENTIFIER" && tokens.currentToken().value === "its") {
6653 var identifier = tokens.matchToken("its");
6654 targets = {
6655 type: "pseudopossessiveIts",
6656 token: identifier,
6657 name: identifier.value,
6658 evaluate: function (context) {
6659 return runtime.resolveSymbol("it", context);
6660 },
6661 };
6662 } else {
6663 tokens.matchToken("my") || tokens.matchToken("me"); // consume optional 'my'
6664 targets = parser.parseElement("implicitMeTarget", tokens);
6665 }
6666 return targets;
6667 }
6668
6669 parser.addCommand("transition", function (parser, runtime, tokens) {
6670 if (tokens.matchToken("transition")) {
6671 var targetsExpr = parsePseudopossessiveTarget(parser, runtime, tokens);
6672
6673 var properties = [];
6674 var from = [];
6675 var to = [];
6676 var currentToken = tokens.currentToken();
6677 while (
6678 !parser.commandBoundary(currentToken) &&
6679 currentToken.value !== "over" &&
6680 currentToken.value !== "using"
6681 ) {
6682 if (tokens.currentToken().type === "STYLE_REF") {
6683 let styleRef = tokens.consumeToken();
6684 let styleProp = styleRef.value.substr(1);
6685 properties.push({
6686 type: "styleRefValue",
6687 evaluate: function () {
6688 return styleProp;
6689 },
6690 });
6691 } else {
6692 properties.push(parser.requireElement("stringLike", tokens));
6693 }
6694
6695 if (tokens.matchToken("from")) {
6696 from.push(parser.requireElement("expression", tokens));
6697 } else {
6698 from.push(null);
6699 }
6700 tokens.requireToken("to");
6701 if (tokens.matchToken("initial")) {
6702 to.push({
6703 type: "initial_literal",
6704 evaluate : function(){
6705 return "initial";
6706 }
6707 });
6708 } else {
6709 to.push(parser.requireElement("expression", tokens));
6710 }
6711 currentToken = tokens.currentToken();
6712 }
6713 if (tokens.matchToken("over")) {
6714 var over = parser.requireElement("expression", tokens);
6715 } else if (tokens.matchToken("using")) {
6716 var using = parser.requireElement("expression", tokens);
6717 }
6718
6719 var transition = {
6720 to: to,
6721 args: [targetsExpr, properties, from, to, using, over],
6722 op: function (context, targets, properties, from, to, using, over) {
6723 runtime.nullCheck(targets, targetsExpr);
6724 var promises = [];
6725 runtime.implicitLoop(targets, function (target) {
6726 var promise = new Promise(function (resolve, reject) {
6727 var initialTransition = target.style.transition;
6728 if (over) {
6729 target.style.transition = "all " + over + "ms ease-in";
6730 } else if (using) {
6731 target.style.transition = using;
6732 } else {
6733 target.style.transition = config.defaultTransition;
6734 }
6735 var internalData = runtime.getInternalData(target);
6736 var computedStyles = getComputedStyle(target);
6737
6738 var initialStyles = {};
6739 for (var i = 0; i < computedStyles.length; i++) {
6740 var name = computedStyles[i];
6741 var initialValue = computedStyles[name];
6742 initialStyles[name] = initialValue;
6743 }
6744
6745 // store intitial values
6746 if (!internalData.initalStyles) {
6747 internalData.initalStyles = initialStyles;
6748 }
6749
6750 for (var i = 0; i < properties.length; i++) {
6751 var property = properties[i];
6752 var fromVal = from[i];
6753 if (fromVal === "computed" || fromVal == null) {
6754 target.style[property] = initialStyles[property];
6755 } else {
6756 target.style[property] = fromVal;
6757 }
6758 }
6759 //console.log("transition started", transition);
6760
6761 var transitionStarted = false;
6762 var resolved = false;
6763
6764 target.addEventListener(
6765 "transitionend",
6766 function () {
6767 if (!resolved) {
6768 //console.log("transition ended", transition);
6769 target.style.transition = initialTransition;
6770 resolved = true;
6771 resolve();
6772 }
6773 },
6774 { once: true }
6775 );
6776
6777 target.addEventListener(
6778 "transitionstart",
6779 function () {
6780 transitionStarted = true;
6781 },
6782 { once: true }
6783 );
6784
6785 // it no transition has started in 100ms, continue
6786 setTimeout(function () {
6787 if (!resolved && !transitionStarted) {
6788 //console.log("transition ended", transition);
6789 target.style.transition = initialTransition;
6790 resolved = true;
6791 resolve();
6792 }
6793 }, 100);
6794
6795 setTimeout(function () {
6796 var autoProps = [];
6797 for (var i = 0; i < properties.length; i++) {
6798 var property = properties[i];
6799 var toVal = to[i];
6800 if (toVal === "initial") {
6801 var propertyValue = internalData.initalStyles[property];
6802 target.style[property] = propertyValue;
6803 } else {
6804 target.style[property] = toVal;
6805 }
6806 //console.log("set", property, "to", target.style[property], "on", target, "value passed in : ", toVal);
6807 }
6808 }, 0);
6809 });
6810 promises.push(promise);
6811 });
6812 return Promise.all(promises).then(function () {
6813 return runtime.findNext(transition, context);
6814 });
6815 },
6816 };
6817 return transition;
6818 }
6819 });
6820
6821 parser.addCommand("measure", function (parser, runtime, tokens) {
6822 if (!tokens.matchToken("measure")) return;
6823
6824 var targetExpr = parsePseudopossessiveTarget(parser, runtime, tokens);
6825
6826 var propsToMeasure = [];
6827 if (!parser.commandBoundary(tokens.currentToken()))
6828 do {
6829 propsToMeasure.push(tokens.matchTokenType("IDENTIFIER").value);
6830 } while (tokens.matchOpToken(","));
6831
6832 return {
6833 properties: propsToMeasure,
6834 args: [targetExpr],
6835 op: function (ctx, target) {
6836 runtime.nullCheck(target, targetExpr);
6837 if (0 in target) target = target[0]; // not measuring multiple elts
6838 var rect = target.getBoundingClientRect();
6839 var scroll = {
6840 top: target.scrollTop,
6841 left: target.scrollLeft,
6842 topMax: target.scrollTopMax,
6843 leftMax: target.scrollLeftMax,
6844 height: target.scrollHeight,
6845 width: target.scrollWidth,
6846 };
6847
6848 ctx.result = {
6849 x: rect.x,
6850 y: rect.y,
6851 left: rect.left,
6852 top: rect.top,
6853 right: rect.right,
6854 bottom: rect.bottom,
6855 width: rect.width,
6856 height: rect.height,
6857 bounds: rect,
6858
6859 scrollLeft: scroll.left,
6860 scrollTop: scroll.top,
6861 scrollLeftMax: scroll.leftMax,
6862 scrollTopMax: scroll.topMax,
6863 scrollWidth: scroll.width,
6864 scrollHeight: scroll.height,
6865 scroll: scroll,
6866 };
6867
6868 runtime.forEach(propsToMeasure, function (prop) {
6869 if (prop in ctx.result) ctx.locals[prop] = ctx.result[prop];
6870 else throw "No such measurement as " + prop;
6871 });
6872
6873 return runtime.findNext(this, ctx);
6874 },
6875 };
6876 });
6877
6878 parser.addLeafExpression("closestExpr", function (parser, runtime, tokens) {
6879 if (tokens.matchToken("closest")) {
6880 if (tokens.matchToken("parent")) {
6881 var parentSearch = true;
6882 }
6883
6884 var css = null;
6885 if (tokens.currentToken().type === "ATTRIBUTE_REF") {
6886 var attributeRef = parser.requireElement("attributeRefAccess", tokens, null);
6887 css = "[" + attributeRef.attribute.name + "]";
6888 }
6889
6890 if (css == null) {
6891 var expr = parser.requireElement("expression", tokens);
6892 if (expr.css == null) {
6893 parser.raiseParseError(tokens, "Expected a CSS expression");
6894 } else {
6895 css = expr.css;
6896 }
6897 }
6898
6899 if (tokens.matchToken("to")) {
6900 var to = parser.parseElement("expression", tokens);
6901 } else {
6902 var to = parser.parseElement("implicitMeTarget", tokens);
6903 }
6904
6905 var closestExpr = {
6906 type: "closestExpr",
6907 parentSearch: parentSearch,
6908 expr: expr,
6909 css: css,
6910 to: to,
6911 args: [to],
6912 op: function (ctx, to) {
6913 if (to == null) {
6914 return null;
6915 } else {
6916 let result = [];
6917 runtime.implicitLoop(to, function(to){
6918 if (parentSearch) {
6919 result.push(to.parentElement ? to.parentElement.closest(css) : null);
6920 } else {
6921 result.push(to.closest(css));
6922 }
6923 })
6924 if (runtime.shouldAutoIterate(to)) {
6925 return result;
6926 } else {
6927 return result[0];
6928 }
6929 }
6930 },
6931 evaluate: function (context) {
6932 return runtime.unifiedEval(this, context);
6933 },
6934 };
6935
6936 if (attributeRef) {
6937 attributeRef.root = closestExpr;
6938 attributeRef.args = [closestExpr];
6939 return attributeRef;
6940 } else {
6941 return closestExpr;
6942 }
6943 }
6944 });
6945
6946 parser.addCommand("go", function (parser, runtime, tokens) {
6947 if (tokens.matchToken("go")) {
6948 if (tokens.matchToken("back")) {
6949 var back = true;
6950 } else {
6951 tokens.matchToken("to");
6952 if (tokens.matchToken("url")) {
6953 var target = parser.requireElement("stringLike", tokens);
6954 var url = true;
6955 if (tokens.matchToken("in")) {
6956 tokens.requireToken("new");
6957 tokens.requireToken("window");
6958 var newWindow = true;
6959 }
6960 } else {
6961 tokens.matchToken("the"); // optional the
6962 var verticalPosition = tokens.matchAnyToken("top", "middle", "bottom");
6963 var horizontalPosition = tokens.matchAnyToken("left", "center", "right");
6964 if (verticalPosition || horizontalPosition) {
6965 tokens.requireToken("of");
6966 }
6967 var target = parser.requireElement("unaryExpression", tokens);
6968
6969 var plusOrMinus = tokens.matchAnyOpToken("+", "-");
6970 if (plusOrMinus) {
6971 tokens.pushFollow("px");
6972 try {
6973 var offset = parser.requireElement("expression", tokens);
6974 } finally {
6975 tokens.popFollow();
6976 }
6977 }
6978 tokens.matchToken("px"); // optional px
6979
6980 var smoothness = tokens.matchAnyToken("smoothly", "instantly");
6981
6982 var scrollOptions = {};
6983 if (verticalPosition) {
6984 if (verticalPosition.value === "top") {
6985 scrollOptions.block = "start";
6986 } else if (verticalPosition.value === "bottom") {
6987 scrollOptions.block = "end";
6988 } else if (verticalPosition.value === "middle") {
6989 scrollOptions.block = "center";
6990 }
6991 }
6992
6993 if (horizontalPosition) {
6994 if (horizontalPosition.value === "left") {
6995 scrollOptions.inline = "start";
6996 } else if (horizontalPosition.value === "center") {
6997 scrollOptions.inline = "center";
6998 } else if (horizontalPosition.value === "right") {
6999 scrollOptions.inline = "end";
7000 }
7001 }
7002
7003 if (smoothness) {
7004 if (smoothness.value === "smoothly") {
7005 scrollOptions.behavior = "smooth";
7006 } else if (smoothness.value === "instantly") {
7007 scrollOptions.behavior = "instant";
7008 }
7009 }
7010 }
7011 }
7012
7013 var goCmd = {
7014 target: target,
7015 args: [target, offset],
7016 op: function (ctx, to, offset) {
7017 if (back) {
7018 window.history.back();
7019 } else if (url) {
7020 if (to) {
7021 if (newWindow) {
7022 window.open(to);
7023 } else {
7024 window.location.href = to;
7025 }
7026 }
7027 } else {
7028 runtime.implicitLoop(to, function (target) {
7029
7030 if (target === window) {
7031 target = document.body;
7032 }
7033
7034 if(plusOrMinus) {
7035 // a top scroll w/ an offset of some sort
7036 var boundingRect = target.getBoundingClientRect();
7037 let scrollShim = document.createElement('div');
7038
7039 if (plusOrMinus.value === "-") {
7040 var finalOffset = -offset;
7041 } else {
7042 var finalOffset = - -offset;
7043 }
7044
7045 scrollShim.style.position = 'absolute';
7046 scrollShim.style.top = (boundingRect.x + finalOffset) + "px";
7047 scrollShim.style.left = (boundingRect.y + finalOffset) + "px";
7048 scrollShim.style.height = (boundingRect.height + (2 * finalOffset)) + "px";
7049 scrollShim.style.width = (boundingRect.width + (2 * finalOffset)) + "px";
7050 scrollShim.style.zIndex = "" + Number.MIN_SAFE_INTEGER;
7051 scrollShim.style.opacity = "0";
7052
7053 document.body.appendChild(scrollShim);
7054 setTimeout(function () {
7055 document.body.removeChild(scrollShim);
7056 }, 100);
7057
7058 target = scrollShim;
7059 }
7060
7061 target.scrollIntoView(scrollOptions);
7062 });
7063 }
7064 return runtime.findNext(goCmd, ctx);
7065 },
7066 };
7067 return goCmd;
7068 }
7069 });
7070
7071 config.conversions.dynamicResolvers.push(function (str, node) {
7072 if (!(str === "Values" || str.indexOf("Values:") === 0)) {
7073 return;
7074 }
7075 var conversion = str.split(":")[1];
7076 /** @type Object<string,string | string[]> */
7077 var result = {};
7078
7079 var implicitLoop = parser.runtime.implicitLoop.bind(parser.runtime);
7080
7081 implicitLoop(node, function (/** @type HTMLInputElement */ node) {
7082 // Try to get a value directly from this node
7083 var input = getInputInfo(node);
7084
7085 if (input !== undefined) {
7086 result[input.name] = input.value;
7087 return;
7088 }
7089
7090 // Otherwise, try to query all child elements of this node that *should* contain values.
7091 if (node.querySelectorAll != undefined) {
7092 /** @type {NodeListOf<HTMLInputElement>} */
7093 var children = node.querySelectorAll("input,select,textarea");
7094 children.forEach(appendValue);
7095 }
7096 });
7097
7098 if (conversion) {
7099 if (conversion === "JSON") {
7100 return JSON.stringify(result);
7101 } else if (conversion === "Form") {
7102 /** @ts-ignore */
7103 // TODO: does this work with multiple inputs of the same name?
7104 return new URLSearchParams(result).toString();
7105 } else {
7106 throw "Unknown conversion: " + conversion;
7107 }
7108 } else {
7109 return result;
7110 }
7111
7112 /**
7113 * @param {HTMLInputElement} node
7114 */
7115 function appendValue(node) {
7116 var info = getInputInfo(node);
7117
7118 if (info == undefined) {
7119 return;
7120 }
7121
7122 // If there is no value already stored in this space.
7123 if (result[info.name] == undefined) {
7124 result[info.name] = info.value;
7125 return;
7126 }
7127
7128 if (Array.isArray(result[info.name]) && Array.isArray(info.value)) {
7129 result[info.name] = [].concat(result[info.name], info.value);
7130 return;
7131 }
7132 }
7133
7134 /**
7135 * @param {HTMLInputElement} node
7136 * @returns {{name:string, value:string | string[]} | undefined}
7137 */
7138 function getInputInfo(node) {
7139 try {
7140 /** @type {{name: string, value: string | string[]}}*/
7141 var result = {
7142 name: node.name,
7143 value: node.value,
7144 };
7145
7146 if (result.name == undefined || result.value == undefined) {
7147 return undefined;
7148 }
7149
7150 if (node.type == "radio" && node.checked == false) {
7151 return undefined;
7152 }
7153
7154 if (node.type == "checkbox") {
7155 if (node.checked == false) {
7156 result.value = undefined;
7157 } else if (typeof result.value === "string") {
7158 result.value = [result.value];
7159 }
7160 }
7161
7162 if (node.type == "select-multiple") {
7163 /** @type {NodeListOf<HTMLSelectElement>} */
7164 var selected = node.querySelectorAll("option[selected]");
7165
7166 result.value = [];
7167 for (var index = 0; index < selected.length; index++) {
7168 result.value.push(selected[index].value);
7169 }
7170 }
7171 return result;
7172 } catch (e) {
7173 return undefined;
7174 }
7175 }
7176 });
7177
7178 config.conversions["HTML"] = function (value) {
7179 var toHTML = /** @returns {string}*/ function (/** @type any*/ value) {
7180 if (value instanceof Array) {
7181 return value
7182 .map(function (item) {
7183 return toHTML(item);
7184 })
7185 .join("");
7186 }
7187
7188 if (value instanceof HTMLElement) {
7189 return value.outerHTML;
7190 }
7191
7192 if (value instanceof NodeList) {
7193 var result = "";
7194 for (var i = 0; i < value.length; i++) {
7195 var node = value[i];
7196 if (node instanceof HTMLElement) {
7197 result += node.outerHTML;
7198 }
7199 }
7200 return result;
7201 }
7202
7203 if (value.toString) {
7204 return value.toString();
7205 }
7206
7207 return "";
7208 };
7209
7210 return toHTML(value);
7211 };
7212
7213 config.conversions["Fragment"] = function (val) {
7214 var frag = document.createDocumentFragment();
7215 parser.runtime.implicitLoop(val, function (val) {
7216 if (val instanceof Node) frag.append(val);
7217 else {
7218 var temp = document.createElement("template");
7219 temp.innerHTML = val;
7220 frag.append(temp.content);
7221 }
7222 });
7223 return frag;
7224 };
7225 }
7226
7227
7228 // Public API
7229
7230 const runtime_ = new Runtime(), lexer_ = runtime_.lexer, parser_ = runtime_.parser
7231
7232 /**
7233 *
7234 * @param {string} src
7235 * @param {Partial<Context>} [ctx]
7236 */
7237 function run(src, ctx) {
7238 return runtime_.evaluate(src, ctx)
7239 }
7240
7241 function browserInit() {
7242 /** @type {HTMLScriptElement[]} */
7243 var scripts = Array.from(globalScope.document.querySelectorAll("script[type='text/hyperscript'][src]"))
7244 Promise.all(
7245 scripts.map(function (script) {
7246 return fetch(script.src)
7247 .then(function (res) {
7248 return res.text();
7249 });
7250 })
7251 )
7252 .then(script_values => script_values.forEach(sc => _hyperscript(sc)))
7253 .then(() => ready(function () {
7254 mergeMetaConfig();
7255 runtime_.processNode(document.documentElement);
7256 globalScope.document.addEventListener("htmx:load", function (/** @type {CustomEvent} */ evt) {
7257 runtime_.processNode(evt.detail.elt);
7258 });
7259 }));
7260
7261 function ready(fn) {
7262 if (document.readyState !== "loading") {
7263 setTimeout(fn);
7264 } else {
7265 document.addEventListener("DOMContentLoaded", fn);
7266 }
7267 }
7268
7269 function getMetaConfig() {
7270 /** @type {HTMLMetaElement} */
7271 var element = document.querySelector('meta[name="htmx-config"]');
7272 if (element) {
7273 return parseJSON(element.content);
7274 } else {
7275 return null;
7276 }
7277 }
7278
7279 function mergeMetaConfig() {
7280 var metaConfig = getMetaConfig();
7281 if (metaConfig) {
7282 Object.assign(config, metaConfig);
7283 }
7284 }
7285 }
7286
7287 /**
7288 * @typedef {Object} HyperscriptAPI
7289 *
7290 * @property {Object} config
7291 * @property {string} config.attributes
7292 * @property {string} config.defaultTransition
7293 * @property {string} config.disableSelector
7294 * @property {typeof conversions} config.conversions
7295 *
7296 * @property {Object} internals
7297 * @property {Lexer} internals.lexer
7298 * @property {typeof Lexer} internals.Lexer
7299 * @property {Parser} internals.parser
7300 * @property {typeof Parser} internals.Parser
7301 * @property {Runtime} internals.runtime
7302 * @property {typeof Runtime} internals.Runtime
7303 *
7304 * @property {typeof ElementCollection} ElementCollection
7305 *
7306 * @property {(keyword: string, definition: ParseRule) => void} addFeature
7307 * @property {(keyword: string, definition: ParseRule) => void} addCommand
7308 * @property {(keyword: string, definition: ParseRule) => void} addLeafExpression
7309 * @property {(keyword: string, definition: ParseRule) => void} addIndirectExpression
7310 *
7311 * @property {(src: string, ctx?: Partial<Context>) => any} evaluate
7312 * @property {(src: string) => ASTNode} parse
7313 * @property {(node: Element) => void} processNode
7314 *
7315 * @property {() => void} browserInit
7316 *
7317 *
7318 * @typedef {HyperscriptAPI & ((src: string, ctx?: Partial<Context>) => any)} Hyperscript
7319 */
7320
7321 /**
7322 * @type {Hyperscript}
7323 */
7324 const _hyperscript = Object.assign(
7325 run,
7326 {
7327 config,
7328
7329 use(plugin) { plugin(_hyperscript) },
7330
7331 internals: {
7332 lexer: lexer_, parser: parser_, runtime: runtime_,
7333 Lexer, Tokens, Parser, Runtime,
7334 },
7335 ElementCollection,
7336
7337 addFeature: parser_.addFeature.bind(parser_),
7338 addCommand: parser_.addCommand.bind(parser_),
7339 addLeafExpression: parser_.addLeafExpression.bind(parser_),
7340 addIndirectExpression: parser_.addIndirectExpression.bind(parser_),
7341
7342 evaluate: runtime_.evaluate.bind(runtime_),
7343 parse: runtime_.parse.bind(runtime_),
7344 processNode: runtime_.processNode.bind(runtime_),
7345
7346 browserInit,
7347 }
7348 )
7349
7350 return _hyperscript
7351})