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