UNPKG

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