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