UNPKG

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