diff --git a/app.py b/app.py
index 3e1577b..a47abe2 100644
--- a/app.py
+++ b/app.py
@@ -194,21 +194,9 @@ def upload_file():
bucket_name, uploaded_file.filename, uploaded_file, size
)
- return """
-
stored
-
- """
+ return "OK"
- return """
- Upload IPS File
-
- """
+ return " NE OK "
diff --git a/static/src/_hyperscript.js b/static/src/_hyperscript.js
new file mode 100644
index 0000000..a41b6be
--- /dev/null
+++ b/static/src/_hyperscript.js
@@ -0,0 +1,7620 @@
+/**
+ * @typedef {Object} Hyperscript
+ */
+
+(function (self, factory) {
+ const _hyperscript = factory(self)
+
+ if (typeof exports === 'object' && typeof exports['nodeName'] !== 'string') {
+ module.exports = _hyperscript
+ } else {
+ self['_hyperscript'] = _hyperscript
+ if ('document' in self) self['_hyperscript'].browserInit()
+ }
+})(typeof self !== 'undefined' ? self : this, (globalScope) => {
+
+ 'use strict';
+
+ /**
+ * @type {Object}
+ * @property {DynamicConverter[]} dynamicResolvers
+ *
+ * @callback DynamicConverter
+ * @param {String} str
+ * @param {*} value
+ * @returns {*}
+ */
+ const conversions = {
+ dynamicResolvers: [
+ function(str, value){
+ if (str === "Fixed") {
+ return Number(value).toFixed();
+ } else if (str.indexOf("Fixed:") === 0) {
+ let num = str.split(":")[1];
+ return Number(value).toFixed(parseInt(num));
+ }
+ }
+ ],
+ String: function (val) {
+ if (val.toString) {
+ return val.toString();
+ } else {
+ return "" + val;
+ }
+ },
+ Int: function (val) {
+ return parseInt(val);
+ },
+ Float: function (val) {
+ return parseFloat(val);
+ },
+ Number: function (val) {
+ return Number(val);
+ },
+ Date: function (val) {
+ return new Date(val);
+ },
+ Array: function (val) {
+ return Array.from(val);
+ },
+ JSON: function (val) {
+ return JSON.stringify(val);
+ },
+ Object: function (val) {
+ if (val instanceof String) {
+ val = val.toString();
+ }
+ if (typeof val === "string") {
+ return JSON.parse(val);
+ } else {
+ return Object.assign({}, val);
+ }
+ },
+ }
+
+ const config = {
+ attributes: "_, script, data-script",
+ defaultTransition: "all 500ms ease-in",
+ disableSelector: "[disable-scripting], [data-disable-scripting]",
+ hideShowStrategies: {},
+ conversions,
+ }
+
+ class Lexer {
+ static OP_TABLE = {
+ "+": "PLUS",
+ "-": "MINUS",
+ "*": "MULTIPLY",
+ "/": "DIVIDE",
+ ".": "PERIOD",
+ "..": "ELLIPSIS",
+ "\\": "BACKSLASH",
+ ":": "COLON",
+ "%": "PERCENT",
+ "|": "PIPE",
+ "!": "EXCLAMATION",
+ "?": "QUESTION",
+ "#": "POUND",
+ "&": "AMPERSAND",
+ $: "DOLLAR",
+ ";": "SEMI",
+ ",": "COMMA",
+ "(": "L_PAREN",
+ ")": "R_PAREN",
+ "<": "L_ANG",
+ ">": "R_ANG",
+ "<=": "LTE_ANG",
+ ">=": "GTE_ANG",
+ "==": "EQ",
+ "===": "EQQ",
+ "!=": "NEQ",
+ "!==": "NEQQ",
+ "{": "L_BRACE",
+ "}": "R_BRACE",
+ "[": "L_BRACKET",
+ "]": "R_BRACKET",
+ "=": "EQUALS",
+ };
+
+ /**
+ * isValidCSSClassChar returns `true` if the provided character is valid in a CSS class.
+ * @param {string} c
+ * @returns boolean
+ */
+ static isValidCSSClassChar(c) {
+ return Lexer.isAlpha(c) || Lexer.isNumeric(c) || c === "-" || c === "_" || c === ":";
+ }
+
+ /**
+ * isValidCSSIDChar returns `true` if the provided character is valid in a CSS ID
+ * @param {string} c
+ * @returns boolean
+ */
+ static isValidCSSIDChar(c) {
+ return Lexer.isAlpha(c) || Lexer.isNumeric(c) || c === "-" || c === "_" || c === ":";
+ }
+
+ /**
+ * isWhitespace returns `true` if the provided character is whitespace.
+ * @param {string} c
+ * @returns boolean
+ */
+ static isWhitespace(c) {
+ return c === " " || c === "\t" || Lexer.isNewline(c);
+ }
+
+ /**
+ * positionString returns a string representation of a Token's line and column details.
+ * @param {Token} token
+ * @returns string
+ */
+ static positionString(token) {
+ return "[Line: " + token.line + ", Column: " + token.column + "]";
+ }
+
+ /**
+ * isNewline returns `true` if the provided character is a carrage return or newline
+ * @param {string} c
+ * @returns boolean
+ */
+ static isNewline(c) {
+ return c === "\r" || c === "\n";
+ }
+
+ /**
+ * isNumeric returns `true` if the provided character is a number (0-9)
+ * @param {string} c
+ * @returns boolean
+ */
+ static isNumeric(c) {
+ return c >= "0" && c <= "9";
+ }
+
+ /**
+ * isAlpha returns `true` if the provided character is a letter in the alphabet
+ * @param {string} c
+ * @returns boolean
+ */
+ static isAlpha(c) {
+ return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z");
+ }
+
+ /**
+ * @param {string} c
+ * @param {boolean} [dollarIsOp]
+ * @returns boolean
+ */
+ static isIdentifierChar(c, dollarIsOp) {
+ return c === "_" || c === "$";
+ }
+
+ /**
+ * @param {string} c
+ * @returns boolean
+ */
+ static isReservedChar(c) {
+ return c === "`" || c === "^";
+ }
+
+ /**
+ * @param {Token[]} tokens
+ * @returns {boolean}
+ */
+ static isValidSingleQuoteStringStart(tokens) {
+ if (tokens.length > 0) {
+ var previousToken = tokens[tokens.length - 1];
+ if (
+ previousToken.type === "IDENTIFIER" ||
+ previousToken.type === "CLASS_REF" ||
+ previousToken.type === "ID_REF"
+ ) {
+ return false;
+ }
+ if (previousToken.op && (previousToken.value === ">" || previousToken.value === ")")) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @param {string} string
+ * @param {boolean} [template]
+ * @returns {Tokens}
+ */
+ static tokenize(string, template) {
+ var tokens = /** @type {Token[]}*/ [];
+ var source = string;
+ var position = 0;
+ var column = 0;
+ var line = 1;
+ var lastToken = "";
+ var templateBraceCount = 0;
+
+ function inTemplate() {
+ return template && templateBraceCount === 0;
+ }
+
+ while (position < source.length) {
+ if ((currentChar() === "-" && nextChar() === "-" && (Lexer.isWhitespace(nextCharAt(2)) || nextCharAt(2) === "" || nextCharAt(2) === "-"))
+ || (currentChar() === "/" && nextChar() === "/" && (Lexer.isWhitespace(nextCharAt(2)) || nextCharAt(2) === "" || nextCharAt(2) === "/"))) {
+ consumeComment();
+ } else if (currentChar() === "/" && nextChar() === "*" && (Lexer.isWhitespace(nextCharAt(2)) || nextCharAt(2) === "" || nextCharAt(2) === "*")) {
+ consumeCommentMultiline();
+ } else {
+ if (Lexer.isWhitespace(currentChar())) {
+ tokens.push(consumeWhitespace());
+ } else if (
+ !possiblePrecedingSymbol() &&
+ currentChar() === "." &&
+ (Lexer.isAlpha(nextChar()) || nextChar() === "{" || nextChar() === "-")
+ ) {
+ tokens.push(consumeClassReference());
+ } else if (
+ !possiblePrecedingSymbol() &&
+ currentChar() === "#" &&
+ (Lexer.isAlpha(nextChar()) || nextChar() === "{")
+ ) {
+ tokens.push(consumeIdReference());
+ } else if (currentChar() === "[" && nextChar() === "@") {
+ tokens.push(consumeAttributeReference());
+ } else if (currentChar() === "@") {
+ tokens.push(consumeShortAttributeReference());
+ } else if (currentChar() === "*" && Lexer.isAlpha(nextChar())) {
+ tokens.push(consumeStyleReference());
+ } else if (Lexer.isAlpha(currentChar()) || (!inTemplate() && Lexer.isIdentifierChar(currentChar()))) {
+ tokens.push(consumeIdentifier());
+ } else if (Lexer.isNumeric(currentChar())) {
+ tokens.push(consumeNumber());
+ } else if (!inTemplate() && (currentChar() === '"' || currentChar() === "`")) {
+ tokens.push(consumeString());
+ } else if (!inTemplate() && currentChar() === "'") {
+ if (Lexer.isValidSingleQuoteStringStart(tokens)) {
+ tokens.push(consumeString());
+ } else {
+ tokens.push(consumeOp());
+ }
+ } else if (Lexer.OP_TABLE[currentChar()]) {
+ if (lastToken === "$" && currentChar() === "{") {
+ templateBraceCount++;
+ }
+ if (currentChar() === "}") {
+ templateBraceCount--;
+ }
+ tokens.push(consumeOp());
+ } else if (inTemplate() || Lexer.isReservedChar(currentChar())) {
+ tokens.push(makeToken("RESERVED", consumeChar()));
+ } else {
+ if (position < source.length) {
+ throw Error("Unknown token: " + currentChar() + " ");
+ }
+ }
+ }
+ }
+
+ return new Tokens(tokens, [], source);
+
+ /**
+ * @param {string} [type]
+ * @param {string} [value]
+ * @returns {Token}
+ */
+ function makeOpToken(type, value) {
+ var token = makeToken(type, value);
+ token.op = true;
+ return token;
+ }
+
+ /**
+ * @param {string} [type]
+ * @param {string} [value]
+ * @returns {Token}
+ */
+ function makeToken(type, value) {
+ return {
+ type: type,
+ value: value || "",
+ start: position,
+ end: position + 1,
+ column: column,
+ line: line,
+ };
+ }
+
+ function consumeComment() {
+ while (currentChar() && !Lexer.isNewline(currentChar())) {
+ consumeChar();
+ }
+ consumeChar(); // Consume newline
+ }
+
+ function consumeCommentMultiline() {
+ while (currentChar() && !(currentChar() === '*' && nextChar() === '/')) {
+ consumeChar();
+ }
+ consumeChar(); // Consume "*/"
+ consumeChar();
+ }
+
+ /**
+ * @returns Token
+ */
+ function consumeClassReference() {
+ var classRef = makeToken("CLASS_REF");
+ var value = consumeChar();
+ if (currentChar() === "{") {
+ classRef.template = true;
+ value += consumeChar();
+ while (currentChar() && currentChar() !== "}") {
+ value += consumeChar();
+ }
+ if (currentChar() !== "}") {
+ throw Error("Unterminated class reference");
+ } else {
+ value += consumeChar(); // consume final curly
+ }
+ } else {
+ while (Lexer.isValidCSSClassChar(currentChar())) {
+ value += consumeChar();
+ }
+ }
+ classRef.value = value;
+ classRef.end = position;
+ return classRef;
+ }
+
+ /**
+ * @returns Token
+ */
+ function consumeAttributeReference() {
+ var attributeRef = makeToken("ATTRIBUTE_REF");
+ var value = consumeChar();
+ while (position < source.length && currentChar() !== "]") {
+ value += consumeChar();
+ }
+ if (currentChar() === "]") {
+ value += consumeChar();
+ }
+ attributeRef.value = value;
+ attributeRef.end = position;
+ return attributeRef;
+ }
+
+ function consumeShortAttributeReference() {
+ var attributeRef = makeToken("ATTRIBUTE_REF");
+ var value = consumeChar();
+ while (Lexer.isValidCSSIDChar(currentChar())) {
+ value += consumeChar();
+ }
+ if (currentChar() === '=') {
+ value += consumeChar();
+ if (currentChar() === '"' || currentChar() === "'") {
+ let stringValue = consumeString();
+ value += stringValue.value;
+ } else if(Lexer.isAlpha(currentChar()) ||
+ Lexer.isNumeric(currentChar()) ||
+ Lexer.isIdentifierChar(currentChar())) {
+ let id = consumeIdentifier();
+ value += id.value;
+ }
+ }
+ attributeRef.value = value;
+ attributeRef.end = position;
+ return attributeRef;
+ }
+
+ function consumeStyleReference() {
+ var styleRef = makeToken("STYLE_REF");
+ var value = consumeChar();
+ while (Lexer.isAlpha(currentChar()) || currentChar() === "-") {
+ value += consumeChar();
+ }
+ styleRef.value = value;
+ styleRef.end = position;
+ return styleRef;
+ }
+
+ /**
+ * @returns Token
+ */
+ function consumeIdReference() {
+ var idRef = makeToken("ID_REF");
+ var value = consumeChar();
+ if (currentChar() === "{") {
+ idRef.template = true;
+ value += consumeChar();
+ while (currentChar() && currentChar() !== "}") {
+ value += consumeChar();
+ }
+ if (currentChar() !== "}") {
+ throw Error("Unterminated id reference");
+ } else {
+ consumeChar(); // consume final quote
+ }
+ } else {
+ while (Lexer.isValidCSSIDChar(currentChar())) {
+ value += consumeChar();
+ }
+ }
+ idRef.value = value;
+ idRef.end = position;
+ return idRef;
+ }
+
+ /**
+ * @returns Token
+ */
+ function consumeIdentifier() {
+ var identifier = makeToken("IDENTIFIER");
+ var value = consumeChar();
+ while (Lexer.isAlpha(currentChar()) ||
+ Lexer.isNumeric(currentChar()) ||
+ Lexer.isIdentifierChar(currentChar())) {
+ value += consumeChar();
+ }
+ if (currentChar() === "!" && value === "beep") {
+ value += consumeChar();
+ }
+ identifier.value = value;
+ identifier.end = position;
+ return identifier;
+ }
+
+ /**
+ * @returns Token
+ */
+ function consumeNumber() {
+ var number = makeToken("NUMBER");
+ var value = consumeChar();
+
+ // given possible XXX.YYY(e|E)[-]ZZZ consume XXX
+ while (Lexer.isNumeric(currentChar())) {
+ value += consumeChar();
+ }
+
+ // consume .YYY
+ if (currentChar() === "." && Lexer.isNumeric(nextChar())) {
+ value += consumeChar();
+ }
+ while (Lexer.isNumeric(currentChar())) {
+ value += consumeChar();
+ }
+
+ // consume (e|E)[-]
+ if (currentChar() === "e" || currentChar() === "E") {
+ // possible scientific notation, e.g. 1e6 or 1e-6
+ if (Lexer.isNumeric(nextChar())) {
+ // e.g. 1e6
+ value += consumeChar();
+ } else if (nextChar() === "-") {
+ // e.g. 1e-6
+ value += consumeChar();
+ // consume the - as well since otherwise we would stop on the next loop
+ value += consumeChar();
+ }
+ }
+
+ // consume ZZZ
+ while (Lexer.isNumeric(currentChar())) {
+ value += consumeChar();
+ }
+ number.value = value;
+ number.end = position;
+ return number;
+ }
+
+ /**
+ * @returns Token
+ */
+ function consumeOp() {
+ var op = makeOpToken();
+ var value = consumeChar(); // consume leading char
+ while (currentChar() && Lexer.OP_TABLE[value + currentChar()]) {
+ value += consumeChar();
+ }
+ op.type = Lexer.OP_TABLE[value];
+ op.value = value;
+ op.end = position;
+ return op;
+ }
+
+ /**
+ * @returns Token
+ */
+ function consumeString() {
+ var string = makeToken("STRING");
+ var startChar = consumeChar(); // consume leading quote
+ var value = "";
+ while (currentChar() && currentChar() !== startChar) {
+ if (currentChar() === "\\") {
+ consumeChar(); // consume escape char and get the next one
+ let nextChar = consumeChar();
+ if (nextChar === "b") {
+ value += "\b";
+ } else if (nextChar === "f") {
+ value += "\f";
+ } else if (nextChar === "n") {
+ value += "\n";
+ } else if (nextChar === "r") {
+ value += "\r";
+ } else if (nextChar === "t") {
+ value += "\t";
+ } else if (nextChar === "v") {
+ value += "\v";
+ } else {
+ value += nextChar;
+ }
+ } else {
+ value += consumeChar();
+ }
+ }
+ if (currentChar() !== startChar) {
+ throw Error("Unterminated string at " + Lexer.positionString(string));
+ } else {
+ consumeChar(); // consume final quote
+ }
+ string.value = value;
+ string.end = position;
+ string.template = startChar === "`";
+ return string;
+ }
+
+ /**
+ * @returns string
+ */
+ function currentChar() {
+ return source.charAt(position);
+ }
+
+ /**
+ * @returns string
+ */
+ function nextChar() {
+ return source.charAt(position + 1);
+ }
+
+ function nextCharAt(number = 1) {
+ return source.charAt(position + number);
+ }
+
+ /**
+ * @returns string
+ */
+ function consumeChar() {
+ lastToken = currentChar();
+ position++;
+ column++;
+ return lastToken;
+ }
+
+ /**
+ * @returns boolean
+ */
+ function possiblePrecedingSymbol() {
+ return (
+ Lexer.isAlpha(lastToken) ||
+ Lexer.isNumeric(lastToken) ||
+ lastToken === ")" ||
+ lastToken === "\"" ||
+ lastToken === "'" ||
+ lastToken === "`" ||
+ lastToken === "}" ||
+ lastToken === "]"
+ );
+ }
+
+ /**
+ * @returns Token
+ */
+ function consumeWhitespace() {
+ var whitespace = makeToken("WHITESPACE");
+ var value = "";
+ while (currentChar() && Lexer.isWhitespace(currentChar())) {
+ if (Lexer.isNewline(currentChar())) {
+ column = 0;
+ line++;
+ }
+ value += consumeChar();
+ }
+ whitespace.value = value;
+ whitespace.end = position;
+ return whitespace;
+ }
+ }
+
+ /**
+ * @param {string} string
+ * @param {boolean} [template]
+ * @returns {Tokens}
+ */
+ tokenize(string, template) {
+ return Lexer.tokenize(string, template)
+ }
+ }
+
+ /**
+ * @typedef {Object} Token
+ * @property {string} [type]
+ * @property {string} value
+ * @property {number} [start]
+ * @property {number} [end]
+ * @property {number} [column]
+ * @property {number} [line]
+ * @property {boolean} [op] `true` if this token represents an operator
+ * @property {boolean} [template] `true` if this token is a template, for class refs, id refs, strings
+ */
+
+ class Tokens {
+ constructor(tokens, consumed, source) {
+ this.tokens = tokens
+ this.consumed = consumed
+ this.source = source
+
+ this.consumeWhitespace(); // consume initial whitespace
+ }
+
+ get list() {
+ return this.tokens
+ }
+
+ /** @type Token | null */
+ _lastConsumed = null;
+
+ consumeWhitespace() {
+ while (this.token(0, true).type === "WHITESPACE") {
+ this.consumed.push(this.tokens.shift());
+ }
+ }
+
+ /**
+ * @param {Tokens} tokens
+ * @param {*} error
+ * @returns {never}
+ */
+ raiseError(tokens, error) {
+ Parser.raiseParseError(tokens, error);
+ }
+
+ /**
+ * @param {string} value
+ * @returns {Token}
+ */
+ requireOpToken(value) {
+ var token = this.matchOpToken(value);
+ if (token) {
+ return token;
+ } else {
+ this.raiseError(this, "Expected '" + value + "' but found '" + this.currentToken().value + "'");
+ }
+ }
+
+ /**
+ * @param {string} op1
+ * @param {string} [op2]
+ * @param {string} [op3]
+ * @returns {Token | void}
+ */
+ matchAnyOpToken(op1, op2, op3) {
+ for (var i = 0; i < arguments.length; i++) {
+ var opToken = arguments[i];
+ var match = this.matchOpToken(opToken);
+ if (match) {
+ return match;
+ }
+ }
+ }
+
+ /**
+ * @param {string} op1
+ * @param {string} [op2]
+ * @param {string} [op3]
+ * @returns {Token | void}
+ */
+ matchAnyToken(op1, op2, op3) {
+ for (var i = 0; i < arguments.length; i++) {
+ var opToken = arguments[i];
+ var match = this.matchToken(opToken);
+ if (match) {
+ return match;
+ }
+ }
+ }
+
+ /**
+ * @param {string} value
+ * @returns {Token | void}
+ */
+ matchOpToken(value) {
+ if (this.currentToken() && this.currentToken().op && this.currentToken().value === value) {
+ return this.consumeToken();
+ }
+ }
+
+ /**
+ * @param {string} type1
+ * @param {string} [type2]
+ * @param {string} [type3]
+ * @param {string} [type4]
+ * @returns {Token}
+ */
+ requireTokenType(type1, type2, type3, type4) {
+ var token = this.matchTokenType(type1, type2, type3, type4);
+ if (token) {
+ return token;
+ } else {
+ this.raiseError(this, "Expected one of " + JSON.stringify([type1, type2, type3]));
+ }
+ }
+
+ /**
+ * @param {string} type1
+ * @param {string} [type2]
+ * @param {string} [type3]
+ * @param {string} [type4]
+ * @returns {Token | void}
+ */
+ matchTokenType(type1, type2, type3, type4) {
+ if (
+ this.currentToken() &&
+ this.currentToken().type &&
+ [type1, type2, type3, type4].indexOf(this.currentToken().type) >= 0
+ ) {
+ return this.consumeToken();
+ }
+ }
+
+ /**
+ * @param {string} value
+ * @param {string} [type]
+ * @returns {Token}
+ */
+ requireToken(value, type) {
+ var token = this.matchToken(value, type);
+ if (token) {
+ return token;
+ } else {
+ this.raiseError(this, "Expected '" + value + "' but found '" + this.currentToken().value + "'");
+ }
+ }
+
+ peekToken(value, peek, type) {
+ peek = peek || 0;
+ type = type || "IDENTIFIER";
+ return this.tokens[peek] && this.tokens[peek].value === value && this.tokens[peek].type === type
+ }
+
+ /**
+ * @param {string} value
+ * @param {string} [type]
+ * @returns {Token | void}
+ */
+ matchToken(value, type) {
+ if (this.follows.indexOf(value) !== -1) {
+ return; // disallowed token here
+ }
+ type = type || "IDENTIFIER";
+ if (this.currentToken() && this.currentToken().value === value && this.currentToken().type === type) {
+ return this.consumeToken();
+ }
+ }
+
+ /**
+ * @returns {Token}
+ */
+ consumeToken() {
+ var match = this.tokens.shift();
+ this.consumed.push(match);
+ this._lastConsumed = match;
+ this.consumeWhitespace(); // consume any whitespace
+ return match;
+ }
+
+ /**
+ * @param {string | null} value
+ * @param {string | null} [type]
+ * @returns {Token[]}
+ */
+ consumeUntil(value, type) {
+ /** @type Token[] */
+ var tokenList = [];
+ var currentToken = this.token(0, true);
+
+ while (
+ (type == null || currentToken.type !== type) &&
+ (value == null || currentToken.value !== value) &&
+ currentToken.type !== "EOF"
+ ) {
+ var match = this.tokens.shift();
+ this.consumed.push(match);
+ tokenList.push(currentToken);
+ currentToken = this.token(0, true);
+ }
+ this.consumeWhitespace(); // consume any whitespace
+ return tokenList;
+ }
+
+ /**
+ * @returns {string}
+ */
+ lastWhitespace() {
+ if (this.consumed[this.consumed.length - 1] && this.consumed[this.consumed.length - 1].type === "WHITESPACE") {
+ return this.consumed[this.consumed.length - 1].value;
+ } else {
+ return "";
+ }
+ }
+
+ consumeUntilWhitespace() {
+ return this.consumeUntil(null, "WHITESPACE");
+ }
+
+ /**
+ * @returns {boolean}
+ */
+ hasMore() {
+ return this.tokens.length > 0;
+ }
+
+ /**
+ * @param {number} n
+ * @param {boolean} [dontIgnoreWhitespace]
+ * @returns {Token}
+ */
+ token(n, dontIgnoreWhitespace) {
+ var /**@type {Token}*/ token;
+ var i = 0;
+ do {
+ if (!dontIgnoreWhitespace) {
+ while (this.tokens[i] && this.tokens[i].type === "WHITESPACE") {
+ i++;
+ }
+ }
+ token = this.tokens[i];
+ n--;
+ i++;
+ } while (n > -1);
+ if (token) {
+ return token;
+ } else {
+ return {
+ type: "EOF",
+ value: "<<>>",
+ };
+ }
+ }
+
+ /**
+ * @returns {Token}
+ */
+ currentToken() {
+ return this.token(0);
+ }
+
+ /**
+ * @returns {Token | null}
+ */
+ lastMatch() {
+ return this._lastConsumed;
+ }
+
+ /**
+ * @returns {string}
+ */
+ static sourceFor = function () {
+ return this.programSource.substring(this.startToken.start, this.endToken.end);
+ }
+
+ /**
+ * @returns {string}
+ */
+ static lineFor = function () {
+ return this.programSource.split("\n")[this.startToken.line - 1];
+ }
+
+ follows = [];
+
+ pushFollow(str) {
+ this.follows.push(str);
+ }
+
+ popFollow() {
+ this.follows.pop();
+ }
+
+ clearFollows() {
+ var tmp = this.follows;
+ this.follows = [];
+ return tmp;
+ }
+
+ restoreFollows(f) {
+ this.follows = f;
+ }
+ }
+
+ /**
+ * @callback ParseRule
+ * @param {Parser} parser
+ * @param {Runtime} runtime
+ * @param {Tokens} tokens
+ * @param {*} [root]
+ * @returns {ASTNode | undefined}
+ *
+ * @typedef {Object} ASTNode
+ * @member {boolean} isFeature
+ * @member {string} type
+ * @member {any[]} args
+ * @member {(this: ASTNode, ctx:Context, root:any, ...args:any) => any} op
+ * @member {(this: ASTNode, context?:Context) => any} evaluate
+ * @member {ASTNode} parent
+ * @member {Set} children
+ * @member {ASTNode} root
+ * @member {String} keyword
+ * @member {Token} endToken
+ * @member {ASTNode} next
+ * @member {(context:Context) => ASTNode} resolveNext
+ * @member {EventSource} eventSource
+ * @member {(this: ASTNode) => void} install
+ * @member {(this: ASTNode, context:Context) => void} execute
+ * @member {(this: ASTNode, target: object, source: object, args?: Object) => void} apply
+ *
+ *
+ */
+
+ class Parser {
+ /**
+ *
+ * @param {Runtime} runtime
+ */
+ constructor(runtime) {
+ this.runtime = runtime
+
+ this.possessivesDisabled = false
+
+ /* ============================================================================================ */
+ /* Core hyperscript Grammar Elements */
+ /* ============================================================================================ */
+ this.addGrammarElement("feature", function (parser, runtime, tokens) {
+ if (tokens.matchOpToken("(")) {
+ var featureElement = parser.requireElement("feature", tokens);
+ tokens.requireOpToken(")");
+ return featureElement;
+ }
+
+ var featureDefinition = parser.FEATURES[tokens.currentToken().value || ""];
+ if (featureDefinition) {
+ return featureDefinition(parser, runtime, tokens);
+ }
+ });
+
+ this.addGrammarElement("command", function (parser, runtime, tokens) {
+ if (tokens.matchOpToken("(")) {
+ const commandElement = parser.requireElement("command", tokens);
+ tokens.requireOpToken(")");
+ return commandElement;
+ }
+
+ var commandDefinition = parser.COMMANDS[tokens.currentToken().value || ""];
+ let commandElement;
+ if (commandDefinition) {
+ commandElement = commandDefinition(parser, runtime, tokens);
+ } else if (tokens.currentToken().type === "IDENTIFIER") {
+ commandElement = parser.parseElement("pseudoCommand", tokens);
+ }
+ if (commandElement) {
+ return parser.parseElement("indirectStatement", tokens, commandElement);
+ }
+
+ return commandElement;
+ });
+
+ this.addGrammarElement("commandList", function (parser, runtime, tokens) {
+ if (tokens.hasMore()) {
+ var cmd = parser.parseElement("command", tokens);
+ if (cmd) {
+ tokens.matchToken("then");
+ const next = parser.parseElement("commandList", tokens);
+ if (next) cmd.next = next;
+ return cmd;
+ }
+ }
+ return {
+ type: "emptyCommandListCommand",
+ op: function(context){
+ return runtime.findNext(this, context);
+ },
+ execute: function (context) {
+ return runtime.unifiedExec(this, context);
+ }
+ }
+ });
+
+ this.addGrammarElement("leaf", function (parser, runtime, tokens) {
+ var result = parser.parseAnyOf(parser.LEAF_EXPRESSIONS, tokens);
+ // symbol is last so it doesn't consume any constants
+ if (result == null) {
+ return parser.parseElement("symbol", tokens);
+ }
+
+ return result;
+ });
+
+ this.addGrammarElement("indirectExpression", function (parser, runtime, tokens, root) {
+ for (var i = 0; i < parser.INDIRECT_EXPRESSIONS.length; i++) {
+ var indirect = parser.INDIRECT_EXPRESSIONS[i];
+ root.endToken = tokens.lastMatch();
+ var result = parser.parseElement(indirect, tokens, root);
+ if (result) {
+ return result;
+ }
+ }
+ return root;
+ });
+
+ this.addGrammarElement("indirectStatement", function (parser, runtime, tokens, root) {
+ if (tokens.matchToken("unless")) {
+ root.endToken = tokens.lastMatch();
+ var conditional = parser.requireElement("expression", tokens);
+ var unless = {
+ type: "unlessStatementModifier",
+ args: [conditional],
+ op: function (context, conditional) {
+ if (conditional) {
+ return this.next;
+ } else {
+ return root;
+ }
+ },
+ execute: function (context) {
+ return runtime.unifiedExec(this, context);
+ },
+ };
+ root.parent = unless;
+ return unless;
+ }
+ return root;
+ });
+
+ this.addGrammarElement("primaryExpression", function (parser, runtime, tokens) {
+ var leaf = parser.parseElement("leaf", tokens);
+ if (leaf) {
+ return parser.parseElement("indirectExpression", tokens, leaf);
+ }
+ parser.raiseParseError(tokens, "Unexpected value: " + tokens.currentToken().value);
+ });
+ }
+
+ use(plugin) {
+ plugin(this)
+ return this
+ }
+
+ /** @type {Object} */
+ GRAMMAR = {};
+
+ /** @type {Object} */
+ COMMANDS = {};
+
+ /** @type {Object} */
+ FEATURES = {};
+
+ /** @type {string[]} */
+ LEAF_EXPRESSIONS = [];
+ /** @type {string[]} */
+ INDIRECT_EXPRESSIONS = [];
+
+ /**
+ * @param {*} parseElement
+ * @param {*} start
+ * @param {Tokens} tokens
+ */
+ initElt(parseElement, start, tokens) {
+ parseElement.startToken = start;
+ parseElement.sourceFor = Tokens.sourceFor;
+ parseElement.lineFor = Tokens.lineFor;
+ parseElement.programSource = tokens.source;
+ }
+
+ /**
+ * @param {string} type
+ * @param {Tokens} tokens
+ * @param {ASTNode?} root
+ * @returns {ASTNode}
+ */
+ parseElement(type, tokens, root = undefined) {
+ var elementDefinition = this.GRAMMAR[type];
+ if (elementDefinition) {
+ var start = tokens.currentToken();
+ var parseElement = elementDefinition(this, this.runtime, tokens, root);
+ if (parseElement) {
+ this.initElt(parseElement, start, tokens);
+ parseElement.endToken = parseElement.endToken || tokens.lastMatch();
+ var root = parseElement.root;
+ while (root != null) {
+ this.initElt(root, start, tokens);
+ root = root.root;
+ }
+ }
+ return parseElement;
+ }
+ }
+
+ /**
+ * @param {string} type
+ * @param {Tokens} tokens
+ * @param {string} [message]
+ * @param {*} [root]
+ * @returns {ASTNode}
+ */
+ requireElement(type, tokens, message, root) {
+ var result = this.parseElement(type, tokens, root);
+ if (!result) Parser.raiseParseError(tokens, message || "Expected " + type);
+ // @ts-ignore
+ return result;
+ }
+
+ /**
+ * @param {string[]} types
+ * @param {Tokens} tokens
+ * @returns {ASTNode}
+ */
+ parseAnyOf(types, tokens) {
+ for (var i = 0; i < types.length; i++) {
+ var type = types[i];
+ var expression = this.parseElement(type, tokens);
+ if (expression) {
+ return expression;
+ }
+ }
+ }
+
+ /**
+ * @param {string} name
+ * @param {ParseRule} definition
+ */
+ addGrammarElement(name, definition) {
+ this.GRAMMAR[name] = definition;
+ }
+
+ /**
+ * @param {string} keyword
+ * @param {ParseRule} definition
+ */
+ addCommand(keyword, definition) {
+ var commandGrammarType = keyword + "Command";
+ var commandDefinitionWrapper = function (parser, runtime, tokens) {
+ const commandElement = definition(parser, runtime, tokens);
+ if (commandElement) {
+ commandElement.type = commandGrammarType;
+ commandElement.execute = function (context) {
+ context.meta.command = commandElement;
+ return runtime.unifiedExec(this, context);
+ };
+ return commandElement;
+ }
+ };
+ this.GRAMMAR[commandGrammarType] = commandDefinitionWrapper;
+ this.COMMANDS[keyword] = commandDefinitionWrapper;
+ }
+
+ /**
+ * @param {string} keyword
+ * @param {ParseRule} definition
+ */
+ addFeature(keyword, definition) {
+ var featureGrammarType = keyword + "Feature";
+
+ /** @type {ParseRule} */
+ var featureDefinitionWrapper = function (parser, runtime, tokens) {
+ var featureElement = definition(parser, runtime, tokens);
+ if (featureElement) {
+ featureElement.isFeature = true;
+ featureElement.keyword = keyword;
+ featureElement.type = featureGrammarType;
+ return featureElement;
+ }
+ };
+ this.GRAMMAR[featureGrammarType] = featureDefinitionWrapper;
+ this.FEATURES[keyword] = featureDefinitionWrapper;
+ }
+
+ /**
+ * @param {string} name
+ * @param {ParseRule} definition
+ */
+ addLeafExpression(name, definition) {
+ this.LEAF_EXPRESSIONS.push(name);
+ this.addGrammarElement(name, definition);
+ }
+
+ /**
+ * @param {string} name
+ * @param {ParseRule} definition
+ */
+ addIndirectExpression(name, definition) {
+ this.INDIRECT_EXPRESSIONS.push(name);
+ this.addGrammarElement(name, definition);
+ }
+
+ /**
+ *
+ * @param {Tokens} tokens
+ * @returns string
+ */
+ static createParserContext(tokens) {
+ var currentToken = tokens.currentToken();
+ var source = tokens.source;
+ var lines = source.split("\n");
+ var line = currentToken && currentToken.line ? currentToken.line - 1 : lines.length - 1;
+ var contextLine = lines[line];
+ var offset = /** @type {number} */ (
+ currentToken && currentToken.line ? currentToken.column : contextLine.length - 1);
+ return contextLine + "\n" + " ".repeat(offset) + "^^\n\n";
+ }
+
+ /**
+ * @param {Tokens} tokens
+ * @param {string} [message]
+ * @returns {never}
+ */
+ static raiseParseError(tokens, message) {
+ message =
+ (message || "Unexpected Token : " + tokens.currentToken().value) + "\n\n" + Parser.createParserContext(tokens);
+ var error = new Error(message);
+ error["tokens"] = tokens;
+ throw error;
+ }
+
+ /**
+ * @param {Tokens} tokens
+ * @param {string} [message]
+ */
+ raiseParseError(tokens, message) {
+ Parser.raiseParseError(tokens, message)
+ }
+
+ /**
+ * @param {Tokens} tokens
+ * @returns {ASTNode}
+ */
+ parseHyperScript(tokens) {
+ var result = this.parseElement("hyperscript", tokens);
+ if (tokens.hasMore()) this.raiseParseError(tokens);
+ if (result) return result;
+ }
+
+ /**
+ * @param {ASTNode | undefined} elt
+ * @param {ASTNode} parent
+ */
+ setParent(elt, parent) {
+ if (typeof elt === 'object') {
+ elt.parent = parent;
+ if (typeof parent === 'object') {
+ parent.children = (parent.children || new Set());
+ parent.children.add(elt)
+ }
+ this.setParent(elt.next, parent);
+ }
+ }
+
+ /**
+ * @param {Token} token
+ * @returns {ParseRule}
+ */
+ commandStart(token) {
+ return this.COMMANDS[token.value || ""];
+ }
+
+ /**
+ * @param {Token} token
+ * @returns {ParseRule}
+ */
+ featureStart(token) {
+ return this.FEATURES[token.value || ""];
+ }
+
+ /**
+ * @param {Token} token
+ * @returns {boolean}
+ */
+ commandBoundary(token) {
+ if (
+ token.value == "end" ||
+ token.value == "then" ||
+ token.value == "else" ||
+ token.value == "otherwise" ||
+ token.value == ")" ||
+ this.commandStart(token) ||
+ this.featureStart(token) ||
+ token.type == "EOF"
+ ) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param {Tokens} tokens
+ * @returns {(string | ASTNode)[]}
+ */
+ parseStringTemplate(tokens) {
+ /** @type {(string | ASTNode)[]} */
+ var returnArr = [""];
+ do {
+ returnArr.push(tokens.lastWhitespace());
+ if (tokens.currentToken().value === "$") {
+ tokens.consumeToken();
+ var startingBrace = tokens.matchOpToken("{");
+ returnArr.push(this.requireElement("expression", tokens));
+ if (startingBrace) {
+ tokens.requireOpToken("}");
+ }
+ returnArr.push("");
+ } else if (tokens.currentToken().value === "\\") {
+ tokens.consumeToken(); // skip next
+ tokens.consumeToken();
+ } else {
+ var token = tokens.consumeToken();
+ returnArr[returnArr.length - 1] += token ? token.value : "";
+ }
+ } while (tokens.hasMore());
+ returnArr.push(tokens.lastWhitespace());
+ return returnArr;
+ }
+
+ /**
+ * @param {ASTNode} commandList
+ */
+ ensureTerminated(commandList) {
+ const runtime = this.runtime
+ var implicitReturn = {
+ type: "implicitReturn",
+ op: function (context) {
+ context.meta.returned = true;
+ if (context.meta.resolve) {
+ context.meta.resolve();
+ }
+ return runtime.HALT;
+ },
+ execute: function (ctx) {
+ // do nothing
+ },
+ };
+
+ var end = commandList;
+ while (end.next) {
+ end = end.next;
+ }
+ end.next = implicitReturn;
+ }
+ }
+
+ class Runtime {
+ /**
+ *
+ * @param {Lexer} [lexer]
+ * @param {Parser} [parser]
+ */
+ constructor(lexer, parser) {
+ this.lexer = lexer ?? new Lexer;
+ this.parser = parser ?? new Parser(this)
+ .use(hyperscriptCoreGrammar)
+ .use(hyperscriptWebGrammar);
+ this.parser.runtime = this
+ }
+
+ /**
+ * @param {HTMLElement} elt
+ * @param {string} selector
+ * @returns boolean
+ */
+ matchesSelector(elt, selector) {
+ // noinspection JSUnresolvedVariable
+ var matchesFunction =
+ // @ts-ignore
+ elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector;
+ return matchesFunction && matchesFunction.call(elt, selector);
+ }
+
+ /**
+ * @param {string} eventName
+ * @param {Object} [detail]
+ * @returns {Event}
+ */
+ makeEvent(eventName, detail) {
+ var evt;
+ if (globalScope.Event && typeof globalScope.Event === "function") {
+ evt = new Event(eventName, {
+ bubbles: true,
+ cancelable: true,
+ });
+ evt['detail'] = detail;
+ } else {
+ evt = document.createEvent("CustomEvent");
+ evt.initCustomEvent(eventName, true, true, detail);
+ }
+ return evt;
+ }
+
+ /**
+ * @param {Element} elt
+ * @param {string} eventName
+ * @param {Object} [detail]
+ * @param {Element} [sender]
+ * @returns {boolean}
+ */
+ triggerEvent(elt, eventName, detail, sender) {
+ detail = detail || {};
+ detail["sender"] = sender;
+ var event = this.makeEvent(eventName, detail);
+ var eventResult = elt.dispatchEvent(event);
+ return eventResult;
+ }
+
+ /**
+ * isArrayLike returns `true` if the provided value is an array or
+ * a NodeList (which is close enough to being an array for our purposes).
+ *
+ * @param {any} value
+ * @returns {value is Array | NodeList}
+ */
+ isArrayLike(value) {
+ return Array.isArray(value) ||
+ (typeof NodeList !== 'undefined' && (value instanceof NodeList || value instanceof HTMLCollection));
+ }
+
+ /**
+ * isIterable returns `true` if the provided value supports the
+ * iterator protocol.
+ *
+ * @param {any} value
+ * @returns {value is Iterable}
+ */
+ isIterable(value) {
+ return typeof value === 'object'
+ && Symbol.iterator in value
+ && typeof value[Symbol.iterator] === 'function';
+ }
+
+ /**
+ * shouldAutoIterate returns `true` if the provided value
+ * should be implicitly iterated over when accessing properties,
+ * and as the target of some commands.
+ *
+ * Currently, this is when the value is an {ElementCollection}
+ * or {isArrayLike} returns true.
+ *
+ * @param {any} value
+ * @returns {value is (any[] | ElementCollection)}
+ */
+ shouldAutoIterate(value) {
+ return value != null && value[shouldAutoIterateSymbol] ||
+ this.isArrayLike(value);
+ }
+
+ /**
+ * forEach executes the provided `func` on every item in the `value` array.
+ * if `value` is a single item (and not an array) then `func` is simply called
+ * once. If `value` is null, then no further actions are taken.
+ *
+ * @template T
+ * @param {T | Iterable} value
+ * @param {(item: T) => void} func
+ */
+ forEach(value, func) {
+ if (value == null) {
+ // do nothing
+ } else if (this.isIterable(value)) {
+ for (const nth of value) {
+ func(nth);
+ }
+ } else if (this.isArrayLike(value)) {
+ for (var i = 0; i < value.length; i++) {
+ func(value[i]);
+ }
+ } else {
+ func(value);
+ }
+ }
+
+ /**
+ * implicitLoop executes the provided `func` on:
+ * - every item of {value}, if {value} should be auto-iterated
+ * (see {shouldAutoIterate})
+ * - {value} otherwise
+ *
+ * @template T
+ * @param {ElementCollection | T | T[]} value
+ * @param {(item: T) => void} func
+ */
+ implicitLoop(value, func) {
+ if (this.shouldAutoIterate(value)) {
+ for (const x of value) func(x);
+ } else {
+ func(value);
+ }
+ }
+
+ wrapArrays(args) {
+ var arr = [];
+ for (var i = 0; i < args.length; i++) {
+ var arg = args[i];
+ if (Array.isArray(arg)) {
+ arr.push(Promise.all(arg));
+ } else {
+ arr.push(arg);
+ }
+ }
+ return arr;
+ }
+
+ unwrapAsyncs(values) {
+ for (var i = 0; i < values.length; i++) {
+ var value = values[i];
+ if (value.asyncWrapper) {
+ values[i] = value.value;
+ }
+ if (Array.isArray(value)) {
+ for (var j = 0; j < value.length; j++) {
+ var valueElement = value[j];
+ if (valueElement.asyncWrapper) {
+ value[j] = valueElement.value;
+ }
+ }
+ }
+ }
+ }
+
+ static HALT = {};
+ HALT = Runtime.HALT;
+
+ /**
+ * @param {ASTNode} command
+ * @param {Context} ctx
+ */
+ unifiedExec(command, ctx) {
+ while (true) {
+ try {
+ var next = this.unifiedEval(command, ctx);
+ } catch (e) {
+ if (ctx.meta.handlingFinally) {
+ console.error(" Exception in finally block: ", e);
+ next = Runtime.HALT;
+ } else {
+ this.registerHyperTrace(ctx, e);
+ if (ctx.meta.errorHandler && !ctx.meta.handlingError) {
+ ctx.meta.handlingError = true;
+ ctx.locals[ctx.meta.errorSymbol] = e;
+ command = ctx.meta.errorHandler;
+ continue;
+ } else {
+ ctx.meta.currentException = e;
+ next = Runtime.HALT;
+ }
+ }
+ }
+ if (next == null) {
+ console.error(command, " did not return a next element to execute! context: ", ctx);
+ return;
+ } else if (next.then) {
+ next.then(resolvedNext => {
+ this.unifiedExec(resolvedNext, ctx);
+ }).catch(reason => {
+ this.unifiedExec({ // Anonymous command to simply throw the exception
+ op: function(){
+ throw reason;
+ }
+ }, ctx);
+ });
+ return;
+ } else if (next === Runtime.HALT) {
+ if (ctx.meta.finallyHandler && !ctx.meta.handlingFinally) {
+ ctx.meta.handlingFinally = true;
+ command = ctx.meta.finallyHandler;
+ } else {
+ if (ctx.meta.onHalt) {
+ ctx.meta.onHalt();
+ }
+ if (ctx.meta.currentException) {
+ if (ctx.meta.reject) {
+ ctx.meta.reject(ctx.meta.currentException);
+ return;
+ } else {
+ throw ctx.meta.currentException;
+ }
+ } else {
+ return;
+ }
+ }
+ } else {
+ command = next; // move to the next command
+ }
+ }
+ }
+
+ /**
+ * @param {*} parseElement
+ * @param {Context} ctx
+ * @returns {*}
+ */
+ unifiedEval(parseElement, ctx) {
+ /** @type any[] */
+ var args = [ctx];
+ var async = false;
+ var wrappedAsyncs = false;
+
+ if (parseElement.args) {
+ for (var i = 0; i < parseElement.args.length; i++) {
+ var argument = parseElement.args[i];
+ if (argument == null) {
+ args.push(null);
+ } else if (Array.isArray(argument)) {
+ var arr = [];
+ for (var j = 0; j < argument.length; j++) {
+ var element = argument[j];
+ var value = element ? element.evaluate(ctx) : null; // OK
+ if (value) {
+ if (value.then) {
+ async = true;
+ } else if (value.asyncWrapper) {
+ wrappedAsyncs = true;
+ }
+ }
+ arr.push(value);
+ }
+ args.push(arr);
+ } else if (argument.evaluate) {
+ var value = argument.evaluate(ctx); // OK
+ if (value) {
+ if (value.then) {
+ async = true;
+ } else if (value.asyncWrapper) {
+ wrappedAsyncs = true;
+ }
+ }
+ args.push(value);
+ } else {
+ args.push(argument);
+ }
+ }
+ }
+ if (async) {
+ return new Promise((resolve, reject) => {
+ args = this.wrapArrays(args);
+ Promise.all(args)
+ .then(function (values) {
+ if (wrappedAsyncs) {
+ this.unwrapAsyncs(values);
+ }
+ try {
+ var apply = parseElement.op.apply(parseElement, values);
+ resolve(apply);
+ } catch (e) {
+ reject(e);
+ }
+ })
+ .catch(function (reason) {
+ reject(reason);
+ });
+ });
+ } else {
+ if (wrappedAsyncs) {
+ this.unwrapAsyncs(args);
+ }
+ return parseElement.op.apply(parseElement, args);
+ }
+ }
+
+ /**
+ * @type {string[] | null}
+ */
+ _scriptAttrs = null;
+
+ /**
+ * getAttributes returns the attribute name(s) to use when
+ * locating hyperscript scripts in a DOM element. If no value
+ * has been configured, it defaults to config.attributes
+ * @returns string[]
+ */
+ getScriptAttributes() {
+ if (this._scriptAttrs == null) {
+ this._scriptAttrs = config.attributes.replace(/ /g, "").split(",");
+ }
+ return this._scriptAttrs;
+ }
+
+ /**
+ * @param {Element} elt
+ * @returns {string | null}
+ */
+ getScript(elt) {
+ for (var i = 0; i < this.getScriptAttributes().length; i++) {
+ var scriptAttribute = this.getScriptAttributes()[i];
+ if (elt.hasAttribute && elt.hasAttribute(scriptAttribute)) {
+ return elt.getAttribute(scriptAttribute);
+ }
+ }
+ if (elt instanceof HTMLScriptElement && elt.type === "text/hyperscript") {
+ return elt.innerText;
+ }
+ return null;
+ }
+
+ hyperscriptFeaturesMap = new WeakMap
+
+ /**
+ * @param {*} elt
+ * @returns {Object}
+ */
+ getHyperscriptFeatures(elt) {
+ var hyperscriptFeatures = this.hyperscriptFeaturesMap.get(elt);
+ if (typeof hyperscriptFeatures === 'undefined') {
+ if (elt) {
+ // in some rare cases, elt is null and this line crashes
+ this.hyperscriptFeaturesMap.set(elt, hyperscriptFeatures = {});
+ }
+ }
+ return hyperscriptFeatures;
+ }
+
+ /**
+ * @param {Object} owner
+ * @param {Context} ctx
+ */
+ addFeatures(owner, ctx) {
+ if (owner) {
+ Object.assign(ctx.locals, this.getHyperscriptFeatures(owner));
+ this.addFeatures(owner.parentElement, ctx);
+ }
+ }
+
+ /**
+ * @param {*} owner
+ * @param {*} feature
+ * @param {*} hyperscriptTarget
+ * @param {*} event
+ * @returns {Context}
+ */
+ makeContext(owner, feature, hyperscriptTarget, event) {
+ return new Context(owner, feature, hyperscriptTarget, event, this)
+ }
+
+ /**
+ * @returns string
+ */
+ getScriptSelector() {
+ return this.getScriptAttributes()
+ .map(function (attribute) {
+ return "[" + attribute + "]";
+ })
+ .join(", ");
+ }
+
+ /**
+ * @param {any} value
+ * @param {string} type
+ * @returns {any}
+ */
+ convertValue(value, type) {
+ var dynamicResolvers = conversions.dynamicResolvers;
+ for (var i = 0; i < dynamicResolvers.length; i++) {
+ var dynamicResolver = dynamicResolvers[i];
+ var converted = dynamicResolver(type, value);
+ if (converted !== undefined) {
+ return converted;
+ }
+ }
+
+ if (value == null) {
+ return null;
+ }
+ var converter = conversions[type];
+ if (converter) {
+ return converter(value);
+ }
+
+ throw "Unknown conversion : " + type;
+ }
+
+ /**
+ * @param {string} src
+ * @returns {ASTNode}
+ */
+ parse(src) {
+ const lexer = this.lexer, parser = this.parser
+ var tokens = lexer.tokenize(src);
+ if (this.parser.commandStart(tokens.currentToken())) {
+ var commandList = parser.requireElement("commandList", tokens);
+ if (tokens.hasMore()) parser.raiseParseError(tokens);
+ parser.ensureTerminated(commandList);
+ return commandList;
+ } else if (parser.featureStart(tokens.currentToken())) {
+ var hyperscript = parser.requireElement("hyperscript", tokens);
+ if (tokens.hasMore()) parser.raiseParseError(tokens);
+ return hyperscript;
+ } else {
+ var expression = parser.requireElement("expression", tokens);
+ if (tokens.hasMore()) parser.raiseParseError(tokens);
+ return expression;
+ }
+ }
+
+ /**
+ *
+ * @param {ASTNode} elt
+ * @param {Context} ctx
+ * @returns {any}
+ */
+ evaluateNoPromise(elt, ctx) {
+ let result = elt.evaluate(ctx);
+ if (result.next) {
+ throw new Error(Tokens.sourceFor.call(elt) + " returned a Promise in a context that they are not allowed.");
+ }
+ return result;
+ }
+
+ /**
+ * @param {string} src
+ * @param {Partial} [ctx]
+ * @param {Object} [args]
+ * @returns {any}
+ */
+ evaluate(src, ctx, args) {
+ class HyperscriptModule extends EventTarget {
+ constructor(mod) {
+ super();
+ this.module = mod;
+ }
+ toString() {
+ return this.module.id;
+ }
+ }
+
+ var body = 'document' in globalScope
+ ? globalScope.document.body
+ : new HyperscriptModule(args && args.module);
+ ctx = Object.assign(this.makeContext(body, null, body, null), ctx || {});
+ var element = this.parse(src);
+ if (element.execute) {
+ element.execute(ctx);
+ return ctx.result;
+ } else if (element.apply) {
+ element.apply(body, body, args);
+ return this.getHyperscriptFeatures(body);
+ } else {
+ return element.evaluate(ctx);
+ }
+
+ function makeModule() {
+ return {}
+ }
+ }
+
+ /**
+ * @param {HTMLElement} elt
+ */
+ processNode(elt) {
+ var selector = this.getScriptSelector();
+ if (this.matchesSelector(elt, selector)) {
+ this.initElement(elt, elt);
+ }
+ if (elt instanceof HTMLScriptElement && elt.type === "text/hyperscript") {
+ this.initElement(elt, document.body);
+ }
+ if (elt.querySelectorAll) {
+ this.forEach(elt.querySelectorAll(selector + ", [type='text/hyperscript']"), elt => {
+ this.initElement(elt, elt instanceof HTMLScriptElement && elt.type === "text/hyperscript" ? document.body : elt);
+ });
+ }
+ }
+
+ /**
+ * @param {Element} elt
+ * @param {Element} [target]
+ */
+ initElement(elt, target) {
+ if (elt.closest && elt.closest(config.disableSelector)) {
+ return;
+ }
+ var internalData = this.getInternalData(elt);
+ if (!internalData.initialized) {
+ var src = this.getScript(elt);
+ if (src) {
+ try {
+ internalData.initialized = true;
+ internalData.script = src;
+ const lexer = this.lexer, parser = this.parser
+ var tokens = lexer.tokenize(src);
+ var hyperScript = parser.parseHyperScript(tokens);
+ if (!hyperScript) return;
+ hyperScript.apply(target || elt, elt);
+ setTimeout(() => {
+ this.triggerEvent(target || elt, "load", {
+ hyperscript: true,
+ });
+ }, 1);
+ } catch (e) {
+ this.triggerEvent(elt, "exception", {
+ error: e,
+ });
+ console.error(
+ "hyperscript errors were found on the following element:",
+ elt,
+ "\n\n",
+ e.message,
+ e.stack
+ );
+ }
+ }
+ }
+ }
+
+ internalDataMap = new WeakMap
+
+ /**
+ * @param {Element} elt
+ * @returns {Object}
+ */
+ getInternalData(elt) {
+ var internalData = this.internalDataMap.get(elt);
+ if (typeof internalData === 'undefined') {
+ this.internalDataMap.set(elt, internalData = {});
+ }
+ return internalData;
+ }
+
+ /**
+ * @param {any} value
+ * @param {string} typeString
+ * @param {boolean} [nullOk]
+ * @returns {boolean}
+ */
+ typeCheck(value, typeString, nullOk) {
+ if (value == null && nullOk) {
+ return true;
+ }
+ var typeName = Object.prototype.toString.call(value).slice(8, -1);
+ return typeName === typeString;
+ }
+
+ getElementScope(context) {
+ var elt = context.meta && context.meta.owner;
+ if (elt) {
+ var internalData = this.getInternalData(elt);
+ var scopeName = "elementScope";
+ if (context.meta.feature && context.meta.feature.behavior) {
+ scopeName = context.meta.feature.behavior + "Scope";
+ }
+ var elementScope = getOrInitObject(internalData, scopeName);
+ return elementScope;
+ } else {
+ return {}; // no element, return empty scope
+ }
+ }
+
+ /**
+ * @param {string} str
+ * @returns {boolean}
+ */
+ isReservedWord(str) {
+ return ["meta", "it", "result", "locals", "event", "target", "detail", "sender", "body"].includes(str)
+ }
+
+ /**
+ * @param {any} context
+ * @returns {boolean}
+ */
+ isHyperscriptContext(context) {
+ return context instanceof Context;
+ }
+
+ /**
+ * @param {string} str
+ * @param {Context} context
+ * @returns {any}
+ */
+ resolveSymbol(str, context, type) {
+ if (str === "me" || str === "my" || str === "I") {
+ return context.me;
+ }
+ if (str === "it" || str === "its" || str === "result") {
+ return context.result;
+ }
+ if (str === "you" || str === "your" || str === "yourself") {
+ return context.you;
+ } else {
+ if (type === "global") {
+ return globalScope[str];
+ } else if (type === "element") {
+ var elementScope = this.getElementScope(context);
+ return elementScope[str];
+ } else if (type === "local") {
+ return context.locals[str];
+ } else {
+ // meta scope (used for event conditionals)
+ if (context.meta && context.meta.context) {
+ var fromMetaContext = context.meta.context[str];
+ if (typeof fromMetaContext !== "undefined") {
+ return fromMetaContext;
+ }
+ // resolve against the `detail` object in the meta context as well
+ if (context.meta.context.detail) {
+ fromMetaContext = context.meta.context.detail[str];
+ if (typeof fromMetaContext !== "undefined") {
+ return fromMetaContext;
+ }
+ }
+ }
+ if (this.isHyperscriptContext(context) && !this.isReservedWord(str)) {
+ // local scope
+ var fromContext = context.locals[str];
+ } else {
+ // direct get from normal JS object or top-level of context
+ var fromContext = context[str];
+ }
+ if (typeof fromContext !== "undefined") {
+ return fromContext;
+ } else {
+ // element scope
+ var elementScope = this.getElementScope(context);
+ fromContext = elementScope[str];
+ if (typeof fromContext !== "undefined") {
+ return fromContext;
+ } else {
+ // global scope
+ return globalScope[str];
+ }
+ }
+ }
+ }
+ }
+
+ setSymbol(str, context, type, value) {
+ if (type === "global") {
+ globalScope[str] = value;
+ } else if (type === "element") {
+ var elementScope = this.getElementScope(context);
+ elementScope[str] = value;
+ } else if (type === "local") {
+ context.locals[str] = value;
+ } else {
+ if (this.isHyperscriptContext(context) && !this.isReservedWord(str) && typeof context.locals[str] !== "undefined") {
+ // local scope
+ context.locals[str] = value;
+ } else {
+ // element scope
+ var elementScope = this.getElementScope(context);
+ var fromContext = elementScope[str];
+ if (typeof fromContext !== "undefined") {
+ elementScope[str] = value;
+ } else {
+ if (this.isHyperscriptContext(context) && !this.isReservedWord(str)) {
+ // local scope
+ context.locals[str] = value;
+ } else {
+ // direct set on normal JS object or top-level of context
+ context[str] = value;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @param {ASTNode} command
+ * @param {Context} context
+ * @returns {undefined | ASTNode}
+ */
+ findNext(command, context) {
+ if (command) {
+ if (command.resolveNext) {
+ return command.resolveNext(context);
+ } else if (command.next) {
+ return command.next;
+ } else {
+ return this.findNext(command.parent, context);
+ }
+ }
+ }
+
+ /**
+ * @param {Object} root
+ * @param {string} property
+ * @param {Getter} getter
+ * @returns {any}
+ *
+ * @callback Getter
+ * @param {Object} root
+ * @param {string} property
+ */
+ flatGet(root, property, getter) {
+ if (root != null) {
+ var val = getter(root, property);
+ if (typeof val !== "undefined") {
+ return val;
+ }
+
+ if (this.shouldAutoIterate(root)) {
+ // flat map
+ var result = [];
+ for (var component of root) {
+ var componentValue = getter(component, property);
+ result.push(componentValue);
+ }
+ return result;
+ }
+ }
+ }
+
+ resolveProperty(root, property) {
+ return this.flatGet(root, property, (root, property) => root[property] )
+ }
+
+ resolveAttribute(root, property) {
+ return this.flatGet(root, property, (root, property) => root.getAttribute && root.getAttribute(property) )
+ }
+
+ /**
+ *
+ * @param {Object} root
+ * @param {string} property
+ * @returns {string}
+ */
+ resolveStyle(root, property) {
+ return this.flatGet(root, property, (root, property) => root.style && root.style[property] )
+ }
+
+ /**
+ *
+ * @param {Object} root
+ * @param {string} property
+ * @returns {string}
+ */
+ resolveComputedStyle(root, property) {
+ return this.flatGet(root, property, (root, property) => getComputedStyle(
+ /** @type {Element} */ (root)).getPropertyValue(property) )
+ }
+
+ /**
+ * @param {Element} elt
+ * @param {string[]} nameSpace
+ * @param {string} name
+ * @param {any} value
+ */
+ assignToNamespace(elt, nameSpace, name, value) {
+ let root
+ if (typeof document !== "undefined" && elt === document.body) {
+ root = globalScope;
+ } else {
+ root = this.getHyperscriptFeatures(elt);
+ }
+ var propertyName;
+ while ((propertyName = nameSpace.shift()) !== undefined) {
+ var newRoot = root[propertyName];
+ if (newRoot == null) {
+ newRoot = {};
+ root[propertyName] = newRoot;
+ }
+ root = newRoot;
+ }
+
+ root[name] = value;
+ }
+
+ getHyperTrace(ctx, thrown) {
+ var trace = [];
+ var root = ctx;
+ while (root.meta.caller) {
+ root = root.meta.caller;
+ }
+ if (root.meta.traceMap) {
+ return root.meta.traceMap.get(thrown, trace);
+ }
+ }
+
+ registerHyperTrace(ctx, thrown) {
+ var trace = [];
+ var root = null;
+ while (ctx != null) {
+ trace.push(ctx);
+ root = ctx;
+ ctx = ctx.meta.caller;
+ }
+ if (root.meta.traceMap == null) {
+ root.meta.traceMap = new Map(); // TODO - WeakMap?
+ }
+ if (!root.meta.traceMap.get(thrown)) {
+ var traceEntry = {
+ trace: trace,
+ print: function (logger) {
+ logger = logger || console.error;
+ logger("hypertrace /// ");
+ var maxLen = 0;
+ for (var i = 0; i < trace.length; i++) {
+ maxLen = Math.max(maxLen, trace[i].meta.feature.displayName.length);
+ }
+ for (var i = 0; i < trace.length; i++) {
+ var traceElt = trace[i];
+ logger(
+ " ->",
+ traceElt.meta.feature.displayName.padEnd(maxLen + 2),
+ "-",
+ traceElt.meta.owner
+ );
+ }
+ },
+ };
+ root.meta.traceMap.set(thrown, traceEntry);
+ }
+ }
+
+ /**
+ * @param {string} str
+ * @returns {string}
+ */
+ escapeSelector(str) {
+ return str.replace(/:/g, function (str) {
+ return "\\" + str;
+ });
+ }
+
+ /**
+ * @param {any} value
+ * @param {*} elt
+ */
+ nullCheck(value, elt) {
+ if (value == null) {
+ throw new Error("'" + elt.sourceFor() + "' is null");
+ }
+ }
+
+ /**
+ * @param {any} value
+ * @returns {boolean}
+ */
+ isEmpty(value) {
+ return value == undefined || value.length === 0;
+ }
+
+ /**
+ * @param {any} value
+ * @returns {boolean}
+ */
+ doesExist(value) {
+ if(value == null){
+ return false;
+ }
+ if (this.shouldAutoIterate(value)) {
+ for (const elt of value) {
+ return true;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @param {Node} node
+ * @returns {Document|ShadowRoot}
+ */
+ getRootNode(node) {
+ if (node && node instanceof Node) {
+ var rv = node.getRootNode();
+ if (rv instanceof Document || rv instanceof ShadowRoot) return rv;
+ }
+ return document;
+ }
+
+ /**
+ *
+ * @param {Element} elt
+ * @param {ASTNode} onFeature
+ * @returns {EventQueue}
+ *
+ * @typedef {{queue:Array, executing:boolean}} EventQueue
+ */
+ getEventQueueFor(elt, onFeature) {
+ let internalData = this.getInternalData(elt);
+ var eventQueuesForElt = internalData.eventQueues;
+ if (eventQueuesForElt == null) {
+ eventQueuesForElt = new Map();
+ internalData.eventQueues = eventQueuesForElt;
+ }
+ var eventQueueForFeature = eventQueuesForElt.get(onFeature);
+ if (eventQueueForFeature == null) {
+ eventQueueForFeature = {queue:[], executing:false};
+ eventQueuesForElt.set(onFeature, eventQueueForFeature);
+ }
+ return eventQueueForFeature;
+ }
+
+ beepValueToConsole(element, expression, value) {
+ if (this.triggerEvent(element, "hyperscript:beep", {element, expression, value})) {
+ var typeName;
+ if (value) {
+ if (value instanceof ElementCollection) {
+ typeName = "ElementCollection";
+ } else if (value.constructor) {
+ typeName = value.constructor.name;
+ } else {
+ typeName = "unknown";
+ }
+ } else {
+ typeName = "object (null)"
+ }
+ var logValue = value;
+ if (typeName === "String") {
+ logValue = '"' + logValue + '"';
+ } else if (value instanceof ElementCollection) {
+ logValue = Array.from(value);
+ }
+ console.log("///_ BEEP! The expression (" + Tokens.sourceFor.call(expression).replace("beep! ", "") + ") evaluates to:", logValue, "of type " + typeName);
+ }
+ }
+
+
+ /** @type string | null */
+ // @ts-ignore
+ hyperscriptUrl = "document" in globalScope && document.currentScript ? document.currentScript.src : null;
+ }
+
+
+ function getCookiesAsArray() {
+ let cookiesAsArray = document.cookie
+ .split("; ")
+ .map(cookieEntry => {
+ let strings = cookieEntry.split("=");
+ return {name: strings[0], value: decodeURIComponent(strings[1])}
+ });
+ return cookiesAsArray;
+ }
+
+ function clearCookie(name) {
+ document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
+ }
+
+ function clearAllCookies() {
+ for (const cookie of getCookiesAsArray()) {
+ clearCookie(cookie.name);
+ }
+ }
+
+ const CookieJar = new Proxy({}, {
+ get(target, prop) {
+ if (prop === 'then' || prop === 'asyncWrapper') { // ignore special symbols
+ return null;
+ } else if (prop === 'length') {
+ return getCookiesAsArray().length
+ } else if (prop === 'clear') {
+ return clearCookie;
+ } else if (prop === 'clearAll') {
+ return clearAllCookies;
+ } else if (typeof prop === "string") {
+ if (!isNaN(prop)) {
+ return getCookiesAsArray()[parseInt(prop)];
+
+ } else {
+ let value = document.cookie
+ .split("; ")
+ .find((row) => row.startsWith(prop + "="))
+ ?.split("=")[1];
+ if(value) {
+ return decodeURIComponent(value);
+ }
+ }
+ } else if (prop === Symbol.iterator) {
+ return getCookiesAsArray()[prop];
+ }
+ },
+ set(target, prop, value) {
+ var finalValue = null;
+ if ('string' === typeof value) {
+ finalValue = encodeURIComponent(value)
+ finalValue += ";samesite=lax"
+ } else {
+ finalValue = encodeURIComponent(value.value);
+ if (value.expires) {
+ finalValue+=";expires=" + value.maxAge;
+ }
+ if (value.maxAge) {
+ finalValue+=";max-age=" + value.maxAge;
+ }
+ if (value.partitioned) {
+ finalValue+=";partitioned=" + value.partitioned;
+ }
+ if (value.path) {
+ finalValue+=";path=" + value.path;
+ }
+ if (value.samesite) {
+ finalValue+=";samesite=" + value.path;
+ }
+ if (value.secure) {
+ finalValue+=";secure=" + value.path;
+ }
+ }
+ document.cookie= prop + "=" + value;
+ return true;
+ }
+ })
+
+ class Context {
+ /**
+ * @param {*} owner
+ * @param {*} feature
+ * @param {*} hyperscriptTarget
+ * @param {*} event
+ */
+ constructor(owner, feature, hyperscriptTarget, event, runtime) {
+ this.meta = {
+ parser: runtime.parser,
+ lexer: runtime.lexer,
+ runtime,
+ owner: owner,
+ feature: feature,
+ iterators: {},
+ ctx: this
+ }
+ this.locals = {
+ cookies:CookieJar
+ };
+ this.me = hyperscriptTarget,
+ this.you = undefined
+ this.result = undefined
+ this.event = event;
+ this.target = event ? event.target : null;
+ this.detail = event ? event.detail : null;
+ this.sender = event ? event.detail ? event.detail.sender : null : null;
+ this.body = "document" in globalScope ? document.body : null;
+ runtime.addFeatures(owner, this);
+ }
+ }
+
+ class ElementCollection {
+ constructor(css, relativeToElement, escape) {
+ this._css = css;
+ this.relativeToElement = relativeToElement;
+ this.escape = escape;
+ this[shouldAutoIterateSymbol] = true;
+ }
+
+ get css() {
+ if (this.escape) {
+ return Runtime.prototype.escapeSelector(this._css);
+ } else {
+ return this._css;
+ }
+ }
+
+ get className() {
+ return this._css.substr(1);
+ }
+
+ get id() {
+ return this.className();
+ }
+
+ contains(elt) {
+ for (let element of this) {
+ if (element.contains(elt)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ get length() {
+ return this.selectMatches().length;
+ }
+
+ [Symbol.iterator]() {
+ let query = this.selectMatches();
+ return query [Symbol.iterator]();
+ }
+
+ selectMatches() {
+ let query = Runtime.prototype.getRootNode(this.relativeToElement).querySelectorAll(this.css);
+ return query;
+ }
+ }
+
+ const shouldAutoIterateSymbol = Symbol()
+
+ function getOrInitObject(root, prop) {
+ var value = root[prop];
+ if (value) {
+ return value;
+ } else {
+ var newObj = {};
+ root[prop] = newObj;
+ return newObj;
+ }
+ }
+
+ /**
+ * parseJSON parses a JSON string into a corresponding value. If the
+ * value passed in is not valid JSON, then it logs an error and returns `null`.
+ *
+ * @param {string} jString
+ * @returns any
+ */
+ function parseJSON(jString) {
+ try {
+ return JSON.parse(jString);
+ } catch (error) {
+ logError(error);
+ return null;
+ }
+ }
+
+ /**
+ * logError writes an error message to the Javascript console. It can take any
+ * value, but msg should commonly be a simple string.
+ * @param {*} msg
+ */
+ function logError(msg) {
+ if (console.error) {
+ console.error(msg);
+ } else if (console.log) {
+ console.log("ERROR: ", msg);
+ }
+ }
+
+ // TODO: JSDoc description of what's happening here
+ function varargConstructor(Cls, args) {
+ return new (Cls.bind.apply(Cls, [Cls].concat(args)))();
+ }
+
+ // Grammar
+
+ /**
+ * @param {Parser} parser
+ */
+ function hyperscriptCoreGrammar(parser) {
+ parser.addLeafExpression("parenthesized", function (parser, _runtime, tokens) {
+ if (tokens.matchOpToken("(")) {
+ var follows = tokens.clearFollows();
+ try {
+ var expr = parser.requireElement("expression", tokens);
+ } finally {
+ tokens.restoreFollows(follows);
+ }
+ tokens.requireOpToken(")");
+ return expr;
+ }
+ });
+
+ parser.addLeafExpression("string", function (parser, runtime, tokens) {
+ var stringToken = tokens.matchTokenType("STRING");
+ if (!stringToken) return;
+ var rawValue = /** @type {string} */ (stringToken.value);
+ /** @type {any[]} */
+ var args;
+ if (stringToken.template) {
+ var innerTokens = Lexer.tokenize(rawValue, true);
+ args = parser.parseStringTemplate(innerTokens);
+ } else {
+ args = [];
+ }
+ return {
+ type: "string",
+ token: stringToken,
+ args: args,
+ op: function (context) {
+ var returnStr = "";
+ for (var i = 1; i < arguments.length; i++) {
+ var val = arguments[i];
+ if (val !== undefined) {
+ returnStr += val;
+ }
+ }
+ return returnStr;
+ },
+ evaluate: function (context) {
+ if (args.length === 0) {
+ return rawValue;
+ } else {
+ return runtime.unifiedEval(this, context);
+ }
+ },
+ };
+ });
+
+ parser.addGrammarElement("nakedString", function (parser, runtime, tokens) {
+ if (tokens.hasMore()) {
+ var tokenArr = tokens.consumeUntilWhitespace();
+ tokens.matchTokenType("WHITESPACE");
+ return {
+ type: "nakedString",
+ tokens: tokenArr,
+ evaluate: function (context) {
+ return tokenArr
+ .map(function (t) {
+ return t.value;
+ })
+ .join("");
+ },
+ };
+ }
+ });
+
+ parser.addLeafExpression("number", function (parser, runtime, tokens) {
+ var number = tokens.matchTokenType("NUMBER");
+ if (!number) return;
+ var numberToken = number;
+ var value = parseFloat(/** @type {string} */ (number.value));
+ return {
+ type: "number",
+ value: value,
+ numberToken: numberToken,
+ evaluate: function () {
+ return value;
+ },
+ };
+ });
+
+ parser.addLeafExpression("idRef", function (parser, runtime, tokens) {
+ var elementId = tokens.matchTokenType("ID_REF");
+ if (!elementId) return;
+ if (!elementId.value) return;
+ // TODO - unify these two expression types
+ if (elementId.template) {
+ var templateValue = elementId.value.substring(2);
+ var innerTokens = Lexer.tokenize(templateValue);
+ var innerExpression = parser.requireElement("expression", innerTokens);
+ return {
+ type: "idRefTemplate",
+ args: [innerExpression],
+ op: function (context, arg) {
+ return runtime.getRootNode(context.me).getElementById(arg);
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ } else {
+ const value = elementId.value.substring(1);
+ return {
+ type: "idRef",
+ css: elementId.value,
+ value: value,
+ evaluate: function (context) {
+ return (
+ runtime.getRootNode(context.me).getElementById(value)
+ );
+ },
+ };
+ }
+ });
+
+ parser.addLeafExpression("classRef", function (parser, runtime, tokens) {
+ var classRef = tokens.matchTokenType("CLASS_REF");
+
+ if (!classRef) return;
+ if (!classRef.value) return;
+
+ // TODO - unify these two expression types
+ if (classRef.template) {
+ var templateValue = classRef.value.substring(2);
+ var innerTokens = Lexer.tokenize(templateValue);
+ var innerExpression = parser.requireElement("expression", innerTokens);
+ return {
+ type: "classRefTemplate",
+ args: [innerExpression],
+ op: function (context, arg) {
+ return new ElementCollection("." + arg, context.me, true)
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ } else {
+ const css = classRef.value;
+ return {
+ type: "classRef",
+ css: css,
+ evaluate: function (context) {
+ return new ElementCollection(css, context.me, true)
+ },
+ };
+ }
+ });
+
+ class TemplatedQueryElementCollection extends ElementCollection {
+ constructor(css, relativeToElement, templateParts) {
+ super(css, relativeToElement);
+ this.templateParts = templateParts;
+ this.elements = templateParts.filter(elt => elt instanceof Element);
+ }
+
+ get css() {
+ let rv = "", i = 0
+ for (const val of this.templateParts) {
+ if (val instanceof Element) {
+ rv += "[data-hs-query-id='" + i++ + "']";
+ } else rv += val;
+ }
+ return rv;
+ }
+
+ [Symbol.iterator]() {
+ this.elements.forEach((el, i) => el.dataset.hsQueryId = i);
+ const rv = super[Symbol.iterator]();
+ this.elements.forEach(el => el.removeAttribute('data-hs-query-id'));
+ return rv;
+ }
+ }
+
+ parser.addLeafExpression("queryRef", function (parser, runtime, tokens) {
+ var queryStart = tokens.matchOpToken("<");
+ if (!queryStart) return;
+ var queryTokens = tokens.consumeUntil("/");
+ tokens.requireOpToken("/");
+ tokens.requireOpToken(">");
+ var queryValue = queryTokens
+ .map(function (t) {
+ if (t.type === "STRING") {
+ return '"' + t.value + '"';
+ } else {
+ return t.value;
+ }
+ })
+ .join("");
+
+ var template, innerTokens, args;
+ if (queryValue.indexOf("$") >= 0) {
+ template = true;
+ innerTokens = Lexer.tokenize(queryValue, true);
+ args = parser.parseStringTemplate(innerTokens);
+ }
+
+ return {
+ type: "queryRef",
+ css: queryValue,
+ args: args,
+ op: function (context, ...args) {
+ if (template) {
+ return new TemplatedQueryElementCollection(queryValue, context.me, args)
+ } else {
+ return new ElementCollection(queryValue, context.me)
+ }
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ });
+
+ parser.addLeafExpression("attributeRef", function (parser, runtime, tokens) {
+ var attributeRef = tokens.matchTokenType("ATTRIBUTE_REF");
+ if (!attributeRef) return;
+ if (!attributeRef.value) return;
+ var outerVal = attributeRef.value;
+ if (outerVal.indexOf("[") === 0) {
+ var innerValue = outerVal.substring(2, outerVal.length - 1);
+ } else {
+ var innerValue = outerVal.substring(1);
+ }
+ var css = "[" + innerValue + "]";
+ var split = innerValue.split("=");
+ var name = split[0];
+ var value = split[1];
+ if (value) {
+ // strip quotes
+ if (value.indexOf('"') === 0) {
+ value = value.substring(1, value.length - 1);
+ }
+ }
+ return {
+ type: "attributeRef",
+ name: name,
+ css: css,
+ value: value,
+ op: function (context) {
+ var target = context.you || context.me;
+ if (target) {
+ return target.getAttribute(name);
+ }
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ });
+
+ parser.addLeafExpression("styleRef", function (parser, runtime, tokens) {
+ var styleRef = tokens.matchTokenType("STYLE_REF");
+ if (!styleRef) return;
+ if (!styleRef.value) return;
+ var styleProp = styleRef.value.substr(1);
+ if (styleProp.startsWith("computed-")) {
+ styleProp = styleProp.substr("computed-".length);
+ return {
+ type: "computedStyleRef",
+ name: styleProp,
+ op: function (context) {
+ var target = context.you || context.me;
+ if (target) {
+ return runtime.resolveComputedStyle(target, styleProp);
+ }
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ } else {
+ return {
+ type: "styleRef",
+ name: styleProp,
+ op: function (context) {
+ var target = context.you || context.me;
+ if (target) {
+ return runtime.resolveStyle(target, styleProp);
+ }
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ }
+ });
+
+ parser.addGrammarElement("objectKey", function (parser, runtime, tokens) {
+ var token;
+ if ((token = tokens.matchTokenType("STRING"))) {
+ return {
+ type: "objectKey",
+ key: token.value,
+ evaluate: function () {
+ return token.value;
+ },
+ };
+ } else if (tokens.matchOpToken("[")) {
+ var expr = parser.parseElement("expression", tokens);
+ tokens.requireOpToken("]");
+ return {
+ type: "objectKey",
+ expr: expr,
+ args: [expr],
+ op: function (ctx, expr) {
+ return expr;
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ } else {
+ var key = "";
+ do {
+ token = tokens.matchTokenType("IDENTIFIER") || tokens.matchOpToken("-");
+ if (token) key += token.value;
+ } while (token);
+ return {
+ type: "objectKey",
+ key: key,
+ evaluate: function () {
+ return key;
+ },
+ };
+ }
+ });
+
+ parser.addLeafExpression("objectLiteral", function (parser, runtime, tokens) {
+ if (!tokens.matchOpToken("{")) return;
+ var keyExpressions = [];
+ var valueExpressions = [];
+ if (!tokens.matchOpToken("}")) {
+ do {
+ var name = parser.requireElement("objectKey", tokens);
+ tokens.requireOpToken(":");
+ var value = parser.requireElement("expression", tokens);
+ valueExpressions.push(value);
+ keyExpressions.push(name);
+ } while (tokens.matchOpToken(","));
+ tokens.requireOpToken("}");
+ }
+ return {
+ type: "objectLiteral",
+ args: [keyExpressions, valueExpressions],
+ op: function (context, keys, values) {
+ var returnVal = {};
+ for (var i = 0; i < keys.length; i++) {
+ returnVal[keys[i]] = values[i];
+ }
+ return returnVal;
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ });
+
+ parser.addGrammarElement("nakedNamedArgumentList", function (parser, runtime, tokens) {
+ var fields = [];
+ var valueExpressions = [];
+ if (tokens.currentToken().type === "IDENTIFIER") {
+ do {
+ var name = tokens.requireTokenType("IDENTIFIER");
+ tokens.requireOpToken(":");
+ var value = parser.requireElement("expression", tokens);
+ valueExpressions.push(value);
+ fields.push({ name: name, value: value });
+ } while (tokens.matchOpToken(","));
+ }
+ return {
+ type: "namedArgumentList",
+ fields: fields,
+ args: [valueExpressions],
+ op: function (context, values) {
+ var returnVal = { _namedArgList_: true };
+ for (var i = 0; i < values.length; i++) {
+ var field = fields[i];
+ returnVal[field.name.value] = values[i];
+ }
+ return returnVal;
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ });
+
+ parser.addGrammarElement("namedArgumentList", function (parser, runtime, tokens) {
+ if (!tokens.matchOpToken("(")) return;
+ var elt = parser.requireElement("nakedNamedArgumentList", tokens);
+ tokens.requireOpToken(")");
+ return elt;
+ });
+
+ parser.addGrammarElement("symbol", function (parser, runtime, tokens) {
+ /** @scope {SymbolScope} */
+ var scope = "default";
+ if (tokens.matchToken("global")) {
+ scope = "global";
+ } else if (tokens.matchToken("element") || tokens.matchToken("module")) {
+ scope = "element";
+ // optional possessive
+ if (tokens.matchOpToken("'")) {
+ tokens.requireToken("s");
+ }
+ } else if (tokens.matchToken("local")) {
+ scope = "local";
+ }
+
+ // TODO better look ahead here
+ let eltPrefix = tokens.matchOpToken(":");
+ let identifier = tokens.matchTokenType("IDENTIFIER");
+ if (identifier && identifier.value) {
+ var name = identifier.value;
+ if (eltPrefix) {
+ name = ":" + name;
+ }
+ if (scope === "default") {
+ if (name.indexOf("$") === 0) {
+ scope = "global";
+ }
+ if (name.indexOf(":") === 0) {
+ scope = "element";
+ }
+ }
+ return {
+ type: "symbol",
+ token: identifier,
+ scope: scope,
+ name: name,
+ evaluate: function (context) {
+ return runtime.resolveSymbol(name, context, scope);
+ },
+ };
+ }
+ });
+
+ parser.addGrammarElement("implicitMeTarget", function (parser, runtime, tokens) {
+ return {
+ type: "implicitMeTarget",
+ evaluate: function (context) {
+ return context.you || context.me;
+ },
+ };
+ });
+
+ parser.addLeafExpression("boolean", function (parser, runtime, tokens) {
+ var booleanLiteral = tokens.matchToken("true") || tokens.matchToken("false");
+ if (!booleanLiteral) return;
+ const value = booleanLiteral.value === "true";
+ return {
+ type: "boolean",
+ evaluate: function (context) {
+ return value;
+ },
+ };
+ });
+
+ parser.addLeafExpression("null", function (parser, runtime, tokens) {
+ if (tokens.matchToken("null")) {
+ return {
+ type: "null",
+ evaluate: function (context) {
+ return null;
+ },
+ };
+ }
+ });
+
+ parser.addLeafExpression("arrayLiteral", function (parser, runtime, tokens) {
+ if (!tokens.matchOpToken("[")) return;
+ var values = [];
+ if (!tokens.matchOpToken("]")) {
+ do {
+ var expr = parser.requireElement("expression", tokens);
+ values.push(expr);
+ } while (tokens.matchOpToken(","));
+ tokens.requireOpToken("]");
+ }
+ return {
+ type: "arrayLiteral",
+ values: values,
+ args: [values],
+ op: function (context, values) {
+ return values;
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ });
+
+ parser.addLeafExpression("blockLiteral", function (parser, runtime, tokens) {
+ if (!tokens.matchOpToken("\\")) return;
+ var args = [];
+ var arg1 = tokens.matchTokenType("IDENTIFIER");
+ if (arg1) {
+ args.push(arg1);
+ while (tokens.matchOpToken(",")) {
+ args.push(tokens.requireTokenType("IDENTIFIER"));
+ }
+ }
+ // TODO compound op token
+ tokens.requireOpToken("-");
+ tokens.requireOpToken(">");
+ var expr = parser.requireElement("expression", tokens);
+ return {
+ type: "blockLiteral",
+ args: args,
+ expr: expr,
+ evaluate: function (ctx) {
+ var returnFunc = function () {
+ //TODO - push scope
+ for (var i = 0; i < args.length; i++) {
+ ctx.locals[args[i].value] = arguments[i];
+ }
+ return expr.evaluate(ctx); //OK
+ };
+ return returnFunc;
+ },
+ };
+ });
+
+ parser.addIndirectExpression("propertyAccess", function (parser, runtime, tokens, root) {
+ if (!tokens.matchOpToken(".")) return;
+ var prop = tokens.requireTokenType("IDENTIFIER");
+ var propertyAccess = {
+ type: "propertyAccess",
+ root: root,
+ prop: prop,
+ args: [root],
+ op: function (_context, rootVal) {
+ var value = runtime.resolveProperty(rootVal, prop.value);
+ return value;
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ return parser.parseElement("indirectExpression", tokens, propertyAccess);
+ });
+
+ parser.addIndirectExpression("of", function (parser, runtime, tokens, root) {
+ if (!tokens.matchToken("of")) return;
+ var newRoot = parser.requireElement("unaryExpression", tokens);
+ // find the urroot
+ var childOfUrRoot = null;
+ var urRoot = root;
+ while (urRoot.root) {
+ childOfUrRoot = urRoot;
+ urRoot = urRoot.root;
+ }
+ if (urRoot.type !== "symbol" && urRoot.type !== "attributeRef" && urRoot.type !== "styleRef" && urRoot.type !== "computedStyleRef") {
+ parser.raiseParseError(tokens, "Cannot take a property of a non-symbol: " + urRoot.type);
+ }
+ var attribute = urRoot.type === "attributeRef";
+ var style = urRoot.type === "styleRef" || urRoot.type === "computedStyleRef";
+ if (attribute || style) {
+ var attributeElt = urRoot
+ }
+ var prop = urRoot.name;
+
+ var propertyAccess = {
+ type: "ofExpression",
+ prop: urRoot.token,
+ root: newRoot,
+ attribute: attributeElt,
+ expression: root,
+ args: [newRoot],
+ op: function (context, rootVal) {
+ if (attribute) {
+ return runtime.resolveAttribute(rootVal, prop);
+ } else if (style) {
+ if (urRoot.type === "computedStyleRef") {
+ return runtime.resolveComputedStyle(rootVal, prop);
+ } else {
+ return runtime.resolveStyle(rootVal, prop);
+ }
+ } else {
+ return runtime.resolveProperty(rootVal, prop);
+ }
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+
+ if (urRoot.type === "attributeRef") {
+ propertyAccess.attribute = urRoot;
+ }
+ if (childOfUrRoot) {
+ childOfUrRoot.root = propertyAccess;
+ childOfUrRoot.args = [propertyAccess];
+ } else {
+ root = propertyAccess;
+ }
+
+ return parser.parseElement("indirectExpression", tokens, root);
+ });
+
+ parser.addIndirectExpression("possessive", function (parser, runtime, tokens, root) {
+ if (parser.possessivesDisabled) {
+ return;
+ }
+ var apostrophe = tokens.matchOpToken("'");
+ if (
+ apostrophe ||
+ (root.type === "symbol" &&
+ (root.name === "my" || root.name === "its" || root.name === "your") &&
+ (tokens.currentToken().type === "IDENTIFIER" || tokens.currentToken().type === "ATTRIBUTE_REF" || tokens.currentToken().type === "STYLE_REF"))
+ ) {
+ if (apostrophe) {
+ tokens.requireToken("s");
+ }
+
+ var attribute, style, prop;
+ attribute = parser.parseElement("attributeRef", tokens);
+ if (attribute == null) {
+ style = parser.parseElement("styleRef", tokens);
+ if (style == null) {
+ prop = tokens.requireTokenType("IDENTIFIER");
+ }
+ }
+ var propertyAccess = {
+ type: "possessive",
+ root: root,
+ attribute: attribute || style,
+ prop: prop,
+ args: [root],
+ op: function (context, rootVal) {
+ if (attribute) {
+ // @ts-ignore
+ var value = runtime.resolveAttribute(rootVal, attribute.name);
+ } else if (style) {
+ var value
+ if (style.type === 'computedStyleRef') {
+ value = runtime.resolveComputedStyle(rootVal, style['name']);
+ } else {
+ value = runtime.resolveStyle(rootVal, style['name']);
+ }
+ } else {
+ var value = runtime.resolveProperty(rootVal, prop.value);
+ }
+ return value;
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ return parser.parseElement("indirectExpression", tokens, propertyAccess);
+ }
+ });
+
+ parser.addIndirectExpression("inExpression", function (parser, runtime, tokens, root) {
+ if (!tokens.matchToken("in")) return;
+ var target = parser.requireElement("unaryExpression", tokens);
+ var propertyAccess = {
+ type: "inExpression",
+ root: root,
+ args: [root, target],
+ op: function (context, rootVal, target) {
+ var returnArr = [];
+ if (rootVal.css) {
+ runtime.implicitLoop(target, function (targetElt) {
+ var results = targetElt.querySelectorAll(rootVal.css);
+ for (var i = 0; i < results.length; i++) {
+ returnArr.push(results[i]);
+ }
+ });
+ } else if (rootVal instanceof Element) {
+ var within = false;
+ runtime.implicitLoop(target, function (targetElt) {
+ if (targetElt.contains(rootVal)) {
+ within = true;
+ }
+ });
+ if(within) {
+ return rootVal;
+ }
+ } else {
+ runtime.implicitLoop(rootVal, function (rootElt) {
+ runtime.implicitLoop(target, function (targetElt) {
+ if (rootElt === targetElt) {
+ returnArr.push(rootElt);
+ }
+ });
+ });
+ }
+ return returnArr;
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ return parser.parseElement("indirectExpression", tokens, propertyAccess);
+ });
+
+ parser.addIndirectExpression("asExpression", function (parser, runtime, tokens, root) {
+ if (!tokens.matchToken("as")) return;
+ tokens.matchToken("a") || tokens.matchToken("an");
+ var conversion = parser.requireElement("dotOrColonPath", tokens).evaluate(); // OK No promise
+ var propertyAccess = {
+ type: "asExpression",
+ root: root,
+ args: [root],
+ op: function (context, rootVal) {
+ return runtime.convertValue(rootVal, conversion);
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ return parser.parseElement("indirectExpression", tokens, propertyAccess);
+ });
+
+ parser.addIndirectExpression("functionCall", function (parser, runtime, tokens, root) {
+ if (!tokens.matchOpToken("(")) return;
+ var args = [];
+ if (!tokens.matchOpToken(")")) {
+ do {
+ args.push(parser.requireElement("expression", tokens));
+ } while (tokens.matchOpToken(","));
+ tokens.requireOpToken(")");
+ }
+
+ if (root.root) {
+ var functionCall = {
+ type: "functionCall",
+ root: root,
+ argExressions: args,
+ args: [root.root, args],
+ op: function (context, rootRoot, args) {
+ runtime.nullCheck(rootRoot, root.root);
+ var func = rootRoot[root.prop.value];
+ runtime.nullCheck(func, root);
+ if (func.hyperfunc) {
+ args.push(context);
+ }
+ return func.apply(rootRoot, args);
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ } else {
+ var functionCall = {
+ type: "functionCall",
+ root: root,
+ argExressions: args,
+ args: [root, args],
+ op: function (context, func, argVals) {
+ runtime.nullCheck(func, root);
+ if (func.hyperfunc) {
+ argVals.push(context);
+ }
+ var apply = func.apply(null, argVals);
+ return apply;
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ }
+ return parser.parseElement("indirectExpression", tokens, functionCall);
+ });
+
+ parser.addIndirectExpression("attributeRefAccess", function (parser, runtime, tokens, root) {
+ var attribute = parser.parseElement("attributeRef", tokens);
+ if (!attribute) return;
+ var attributeAccess = {
+ type: "attributeRefAccess",
+ root: root,
+ attribute: attribute,
+ args: [root],
+ op: function (_ctx, rootVal) {
+ // @ts-ignore
+ var value = runtime.resolveAttribute(rootVal, attribute.name);
+ return value;
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ return attributeAccess;
+ });
+
+ parser.addIndirectExpression("arrayIndex", function (parser, runtime, tokens, root) {
+ if (!tokens.matchOpToken("[")) return;
+ var andBefore = false;
+ var andAfter = false;
+ var firstIndex = null;
+ var secondIndex = null;
+
+ if (tokens.matchOpToken("..")) {
+ andBefore = true;
+ firstIndex = parser.requireElement("expression", tokens);
+ } else {
+ firstIndex = parser.requireElement("expression", tokens);
+
+ if (tokens.matchOpToken("..")) {
+ andAfter = true;
+ var current = tokens.currentToken();
+ if (current.type !== "R_BRACKET") {
+ secondIndex = parser.parseElement("expression", tokens);
+ }
+ }
+ }
+ tokens.requireOpToken("]");
+
+ var arrayIndex = {
+ type: "arrayIndex",
+ root: root,
+ prop: firstIndex,
+ firstIndex: firstIndex,
+ secondIndex: secondIndex,
+ args: [root, firstIndex, secondIndex],
+ op: function (_ctx, root, firstIndex, secondIndex) {
+ if (root == null) {
+ return null;
+ }
+ if (andBefore) {
+ if (firstIndex < 0) {
+ firstIndex = root.length + firstIndex;
+ }
+ return root.slice(0, firstIndex + 1); // returns all items from beginning to firstIndex (inclusive)
+ } else if (andAfter) {
+ if (secondIndex != null) {
+ if (secondIndex < 0) {
+ secondIndex = root.length + secondIndex;
+ }
+ return root.slice(firstIndex, secondIndex + 1); // returns all items from firstIndex to secondIndex (inclusive)
+ } else {
+ return root.slice(firstIndex); // returns from firstIndex to end of array
+ }
+ } else {
+ return root[firstIndex];
+ }
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+
+ return parser.parseElement("indirectExpression", tokens, arrayIndex);
+ });
+
+ // taken from https://drafts.csswg.org/css-values-4/#relative-length
+ // and https://drafts.csswg.org/css-values-4/#absolute-length
+ // (NB: we do not support `in` dues to conflicts w/ the hyperscript grammar)
+ var STRING_POSTFIXES = [
+ 'em', 'ex', 'cap', 'ch', 'ic', 'rem', 'lh', 'rlh', 'vw', 'vh', 'vi', 'vb', 'vmin', 'vmax',
+ 'cm', 'mm', 'Q', 'pc', 'pt', 'px'
+ ];
+ parser.addGrammarElement("postfixExpression", function (parser, runtime, tokens) {
+ var root = parser.parseElement("primaryExpression", tokens);
+
+ let stringPosfix = tokens.matchAnyToken.apply(tokens, STRING_POSTFIXES) || tokens.matchOpToken("%");
+ if (stringPosfix) {
+ return {
+ type: "stringPostfix",
+ postfix: stringPosfix.value,
+ args: [root],
+ op: function (context, val) {
+ return "" + val + stringPosfix.value;
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ }
+
+ var timeFactor = null;
+ if (tokens.matchToken("s") || tokens.matchToken("seconds")) {
+ timeFactor = 1000;
+ } else if (tokens.matchToken("ms") || tokens.matchToken("milliseconds")) {
+ timeFactor = 1;
+ }
+ if (timeFactor) {
+ return {
+ type: "timeExpression",
+ time: root,
+ factor: timeFactor,
+ args: [root],
+ op: function (_context, val) {
+ return val * timeFactor;
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ }
+
+ if (tokens.matchOpToken(":")) {
+ var typeName = tokens.requireTokenType("IDENTIFIER");
+ if (!typeName.value) return;
+ var nullOk = !tokens.matchOpToken("!");
+ return {
+ type: "typeCheck",
+ typeName: typeName,
+ nullOk: nullOk,
+ args: [root],
+ op: function (context, val) {
+ var passed = runtime.typeCheck(val, this.typeName.value, nullOk);
+ if (passed) {
+ return val;
+ } else {
+ throw new Error("Typecheck failed! Expected: " + typeName.value);
+ }
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ } else {
+ return root;
+ }
+ });
+
+ parser.addGrammarElement("logicalNot", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("not")) return;
+ var root = parser.requireElement("unaryExpression", tokens);
+ return {
+ type: "logicalNot",
+ root: root,
+ args: [root],
+ op: function (context, val) {
+ return !val;
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ });
+
+ parser.addGrammarElement("noExpression", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("no")) return;
+ var root = parser.requireElement("unaryExpression", tokens);
+ return {
+ type: "noExpression",
+ root: root,
+ args: [root],
+ op: function (_context, val) {
+ return runtime.isEmpty(val);
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ });
+
+ parser.addLeafExpression("some", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("some")) return;
+ var root = parser.requireElement("expression", tokens);
+ return {
+ type: "noExpression",
+ root: root,
+ args: [root],
+ op: function (_context, val) {
+ return !runtime.isEmpty(val);
+ },
+ evaluate(context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ });
+
+ parser.addGrammarElement("negativeNumber", function (parser, runtime, tokens) {
+ if (!tokens.matchOpToken("-")) return;
+ var root = parser.requireElement("unaryExpression", tokens);
+ return {
+ type: "negativeNumber",
+ root: root,
+ args: [root],
+ op: function (context, value) {
+ return -1 * value;
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ });
+
+ parser.addGrammarElement("unaryExpression", function (parser, runtime, tokens) {
+ tokens.matchToken("the"); // optional "the"
+ return parser.parseAnyOf(
+ ["beepExpression", "logicalNot", "relativePositionalExpression", "positionalExpression", "noExpression", "negativeNumber", "postfixExpression"],
+ tokens
+ );
+ });
+
+ parser.addGrammarElement("beepExpression", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("beep!")) return;
+ var expression = parser.parseElement("unaryExpression", tokens);
+ if (expression) {
+ expression['booped'] = true;
+ var originalEvaluate = expression.evaluate;
+ expression.evaluate = function(ctx){
+ let value = originalEvaluate.apply(expression, arguments);
+ let element = ctx.me;
+ runtime.beepValueToConsole(element, expression, value);
+ return value;
+ }
+ return expression;
+ }
+ });
+
+ var scanForwardQuery = function(start, root, match, wrap) {
+ var results = root.querySelectorAll(match);
+ for (var i = 0; i < results.length; i++) {
+ var elt = results[i];
+ if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_PRECEDING) {
+ return elt;
+ }
+ }
+ if (wrap) {
+ return results[0];
+ }
+ }
+
+ var scanBackwardsQuery = function(start, root, match, wrap) {
+ var results = root.querySelectorAll(match);
+ for (var i = results.length - 1; i >= 0; i--) {
+ var elt = results[i];
+ if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_FOLLOWING) {
+ return elt;
+ }
+ }
+ if (wrap) {
+ return results[results.length - 1];
+ }
+ }
+
+ var scanForwardArray = function(start, array, match, wrap) {
+ var matches = [];
+ Runtime.prototype.forEach(array, function(elt){
+ if (elt.matches(match) || elt === start) {
+ matches.push(elt);
+ }
+ })
+ for (var i = 0; i < matches.length - 1; i++) {
+ var elt = matches[i];
+ if (elt === start) {
+ return matches[i + 1];
+ }
+ }
+ if (wrap) {
+ var first = matches[0];
+ if (first && first.matches(match)) {
+ return first;
+ }
+ }
+ }
+
+ var scanBackwardsArray = function(start, array, match, wrap) {
+ return scanForwardArray(start, Array.from(array).reverse(), match, wrap);
+ }
+
+ parser.addGrammarElement("relativePositionalExpression", function (parser, runtime, tokens) {
+ var op = tokens.matchAnyToken("next", "previous");
+ if (!op) return;
+ var forwardSearch = op.value === "next";
+
+ var thingElt = parser.parseElement("expression", tokens);
+
+ if (tokens.matchToken("from")) {
+ tokens.pushFollow("in");
+ try {
+ var from = parser.requireElement("unaryExpression", tokens);
+ } finally {
+ tokens.popFollow();
+ }
+ } else {
+ var from = parser.requireElement("implicitMeTarget", tokens);
+ }
+
+ var inSearch = false;
+ var withinElt;
+ if (tokens.matchToken("in")) {
+ inSearch = true;
+ var inElt = parser.requireElement("unaryExpression", tokens);
+ } else if (tokens.matchToken("within")) {
+ withinElt = parser.requireElement("unaryExpression", tokens);
+ } else {
+ withinElt = document.body;
+ }
+
+ var wrapping = false;
+ if (tokens.matchToken("with")) {
+ tokens.requireToken("wrapping")
+ wrapping = true;
+ }
+
+ return {
+ type: "relativePositionalExpression",
+ from: from,
+ forwardSearch: forwardSearch,
+ inSearch: inSearch,
+ wrapping: wrapping,
+ inElt: inElt,
+ withinElt: withinElt,
+ operator: op.value,
+ args: [thingElt, from, inElt, withinElt],
+ op: function (context, thing, from, inElt, withinElt) {
+
+ var css = thing.css;
+ if (css == null) {
+ throw "Expected a CSS value to be returned by " + Tokens.sourceFor.apply(thingElt);
+ }
+
+ if(inSearch) {
+ if (inElt) {
+ if (forwardSearch) {
+ return scanForwardArray(from, inElt, css, wrapping);
+ } else {
+ return scanBackwardsArray(from, inElt, css, wrapping);
+ }
+ }
+ } else {
+ if (withinElt) {
+ if (forwardSearch) {
+ return scanForwardQuery(from, withinElt, css, wrapping);
+ } else {
+ return scanBackwardsQuery(from, withinElt, css, wrapping);
+ }
+ }
+ }
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ }
+
+ });
+
+ parser.addGrammarElement("positionalExpression", function (parser, runtime, tokens) {
+ var op = tokens.matchAnyToken("first", "last", "random");
+ if (!op) return;
+ tokens.matchAnyToken("in", "from", "of");
+ var rhs = parser.requireElement("unaryExpression", tokens);
+ const operator = op.value;
+ return {
+ type: "positionalExpression",
+ rhs: rhs,
+ operator: op.value,
+ args: [rhs],
+ op: function (context, rhsVal) {
+ if (rhsVal && !Array.isArray(rhsVal)) {
+ if (rhsVal.children) {
+ rhsVal = rhsVal.children;
+ } else {
+ rhsVal = Array.from(rhsVal);
+ }
+ }
+ if (rhsVal) {
+ if (operator === "first") {
+ return rhsVal[0];
+ } else if (operator === "last") {
+ return rhsVal[rhsVal.length - 1];
+ } else if (operator === "random") {
+ return rhsVal[Math.floor(Math.random() * rhsVal.length)];
+ }
+ }
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ });
+
+ parser.addGrammarElement("mathOperator", function (parser, runtime, tokens) {
+ var expr = parser.parseElement("unaryExpression", tokens);
+ var mathOp,
+ initialMathOp = null;
+ mathOp = tokens.matchAnyOpToken("+", "-", "*", "/") || tokens.matchToken('mod');
+ while (mathOp) {
+ initialMathOp = initialMathOp || mathOp;
+ var operator = mathOp.value;
+ if (initialMathOp.value !== operator) {
+ parser.raiseParseError(tokens, "You must parenthesize math operations with different operators");
+ }
+ var rhs = parser.parseElement("unaryExpression", tokens);
+ expr = {
+ type: "mathOperator",
+ lhs: expr,
+ rhs: rhs,
+ operator: operator,
+ args: [expr, rhs],
+ op: function (context, lhsVal, rhsVal) {
+ if (operator === "+") {
+ return lhsVal + rhsVal;
+ } else if (operator === "-") {
+ return lhsVal - rhsVal;
+ } else if (operator === "*") {
+ return lhsVal * rhsVal;
+ } else if (operator === "/") {
+ return lhsVal / rhsVal;
+ } else if (operator === "mod") {
+ return lhsVal % rhsVal;
+ }
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ mathOp = tokens.matchAnyOpToken("+", "-", "*", "/") || tokens.matchToken('mod');
+ }
+ return expr;
+ });
+
+ parser.addGrammarElement("mathExpression", function (parser, runtime, tokens) {
+ return parser.parseAnyOf(["mathOperator", "unaryExpression"], tokens);
+ });
+
+ function sloppyContains(src, container, value){
+ if (container['contains']) {
+ return container.contains(value);
+ } else if (container['includes']) {
+ return container.includes(value);
+ } else {
+ throw Error("The value of " + src.sourceFor() + " does not have a contains or includes method on it");
+ }
+ }
+ function sloppyMatches(src, target, toMatch){
+ if (target['match']) {
+ return !!target.match(toMatch);
+ } else if (target['matches']) {
+ return target.matches(toMatch);
+ } else {
+ throw Error("The value of " + src.sourceFor() + " does not have a match or matches method on it");
+ }
+ }
+
+ parser.addGrammarElement("comparisonOperator", function (parser, runtime, tokens) {
+ var expr = parser.parseElement("mathExpression", tokens);
+ var comparisonToken = tokens.matchAnyOpToken("<", ">", "<=", ">=", "==", "===", "!=", "!==");
+ var operator = comparisonToken ? comparisonToken.value : null;
+ var hasRightValue = true; // By default, most comparisons require two values, but there are some exceptions.
+ var typeCheck = false;
+
+ if (operator == null) {
+ if (tokens.matchToken("is") || tokens.matchToken("am")) {
+ if (tokens.matchToken("not")) {
+ if (tokens.matchToken("in")) {
+ operator = "not in";
+ } else if (tokens.matchToken("a")) {
+ operator = "not a";
+ typeCheck = true;
+ } else if (tokens.matchToken("empty")) {
+ operator = "not empty";
+ hasRightValue = false;
+ } else {
+ if (tokens.matchToken("really")) {
+ operator = "!==";
+ } else {
+ operator = "!=";
+ }
+ // consume additional optional syntax
+ if (tokens.matchToken("equal")) {
+ tokens.matchToken("to");
+ }
+ }
+ } else if (tokens.matchToken("in")) {
+ operator = "in";
+ } else if (tokens.matchToken("a")) {
+ operator = "a";
+ typeCheck = true;
+ } else if (tokens.matchToken("empty")) {
+ operator = "empty";
+ hasRightValue = false;
+ } else if (tokens.matchToken("less")) {
+ tokens.requireToken("than");
+ if (tokens.matchToken("or")) {
+ tokens.requireToken("equal");
+ tokens.requireToken("to");
+ operator = "<=";
+ } else {
+ operator = "<";
+ }
+ } else if (tokens.matchToken("greater")) {
+ tokens.requireToken("than");
+ if (tokens.matchToken("or")) {
+ tokens.requireToken("equal");
+ tokens.requireToken("to");
+ operator = ">=";
+ } else {
+ operator = ">";
+ }
+ } else {
+ if (tokens.matchToken("really")) {
+ operator = "===";
+ } else {
+ operator = "==";
+ }
+ if (tokens.matchToken("equal")) {
+ tokens.matchToken("to");
+ }
+ }
+ } else if (tokens.matchToken("equals")) {
+ operator = "==";
+ } else if (tokens.matchToken("really")) {
+ tokens.requireToken("equals")
+ operator = "===";
+ } else if (tokens.matchToken("exist") || tokens.matchToken("exists")) {
+ operator = "exist";
+ hasRightValue = false;
+ } else if (tokens.matchToken("matches") || tokens.matchToken("match")) {
+ operator = "match";
+ } else if (tokens.matchToken("contains") || tokens.matchToken("contain")) {
+ operator = "contain";
+ } else if (tokens.matchToken("includes") || tokens.matchToken("include")) {
+ operator = "include";
+ } else if (tokens.matchToken("do") || tokens.matchToken("does")) {
+ tokens.requireToken("not");
+ if (tokens.matchToken("matches") || tokens.matchToken("match")) {
+ operator = "not match";
+ } else if (tokens.matchToken("contains") || tokens.matchToken("contain")) {
+ operator = "not contain";
+ } else if (tokens.matchToken("exist") || tokens.matchToken("exist")) {
+ operator = "not exist";
+ hasRightValue = false;
+ } else if (tokens.matchToken("include")) {
+ operator = "not include";
+ } else {
+ parser.raiseParseError(tokens, "Expected matches or contains");
+ }
+ }
+ }
+
+ if (operator) {
+ // Do not allow chained comparisons, which is dumb
+ var typeName, nullOk, rhs
+ if (typeCheck) {
+ typeName = tokens.requireTokenType("IDENTIFIER");
+ nullOk = !tokens.matchOpToken("!");
+ } else if (hasRightValue) {
+ rhs = parser.requireElement("mathExpression", tokens);
+ if (operator === "match" || operator === "not match") {
+ rhs = rhs.css ? rhs.css : rhs;
+ }
+ }
+ var lhs = expr;
+ expr = {
+ type: "comparisonOperator",
+ operator: operator,
+ typeName: typeName,
+ nullOk: nullOk,
+ lhs: expr,
+ rhs: rhs,
+ args: [expr, rhs],
+ op: function (context, lhsVal, rhsVal) {
+ if (operator === "==") {
+ return lhsVal == rhsVal;
+ } else if (operator === "!=") {
+ return lhsVal != rhsVal;
+ }
+ if (operator === "===") {
+ return lhsVal === rhsVal;
+ } else if (operator === "!==") {
+ return lhsVal !== rhsVal;
+ }
+ if (operator === "match") {
+ return lhsVal != null && sloppyMatches(lhs, lhsVal, rhsVal);
+ }
+ if (operator === "not match") {
+ return lhsVal == null || !sloppyMatches(lhs, lhsVal, rhsVal);
+ }
+ if (operator === "in") {
+ return rhsVal != null && sloppyContains(rhs, rhsVal, lhsVal);
+ }
+ if (operator === "not in") {
+ return rhsVal == null || !sloppyContains(rhs, rhsVal, lhsVal);
+ }
+ if (operator === "contain") {
+ return lhsVal != null && sloppyContains(lhs, lhsVal, rhsVal);
+ }
+ if (operator === "not contain") {
+ return lhsVal == null || !sloppyContains(lhs, lhsVal, rhsVal);
+ }
+ if (operator === "include") {
+ return lhsVal != null && sloppyContains(lhs, lhsVal, rhsVal);
+ }
+ if (operator === "not include") {
+ return lhsVal == null || !sloppyContains(lhs, lhsVal, rhsVal);
+ }
+ if (operator === "===") {
+ return lhsVal === rhsVal;
+ } else if (operator === "!==") {
+ return lhsVal !== rhsVal;
+ } else if (operator === "<") {
+ return lhsVal < rhsVal;
+ } else if (operator === ">") {
+ return lhsVal > rhsVal;
+ } else if (operator === "<=") {
+ return lhsVal <= rhsVal;
+ } else if (operator === ">=") {
+ return lhsVal >= rhsVal;
+ } else if (operator === "empty") {
+ return runtime.isEmpty(lhsVal);
+ } else if (operator === "not empty") {
+ return !runtime.isEmpty(lhsVal);
+ } else if (operator === "exist") {
+ return runtime.doesExist(lhsVal);
+ } else if (operator === "not exist") {
+ return !runtime.doesExist(lhsVal);
+ } else if (operator === "a") {
+ return runtime.typeCheck(lhsVal, typeName.value, nullOk);
+ } else if (operator === "not a") {
+ return !runtime.typeCheck(lhsVal, typeName.value, nullOk);
+ } else {
+ throw "Unknown comparison : " + operator;
+ }
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ }
+ return expr;
+ });
+
+ parser.addGrammarElement("comparisonExpression", function (parser, runtime, tokens) {
+ return parser.parseAnyOf(["comparisonOperator", "mathExpression"], tokens);
+ });
+
+ parser.addGrammarElement("logicalOperator", function (parser, runtime, tokens) {
+ var expr = parser.parseElement("comparisonExpression", tokens);
+ var logicalOp,
+ initialLogicalOp = null;
+ logicalOp = tokens.matchToken("and") || tokens.matchToken("or");
+ while (logicalOp) {
+ initialLogicalOp = initialLogicalOp || logicalOp;
+ if (initialLogicalOp.value !== logicalOp.value) {
+ parser.raiseParseError(tokens, "You must parenthesize logical operations with different operators");
+ }
+ var rhs = parser.requireElement("comparisonExpression", tokens);
+ const operator = logicalOp.value;
+ expr = {
+ type: "logicalOperator",
+ operator: operator,
+ lhs: expr,
+ rhs: rhs,
+ args: [expr, rhs],
+ op: function (context, lhsVal, rhsVal) {
+ if (operator === "and") {
+ return lhsVal && rhsVal;
+ } else {
+ return lhsVal || rhsVal;
+ }
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+ logicalOp = tokens.matchToken("and") || tokens.matchToken("or");
+ }
+ return expr;
+ });
+
+ parser.addGrammarElement("logicalExpression", function (parser, runtime, tokens) {
+ return parser.parseAnyOf(["logicalOperator", "mathExpression"], tokens);
+ });
+
+ parser.addGrammarElement("asyncExpression", function (parser, runtime, tokens) {
+ if (tokens.matchToken("async")) {
+ var value = parser.requireElement("logicalExpression", tokens);
+ var expr = {
+ type: "asyncExpression",
+ value: value,
+ evaluate: function (context) {
+ return {
+ asyncWrapper: true,
+ value: this.value.evaluate(context), //OK
+ };
+ },
+ };
+ return expr;
+ } else {
+ return parser.parseElement("logicalExpression", tokens);
+ }
+ });
+
+ parser.addGrammarElement("expression", function (parser, runtime, tokens) {
+ tokens.matchToken("the"); // optional the
+ return parser.parseElement("asyncExpression", tokens);
+ });
+
+ parser.addGrammarElement("assignableExpression", function (parser, runtime, tokens) {
+ tokens.matchToken("the"); // optional the
+
+ // TODO obviously we need to generalize this as a left hand side / targetable concept
+ var expr = parser.parseElement("primaryExpression", tokens);
+ if (expr && (
+ expr.type === "symbol" ||
+ expr.type === "ofExpression" ||
+ expr.type === "propertyAccess" ||
+ expr.type === "attributeRefAccess" ||
+ expr.type === "attributeRef" ||
+ expr.type === "styleRef" ||
+ expr.type === "arrayIndex" ||
+ expr.type === "possessive")
+ ) {
+ return expr;
+ } else {
+ parser.raiseParseError(
+ tokens,
+ "A target expression must be writable. The expression type '" + (expr && expr.type) + "' is not."
+ );
+ }
+ return expr;
+ });
+
+ parser.addGrammarElement("hyperscript", function (parser, runtime, tokens) {
+ var features = [];
+
+ if (tokens.hasMore()) {
+ while (parser.featureStart(tokens.currentToken()) || tokens.currentToken().value === "(") {
+ var feature = parser.requireElement("feature", tokens);
+ features.push(feature);
+ tokens.matchToken("end"); // optional end
+ }
+ }
+ return {
+ type: "hyperscript",
+ features: features,
+ apply: function (target, source, args) {
+ // no op
+ for (const feature of features) {
+ feature.install(target, source, args);
+ }
+ },
+ };
+ });
+
+ var parseEventArgs = function (tokens) {
+ var args = [];
+ // handle argument list (look ahead 3)
+ if (
+ tokens.token(0).value === "(" &&
+ (tokens.token(1).value === ")" || tokens.token(2).value === "," || tokens.token(2).value === ")")
+ ) {
+ tokens.matchOpToken("(");
+ do {
+ args.push(tokens.requireTokenType("IDENTIFIER"));
+ } while (tokens.matchOpToken(","));
+ tokens.requireOpToken(")");
+ }
+ return args;
+ };
+
+ parser.addFeature("on", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("on")) return;
+ var every = false;
+ if (tokens.matchToken("every")) {
+ every = true;
+ }
+ var events = [];
+ var displayName = null;
+ do {
+ var on = parser.requireElement("eventName", tokens, "Expected event name");
+
+ var eventName = on.evaluate(); // OK No Promise
+
+ if (displayName) {
+ displayName = displayName + " or " + eventName;
+ } else {
+ displayName = "on " + eventName;
+ }
+ var args = parseEventArgs(tokens);
+
+ var filter = null;
+ if (tokens.matchOpToken("[")) {
+ filter = parser.requireElement("expression", tokens);
+ tokens.requireOpToken("]");
+ }
+
+ var startCount, endCount ,unbounded;
+ if (tokens.currentToken().type === "NUMBER") {
+ var startCountToken = tokens.consumeToken();
+ if (!startCountToken.value) return;
+ startCount = parseInt(startCountToken.value);
+ if (tokens.matchToken("to")) {
+ var endCountToken = tokens.consumeToken();
+ if (!endCountToken.value) return;
+ endCount = parseInt(endCountToken.value);
+ } else if (tokens.matchToken("and")) {
+ unbounded = true;
+ tokens.requireToken("on");
+ }
+ }
+
+ var intersectionSpec, mutationSpec;
+ if (eventName === "intersection") {
+ intersectionSpec = {};
+ if (tokens.matchToken("with")) {
+ intersectionSpec["with"] = parser.requireElement("expression", tokens).evaluate();
+ }
+ if (tokens.matchToken("having")) {
+ do {
+ if (tokens.matchToken("margin")) {
+ intersectionSpec["rootMargin"] = parser.requireElement("stringLike", tokens).evaluate();
+ } else if (tokens.matchToken("threshold")) {
+ intersectionSpec["threshold"] = parser.requireElement("expression", tokens).evaluate();
+ } else {
+ parser.raiseParseError(tokens, "Unknown intersection config specification");
+ }
+ } while (tokens.matchToken("and"));
+ }
+ } else if (eventName === "mutation") {
+ mutationSpec = {};
+ if (tokens.matchToken("of")) {
+ do {
+ if (tokens.matchToken("anything")) {
+ mutationSpec["attributes"] = true;
+ mutationSpec["subtree"] = true;
+ mutationSpec["characterData"] = true;
+ mutationSpec["childList"] = true;
+ } else if (tokens.matchToken("childList")) {
+ mutationSpec["childList"] = true;
+ } else if (tokens.matchToken("attributes")) {
+ mutationSpec["attributes"] = true;
+ mutationSpec["attributeOldValue"] = true;
+ } else if (tokens.matchToken("subtree")) {
+ mutationSpec["subtree"] = true;
+ } else if (tokens.matchToken("characterData")) {
+ mutationSpec["characterData"] = true;
+ mutationSpec["characterDataOldValue"] = true;
+ } else if (tokens.currentToken().type === "ATTRIBUTE_REF") {
+ var attribute = tokens.consumeToken();
+ if (mutationSpec["attributeFilter"] == null) {
+ mutationSpec["attributeFilter"] = [];
+ }
+ if (attribute.value.indexOf("@") == 0) {
+ mutationSpec["attributeFilter"].push(attribute.value.substring(1));
+ } else {
+ parser.raiseParseError(
+ tokens,
+ "Only shorthand attribute references are allowed here"
+ );
+ }
+ } else {
+ parser.raiseParseError(tokens, "Unknown mutation config specification");
+ }
+ } while (tokens.matchToken("or"));
+ } else {
+ mutationSpec["attributes"] = true;
+ mutationSpec["characterData"] = true;
+ mutationSpec["childList"] = true;
+ }
+ }
+
+ var from = null;
+ var elsewhere = false;
+ if (tokens.matchToken("from")) {
+ if (tokens.matchToken("elsewhere")) {
+ elsewhere = true;
+ } else {
+ tokens.pushFollow("or");
+ try {
+ from = parser.requireElement("expression", tokens)
+ } finally {
+ tokens.popFollow();
+ }
+ if (!from) {
+ parser.raiseParseError(tokens, 'Expected either target value or "elsewhere".');
+ }
+ }
+ }
+ // support both "elsewhere" and "from elsewhere"
+ if (from === null && elsewhere === false && tokens.matchToken("elsewhere")) {
+ elsewhere = true;
+ }
+
+ if (tokens.matchToken("in")) {
+ var inExpr = parser.parseElement('unaryExpression', tokens);
+ }
+
+ if (tokens.matchToken("debounced")) {
+ tokens.requireToken("at");
+ var timeExpr = parser.requireElement("unaryExpression", tokens);
+ // @ts-ignore
+ var debounceTime = timeExpr.evaluate({}); // OK No promise TODO make a literal time expr
+ } else if (tokens.matchToken("throttled")) {
+ tokens.requireToken("at");
+ var timeExpr = parser.requireElement("unaryExpression", tokens);
+ // @ts-ignore
+ var throttleTime = timeExpr.evaluate({}); // OK No promise TODO make a literal time expr
+ }
+
+ events.push({
+ execCount: 0,
+ every: every,
+ on: eventName,
+ args: args,
+ filter: filter,
+ from: from,
+ inExpr: inExpr,
+ elsewhere: elsewhere,
+ startCount: startCount,
+ endCount: endCount,
+ unbounded: unbounded,
+ debounceTime: debounceTime,
+ throttleTime: throttleTime,
+ mutationSpec: mutationSpec,
+ intersectionSpec: intersectionSpec,
+ debounced: undefined,
+ lastExec: undefined,
+ });
+ } while (tokens.matchToken("or"));
+
+ var queueLast = true;
+ if (!every) {
+ if (tokens.matchToken("queue")) {
+ if (tokens.matchToken("all")) {
+ var queueAll = true;
+ var queueLast = false;
+ } else if (tokens.matchToken("first")) {
+ var queueFirst = true;
+ } else if (tokens.matchToken("none")) {
+ var queueNone = true;
+ } else {
+ tokens.requireToken("last");
+ }
+ }
+ }
+
+ var start = parser.requireElement("commandList", tokens);
+ parser.ensureTerminated(start);
+
+ var errorSymbol, errorHandler;
+ if (tokens.matchToken("catch")) {
+ errorSymbol = tokens.requireTokenType("IDENTIFIER").value;
+ errorHandler = parser.requireElement("commandList", tokens);
+ parser.ensureTerminated(errorHandler);
+ }
+
+ if (tokens.matchToken("finally")) {
+ var finallyHandler = parser.requireElement("commandList", tokens);
+ parser.ensureTerminated(finallyHandler);
+ }
+
+ var onFeature = {
+ displayName: displayName,
+ events: events,
+ start: start,
+ every: every,
+ execCount: 0,
+ errorHandler: errorHandler,
+ errorSymbol: errorSymbol,
+ execute: function (/** @type {Context} */ ctx) {
+ let eventQueueInfo = runtime.getEventQueueFor(ctx.me, onFeature);
+ if (eventQueueInfo.executing && every === false) {
+ if (queueNone || (queueFirst && eventQueueInfo.queue.length > 0)) {
+ return;
+ }
+ if (queueLast) {
+ eventQueueInfo.queue.length = 0;
+ }
+ eventQueueInfo.queue.push(ctx);
+ return;
+ }
+ onFeature.execCount++;
+ eventQueueInfo.executing = true;
+ ctx.meta.onHalt = function () {
+ eventQueueInfo.executing = false;
+ var queued = eventQueueInfo.queue.shift();
+ if (queued) {
+ setTimeout(function () {
+ onFeature.execute(queued);
+ }, 1);
+ }
+ };
+ ctx.meta.reject = function (err) {
+ console.error(err.message ? err.message : err);
+ var hypertrace = runtime.getHyperTrace(ctx, err);
+ if (hypertrace) {
+ hypertrace.print();
+ }
+ runtime.triggerEvent(ctx.me, "exception", {
+ error: err,
+ });
+ };
+ start.execute(ctx);
+ },
+ install: function (elt, source) {
+ for (const eventSpec of onFeature.events) {
+ var targets;
+ if (eventSpec.elsewhere) {
+ targets = [document];
+ } else if (eventSpec.from) {
+ targets = eventSpec.from.evaluate(runtime.makeContext(elt, onFeature, elt, null));
+ } else {
+ targets = [elt];
+ }
+ runtime.implicitLoop(targets, function (target) {
+ // OK NO PROMISE
+
+ var eventName = eventSpec.on;
+ if (target == null) {
+ console.warn("'%s' feature ignored because target does not exists:", displayName, elt);
+ return;
+ }
+
+ if (eventSpec.mutationSpec) {
+ eventName = "hyperscript:mutation";
+ const observer = new MutationObserver(function (mutationList, observer) {
+ if (!onFeature.executing) {
+ runtime.triggerEvent(target, eventName, {
+ mutationList: mutationList,
+ observer: observer,
+ });
+ }
+ });
+ observer.observe(target, eventSpec.mutationSpec);
+ }
+
+ if (eventSpec.intersectionSpec) {
+ eventName = "hyperscript:intersection";
+ const observer = new IntersectionObserver(function (entries) {
+ for (const entry of entries) {
+ var detail = {
+ observer: observer,
+ };
+ detail = Object.assign(detail, entry);
+ detail["intersecting"] = entry.isIntersecting;
+ runtime.triggerEvent(target, eventName, detail);
+ }
+ }, eventSpec.intersectionSpec);
+ observer.observe(target);
+ }
+
+ var addEventListener = target.addEventListener || target.on;
+ addEventListener.call(target, eventName, function listener(evt) {
+ // OK NO PROMISE
+ if (typeof Node !== 'undefined' && elt instanceof Node && target !== elt && !elt.isConnected) {
+ target.removeEventListener(eventName, listener);
+ return;
+ }
+
+ var ctx = runtime.makeContext(elt, onFeature, elt, evt);
+ if (eventSpec.elsewhere && elt.contains(evt.target)) {
+ return;
+ }
+ if (eventSpec.from) {
+ ctx.result = target;
+ }
+
+ // establish context
+ for (const arg of eventSpec.args) {
+ let eventValue = ctx.event[arg.value];
+ if (eventValue !== undefined) {
+ ctx.locals[arg.value] = eventValue;
+ } else if ('detail' in ctx.event) {
+ ctx.locals[arg.value] = ctx.event['detail'][arg.value];
+ }
+ }
+
+ // install error handler if any
+ ctx.meta.errorHandler = errorHandler;
+ ctx.meta.errorSymbol = errorSymbol;
+ ctx.meta.finallyHandler = finallyHandler;
+
+ // apply filter
+ if (eventSpec.filter) {
+ var initialCtx = ctx.meta.context;
+ ctx.meta.context = ctx.event;
+ try {
+ var value = eventSpec.filter.evaluate(ctx); //OK NO PROMISE
+ if (value) {
+ // match the javascript semantics for if statements
+ } else {
+ return;
+ }
+ } finally {
+ ctx.meta.context = initialCtx;
+ }
+ }
+
+ if (eventSpec.inExpr) {
+ var inElement = evt.target;
+ while (true) {
+ if (inElement.matches && inElement.matches(eventSpec.inExpr.css)) {
+ ctx.result = inElement;
+ break;
+ } else {
+ inElement = inElement.parentElement;
+ if (inElement == null) {
+ return; // no match found
+ }
+ }
+ }
+ }
+
+ // verify counts
+ eventSpec.execCount++;
+ if (eventSpec.startCount) {
+ if (eventSpec.endCount) {
+ if (
+ eventSpec.execCount < eventSpec.startCount ||
+ eventSpec.execCount > eventSpec.endCount
+ ) {
+ return;
+ }
+ } else if (eventSpec.unbounded) {
+ if (eventSpec.execCount < eventSpec.startCount) {
+ return;
+ }
+ } else if (eventSpec.execCount !== eventSpec.startCount) {
+ return;
+ }
+ }
+
+ //debounce
+ if (eventSpec.debounceTime) {
+ if (eventSpec.debounced) {
+ clearTimeout(eventSpec.debounced);
+ }
+ eventSpec.debounced = setTimeout(function () {
+ onFeature.execute(ctx);
+ }, eventSpec.debounceTime);
+ return;
+ }
+
+ // throttle
+ if (eventSpec.throttleTime) {
+ if (
+ eventSpec.lastExec &&
+ Date.now() < (eventSpec.lastExec + eventSpec.throttleTime)
+ ) {
+ return;
+ } else {
+ eventSpec.lastExec = Date.now();
+ }
+ }
+
+ // apply execute
+ onFeature.execute(ctx);
+ });
+ });
+ }
+ },
+ };
+ parser.setParent(start, onFeature);
+ return onFeature;
+ });
+
+ parser.addFeature("def", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("def")) return;
+ var functionName = parser.requireElement("dotOrColonPath", tokens);
+ var nameVal = functionName.evaluate(); // OK
+ var nameSpace = nameVal.split(".");
+ var funcName = nameSpace.pop();
+
+ var args = [];
+ if (tokens.matchOpToken("(")) {
+ if (tokens.matchOpToken(")")) {
+ // emtpy args list
+ } else {
+ do {
+ args.push(tokens.requireTokenType("IDENTIFIER"));
+ } while (tokens.matchOpToken(","));
+ tokens.requireOpToken(")");
+ }
+ }
+
+ var start = parser.requireElement("commandList", tokens);
+
+ var errorSymbol, errorHandler;
+ if (tokens.matchToken("catch")) {
+ errorSymbol = tokens.requireTokenType("IDENTIFIER").value;
+ errorHandler = parser.parseElement("commandList", tokens);
+ }
+
+ if (tokens.matchToken("finally")) {
+ var finallyHandler = parser.requireElement("commandList", tokens);
+ parser.ensureTerminated(finallyHandler);
+ }
+
+ var functionFeature = {
+ displayName:
+ funcName +
+ "(" +
+ args
+ .map(function (arg) {
+ return arg.value;
+ })
+ .join(", ") +
+ ")",
+ name: funcName,
+ args: args,
+ start: start,
+ errorHandler: errorHandler,
+ errorSymbol: errorSymbol,
+ finallyHandler: finallyHandler,
+ install: function (target, source) {
+ var func = function () {
+ // null, worker
+ var ctx = runtime.makeContext(source, functionFeature, target, null);
+
+ // install error handler if any
+ ctx.meta.errorHandler = errorHandler;
+ ctx.meta.errorSymbol = errorSymbol;
+ ctx.meta.finallyHandler = finallyHandler;
+
+ for (var i = 0; i < args.length; i++) {
+ var name = args[i];
+ var argumentVal = arguments[i];
+ if (name) {
+ ctx.locals[name.value] = argumentVal;
+ }
+ }
+ ctx.meta.caller = arguments[args.length];
+ if (ctx.meta.caller) {
+ ctx.meta.callingCommand = ctx.meta.caller.meta.command;
+ }
+ var resolve,
+ reject = null;
+ var promise = new Promise(function (theResolve, theReject) {
+ resolve = theResolve;
+ reject = theReject;
+ });
+ start.execute(ctx);
+ if (ctx.meta.returned) {
+ return ctx.meta.returnValue;
+ } else {
+ ctx.meta.resolve = resolve;
+ ctx.meta.reject = reject;
+ return promise;
+ }
+ };
+ func.hyperfunc = true;
+ func.hypername = nameVal;
+ runtime.assignToNamespace(target, nameSpace, funcName, func);
+ },
+ };
+
+ parser.ensureTerminated(start);
+
+ // terminate error handler if any
+ if (errorHandler) {
+ parser.ensureTerminated(errorHandler);
+ }
+
+ parser.setParent(start, functionFeature);
+ return functionFeature;
+ });
+
+ parser.addFeature("set", function (parser, runtime, tokens) {
+ let setCmd = parser.parseElement("setCommand", tokens);
+ if (setCmd) {
+ if (setCmd.target.scope !== "element") {
+ parser.raiseParseError(tokens, "variables declared at the feature level must be element scoped.");
+ }
+ let setFeature = {
+ start: setCmd,
+ install: function (target, source) {
+ setCmd && setCmd.execute(runtime.makeContext(target, setFeature, target, null));
+ },
+ };
+ parser.ensureTerminated(setCmd);
+ return setFeature;
+ }
+ });
+
+ parser.addFeature("init", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("init")) return;
+
+ var immediately = tokens.matchToken("immediately");
+
+ var start = parser.requireElement("commandList", tokens);
+ var initFeature = {
+ start: start,
+ install: function (target, source) {
+ let handler = function () {
+ start && start.execute(runtime.makeContext(target, initFeature, target, null));
+ };
+ if (immediately) {
+ handler();
+ } else {
+ setTimeout(handler, 0);
+ }
+ },
+ };
+
+ // terminate body
+ parser.ensureTerminated(start);
+ parser.setParent(start, initFeature);
+ return initFeature;
+ });
+
+ parser.addFeature("worker", function (parser, runtime, tokens) {
+ if (tokens.matchToken("worker")) {
+ parser.raiseParseError(
+ tokens,
+ "In order to use the 'worker' feature, include " +
+ "the _hyperscript worker plugin. See " +
+ "https://hyperscript.org/features/worker/ for " +
+ "more info."
+ );
+ return undefined
+ }
+ });
+
+ parser.addFeature("behavior", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("behavior")) return;
+ var path = parser.requireElement("dotOrColonPath", tokens).evaluate();
+ var nameSpace = path.split(".");
+ var name = nameSpace.pop();
+
+ var formalParams = [];
+ if (tokens.matchOpToken("(") && !tokens.matchOpToken(")")) {
+ do {
+ formalParams.push(tokens.requireTokenType("IDENTIFIER").value);
+ } while (tokens.matchOpToken(","));
+ tokens.requireOpToken(")");
+ }
+ var hs = parser.requireElement("hyperscript", tokens);
+ for (var i = 0; i < hs.features.length; i++) {
+ var feature = hs.features[i];
+ feature.behavior = path;
+ }
+
+ return {
+ install: function (target, source) {
+ runtime.assignToNamespace(
+ globalScope.document && globalScope.document.body,
+ nameSpace,
+ name,
+ function (target, source, innerArgs) {
+ var internalData = runtime.getInternalData(target);
+ var elementScope = getOrInitObject(internalData, path + "Scope");
+ for (var i = 0; i < formalParams.length; i++) {
+ elementScope[formalParams[i]] = innerArgs[formalParams[i]];
+ }
+ hs.apply(target, source);
+ }
+ );
+ },
+ };
+ });
+
+ parser.addFeature("install", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("install")) return;
+ var behaviorPath = parser.requireElement("dotOrColonPath", tokens).evaluate();
+ var behaviorNamespace = behaviorPath.split(".");
+ var args = parser.parseElement("namedArgumentList", tokens);
+
+ var installFeature;
+ return (installFeature = {
+ install: function (target, source) {
+ runtime.unifiedEval(
+ {
+ args: [args],
+ op: function (ctx, args) {
+ var behavior = globalScope;
+ for (var i = 0; i < behaviorNamespace.length; i++) {
+ behavior = behavior[behaviorNamespace[i]];
+ if (typeof behavior !== "object" && typeof behavior !== "function")
+ throw new Error("No such behavior defined as " + behaviorPath);
+ }
+
+ if (!(behavior instanceof Function))
+ throw new Error(behaviorPath + " is not a behavior");
+
+ behavior(target, source, args);
+ },
+ },
+ runtime.makeContext(target, installFeature, target, null)
+ );
+ },
+ });
+ });
+
+ parser.addGrammarElement("jsBody", function (parser, runtime, tokens) {
+ var jsSourceStart = tokens.currentToken().start;
+ var jsLastToken = tokens.currentToken();
+
+ var funcNames = [];
+ var funcName = "";
+ var expectFunctionDeclaration = false;
+ while (tokens.hasMore()) {
+ jsLastToken = tokens.consumeToken();
+ var peek = tokens.token(0, true);
+ if (peek.type === "IDENTIFIER" && peek.value === "end") {
+ break;
+ }
+ if (expectFunctionDeclaration) {
+ if (jsLastToken.type === "IDENTIFIER" || jsLastToken.type === "NUMBER") {
+ funcName += jsLastToken.value;
+ } else {
+ if (funcName !== "") funcNames.push(funcName);
+ funcName = "";
+ expectFunctionDeclaration = false;
+ }
+ } else if (jsLastToken.type === "IDENTIFIER" && jsLastToken.value === "function") {
+ expectFunctionDeclaration = true;
+ }
+ }
+ var jsSourceEnd = jsLastToken.end + 1;
+
+ return {
+ type: "jsBody",
+ exposedFunctionNames: funcNames,
+ jsSource: tokens.source.substring(jsSourceStart, jsSourceEnd),
+ };
+ });
+
+ parser.addFeature("js", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("js")) return;
+ var jsBody = parser.requireElement("jsBody", tokens);
+
+ var jsSource =
+ jsBody.jsSource +
+ "\nreturn { " +
+ jsBody.exposedFunctionNames
+ .map(function (name) {
+ return name + ":" + name;
+ })
+ .join(",") +
+ " } ";
+ var func = new Function(jsSource);
+
+ return {
+ jsSource: jsSource,
+ function: func,
+ exposedFunctionNames: jsBody.exposedFunctionNames,
+ install: function () {
+ Object.assign(globalScope, func());
+ },
+ };
+ });
+
+ parser.addCommand("js", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("js")) return;
+ // Parse inputs
+ var inputs = [];
+ if (tokens.matchOpToken("(")) {
+ if (tokens.matchOpToken(")")) {
+ // empty input list
+ } else {
+ do {
+ var inp = tokens.requireTokenType("IDENTIFIER");
+ inputs.push(inp.value);
+ } while (tokens.matchOpToken(","));
+ tokens.requireOpToken(")");
+ }
+ }
+
+ var jsBody = parser.requireElement("jsBody", tokens);
+ tokens.matchToken("end");
+
+ var func = varargConstructor(Function, inputs.concat([jsBody.jsSource]));
+
+ var command = {
+ jsSource: jsBody.jsSource,
+ function: func,
+ inputs: inputs,
+ op: function (context) {
+ var args = [];
+ inputs.forEach(function (input) {
+ args.push(runtime.resolveSymbol(input, context, 'default'));
+ });
+ var result = func.apply(globalScope, args);
+ if (result && typeof result.then === "function") {
+ return new Promise(function (resolve) {
+ result.then(function (actualResult) {
+ context.result = actualResult;
+ resolve(runtime.findNext(this, context));
+ });
+ });
+ } else {
+ context.result = result;
+ return runtime.findNext(this, context);
+ }
+ },
+ };
+ return command;
+ });
+
+ parser.addCommand("async", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("async")) return;
+ if (tokens.matchToken("do")) {
+ var body = parser.requireElement("commandList", tokens);
+
+ // Append halt
+ var end = body;
+ while (end.next) end = end.next;
+ end.next = runtime.HALT;
+
+ tokens.requireToken("end");
+ } else {
+ var body = parser.requireElement("command", tokens);
+ }
+ var command = {
+ body: body,
+ op: function (context) {
+ setTimeout(function () {
+ body.execute(context);
+ });
+ return runtime.findNext(this, context);
+ },
+ };
+ parser.setParent(body, command);
+ return command;
+ });
+
+ parser.addCommand("tell", function (parser, runtime, tokens) {
+ var startToken = tokens.currentToken();
+ if (!tokens.matchToken("tell")) return;
+ var value = parser.requireElement("expression", tokens);
+ var body = parser.requireElement("commandList", tokens);
+ if (tokens.hasMore() && !parser.featureStart(tokens.currentToken())) {
+ tokens.requireToken("end");
+ }
+ var slot = "tell_" + startToken.start;
+ var tellCmd = {
+ value: value,
+ body: body,
+ args: [value],
+ resolveNext: function (context) {
+ var iterator = context.meta.iterators[slot];
+ if (iterator.index < iterator.value.length) {
+ context.you = iterator.value[iterator.index++];
+ return body;
+ } else {
+ // restore original me
+ context.you = iterator.originalYou;
+ if (this.next) {
+ return this.next;
+ } else {
+ return runtime.findNext(this.parent, context);
+ }
+ }
+ },
+ op: function (context, value) {
+ if (value == null) {
+ value = [];
+ } else if (!(Array.isArray(value) || value instanceof NodeList)) {
+ value = [value];
+ }
+ context.meta.iterators[slot] = {
+ originalYou: context.you,
+ index: 0,
+ value: value,
+ };
+ return this.resolveNext(context);
+ },
+ };
+ parser.setParent(body, tellCmd);
+ return tellCmd;
+ });
+
+ parser.addCommand("wait", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("wait")) return;
+ var command;
+
+ // wait on event
+ if (tokens.matchToken("for")) {
+ tokens.matchToken("a"); // optional "a"
+ var events = [];
+ do {
+ var lookahead = tokens.token(0);
+ if (lookahead.type === 'NUMBER' || lookahead.type === 'L_PAREN') {
+ events.push({
+ time: parser.requireElement('expression', tokens).evaluate() // TODO: do we want to allow async here?
+ })
+ } else {
+ events.push({
+ name: parser.requireElement("dotOrColonPath", tokens, "Expected event name").evaluate(),
+ args: parseEventArgs(tokens),
+ });
+ }
+ } while (tokens.matchToken("or"));
+
+ if (tokens.matchToken("from")) {
+ var on = parser.requireElement("expression", tokens);
+ }
+
+ // wait on event
+ command = {
+ event: events,
+ on: on,
+ args: [on],
+ op: function (context, on) {
+ var target = on ? on : context.me;
+ if (!(target instanceof EventTarget))
+ throw new Error("Not a valid event target: " + this.on.sourceFor());
+ return new Promise((resolve) => {
+ var resolved = false;
+ for (const eventInfo of events) {
+ var listener = (event) => {
+ context.result = event;
+ if (eventInfo.args) {
+ for (const arg of eventInfo.args) {
+ context.locals[arg.value] =
+ event[arg.value] || (event.detail ? event.detail[arg.value] : null);
+ }
+ }
+ if (!resolved) {
+ resolved = true;
+ resolve(runtime.findNext(this, context));
+ }
+ };
+ if (eventInfo.name){
+ target.addEventListener(eventInfo.name, listener, {once: true});
+ } else if (eventInfo.time != null) {
+ setTimeout(listener, eventInfo.time, eventInfo.time)
+ }
+ }
+ });
+ },
+ };
+ return command;
+ } else {
+ var time;
+ if (tokens.matchToken("a")) {
+ tokens.requireToken("tick");
+ time = 0;
+ } else {
+ time = parser.requireElement("expression", tokens);
+ }
+
+ command = {
+ type: "waitCmd",
+ time: time,
+ args: [time],
+ op: function (context, timeValue) {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(runtime.findNext(this, context));
+ }, timeValue);
+ });
+ },
+ execute: function (context) {
+ return runtime.unifiedExec(this, context);
+ },
+ };
+ return command;
+ }
+ });
+
+ // TODO - colon path needs to eventually become part of ruby-style symbols
+ parser.addGrammarElement("dotOrColonPath", function (parser, runtime, tokens) {
+ var root = tokens.matchTokenType("IDENTIFIER");
+ if (root) {
+ var path = [root.value];
+
+ var separator = tokens.matchOpToken(".") || tokens.matchOpToken(":");
+ if (separator) {
+ do {
+ path.push(tokens.requireTokenType("IDENTIFIER", "NUMBER").value);
+ } while (tokens.matchOpToken(separator.value));
+ }
+
+ return {
+ type: "dotOrColonPath",
+ path: path,
+ evaluate: function () {
+ return path.join(separator ? separator.value : "");
+ },
+ };
+ }
+ });
+
+
+ parser.addGrammarElement("eventName", function (parser, runtime, tokens) {
+ var token;
+ if ((token = tokens.matchTokenType("STRING"))) {
+ return {
+ evaluate: function() {
+ return token.value;
+ },
+ };
+ }
+
+ return parser.parseElement("dotOrColonPath", tokens);
+ });
+
+ function parseSendCmd(cmdType, parser, runtime, tokens) {
+ var eventName = parser.requireElement("eventName", tokens);
+
+ var details = parser.parseElement("namedArgumentList", tokens);
+ if ((cmdType === "send" && tokens.matchToken("to")) ||
+ (cmdType === "trigger" && tokens.matchToken("on"))) {
+ var toExpr = parser.requireElement("expression", tokens);
+ } else {
+ var toExpr = parser.requireElement("implicitMeTarget", tokens);
+ }
+
+ var sendCmd = {
+ eventName: eventName,
+ details: details,
+ to: toExpr,
+ args: [toExpr, eventName, details],
+ op: function (context, to, eventName, details) {
+ runtime.nullCheck(to, toExpr);
+ runtime.implicitLoop(to, function (target) {
+ runtime.triggerEvent(target, eventName, details, context.me);
+ });
+ return runtime.findNext(sendCmd, context);
+ },
+ };
+ return sendCmd;
+ }
+
+ parser.addCommand("trigger", function (parser, runtime, tokens) {
+ if (tokens.matchToken("trigger")) {
+ return parseSendCmd("trigger", parser, runtime, tokens);
+ }
+ });
+
+ parser.addCommand("send", function (parser, runtime, tokens) {
+ if (tokens.matchToken("send")) {
+ return parseSendCmd("send", parser, runtime, tokens);
+ }
+ });
+
+ var parseReturnFunction = function (parser, runtime, tokens, returnAValue) {
+ if (returnAValue) {
+ if (parser.commandBoundary(tokens.currentToken())) {
+ parser.raiseParseError(tokens, "'return' commands must return a value. If you do not wish to return a value, use 'exit' instead.");
+ } else {
+ var value = parser.requireElement("expression", tokens);
+ }
+ }
+
+ var returnCmd = {
+ value: value,
+ args: [value],
+ op: function (context, value) {
+ var resolve = context.meta.resolve;
+ context.meta.returned = true;
+ context.meta.returnValue = value;
+ if (resolve) {
+ if (value) {
+ resolve(value);
+ } else {
+ resolve();
+ }
+ }
+ return runtime.HALT;
+ },
+ };
+ return returnCmd;
+ };
+
+ parser.addCommand("return", function (parser, runtime, tokens) {
+ if (tokens.matchToken("return")) {
+ return parseReturnFunction(parser, runtime, tokens, true);
+ }
+ });
+
+ parser.addCommand("exit", function (parser, runtime, tokens) {
+ if (tokens.matchToken("exit")) {
+ return parseReturnFunction(parser, runtime, tokens, false);
+ }
+ });
+
+ parser.addCommand("halt", function (parser, runtime, tokens) {
+ if (tokens.matchToken("halt")) {
+ if (tokens.matchToken("the")) {
+ tokens.requireToken("event");
+ // optional possessive
+ if (tokens.matchOpToken("'")) {
+ tokens.requireToken("s");
+ }
+ var keepExecuting = true;
+ }
+ if (tokens.matchToken("bubbling")) {
+ var bubbling = true;
+ } else if (tokens.matchToken("default")) {
+ var haltDefault = true;
+ }
+ var exit = parseReturnFunction(parser, runtime, tokens, false);
+
+ var haltCmd = {
+ keepExecuting: true,
+ bubbling: bubbling,
+ haltDefault: haltDefault,
+ exit: exit,
+ op: function (ctx) {
+ if (ctx.event) {
+ if (bubbling) {
+ ctx.event.stopPropagation();
+ } else if (haltDefault) {
+ ctx.event.preventDefault();
+ } else {
+ ctx.event.stopPropagation();
+ ctx.event.preventDefault();
+ }
+ if (keepExecuting) {
+ return runtime.findNext(this, ctx);
+ } else {
+ return exit;
+ }
+ }
+ },
+ };
+ return haltCmd;
+ }
+ });
+
+ parser.addCommand("log", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("log")) return;
+ var exprs = [parser.parseElement("expression", tokens)];
+ while (tokens.matchOpToken(",")) {
+ exprs.push(parser.requireElement("expression", tokens));
+ }
+ if (tokens.matchToken("with")) {
+ var withExpr = parser.requireElement("expression", tokens);
+ }
+ var logCmd = {
+ exprs: exprs,
+ withExpr: withExpr,
+ args: [withExpr, exprs],
+ op: function (ctx, withExpr, values) {
+ if (withExpr) {
+ withExpr.apply(null, values);
+ } else {
+ console.log.apply(null, values);
+ }
+ return runtime.findNext(this, ctx);
+ },
+ };
+ return logCmd;
+ });
+
+ parser.addCommand("beep!", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("beep!")) return;
+ var exprs = [parser.parseElement("expression", tokens)];
+ while (tokens.matchOpToken(",")) {
+ exprs.push(parser.requireElement("expression", tokens));
+ }
+ var beepCmd = {
+ exprs: exprs,
+ args: [exprs],
+ op: function (ctx, values) {
+ for (let i = 0; i < exprs.length; i++) {
+ const expr = exprs[i];
+ const val = values[i];
+ runtime.beepValueToConsole(ctx.me, expr, val);
+ }
+ return runtime.findNext(this, ctx);
+ },
+ };
+ return beepCmd;
+ });
+
+ parser.addCommand("throw", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("throw")) return;
+ var expr = parser.requireElement("expression", tokens);
+ var throwCmd = {
+ expr: expr,
+ args: [expr],
+ op: function (ctx, expr) {
+ runtime.registerHyperTrace(ctx, expr);
+ throw expr;
+ },
+ };
+ return throwCmd;
+ });
+
+ var parseCallOrGet = function (parser, runtime, tokens) {
+ var expr = parser.requireElement("expression", tokens);
+ var callCmd = {
+ expr: expr,
+ args: [expr],
+ op: function (context, result) {
+ context.result = result;
+ return runtime.findNext(callCmd, context);
+ },
+ };
+ return callCmd;
+ };
+ parser.addCommand("call", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("call")) return;
+ var call = parseCallOrGet(parser, runtime, tokens);
+ if (call.expr && call.expr.type !== "functionCall") {
+ parser.raiseParseError(tokens, "Must be a function invocation");
+ }
+ return call;
+ });
+ parser.addCommand("get", function (parser, runtime, tokens) {
+ if (tokens.matchToken("get")) {
+ return parseCallOrGet(parser, runtime, tokens);
+ }
+ });
+
+ parser.addCommand("make", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("make")) return;
+ tokens.matchToken("a") || tokens.matchToken("an");
+
+ var expr = parser.requireElement("expression", tokens);
+
+ var args = [];
+ if (expr.type !== "queryRef" && tokens.matchToken("from")) {
+ do {
+ args.push(parser.requireElement("expression", tokens));
+ } while (tokens.matchOpToken(","));
+ }
+
+ if (tokens.matchToken("called")) {
+ var target = parser.requireElement("symbol", tokens);
+ }
+
+ var command;
+ if (expr.type === "queryRef") {
+ command = {
+ op: function (ctx) {
+ var match,
+ tagname = "div",
+ id,
+ classes = [];
+ var re = /(?:(^|#|\.)([^#\. ]+))/g;
+ while ((match = re.exec(expr.css))) {
+ if (match[1] === "") tagname = match[2].trim();
+ else if (match[1] === "#") id = match[2].trim();
+ else classes.push(match[2].trim());
+ }
+
+ var result = document.createElement(tagname);
+ if (id !== undefined) result.id = id;
+ for (var i = 0; i < classes.length; i++) {
+ var cls = classes[i];
+ result.classList.add(cls)
+ }
+
+ ctx.result = result;
+ if (target){
+ runtime.setSymbol(target.name, ctx, target.scope, result);
+ }
+
+ return runtime.findNext(this, ctx);
+ },
+ };
+ return command;
+ } else {
+ command = {
+ args: [expr, args],
+ op: function (ctx, expr, args) {
+ ctx.result = varargConstructor(expr, args);
+ if (target){
+ runtime.setSymbol(target.name, ctx, target.scope, ctx.result);
+ }
+
+ return runtime.findNext(this, ctx);
+ },
+ };
+ return command;
+ }
+ });
+
+ parser.addGrammarElement("pseudoCommand", function (parser, runtime, tokens) {
+
+ let lookAhead = tokens.token(1);
+ if (!(lookAhead && lookAhead.op && (lookAhead.value === '.' || lookAhead.value === "("))) {
+ return null;
+ }
+
+ var expr = parser.requireElement("primaryExpression", tokens);
+
+ var rootRoot = expr.root;
+ var root = expr;
+ while (rootRoot.root != null) {
+ root = root.root;
+ rootRoot = rootRoot.root;
+ }
+
+ if (expr.type !== "functionCall") {
+ parser.raiseParseError(tokens, "Pseudo-commands must be function calls");
+ }
+
+ if (root.type === "functionCall" && root.root.root == null) {
+ if (tokens.matchAnyToken("the", "to", "on", "with", "into", "from", "at")) {
+ var realRoot = parser.requireElement("expression", tokens);
+ } else if (tokens.matchToken("me")) {
+ var realRoot = parser.requireElement("implicitMeTarget", tokens);
+ }
+ }
+
+ /** @type {ASTNode} */
+
+ var pseudoCommand
+ if(realRoot){
+ pseudoCommand = {
+ type: "pseudoCommand",
+ root: realRoot,
+ argExressions: root.argExressions,
+ args: [realRoot, root.argExressions],
+ op: function (context, rootRoot, args) {
+ runtime.nullCheck(rootRoot, realRoot);
+ var func = rootRoot[root.root.name];
+ runtime.nullCheck(func, root);
+ if (func.hyperfunc) {
+ args.push(context);
+ }
+ context.result = func.apply(rootRoot, args);
+ return runtime.findNext(pseudoCommand, context);
+ },
+ execute: function (context) {
+ return runtime.unifiedExec(this, context);
+ },
+ }
+ } else {
+ pseudoCommand = {
+ type: "pseudoCommand",
+ expr: expr,
+ args: [expr],
+ op: function (context, result) {
+ context.result = result;
+ return runtime.findNext(pseudoCommand, context);
+ },
+ execute: function (context) {
+ return runtime.unifiedExec(this, context);
+ },
+ };
+ }
+
+ return pseudoCommand;
+ });
+
+ /**
+ * @param {Parser} parser
+ * @param {Runtime} runtime
+ * @param {Tokens} tokens
+ * @param {*} target
+ * @param {*} value
+ * @returns
+ */
+ var makeSetter = function (parser, runtime, tokens, target, value) {
+
+ var symbolWrite = target.type === "symbol";
+ var attributeWrite = target.type === "attributeRef";
+ var styleWrite = target.type === "styleRef";
+ var arrayWrite = target.type === "arrayIndex";
+
+ if (!(attributeWrite || styleWrite || symbolWrite) && target.root == null) {
+ parser.raiseParseError(tokens, "Can only put directly into symbols, not references");
+ }
+
+ var rootElt = null;
+ var prop = null;
+ if (symbolWrite) {
+ // rootElt is null
+ } else if (attributeWrite || styleWrite) {
+ rootElt = parser.requireElement("implicitMeTarget", tokens);
+ var attribute = target;
+ } else if(arrayWrite) {
+ prop = target.firstIndex;
+ rootElt = target.root;
+ } else {
+ prop = target.prop ? target.prop.value : null;
+ var attribute = target.attribute;
+ rootElt = target.root;
+ }
+
+ /** @type {ASTNode} */
+ var setCmd = {
+ target: target,
+ symbolWrite: symbolWrite,
+ value: value,
+ args: [rootElt, prop, value],
+ op: function (context, root, prop, valueToSet) {
+ if (symbolWrite) {
+ runtime.setSymbol(target.name, context, target.scope, valueToSet);
+ } else {
+ runtime.nullCheck(root, rootElt);
+ if (arrayWrite) {
+ root[prop] = valueToSet;
+ } else {
+ runtime.implicitLoop(root, function (elt) {
+ if (attribute) {
+ if (attribute.type === "attributeRef") {
+ if (valueToSet == null) {
+ elt.removeAttribute(attribute.name);
+ } else {
+ elt.setAttribute(attribute.name, valueToSet);
+ }
+ } else {
+ elt.style[attribute.name] = valueToSet;
+ }
+ } else {
+ elt[prop] = valueToSet;
+ }
+ });
+ }
+ }
+ return runtime.findNext(this, context);
+ },
+ };
+ return setCmd;
+ };
+
+ parser.addCommand("default", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("default")) return;
+ var target = parser.requireElement("assignableExpression", tokens);
+ tokens.requireToken("to");
+
+ var value = parser.requireElement("expression", tokens);
+
+ /** @type {ASTNode} */
+ var setter = makeSetter(parser, runtime, tokens, target, value);
+ var defaultCmd = {
+ target: target,
+ value: value,
+ setter: setter,
+ args: [target],
+ op: function (context, target) {
+ if (target) {
+ return runtime.findNext(this, context);
+ } else {
+ return setter;
+ }
+ },
+ };
+ setter.parent = defaultCmd;
+ return defaultCmd;
+ });
+
+ parser.addCommand("set", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("set")) return;
+ if (tokens.currentToken().type === "L_BRACE") {
+ var obj = parser.requireElement("objectLiteral", tokens);
+ tokens.requireToken("on");
+ var target = parser.requireElement("expression", tokens);
+
+ var command = {
+ objectLiteral: obj,
+ target: target,
+ args: [obj, target],
+ op: function (ctx, obj, target) {
+ Object.assign(target, obj);
+ return runtime.findNext(this, ctx);
+ },
+ };
+ return command;
+ }
+
+ try {
+ tokens.pushFollow("to");
+ var target = parser.requireElement("assignableExpression", tokens);
+ } finally {
+ tokens.popFollow();
+ }
+ tokens.requireToken("to");
+ var value = parser.requireElement("expression", tokens);
+ return makeSetter(parser, runtime, tokens, target, value);
+ });
+
+ parser.addCommand("if", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("if")) return;
+ var expr = parser.requireElement("expression", tokens);
+ tokens.matchToken("then"); // optional 'then'
+ var trueBranch = parser.parseElement("commandList", tokens);
+ var nestedIfStmt = false;
+ if (tokens.matchToken("else") || tokens.matchToken("otherwise")) {
+ nestedIfStmt = tokens.peekToken("if");
+ var falseBranch = parser.parseElement("commandList", tokens);
+ }
+ if (tokens.hasMore() && !nestedIfStmt) {
+ tokens.requireToken("end");
+ }
+
+ /** @type {ASTNode} */
+ var ifCmd = {
+ expr: expr,
+ trueBranch: trueBranch,
+ falseBranch: falseBranch,
+ args: [expr],
+ op: function (context, exprValue) {
+ if (exprValue) {
+ return trueBranch;
+ } else if (falseBranch) {
+ return falseBranch;
+ } else {
+ return runtime.findNext(this, context);
+ }
+ },
+ };
+ parser.setParent(trueBranch, ifCmd);
+ parser.setParent(falseBranch, ifCmd);
+ return ifCmd;
+ });
+
+ var parseRepeatExpression = function (parser, tokens, runtime, startedWithForToken) {
+ var innerStartToken = tokens.currentToken();
+ var identifier;
+ if (tokens.matchToken("for") || startedWithForToken) {
+ var identifierToken = tokens.requireTokenType("IDENTIFIER");
+ identifier = identifierToken.value;
+ tokens.requireToken("in");
+ var expression = parser.requireElement("expression", tokens);
+ } else if (tokens.matchToken("in")) {
+ identifier = "it";
+ var expression = parser.requireElement("expression", tokens);
+ } else if (tokens.matchToken("while")) {
+ var whileExpr = parser.requireElement("expression", tokens);
+ } else if (tokens.matchToken("until")) {
+ var isUntil = true;
+ if (tokens.matchToken("event")) {
+ var evt = parser.requireElement("dotOrColonPath", tokens, "Expected event name");
+ if (tokens.matchToken("from")) {
+ var on = parser.requireElement("expression", tokens);
+ }
+ } else {
+ var whileExpr = parser.requireElement("expression", tokens);
+ }
+ } else {
+ if (!parser.commandBoundary(tokens.currentToken()) &&
+ tokens.currentToken().value !== 'forever') {
+ var times = parser.requireElement("expression", tokens);
+ tokens.requireToken("times");
+ } else {
+ tokens.matchToken("forever"); // consume optional forever
+ var forever = true;
+ }
+ }
+
+ if (tokens.matchToken("index")) {
+ var identifierToken = tokens.requireTokenType("IDENTIFIER");
+ var indexIdentifier = identifierToken.value;
+ }
+
+ var loop = parser.parseElement("commandList", tokens);
+ if (loop && evt) {
+ // if this is an event based loop, wait a tick at the end of the loop so that
+ // events have a chance to trigger in the loop condition o_O)))
+ var last = loop;
+ while (last.next) {
+ last = last.next;
+ }
+ var waitATick = {
+ type: "waitATick",
+ op: function () {
+ return new Promise(function (resolve) {
+ setTimeout(function () {
+ resolve(runtime.findNext(waitATick));
+ }, 0);
+ });
+ },
+ };
+ last.next = waitATick;
+ }
+ if (tokens.hasMore()) {
+ tokens.requireToken("end");
+ }
+
+ if (identifier == null) {
+ identifier = "_implicit_repeat_" + innerStartToken.start;
+ var slot = identifier;
+ } else {
+ var slot = identifier + "_" + innerStartToken.start;
+ }
+
+ var repeatCmd = {
+ identifier: identifier,
+ indexIdentifier: indexIdentifier,
+ slot: slot,
+ expression: expression,
+ forever: forever,
+ times: times,
+ until: isUntil,
+ event: evt,
+ on: on,
+ whileExpr: whileExpr,
+ resolveNext: function () {
+ return this;
+ },
+ loop: loop,
+ args: [whileExpr, times],
+ op: function (context, whileValue, times) {
+ var iteratorInfo = context.meta.iterators[slot];
+ var keepLooping = false;
+ var loopVal = null;
+ if (this.forever) {
+ keepLooping = true;
+ } else if (this.until) {
+ if (evt) {
+ keepLooping = context.meta.iterators[slot].eventFired === false;
+ } else {
+ keepLooping = whileValue !== true;
+ }
+ } else if (whileExpr) {
+ keepLooping = whileValue;
+ } else if (times) {
+ keepLooping = iteratorInfo.index < times;
+ } else {
+ var nextValFromIterator = iteratorInfo.iterator.next();
+ keepLooping = !nextValFromIterator.done;
+ loopVal = nextValFromIterator.value;
+ }
+
+ if (keepLooping) {
+ if (iteratorInfo.value) {
+ context.result = context.locals[identifier] = loopVal;
+ } else {
+ context.result = iteratorInfo.index;
+ }
+ if (indexIdentifier) {
+ context.locals[indexIdentifier] = iteratorInfo.index;
+ }
+ iteratorInfo.index++;
+ return loop;
+ } else {
+ context.meta.iterators[slot] = null;
+ return runtime.findNext(this.parent, context);
+ }
+ },
+ };
+ parser.setParent(loop, repeatCmd);
+ var repeatInit = {
+ name: "repeatInit",
+ args: [expression, evt, on],
+ op: function (context, value, event, on) {
+ var iteratorInfo = {
+ index: 0,
+ value: value,
+ eventFired: false,
+ };
+ context.meta.iterators[slot] = iteratorInfo;
+ if (value && value[Symbol.iterator]) {
+ iteratorInfo.iterator = value[Symbol.iterator]();
+ }
+ if (evt) {
+ var target = on || context.me;
+ target.addEventListener(
+ event,
+ function (e) {
+ context.meta.iterators[slot].eventFired = true;
+ },
+ { once: true }
+ );
+ }
+ return repeatCmd; // continue to loop
+ },
+ execute: function (context) {
+ return runtime.unifiedExec(this, context);
+ },
+ };
+ parser.setParent(repeatCmd, repeatInit);
+ return repeatInit;
+ };
+
+ parser.addCommand("repeat", function (parser, runtime, tokens) {
+ if (tokens.matchToken("repeat")) {
+ return parseRepeatExpression(parser, tokens, runtime, false);
+ }
+ });
+
+ parser.addCommand("for", function (parser, runtime, tokens) {
+ if (tokens.matchToken("for")) {
+ return parseRepeatExpression(parser, tokens, runtime, true);
+ }
+ });
+
+ parser.addCommand("continue", function (parser, runtime, tokens) {
+
+ if (!tokens.matchToken("continue")) return;
+
+ var command = {
+ op: function (context) {
+
+ // scan for the closest repeat statement
+ for (var parent = this.parent ; true ; parent = parent.parent) {
+
+ if (parent == undefined) {
+ parser.raiseParseError(tokens, "Command `continue` cannot be used outside of a `repeat` loop.")
+ }
+ if (parent.loop != undefined) {
+ return parent.resolveNext(context)
+ }
+ }
+ }
+ };
+ return command;
+ });
+
+ parser.addCommand("break", function (parser, runtime, tokens) {
+
+ if (!tokens.matchToken("break")) return;
+
+ var command = {
+ op: function (context) {
+
+ // scan for the closest repeat statement
+ for (var parent = this.parent ; true ; parent = parent.parent) {
+
+ if (parent == undefined) {
+ parser.raiseParseError(tokens, "Command `continue` cannot be used outside of a `repeat` loop.")
+ }
+ if (parent.loop != undefined) {
+ return runtime.findNext(parent.parent, context);
+ }
+ }
+ }
+ };
+ return command;
+ });
+
+ parser.addGrammarElement("stringLike", function (parser, runtime, tokens) {
+ return parser.parseAnyOf(["string", "nakedString"], tokens);
+ });
+
+ parser.addCommand("append", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("append")) return;
+ var targetExpr = null;
+
+ var value = parser.requireElement("expression", tokens);
+
+ /** @type {ASTNode} */
+ var implicitResultSymbol = {
+ type: "symbol",
+ evaluate: function (context) {
+ return runtime.resolveSymbol("result", context);
+ },
+ };
+
+ if (tokens.matchToken("to")) {
+ targetExpr = parser.requireElement("expression", tokens);
+ } else {
+ targetExpr = implicitResultSymbol;
+ }
+
+ var setter = null;
+ if (targetExpr.type === "symbol" || targetExpr.type === "attributeRef" || targetExpr.root != null) {
+ setter = makeSetter(parser, runtime, tokens, targetExpr, implicitResultSymbol);
+ }
+
+ var command = {
+ value: value,
+ target: targetExpr,
+ args: [targetExpr, value],
+ op: function (context, target, value) {
+ if (Array.isArray(target)) {
+ target.push(value);
+ return runtime.findNext(this, context);
+ } else if (target instanceof Element) {
+ target.innerHTML += value;
+ return runtime.findNext(this, context);
+ } else if(setter) {
+ context.result = (target || "") + value;
+ return setter;
+ } else {
+ throw Error("Unable to append a value!")
+ }
+ },
+ execute: function (context) {
+ return runtime.unifiedExec(this, context/*, value, target*/);
+ },
+ };
+
+ if (setter != null) {
+ setter.parent = command;
+ }
+
+ return command;
+ });
+
+ function parsePickRange(parser, runtime, tokens) {
+ tokens.matchToken("at") || tokens.matchToken("from");
+ const rv = { includeStart: true, includeEnd: false }
+
+ rv.from = tokens.matchToken("start") ? 0 : parser.requireElement("expression", tokens)
+
+ if (tokens.matchToken("to") || tokens.matchOpToken("..")) {
+ if (tokens.matchToken("end")) {
+ rv.toEnd = true;
+ } else {
+ rv.to = parser.requireElement("expression", tokens);
+ }
+ }
+
+ if (tokens.matchToken("inclusive")) rv.includeEnd = true;
+ else if (tokens.matchToken("exclusive")) rv.includeStart = false;
+
+ return rv;
+ }
+
+ class RegExpIterator {
+ constructor(re, str) {
+ this.re = re;
+ this.str = str;
+ }
+
+ next() {
+ const match = this.re.exec(this.str);
+ if (match === null) return { done: true };
+ else return { value: match };
+ }
+ }
+
+ class RegExpIterable {
+ constructor(re, flags, str) {
+ this.re = re;
+ this.flags = flags;
+ this.str = str;
+ }
+
+ [Symbol.iterator]() {
+ return new RegExpIterator(new RegExp(this.re, this.flags), this.str);
+ }
+ }
+
+ parser.addCommand("pick", (parser, runtime, tokens) => {
+ if (!tokens.matchToken("pick")) return;
+
+ tokens.matchToken("the");
+
+ if (tokens.matchToken("item") || tokens.matchToken("items")
+ || tokens.matchToken("character") || tokens.matchToken("characters")) {
+ const range = parsePickRange(parser, runtime, tokens);
+
+ tokens.requireToken("from");
+ const root = parser.requireElement("expression", tokens);
+
+ return {
+ args: [root, range.from, range.to],
+ op(ctx, root, from, to) {
+ if (range.toEnd) to = root.length;
+ if (!range.includeStart) from++;
+ if (range.includeEnd) to++;
+ if (to == null || to == undefined) to = from + 1;
+ ctx.result = root.slice(from, to);
+ return runtime.findNext(this, ctx);
+ }
+ }
+ }
+
+ if (tokens.matchToken("match")) {
+ tokens.matchToken("of");
+ const re = parser.parseElement("expression", tokens);
+ let flags = ""
+ if (tokens.matchOpToken("|")) {
+ flags = tokens.requireToken("identifier").value;
+ }
+
+ tokens.requireToken("from");
+ const root = parser.parseElement("expression", tokens);
+
+ return {
+ args: [root, re],
+ op(ctx, root, re) {
+ ctx.result = new RegExp(re, flags).exec(root);
+ return runtime.findNext(this, ctx);
+ }
+ }
+ }
+
+ if (tokens.matchToken("matches")) {
+ tokens.matchToken("of");
+ const re = parser.parseElement("expression", tokens);
+ let flags = "gu"
+ if (tokens.matchOpToken("|")) {
+ flags = 'g' + tokens.requireToken("identifier").value.replace('g', '');
+ }
+ console.log('flags', flags)
+
+ tokens.requireToken("from");
+ const root = parser.parseElement("expression", tokens);
+
+ return {
+ args: [root, re],
+ op(ctx, root, re) {
+ ctx.result = new RegExpIterable(re, flags, root);
+ return runtime.findNext(this, ctx);
+ }
+ }
+ }
+ });
+
+ parser.addCommand("increment", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("increment")) return;
+ var amountExpr;
+
+ // This is optional. Defaults to "result"
+ var target = parser.parseElement("assignableExpression", tokens);
+
+ // This is optional. Defaults to 1.
+ if (tokens.matchToken("by")) {
+ amountExpr = parser.requireElement("expression", tokens);
+ }
+
+ var implicitIncrementOp = {
+ type: "implicitIncrementOp",
+ target: target,
+ args: [target, amountExpr],
+ op: function (context, targetValue, amount) {
+ targetValue = targetValue ? parseFloat(targetValue) : 0;
+ amount = amountExpr ? parseFloat(amount) : 1;
+ var newValue = targetValue + amount;
+ context.result = newValue;
+ return newValue;
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ }
+ };
+
+ return makeSetter(parser, runtime, tokens, target, implicitIncrementOp);
+ });
+
+ parser.addCommand("decrement", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("decrement")) return;
+ var amountExpr;
+
+ // This is optional. Defaults to "result"
+ var target = parser.parseElement("assignableExpression", tokens);
+
+ // This is optional. Defaults to 1.
+ if (tokens.matchToken("by")) {
+ amountExpr = parser.requireElement("expression", tokens);
+ }
+
+ var implicitDecrementOp = {
+ type: "implicitDecrementOp",
+ target: target,
+ args: [target, amountExpr],
+ op: function (context, targetValue, amount) {
+ targetValue = targetValue ? parseFloat(targetValue) : 0;
+ amount = amountExpr ? parseFloat(amount) : 1;
+ var newValue = targetValue - amount;
+ context.result = newValue;
+ return newValue;
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ }
+ };
+
+ return makeSetter(parser, runtime, tokens, target, implicitDecrementOp);
+ });
+
+ function parseConversionInfo(tokens, parser) {
+ var type = "text";
+ var conversion;
+ tokens.matchToken("a") || tokens.matchToken("an");
+ if (tokens.matchToken("json") || tokens.matchToken("Object")) {
+ type = "json";
+ } else if (tokens.matchToken("response")) {
+ type = "response";
+ } else if (tokens.matchToken("html")) {
+ type = "html";
+ } else if (tokens.matchToken("text")) {
+ // default, ignore
+ } else {
+ conversion = parser.requireElement("dotOrColonPath", tokens).evaluate();
+ }
+ return {type, conversion};
+ }
+
+ parser.addCommand("fetch", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("fetch")) return;
+ var url = parser.requireElement("stringLike", tokens);
+
+ if (tokens.matchToken("as")) {
+ var conversionInfo = parseConversionInfo(tokens, parser);
+ }
+
+ if (tokens.matchToken("with") && tokens.currentToken().value !== "{") {
+ var args = parser.parseElement("nakedNamedArgumentList", tokens);
+ } else {
+ var args = parser.parseElement("objectLiteral", tokens);
+ }
+
+ if (conversionInfo == null && tokens.matchToken("as")) {
+ conversionInfo = parseConversionInfo(tokens, parser);
+ }
+
+ var type = conversionInfo ? conversionInfo.type : "text";
+ var conversion = conversionInfo ? conversionInfo.conversion : null
+
+ /** @type {ASTNode} */
+ var fetchCmd = {
+ url: url,
+ argExpressions: args,
+ args: [url, args],
+ op: function (context, url, args) {
+ var detail = args || {};
+ detail["sender"] = context.me;
+ detail["headers"] = detail["headers"] || {}
+ var abortController = new AbortController();
+ let abortListener = context.me.addEventListener('fetch:abort', function(){
+ abortController.abort();
+ }, {once: true});
+ detail['signal'] = abortController.signal;
+ runtime.triggerEvent(context.me, "hyperscript:beforeFetch", detail);
+ runtime.triggerEvent(context.me, "fetch:beforeRequest", detail);
+ args = detail;
+ var finished = false;
+ if (args.timeout) {
+ setTimeout(function () {
+ if (!finished) {
+ abortController.abort();
+ }
+ }, args.timeout);
+ }
+ return fetch(url, args)
+ .then(function (resp) {
+ let resultDetails = {response:resp};
+ runtime.triggerEvent(context.me, "fetch:afterResponse", resultDetails);
+ resp = resultDetails.response;
+
+ if (type === "response") {
+ context.result = resp;
+ runtime.triggerEvent(context.me, "fetch:afterRequest", {result:resp});
+ finished = true;
+ return runtime.findNext(fetchCmd, context);
+ }
+ if (type === "json") {
+ return resp.json().then(function (result) {
+ context.result = result;
+ runtime.triggerEvent(context.me, "fetch:afterRequest", {result});
+ finished = true;
+ return runtime.findNext(fetchCmd, context);
+ });
+ }
+ return resp.text().then(function (result) {
+ if (conversion) result = runtime.convertValue(result, conversion);
+
+ if (type === "html") result = runtime.convertValue(result, "Fragment");
+
+ context.result = result;
+ runtime.triggerEvent(context.me, "fetch:afterRequest", {result});
+ finished = true;
+ return runtime.findNext(fetchCmd, context);
+ });
+ })
+ .catch(function (reason) {
+ runtime.triggerEvent(context.me, "fetch:error", {
+ reason: reason,
+ });
+ throw reason;
+ }).finally(function(){
+ context.me.removeEventListener('fetch:abort', abortListener);
+ });
+ },
+ };
+ return fetchCmd;
+ });
+ }
+
+ function hyperscriptWebGrammar(parser) {
+ parser.addCommand("settle", function (parser, runtime, tokens) {
+ if (tokens.matchToken("settle")) {
+ if (!parser.commandBoundary(tokens.currentToken())) {
+ var onExpr = parser.requireElement("expression", tokens);
+ } else {
+ var onExpr = parser.requireElement("implicitMeTarget", tokens);
+ }
+
+ var settleCommand = {
+ type: "settleCmd",
+ args: [onExpr],
+ op: function (context, on) {
+ runtime.nullCheck(on, onExpr);
+ var resolve = null;
+ var resolved = false;
+ var transitionStarted = false;
+
+ var promise = new Promise(function (r) {
+ resolve = r;
+ });
+
+ // listen for a transition begin
+ on.addEventListener(
+ "transitionstart",
+ function () {
+ transitionStarted = true;
+ },
+ { once: true }
+ );
+
+ // if no transition begins in 500ms, cancel
+ setTimeout(function () {
+ if (!transitionStarted && !resolved) {
+ resolve(runtime.findNext(settleCommand, context));
+ }
+ }, 500);
+
+ // continue on a transition emd
+ on.addEventListener(
+ "transitionend",
+ function () {
+ if (!resolved) {
+ resolve(runtime.findNext(settleCommand, context));
+ }
+ },
+ { once: true }
+ );
+ return promise;
+ },
+ execute: function (context) {
+ return runtime.unifiedExec(this, context);
+ },
+ };
+ return settleCommand;
+ }
+ });
+
+ parser.addCommand("add", function (parser, runtime, tokens) {
+ if (tokens.matchToken("add")) {
+ var classRef = parser.parseElement("classRef", tokens);
+ var attributeRef = null;
+ var cssDeclaration = null;
+ if (classRef == null) {
+ attributeRef = parser.parseElement("attributeRef", tokens);
+ if (attributeRef == null) {
+ cssDeclaration = parser.parseElement("styleLiteral", tokens);
+ if (cssDeclaration == null) {
+ parser.raiseParseError(tokens, "Expected either a class reference or attribute expression");
+ }
+ }
+ } else {
+ var classRefs = [classRef];
+ while ((classRef = parser.parseElement("classRef", tokens))) {
+ classRefs.push(classRef);
+ }
+ }
+
+ if (tokens.matchToken("to")) {
+ var toExpr = parser.requireElement("expression", tokens);
+ } else {
+ var toExpr = parser.requireElement("implicitMeTarget", tokens);
+ }
+
+ if (tokens.matchToken("when")) {
+ if (cssDeclaration) {
+ parser.raiseParseError(tokens, "Only class and properties are supported with a when clause")
+ }
+ var when = parser.requireElement("expression", tokens);
+ }
+
+ if (classRefs) {
+ return {
+ classRefs: classRefs,
+ to: toExpr,
+ args: [toExpr, classRefs],
+ op: function (context, to, classRefs) {
+ runtime.nullCheck(to, toExpr);
+ runtime.forEach(classRefs, function (classRef) {
+ runtime.implicitLoop(to, function (target) {
+ if (when) {
+ context.result = target;
+ let whenResult = runtime.evaluateNoPromise(when, context);
+ if (whenResult) {
+ if (target instanceof Element) target.classList.add(classRef.className);
+ } else {
+ if (target instanceof Element) target.classList.remove(classRef.className);
+ }
+ context.result = null;
+ } else {
+ if (target instanceof Element) target.classList.add(classRef.className);
+ }
+ });
+ });
+ return runtime.findNext(this, context);
+ },
+ };
+ } else if (attributeRef) {
+ return {
+ type: "addCmd",
+ attributeRef: attributeRef,
+ to: toExpr,
+ args: [toExpr],
+ op: function (context, to, attrRef) {
+ runtime.nullCheck(to, toExpr);
+ runtime.implicitLoop(to, function (target) {
+ if (when) {
+ context.result = target;
+ let whenResult = runtime.evaluateNoPromise(when, context);
+ if (whenResult) {
+ target.setAttribute(attributeRef.name, attributeRef.value);
+ } else {
+ target.removeAttribute(attributeRef.name);
+ }
+ context.result = null;
+ } else {
+ target.setAttribute(attributeRef.name, attributeRef.value);
+ }
+ });
+ return runtime.findNext(this, context);
+ },
+ execute: function (ctx) {
+ return runtime.unifiedExec(this, ctx);
+ },
+ };
+ } else {
+ return {
+ type: "addCmd",
+ cssDeclaration: cssDeclaration,
+ to: toExpr,
+ args: [toExpr, cssDeclaration],
+ op: function (context, to, css) {
+ runtime.nullCheck(to, toExpr);
+ runtime.implicitLoop(to, function (target) {
+ target.style.cssText += css;
+ });
+ return runtime.findNext(this, context);
+ },
+ execute: function (ctx) {
+ return runtime.unifiedExec(this, ctx);
+ },
+ };
+ }
+ }
+ });
+
+ parser.addGrammarElement("styleLiteral", function (parser, runtime, tokens) {
+ if (!tokens.matchOpToken("{")) return;
+
+ var stringParts = [""]
+ var exprs = []
+
+ while (tokens.hasMore()) {
+ if (tokens.matchOpToken("\\")) {
+ tokens.consumeToken();
+ } else if (tokens.matchOpToken("}")) {
+ break;
+ } else if (tokens.matchToken("$")) {
+ var opencurly = tokens.matchOpToken("{");
+ var expr = parser.parseElement("expression", tokens);
+ if (opencurly) tokens.requireOpToken("}");
+
+ exprs.push(expr)
+ stringParts.push("")
+ } else {
+ var tok = tokens.consumeToken();
+ stringParts[stringParts.length-1] += tokens.source.substring(tok.start, tok.end);
+ }
+
+ stringParts[stringParts.length-1] += tokens.lastWhitespace();
+ }
+
+ return {
+ type: "styleLiteral",
+ args: [exprs],
+ op: function (ctx, exprs) {
+ var rv = "";
+
+ stringParts.forEach(function (part, idx) {
+ rv += part;
+ if (idx in exprs) rv += exprs[idx];
+ });
+
+ return rv;
+ },
+ evaluate: function(ctx) {
+ return runtime.unifiedEval(this, ctx);
+ }
+ }
+ })
+
+ parser.addCommand("remove", function (parser, runtime, tokens) {
+ if (tokens.matchToken("remove")) {
+ var classRef = parser.parseElement("classRef", tokens);
+ var attributeRef = null;
+ var elementExpr = null;
+ if (classRef == null) {
+ attributeRef = parser.parseElement("attributeRef", tokens);
+ if (attributeRef == null) {
+ elementExpr = parser.parseElement("expression", tokens);
+ if (elementExpr == null) {
+ parser.raiseParseError(
+ tokens,
+ "Expected either a class reference, attribute expression or value expression"
+ );
+ }
+ }
+ } else {
+ var classRefs = [classRef];
+ while ((classRef = parser.parseElement("classRef", tokens))) {
+ classRefs.push(classRef);
+ }
+ }
+
+ if (tokens.matchToken("from")) {
+ var fromExpr = parser.requireElement("expression", tokens);
+ } else {
+ if (elementExpr == null) {
+ var fromExpr = parser.requireElement("implicitMeTarget", tokens);
+ }
+ }
+
+ if (elementExpr) {
+ return {
+ elementExpr: elementExpr,
+ from: fromExpr,
+ args: [elementExpr, fromExpr],
+ op: function (context, element, from) {
+ runtime.nullCheck(element, elementExpr);
+ runtime.implicitLoop(element, function (target) {
+ if (target.parentElement && (from == null || from.contains(target))) {
+ target.parentElement.removeChild(target);
+ }
+ });
+ return runtime.findNext(this, context);
+ },
+ };
+ } else {
+ return {
+ classRefs: classRefs,
+ attributeRef: attributeRef,
+ elementExpr: elementExpr,
+ from: fromExpr,
+ args: [classRefs, fromExpr],
+ op: function (context, classRefs, from) {
+ runtime.nullCheck(from, fromExpr);
+ if (classRefs) {
+ runtime.forEach(classRefs, function (classRef) {
+ runtime.implicitLoop(from, function (target) {
+ target.classList.remove(classRef.className);
+ });
+ });
+ } else {
+ runtime.implicitLoop(from, function (target) {
+ target.removeAttribute(attributeRef.name);
+ });
+ }
+ return runtime.findNext(this, context);
+ },
+ };
+ }
+ }
+ });
+
+ parser.addCommand("toggle", function (parser, runtime, tokens) {
+ if (tokens.matchToken("toggle")) {
+ tokens.matchAnyToken("the", "my");
+ if (tokens.currentToken().type === "STYLE_REF") {
+ let styleRef = tokens.consumeToken();
+ var name = styleRef.value.substr(1);
+ var visibility = true;
+ var hideShowStrategy = resolveHideShowStrategy(parser, tokens, name);
+ if (tokens.matchToken("of")) {
+ tokens.pushFollow("with");
+ try {
+ var onExpr = parser.requireElement("expression", tokens);
+ } finally {
+ tokens.popFollow();
+ }
+ } else {
+ var onExpr = parser.requireElement("implicitMeTarget", tokens);
+ }
+ } else if (tokens.matchToken("between")) {
+ var between = true;
+ var classRef = parser.parseElement("classRef", tokens);
+ tokens.requireToken("and");
+ var classRef2 = parser.requireElement("classRef", tokens);
+ } else {
+ var classRef = parser.parseElement("classRef", tokens);
+ var attributeRef = null;
+ if (classRef == null) {
+ attributeRef = parser.parseElement("attributeRef", tokens);
+ if (attributeRef == null) {
+ parser.raiseParseError(tokens, "Expected either a class reference or attribute expression");
+ }
+ } else {
+ var classRefs = [classRef];
+ while ((classRef = parser.parseElement("classRef", tokens))) {
+ classRefs.push(classRef);
+ }
+ }
+ }
+
+ if (visibility !== true) {
+ if (tokens.matchToken("on")) {
+ var onExpr = parser.requireElement("expression", tokens);
+ } else {
+ var onExpr = parser.requireElement("implicitMeTarget", tokens);
+ }
+ }
+
+ if (tokens.matchToken("for")) {
+ var time = parser.requireElement("expression", tokens);
+ } else if (tokens.matchToken("until")) {
+ var evt = parser.requireElement("dotOrColonPath", tokens, "Expected event name");
+ if (tokens.matchToken("from")) {
+ var from = parser.requireElement("expression", tokens);
+ }
+ }
+
+ var toggleCmd = {
+ classRef: classRef,
+ classRef2: classRef2,
+ classRefs: classRefs,
+ attributeRef: attributeRef,
+ on: onExpr,
+ time: time,
+ evt: evt,
+ from: from,
+ toggle: function (on, classRef, classRef2, classRefs) {
+ runtime.nullCheck(on, onExpr);
+ if (visibility) {
+ runtime.implicitLoop(on, function (target) {
+ hideShowStrategy("toggle", target);
+ });
+ } else if (between) {
+ runtime.implicitLoop(on, function (target) {
+ if (target.classList.contains(classRef.className)) {
+ target.classList.remove(classRef.className);
+ target.classList.add(classRef2.className);
+ } else {
+ target.classList.add(classRef.className);
+ target.classList.remove(classRef2.className);
+ }
+ });
+ } else if (classRefs) {
+ runtime.forEach(classRefs, function (classRef) {
+ runtime.implicitLoop(on, function (target) {
+ target.classList.toggle(classRef.className);
+ });
+ });
+ } else {
+ runtime.forEach(on, function (target) {
+ if (target.hasAttribute(attributeRef.name)) {
+ target.removeAttribute(attributeRef.name);
+ } else {
+ target.setAttribute(attributeRef.name, attributeRef.value);
+ }
+ });
+ }
+ },
+ args: [onExpr, time, evt, from, classRef, classRef2, classRefs],
+ op: function (context, on, time, evt, from, classRef, classRef2, classRefs) {
+ if (time) {
+ return new Promise(function (resolve) {
+ toggleCmd.toggle(on, classRef, classRef2, classRefs);
+ setTimeout(function () {
+ toggleCmd.toggle(on, classRef, classRef2, classRefs);
+ resolve(runtime.findNext(toggleCmd, context));
+ }, time);
+ });
+ } else if (evt) {
+ return new Promise(function (resolve) {
+ var target = from || context.me;
+ target.addEventListener(
+ evt,
+ function () {
+ toggleCmd.toggle(on, classRef, classRef2, classRefs);
+ resolve(runtime.findNext(toggleCmd, context));
+ },
+ { once: true }
+ );
+ toggleCmd.toggle(on, classRef, classRef2, classRefs);
+ });
+ } else {
+ this.toggle(on, classRef, classRef2, classRefs);
+ return runtime.findNext(toggleCmd, context);
+ }
+ },
+ };
+ return toggleCmd;
+ }
+ });
+
+ var HIDE_SHOW_STRATEGIES = {
+ display: function (op, element, arg) {
+ if (arg) {
+ element.style.display = arg;
+ } else if (op === "toggle") {
+ if (getComputedStyle(element).display === "none") {
+ HIDE_SHOW_STRATEGIES.display("show", element, arg);
+ } else {
+ HIDE_SHOW_STRATEGIES.display("hide", element, arg);
+ }
+ } else if (op === "hide") {
+ const internalData = parser.runtime.getInternalData(element);
+ if (internalData.originalDisplay == null) {
+ internalData.originalDisplay = element.style.display;
+ }
+ element.style.display = "none";
+ } else {
+ const internalData = parser.runtime.getInternalData(element);
+ if (internalData.originalDisplay && internalData.originalDisplay !== 'none') {
+ element.style.display = internalData.originalDisplay;
+ } else {
+ element.style.removeProperty('display');
+ }
+ }
+ },
+ visibility: function (op, element, arg) {
+ if (arg) {
+ element.style.visibility = arg;
+ } else if (op === "toggle") {
+ if (getComputedStyle(element).visibility === "hidden") {
+ HIDE_SHOW_STRATEGIES.visibility("show", element, arg);
+ } else {
+ HIDE_SHOW_STRATEGIES.visibility("hide", element, arg);
+ }
+ } else if (op === "hide") {
+ element.style.visibility = "hidden";
+ } else {
+ element.style.visibility = "visible";
+ }
+ },
+ opacity: function (op, element, arg) {
+ if (arg) {
+ element.style.opacity = arg;
+ } else if (op === "toggle") {
+ if (getComputedStyle(element).opacity === "0") {
+ HIDE_SHOW_STRATEGIES.opacity("show", element, arg);
+ } else {
+ HIDE_SHOW_STRATEGIES.opacity("hide", element, arg);
+ }
+ } else if (op === "hide") {
+ element.style.opacity = "0";
+ } else {
+ element.style.opacity = "1";
+ }
+ },
+ };
+
+ var parseShowHideTarget = function (parser, runtime, tokens) {
+ var target;
+ var currentTokenValue = tokens.currentToken();
+ if (currentTokenValue.value === "when" || currentTokenValue.value === "with" || parser.commandBoundary(currentTokenValue)) {
+ target = parser.parseElement("implicitMeTarget", tokens);
+ } else {
+ target = parser.parseElement("expression", tokens);
+ }
+ return target;
+ };
+
+ var resolveHideShowStrategy = function (parser, tokens, name) {
+ var configDefault = config.defaultHideShowStrategy;
+ var strategies = HIDE_SHOW_STRATEGIES;
+ if (config.hideShowStrategies) {
+ strategies = Object.assign(strategies, config.hideShowStrategies); // merge in user provided strategies
+ }
+ name = name || configDefault || "display";
+ var value = strategies[name];
+ if (value == null) {
+ parser.raiseParseError(tokens, "Unknown show/hide strategy : " + name);
+ }
+ return value;
+ };
+
+ parser.addCommand("hide", function (parser, runtime, tokens) {
+ if (tokens.matchToken("hide")) {
+ var targetExpr = parseShowHideTarget(parser, runtime, tokens);
+
+ var name = null;
+ if (tokens.matchToken("with")) {
+ name = tokens.requireTokenType("IDENTIFIER", "STYLE_REF").value;
+ if (name.indexOf("*") === 0) {
+ name = name.substr(1);
+ }
+ }
+ var hideShowStrategy = resolveHideShowStrategy(parser, tokens, name);
+
+ return {
+ target: targetExpr,
+ args: [targetExpr],
+ op: function (ctx, target) {
+ runtime.nullCheck(target, targetExpr);
+ runtime.implicitLoop(target, function (elt) {
+ hideShowStrategy("hide", elt);
+ });
+ return runtime.findNext(this, ctx);
+ },
+ };
+ }
+ });
+
+ parser.addCommand("show", function (parser, runtime, tokens) {
+ if (tokens.matchToken("show")) {
+ var targetExpr = parseShowHideTarget(parser, runtime, tokens);
+
+ var name = null;
+ if (tokens.matchToken("with")) {
+ name = tokens.requireTokenType("IDENTIFIER", "STYLE_REF").value;
+ if (name.indexOf("*") === 0) {
+ name = name.substr(1);
+ }
+ }
+ var arg = null;
+ if (tokens.matchOpToken(":")) {
+ var tokenArr = tokens.consumeUntilWhitespace();
+ tokens.matchTokenType("WHITESPACE");
+ arg = tokenArr
+ .map(function (t) {
+ return t.value;
+ })
+ .join("");
+ }
+
+ if (tokens.matchToken("when")) {
+ var when = parser.requireElement("expression", tokens);
+ }
+
+ var hideShowStrategy = resolveHideShowStrategy(parser, tokens, name);
+
+ return {
+ target: targetExpr,
+ when: when,
+ args: [targetExpr],
+ op: function (ctx, target) {
+ runtime.nullCheck(target, targetExpr);
+ runtime.implicitLoop(target, function (elt) {
+ if (when) {
+ ctx.result = elt;
+ let whenResult = runtime.evaluateNoPromise(when, ctx);
+ if (whenResult) {
+ hideShowStrategy("show", elt, arg);
+ } else {
+ hideShowStrategy("hide", elt);
+ }
+ ctx.result = null;
+ } else {
+ hideShowStrategy("show", elt, arg);
+ }
+ });
+ return runtime.findNext(this, ctx);
+ },
+ };
+ }
+ });
+
+ parser.addCommand("take", function (parser, runtime, tokens) {
+ if (tokens.matchToken("take")) {
+ var classRef = parser.parseElement("classRef", tokens);
+
+ var attributeRef = null;
+ var replacementValue = null;
+
+ if (classRef == null) {
+ attributeRef = parser.parseElement("attributeRef", tokens);
+ if (attributeRef == null) {
+ parser.raiseParseError(tokens, "Expected either a class reference or attribute expression");
+ }
+
+ if (tokens.matchToken("with")) {
+ replacementValue = parser.requireElement("expression", tokens);
+ }
+ }
+
+ if (tokens.matchToken("from")) {
+ var fromExpr = parser.requireElement("expression", tokens);
+ } else {
+ var fromExpr = classRef;
+ }
+
+ if (tokens.matchToken("for")) {
+ var forExpr = parser.requireElement("expression", tokens);
+ } else {
+ var forExpr = parser.requireElement("implicitMeTarget", tokens);
+ }
+
+ if (classRef) {
+ var takeCmd = {
+ classRef: classRef,
+ from: fromExpr,
+ forElt: forExpr,
+ args: [classRef, fromExpr, forExpr],
+ op: function (context, eltColl, from, forElt) {
+ runtime.nullCheck(from, fromExpr);
+ runtime.nullCheck(forElt, forExpr);
+ var clazz = eltColl.className;
+ runtime.implicitLoop(from, function (target) {
+ target.classList.remove(clazz);
+ });
+ runtime.implicitLoop(forElt, function (target) {
+ target.classList.add(clazz);
+ });
+ return runtime.findNext(this, context);
+ },
+ };
+ return takeCmd;
+ } else {
+ var takeCmd = {
+ attributeRef: attributeRef,
+ from: fromExpr,
+ forElt: forExpr,
+ args: [fromExpr, forExpr, replacementValue],
+ op: function (context, from, forElt, replacementValue) {
+ runtime.nullCheck(from, fromExpr);
+ runtime.nullCheck(forElt, forExpr);
+ runtime.implicitLoop(from, function (target) {
+ if (!replacementValue) {
+ target.removeAttribute(attributeRef.name);
+ } else {
+ target.setAttribute(attributeRef.name, replacementValue)
+ }
+ });
+ runtime.implicitLoop(forElt, function (target) {
+ target.setAttribute(attributeRef.name, attributeRef.value || "")
+ });
+ return runtime.findNext(this, context);
+ },
+ };
+ return takeCmd;
+ }
+ }
+ });
+
+ function putInto(runtime, context, prop, valueToPut) {
+ if (prop != null) {
+ var value = runtime.resolveSymbol(prop, context);
+ } else {
+ var value = context;
+ }
+ if (value instanceof Element || value instanceof HTMLDocument) {
+ while (value.firstChild) value.removeChild(value.firstChild);
+ value.append(parser.runtime.convertValue(valueToPut, "Fragment"));
+ runtime.processNode(value);
+ } else {
+ if (prop != null) {
+ runtime.setSymbol(prop, context, null, valueToPut);
+ } else {
+ throw "Don't know how to put a value into " + typeof context;
+ }
+ }
+ }
+
+ parser.addCommand("put", function (parser, runtime, tokens) {
+ if (tokens.matchToken("put")) {
+ var value = parser.requireElement("expression", tokens);
+
+ var operationToken = tokens.matchAnyToken("into", "before", "after");
+
+ if (operationToken == null && tokens.matchToken("at")) {
+ tokens.matchToken("the"); // optional "the"
+ operationToken = tokens.matchAnyToken("start", "end");
+ tokens.requireToken("of");
+ }
+
+ if (operationToken == null) {
+ parser.raiseParseError(tokens, "Expected one of 'into', 'before', 'at start of', 'at end of', 'after'");
+ }
+ var target = parser.requireElement("expression", tokens);
+
+ var operation = operationToken.value;
+
+ var arrayIndex = false;
+ var symbolWrite = false;
+ var rootExpr = null;
+ var prop = null;
+
+ if (target.type === "arrayIndex" && operation === "into") {
+ arrayIndex = true;
+ prop = target.prop;
+ rootExpr = target.root;
+ } else if (target.prop && target.root && operation === "into") {
+ prop = target.prop.value;
+ rootExpr = target.root;
+ } else if (target.type === "symbol" && operation === "into") {
+ symbolWrite = true;
+ prop = target.name;
+ } else if (target.type === "attributeRef" && operation === "into") {
+ var attributeWrite = true;
+ prop = target.name;
+ rootExpr = parser.requireElement("implicitMeTarget", tokens);
+ } else if (target.type === "styleRef" && operation === "into") {
+ var styleWrite = true;
+ prop = target.name;
+ rootExpr = parser.requireElement("implicitMeTarget", tokens);
+ } else if (target.attribute && operation === "into") {
+ var attributeWrite = target.attribute.type === "attributeRef";
+ var styleWrite = target.attribute.type === "styleRef";
+ prop = target.attribute.name;
+ rootExpr = target.root;
+ } else {
+ rootExpr = target;
+ }
+
+ var putCmd = {
+ target: target,
+ operation: operation,
+ symbolWrite: symbolWrite,
+ value: value,
+ args: [rootExpr, prop, value],
+ op: function (context, root, prop, valueToPut) {
+ if (symbolWrite) {
+ putInto(runtime, context, prop, valueToPut);
+ } else {
+ runtime.nullCheck(root, rootExpr);
+ if (operation === "into") {
+ if (attributeWrite) {
+ runtime.implicitLoop(root, function (elt) {
+ elt.setAttribute(prop, valueToPut);
+ });
+ } else if (styleWrite) {
+ runtime.implicitLoop(root, function (elt) {
+ elt.style[prop] = valueToPut;
+ });
+ } else if (arrayIndex) {
+ root[prop] = valueToPut;
+ } else {
+ runtime.implicitLoop(root, function (elt) {
+ putInto(runtime, elt, prop, valueToPut);
+ });
+ }
+ } else {
+ var op =
+ operation === "before"
+ ? Element.prototype.before
+ : operation === "after"
+ ? Element.prototype.after
+ : operation === "start"
+ ? Element.prototype.prepend
+ : operation === "end"
+ ? Element.prototype.append
+ : Element.prototype.append; // unreachable
+
+ runtime.implicitLoop(root, function (elt) {
+ op.call(
+ elt,
+ valueToPut instanceof Node
+ ? valueToPut
+ : runtime.convertValue(valueToPut, "Fragment")
+ );
+ // process any new content
+ if (elt.parentElement) {
+ runtime.processNode(elt.parentElement);
+ } else {
+ runtime.processNode(elt);
+ }
+ });
+ }
+ }
+ return runtime.findNext(this, context);
+ },
+ };
+ return putCmd;
+ }
+ });
+
+ function parsePseudopossessiveTarget(parser, runtime, tokens) {
+ var targets;
+ if (
+ tokens.matchToken("the") ||
+ tokens.matchToken("element") ||
+ tokens.matchToken("elements") ||
+ tokens.currentToken().type === "CLASS_REF" ||
+ tokens.currentToken().type === "ID_REF" ||
+ (tokens.currentToken().op && tokens.currentToken().value === "<")
+ ) {
+ parser.possessivesDisabled = true;
+ try {
+ targets = parser.parseElement("expression", tokens);
+ } finally {
+ delete parser.possessivesDisabled;
+ }
+ // optional possessive
+ if (tokens.matchOpToken("'")) {
+ tokens.requireToken("s");
+ }
+ } else if (tokens.currentToken().type === "IDENTIFIER" && tokens.currentToken().value === "its") {
+ var identifier = tokens.matchToken("its");
+ targets = {
+ type: "pseudopossessiveIts",
+ token: identifier,
+ name: identifier.value,
+ evaluate: function (context) {
+ return runtime.resolveSymbol("it", context);
+ },
+ };
+ } else {
+ tokens.matchToken("my") || tokens.matchToken("me"); // consume optional 'my'
+ targets = parser.parseElement("implicitMeTarget", tokens);
+ }
+ return targets;
+ }
+
+ parser.addCommand("transition", function (parser, runtime, tokens) {
+ if (tokens.matchToken("transition")) {
+ var targetsExpr = parsePseudopossessiveTarget(parser, runtime, tokens);
+
+ var properties = [];
+ var from = [];
+ var to = [];
+ var currentToken = tokens.currentToken();
+ while (
+ !parser.commandBoundary(currentToken) &&
+ currentToken.value !== "over" &&
+ currentToken.value !== "using"
+ ) {
+ if (tokens.currentToken().type === "STYLE_REF") {
+ let styleRef = tokens.consumeToken();
+ let styleProp = styleRef.value.substr(1);
+ properties.push({
+ type: "styleRefValue",
+ evaluate: function () {
+ return styleProp;
+ },
+ });
+ } else {
+ properties.push(parser.requireElement("stringLike", tokens));
+ }
+
+ if (tokens.matchToken("from")) {
+ from.push(parser.requireElement("expression", tokens));
+ } else {
+ from.push(null);
+ }
+ tokens.requireToken("to");
+ if (tokens.matchToken("initial")) {
+ to.push({
+ type: "initial_literal",
+ evaluate : function(){
+ return "initial";
+ }
+ });
+ } else {
+ to.push(parser.requireElement("expression", tokens));
+ }
+ currentToken = tokens.currentToken();
+ }
+ if (tokens.matchToken("over")) {
+ var over = parser.requireElement("expression", tokens);
+ } else if (tokens.matchToken("using")) {
+ var using = parser.requireElement("expression", tokens);
+ }
+
+ var transition = {
+ to: to,
+ args: [targetsExpr, properties, from, to, using, over],
+ op: function (context, targets, properties, from, to, using, over) {
+ runtime.nullCheck(targets, targetsExpr);
+ var promises = [];
+ runtime.implicitLoop(targets, function (target) {
+ var promise = new Promise(function (resolve, reject) {
+ var initialTransition = target.style.transition;
+ if (over) {
+ target.style.transition = "all " + over + "ms ease-in";
+ } else if (using) {
+ target.style.transition = using;
+ } else {
+ target.style.transition = config.defaultTransition;
+ }
+ var internalData = runtime.getInternalData(target);
+ var computedStyles = getComputedStyle(target);
+
+ var initialStyles = {};
+ for (var i = 0; i < computedStyles.length; i++) {
+ var name = computedStyles[i];
+ var initialValue = computedStyles[name];
+ initialStyles[name] = initialValue;
+ }
+
+ // store intitial values
+ if (!internalData.initalStyles) {
+ internalData.initalStyles = initialStyles;
+ }
+
+ for (var i = 0; i < properties.length; i++) {
+ var property = properties[i];
+ var fromVal = from[i];
+ if (fromVal === "computed" || fromVal == null) {
+ target.style[property] = initialStyles[property];
+ } else {
+ target.style[property] = fromVal;
+ }
+ }
+ //console.log("transition started", transition);
+
+ var transitionStarted = false;
+ var resolved = false;
+
+ target.addEventListener(
+ "transitionend",
+ function () {
+ if (!resolved) {
+ //console.log("transition ended", transition);
+ target.style.transition = initialTransition;
+ resolved = true;
+ resolve();
+ }
+ },
+ { once: true }
+ );
+
+ target.addEventListener(
+ "transitionstart",
+ function () {
+ transitionStarted = true;
+ },
+ { once: true }
+ );
+
+ // it no transition has started in 100ms, continue
+ setTimeout(function () {
+ if (!resolved && !transitionStarted) {
+ //console.log("transition ended", transition);
+ target.style.transition = initialTransition;
+ resolved = true;
+ resolve();
+ }
+ }, 100);
+
+ setTimeout(function () {
+ var autoProps = [];
+ for (var i = 0; i < properties.length; i++) {
+ var property = properties[i];
+ var toVal = to[i];
+ if (toVal === "initial") {
+ var propertyValue = internalData.initalStyles[property];
+ target.style[property] = propertyValue;
+ } else {
+ target.style[property] = toVal;
+ }
+ //console.log("set", property, "to", target.style[property], "on", target, "value passed in : ", toVal);
+ }
+ }, 0);
+ });
+ promises.push(promise);
+ });
+ return Promise.all(promises).then(function () {
+ return runtime.findNext(transition, context);
+ });
+ },
+ };
+ return transition;
+ }
+ });
+
+ parser.addCommand("measure", function (parser, runtime, tokens) {
+ if (!tokens.matchToken("measure")) return;
+
+ var targetExpr = parsePseudopossessiveTarget(parser, runtime, tokens);
+
+ var propsToMeasure = [];
+ if (!parser.commandBoundary(tokens.currentToken()))
+ do {
+ propsToMeasure.push(tokens.matchTokenType("IDENTIFIER").value);
+ } while (tokens.matchOpToken(","));
+
+ return {
+ properties: propsToMeasure,
+ args: [targetExpr],
+ op: function (ctx, target) {
+ runtime.nullCheck(target, targetExpr);
+ if (0 in target) target = target[0]; // not measuring multiple elts
+ var rect = target.getBoundingClientRect();
+ var scroll = {
+ top: target.scrollTop,
+ left: target.scrollLeft,
+ topMax: target.scrollTopMax,
+ leftMax: target.scrollLeftMax,
+ height: target.scrollHeight,
+ width: target.scrollWidth,
+ };
+
+ ctx.result = {
+ x: rect.x,
+ y: rect.y,
+ left: rect.left,
+ top: rect.top,
+ right: rect.right,
+ bottom: rect.bottom,
+ width: rect.width,
+ height: rect.height,
+ bounds: rect,
+
+ scrollLeft: scroll.left,
+ scrollTop: scroll.top,
+ scrollLeftMax: scroll.leftMax,
+ scrollTopMax: scroll.topMax,
+ scrollWidth: scroll.width,
+ scrollHeight: scroll.height,
+ scroll: scroll,
+ };
+
+ runtime.forEach(propsToMeasure, function (prop) {
+ if (prop in ctx.result) ctx.locals[prop] = ctx.result[prop];
+ else throw "No such measurement as " + prop;
+ });
+
+ return runtime.findNext(this, ctx);
+ },
+ };
+ });
+
+ parser.addLeafExpression("closestExpr", function (parser, runtime, tokens) {
+ if (tokens.matchToken("closest")) {
+ if (tokens.matchToken("parent")) {
+ var parentSearch = true;
+ }
+
+ var css = null;
+ if (tokens.currentToken().type === "ATTRIBUTE_REF") {
+ var attributeRef = parser.requireElement("attributeRefAccess", tokens, null);
+ css = "[" + attributeRef.attribute.name + "]";
+ }
+
+ if (css == null) {
+ var expr = parser.requireElement("expression", tokens);
+ if (expr.css == null) {
+ parser.raiseParseError(tokens, "Expected a CSS expression");
+ } else {
+ css = expr.css;
+ }
+ }
+
+ if (tokens.matchToken("to")) {
+ var to = parser.parseElement("expression", tokens);
+ } else {
+ var to = parser.parseElement("implicitMeTarget", tokens);
+ }
+
+ var closestExpr = {
+ type: "closestExpr",
+ parentSearch: parentSearch,
+ expr: expr,
+ css: css,
+ to: to,
+ args: [to],
+ op: function (ctx, to) {
+ if (to == null) {
+ return null;
+ } else {
+ let result = [];
+ runtime.implicitLoop(to, function(to){
+ if (parentSearch) {
+ result.push(to.parentElement ? to.parentElement.closest(css) : null);
+ } else {
+ result.push(to.closest(css));
+ }
+ })
+ if (runtime.shouldAutoIterate(to)) {
+ return result;
+ } else {
+ return result[0];
+ }
+ }
+ },
+ evaluate: function (context) {
+ return runtime.unifiedEval(this, context);
+ },
+ };
+
+ if (attributeRef) {
+ attributeRef.root = closestExpr;
+ attributeRef.args = [closestExpr];
+ return attributeRef;
+ } else {
+ return closestExpr;
+ }
+ }
+ });
+
+ parser.addCommand("go", function (parser, runtime, tokens) {
+ if (tokens.matchToken("go")) {
+ if (tokens.matchToken("back")) {
+ var back = true;
+ } else {
+ tokens.matchToken("to");
+ if (tokens.matchToken("url")) {
+ var target = parser.requireElement("stringLike", tokens);
+ var url = true;
+ if (tokens.matchToken("in")) {
+ tokens.requireToken("new");
+ tokens.requireToken("window");
+ var newWindow = true;
+ }
+ } else {
+ tokens.matchToken("the"); // optional the
+ var verticalPosition = tokens.matchAnyToken("top", "middle", "bottom");
+ var horizontalPosition = tokens.matchAnyToken("left", "center", "right");
+ if (verticalPosition || horizontalPosition) {
+ tokens.requireToken("of");
+ }
+ var target = parser.requireElement("unaryExpression", tokens);
+
+ var plusOrMinus = tokens.matchAnyOpToken("+", "-");
+ if (plusOrMinus) {
+ tokens.pushFollow("px");
+ try {
+ var offset = parser.requireElement("expression", tokens);
+ } finally {
+ tokens.popFollow();
+ }
+ }
+ tokens.matchToken("px"); // optional px
+
+ var smoothness = tokens.matchAnyToken("smoothly", "instantly");
+
+ var scrollOptions = {};
+ if (verticalPosition) {
+ if (verticalPosition.value === "top") {
+ scrollOptions.block = "start";
+ } else if (verticalPosition.value === "bottom") {
+ scrollOptions.block = "end";
+ } else if (verticalPosition.value === "middle") {
+ scrollOptions.block = "center";
+ }
+ }
+
+ if (horizontalPosition) {
+ if (horizontalPosition.value === "left") {
+ scrollOptions.inline = "start";
+ } else if (horizontalPosition.value === "center") {
+ scrollOptions.inline = "center";
+ } else if (horizontalPosition.value === "right") {
+ scrollOptions.inline = "end";
+ }
+ }
+
+ if (smoothness) {
+ if (smoothness.value === "smoothly") {
+ scrollOptions.behavior = "smooth";
+ } else if (smoothness.value === "instantly") {
+ scrollOptions.behavior = "instant";
+ }
+ }
+ }
+ }
+
+ var goCmd = {
+ target: target,
+ args: [target, offset],
+ op: function (ctx, to, offset) {
+ if (back) {
+ window.history.back();
+ } else if (url) {
+ if (to) {
+ if (newWindow) {
+ window.open(to);
+ } else {
+ window.location.href = to;
+ }
+ }
+ } else {
+ runtime.implicitLoop(to, function (target) {
+
+ if (target === window) {
+ target = document.body;
+ }
+
+ if(plusOrMinus) {
+ // a top scroll w/ an offset of some sort
+ var boundingRect = target.getBoundingClientRect();
+ let scrollShim = document.createElement('div');
+
+ if (plusOrMinus.value === "-") {
+ var finalOffset = -offset;
+ } else {
+ var finalOffset = - -offset;
+ }
+
+ scrollShim.style.position = 'absolute';
+ scrollShim.style.top = (boundingRect.x + finalOffset) + "px";
+ scrollShim.style.left = (boundingRect.y + finalOffset) + "px";
+ scrollShim.style.height = (boundingRect.height + (2 * finalOffset)) + "px";
+ scrollShim.style.width = (boundingRect.width + (2 * finalOffset)) + "px";
+ scrollShim.style.zIndex = "" + Number.MIN_SAFE_INTEGER;
+ scrollShim.style.opacity = "0";
+
+ document.body.appendChild(scrollShim);
+ setTimeout(function () {
+ document.body.removeChild(scrollShim);
+ }, 100);
+
+ target = scrollShim;
+ }
+
+ target.scrollIntoView(scrollOptions);
+ });
+ }
+ return runtime.findNext(goCmd, ctx);
+ },
+ };
+ return goCmd;
+ }
+ });
+
+ config.conversions.dynamicResolvers.push(function (str, node) {
+ if (!(str === "Values" || str.indexOf("Values:") === 0)) {
+ return;
+ }
+ var conversion = str.split(":")[1];
+ /** @type Object */
+ var result = {};
+
+ var implicitLoop = parser.runtime.implicitLoop.bind(parser.runtime);
+
+ implicitLoop(node, function (/** @type HTMLInputElement */ node) {
+ // Try to get a value directly from this node
+ var input = getInputInfo(node);
+
+ if (input !== undefined) {
+ result[input.name] = input.value;
+ return;
+ }
+
+ // Otherwise, try to query all child elements of this node that *should* contain values.
+ if (node.querySelectorAll != undefined) {
+ /** @type {NodeListOf} */
+ var children = node.querySelectorAll("input,select,textarea");
+ children.forEach(appendValue);
+ }
+ });
+
+ if (conversion) {
+ if (conversion === "JSON") {
+ return JSON.stringify(result);
+ } else if (conversion === "Form") {
+ /** @ts-ignore */
+ // TODO: does this work with multiple inputs of the same name?
+ return new URLSearchParams(result).toString();
+ } else {
+ throw "Unknown conversion: " + conversion;
+ }
+ } else {
+ return result;
+ }
+
+ /**
+ * @param {HTMLInputElement} node
+ */
+ function appendValue(node) {
+ var info = getInputInfo(node);
+
+ if (info == undefined) {
+ return;
+ }
+
+ // If there is no value already stored in this space.
+ if (result[info.name] == undefined) {
+ result[info.name] = info.value;
+ return;
+ }
+
+ if (Array.isArray(result[info.name]) && Array.isArray(info.value)) {
+ result[info.name] = [].concat(result[info.name], info.value);
+ return;
+ }
+ }
+
+ /**
+ * @param {HTMLInputElement} node
+ * @returns {{name:string, value:string | string[]} | undefined}
+ */
+ function getInputInfo(node) {
+ try {
+ /** @type {{name: string, value: string | string[]}}*/
+ var result = {
+ name: node.name,
+ value: node.value,
+ };
+
+ if (result.name == undefined || result.value == undefined) {
+ return undefined;
+ }
+
+ if (node.type == "radio" && node.checked == false) {
+ return undefined;
+ }
+
+ if (node.type == "checkbox") {
+ if (node.checked == false) {
+ result.value = undefined;
+ } else if (typeof result.value === "string") {
+ result.value = [result.value];
+ }
+ }
+
+ if (node.type == "select-multiple") {
+ /** @type {NodeListOf} */
+ var selected = node.querySelectorAll("option[selected]");
+
+ result.value = [];
+ for (var index = 0; index < selected.length; index++) {
+ result.value.push(selected[index].value);
+ }
+ }
+ return result;
+ } catch (e) {
+ return undefined;
+ }
+ }
+ });
+
+ config.conversions["HTML"] = function (value) {
+ var toHTML = /** @returns {string}*/ function (/** @type any*/ value) {
+ if (value instanceof Array) {
+ return value
+ .map(function (item) {
+ return toHTML(item);
+ })
+ .join("");
+ }
+
+ if (value instanceof HTMLElement) {
+ return value.outerHTML;
+ }
+
+ if (value instanceof NodeList) {
+ var result = "";
+ for (var i = 0; i < value.length; i++) {
+ var node = value[i];
+ if (node instanceof HTMLElement) {
+ result += node.outerHTML;
+ }
+ }
+ return result;
+ }
+
+ if (value.toString) {
+ return value.toString();
+ }
+
+ return "";
+ };
+
+ return toHTML(value);
+ };
+
+ config.conversions["Fragment"] = function (val) {
+ var frag = document.createDocumentFragment();
+ parser.runtime.implicitLoop(val, function (val) {
+ if (val instanceof Node) frag.append(val);
+ else {
+ var temp = document.createElement("template");
+ temp.innerHTML = val;
+ frag.append(temp.content);
+ }
+ });
+ return frag;
+ };
+ }
+
+
+ // Public API
+
+ const runtime_ = new Runtime(), lexer_ = runtime_.lexer, parser_ = runtime_.parser
+
+ /**
+ *
+ * @param {string} src
+ * @param {Partial} [ctx]
+ */
+ function run(src, ctx) {
+ return runtime_.evaluate(src, ctx)
+ }
+
+ function browserInit() {
+ /** @type {HTMLScriptElement[]} */
+ var scripts = Array.from(globalScope.document.querySelectorAll("script[type='text/hyperscript'][src]"))
+ Promise.all(
+ scripts.map(function (script) {
+ return fetch(script.src)
+ .then(function (res) {
+ return res.text();
+ });
+ })
+ )
+ .then(script_values => script_values.forEach(sc => _hyperscript(sc)))
+ .then(() => ready(function () {
+ mergeMetaConfig();
+ runtime_.processNode(document.documentElement);
+ globalScope.document.addEventListener("htmx:load", function (/** @type {CustomEvent} */ evt) {
+ runtime_.processNode(evt.detail.elt);
+ });
+ }));
+
+ function ready(fn) {
+ if (document.readyState !== "loading") {
+ setTimeout(fn);
+ } else {
+ document.addEventListener("DOMContentLoaded", fn);
+ }
+ }
+
+ function getMetaConfig() {
+ /** @type {HTMLMetaElement} */
+ var element = document.querySelector('meta[name="htmx-config"]');
+ if (element) {
+ return parseJSON(element.content);
+ } else {
+ return null;
+ }
+ }
+
+ function mergeMetaConfig() {
+ var metaConfig = getMetaConfig();
+ if (metaConfig) {
+ Object.assign(config, metaConfig);
+ }
+ }
+ }
+
+ /**
+ * @typedef {Object} HyperscriptAPI
+ *
+ * @property {Object} config
+ * @property {string} config.attributes
+ * @property {string} config.defaultTransition
+ * @property {string} config.disableSelector
+ * @property {typeof conversions} config.conversions
+ *
+ * @property {Object} internals
+ * @property {Lexer} internals.lexer
+ * @property {typeof Lexer} internals.Lexer
+ * @property {Parser} internals.parser
+ * @property {typeof Parser} internals.Parser
+ * @property {Runtime} internals.runtime
+ * @property {typeof Runtime} internals.Runtime
+ *
+ * @property {typeof ElementCollection} ElementCollection
+ *
+ * @property {(keyword: string, definition: ParseRule) => void} addFeature
+ * @property {(keyword: string, definition: ParseRule) => void} addCommand
+ * @property {(keyword: string, definition: ParseRule) => void} addLeafExpression
+ * @property {(keyword: string, definition: ParseRule) => void} addIndirectExpression
+ *
+ * @property {(src: string, ctx?: Partial) => any} evaluate
+ * @property {(src: string) => ASTNode} parse
+ * @property {(node: Element) => void} processNode
+ *
+ * @property {() => void} browserInit
+ *
+ *
+ * @typedef {HyperscriptAPI & ((src: string, ctx?: Partial) => any)} Hyperscript
+ */
+
+ /**
+ * @type {Hyperscript}
+ */
+ const _hyperscript = Object.assign(
+ run,
+ {
+ config,
+
+ use(plugin) { plugin(_hyperscript) },
+
+ internals: {
+ lexer: lexer_, parser: parser_, runtime: runtime_,
+ Lexer, Tokens, Parser, Runtime,
+ },
+ ElementCollection,
+
+ addFeature: parser_.addFeature.bind(parser_),
+ addCommand: parser_.addCommand.bind(parser_),
+ addLeafExpression: parser_.addLeafExpression.bind(parser_),
+ addIndirectExpression: parser_.addIndirectExpression.bind(parser_),
+
+ evaluate: runtime_.evaluate.bind(runtime_),
+ parse: runtime_.parse.bind(runtime_),
+ processNode: runtime_.processNode.bind(runtime_),
+
+ browserInit,
+ }
+ )
+
+ return _hyperscript
+})
diff --git a/templates/interactive-posts.html b/templates/interactive-posts.html
index eccf724..dc07a5f 100644
--- a/templates/interactive-posts.html
+++ b/templates/interactive-posts.html
@@ -16,12 +16,15 @@
{% include 'answer-post.html' %}
-