UNPKG

153 kBJavaScriptView Raw
1///=========================================================================
2/// This module provides the core runtime and grammar for hyperscript
3///=========================================================================
4
5import {getOrInitObject, mergeObjects, parseJSON, varargConstructor} from "./utils.js";
6
7
8/**
9 * @type {HyperscriptObject}
10 */
11let _hyperscript
12
13var globalScope = globalThis;
14
15//====================================================================
16// Standard library
17//====================================================================
18
19class ElementCollection {
20 constructor(css, relativeToElement, escape) {
21 this._css = css;
22 this.relativeToElement = relativeToElement;
23 this.escape = escape;
24 }
25
26 get css() {
27 if (this.escape) {
28 return _runtime.escapeSelector(this._css);
29 } else {
30 return this._css;
31 }
32 }
33
34 get className() {
35 return this._css.substr(1);
36 }
37
38 get id() {
39 return this.className();
40 }
41
42 contains(elt) {
43 for (let element of this) {
44 if (element.contains(elt)) {
45 return true;
46 }
47 }
48 return false;
49 }
50
51 get length() {
52 return this.selectMatches().length;
53 }
54
55 [Symbol.iterator]() {
56 let query = this.selectMatches();
57 return query [Symbol.iterator]();
58 }
59
60 selectMatches() {
61 let query = _runtime.getRootNode(this.relativeToElement).querySelectorAll(this.css);
62 return query;
63 }
64}
65
66//====================================================================
67// Lexer
68//====================================================================
69
70/** @type LexerObject */
71var _lexer = (function () {
72 var OP_TABLE = {
73 "+": "PLUS",
74 "-": "MINUS",
75 "*": "MULTIPLY",
76 "/": "DIVIDE",
77 ".": "PERIOD",
78 "..": "ELLIPSIS",
79 "\\": "BACKSLASH",
80 ":": "COLON",
81 "%": "PERCENT",
82 "|": "PIPE",
83 "!": "EXCLAMATION",
84 "?": "QUESTION",
85 "#": "POUND",
86 "&": "AMPERSAND",
87 $: "DOLLAR",
88 ";": "SEMI",
89 ",": "COMMA",
90 "(": "L_PAREN",
91 ")": "R_PAREN",
92 "<": "L_ANG",
93 ">": "R_ANG",
94 "<=": "LTE_ANG",
95 ">=": "GTE_ANG",
96 "==": "EQ",
97 "===": "EQQ",
98 "!=": "NEQ",
99 "!==": "NEQQ",
100 "{": "L_BRACE",
101 "}": "R_BRACE",
102 "[": "L_BRACKET",
103 "]": "R_BRACKET",
104 "=": "EQUALS",
105 };
106
107 /**
108 * isValidCSSClassChar returns `true` if the provided character is valid in a CSS class.
109 * @param {string} c
110 * @returns boolean
111 */
112 function isValidCSSClassChar(c) {
113 return isAlpha(c) || isNumeric(c) || c === "-" || c === "_" || c === ":";
114 }
115
116 /**
117 * isValidCSSIDChar returns `true` if the provided character is valid in a CSS ID
118 * @param {string} c
119 * @returns boolean
120 */
121 function isValidCSSIDChar(c) {
122 return isAlpha(c) || isNumeric(c) || c === "-" || c === "_" || c === ":";
123 }
124
125 /**
126 * isWhitespace returns `true` if the provided character is whitespace.
127 * @param {string} c
128 * @returns boolean
129 */
130 function isWhitespace(c) {
131 return c === " " || c === "\t" || isNewline(c);
132 }
133
134 /**
135 * positionString returns a string representation of a Token's line and column details.
136 * @param {Token} token
137 * @returns string
138 */
139 function positionString(token) {
140 return "[Line: " + token.line + ", Column: " + token.column + "]";
141 }
142
143 /**
144 * isNewline returns `true` if the provided character is a carrage return or newline
145 * @param {string} c
146 * @returns boolean
147 */
148 function isNewline(c) {
149 return c === "\r" || c === "\n";
150 }
151
152 /**
153 * isNumeric returns `true` if the provided character is a number (0-9)
154 * @param {string} c
155 * @returns boolean
156 */
157 function isNumeric(c) {
158 return c >= "0" && c <= "9";
159 }
160
161 /**
162 * isAlpha returns `true` if the provided character is a letter in the alphabet
163 * @param {string} c
164 * @returns boolean
165 */
166 function isAlpha(c) {
167 return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z");
168 }
169
170 /**
171 * @param {string} c
172 * @param {boolean} [dollarIsOp]
173 * @returns boolean
174 */
175 function isIdentifierChar(c, dollarIsOp) {
176 return c === "_" || c === "$";
177 }
178
179 /**
180 * @param {string} c
181 * @returns boolean
182 */
183 function isReservedChar(c) {
184 return c === "`" || c === "^";
185 }
186
187 /**
188 * @param {Token[]} tokens
189 * @param {Token[]} consumed
190 * @param {string} source
191 * @returns {TokensObject}
192 */
193 function makeTokensObject(tokens, consumed, source) {
194 consumeWhitespace(); // consume initial whitespace
195
196 /** @type Token | null */
197 var _lastConsumed = null;
198
199 function consumeWhitespace() {
200 while (token(0, true).type === "WHITESPACE") {
201 consumed.push(tokens.shift());
202 }
203 }
204
205 /**
206 * @param {TokensObject} tokens
207 * @param {*} error
208 */
209 function raiseError(tokens, error) {
210 _parser.raiseParseError(tokens, error);
211 }
212
213 /**
214 * @param {string} value
215 * @returns {Token}
216 */
217 function requireOpToken(value) {
218 var token = matchOpToken(value);
219 if (token) {
220 return token;
221 } else {
222 raiseError(this, "Expected '" + value + "' but found '" + currentToken().value + "'");
223 }
224 }
225
226 /**
227 * @param {string} op1
228 * @param {string} [op2]
229 * @param {string} [op3]
230 * @returns {Token | void}
231 */
232 function matchAnyOpToken(op1, op2, op3) {
233 for (var i = 0; i < arguments.length; i++) {
234 var opToken = arguments[i];
235 var match = matchOpToken(opToken);
236 if (match) {
237 return match;
238 }
239 }
240 }
241
242 /**
243 * @param {string} op1
244 * @param {string} [op2]
245 * @param {string} [op3]
246 * @returns {Token | void}
247 */
248 function matchAnyToken(op1, op2, op3) {
249 for (var i = 0; i < arguments.length; i++) {
250 var opToken = arguments[i];
251 var match = matchToken(opToken);
252 if (match) {
253 return match;
254 }
255 }
256 }
257
258 /**
259 * @param {string} value
260 * @returns {Token | void}
261 */
262 function matchOpToken(value) {
263 if (currentToken() && currentToken().op && currentToken().value === value) {
264 return consumeToken();
265 }
266 }
267
268 /**
269 * @param {string} type1
270 * @param {string} [type2]
271 * @param {string} [type3]
272 * @param {string} [type4]
273 * @returns {Token}
274 */
275 function requireTokenType(type1, type2, type3, type4) {
276 var token = matchTokenType(type1, type2, type3, type4);
277 if (token) {
278 return token;
279 } else {
280 raiseError(this, "Expected one of " + JSON.stringify([type1, type2, type3]));
281 }
282 }
283
284 /**
285 * @param {string} type1
286 * @param {string} [type2]
287 * @param {string} [type3]
288 * @param {string} [type4]
289 * @returns {Token | void}
290 */
291 function matchTokenType(type1, type2, type3, type4) {
292 if (
293 currentToken() &&
294 currentToken().type &&
295 [type1, type2, type3, type4].indexOf(currentToken().type) >= 0
296 ) {
297 return consumeToken();
298 }
299 }
300
301 /**
302 * @param {string} value
303 * @param {string} [type]
304 * @returns {Token}
305 */
306 function requireToken(value, type) {
307 var token = matchToken(value, type);
308 if (token) {
309 return token;
310 } else {
311 raiseError(this, "Expected '" + value + "' but found '" + currentToken().value + "'");
312 }
313 }
314
315 function peekToken(value, peek, type) {
316 return tokens[peek] && tokens[peek].value === value && tokens[peek].type === type
317 }
318
319 /**
320 * @param {string} value
321 * @param {string} [type]
322 * @returns {Token | void}
323 */
324 function matchToken(value, type) {
325 if (follows.indexOf(value) !== -1) {
326 return; // disallowed token here
327 }
328 var type = type || "IDENTIFIER";
329 if (currentToken() && currentToken().value === value && currentToken().type === type) {
330 return consumeToken();
331 }
332 }
333
334 /**
335 * @returns {Token}
336 */
337 function consumeToken() {
338 var match = tokens.shift();
339 consumed.push(match);
340 _lastConsumed = match;
341 consumeWhitespace(); // consume any whitespace
342 return match;
343 }
344
345 /**
346 * @param {string} value
347 * @param {string} [type]
348 * @returns {Token[]}
349 */
350 function consumeUntil(value, type) {
351 /** @type Token[] */
352 var tokenList = [];
353 var currentToken = token(0, true);
354
355 while (
356 (type == null || currentToken.type !== type) &&
357 (value == null || currentToken.value !== value) &&
358 currentToken.type !== "EOF"
359 ) {
360 var match = tokens.shift();
361 consumed.push(match);
362 tokenList.push(currentToken);
363 currentToken = token(0, true);
364 }
365 consumeWhitespace(); // consume any whitespace
366 return tokenList;
367 }
368
369 /**
370 * @returns {string}
371 */
372 function lastWhitespace() {
373 if (consumed[consumed.length - 1] && consumed[consumed.length - 1].type === "WHITESPACE") {
374 return consumed[consumed.length - 1].value;
375 } else {
376 return "";
377 }
378 }
379
380 function consumeUntilWhitespace() {
381 return consumeUntil(null, "WHITESPACE");
382 }
383
384 /**
385 * @returns {boolean}
386 */
387 function hasMore() {
388 return tokens.length > 0;
389 }
390
391 /**
392 * @param {number} n
393 * @param {boolean} [dontIgnoreWhitespace]
394 * @returns {Token}
395 */
396 function token(n, dontIgnoreWhitespace) {
397 var /**@type {Token}*/ token;
398 var i = 0;
399 do {
400 if (!dontIgnoreWhitespace) {
401 while (tokens[i] && tokens[i].type === "WHITESPACE") {
402 i++;
403 }
404 }
405 token = tokens[i];
406 n--;
407 i++;
408 } while (n > -1);
409 if (token) {
410 return token;
411 } else {
412 return {
413 type: "EOF",
414 value: "<<<EOF>>>",
415 };
416 }
417 }
418
419 /**
420 * @returns {Token}
421 */
422 function currentToken() {
423 return token(0);
424 }
425
426 /**
427 * @returns {Token | null}
428 */
429 function lastMatch() {
430 return _lastConsumed;
431 }
432
433 /**
434 * @returns {string}
435 */
436 function sourceFor() {
437 return source.substring(this.startToken.start, this.endToken.end);
438 }
439
440 /**
441 * @returns {string}
442 */
443 function lineFor() {
444 return source.split("\n")[this.startToken.line - 1];
445 }
446
447 var follows = [];
448
449 function pushFollow(str) {
450 follows.push(str);
451 }
452
453 function popFollow() {
454 follows.pop();
455 }
456
457 function clearFollows() {
458 var tmp = follows;
459 follows = [];
460 return tmp;
461 }
462
463 function restoreFollows(f) {
464 follows = f;
465 }
466
467 /** @type {TokensObject} */
468 return {
469 pushFollow: pushFollow,
470 popFollow: popFollow,
471 clearFollow: clearFollows,
472 restoreFollow: restoreFollows,
473 matchAnyToken: matchAnyToken,
474 matchAnyOpToken: matchAnyOpToken,
475 matchOpToken: matchOpToken,
476 requireOpToken: requireOpToken,
477 matchTokenType: matchTokenType,
478 requireTokenType: requireTokenType,
479 consumeToken: consumeToken,
480 peekToken: peekToken,
481 matchToken: matchToken,
482 requireToken: requireToken,
483 list: tokens,
484 consumed: consumed,
485 source: source,
486 hasMore: hasMore,
487 currentToken: currentToken,
488 lastMatch: lastMatch,
489 token: token,
490 consumeUntil: consumeUntil,
491 consumeUntilWhitespace: consumeUntilWhitespace,
492 lastWhitespace: lastWhitespace,
493 sourceFor: sourceFor,
494 lineFor: lineFor,
495 };
496 }
497
498 /**
499 * @param {Token[]} tokens
500 * @returns {boolean}
501 */
502 function isValidSingleQuoteStringStart(tokens) {
503 if (tokens.length > 0) {
504 var previousToken = tokens[tokens.length - 1];
505 if (
506 previousToken.type === "IDENTIFIER" ||
507 previousToken.type === "CLASS_REF" ||
508 previousToken.type === "ID_REF"
509 ) {
510 return false;
511 }
512 if (previousToken.op && (previousToken.value === ">" || previousToken.value === ")")) {
513 return false;
514 }
515 }
516 return true;
517 }
518
519 /**
520 * @param {string} string
521 * @param {boolean} [template]
522 * @returns {TokensObject}
523 */
524 function tokenize(string, template) {
525 var tokens = /** @type {Token[]}*/ [];
526 var source = string;
527 var position = 0;
528 var column = 0;
529 var line = 1;
530 var lastToken = "<START>";
531 var templateBraceCount = 0;
532
533 function inTemplate() {
534 return template && templateBraceCount === 0;
535 }
536
537 while (position < source.length) {
538 if (currentChar() === "-" && nextChar() === "-" && (isWhitespace(charAfterThat()) || charAfterThat() === "")) {
539 consumeComment();
540 } else {
541 if (isWhitespace(currentChar())) {
542 tokens.push(consumeWhitespace());
543 } else if (
544 !possiblePrecedingSymbol() &&
545 currentChar() === "." &&
546 (isAlpha(nextChar()) || nextChar() === "{")
547 ) {
548 tokens.push(consumeClassReference());
549 } else if (
550 !possiblePrecedingSymbol() &&
551 currentChar() === "#" &&
552 (isAlpha(nextChar()) || nextChar() === "{")
553 ) {
554 tokens.push(consumeIdReference());
555 } else if (currentChar() === "[" && nextChar() === "@") {
556 tokens.push(consumeAttributeReference());
557 } else if (currentChar() === "@") {
558 tokens.push(consumeShortAttributeReference());
559 } else if (currentChar() === "*" && isAlpha(nextChar())) {
560 tokens.push(consumeStyleReference());
561 } else if (isAlpha(currentChar()) || (!inTemplate() && isIdentifierChar(currentChar()))) {
562 tokens.push(consumeIdentifier());
563 } else if (isNumeric(currentChar())) {
564 tokens.push(consumeNumber());
565 } else if (!inTemplate() && (currentChar() === '"' || currentChar() === "`")) {
566 tokens.push(consumeString());
567 } else if (!inTemplate() && currentChar() === "'") {
568 if (isValidSingleQuoteStringStart(tokens)) {
569 tokens.push(consumeString());
570 } else {
571 tokens.push(consumeOp());
572 }
573 } else if (OP_TABLE[currentChar()]) {
574 if (lastToken === "$" && currentChar() === "{") {
575 templateBraceCount++;
576 }
577 if (currentChar() === "}") {
578 templateBraceCount--;
579 }
580 tokens.push(consumeOp());
581 } else if (inTemplate() || isReservedChar(currentChar())) {
582 tokens.push(makeToken("RESERVED", consumeChar()));
583 } else {
584 if (position < source.length) {
585 throw Error("Unknown token: " + currentChar() + " ");
586 }
587 }
588 }
589 }
590
591 return makeTokensObject(tokens, [], source);
592
593 /**
594 * @param {string} [type]
595 * @param {string} [value]
596 * @returns {Token}
597 */
598 function makeOpToken(type, value) {
599 var token = makeToken(type, value);
600 token.op = true;
601 return token;
602 }
603
604 /**
605 * @param {string} [type]
606 * @param {string} [value]
607 * @returns {Token}
608 */
609 function makeToken(type, value) {
610 return {
611 type: type,
612 value: value,
613 start: position,
614 end: position + 1,
615 column: column,
616 line: line,
617 };
618 }
619
620 function consumeComment() {
621 while (currentChar() && !isNewline(currentChar())) {
622 consumeChar();
623 }
624 consumeChar();
625 }
626
627 /**
628 * @returns Token
629 */
630 function consumeClassReference() {
631 var classRef = makeToken("CLASS_REF");
632 var value = consumeChar();
633 if (currentChar() === "{") {
634 classRef.template = true;
635 value += consumeChar();
636 while (currentChar() && currentChar() !== "}") {
637 value += consumeChar();
638 }
639 if (currentChar() !== "}") {
640 throw Error("Unterminated class reference");
641 } else {
642 value += consumeChar(); // consume final curly
643 }
644 } else {
645 while (isValidCSSClassChar(currentChar())) {
646 value += consumeChar();
647 }
648 }
649 classRef.value = value;
650 classRef.end = position;
651 return classRef;
652 }
653
654 /**
655 * @returns Token
656 */
657 function consumeAttributeReference() {
658 var attributeRef = makeToken("ATTRIBUTE_REF");
659 var value = consumeChar();
660 while (position < source.length && currentChar() !== "]") {
661 value += consumeChar();
662 }
663 if (currentChar() === "]") {
664 value += consumeChar();
665 }
666 attributeRef.value = value;
667 attributeRef.end = position;
668 return attributeRef;
669 }
670
671 function consumeShortAttributeReference() {
672 var attributeRef = makeToken("ATTRIBUTE_REF");
673 var value = consumeChar();
674 while (isValidCSSIDChar(currentChar())) {
675 value += consumeChar();
676 }
677 attributeRef.value = value;
678 attributeRef.end = position;
679 return attributeRef;
680 }
681
682 function consumeStyleReference() {
683 var styleRef = makeToken("STYLE_REF");
684 var value = consumeChar();
685 while (isAlpha(currentChar()) || currentChar() === "-") {
686 value += consumeChar();
687 }
688 styleRef.value = value;
689 styleRef.end = position;
690 return styleRef;
691 }
692
693 /**
694 * @returns Token
695 */
696 function consumeIdReference() {
697 var idRef = makeToken("ID_REF");
698 var value = consumeChar();
699 if (currentChar() === "{") {
700 idRef.template = true;
701 value += consumeChar();
702 while (currentChar() && currentChar() !== "}") {
703 value += consumeChar();
704 }
705 if (currentChar() !== "}") {
706 throw Error("Unterminated id reference");
707 } else {
708 consumeChar(); // consume final quote
709 }
710 } else {
711 while (isValidCSSIDChar(currentChar())) {
712 value += consumeChar();
713 }
714 }
715 idRef.value = value;
716 idRef.end = position;
717 return idRef;
718 }
719
720 /**
721 * @returns Token
722 */
723 function consumeIdentifier() {
724 var identifier = makeToken("IDENTIFIER");
725 var value = consumeChar();
726 while (isAlpha(currentChar()) ||
727 isNumeric(currentChar()) ||
728 isIdentifierChar(currentChar())) {
729 value += consumeChar();
730 }
731 if (currentChar() === "!" && value === "beep") {
732 value += consumeChar();
733 }
734 identifier.value = value;
735 identifier.end = position;
736 return identifier;
737 }
738
739 /**
740 * @returns Token
741 */
742 function consumeNumber() {
743 var number = makeToken("NUMBER");
744 var value = consumeChar();
745 while (isNumeric(currentChar())) {
746 value += consumeChar();
747 }
748 if (currentChar() === "." && isNumeric(nextChar())) {
749 value += consumeChar();
750 }
751 while (isNumeric(currentChar())) {
752 value += consumeChar();
753 }
754 number.value = value;
755 number.end = position;
756 return number;
757 }
758
759 /**
760 * @returns Token
761 */
762 function consumeOp() {
763 var op = makeOpToken();
764 var value = consumeChar(); // consume leading char
765 while (currentChar() && OP_TABLE[value + currentChar()]) {
766 value += consumeChar();
767 }
768 op.type = OP_TABLE[value];
769 op.value = value;
770 op.end = position;
771 return op;
772 }
773
774 /**
775 * @returns Token
776 */
777 function consumeString() {
778 var string = makeToken("STRING");
779 var startChar = consumeChar(); // consume leading quote
780 var value = "";
781 while (currentChar() && currentChar() !== startChar) {
782 if (currentChar() === "\\") {
783 consumeChar(); // consume escape char and get the next one
784 let nextChar = consumeChar();
785 if (nextChar === "b") {
786 value += "\b";
787 } else if (nextChar === "f") {
788 value += "\f";
789 } else if (nextChar === "n") {
790 value += "\n";
791 } else if (nextChar === "r") {
792 value += "\r";
793 } else if (nextChar === "t") {
794 value += "\t";
795 } else if (nextChar === "v") {
796 value += "\v";
797 } else {
798 value += nextChar;
799 }
800 } else {
801 value += consumeChar();
802 }
803 }
804 if (currentChar() !== startChar) {
805 throw Error("Unterminated string at " + positionString(string));
806 } else {
807 consumeChar(); // consume final quote
808 }
809 string.value = value;
810 string.end = position;
811 string.template = startChar === "`";
812 return string;
813 }
814
815 /**
816 * @returns string
817 */
818 function currentChar() {
819 return source.charAt(position);
820 }
821
822 /**
823 * @returns string
824 */
825 function nextChar() {
826 return source.charAt(position + 1);
827 }
828
829 function charAfterThat() {
830 return source.charAt(position + 2);
831 }
832
833 /**
834 * @returns string
835 */
836 function consumeChar() {
837 lastToken = currentChar();
838 position++;
839 column++;
840 return lastToken;
841 }
842
843 /**
844 * @returns boolean
845 */
846 function possiblePrecedingSymbol() {
847 return (
848 isAlpha(lastToken) ||
849 isNumeric(lastToken) ||
850 lastToken === ")" ||
851 lastToken === "\"" ||
852 lastToken === "'" ||
853 lastToken === "`" ||
854 lastToken === "}" ||
855 lastToken === "]"
856 );
857 }
858
859 /**
860 * @returns Token
861 */
862 function consumeWhitespace() {
863 var whitespace = makeToken("WHITESPACE");
864 var value = "";
865 while (currentChar() && isWhitespace(currentChar())) {
866 if (isNewline(currentChar())) {
867 column = 0;
868 line++;
869 }
870 value += consumeChar();
871 }
872 whitespace.value = value;
873 whitespace.end = position;
874 return whitespace;
875 }
876 }
877
878 return {
879 tokenize: tokenize,
880 makeTokensObject: makeTokensObject,
881 };
882})();
883
884//====================================================================
885// Parser
886//====================================================================
887
888/** @type ParserObject */
889var _parser = (function () {
890 /** @type {Object<string,GrammarDefinition>} */
891 var GRAMMAR = {};
892
893 /** @type {Object<string,GrammarDefinition>} */
894 var COMMANDS = {};
895
896 /** @type {Object<string,GrammarDefinition>} */
897 var FEATURES = {};
898
899 var LEAF_EXPRESSIONS = [];
900 var INDIRECT_EXPRESSIONS = [];
901
902 /**
903 * @param {*} parseElement
904 * @param {*} start
905 * @param {TokensObject} tokens
906 */
907 function initElt(parseElement, start, tokens) {
908 parseElement.startToken = start;
909 parseElement.sourceFor = tokens.sourceFor;
910 parseElement.lineFor = tokens.lineFor;
911 parseElement.programSource = tokens.source;
912 }
913
914 /**
915 * @param {string} type
916 * @param {TokensObject} tokens
917 * @param {GrammarElement?} root
918 * @returns GrammarElement
919 */
920 function parseElement(type, tokens, root = undefined) {
921 var elementDefinition = GRAMMAR[type];
922 if (elementDefinition) {
923 var start = tokens.currentToken();
924 var parseElement = elementDefinition(_parser, _runtime, tokens, root);
925 if (parseElement) {
926 initElt(parseElement, start, tokens);
927 parseElement.endToken = parseElement.endToken || tokens.lastMatch();
928 var root = parseElement.root;
929 while (root != null) {
930 initElt(root, start, tokens);
931 root = root.root;
932 }
933 }
934 return parseElement;
935 }
936 }
937
938 /**
939 * @param {string} type
940 * @param {TokensObject} tokens
941 * @param {string} [message]
942 * @param {*} [root]
943 * @returns {GrammarElement}
944 */
945 function requireElement(type, tokens, message, root) {
946 var result = parseElement(type, tokens, root);
947 if (!result) raiseParseError(tokens, message || "Expected " + type);
948 // @ts-ignore
949 return result;
950 }
951
952 /**
953 * @param {string[]} types
954 * @param {TokensObject} tokens
955 * @returns {GrammarElement}
956 */
957 function parseAnyOf(types, tokens) {
958 for (var i = 0; i < types.length; i++) {
959 var type = types[i];
960 var expression = parseElement(type, tokens);
961 if (expression) {
962 return expression;
963 }
964 }
965 }
966
967 /**
968 * @param {string} name
969 * @param {GrammarDefinition} definition
970 */
971 function addGrammarElement(name, definition) {
972 GRAMMAR[name] = definition;
973 }
974
975 /**
976 * @param {string} keyword
977 * @param {GrammarDefinition} definition
978 */
979 function addCommand(keyword, definition) {
980 var commandGrammarType = keyword + "Command";
981 var commandDefinitionWrapper = function (parser, runtime, tokens) {
982 const commandElement = definition(parser, runtime, tokens);
983 if (commandElement) {
984 commandElement.type = commandGrammarType;
985 commandElement.execute = function (context) {
986 context.meta.command = commandElement;
987 return runtime.unifiedExec(this, context);
988 };
989 return commandElement;
990 }
991 };
992 GRAMMAR[commandGrammarType] = commandDefinitionWrapper;
993 COMMANDS[keyword] = commandDefinitionWrapper;
994 }
995
996 /**
997 * @param {string} keyword
998 * @param {GrammarDefinition} definition
999 */
1000 function addFeature(keyword, definition) {
1001 var featureGrammarType = keyword + "Feature";
1002
1003 /** @type {GrammarDefinition} */
1004 var featureDefinitionWrapper = function (parser, runtime, tokens) {
1005 var featureElement = definition(parser, runtime, tokens);
1006 if (featureElement) {
1007 featureElement.isFeature = true;
1008 featureElement.keyword = keyword;
1009 featureElement.type = featureGrammarType;
1010 return featureElement;
1011 }
1012 };
1013 GRAMMAR[featureGrammarType] = featureDefinitionWrapper;
1014 FEATURES[keyword] = featureDefinitionWrapper;
1015 }
1016
1017 /**
1018 * @param {string} name
1019 * @param {GrammarDefinition} definition
1020 */
1021 function addLeafExpression(name, definition) {
1022 LEAF_EXPRESSIONS.push(name);
1023 addGrammarElement(name, definition);
1024 }
1025
1026 /**
1027 * @param {string} name
1028 * @param {GrammarDefinition} definition
1029 */
1030 function addIndirectExpression(name, definition) {
1031 INDIRECT_EXPRESSIONS.push(name);
1032 addGrammarElement(name, definition);
1033 }
1034
1035 /* ============================================================================================ */
1036 /* Core hyperscript Grammar Elements */
1037 /* ============================================================================================ */
1038 addGrammarElement("feature", function (parser, runtime, tokens) {
1039 if (tokens.matchOpToken("(")) {
1040 var featureElement = parser.requireElement("feature", tokens);
1041 tokens.requireOpToken(")");
1042 return featureElement;
1043 }
1044
1045 var featureDefinition = FEATURES[tokens.currentToken().value];
1046 if (featureDefinition) {
1047 return featureDefinition(parser, runtime, tokens);
1048 }
1049 });
1050
1051 addGrammarElement("command", function (parser, runtime, tokens) {
1052 if (tokens.matchOpToken("(")) {
1053 const commandElement = parser.requireElement("command", tokens);
1054 tokens.requireOpToken(")");
1055 return commandElement;
1056 }
1057
1058 var commandDefinition = COMMANDS[tokens.currentToken().value];
1059 let commandElement;
1060 if (commandDefinition) {
1061 commandElement = commandDefinition(parser, runtime, tokens);
1062 } else if (tokens.currentToken().type === "IDENTIFIER") {
1063 commandElement = parser.parseElement("pseudoCommand", tokens);
1064 }
1065 if (commandElement) {
1066 return parser.parseElement("indirectStatement", tokens, commandElement);
1067 }
1068
1069 return commandElement;
1070 });
1071
1072 addGrammarElement("commandList", function (parser, runtime, tokens) {
1073 var cmd = parser.parseElement("command", tokens);
1074 if (cmd) {
1075 tokens.matchToken("then");
1076 const next = parser.parseElement("commandList", tokens);
1077 if (next) cmd.next = next;
1078 return cmd;
1079 }
1080 });
1081
1082 addGrammarElement("leaf", function (parser, runtime, tokens) {
1083 var result = parseAnyOf(LEAF_EXPRESSIONS, tokens);
1084 // symbol is last so it doesn't consume any constants
1085 if (result == null) {
1086 return parseElement("symbol", tokens);
1087 }
1088
1089 return result;
1090 });
1091
1092 addGrammarElement("indirectExpression", function (parser, runtime, tokens, root) {
1093 for (var i = 0; i < INDIRECT_EXPRESSIONS.length; i++) {
1094 var indirect = INDIRECT_EXPRESSIONS[i];
1095 root.endToken = tokens.lastMatch();
1096 var result = parser.parseElement(indirect, tokens, root);
1097 if (result) {
1098 return result;
1099 }
1100 }
1101 return root;
1102 });
1103
1104 addGrammarElement("indirectStatement", function (parser, runtime, tokens, root) {
1105 if (tokens.matchToken("unless")) {
1106 root.endToken = tokens.lastMatch();
1107 var conditional = parser.requireElement("expression", tokens);
1108 var unless = {
1109 type: "unlessStatementModifier",
1110 args: [conditional],
1111 op: function (context, conditional) {
1112 if (conditional) {
1113 return this.next;
1114 } else {
1115 return root;
1116 }
1117 },
1118 execute: function (context) {
1119 return runtime.unifiedExec(this, context);
1120 },
1121 };
1122 root.parent = unless;
1123 return unless;
1124 }
1125 return root;
1126 });
1127
1128 addGrammarElement("primaryExpression", function (parser, runtime, tokens) {
1129 var leaf = parser.parseElement("leaf", tokens);
1130 if (leaf) {
1131 return parser.parseElement("indirectExpression", tokens, leaf);
1132 }
1133 parser.raiseParseError(tokens, "Unexpected value: " + tokens.currentToken().value);
1134 });
1135
1136 /* ============================================================================================ */
1137 /* END Core hyperscript Grammar Elements */
1138
1139 /* ============================================================================================ */
1140
1141 /**
1142 *
1143 * @param {TokensObject} tokens
1144 * @returns string
1145 */
1146 function createParserContext(tokens) {
1147 var currentToken = tokens.currentToken();
1148 var source = tokens.source;
1149 var lines = source.split("\n");
1150 var line = currentToken && currentToken.line ? currentToken.line - 1 : lines.length - 1;
1151 var contextLine = lines[line];
1152 var offset = currentToken && currentToken.line ? currentToken.column : contextLine.length - 1;
1153 return contextLine + "\n" + " ".repeat(offset) + "^^\n\n";
1154 }
1155
1156 /**
1157 * @param {TokensObject} tokens
1158 * @param {string} [message]
1159 */
1160 function raiseParseError(tokens, message) {
1161 message =
1162 (message || "Unexpected Token : " + tokens.currentToken().value) + "\n\n" + createParserContext(tokens);
1163 var error = new Error(message);
1164 error["tokens"] = tokens;
1165 throw error;
1166 }
1167
1168 /**
1169 * @param {TokensObject} tokens
1170 * @returns {GrammarElement}
1171 */
1172 function parseHyperScript(tokens) {
1173 var result = parseElement("hyperscript", tokens);
1174 if (tokens.hasMore()) raiseParseError(tokens);
1175 if (result) return result;
1176 }
1177
1178 /**
1179 * @param {GrammarElement} elt
1180 * @param {GrammarElement} parent
1181 */
1182 function setParent(elt, parent) {
1183 if (typeof elt === 'object') {
1184 elt.parent = parent;
1185 if (typeof parent === 'object') {
1186 parent.children = (parent.children || new Set());
1187 parent.children.add(elt)
1188 }
1189 setParent(elt.next, parent);
1190 }
1191 }
1192
1193 /**
1194 * @param {Token} token
1195 * @returns {GrammarDefinition}
1196 */
1197 function commandStart(token) {
1198 return COMMANDS[token.value];
1199 }
1200
1201 /**
1202 * @param {Token} token
1203 * @returns {GrammarDefinition}
1204 */
1205 function featureStart(token) {
1206 return FEATURES[token.value];
1207 }
1208
1209 /**
1210 * @param {Token} token
1211 * @returns {boolean}
1212 */
1213 function commandBoundary(token) {
1214 if (
1215 token.value == "end" ||
1216 token.value == "then" ||
1217 token.value == "else" ||
1218 token.value == "otherwise" ||
1219 token.value == ")" ||
1220 commandStart(token) ||
1221 featureStart(token) ||
1222 token.type == "EOF"
1223 ) {
1224 return true;
1225 }
1226 return false;
1227 }
1228
1229 /**
1230 * @param {TokensObject} tokens
1231 * @returns {(string | GrammarElement)[]}
1232 */
1233 function parseStringTemplate(tokens) {
1234 /** @type {(string | GrammarElement)[]} */
1235 var returnArr = [""];
1236 do {
1237 returnArr.push(tokens.lastWhitespace());
1238 if (tokens.currentToken().value === "$") {
1239 tokens.consumeToken();
1240 var startingBrace = tokens.matchOpToken("{");
1241 returnArr.push(requireElement("expression", tokens));
1242 if (startingBrace) {
1243 tokens.requireOpToken("}");
1244 }
1245 returnArr.push("");
1246 } else if (tokens.currentToken().value === "\\") {
1247 tokens.consumeToken(); // skip next
1248 tokens.consumeToken();
1249 } else {
1250 var token = tokens.consumeToken();
1251 returnArr[returnArr.length - 1] += token ? token.value : "";
1252 }
1253 } while (tokens.hasMore());
1254 returnArr.push(tokens.lastWhitespace());
1255 return returnArr;
1256 }
1257
1258 /**
1259 * @param {GrammarElement} commandList
1260 */
1261 function ensureTerminated(commandList) {
1262 var implicitReturn = {
1263 type: "implicitReturn",
1264 op: function (context) {
1265 context.meta.returned = true;
1266 if (context.meta.resolve) {
1267 context.meta.resolve();
1268 }
1269 return _runtime.HALT;
1270 },
1271 execute: function (ctx) {
1272 // do nothing
1273 },
1274 };
1275
1276 var end = commandList;
1277 while (end.next) {
1278 end = end.next;
1279 }
1280 end.next = implicitReturn;
1281 }
1282
1283
1284 // parser API
1285 return {
1286 setParent,
1287 requireElement,
1288 parseElement,
1289 featureStart,
1290 commandStart,
1291 commandBoundary,
1292 parseAnyOf,
1293 parseHyperScript,
1294 raiseParseError,
1295 addGrammarElement,
1296 addCommand,
1297 addFeature,
1298 addLeafExpression,
1299 addIndirectExpression,
1300 parseStringTemplate,
1301 ensureTerminated,
1302 };
1303})();
1304
1305//====================================================================
1306// Runtime
1307//====================================================================
1308
1309var CONVERSIONS = {
1310 dynamicResolvers: /** @type DynamicConversionFunction[] */ [
1311 function(str, value){
1312 if (str === "Fixed") {
1313 return Number(value).toFixed();
1314 } else if (str.indexOf("Fixed:") === 0) {
1315 let num = str.split(":")[1];
1316 return Number(value).toFixed(parseInt(num));
1317 }
1318 }
1319 ],
1320 String: function (val) {
1321 if (val.toString) {
1322 return val.toString();
1323 } else {
1324 return "" + val;
1325 }
1326 },
1327 Int: function (val) {
1328 return parseInt(val);
1329 },
1330 Float: function (val) {
1331 return parseFloat(val);
1332 },
1333 Number: function (val) {
1334 return Number(val);
1335 },
1336 Date: function (val) {
1337 return new Date(val);
1338 },
1339 Array: function (val) {
1340 return Array.from(val);
1341 },
1342 JSON: function (val) {
1343 return JSON.stringify(val);
1344 },
1345 Object: function (val) {
1346 if (val instanceof String) {
1347 val = val.toString();
1348 }
1349 if (typeof val === "string") {
1350 return JSON.parse(val);
1351 } else {
1352 return mergeObjects({}, val);
1353 }
1354 },
1355};
1356
1357/********************************************
1358 * RUNTIME OBJECT
1359 ********************************************/
1360
1361/** @type {RuntimeObject} */
1362var _runtime = (function () {
1363 /**
1364 * @param {HTMLElement} elt
1365 * @param {string} selector
1366 * @returns boolean
1367 */
1368 function matchesSelector(elt, selector) {
1369 // noinspection JSUnresolvedVariable
1370 var matchesFunction =
1371 // @ts-ignore
1372 elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector;
1373 return matchesFunction && matchesFunction.call(elt, selector);
1374 }
1375
1376 /**
1377 * @param {string} eventName
1378 * @param {Object} [detail]
1379 * @returns {Event}
1380 */
1381 function makeEvent(eventName, detail) {
1382 var evt;
1383 if (globalScope.Event && typeof globalScope.Event === "function") {
1384 evt = new Event(eventName, {
1385 bubbles: true,
1386 cancelable: true,
1387 });
1388 evt['detail'] = detail;
1389 } else {
1390 evt = document.createEvent("CustomEvent");
1391 evt.initCustomEvent(eventName, true, true, detail);
1392 }
1393 return evt;
1394 }
1395
1396 /**
1397 * @param {Element} elt
1398 * @param {string} eventName
1399 * @param {Object} [detail]
1400 * @param {Element} sender
1401 * @returns {boolean}
1402 */
1403 function triggerEvent(elt, eventName, detail, sender) {
1404 detail = detail || {};
1405 detail["sender"] = sender;
1406 var event = makeEvent(eventName, detail);
1407 var eventResult = elt.dispatchEvent(event);
1408 return eventResult;
1409 }
1410
1411 /**
1412 * isArrayLike returns `true` if the provided value is an array or
1413 * a NodeList (which is close enough to being an array for our purposes).
1414 *
1415 * @param {any} value
1416 * @returns {value is Array | NodeList}
1417 */
1418 function isArrayLike(value) {
1419 return Array.isArray(value) ||
1420 (typeof NodeList !== 'undefined' && (value instanceof NodeList || value instanceof HTMLCollection));
1421 }
1422
1423 /**
1424 * isIterable returns `true` if the provided value supports the
1425 * iterator protocol.
1426 *
1427 * @param {any} value
1428 * @returns {value is Iterable}
1429 */
1430 function isIterable(value) {
1431 return typeof value === 'object'
1432 && Symbol.iterator in value
1433 && typeof value[Symbol.iterator] === 'function';
1434 }
1435
1436 /**
1437 * shouldAutoIterate returns `true` if the provided value
1438 * should be implicitly iterated over when accessing properties,
1439 * and as the target of some commands.
1440 *
1441 * Currently, this is when the value is an {ElementCollection}
1442 * or {isArrayLike} returns true.
1443 *
1444 * @param {any} value
1445 * @returns {value is any[] | NodeList | ElementCollection}
1446 */
1447 function shouldAutoIterate(value) {
1448 return value instanceof ElementCollection ||
1449 isArrayLike(value);
1450 }
1451
1452 /**
1453 * forEach executes the provided `func` on every item in the `value` array.
1454 * if `value` is a single item (and not an array) then `func` is simply called
1455 * once. If `value` is null, then no further actions are taken.
1456 *
1457 * @template T
1458 * @param {T | Iterable<T>} value
1459 * @param {(item: T) => void} func
1460 */
1461 function forEach(value, func) {
1462 if (value == null) {
1463 // do nothing
1464 } else if (isIterable(value)) {
1465 for (const nth of value) {
1466 func(nth);
1467 }
1468 } else if (isArrayLike(value)) {
1469 for (var i = 0; i < value.length; i++) {
1470 func(value[i]);
1471 }
1472 } else {
1473 func(value);
1474 }
1475 }
1476
1477 /**
1478 * implicitLoop executes the provided `func` on:
1479 * - every item of {value}, if {value} should be auto-iterated
1480 * (see {shouldAutoIterate})
1481 * - {value} otherwise
1482 *
1483 * @template T
1484 * @param {NodeList | T | T[]} value
1485 * @param {(item:Node | T) => void} func
1486 */
1487 function implicitLoop(value, func) {
1488 if (shouldAutoIterate(value)) {
1489 for (const x of value) func(x);
1490 } else {
1491 func(value);
1492 }
1493 }
1494
1495 function wrapArrays(args) {
1496 var arr = [];
1497 for (var i = 0; i < args.length; i++) {
1498 var arg = args[i];
1499 if (Array.isArray(arg)) {
1500 arr.push(Promise.all(arg));
1501 } else {
1502 arr.push(arg);
1503 }
1504 }
1505 return arr;
1506 }
1507
1508 function unwrapAsyncs(values) {
1509 for (var i = 0; i < values.length; i++) {
1510 var value = values[i];
1511 if (value.asyncWrapper) {
1512 values[i] = value.value;
1513 }
1514 if (Array.isArray(value)) {
1515 for (var j = 0; j < value.length; j++) {
1516 var valueElement = value[j];
1517 if (valueElement.asyncWrapper) {
1518 value[j] = valueElement.value;
1519 }
1520 }
1521 }
1522 }
1523 }
1524
1525 var HALT = {};
1526
1527 /**
1528 * @param {GrammarElement} command
1529 * @param {Context} ctx
1530 */
1531 function unifiedExec(command, ctx) {
1532 while (true) {
1533 try {
1534 var next = unifiedEval(command, ctx);
1535 } catch (e) {
1536 if (ctx.meta.handlingFinally) {
1537 console.error(" Exception in finally block: ", e);
1538 next = HALT;
1539 } else {
1540 _runtime.registerHyperTrace(ctx, e);
1541 if (ctx.meta.errorHandler && !ctx.meta.handlingError) {
1542 ctx.meta.handlingError = true;
1543 ctx[ctx.meta.errorSymbol] = e;
1544 command = ctx.meta.errorHandler;
1545 continue;
1546 } else {
1547 ctx.meta.currentException = e;
1548 next = HALT;
1549 }
1550 }
1551 }
1552 if (next == null) {
1553 console.error(command, " did not return a next element to execute! context: ", ctx);
1554 return;
1555 } else if (next.then) {
1556 next.then(function (resolvedNext) {
1557 unifiedExec(resolvedNext, ctx);
1558 }).catch(function (reason) {
1559 unifiedExec({ // Anonymous command to simply throw the exception
1560 op: function(){
1561 throw reason;
1562 }
1563 }, ctx);
1564 });
1565 return;
1566 } else if (next === HALT) {
1567 if (ctx.meta.finallyHandler && !ctx.meta.handlingFinally) {
1568 ctx.meta.handlingFinally = true;
1569 command = ctx.meta.finallyHandler;
1570 } else {
1571 if (ctx.meta.onHalt) {
1572 ctx.meta.onHalt();
1573 }
1574 if (ctx.meta.currentException) {
1575 if (ctx.meta.reject) {
1576 ctx.meta.reject(ctx.meta.currentException);
1577 return;
1578 } else {
1579 throw ctx.meta.currentException;
1580 }
1581 } else {
1582 return;
1583 }
1584 }
1585 } else {
1586 command = next; // move to the next command
1587 }
1588 }
1589 }
1590
1591 /**
1592 * @param {*} parseElement
1593 * @param {Context} ctx
1594 * @returns {*}
1595 */
1596 function unifiedEval(parseElement, ctx) {
1597 /** @type any[] */
1598 var args = [ctx];
1599 var async = false;
1600 var wrappedAsyncs = false;
1601
1602 if (parseElement.args) {
1603 for (var i = 0; i < parseElement.args.length; i++) {
1604 var argument = parseElement.args[i];
1605 if (argument == null) {
1606 args.push(null);
1607 } else if (Array.isArray(argument)) {
1608 var arr = [];
1609 for (var j = 0; j < argument.length; j++) {
1610 var element = argument[j];
1611 var value = element ? element.evaluate(ctx) : null; // OK
1612 if (value) {
1613 if (value.then) {
1614 async = true;
1615 } else if (value.asyncWrapper) {
1616 wrappedAsyncs = true;
1617 }
1618 }
1619 arr.push(value);
1620 }
1621 args.push(arr);
1622 } else if (argument.evaluate) {
1623 var value = argument.evaluate(ctx); // OK
1624 if (value) {
1625 if (value.then) {
1626 async = true;
1627 } else if (value.asyncWrapper) {
1628 wrappedAsyncs = true;
1629 }
1630 }
1631 args.push(value);
1632 } else {
1633 args.push(argument);
1634 }
1635 }
1636 }
1637 if (async) {
1638 return new Promise(function (resolve, reject) {
1639 args = wrapArrays(args);
1640 Promise.all(args)
1641 .then(function (values) {
1642 if (wrappedAsyncs) {
1643 unwrapAsyncs(values);
1644 }
1645 try {
1646 var apply = parseElement.op.apply(parseElement, values);
1647 resolve(apply);
1648 } catch (e) {
1649 reject(e);
1650 }
1651 })
1652 .catch(function (reason) {
1653 reject(reason);
1654 });
1655 });
1656 } else {
1657 if (wrappedAsyncs) {
1658 unwrapAsyncs(args);
1659 }
1660 return parseElement.op.apply(parseElement, args);
1661 }
1662 }
1663
1664 let _scriptAttrs = null;
1665
1666 /**
1667 * getAttributes returns the attribute name(s) to use when
1668 * locating hyperscript scripts in a DOM element. If no value
1669 * has been configured, it defaults to _hyperscript.config.attributes
1670 * @returns string[]
1671 */
1672 function getScriptAttributes() {
1673 if (_scriptAttrs == null) {
1674 _scriptAttrs = _hyperscript.config.attributes.replace(/ /g, "").split(",");
1675 }
1676 return _scriptAttrs;
1677 }
1678
1679 /**
1680 * @param {Element} elt
1681 * @returns {string | null}
1682 */
1683 function getScript(elt) {
1684 for (var i = 0; i < getScriptAttributes().length; i++) {
1685 var scriptAttribute = getScriptAttributes()[i];
1686 if (elt.hasAttribute && elt.hasAttribute(scriptAttribute)) {
1687 return elt.getAttribute(scriptAttribute);
1688 }
1689 }
1690 if (elt instanceof HTMLScriptElement && elt.type === "text/hyperscript") {
1691 return elt.innerText;
1692 }
1693 return null;
1694 }
1695
1696 var hyperscriptFeaturesMap = new WeakMap
1697
1698 /**
1699 * @param {*} elt
1700 * @returns {Object}
1701 */
1702 function getHyperscriptFeatures(elt) {
1703 var hyperscriptFeatures = hyperscriptFeaturesMap.get(elt);
1704 if (typeof hyperscriptFeatures === 'undefined') {
1705 hyperscriptFeaturesMap.set(elt, hyperscriptFeatures = {});
1706 }
1707 return hyperscriptFeatures;
1708 }
1709
1710 /**
1711 * @param {Object} owner
1712 * @param {Context} ctx
1713 */
1714 function addFeatures(owner, ctx) {
1715 if (owner) {
1716 mergeObjects(ctx, getHyperscriptFeatures(owner));
1717 addFeatures(owner.parentElement, ctx);
1718 }
1719 }
1720
1721 /**
1722 * @param {*} owner
1723 * @param {*} feature
1724 * @param {*} hyperscriptTarget
1725 * @param {*} event
1726 * @returns {Context}
1727 */
1728 function makeContext(owner, feature, hyperscriptTarget, event) {
1729 /** @type {Context} */
1730 var ctx = {
1731 meta: {
1732 parser: _parser,
1733 lexer: _lexer,
1734 runtime: _runtime,
1735 owner: owner,
1736 feature: feature,
1737 iterators: {},
1738 },
1739 me: hyperscriptTarget,
1740 event: event,
1741 target: event ? event.target : null,
1742 detail: event ? event.detail : null,
1743 sender: event ? event.detail ? event.detail.sender : null : null,
1744 body: "document" in globalScope ? document.body : null,
1745 };
1746 ctx.meta.ctx = ctx;
1747 addFeatures(owner, ctx);
1748 return ctx;
1749 }
1750
1751 /**
1752 * @returns string
1753 */
1754 function getScriptSelector() {
1755 return getScriptAttributes()
1756 .map(function (attribute) {
1757 return "[" + attribute + "]";
1758 })
1759 .join(", ");
1760 }
1761
1762 /**
1763 * @param {any} value
1764 * @param {string} type
1765 * @returns {any}
1766 */
1767 function convertValue(value, type) {
1768 var dynamicResolvers = CONVERSIONS.dynamicResolvers;
1769 for (var i = 0; i < dynamicResolvers.length; i++) {
1770 var dynamicResolver = dynamicResolvers[i];
1771 var converted = dynamicResolver(type, value);
1772 if (converted !== undefined) {
1773 return converted;
1774 }
1775 }
1776
1777 if (value == null) {
1778 return null;
1779 }
1780 var converter = CONVERSIONS[type];
1781 if (converter) {
1782 return converter(value);
1783 }
1784
1785 throw "Unknown conversion : " + type;
1786 }
1787
1788 // TODO: There do not seem to be any references to this function.
1789 // Is it still in use, or can it be removed?
1790 function isType(o, type) {
1791 return Object.prototype.toString.call(o) === "[object " + type + "]";
1792 }
1793
1794 /**
1795 * @param {string} src
1796 * @returns {GrammarElement}
1797 */
1798 function parse(src) {
1799 var tokens = _lexer.tokenize(src);
1800 if (_parser.commandStart(tokens.currentToken())) {
1801 var commandList = _parser.requireElement("commandList", tokens);
1802 if (tokens.hasMore()) _parser.raiseParseError(tokens);
1803 _parser.ensureTerminated(commandList);
1804 return commandList;
1805 } else if (_parser.featureStart(tokens.currentToken())) {
1806 var hyperscript = _parser.requireElement("hyperscript", tokens);
1807 if (tokens.hasMore()) _parser.raiseParseError(tokens);
1808 return hyperscript;
1809 } else {
1810 var expression = _parser.requireElement("expression", tokens);
1811 if (tokens.hasMore()) _parser.raiseParseError(tokens);
1812 return expression;
1813 }
1814 }
1815
1816 /**
1817 *
1818 * @param {GrammarElement} elt
1819 * @param {Context} ctx
1820 * @returns {any}
1821 */
1822 function evaluateNoPromise(elt, ctx) {
1823 let result = elt.evaluate(ctx);
1824 if (result.next) {
1825 throw new Error(elt.sourceFor() + " returned a Promise in a context that they are not allowed.");
1826 }
1827 return result;
1828 }
1829
1830 /**
1831 * @param {string} src
1832 * @param {Context} [ctx]
1833 * @param {Object} [args]
1834 * @returns {any}
1835 */
1836 function evaluate(src, ctx, args) {
1837 class HyperscriptModule extends EventTarget {
1838 constructor(mod) {
1839 super();
1840 this.module = mod;
1841 }
1842 toString() {
1843 return this.module.id;
1844 }
1845 }
1846
1847 var body = 'document' in globalScope
1848 ? globalScope.document.body
1849 : new HyperscriptModule(args && args.module);
1850 ctx = mergeObjects(makeContext(body, null, body, null), ctx || {});
1851 var element = parse(src);
1852 if (element.execute) {
1853 element.execute(ctx);
1854 return ctx.result;
1855 } else if (element.apply) {
1856 element.apply(body, body, args);
1857 return getHyperscriptFeatures(body);
1858 } else {
1859 return element.evaluate(ctx);
1860 }
1861
1862 function makeModule() {
1863 return {}
1864 }
1865 }
1866
1867 /**
1868 * @param {HTMLElement} elt
1869 */
1870 function processNode(elt) {
1871 var selector = _runtime.getScriptSelector();
1872 if (matchesSelector(elt, selector)) {
1873 initElement(elt, elt);
1874 }
1875 if (elt instanceof HTMLScriptElement && elt.type === "text/hyperscript") {
1876 initElement(elt, document.body);
1877 }
1878 if (elt.querySelectorAll) {
1879 forEach(elt.querySelectorAll(selector + ", [type='text/hyperscript']"), function (elt) {
1880 initElement(elt, elt instanceof HTMLScriptElement && elt.type === "text/hyperscript" ? document.body : elt);
1881 });
1882 }
1883 }
1884
1885 /**
1886 * @param {Element} elt
1887 * @param {Element} [target]
1888 */
1889 function initElement(elt, target) {
1890 if (elt.closest && elt.closest(_hyperscript.config.disableSelector)) {
1891 return;
1892 }
1893 var internalData = getInternalData(elt);
1894 if (!internalData.initialized) {
1895 var src = getScript(elt);
1896 if (src) {
1897 try {
1898 internalData.initialized = true;
1899 internalData.script = src;
1900 var tokens = _lexer.tokenize(src);
1901 var hyperScript = _parser.parseHyperScript(tokens);
1902 if (!hyperScript) return;
1903 hyperScript.apply(target || elt, elt);
1904 setTimeout(function () {
1905 triggerEvent(target || elt, "load", {
1906 hyperscript: true,
1907 });
1908 }, 1);
1909 } catch (e) {
1910 _runtime.triggerEvent(elt, "exception", {
1911 error: e,
1912 });
1913 console.error(
1914 "hyperscript errors were found on the following element:",
1915 elt,
1916 "\n\n",
1917 e.message,
1918 e.stack
1919 );
1920 }
1921 }
1922 }
1923 }
1924
1925 var internalDataMap = new WeakMap
1926
1927 /**
1928 * @param {Element} elt
1929 * @returns {Object}
1930 */
1931 function getInternalData(elt) {
1932 var internalData = internalDataMap.get(elt);
1933 if (typeof internalData === 'undefined') {
1934 internalDataMap.set(elt, internalData = {});
1935 }
1936 return internalData;
1937 }
1938
1939 /**
1940 * @param {any} value
1941 * @param {string} typeString
1942 * @param {boolean} [nullOk]
1943 * @returns {boolean}
1944 */
1945 function typeCheck(value, typeString, nullOk) {
1946 if (value == null && nullOk) {
1947 return true;
1948 }
1949 var typeName = Object.prototype.toString.call(value).slice(8, -1);
1950 return typeName === typeString;
1951 }
1952
1953 function getElementScope(context) {
1954 var elt = context.meta && context.meta.owner;
1955 if (elt) {
1956 var internalData = getInternalData(elt);
1957 var scopeName = "elementScope";
1958 if (context.meta.feature && context.meta.feature.behavior) {
1959 scopeName = context.meta.feature.behavior + "Scope";
1960 }
1961 var elementScope = getOrInitObject(internalData, scopeName);
1962 return elementScope;
1963 } else {
1964 return {}; // no element, return empty scope
1965 }
1966 }
1967
1968 /**
1969 * @param {string} str
1970 * @param {Context} context
1971 * @returns {any}
1972 */
1973 function resolveSymbol(str, context, type) {
1974 if (str === "me" || str === "my" || str === "I") {
1975 return context["me"];
1976 }
1977 if (str === "it" || str === "its") {
1978 return context["result"];
1979 }
1980 if (str === "you" || str === "your" || str === "yourself") {
1981 return context["beingTold"];
1982 } else {
1983 if (type === "global") {
1984 return globalScope[str];
1985 } else if (type === "element") {
1986 var elementScope = getElementScope(context);
1987 return elementScope[str];
1988 } else if (type === "local") {
1989 return context[str];
1990 } else {
1991 // meta scope (used for event conditionals)
1992 if (context.meta && context.meta.context) {
1993 var fromMetaContext = context.meta.context[str];
1994 if (typeof fromMetaContext !== "undefined") {
1995 return fromMetaContext;
1996 }
1997 }
1998 // local scope
1999 var fromContext = context[str];
2000 if (typeof fromContext !== "undefined") {
2001 return fromContext;
2002 } else {
2003 // element scope
2004 var elementScope = getElementScope(context);
2005 fromContext = elementScope[str];
2006 if (typeof fromContext !== "undefined") {
2007 return fromContext;
2008 } else {
2009 // global scope
2010 return globalScope[str];
2011 }
2012 }
2013 }
2014 }
2015 }
2016
2017 function setSymbol(str, context, type, value) {
2018 if (type === "global") {
2019 globalScope[str] = value;
2020 } else if (type === "element") {
2021 var elementScope = getElementScope(context);
2022 elementScope[str] = value;
2023 } else if (type === "local") {
2024 context[str] = value;
2025 } else {
2026 // local scope
2027 var fromContext = context[str];
2028 if (typeof fromContext !== "undefined") {
2029 context[str] = value;
2030 } else {
2031 // element scope
2032 var elementScope = getElementScope(context);
2033 fromContext = elementScope[str];
2034 if (typeof fromContext !== "undefined") {
2035 elementScope[str] = value;
2036 } else {
2037 context[str] = value;
2038 }
2039 }
2040 }
2041 }
2042
2043 /**
2044 * @param {GrammarElement} command
2045 * @param {Context} context
2046 * @returns {undefined | GrammarElement}
2047 */
2048 function findNext(command, context) {
2049 if (command) {
2050 if (command.resolveNext) {
2051 return command.resolveNext(context);
2052 } else if (command.next) {
2053 return command.next;
2054 } else {
2055 return findNext(command.parent, context);
2056 }
2057 }
2058 }
2059
2060 /**
2061 * @param {Object<string,any>} root
2062 * @param {string} property
2063 * @param {boolean} attribute
2064 * @returns {any}
2065 */
2066 function flatGet(root, property, getter) {
2067 if (root != null) {
2068 var val = getter(root, property);
2069 if (typeof val !== "undefined") {
2070 return val;
2071 }
2072
2073 if (shouldAutoIterate(root)) {
2074 // flat map
2075 var result = [];
2076 for (var component of root) {
2077 var componentValue = getter(component, property);
2078 if (componentValue) {
2079 result.push(componentValue);
2080 }
2081 }
2082 return result;
2083 }
2084 }
2085 }
2086
2087 function resolveProperty(root, property) {
2088 return flatGet(root, property, (root, property) => root[property] )
2089 }
2090
2091 function resolveAttribute(root, property) {
2092 return flatGet(root, property, (root, property) => root.getAttribute && root.getAttribute(property) )
2093 }
2094
2095 /**
2096 *
2097 * @param {Object<string, any>} root
2098 * @param {string} property
2099 * @returns {string}
2100 */
2101 function resolveStyle(root, property) {
2102 return flatGet(root, property, (root, property) => root.style && root.style[property] )
2103 }
2104
2105 /**
2106 *
2107 * @param {Object<string, any>} root
2108 * @param {string} property
2109 * @returns {string}
2110 */
2111 function resolveComputedStyle(root, property) {
2112 return flatGet(root, property, (root, property) => getComputedStyle(root).getPropertyValue(property) )
2113 }
2114
2115 /**
2116 * @param {Element} elt
2117 * @param {string[]} nameSpace
2118 * @param {string} name
2119 * @param {any} value
2120 */
2121 function assignToNamespace(elt, nameSpace, name, value) {
2122 let root
2123 if (typeof document !== "undefined" && elt === document.body) {
2124 root = globalScope;
2125 } else {
2126 root = getHyperscriptFeatures(elt);
2127 }
2128 while (nameSpace.length > 0) {
2129 var propertyName = nameSpace.shift();
2130 var newRoot = root[propertyName];
2131 if (newRoot == null) {
2132 newRoot = {};
2133 root[propertyName] = newRoot;
2134 }
2135 root = newRoot;
2136 }
2137
2138 root[name] = value;
2139 }
2140
2141 function getHyperTrace(ctx, thrown) {
2142 var trace = [];
2143 var root = ctx;
2144 while (root.meta.caller) {
2145 root = root.meta.caller;
2146 }
2147 if (root.meta.traceMap) {
2148 return root.meta.traceMap.get(thrown, trace);
2149 }
2150 }
2151
2152 function registerHyperTrace(ctx, thrown) {
2153 var trace = [];
2154 var root = null;
2155 while (ctx != null) {
2156 trace.push(ctx);
2157 root = ctx;
2158 ctx = ctx.meta.caller;
2159 }
2160 if (root.meta.traceMap == null) {
2161 root.meta.traceMap = new Map(); // TODO - WeakMap?
2162 }
2163 if (!root.meta.traceMap.get(thrown)) {
2164 var traceEntry = {
2165 trace: trace,
2166 print: function (logger) {
2167 logger = logger || console.error;
2168 logger("hypertrace /// ");
2169 var maxLen = 0;
2170 for (var i = 0; i < trace.length; i++) {
2171 maxLen = Math.max(maxLen, trace[i].meta.feature.displayName.length);
2172 }
2173 for (var i = 0; i < trace.length; i++) {
2174 var traceElt = trace[i];
2175 logger(
2176 " ->",
2177 traceElt.meta.feature.displayName.padEnd(maxLen + 2),
2178 "-",
2179 traceElt.meta.owner
2180 );
2181 }
2182 },
2183 };
2184 root.meta.traceMap.set(thrown, traceEntry);
2185 }
2186 }
2187
2188 /**
2189 * @param {string} str
2190 * @returns {string}
2191 */
2192 function escapeSelector(str) {
2193 return str.replace(/:/g, function (str) {
2194 return "\\" + str;
2195 });
2196 }
2197
2198 /**
2199 * @param {any} value
2200 * @param {*} elt
2201 */
2202 function nullCheck(value, elt) {
2203 if (value == null) {
2204 throw new Error("'" + elt.sourceFor() + "' is null");
2205 }
2206 }
2207
2208 /**
2209 * @param {any} value
2210 * @returns {boolean}
2211 */
2212 function isEmpty(value) {
2213 return value == undefined || value.length === 0;
2214 }
2215
2216 /**
2217 * @param {any} value
2218 * @returns {boolean}
2219 */
2220 function doesExist(value) {
2221 if(value == null){
2222 return false;
2223 }
2224 if (shouldAutoIterate(value)) {
2225 for (const elt of value) {
2226 return true;
2227 }
2228 }
2229 return false;
2230 }
2231
2232 /**
2233 * @param {Node} node
2234 * @returns {Document|ShadowRoot}
2235 */
2236 function getRootNode(node) {
2237 if (node && node instanceof Node) {
2238 var rv = node.getRootNode();
2239 if (rv instanceof Document || rv instanceof ShadowRoot) return rv;
2240 }
2241 return document;
2242 }
2243
2244 /**
2245 *
2246 * @param {Element} elt
2247 * @param {GrammarElement} onFeature
2248 * @returns {EventQueue}
2249 */
2250 function getEventQueueFor(elt, onFeature) {
2251 let internalData = getInternalData(elt);
2252 var eventQueuesForElt = internalData.eventQueues;
2253 if (eventQueuesForElt == null) {
2254 eventQueuesForElt = new Map();
2255 internalData.eventQueues = eventQueuesForElt;
2256 }
2257 var eventQueueForFeature = eventQueuesForElt.get(onFeature);
2258 if (eventQueueForFeature == null) {
2259 eventQueueForFeature = {queue:[], executing:false};
2260 eventQueuesForElt.set(onFeature, eventQueueForFeature);
2261 }
2262 return eventQueueForFeature;
2263 }
2264
2265 /** @type string | null */
2266 // @ts-ignore
2267 var hyperscriptUrl = "document" in globalScope ? import.meta.url : null;
2268
2269 /** @type {RuntimeObject} */
2270 return {
2271 typeCheck,
2272 forEach,
2273 implicitLoop,
2274 shouldAutoIterate,
2275 triggerEvent,
2276 matchesSelector,
2277 getScript,
2278 processNode,
2279 evaluate,
2280 evaluateNoPromise,
2281 parse,
2282 getScriptSelector,
2283 resolveSymbol,
2284 setSymbol,
2285 makeContext,
2286 findNext,
2287 unifiedEval,
2288 convertValue,
2289 unifiedExec,
2290 resolveProperty,
2291 resolveAttribute,
2292 resolveStyle,
2293 resolveComputedStyle,
2294 assignToNamespace,
2295 registerHyperTrace,
2296 getHyperTrace,
2297 getInternalData,
2298 getHyperscriptFeatures,
2299 escapeSelector,
2300 nullCheck,
2301 isEmpty,
2302 doesExist,
2303 getRootNode,
2304 getEventQueueFor,
2305 hyperscriptUrl,
2306 HALT,
2307 };
2308})();
2309
2310//====================================================================
2311// Grammar
2312//====================================================================
2313{
2314 _parser.addLeafExpression("parenthesized", function (parser, _runtime, tokens) {
2315 if (tokens.matchOpToken("(")) {
2316 var follows = tokens.clearFollow();
2317 try {
2318 var expr = parser.requireElement("expression", tokens);
2319 } finally {
2320 tokens.restoreFollow(follows);
2321 }
2322 tokens.requireOpToken(")");
2323 return expr;
2324 }
2325 });
2326
2327 _parser.addLeafExpression("string", function (parser, runtime, tokens) {
2328 var stringToken = tokens.matchTokenType("STRING");
2329 if (!stringToken) return;
2330 var rawValue = stringToken.value;
2331 /** @type {any[]} */
2332 var args;
2333 if (stringToken.template) {
2334 var innerTokens = _lexer.tokenize(rawValue, true);
2335 args = parser.parseStringTemplate(innerTokens);
2336 } else {
2337 args = [];
2338 }
2339 return {
2340 type: "string",
2341 token: stringToken,
2342 args: args,
2343 op: function (context) {
2344 var returnStr = "";
2345 for (var i = 1; i < arguments.length; i++) {
2346 var val = arguments[i];
2347 if (val !== undefined) {
2348 returnStr += val;
2349 }
2350 }
2351 return returnStr;
2352 },
2353 evaluate: function (context) {
2354 if (args.length === 0) {
2355 return rawValue;
2356 } else {
2357 return runtime.unifiedEval(this, context);
2358 }
2359 },
2360 };
2361 });
2362
2363 _parser.addGrammarElement("nakedString", function (parser, runtime, tokens) {
2364 if (tokens.hasMore()) {
2365 var tokenArr = tokens.consumeUntilWhitespace();
2366 tokens.matchTokenType("WHITESPACE");
2367 return {
2368 type: "nakedString",
2369 tokens: tokenArr,
2370 evaluate: function (context) {
2371 return tokenArr
2372 .map(function (t) {
2373 return t.value;
2374 })
2375 .join("");
2376 },
2377 };
2378 }
2379 });
2380
2381 _parser.addLeafExpression("number", function (parser, runtime, tokens) {
2382 var number = tokens.matchTokenType("NUMBER");
2383 if (!number) return;
2384 var numberToken = number;
2385 var value = parseFloat(number.value);
2386 return {
2387 type: "number",
2388 value: value,
2389 numberToken: numberToken,
2390 evaluate: function () {
2391 return value;
2392 },
2393 };
2394 });
2395
2396 _parser.addLeafExpression("idRef", function (parser, runtime, tokens) {
2397 var elementId = tokens.matchTokenType("ID_REF");
2398 if (!elementId) return;
2399 // TODO - unify these two expression types
2400 if (elementId.template) {
2401 var templateValue = elementId.value.substr(2, elementId.value.length - 2);
2402 var innerTokens = _lexer.tokenize(templateValue);
2403 var innerExpression = parser.requireElement("expression", innerTokens);
2404 return {
2405 type: "idRefTemplate",
2406 args: [innerExpression],
2407 op: function (context, arg) {
2408 return runtime.getRootNode(context.me).getElementById(arg);
2409 },
2410 evaluate: function (context) {
2411 return runtime.unifiedEval(this, context);
2412 },
2413 };
2414 } else {
2415 const value = elementId.value.substr(1);
2416 return {
2417 type: "idRef",
2418 css: elementId.value,
2419 value: value,
2420 evaluate: function (context) {
2421 return (
2422 runtime.getRootNode(context.me).getElementById(value)
2423 );
2424 },
2425 };
2426 }
2427 });
2428
2429 _parser.addLeafExpression("classRef", function (parser, runtime, tokens) {
2430 var classRef = tokens.matchTokenType("CLASS_REF");
2431
2432 if (!classRef) return;
2433
2434 // TODO - unify these two expression types
2435 if (classRef.template) {
2436 var templateValue = classRef.value.substr(2, classRef.value.length - 2);
2437 var innerTokens = _lexer.tokenize(templateValue);
2438 var innerExpression = parser.requireElement("expression", innerTokens);
2439 return {
2440 type: "classRefTemplate",
2441 args: [innerExpression],
2442 op: function (context, arg) {
2443 return new ElementCollection("." + arg, context.me, true)
2444 },
2445 evaluate: function (context) {
2446 return runtime.unifiedEval(this, context);
2447 },
2448 };
2449 } else {
2450 const css = classRef.value;
2451 return {
2452 type: "classRef",
2453 css: css,
2454 evaluate: function (context) {
2455 return new ElementCollection(css, context.me, true)
2456 },
2457 };
2458 }
2459 });
2460
2461 class TemplatedQueryElementCollection extends ElementCollection {
2462 constructor(css, relativeToElement, templateParts) {
2463 super(css, relativeToElement);
2464 this.templateParts = templateParts;
2465 this.elements = templateParts.filter(elt => elt instanceof Element);
2466 }
2467
2468 get css() {
2469 let rv = "", i = 0
2470 for (const val of this.templateParts) {
2471 if (val instanceof Element) {
2472 rv += "[data-hs-query-id='" + i++ + "']";
2473 } else rv += val;
2474 }
2475 return rv;
2476 }
2477
2478 [Symbol.iterator]() {
2479 this.elements.forEach((el, i) => el.dataset.hsQueryId = i);
2480 const rv = super[Symbol.iterator]();
2481 this.elements.forEach(el => el.removeAttribute('data-hs-query-id'));
2482 return rv;
2483 }
2484 }
2485
2486 _parser.addLeafExpression("queryRef", function (parser, runtime, tokens) {
2487 var queryStart = tokens.matchOpToken("<");
2488 if (!queryStart) return;
2489 var queryTokens = tokens.consumeUntil("/");
2490 tokens.requireOpToken("/");
2491 tokens.requireOpToken(">");
2492 var queryValue = queryTokens
2493 .map(function (t) {
2494 if (t.type === "STRING") {
2495 return '"' + t.value + '"';
2496 } else {
2497 return t.value;
2498 }
2499 })
2500 .join("");
2501
2502 if (queryValue.indexOf("$") >= 0) {
2503 var template = true;
2504 var innerTokens = _lexer.tokenize(queryValue, true);
2505 var args = parser.parseStringTemplate(innerTokens);
2506 }
2507
2508 return {
2509 type: "queryRef",
2510 css: queryValue,
2511 args: args,
2512 op: function (context, ...args) {
2513 if (template) {
2514 return new TemplatedQueryElementCollection(queryValue, context.me, args)
2515 } else {
2516 return new ElementCollection(queryValue, context.me)
2517 }
2518 },
2519 evaluate: function (context) {
2520 return runtime.unifiedEval(this, context);
2521 },
2522 };
2523 });
2524
2525 _parser.addLeafExpression("attributeRef", function (parser, runtime, tokens) {
2526 var attributeRef = tokens.matchTokenType("ATTRIBUTE_REF");
2527 if (!attributeRef) return;
2528 var outerVal = attributeRef.value;
2529 if (outerVal.indexOf("[") === 0) {
2530 var innerValue = outerVal.substring(2, outerVal.length - 1);
2531 } else {
2532 var innerValue = outerVal.substring(1);
2533 }
2534 var css = "[" + innerValue + "]";
2535 var split = innerValue.split("=");
2536 var name = split[0];
2537 var value = split[1];
2538 if (value) {
2539 // strip quotes
2540 if (value.indexOf('"') === 0) {
2541 value = value.substring(1, value.length - 1);
2542 }
2543 }
2544 return {
2545 type: "attributeRef",
2546 name: name,
2547 css: css,
2548 value: value,
2549 op: function (context) {
2550 var target = context.beingTold || context.me;
2551 if (target) {
2552 return target.getAttribute(name);
2553 }
2554 },
2555 evaluate: function (context) {
2556 return runtime.unifiedEval(this, context);
2557 },
2558 };
2559 });
2560
2561 _parser.addLeafExpression("styleRef", function (parser, runtime, tokens) {
2562 var styleRef = tokens.matchTokenType("STYLE_REF");
2563 if (!styleRef) return;
2564 var styleProp = styleRef.value.substr(1);
2565 if (styleProp.startsWith("computed-")) {
2566 styleProp = styleProp.substr("computed-".length);
2567 return {
2568 type: "computedStyleRef",
2569 name: styleProp,
2570 op: function (context) {
2571 var target = context.beingTold || context.me;
2572 if (target) {
2573 return runtime.resolveComputedStyle(target, styleProp);
2574 }
2575 },
2576 evaluate: function (context) {
2577 return runtime.unifiedEval(this, context);
2578 },
2579 };
2580 } else {
2581 return {
2582 type: "styleRef",
2583 name: styleProp,
2584 op: function (context) {
2585 var target = context.beingTold || context.me;
2586 if (target) {
2587 return runtime.resolveStyle(target, styleProp);
2588 }
2589 },
2590 evaluate: function (context) {
2591 return runtime.unifiedEval(this, context);
2592 },
2593 };
2594 }
2595 });
2596
2597 _parser.addGrammarElement("objectKey", function (parser, runtime, tokens) {
2598 var token;
2599 if ((token = tokens.matchTokenType("STRING"))) {
2600 return {
2601 type: "objectKey",
2602 key: token.value,
2603 evaluate: function () {
2604 return token.value;
2605 },
2606 };
2607 } else if (tokens.matchOpToken("[")) {
2608 var expr = parser.parseElement("expression", tokens);
2609 tokens.requireOpToken("]");
2610 return {
2611 type: "objectKey",
2612 expr: expr,
2613 args: [expr],
2614 op: function (ctx, expr) {
2615 return expr;
2616 },
2617 evaluate: function (context) {
2618 return runtime.unifiedEval(this, context);
2619 },
2620 };
2621 } else {
2622 var key = "";
2623 do {
2624 token = tokens.matchTokenType("IDENTIFIER") || tokens.matchOpToken("-");
2625 if (token) key += token.value;
2626 } while (token);
2627 return {
2628 type: "objectKey",
2629 key: key,
2630 evaluate: function () {
2631 return key;
2632 },
2633 };
2634 }
2635 });
2636
2637 _parser.addLeafExpression("objectLiteral", function (parser, runtime, tokens) {
2638 if (!tokens.matchOpToken("{")) return;
2639 var keyExpressions = [];
2640 var valueExpressions = [];
2641 if (!tokens.matchOpToken("}")) {
2642 do {
2643 var name = parser.requireElement("objectKey", tokens);
2644 tokens.requireOpToken(":");
2645 var value = parser.requireElement("expression", tokens);
2646 valueExpressions.push(value);
2647 keyExpressions.push(name);
2648 } while (tokens.matchOpToken(","));
2649 tokens.requireOpToken("}");
2650 }
2651 return {
2652 type: "objectLiteral",
2653 args: [keyExpressions, valueExpressions],
2654 op: function (context, keys, values) {
2655 var returnVal = {};
2656 for (var i = 0; i < keys.length; i++) {
2657 returnVal[keys[i]] = values[i];
2658 }
2659 return returnVal;
2660 },
2661 evaluate: function (context) {
2662 return runtime.unifiedEval(this, context);
2663 },
2664 };
2665 });
2666
2667 _parser.addGrammarElement("nakedNamedArgumentList", function (parser, runtime, tokens) {
2668 var fields = [];
2669 var valueExpressions = [];
2670 if (tokens.currentToken().type === "IDENTIFIER") {
2671 do {
2672 var name = tokens.requireTokenType("IDENTIFIER");
2673 tokens.requireOpToken(":");
2674 var value = parser.requireElement("expression", tokens);
2675 valueExpressions.push(value);
2676 fields.push({ name: name, value: value });
2677 } while (tokens.matchOpToken(","));
2678 }
2679 return {
2680 type: "namedArgumentList",
2681 fields: fields,
2682 args: [valueExpressions],
2683 op: function (context, values) {
2684 var returnVal = { _namedArgList_: true };
2685 for (var i = 0; i < values.length; i++) {
2686 var field = fields[i];
2687 returnVal[field.name.value] = values[i];
2688 }
2689 return returnVal;
2690 },
2691 evaluate: function (context) {
2692 return runtime.unifiedEval(this, context);
2693 },
2694 };
2695 });
2696
2697 _parser.addGrammarElement("namedArgumentList", function (parser, runtime, tokens) {
2698 if (!tokens.matchOpToken("(")) return;
2699 var elt = parser.requireElement("nakedNamedArgumentList", tokens);
2700 tokens.requireOpToken(")");
2701 return elt;
2702 });
2703
2704 _parser.addGrammarElement("symbol", function (parser, runtime, tokens) {
2705 /** @scope {SymbolScope} */
2706 var scope = "default";
2707 if (tokens.matchToken("global")) {
2708 scope = "global";
2709 } else if (tokens.matchToken("element") || tokens.matchToken("module")) {
2710 scope = "element";
2711 // optional possessive
2712 if (tokens.matchOpToken("'")) {
2713 tokens.requireToken("s");
2714 }
2715 } else if (tokens.matchToken("local")) {
2716 scope = "local";
2717 }
2718
2719 // TODO better look ahead here
2720 let eltPrefix = tokens.matchOpToken(":");
2721 let identifier = tokens.matchTokenType("IDENTIFIER");
2722 if (identifier) {
2723 var name = identifier.value;
2724 if (eltPrefix) {
2725 name = ":" + name;
2726 }
2727 if (scope === "default") {
2728 if (name.indexOf("$") === 0) {
2729 scope = "global";
2730 }
2731 if (name.indexOf(":") === 0) {
2732 scope = "element";
2733 }
2734 }
2735 return {
2736 type: "symbol",
2737 token: identifier,
2738 scope: scope,
2739 name: name,
2740 evaluate: function (context) {
2741 return runtime.resolveSymbol(name, context, scope);
2742 },
2743 };
2744 }
2745 });
2746
2747 _parser.addGrammarElement("implicitMeTarget", function (parser, runtime, tokens) {
2748 return {
2749 type: "implicitMeTarget",
2750 evaluate: function (context) {
2751 return context.beingTold || context.me;
2752 },
2753 };
2754 });
2755
2756 _parser.addLeafExpression("boolean", function (parser, runtime, tokens) {
2757 var booleanLiteral = tokens.matchToken("true") || tokens.matchToken("false");
2758 if (!booleanLiteral) return;
2759 const value = booleanLiteral.value === "true";
2760 return {
2761 type: "boolean",
2762 evaluate: function (context) {
2763 return value;
2764 },
2765 };
2766 });
2767
2768 _parser.addLeafExpression("null", function (parser, runtime, tokens) {
2769 if (tokens.matchToken("null")) {
2770 return {
2771 type: "null",
2772 evaluate: function (context) {
2773 return null;
2774 },
2775 };
2776 }
2777 });
2778
2779 _parser.addLeafExpression("arrayLiteral", function (parser, runtime, tokens) {
2780 if (!tokens.matchOpToken("[")) return;
2781 var values = [];
2782 if (!tokens.matchOpToken("]")) {
2783 do {
2784 var expr = parser.requireElement("expression", tokens);
2785 values.push(expr);
2786 } while (tokens.matchOpToken(","));
2787 tokens.requireOpToken("]");
2788 }
2789 return {
2790 type: "arrayLiteral",
2791 values: values,
2792 args: [values],
2793 op: function (context, values) {
2794 return values;
2795 },
2796 evaluate: function (context) {
2797 return runtime.unifiedEval(this, context);
2798 },
2799 };
2800 });
2801
2802 _parser.addLeafExpression("blockLiteral", function (parser, runtime, tokens) {
2803 if (!tokens.matchOpToken("\\")) return;
2804 var args = [];
2805 var arg1 = tokens.matchTokenType("IDENTIFIER");
2806 if (arg1) {
2807 args.push(arg1);
2808 while (tokens.matchOpToken(",")) {
2809 args.push(tokens.requireTokenType("IDENTIFIER"));
2810 }
2811 }
2812 // TODO compound op token
2813 tokens.requireOpToken("-");
2814 tokens.requireOpToken(">");
2815 var expr = parser.requireElement("expression", tokens);
2816 return {
2817 type: "blockLiteral",
2818 args: args,
2819 expr: expr,
2820 evaluate: function (ctx) {
2821 var returnFunc = function () {
2822 //TODO - push scope
2823 for (var i = 0; i < args.length; i++) {
2824 ctx[args[i].value] = arguments[i];
2825 }
2826 return expr.evaluate(ctx); //OK
2827 };
2828 return returnFunc;
2829 },
2830 };
2831 });
2832
2833 _parser.addIndirectExpression("propertyAccess", function (parser, runtime, tokens, root) {
2834 if (!tokens.matchOpToken(".")) return;
2835 var prop = tokens.requireTokenType("IDENTIFIER");
2836 var propertyAccess = {
2837 type: "propertyAccess",
2838 root: root,
2839 prop: prop,
2840 args: [root],
2841 op: function (_context, rootVal) {
2842 var value = runtime.resolveProperty(rootVal, prop.value);
2843 return value;
2844 },
2845 evaluate: function (context) {
2846 return runtime.unifiedEval(this, context);
2847 },
2848 };
2849 return parser.parseElement("indirectExpression", tokens, propertyAccess);
2850 });
2851
2852 _parser.addIndirectExpression("of", function (parser, runtime, tokens, root) {
2853 if (!tokens.matchToken("of")) return;
2854 var newRoot = parser.requireElement("unaryExpression", tokens);
2855 // find the urroot
2856 var childOfUrRoot = null;
2857 var urRoot = root;
2858 while (urRoot.root) {
2859 childOfUrRoot = urRoot;
2860 urRoot = urRoot.root;
2861 }
2862 if (urRoot.type !== "symbol" && urRoot.type !== "attributeRef" && urRoot.type !== "styleRef" && urRoot.type !== "computedStyleRef") {
2863 parser.raiseParseError(tokens, "Cannot take a property of a non-symbol: " + urRoot.type);
2864 }
2865 var attribute = urRoot.type === "attributeRef";
2866 var style = urRoot.type === "styleRef" || urRoot.type === "computedStyleRef";
2867 if (attribute || style) {
2868 var attributeElt = urRoot
2869 }
2870 var prop = urRoot.name;
2871
2872 var propertyAccess = {
2873 type: "ofExpression",
2874 prop: urRoot.token,
2875 root: newRoot,
2876 attribute: attributeElt,
2877 expression: root,
2878 args: [newRoot],
2879 op: function (context, rootVal) {
2880 if (attribute) {
2881 return runtime.resolveAttribute(rootVal, prop);
2882 } else if (style) {
2883 if (urRoot.type === "computedStyleRef") {
2884 return runtime.resolveComputedStyle(rootVal, prop);
2885 } else {
2886 return runtime.resolveStyle(rootVal, prop);
2887 }
2888 } else {
2889 return runtime.resolveProperty(rootVal, prop);
2890 }
2891 },
2892 evaluate: function (context) {
2893 return runtime.unifiedEval(this, context);
2894 },
2895 };
2896
2897 if (urRoot.type === "attributeRef") {
2898 propertyAccess.attribute = urRoot;
2899 }
2900 if (childOfUrRoot) {
2901 childOfUrRoot.root = propertyAccess;
2902 childOfUrRoot.args = [propertyAccess];
2903 } else {
2904 root = propertyAccess;
2905 }
2906
2907 return parser.parseElement("indirectExpression", tokens, root);
2908 });
2909
2910 _parser.addIndirectExpression("possessive", function (parser, runtime, tokens, root) {
2911 if (parser.possessivesDisabled) {
2912 return;
2913 }
2914 var apostrophe = tokens.matchOpToken("'");
2915 if (
2916 apostrophe ||
2917 (root.type === "symbol" &&
2918 (root.name === "my" || root.name === "its" || root.name === "your") &&
2919 (tokens.currentToken().type === "IDENTIFIER" || tokens.currentToken().type === "ATTRIBUTE_REF" || tokens.currentToken().type === "STYLE_REF"))
2920 ) {
2921 if (apostrophe) {
2922 tokens.requireToken("s");
2923 }
2924 var attribute = parser.parseElement("attributeRef", tokens);
2925 if (attribute == null) {
2926 var style = parser.parseElement("styleRef", tokens);
2927 if (style == null) {
2928 var prop = tokens.requireTokenType("IDENTIFIER");
2929 }
2930 }
2931 var propertyAccess = {
2932 type: "possessive",
2933 root: root,
2934 attribute: attribute || style,
2935 prop: prop,
2936 args: [root],
2937 op: function (context, rootVal) {
2938 if (attribute) {
2939 // @ts-ignore
2940 var value = runtime.resolveAttribute(rootVal, attribute.name);
2941 } else if (style) {
2942 // @ts-ignore
2943 if (style.type === 'computedStyleRef') {
2944 var value = runtime.resolveComputedStyle(rootVal, style.name);
2945 } else {
2946 var value = runtime.resolveStyle(rootVal, style.name);
2947 }
2948 } else {
2949 var value = runtime.resolveProperty(rootVal, prop.value);
2950 }
2951 return value;
2952 },
2953 evaluate: function (context) {
2954 return runtime.unifiedEval(this, context);
2955 },
2956 };
2957 return parser.parseElement("indirectExpression", tokens, propertyAccess);
2958 }
2959 });
2960
2961 _parser.addIndirectExpression("inExpression", function (parser, runtime, tokens, root) {
2962 if (!tokens.matchToken("in")) return;
2963 var target = parser.requireElement("unaryExpression", tokens);
2964 var propertyAccess = {
2965 type: "inExpression",
2966 root: root,
2967 args: [root, target],
2968 op: function (context, rootVal, target) {
2969 var returnArr = [];
2970 if (rootVal.css) {
2971 runtime.implicitLoop(target, function (targetElt) {
2972 var results = targetElt.querySelectorAll(rootVal.css);
2973 for (var i = 0; i < results.length; i++) {
2974 returnArr.push(results[i]);
2975 }
2976 });
2977 } else if (rootVal instanceof Element) {
2978 var within = false;
2979 runtime.implicitLoop(target, function (targetElt) {
2980 if (targetElt.contains(rootVal)) {
2981 within = true;
2982 }
2983 });
2984 if(within) {
2985 return rootVal;
2986 }
2987 } else {
2988 runtime.implicitLoop(rootVal, function (rootElt) {
2989 runtime.implicitLoop(target, function (targetElt) {
2990 if (rootElt === targetElt) {
2991 returnArr.push(rootElt);
2992 }
2993 });
2994 });
2995 }
2996 return returnArr;
2997 },
2998 evaluate: function (context) {
2999 return runtime.unifiedEval(this, context);
3000 },
3001 };
3002 return parser.parseElement("indirectExpression", tokens, propertyAccess);
3003 });
3004
3005 _parser.addIndirectExpression("asExpression", function (parser, runtime, tokens, root) {
3006 if (!tokens.matchToken("as")) return;
3007 tokens.matchToken("a") || tokens.matchToken("an");
3008 var conversion = parser.requireElement("dotOrColonPath", tokens).evaluate(); // OK No promise
3009 var propertyAccess = {
3010 type: "asExpression",
3011 root: root,
3012 args: [root],
3013 op: function (context, rootVal) {
3014 return runtime.convertValue(rootVal, conversion);
3015 },
3016 evaluate: function (context) {
3017 return runtime.unifiedEval(this, context);
3018 },
3019 };
3020 return parser.parseElement("indirectExpression", tokens, propertyAccess);
3021 });
3022
3023 _parser.addIndirectExpression("functionCall", function (parser, runtime, tokens, root) {
3024 if (!tokens.matchOpToken("(")) return;
3025 var args = [];
3026 if (!tokens.matchOpToken(")")) {
3027 do {
3028 args.push(parser.requireElement("expression", tokens));
3029 } while (tokens.matchOpToken(","));
3030 tokens.requireOpToken(")");
3031 }
3032
3033 if (root.root) {
3034 var functionCall = {
3035 type: "functionCall",
3036 root: root,
3037 argExressions: args,
3038 args: [root.root, args],
3039 op: function (context, rootRoot, args) {
3040 runtime.nullCheck(rootRoot, root.root);
3041 var func = rootRoot[root.prop.value];
3042 runtime.nullCheck(func, root);
3043 if (func.hyperfunc) {
3044 args.push(context);
3045 }
3046 return func.apply(rootRoot, args);
3047 },
3048 evaluate: function (context) {
3049 return runtime.unifiedEval(this, context);
3050 },
3051 };
3052 } else {
3053 var functionCall = {
3054 type: "functionCall",
3055 root: root,
3056 argExressions: args,
3057 args: [root, args],
3058 op: function (context, func, argVals) {
3059 runtime.nullCheck(func, root);
3060 if (func.hyperfunc) {
3061 argVals.push(context);
3062 }
3063 var apply = func.apply(null, argVals);
3064 return apply;
3065 },
3066 evaluate: function (context) {
3067 return runtime.unifiedEval(this, context);
3068 },
3069 };
3070 }
3071 return parser.parseElement("indirectExpression", tokens, functionCall);
3072 });
3073
3074 _parser.addIndirectExpression("attributeRefAccess", function (parser, runtime, tokens, root) {
3075 var attribute = parser.parseElement("attributeRef", tokens);
3076 if (!attribute) return;
3077 var attributeAccess = {
3078 type: "attributeRefAccess",
3079 root: root,
3080 attribute: attribute,
3081 args: [root],
3082 op: function (_ctx, rootVal) {
3083 // @ts-ignore
3084 var value = runtime.resolveAttribute(rootVal, attribute.name);
3085 return value;
3086 },
3087 evaluate: function (context) {
3088 return _runtime.unifiedEval(this, context);
3089 },
3090 };
3091 return attributeAccess;
3092 });
3093
3094 _parser.addIndirectExpression("arrayIndex", function (parser, runtime, tokens, root) {
3095 if (!tokens.matchOpToken("[")) return;
3096 var andBefore = false;
3097 var andAfter = false;
3098 var firstIndex = null;
3099 var secondIndex = null;
3100
3101 if (tokens.matchOpToken("..")) {
3102 andBefore = true;
3103 firstIndex = parser.requireElement("expression", tokens);
3104 } else {
3105 firstIndex = parser.requireElement("expression", tokens);
3106
3107 if (tokens.matchOpToken("..")) {
3108 andAfter = true;
3109 var current = tokens.currentToken();
3110 if (current.type !== "R_BRACKET") {
3111 secondIndex = parser.parseElement("expression", tokens);
3112 }
3113 }
3114 }
3115 tokens.requireOpToken("]");
3116
3117 var arrayIndex = {
3118 type: "arrayIndex",
3119 root: root,
3120 prop: firstIndex,
3121 firstIndex: firstIndex,
3122 secondIndex: secondIndex,
3123 args: [root, firstIndex, secondIndex],
3124 op: function (_ctx, root, firstIndex, secondIndex) {
3125 if (root == null) {
3126 return null;
3127 }
3128 if (andBefore) {
3129 if (firstIndex < 0) {
3130 firstIndex = root.length + firstIndex;
3131 }
3132 return root.slice(0, firstIndex + 1); // returns all items from beginning to firstIndex (inclusive)
3133 } else if (andAfter) {
3134 if (secondIndex != null) {
3135 if (secondIndex < 0) {
3136 secondIndex = root.length + secondIndex;
3137 }
3138 return root.slice(firstIndex, secondIndex + 1); // returns all items from firstIndex to secondIndex (inclusive)
3139 } else {
3140 return root.slice(firstIndex); // returns from firstIndex to end of array
3141 }
3142 } else {
3143 return root[firstIndex];
3144 }
3145 },
3146 evaluate: function (context) {
3147 return _runtime.unifiedEval(this, context);
3148 },
3149 };
3150
3151 return _parser.parseElement("indirectExpression", tokens, arrayIndex);
3152 });
3153
3154 // taken from https://drafts.csswg.org/css-values-4/#relative-length
3155 // and https://drafts.csswg.org/css-values-4/#absolute-length
3156 // (NB: we do not support `in` dues to conflicts w/ the hyperscript grammar)
3157 var STRING_POSTFIXES = [
3158 'em', 'ex', 'cap', 'ch', 'ic', 'rem', 'lh', 'rlh', 'vw', 'vh', 'vi', 'vb', 'vmin', 'vmax',
3159 'cm', 'mm', 'Q', 'pc', 'pt', 'px'
3160 ];
3161 _parser.addGrammarElement("postfixExpression", function (parser, runtime, tokens) {
3162 var root = parser.parseElement("primaryExpression", tokens);
3163
3164 let stringPosfix = tokens.matchAnyToken.apply(tokens, STRING_POSTFIXES) || tokens.matchOpToken("%");
3165 if (stringPosfix) {
3166 return {
3167 type: "stringPostfix",
3168 postfix: stringPosfix.value,
3169 args: [root],
3170 op: function (context, val) {
3171 return "" + val + stringPosfix.value;
3172 },
3173 evaluate: function (context) {
3174 return runtime.unifiedEval(this, context);
3175 },
3176 };
3177 }
3178
3179 var timeFactor = null;
3180 if (tokens.matchToken("s") || tokens.matchToken("seconds")) {
3181 timeFactor = 1000;
3182 } else if (tokens.matchToken("ms") || tokens.matchToken("milliseconds")) {
3183 timeFactor = 1;
3184 }
3185 if (timeFactor) {
3186 return {
3187 type: "timeExpression",
3188 time: root,
3189 factor: timeFactor,
3190 args: [root],
3191 op: function (_context, val) {
3192 return val * timeFactor;
3193 },
3194 evaluate: function (context) {
3195 return runtime.unifiedEval(this, context);
3196 },
3197 };
3198 }
3199
3200 if (tokens.matchOpToken(":")) {
3201 var typeName = tokens.requireTokenType("IDENTIFIER");
3202 var nullOk = !tokens.matchOpToken("!");
3203 return {
3204 type: "typeCheck",
3205 typeName: typeName,
3206 nullOk: nullOk,
3207 args: [root],
3208 op: function (context, val) {
3209 var passed = runtime.typeCheck(val, typeName.value, nullOk);
3210 if (passed) {
3211 return val;
3212 } else {
3213 throw new Error("Typecheck failed! Expected: " + typeName.value);
3214 }
3215 },
3216 evaluate: function (context) {
3217 return runtime.unifiedEval(this, context);
3218 },
3219 };
3220 } else {
3221 return root;
3222 }
3223 });
3224
3225 _parser.addGrammarElement("logicalNot", function (parser, runtime, tokens) {
3226 if (!tokens.matchToken("not")) return;
3227 var root = parser.requireElement("unaryExpression", tokens);
3228 return {
3229 type: "logicalNot",
3230 root: root,
3231 args: [root],
3232 op: function (context, val) {
3233 return !val;
3234 },
3235 evaluate: function (context) {
3236 return runtime.unifiedEval(this, context);
3237 },
3238 };
3239 });
3240
3241 _parser.addGrammarElement("noExpression", function (parser, runtime, tokens) {
3242 if (!tokens.matchToken("no")) return;
3243 var root = parser.requireElement("unaryExpression", tokens);
3244 return {
3245 type: "noExpression",
3246 root: root,
3247 args: [root],
3248 op: function (_context, val) {
3249 return runtime.isEmpty(val);
3250 },
3251 evaluate: function (context) {
3252 return runtime.unifiedEval(this, context);
3253 },
3254 };
3255 });
3256
3257 _parser.addLeafExpression("some", function (parser, runtime, tokens) {
3258 if (!tokens.matchToken("some")) return;
3259 var root = parser.requireElement("expression", tokens);
3260 return {
3261 type: "noExpression",
3262 root: root,
3263 args: [root],
3264 op: function (_context, val) {
3265 return !runtime.isEmpty(val);
3266 },
3267 evaluate(context) {
3268 return runtime.unifiedEval(this, context);
3269 },
3270 };
3271 });
3272
3273 _parser.addGrammarElement("negativeNumber", function (parser, runtime, tokens) {
3274 if (!tokens.matchOpToken("-")) return;
3275 var root = parser.requireElement("unaryExpression", tokens);
3276 return {
3277 type: "negativeNumber",
3278 root: root,
3279 args: [root],
3280 op: function (context, value) {
3281 return -1 * value;
3282 },
3283 evaluate: function (context) {
3284 return runtime.unifiedEval(this, context);
3285 },
3286 };
3287 });
3288
3289 _parser.addGrammarElement("unaryExpression", function (parser, runtime, tokens) {
3290 tokens.matchToken("the"); // optional "the"
3291 return parser.parseAnyOf(
3292 ["beepExpression", "logicalNot", "relativePositionalExpression", "positionalExpression", "noExpression", "negativeNumber", "postfixExpression"],
3293 tokens
3294 );
3295 });
3296
3297 _parser.addGrammarElement("beepExpression", function (parser, runtime, tokens) {
3298 if (!tokens.matchToken("beep!")) return;
3299 var expression = parser.parseElement("unaryExpression", tokens);
3300 if (expression) {
3301 expression.booped = true;
3302 var originalEvaluate = expression.evaluate;
3303 expression.evaluate = function(ctx){
3304 let value = originalEvaluate.apply(expression, arguments);
3305 let element = ctx.me;
3306 if (runtime.triggerEvent(element, "hyperscript:beep", {element, expression, value})) {
3307 var typeName;
3308 if (value) {
3309 if (value instanceof ElementCollection){
3310 typeName = "ElementCollection";
3311 } else if (value.constructor) {
3312 typeName = value.constructor.name;
3313 } else {
3314 typeName = "unknown";
3315 }
3316 } else {
3317 typeName = "object (null)"
3318 }
3319 var logValue = value;
3320 if (typeName === "String") {
3321 logValue = '"' + logValue + '"';
3322 } else if (value instanceof ElementCollection) {
3323 logValue = Array.from(value);
3324 }
3325 console.log("///_ BEEP! The expression (" + expression.sourceFor().substr(6) + ") evaluates to:", logValue, "of type " + typeName);
3326 }
3327 return value;
3328 }
3329 return expression;
3330 }
3331 });
3332
3333 var scanForwardQuery = function(start, root, match, wrap) {
3334 var results = root.querySelectorAll(match);
3335 for (var i = 0; i < results.length; i++) {
3336 var elt = results[i];
3337 if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_PRECEDING) {
3338 return elt;
3339 }
3340 }
3341 if (wrap) {
3342 return results[0];
3343 }
3344 }
3345
3346 var scanBackwardsQuery = function(start, root, match, wrap) {
3347 var results = root.querySelectorAll(match);
3348 for (var i = results.length - 1; i >= 0; i--) {
3349 var elt = results[i];
3350 if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_FOLLOWING) {
3351 return elt;
3352 }
3353 }
3354 if (wrap) {
3355 return results[results.length - 1];
3356 }
3357 }
3358
3359 var scanForwardArray = function(start, array, match, wrap) {
3360 var matches = [];
3361 _runtime.forEach(array, function(elt){
3362 if (elt.matches(match) || elt === start) {
3363 matches.push(elt);
3364 }
3365 })
3366 for (var i = 0; i < matches.length - 1; i++) {
3367 var elt = matches[i];
3368 if (elt === start) {
3369 return matches[i + 1];
3370 }
3371 }
3372 if (wrap) {
3373 var first = matches[0];
3374 if (first && first.matches(match)) {
3375 return first;
3376 }
3377 }
3378 }
3379
3380 var scanBackwardsArray = function(start, array, match, wrap) {
3381 return scanForwardArray(start, Array.from(array).reverse(), match, wrap);
3382 }
3383
3384 _parser.addGrammarElement("relativePositionalExpression", function (parser, runtime, tokens) {
3385 var op = tokens.matchAnyToken("next", "previous");
3386 if (!op) return;
3387 if (op.value === "next") {
3388 var forwardSearch = true;
3389 }
3390
3391 var thing = parser.parseElement("expression", tokens);
3392
3393 if (tokens.matchToken("from")) {
3394 tokens.pushFollow("in");
3395 try {
3396 var from = parser.requireElement("unaryExpression", tokens);
3397 } finally {
3398 tokens.popFollow();
3399 }
3400 } else {
3401 var from = parser.requireElement("implicitMeTarget", tokens);
3402 }
3403
3404 var inSearch = false;
3405 var withinElt;
3406 if (tokens.matchToken("in")) {
3407 inSearch = true;
3408 var inElt = parser.requireElement("unaryExpression", tokens);
3409 } else if (tokens.matchToken("within")) {
3410 withinElt = parser.requireElement("unaryExpression", tokens);
3411 } else {
3412 withinElt = document.body;
3413 }
3414
3415 var wrapping = false;
3416 if (tokens.matchToken("with")) {
3417 tokens.requireToken("wrapping")
3418 wrapping = true;
3419 }
3420
3421 return {
3422 type: "relativePositionalExpression",
3423 from: from,
3424 forwardSearch: forwardSearch,
3425 inSearch: inSearch,
3426 wrapping: wrapping,
3427 inElt: inElt,
3428 withinElt: withinElt,
3429 operator: op.value,
3430 args: [thing, from, inElt, withinElt],
3431 op: function (context, thing, from, inElt, withinElt) {
3432
3433 var css = thing.css;
3434 if (css == null) {
3435 throw "Expected a CSS value";
3436 }
3437
3438 if(inSearch) {
3439 if (inElt) {
3440 if (forwardSearch) {
3441 return scanForwardArray(from, inElt, css, wrapping);
3442 } else {
3443 return scanBackwardsArray(from, inElt, css, wrapping);
3444 }
3445 }
3446 } else {
3447 if (withinElt) {
3448 if (forwardSearch) {
3449 return scanForwardQuery(from, withinElt, css, wrapping);
3450 } else {
3451 return scanBackwardsQuery(from, withinElt, css, wrapping);
3452 }
3453 }
3454 }
3455 },
3456 evaluate: function (context) {
3457 return runtime.unifiedEval(this, context);
3458 },
3459 }
3460
3461 });
3462
3463 _parser.addGrammarElement("positionalExpression", function (parser, runtime, tokens) {
3464 var op = tokens.matchAnyToken("first", "last", "random");
3465 if (!op) return;
3466 tokens.matchAnyToken("in", "from", "of");
3467 var rhs = parser.requireElement("unaryExpression", tokens);
3468 const operator = op.value;
3469 return {
3470 type: "positionalExpression",
3471 rhs: rhs,
3472 operator: op.value,
3473 args: [rhs],
3474 op: function (context, rhsVal) {
3475 if (rhsVal && !Array.isArray(rhsVal)) {
3476 if (rhsVal.children) {
3477 rhsVal = rhsVal.children;
3478 } else {
3479 rhsVal = Array.from(rhsVal);
3480 }
3481 }
3482 if (rhsVal) {
3483 if (operator === "first") {
3484 return rhsVal[0];
3485 } else if (operator === "last") {
3486 return rhsVal[rhsVal.length - 1];
3487 } else if (operator === "random") {
3488 return rhsVal[Math.floor(Math.random() * rhsVal.length)];
3489 }
3490 }
3491 },
3492 evaluate: function (context) {
3493 return runtime.unifiedEval(this, context);
3494 },
3495 };
3496 });
3497
3498 _parser.addGrammarElement("mathOperator", function (parser, runtime, tokens) {
3499 var expr = parser.parseElement("unaryExpression", tokens);
3500 var mathOp,
3501 initialMathOp = null;
3502 mathOp = tokens.matchAnyOpToken("+", "-", "*", "/", "%");
3503 while (mathOp) {
3504 initialMathOp = initialMathOp || mathOp;
3505 var operator = mathOp.value;
3506 if (initialMathOp.value !== operator) {
3507 parser.raiseParseError(tokens, "You must parenthesize math operations with different operators");
3508 }
3509 var rhs = parser.parseElement("unaryExpression", tokens);
3510 expr = {
3511 type: "mathOperator",
3512 lhs: expr,
3513 rhs: rhs,
3514 operator: operator,
3515 args: [expr, rhs],
3516 op: function (context, lhsVal, rhsVal) {
3517 if (operator === "+") {
3518 return lhsVal + rhsVal;
3519 } else if (operator === "-") {
3520 return lhsVal - rhsVal;
3521 } else if (operator === "*") {
3522 return lhsVal * rhsVal;
3523 } else if (operator === "/") {
3524 return lhsVal / rhsVal;
3525 } else if (operator === "%") {
3526 return lhsVal % rhsVal;
3527 }
3528 },
3529 evaluate: function (context) {
3530 return runtime.unifiedEval(this, context);
3531 },
3532 };
3533 mathOp = tokens.matchAnyOpToken("+", "-", "*", "/", "%");
3534 }
3535 return expr;
3536 });
3537
3538 _parser.addGrammarElement("mathExpression", function (parser, runtime, tokens) {
3539 return parser.parseAnyOf(["mathOperator", "unaryExpression"], tokens);
3540 });
3541
3542 function sloppyContains(src, container, value){
3543 if (container['contains']) {
3544 return container.contains(value);
3545 } else if (container['includes']) {
3546 return container.includes(value);
3547 } else {
3548 throw Error("The value of " + src.sourceFor() + " does not have a contains or includes method on it");
3549 }
3550 }
3551 function sloppyMatches(src, target, toMatch){
3552 if (target['match']) {
3553 return !!target.match(toMatch);
3554 } else if (target['matches']) {
3555 return target.matches(toMatch);
3556 } else {
3557 throw Error("The value of " + src.sourceFor() + " does not have a match or matches method on it");
3558 }
3559 }
3560
3561 _parser.addGrammarElement("comparisonOperator", function (parser, runtime, tokens) {
3562 var expr = parser.parseElement("mathExpression", tokens);
3563 var comparisonToken = tokens.matchAnyOpToken("<", ">", "<=", ">=", "==", "===", "!=", "!==");
3564 var operator = comparisonToken ? comparisonToken.value : null;
3565 var hasRightValue = true; // By default, most comparisons require two values, but there are some exceptions.
3566 var typeCheck = false;
3567
3568 if (operator == null) {
3569 if (tokens.matchToken("is") || tokens.matchToken("am")) {
3570 if (tokens.matchToken("not")) {
3571 if (tokens.matchToken("in")) {
3572 operator = "not in";
3573 } else if (tokens.matchToken("a")) {
3574 operator = "not a";
3575 typeCheck = true;
3576 } else if (tokens.matchToken("empty")) {
3577 operator = "not empty";
3578 hasRightValue = false;
3579 } else {
3580 operator = "!=";
3581 }
3582 } else if (tokens.matchToken("in")) {
3583 operator = "in";
3584 } else if (tokens.matchToken("a")) {
3585 operator = "a";
3586 typeCheck = true;
3587 } else if (tokens.matchToken("empty")) {
3588 operator = "empty";
3589 hasRightValue = false;
3590 } else if (tokens.matchToken("less")) {
3591 tokens.requireToken("than");
3592 if (tokens.matchToken("or")) {
3593 tokens.requireToken("equal");
3594 tokens.requireToken("to");
3595 operator = "<=";
3596 } else {
3597 operator = "<";
3598 }
3599 } else if (tokens.matchToken("greater")) {
3600 tokens.requireToken("than");
3601 if (tokens.matchToken("or")) {
3602 tokens.requireToken("equal");
3603 tokens.requireToken("to");
3604 operator = ">=";
3605 } else {
3606 operator = ">";
3607 }
3608 } else {
3609 operator = "==";
3610 }
3611 } else if (tokens.matchToken("exist") || tokens.matchToken("exists")) {
3612 operator = "exist";
3613 hasRightValue = false;
3614 } else if (tokens.matchToken("matches") || tokens.matchToken("match")) {
3615 operator = "match";
3616 } else if (tokens.matchToken("contains") || tokens.matchToken("contain")) {
3617 operator = "contain";
3618 } else if (tokens.matchToken("includes") || tokens.matchToken("include")) {
3619 operator = "include";
3620 } else if (tokens.matchToken("do") || tokens.matchToken("does")) {
3621 tokens.requireToken("not");
3622 if (tokens.matchToken("matches") || tokens.matchToken("match")) {
3623 operator = "not match";
3624 } else if (tokens.matchToken("contains") || tokens.matchToken("contain")) {
3625 operator = "not contain";
3626 } else if (tokens.matchToken("exist") || tokens.matchToken("exist")) {
3627 operator = "not exist";
3628 hasRightValue = false;
3629 } else if (tokens.matchToken("include")) {
3630 operator = "not include";
3631 } else {
3632 parser.raiseParseError(tokens, "Expected matches or contains");
3633 }
3634 }
3635 }
3636
3637 if (operator) {
3638 // Do not allow chained comparisons, which is dumb
3639 if (typeCheck) {
3640 var typeName = tokens.requireTokenType("IDENTIFIER");
3641 var nullOk = !tokens.matchOpToken("!");
3642 } else if (hasRightValue) {
3643 var rhs = parser.requireElement("mathExpression", tokens);
3644 if (operator === "match" || operator === "not match") {
3645 rhs = rhs.css ? rhs.css : rhs;
3646 }
3647 }
3648 var lhs = expr;
3649 expr = {
3650 type: "comparisonOperator",
3651 operator: operator,
3652 typeName: typeName,
3653 nullOk: nullOk,
3654 lhs: expr,
3655 rhs: rhs,
3656 args: [expr, rhs],
3657 op: function (context, lhsVal, rhsVal) {
3658 if (operator === "==") {
3659 return lhsVal == rhsVal;
3660 } else if (operator === "!=") {
3661 return lhsVal != rhsVal;
3662 }
3663 if (operator === "match") {
3664 return lhsVal != null && sloppyMatches(lhs, lhsVal, rhsVal);
3665 }
3666 if (operator === "not match") {
3667 return lhsVal == null || !sloppyMatches(lhs, lhsVal, rhsVal);
3668 }
3669 if (operator === "in") {
3670 return rhsVal != null && sloppyContains(rhs, rhsVal, lhsVal);
3671 }
3672 if (operator === "not in") {
3673 return rhsVal == null || !sloppyContains(rhs, rhsVal, lhsVal);
3674 }
3675 if (operator === "contain") {
3676 return lhsVal != null && sloppyContains(lhs, lhsVal, rhsVal);
3677 }
3678 if (operator === "not contain") {
3679 return lhsVal == null || !sloppyContains(lhs, lhsVal, rhsVal);
3680 }
3681 if (operator === "include") {
3682 return lhsVal != null && sloppyContains(lhs, lhsVal, rhsVal);
3683 }
3684 if (operator === "not include") {
3685 return lhsVal == null || !sloppyContains(lhs, lhsVal, rhsVal);
3686 }
3687 if (operator === "===") {
3688 return lhsVal === rhsVal;
3689 } else if (operator === "!==") {
3690 return lhsVal !== rhsVal;
3691 } else if (operator === "<") {
3692 return lhsVal < rhsVal;
3693 } else if (operator === ">") {
3694 return lhsVal > rhsVal;
3695 } else if (operator === "<=") {
3696 return lhsVal <= rhsVal;
3697 } else if (operator === ">=") {
3698 return lhsVal >= rhsVal;
3699 } else if (operator === "empty") {
3700 return runtime.isEmpty(lhsVal);
3701 } else if (operator === "not empty") {
3702 return !runtime.isEmpty(lhsVal);
3703 } else if (operator === "exist") {
3704 return runtime.doesExist(lhsVal);
3705 } else if (operator === "not exist") {
3706 return !runtime.doesExist(lhsVal);
3707 } else if (operator === "a") {
3708 return runtime.typeCheck(lhsVal, typeName.value, nullOk);
3709 } else if (operator === "not a") {
3710 return !runtime.typeCheck(lhsVal, typeName.value, nullOk);
3711 } else {
3712 throw "Unknown comparison : " + operator;
3713 }
3714 },
3715 evaluate: function (context) {
3716 return runtime.unifiedEval(this, context);
3717 },
3718 };
3719 }
3720 return expr;
3721 });
3722
3723 _parser.addGrammarElement("comparisonExpression", function (parser, runtime, tokens) {
3724 return parser.parseAnyOf(["comparisonOperator", "mathExpression"], tokens);
3725 });
3726
3727 _parser.addGrammarElement("logicalOperator", function (parser, runtime, tokens) {
3728 var expr = parser.parseElement("comparisonExpression", tokens);
3729 var logicalOp,
3730 initialLogicalOp = null;
3731 logicalOp = tokens.matchToken("and") || tokens.matchToken("or");
3732 while (logicalOp) {
3733 initialLogicalOp = initialLogicalOp || logicalOp;
3734 if (initialLogicalOp.value !== logicalOp.value) {
3735 parser.raiseParseError(tokens, "You must parenthesize logical operations with different operators");
3736 }
3737 var rhs = parser.requireElement("comparisonExpression", tokens);
3738 const operator = logicalOp.value;
3739 expr = {
3740 type: "logicalOperator",
3741 operator: operator,
3742 lhs: expr,
3743 rhs: rhs,
3744 args: [expr, rhs],
3745 op: function (context, lhsVal, rhsVal) {
3746 if (operator === "and") {
3747 return lhsVal && rhsVal;
3748 } else {
3749 return lhsVal || rhsVal;
3750 }
3751 },
3752 evaluate: function (context) {
3753 return runtime.unifiedEval(this, context);
3754 },
3755 };
3756 logicalOp = tokens.matchToken("and") || tokens.matchToken("or");
3757 }
3758 return expr;
3759 });
3760
3761 _parser.addGrammarElement("logicalExpression", function (parser, runtime, tokens) {
3762 return parser.parseAnyOf(["logicalOperator", "mathExpression"], tokens);
3763 });
3764
3765 _parser.addGrammarElement("asyncExpression", function (parser, runtime, tokens) {
3766 if (tokens.matchToken("async")) {
3767 var value = parser.requireElement("logicalExpression", tokens);
3768 var expr = {
3769 type: "asyncExpression",
3770 value: value,
3771 evaluate: function (context) {
3772 return {
3773 asyncWrapper: true,
3774 value: this.value.evaluate(context), //OK
3775 };
3776 },
3777 };
3778 return expr;
3779 } else {
3780 return parser.parseElement("logicalExpression", tokens);
3781 }
3782 });
3783
3784 _parser.addGrammarElement("expression", function (parser, runtime, tokens) {
3785 tokens.matchToken("the"); // optional the
3786 return parser.parseElement("asyncExpression", tokens);
3787 });
3788
3789 _parser.addGrammarElement("assignableExpression", function (parser, runtime, tokens) {
3790 tokens.matchToken("the"); // optional the
3791
3792 // TODO obviously we need to generalize this as a left hand side / targetable concept
3793 var expr = parser.parseElement("primaryExpression", tokens);
3794 if (expr && (
3795 expr.type === "symbol" ||
3796 expr.type === "ofExpression" ||
3797 expr.type === "propertyAccess" ||
3798 expr.type === "attributeRefAccess" ||
3799 expr.type === "attributeRef" ||
3800 expr.type === "styleRef" ||
3801 expr.type === "arrayIndex" ||
3802 expr.type === "possessive")
3803 ) {
3804 return expr;
3805 } else {
3806 _parser.raiseParseError(
3807 tokens,
3808 "A target expression must be writable. The expression type '" + (expr && expr.type) + "' is not."
3809 );
3810 }
3811 return expr;
3812 });
3813
3814 _parser.addGrammarElement("hyperscript", function (parser, runtime, tokens) {
3815 var features = [];
3816
3817 if (tokens.hasMore()) {
3818 while (parser.featureStart(tokens.currentToken()) || tokens.currentToken().value === "(") {
3819 var feature = parser.requireElement("feature", tokens);
3820 features.push(feature);
3821 tokens.matchToken("end"); // optional end
3822 }
3823 }
3824 return {
3825 type: "hyperscript",
3826 features: features,
3827 apply: function (target, source, args) {
3828 // no op
3829 for (const feature of features) {
3830 feature.install(target, source, args);
3831 }
3832 },
3833 };
3834 });
3835
3836 var parseEventArgs = function (tokens) {
3837 var args = [];
3838 // handle argument list (look ahead 3)
3839 if (
3840 tokens.token(0).value === "(" &&
3841 (tokens.token(1).value === ")" || tokens.token(2).value === "," || tokens.token(2).value === ")")
3842 ) {
3843 tokens.matchOpToken("(");
3844 do {
3845 args.push(tokens.requireTokenType("IDENTIFIER"));
3846 } while (tokens.matchOpToken(","));
3847 tokens.requireOpToken(")");
3848 }
3849 return args;
3850 };
3851
3852 _parser.addFeature("on", function (parser, runtime, tokens) {
3853 if (!tokens.matchToken("on")) return;
3854 var every = false;
3855 if (tokens.matchToken("every")) {
3856 every = true;
3857 }
3858 var events = [];
3859 var displayName = null;
3860 do {
3861 var on = parser.requireElement("eventName", tokens, "Expected event name");
3862
3863 var eventName = on.evaluate(); // OK No Promise
3864
3865 if (displayName) {
3866 displayName = displayName + " or " + eventName;
3867 } else {
3868 displayName = "on " + eventName;
3869 }
3870 var args = parseEventArgs(tokens);
3871
3872 var filter = null;
3873 if (tokens.matchOpToken("[")) {
3874 filter = parser.requireElement("expression", tokens);
3875 tokens.requireOpToken("]");
3876 }
3877
3878 if (tokens.currentToken().type === "NUMBER") {
3879 var startCountToken = tokens.consumeToken();
3880 var startCount = parseInt(startCountToken.value);
3881 if (tokens.matchToken("to")) {
3882 var endCountToken = tokens.consumeToken();
3883 var endCount = parseInt(endCountToken.value);
3884 } else if (tokens.matchToken("and")) {
3885 var unbounded = true;
3886 tokens.requireToken("on");
3887 }
3888 }
3889
3890 if (eventName === "intersection") {
3891 var intersectionSpec = {};
3892 if (tokens.matchToken("with")) {
3893 intersectionSpec["with"] = parser.requireElement("expression", tokens).evaluate();
3894 }
3895 if (tokens.matchToken("having")) {
3896 do {
3897 if (tokens.matchToken("margin")) {
3898 intersectionSpec["rootMargin"] = parser.requireElement("stringLike", tokens).evaluate();
3899 } else if (tokens.matchToken("threshold")) {
3900 intersectionSpec["threshold"] = parser.requireElement("expression", tokens).evaluate();
3901 } else {
3902 parser.raiseParseError(tokens, "Unknown intersection config specification");
3903 }
3904 } while (tokens.matchToken("and"));
3905 }
3906 } else if (eventName === "mutation") {
3907 var mutationSpec = {};
3908 if (tokens.matchToken("of")) {
3909 do {
3910 if (tokens.matchToken("anything")) {
3911 mutationSpec["attributes"] = true;
3912 mutationSpec["subtree"] = true;
3913 mutationSpec["characterData"] = true;
3914 mutationSpec["childList"] = true;
3915 } else if (tokens.matchToken("childList")) {
3916 mutationSpec["childList"] = true;
3917 } else if (tokens.matchToken("attributes")) {
3918 mutationSpec["attributes"] = true;
3919 mutationSpec["attributeOldValue"] = true;
3920 } else if (tokens.matchToken("subtree")) {
3921 mutationSpec["subtree"] = true;
3922 } else if (tokens.matchToken("characterData")) {
3923 mutationSpec["characterData"] = true;
3924 mutationSpec["characterDataOldValue"] = true;
3925 } else if (tokens.currentToken().type === "ATTRIBUTE_REF") {
3926 var attribute = tokens.consumeToken();
3927 if (mutationSpec["attributeFilter"] == null) {
3928 mutationSpec["attributeFilter"] = [];
3929 }
3930 if (attribute.value.indexOf("@") == 0) {
3931 mutationSpec["attributeFilter"].push(attribute.value.substring(1));
3932 } else {
3933 parser.raiseParseError(
3934 tokens,
3935 "Only shorthand attribute references are allowed here"
3936 );
3937 }
3938 } else {
3939 parser.raiseParseError(tokens, "Unknown mutation config specification");
3940 }
3941 } while (tokens.matchToken("or"));
3942 } else {
3943 mutationSpec["attributes"] = true;
3944 mutationSpec["characterData"] = true;
3945 mutationSpec["childList"] = true;
3946 }
3947 }
3948
3949 var from = null;
3950 var elsewhere = false;
3951 if (tokens.matchToken("from")) {
3952 if (tokens.matchToken("elsewhere")) {
3953 elsewhere = true;
3954 } else {
3955 from = parser.parseElement("expression", tokens);
3956 if (!from) {
3957 parser.raiseParseError(tokens, 'Expected either target value or "elsewhere".');
3958 }
3959 }
3960 }
3961 // support both "elsewhere" and "from elsewhere"
3962 if (from === null && elsewhere === false && tokens.matchToken("elsewhere")) {
3963 elsewhere = true;
3964 }
3965
3966 if (tokens.matchToken("in")) {
3967 var inExpr = parser.parseElement('unaryExpression', tokens);
3968 }
3969
3970 if (tokens.matchToken("debounced")) {
3971 tokens.requireToken("at");
3972 var timeExpr = parser.requireElement("expression", tokens);
3973 // @ts-ignore
3974 var debounceTime = timeExpr.evaluate({}); // OK No promise TODO make a literal time expr
3975 } else if (tokens.matchToken("throttled")) {
3976 tokens.requireToken("at");
3977 var timeExpr = parser.requireElement("expression", tokens);
3978 // @ts-ignore
3979 var throttleTime = timeExpr.evaluate({}); // OK No promise TODO make a literal time expr
3980 }
3981
3982 events.push({
3983 execCount: 0,
3984 every: every,
3985 on: eventName,
3986 args: args,
3987 filter: filter,
3988 from: from,
3989 inExpr: inExpr,
3990 elsewhere: elsewhere,
3991 startCount: startCount,
3992 endCount: endCount,
3993 unbounded: unbounded,
3994 debounceTime: debounceTime,
3995 throttleTime: throttleTime,
3996 mutationSpec: mutationSpec,
3997 intersectionSpec: intersectionSpec,
3998 debounced: undefined,
3999 lastExec: undefined,
4000 });
4001 } while (tokens.matchToken("or"));
4002
4003 var queueLast = true;
4004 if (!every) {
4005 if (tokens.matchToken("queue")) {
4006 if (tokens.matchToken("all")) {
4007 var queueAll = true;
4008 var queueLast = false;
4009 } else if (tokens.matchToken("first")) {
4010 var queueFirst = true;
4011 } else if (tokens.matchToken("none")) {
4012 var queueNone = true;
4013 } else {
4014 tokens.requireToken("last");
4015 }
4016 }
4017 }
4018
4019 var start = parser.requireElement("commandList", tokens);
4020 parser.ensureTerminated(start);
4021
4022 if (tokens.matchToken("catch")) {
4023 var errorSymbol = tokens.requireTokenType("IDENTIFIER").value;
4024 var errorHandler = parser.requireElement("commandList", tokens);
4025 parser.ensureTerminated(errorHandler);
4026 }
4027
4028 if (tokens.matchToken("finally")) {
4029 var finallyHandler = parser.requireElement("commandList", tokens);
4030 parser.ensureTerminated(finallyHandler);
4031 }
4032
4033 var onFeature = {
4034 displayName: displayName,
4035 events: events,
4036 start: start,
4037 every: every,
4038 execCount: 0,
4039 errorHandler: errorHandler,
4040 errorSymbol: errorSymbol,
4041 execute: function (/** @type {Context} */ ctx) {
4042 let eventQueueInfo = runtime.getEventQueueFor(ctx.me, onFeature);
4043 if (eventQueueInfo.executing && every === false) {
4044 if (queueNone || (queueFirst && eventQueueInfo.queue.length > 0)) {
4045 return;
4046 }
4047 if (queueLast) {
4048 eventQueueInfo.queue.length = 0;
4049 }
4050 eventQueueInfo.queue.push(ctx);
4051 return;
4052 }
4053 onFeature.execCount++;
4054 eventQueueInfo.executing = true;
4055 ctx.meta.onHalt = function () {
4056 eventQueueInfo.executing = false;
4057 var queued = eventQueueInfo.queue.shift();
4058 if (queued) {
4059 setTimeout(function () {
4060 onFeature.execute(queued);
4061 }, 1);
4062 }
4063 };
4064 ctx.meta.reject = function (err) {
4065 console.error(err.message ? err.message : err);
4066 var hypertrace = runtime.getHyperTrace(ctx, err);
4067 if (hypertrace) {
4068 hypertrace.print();
4069 }
4070 runtime.triggerEvent(ctx.me, "exception", {
4071 error: err,
4072 });
4073 };
4074 start.execute(ctx);
4075 },
4076 install: function (elt, source) {
4077 for (const eventSpec of onFeature.events) {
4078 var targets;
4079 if (eventSpec.elsewhere) {
4080 targets = [document];
4081 } else if (eventSpec.from) {
4082 targets = eventSpec.from.evaluate(runtime.makeContext(elt, onFeature, elt, null));
4083 } else {
4084 targets = [elt];
4085 }
4086 runtime.implicitLoop(targets, function (target) {
4087 // OK NO PROMISE
4088
4089 var eventName = eventSpec.on;
4090 if (eventSpec.mutationSpec) {
4091 eventName = "hyperscript:mutation";
4092 const observer = new MutationObserver(function (mutationList, observer) {
4093 if (!onFeature.executing) {
4094 _runtime.triggerEvent(target, eventName, {
4095 mutationList: mutationList,
4096 observer: observer,
4097 });
4098 }
4099 });
4100 observer.observe(target, eventSpec.mutationSpec);
4101 }
4102
4103 if (eventSpec.intersectionSpec) {
4104 eventName = "hyperscript:insersection";
4105 const observer = new IntersectionObserver(function (entries) {
4106 for (const entry of entries) {
4107 var detail = {
4108 observer: observer,
4109 };
4110 detail = mergeObjects(detail, entry);
4111 detail["intersecting"] = entry.isIntersecting;
4112 _runtime.triggerEvent(target, eventName, detail);
4113 }
4114 }, eventSpec.intersectionSpec);
4115 observer.observe(target);
4116 }
4117
4118 var addEventListener = target.addEventListener || target.on;
4119 addEventListener.call(target, eventName, function listener(evt) {
4120 // OK NO PROMISE
4121 if (typeof Node !== 'undefined' && elt instanceof Node && target !== elt && !elt.isConnected) {
4122 target.removeEventListener(eventName, listener);
4123 return;
4124 }
4125
4126 var ctx = runtime.makeContext(elt, onFeature, elt, evt);
4127 if (eventSpec.elsewhere && elt.contains(evt.target)) {
4128 return;
4129 }
4130 if (eventSpec.from) {
4131 ctx.result = target;
4132 }
4133
4134 // establish context
4135 for (const arg of eventSpec.args) {
4136 let eventValue = ctx.event[arg.value];
4137 if (eventValue !== undefined) {
4138 ctx[arg.value] = eventValue;
4139 } else if ('detail' in ctx.event) {
4140 ctx[arg.value] = ctx.event['detail'][arg.value];
4141 }
4142 }
4143
4144 // install error handler if any
4145 ctx.meta.errorHandler = errorHandler;
4146 ctx.meta.errorSymbol = errorSymbol;
4147 ctx.meta.finallyHandler = finallyHandler;
4148
4149 // apply filter
4150 if (eventSpec.filter) {
4151 var initialCtx = ctx.meta.context;
4152 ctx.meta.context = ctx.event;
4153 try {
4154 var value = eventSpec.filter.evaluate(ctx); //OK NO PROMISE
4155 if (value) {
4156 // match the javascript semantics for if statements
4157 } else {
4158 return;
4159 }
4160 } finally {
4161 ctx.meta.context = initialCtx;
4162 }
4163 }
4164
4165 if (eventSpec.inExpr) {
4166 var inElement = evt.target;
4167 while (true) {
4168 if (inElement.matches && inElement.matches(eventSpec.inExpr.css)) {
4169 ctx.result = inElement;
4170 break;
4171 } else {
4172 inElement = inElement.parentElement;
4173 if (inElement == null) {
4174 return; // no match found
4175 }
4176 }
4177 }
4178 }
4179
4180 // verify counts
4181 eventSpec.execCount++;
4182 if (eventSpec.startCount) {
4183 if (eventSpec.endCount) {
4184 if (
4185 eventSpec.execCount < eventSpec.startCount ||
4186 eventSpec.execCount > eventSpec.endCount
4187 ) {
4188 return;
4189 }
4190 } else if (eventSpec.unbounded) {
4191 if (eventSpec.execCount < eventSpec.startCount) {
4192 return;
4193 }
4194 } else if (eventSpec.execCount !== eventSpec.startCount) {
4195 return;
4196 }
4197 }
4198
4199 //debounce
4200 if (eventSpec.debounceTime) {
4201 if (eventSpec.debounced) {
4202 clearTimeout(eventSpec.debounced);
4203 }
4204 eventSpec.debounced = setTimeout(function () {
4205 onFeature.execute(ctx);
4206 }, eventSpec.debounceTime);
4207 return;
4208 }
4209
4210 // throttle
4211 if (eventSpec.throttleTime) {
4212 if (
4213 eventSpec.lastExec &&
4214 Date.now() < eventSpec.lastExec + eventSpec.throttleTime
4215 ) {
4216 return;
4217 } else {
4218 eventSpec.lastExec = Date.now();
4219 }
4220 }
4221
4222 // apply execute
4223 onFeature.execute(ctx);
4224 });
4225 });
4226 }
4227 },
4228 };
4229 parser.setParent(start, onFeature);
4230 return onFeature;
4231 });
4232
4233 _parser.addFeature("def", function (parser, runtime, tokens) {
4234 if (!tokens.matchToken("def")) return;
4235 var functionName = parser.requireElement("dotOrColonPath", tokens);
4236 var nameVal = functionName.evaluate(); // OK
4237 var nameSpace = nameVal.split(".");
4238 var funcName = nameSpace.pop();
4239
4240 var args = [];
4241 if (tokens.matchOpToken("(")) {
4242 if (tokens.matchOpToken(")")) {
4243 // emtpy args list
4244 } else {
4245 do {
4246 args.push(tokens.requireTokenType("IDENTIFIER"));
4247 } while (tokens.matchOpToken(","));
4248 tokens.requireOpToken(")");
4249 }
4250 }
4251
4252 var start = parser.requireElement("commandList", tokens);
4253
4254 if (tokens.matchToken("catch")) {
4255 var errorSymbol = tokens.requireTokenType("IDENTIFIER").value;
4256 var errorHandler = parser.parseElement("commandList", tokens);
4257 }
4258
4259 if (tokens.matchToken("finally")) {
4260 var finallyHandler = parser.requireElement("commandList", tokens);
4261 parser.ensureTerminated(finallyHandler);
4262 }
4263
4264 var functionFeature = {
4265 displayName:
4266 funcName +
4267 "(" +
4268 args
4269 .map(function (arg) {
4270 return arg.value;
4271 })
4272 .join(", ") +
4273 ")",
4274 name: funcName,
4275 args: args,
4276 start: start,
4277 errorHandler: errorHandler,
4278 errorSymbol: errorSymbol,
4279 finallyHandler: finallyHandler,
4280 install: function (target, source) {
4281 var func = function () {
4282 // null, worker
4283 var ctx = runtime.makeContext(source, functionFeature, target, null);
4284
4285 // install error handler if any
4286 ctx.meta.errorHandler = errorHandler;
4287 ctx.meta.errorSymbol = errorSymbol;
4288 ctx.meta.finallyHandler = finallyHandler;
4289
4290 for (var i = 0; i < args.length; i++) {
4291 var name = args[i];
4292 var argumentVal = arguments[i];
4293 if (name) {
4294 ctx[name.value] = argumentVal;
4295 }
4296 }
4297 ctx.meta.caller = arguments[args.length];
4298 if (ctx.meta.caller) {
4299 ctx.meta.callingCommand = ctx.meta.caller.meta.command;
4300 }
4301 var resolve,
4302 reject = null;
4303 var promise = new Promise(function (theResolve, theReject) {
4304 resolve = theResolve;
4305 reject = theReject;
4306 });
4307 start.execute(ctx);
4308 if (ctx.meta.returned) {
4309 return ctx.meta.returnValue;
4310 } else {
4311 ctx.meta.resolve = resolve;
4312 ctx.meta.reject = reject;
4313 return promise;
4314 }
4315 };
4316 func.hyperfunc = true;
4317 func.hypername = nameVal;
4318 runtime.assignToNamespace(target, nameSpace, funcName, func);
4319 },
4320 };
4321
4322 parser.ensureTerminated(start);
4323
4324 // terminate error handler if any
4325 if (errorHandler) {
4326 parser.ensureTerminated(errorHandler);
4327 }
4328
4329 parser.setParent(start, functionFeature);
4330 return functionFeature;
4331 });
4332
4333 _parser.addFeature("set", function (parser, runtime, tokens) {
4334 let setCmd = parser.parseElement("setCommand", tokens);
4335 if (setCmd) {
4336 if (setCmd.target.scope !== "element") {
4337 parser.raiseParseError(tokens, "variables declared at the feature level must be element scoped.");
4338 }
4339 let setFeature = {
4340 start: setCmd,
4341 install: function (target, source) {
4342 setCmd && setCmd.execute(runtime.makeContext(target, setFeature, target, null));
4343 },
4344 };
4345 parser.ensureTerminated(setCmd);
4346 return setFeature;
4347 }
4348 });
4349
4350 _parser.addFeature("init", function (parser, runtime, tokens) {
4351 if (!tokens.matchToken("init")) return;
4352
4353 var start = parser.requireElement("commandList", tokens);
4354 var initFeature = {
4355 start: start,
4356 install: function (target, source) {
4357 setTimeout(function () {
4358 start && start.execute(runtime.makeContext(target, initFeature, target, null));
4359 }, 0);
4360 },
4361 };
4362
4363 // terminate body
4364 parser.ensureTerminated(start);
4365 parser.setParent(start, initFeature);
4366 return initFeature;
4367 });
4368
4369 _parser.addFeature("worker", function (parser, runtime, tokens) {
4370 if (tokens.matchToken("worker")) {
4371 parser.raiseParseError(
4372 tokens,
4373 "In order to use the 'worker' feature, include " +
4374 "the _hyperscript worker plugin. See " +
4375 "https://hyperscript.org/features/worker/ for " +
4376 "more info."
4377 );
4378 }
4379 });
4380
4381 _parser.addFeature("behavior", function (parser, runtime, tokens) {
4382 if (!tokens.matchToken("behavior")) return;
4383 var path = parser.requireElement("dotOrColonPath", tokens).evaluate();
4384 var nameSpace = path.split(".");
4385 var name = nameSpace.pop();
4386
4387 var formalParams = [];
4388 if (tokens.matchOpToken("(") && !tokens.matchOpToken(")")) {
4389 do {
4390 formalParams.push(tokens.requireTokenType("IDENTIFIER").value);
4391 } while (tokens.matchOpToken(","));
4392 tokens.requireOpToken(")");
4393 }
4394 var hs = parser.requireElement("hyperscript", tokens);
4395 for (var i = 0; i < hs.features.length; i++) {
4396 var feature = hs.features[i];
4397 feature.behavior = path;
4398 }
4399
4400 return {
4401 install: function (target, source) {
4402 runtime.assignToNamespace(
4403 globalScope.document && globalScope.document.body,
4404 nameSpace,
4405 name,
4406 function (target, source, innerArgs) {
4407 var internalData = runtime.getInternalData(target);
4408 var elementScope = getOrInitObject(internalData, path + "Scope");
4409 for (var i = 0; i < formalParams.length; i++) {
4410 elementScope[formalParams[i]] = innerArgs[formalParams[i]];
4411 }
4412 hs.apply(target, source);
4413 }
4414 );
4415 },
4416 };
4417 });
4418
4419 _parser.addFeature("install", function (parser, runtime, tokens) {
4420 if (!tokens.matchToken("install")) return;
4421 var behaviorPath = parser.requireElement("dotOrColonPath", tokens).evaluate();
4422 var behaviorNamespace = behaviorPath.split(".");
4423 var args = parser.parseElement("namedArgumentList", tokens);
4424
4425 var installFeature;
4426 return (installFeature = {
4427 install: function (target, source) {
4428 runtime.unifiedEval(
4429 {
4430 args: [args],
4431 op: function (ctx, args) {
4432 var behavior = globalScope;
4433 for (var i = 0; i < behaviorNamespace.length; i++) {
4434 behavior = behavior[behaviorNamespace[i]];
4435 if (typeof behavior !== "object" && typeof behavior !== "function")
4436 throw new Error("No such behavior defined as " + behaviorPath);
4437 }
4438
4439 if (!(behavior instanceof Function))
4440 throw new Error(behaviorPath + " is not a behavior");
4441
4442 behavior(target, source, args);
4443 },
4444 },
4445 runtime.makeContext(target, installFeature, target)
4446 );
4447 },
4448 });
4449 });
4450
4451 _parser.addGrammarElement("jsBody", function (parser, runtime, tokens) {
4452 var jsSourceStart = tokens.currentToken().start;
4453 var jsLastToken = tokens.currentToken();
4454
4455 var funcNames = [];
4456 var funcName = "";
4457 var expectFunctionDeclaration = false;
4458 while (tokens.hasMore()) {
4459 jsLastToken = tokens.consumeToken();
4460 var peek = tokens.token(0, true);
4461 if (peek.type === "IDENTIFIER" && peek.value === "end") {
4462 break;
4463 }
4464 if (expectFunctionDeclaration) {
4465 if (jsLastToken.type === "IDENTIFIER" || jsLastToken.type === "NUMBER") {
4466 funcName += jsLastToken.value;
4467 } else {
4468 if (funcName !== "") funcNames.push(funcName);
4469 funcName = "";
4470 expectFunctionDeclaration = false;
4471 }
4472 } else if (jsLastToken.type === "IDENTIFIER" && jsLastToken.value === "function") {
4473 expectFunctionDeclaration = true;
4474 }
4475 }
4476 var jsSourceEnd = jsLastToken.end + 1;
4477
4478 return {
4479 type: "jsBody",
4480 exposedFunctionNames: funcNames,
4481 jsSource: tokens.source.substring(jsSourceStart, jsSourceEnd),
4482 };
4483 });
4484
4485 _parser.addFeature("js", function (parser, runtime, tokens) {
4486 if (!tokens.matchToken("js")) return;
4487 var jsBody = parser.requireElement("jsBody", tokens);
4488
4489 var jsSource =
4490 jsBody.jsSource +
4491 "\nreturn { " +
4492 jsBody.exposedFunctionNames
4493 .map(function (name) {
4494 return name + ":" + name;
4495 })
4496 .join(",") +
4497 " } ";
4498 var func = new Function(jsSource);
4499
4500 return {
4501 jsSource: jsSource,
4502 function: func,
4503 exposedFunctionNames: jsBody.exposedFunctionNames,
4504 install: function () {
4505 mergeObjects(globalScope, func());
4506 },
4507 };
4508 });
4509
4510 _parser.addCommand("js", function (parser, runtime, tokens) {
4511 if (!tokens.matchToken("js")) return;
4512 // Parse inputs
4513 var inputs = [];
4514 if (tokens.matchOpToken("(")) {
4515 if (tokens.matchOpToken(")")) {
4516 // empty input list
4517 } else {
4518 do {
4519 var inp = tokens.requireTokenType("IDENTIFIER");
4520 inputs.push(inp.value);
4521 } while (tokens.matchOpToken(","));
4522 tokens.requireOpToken(")");
4523 }
4524 }
4525
4526 var jsBody = parser.requireElement("jsBody", tokens);
4527 tokens.matchToken("end");
4528
4529 var func = varargConstructor(Function, inputs.concat([jsBody.jsSource]));
4530
4531 var command = {
4532 jsSource: jsBody.jsSource,
4533 function: func,
4534 inputs: inputs,
4535 op: function (context) {
4536 var args = [];
4537 inputs.forEach(function (input) {
4538 args.push(runtime.resolveSymbol(input, context, 'default'));
4539 });
4540 var result = func.apply(globalScope, args);
4541 if (result && typeof result.then === "function") {
4542 return new Promise(function (resolve) {
4543 result.then(function (actualResult) {
4544 context.result = actualResult;
4545 resolve(runtime.findNext(this, context));
4546 });
4547 });
4548 } else {
4549 context.result = result;
4550 return runtime.findNext(this, context);
4551 }
4552 },
4553 };
4554 return command;
4555 });
4556
4557 _parser.addCommand("async", function (parser, runtime, tokens) {
4558 if (!tokens.matchToken("async")) return;
4559 if (tokens.matchToken("do")) {
4560 var body = parser.requireElement("commandList", tokens);
4561
4562 // Append halt
4563 var end = body;
4564 while (end.next) end = end.next;
4565 end.next = runtime.HALT;
4566
4567 tokens.requireToken("end");
4568 } else {
4569 var body = parser.requireElement("command", tokens);
4570 }
4571 var command = {
4572 body: body,
4573 op: function (context) {
4574 setTimeout(function () {
4575 body.execute(context);
4576 });
4577 return runtime.findNext(this, context);
4578 },
4579 };
4580 parser.setParent(body, command);
4581 return command;
4582 });
4583
4584 _parser.addCommand("tell", function (parser, runtime, tokens) {
4585 var startToken = tokens.currentToken();
4586 if (!tokens.matchToken("tell")) return;
4587 var value = parser.requireElement("expression", tokens);
4588 var body = parser.requireElement("commandList", tokens);
4589 if (tokens.hasMore() && !parser.featureStart(tokens.currentToken())) {
4590 tokens.requireToken("end");
4591 }
4592 var slot = "tell_" + startToken.start;
4593 var tellCmd = {
4594 value: value,
4595 body: body,
4596 args: [value],
4597 resolveNext: function (context) {
4598 var iterator = context.meta.iterators[slot];
4599 if (iterator.index < iterator.value.length) {
4600 context.beingTold = iterator.value[iterator.index++];
4601 return body;
4602 } else {
4603 // restore original me
4604 context.beingTold = iterator.originalBeingTold;
4605 if (this.next) {
4606 return this.next;
4607 } else {
4608 return runtime.findNext(this.parent, context);
4609 }
4610 }
4611 },
4612 op: function (context, value) {
4613 if (value == null) {
4614 value = [];
4615 } else if (!(Array.isArray(value) || value instanceof NodeList)) {
4616 value = [value];
4617 }
4618 context.meta.iterators[slot] = {
4619 originalBeingTold: context.beingTold,
4620 index: 0,
4621 value: value,
4622 };
4623 return this.resolveNext(context);
4624 },
4625 };
4626 parser.setParent(body, tellCmd);
4627 return tellCmd;
4628 });
4629
4630 _parser.addCommand("wait", function (parser, runtime, tokens) {
4631 if (!tokens.matchToken("wait")) return;
4632 var command;
4633
4634 // wait on event
4635 if (tokens.matchToken("for")) {
4636 tokens.matchToken("a"); // optional "a"
4637 var events = [];
4638 do {
4639 var lookahead = tokens.token(0);
4640 if (lookahead.type === 'NUMBER' || lookahead.type === 'L_PAREN') {
4641 events.push({
4642 time: parser.requireElement('expression', tokens).evaluate() // TODO: do we want to allow async here?
4643 })
4644 } else {
4645 events.push({
4646 name: _parser.requireElement("dotOrColonPath", tokens, "Expected event name").evaluate(),
4647 args: parseEventArgs(tokens),
4648 });
4649 }
4650 } while (tokens.matchToken("or"));
4651
4652 if (tokens.matchToken("from")) {
4653 var on = parser.requireElement("expression", tokens);
4654 }
4655
4656 // wait on event
4657 command = {
4658 event: events,
4659 on: on,
4660 args: [on],
4661 op: function (context, on) {
4662 var target = on ? on : context.me;
4663 if (!(target instanceof EventTarget))
4664 throw new Error("Not a valid event target: " + this.on.sourceFor());
4665 return new Promise((resolve) => {
4666 var resolved = false;
4667 for (const eventInfo of events) {
4668 var listener = (event) => {
4669 context.result = event;
4670 if (eventInfo.args) {
4671 for (const arg of eventInfo.args) {
4672 context[arg.value] =
4673 event[arg.value] || (event.detail ? event.detail[arg.value] : null);
4674 }
4675 }
4676 if (!resolved) {
4677 resolved = true;
4678 resolve(runtime.findNext(this, context));
4679 }
4680 };
4681 if (eventInfo.name){
4682 target.addEventListener(eventInfo.name, listener, {once: true});
4683 } else if (eventInfo.time != null) {
4684 setTimeout(listener, eventInfo.time, eventInfo.time)
4685 }
4686 }
4687 });
4688 },
4689 };
4690 return command;
4691 } else {
4692 var time;
4693 if (tokens.matchToken("a")) {
4694 tokens.requireToken("tick");
4695 time = 0;
4696 } else {
4697 time = _parser.requireElement("expression", tokens);
4698 }
4699
4700 command = {
4701 type: "waitCmd",
4702 time: time,
4703 args: [time],
4704 op: function (context, timeValue) {
4705 return new Promise((resolve) => {
4706 setTimeout(() => {
4707 resolve(runtime.findNext(this, context));
4708 }, timeValue);
4709 });
4710 },
4711 execute: function (context) {
4712 return runtime.unifiedExec(this, context);
4713 },
4714 };
4715 return command;
4716 }
4717 });
4718
4719 // TODO - colon path needs to eventually become part of ruby-style symbols
4720 _parser.addGrammarElement("dotOrColonPath", function (parser, runtime, tokens) {
4721 var root = tokens.matchTokenType("IDENTIFIER");
4722 if (root) {
4723 var path = [root.value];
4724
4725 var separator = tokens.matchOpToken(".") || tokens.matchOpToken(":");
4726 if (separator) {
4727 do {
4728 path.push(tokens.requireTokenType("IDENTIFIER", "NUMBER").value);
4729 } while (tokens.matchOpToken(separator.value));
4730 }
4731
4732 return {
4733 type: "dotOrColonPath",
4734 path: path,
4735 evaluate: function () {
4736 return path.join(separator ? separator.value : "");
4737 },
4738 };
4739 }
4740 });
4741
4742
4743 _parser.addGrammarElement("eventName", function (parser, runtime, tokens) {
4744 var token;
4745 if ((token = tokens.matchTokenType("STRING"))) {
4746 return {
4747 evaluate: function() {
4748 return token.value;
4749 },
4750 };
4751 }
4752
4753 return parser.parseElement("dotOrColonPath", tokens);
4754 });
4755
4756 function parseSendCmd(cmdType, parser, runtime, tokens) {
4757 var eventName = parser.requireElement("eventName", tokens);
4758
4759 var details = parser.parseElement("namedArgumentList", tokens);
4760 if ((cmdType === "send" && tokens.matchToken("to")) ||
4761 (cmdType === "trigger" && tokens.matchToken("on"))) {
4762 var toExpr = parser.requireElement("expression", tokens);
4763 } else {
4764 var toExpr = parser.requireElement("implicitMeTarget", tokens);
4765 }
4766
4767 var sendCmd = {
4768 eventName: eventName,
4769 details: details,
4770 to: toExpr,
4771 args: [toExpr, eventName, details],
4772 op: function (context, to, eventName, details) {
4773 runtime.nullCheck(to, toExpr);
4774 runtime.forEach(to, function (target) {
4775 runtime.triggerEvent(target, eventName, details, context.me);
4776 });
4777 return runtime.findNext(sendCmd, context);
4778 },
4779 };
4780 return sendCmd;
4781 }
4782
4783 _parser.addCommand("trigger", function (parser, runtime, tokens) {
4784 if (tokens.matchToken("trigger")) {
4785 return parseSendCmd("trigger", parser, runtime, tokens);
4786 }
4787 });
4788
4789 _parser.addCommand("send", function (parser, runtime, tokens) {
4790 if (tokens.matchToken("send")) {
4791 return parseSendCmd("send", parser, runtime, tokens);
4792 }
4793 });
4794
4795 var parseReturnFunction = function (parser, runtime, tokens, returnAValue) {
4796 if (returnAValue) {
4797 if (parser.commandBoundary(tokens.currentToken())) {
4798 parser.raiseParseError(tokens, "'return' commands must return a value. If you do not wish to return a value, use 'exit' instead.");
4799 } else {
4800 var value = parser.requireElement("expression", tokens);
4801 }
4802 }
4803
4804 var returnCmd = {
4805 value: value,
4806 args: [value],
4807 op: function (context, value) {
4808 var resolve = context.meta.resolve;
4809 context.meta.returned = true;
4810 context.meta.returnValue = value;
4811 if (resolve) {
4812 if (value) {
4813 resolve(value);
4814 } else {
4815 resolve();
4816 }
4817 }
4818 return runtime.HALT;
4819 },
4820 };
4821 return returnCmd;
4822 };
4823
4824 _parser.addCommand("return", function (parser, runtime, tokens) {
4825 if (tokens.matchToken("return")) {
4826 return parseReturnFunction(parser, runtime, tokens, true);
4827 }
4828 });
4829
4830 _parser.addCommand("exit", function (parser, runtime, tokens) {
4831 if (tokens.matchToken("exit")) {
4832 return parseReturnFunction(parser, runtime, tokens, false);
4833 }
4834 });
4835
4836 _parser.addCommand("halt", function (parser, runtime, tokens) {
4837 if (tokens.matchToken("halt")) {
4838 if (tokens.matchToken("the")) {
4839 tokens.requireToken("event");
4840 // optional possessive
4841 if (tokens.matchOpToken("'")) {
4842 tokens.requireToken("s");
4843 }
4844 var keepExecuting = true;
4845 }
4846 if (tokens.matchToken("bubbling")) {
4847 var bubbling = true;
4848 } else if (tokens.matchToken("default")) {
4849 var haltDefault = true;
4850 }
4851 var exit = parseReturnFunction(parser, runtime, tokens, false);
4852
4853 var haltCmd = {
4854 keepExecuting: true,
4855 bubbling: bubbling,
4856 haltDefault: haltDefault,
4857 exit: exit,
4858 op: function (ctx) {
4859 if (ctx.event) {
4860 if (bubbling) {
4861 ctx.event.stopPropagation();
4862 } else if (haltDefault) {
4863 ctx.event.preventDefault();
4864 } else {
4865 ctx.event.stopPropagation();
4866 ctx.event.preventDefault();
4867 }
4868 if (keepExecuting) {
4869 return runtime.findNext(this, ctx);
4870 } else {
4871 return exit;
4872 }
4873 }
4874 },
4875 };
4876 return haltCmd;
4877 }
4878 });
4879
4880 _parser.addCommand("log", function (parser, runtime, tokens) {
4881 if (!tokens.matchToken("log")) return;
4882 var exprs = [parser.parseElement("expression", tokens)];
4883 while (tokens.matchOpToken(",")) {
4884 exprs.push(parser.requireElement("expression", tokens));
4885 }
4886 if (tokens.matchToken("with")) {
4887 var withExpr = parser.requireElement("expression", tokens);
4888 }
4889 var logCmd = {
4890 exprs: exprs,
4891 withExpr: withExpr,
4892 args: [withExpr, exprs],
4893 op: function (ctx, withExpr, values) {
4894 if (withExpr) {
4895 withExpr.apply(null, values);
4896 } else {
4897 console.log.apply(null, values);
4898 }
4899 return runtime.findNext(this, ctx);
4900 },
4901 };
4902 return logCmd;
4903 });
4904
4905 _parser.addCommand("throw", function (parser, runtime, tokens) {
4906 if (!tokens.matchToken("throw")) return;
4907 var expr = parser.requireElement("expression", tokens);
4908 var throwCmd = {
4909 expr: expr,
4910 args: [expr],
4911 op: function (ctx, expr) {
4912 runtime.registerHyperTrace(ctx, expr);
4913 throw expr;
4914 },
4915 };
4916 return throwCmd;
4917 });
4918
4919 var parseCallOrGet = function (parser, runtime, tokens) {
4920 var expr = parser.requireElement("expression", tokens);
4921 var callCmd = {
4922 expr: expr,
4923 args: [expr],
4924 op: function (context, result) {
4925 context.result = result;
4926 return runtime.findNext(callCmd, context);
4927 },
4928 };
4929 return callCmd;
4930 };
4931 _parser.addCommand("call", function (parser, runtime, tokens) {
4932 if (!tokens.matchToken("call")) return;
4933 var call = parseCallOrGet(parser, runtime, tokens);
4934 if (call.expr && call.expr.type !== "functionCall") {
4935 parser.raiseParseError(tokens, "Must be a function invocation");
4936 }
4937 return call;
4938 });
4939 _parser.addCommand("get", function (parser, runtime, tokens) {
4940 if (tokens.matchToken("get")) {
4941 return parseCallOrGet(parser, runtime, tokens);
4942 }
4943 });
4944
4945 _parser.addCommand("make", function (parser, runtime, tokens) {
4946 if (!tokens.matchToken("make")) return;
4947 tokens.matchToken("a") || tokens.matchToken("an");
4948
4949 var expr = parser.requireElement("expression", tokens);
4950
4951 var args = [];
4952 if (expr.type !== "queryRef" && tokens.matchToken("from")) {
4953 do {
4954 args.push(parser.requireElement("expression", tokens));
4955 } while (tokens.matchOpToken(","));
4956 }
4957
4958 if (tokens.matchToken("called")) {
4959 var target = parser.requireElement("symbol", tokens);
4960 }
4961
4962 var command;
4963 if (expr.type === "queryRef") {
4964 command = {
4965 op: function (ctx) {
4966 var match,
4967 tagname = "div",
4968 id,
4969 classes = [];
4970 var re = /(?:(^|#|\.)([^#\. ]+))/g;
4971 while ((match = re.exec(expr.css))) {
4972 if (match[1] === "") tagname = match[2].trim();
4973 else if (match[1] === "#") id = match[2].trim();
4974 else classes.push(match[2].trim());
4975 }
4976
4977 var result = document.createElement(tagname);
4978 if (id !== undefined) result.id = id;
4979 for (var i = 0; i < classes.length; i++) {
4980 var cls = classes[i];
4981 result.classList.add(cls)
4982 }
4983
4984 ctx.result = result;
4985 if (target){
4986 runtime.setSymbol(target.name, ctx, target.scope, result);
4987 }
4988
4989 return runtime.findNext(this, ctx);
4990 },
4991 };
4992 return command;
4993 } else {
4994 command = {
4995 args: [expr, args],
4996 op: function (ctx, expr, args) {
4997 ctx.result = varargConstructor(expr, args);
4998 if (target){
4999 runtime.setSymbol(target.name, ctx, target.scope, ctx.result);
5000 }
5001
5002 return runtime.findNext(this, ctx);
5003 },
5004 };
5005 return command;
5006 }
5007 });
5008
5009 _parser.addGrammarElement("pseudoCommand", function (parser, runtime, tokens) {
5010
5011 let lookAhead = tokens.token(1);
5012 if (!(lookAhead && lookAhead.op && (lookAhead.value === '.' || lookAhead.value === "("))) {
5013 return null;
5014 }
5015
5016 var expr = parser.requireElement("primaryExpression", tokens);
5017
5018 var rootRoot = expr.root;
5019 var root = expr;
5020 while (rootRoot.root != null) {
5021 root = root.root;
5022 rootRoot = rootRoot.root;
5023 }
5024
5025 if (expr.type !== "functionCall") {
5026 parser.raiseParseError(tokens, "Pseudo-commands must be function calls");
5027 }
5028
5029 if (root.type === "functionCall" && root.root.root == null) {
5030 if (tokens.matchAnyToken("the", "to", "on", "with", "into", "from", "at")) {
5031 var realRoot = parser.requireElement("expression", tokens);
5032 } else if (tokens.matchToken("me")) {
5033 var realRoot = parser.requireElement("implicitMeTarget", tokens);
5034 }
5035 }
5036
5037 /** @type {GrammarElement} */
5038
5039 if(realRoot){
5040 var pseudoCommand = {
5041 type: "pseudoCommand",
5042 root: realRoot,
5043 argExressions: root.argExressions,
5044 args: [realRoot, root.argExressions],
5045 op: function (context, rootRoot, args) {
5046 runtime.nullCheck(rootRoot, realRoot);
5047 var func = rootRoot[root.root.name];
5048 runtime.nullCheck(func, root);
5049 if (func.hyperfunc) {
5050 args.push(context);
5051 }
5052 context.result = func.apply(rootRoot, args);
5053 return runtime.findNext(pseudoCommand, context);
5054 },
5055 execute: function (context) {
5056 return runtime.unifiedExec(this, context);
5057 },
5058 }
5059 } else {
5060 var pseudoCommand = {
5061 type: "pseudoCommand",
5062 expr: expr,
5063 args: [expr],
5064 op: function (context, result) {
5065 context.result = result;
5066 return runtime.findNext(pseudoCommand, context);
5067 },
5068 execute: function (context) {
5069 return runtime.unifiedExec(this, context);
5070 },
5071 };
5072 }
5073
5074 return pseudoCommand;
5075 });
5076
5077 /**
5078 * @param {ParserObject} parser
5079 * @param {RuntimeObject} runtime
5080 * @param {TokensObject} tokens
5081 * @param {*} target
5082 * @param {*} value
5083 * @returns
5084 */
5085 var makeSetter = function (parser, runtime, tokens, target, value) {
5086
5087 var symbolWrite = target.type === "symbol";
5088 var attributeWrite = target.type === "attributeRef";
5089 var styleWrite = target.type === "styleRef";
5090 var arrayWrite = target.type === "arrayIndex";
5091
5092 if (!(attributeWrite || styleWrite || symbolWrite) && target.root == null) {
5093 parser.raiseParseError(tokens, "Can only put directly into symbols, not references");
5094 }
5095
5096 var rootElt = null;
5097 var prop = null;
5098 if (symbolWrite) {
5099 // rootElt is null
5100 } else if (attributeWrite || styleWrite) {
5101 rootElt = parser.requireElement("implicitMeTarget", tokens);
5102 var attribute = target;
5103 } else if(arrayWrite) {
5104 prop = target.firstIndex;
5105 rootElt = target.root;
5106 } else {
5107 prop = target.prop ? target.prop.value : null;
5108 var attribute = target.attribute;
5109 rootElt = target.root;
5110 }
5111
5112 /** @type {GrammarElement} */
5113 var setCmd = {
5114 target: target,
5115 symbolWrite: symbolWrite,
5116 value: value,
5117 args: [rootElt, prop, value],
5118 op: function (context, root, prop, valueToSet) {
5119 if (symbolWrite) {
5120 runtime.setSymbol(target.name, context, target.scope, valueToSet);
5121 } else {
5122 runtime.nullCheck(root, rootElt);
5123 if (arrayWrite) {
5124 root[prop] = valueToSet;
5125 } else {
5126 runtime.implicitLoop(root, function (elt) {
5127 if (attribute) {
5128 if (attribute.type === "attributeRef") {
5129 if (valueToSet == null) {
5130 elt.removeAttribute(attribute.name);
5131 } else {
5132 elt.setAttribute(attribute.name, valueToSet);
5133 }
5134 } else {
5135 elt.style[attribute.name] = valueToSet;
5136 }
5137 } else {
5138 elt[prop] = valueToSet;
5139 }
5140 });
5141 }
5142 }
5143 return runtime.findNext(this, context);
5144 },
5145 };
5146 return setCmd;
5147 };
5148
5149 _parser.addCommand("default", function (parser, runtime, tokens) {
5150 if (!tokens.matchToken("default")) return;
5151 var target = parser.requireElement("assignableExpression", tokens);
5152 tokens.requireToken("to");
5153
5154 var value = parser.requireElement("expression", tokens);
5155
5156 /** @type {GrammarElement} */
5157 var setter = makeSetter(parser, runtime, tokens, target, value);
5158 var defaultCmd = {
5159 target: target,
5160 value: value,
5161 setter: setter,
5162 args: [target],
5163 op: function (context, target) {
5164 if (target) {
5165 return runtime.findNext(this, context);
5166 } else {
5167 return setter;
5168 }
5169 },
5170 };
5171 setter.parent = defaultCmd;
5172 return defaultCmd;
5173 });
5174
5175 _parser.addCommand("set", function (parser, runtime, tokens) {
5176 if (!tokens.matchToken("set")) return;
5177 if (tokens.currentToken().type === "L_BRACE") {
5178 var obj = parser.requireElement("objectLiteral", tokens);
5179 tokens.requireToken("on");
5180 var target = parser.requireElement("expression", tokens);
5181
5182 var command = {
5183 objectLiteral: obj,
5184 target: target,
5185 args: [obj, target],
5186 op: function (ctx, obj, target) {
5187 mergeObjects(target, obj);
5188 return runtime.findNext(this, ctx);
5189 },
5190 };
5191 return command;
5192 }
5193
5194 try {
5195 tokens.pushFollow("to");
5196 var target = parser.requireElement("assignableExpression", tokens);
5197 } finally {
5198 tokens.popFollow();
5199 }
5200 tokens.requireToken("to");
5201 var value = parser.requireElement("expression", tokens);
5202 return makeSetter(parser, runtime, tokens, target, value);
5203 });
5204
5205 _parser.addCommand("if", function (parser, runtime, tokens) {
5206 if (!tokens.matchToken("if")) return;
5207 var expr = parser.requireElement("expression", tokens);
5208 tokens.matchToken("then"); // optional 'then'
5209 var trueBranch = parser.parseElement("commandList", tokens);
5210 if (tokens.matchToken("else") || tokens.matchToken("otherwise")) {
5211 var falseBranch = parser.parseElement("commandList", tokens);
5212 }
5213 if (tokens.hasMore()) {
5214 tokens.requireToken("end");
5215 }
5216
5217 /** @type {GrammarElement} */
5218 var ifCmd = {
5219 expr: expr,
5220 trueBranch: trueBranch,
5221 falseBranch: falseBranch,
5222 args: [expr],
5223 op: function (context, exprValue) {
5224 if (exprValue) {
5225 return trueBranch;
5226 } else if (falseBranch) {
5227 return falseBranch;
5228 } else {
5229 return runtime.findNext(this, context);
5230 }
5231 },
5232 };
5233 parser.setParent(trueBranch, ifCmd);
5234 parser.setParent(falseBranch, ifCmd);
5235 return ifCmd;
5236 });
5237
5238 var parseRepeatExpression = function (parser, tokens, runtime, startedWithForToken) {
5239 var innerStartToken = tokens.currentToken();
5240 var identifier;
5241 if (tokens.matchToken("for") || startedWithForToken) {
5242 var identifierToken = tokens.requireTokenType("IDENTIFIER");
5243 identifier = identifierToken.value;
5244 tokens.requireToken("in");
5245 var expression = parser.requireElement("expression", tokens);
5246 } else if (tokens.matchToken("in")) {
5247 identifier = "it";
5248 var expression = parser.requireElement("expression", tokens);
5249 } else if (tokens.matchToken("while")) {
5250 var whileExpr = parser.requireElement("expression", tokens);
5251 } else if (tokens.matchToken("until")) {
5252 var isUntil = true;
5253 if (tokens.matchToken("event")) {
5254 var evt = _parser.requireElement("dotOrColonPath", tokens, "Expected event name");
5255 if (tokens.matchToken("from")) {
5256 var on = parser.requireElement("expression", tokens);
5257 }
5258 } else {
5259 var whileExpr = parser.requireElement("expression", tokens);
5260 }
5261 } else {
5262 if (!parser.commandBoundary(tokens.currentToken()) &&
5263 tokens.currentToken().value !== 'forever') {
5264 var times = parser.requireElement("expression", tokens);
5265 tokens.requireToken("times");
5266 } else {
5267 tokens.matchToken("forever"); // consume optional forever
5268 var forever = true;
5269 }
5270 }
5271
5272 if (tokens.matchToken("index")) {
5273 var identifierToken = tokens.requireTokenType("IDENTIFIER");
5274 var indexIdentifier = identifierToken.value;
5275 }
5276
5277 var loop = parser.parseElement("commandList", tokens);
5278 if (loop && evt) {
5279 // if this is an event based loop, wait a tick at the end of the loop so that
5280 // events have a chance to trigger in the loop condition o_O)))
5281 var last = loop;
5282 while (last.next) {
5283 last = last.next;
5284 }
5285 var waitATick = {
5286 type: "waitATick",
5287 op: function () {
5288 return new Promise(function (resolve) {
5289 setTimeout(function () {
5290 resolve(runtime.findNext(waitATick));
5291 }, 0);
5292 });
5293 },
5294 };
5295 last.next = waitATick;
5296 }
5297 if (tokens.hasMore()) {
5298 tokens.requireToken("end");
5299 }
5300
5301 if (identifier == null) {
5302 identifier = "_implicit_repeat_" + innerStartToken.start;
5303 var slot = identifier;
5304 } else {
5305 var slot = identifier + "_" + innerStartToken.start;
5306 }
5307
5308 var repeatCmd = {
5309 identifier: identifier,
5310 indexIdentifier: indexIdentifier,
5311 slot: slot,
5312 expression: expression,
5313 forever: forever,
5314 times: times,
5315 until: isUntil,
5316 event: evt,
5317 on: on,
5318 whileExpr: whileExpr,
5319 resolveNext: function () {
5320 return this;
5321 },
5322 loop: loop,
5323 args: [whileExpr, times],
5324 op: function (context, whileValue, times) {
5325 var iteratorInfo = context.meta.iterators[slot];
5326 var keepLooping = false;
5327 var loopVal = null;
5328 if (this.forever) {
5329 keepLooping = true;
5330 } else if (this.until) {
5331 if (evt) {
5332 keepLooping = context.meta.iterators[slot].eventFired === false;
5333 } else {
5334 keepLooping = whileValue !== true;
5335 }
5336 } else if (whileExpr) {
5337 keepLooping = whileValue;
5338 } else if (times) {
5339 keepLooping = iteratorInfo.index < times;
5340 } else {
5341 var nextValFromIterator = iteratorInfo.iterator.next();
5342 keepLooping = !nextValFromIterator.done;
5343 loopVal = nextValFromIterator.value;
5344 }
5345
5346 if (keepLooping) {
5347 if (iteratorInfo.value) {
5348 context.result = context[identifier] = loopVal;
5349 } else {
5350 context.result = iteratorInfo.index;
5351 }
5352 if (indexIdentifier) {
5353 context[indexIdentifier] = iteratorInfo.index;
5354 }
5355 iteratorInfo.index++;
5356 return loop;
5357 } else {
5358 context.meta.iterators[slot] = null;
5359 return runtime.findNext(this.parent, context);
5360 }
5361 },
5362 };
5363 parser.setParent(loop, repeatCmd);
5364 var repeatInit = {
5365 name: "repeatInit",
5366 args: [expression, evt, on],
5367 op: function (context, value, event, on) {
5368 var iteratorInfo = {
5369 index: 0,
5370 value: value,
5371 eventFired: false,
5372 };
5373 context.meta.iterators[slot] = iteratorInfo;
5374 if (value && value[Symbol.iterator]) {
5375 iteratorInfo.iterator = value[Symbol.iterator]();
5376 }
5377 if (evt) {
5378 var target = on || context.me;
5379 target.addEventListener(
5380 event,
5381 function (e) {
5382 context.meta.iterators[slot].eventFired = true;
5383 },
5384 { once: true }
5385 );
5386 }
5387 return repeatCmd; // continue to loop
5388 },
5389 execute: function (context) {
5390 return runtime.unifiedExec(this, context);
5391 },
5392 };
5393 parser.setParent(repeatCmd, repeatInit);
5394 return repeatInit;
5395 };
5396
5397 _parser.addCommand("repeat", function (parser, runtime, tokens) {
5398 if (tokens.matchToken("repeat")) {
5399 return parseRepeatExpression(parser, tokens, runtime, false);
5400 }
5401 });
5402
5403 _parser.addCommand("for", function (parser, runtime, tokens) {
5404 if (tokens.matchToken("for")) {
5405 return parseRepeatExpression(parser, tokens, runtime, true);
5406 }
5407 });
5408
5409 _parser.addCommand("continue", function (parser, runtime, tokens) {
5410
5411 if (!tokens.matchToken("continue")) return;
5412
5413 var command = {
5414 op: function (context) {
5415
5416 // scan for the closest repeat statement
5417 for (var parent = this.parent ; true ; parent = parent.parent) {
5418
5419 if (parent == undefined) {
5420 parser.raiseParseError(tokens, "Command `continue` cannot be used outside of a `repeat` loop.")
5421 }
5422 if (parent.loop != undefined) {
5423 return parent.resolveNext(context)
5424 }
5425 }
5426 }
5427 };
5428 return command;
5429 });
5430
5431 _parser.addCommand("break", function (parser, runtime, tokens) {
5432
5433 if (!tokens.matchToken("break")) return;
5434
5435 var command = {
5436 op: function (context) {
5437
5438 // scan for the closest repeat statement
5439 for (var parent = this.parent ; true ; parent = parent.parent) {
5440
5441 if (parent == undefined) {
5442 parser.raiseParseError(tokens, "Command `continue` cannot be used outside of a `repeat` loop.")
5443 }
5444 if (parent.loop != undefined) {
5445 return runtime.findNext(parent.parent, context);
5446 }
5447 }
5448 }
5449 };
5450 return command;
5451 });
5452
5453 _parser.addGrammarElement("stringLike", function (parser, runtime, tokens) {
5454 return _parser.parseAnyOf(["string", "nakedString"], tokens);
5455 });
5456
5457 _parser.addCommand("append", function (parser, runtime, tokens) {
5458 if (!tokens.matchToken("append")) return;
5459 var targetExpr = null;
5460
5461 var value = parser.requireElement("expression", tokens);
5462
5463 var implicitResultSymbol = {
5464 type: "symbol",
5465 evaluate: function (context) {
5466 return runtime.resolveSymbol("result", context);
5467 },
5468 };
5469
5470 if (tokens.matchToken("to")) {
5471 targetExpr = parser.requireElement("expression", tokens);
5472 } else {
5473 targetExpr = implicitResultSymbol;
5474 }
5475
5476 var setter = null;
5477 if (targetExpr.type === "symbol" || targetExpr.type === "attributeRef" || targetExpr.root != null) {
5478 setter = makeSetter(parser, runtime, tokens, targetExpr, implicitResultSymbol);
5479 }
5480
5481 var command = {
5482 value: value,
5483 target: targetExpr,
5484 args: [targetExpr, value],
5485 op: function (context, target, value) {
5486 if (Array.isArray(target)) {
5487 target.push(value);
5488 return runtime.findNext(this, context);
5489 } else if (target instanceof Element) {
5490 target.innerHTML += value;
5491 return runtime.findNext(this, context);
5492 } else if(setter) {
5493 context.result = (target || "") + value;
5494 return setter;
5495 } else {
5496 throw Error("Unable to append a value!")
5497 }
5498 },
5499 execute: function (context) {
5500 return runtime.unifiedExec(this, context/*, value, target*/);
5501 },
5502 };
5503
5504 if (setter != null) {
5505 setter.parent = command;
5506 }
5507
5508 return command;
5509 });
5510
5511 _parser.addCommand("increment", function (parser, runtime, tokens) {
5512 if (!tokens.matchToken("increment")) return;
5513 var amount;
5514
5515 // This is optional. Defaults to "result"
5516 var target = parser.parseElement("assignableExpression", tokens);
5517
5518 // This is optional. Defaults to 1.
5519 if (tokens.matchToken("by")) {
5520 amount = parser.requireElement("expression", tokens);
5521 }
5522
5523 var implicitIncrementOp = {
5524 type: "implicitIncrementOp",
5525 target: target,
5526 args: [target, amount],
5527 op: function (context, targetValue, amount) {
5528 targetValue = targetValue ? parseFloat(targetValue) : 0;
5529 amount = amount ? parseFloat(amount) : 1;
5530 var newValue = targetValue + amount;
5531 context.result = newValue;
5532 return newValue;
5533 },
5534 evaluate: function (context) {
5535 return runtime.unifiedEval(this, context);
5536 }
5537 };
5538
5539 return makeSetter(parser, runtime, tokens, target, implicitIncrementOp);
5540 });
5541
5542 _parser.addCommand("decrement", function (parser, runtime, tokens) {
5543 if (!tokens.matchToken("decrement")) return;
5544 var amount;
5545
5546 // This is optional. Defaults to "result"
5547 var target = parser.parseElement("assignableExpression", tokens);
5548
5549 // This is optional. Defaults to 1.
5550 if (tokens.matchToken("by")) {
5551 amount = parser.requireElement("expression", tokens);
5552 }
5553
5554 var implicitDecrementOp = {
5555 type: "implicitDecrementOp",
5556 target: target,
5557 args: [target, amount],
5558 op: function (context, targetValue, amount) {
5559 targetValue = targetValue ? parseFloat(targetValue) : 0;
5560 amount = amount ? parseFloat(amount) : 1;
5561 var newValue = targetValue - amount;
5562 context.result = newValue;
5563 return newValue;
5564 },
5565 evaluate: function (context) {
5566 return runtime.unifiedEval(this, context);
5567 }
5568 };
5569
5570 return makeSetter(parser, runtime, tokens, target, implicitDecrementOp);
5571 });
5572
5573 function parseConversionInfo(tokens, parser) {
5574 var type = "text";
5575 var conversion;
5576 tokens.matchToken("a") || tokens.matchToken("an");
5577 if (tokens.matchToken("json") || tokens.matchToken("Object")) {
5578 type = "json";
5579 } else if (tokens.matchToken("response")) {
5580 type = "response";
5581 } else if (tokens.matchToken("html")) {
5582 type = "html";
5583 } else if (tokens.matchToken("text")) {
5584 // default, ignore
5585 } else {
5586 conversion = parser.requireElement("dotOrColonPath", tokens).evaluate();
5587 }
5588 return {type, conversion};
5589 }
5590
5591 _parser.addCommand("fetch", function (parser, runtime, tokens) {
5592 if (!tokens.matchToken("fetch")) return;
5593 var url = parser.requireElement("stringLike", tokens);
5594
5595 if (tokens.matchToken("as")) {
5596 var conversionInfo = parseConversionInfo(tokens, parser);
5597 }
5598
5599 if (tokens.matchToken("with") && tokens.currentToken().value !== "{") {
5600 var args = parser.parseElement("nakedNamedArgumentList", tokens);
5601 } else {
5602 var args = parser.parseElement("objectLiteral", tokens);
5603 }
5604
5605 if (conversionInfo == null && tokens.matchToken("as")) {
5606 conversionInfo = parseConversionInfo(tokens, parser);
5607 }
5608
5609 var type = conversionInfo ? conversionInfo.type : "text";
5610 var conversion = conversionInfo ? conversionInfo.conversion : null
5611
5612 /** @type {GrammarElement} */
5613 var fetchCmd = {
5614 url: url,
5615 argExpressions: args,
5616 args: [url, args],
5617 op: function (context, url, args) {
5618 var detail = args || {};
5619 detail["sender"] = context.me;
5620 detail["headers"] = detail["headers"] || {}
5621 var abortController = new AbortController();
5622 let abortListener = context.me.addEventListener('fetch:abort', function(){
5623 abortController.abort();
5624 }, {once: true});
5625 detail['signal'] = abortController.signal;
5626 runtime.triggerEvent(context.me, "hyperscript:beforeFetch", detail);
5627 runtime.triggerEvent(context.me, "fetch:beforeRequest", detail);
5628 args = detail;
5629 var finished = false;
5630 if (args.timeout) {
5631 setTimeout(function () {
5632 if (!finished) {
5633 abortController.abort();
5634 }
5635 }, args.timeout);
5636 }
5637 return fetch(url, args)
5638 .then(function (resp) {
5639 let resultDetails = {response:resp};
5640 runtime.triggerEvent(context.me, "fetch:afterResponse", resultDetails);
5641 resp = resultDetails.response;
5642
5643 if (type === "response") {
5644 context.result = resp;
5645 runtime.triggerEvent(context.me, "fetch:afterRequest", {result:resp});
5646 finished = true;
5647 return runtime.findNext(fetchCmd, context);
5648 }
5649 if (type === "json") {
5650 return resp.json().then(function (result) {
5651 context.result = result;
5652 runtime.triggerEvent(context.me, "fetch:afterRequest", {result});
5653 finished = true;
5654 return runtime.findNext(fetchCmd, context);
5655 });
5656 }
5657 return resp.text().then(function (result) {
5658 if (conversion) result = runtime.convertValue(result, conversion);
5659
5660 if (type === "html") result = runtime.convertValue(result, "Fragment");
5661
5662 context.result = result;
5663 runtime.triggerEvent(context.me, "fetch:afterRequest", {result});
5664 finished = true;
5665 return runtime.findNext(fetchCmd, context);
5666 });
5667 })
5668 .catch(function (reason) {
5669 runtime.triggerEvent(context.me, "fetch:error", {
5670 reason: reason,
5671 });
5672 throw reason;
5673 }).finally(function(){
5674 context.me.removeEventListener('fetch:abort', abortListener);
5675 });
5676 },
5677 };
5678 return fetchCmd;
5679 });
5680}
5681
5682//====================================================================
5683// Initialization
5684//====================================================================
5685function ready(fn) {
5686 if (document.readyState !== "loading") {
5687 setTimeout(fn);
5688 } else {
5689 document.addEventListener("DOMContentLoaded", fn);
5690 }
5691}
5692
5693function getMetaConfig() {
5694 /** @type {HTMLMetaElement} */
5695 var element = document.querySelector('meta[name="htmx-config"]');
5696 if (element) {
5697 return parseJSON(element.content);
5698 } else {
5699 return null;
5700 }
5701}
5702
5703function mergeMetaConfig() {
5704 var metaConfig = getMetaConfig();
5705 if (metaConfig) {
5706 _hyperscript.config = mergeObjects(_hyperscript.config, metaConfig);
5707 }
5708}
5709
5710if ("document" in globalScope) {
5711 /** @type {HTMLScriptElement[]} */
5712 var scripts = Array.from(document.querySelectorAll("script[type='text/hyperscript'][src]"))
5713 Promise.all(
5714 scripts.map(function (script) {
5715 return fetch(script.src)
5716 .then(function (res) {
5717 return res.text();
5718 });
5719 })
5720 )
5721 .then(function (script_values) {
5722 // forEach instead of map, since the return value of .evaluate is not used by the then
5723 return script_values.forEach(_runtime.evaluate);
5724 })
5725 .then(function () {
5726 ready(function () {
5727 mergeMetaConfig();
5728 _runtime.processNode(document.documentElement);
5729 document.addEventListener("htmx:load", function (/** @type {CustomEvent} */ evt) {
5730
5731 _runtime.processNode(evt.detail.elt);
5732 });
5733 });
5734 });
5735}
5736
5737//====================================================================
5738// API
5739//====================================================================
5740/** @type {HyperscriptObject} */
5741export default _hyperscript = mergeObjects(
5742 function (str, ctx) {
5743 return _runtime.evaluate(str, ctx); //OK
5744 },
5745 {
5746 internals: {
5747 lexer: _lexer,
5748 parser: _parser,
5749 runtime: _runtime,
5750 },
5751 ElementCollection: ElementCollection,
5752 addFeature: function (keyword, definition) {
5753 _parser.addFeature(keyword, definition);
5754 },
5755 addCommand: function (keyword, definition) {
5756 _parser.addCommand(keyword, definition);
5757 },
5758 addLeafExpression: function (name, definition) {
5759 _parser.addLeafExpression(name, definition);
5760 },
5761 addIndirectExpression: function (name, definition) {
5762 _parser.addIndirectExpression(name, definition);
5763 },
5764 evaluate: _runtime.evaluate.bind(_runtime),
5765 parse: _runtime.parse.bind(_runtime),
5766 processNode: _runtime.processNode.bind(_runtime),
5767 config: {
5768 attributes: "_, script, data-script",
5769 defaultTransition: "all 500ms ease-in",
5770 disableSelector: "[disable-scripting], [data-disable-scripting]",
5771 conversions: CONVERSIONS,
5772 },
5773 }
5774);