UNPKG

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