` implies\n * that `width=0` and `height=1`)\n */\n this._stylesIndex = new Map();\n /**\n * Represents the location of each class binding in the template\n * (e.g. `
` implies\n * that `big=0` and `hidden=1`)\n */\n this._classesIndex = new Map();\n this._initialStyleValues = [];\n this._initialClassValues = [];\n }\n /**\n * Registers a given input to the styling builder to be later used when producing AOT code.\n *\n * The code below will only accept the input if it is somehow tied to styling (whether it be\n * style/class bindings or static style/class attributes).\n */\n registerBoundInput(input) {\n // [attr.style] or [attr.class] are skipped in the code below,\n // they should not be treated as styling-based bindings since\n // they are intended to be written directly to the attr and\n // will therefore skip all style/class resolution that is present\n // with style=\"\", [style]=\"\" and [style.prop]=\"\", class=\"\",\n // [class.prop]=\"\". [class]=\"\" assignments\n let binding = null;\n let name = input.name;\n switch (input.type) {\n case 0 /* BindingType.Property */:\n binding = this.registerInputBasedOnName(name, input.value, input.sourceSpan);\n break;\n case 3 /* BindingType.Style */:\n binding = this.registerStyleInput(name, false, input.value, input.sourceSpan, input.unit);\n break;\n case 2 /* BindingType.Class */:\n binding = this.registerClassInput(name, false, input.value, input.sourceSpan);\n break;\n }\n return binding ? true : false;\n }\n registerInputBasedOnName(name, expression, sourceSpan) {\n let binding = null;\n const prefix = name.substring(0, 6);\n const isStyle = name === 'style' || prefix === 'style.' || prefix === 'style!';\n const isClass = !isStyle && (name === 'class' || prefix === 'class.' || prefix === 'class!');\n if (isStyle || isClass) {\n const isMapBased = name.charAt(5) !== '.'; // style.prop or class.prop makes this a no\n const property = name.slice(isMapBased ? 5 : 6); // the dot explains why there's a +1\n if (isStyle) {\n binding = this.registerStyleInput(property, isMapBased, expression, sourceSpan);\n } else {\n binding = this.registerClassInput(property, isMapBased, expression, sourceSpan);\n }\n }\n return binding;\n }\n registerStyleInput(name, isMapBased, value, sourceSpan, suffix) {\n if (isEmptyExpression(value)) {\n return null;\n }\n // CSS custom properties are case-sensitive so we shouldn't normalize them.\n // See: https://www.w3.org/TR/css-variables-1/#defining-variables\n if (!isCssCustomProperty(name)) {\n name = hyphenate$1(name);\n }\n const {\n property,\n hasOverrideFlag,\n suffix: bindingSuffix\n } = parseProperty(name);\n suffix = typeof suffix === 'string' && suffix.length !== 0 ? suffix : bindingSuffix;\n const entry = {\n name: property,\n suffix: suffix,\n value,\n sourceSpan,\n hasOverrideFlag\n };\n if (isMapBased) {\n this._styleMapInput = entry;\n } else {\n (this._singleStyleInputs = this._singleStyleInputs || []).push(entry);\n registerIntoMap(this._stylesIndex, property);\n }\n this._lastStylingInput = entry;\n this._firstStylingInput = this._firstStylingInput || entry;\n this._checkForPipes(value);\n this.hasBindings = true;\n return entry;\n }\n registerClassInput(name, isMapBased, value, sourceSpan) {\n if (isEmptyExpression(value)) {\n return null;\n }\n const {\n property,\n hasOverrideFlag\n } = parseProperty(name);\n const entry = {\n name: property,\n value,\n sourceSpan,\n hasOverrideFlag,\n suffix: null\n };\n if (isMapBased) {\n this._classMapInput = entry;\n } else {\n (this._singleClassInputs = this._singleClassInputs || []).push(entry);\n registerIntoMap(this._classesIndex, property);\n }\n this._lastStylingInput = entry;\n this._firstStylingInput = this._firstStylingInput || entry;\n this._checkForPipes(value);\n this.hasBindings = true;\n return entry;\n }\n _checkForPipes(value) {\n if (value instanceof ASTWithSource && value.ast instanceof BindingPipe) {\n this.hasBindingsWithPipes = true;\n }\n }\n /**\n * Registers the element's static style string value to the builder.\n *\n * @param value the style string (e.g. `width:100px; height:200px;`)\n */\n registerStyleAttr(value) {\n this._initialStyleValues = parse(value);\n this._hasInitialValues = true;\n }\n /**\n * Registers the element's static class string value to the builder.\n *\n * @param value the className string (e.g. `disabled gold zoom`)\n */\n registerClassAttr(value) {\n this._initialClassValues = value.trim().split(/\\s+/g);\n this._hasInitialValues = true;\n }\n /**\n * Appends all styling-related expressions to the provided attrs array.\n *\n * @param attrs an existing array where each of the styling expressions\n * will be inserted into.\n */\n populateInitialStylingAttrs(attrs) {\n // [CLASS_MARKER, 'foo', 'bar', 'baz' ...]\n if (this._initialClassValues.length) {\n attrs.push(literal(1 /* AttributeMarker.Classes */));\n for (let i = 0; i < this._initialClassValues.length; i++) {\n attrs.push(literal(this._initialClassValues[i]));\n }\n }\n // [STYLE_MARKER, 'width', '200px', 'height', '100px', ...]\n if (this._initialStyleValues.length) {\n attrs.push(literal(2 /* AttributeMarker.Styles */));\n for (let i = 0; i < this._initialStyleValues.length; i += 2) {\n attrs.push(literal(this._initialStyleValues[i]), literal(this._initialStyleValues[i + 1]));\n }\n }\n }\n /**\n * Builds an instruction with all the expressions and parameters for `elementHostAttrs`.\n *\n * The instruction generation code below is used for producing the AOT statement code which is\n * responsible for registering initial styles (within a directive hostBindings' creation block),\n * as well as any of the provided attribute values, to the directive host element.\n */\n assignHostAttrs(attrs, definitionMap) {\n if (this._directiveExpr && (attrs.length || this._hasInitialValues)) {\n this.populateInitialStylingAttrs(attrs);\n definitionMap.set('hostAttrs', literalArr(attrs));\n }\n }\n /**\n * Builds an instruction with all the expressions and parameters for `classMap`.\n *\n * The instruction data will contain all expressions for `classMap` to function\n * which includes the `[class]` expression params.\n */\n buildClassMapInstruction(valueConverter) {\n if (this._classMapInput) {\n return this._buildMapBasedInstruction(valueConverter, true, this._classMapInput);\n }\n return null;\n }\n /**\n * Builds an instruction with all the expressions and parameters for `styleMap`.\n *\n * The instruction data will contain all expressions for `styleMap` to function\n * which includes the `[style]` expression params.\n */\n buildStyleMapInstruction(valueConverter) {\n if (this._styleMapInput) {\n return this._buildMapBasedInstruction(valueConverter, false, this._styleMapInput);\n }\n return null;\n }\n _buildMapBasedInstruction(valueConverter, isClassBased, stylingInput) {\n // each styling binding value is stored in the LView\n // map-based bindings allocate two slots: one for the\n // previous binding value and another for the previous\n // className or style attribute value.\n let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;\n // these values must be outside of the update block so that they can\n // be evaluated (the AST visit call) during creation time so that any\n // pipes can be picked up in time before the template is built\n const mapValue = stylingInput.value.visit(valueConverter);\n let reference;\n if (mapValue instanceof Interpolation$1) {\n totalBindingSlotsRequired += mapValue.expressions.length;\n reference = isClassBased ? getClassMapInterpolationExpression(mapValue) : getStyleMapInterpolationExpression(mapValue);\n } else {\n reference = isClassBased ? Identifiers.classMap : Identifiers.styleMap;\n }\n return {\n reference,\n calls: [{\n supportsInterpolation: true,\n sourceSpan: stylingInput.sourceSpan,\n allocateBindingSlots: totalBindingSlotsRequired,\n params: convertFn => {\n const convertResult = convertFn(mapValue);\n const params = Array.isArray(convertResult) ? convertResult : [convertResult];\n return params;\n }\n }]\n };\n }\n _buildSingleInputs(reference, inputs, valueConverter, getInterpolationExpressionFn, isClassBased) {\n const instructions = [];\n inputs.forEach(input => {\n const previousInstruction = instructions[instructions.length - 1];\n const value = input.value.visit(valueConverter);\n let referenceForCall = reference;\n // each styling binding value is stored in the LView\n // but there are two values stored for each binding:\n // 1) the value itself\n // 2) an intermediate value (concatenation of style up to this point).\n // We need to store the intermediate value so that we don't allocate\n // the strings on each CD.\n let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;\n if (value instanceof Interpolation$1) {\n totalBindingSlotsRequired += value.expressions.length;\n if (getInterpolationExpressionFn) {\n referenceForCall = getInterpolationExpressionFn(value);\n }\n }\n const call = {\n sourceSpan: input.sourceSpan,\n allocateBindingSlots: totalBindingSlotsRequired,\n supportsInterpolation: !!getInterpolationExpressionFn,\n params: convertFn => {\n // params => stylingProp(propName, value, suffix)\n const params = [];\n params.push(literal(input.name));\n const convertResult = convertFn(value);\n if (Array.isArray(convertResult)) {\n params.push(...convertResult);\n } else {\n params.push(convertResult);\n }\n // [style.prop] bindings may use suffix values (e.g. px, em, etc...), therefore,\n // if that is detected then we need to pass that in as an optional param.\n if (!isClassBased && input.suffix !== null) {\n params.push(literal(input.suffix));\n }\n return params;\n }\n };\n // If we ended up generating a call to the same instruction as the previous styling property\n // we can chain the calls together safely to save some bytes, otherwise we have to generate\n // a separate instruction call. This is primarily a concern with interpolation instructions\n // where we may start off with one `reference`, but end up using another based on the\n // number of interpolations.\n if (previousInstruction && previousInstruction.reference === referenceForCall) {\n previousInstruction.calls.push(call);\n } else {\n instructions.push({\n reference: referenceForCall,\n calls: [call]\n });\n }\n });\n return instructions;\n }\n _buildClassInputs(valueConverter) {\n if (this._singleClassInputs) {\n return this._buildSingleInputs(Identifiers.classProp, this._singleClassInputs, valueConverter, null, true);\n }\n return [];\n }\n _buildStyleInputs(valueConverter) {\n if (this._singleStyleInputs) {\n return this._buildSingleInputs(Identifiers.styleProp, this._singleStyleInputs, valueConverter, getStylePropInterpolationExpression, false);\n }\n return [];\n }\n /**\n * Constructs all instructions which contain the expressions that will be placed\n * into the update block of a template function or a directive hostBindings function.\n */\n buildUpdateLevelInstructions(valueConverter) {\n const instructions = [];\n if (this.hasBindings) {\n const styleMapInstruction = this.buildStyleMapInstruction(valueConverter);\n if (styleMapInstruction) {\n instructions.push(styleMapInstruction);\n }\n const classMapInstruction = this.buildClassMapInstruction(valueConverter);\n if (classMapInstruction) {\n instructions.push(classMapInstruction);\n }\n instructions.push(...this._buildStyleInputs(valueConverter));\n instructions.push(...this._buildClassInputs(valueConverter));\n }\n return instructions;\n }\n}\nfunction registerIntoMap(map, key) {\n if (!map.has(key)) {\n map.set(key, map.size);\n }\n}\nfunction parseProperty(name) {\n let hasOverrideFlag = false;\n const overrideIndex = name.indexOf(IMPORTANT_FLAG);\n if (overrideIndex !== -1) {\n name = overrideIndex > 0 ? name.substring(0, overrideIndex) : '';\n hasOverrideFlag = true;\n }\n let suffix = null;\n let property = name;\n const unitIndex = name.lastIndexOf('.');\n if (unitIndex > 0) {\n suffix = name.slice(unitIndex + 1);\n property = name.substring(0, unitIndex);\n }\n return {\n property,\n suffix,\n hasOverrideFlag\n };\n}\n/**\n * Gets the instruction to generate for an interpolated class map.\n * @param interpolation An Interpolation AST\n */\nfunction getClassMapInterpolationExpression(interpolation) {\n switch (getInterpolationArgsLength(interpolation)) {\n case 1:\n return Identifiers.classMap;\n case 3:\n return Identifiers.classMapInterpolate1;\n case 5:\n return Identifiers.classMapInterpolate2;\n case 7:\n return Identifiers.classMapInterpolate3;\n case 9:\n return Identifiers.classMapInterpolate4;\n case 11:\n return Identifiers.classMapInterpolate5;\n case 13:\n return Identifiers.classMapInterpolate6;\n case 15:\n return Identifiers.classMapInterpolate7;\n case 17:\n return Identifiers.classMapInterpolate8;\n default:\n return Identifiers.classMapInterpolateV;\n }\n}\n/**\n * Gets the instruction to generate for an interpolated style map.\n * @param interpolation An Interpolation AST\n */\nfunction getStyleMapInterpolationExpression(interpolation) {\n switch (getInterpolationArgsLength(interpolation)) {\n case 1:\n return Identifiers.styleMap;\n case 3:\n return Identifiers.styleMapInterpolate1;\n case 5:\n return Identifiers.styleMapInterpolate2;\n case 7:\n return Identifiers.styleMapInterpolate3;\n case 9:\n return Identifiers.styleMapInterpolate4;\n case 11:\n return Identifiers.styleMapInterpolate5;\n case 13:\n return Identifiers.styleMapInterpolate6;\n case 15:\n return Identifiers.styleMapInterpolate7;\n case 17:\n return Identifiers.styleMapInterpolate8;\n default:\n return Identifiers.styleMapInterpolateV;\n }\n}\n/**\n * Gets the instruction to generate for an interpolated style prop.\n * @param interpolation An Interpolation AST\n */\nfunction getStylePropInterpolationExpression(interpolation) {\n switch (getInterpolationArgsLength(interpolation)) {\n case 1:\n return Identifiers.styleProp;\n case 3:\n return Identifiers.stylePropInterpolate1;\n case 5:\n return Identifiers.stylePropInterpolate2;\n case 7:\n return Identifiers.stylePropInterpolate3;\n case 9:\n return Identifiers.stylePropInterpolate4;\n case 11:\n return Identifiers.stylePropInterpolate5;\n case 13:\n return Identifiers.stylePropInterpolate6;\n case 15:\n return Identifiers.stylePropInterpolate7;\n case 17:\n return Identifiers.stylePropInterpolate8;\n default:\n return Identifiers.stylePropInterpolateV;\n }\n}\n/**\n * Checks whether property name is a custom CSS property.\n * See: https://www.w3.org/TR/css-variables-1\n */\nfunction isCssCustomProperty(name) {\n return name.startsWith('--');\n}\nfunction isEmptyExpression(ast) {\n if (ast instanceof ASTWithSource) {\n ast = ast.ast;\n }\n return ast instanceof EmptyExpr$1;\n}\nvar TokenType;\n(function (TokenType) {\n TokenType[TokenType[\"Character\"] = 0] = \"Character\";\n TokenType[TokenType[\"Identifier\"] = 1] = \"Identifier\";\n TokenType[TokenType[\"PrivateIdentifier\"] = 2] = \"PrivateIdentifier\";\n TokenType[TokenType[\"Keyword\"] = 3] = \"Keyword\";\n TokenType[TokenType[\"String\"] = 4] = \"String\";\n TokenType[TokenType[\"Operator\"] = 5] = \"Operator\";\n TokenType[TokenType[\"Number\"] = 6] = \"Number\";\n TokenType[TokenType[\"Error\"] = 7] = \"Error\";\n})(TokenType || (TokenType = {}));\nconst KEYWORDS = ['var', 'let', 'as', 'null', 'undefined', 'true', 'false', 'if', 'else', 'this'];\nclass Lexer {\n tokenize(text) {\n const scanner = new _Scanner(text);\n const tokens = [];\n let token = scanner.scanToken();\n while (token != null) {\n tokens.push(token);\n token = scanner.scanToken();\n }\n return tokens;\n }\n}\nclass Token {\n constructor(index, end, type, numValue, strValue) {\n this.index = index;\n this.end = end;\n this.type = type;\n this.numValue = numValue;\n this.strValue = strValue;\n }\n isCharacter(code) {\n return this.type == TokenType.Character && this.numValue == code;\n }\n isNumber() {\n return this.type == TokenType.Number;\n }\n isString() {\n return this.type == TokenType.String;\n }\n isOperator(operator) {\n return this.type == TokenType.Operator && this.strValue == operator;\n }\n isIdentifier() {\n return this.type == TokenType.Identifier;\n }\n isPrivateIdentifier() {\n return this.type == TokenType.PrivateIdentifier;\n }\n isKeyword() {\n return this.type == TokenType.Keyword;\n }\n isKeywordLet() {\n return this.type == TokenType.Keyword && this.strValue == 'let';\n }\n isKeywordAs() {\n return this.type == TokenType.Keyword && this.strValue == 'as';\n }\n isKeywordNull() {\n return this.type == TokenType.Keyword && this.strValue == 'null';\n }\n isKeywordUndefined() {\n return this.type == TokenType.Keyword && this.strValue == 'undefined';\n }\n isKeywordTrue() {\n return this.type == TokenType.Keyword && this.strValue == 'true';\n }\n isKeywordFalse() {\n return this.type == TokenType.Keyword && this.strValue == 'false';\n }\n isKeywordThis() {\n return this.type == TokenType.Keyword && this.strValue == 'this';\n }\n isError() {\n return this.type == TokenType.Error;\n }\n toNumber() {\n return this.type == TokenType.Number ? this.numValue : -1;\n }\n toString() {\n switch (this.type) {\n case TokenType.Character:\n case TokenType.Identifier:\n case TokenType.Keyword:\n case TokenType.Operator:\n case TokenType.PrivateIdentifier:\n case TokenType.String:\n case TokenType.Error:\n return this.strValue;\n case TokenType.Number:\n return this.numValue.toString();\n default:\n return null;\n }\n }\n}\nfunction newCharacterToken(index, end, code) {\n return new Token(index, end, TokenType.Character, code, String.fromCharCode(code));\n}\nfunction newIdentifierToken(index, end, text) {\n return new Token(index, end, TokenType.Identifier, 0, text);\n}\nfunction newPrivateIdentifierToken(index, end, text) {\n return new Token(index, end, TokenType.PrivateIdentifier, 0, text);\n}\nfunction newKeywordToken(index, end, text) {\n return new Token(index, end, TokenType.Keyword, 0, text);\n}\nfunction newOperatorToken(index, end, text) {\n return new Token(index, end, TokenType.Operator, 0, text);\n}\nfunction newStringToken(index, end, text) {\n return new Token(index, end, TokenType.String, 0, text);\n}\nfunction newNumberToken(index, end, n) {\n return new Token(index, end, TokenType.Number, n, '');\n}\nfunction newErrorToken(index, end, message) {\n return new Token(index, end, TokenType.Error, 0, message);\n}\nconst EOF = new Token(-1, -1, TokenType.Character, 0, '');\nclass _Scanner {\n constructor(input) {\n this.input = input;\n this.peek = 0;\n this.index = -1;\n this.length = input.length;\n this.advance();\n }\n advance() {\n this.peek = ++this.index >= this.length ? $EOF : this.input.charCodeAt(this.index);\n }\n scanToken() {\n const input = this.input,\n length = this.length;\n let peek = this.peek,\n index = this.index;\n // Skip whitespace.\n while (peek <= $SPACE) {\n if (++index >= length) {\n peek = $EOF;\n break;\n } else {\n peek = input.charCodeAt(index);\n }\n }\n this.peek = peek;\n this.index = index;\n if (index >= length) {\n return null;\n }\n // Handle identifiers and numbers.\n if (isIdentifierStart(peek)) return this.scanIdentifier();\n if (isDigit(peek)) return this.scanNumber(index);\n const start = index;\n switch (peek) {\n case $PERIOD:\n this.advance();\n return isDigit(this.peek) ? this.scanNumber(start) : newCharacterToken(start, this.index, $PERIOD);\n case $LPAREN:\n case $RPAREN:\n case $LBRACE:\n case $RBRACE:\n case $LBRACKET:\n case $RBRACKET:\n case $COMMA:\n case $COLON:\n case $SEMICOLON:\n return this.scanCharacter(start, peek);\n case $SQ:\n case $DQ:\n return this.scanString();\n case $HASH:\n return this.scanPrivateIdentifier();\n case $PLUS:\n case $MINUS:\n case $STAR:\n case $SLASH:\n case $PERCENT:\n case $CARET:\n return this.scanOperator(start, String.fromCharCode(peek));\n case $QUESTION:\n return this.scanQuestion(start);\n case $LT:\n case $GT:\n return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '=');\n case $BANG:\n case $EQ:\n return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '=', $EQ, '=');\n case $AMPERSAND:\n return this.scanComplexOperator(start, '&', $AMPERSAND, '&');\n case $BAR:\n return this.scanComplexOperator(start, '|', $BAR, '|');\n case $NBSP:\n while (isWhitespace(this.peek)) this.advance();\n return this.scanToken();\n }\n this.advance();\n return this.error(`Unexpected character [${String.fromCharCode(peek)}]`, 0);\n }\n scanCharacter(start, code) {\n this.advance();\n return newCharacterToken(start, this.index, code);\n }\n scanOperator(start, str) {\n this.advance();\n return newOperatorToken(start, this.index, str);\n }\n /**\n * Tokenize a 2/3 char long operator\n *\n * @param start start index in the expression\n * @param one first symbol (always part of the operator)\n * @param twoCode code point for the second symbol\n * @param two second symbol (part of the operator when the second code point matches)\n * @param threeCode code point for the third symbol\n * @param three third symbol (part of the operator when provided and matches source expression)\n */\n scanComplexOperator(start, one, twoCode, two, threeCode, three) {\n this.advance();\n let str = one;\n if (this.peek == twoCode) {\n this.advance();\n str += two;\n }\n if (threeCode != null && this.peek == threeCode) {\n this.advance();\n str += three;\n }\n return newOperatorToken(start, this.index, str);\n }\n scanIdentifier() {\n const start = this.index;\n this.advance();\n while (isIdentifierPart(this.peek)) this.advance();\n const str = this.input.substring(start, this.index);\n return KEYWORDS.indexOf(str) > -1 ? newKeywordToken(start, this.index, str) : newIdentifierToken(start, this.index, str);\n }\n /** Scans an ECMAScript private identifier. */\n scanPrivateIdentifier() {\n const start = this.index;\n this.advance();\n if (!isIdentifierStart(this.peek)) {\n return this.error('Invalid character [#]', -1);\n }\n while (isIdentifierPart(this.peek)) this.advance();\n const identifierName = this.input.substring(start, this.index);\n return newPrivateIdentifierToken(start, this.index, identifierName);\n }\n scanNumber(start) {\n let simple = this.index === start;\n let hasSeparators = false;\n this.advance(); // Skip initial digit.\n while (true) {\n if (isDigit(this.peek)) {\n // Do nothing.\n } else if (this.peek === $_) {\n // Separators are only valid when they're surrounded by digits. E.g. `1_0_1` is\n // valid while `_101` and `101_` are not. The separator can't be next to the decimal\n // point or another separator either. Note that it's unlikely that we'll hit a case where\n // the underscore is at the start, because that's a valid identifier and it will be picked\n // up earlier in the parsing. We validate for it anyway just in case.\n if (!isDigit(this.input.charCodeAt(this.index - 1)) || !isDigit(this.input.charCodeAt(this.index + 1))) {\n return this.error('Invalid numeric separator', 0);\n }\n hasSeparators = true;\n } else if (this.peek === $PERIOD) {\n simple = false;\n } else if (isExponentStart(this.peek)) {\n this.advance();\n if (isExponentSign(this.peek)) this.advance();\n if (!isDigit(this.peek)) return this.error('Invalid exponent', -1);\n simple = false;\n } else {\n break;\n }\n this.advance();\n }\n let str = this.input.substring(start, this.index);\n if (hasSeparators) {\n str = str.replace(/_/g, '');\n }\n const value = simple ? parseIntAutoRadix(str) : parseFloat(str);\n return newNumberToken(start, this.index, value);\n }\n scanString() {\n const start = this.index;\n const quote = this.peek;\n this.advance(); // Skip initial quote.\n let buffer = '';\n let marker = this.index;\n const input = this.input;\n while (this.peek != quote) {\n if (this.peek == $BACKSLASH) {\n buffer += input.substring(marker, this.index);\n let unescapedCode;\n this.advance(); // mutates this.peek\n // @ts-expect-error see microsoft/TypeScript#9998\n if (this.peek == $u) {\n // 4 character hex code for unicode character.\n const hex = input.substring(this.index + 1, this.index + 5);\n if (/^[0-9a-f]+$/i.test(hex)) {\n unescapedCode = parseInt(hex, 16);\n } else {\n return this.error(`Invalid unicode escape [\\\\u${hex}]`, 0);\n }\n for (let i = 0; i < 5; i++) {\n this.advance();\n }\n } else {\n unescapedCode = unescape(this.peek);\n this.advance();\n }\n buffer += String.fromCharCode(unescapedCode);\n marker = this.index;\n } else if (this.peek == $EOF) {\n return this.error('Unterminated quote', 0);\n } else {\n this.advance();\n }\n }\n const last = input.substring(marker, this.index);\n this.advance(); // Skip terminating quote.\n return newStringToken(start, this.index, buffer + last);\n }\n scanQuestion(start) {\n this.advance();\n let str = '?';\n // Either `a ?? b` or 'a?.b'.\n if (this.peek === $QUESTION || this.peek === $PERIOD) {\n str += this.peek === $PERIOD ? '.' : '?';\n this.advance();\n }\n return newOperatorToken(start, this.index, str);\n }\n error(message, offset) {\n const position = this.index + offset;\n return newErrorToken(position, this.index, `Lexer Error: ${message} at column ${position} in expression [${this.input}]`);\n }\n}\nfunction isIdentifierStart(code) {\n return $a <= code && code <= $z || $A <= code && code <= $Z || code == $_ || code == $$;\n}\nfunction isIdentifier(input) {\n if (input.length == 0) return false;\n const scanner = new _Scanner(input);\n if (!isIdentifierStart(scanner.peek)) return false;\n scanner.advance();\n while (scanner.peek !== $EOF) {\n if (!isIdentifierPart(scanner.peek)) return false;\n scanner.advance();\n }\n return true;\n}\nfunction isIdentifierPart(code) {\n return isAsciiLetter(code) || isDigit(code) || code == $_ || code == $$;\n}\nfunction isExponentStart(code) {\n return code == $e || code == $E;\n}\nfunction isExponentSign(code) {\n return code == $MINUS || code == $PLUS;\n}\nfunction unescape(code) {\n switch (code) {\n case $n:\n return $LF;\n case $f:\n return $FF;\n case $r:\n return $CR;\n case $t:\n return $TAB;\n case $v:\n return $VTAB;\n default:\n return code;\n }\n}\nfunction parseIntAutoRadix(text) {\n const result = parseInt(text);\n if (isNaN(result)) {\n throw new Error('Invalid integer literal when parsing ' + text);\n }\n return result;\n}\nclass SplitInterpolation {\n constructor(strings, expressions, offsets) {\n this.strings = strings;\n this.expressions = expressions;\n this.offsets = offsets;\n }\n}\nclass TemplateBindingParseResult {\n constructor(templateBindings, warnings, errors) {\n this.templateBindings = templateBindings;\n this.warnings = warnings;\n this.errors = errors;\n }\n}\nclass Parser$1 {\n constructor(_lexer) {\n this._lexer = _lexer;\n this.errors = [];\n }\n parseAction(input, isAssignmentEvent, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {\n this._checkNoInterpolation(input, location, interpolationConfig);\n const sourceToLex = this._stripComments(input);\n const tokens = this._lexer.tokenize(sourceToLex);\n let flags = 1 /* ParseFlags.Action */;\n if (isAssignmentEvent) {\n flags |= 2 /* ParseFlags.AssignmentEvent */;\n }\n const ast = new _ParseAST(input, location, absoluteOffset, tokens, flags, this.errors, 0).parseChain();\n return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);\n }\n parseBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {\n const ast = this._parseBindingAst(input, location, absoluteOffset, interpolationConfig);\n return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);\n }\n checkSimpleExpression(ast) {\n const checker = new SimpleExpressionChecker();\n ast.visit(checker);\n return checker.errors;\n }\n // Host bindings parsed here\n parseSimpleBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {\n const ast = this._parseBindingAst(input, location, absoluteOffset, interpolationConfig);\n const errors = this.checkSimpleExpression(ast);\n if (errors.length > 0) {\n this._reportError(`Host binding expression cannot contain ${errors.join(' ')}`, input, location);\n }\n return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);\n }\n _reportError(message, input, errLocation, ctxLocation) {\n this.errors.push(new ParserError(message, input, errLocation, ctxLocation));\n }\n _parseBindingAst(input, location, absoluteOffset, interpolationConfig) {\n this._checkNoInterpolation(input, location, interpolationConfig);\n const sourceToLex = this._stripComments(input);\n const tokens = this._lexer.tokenize(sourceToLex);\n return new _ParseAST(input, location, absoluteOffset, tokens, 0 /* ParseFlags.None */, this.errors, 0).parseChain();\n }\n /**\n * Parse microsyntax template expression and return a list of bindings or\n * parsing errors in case the given expression is invalid.\n *\n * For example,\n * ```\n *
\n * ^ ^ absoluteValueOffset for `templateValue`\n * absoluteKeyOffset for `templateKey`\n * ```\n * contains three bindings:\n * 1. ngFor -> null\n * 2. item -> NgForOfContext.$implicit\n * 3. ngForOf -> items\n *\n * This is apparent from the de-sugared template:\n * ```\n *
\n * ```\n *\n * @param templateKey name of directive, without the * prefix. For example: ngIf, ngFor\n * @param templateValue RHS of the microsyntax attribute\n * @param templateUrl template filename if it's external, component filename if it's inline\n * @param absoluteKeyOffset start of the `templateKey`\n * @param absoluteValueOffset start of the `templateValue`\n */\n parseTemplateBindings(templateKey, templateValue, templateUrl, absoluteKeyOffset, absoluteValueOffset) {\n const tokens = this._lexer.tokenize(templateValue);\n const parser = new _ParseAST(templateValue, templateUrl, absoluteValueOffset, tokens, 0 /* ParseFlags.None */, this.errors, 0 /* relative offset */);\n return parser.parseTemplateBindings({\n source: templateKey,\n span: new AbsoluteSourceSpan(absoluteKeyOffset, absoluteKeyOffset + templateKey.length)\n });\n }\n parseInterpolation(input, location, absoluteOffset, interpolatedTokens, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {\n const {\n strings,\n expressions,\n offsets\n } = this.splitInterpolation(input, location, interpolatedTokens, interpolationConfig);\n if (expressions.length === 0) return null;\n const expressionNodes = [];\n for (let i = 0; i < expressions.length; ++i) {\n const expressionText = expressions[i].text;\n const sourceToLex = this._stripComments(expressionText);\n const tokens = this._lexer.tokenize(sourceToLex);\n const ast = new _ParseAST(input, location, absoluteOffset, tokens, 0 /* ParseFlags.None */, this.errors, offsets[i]).parseChain();\n expressionNodes.push(ast);\n }\n return this.createInterpolationAst(strings.map(s => s.text), expressionNodes, input, location, absoluteOffset);\n }\n /**\n * Similar to `parseInterpolation`, but treats the provided string as a single expression\n * element that would normally appear within the interpolation prefix and suffix (`{{` and `}}`).\n * This is used for parsing the switch expression in ICUs.\n */\n parseInterpolationExpression(expression, location, absoluteOffset) {\n const sourceToLex = this._stripComments(expression);\n const tokens = this._lexer.tokenize(sourceToLex);\n const ast = new _ParseAST(expression, location, absoluteOffset, tokens, 0 /* ParseFlags.None */, this.errors, 0).parseChain();\n const strings = ['', '']; // The prefix and suffix strings are both empty\n return this.createInterpolationAst(strings, [ast], expression, location, absoluteOffset);\n }\n createInterpolationAst(strings, expressions, input, location, absoluteOffset) {\n const span = new ParseSpan(0, input.length);\n const interpolation = new Interpolation$1(span, span.toAbsolute(absoluteOffset), strings, expressions);\n return new ASTWithSource(interpolation, input, location, absoluteOffset, this.errors);\n }\n /**\n * Splits a string of text into \"raw\" text segments and expressions present in interpolations in\n * the string.\n * Returns `null` if there are no interpolations, otherwise a\n * `SplitInterpolation` with splits that look like\n * ... \n */\n splitInterpolation(input, location, interpolatedTokens, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {\n const strings = [];\n const expressions = [];\n const offsets = [];\n const inputToTemplateIndexMap = interpolatedTokens ? getIndexMapForOriginalTemplate(interpolatedTokens) : null;\n let i = 0;\n let atInterpolation = false;\n let extendLastString = false;\n let {\n start: interpStart,\n end: interpEnd\n } = interpolationConfig;\n while (i < input.length) {\n if (!atInterpolation) {\n // parse until starting {{\n const start = i;\n i = input.indexOf(interpStart, i);\n if (i === -1) {\n i = input.length;\n }\n const text = input.substring(start, i);\n strings.push({\n text,\n start,\n end: i\n });\n atInterpolation = true;\n } else {\n // parse from starting {{ to ending }} while ignoring content inside quotes.\n const fullStart = i;\n const exprStart = fullStart + interpStart.length;\n const exprEnd = this._getInterpolationEndIndex(input, interpEnd, exprStart);\n if (exprEnd === -1) {\n // Could not find the end of the interpolation; do not parse an expression.\n // Instead we should extend the content on the last raw string.\n atInterpolation = false;\n extendLastString = true;\n break;\n }\n const fullEnd = exprEnd + interpEnd.length;\n const text = input.substring(exprStart, exprEnd);\n if (text.trim().length === 0) {\n this._reportError('Blank expressions are not allowed in interpolated strings', input, `at column ${i} in`, location);\n }\n expressions.push({\n text,\n start: fullStart,\n end: fullEnd\n });\n const startInOriginalTemplate = inputToTemplateIndexMap?.get(fullStart) ?? fullStart;\n const offset = startInOriginalTemplate + interpStart.length;\n offsets.push(offset);\n i = fullEnd;\n atInterpolation = false;\n }\n }\n if (!atInterpolation) {\n // If we are now at a text section, add the remaining content as a raw string.\n if (extendLastString) {\n const piece = strings[strings.length - 1];\n piece.text += input.substring(i);\n piece.end = input.length;\n } else {\n strings.push({\n text: input.substring(i),\n start: i,\n end: input.length\n });\n }\n }\n return new SplitInterpolation(strings, expressions, offsets);\n }\n wrapLiteralPrimitive(input, location, absoluteOffset) {\n const span = new ParseSpan(0, input == null ? 0 : input.length);\n return new ASTWithSource(new LiteralPrimitive(span, span.toAbsolute(absoluteOffset), input), input, location, absoluteOffset, this.errors);\n }\n _stripComments(input) {\n const i = this._commentStart(input);\n return i != null ? input.substring(0, i) : input;\n }\n _commentStart(input) {\n let outerQuote = null;\n for (let i = 0; i < input.length - 1; i++) {\n const char = input.charCodeAt(i);\n const nextChar = input.charCodeAt(i + 1);\n if (char === $SLASH && nextChar == $SLASH && outerQuote == null) return i;\n if (outerQuote === char) {\n outerQuote = null;\n } else if (outerQuote == null && isQuote(char)) {\n outerQuote = char;\n }\n }\n return null;\n }\n _checkNoInterpolation(input, location, {\n start,\n end\n }) {\n let startIndex = -1;\n let endIndex = -1;\n for (const charIndex of this._forEachUnquotedChar(input, 0)) {\n if (startIndex === -1) {\n if (input.startsWith(start)) {\n startIndex = charIndex;\n }\n } else {\n endIndex = this._getInterpolationEndIndex(input, end, charIndex);\n if (endIndex > -1) {\n break;\n }\n }\n }\n if (startIndex > -1 && endIndex > -1) {\n this._reportError(`Got interpolation (${start}${end}) where expression was expected`, input, `at column ${startIndex} in`, location);\n }\n }\n /**\n * Finds the index of the end of an interpolation expression\n * while ignoring comments and quoted content.\n */\n _getInterpolationEndIndex(input, expressionEnd, start) {\n for (const charIndex of this._forEachUnquotedChar(input, start)) {\n if (input.startsWith(expressionEnd, charIndex)) {\n return charIndex;\n }\n // Nothing else in the expression matters after we've\n // hit a comment so look directly for the end token.\n if (input.startsWith('//', charIndex)) {\n return input.indexOf(expressionEnd, charIndex);\n }\n }\n return -1;\n }\n /**\n * Generator used to iterate over the character indexes of a string that are outside of quotes.\n * @param input String to loop through.\n * @param start Index within the string at which to start.\n */\n *_forEachUnquotedChar(input, start) {\n let currentQuote = null;\n let escapeCount = 0;\n for (let i = start; i < input.length; i++) {\n const char = input[i];\n // Skip the characters inside quotes. Note that we only care about the outer-most\n // quotes matching up and we need to account for escape characters.\n if (isQuote(input.charCodeAt(i)) && (currentQuote === null || currentQuote === char) && escapeCount % 2 === 0) {\n currentQuote = currentQuote === null ? char : null;\n } else if (currentQuote === null) {\n yield i;\n }\n escapeCount = char === '\\\\' ? escapeCount + 1 : 0;\n }\n }\n}\n/** Describes a stateful context an expression parser is in. */\nvar ParseContextFlags;\n(function (ParseContextFlags) {\n ParseContextFlags[ParseContextFlags[\"None\"] = 0] = \"None\";\n /**\n * A Writable context is one in which a value may be written to an lvalue.\n * For example, after we see a property access, we may expect a write to the\n * property via the \"=\" operator.\n * prop\n * ^ possible \"=\" after\n */\n ParseContextFlags[ParseContextFlags[\"Writable\"] = 1] = \"Writable\";\n})(ParseContextFlags || (ParseContextFlags = {}));\nclass _ParseAST {\n constructor(input, location, absoluteOffset, tokens, parseFlags, errors, offset) {\n this.input = input;\n this.location = location;\n this.absoluteOffset = absoluteOffset;\n this.tokens = tokens;\n this.parseFlags = parseFlags;\n this.errors = errors;\n this.offset = offset;\n this.rparensExpected = 0;\n this.rbracketsExpected = 0;\n this.rbracesExpected = 0;\n this.context = ParseContextFlags.None;\n // Cache of expression start and input indeces to the absolute source span they map to, used to\n // prevent creating superfluous source spans in `sourceSpan`.\n // A serial of the expression start and input index is used for mapping because both are stateful\n // and may change for subsequent expressions visited by the parser.\n this.sourceSpanCache = new Map();\n this.index = 0;\n }\n peek(offset) {\n const i = this.index + offset;\n return i < this.tokens.length ? this.tokens[i] : EOF;\n }\n get next() {\n return this.peek(0);\n }\n /** Whether all the parser input has been processed. */\n get atEOF() {\n return this.index >= this.tokens.length;\n }\n /**\n * Index of the next token to be processed, or the end of the last token if all have been\n * processed.\n */\n get inputIndex() {\n return this.atEOF ? this.currentEndIndex : this.next.index + this.offset;\n }\n /**\n * End index of the last processed token, or the start of the first token if none have been\n * processed.\n */\n get currentEndIndex() {\n if (this.index > 0) {\n const curToken = this.peek(-1);\n return curToken.end + this.offset;\n }\n // No tokens have been processed yet; return the next token's start or the length of the input\n // if there is no token.\n if (this.tokens.length === 0) {\n return this.input.length + this.offset;\n }\n return this.next.index + this.offset;\n }\n /**\n * Returns the absolute offset of the start of the current token.\n */\n get currentAbsoluteOffset() {\n return this.absoluteOffset + this.inputIndex;\n }\n /**\n * Retrieve a `ParseSpan` from `start` to the current position (or to `artificialEndIndex` if\n * provided).\n *\n * @param start Position from which the `ParseSpan` will start.\n * @param artificialEndIndex Optional ending index to be used if provided (and if greater than the\n * natural ending index)\n */\n span(start, artificialEndIndex) {\n let endIndex = this.currentEndIndex;\n if (artificialEndIndex !== undefined && artificialEndIndex > this.currentEndIndex) {\n endIndex = artificialEndIndex;\n }\n // In some unusual parsing scenarios (like when certain tokens are missing and an `EmptyExpr` is\n // being created), the current token may already be advanced beyond the `currentEndIndex`. This\n // appears to be a deep-seated parser bug.\n //\n // As a workaround for now, swap the start and end indices to ensure a valid `ParseSpan`.\n // TODO(alxhub): fix the bug upstream in the parser state, and remove this workaround.\n if (start > endIndex) {\n const tmp = endIndex;\n endIndex = start;\n start = tmp;\n }\n return new ParseSpan(start, endIndex);\n }\n sourceSpan(start, artificialEndIndex) {\n const serial = `${start}@${this.inputIndex}:${artificialEndIndex}`;\n if (!this.sourceSpanCache.has(serial)) {\n this.sourceSpanCache.set(serial, this.span(start, artificialEndIndex).toAbsolute(this.absoluteOffset));\n }\n return this.sourceSpanCache.get(serial);\n }\n advance() {\n this.index++;\n }\n /**\n * Executes a callback in the provided context.\n */\n withContext(context, cb) {\n this.context |= context;\n const ret = cb();\n this.context ^= context;\n return ret;\n }\n consumeOptionalCharacter(code) {\n if (this.next.isCharacter(code)) {\n this.advance();\n return true;\n } else {\n return false;\n }\n }\n peekKeywordLet() {\n return this.next.isKeywordLet();\n }\n peekKeywordAs() {\n return this.next.isKeywordAs();\n }\n /**\n * Consumes an expected character, otherwise emits an error about the missing expected character\n * and skips over the token stream until reaching a recoverable point.\n *\n * See `this.error` and `this.skip` for more details.\n */\n expectCharacter(code) {\n if (this.consumeOptionalCharacter(code)) return;\n this.error(`Missing expected ${String.fromCharCode(code)}`);\n }\n consumeOptionalOperator(op) {\n if (this.next.isOperator(op)) {\n this.advance();\n return true;\n } else {\n return false;\n }\n }\n expectOperator(operator) {\n if (this.consumeOptionalOperator(operator)) return;\n this.error(`Missing expected operator ${operator}`);\n }\n prettyPrintToken(tok) {\n return tok === EOF ? 'end of input' : `token ${tok}`;\n }\n expectIdentifierOrKeyword() {\n const n = this.next;\n if (!n.isIdentifier() && !n.isKeyword()) {\n if (n.isPrivateIdentifier()) {\n this._reportErrorForPrivateIdentifier(n, 'expected identifier or keyword');\n } else {\n this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier or keyword`);\n }\n return null;\n }\n this.advance();\n return n.toString();\n }\n expectIdentifierOrKeywordOrString() {\n const n = this.next;\n if (!n.isIdentifier() && !n.isKeyword() && !n.isString()) {\n if (n.isPrivateIdentifier()) {\n this._reportErrorForPrivateIdentifier(n, 'expected identifier, keyword or string');\n } else {\n this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier, keyword, or string`);\n }\n return '';\n }\n this.advance();\n return n.toString();\n }\n parseChain() {\n const exprs = [];\n const start = this.inputIndex;\n while (this.index < this.tokens.length) {\n const expr = this.parsePipe();\n exprs.push(expr);\n if (this.consumeOptionalCharacter($SEMICOLON)) {\n if (!(this.parseFlags & 1 /* ParseFlags.Action */)) {\n this.error('Binding expression cannot contain chained expression');\n }\n while (this.consumeOptionalCharacter($SEMICOLON)) {} // read all semicolons\n } else if (this.index < this.tokens.length) {\n const errorIndex = this.index;\n this.error(`Unexpected token '${this.next}'`);\n // The `error` call above will skip ahead to the next recovery point in an attempt to\n // recover part of the expression, but that might be the token we started from which will\n // lead to an infinite loop. If that's the case, break the loop assuming that we can't\n // parse further.\n if (this.index === errorIndex) {\n break;\n }\n }\n }\n if (exprs.length === 0) {\n // We have no expressions so create an empty expression that spans the entire input length\n const artificialStart = this.offset;\n const artificialEnd = this.offset + this.input.length;\n return new EmptyExpr$1(this.span(artificialStart, artificialEnd), this.sourceSpan(artificialStart, artificialEnd));\n }\n if (exprs.length == 1) return exprs[0];\n return new Chain(this.span(start), this.sourceSpan(start), exprs);\n }\n parsePipe() {\n const start = this.inputIndex;\n let result = this.parseExpression();\n if (this.consumeOptionalOperator('|')) {\n if (this.parseFlags & 1 /* ParseFlags.Action */) {\n this.error('Cannot have a pipe in an action expression');\n }\n do {\n const nameStart = this.inputIndex;\n let nameId = this.expectIdentifierOrKeyword();\n let nameSpan;\n let fullSpanEnd = undefined;\n if (nameId !== null) {\n nameSpan = this.sourceSpan(nameStart);\n } else {\n // No valid identifier was found, so we'll assume an empty pipe name ('').\n nameId = '';\n // However, there may have been whitespace present between the pipe character and the next\n // token in the sequence (or the end of input). We want to track this whitespace so that\n // the `BindingPipe` we produce covers not just the pipe character, but any trailing\n // whitespace beyond it. Another way of thinking about this is that the zero-length name\n // is assumed to be at the end of any whitespace beyond the pipe character.\n //\n // Therefore, we push the end of the `ParseSpan` for this pipe all the way up to the\n // beginning of the next token, or until the end of input if the next token is EOF.\n fullSpanEnd = this.next.index !== -1 ? this.next.index : this.input.length + this.offset;\n // The `nameSpan` for an empty pipe name is zero-length at the end of any whitespace\n // beyond the pipe character.\n nameSpan = new ParseSpan(fullSpanEnd, fullSpanEnd).toAbsolute(this.absoluteOffset);\n }\n const args = [];\n while (this.consumeOptionalCharacter($COLON)) {\n args.push(this.parseExpression());\n // If there are additional expressions beyond the name, then the artificial end for the\n // name is no longer relevant.\n }\n result = new BindingPipe(this.span(start), this.sourceSpan(start, fullSpanEnd), result, nameId, args, nameSpan);\n } while (this.consumeOptionalOperator('|'));\n }\n return result;\n }\n parseExpression() {\n return this.parseConditional();\n }\n parseConditional() {\n const start = this.inputIndex;\n const result = this.parseLogicalOr();\n if (this.consumeOptionalOperator('?')) {\n const yes = this.parsePipe();\n let no;\n if (!this.consumeOptionalCharacter($COLON)) {\n const end = this.inputIndex;\n const expression = this.input.substring(start, end);\n this.error(`Conditional expression ${expression} requires all 3 expressions`);\n no = new EmptyExpr$1(this.span(start), this.sourceSpan(start));\n } else {\n no = this.parsePipe();\n }\n return new Conditional(this.span(start), this.sourceSpan(start), result, yes, no);\n } else {\n return result;\n }\n }\n parseLogicalOr() {\n // '||'\n const start = this.inputIndex;\n let result = this.parseLogicalAnd();\n while (this.consumeOptionalOperator('||')) {\n const right = this.parseLogicalAnd();\n result = new Binary(this.span(start), this.sourceSpan(start), '||', result, right);\n }\n return result;\n }\n parseLogicalAnd() {\n // '&&'\n const start = this.inputIndex;\n let result = this.parseNullishCoalescing();\n while (this.consumeOptionalOperator('&&')) {\n const right = this.parseNullishCoalescing();\n result = new Binary(this.span(start), this.sourceSpan(start), '&&', result, right);\n }\n return result;\n }\n parseNullishCoalescing() {\n // '??'\n const start = this.inputIndex;\n let result = this.parseEquality();\n while (this.consumeOptionalOperator('??')) {\n const right = this.parseEquality();\n result = new Binary(this.span(start), this.sourceSpan(start), '??', result, right);\n }\n return result;\n }\n parseEquality() {\n // '==','!=','===','!=='\n const start = this.inputIndex;\n let result = this.parseRelational();\n while (this.next.type == TokenType.Operator) {\n const operator = this.next.strValue;\n switch (operator) {\n case '==':\n case '===':\n case '!=':\n case '!==':\n this.advance();\n const right = this.parseRelational();\n result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);\n continue;\n }\n break;\n }\n return result;\n }\n parseRelational() {\n // '<', '>', '<=', '>='\n const start = this.inputIndex;\n let result = this.parseAdditive();\n while (this.next.type == TokenType.Operator) {\n const operator = this.next.strValue;\n switch (operator) {\n case '<':\n case '>':\n case '<=':\n case '>=':\n this.advance();\n const right = this.parseAdditive();\n result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);\n continue;\n }\n break;\n }\n return result;\n }\n parseAdditive() {\n // '+', '-'\n const start = this.inputIndex;\n let result = this.parseMultiplicative();\n while (this.next.type == TokenType.Operator) {\n const operator = this.next.strValue;\n switch (operator) {\n case '+':\n case '-':\n this.advance();\n let right = this.parseMultiplicative();\n result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);\n continue;\n }\n break;\n }\n return result;\n }\n parseMultiplicative() {\n // '*', '%', '/'\n const start = this.inputIndex;\n let result = this.parsePrefix();\n while (this.next.type == TokenType.Operator) {\n const operator = this.next.strValue;\n switch (operator) {\n case '*':\n case '%':\n case '/':\n this.advance();\n let right = this.parsePrefix();\n result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);\n continue;\n }\n break;\n }\n return result;\n }\n parsePrefix() {\n if (this.next.type == TokenType.Operator) {\n const start = this.inputIndex;\n const operator = this.next.strValue;\n let result;\n switch (operator) {\n case '+':\n this.advance();\n result = this.parsePrefix();\n return Unary.createPlus(this.span(start), this.sourceSpan(start), result);\n case '-':\n this.advance();\n result = this.parsePrefix();\n return Unary.createMinus(this.span(start), this.sourceSpan(start), result);\n case '!':\n this.advance();\n result = this.parsePrefix();\n return new PrefixNot(this.span(start), this.sourceSpan(start), result);\n }\n }\n return this.parseCallChain();\n }\n parseCallChain() {\n const start = this.inputIndex;\n let result = this.parsePrimary();\n while (true) {\n if (this.consumeOptionalCharacter($PERIOD)) {\n result = this.parseAccessMember(result, start, false);\n } else if (this.consumeOptionalOperator('?.')) {\n if (this.consumeOptionalCharacter($LPAREN)) {\n result = this.parseCall(result, start, true);\n } else {\n result = this.consumeOptionalCharacter($LBRACKET) ? this.parseKeyedReadOrWrite(result, start, true) : this.parseAccessMember(result, start, true);\n }\n } else if (this.consumeOptionalCharacter($LBRACKET)) {\n result = this.parseKeyedReadOrWrite(result, start, false);\n } else if (this.consumeOptionalCharacter($LPAREN)) {\n result = this.parseCall(result, start, false);\n } else if (this.consumeOptionalOperator('!')) {\n result = new NonNullAssert(this.span(start), this.sourceSpan(start), result);\n } else {\n return result;\n }\n }\n }\n parsePrimary() {\n const start = this.inputIndex;\n if (this.consumeOptionalCharacter($LPAREN)) {\n this.rparensExpected++;\n const result = this.parsePipe();\n this.rparensExpected--;\n this.expectCharacter($RPAREN);\n return result;\n } else if (this.next.isKeywordNull()) {\n this.advance();\n return new LiteralPrimitive(this.span(start), this.sourceSpan(start), null);\n } else if (this.next.isKeywordUndefined()) {\n this.advance();\n return new LiteralPrimitive(this.span(start), this.sourceSpan(start), void 0);\n } else if (this.next.isKeywordTrue()) {\n this.advance();\n return new LiteralPrimitive(this.span(start), this.sourceSpan(start), true);\n } else if (this.next.isKeywordFalse()) {\n this.advance();\n return new LiteralPrimitive(this.span(start), this.sourceSpan(start), false);\n } else if (this.next.isKeywordThis()) {\n this.advance();\n return new ThisReceiver(this.span(start), this.sourceSpan(start));\n } else if (this.consumeOptionalCharacter($LBRACKET)) {\n this.rbracketsExpected++;\n const elements = this.parseExpressionList($RBRACKET);\n this.rbracketsExpected--;\n this.expectCharacter($RBRACKET);\n return new LiteralArray(this.span(start), this.sourceSpan(start), elements);\n } else if (this.next.isCharacter($LBRACE)) {\n return this.parseLiteralMap();\n } else if (this.next.isIdentifier()) {\n return this.parseAccessMember(new ImplicitReceiver(this.span(start), this.sourceSpan(start)), start, false);\n } else if (this.next.isNumber()) {\n const value = this.next.toNumber();\n this.advance();\n return new LiteralPrimitive(this.span(start), this.sourceSpan(start), value);\n } else if (this.next.isString()) {\n const literalValue = this.next.toString();\n this.advance();\n return new LiteralPrimitive(this.span(start), this.sourceSpan(start), literalValue);\n } else if (this.next.isPrivateIdentifier()) {\n this._reportErrorForPrivateIdentifier(this.next, null);\n return new EmptyExpr$1(this.span(start), this.sourceSpan(start));\n } else if (this.index >= this.tokens.length) {\n this.error(`Unexpected end of expression: ${this.input}`);\n return new EmptyExpr$1(this.span(start), this.sourceSpan(start));\n } else {\n this.error(`Unexpected token ${this.next}`);\n return new EmptyExpr$1(this.span(start), this.sourceSpan(start));\n }\n }\n parseExpressionList(terminator) {\n const result = [];\n do {\n if (!this.next.isCharacter(terminator)) {\n result.push(this.parsePipe());\n } else {\n break;\n }\n } while (this.consumeOptionalCharacter($COMMA));\n return result;\n }\n parseLiteralMap() {\n const keys = [];\n const values = [];\n const start = this.inputIndex;\n this.expectCharacter($LBRACE);\n if (!this.consumeOptionalCharacter($RBRACE)) {\n this.rbracesExpected++;\n do {\n const keyStart = this.inputIndex;\n const quoted = this.next.isString();\n const key = this.expectIdentifierOrKeywordOrString();\n keys.push({\n key,\n quoted\n });\n // Properties with quoted keys can't use the shorthand syntax.\n if (quoted) {\n this.expectCharacter($COLON);\n values.push(this.parsePipe());\n } else if (this.consumeOptionalCharacter($COLON)) {\n values.push(this.parsePipe());\n } else {\n const span = this.span(keyStart);\n const sourceSpan = this.sourceSpan(keyStart);\n values.push(new PropertyRead(span, sourceSpan, sourceSpan, new ImplicitReceiver(span, sourceSpan), key));\n }\n } while (this.consumeOptionalCharacter($COMMA) && !this.next.isCharacter($RBRACE));\n this.rbracesExpected--;\n this.expectCharacter($RBRACE);\n }\n return new LiteralMap(this.span(start), this.sourceSpan(start), keys, values);\n }\n parseAccessMember(readReceiver, start, isSafe) {\n const nameStart = this.inputIndex;\n const id = this.withContext(ParseContextFlags.Writable, () => {\n const id = this.expectIdentifierOrKeyword() ?? '';\n if (id.length === 0) {\n this.error(`Expected identifier for property access`, readReceiver.span.end);\n }\n return id;\n });\n const nameSpan = this.sourceSpan(nameStart);\n let receiver;\n if (isSafe) {\n if (this.consumeOptionalAssignment()) {\n this.error('The \\'?.\\' operator cannot be used in the assignment');\n receiver = new EmptyExpr$1(this.span(start), this.sourceSpan(start));\n } else {\n receiver = new SafePropertyRead(this.span(start), this.sourceSpan(start), nameSpan, readReceiver, id);\n }\n } else {\n if (this.consumeOptionalAssignment()) {\n if (!(this.parseFlags & 1 /* ParseFlags.Action */)) {\n this.error('Bindings cannot contain assignments');\n return new EmptyExpr$1(this.span(start), this.sourceSpan(start));\n }\n const value = this.parseConditional();\n receiver = new PropertyWrite(this.span(start), this.sourceSpan(start), nameSpan, readReceiver, id, value);\n } else {\n receiver = new PropertyRead(this.span(start), this.sourceSpan(start), nameSpan, readReceiver, id);\n }\n }\n return receiver;\n }\n parseCall(receiver, start, isSafe) {\n const argumentStart = this.inputIndex;\n this.rparensExpected++;\n const args = this.parseCallArguments();\n const argumentSpan = this.span(argumentStart, this.inputIndex).toAbsolute(this.absoluteOffset);\n this.expectCharacter($RPAREN);\n this.rparensExpected--;\n const span = this.span(start);\n const sourceSpan = this.sourceSpan(start);\n return isSafe ? new SafeCall(span, sourceSpan, receiver, args, argumentSpan) : new Call(span, sourceSpan, receiver, args, argumentSpan);\n }\n consumeOptionalAssignment() {\n // When parsing assignment events (originating from two-way-binding aka banana-in-a-box syntax),\n // it is valid for the primary expression to be terminated by the non-null operator. This\n // primary expression is substituted as LHS of the assignment operator to achieve\n // two-way-binding, such that the LHS could be the non-null operator. The grammar doesn't\n // naturally allow for this syntax, so assignment events are parsed specially.\n if (this.parseFlags & 2 /* ParseFlags.AssignmentEvent */ && this.next.isOperator('!') && this.peek(1).isOperator('=')) {\n // First skip over the ! operator.\n this.advance();\n // Then skip over the = operator, to fully consume the optional assignment operator.\n this.advance();\n return true;\n }\n return this.consumeOptionalOperator('=');\n }\n parseCallArguments() {\n if (this.next.isCharacter($RPAREN)) return [];\n const positionals = [];\n do {\n positionals.push(this.parsePipe());\n } while (this.consumeOptionalCharacter($COMMA));\n return positionals;\n }\n /**\n * Parses an identifier, a keyword, a string with an optional `-` in between,\n * and returns the string along with its absolute source span.\n */\n expectTemplateBindingKey() {\n let result = '';\n let operatorFound = false;\n const start = this.currentAbsoluteOffset;\n do {\n result += this.expectIdentifierOrKeywordOrString();\n operatorFound = this.consumeOptionalOperator('-');\n if (operatorFound) {\n result += '-';\n }\n } while (operatorFound);\n return {\n source: result,\n span: new AbsoluteSourceSpan(start, start + result.length)\n };\n }\n /**\n * Parse microsyntax template expression and return a list of bindings or\n * parsing errors in case the given expression is invalid.\n *\n * For example,\n * ```\n * \n * ```\n * contains five bindings:\n * 1. ngFor -> null\n * 2. item -> NgForOfContext.$implicit\n * 3. ngForOf -> items\n * 4. i -> NgForOfContext.index\n * 5. ngForTrackBy -> func\n *\n * For a full description of the microsyntax grammar, see\n * https://gist.github.com/mhevery/d3530294cff2e4a1b3fe15ff75d08855\n *\n * @param templateKey name of the microsyntax directive, like ngIf, ngFor,\n * without the *, along with its absolute span.\n */\n parseTemplateBindings(templateKey) {\n const bindings = [];\n // The first binding is for the template key itself\n // In *ngFor=\"let item of items\", key = \"ngFor\", value = null\n // In *ngIf=\"cond | pipe\", key = \"ngIf\", value = \"cond | pipe\"\n bindings.push(...this.parseDirectiveKeywordBindings(templateKey));\n while (this.index < this.tokens.length) {\n // If it starts with 'let', then this must be variable declaration\n const letBinding = this.parseLetBinding();\n if (letBinding) {\n bindings.push(letBinding);\n } else {\n // Two possible cases here, either `value \"as\" key` or\n // \"directive-keyword expression\". We don't know which case, but both\n // \"value\" and \"directive-keyword\" are template binding key, so consume\n // the key first.\n const key = this.expectTemplateBindingKey();\n // Peek at the next token, if it is \"as\" then this must be variable\n // declaration.\n const binding = this.parseAsBinding(key);\n if (binding) {\n bindings.push(binding);\n } else {\n // Otherwise the key must be a directive keyword, like \"of\". Transform\n // the key to actual key. Eg. of -> ngForOf, trackBy -> ngForTrackBy\n key.source = templateKey.source + key.source.charAt(0).toUpperCase() + key.source.substring(1);\n bindings.push(...this.parseDirectiveKeywordBindings(key));\n }\n }\n this.consumeStatementTerminator();\n }\n return new TemplateBindingParseResult(bindings, [] /* warnings */, this.errors);\n }\n parseKeyedReadOrWrite(receiver, start, isSafe) {\n return this.withContext(ParseContextFlags.Writable, () => {\n this.rbracketsExpected++;\n const key = this.parsePipe();\n if (key instanceof EmptyExpr$1) {\n this.error(`Key access cannot be empty`);\n }\n this.rbracketsExpected--;\n this.expectCharacter($RBRACKET);\n if (this.consumeOptionalOperator('=')) {\n if (isSafe) {\n this.error('The \\'?.\\' operator cannot be used in the assignment');\n } else {\n const value = this.parseConditional();\n return new KeyedWrite(this.span(start), this.sourceSpan(start), receiver, key, value);\n }\n } else {\n return isSafe ? new SafeKeyedRead(this.span(start), this.sourceSpan(start), receiver, key) : new KeyedRead(this.span(start), this.sourceSpan(start), receiver, key);\n }\n return new EmptyExpr$1(this.span(start), this.sourceSpan(start));\n });\n }\n /**\n * Parse a directive keyword, followed by a mandatory expression.\n * For example, \"of items\", \"trackBy: func\".\n * The bindings are: ngForOf -> items, ngForTrackBy -> func\n * There could be an optional \"as\" binding that follows the expression.\n * For example,\n * ```\n * *ngFor=\"let item of items | slice:0:1 as collection\".\n * ^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^\n * keyword bound target optional 'as' binding\n * ```\n *\n * @param key binding key, for example, ngFor, ngIf, ngForOf, along with its\n * absolute span.\n */\n parseDirectiveKeywordBindings(key) {\n const bindings = [];\n this.consumeOptionalCharacter($COLON); // trackBy: trackByFunction\n const value = this.getDirectiveBoundTarget();\n let spanEnd = this.currentAbsoluteOffset;\n // The binding could optionally be followed by \"as\". For example,\n // *ngIf=\"cond | pipe as x\". In this case, the key in the \"as\" binding\n // is \"x\" and the value is the template key itself (\"ngIf\"). Note that the\n // 'key' in the current context now becomes the \"value\" in the next binding.\n const asBinding = this.parseAsBinding(key);\n if (!asBinding) {\n this.consumeStatementTerminator();\n spanEnd = this.currentAbsoluteOffset;\n }\n const sourceSpan = new AbsoluteSourceSpan(key.span.start, spanEnd);\n bindings.push(new ExpressionBinding(sourceSpan, key, value));\n if (asBinding) {\n bindings.push(asBinding);\n }\n return bindings;\n }\n /**\n * Return the expression AST for the bound target of a directive keyword\n * binding. For example,\n * ```\n * *ngIf=\"condition | pipe\"\n * ^^^^^^^^^^^^^^^^ bound target for \"ngIf\"\n * *ngFor=\"let item of items\"\n * ^^^^^ bound target for \"ngForOf\"\n * ```\n */\n getDirectiveBoundTarget() {\n if (this.next === EOF || this.peekKeywordAs() || this.peekKeywordLet()) {\n return null;\n }\n const ast = this.parsePipe(); // example: \"condition | async\"\n const {\n start,\n end\n } = ast.span;\n const value = this.input.substring(start, end);\n return new ASTWithSource(ast, value, this.location, this.absoluteOffset + start, this.errors);\n }\n /**\n * Return the binding for a variable declared using `as`. Note that the order\n * of the key-value pair in this declaration is reversed. For example,\n * ```\n * *ngFor=\"let item of items; index as i\"\n * ^^^^^ ^\n * value key\n * ```\n *\n * @param value name of the value in the declaration, \"ngIf\" in the example\n * above, along with its absolute span.\n */\n parseAsBinding(value) {\n if (!this.peekKeywordAs()) {\n return null;\n }\n this.advance(); // consume the 'as' keyword\n const key = this.expectTemplateBindingKey();\n this.consumeStatementTerminator();\n const sourceSpan = new AbsoluteSourceSpan(value.span.start, this.currentAbsoluteOffset);\n return new VariableBinding(sourceSpan, key, value);\n }\n /**\n * Return the binding for a variable declared using `let`. For example,\n * ```\n * *ngFor=\"let item of items; let i=index;\"\n * ^^^^^^^^ ^^^^^^^^^^^\n * ```\n * In the first binding, `item` is bound to `NgForOfContext.$implicit`.\n * In the second binding, `i` is bound to `NgForOfContext.index`.\n */\n parseLetBinding() {\n if (!this.peekKeywordLet()) {\n return null;\n }\n const spanStart = this.currentAbsoluteOffset;\n this.advance(); // consume the 'let' keyword\n const key = this.expectTemplateBindingKey();\n let value = null;\n if (this.consumeOptionalOperator('=')) {\n value = this.expectTemplateBindingKey();\n }\n this.consumeStatementTerminator();\n const sourceSpan = new AbsoluteSourceSpan(spanStart, this.currentAbsoluteOffset);\n return new VariableBinding(sourceSpan, key, value);\n }\n /**\n * Consume the optional statement terminator: semicolon or comma.\n */\n consumeStatementTerminator() {\n this.consumeOptionalCharacter($SEMICOLON) || this.consumeOptionalCharacter($COMMA);\n }\n /**\n * Records an error and skips over the token stream until reaching a recoverable point. See\n * `this.skip` for more details on token skipping.\n */\n error(message, index = null) {\n this.errors.push(new ParserError(message, this.input, this.locationText(index), this.location));\n this.skip();\n }\n locationText(index = null) {\n if (index == null) index = this.index;\n return index < this.tokens.length ? `at column ${this.tokens[index].index + 1} in` : `at the end of the expression`;\n }\n /**\n * Records an error for an unexpected private identifier being discovered.\n * @param token Token representing a private identifier.\n * @param extraMessage Optional additional message being appended to the error.\n */\n _reportErrorForPrivateIdentifier(token, extraMessage) {\n let errorMessage = `Private identifiers are not supported. Unexpected private identifier: ${token}`;\n if (extraMessage !== null) {\n errorMessage += `, ${extraMessage}`;\n }\n this.error(errorMessage);\n }\n /**\n * Error recovery should skip tokens until it encounters a recovery point.\n *\n * The following are treated as unconditional recovery points:\n * - end of input\n * - ';' (parseChain() is always the root production, and it expects a ';')\n * - '|' (since pipes may be chained and each pipe expression may be treated independently)\n *\n * The following are conditional recovery points:\n * - ')', '}', ']' if one of calling productions is expecting one of these symbols\n * - This allows skip() to recover from errors such as '(a.) + 1' allowing more of the AST to\n * be retained (it doesn't skip any tokens as the ')' is retained because of the '(' begins\n * an '('
')' production).\n * The recovery points of grouping symbols must be conditional as they must be skipped if\n * none of the calling productions are not expecting the closing token else we will never\n * make progress in the case of an extraneous group closing symbol (such as a stray ')').\n * That is, we skip a closing symbol if we are not in a grouping production.\n * - '=' in a `Writable` context\n * - In this context, we are able to recover after seeing the `=` operator, which\n * signals the presence of an independent rvalue expression following the `=` operator.\n *\n * If a production expects one of these token it increments the corresponding nesting count,\n * and then decrements it just prior to checking if the token is in the input.\n */\n skip() {\n let n = this.next;\n while (this.index < this.tokens.length && !n.isCharacter($SEMICOLON) && !n.isOperator('|') && (this.rparensExpected <= 0 || !n.isCharacter($RPAREN)) && (this.rbracesExpected <= 0 || !n.isCharacter($RBRACE)) && (this.rbracketsExpected <= 0 || !n.isCharacter($RBRACKET)) && (!(this.context & ParseContextFlags.Writable) || !n.isOperator('='))) {\n if (this.next.isError()) {\n this.errors.push(new ParserError(this.next.toString(), this.input, this.locationText(), this.location));\n }\n this.advance();\n n = this.next;\n }\n }\n}\nclass SimpleExpressionChecker extends RecursiveAstVisitor {\n constructor() {\n super(...arguments);\n this.errors = [];\n }\n visitPipe() {\n this.errors.push('pipes');\n }\n}\n/**\n * Computes the real offset in the original template for indexes in an interpolation.\n *\n * Because templates can have encoded HTML entities and the input passed to the parser at this stage\n * of the compiler is the _decoded_ value, we need to compute the real offset using the original\n * encoded values in the interpolated tokens. Note that this is only a special case handling for\n * `MlParserTokenType.ENCODED_ENTITY` token types. All other interpolated tokens are expected to\n * have parts which exactly match the input string for parsing the interpolation.\n *\n * @param interpolatedTokens The tokens for the interpolated value.\n *\n * @returns A map of index locations in the decoded template to indexes in the original template\n */\nfunction getIndexMapForOriginalTemplate(interpolatedTokens) {\n let offsetMap = new Map();\n let consumedInOriginalTemplate = 0;\n let consumedInInput = 0;\n let tokenIndex = 0;\n while (tokenIndex < interpolatedTokens.length) {\n const currentToken = interpolatedTokens[tokenIndex];\n if (currentToken.type === 9 /* MlParserTokenType.ENCODED_ENTITY */) {\n const [decoded, encoded] = currentToken.parts;\n consumedInOriginalTemplate += encoded.length;\n consumedInInput += decoded.length;\n } else {\n const lengthOfParts = currentToken.parts.reduce((sum, current) => sum + current.length, 0);\n consumedInInput += lengthOfParts;\n consumedInOriginalTemplate += lengthOfParts;\n }\n offsetMap.set(consumedInInput, consumedInOriginalTemplate);\n tokenIndex++;\n }\n return offsetMap;\n}\nclass NodeWithI18n {\n constructor(sourceSpan, i18n) {\n this.sourceSpan = sourceSpan;\n this.i18n = i18n;\n }\n}\nclass Text extends NodeWithI18n {\n constructor(value, sourceSpan, tokens, i18n) {\n super(sourceSpan, i18n);\n this.value = value;\n this.tokens = tokens;\n }\n visit(visitor, context) {\n return visitor.visitText(this, context);\n }\n}\nclass Expansion extends NodeWithI18n {\n constructor(switchValue, type, cases, sourceSpan, switchValueSourceSpan, i18n) {\n super(sourceSpan, i18n);\n this.switchValue = switchValue;\n this.type = type;\n this.cases = cases;\n this.switchValueSourceSpan = switchValueSourceSpan;\n }\n visit(visitor, context) {\n return visitor.visitExpansion(this, context);\n }\n}\nclass ExpansionCase {\n constructor(value, expression, sourceSpan, valueSourceSpan, expSourceSpan) {\n this.value = value;\n this.expression = expression;\n this.sourceSpan = sourceSpan;\n this.valueSourceSpan = valueSourceSpan;\n this.expSourceSpan = expSourceSpan;\n }\n visit(visitor, context) {\n return visitor.visitExpansionCase(this, context);\n }\n}\nclass Attribute extends NodeWithI18n {\n constructor(name, value, sourceSpan, keySpan, valueSpan, valueTokens, i18n) {\n super(sourceSpan, i18n);\n this.name = name;\n this.value = value;\n this.keySpan = keySpan;\n this.valueSpan = valueSpan;\n this.valueTokens = valueTokens;\n }\n visit(visitor, context) {\n return visitor.visitAttribute(this, context);\n }\n}\nclass Element extends NodeWithI18n {\n constructor(name, attrs, children, sourceSpan, startSourceSpan, endSourceSpan = null, i18n) {\n super(sourceSpan, i18n);\n this.name = name;\n this.attrs = attrs;\n this.children = children;\n this.startSourceSpan = startSourceSpan;\n this.endSourceSpan = endSourceSpan;\n }\n visit(visitor, context) {\n return visitor.visitElement(this, context);\n }\n}\nclass Comment {\n constructor(value, sourceSpan) {\n this.value = value;\n this.sourceSpan = sourceSpan;\n }\n visit(visitor, context) {\n return visitor.visitComment(this, context);\n }\n}\nclass BlockGroup {\n constructor(blocks, sourceSpan, startSourceSpan, endSourceSpan = null) {\n this.blocks = blocks;\n this.sourceSpan = sourceSpan;\n this.startSourceSpan = startSourceSpan;\n this.endSourceSpan = endSourceSpan;\n }\n visit(visitor, context) {\n return visitor.visitBlockGroup(this, context);\n }\n}\nclass Block {\n constructor(name, parameters, children, sourceSpan, startSourceSpan, endSourceSpan = null) {\n this.name = name;\n this.parameters = parameters;\n this.children = children;\n this.sourceSpan = sourceSpan;\n this.startSourceSpan = startSourceSpan;\n this.endSourceSpan = endSourceSpan;\n }\n visit(visitor, context) {\n return visitor.visitBlock(this, context);\n }\n}\nclass BlockParameter {\n constructor(expression, sourceSpan) {\n this.expression = expression;\n this.sourceSpan = sourceSpan;\n }\n visit(visitor, context) {\n return visitor.visitBlockParameter(this, context);\n }\n}\nfunction visitAll(visitor, nodes, context = null) {\n const result = [];\n const visit = visitor.visit ? ast => visitor.visit(ast, context) || ast.visit(visitor, context) : ast => ast.visit(visitor, context);\n nodes.forEach(ast => {\n const astResult = visit(ast);\n if (astResult) {\n result.push(astResult);\n }\n });\n return result;\n}\nclass RecursiveVisitor {\n constructor() {}\n visitElement(ast, context) {\n this.visitChildren(context, visit => {\n visit(ast.attrs);\n visit(ast.children);\n });\n }\n visitAttribute(ast, context) {}\n visitText(ast, context) {}\n visitComment(ast, context) {}\n visitExpansion(ast, context) {\n return this.visitChildren(context, visit => {\n visit(ast.cases);\n });\n }\n visitExpansionCase(ast, context) {}\n visitBlockGroup(ast, context) {\n this.visitChildren(context, visit => {\n visit(ast.blocks);\n });\n }\n visitBlock(block, context) {\n this.visitChildren(context, visit => {\n visit(block.parameters);\n visit(block.children);\n });\n }\n visitBlockParameter(ast, context) {}\n visitChildren(context, cb) {\n let results = [];\n let t = this;\n function visit(children) {\n if (children) results.push(visitAll(t, children, context));\n }\n cb(visit);\n return Array.prototype.concat.apply([], results);\n }\n}\nclass ElementSchemaRegistry {}\nconst BOOLEAN = 'boolean';\nconst NUMBER = 'number';\nconst STRING = 'string';\nconst OBJECT = 'object';\n/**\n * This array represents the DOM schema. It encodes inheritance, properties, and events.\n *\n * ## Overview\n *\n * Each line represents one kind of element. The `element_inheritance` and properties are joined\n * using `element_inheritance|properties` syntax.\n *\n * ## Element Inheritance\n *\n * The `element_inheritance` can be further subdivided as `element1,element2,...^parentElement`.\n * Here the individual elements are separated by `,` (commas). Every element in the list\n * has identical properties.\n *\n * An `element` may inherit additional properties from `parentElement` If no `^parentElement` is\n * specified then `\"\"` (blank) element is assumed.\n *\n * NOTE: The blank element inherits from root `[Element]` element, the super element of all\n * elements.\n *\n * NOTE an element prefix such as `:svg:` has no special meaning to the schema.\n *\n * ## Properties\n *\n * Each element has a set of properties separated by `,` (commas). Each property can be prefixed\n * by a special character designating its type:\n *\n * - (no prefix): property is a string.\n * - `*`: property represents an event.\n * - `!`: property is a boolean.\n * - `#`: property is a number.\n * - `%`: property is an object.\n *\n * ## Query\n *\n * The class creates an internal squas representation which allows to easily answer the query of\n * if a given property exist on a given element.\n *\n * NOTE: We don't yet support querying for types or events.\n * NOTE: This schema is auto extracted from `schema_extractor.ts` located in the test folder,\n * see dom_element_schema_registry_spec.ts\n */\n// =================================================================================================\n// =================================================================================================\n// =========== S T O P - S T O P - S T O P - S T O P - S T O P - S T O P ===========\n// =================================================================================================\n// =================================================================================================\n//\n// DO NOT EDIT THIS DOM SCHEMA WITHOUT A SECURITY REVIEW!\n//\n// Newly added properties must be security reviewed and assigned an appropriate SecurityContext in\n// dom_security_schema.ts. Reach out to mprobst & rjamet for details.\n//\n// =================================================================================================\nconst SCHEMA = ['[Element]|textContent,%ariaAtomic,%ariaAutoComplete,%ariaBusy,%ariaChecked,%ariaColCount,%ariaColIndex,%ariaColSpan,%ariaCurrent,%ariaDescription,%ariaDisabled,%ariaExpanded,%ariaHasPopup,%ariaHidden,%ariaKeyShortcuts,%ariaLabel,%ariaLevel,%ariaLive,%ariaModal,%ariaMultiLine,%ariaMultiSelectable,%ariaOrientation,%ariaPlaceholder,%ariaPosInSet,%ariaPressed,%ariaReadOnly,%ariaRelevant,%ariaRequired,%ariaRoleDescription,%ariaRowCount,%ariaRowIndex,%ariaRowSpan,%ariaSelected,%ariaSetSize,%ariaSort,%ariaValueMax,%ariaValueMin,%ariaValueNow,%ariaValueText,%classList,className,elementTiming,id,innerHTML,*beforecopy,*beforecut,*beforepaste,*fullscreenchange,*fullscreenerror,*search,*webkitfullscreenchange,*webkitfullscreenerror,outerHTML,%part,#scrollLeft,#scrollTop,slot' + /* added manually to avoid breaking changes */\n',*message,*mozfullscreenchange,*mozfullscreenerror,*mozpointerlockchange,*mozpointerlockerror,*webglcontextcreationerror,*webglcontextlost,*webglcontextrestored', '[HTMLElement]^[Element]|accessKey,autocapitalize,!autofocus,contentEditable,dir,!draggable,enterKeyHint,!hidden,innerText,inputMode,lang,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,outerText,!spellcheck,%style,#tabIndex,title,!translate,virtualKeyboardPolicy', 'abbr,address,article,aside,b,bdi,bdo,cite,content,code,dd,dfn,dt,em,figcaption,figure,footer,header,hgroup,i,kbd,main,mark,nav,noscript,rb,rp,rt,rtc,ruby,s,samp,section,small,strong,sub,sup,u,var,wbr^[HTMLElement]|accessKey,autocapitalize,!autofocus,contentEditable,dir,!draggable,enterKeyHint,!hidden,innerText,inputMode,lang,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,outerText,!spellcheck,%style,#tabIndex,title,!translate,virtualKeyboardPolicy', 'media^[HTMLElement]|!autoplay,!controls,%controlsList,%crossOrigin,#currentTime,!defaultMuted,#defaultPlaybackRate,!disableRemotePlayback,!loop,!muted,*encrypted,*waitingforkey,#playbackRate,preload,!preservesPitch,src,%srcObject,#volume', ':svg:^[HTMLElement]|!autofocus,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,%style,#tabIndex', ':svg:graphics^:svg:|', ':svg:animation^:svg:|*begin,*end,*repeat', ':svg:geometry^:svg:|', ':svg:componentTransferFunction^:svg:|', ':svg:gradient^:svg:|', ':svg:textContent^:svg:graphics|', ':svg:textPositioning^:svg:textContent|', 'a^[HTMLElement]|charset,coords,download,hash,host,hostname,href,hreflang,name,password,pathname,ping,port,protocol,referrerPolicy,rel,%relList,rev,search,shape,target,text,type,username', 'area^[HTMLElement]|alt,coords,download,hash,host,hostname,href,!noHref,password,pathname,ping,port,protocol,referrerPolicy,rel,%relList,search,shape,target,username', 'audio^media|', 'br^[HTMLElement]|clear', 'base^[HTMLElement]|href,target', 'body^[HTMLElement]|aLink,background,bgColor,link,*afterprint,*beforeprint,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*messageerror,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,text,vLink', 'button^[HTMLElement]|!disabled,formAction,formEnctype,formMethod,!formNoValidate,formTarget,name,type,value', 'canvas^[HTMLElement]|#height,#width', 'content^[HTMLElement]|select', 'dl^[HTMLElement]|!compact', 'data^[HTMLElement]|value', 'datalist^[HTMLElement]|', 'details^[HTMLElement]|!open', 'dialog^[HTMLElement]|!open,returnValue', 'dir^[HTMLElement]|!compact', 'div^[HTMLElement]|align', 'embed^[HTMLElement]|align,height,name,src,type,width', 'fieldset^[HTMLElement]|!disabled,name', 'font^[HTMLElement]|color,face,size', 'form^[HTMLElement]|acceptCharset,action,autocomplete,encoding,enctype,method,name,!noValidate,target', 'frame^[HTMLElement]|frameBorder,longDesc,marginHeight,marginWidth,name,!noResize,scrolling,src', 'frameset^[HTMLElement]|cols,*afterprint,*beforeprint,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*messageerror,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,rows', 'hr^[HTMLElement]|align,color,!noShade,size,width', 'head^[HTMLElement]|', 'h1,h2,h3,h4,h5,h6^[HTMLElement]|align', 'html^[HTMLElement]|version', 'iframe^[HTMLElement]|align,allow,!allowFullscreen,!allowPaymentRequest,csp,frameBorder,height,loading,longDesc,marginHeight,marginWidth,name,referrerPolicy,%sandbox,scrolling,src,srcdoc,width', 'img^[HTMLElement]|align,alt,border,%crossOrigin,decoding,#height,#hspace,!isMap,loading,longDesc,lowsrc,name,referrerPolicy,sizes,src,srcset,useMap,#vspace,#width', 'input^[HTMLElement]|accept,align,alt,autocomplete,!checked,!defaultChecked,defaultValue,dirName,!disabled,%files,formAction,formEnctype,formMethod,!formNoValidate,formTarget,#height,!incremental,!indeterminate,max,#maxLength,min,#minLength,!multiple,name,pattern,placeholder,!readOnly,!required,selectionDirection,#selectionEnd,#selectionStart,#size,src,step,type,useMap,value,%valueAsDate,#valueAsNumber,#width', 'li^[HTMLElement]|type,#value', 'label^[HTMLElement]|htmlFor', 'legend^[HTMLElement]|align', 'link^[HTMLElement]|as,charset,%crossOrigin,!disabled,href,hreflang,imageSizes,imageSrcset,integrity,media,referrerPolicy,rel,%relList,rev,%sizes,target,type', 'map^[HTMLElement]|name', 'marquee^[HTMLElement]|behavior,bgColor,direction,height,#hspace,#loop,#scrollAmount,#scrollDelay,!trueSpeed,#vspace,width', 'menu^[HTMLElement]|!compact', 'meta^[HTMLElement]|content,httpEquiv,media,name,scheme', 'meter^[HTMLElement]|#high,#low,#max,#min,#optimum,#value', 'ins,del^[HTMLElement]|cite,dateTime', 'ol^[HTMLElement]|!compact,!reversed,#start,type', 'object^[HTMLElement]|align,archive,border,code,codeBase,codeType,data,!declare,height,#hspace,name,standby,type,useMap,#vspace,width', 'optgroup^[HTMLElement]|!disabled,label', 'option^[HTMLElement]|!defaultSelected,!disabled,label,!selected,text,value', 'output^[HTMLElement]|defaultValue,%htmlFor,name,value', 'p^[HTMLElement]|align', 'param^[HTMLElement]|name,type,value,valueType', 'picture^[HTMLElement]|', 'pre^[HTMLElement]|#width', 'progress^[HTMLElement]|#max,#value', 'q,blockquote,cite^[HTMLElement]|', 'script^[HTMLElement]|!async,charset,%crossOrigin,!defer,event,htmlFor,integrity,!noModule,%referrerPolicy,src,text,type', 'select^[HTMLElement]|autocomplete,!disabled,#length,!multiple,name,!required,#selectedIndex,#size,value', 'slot^[HTMLElement]|name', 'source^[HTMLElement]|#height,media,sizes,src,srcset,type,#width', 'span^[HTMLElement]|', 'style^[HTMLElement]|!disabled,media,type', 'caption^[HTMLElement]|align', 'th,td^[HTMLElement]|abbr,align,axis,bgColor,ch,chOff,#colSpan,headers,height,!noWrap,#rowSpan,scope,vAlign,width', 'col,colgroup^[HTMLElement]|align,ch,chOff,#span,vAlign,width', 'table^[HTMLElement]|align,bgColor,border,%caption,cellPadding,cellSpacing,frame,rules,summary,%tFoot,%tHead,width', 'tr^[HTMLElement]|align,bgColor,ch,chOff,vAlign', 'tfoot,thead,tbody^[HTMLElement]|align,ch,chOff,vAlign', 'template^[HTMLElement]|', 'textarea^[HTMLElement]|autocomplete,#cols,defaultValue,dirName,!disabled,#maxLength,#minLength,name,placeholder,!readOnly,!required,#rows,selectionDirection,#selectionEnd,#selectionStart,value,wrap', 'time^[HTMLElement]|dateTime', 'title^[HTMLElement]|text', 'track^[HTMLElement]|!default,kind,label,src,srclang', 'ul^[HTMLElement]|!compact,type', 'unknown^[HTMLElement]|', 'video^media|!disablePictureInPicture,#height,*enterpictureinpicture,*leavepictureinpicture,!playsInline,poster,#width', ':svg:a^:svg:graphics|', ':svg:animate^:svg:animation|', ':svg:animateMotion^:svg:animation|', ':svg:animateTransform^:svg:animation|', ':svg:circle^:svg:geometry|', ':svg:clipPath^:svg:graphics|', ':svg:defs^:svg:graphics|', ':svg:desc^:svg:|', ':svg:discard^:svg:|', ':svg:ellipse^:svg:geometry|', ':svg:feBlend^:svg:|', ':svg:feColorMatrix^:svg:|', ':svg:feComponentTransfer^:svg:|', ':svg:feComposite^:svg:|', ':svg:feConvolveMatrix^:svg:|', ':svg:feDiffuseLighting^:svg:|', ':svg:feDisplacementMap^:svg:|', ':svg:feDistantLight^:svg:|', ':svg:feDropShadow^:svg:|', ':svg:feFlood^:svg:|', ':svg:feFuncA^:svg:componentTransferFunction|', ':svg:feFuncB^:svg:componentTransferFunction|', ':svg:feFuncG^:svg:componentTransferFunction|', ':svg:feFuncR^:svg:componentTransferFunction|', ':svg:feGaussianBlur^:svg:|', ':svg:feImage^:svg:|', ':svg:feMerge^:svg:|', ':svg:feMergeNode^:svg:|', ':svg:feMorphology^:svg:|', ':svg:feOffset^:svg:|', ':svg:fePointLight^:svg:|', ':svg:feSpecularLighting^:svg:|', ':svg:feSpotLight^:svg:|', ':svg:feTile^:svg:|', ':svg:feTurbulence^:svg:|', ':svg:filter^:svg:|', ':svg:foreignObject^:svg:graphics|', ':svg:g^:svg:graphics|', ':svg:image^:svg:graphics|decoding', ':svg:line^:svg:geometry|', ':svg:linearGradient^:svg:gradient|', ':svg:mpath^:svg:|', ':svg:marker^:svg:|', ':svg:mask^:svg:|', ':svg:metadata^:svg:|', ':svg:path^:svg:geometry|', ':svg:pattern^:svg:|', ':svg:polygon^:svg:geometry|', ':svg:polyline^:svg:geometry|', ':svg:radialGradient^:svg:gradient|', ':svg:rect^:svg:geometry|', ':svg:svg^:svg:graphics|#currentScale,#zoomAndPan', ':svg:script^:svg:|type', ':svg:set^:svg:animation|', ':svg:stop^:svg:|', ':svg:style^:svg:|!disabled,media,title,type', ':svg:switch^:svg:graphics|', ':svg:symbol^:svg:|', ':svg:tspan^:svg:textPositioning|', ':svg:text^:svg:textPositioning|', ':svg:textPath^:svg:textContent|', ':svg:title^:svg:|', ':svg:use^:svg:graphics|', ':svg:view^:svg:|#zoomAndPan', 'data^[HTMLElement]|value', 'keygen^[HTMLElement]|!autofocus,challenge,!disabled,form,keytype,name', 'menuitem^[HTMLElement]|type,label,icon,!disabled,!checked,radiogroup,!default', 'summary^[HTMLElement]|', 'time^[HTMLElement]|dateTime', ':svg:cursor^:svg:|'];\nconst _ATTR_TO_PROP = new Map(Object.entries({\n 'class': 'className',\n 'for': 'htmlFor',\n 'formaction': 'formAction',\n 'innerHtml': 'innerHTML',\n 'readonly': 'readOnly',\n 'tabindex': 'tabIndex'\n}));\n// Invert _ATTR_TO_PROP.\nconst _PROP_TO_ATTR = Array.from(_ATTR_TO_PROP).reduce((inverted, [propertyName, attributeName]) => {\n inverted.set(propertyName, attributeName);\n return inverted;\n}, new Map());\nclass DomElementSchemaRegistry extends ElementSchemaRegistry {\n constructor() {\n super();\n this._schema = new Map();\n // We don't allow binding to events for security reasons. Allowing event bindings would almost\n // certainly introduce bad XSS vulnerabilities. Instead, we store events in a separate schema.\n this._eventSchema = new Map();\n SCHEMA.forEach(encodedType => {\n const type = new Map();\n const events = new Set();\n const [strType, strProperties] = encodedType.split('|');\n const properties = strProperties.split(',');\n const [typeNames, superName] = strType.split('^');\n typeNames.split(',').forEach(tag => {\n this._schema.set(tag.toLowerCase(), type);\n this._eventSchema.set(tag.toLowerCase(), events);\n });\n const superType = superName && this._schema.get(superName.toLowerCase());\n if (superType) {\n for (const [prop, value] of superType) {\n type.set(prop, value);\n }\n for (const superEvent of this._eventSchema.get(superName.toLowerCase())) {\n events.add(superEvent);\n }\n }\n properties.forEach(property => {\n if (property.length > 0) {\n switch (property[0]) {\n case '*':\n events.add(property.substring(1));\n break;\n case '!':\n type.set(property.substring(1), BOOLEAN);\n break;\n case '#':\n type.set(property.substring(1), NUMBER);\n break;\n case '%':\n type.set(property.substring(1), OBJECT);\n break;\n default:\n type.set(property, STRING);\n }\n }\n });\n });\n }\n hasProperty(tagName, propName, schemaMetas) {\n if (schemaMetas.some(schema => schema.name === NO_ERRORS_SCHEMA.name)) {\n return true;\n }\n if (tagName.indexOf('-') > -1) {\n if (isNgContainer(tagName) || isNgContent(tagName)) {\n return false;\n }\n if (schemaMetas.some(schema => schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) {\n // Can't tell now as we don't know which properties a custom element will get\n // once it is instantiated\n return true;\n }\n }\n const elementProperties = this._schema.get(tagName.toLowerCase()) || this._schema.get('unknown');\n return elementProperties.has(propName);\n }\n hasElement(tagName, schemaMetas) {\n if (schemaMetas.some(schema => schema.name === NO_ERRORS_SCHEMA.name)) {\n return true;\n }\n if (tagName.indexOf('-') > -1) {\n if (isNgContainer(tagName) || isNgContent(tagName)) {\n return true;\n }\n if (schemaMetas.some(schema => schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) {\n // Allow any custom elements\n return true;\n }\n }\n return this._schema.has(tagName.toLowerCase());\n }\n /**\n * securityContext returns the security context for the given property on the given DOM tag.\n *\n * Tag and property name are statically known and cannot change at runtime, i.e. it is not\n * possible to bind a value into a changing attribute or tag name.\n *\n * The filtering is based on a list of allowed tags|attributes. All attributes in the schema\n * above are assumed to have the 'NONE' security context, i.e. that they are safe inert\n * string values. Only specific well known attack vectors are assigned their appropriate context.\n */\n securityContext(tagName, propName, isAttribute) {\n if (isAttribute) {\n // NB: For security purposes, use the mapped property name, not the attribute name.\n propName = this.getMappedPropName(propName);\n }\n // Make sure comparisons are case insensitive, so that case differences between attribute and\n // property names do not have a security impact.\n tagName = tagName.toLowerCase();\n propName = propName.toLowerCase();\n let ctx = SECURITY_SCHEMA()[tagName + '|' + propName];\n if (ctx) {\n return ctx;\n }\n ctx = SECURITY_SCHEMA()['*|' + propName];\n return ctx ? ctx : SecurityContext.NONE;\n }\n getMappedPropName(propName) {\n return _ATTR_TO_PROP.get(propName) ?? propName;\n }\n getDefaultComponentElementName() {\n return 'ng-component';\n }\n validateProperty(name) {\n if (name.toLowerCase().startsWith('on')) {\n const msg = `Binding to event property '${name}' is disallowed for security reasons, ` + `please use (${name.slice(2)})=...` + `\\nIf '${name}' is a directive input, make sure the directive is imported by the` + ` current module.`;\n return {\n error: true,\n msg: msg\n };\n } else {\n return {\n error: false\n };\n }\n }\n validateAttribute(name) {\n if (name.toLowerCase().startsWith('on')) {\n const msg = `Binding to event attribute '${name}' is disallowed for security reasons, ` + `please use (${name.slice(2)})=...`;\n return {\n error: true,\n msg: msg\n };\n } else {\n return {\n error: false\n };\n }\n }\n allKnownElementNames() {\n return Array.from(this._schema.keys());\n }\n allKnownAttributesOfElement(tagName) {\n const elementProperties = this._schema.get(tagName.toLowerCase()) || this._schema.get('unknown');\n // Convert properties to attributes.\n return Array.from(elementProperties.keys()).map(prop => _PROP_TO_ATTR.get(prop) ?? prop);\n }\n allKnownEventsOfElement(tagName) {\n return Array.from(this._eventSchema.get(tagName.toLowerCase()) ?? []);\n }\n normalizeAnimationStyleProperty(propName) {\n return dashCaseToCamelCase(propName);\n }\n normalizeAnimationStyleValue(camelCaseProp, userProvidedProp, val) {\n let unit = '';\n const strVal = val.toString().trim();\n let errorMsg = null;\n if (_isPixelDimensionStyle(camelCaseProp) && val !== 0 && val !== '0') {\n if (typeof val === 'number') {\n unit = 'px';\n } else {\n const valAndSuffixMatch = val.match(/^[+-]?[\\d\\.]+([a-z]*)$/);\n if (valAndSuffixMatch && valAndSuffixMatch[1].length == 0) {\n errorMsg = `Please provide a CSS unit value for ${userProvidedProp}:${val}`;\n }\n }\n }\n return {\n error: errorMsg,\n value: strVal + unit\n };\n }\n}\nfunction _isPixelDimensionStyle(prop) {\n switch (prop) {\n case 'width':\n case 'height':\n case 'minWidth':\n case 'minHeight':\n case 'maxWidth':\n case 'maxHeight':\n case 'left':\n case 'top':\n case 'bottom':\n case 'right':\n case 'fontSize':\n case 'outlineWidth':\n case 'outlineOffset':\n case 'paddingTop':\n case 'paddingLeft':\n case 'paddingBottom':\n case 'paddingRight':\n case 'marginTop':\n case 'marginLeft':\n case 'marginBottom':\n case 'marginRight':\n case 'borderRadius':\n case 'borderWidth':\n case 'borderTopWidth':\n case 'borderLeftWidth':\n case 'borderRightWidth':\n case 'borderBottomWidth':\n case 'textIndent':\n return true;\n default:\n return false;\n }\n}\nclass HtmlTagDefinition {\n constructor({\n closedByChildren,\n implicitNamespacePrefix,\n contentType = TagContentType.PARSABLE_DATA,\n closedByParent = false,\n isVoid = false,\n ignoreFirstLf = false,\n preventNamespaceInheritance = false,\n canSelfClose = false\n } = {}) {\n this.closedByChildren = {};\n this.closedByParent = false;\n if (closedByChildren && closedByChildren.length > 0) {\n closedByChildren.forEach(tagName => this.closedByChildren[tagName] = true);\n }\n this.isVoid = isVoid;\n this.closedByParent = closedByParent || isVoid;\n this.implicitNamespacePrefix = implicitNamespacePrefix || null;\n this.contentType = contentType;\n this.ignoreFirstLf = ignoreFirstLf;\n this.preventNamespaceInheritance = preventNamespaceInheritance;\n this.canSelfClose = canSelfClose ?? isVoid;\n }\n isClosedByChild(name) {\n return this.isVoid || name.toLowerCase() in this.closedByChildren;\n }\n getContentType(prefix) {\n if (typeof this.contentType === 'object') {\n const overrideType = prefix === undefined ? undefined : this.contentType[prefix];\n return overrideType ?? this.contentType.default;\n }\n return this.contentType;\n }\n}\nlet DEFAULT_TAG_DEFINITION;\n// see https://www.w3.org/TR/html51/syntax.html#optional-tags\n// This implementation does not fully conform to the HTML5 spec.\nlet TAG_DEFINITIONS;\nfunction getHtmlTagDefinition(tagName) {\n if (!TAG_DEFINITIONS) {\n DEFAULT_TAG_DEFINITION = new HtmlTagDefinition({\n canSelfClose: true\n });\n TAG_DEFINITIONS = {\n 'base': new HtmlTagDefinition({\n isVoid: true\n }),\n 'meta': new HtmlTagDefinition({\n isVoid: true\n }),\n 'area': new HtmlTagDefinition({\n isVoid: true\n }),\n 'embed': new HtmlTagDefinition({\n isVoid: true\n }),\n 'link': new HtmlTagDefinition({\n isVoid: true\n }),\n 'img': new HtmlTagDefinition({\n isVoid: true\n }),\n 'input': new HtmlTagDefinition({\n isVoid: true\n }),\n 'param': new HtmlTagDefinition({\n isVoid: true\n }),\n 'hr': new HtmlTagDefinition({\n isVoid: true\n }),\n 'br': new HtmlTagDefinition({\n isVoid: true\n }),\n 'source': new HtmlTagDefinition({\n isVoid: true\n }),\n 'track': new HtmlTagDefinition({\n isVoid: true\n }),\n 'wbr': new HtmlTagDefinition({\n isVoid: true\n }),\n 'p': new HtmlTagDefinition({\n closedByChildren: ['address', 'article', 'aside', 'blockquote', 'div', 'dl', 'fieldset', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'main', 'nav', 'ol', 'p', 'pre', 'section', 'table', 'ul'],\n closedByParent: true\n }),\n 'thead': new HtmlTagDefinition({\n closedByChildren: ['tbody', 'tfoot']\n }),\n 'tbody': new HtmlTagDefinition({\n closedByChildren: ['tbody', 'tfoot'],\n closedByParent: true\n }),\n 'tfoot': new HtmlTagDefinition({\n closedByChildren: ['tbody'],\n closedByParent: true\n }),\n 'tr': new HtmlTagDefinition({\n closedByChildren: ['tr'],\n closedByParent: true\n }),\n 'td': new HtmlTagDefinition({\n closedByChildren: ['td', 'th'],\n closedByParent: true\n }),\n 'th': new HtmlTagDefinition({\n closedByChildren: ['td', 'th'],\n closedByParent: true\n }),\n 'col': new HtmlTagDefinition({\n isVoid: true\n }),\n 'svg': new HtmlTagDefinition({\n implicitNamespacePrefix: 'svg'\n }),\n 'foreignObject': new HtmlTagDefinition({\n // Usually the implicit namespace here would be redundant since it will be inherited from\n // the parent `svg`, but we have to do it for `foreignObject`, because the way the parser\n // works is that the parent node of an end tag is its own start tag which means that\n // the `preventNamespaceInheritance` on `foreignObject` would have it default to the\n // implicit namespace which is `html`, unless specified otherwise.\n implicitNamespacePrefix: 'svg',\n // We want to prevent children of foreignObject from inheriting its namespace, because\n // the point of the element is to allow nodes from other namespaces to be inserted.\n preventNamespaceInheritance: true\n }),\n 'math': new HtmlTagDefinition({\n implicitNamespacePrefix: 'math'\n }),\n 'li': new HtmlTagDefinition({\n closedByChildren: ['li'],\n closedByParent: true\n }),\n 'dt': new HtmlTagDefinition({\n closedByChildren: ['dt', 'dd']\n }),\n 'dd': new HtmlTagDefinition({\n closedByChildren: ['dt', 'dd'],\n closedByParent: true\n }),\n 'rb': new HtmlTagDefinition({\n closedByChildren: ['rb', 'rt', 'rtc', 'rp'],\n closedByParent: true\n }),\n 'rt': new HtmlTagDefinition({\n closedByChildren: ['rb', 'rt', 'rtc', 'rp'],\n closedByParent: true\n }),\n 'rtc': new HtmlTagDefinition({\n closedByChildren: ['rb', 'rtc', 'rp'],\n closedByParent: true\n }),\n 'rp': new HtmlTagDefinition({\n closedByChildren: ['rb', 'rt', 'rtc', 'rp'],\n closedByParent: true\n }),\n 'optgroup': new HtmlTagDefinition({\n closedByChildren: ['optgroup'],\n closedByParent: true\n }),\n 'option': new HtmlTagDefinition({\n closedByChildren: ['option', 'optgroup'],\n closedByParent: true\n }),\n 'pre': new HtmlTagDefinition({\n ignoreFirstLf: true\n }),\n 'listing': new HtmlTagDefinition({\n ignoreFirstLf: true\n }),\n 'style': new HtmlTagDefinition({\n contentType: TagContentType.RAW_TEXT\n }),\n 'script': new HtmlTagDefinition({\n contentType: TagContentType.RAW_TEXT\n }),\n 'title': new HtmlTagDefinition({\n // The browser supports two separate `title` tags which have to use\n // a different content type: `HTMLTitleElement` and `SVGTitleElement`\n contentType: {\n default: TagContentType.ESCAPABLE_RAW_TEXT,\n svg: TagContentType.PARSABLE_DATA\n }\n }),\n 'textarea': new HtmlTagDefinition({\n contentType: TagContentType.ESCAPABLE_RAW_TEXT,\n ignoreFirstLf: true\n })\n };\n new DomElementSchemaRegistry().allKnownElementNames().forEach(knownTagName => {\n if (!TAG_DEFINITIONS.hasOwnProperty(knownTagName) && getNsPrefix(knownTagName) === null) {\n TAG_DEFINITIONS[knownTagName] = new HtmlTagDefinition({\n canSelfClose: false\n });\n }\n });\n }\n // We have to make both a case-sensitive and a case-insensitive lookup, because\n // HTML tag names are case insensitive, whereas some SVG tags are case sensitive.\n return TAG_DEFINITIONS[tagName] ?? TAG_DEFINITIONS[tagName.toLowerCase()] ?? DEFAULT_TAG_DEFINITION;\n}\n\n// Mapping between all HTML entity names and their unicode representation.\n// Generated from https://html.spec.whatwg.org/multipage/entities.json by stripping\n// the `&` and `;` from the keys and removing the duplicates.\n// see https://www.w3.org/TR/html51/syntax.html#named-character-references\nconst NAMED_ENTITIES = {\n 'AElig': '\\u00C6',\n 'AMP': '\\u0026',\n 'amp': '\\u0026',\n 'Aacute': '\\u00C1',\n 'Abreve': '\\u0102',\n 'Acirc': '\\u00C2',\n 'Acy': '\\u0410',\n 'Afr': '\\uD835\\uDD04',\n 'Agrave': '\\u00C0',\n 'Alpha': '\\u0391',\n 'Amacr': '\\u0100',\n 'And': '\\u2A53',\n 'Aogon': '\\u0104',\n 'Aopf': '\\uD835\\uDD38',\n 'ApplyFunction': '\\u2061',\n 'af': '\\u2061',\n 'Aring': '\\u00C5',\n 'angst': '\\u00C5',\n 'Ascr': '\\uD835\\uDC9C',\n 'Assign': '\\u2254',\n 'colone': '\\u2254',\n 'coloneq': '\\u2254',\n 'Atilde': '\\u00C3',\n 'Auml': '\\u00C4',\n 'Backslash': '\\u2216',\n 'setminus': '\\u2216',\n 'setmn': '\\u2216',\n 'smallsetminus': '\\u2216',\n 'ssetmn': '\\u2216',\n 'Barv': '\\u2AE7',\n 'Barwed': '\\u2306',\n 'doublebarwedge': '\\u2306',\n 'Bcy': '\\u0411',\n 'Because': '\\u2235',\n 'becaus': '\\u2235',\n 'because': '\\u2235',\n 'Bernoullis': '\\u212C',\n 'Bscr': '\\u212C',\n 'bernou': '\\u212C',\n 'Beta': '\\u0392',\n 'Bfr': '\\uD835\\uDD05',\n 'Bopf': '\\uD835\\uDD39',\n 'Breve': '\\u02D8',\n 'breve': '\\u02D8',\n 'Bumpeq': '\\u224E',\n 'HumpDownHump': '\\u224E',\n 'bump': '\\u224E',\n 'CHcy': '\\u0427',\n 'COPY': '\\u00A9',\n 'copy': '\\u00A9',\n 'Cacute': '\\u0106',\n 'Cap': '\\u22D2',\n 'CapitalDifferentialD': '\\u2145',\n 'DD': '\\u2145',\n 'Cayleys': '\\u212D',\n 'Cfr': '\\u212D',\n 'Ccaron': '\\u010C',\n 'Ccedil': '\\u00C7',\n 'Ccirc': '\\u0108',\n 'Cconint': '\\u2230',\n 'Cdot': '\\u010A',\n 'Cedilla': '\\u00B8',\n 'cedil': '\\u00B8',\n 'CenterDot': '\\u00B7',\n 'centerdot': '\\u00B7',\n 'middot': '\\u00B7',\n 'Chi': '\\u03A7',\n 'CircleDot': '\\u2299',\n 'odot': '\\u2299',\n 'CircleMinus': '\\u2296',\n 'ominus': '\\u2296',\n 'CirclePlus': '\\u2295',\n 'oplus': '\\u2295',\n 'CircleTimes': '\\u2297',\n 'otimes': '\\u2297',\n 'ClockwiseContourIntegral': '\\u2232',\n 'cwconint': '\\u2232',\n 'CloseCurlyDoubleQuote': '\\u201D',\n 'rdquo': '\\u201D',\n 'rdquor': '\\u201D',\n 'CloseCurlyQuote': '\\u2019',\n 'rsquo': '\\u2019',\n 'rsquor': '\\u2019',\n 'Colon': '\\u2237',\n 'Proportion': '\\u2237',\n 'Colone': '\\u2A74',\n 'Congruent': '\\u2261',\n 'equiv': '\\u2261',\n 'Conint': '\\u222F',\n 'DoubleContourIntegral': '\\u222F',\n 'ContourIntegral': '\\u222E',\n 'conint': '\\u222E',\n 'oint': '\\u222E',\n 'Copf': '\\u2102',\n 'complexes': '\\u2102',\n 'Coproduct': '\\u2210',\n 'coprod': '\\u2210',\n 'CounterClockwiseContourIntegral': '\\u2233',\n 'awconint': '\\u2233',\n 'Cross': '\\u2A2F',\n 'Cscr': '\\uD835\\uDC9E',\n 'Cup': '\\u22D3',\n 'CupCap': '\\u224D',\n 'asympeq': '\\u224D',\n 'DDotrahd': '\\u2911',\n 'DJcy': '\\u0402',\n 'DScy': '\\u0405',\n 'DZcy': '\\u040F',\n 'Dagger': '\\u2021',\n 'ddagger': '\\u2021',\n 'Darr': '\\u21A1',\n 'Dashv': '\\u2AE4',\n 'DoubleLeftTee': '\\u2AE4',\n 'Dcaron': '\\u010E',\n 'Dcy': '\\u0414',\n 'Del': '\\u2207',\n 'nabla': '\\u2207',\n 'Delta': '\\u0394',\n 'Dfr': '\\uD835\\uDD07',\n 'DiacriticalAcute': '\\u00B4',\n 'acute': '\\u00B4',\n 'DiacriticalDot': '\\u02D9',\n 'dot': '\\u02D9',\n 'DiacriticalDoubleAcute': '\\u02DD',\n 'dblac': '\\u02DD',\n 'DiacriticalGrave': '\\u0060',\n 'grave': '\\u0060',\n 'DiacriticalTilde': '\\u02DC',\n 'tilde': '\\u02DC',\n 'Diamond': '\\u22C4',\n 'diam': '\\u22C4',\n 'diamond': '\\u22C4',\n 'DifferentialD': '\\u2146',\n 'dd': '\\u2146',\n 'Dopf': '\\uD835\\uDD3B',\n 'Dot': '\\u00A8',\n 'DoubleDot': '\\u00A8',\n 'die': '\\u00A8',\n 'uml': '\\u00A8',\n 'DotDot': '\\u20DC',\n 'DotEqual': '\\u2250',\n 'doteq': '\\u2250',\n 'esdot': '\\u2250',\n 'DoubleDownArrow': '\\u21D3',\n 'Downarrow': '\\u21D3',\n 'dArr': '\\u21D3',\n 'DoubleLeftArrow': '\\u21D0',\n 'Leftarrow': '\\u21D0',\n 'lArr': '\\u21D0',\n 'DoubleLeftRightArrow': '\\u21D4',\n 'Leftrightarrow': '\\u21D4',\n 'hArr': '\\u21D4',\n 'iff': '\\u21D4',\n 'DoubleLongLeftArrow': '\\u27F8',\n 'Longleftarrow': '\\u27F8',\n 'xlArr': '\\u27F8',\n 'DoubleLongLeftRightArrow': '\\u27FA',\n 'Longleftrightarrow': '\\u27FA',\n 'xhArr': '\\u27FA',\n 'DoubleLongRightArrow': '\\u27F9',\n 'Longrightarrow': '\\u27F9',\n 'xrArr': '\\u27F9',\n 'DoubleRightArrow': '\\u21D2',\n 'Implies': '\\u21D2',\n 'Rightarrow': '\\u21D2',\n 'rArr': '\\u21D2',\n 'DoubleRightTee': '\\u22A8',\n 'vDash': '\\u22A8',\n 'DoubleUpArrow': '\\u21D1',\n 'Uparrow': '\\u21D1',\n 'uArr': '\\u21D1',\n 'DoubleUpDownArrow': '\\u21D5',\n 'Updownarrow': '\\u21D5',\n 'vArr': '\\u21D5',\n 'DoubleVerticalBar': '\\u2225',\n 'par': '\\u2225',\n 'parallel': '\\u2225',\n 'shortparallel': '\\u2225',\n 'spar': '\\u2225',\n 'DownArrow': '\\u2193',\n 'ShortDownArrow': '\\u2193',\n 'darr': '\\u2193',\n 'downarrow': '\\u2193',\n 'DownArrowBar': '\\u2913',\n 'DownArrowUpArrow': '\\u21F5',\n 'duarr': '\\u21F5',\n 'DownBreve': '\\u0311',\n 'DownLeftRightVector': '\\u2950',\n 'DownLeftTeeVector': '\\u295E',\n 'DownLeftVector': '\\u21BD',\n 'leftharpoondown': '\\u21BD',\n 'lhard': '\\u21BD',\n 'DownLeftVectorBar': '\\u2956',\n 'DownRightTeeVector': '\\u295F',\n 'DownRightVector': '\\u21C1',\n 'rhard': '\\u21C1',\n 'rightharpoondown': '\\u21C1',\n 'DownRightVectorBar': '\\u2957',\n 'DownTee': '\\u22A4',\n 'top': '\\u22A4',\n 'DownTeeArrow': '\\u21A7',\n 'mapstodown': '\\u21A7',\n 'Dscr': '\\uD835\\uDC9F',\n 'Dstrok': '\\u0110',\n 'ENG': '\\u014A',\n 'ETH': '\\u00D0',\n 'Eacute': '\\u00C9',\n 'Ecaron': '\\u011A',\n 'Ecirc': '\\u00CA',\n 'Ecy': '\\u042D',\n 'Edot': '\\u0116',\n 'Efr': '\\uD835\\uDD08',\n 'Egrave': '\\u00C8',\n 'Element': '\\u2208',\n 'in': '\\u2208',\n 'isin': '\\u2208',\n 'isinv': '\\u2208',\n 'Emacr': '\\u0112',\n 'EmptySmallSquare': '\\u25FB',\n 'EmptyVerySmallSquare': '\\u25AB',\n 'Eogon': '\\u0118',\n 'Eopf': '\\uD835\\uDD3C',\n 'Epsilon': '\\u0395',\n 'Equal': '\\u2A75',\n 'EqualTilde': '\\u2242',\n 'eqsim': '\\u2242',\n 'esim': '\\u2242',\n 'Equilibrium': '\\u21CC',\n 'rightleftharpoons': '\\u21CC',\n 'rlhar': '\\u21CC',\n 'Escr': '\\u2130',\n 'expectation': '\\u2130',\n 'Esim': '\\u2A73',\n 'Eta': '\\u0397',\n 'Euml': '\\u00CB',\n 'Exists': '\\u2203',\n 'exist': '\\u2203',\n 'ExponentialE': '\\u2147',\n 'ee': '\\u2147',\n 'exponentiale': '\\u2147',\n 'Fcy': '\\u0424',\n 'Ffr': '\\uD835\\uDD09',\n 'FilledSmallSquare': '\\u25FC',\n 'FilledVerySmallSquare': '\\u25AA',\n 'blacksquare': '\\u25AA',\n 'squarf': '\\u25AA',\n 'squf': '\\u25AA',\n 'Fopf': '\\uD835\\uDD3D',\n 'ForAll': '\\u2200',\n 'forall': '\\u2200',\n 'Fouriertrf': '\\u2131',\n 'Fscr': '\\u2131',\n 'GJcy': '\\u0403',\n 'GT': '\\u003E',\n 'gt': '\\u003E',\n 'Gamma': '\\u0393',\n 'Gammad': '\\u03DC',\n 'Gbreve': '\\u011E',\n 'Gcedil': '\\u0122',\n 'Gcirc': '\\u011C',\n 'Gcy': '\\u0413',\n 'Gdot': '\\u0120',\n 'Gfr': '\\uD835\\uDD0A',\n 'Gg': '\\u22D9',\n 'ggg': '\\u22D9',\n 'Gopf': '\\uD835\\uDD3E',\n 'GreaterEqual': '\\u2265',\n 'ge': '\\u2265',\n 'geq': '\\u2265',\n 'GreaterEqualLess': '\\u22DB',\n 'gel': '\\u22DB',\n 'gtreqless': '\\u22DB',\n 'GreaterFullEqual': '\\u2267',\n 'gE': '\\u2267',\n 'geqq': '\\u2267',\n 'GreaterGreater': '\\u2AA2',\n 'GreaterLess': '\\u2277',\n 'gl': '\\u2277',\n 'gtrless': '\\u2277',\n 'GreaterSlantEqual': '\\u2A7E',\n 'geqslant': '\\u2A7E',\n 'ges': '\\u2A7E',\n 'GreaterTilde': '\\u2273',\n 'gsim': '\\u2273',\n 'gtrsim': '\\u2273',\n 'Gscr': '\\uD835\\uDCA2',\n 'Gt': '\\u226B',\n 'NestedGreaterGreater': '\\u226B',\n 'gg': '\\u226B',\n 'HARDcy': '\\u042A',\n 'Hacek': '\\u02C7',\n 'caron': '\\u02C7',\n 'Hat': '\\u005E',\n 'Hcirc': '\\u0124',\n 'Hfr': '\\u210C',\n 'Poincareplane': '\\u210C',\n 'HilbertSpace': '\\u210B',\n 'Hscr': '\\u210B',\n 'hamilt': '\\u210B',\n 'Hopf': '\\u210D',\n 'quaternions': '\\u210D',\n 'HorizontalLine': '\\u2500',\n 'boxh': '\\u2500',\n 'Hstrok': '\\u0126',\n 'HumpEqual': '\\u224F',\n 'bumpe': '\\u224F',\n 'bumpeq': '\\u224F',\n 'IEcy': '\\u0415',\n 'IJlig': '\\u0132',\n 'IOcy': '\\u0401',\n 'Iacute': '\\u00CD',\n 'Icirc': '\\u00CE',\n 'Icy': '\\u0418',\n 'Idot': '\\u0130',\n 'Ifr': '\\u2111',\n 'Im': '\\u2111',\n 'image': '\\u2111',\n 'imagpart': '\\u2111',\n 'Igrave': '\\u00CC',\n 'Imacr': '\\u012A',\n 'ImaginaryI': '\\u2148',\n 'ii': '\\u2148',\n 'Int': '\\u222C',\n 'Integral': '\\u222B',\n 'int': '\\u222B',\n 'Intersection': '\\u22C2',\n 'bigcap': '\\u22C2',\n 'xcap': '\\u22C2',\n 'InvisibleComma': '\\u2063',\n 'ic': '\\u2063',\n 'InvisibleTimes': '\\u2062',\n 'it': '\\u2062',\n 'Iogon': '\\u012E',\n 'Iopf': '\\uD835\\uDD40',\n 'Iota': '\\u0399',\n 'Iscr': '\\u2110',\n 'imagline': '\\u2110',\n 'Itilde': '\\u0128',\n 'Iukcy': '\\u0406',\n 'Iuml': '\\u00CF',\n 'Jcirc': '\\u0134',\n 'Jcy': '\\u0419',\n 'Jfr': '\\uD835\\uDD0D',\n 'Jopf': '\\uD835\\uDD41',\n 'Jscr': '\\uD835\\uDCA5',\n 'Jsercy': '\\u0408',\n 'Jukcy': '\\u0404',\n 'KHcy': '\\u0425',\n 'KJcy': '\\u040C',\n 'Kappa': '\\u039A',\n 'Kcedil': '\\u0136',\n 'Kcy': '\\u041A',\n 'Kfr': '\\uD835\\uDD0E',\n 'Kopf': '\\uD835\\uDD42',\n 'Kscr': '\\uD835\\uDCA6',\n 'LJcy': '\\u0409',\n 'LT': '\\u003C',\n 'lt': '\\u003C',\n 'Lacute': '\\u0139',\n 'Lambda': '\\u039B',\n 'Lang': '\\u27EA',\n 'Laplacetrf': '\\u2112',\n 'Lscr': '\\u2112',\n 'lagran': '\\u2112',\n 'Larr': '\\u219E',\n 'twoheadleftarrow': '\\u219E',\n 'Lcaron': '\\u013D',\n 'Lcedil': '\\u013B',\n 'Lcy': '\\u041B',\n 'LeftAngleBracket': '\\u27E8',\n 'lang': '\\u27E8',\n 'langle': '\\u27E8',\n 'LeftArrow': '\\u2190',\n 'ShortLeftArrow': '\\u2190',\n 'larr': '\\u2190',\n 'leftarrow': '\\u2190',\n 'slarr': '\\u2190',\n 'LeftArrowBar': '\\u21E4',\n 'larrb': '\\u21E4',\n 'LeftArrowRightArrow': '\\u21C6',\n 'leftrightarrows': '\\u21C6',\n 'lrarr': '\\u21C6',\n 'LeftCeiling': '\\u2308',\n 'lceil': '\\u2308',\n 'LeftDoubleBracket': '\\u27E6',\n 'lobrk': '\\u27E6',\n 'LeftDownTeeVector': '\\u2961',\n 'LeftDownVector': '\\u21C3',\n 'dharl': '\\u21C3',\n 'downharpoonleft': '\\u21C3',\n 'LeftDownVectorBar': '\\u2959',\n 'LeftFloor': '\\u230A',\n 'lfloor': '\\u230A',\n 'LeftRightArrow': '\\u2194',\n 'harr': '\\u2194',\n 'leftrightarrow': '\\u2194',\n 'LeftRightVector': '\\u294E',\n 'LeftTee': '\\u22A3',\n 'dashv': '\\u22A3',\n 'LeftTeeArrow': '\\u21A4',\n 'mapstoleft': '\\u21A4',\n 'LeftTeeVector': '\\u295A',\n 'LeftTriangle': '\\u22B2',\n 'vartriangleleft': '\\u22B2',\n 'vltri': '\\u22B2',\n 'LeftTriangleBar': '\\u29CF',\n 'LeftTriangleEqual': '\\u22B4',\n 'ltrie': '\\u22B4',\n 'trianglelefteq': '\\u22B4',\n 'LeftUpDownVector': '\\u2951',\n 'LeftUpTeeVector': '\\u2960',\n 'LeftUpVector': '\\u21BF',\n 'uharl': '\\u21BF',\n 'upharpoonleft': '\\u21BF',\n 'LeftUpVectorBar': '\\u2958',\n 'LeftVector': '\\u21BC',\n 'leftharpoonup': '\\u21BC',\n 'lharu': '\\u21BC',\n 'LeftVectorBar': '\\u2952',\n 'LessEqualGreater': '\\u22DA',\n 'leg': '\\u22DA',\n 'lesseqgtr': '\\u22DA',\n 'LessFullEqual': '\\u2266',\n 'lE': '\\u2266',\n 'leqq': '\\u2266',\n 'LessGreater': '\\u2276',\n 'lessgtr': '\\u2276',\n 'lg': '\\u2276',\n 'LessLess': '\\u2AA1',\n 'LessSlantEqual': '\\u2A7D',\n 'leqslant': '\\u2A7D',\n 'les': '\\u2A7D',\n 'LessTilde': '\\u2272',\n 'lesssim': '\\u2272',\n 'lsim': '\\u2272',\n 'Lfr': '\\uD835\\uDD0F',\n 'Ll': '\\u22D8',\n 'Lleftarrow': '\\u21DA',\n 'lAarr': '\\u21DA',\n 'Lmidot': '\\u013F',\n 'LongLeftArrow': '\\u27F5',\n 'longleftarrow': '\\u27F5',\n 'xlarr': '\\u27F5',\n 'LongLeftRightArrow': '\\u27F7',\n 'longleftrightarrow': '\\u27F7',\n 'xharr': '\\u27F7',\n 'LongRightArrow': '\\u27F6',\n 'longrightarrow': '\\u27F6',\n 'xrarr': '\\u27F6',\n 'Lopf': '\\uD835\\uDD43',\n 'LowerLeftArrow': '\\u2199',\n 'swarr': '\\u2199',\n 'swarrow': '\\u2199',\n 'LowerRightArrow': '\\u2198',\n 'searr': '\\u2198',\n 'searrow': '\\u2198',\n 'Lsh': '\\u21B0',\n 'lsh': '\\u21B0',\n 'Lstrok': '\\u0141',\n 'Lt': '\\u226A',\n 'NestedLessLess': '\\u226A',\n 'll': '\\u226A',\n 'Map': '\\u2905',\n 'Mcy': '\\u041C',\n 'MediumSpace': '\\u205F',\n 'Mellintrf': '\\u2133',\n 'Mscr': '\\u2133',\n 'phmmat': '\\u2133',\n 'Mfr': '\\uD835\\uDD10',\n 'MinusPlus': '\\u2213',\n 'mnplus': '\\u2213',\n 'mp': '\\u2213',\n 'Mopf': '\\uD835\\uDD44',\n 'Mu': '\\u039C',\n 'NJcy': '\\u040A',\n 'Nacute': '\\u0143',\n 'Ncaron': '\\u0147',\n 'Ncedil': '\\u0145',\n 'Ncy': '\\u041D',\n 'NegativeMediumSpace': '\\u200B',\n 'NegativeThickSpace': '\\u200B',\n 'NegativeThinSpace': '\\u200B',\n 'NegativeVeryThinSpace': '\\u200B',\n 'ZeroWidthSpace': '\\u200B',\n 'NewLine': '\\u000A',\n 'Nfr': '\\uD835\\uDD11',\n 'NoBreak': '\\u2060',\n 'NonBreakingSpace': '\\u00A0',\n 'nbsp': '\\u00A0',\n 'Nopf': '\\u2115',\n 'naturals': '\\u2115',\n 'Not': '\\u2AEC',\n 'NotCongruent': '\\u2262',\n 'nequiv': '\\u2262',\n 'NotCupCap': '\\u226D',\n 'NotDoubleVerticalBar': '\\u2226',\n 'npar': '\\u2226',\n 'nparallel': '\\u2226',\n 'nshortparallel': '\\u2226',\n 'nspar': '\\u2226',\n 'NotElement': '\\u2209',\n 'notin': '\\u2209',\n 'notinva': '\\u2209',\n 'NotEqual': '\\u2260',\n 'ne': '\\u2260',\n 'NotEqualTilde': '\\u2242\\u0338',\n 'nesim': '\\u2242\\u0338',\n 'NotExists': '\\u2204',\n 'nexist': '\\u2204',\n 'nexists': '\\u2204',\n 'NotGreater': '\\u226F',\n 'ngt': '\\u226F',\n 'ngtr': '\\u226F',\n 'NotGreaterEqual': '\\u2271',\n 'nge': '\\u2271',\n 'ngeq': '\\u2271',\n 'NotGreaterFullEqual': '\\u2267\\u0338',\n 'ngE': '\\u2267\\u0338',\n 'ngeqq': '\\u2267\\u0338',\n 'NotGreaterGreater': '\\u226B\\u0338',\n 'nGtv': '\\u226B\\u0338',\n 'NotGreaterLess': '\\u2279',\n 'ntgl': '\\u2279',\n 'NotGreaterSlantEqual': '\\u2A7E\\u0338',\n 'ngeqslant': '\\u2A7E\\u0338',\n 'nges': '\\u2A7E\\u0338',\n 'NotGreaterTilde': '\\u2275',\n 'ngsim': '\\u2275',\n 'NotHumpDownHump': '\\u224E\\u0338',\n 'nbump': '\\u224E\\u0338',\n 'NotHumpEqual': '\\u224F\\u0338',\n 'nbumpe': '\\u224F\\u0338',\n 'NotLeftTriangle': '\\u22EA',\n 'nltri': '\\u22EA',\n 'ntriangleleft': '\\u22EA',\n 'NotLeftTriangleBar': '\\u29CF\\u0338',\n 'NotLeftTriangleEqual': '\\u22EC',\n 'nltrie': '\\u22EC',\n 'ntrianglelefteq': '\\u22EC',\n 'NotLess': '\\u226E',\n 'nless': '\\u226E',\n 'nlt': '\\u226E',\n 'NotLessEqual': '\\u2270',\n 'nle': '\\u2270',\n 'nleq': '\\u2270',\n 'NotLessGreater': '\\u2278',\n 'ntlg': '\\u2278',\n 'NotLessLess': '\\u226A\\u0338',\n 'nLtv': '\\u226A\\u0338',\n 'NotLessSlantEqual': '\\u2A7D\\u0338',\n 'nleqslant': '\\u2A7D\\u0338',\n 'nles': '\\u2A7D\\u0338',\n 'NotLessTilde': '\\u2274',\n 'nlsim': '\\u2274',\n 'NotNestedGreaterGreater': '\\u2AA2\\u0338',\n 'NotNestedLessLess': '\\u2AA1\\u0338',\n 'NotPrecedes': '\\u2280',\n 'npr': '\\u2280',\n 'nprec': '\\u2280',\n 'NotPrecedesEqual': '\\u2AAF\\u0338',\n 'npre': '\\u2AAF\\u0338',\n 'npreceq': '\\u2AAF\\u0338',\n 'NotPrecedesSlantEqual': '\\u22E0',\n 'nprcue': '\\u22E0',\n 'NotReverseElement': '\\u220C',\n 'notni': '\\u220C',\n 'notniva': '\\u220C',\n 'NotRightTriangle': '\\u22EB',\n 'nrtri': '\\u22EB',\n 'ntriangleright': '\\u22EB',\n 'NotRightTriangleBar': '\\u29D0\\u0338',\n 'NotRightTriangleEqual': '\\u22ED',\n 'nrtrie': '\\u22ED',\n 'ntrianglerighteq': '\\u22ED',\n 'NotSquareSubset': '\\u228F\\u0338',\n 'NotSquareSubsetEqual': '\\u22E2',\n 'nsqsube': '\\u22E2',\n 'NotSquareSuperset': '\\u2290\\u0338',\n 'NotSquareSupersetEqual': '\\u22E3',\n 'nsqsupe': '\\u22E3',\n 'NotSubset': '\\u2282\\u20D2',\n 'nsubset': '\\u2282\\u20D2',\n 'vnsub': '\\u2282\\u20D2',\n 'NotSubsetEqual': '\\u2288',\n 'nsube': '\\u2288',\n 'nsubseteq': '\\u2288',\n 'NotSucceeds': '\\u2281',\n 'nsc': '\\u2281',\n 'nsucc': '\\u2281',\n 'NotSucceedsEqual': '\\u2AB0\\u0338',\n 'nsce': '\\u2AB0\\u0338',\n 'nsucceq': '\\u2AB0\\u0338',\n 'NotSucceedsSlantEqual': '\\u22E1',\n 'nsccue': '\\u22E1',\n 'NotSucceedsTilde': '\\u227F\\u0338',\n 'NotSuperset': '\\u2283\\u20D2',\n 'nsupset': '\\u2283\\u20D2',\n 'vnsup': '\\u2283\\u20D2',\n 'NotSupersetEqual': '\\u2289',\n 'nsupe': '\\u2289',\n 'nsupseteq': '\\u2289',\n 'NotTilde': '\\u2241',\n 'nsim': '\\u2241',\n 'NotTildeEqual': '\\u2244',\n 'nsime': '\\u2244',\n 'nsimeq': '\\u2244',\n 'NotTildeFullEqual': '\\u2247',\n 'ncong': '\\u2247',\n 'NotTildeTilde': '\\u2249',\n 'nap': '\\u2249',\n 'napprox': '\\u2249',\n 'NotVerticalBar': '\\u2224',\n 'nmid': '\\u2224',\n 'nshortmid': '\\u2224',\n 'nsmid': '\\u2224',\n 'Nscr': '\\uD835\\uDCA9',\n 'Ntilde': '\\u00D1',\n 'Nu': '\\u039D',\n 'OElig': '\\u0152',\n 'Oacute': '\\u00D3',\n 'Ocirc': '\\u00D4',\n 'Ocy': '\\u041E',\n 'Odblac': '\\u0150',\n 'Ofr': '\\uD835\\uDD12',\n 'Ograve': '\\u00D2',\n 'Omacr': '\\u014C',\n 'Omega': '\\u03A9',\n 'ohm': '\\u03A9',\n 'Omicron': '\\u039F',\n 'Oopf': '\\uD835\\uDD46',\n 'OpenCurlyDoubleQuote': '\\u201C',\n 'ldquo': '\\u201C',\n 'OpenCurlyQuote': '\\u2018',\n 'lsquo': '\\u2018',\n 'Or': '\\u2A54',\n 'Oscr': '\\uD835\\uDCAA',\n 'Oslash': '\\u00D8',\n 'Otilde': '\\u00D5',\n 'Otimes': '\\u2A37',\n 'Ouml': '\\u00D6',\n 'OverBar': '\\u203E',\n 'oline': '\\u203E',\n 'OverBrace': '\\u23DE',\n 'OverBracket': '\\u23B4',\n 'tbrk': '\\u23B4',\n 'OverParenthesis': '\\u23DC',\n 'PartialD': '\\u2202',\n 'part': '\\u2202',\n 'Pcy': '\\u041F',\n 'Pfr': '\\uD835\\uDD13',\n 'Phi': '\\u03A6',\n 'Pi': '\\u03A0',\n 'PlusMinus': '\\u00B1',\n 'plusmn': '\\u00B1',\n 'pm': '\\u00B1',\n 'Popf': '\\u2119',\n 'primes': '\\u2119',\n 'Pr': '\\u2ABB',\n 'Precedes': '\\u227A',\n 'pr': '\\u227A',\n 'prec': '\\u227A',\n 'PrecedesEqual': '\\u2AAF',\n 'pre': '\\u2AAF',\n 'preceq': '\\u2AAF',\n 'PrecedesSlantEqual': '\\u227C',\n 'prcue': '\\u227C',\n 'preccurlyeq': '\\u227C',\n 'PrecedesTilde': '\\u227E',\n 'precsim': '\\u227E',\n 'prsim': '\\u227E',\n 'Prime': '\\u2033',\n 'Product': '\\u220F',\n 'prod': '\\u220F',\n 'Proportional': '\\u221D',\n 'prop': '\\u221D',\n 'propto': '\\u221D',\n 'varpropto': '\\u221D',\n 'vprop': '\\u221D',\n 'Pscr': '\\uD835\\uDCAB',\n 'Psi': '\\u03A8',\n 'QUOT': '\\u0022',\n 'quot': '\\u0022',\n 'Qfr': '\\uD835\\uDD14',\n 'Qopf': '\\u211A',\n 'rationals': '\\u211A',\n 'Qscr': '\\uD835\\uDCAC',\n 'RBarr': '\\u2910',\n 'drbkarow': '\\u2910',\n 'REG': '\\u00AE',\n 'circledR': '\\u00AE',\n 'reg': '\\u00AE',\n 'Racute': '\\u0154',\n 'Rang': '\\u27EB',\n 'Rarr': '\\u21A0',\n 'twoheadrightarrow': '\\u21A0',\n 'Rarrtl': '\\u2916',\n 'Rcaron': '\\u0158',\n 'Rcedil': '\\u0156',\n 'Rcy': '\\u0420',\n 'Re': '\\u211C',\n 'Rfr': '\\u211C',\n 'real': '\\u211C',\n 'realpart': '\\u211C',\n 'ReverseElement': '\\u220B',\n 'SuchThat': '\\u220B',\n 'ni': '\\u220B',\n 'niv': '\\u220B',\n 'ReverseEquilibrium': '\\u21CB',\n 'leftrightharpoons': '\\u21CB',\n 'lrhar': '\\u21CB',\n 'ReverseUpEquilibrium': '\\u296F',\n 'duhar': '\\u296F',\n 'Rho': '\\u03A1',\n 'RightAngleBracket': '\\u27E9',\n 'rang': '\\u27E9',\n 'rangle': '\\u27E9',\n 'RightArrow': '\\u2192',\n 'ShortRightArrow': '\\u2192',\n 'rarr': '\\u2192',\n 'rightarrow': '\\u2192',\n 'srarr': '\\u2192',\n 'RightArrowBar': '\\u21E5',\n 'rarrb': '\\u21E5',\n 'RightArrowLeftArrow': '\\u21C4',\n 'rightleftarrows': '\\u21C4',\n 'rlarr': '\\u21C4',\n 'RightCeiling': '\\u2309',\n 'rceil': '\\u2309',\n 'RightDoubleBracket': '\\u27E7',\n 'robrk': '\\u27E7',\n 'RightDownTeeVector': '\\u295D',\n 'RightDownVector': '\\u21C2',\n 'dharr': '\\u21C2',\n 'downharpoonright': '\\u21C2',\n 'RightDownVectorBar': '\\u2955',\n 'RightFloor': '\\u230B',\n 'rfloor': '\\u230B',\n 'RightTee': '\\u22A2',\n 'vdash': '\\u22A2',\n 'RightTeeArrow': '\\u21A6',\n 'map': '\\u21A6',\n 'mapsto': '\\u21A6',\n 'RightTeeVector': '\\u295B',\n 'RightTriangle': '\\u22B3',\n 'vartriangleright': '\\u22B3',\n 'vrtri': '\\u22B3',\n 'RightTriangleBar': '\\u29D0',\n 'RightTriangleEqual': '\\u22B5',\n 'rtrie': '\\u22B5',\n 'trianglerighteq': '\\u22B5',\n 'RightUpDownVector': '\\u294F',\n 'RightUpTeeVector': '\\u295C',\n 'RightUpVector': '\\u21BE',\n 'uharr': '\\u21BE',\n 'upharpoonright': '\\u21BE',\n 'RightUpVectorBar': '\\u2954',\n 'RightVector': '\\u21C0',\n 'rharu': '\\u21C0',\n 'rightharpoonup': '\\u21C0',\n 'RightVectorBar': '\\u2953',\n 'Ropf': '\\u211D',\n 'reals': '\\u211D',\n 'RoundImplies': '\\u2970',\n 'Rrightarrow': '\\u21DB',\n 'rAarr': '\\u21DB',\n 'Rscr': '\\u211B',\n 'realine': '\\u211B',\n 'Rsh': '\\u21B1',\n 'rsh': '\\u21B1',\n 'RuleDelayed': '\\u29F4',\n 'SHCHcy': '\\u0429',\n 'SHcy': '\\u0428',\n 'SOFTcy': '\\u042C',\n 'Sacute': '\\u015A',\n 'Sc': '\\u2ABC',\n 'Scaron': '\\u0160',\n 'Scedil': '\\u015E',\n 'Scirc': '\\u015C',\n 'Scy': '\\u0421',\n 'Sfr': '\\uD835\\uDD16',\n 'ShortUpArrow': '\\u2191',\n 'UpArrow': '\\u2191',\n 'uarr': '\\u2191',\n 'uparrow': '\\u2191',\n 'Sigma': '\\u03A3',\n 'SmallCircle': '\\u2218',\n 'compfn': '\\u2218',\n 'Sopf': '\\uD835\\uDD4A',\n 'Sqrt': '\\u221A',\n 'radic': '\\u221A',\n 'Square': '\\u25A1',\n 'squ': '\\u25A1',\n 'square': '\\u25A1',\n 'SquareIntersection': '\\u2293',\n 'sqcap': '\\u2293',\n 'SquareSubset': '\\u228F',\n 'sqsub': '\\u228F',\n 'sqsubset': '\\u228F',\n 'SquareSubsetEqual': '\\u2291',\n 'sqsube': '\\u2291',\n 'sqsubseteq': '\\u2291',\n 'SquareSuperset': '\\u2290',\n 'sqsup': '\\u2290',\n 'sqsupset': '\\u2290',\n 'SquareSupersetEqual': '\\u2292',\n 'sqsupe': '\\u2292',\n 'sqsupseteq': '\\u2292',\n 'SquareUnion': '\\u2294',\n 'sqcup': '\\u2294',\n 'Sscr': '\\uD835\\uDCAE',\n 'Star': '\\u22C6',\n 'sstarf': '\\u22C6',\n 'Sub': '\\u22D0',\n 'Subset': '\\u22D0',\n 'SubsetEqual': '\\u2286',\n 'sube': '\\u2286',\n 'subseteq': '\\u2286',\n 'Succeeds': '\\u227B',\n 'sc': '\\u227B',\n 'succ': '\\u227B',\n 'SucceedsEqual': '\\u2AB0',\n 'sce': '\\u2AB0',\n 'succeq': '\\u2AB0',\n 'SucceedsSlantEqual': '\\u227D',\n 'sccue': '\\u227D',\n 'succcurlyeq': '\\u227D',\n 'SucceedsTilde': '\\u227F',\n 'scsim': '\\u227F',\n 'succsim': '\\u227F',\n 'Sum': '\\u2211',\n 'sum': '\\u2211',\n 'Sup': '\\u22D1',\n 'Supset': '\\u22D1',\n 'Superset': '\\u2283',\n 'sup': '\\u2283',\n 'supset': '\\u2283',\n 'SupersetEqual': '\\u2287',\n 'supe': '\\u2287',\n 'supseteq': '\\u2287',\n 'THORN': '\\u00DE',\n 'TRADE': '\\u2122',\n 'trade': '\\u2122',\n 'TSHcy': '\\u040B',\n 'TScy': '\\u0426',\n 'Tab': '\\u0009',\n 'Tau': '\\u03A4',\n 'Tcaron': '\\u0164',\n 'Tcedil': '\\u0162',\n 'Tcy': '\\u0422',\n 'Tfr': '\\uD835\\uDD17',\n 'Therefore': '\\u2234',\n 'there4': '\\u2234',\n 'therefore': '\\u2234',\n 'Theta': '\\u0398',\n 'ThickSpace': '\\u205F\\u200A',\n 'ThinSpace': '\\u2009',\n 'thinsp': '\\u2009',\n 'Tilde': '\\u223C',\n 'sim': '\\u223C',\n 'thicksim': '\\u223C',\n 'thksim': '\\u223C',\n 'TildeEqual': '\\u2243',\n 'sime': '\\u2243',\n 'simeq': '\\u2243',\n 'TildeFullEqual': '\\u2245',\n 'cong': '\\u2245',\n 'TildeTilde': '\\u2248',\n 'ap': '\\u2248',\n 'approx': '\\u2248',\n 'asymp': '\\u2248',\n 'thickapprox': '\\u2248',\n 'thkap': '\\u2248',\n 'Topf': '\\uD835\\uDD4B',\n 'TripleDot': '\\u20DB',\n 'tdot': '\\u20DB',\n 'Tscr': '\\uD835\\uDCAF',\n 'Tstrok': '\\u0166',\n 'Uacute': '\\u00DA',\n 'Uarr': '\\u219F',\n 'Uarrocir': '\\u2949',\n 'Ubrcy': '\\u040E',\n 'Ubreve': '\\u016C',\n 'Ucirc': '\\u00DB',\n 'Ucy': '\\u0423',\n 'Udblac': '\\u0170',\n 'Ufr': '\\uD835\\uDD18',\n 'Ugrave': '\\u00D9',\n 'Umacr': '\\u016A',\n 'UnderBar': '\\u005F',\n 'lowbar': '\\u005F',\n 'UnderBrace': '\\u23DF',\n 'UnderBracket': '\\u23B5',\n 'bbrk': '\\u23B5',\n 'UnderParenthesis': '\\u23DD',\n 'Union': '\\u22C3',\n 'bigcup': '\\u22C3',\n 'xcup': '\\u22C3',\n 'UnionPlus': '\\u228E',\n 'uplus': '\\u228E',\n 'Uogon': '\\u0172',\n 'Uopf': '\\uD835\\uDD4C',\n 'UpArrowBar': '\\u2912',\n 'UpArrowDownArrow': '\\u21C5',\n 'udarr': '\\u21C5',\n 'UpDownArrow': '\\u2195',\n 'updownarrow': '\\u2195',\n 'varr': '\\u2195',\n 'UpEquilibrium': '\\u296E',\n 'udhar': '\\u296E',\n 'UpTee': '\\u22A5',\n 'bot': '\\u22A5',\n 'bottom': '\\u22A5',\n 'perp': '\\u22A5',\n 'UpTeeArrow': '\\u21A5',\n 'mapstoup': '\\u21A5',\n 'UpperLeftArrow': '\\u2196',\n 'nwarr': '\\u2196',\n 'nwarrow': '\\u2196',\n 'UpperRightArrow': '\\u2197',\n 'nearr': '\\u2197',\n 'nearrow': '\\u2197',\n 'Upsi': '\\u03D2',\n 'upsih': '\\u03D2',\n 'Upsilon': '\\u03A5',\n 'Uring': '\\u016E',\n 'Uscr': '\\uD835\\uDCB0',\n 'Utilde': '\\u0168',\n 'Uuml': '\\u00DC',\n 'VDash': '\\u22AB',\n 'Vbar': '\\u2AEB',\n 'Vcy': '\\u0412',\n 'Vdash': '\\u22A9',\n 'Vdashl': '\\u2AE6',\n 'Vee': '\\u22C1',\n 'bigvee': '\\u22C1',\n 'xvee': '\\u22C1',\n 'Verbar': '\\u2016',\n 'Vert': '\\u2016',\n 'VerticalBar': '\\u2223',\n 'mid': '\\u2223',\n 'shortmid': '\\u2223',\n 'smid': '\\u2223',\n 'VerticalLine': '\\u007C',\n 'verbar': '\\u007C',\n 'vert': '\\u007C',\n 'VerticalSeparator': '\\u2758',\n 'VerticalTilde': '\\u2240',\n 'wr': '\\u2240',\n 'wreath': '\\u2240',\n 'VeryThinSpace': '\\u200A',\n 'hairsp': '\\u200A',\n 'Vfr': '\\uD835\\uDD19',\n 'Vopf': '\\uD835\\uDD4D',\n 'Vscr': '\\uD835\\uDCB1',\n 'Vvdash': '\\u22AA',\n 'Wcirc': '\\u0174',\n 'Wedge': '\\u22C0',\n 'bigwedge': '\\u22C0',\n 'xwedge': '\\u22C0',\n 'Wfr': '\\uD835\\uDD1A',\n 'Wopf': '\\uD835\\uDD4E',\n 'Wscr': '\\uD835\\uDCB2',\n 'Xfr': '\\uD835\\uDD1B',\n 'Xi': '\\u039E',\n 'Xopf': '\\uD835\\uDD4F',\n 'Xscr': '\\uD835\\uDCB3',\n 'YAcy': '\\u042F',\n 'YIcy': '\\u0407',\n 'YUcy': '\\u042E',\n 'Yacute': '\\u00DD',\n 'Ycirc': '\\u0176',\n 'Ycy': '\\u042B',\n 'Yfr': '\\uD835\\uDD1C',\n 'Yopf': '\\uD835\\uDD50',\n 'Yscr': '\\uD835\\uDCB4',\n 'Yuml': '\\u0178',\n 'ZHcy': '\\u0416',\n 'Zacute': '\\u0179',\n 'Zcaron': '\\u017D',\n 'Zcy': '\\u0417',\n 'Zdot': '\\u017B',\n 'Zeta': '\\u0396',\n 'Zfr': '\\u2128',\n 'zeetrf': '\\u2128',\n 'Zopf': '\\u2124',\n 'integers': '\\u2124',\n 'Zscr': '\\uD835\\uDCB5',\n 'aacute': '\\u00E1',\n 'abreve': '\\u0103',\n 'ac': '\\u223E',\n 'mstpos': '\\u223E',\n 'acE': '\\u223E\\u0333',\n 'acd': '\\u223F',\n 'acirc': '\\u00E2',\n 'acy': '\\u0430',\n 'aelig': '\\u00E6',\n 'afr': '\\uD835\\uDD1E',\n 'agrave': '\\u00E0',\n 'alefsym': '\\u2135',\n 'aleph': '\\u2135',\n 'alpha': '\\u03B1',\n 'amacr': '\\u0101',\n 'amalg': '\\u2A3F',\n 'and': '\\u2227',\n 'wedge': '\\u2227',\n 'andand': '\\u2A55',\n 'andd': '\\u2A5C',\n 'andslope': '\\u2A58',\n 'andv': '\\u2A5A',\n 'ang': '\\u2220',\n 'angle': '\\u2220',\n 'ange': '\\u29A4',\n 'angmsd': '\\u2221',\n 'measuredangle': '\\u2221',\n 'angmsdaa': '\\u29A8',\n 'angmsdab': '\\u29A9',\n 'angmsdac': '\\u29AA',\n 'angmsdad': '\\u29AB',\n 'angmsdae': '\\u29AC',\n 'angmsdaf': '\\u29AD',\n 'angmsdag': '\\u29AE',\n 'angmsdah': '\\u29AF',\n 'angrt': '\\u221F',\n 'angrtvb': '\\u22BE',\n 'angrtvbd': '\\u299D',\n 'angsph': '\\u2222',\n 'angzarr': '\\u237C',\n 'aogon': '\\u0105',\n 'aopf': '\\uD835\\uDD52',\n 'apE': '\\u2A70',\n 'apacir': '\\u2A6F',\n 'ape': '\\u224A',\n 'approxeq': '\\u224A',\n 'apid': '\\u224B',\n 'apos': '\\u0027',\n 'aring': '\\u00E5',\n 'ascr': '\\uD835\\uDCB6',\n 'ast': '\\u002A',\n 'midast': '\\u002A',\n 'atilde': '\\u00E3',\n 'auml': '\\u00E4',\n 'awint': '\\u2A11',\n 'bNot': '\\u2AED',\n 'backcong': '\\u224C',\n 'bcong': '\\u224C',\n 'backepsilon': '\\u03F6',\n 'bepsi': '\\u03F6',\n 'backprime': '\\u2035',\n 'bprime': '\\u2035',\n 'backsim': '\\u223D',\n 'bsim': '\\u223D',\n 'backsimeq': '\\u22CD',\n 'bsime': '\\u22CD',\n 'barvee': '\\u22BD',\n 'barwed': '\\u2305',\n 'barwedge': '\\u2305',\n 'bbrktbrk': '\\u23B6',\n 'bcy': '\\u0431',\n 'bdquo': '\\u201E',\n 'ldquor': '\\u201E',\n 'bemptyv': '\\u29B0',\n 'beta': '\\u03B2',\n 'beth': '\\u2136',\n 'between': '\\u226C',\n 'twixt': '\\u226C',\n 'bfr': '\\uD835\\uDD1F',\n 'bigcirc': '\\u25EF',\n 'xcirc': '\\u25EF',\n 'bigodot': '\\u2A00',\n 'xodot': '\\u2A00',\n 'bigoplus': '\\u2A01',\n 'xoplus': '\\u2A01',\n 'bigotimes': '\\u2A02',\n 'xotime': '\\u2A02',\n 'bigsqcup': '\\u2A06',\n 'xsqcup': '\\u2A06',\n 'bigstar': '\\u2605',\n 'starf': '\\u2605',\n 'bigtriangledown': '\\u25BD',\n 'xdtri': '\\u25BD',\n 'bigtriangleup': '\\u25B3',\n 'xutri': '\\u25B3',\n 'biguplus': '\\u2A04',\n 'xuplus': '\\u2A04',\n 'bkarow': '\\u290D',\n 'rbarr': '\\u290D',\n 'blacklozenge': '\\u29EB',\n 'lozf': '\\u29EB',\n 'blacktriangle': '\\u25B4',\n 'utrif': '\\u25B4',\n 'blacktriangledown': '\\u25BE',\n 'dtrif': '\\u25BE',\n 'blacktriangleleft': '\\u25C2',\n 'ltrif': '\\u25C2',\n 'blacktriangleright': '\\u25B8',\n 'rtrif': '\\u25B8',\n 'blank': '\\u2423',\n 'blk12': '\\u2592',\n 'blk14': '\\u2591',\n 'blk34': '\\u2593',\n 'block': '\\u2588',\n 'bne': '\\u003D\\u20E5',\n 'bnequiv': '\\u2261\\u20E5',\n 'bnot': '\\u2310',\n 'bopf': '\\uD835\\uDD53',\n 'bowtie': '\\u22C8',\n 'boxDL': '\\u2557',\n 'boxDR': '\\u2554',\n 'boxDl': '\\u2556',\n 'boxDr': '\\u2553',\n 'boxH': '\\u2550',\n 'boxHD': '\\u2566',\n 'boxHU': '\\u2569',\n 'boxHd': '\\u2564',\n 'boxHu': '\\u2567',\n 'boxUL': '\\u255D',\n 'boxUR': '\\u255A',\n 'boxUl': '\\u255C',\n 'boxUr': '\\u2559',\n 'boxV': '\\u2551',\n 'boxVH': '\\u256C',\n 'boxVL': '\\u2563',\n 'boxVR': '\\u2560',\n 'boxVh': '\\u256B',\n 'boxVl': '\\u2562',\n 'boxVr': '\\u255F',\n 'boxbox': '\\u29C9',\n 'boxdL': '\\u2555',\n 'boxdR': '\\u2552',\n 'boxdl': '\\u2510',\n 'boxdr': '\\u250C',\n 'boxhD': '\\u2565',\n 'boxhU': '\\u2568',\n 'boxhd': '\\u252C',\n 'boxhu': '\\u2534',\n 'boxminus': '\\u229F',\n 'minusb': '\\u229F',\n 'boxplus': '\\u229E',\n 'plusb': '\\u229E',\n 'boxtimes': '\\u22A0',\n 'timesb': '\\u22A0',\n 'boxuL': '\\u255B',\n 'boxuR': '\\u2558',\n 'boxul': '\\u2518',\n 'boxur': '\\u2514',\n 'boxv': '\\u2502',\n 'boxvH': '\\u256A',\n 'boxvL': '\\u2561',\n 'boxvR': '\\u255E',\n 'boxvh': '\\u253C',\n 'boxvl': '\\u2524',\n 'boxvr': '\\u251C',\n 'brvbar': '\\u00A6',\n 'bscr': '\\uD835\\uDCB7',\n 'bsemi': '\\u204F',\n 'bsol': '\\u005C',\n 'bsolb': '\\u29C5',\n 'bsolhsub': '\\u27C8',\n 'bull': '\\u2022',\n 'bullet': '\\u2022',\n 'bumpE': '\\u2AAE',\n 'cacute': '\\u0107',\n 'cap': '\\u2229',\n 'capand': '\\u2A44',\n 'capbrcup': '\\u2A49',\n 'capcap': '\\u2A4B',\n 'capcup': '\\u2A47',\n 'capdot': '\\u2A40',\n 'caps': '\\u2229\\uFE00',\n 'caret': '\\u2041',\n 'ccaps': '\\u2A4D',\n 'ccaron': '\\u010D',\n 'ccedil': '\\u00E7',\n 'ccirc': '\\u0109',\n 'ccups': '\\u2A4C',\n 'ccupssm': '\\u2A50',\n 'cdot': '\\u010B',\n 'cemptyv': '\\u29B2',\n 'cent': '\\u00A2',\n 'cfr': '\\uD835\\uDD20',\n 'chcy': '\\u0447',\n 'check': '\\u2713',\n 'checkmark': '\\u2713',\n 'chi': '\\u03C7',\n 'cir': '\\u25CB',\n 'cirE': '\\u29C3',\n 'circ': '\\u02C6',\n 'circeq': '\\u2257',\n 'cire': '\\u2257',\n 'circlearrowleft': '\\u21BA',\n 'olarr': '\\u21BA',\n 'circlearrowright': '\\u21BB',\n 'orarr': '\\u21BB',\n 'circledS': '\\u24C8',\n 'oS': '\\u24C8',\n 'circledast': '\\u229B',\n 'oast': '\\u229B',\n 'circledcirc': '\\u229A',\n 'ocir': '\\u229A',\n 'circleddash': '\\u229D',\n 'odash': '\\u229D',\n 'cirfnint': '\\u2A10',\n 'cirmid': '\\u2AEF',\n 'cirscir': '\\u29C2',\n 'clubs': '\\u2663',\n 'clubsuit': '\\u2663',\n 'colon': '\\u003A',\n 'comma': '\\u002C',\n 'commat': '\\u0040',\n 'comp': '\\u2201',\n 'complement': '\\u2201',\n 'congdot': '\\u2A6D',\n 'copf': '\\uD835\\uDD54',\n 'copysr': '\\u2117',\n 'crarr': '\\u21B5',\n 'cross': '\\u2717',\n 'cscr': '\\uD835\\uDCB8',\n 'csub': '\\u2ACF',\n 'csube': '\\u2AD1',\n 'csup': '\\u2AD0',\n 'csupe': '\\u2AD2',\n 'ctdot': '\\u22EF',\n 'cudarrl': '\\u2938',\n 'cudarrr': '\\u2935',\n 'cuepr': '\\u22DE',\n 'curlyeqprec': '\\u22DE',\n 'cuesc': '\\u22DF',\n 'curlyeqsucc': '\\u22DF',\n 'cularr': '\\u21B6',\n 'curvearrowleft': '\\u21B6',\n 'cularrp': '\\u293D',\n 'cup': '\\u222A',\n 'cupbrcap': '\\u2A48',\n 'cupcap': '\\u2A46',\n 'cupcup': '\\u2A4A',\n 'cupdot': '\\u228D',\n 'cupor': '\\u2A45',\n 'cups': '\\u222A\\uFE00',\n 'curarr': '\\u21B7',\n 'curvearrowright': '\\u21B7',\n 'curarrm': '\\u293C',\n 'curlyvee': '\\u22CE',\n 'cuvee': '\\u22CE',\n 'curlywedge': '\\u22CF',\n 'cuwed': '\\u22CF',\n 'curren': '\\u00A4',\n 'cwint': '\\u2231',\n 'cylcty': '\\u232D',\n 'dHar': '\\u2965',\n 'dagger': '\\u2020',\n 'daleth': '\\u2138',\n 'dash': '\\u2010',\n 'hyphen': '\\u2010',\n 'dbkarow': '\\u290F',\n 'rBarr': '\\u290F',\n 'dcaron': '\\u010F',\n 'dcy': '\\u0434',\n 'ddarr': '\\u21CA',\n 'downdownarrows': '\\u21CA',\n 'ddotseq': '\\u2A77',\n 'eDDot': '\\u2A77',\n 'deg': '\\u00B0',\n 'delta': '\\u03B4',\n 'demptyv': '\\u29B1',\n 'dfisht': '\\u297F',\n 'dfr': '\\uD835\\uDD21',\n 'diamondsuit': '\\u2666',\n 'diams': '\\u2666',\n 'digamma': '\\u03DD',\n 'gammad': '\\u03DD',\n 'disin': '\\u22F2',\n 'div': '\\u00F7',\n 'divide': '\\u00F7',\n 'divideontimes': '\\u22C7',\n 'divonx': '\\u22C7',\n 'djcy': '\\u0452',\n 'dlcorn': '\\u231E',\n 'llcorner': '\\u231E',\n 'dlcrop': '\\u230D',\n 'dollar': '\\u0024',\n 'dopf': '\\uD835\\uDD55',\n 'doteqdot': '\\u2251',\n 'eDot': '\\u2251',\n 'dotminus': '\\u2238',\n 'minusd': '\\u2238',\n 'dotplus': '\\u2214',\n 'plusdo': '\\u2214',\n 'dotsquare': '\\u22A1',\n 'sdotb': '\\u22A1',\n 'drcorn': '\\u231F',\n 'lrcorner': '\\u231F',\n 'drcrop': '\\u230C',\n 'dscr': '\\uD835\\uDCB9',\n 'dscy': '\\u0455',\n 'dsol': '\\u29F6',\n 'dstrok': '\\u0111',\n 'dtdot': '\\u22F1',\n 'dtri': '\\u25BF',\n 'triangledown': '\\u25BF',\n 'dwangle': '\\u29A6',\n 'dzcy': '\\u045F',\n 'dzigrarr': '\\u27FF',\n 'eacute': '\\u00E9',\n 'easter': '\\u2A6E',\n 'ecaron': '\\u011B',\n 'ecir': '\\u2256',\n 'eqcirc': '\\u2256',\n 'ecirc': '\\u00EA',\n 'ecolon': '\\u2255',\n 'eqcolon': '\\u2255',\n 'ecy': '\\u044D',\n 'edot': '\\u0117',\n 'efDot': '\\u2252',\n 'fallingdotseq': '\\u2252',\n 'efr': '\\uD835\\uDD22',\n 'eg': '\\u2A9A',\n 'egrave': '\\u00E8',\n 'egs': '\\u2A96',\n 'eqslantgtr': '\\u2A96',\n 'egsdot': '\\u2A98',\n 'el': '\\u2A99',\n 'elinters': '\\u23E7',\n 'ell': '\\u2113',\n 'els': '\\u2A95',\n 'eqslantless': '\\u2A95',\n 'elsdot': '\\u2A97',\n 'emacr': '\\u0113',\n 'empty': '\\u2205',\n 'emptyset': '\\u2205',\n 'emptyv': '\\u2205',\n 'varnothing': '\\u2205',\n 'emsp13': '\\u2004',\n 'emsp14': '\\u2005',\n 'emsp': '\\u2003',\n 'eng': '\\u014B',\n 'ensp': '\\u2002',\n 'eogon': '\\u0119',\n 'eopf': '\\uD835\\uDD56',\n 'epar': '\\u22D5',\n 'eparsl': '\\u29E3',\n 'eplus': '\\u2A71',\n 'epsi': '\\u03B5',\n 'epsilon': '\\u03B5',\n 'epsiv': '\\u03F5',\n 'straightepsilon': '\\u03F5',\n 'varepsilon': '\\u03F5',\n 'equals': '\\u003D',\n 'equest': '\\u225F',\n 'questeq': '\\u225F',\n 'equivDD': '\\u2A78',\n 'eqvparsl': '\\u29E5',\n 'erDot': '\\u2253',\n 'risingdotseq': '\\u2253',\n 'erarr': '\\u2971',\n 'escr': '\\u212F',\n 'eta': '\\u03B7',\n 'eth': '\\u00F0',\n 'euml': '\\u00EB',\n 'euro': '\\u20AC',\n 'excl': '\\u0021',\n 'fcy': '\\u0444',\n 'female': '\\u2640',\n 'ffilig': '\\uFB03',\n 'fflig': '\\uFB00',\n 'ffllig': '\\uFB04',\n 'ffr': '\\uD835\\uDD23',\n 'filig': '\\uFB01',\n 'fjlig': '\\u0066\\u006A',\n 'flat': '\\u266D',\n 'fllig': '\\uFB02',\n 'fltns': '\\u25B1',\n 'fnof': '\\u0192',\n 'fopf': '\\uD835\\uDD57',\n 'fork': '\\u22D4',\n 'pitchfork': '\\u22D4',\n 'forkv': '\\u2AD9',\n 'fpartint': '\\u2A0D',\n 'frac12': '\\u00BD',\n 'half': '\\u00BD',\n 'frac13': '\\u2153',\n 'frac14': '\\u00BC',\n 'frac15': '\\u2155',\n 'frac16': '\\u2159',\n 'frac18': '\\u215B',\n 'frac23': '\\u2154',\n 'frac25': '\\u2156',\n 'frac34': '\\u00BE',\n 'frac35': '\\u2157',\n 'frac38': '\\u215C',\n 'frac45': '\\u2158',\n 'frac56': '\\u215A',\n 'frac58': '\\u215D',\n 'frac78': '\\u215E',\n 'frasl': '\\u2044',\n 'frown': '\\u2322',\n 'sfrown': '\\u2322',\n 'fscr': '\\uD835\\uDCBB',\n 'gEl': '\\u2A8C',\n 'gtreqqless': '\\u2A8C',\n 'gacute': '\\u01F5',\n 'gamma': '\\u03B3',\n 'gap': '\\u2A86',\n 'gtrapprox': '\\u2A86',\n 'gbreve': '\\u011F',\n 'gcirc': '\\u011D',\n 'gcy': '\\u0433',\n 'gdot': '\\u0121',\n 'gescc': '\\u2AA9',\n 'gesdot': '\\u2A80',\n 'gesdoto': '\\u2A82',\n 'gesdotol': '\\u2A84',\n 'gesl': '\\u22DB\\uFE00',\n 'gesles': '\\u2A94',\n 'gfr': '\\uD835\\uDD24',\n 'gimel': '\\u2137',\n 'gjcy': '\\u0453',\n 'glE': '\\u2A92',\n 'gla': '\\u2AA5',\n 'glj': '\\u2AA4',\n 'gnE': '\\u2269',\n 'gneqq': '\\u2269',\n 'gnap': '\\u2A8A',\n 'gnapprox': '\\u2A8A',\n 'gne': '\\u2A88',\n 'gneq': '\\u2A88',\n 'gnsim': '\\u22E7',\n 'gopf': '\\uD835\\uDD58',\n 'gscr': '\\u210A',\n 'gsime': '\\u2A8E',\n 'gsiml': '\\u2A90',\n 'gtcc': '\\u2AA7',\n 'gtcir': '\\u2A7A',\n 'gtdot': '\\u22D7',\n 'gtrdot': '\\u22D7',\n 'gtlPar': '\\u2995',\n 'gtquest': '\\u2A7C',\n 'gtrarr': '\\u2978',\n 'gvertneqq': '\\u2269\\uFE00',\n 'gvnE': '\\u2269\\uFE00',\n 'hardcy': '\\u044A',\n 'harrcir': '\\u2948',\n 'harrw': '\\u21AD',\n 'leftrightsquigarrow': '\\u21AD',\n 'hbar': '\\u210F',\n 'hslash': '\\u210F',\n 'planck': '\\u210F',\n 'plankv': '\\u210F',\n 'hcirc': '\\u0125',\n 'hearts': '\\u2665',\n 'heartsuit': '\\u2665',\n 'hellip': '\\u2026',\n 'mldr': '\\u2026',\n 'hercon': '\\u22B9',\n 'hfr': '\\uD835\\uDD25',\n 'hksearow': '\\u2925',\n 'searhk': '\\u2925',\n 'hkswarow': '\\u2926',\n 'swarhk': '\\u2926',\n 'hoarr': '\\u21FF',\n 'homtht': '\\u223B',\n 'hookleftarrow': '\\u21A9',\n 'larrhk': '\\u21A9',\n 'hookrightarrow': '\\u21AA',\n 'rarrhk': '\\u21AA',\n 'hopf': '\\uD835\\uDD59',\n 'horbar': '\\u2015',\n 'hscr': '\\uD835\\uDCBD',\n 'hstrok': '\\u0127',\n 'hybull': '\\u2043',\n 'iacute': '\\u00ED',\n 'icirc': '\\u00EE',\n 'icy': '\\u0438',\n 'iecy': '\\u0435',\n 'iexcl': '\\u00A1',\n 'ifr': '\\uD835\\uDD26',\n 'igrave': '\\u00EC',\n 'iiiint': '\\u2A0C',\n 'qint': '\\u2A0C',\n 'iiint': '\\u222D',\n 'tint': '\\u222D',\n 'iinfin': '\\u29DC',\n 'iiota': '\\u2129',\n 'ijlig': '\\u0133',\n 'imacr': '\\u012B',\n 'imath': '\\u0131',\n 'inodot': '\\u0131',\n 'imof': '\\u22B7',\n 'imped': '\\u01B5',\n 'incare': '\\u2105',\n 'infin': '\\u221E',\n 'infintie': '\\u29DD',\n 'intcal': '\\u22BA',\n 'intercal': '\\u22BA',\n 'intlarhk': '\\u2A17',\n 'intprod': '\\u2A3C',\n 'iprod': '\\u2A3C',\n 'iocy': '\\u0451',\n 'iogon': '\\u012F',\n 'iopf': '\\uD835\\uDD5A',\n 'iota': '\\u03B9',\n 'iquest': '\\u00BF',\n 'iscr': '\\uD835\\uDCBE',\n 'isinE': '\\u22F9',\n 'isindot': '\\u22F5',\n 'isins': '\\u22F4',\n 'isinsv': '\\u22F3',\n 'itilde': '\\u0129',\n 'iukcy': '\\u0456',\n 'iuml': '\\u00EF',\n 'jcirc': '\\u0135',\n 'jcy': '\\u0439',\n 'jfr': '\\uD835\\uDD27',\n 'jmath': '\\u0237',\n 'jopf': '\\uD835\\uDD5B',\n 'jscr': '\\uD835\\uDCBF',\n 'jsercy': '\\u0458',\n 'jukcy': '\\u0454',\n 'kappa': '\\u03BA',\n 'kappav': '\\u03F0',\n 'varkappa': '\\u03F0',\n 'kcedil': '\\u0137',\n 'kcy': '\\u043A',\n 'kfr': '\\uD835\\uDD28',\n 'kgreen': '\\u0138',\n 'khcy': '\\u0445',\n 'kjcy': '\\u045C',\n 'kopf': '\\uD835\\uDD5C',\n 'kscr': '\\uD835\\uDCC0',\n 'lAtail': '\\u291B',\n 'lBarr': '\\u290E',\n 'lEg': '\\u2A8B',\n 'lesseqqgtr': '\\u2A8B',\n 'lHar': '\\u2962',\n 'lacute': '\\u013A',\n 'laemptyv': '\\u29B4',\n 'lambda': '\\u03BB',\n 'langd': '\\u2991',\n 'lap': '\\u2A85',\n 'lessapprox': '\\u2A85',\n 'laquo': '\\u00AB',\n 'larrbfs': '\\u291F',\n 'larrfs': '\\u291D',\n 'larrlp': '\\u21AB',\n 'looparrowleft': '\\u21AB',\n 'larrpl': '\\u2939',\n 'larrsim': '\\u2973',\n 'larrtl': '\\u21A2',\n 'leftarrowtail': '\\u21A2',\n 'lat': '\\u2AAB',\n 'latail': '\\u2919',\n 'late': '\\u2AAD',\n 'lates': '\\u2AAD\\uFE00',\n 'lbarr': '\\u290C',\n 'lbbrk': '\\u2772',\n 'lbrace': '\\u007B',\n 'lcub': '\\u007B',\n 'lbrack': '\\u005B',\n 'lsqb': '\\u005B',\n 'lbrke': '\\u298B',\n 'lbrksld': '\\u298F',\n 'lbrkslu': '\\u298D',\n 'lcaron': '\\u013E',\n 'lcedil': '\\u013C',\n 'lcy': '\\u043B',\n 'ldca': '\\u2936',\n 'ldrdhar': '\\u2967',\n 'ldrushar': '\\u294B',\n 'ldsh': '\\u21B2',\n 'le': '\\u2264',\n 'leq': '\\u2264',\n 'leftleftarrows': '\\u21C7',\n 'llarr': '\\u21C7',\n 'leftthreetimes': '\\u22CB',\n 'lthree': '\\u22CB',\n 'lescc': '\\u2AA8',\n 'lesdot': '\\u2A7F',\n 'lesdoto': '\\u2A81',\n 'lesdotor': '\\u2A83',\n 'lesg': '\\u22DA\\uFE00',\n 'lesges': '\\u2A93',\n 'lessdot': '\\u22D6',\n 'ltdot': '\\u22D6',\n 'lfisht': '\\u297C',\n 'lfr': '\\uD835\\uDD29',\n 'lgE': '\\u2A91',\n 'lharul': '\\u296A',\n 'lhblk': '\\u2584',\n 'ljcy': '\\u0459',\n 'llhard': '\\u296B',\n 'lltri': '\\u25FA',\n 'lmidot': '\\u0140',\n 'lmoust': '\\u23B0',\n 'lmoustache': '\\u23B0',\n 'lnE': '\\u2268',\n 'lneqq': '\\u2268',\n 'lnap': '\\u2A89',\n 'lnapprox': '\\u2A89',\n 'lne': '\\u2A87',\n 'lneq': '\\u2A87',\n 'lnsim': '\\u22E6',\n 'loang': '\\u27EC',\n 'loarr': '\\u21FD',\n 'longmapsto': '\\u27FC',\n 'xmap': '\\u27FC',\n 'looparrowright': '\\u21AC',\n 'rarrlp': '\\u21AC',\n 'lopar': '\\u2985',\n 'lopf': '\\uD835\\uDD5D',\n 'loplus': '\\u2A2D',\n 'lotimes': '\\u2A34',\n 'lowast': '\\u2217',\n 'loz': '\\u25CA',\n 'lozenge': '\\u25CA',\n 'lpar': '\\u0028',\n 'lparlt': '\\u2993',\n 'lrhard': '\\u296D',\n 'lrm': '\\u200E',\n 'lrtri': '\\u22BF',\n 'lsaquo': '\\u2039',\n 'lscr': '\\uD835\\uDCC1',\n 'lsime': '\\u2A8D',\n 'lsimg': '\\u2A8F',\n 'lsquor': '\\u201A',\n 'sbquo': '\\u201A',\n 'lstrok': '\\u0142',\n 'ltcc': '\\u2AA6',\n 'ltcir': '\\u2A79',\n 'ltimes': '\\u22C9',\n 'ltlarr': '\\u2976',\n 'ltquest': '\\u2A7B',\n 'ltrPar': '\\u2996',\n 'ltri': '\\u25C3',\n 'triangleleft': '\\u25C3',\n 'lurdshar': '\\u294A',\n 'luruhar': '\\u2966',\n 'lvertneqq': '\\u2268\\uFE00',\n 'lvnE': '\\u2268\\uFE00',\n 'mDDot': '\\u223A',\n 'macr': '\\u00AF',\n 'strns': '\\u00AF',\n 'male': '\\u2642',\n 'malt': '\\u2720',\n 'maltese': '\\u2720',\n 'marker': '\\u25AE',\n 'mcomma': '\\u2A29',\n 'mcy': '\\u043C',\n 'mdash': '\\u2014',\n 'mfr': '\\uD835\\uDD2A',\n 'mho': '\\u2127',\n 'micro': '\\u00B5',\n 'midcir': '\\u2AF0',\n 'minus': '\\u2212',\n 'minusdu': '\\u2A2A',\n 'mlcp': '\\u2ADB',\n 'models': '\\u22A7',\n 'mopf': '\\uD835\\uDD5E',\n 'mscr': '\\uD835\\uDCC2',\n 'mu': '\\u03BC',\n 'multimap': '\\u22B8',\n 'mumap': '\\u22B8',\n 'nGg': '\\u22D9\\u0338',\n 'nGt': '\\u226B\\u20D2',\n 'nLeftarrow': '\\u21CD',\n 'nlArr': '\\u21CD',\n 'nLeftrightarrow': '\\u21CE',\n 'nhArr': '\\u21CE',\n 'nLl': '\\u22D8\\u0338',\n 'nLt': '\\u226A\\u20D2',\n 'nRightarrow': '\\u21CF',\n 'nrArr': '\\u21CF',\n 'nVDash': '\\u22AF',\n 'nVdash': '\\u22AE',\n 'nacute': '\\u0144',\n 'nang': '\\u2220\\u20D2',\n 'napE': '\\u2A70\\u0338',\n 'napid': '\\u224B\\u0338',\n 'napos': '\\u0149',\n 'natur': '\\u266E',\n 'natural': '\\u266E',\n 'ncap': '\\u2A43',\n 'ncaron': '\\u0148',\n 'ncedil': '\\u0146',\n 'ncongdot': '\\u2A6D\\u0338',\n 'ncup': '\\u2A42',\n 'ncy': '\\u043D',\n 'ndash': '\\u2013',\n 'neArr': '\\u21D7',\n 'nearhk': '\\u2924',\n 'nedot': '\\u2250\\u0338',\n 'nesear': '\\u2928',\n 'toea': '\\u2928',\n 'nfr': '\\uD835\\uDD2B',\n 'nharr': '\\u21AE',\n 'nleftrightarrow': '\\u21AE',\n 'nhpar': '\\u2AF2',\n 'nis': '\\u22FC',\n 'nisd': '\\u22FA',\n 'njcy': '\\u045A',\n 'nlE': '\\u2266\\u0338',\n 'nleqq': '\\u2266\\u0338',\n 'nlarr': '\\u219A',\n 'nleftarrow': '\\u219A',\n 'nldr': '\\u2025',\n 'nopf': '\\uD835\\uDD5F',\n 'not': '\\u00AC',\n 'notinE': '\\u22F9\\u0338',\n 'notindot': '\\u22F5\\u0338',\n 'notinvb': '\\u22F7',\n 'notinvc': '\\u22F6',\n 'notnivb': '\\u22FE',\n 'notnivc': '\\u22FD',\n 'nparsl': '\\u2AFD\\u20E5',\n 'npart': '\\u2202\\u0338',\n 'npolint': '\\u2A14',\n 'nrarr': '\\u219B',\n 'nrightarrow': '\\u219B',\n 'nrarrc': '\\u2933\\u0338',\n 'nrarrw': '\\u219D\\u0338',\n 'nscr': '\\uD835\\uDCC3',\n 'nsub': '\\u2284',\n 'nsubE': '\\u2AC5\\u0338',\n 'nsubseteqq': '\\u2AC5\\u0338',\n 'nsup': '\\u2285',\n 'nsupE': '\\u2AC6\\u0338',\n 'nsupseteqq': '\\u2AC6\\u0338',\n 'ntilde': '\\u00F1',\n 'nu': '\\u03BD',\n 'num': '\\u0023',\n 'numero': '\\u2116',\n 'numsp': '\\u2007',\n 'nvDash': '\\u22AD',\n 'nvHarr': '\\u2904',\n 'nvap': '\\u224D\\u20D2',\n 'nvdash': '\\u22AC',\n 'nvge': '\\u2265\\u20D2',\n 'nvgt': '\\u003E\\u20D2',\n 'nvinfin': '\\u29DE',\n 'nvlArr': '\\u2902',\n 'nvle': '\\u2264\\u20D2',\n 'nvlt': '\\u003C\\u20D2',\n 'nvltrie': '\\u22B4\\u20D2',\n 'nvrArr': '\\u2903',\n 'nvrtrie': '\\u22B5\\u20D2',\n 'nvsim': '\\u223C\\u20D2',\n 'nwArr': '\\u21D6',\n 'nwarhk': '\\u2923',\n 'nwnear': '\\u2927',\n 'oacute': '\\u00F3',\n 'ocirc': '\\u00F4',\n 'ocy': '\\u043E',\n 'odblac': '\\u0151',\n 'odiv': '\\u2A38',\n 'odsold': '\\u29BC',\n 'oelig': '\\u0153',\n 'ofcir': '\\u29BF',\n 'ofr': '\\uD835\\uDD2C',\n 'ogon': '\\u02DB',\n 'ograve': '\\u00F2',\n 'ogt': '\\u29C1',\n 'ohbar': '\\u29B5',\n 'olcir': '\\u29BE',\n 'olcross': '\\u29BB',\n 'olt': '\\u29C0',\n 'omacr': '\\u014D',\n 'omega': '\\u03C9',\n 'omicron': '\\u03BF',\n 'omid': '\\u29B6',\n 'oopf': '\\uD835\\uDD60',\n 'opar': '\\u29B7',\n 'operp': '\\u29B9',\n 'or': '\\u2228',\n 'vee': '\\u2228',\n 'ord': '\\u2A5D',\n 'order': '\\u2134',\n 'orderof': '\\u2134',\n 'oscr': '\\u2134',\n 'ordf': '\\u00AA',\n 'ordm': '\\u00BA',\n 'origof': '\\u22B6',\n 'oror': '\\u2A56',\n 'orslope': '\\u2A57',\n 'orv': '\\u2A5B',\n 'oslash': '\\u00F8',\n 'osol': '\\u2298',\n 'otilde': '\\u00F5',\n 'otimesas': '\\u2A36',\n 'ouml': '\\u00F6',\n 'ovbar': '\\u233D',\n 'para': '\\u00B6',\n 'parsim': '\\u2AF3',\n 'parsl': '\\u2AFD',\n 'pcy': '\\u043F',\n 'percnt': '\\u0025',\n 'period': '\\u002E',\n 'permil': '\\u2030',\n 'pertenk': '\\u2031',\n 'pfr': '\\uD835\\uDD2D',\n 'phi': '\\u03C6',\n 'phiv': '\\u03D5',\n 'straightphi': '\\u03D5',\n 'varphi': '\\u03D5',\n 'phone': '\\u260E',\n 'pi': '\\u03C0',\n 'piv': '\\u03D6',\n 'varpi': '\\u03D6',\n 'planckh': '\\u210E',\n 'plus': '\\u002B',\n 'plusacir': '\\u2A23',\n 'pluscir': '\\u2A22',\n 'plusdu': '\\u2A25',\n 'pluse': '\\u2A72',\n 'plussim': '\\u2A26',\n 'plustwo': '\\u2A27',\n 'pointint': '\\u2A15',\n 'popf': '\\uD835\\uDD61',\n 'pound': '\\u00A3',\n 'prE': '\\u2AB3',\n 'prap': '\\u2AB7',\n 'precapprox': '\\u2AB7',\n 'precnapprox': '\\u2AB9',\n 'prnap': '\\u2AB9',\n 'precneqq': '\\u2AB5',\n 'prnE': '\\u2AB5',\n 'precnsim': '\\u22E8',\n 'prnsim': '\\u22E8',\n 'prime': '\\u2032',\n 'profalar': '\\u232E',\n 'profline': '\\u2312',\n 'profsurf': '\\u2313',\n 'prurel': '\\u22B0',\n 'pscr': '\\uD835\\uDCC5',\n 'psi': '\\u03C8',\n 'puncsp': '\\u2008',\n 'qfr': '\\uD835\\uDD2E',\n 'qopf': '\\uD835\\uDD62',\n 'qprime': '\\u2057',\n 'qscr': '\\uD835\\uDCC6',\n 'quatint': '\\u2A16',\n 'quest': '\\u003F',\n 'rAtail': '\\u291C',\n 'rHar': '\\u2964',\n 'race': '\\u223D\\u0331',\n 'racute': '\\u0155',\n 'raemptyv': '\\u29B3',\n 'rangd': '\\u2992',\n 'range': '\\u29A5',\n 'raquo': '\\u00BB',\n 'rarrap': '\\u2975',\n 'rarrbfs': '\\u2920',\n 'rarrc': '\\u2933',\n 'rarrfs': '\\u291E',\n 'rarrpl': '\\u2945',\n 'rarrsim': '\\u2974',\n 'rarrtl': '\\u21A3',\n 'rightarrowtail': '\\u21A3',\n 'rarrw': '\\u219D',\n 'rightsquigarrow': '\\u219D',\n 'ratail': '\\u291A',\n 'ratio': '\\u2236',\n 'rbbrk': '\\u2773',\n 'rbrace': '\\u007D',\n 'rcub': '\\u007D',\n 'rbrack': '\\u005D',\n 'rsqb': '\\u005D',\n 'rbrke': '\\u298C',\n 'rbrksld': '\\u298E',\n 'rbrkslu': '\\u2990',\n 'rcaron': '\\u0159',\n 'rcedil': '\\u0157',\n 'rcy': '\\u0440',\n 'rdca': '\\u2937',\n 'rdldhar': '\\u2969',\n 'rdsh': '\\u21B3',\n 'rect': '\\u25AD',\n 'rfisht': '\\u297D',\n 'rfr': '\\uD835\\uDD2F',\n 'rharul': '\\u296C',\n 'rho': '\\u03C1',\n 'rhov': '\\u03F1',\n 'varrho': '\\u03F1',\n 'rightrightarrows': '\\u21C9',\n 'rrarr': '\\u21C9',\n 'rightthreetimes': '\\u22CC',\n 'rthree': '\\u22CC',\n 'ring': '\\u02DA',\n 'rlm': '\\u200F',\n 'rmoust': '\\u23B1',\n 'rmoustache': '\\u23B1',\n 'rnmid': '\\u2AEE',\n 'roang': '\\u27ED',\n 'roarr': '\\u21FE',\n 'ropar': '\\u2986',\n 'ropf': '\\uD835\\uDD63',\n 'roplus': '\\u2A2E',\n 'rotimes': '\\u2A35',\n 'rpar': '\\u0029',\n 'rpargt': '\\u2994',\n 'rppolint': '\\u2A12',\n 'rsaquo': '\\u203A',\n 'rscr': '\\uD835\\uDCC7',\n 'rtimes': '\\u22CA',\n 'rtri': '\\u25B9',\n 'triangleright': '\\u25B9',\n 'rtriltri': '\\u29CE',\n 'ruluhar': '\\u2968',\n 'rx': '\\u211E',\n 'sacute': '\\u015B',\n 'scE': '\\u2AB4',\n 'scap': '\\u2AB8',\n 'succapprox': '\\u2AB8',\n 'scaron': '\\u0161',\n 'scedil': '\\u015F',\n 'scirc': '\\u015D',\n 'scnE': '\\u2AB6',\n 'succneqq': '\\u2AB6',\n 'scnap': '\\u2ABA',\n 'succnapprox': '\\u2ABA',\n 'scnsim': '\\u22E9',\n 'succnsim': '\\u22E9',\n 'scpolint': '\\u2A13',\n 'scy': '\\u0441',\n 'sdot': '\\u22C5',\n 'sdote': '\\u2A66',\n 'seArr': '\\u21D8',\n 'sect': '\\u00A7',\n 'semi': '\\u003B',\n 'seswar': '\\u2929',\n 'tosa': '\\u2929',\n 'sext': '\\u2736',\n 'sfr': '\\uD835\\uDD30',\n 'sharp': '\\u266F',\n 'shchcy': '\\u0449',\n 'shcy': '\\u0448',\n 'shy': '\\u00AD',\n 'sigma': '\\u03C3',\n 'sigmaf': '\\u03C2',\n 'sigmav': '\\u03C2',\n 'varsigma': '\\u03C2',\n 'simdot': '\\u2A6A',\n 'simg': '\\u2A9E',\n 'simgE': '\\u2AA0',\n 'siml': '\\u2A9D',\n 'simlE': '\\u2A9F',\n 'simne': '\\u2246',\n 'simplus': '\\u2A24',\n 'simrarr': '\\u2972',\n 'smashp': '\\u2A33',\n 'smeparsl': '\\u29E4',\n 'smile': '\\u2323',\n 'ssmile': '\\u2323',\n 'smt': '\\u2AAA',\n 'smte': '\\u2AAC',\n 'smtes': '\\u2AAC\\uFE00',\n 'softcy': '\\u044C',\n 'sol': '\\u002F',\n 'solb': '\\u29C4',\n 'solbar': '\\u233F',\n 'sopf': '\\uD835\\uDD64',\n 'spades': '\\u2660',\n 'spadesuit': '\\u2660',\n 'sqcaps': '\\u2293\\uFE00',\n 'sqcups': '\\u2294\\uFE00',\n 'sscr': '\\uD835\\uDCC8',\n 'star': '\\u2606',\n 'sub': '\\u2282',\n 'subset': '\\u2282',\n 'subE': '\\u2AC5',\n 'subseteqq': '\\u2AC5',\n 'subdot': '\\u2ABD',\n 'subedot': '\\u2AC3',\n 'submult': '\\u2AC1',\n 'subnE': '\\u2ACB',\n 'subsetneqq': '\\u2ACB',\n 'subne': '\\u228A',\n 'subsetneq': '\\u228A',\n 'subplus': '\\u2ABF',\n 'subrarr': '\\u2979',\n 'subsim': '\\u2AC7',\n 'subsub': '\\u2AD5',\n 'subsup': '\\u2AD3',\n 'sung': '\\u266A',\n 'sup1': '\\u00B9',\n 'sup2': '\\u00B2',\n 'sup3': '\\u00B3',\n 'supE': '\\u2AC6',\n 'supseteqq': '\\u2AC6',\n 'supdot': '\\u2ABE',\n 'supdsub': '\\u2AD8',\n 'supedot': '\\u2AC4',\n 'suphsol': '\\u27C9',\n 'suphsub': '\\u2AD7',\n 'suplarr': '\\u297B',\n 'supmult': '\\u2AC2',\n 'supnE': '\\u2ACC',\n 'supsetneqq': '\\u2ACC',\n 'supne': '\\u228B',\n 'supsetneq': '\\u228B',\n 'supplus': '\\u2AC0',\n 'supsim': '\\u2AC8',\n 'supsub': '\\u2AD4',\n 'supsup': '\\u2AD6',\n 'swArr': '\\u21D9',\n 'swnwar': '\\u292A',\n 'szlig': '\\u00DF',\n 'target': '\\u2316',\n 'tau': '\\u03C4',\n 'tcaron': '\\u0165',\n 'tcedil': '\\u0163',\n 'tcy': '\\u0442',\n 'telrec': '\\u2315',\n 'tfr': '\\uD835\\uDD31',\n 'theta': '\\u03B8',\n 'thetasym': '\\u03D1',\n 'thetav': '\\u03D1',\n 'vartheta': '\\u03D1',\n 'thorn': '\\u00FE',\n 'times': '\\u00D7',\n 'timesbar': '\\u2A31',\n 'timesd': '\\u2A30',\n 'topbot': '\\u2336',\n 'topcir': '\\u2AF1',\n 'topf': '\\uD835\\uDD65',\n 'topfork': '\\u2ADA',\n 'tprime': '\\u2034',\n 'triangle': '\\u25B5',\n 'utri': '\\u25B5',\n 'triangleq': '\\u225C',\n 'trie': '\\u225C',\n 'tridot': '\\u25EC',\n 'triminus': '\\u2A3A',\n 'triplus': '\\u2A39',\n 'trisb': '\\u29CD',\n 'tritime': '\\u2A3B',\n 'trpezium': '\\u23E2',\n 'tscr': '\\uD835\\uDCC9',\n 'tscy': '\\u0446',\n 'tshcy': '\\u045B',\n 'tstrok': '\\u0167',\n 'uHar': '\\u2963',\n 'uacute': '\\u00FA',\n 'ubrcy': '\\u045E',\n 'ubreve': '\\u016D',\n 'ucirc': '\\u00FB',\n 'ucy': '\\u0443',\n 'udblac': '\\u0171',\n 'ufisht': '\\u297E',\n 'ufr': '\\uD835\\uDD32',\n 'ugrave': '\\u00F9',\n 'uhblk': '\\u2580',\n 'ulcorn': '\\u231C',\n 'ulcorner': '\\u231C',\n 'ulcrop': '\\u230F',\n 'ultri': '\\u25F8',\n 'umacr': '\\u016B',\n 'uogon': '\\u0173',\n 'uopf': '\\uD835\\uDD66',\n 'upsi': '\\u03C5',\n 'upsilon': '\\u03C5',\n 'upuparrows': '\\u21C8',\n 'uuarr': '\\u21C8',\n 'urcorn': '\\u231D',\n 'urcorner': '\\u231D',\n 'urcrop': '\\u230E',\n 'uring': '\\u016F',\n 'urtri': '\\u25F9',\n 'uscr': '\\uD835\\uDCCA',\n 'utdot': '\\u22F0',\n 'utilde': '\\u0169',\n 'uuml': '\\u00FC',\n 'uwangle': '\\u29A7',\n 'vBar': '\\u2AE8',\n 'vBarv': '\\u2AE9',\n 'vangrt': '\\u299C',\n 'varsubsetneq': '\\u228A\\uFE00',\n 'vsubne': '\\u228A\\uFE00',\n 'varsubsetneqq': '\\u2ACB\\uFE00',\n 'vsubnE': '\\u2ACB\\uFE00',\n 'varsupsetneq': '\\u228B\\uFE00',\n 'vsupne': '\\u228B\\uFE00',\n 'varsupsetneqq': '\\u2ACC\\uFE00',\n 'vsupnE': '\\u2ACC\\uFE00',\n 'vcy': '\\u0432',\n 'veebar': '\\u22BB',\n 'veeeq': '\\u225A',\n 'vellip': '\\u22EE',\n 'vfr': '\\uD835\\uDD33',\n 'vopf': '\\uD835\\uDD67',\n 'vscr': '\\uD835\\uDCCB',\n 'vzigzag': '\\u299A',\n 'wcirc': '\\u0175',\n 'wedbar': '\\u2A5F',\n 'wedgeq': '\\u2259',\n 'weierp': '\\u2118',\n 'wp': '\\u2118',\n 'wfr': '\\uD835\\uDD34',\n 'wopf': '\\uD835\\uDD68',\n 'wscr': '\\uD835\\uDCCC',\n 'xfr': '\\uD835\\uDD35',\n 'xi': '\\u03BE',\n 'xnis': '\\u22FB',\n 'xopf': '\\uD835\\uDD69',\n 'xscr': '\\uD835\\uDCCD',\n 'yacute': '\\u00FD',\n 'yacy': '\\u044F',\n 'ycirc': '\\u0177',\n 'ycy': '\\u044B',\n 'yen': '\\u00A5',\n 'yfr': '\\uD835\\uDD36',\n 'yicy': '\\u0457',\n 'yopf': '\\uD835\\uDD6A',\n 'yscr': '\\uD835\\uDCCE',\n 'yucy': '\\u044E',\n 'yuml': '\\u00FF',\n 'zacute': '\\u017A',\n 'zcaron': '\\u017E',\n 'zcy': '\\u0437',\n 'zdot': '\\u017C',\n 'zeta': '\\u03B6',\n 'zfr': '\\uD835\\uDD37',\n 'zhcy': '\\u0436',\n 'zigrarr': '\\u21DD',\n 'zopf': '\\uD835\\uDD6B',\n 'zscr': '\\uD835\\uDCCF',\n 'zwj': '\\u200D',\n 'zwnj': '\\u200C'\n};\n// The &ngsp; pseudo-entity is denoting a space.\n// 0xE500 is a PUA (Private Use Areas) unicode character\n// This is inspired by the Angular Dart implementation.\nconst NGSP_UNICODE = '\\uE500';\nNAMED_ENTITIES['ngsp'] = NGSP_UNICODE;\nclass TokenError extends ParseError {\n constructor(errorMsg, tokenType, span) {\n super(span, errorMsg);\n this.tokenType = tokenType;\n }\n}\nclass TokenizeResult {\n constructor(tokens, errors, nonNormalizedIcuExpressions) {\n this.tokens = tokens;\n this.errors = errors;\n this.nonNormalizedIcuExpressions = nonNormalizedIcuExpressions;\n }\n}\nfunction tokenize(source, url, getTagDefinition, options = {}) {\n const tokenizer = new _Tokenizer(new ParseSourceFile(source, url), getTagDefinition, options);\n tokenizer.tokenize();\n return new TokenizeResult(mergeTextTokens(tokenizer.tokens), tokenizer.errors, tokenizer.nonNormalizedIcuExpressions);\n}\nconst _CR_OR_CRLF_REGEXP = /\\r\\n?/g;\nfunction _unexpectedCharacterErrorMsg(charCode) {\n const char = charCode === $EOF ? 'EOF' : String.fromCharCode(charCode);\n return `Unexpected character \"${char}\"`;\n}\nfunction _unknownEntityErrorMsg(entitySrc) {\n return `Unknown entity \"${entitySrc}\" - use the \";\" or \";\" syntax`;\n}\nfunction _unparsableEntityErrorMsg(type, entityStr) {\n return `Unable to parse entity \"${entityStr}\" - ${type} character reference entities must end with \";\"`;\n}\nvar CharacterReferenceType;\n(function (CharacterReferenceType) {\n CharacterReferenceType[\"HEX\"] = \"hexadecimal\";\n CharacterReferenceType[\"DEC\"] = \"decimal\";\n})(CharacterReferenceType || (CharacterReferenceType = {}));\nclass _ControlFlowError {\n constructor(error) {\n this.error = error;\n }\n}\n// See https://www.w3.org/TR/html51/syntax.html#writing-html-documents\nclass _Tokenizer {\n /**\n * @param _file The html source file being tokenized.\n * @param _getTagDefinition A function that will retrieve a tag definition for a given tag name.\n * @param options Configuration of the tokenization.\n */\n constructor(_file, _getTagDefinition, options) {\n this._getTagDefinition = _getTagDefinition;\n this._currentTokenStart = null;\n this._currentTokenType = null;\n this._expansionCaseStack = [];\n this._inInterpolation = false;\n this.tokens = [];\n this.errors = [];\n this.nonNormalizedIcuExpressions = [];\n this._tokenizeIcu = options.tokenizeExpansionForms || false;\n this._interpolationConfig = options.interpolationConfig || DEFAULT_INTERPOLATION_CONFIG;\n this._leadingTriviaCodePoints = options.leadingTriviaChars && options.leadingTriviaChars.map(c => c.codePointAt(0) || 0);\n const range = options.range || {\n endPos: _file.content.length,\n startPos: 0,\n startLine: 0,\n startCol: 0\n };\n this._cursor = options.escapedString ? new EscapedCharacterCursor(_file, range) : new PlainCharacterCursor(_file, range);\n this._preserveLineEndings = options.preserveLineEndings || false;\n this._i18nNormalizeLineEndingsInICUs = options.i18nNormalizeLineEndingsInICUs || false;\n this._tokenizeBlocks = options.tokenizeBlocks || false;\n try {\n this._cursor.init();\n } catch (e) {\n this.handleError(e);\n }\n }\n _processCarriageReturns(content) {\n if (this._preserveLineEndings) {\n return content;\n }\n // https://www.w3.org/TR/html51/syntax.html#preprocessing-the-input-stream\n // In order to keep the original position in the source, we can not\n // pre-process it.\n // Instead CRs are processed right before instantiating the tokens.\n return content.replace(_CR_OR_CRLF_REGEXP, '\\n');\n }\n tokenize() {\n while (this._cursor.peek() !== $EOF) {\n const start = this._cursor.clone();\n try {\n if (this._attemptCharCode($LT)) {\n if (this._attemptCharCode($BANG)) {\n if (this._attemptCharCode($LBRACKET)) {\n this._consumeCdata(start);\n } else if (this._attemptCharCode($MINUS)) {\n this._consumeComment(start);\n } else {\n this._consumeDocType(start);\n }\n } else if (this._attemptCharCode($SLASH)) {\n this._consumeTagClose(start);\n } else {\n this._consumeTagOpen(start);\n }\n } else if (this._tokenizeBlocks && this._attemptStr('{#')) {\n this._consumeBlockGroupOpen(start);\n } else if (this._tokenizeBlocks && this._attemptStr('{/')) {\n this._consumeBlockGroupClose(start);\n } else if (this._tokenizeBlocks && this._attemptStr('{:')) {\n this._consumeBlock(start);\n } else if (!(this._tokenizeIcu && this._tokenizeExpansionForm())) {\n // In (possibly interpolated) text the end of the text is given by `isTextEnd()`, while\n // the premature end of an interpolation is given by the start of a new HTML element.\n this._consumeWithInterpolation(5 /* TokenType.TEXT */, 8 /* TokenType.INTERPOLATION */, () => this._isTextEnd(), () => this._isTagStart());\n }\n } catch (e) {\n this.handleError(e);\n }\n }\n this._beginToken(24 /* TokenType.EOF */);\n this._endToken([]);\n }\n _consumeBlockGroupOpen(start) {\n this._beginToken(25 /* TokenType.BLOCK_GROUP_OPEN_START */, start);\n const nameCursor = this._cursor.clone();\n this._attemptCharCodeUntilFn(code => !isBlockNameChar(code));\n this._endToken([this._cursor.getChars(nameCursor)]);\n this._consumeBlockParameters();\n this._beginToken(26 /* TokenType.BLOCK_GROUP_OPEN_END */);\n this._requireCharCode($RBRACE);\n this._endToken([]);\n }\n _consumeBlockGroupClose(start) {\n this._beginToken(27 /* TokenType.BLOCK_GROUP_CLOSE */, start);\n const nameCursor = this._cursor.clone();\n this._attemptCharCodeUntilFn(code => !isBlockNameChar(code));\n const name = this._cursor.getChars(nameCursor);\n this._requireCharCode($RBRACE);\n this._endToken([name]);\n }\n _consumeBlock(start) {\n this._beginToken(29 /* TokenType.BLOCK_OPEN_START */, start);\n const nameCursor = this._cursor.clone();\n this._attemptCharCodeUntilFn(code => !isBlockNameChar(code));\n this._endToken([this._cursor.getChars(nameCursor)]);\n this._consumeBlockParameters();\n this._beginToken(30 /* TokenType.BLOCK_OPEN_END */);\n this._requireCharCode($RBRACE);\n this._endToken([]);\n }\n _consumeBlockParameters() {\n // Trim the whitespace until the first parameter.\n this._attemptCharCodeUntilFn(isBlockParameterChar);\n while (this._cursor.peek() !== $RBRACE && this._cursor.peek() !== $EOF) {\n this._beginToken(28 /* TokenType.BLOCK_PARAMETER */);\n const start = this._cursor.clone();\n let inQuote = null;\n let openBraces = 0;\n // Consume the parameter until the next semicolon or brace.\n // Note that we skip over semicolons/braces inside of strings.\n while (this._cursor.peek() !== $SEMICOLON && this._cursor.peek() !== $EOF || inQuote !== null) {\n const char = this._cursor.peek();\n // Skip to the next character if it was escaped.\n if (char === $BACKSLASH) {\n this._cursor.advance();\n } else if (char === inQuote) {\n inQuote = null;\n } else if (inQuote === null && isQuote(char)) {\n inQuote = char;\n } else if (char === $LBRACE && inQuote === null) {\n openBraces++;\n } else if (char === $RBRACE && inQuote === null) {\n if (openBraces === 0) {\n break;\n } else if (openBraces > 0) {\n openBraces--;\n }\n }\n this._cursor.advance();\n }\n this._endToken([this._cursor.getChars(start)]);\n // Skip to the next parameter.\n this._attemptCharCodeUntilFn(isBlockParameterChar);\n }\n }\n /**\n * @returns whether an ICU token has been created\n * @internal\n */\n _tokenizeExpansionForm() {\n if (this.isExpansionFormStart()) {\n this._consumeExpansionFormStart();\n return true;\n }\n if (isExpansionCaseStart(this._cursor.peek()) && this._isInExpansionForm()) {\n this._consumeExpansionCaseStart();\n return true;\n }\n if (this._cursor.peek() === $RBRACE) {\n if (this._isInExpansionCase()) {\n this._consumeExpansionCaseEnd();\n return true;\n }\n if (this._isInExpansionForm()) {\n this._consumeExpansionFormEnd();\n return true;\n }\n }\n return false;\n }\n _beginToken(type, start = this._cursor.clone()) {\n this._currentTokenStart = start;\n this._currentTokenType = type;\n }\n _endToken(parts, end) {\n if (this._currentTokenStart === null) {\n throw new TokenError('Programming error - attempted to end a token when there was no start to the token', this._currentTokenType, this._cursor.getSpan(end));\n }\n if (this._currentTokenType === null) {\n throw new TokenError('Programming error - attempted to end a token which has no token type', null, this._cursor.getSpan(this._currentTokenStart));\n }\n const token = {\n type: this._currentTokenType,\n parts,\n sourceSpan: (end ?? this._cursor).getSpan(this._currentTokenStart, this._leadingTriviaCodePoints)\n };\n this.tokens.push(token);\n this._currentTokenStart = null;\n this._currentTokenType = null;\n return token;\n }\n _createError(msg, span) {\n if (this._isInExpansionForm()) {\n msg += ` (Do you have an unescaped \"{\" in your template? Use \"{{ '{' }}\") to escape it.)`;\n }\n const error = new TokenError(msg, this._currentTokenType, span);\n this._currentTokenStart = null;\n this._currentTokenType = null;\n return new _ControlFlowError(error);\n }\n handleError(e) {\n if (e instanceof CursorError) {\n e = this._createError(e.msg, this._cursor.getSpan(e.cursor));\n }\n if (e instanceof _ControlFlowError) {\n this.errors.push(e.error);\n } else {\n throw e;\n }\n }\n _attemptCharCode(charCode) {\n if (this._cursor.peek() === charCode) {\n this._cursor.advance();\n return true;\n }\n return false;\n }\n _attemptCharCodeCaseInsensitive(charCode) {\n if (compareCharCodeCaseInsensitive(this._cursor.peek(), charCode)) {\n this._cursor.advance();\n return true;\n }\n return false;\n }\n _requireCharCode(charCode) {\n const location = this._cursor.clone();\n if (!this._attemptCharCode(charCode)) {\n throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location));\n }\n }\n _attemptStr(chars) {\n const len = chars.length;\n if (this._cursor.charsLeft() < len) {\n return false;\n }\n const initialPosition = this._cursor.clone();\n for (let i = 0; i < len; i++) {\n if (!this._attemptCharCode(chars.charCodeAt(i))) {\n // If attempting to parse the string fails, we want to reset the parser\n // to where it was before the attempt\n this._cursor = initialPosition;\n return false;\n }\n }\n return true;\n }\n _attemptStrCaseInsensitive(chars) {\n for (let i = 0; i < chars.length; i++) {\n if (!this._attemptCharCodeCaseInsensitive(chars.charCodeAt(i))) {\n return false;\n }\n }\n return true;\n }\n _requireStr(chars) {\n const location = this._cursor.clone();\n if (!this._attemptStr(chars)) {\n throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location));\n }\n }\n _attemptCharCodeUntilFn(predicate) {\n while (!predicate(this._cursor.peek())) {\n this._cursor.advance();\n }\n }\n _requireCharCodeUntilFn(predicate, len) {\n const start = this._cursor.clone();\n this._attemptCharCodeUntilFn(predicate);\n if (this._cursor.diff(start) < len) {\n throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));\n }\n }\n _attemptUntilChar(char) {\n while (this._cursor.peek() !== char) {\n this._cursor.advance();\n }\n }\n _readChar() {\n // Don't rely upon reading directly from `_input` as the actual char value\n // may have been generated from an escape sequence.\n const char = String.fromCodePoint(this._cursor.peek());\n this._cursor.advance();\n return char;\n }\n _consumeEntity(textTokenType) {\n this._beginToken(9 /* TokenType.ENCODED_ENTITY */);\n const start = this._cursor.clone();\n this._cursor.advance();\n if (this._attemptCharCode($HASH)) {\n const isHex = this._attemptCharCode($x) || this._attemptCharCode($X);\n const codeStart = this._cursor.clone();\n this._attemptCharCodeUntilFn(isDigitEntityEnd);\n if (this._cursor.peek() != $SEMICOLON) {\n // Advance cursor to include the peeked character in the string provided to the error\n // message.\n this._cursor.advance();\n const entityType = isHex ? CharacterReferenceType.HEX : CharacterReferenceType.DEC;\n throw this._createError(_unparsableEntityErrorMsg(entityType, this._cursor.getChars(start)), this._cursor.getSpan());\n }\n const strNum = this._cursor.getChars(codeStart);\n this._cursor.advance();\n try {\n const charCode = parseInt(strNum, isHex ? 16 : 10);\n this._endToken([String.fromCharCode(charCode), this._cursor.getChars(start)]);\n } catch {\n throw this._createError(_unknownEntityErrorMsg(this._cursor.getChars(start)), this._cursor.getSpan());\n }\n } else {\n const nameStart = this._cursor.clone();\n this._attemptCharCodeUntilFn(isNamedEntityEnd);\n if (this._cursor.peek() != $SEMICOLON) {\n // No semicolon was found so abort the encoded entity token that was in progress, and treat\n // this as a text token\n this._beginToken(textTokenType, start);\n this._cursor = nameStart;\n this._endToken(['&']);\n } else {\n const name = this._cursor.getChars(nameStart);\n this._cursor.advance();\n const char = NAMED_ENTITIES[name];\n if (!char) {\n throw this._createError(_unknownEntityErrorMsg(name), this._cursor.getSpan(start));\n }\n this._endToken([char, `&${name};`]);\n }\n }\n }\n _consumeRawText(consumeEntities, endMarkerPredicate) {\n this._beginToken(consumeEntities ? 6 /* TokenType.ESCAPABLE_RAW_TEXT */ : 7 /* TokenType.RAW_TEXT */);\n const parts = [];\n while (true) {\n const tagCloseStart = this._cursor.clone();\n const foundEndMarker = endMarkerPredicate();\n this._cursor = tagCloseStart;\n if (foundEndMarker) {\n break;\n }\n if (consumeEntities && this._cursor.peek() === $AMPERSAND) {\n this._endToken([this._processCarriageReturns(parts.join(''))]);\n parts.length = 0;\n this._consumeEntity(6 /* TokenType.ESCAPABLE_RAW_TEXT */);\n this._beginToken(6 /* TokenType.ESCAPABLE_RAW_TEXT */);\n } else {\n parts.push(this._readChar());\n }\n }\n this._endToken([this._processCarriageReturns(parts.join(''))]);\n }\n _consumeComment(start) {\n this._beginToken(10 /* TokenType.COMMENT_START */, start);\n this._requireCharCode($MINUS);\n this._endToken([]);\n this._consumeRawText(false, () => this._attemptStr('-->'));\n this._beginToken(11 /* TokenType.COMMENT_END */);\n this._requireStr('-->');\n this._endToken([]);\n }\n _consumeCdata(start) {\n this._beginToken(12 /* TokenType.CDATA_START */, start);\n this._requireStr('CDATA[');\n this._endToken([]);\n this._consumeRawText(false, () => this._attemptStr(']]>'));\n this._beginToken(13 /* TokenType.CDATA_END */);\n this._requireStr(']]>');\n this._endToken([]);\n }\n _consumeDocType(start) {\n this._beginToken(18 /* TokenType.DOC_TYPE */, start);\n const contentStart = this._cursor.clone();\n this._attemptUntilChar($GT);\n const content = this._cursor.getChars(contentStart);\n this._cursor.advance();\n this._endToken([content]);\n }\n _consumePrefixAndName() {\n const nameOrPrefixStart = this._cursor.clone();\n let prefix = '';\n while (this._cursor.peek() !== $COLON && !isPrefixEnd(this._cursor.peek())) {\n this._cursor.advance();\n }\n let nameStart;\n if (this._cursor.peek() === $COLON) {\n prefix = this._cursor.getChars(nameOrPrefixStart);\n this._cursor.advance();\n nameStart = this._cursor.clone();\n } else {\n nameStart = nameOrPrefixStart;\n }\n this._requireCharCodeUntilFn(isNameEnd, prefix === '' ? 0 : 1);\n const name = this._cursor.getChars(nameStart);\n return [prefix, name];\n }\n _consumeTagOpen(start) {\n let tagName;\n let prefix;\n let openTagToken;\n try {\n if (!isAsciiLetter(this._cursor.peek())) {\n throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));\n }\n openTagToken = this._consumeTagOpenStart(start);\n prefix = openTagToken.parts[0];\n tagName = openTagToken.parts[1];\n this._attemptCharCodeUntilFn(isNotWhitespace);\n while (this._cursor.peek() !== $SLASH && this._cursor.peek() !== $GT && this._cursor.peek() !== $LT && this._cursor.peek() !== $EOF) {\n this._consumeAttributeName();\n this._attemptCharCodeUntilFn(isNotWhitespace);\n if (this._attemptCharCode($EQ)) {\n this._attemptCharCodeUntilFn(isNotWhitespace);\n this._consumeAttributeValue();\n }\n this._attemptCharCodeUntilFn(isNotWhitespace);\n }\n this._consumeTagOpenEnd();\n } catch (e) {\n if (e instanceof _ControlFlowError) {\n if (openTagToken) {\n // We errored before we could close the opening tag, so it is incomplete.\n openTagToken.type = 4 /* TokenType.INCOMPLETE_TAG_OPEN */;\n } else {\n // When the start tag is invalid, assume we want a \"<\" as text.\n // Back to back text tokens are merged at the end.\n this._beginToken(5 /* TokenType.TEXT */, start);\n this._endToken(['<']);\n }\n return;\n }\n throw e;\n }\n const contentTokenType = this._getTagDefinition(tagName).getContentType(prefix);\n if (contentTokenType === TagContentType.RAW_TEXT) {\n this._consumeRawTextWithTagClose(prefix, tagName, false);\n } else if (contentTokenType === TagContentType.ESCAPABLE_RAW_TEXT) {\n this._consumeRawTextWithTagClose(prefix, tagName, true);\n }\n }\n _consumeRawTextWithTagClose(prefix, tagName, consumeEntities) {\n this._consumeRawText(consumeEntities, () => {\n if (!this._attemptCharCode($LT)) return false;\n if (!this._attemptCharCode($SLASH)) return false;\n this._attemptCharCodeUntilFn(isNotWhitespace);\n if (!this._attemptStrCaseInsensitive(tagName)) return false;\n this._attemptCharCodeUntilFn(isNotWhitespace);\n return this._attemptCharCode($GT);\n });\n this._beginToken(3 /* TokenType.TAG_CLOSE */);\n this._requireCharCodeUntilFn(code => code === $GT, 3);\n this._cursor.advance(); // Consume the `>`\n this._endToken([prefix, tagName]);\n }\n _consumeTagOpenStart(start) {\n this._beginToken(0 /* TokenType.TAG_OPEN_START */, start);\n const parts = this._consumePrefixAndName();\n return this._endToken(parts);\n }\n _consumeAttributeName() {\n const attrNameStart = this._cursor.peek();\n if (attrNameStart === $SQ || attrNameStart === $DQ) {\n throw this._createError(_unexpectedCharacterErrorMsg(attrNameStart), this._cursor.getSpan());\n }\n this._beginToken(14 /* TokenType.ATTR_NAME */);\n const prefixAndName = this._consumePrefixAndName();\n this._endToken(prefixAndName);\n }\n _consumeAttributeValue() {\n if (this._cursor.peek() === $SQ || this._cursor.peek() === $DQ) {\n const quoteChar = this._cursor.peek();\n this._consumeQuote(quoteChar);\n // In an attribute then end of the attribute value and the premature end to an interpolation\n // are both triggered by the `quoteChar`.\n const endPredicate = () => this._cursor.peek() === quoteChar;\n this._consumeWithInterpolation(16 /* TokenType.ATTR_VALUE_TEXT */, 17 /* TokenType.ATTR_VALUE_INTERPOLATION */, endPredicate, endPredicate);\n this._consumeQuote(quoteChar);\n } else {\n const endPredicate = () => isNameEnd(this._cursor.peek());\n this._consumeWithInterpolation(16 /* TokenType.ATTR_VALUE_TEXT */, 17 /* TokenType.ATTR_VALUE_INTERPOLATION */, endPredicate, endPredicate);\n }\n }\n _consumeQuote(quoteChar) {\n this._beginToken(15 /* TokenType.ATTR_QUOTE */);\n this._requireCharCode(quoteChar);\n this._endToken([String.fromCodePoint(quoteChar)]);\n }\n _consumeTagOpenEnd() {\n const tokenType = this._attemptCharCode($SLASH) ? 2 /* TokenType.TAG_OPEN_END_VOID */ : 1 /* TokenType.TAG_OPEN_END */;\n this._beginToken(tokenType);\n this._requireCharCode($GT);\n this._endToken([]);\n }\n _consumeTagClose(start) {\n this._beginToken(3 /* TokenType.TAG_CLOSE */, start);\n this._attemptCharCodeUntilFn(isNotWhitespace);\n const prefixAndName = this._consumePrefixAndName();\n this._attemptCharCodeUntilFn(isNotWhitespace);\n this._requireCharCode($GT);\n this._endToken(prefixAndName);\n }\n _consumeExpansionFormStart() {\n this._beginToken(19 /* TokenType.EXPANSION_FORM_START */);\n this._requireCharCode($LBRACE);\n this._endToken([]);\n this._expansionCaseStack.push(19 /* TokenType.EXPANSION_FORM_START */);\n this._beginToken(7 /* TokenType.RAW_TEXT */);\n const condition = this._readUntil($COMMA);\n const normalizedCondition = this._processCarriageReturns(condition);\n if (this._i18nNormalizeLineEndingsInICUs) {\n // We explicitly want to normalize line endings for this text.\n this._endToken([normalizedCondition]);\n } else {\n // We are not normalizing line endings.\n const conditionToken = this._endToken([condition]);\n if (normalizedCondition !== condition) {\n this.nonNormalizedIcuExpressions.push(conditionToken);\n }\n }\n this._requireCharCode($COMMA);\n this._attemptCharCodeUntilFn(isNotWhitespace);\n this._beginToken(7 /* TokenType.RAW_TEXT */);\n const type = this._readUntil($COMMA);\n this._endToken([type]);\n this._requireCharCode($COMMA);\n this._attemptCharCodeUntilFn(isNotWhitespace);\n }\n _consumeExpansionCaseStart() {\n this._beginToken(20 /* TokenType.EXPANSION_CASE_VALUE */);\n const value = this._readUntil($LBRACE).trim();\n this._endToken([value]);\n this._attemptCharCodeUntilFn(isNotWhitespace);\n this._beginToken(21 /* TokenType.EXPANSION_CASE_EXP_START */);\n this._requireCharCode($LBRACE);\n this._endToken([]);\n this._attemptCharCodeUntilFn(isNotWhitespace);\n this._expansionCaseStack.push(21 /* TokenType.EXPANSION_CASE_EXP_START */);\n }\n _consumeExpansionCaseEnd() {\n this._beginToken(22 /* TokenType.EXPANSION_CASE_EXP_END */);\n this._requireCharCode($RBRACE);\n this._endToken([]);\n this._attemptCharCodeUntilFn(isNotWhitespace);\n this._expansionCaseStack.pop();\n }\n _consumeExpansionFormEnd() {\n this._beginToken(23 /* TokenType.EXPANSION_FORM_END */);\n this._requireCharCode($RBRACE);\n this._endToken([]);\n this._expansionCaseStack.pop();\n }\n /**\n * Consume a string that may contain interpolation expressions.\n *\n * The first token consumed will be of `tokenType` and then there will be alternating\n * `interpolationTokenType` and `tokenType` tokens until the `endPredicate()` returns true.\n *\n * If an interpolation token ends prematurely it will have no end marker in its `parts` array.\n *\n * @param textTokenType the kind of tokens to interleave around interpolation tokens.\n * @param interpolationTokenType the kind of tokens that contain interpolation.\n * @param endPredicate a function that should return true when we should stop consuming.\n * @param endInterpolation a function that should return true if there is a premature end to an\n * interpolation expression - i.e. before we get to the normal interpolation closing marker.\n */\n _consumeWithInterpolation(textTokenType, interpolationTokenType, endPredicate, endInterpolation) {\n this._beginToken(textTokenType);\n const parts = [];\n while (!endPredicate()) {\n const current = this._cursor.clone();\n if (this._interpolationConfig && this._attemptStr(this._interpolationConfig.start)) {\n this._endToken([this._processCarriageReturns(parts.join(''))], current);\n parts.length = 0;\n this._consumeInterpolation(interpolationTokenType, current, endInterpolation);\n this._beginToken(textTokenType);\n } else if (this._cursor.peek() === $AMPERSAND) {\n this._endToken([this._processCarriageReturns(parts.join(''))]);\n parts.length = 0;\n this._consumeEntity(textTokenType);\n this._beginToken(textTokenType);\n } else {\n parts.push(this._readChar());\n }\n }\n // It is possible that an interpolation was started but not ended inside this text token.\n // Make sure that we reset the state of the lexer correctly.\n this._inInterpolation = false;\n this._endToken([this._processCarriageReturns(parts.join(''))]);\n }\n /**\n * Consume a block of text that has been interpreted as an Angular interpolation.\n *\n * @param interpolationTokenType the type of the interpolation token to generate.\n * @param interpolationStart a cursor that points to the start of this interpolation.\n * @param prematureEndPredicate a function that should return true if the next characters indicate\n * an end to the interpolation before its normal closing marker.\n */\n _consumeInterpolation(interpolationTokenType, interpolationStart, prematureEndPredicate) {\n const parts = [];\n this._beginToken(interpolationTokenType, interpolationStart);\n parts.push(this._interpolationConfig.start);\n // Find the end of the interpolation, ignoring content inside quotes.\n const expressionStart = this._cursor.clone();\n let inQuote = null;\n let inComment = false;\n while (this._cursor.peek() !== $EOF && (prematureEndPredicate === null || !prematureEndPredicate())) {\n const current = this._cursor.clone();\n if (this._isTagStart()) {\n // We are starting what looks like an HTML element in the middle of this interpolation.\n // Reset the cursor to before the `<` character and end the interpolation token.\n // (This is actually wrong but here for backward compatibility).\n this._cursor = current;\n parts.push(this._getProcessedChars(expressionStart, current));\n this._endToken(parts);\n return;\n }\n if (inQuote === null) {\n if (this._attemptStr(this._interpolationConfig.end)) {\n // We are not in a string, and we hit the end interpolation marker\n parts.push(this._getProcessedChars(expressionStart, current));\n parts.push(this._interpolationConfig.end);\n this._endToken(parts);\n return;\n } else if (this._attemptStr('//')) {\n // Once we are in a comment we ignore any quotes\n inComment = true;\n }\n }\n const char = this._cursor.peek();\n this._cursor.advance();\n if (char === $BACKSLASH) {\n // Skip the next character because it was escaped.\n this._cursor.advance();\n } else if (char === inQuote) {\n // Exiting the current quoted string\n inQuote = null;\n } else if (!inComment && inQuote === null && isQuote(char)) {\n // Entering a new quoted string\n inQuote = char;\n }\n }\n // We hit EOF without finding a closing interpolation marker\n parts.push(this._getProcessedChars(expressionStart, this._cursor));\n this._endToken(parts);\n }\n _getProcessedChars(start, end) {\n return this._processCarriageReturns(end.getChars(start));\n }\n _isTextEnd() {\n if (this._isTagStart() || this._isBlockStart() || this._cursor.peek() === $EOF) {\n return true;\n }\n if (this._tokenizeIcu && !this._inInterpolation) {\n if (this.isExpansionFormStart()) {\n // start of an expansion form\n return true;\n }\n if (this._cursor.peek() === $RBRACE && this._isInExpansionCase()) {\n // end of and expansion case\n return true;\n }\n }\n return false;\n }\n /**\n * Returns true if the current cursor is pointing to the start of a tag\n * (opening/closing/comments/cdata/etc).\n */\n _isTagStart() {\n if (this._cursor.peek() === $LT) {\n // We assume that `<` followed by whitespace is not the start of an HTML element.\n const tmp = this._cursor.clone();\n tmp.advance();\n // If the next character is alphabetic, ! nor / then it is a tag start\n const code = tmp.peek();\n if ($a <= code && code <= $z || $A <= code && code <= $Z || code === $SLASH || code === $BANG) {\n return true;\n }\n }\n return false;\n }\n _isBlockStart() {\n if (this._tokenizeBlocks && this._cursor.peek() === $LBRACE) {\n const tmp = this._cursor.clone();\n // Check that the cursor is on a `{#`, `{/` or `{:`.\n tmp.advance();\n const next = tmp.peek();\n if (next !== $BANG && next !== $SLASH && next !== $COLON) {\n return false;\n }\n // If it is, also verify that the next character is a valid block identifier.\n tmp.advance();\n if (isBlockNameChar(tmp.peek())) {\n return true;\n }\n }\n return false;\n }\n _readUntil(char) {\n const start = this._cursor.clone();\n this._attemptUntilChar(char);\n return this._cursor.getChars(start);\n }\n _isInExpansionCase() {\n return this._expansionCaseStack.length > 0 && this._expansionCaseStack[this._expansionCaseStack.length - 1] === 21 /* TokenType.EXPANSION_CASE_EXP_START */;\n }\n _isInExpansionForm() {\n return this._expansionCaseStack.length > 0 && this._expansionCaseStack[this._expansionCaseStack.length - 1] === 19 /* TokenType.EXPANSION_FORM_START */;\n }\n isExpansionFormStart() {\n if (this._cursor.peek() !== $LBRACE) {\n return false;\n }\n if (this._interpolationConfig) {\n const start = this._cursor.clone();\n const isInterpolation = this._attemptStr(this._interpolationConfig.start);\n this._cursor = start;\n return !isInterpolation;\n }\n return true;\n }\n}\nfunction isNotWhitespace(code) {\n return !isWhitespace(code) || code === $EOF;\n}\nfunction isNameEnd(code) {\n return isWhitespace(code) || code === $GT || code === $LT || code === $SLASH || code === $SQ || code === $DQ || code === $EQ || code === $EOF;\n}\nfunction isPrefixEnd(code) {\n return (code < $a || $z < code) && (code < $A || $Z < code) && (code < $0 || code > $9);\n}\nfunction isDigitEntityEnd(code) {\n return code === $SEMICOLON || code === $EOF || !isAsciiHexDigit(code);\n}\nfunction isNamedEntityEnd(code) {\n return code === $SEMICOLON || code === $EOF || !isAsciiLetter(code);\n}\nfunction isExpansionCaseStart(peek) {\n return peek !== $RBRACE;\n}\nfunction compareCharCodeCaseInsensitive(code1, code2) {\n return toUpperCaseCharCode(code1) === toUpperCaseCharCode(code2);\n}\nfunction toUpperCaseCharCode(code) {\n return code >= $a && code <= $z ? code - $a + $A : code;\n}\nfunction isBlockNameChar(code) {\n return isAsciiLetter(code) || isDigit(code) || code === $_;\n}\nfunction isBlockParameterChar(code) {\n return code !== $SEMICOLON && isNotWhitespace(code);\n}\nfunction mergeTextTokens(srcTokens) {\n const dstTokens = [];\n let lastDstToken = undefined;\n for (let i = 0; i < srcTokens.length; i++) {\n const token = srcTokens[i];\n if (lastDstToken && lastDstToken.type === 5 /* TokenType.TEXT */ && token.type === 5 /* TokenType.TEXT */ || lastDstToken && lastDstToken.type === 16 /* TokenType.ATTR_VALUE_TEXT */ && token.type === 16 /* TokenType.ATTR_VALUE_TEXT */) {\n lastDstToken.parts[0] += token.parts[0];\n lastDstToken.sourceSpan.end = token.sourceSpan.end;\n } else {\n lastDstToken = token;\n dstTokens.push(lastDstToken);\n }\n }\n return dstTokens;\n}\nclass PlainCharacterCursor {\n constructor(fileOrCursor, range) {\n if (fileOrCursor instanceof PlainCharacterCursor) {\n this.file = fileOrCursor.file;\n this.input = fileOrCursor.input;\n this.end = fileOrCursor.end;\n const state = fileOrCursor.state;\n // Note: avoid using `{...fileOrCursor.state}` here as that has a severe performance penalty.\n // In ES5 bundles the object spread operator is translated into the `__assign` helper, which\n // is not optimized by VMs as efficiently as a raw object literal. Since this constructor is\n // called in tight loops, this difference matters.\n this.state = {\n peek: state.peek,\n offset: state.offset,\n line: state.line,\n column: state.column\n };\n } else {\n if (!range) {\n throw new Error('Programming error: the range argument must be provided with a file argument.');\n }\n this.file = fileOrCursor;\n this.input = fileOrCursor.content;\n this.end = range.endPos;\n this.state = {\n peek: -1,\n offset: range.startPos,\n line: range.startLine,\n column: range.startCol\n };\n }\n }\n clone() {\n return new PlainCharacterCursor(this);\n }\n peek() {\n return this.state.peek;\n }\n charsLeft() {\n return this.end - this.state.offset;\n }\n diff(other) {\n return this.state.offset - other.state.offset;\n }\n advance() {\n this.advanceState(this.state);\n }\n init() {\n this.updatePeek(this.state);\n }\n getSpan(start, leadingTriviaCodePoints) {\n start = start || this;\n let fullStart = start;\n if (leadingTriviaCodePoints) {\n while (this.diff(start) > 0 && leadingTriviaCodePoints.indexOf(start.peek()) !== -1) {\n if (fullStart === start) {\n start = start.clone();\n }\n start.advance();\n }\n }\n const startLocation = this.locationFromCursor(start);\n const endLocation = this.locationFromCursor(this);\n const fullStartLocation = fullStart !== start ? this.locationFromCursor(fullStart) : startLocation;\n return new ParseSourceSpan(startLocation, endLocation, fullStartLocation);\n }\n getChars(start) {\n return this.input.substring(start.state.offset, this.state.offset);\n }\n charAt(pos) {\n return this.input.charCodeAt(pos);\n }\n advanceState(state) {\n if (state.offset >= this.end) {\n this.state = state;\n throw new CursorError('Unexpected character \"EOF\"', this);\n }\n const currentChar = this.charAt(state.offset);\n if (currentChar === $LF) {\n state.line++;\n state.column = 0;\n } else if (!isNewLine(currentChar)) {\n state.column++;\n }\n state.offset++;\n this.updatePeek(state);\n }\n updatePeek(state) {\n state.peek = state.offset >= this.end ? $EOF : this.charAt(state.offset);\n }\n locationFromCursor(cursor) {\n return new ParseLocation(cursor.file, cursor.state.offset, cursor.state.line, cursor.state.column);\n }\n}\nclass EscapedCharacterCursor extends PlainCharacterCursor {\n constructor(fileOrCursor, range) {\n if (fileOrCursor instanceof EscapedCharacterCursor) {\n super(fileOrCursor);\n this.internalState = {\n ...fileOrCursor.internalState\n };\n } else {\n super(fileOrCursor, range);\n this.internalState = this.state;\n }\n }\n advance() {\n this.state = this.internalState;\n super.advance();\n this.processEscapeSequence();\n }\n init() {\n super.init();\n this.processEscapeSequence();\n }\n clone() {\n return new EscapedCharacterCursor(this);\n }\n getChars(start) {\n const cursor = start.clone();\n let chars = '';\n while (cursor.internalState.offset < this.internalState.offset) {\n chars += String.fromCodePoint(cursor.peek());\n cursor.advance();\n }\n return chars;\n }\n /**\n * Process the escape sequence that starts at the current position in the text.\n *\n * This method is called to ensure that `peek` has the unescaped value of escape sequences.\n */\n processEscapeSequence() {\n const peek = () => this.internalState.peek;\n if (peek() === $BACKSLASH) {\n // We have hit an escape sequence so we need the internal state to become independent\n // of the external state.\n this.internalState = {\n ...this.state\n };\n // Move past the backslash\n this.advanceState(this.internalState);\n // First check for standard control char sequences\n if (peek() === $n) {\n this.state.peek = $LF;\n } else if (peek() === $r) {\n this.state.peek = $CR;\n } else if (peek() === $v) {\n this.state.peek = $VTAB;\n } else if (peek() === $t) {\n this.state.peek = $TAB;\n } else if (peek() === $b) {\n this.state.peek = $BSPACE;\n } else if (peek() === $f) {\n this.state.peek = $FF;\n }\n // Now consider more complex sequences\n else if (peek() === $u) {\n // Unicode code-point sequence\n this.advanceState(this.internalState); // advance past the `u` char\n if (peek() === $LBRACE) {\n // Variable length Unicode, e.g. `\\x{123}`\n this.advanceState(this.internalState); // advance past the `{` char\n // Advance past the variable number of hex digits until we hit a `}` char\n const digitStart = this.clone();\n let length = 0;\n while (peek() !== $RBRACE) {\n this.advanceState(this.internalState);\n length++;\n }\n this.state.peek = this.decodeHexDigits(digitStart, length);\n } else {\n // Fixed length Unicode, e.g. `\\u1234`\n const digitStart = this.clone();\n this.advanceState(this.internalState);\n this.advanceState(this.internalState);\n this.advanceState(this.internalState);\n this.state.peek = this.decodeHexDigits(digitStart, 4);\n }\n } else if (peek() === $x) {\n // Hex char code, e.g. `\\x2F`\n this.advanceState(this.internalState); // advance past the `x` char\n const digitStart = this.clone();\n this.advanceState(this.internalState);\n this.state.peek = this.decodeHexDigits(digitStart, 2);\n } else if (isOctalDigit(peek())) {\n // Octal char code, e.g. `\\012`,\n let octal = '';\n let length = 0;\n let previous = this.clone();\n while (isOctalDigit(peek()) && length < 3) {\n previous = this.clone();\n octal += String.fromCodePoint(peek());\n this.advanceState(this.internalState);\n length++;\n }\n this.state.peek = parseInt(octal, 8);\n // Backup one char\n this.internalState = previous.internalState;\n } else if (isNewLine(this.internalState.peek)) {\n // Line continuation `\\` followed by a new line\n this.advanceState(this.internalState); // advance over the newline\n this.state = this.internalState;\n } else {\n // If none of the `if` blocks were executed then we just have an escaped normal character.\n // In that case we just, effectively, skip the backslash from the character.\n this.state.peek = this.internalState.peek;\n }\n }\n }\n decodeHexDigits(start, length) {\n const hex = this.input.slice(start.internalState.offset, start.internalState.offset + length);\n const charCode = parseInt(hex, 16);\n if (!isNaN(charCode)) {\n return charCode;\n } else {\n start.state = start.internalState;\n throw new CursorError('Invalid hexadecimal escape sequence', start);\n }\n }\n}\nclass CursorError {\n constructor(msg, cursor) {\n this.msg = msg;\n this.cursor = cursor;\n }\n}\nclass TreeError extends ParseError {\n static create(elementName, span, msg) {\n return new TreeError(elementName, span, msg);\n }\n constructor(elementName, span, msg) {\n super(span, msg);\n this.elementName = elementName;\n }\n}\nclass ParseTreeResult {\n constructor(rootNodes, errors) {\n this.rootNodes = rootNodes;\n this.errors = errors;\n }\n}\nclass Parser {\n constructor(getTagDefinition) {\n this.getTagDefinition = getTagDefinition;\n }\n parse(source, url, options) {\n const tokenizeResult = tokenize(source, url, this.getTagDefinition, options);\n const parser = new _TreeBuilder(tokenizeResult.tokens, this.getTagDefinition);\n parser.build();\n return new ParseTreeResult(parser.rootNodes, tokenizeResult.errors.concat(parser.errors));\n }\n}\nclass _TreeBuilder {\n constructor(tokens, getTagDefinition) {\n this.tokens = tokens;\n this.getTagDefinition = getTagDefinition;\n this._index = -1;\n this._containerStack = [];\n this.rootNodes = [];\n this.errors = [];\n this._advance();\n }\n build() {\n while (this._peek.type !== 24 /* TokenType.EOF */) {\n if (this._peek.type === 0 /* TokenType.TAG_OPEN_START */ || this._peek.type === 4 /* TokenType.INCOMPLETE_TAG_OPEN */) {\n this._consumeStartTag(this._advance());\n } else if (this._peek.type === 3 /* TokenType.TAG_CLOSE */) {\n this._consumeEndTag(this._advance());\n } else if (this._peek.type === 12 /* TokenType.CDATA_START */) {\n this._closeVoidElement();\n this._consumeCdata(this._advance());\n } else if (this._peek.type === 10 /* TokenType.COMMENT_START */) {\n this._closeVoidElement();\n this._consumeComment(this._advance());\n } else if (this._peek.type === 5 /* TokenType.TEXT */ || this._peek.type === 7 /* TokenType.RAW_TEXT */ || this._peek.type === 6 /* TokenType.ESCAPABLE_RAW_TEXT */) {\n this._closeVoidElement();\n this._consumeText(this._advance());\n } else if (this._peek.type === 19 /* TokenType.EXPANSION_FORM_START */) {\n this._consumeExpansion(this._advance());\n } else if (this._peek.type === 25 /* TokenType.BLOCK_GROUP_OPEN_START */) {\n this._closeVoidElement();\n this._consumeBlockGroupOpen(this._advance());\n } else if (this._peek.type === 29 /* TokenType.BLOCK_OPEN_START */) {\n this._closeVoidElement();\n this._consumeBlock(this._advance(), 30 /* TokenType.BLOCK_OPEN_END */);\n } else if (this._peek.type === 27 /* TokenType.BLOCK_GROUP_CLOSE */) {\n this._closeVoidElement();\n this._consumeBlockGroupClose(this._advance());\n } else {\n // Skip all other tokens...\n this._advance();\n }\n }\n }\n _advance() {\n const prev = this._peek;\n if (this._index < this.tokens.length - 1) {\n // Note: there is always an EOF token at the end\n this._index++;\n }\n this._peek = this.tokens[this._index];\n return prev;\n }\n _advanceIf(type) {\n if (this._peek.type === type) {\n return this._advance();\n }\n return null;\n }\n _consumeCdata(_startToken) {\n this._consumeText(this._advance());\n this._advanceIf(13 /* TokenType.CDATA_END */);\n }\n _consumeComment(token) {\n const text = this._advanceIf(7 /* TokenType.RAW_TEXT */);\n const endToken = this._advanceIf(11 /* TokenType.COMMENT_END */);\n const value = text != null ? text.parts[0].trim() : null;\n const sourceSpan = endToken == null ? token.sourceSpan : new ParseSourceSpan(token.sourceSpan.start, endToken.sourceSpan.end, token.sourceSpan.fullStart);\n this._addToParent(new Comment(value, sourceSpan));\n }\n _consumeExpansion(token) {\n const switchValue = this._advance();\n const type = this._advance();\n const cases = [];\n // read =\n while (this._peek.type === 20 /* TokenType.EXPANSION_CASE_VALUE */) {\n const expCase = this._parseExpansionCase();\n if (!expCase) return; // error\n cases.push(expCase);\n }\n // read the final }\n if (this._peek.type !== 23 /* TokenType.EXPANSION_FORM_END */) {\n this.errors.push(TreeError.create(null, this._peek.sourceSpan, `Invalid ICU message. Missing '}'.`));\n return;\n }\n const sourceSpan = new ParseSourceSpan(token.sourceSpan.start, this._peek.sourceSpan.end, token.sourceSpan.fullStart);\n this._addToParent(new Expansion(switchValue.parts[0], type.parts[0], cases, sourceSpan, switchValue.sourceSpan));\n this._advance();\n }\n _parseExpansionCase() {\n const value = this._advance();\n // read {\n if (this._peek.type !== 21 /* TokenType.EXPANSION_CASE_EXP_START */) {\n this.errors.push(TreeError.create(null, this._peek.sourceSpan, `Invalid ICU message. Missing '{'.`));\n return null;\n }\n // read until }\n const start = this._advance();\n const exp = this._collectExpansionExpTokens(start);\n if (!exp) return null;\n const end = this._advance();\n exp.push({\n type: 24 /* TokenType.EOF */,\n parts: [],\n sourceSpan: end.sourceSpan\n });\n // parse everything in between { and }\n const expansionCaseParser = new _TreeBuilder(exp, this.getTagDefinition);\n expansionCaseParser.build();\n if (expansionCaseParser.errors.length > 0) {\n this.errors = this.errors.concat(expansionCaseParser.errors);\n return null;\n }\n const sourceSpan = new ParseSourceSpan(value.sourceSpan.start, end.sourceSpan.end, value.sourceSpan.fullStart);\n const expSourceSpan = new ParseSourceSpan(start.sourceSpan.start, end.sourceSpan.end, start.sourceSpan.fullStart);\n return new ExpansionCase(value.parts[0], expansionCaseParser.rootNodes, sourceSpan, value.sourceSpan, expSourceSpan);\n }\n _collectExpansionExpTokens(start) {\n const exp = [];\n const expansionFormStack = [21 /* TokenType.EXPANSION_CASE_EXP_START */];\n while (true) {\n if (this._peek.type === 19 /* TokenType.EXPANSION_FORM_START */ || this._peek.type === 21 /* TokenType.EXPANSION_CASE_EXP_START */) {\n expansionFormStack.push(this._peek.type);\n }\n if (this._peek.type === 22 /* TokenType.EXPANSION_CASE_EXP_END */) {\n if (lastOnStack(expansionFormStack, 21 /* TokenType.EXPANSION_CASE_EXP_START */)) {\n expansionFormStack.pop();\n if (expansionFormStack.length === 0) return exp;\n } else {\n this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));\n return null;\n }\n }\n if (this._peek.type === 23 /* TokenType.EXPANSION_FORM_END */) {\n if (lastOnStack(expansionFormStack, 19 /* TokenType.EXPANSION_FORM_START */)) {\n expansionFormStack.pop();\n } else {\n this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));\n return null;\n }\n }\n if (this._peek.type === 24 /* TokenType.EOF */) {\n this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));\n return null;\n }\n exp.push(this._advance());\n }\n }\n _consumeText(token) {\n const tokens = [token];\n const startSpan = token.sourceSpan;\n let text = token.parts[0];\n if (text.length > 0 && text[0] === '\\n') {\n const parent = this._getContainer();\n // This is unlikely to happen, but we have an assertion just in case.\n if (parent instanceof BlockGroup) {\n this.errors.push(TreeError.create(null, startSpan, 'Text cannot be placed directly inside of a block group.'));\n return null;\n }\n if (parent != null && parent.children.length === 0 && this.getTagDefinition(parent.name).ignoreFirstLf) {\n text = text.substring(1);\n tokens[0] = {\n type: token.type,\n sourceSpan: token.sourceSpan,\n parts: [text]\n };\n }\n }\n while (this._peek.type === 8 /* TokenType.INTERPOLATION */ || this._peek.type === 5 /* TokenType.TEXT */ || this._peek.type === 9 /* TokenType.ENCODED_ENTITY */) {\n token = this._advance();\n tokens.push(token);\n if (token.type === 8 /* TokenType.INTERPOLATION */) {\n // For backward compatibility we decode HTML entities that appear in interpolation\n // expressions. This is arguably a bug, but it could be a considerable breaking change to\n // fix it. It should be addressed in a larger project to refactor the entire parser/lexer\n // chain after View Engine has been removed.\n text += token.parts.join('').replace(/&([^;]+);/g, decodeEntity);\n } else if (token.type === 9 /* TokenType.ENCODED_ENTITY */) {\n text += token.parts[0];\n } else {\n text += token.parts.join('');\n }\n }\n if (text.length > 0) {\n const endSpan = token.sourceSpan;\n this._addToParent(new Text(text, new ParseSourceSpan(startSpan.start, endSpan.end, startSpan.fullStart, startSpan.details), tokens));\n }\n }\n _closeVoidElement() {\n const el = this._getContainer();\n if (el instanceof Element && this.getTagDefinition(el.name).isVoid) {\n this._containerStack.pop();\n }\n }\n _consumeStartTag(startTagToken) {\n const [prefix, name] = startTagToken.parts;\n const attrs = [];\n while (this._peek.type === 14 /* TokenType.ATTR_NAME */) {\n attrs.push(this._consumeAttr(this._advance()));\n }\n const fullName = this._getElementFullName(prefix, name, this._getClosestParentElement());\n let selfClosing = false;\n // Note: There could have been a tokenizer error\n // so that we don't get a token for the end tag...\n if (this._peek.type === 2 /* TokenType.TAG_OPEN_END_VOID */) {\n this._advance();\n selfClosing = true;\n const tagDef = this.getTagDefinition(fullName);\n if (!(tagDef.canSelfClose || getNsPrefix(fullName) !== null || tagDef.isVoid)) {\n this.errors.push(TreeError.create(fullName, startTagToken.sourceSpan, `Only void, custom and foreign elements can be self closed \"${startTagToken.parts[1]}\"`));\n }\n } else if (this._peek.type === 1 /* TokenType.TAG_OPEN_END */) {\n this._advance();\n selfClosing = false;\n }\n const end = this._peek.sourceSpan.fullStart;\n const span = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);\n // Create a separate `startSpan` because `span` will be modified when there is an `end` span.\n const startSpan = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);\n const el = new Element(fullName, attrs, [], span, startSpan, undefined);\n const parentEl = this._getContainer();\n this._pushContainer(el, parentEl instanceof Element && this.getTagDefinition(parentEl.name).isClosedByChild(el.name));\n if (selfClosing) {\n // Elements that are self-closed have their `endSourceSpan` set to the full span, as the\n // element start tag also represents the end tag.\n this._popContainer(fullName, Element, span);\n } else if (startTagToken.type === 4 /* TokenType.INCOMPLETE_TAG_OPEN */) {\n // We already know the opening tag is not complete, so it is unlikely it has a corresponding\n // close tag. Let's optimistically parse it as a full element and emit an error.\n this._popContainer(fullName, Element, null);\n this.errors.push(TreeError.create(fullName, span, `Opening tag \"${fullName}\" not terminated.`));\n }\n }\n _pushContainer(node, isClosedByChild) {\n if (isClosedByChild) {\n this._containerStack.pop();\n }\n this._addToParent(node);\n this._containerStack.push(node);\n }\n _consumeEndTag(endTagToken) {\n const fullName = this._getElementFullName(endTagToken.parts[0], endTagToken.parts[1], this._getClosestParentElement());\n if (this.getTagDefinition(fullName).isVoid) {\n this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, `Void elements do not have end tags \"${endTagToken.parts[1]}\"`));\n } else if (!this._popContainer(fullName, Element, endTagToken.sourceSpan)) {\n const errMsg = `Unexpected closing tag \"${fullName}\". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags`;\n this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, errMsg));\n }\n }\n /**\n * Closes the nearest element with the tag name `fullName` in the parse tree.\n * `endSourceSpan` is the span of the closing tag, or null if the element does\n * not have a closing tag (for example, this happens when an incomplete\n * opening tag is recovered).\n */\n _popContainer(fullName, expectedType, endSourceSpan) {\n let unexpectedCloseTagDetected = false;\n for (let stackIndex = this._containerStack.length - 1; stackIndex >= 0; stackIndex--) {\n const node = this._containerStack[stackIndex];\n const name = node instanceof BlockGroup ? node.blocks[0]?.name : node.name;\n if (name === fullName && node instanceof expectedType) {\n // Record the parse span with the element that is being closed. Any elements that are\n // removed from the element stack at this point are closed implicitly, so they won't get\n // an end source span (as there is no explicit closing element).\n node.endSourceSpan = endSourceSpan;\n node.sourceSpan.end = endSourceSpan !== null ? endSourceSpan.end : node.sourceSpan.end;\n this._containerStack.splice(stackIndex, this._containerStack.length - stackIndex);\n return !unexpectedCloseTagDetected;\n }\n // Blocks are self-closing while block groups and (most times) elements are not.\n if (node instanceof BlockGroup || node instanceof Element && !this.getTagDefinition(node.name).closedByParent) {\n // Note that we encountered an unexpected close tag but continue processing the element\n // stack so we can assign an `endSourceSpan` if there is a corresponding start tag for this\n // end tag in the stack.\n unexpectedCloseTagDetected = true;\n }\n }\n return false;\n }\n _consumeAttr(attrName) {\n const fullName = mergeNsAndName(attrName.parts[0], attrName.parts[1]);\n let attrEnd = attrName.sourceSpan.end;\n // Consume any quote\n if (this._peek.type === 15 /* TokenType.ATTR_QUOTE */) {\n this._advance();\n }\n // Consume the attribute value\n let value = '';\n const valueTokens = [];\n let valueStartSpan = undefined;\n let valueEnd = undefined;\n // NOTE: We need to use a new variable `nextTokenType` here to hide the actual type of\n // `_peek.type` from TS. Otherwise TS will narrow the type of `_peek.type` preventing it from\n // being able to consider `ATTR_VALUE_INTERPOLATION` as an option. This is because TS is not\n // able to see that `_advance()` will actually mutate `_peek`.\n const nextTokenType = this._peek.type;\n if (nextTokenType === 16 /* TokenType.ATTR_VALUE_TEXT */) {\n valueStartSpan = this._peek.sourceSpan;\n valueEnd = this._peek.sourceSpan.end;\n while (this._peek.type === 16 /* TokenType.ATTR_VALUE_TEXT */ || this._peek.type === 17 /* TokenType.ATTR_VALUE_INTERPOLATION */ || this._peek.type === 9 /* TokenType.ENCODED_ENTITY */) {\n const valueToken = this._advance();\n valueTokens.push(valueToken);\n if (valueToken.type === 17 /* TokenType.ATTR_VALUE_INTERPOLATION */) {\n // For backward compatibility we decode HTML entities that appear in interpolation\n // expressions. This is arguably a bug, but it could be a considerable breaking change to\n // fix it. It should be addressed in a larger project to refactor the entire parser/lexer\n // chain after View Engine has been removed.\n value += valueToken.parts.join('').replace(/&([^;]+);/g, decodeEntity);\n } else if (valueToken.type === 9 /* TokenType.ENCODED_ENTITY */) {\n value += valueToken.parts[0];\n } else {\n value += valueToken.parts.join('');\n }\n valueEnd = attrEnd = valueToken.sourceSpan.end;\n }\n }\n // Consume any quote\n if (this._peek.type === 15 /* TokenType.ATTR_QUOTE */) {\n const quoteToken = this._advance();\n attrEnd = quoteToken.sourceSpan.end;\n }\n const valueSpan = valueStartSpan && valueEnd && new ParseSourceSpan(valueStartSpan.start, valueEnd, valueStartSpan.fullStart);\n return new Attribute(fullName, value, new ParseSourceSpan(attrName.sourceSpan.start, attrEnd, attrName.sourceSpan.fullStart), attrName.sourceSpan, valueSpan, valueTokens.length > 0 ? valueTokens : undefined, undefined);\n }\n _consumeBlockGroupOpen(token) {\n const end = this._peek.sourceSpan.fullStart;\n const span = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);\n // Create a separate `startSpan` because `span` will be modified when there is an `end` span.\n const startSpan = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);\n const blockGroup = new BlockGroup([], span, startSpan, null);\n this._pushContainer(blockGroup, false);\n const implicitBlock = this._consumeBlock(token, 26 /* TokenType.BLOCK_GROUP_OPEN_END */);\n // Block parameters are consumed as a part of the implicit block so we need to expand the\n // start source span once the block is parsed to include the full opening tag.\n startSpan.end = implicitBlock.startSourceSpan.end;\n }\n _consumeBlock(token, closeToken) {\n // The start of a block implicitly closes the previous block.\n this._conditionallyClosePreviousBlock();\n const parameters = [];\n while (this._peek.type === 28 /* TokenType.BLOCK_PARAMETER */) {\n const paramToken = this._advance();\n parameters.push(new BlockParameter(paramToken.parts[0], paramToken.sourceSpan));\n }\n if (this._peek.type === closeToken) {\n this._advance();\n }\n const end = this._peek.sourceSpan.fullStart;\n const span = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);\n // Create a separate `startSpan` because `span` will be modified when there is an `end` span.\n const startSpan = new ParseSourceSpan(token.sourceSpan.start, end, token.sourceSpan.fullStart);\n const block = new Block(token.parts[0], parameters, [], span, startSpan);\n const parent = this._getContainer();\n if (!(parent instanceof BlockGroup)) {\n this.errors.push(TreeError.create(block.name, block.sourceSpan, 'Blocks can only be placed inside of block groups.'));\n } else {\n parent.blocks.push(block);\n this._containerStack.push(block);\n }\n return block;\n }\n _consumeBlockGroupClose(token) {\n const name = token.parts[0];\n const previousContainer = this._getContainer();\n // Blocks are implcitly closed by the block group.\n this._conditionallyClosePreviousBlock();\n if (!this._popContainer(name, BlockGroup, token.sourceSpan)) {\n const context = previousContainer instanceof Element ? `There is an unclosed \"${previousContainer.name}\" HTML tag named that may have to be closed first.` : `The block may have been closed earlier.`;\n this.errors.push(TreeError.create(name, token.sourceSpan, `Unexpected closing block \"${name}\". ${context}`));\n }\n }\n _conditionallyClosePreviousBlock() {\n const container = this._getContainer();\n if (container instanceof Block) {\n // Blocks don't have an explicit closing tag, they're closed either by the next block or\n // the end of the block group. Infer the end span from the last child node.\n const lastChild = container.children.length ? container.children[container.children.length - 1] : null;\n const endSpan = lastChild === null ? null : new ParseSourceSpan(lastChild.sourceSpan.end, lastChild.sourceSpan.end);\n this._popContainer(container.name, Block, endSpan);\n }\n }\n _getContainer() {\n return this._containerStack.length > 0 ? this._containerStack[this._containerStack.length - 1] : null;\n }\n _getClosestParentElement() {\n for (let i = this._containerStack.length - 1; i > -1; i--) {\n if (this._containerStack[i] instanceof Element) {\n return this._containerStack[i];\n }\n }\n return null;\n }\n _addToParent(node) {\n const parent = this._getContainer();\n if (parent === null) {\n this.rootNodes.push(node);\n } else if (parent instanceof BlockGroup) {\n // Due to how parsing is set up, we're unlikely to hit this code path, but we\n // have the assertion here just in case and to satisfy the type checker.\n this.errors.push(TreeError.create(null, node.sourceSpan, 'Block groups can only contain blocks.'));\n } else {\n parent.children.push(node);\n }\n }\n _getElementFullName(prefix, localName, parentElement) {\n if (prefix === '') {\n prefix = this.getTagDefinition(localName).implicitNamespacePrefix || '';\n if (prefix === '' && parentElement != null) {\n const parentTagName = splitNsName(parentElement.name)[1];\n const parentTagDefinition = this.getTagDefinition(parentTagName);\n if (!parentTagDefinition.preventNamespaceInheritance) {\n prefix = getNsPrefix(parentElement.name);\n }\n }\n }\n return mergeNsAndName(prefix, localName);\n }\n}\nfunction lastOnStack(stack, element) {\n return stack.length > 0 && stack[stack.length - 1] === element;\n}\n/**\n * Decode the `entity` string, which we believe is the contents of an HTML entity.\n *\n * If the string is not actually a valid/known entity then just return the original `match` string.\n */\nfunction decodeEntity(match, entity) {\n if (NAMED_ENTITIES[entity] !== undefined) {\n return NAMED_ENTITIES[entity] || match;\n }\n if (/^#x[a-f0-9]+$/i.test(entity)) {\n return String.fromCodePoint(parseInt(entity.slice(2), 16));\n }\n if (/^#\\d+$/.test(entity)) {\n return String.fromCodePoint(parseInt(entity.slice(1), 10));\n }\n return match;\n}\nclass HtmlParser extends Parser {\n constructor() {\n super(getHtmlTagDefinition);\n }\n parse(source, url, options) {\n return super.parse(source, url, options);\n }\n}\nconst PRESERVE_WS_ATTR_NAME = 'ngPreserveWhitespaces';\nconst SKIP_WS_TRIM_TAGS = new Set(['pre', 'template', 'textarea', 'script', 'style']);\n// Equivalent to \\s with \\u00a0 (non-breaking space) excluded.\n// Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp\nconst WS_CHARS = ' \\f\\n\\r\\t\\v\\u1680\\u180e\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff';\nconst NO_WS_REGEXP = new RegExp(`[^${WS_CHARS}]`);\nconst WS_REPLACE_REGEXP = new RegExp(`[${WS_CHARS}]{2,}`, 'g');\nfunction hasPreserveWhitespacesAttr(attrs) {\n return attrs.some(attr => attr.name === PRESERVE_WS_ATTR_NAME);\n}\n/**\n * &ngsp; is a placeholder for non-removable space\n * &ngsp; is converted to the 0xE500 PUA (Private Use Areas) unicode character\n * and later on replaced by a space.\n */\nfunction replaceNgsp(value) {\n // lexer is replacing the &ngsp; pseudo-entity with NGSP_UNICODE\n return value.replace(new RegExp(NGSP_UNICODE, 'g'), ' ');\n}\n/**\n * This visitor can walk HTML parse tree and remove / trim text nodes using the following rules:\n * - consider spaces, tabs and new lines as whitespace characters;\n * - drop text nodes consisting of whitespace characters only;\n * - for all other text nodes replace consecutive whitespace characters with one space;\n * - convert &ngsp; pseudo-entity to a single space;\n *\n * Removal and trimming of whitespaces have positive performance impact (less code to generate\n * while compiling templates, faster view creation). At the same time it can be \"destructive\"\n * in some cases (whitespaces can influence layout). Because of the potential of breaking layout\n * this visitor is not activated by default in Angular 5 and people need to explicitly opt-in for\n * whitespace removal. The default option for whitespace removal will be revisited in Angular 6\n * and might be changed to \"on\" by default.\n */\nclass WhitespaceVisitor {\n visitElement(element, context) {\n if (SKIP_WS_TRIM_TAGS.has(element.name) || hasPreserveWhitespacesAttr(element.attrs)) {\n // don't descent into elements where we need to preserve whitespaces\n // but still visit all attributes to eliminate one used as a market to preserve WS\n return new Element(element.name, visitAll(this, element.attrs), element.children, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);\n }\n return new Element(element.name, element.attrs, visitAllWithSiblings(this, element.children), element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);\n }\n visitAttribute(attribute, context) {\n return attribute.name !== PRESERVE_WS_ATTR_NAME ? attribute : null;\n }\n visitText(text, context) {\n const isNotBlank = text.value.match(NO_WS_REGEXP);\n const hasExpansionSibling = context && (context.prev instanceof Expansion || context.next instanceof Expansion);\n if (isNotBlank || hasExpansionSibling) {\n // Process the whitespace in the tokens of this Text node\n const tokens = text.tokens.map(token => token.type === 5 /* TokenType.TEXT */ ? createWhitespaceProcessedTextToken(token) : token);\n // Process the whitespace of the value of this Text node\n const value = processWhitespace(text.value);\n return new Text(value, text.sourceSpan, tokens, text.i18n);\n }\n return null;\n }\n visitComment(comment, context) {\n return comment;\n }\n visitExpansion(expansion, context) {\n return expansion;\n }\n visitExpansionCase(expansionCase, context) {\n return expansionCase;\n }\n visitBlockGroup(group, context) {\n return new BlockGroup(visitAllWithSiblings(this, group.blocks), group.sourceSpan, group.startSourceSpan, group.endSourceSpan);\n }\n visitBlock(block, context) {\n return new Block(block.name, block.parameters, visitAllWithSiblings(this, block.children), block.sourceSpan, block.startSourceSpan);\n }\n visitBlockParameter(parameter, context) {\n return parameter;\n }\n}\nfunction createWhitespaceProcessedTextToken({\n type,\n parts,\n sourceSpan\n}) {\n return {\n type,\n parts: [processWhitespace(parts[0])],\n sourceSpan\n };\n}\nfunction processWhitespace(text) {\n return replaceNgsp(text).replace(WS_REPLACE_REGEXP, ' ');\n}\nfunction removeWhitespaces(htmlAstWithErrors) {\n return new ParseTreeResult(visitAll(new WhitespaceVisitor(), htmlAstWithErrors.rootNodes), htmlAstWithErrors.errors);\n}\nfunction visitAllWithSiblings(visitor, nodes) {\n const result = [];\n nodes.forEach((ast, i) => {\n const context = {\n prev: nodes[i - 1],\n next: nodes[i + 1]\n };\n const astResult = ast.visit(visitor, context);\n if (astResult) {\n result.push(astResult);\n }\n });\n return result;\n}\nfunction mapEntry(key, value) {\n return {\n key,\n value,\n quoted: false\n };\n}\nfunction mapLiteral(obj, quoted = false) {\n return literalMap(Object.keys(obj).map(key => ({\n key,\n quoted,\n value: obj[key]\n })));\n}\n\n/**\n * Set of tagName|propertyName corresponding to Trusted Types sinks. Properties applying to all\n * tags use '*'.\n *\n * Extracted from, and should be kept in sync with\n * https://w3c.github.io/webappsec-trusted-types/dist/spec/#integrations\n */\nconst TRUSTED_TYPES_SINKS = new Set([\n// NOTE: All strings in this set *must* be lowercase!\n// TrustedHTML\n'iframe|srcdoc', '*|innerhtml', '*|outerhtml',\n// NB: no TrustedScript here, as the corresponding tags are stripped by the compiler.\n// TrustedScriptURL\n'embed|src', 'object|codebase', 'object|data']);\n/**\n * isTrustedTypesSink returns true if the given property on the given DOM tag is a Trusted Types\n * sink. In that case, use `ElementSchemaRegistry.securityContext` to determine which particular\n * Trusted Type is required for values passed to the sink:\n * - SecurityContext.HTML corresponds to TrustedHTML\n * - SecurityContext.RESOURCE_URL corresponds to TrustedScriptURL\n */\nfunction isTrustedTypesSink(tagName, propName) {\n // Make sure comparisons are case insensitive, so that case differences between attribute and\n // property names do not have a security impact.\n tagName = tagName.toLowerCase();\n propName = propName.toLowerCase();\n return TRUSTED_TYPES_SINKS.has(tagName + '|' + propName) || TRUSTED_TYPES_SINKS.has('*|' + propName);\n}\nconst PROPERTY_PARTS_SEPARATOR = '.';\nconst ATTRIBUTE_PREFIX = 'attr';\nconst CLASS_PREFIX = 'class';\nconst STYLE_PREFIX = 'style';\nconst TEMPLATE_ATTR_PREFIX$1 = '*';\nconst ANIMATE_PROP_PREFIX = 'animate-';\n/**\n * Parses bindings in templates and in the directive host area.\n */\nclass BindingParser {\n constructor(_exprParser, _interpolationConfig, _schemaRegistry, errors) {\n this._exprParser = _exprParser;\n this._interpolationConfig = _interpolationConfig;\n this._schemaRegistry = _schemaRegistry;\n this.errors = errors;\n }\n get interpolationConfig() {\n return this._interpolationConfig;\n }\n createBoundHostProperties(properties, sourceSpan) {\n const boundProps = [];\n for (const propName of Object.keys(properties)) {\n const expression = properties[propName];\n if (typeof expression === 'string') {\n this.parsePropertyBinding(propName, expression, true, sourceSpan, sourceSpan.start.offset, undefined, [],\n // Use the `sourceSpan` for `keySpan`. This isn't really accurate, but neither is the\n // sourceSpan, as it represents the sourceSpan of the host itself rather than the\n // source of the host binding (which doesn't exist in the template). Regardless,\n // neither of these values are used in Ivy but are only here to satisfy the function\n // signature. This should likely be refactored in the future so that `sourceSpan`\n // isn't being used inaccurately.\n boundProps, sourceSpan);\n } else {\n this._reportError(`Value of the host property binding \"${propName}\" needs to be a string representing an expression but got \"${expression}\" (${typeof expression})`, sourceSpan);\n }\n }\n return boundProps;\n }\n createDirectiveHostEventAsts(hostListeners, sourceSpan) {\n const targetEvents = [];\n for (const propName of Object.keys(hostListeners)) {\n const expression = hostListeners[propName];\n if (typeof expression === 'string') {\n // Use the `sourceSpan` for `keySpan` and `handlerSpan`. This isn't really accurate, but\n // neither is the `sourceSpan`, as it represents the `sourceSpan` of the host itself\n // rather than the source of the host binding (which doesn't exist in the template).\n // Regardless, neither of these values are used in Ivy but are only here to satisfy the\n // function signature. This should likely be refactored in the future so that `sourceSpan`\n // isn't being used inaccurately.\n this.parseEvent(propName, expression, /* isAssignmentEvent */false, sourceSpan, sourceSpan, [], targetEvents, sourceSpan);\n } else {\n this._reportError(`Value of the host listener \"${propName}\" needs to be a string representing an expression but got \"${expression}\" (${typeof expression})`, sourceSpan);\n }\n }\n return targetEvents;\n }\n parseInterpolation(value, sourceSpan, interpolatedTokens) {\n const sourceInfo = sourceSpan.start.toString();\n const absoluteOffset = sourceSpan.fullStart.offset;\n try {\n const ast = this._exprParser.parseInterpolation(value, sourceInfo, absoluteOffset, interpolatedTokens, this._interpolationConfig);\n if (ast) this._reportExpressionParserErrors(ast.errors, sourceSpan);\n return ast;\n } catch (e) {\n this._reportError(`${e}`, sourceSpan);\n return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);\n }\n }\n /**\n * Similar to `parseInterpolation`, but treats the provided string as a single expression\n * element that would normally appear within the interpolation prefix and suffix (`{{` and `}}`).\n * This is used for parsing the switch expression in ICUs.\n */\n parseInterpolationExpression(expression, sourceSpan) {\n const sourceInfo = sourceSpan.start.toString();\n const absoluteOffset = sourceSpan.start.offset;\n try {\n const ast = this._exprParser.parseInterpolationExpression(expression, sourceInfo, absoluteOffset);\n if (ast) this._reportExpressionParserErrors(ast.errors, sourceSpan);\n return ast;\n } catch (e) {\n this._reportError(`${e}`, sourceSpan);\n return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);\n }\n }\n /**\n * Parses the bindings in a microsyntax expression, and converts them to\n * `ParsedProperty` or `ParsedVariable`.\n *\n * @param tplKey template binding name\n * @param tplValue template binding value\n * @param sourceSpan span of template binding relative to entire the template\n * @param absoluteValueOffset start of the tplValue relative to the entire template\n * @param targetMatchableAttrs potential attributes to match in the template\n * @param targetProps target property bindings in the template\n * @param targetVars target variables in the template\n */\n parseInlineTemplateBinding(tplKey, tplValue, sourceSpan, absoluteValueOffset, targetMatchableAttrs, targetProps, targetVars, isIvyAst) {\n const absoluteKeyOffset = sourceSpan.start.offset + TEMPLATE_ATTR_PREFIX$1.length;\n const bindings = this._parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset);\n for (const binding of bindings) {\n // sourceSpan is for the entire HTML attribute. bindingSpan is for a particular\n // binding within the microsyntax expression so it's more narrow than sourceSpan.\n const bindingSpan = moveParseSourceSpan(sourceSpan, binding.sourceSpan);\n const key = binding.key.source;\n const keySpan = moveParseSourceSpan(sourceSpan, binding.key.span);\n if (binding instanceof VariableBinding) {\n const value = binding.value ? binding.value.source : '$implicit';\n const valueSpan = binding.value ? moveParseSourceSpan(sourceSpan, binding.value.span) : undefined;\n targetVars.push(new ParsedVariable(key, value, bindingSpan, keySpan, valueSpan));\n } else if (binding.value) {\n const srcSpan = isIvyAst ? bindingSpan : sourceSpan;\n const valueSpan = moveParseSourceSpan(sourceSpan, binding.value.ast.sourceSpan);\n this._parsePropertyAst(key, binding.value, srcSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);\n } else {\n targetMatchableAttrs.push([key, '' /* value */]);\n // Since this is a literal attribute with no RHS, source span should be\n // just the key span.\n this.parseLiteralAttr(key, null /* value */, keySpan, absoluteValueOffset, undefined /* valueSpan */, targetMatchableAttrs, targetProps, keySpan);\n }\n }\n }\n /**\n * Parses the bindings in a microsyntax expression, e.g.\n * ```\n * \n * ```\n *\n * @param tplKey template binding name\n * @param tplValue template binding value\n * @param sourceSpan span of template binding relative to entire the template\n * @param absoluteKeyOffset start of the `tplKey`\n * @param absoluteValueOffset start of the `tplValue`\n */\n _parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset) {\n const sourceInfo = sourceSpan.start.toString();\n try {\n const bindingsResult = this._exprParser.parseTemplateBindings(tplKey, tplValue, sourceInfo, absoluteKeyOffset, absoluteValueOffset);\n this._reportExpressionParserErrors(bindingsResult.errors, sourceSpan);\n bindingsResult.warnings.forEach(warning => {\n this._reportError(warning, sourceSpan, ParseErrorLevel.WARNING);\n });\n return bindingsResult.templateBindings;\n } catch (e) {\n this._reportError(`${e}`, sourceSpan);\n return [];\n }\n }\n parseLiteralAttr(name, value, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs, targetProps, keySpan) {\n if (isAnimationLabel(name)) {\n name = name.substring(1);\n if (keySpan !== undefined) {\n keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));\n }\n if (value) {\n this._reportError(`Assigning animation triggers via @prop=\"exp\" attributes with an expression is invalid.` + ` Use property bindings (e.g. [@prop]=\"exp\") or use an attribute without a value (e.g. @prop) instead.`, sourceSpan, ParseErrorLevel.ERROR);\n }\n this._parseAnimation(name, value, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);\n } else {\n targetProps.push(new ParsedProperty(name, this._exprParser.wrapLiteralPrimitive(value, '', absoluteOffset), ParsedPropertyType.LITERAL_ATTR, sourceSpan, keySpan, valueSpan));\n }\n }\n parsePropertyBinding(name, expression, isHost, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs, targetProps, keySpan) {\n if (name.length === 0) {\n this._reportError(`Property name is missing in binding`, sourceSpan);\n }\n let isAnimationProp = false;\n if (name.startsWith(ANIMATE_PROP_PREFIX)) {\n isAnimationProp = true;\n name = name.substring(ANIMATE_PROP_PREFIX.length);\n if (keySpan !== undefined) {\n keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + ANIMATE_PROP_PREFIX.length, keySpan.end.offset));\n }\n } else if (isAnimationLabel(name)) {\n isAnimationProp = true;\n name = name.substring(1);\n if (keySpan !== undefined) {\n keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));\n }\n }\n if (isAnimationProp) {\n this._parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);\n } else {\n this._parsePropertyAst(name, this.parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);\n }\n }\n parsePropertyInterpolation(name, value, sourceSpan, valueSpan, targetMatchableAttrs, targetProps, keySpan, interpolatedTokens) {\n const expr = this.parseInterpolation(value, valueSpan || sourceSpan, interpolatedTokens);\n if (expr) {\n this._parsePropertyAst(name, expr, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);\n return true;\n }\n return false;\n }\n _parsePropertyAst(name, ast, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps) {\n targetMatchableAttrs.push([name, ast.source]);\n targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.DEFAULT, sourceSpan, keySpan, valueSpan));\n }\n _parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps) {\n if (name.length === 0) {\n this._reportError('Animation trigger is missing', sourceSpan);\n }\n // This will occur when a @trigger is not paired with an expression.\n // For animations it is valid to not have an expression since */void\n // states will be applied by angular when the element is attached/detached\n const ast = this.parseBinding(expression || 'undefined', false, valueSpan || sourceSpan, absoluteOffset);\n targetMatchableAttrs.push([name, ast.source]);\n targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.ANIMATION, sourceSpan, keySpan, valueSpan));\n }\n parseBinding(value, isHostBinding, sourceSpan, absoluteOffset) {\n const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown)').toString();\n try {\n const ast = isHostBinding ? this._exprParser.parseSimpleBinding(value, sourceInfo, absoluteOffset, this._interpolationConfig) : this._exprParser.parseBinding(value, sourceInfo, absoluteOffset, this._interpolationConfig);\n if (ast) this._reportExpressionParserErrors(ast.errors, sourceSpan);\n return ast;\n } catch (e) {\n this._reportError(`${e}`, sourceSpan);\n return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);\n }\n }\n createBoundElementProperty(elementSelector, boundProp, skipValidation = false, mapPropertyName = true) {\n if (boundProp.isAnimation) {\n return new BoundElementProperty(boundProp.name, 4 /* BindingType.Animation */, SecurityContext.NONE, boundProp.expression, null, boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan);\n }\n let unit = null;\n let bindingType = undefined;\n let boundPropertyName = null;\n const parts = boundProp.name.split(PROPERTY_PARTS_SEPARATOR);\n let securityContexts = undefined;\n // Check for special cases (prefix style, attr, class)\n if (parts.length > 1) {\n if (parts[0] == ATTRIBUTE_PREFIX) {\n boundPropertyName = parts.slice(1).join(PROPERTY_PARTS_SEPARATOR);\n if (!skipValidation) {\n this._validatePropertyOrAttributeName(boundPropertyName, boundProp.sourceSpan, true);\n }\n securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, boundPropertyName, true);\n const nsSeparatorIdx = boundPropertyName.indexOf(':');\n if (nsSeparatorIdx > -1) {\n const ns = boundPropertyName.substring(0, nsSeparatorIdx);\n const name = boundPropertyName.substring(nsSeparatorIdx + 1);\n boundPropertyName = mergeNsAndName(ns, name);\n }\n bindingType = 1 /* BindingType.Attribute */;\n } else if (parts[0] == CLASS_PREFIX) {\n boundPropertyName = parts[1];\n bindingType = 2 /* BindingType.Class */;\n securityContexts = [SecurityContext.NONE];\n } else if (parts[0] == STYLE_PREFIX) {\n unit = parts.length > 2 ? parts[2] : null;\n boundPropertyName = parts[1];\n bindingType = 3 /* BindingType.Style */;\n securityContexts = [SecurityContext.STYLE];\n }\n }\n // If not a special case, use the full property name\n if (boundPropertyName === null) {\n const mappedPropName = this._schemaRegistry.getMappedPropName(boundProp.name);\n boundPropertyName = mapPropertyName ? mappedPropName : boundProp.name;\n securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, mappedPropName, false);\n bindingType = 0 /* BindingType.Property */;\n if (!skipValidation) {\n this._validatePropertyOrAttributeName(mappedPropName, boundProp.sourceSpan, false);\n }\n }\n return new BoundElementProperty(boundPropertyName, bindingType, securityContexts[0], boundProp.expression, unit, boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan);\n }\n // TODO: keySpan should be required but was made optional to avoid changing VE parser.\n parseEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {\n if (name.length === 0) {\n this._reportError(`Event name is missing in binding`, sourceSpan);\n }\n if (isAnimationLabel(name)) {\n name = name.slice(1);\n if (keySpan !== undefined) {\n keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));\n }\n this._parseAnimationEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetEvents, keySpan);\n } else {\n this._parseRegularEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan);\n }\n }\n calcPossibleSecurityContexts(selector, propName, isAttribute) {\n const prop = this._schemaRegistry.getMappedPropName(propName);\n return calcPossibleSecurityContexts(this._schemaRegistry, selector, prop, isAttribute);\n }\n _parseAnimationEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetEvents, keySpan) {\n const matches = splitAtPeriod(name, [name, '']);\n const eventName = matches[0];\n const phase = matches[1].toLowerCase();\n const ast = this._parseAction(expression, isAssignmentEvent, handlerSpan);\n targetEvents.push(new ParsedEvent(eventName, phase, 1 /* ParsedEventType.Animation */, ast, sourceSpan, handlerSpan, keySpan));\n if (eventName.length === 0) {\n this._reportError(`Animation event name is missing in binding`, sourceSpan);\n }\n if (phase) {\n if (phase !== 'start' && phase !== 'done') {\n this._reportError(`The provided animation output phase value \"${phase}\" for \"@${eventName}\" is not supported (use start or done)`, sourceSpan);\n }\n } else {\n this._reportError(`The animation trigger output event (@${eventName}) is missing its phase value name (start or done are currently supported)`, sourceSpan);\n }\n }\n _parseRegularEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {\n // long format: 'target: eventName'\n const [target, eventName] = splitAtColon(name, [null, name]);\n const ast = this._parseAction(expression, isAssignmentEvent, handlerSpan);\n targetMatchableAttrs.push([name, ast.source]);\n targetEvents.push(new ParsedEvent(eventName, target, 0 /* ParsedEventType.Regular */, ast, sourceSpan, handlerSpan, keySpan));\n // Don't detect directives for event names for now,\n // so don't add the event name to the matchableAttrs\n }\n _parseAction(value, isAssignmentEvent, sourceSpan) {\n const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown').toString();\n const absoluteOffset = sourceSpan && sourceSpan.start ? sourceSpan.start.offset : 0;\n try {\n const ast = this._exprParser.parseAction(value, isAssignmentEvent, sourceInfo, absoluteOffset, this._interpolationConfig);\n if (ast) {\n this._reportExpressionParserErrors(ast.errors, sourceSpan);\n }\n if (!ast || ast.ast instanceof EmptyExpr$1) {\n this._reportError(`Empty expressions are not allowed`, sourceSpan);\n return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);\n }\n return ast;\n } catch (e) {\n this._reportError(`${e}`, sourceSpan);\n return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);\n }\n }\n _reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {\n this.errors.push(new ParseError(sourceSpan, message, level));\n }\n _reportExpressionParserErrors(errors, sourceSpan) {\n for (const error of errors) {\n this._reportError(error.message, sourceSpan);\n }\n }\n /**\n * @param propName the name of the property / attribute\n * @param sourceSpan\n * @param isAttr true when binding to an attribute\n */\n _validatePropertyOrAttributeName(propName, sourceSpan, isAttr) {\n const report = isAttr ? this._schemaRegistry.validateAttribute(propName) : this._schemaRegistry.validateProperty(propName);\n if (report.error) {\n this._reportError(report.msg, sourceSpan, ParseErrorLevel.ERROR);\n }\n }\n}\nclass PipeCollector extends RecursiveAstVisitor {\n constructor() {\n super(...arguments);\n this.pipes = new Map();\n }\n visitPipe(ast, context) {\n this.pipes.set(ast.name, ast);\n ast.exp.visit(this);\n this.visitAll(ast.args, context);\n return null;\n }\n}\nfunction isAnimationLabel(name) {\n return name[0] == '@';\n}\nfunction calcPossibleSecurityContexts(registry, selector, propName, isAttribute) {\n const ctxs = [];\n CssSelector.parse(selector).forEach(selector => {\n const elementNames = selector.element ? [selector.element] : registry.allKnownElementNames();\n const notElementNames = new Set(selector.notSelectors.filter(selector => selector.isElementSelector()).map(selector => selector.element));\n const possibleElementNames = elementNames.filter(elementName => !notElementNames.has(elementName));\n ctxs.push(...possibleElementNames.map(elementName => registry.securityContext(elementName, propName, isAttribute)));\n });\n return ctxs.length === 0 ? [SecurityContext.NONE] : Array.from(new Set(ctxs)).sort();\n}\n/**\n * Compute a new ParseSourceSpan based off an original `sourceSpan` by using\n * absolute offsets from the specified `absoluteSpan`.\n *\n * @param sourceSpan original source span\n * @param absoluteSpan absolute source span to move to\n */\nfunction moveParseSourceSpan(sourceSpan, absoluteSpan) {\n // The difference of two absolute offsets provide the relative offset\n const startDiff = absoluteSpan.start - sourceSpan.start.offset;\n const endDiff = absoluteSpan.end - sourceSpan.end.offset;\n return new ParseSourceSpan(sourceSpan.start.moveBy(startDiff), sourceSpan.end.moveBy(endDiff), sourceSpan.fullStart.moveBy(startDiff), sourceSpan.details);\n}\n\n// Some of the code comes from WebComponents.JS\n// https://github.com/webcomponents/webcomponentsjs/blob/master/src/HTMLImports/path.js\nfunction isStyleUrlResolvable(url) {\n if (url == null || url.length === 0 || url[0] == '/') return false;\n const schemeMatch = url.match(URL_WITH_SCHEMA_REGEXP);\n return schemeMatch === null || schemeMatch[1] == 'package' || schemeMatch[1] == 'asset';\n}\nconst URL_WITH_SCHEMA_REGEXP = /^([^:/?#]+):/;\nconst NG_CONTENT_SELECT_ATTR$1 = 'select';\nconst LINK_ELEMENT = 'link';\nconst LINK_STYLE_REL_ATTR = 'rel';\nconst LINK_STYLE_HREF_ATTR = 'href';\nconst LINK_STYLE_REL_VALUE = 'stylesheet';\nconst STYLE_ELEMENT = 'style';\nconst SCRIPT_ELEMENT = 'script';\nconst NG_NON_BINDABLE_ATTR = 'ngNonBindable';\nconst NG_PROJECT_AS = 'ngProjectAs';\nfunction preparseElement(ast) {\n let selectAttr = null;\n let hrefAttr = null;\n let relAttr = null;\n let nonBindable = false;\n let projectAs = '';\n ast.attrs.forEach(attr => {\n const lcAttrName = attr.name.toLowerCase();\n if (lcAttrName == NG_CONTENT_SELECT_ATTR$1) {\n selectAttr = attr.value;\n } else if (lcAttrName == LINK_STYLE_HREF_ATTR) {\n hrefAttr = attr.value;\n } else if (lcAttrName == LINK_STYLE_REL_ATTR) {\n relAttr = attr.value;\n } else if (attr.name == NG_NON_BINDABLE_ATTR) {\n nonBindable = true;\n } else if (attr.name == NG_PROJECT_AS) {\n if (attr.value.length > 0) {\n projectAs = attr.value;\n }\n }\n });\n selectAttr = normalizeNgContentSelect(selectAttr);\n const nodeName = ast.name.toLowerCase();\n let type = PreparsedElementType.OTHER;\n if (isNgContent(nodeName)) {\n type = PreparsedElementType.NG_CONTENT;\n } else if (nodeName == STYLE_ELEMENT) {\n type = PreparsedElementType.STYLE;\n } else if (nodeName == SCRIPT_ELEMENT) {\n type = PreparsedElementType.SCRIPT;\n } else if (nodeName == LINK_ELEMENT && relAttr == LINK_STYLE_REL_VALUE) {\n type = PreparsedElementType.STYLESHEET;\n }\n return new PreparsedElement(type, selectAttr, hrefAttr, nonBindable, projectAs);\n}\nvar PreparsedElementType;\n(function (PreparsedElementType) {\n PreparsedElementType[PreparsedElementType[\"NG_CONTENT\"] = 0] = \"NG_CONTENT\";\n PreparsedElementType[PreparsedElementType[\"STYLE\"] = 1] = \"STYLE\";\n PreparsedElementType[PreparsedElementType[\"STYLESHEET\"] = 2] = \"STYLESHEET\";\n PreparsedElementType[PreparsedElementType[\"SCRIPT\"] = 3] = \"SCRIPT\";\n PreparsedElementType[PreparsedElementType[\"OTHER\"] = 4] = \"OTHER\";\n})(PreparsedElementType || (PreparsedElementType = {}));\nclass PreparsedElement {\n constructor(type, selectAttr, hrefAttr, nonBindable, projectAs) {\n this.type = type;\n this.selectAttr = selectAttr;\n this.hrefAttr = hrefAttr;\n this.nonBindable = nonBindable;\n this.projectAs = projectAs;\n }\n}\nfunction normalizeNgContentSelect(selectAttr) {\n if (selectAttr === null || selectAttr.length === 0) {\n return '*';\n }\n return selectAttr;\n}\n\n/** Pattern for a timing value in a trigger. */\nconst TIME_PATTERN = /^\\d+(ms|s)?$/;\n/** Pattern for a separator between keywords in a trigger expression. */\nconst SEPARATOR_PATTERN = /^\\s$/;\n/** Pairs of characters that form syntax that is comma-delimited. */\nconst COMMA_DELIMITED_SYNTAX = new Map([[$LBRACE, $RBRACE], [$LBRACKET, $RBRACKET], [$LPAREN, $RPAREN] // Function calls\n]);\n/** Possible types of `on` triggers. */\nvar OnTriggerType;\n(function (OnTriggerType) {\n OnTriggerType[\"IDLE\"] = \"idle\";\n OnTriggerType[\"TIMER\"] = \"timer\";\n OnTriggerType[\"INTERACTION\"] = \"interaction\";\n OnTriggerType[\"IMMEDIATE\"] = \"immediate\";\n OnTriggerType[\"HOVER\"] = \"hover\";\n OnTriggerType[\"VIEWPORT\"] = \"viewport\";\n})(OnTriggerType || (OnTriggerType = {}));\n/** Parses a `when` deferred trigger. */\nfunction parseWhenTrigger({\n expression,\n sourceSpan\n}, bindingParser, errors) {\n const whenIndex = expression.indexOf('when');\n // This is here just to be safe, we shouldn't enter this function\n // in the first place if a block doesn't have the \"when\" keyword.\n if (whenIndex === -1) {\n errors.push(new ParseError(sourceSpan, `Could not find \"when\" keyword in expression`));\n return null;\n }\n const start = getTriggerParametersStart(expression, whenIndex + 1);\n const parsed = bindingParser.parseBinding(expression.slice(start), false, sourceSpan, sourceSpan.start.offset + start);\n return new BoundDeferredTrigger(parsed, sourceSpan);\n}\n/** Parses an `on` trigger */\nfunction parseOnTrigger({\n expression,\n sourceSpan\n}, errors) {\n const onIndex = expression.indexOf('on');\n // This is here just to be safe, we shouldn't enter this function\n // in the first place if a block doesn't have the \"on\" keyword.\n if (onIndex === -1) {\n errors.push(new ParseError(sourceSpan, `Could not find \"on\" keyword in expression`));\n return [];\n }\n const start = getTriggerParametersStart(expression, onIndex + 1);\n return new OnTriggerParser(expression, start, sourceSpan, errors).parse();\n}\nclass OnTriggerParser {\n constructor(expression, start, span, errors) {\n this.expression = expression;\n this.start = start;\n this.span = span;\n this.errors = errors;\n this.index = 0;\n this.triggers = [];\n this.tokens = new Lexer().tokenize(expression.slice(start));\n }\n parse() {\n while (this.tokens.length > 0 && this.index < this.tokens.length) {\n const token = this.token();\n if (!token.isIdentifier()) {\n this.unexpectedToken(token);\n break;\n }\n // An identifier immediately followed by a comma or the end of\n // the expression cannot have parameters so we can exit early.\n if (this.isFollowedByOrLast($COMMA)) {\n this.consumeTrigger(token, []);\n this.advance();\n } else if (this.isFollowedByOrLast($LPAREN)) {\n this.advance(); // Advance to the opening paren.\n const prevErrors = this.errors.length;\n const parameters = this.consumeParameters();\n if (this.errors.length !== prevErrors) {\n break;\n }\n this.consumeTrigger(token, parameters);\n this.advance(); // Advance past the closing paren.\n } else if (this.index < this.tokens.length - 1) {\n this.unexpectedToken(this.tokens[this.index + 1]);\n }\n this.advance();\n }\n return this.triggers;\n }\n advance() {\n this.index++;\n }\n isFollowedByOrLast(char) {\n if (this.index === this.tokens.length - 1) {\n return true;\n }\n return this.tokens[this.index + 1].isCharacter(char);\n }\n token() {\n return this.tokens[Math.min(this.index, this.tokens.length - 1)];\n }\n consumeTrigger(identifier, parameters) {\n const startSpan = this.span.start.moveBy(this.start + identifier.index - this.tokens[0].index);\n const endSpan = startSpan.moveBy(this.token().end - identifier.index);\n const sourceSpan = new ParseSourceSpan(startSpan, endSpan);\n try {\n switch (identifier.toString()) {\n case OnTriggerType.IDLE:\n this.triggers.push(createIdleTrigger(parameters, sourceSpan));\n break;\n case OnTriggerType.TIMER:\n this.triggers.push(createTimerTrigger(parameters, sourceSpan));\n break;\n case OnTriggerType.INTERACTION:\n this.triggers.push(createInteractionTrigger(parameters, sourceSpan));\n break;\n case OnTriggerType.IMMEDIATE:\n this.triggers.push(createImmediateTrigger(parameters, sourceSpan));\n break;\n case OnTriggerType.HOVER:\n this.triggers.push(createHoverTrigger(parameters, sourceSpan));\n break;\n case OnTriggerType.VIEWPORT:\n this.triggers.push(createViewportTrigger(parameters, sourceSpan));\n break;\n default:\n throw new Error(`Unrecognized trigger type \"${identifier}\"`);\n }\n } catch (e) {\n this.error(identifier, e.message);\n }\n }\n consumeParameters() {\n const parameters = [];\n if (!this.token().isCharacter($LPAREN)) {\n this.unexpectedToken(this.token());\n return parameters;\n }\n this.advance();\n const commaDelimStack = [];\n let current = '';\n while (this.index < this.tokens.length) {\n const token = this.token();\n // Stop parsing if we've hit the end character and we're outside of a comma-delimited syntax.\n // Note that we don't need to account for strings here since the lexer already parsed them\n // into string tokens.\n if (token.isCharacter($RPAREN) && commaDelimStack.length === 0) {\n if (current.length) {\n parameters.push(current);\n }\n break;\n }\n // In the `on` microsyntax \"top-level\" commas (e.g. ones outside of an parameters) separate\n // the different triggers (e.g. `on idle,timer(500)`). This is problematic, because the\n // function-like syntax also implies that multiple parameters can be passed into the\n // individual trigger (e.g. `on foo(a, b)`). To avoid tripping up the parser with commas that\n // are part of other sorts of syntax (object literals, arrays), we treat anything inside\n // a comma-delimited syntax block as plain text.\n if (token.type === TokenType.Character && COMMA_DELIMITED_SYNTAX.has(token.numValue)) {\n commaDelimStack.push(COMMA_DELIMITED_SYNTAX.get(token.numValue));\n }\n if (commaDelimStack.length > 0 && token.isCharacter(commaDelimStack[commaDelimStack.length - 1])) {\n commaDelimStack.pop();\n }\n // If we hit a comma outside of a comma-delimited syntax, it means\n // that we're at the top level and we're starting a new parameter.\n if (commaDelimStack.length === 0 && token.isCharacter($COMMA) && current.length > 0) {\n parameters.push(current);\n current = '';\n this.advance();\n continue;\n }\n // Otherwise treat the token as a plain text character in the current parameter.\n current += this.tokenText();\n this.advance();\n }\n if (!this.token().isCharacter($RPAREN) || commaDelimStack.length > 0) {\n this.error(this.token(), 'Unexpected end of expression');\n }\n if (this.index < this.tokens.length - 1 && !this.tokens[this.index + 1].isCharacter($COMMA)) {\n this.unexpectedToken(this.tokens[this.index + 1]);\n }\n return parameters;\n }\n tokenText() {\n // Tokens have a toString already which we could use, but for string tokens it omits the quotes.\n // Eventually we could expose this information on the token directly.\n return this.expression.slice(this.start + this.token().index, this.start + this.token().end);\n }\n error(token, message) {\n const newStart = this.span.start.moveBy(this.start + token.index);\n const newEnd = newStart.moveBy(token.end - token.index);\n this.errors.push(new ParseError(new ParseSourceSpan(newStart, newEnd), message));\n }\n unexpectedToken(token) {\n this.error(token, `Unexpected token \"${token}\"`);\n }\n}\nfunction createIdleTrigger(parameters, sourceSpan) {\n if (parameters.length > 0) {\n throw new Error(`\"${OnTriggerType.IDLE}\" trigger cannot have parameters`);\n }\n return new IdleDeferredTrigger(sourceSpan);\n}\nfunction createTimerTrigger(parameters, sourceSpan) {\n if (parameters.length !== 1) {\n throw new Error(`\"${OnTriggerType.TIMER}\" trigger must have exactly one parameter`);\n }\n const delay = parseDeferredTime(parameters[0]);\n if (delay === null) {\n throw new Error(`Could not parse time value of trigger \"${OnTriggerType.TIMER}\"`);\n }\n return new TimerDeferredTrigger(delay, sourceSpan);\n}\nfunction createInteractionTrigger(parameters, sourceSpan) {\n if (parameters.length > 1) {\n throw new Error(`\"${OnTriggerType.INTERACTION}\" trigger can only have zero or one parameters`);\n }\n return new InteractionDeferredTrigger(parameters[0] ?? null, sourceSpan);\n}\nfunction createImmediateTrigger(parameters, sourceSpan) {\n if (parameters.length > 0) {\n throw new Error(`\"${OnTriggerType.IMMEDIATE}\" trigger cannot have parameters`);\n }\n return new ImmediateDeferredTrigger(sourceSpan);\n}\nfunction createHoverTrigger(parameters, sourceSpan) {\n if (parameters.length > 0) {\n throw new Error(`\"${OnTriggerType.HOVER}\" trigger cannot have parameters`);\n }\n return new HoverDeferredTrigger(sourceSpan);\n}\nfunction createViewportTrigger(parameters, sourceSpan) {\n // TODO: the RFC has some more potential parameters for `viewport`.\n if (parameters.length > 1) {\n throw new Error(`\"${OnTriggerType.VIEWPORT}\" trigger can only have zero or one parameters`);\n }\n return new ViewportDeferredTrigger(parameters[0] ?? null, sourceSpan);\n}\n/** Gets the index within an expression at which the trigger parameters start. */\nfunction getTriggerParametersStart(value, startPosition = 0) {\n let hasFoundSeparator = false;\n for (let i = startPosition; i < value.length; i++) {\n if (SEPARATOR_PATTERN.test(value[i])) {\n hasFoundSeparator = true;\n } else if (hasFoundSeparator) {\n return i;\n }\n }\n return -1;\n}\n/**\n * Parses a time expression from a deferred trigger to\n * milliseconds. Returns null if it cannot be parsed.\n */\nfunction parseDeferredTime(value) {\n const match = value.match(TIME_PATTERN);\n if (!match) {\n return null;\n }\n const [time, units] = match;\n return parseInt(time) * (units === 's' ? 1000 : 1);\n}\n\n/** Pattern to identify a `prefetch when` trigger. */\nconst PREFETCH_WHEN_PATTERN = /^prefetch\\s+when\\s/;\n/** Pattern to identify a `prefetch on` trigger. */\nconst PREFETCH_ON_PATTERN = /^prefetch\\s+on\\s/;\n/** Pattern to identify a `minimum` parameter in a block. */\nconst MINIMUM_PARAMETER_PATTERN = /^minimum\\s/;\n/** Pattern to identify a `after` parameter in a block. */\nconst AFTER_PARAMETER_PATTERN = /^after\\s/;\n/** Pattern to identify a `when` parameter in a block. */\nconst WHEN_PARAMETER_PATTERN = /^when\\s/;\n/** Pattern to identify a `on` parameter in a block. */\nconst ON_PARAMETER_PATTERN = /^on\\s/;\n/** Possible types of secondary deferred blocks. */\nvar SecondaryDeferredBlockType;\n(function (SecondaryDeferredBlockType) {\n SecondaryDeferredBlockType[\"PLACEHOLDER\"] = \"placeholder\";\n SecondaryDeferredBlockType[\"LOADING\"] = \"loading\";\n SecondaryDeferredBlockType[\"ERROR\"] = \"error\";\n})(SecondaryDeferredBlockType || (SecondaryDeferredBlockType = {}));\n/** Creates a deferred block from an HTML AST node. */\nfunction createDeferredBlock(ast, visitor, bindingParser) {\n const errors = [];\n const [primaryBlock, ...secondaryBlocks] = ast.blocks;\n const {\n triggers,\n prefetchTriggers\n } = parsePrimaryTriggers(primaryBlock.parameters, bindingParser, errors);\n const {\n placeholder,\n loading,\n error\n } = parseSecondaryBlocks(secondaryBlocks, errors, visitor);\n return {\n node: new DeferredBlock(visitAll(visitor, primaryBlock.children), triggers, prefetchTriggers, placeholder, loading, error, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan),\n errors\n };\n}\nfunction parseSecondaryBlocks(blocks, errors, visitor) {\n let placeholder = null;\n let loading = null;\n let error = null;\n for (const block of blocks) {\n try {\n switch (block.name) {\n case SecondaryDeferredBlockType.PLACEHOLDER:\n if (placeholder !== null) {\n errors.push(new ParseError(block.startSourceSpan, `\"defer\" block can only have one \"${SecondaryDeferredBlockType.PLACEHOLDER}\" block`));\n } else {\n placeholder = parsePlaceholderBlock(block, visitor);\n }\n break;\n case SecondaryDeferredBlockType.LOADING:\n if (loading !== null) {\n errors.push(new ParseError(block.startSourceSpan, `\"defer\" block can only have one \"${SecondaryDeferredBlockType.LOADING}\" block`));\n } else {\n loading = parseLoadingBlock(block, visitor);\n }\n break;\n case SecondaryDeferredBlockType.ERROR:\n if (error !== null) {\n errors.push(new ParseError(block.startSourceSpan, `\"defer\" block can only have one \"${SecondaryDeferredBlockType.ERROR}\" block`));\n } else {\n error = parseErrorBlock(block, visitor);\n }\n break;\n default:\n errors.push(new ParseError(block.startSourceSpan, `Unrecognized block \"${block.name}\"`));\n break;\n }\n } catch (e) {\n errors.push(new ParseError(block.startSourceSpan, e.message));\n }\n }\n return {\n placeholder,\n loading,\n error\n };\n}\nfunction parsePlaceholderBlock(ast, visitor) {\n let minimumTime = null;\n for (const param of ast.parameters) {\n if (MINIMUM_PARAMETER_PATTERN.test(param.expression)) {\n const parsedTime = parseDeferredTime(param.expression.slice(getTriggerParametersStart(param.expression)));\n if (parsedTime === null) {\n throw new Error(`Could not parse time value of parameter \"minimum\"`);\n }\n minimumTime = parsedTime;\n } else {\n throw new Error(`Unrecognized parameter in \"${SecondaryDeferredBlockType.PLACEHOLDER}\" block: \"${param.expression}\"`);\n }\n }\n return new DeferredBlockPlaceholder(visitAll(visitor, ast.children), minimumTime, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);\n}\nfunction parseLoadingBlock(ast, visitor) {\n let afterTime = null;\n let minimumTime = null;\n for (const param of ast.parameters) {\n if (AFTER_PARAMETER_PATTERN.test(param.expression)) {\n const parsedTime = parseDeferredTime(param.expression.slice(getTriggerParametersStart(param.expression)));\n if (parsedTime === null) {\n throw new Error(`Could not parse time value of parameter \"after\"`);\n }\n afterTime = parsedTime;\n } else if (MINIMUM_PARAMETER_PATTERN.test(param.expression)) {\n const parsedTime = parseDeferredTime(param.expression.slice(getTriggerParametersStart(param.expression)));\n if (parsedTime === null) {\n throw new Error(`Could not parse time value of parameter \"minimum\"`);\n }\n minimumTime = parsedTime;\n } else {\n throw new Error(`Unrecognized parameter in \"${SecondaryDeferredBlockType.LOADING}\" block: \"${param.expression}\"`);\n }\n }\n return new DeferredBlockLoading(visitAll(visitor, ast.children), afterTime, minimumTime, ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);\n}\nfunction parseErrorBlock(ast, visitor) {\n if (ast.parameters.length > 0) {\n throw new Error(`\"${SecondaryDeferredBlockType.ERROR}\" block cannot have parameters`);\n }\n return new DeferredBlockError(visitAll(visitor, ast.children), ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);\n}\nfunction parsePrimaryTriggers(params, bindingParser, errors) {\n const triggers = [];\n const prefetchTriggers = [];\n for (const param of params) {\n // The lexer ignores the leading spaces so we can assume\n // that the expression starts with a keyword.\n if (WHEN_PARAMETER_PATTERN.test(param.expression)) {\n const result = parseWhenTrigger(param, bindingParser, errors);\n result !== null && triggers.push(result);\n } else if (ON_PARAMETER_PATTERN.test(param.expression)) {\n triggers.push(...parseOnTrigger(param, errors));\n } else if (PREFETCH_WHEN_PATTERN.test(param.expression)) {\n const result = parseWhenTrigger(param, bindingParser, errors);\n result !== null && prefetchTriggers.push(result);\n } else if (PREFETCH_ON_PATTERN.test(param.expression)) {\n prefetchTriggers.push(...parseOnTrigger(param, errors));\n } else {\n errors.push(new ParseError(param.sourceSpan, 'Unrecognized trigger'));\n }\n }\n return {\n triggers,\n prefetchTriggers\n };\n}\nconst BIND_NAME_REGEXP = /^(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.*)$/;\n// Group 1 = \"bind-\"\nconst KW_BIND_IDX = 1;\n// Group 2 = \"let-\"\nconst KW_LET_IDX = 2;\n// Group 3 = \"ref-/#\"\nconst KW_REF_IDX = 3;\n// Group 4 = \"on-\"\nconst KW_ON_IDX = 4;\n// Group 5 = \"bindon-\"\nconst KW_BINDON_IDX = 5;\n// Group 6 = \"@\"\nconst KW_AT_IDX = 6;\n// Group 7 = the identifier after \"bind-\", \"let-\", \"ref-/#\", \"on-\", \"bindon-\" or \"@\"\nconst IDENT_KW_IDX = 7;\nconst BINDING_DELIMS = {\n BANANA_BOX: {\n start: '[(',\n end: ')]'\n },\n PROPERTY: {\n start: '[',\n end: ']'\n },\n EVENT: {\n start: '(',\n end: ')'\n }\n};\nconst TEMPLATE_ATTR_PREFIX = '*';\nfunction htmlAstToRender3Ast(htmlNodes, bindingParser, options) {\n const transformer = new HtmlAstToIvyAst(bindingParser, options);\n const ivyNodes = visitAll(transformer, htmlNodes);\n // Errors might originate in either the binding parser or the html to ivy transformer\n const allErrors = bindingParser.errors.concat(transformer.errors);\n const result = {\n nodes: ivyNodes,\n errors: allErrors,\n styleUrls: transformer.styleUrls,\n styles: transformer.styles,\n ngContentSelectors: transformer.ngContentSelectors\n };\n if (options.collectCommentNodes) {\n result.commentNodes = transformer.commentNodes;\n }\n return result;\n}\nclass HtmlAstToIvyAst {\n constructor(bindingParser, options) {\n this.bindingParser = bindingParser;\n this.options = options;\n this.errors = [];\n this.styles = [];\n this.styleUrls = [];\n this.ngContentSelectors = [];\n // This array will be populated if `Render3ParseOptions['collectCommentNodes']` is true\n this.commentNodes = [];\n this.inI18nBlock = false;\n }\n // HTML visitor\n visitElement(element) {\n const isI18nRootElement = isI18nRootNode(element.i18n);\n if (isI18nRootElement) {\n if (this.inI18nBlock) {\n this.reportError('Cannot mark an element as translatable inside of a translatable section. Please remove the nested i18n marker.', element.sourceSpan);\n }\n this.inI18nBlock = true;\n }\n const preparsedElement = preparseElement(element);\n if (preparsedElement.type === PreparsedElementType.SCRIPT) {\n return null;\n } else if (preparsedElement.type === PreparsedElementType.STYLE) {\n const contents = textContents(element);\n if (contents !== null) {\n this.styles.push(contents);\n }\n return null;\n } else if (preparsedElement.type === PreparsedElementType.STYLESHEET && isStyleUrlResolvable(preparsedElement.hrefAttr)) {\n this.styleUrls.push(preparsedElement.hrefAttr);\n return null;\n }\n // Whether the element is a ``\n const isTemplateElement = isNgTemplate(element.name);\n const parsedProperties = [];\n const boundEvents = [];\n const variables = [];\n const references = [];\n const attributes = [];\n const i18nAttrsMeta = {};\n const templateParsedProperties = [];\n const templateVariables = [];\n // Whether the element has any *-attribute\n let elementHasInlineTemplate = false;\n for (const attribute of element.attrs) {\n let hasBinding = false;\n const normalizedName = normalizeAttributeName(attribute.name);\n // `*attr` defines template bindings\n let isTemplateBinding = false;\n if (attribute.i18n) {\n i18nAttrsMeta[attribute.name] = attribute.i18n;\n }\n if (normalizedName.startsWith(TEMPLATE_ATTR_PREFIX)) {\n // *-attributes\n if (elementHasInlineTemplate) {\n this.reportError(`Can't have multiple template bindings on one element. Use only one attribute prefixed with *`, attribute.sourceSpan);\n }\n isTemplateBinding = true;\n elementHasInlineTemplate = true;\n const templateValue = attribute.value;\n const templateKey = normalizedName.substring(TEMPLATE_ATTR_PREFIX.length);\n const parsedVariables = [];\n const absoluteValueOffset = attribute.valueSpan ? attribute.valueSpan.start.offset :\n // If there is no value span the attribute does not have a value, like `attr` in\n //``. In this case, point to one character beyond the last character of\n // the attribute name.\n attribute.sourceSpan.start.offset + attribute.name.length;\n this.bindingParser.parseInlineTemplateBinding(templateKey, templateValue, attribute.sourceSpan, absoluteValueOffset, [], templateParsedProperties, parsedVariables, true /* isIvyAst */);\n templateVariables.push(...parsedVariables.map(v => new Variable(v.name, v.value, v.sourceSpan, v.keySpan, v.valueSpan)));\n } else {\n // Check for variables, events, property bindings, interpolation\n hasBinding = this.parseAttribute(isTemplateElement, attribute, [], parsedProperties, boundEvents, variables, references);\n }\n if (!hasBinding && !isTemplateBinding) {\n // don't include the bindings as attributes as well in the AST\n attributes.push(this.visitAttribute(attribute));\n }\n }\n let children;\n if (preparsedElement.nonBindable) {\n // The `NonBindableVisitor` may need to return an array of nodes for block groups so we need\n // to flatten the array here. Avoid doing this for the `HtmlAstToIvyAst` since `flat` creates\n // a new array.\n children = visitAll(NON_BINDABLE_VISITOR, element.children).flat(Infinity);\n } else {\n children = visitAll(this, element.children);\n }\n let parsedElement;\n if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {\n // ``\n if (element.children && !element.children.every(node => isEmptyTextNode(node) || isCommentNode(node))) {\n this.reportError(` element cannot have content.`, element.sourceSpan);\n }\n const selector = preparsedElement.selectAttr;\n const attrs = element.attrs.map(attr => this.visitAttribute(attr));\n parsedElement = new Content(selector, attrs, element.sourceSpan, element.i18n);\n this.ngContentSelectors.push(selector);\n } else if (isTemplateElement) {\n // ``\n const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta);\n parsedElement = new Template(element.name, attributes, attrs.bound, boundEvents, [/* no template attributes */], children, references, variables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);\n } else {\n const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta);\n parsedElement = new Element$1(element.name, attributes, attrs.bound, boundEvents, children, references, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);\n }\n if (elementHasInlineTemplate) {\n // If this node is an inline-template (e.g. has *ngFor) then we need to create a template\n // node that contains this node.\n // Moreover, if the node is an element, then we need to hoist its attributes to the template\n // node for matching against content projection selectors.\n const attrs = this.extractAttributes('ng-template', templateParsedProperties, i18nAttrsMeta);\n const templateAttrs = [];\n attrs.literal.forEach(attr => templateAttrs.push(attr));\n attrs.bound.forEach(attr => templateAttrs.push(attr));\n const hoistedAttrs = parsedElement instanceof Element$1 ? {\n attributes: parsedElement.attributes,\n inputs: parsedElement.inputs,\n outputs: parsedElement.outputs\n } : {\n attributes: [],\n inputs: [],\n outputs: []\n };\n // For s with structural directives on them, avoid passing i18n information to\n // the wrapping template to prevent unnecessary i18n instructions from being generated. The\n // necessary i18n meta information will be extracted from child elements.\n const i18n = isTemplateElement && isI18nRootElement ? undefined : element.i18n;\n const name = parsedElement instanceof Template ? null : parsedElement.name;\n parsedElement = new Template(name, hoistedAttrs.attributes, hoistedAttrs.inputs, hoistedAttrs.outputs, templateAttrs, [parsedElement], [/* no references */], templateVariables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, i18n);\n }\n if (isI18nRootElement) {\n this.inI18nBlock = false;\n }\n return parsedElement;\n }\n visitAttribute(attribute) {\n return new TextAttribute(attribute.name, attribute.value, attribute.sourceSpan, attribute.keySpan, attribute.valueSpan, attribute.i18n);\n }\n visitText(text) {\n return this._visitTextWithInterpolation(text.value, text.sourceSpan, text.tokens, text.i18n);\n }\n visitExpansion(expansion) {\n if (!expansion.i18n) {\n // do not generate Icu in case it was created\n // outside of i18n block in a template\n return null;\n }\n if (!isI18nRootNode(expansion.i18n)) {\n throw new Error(`Invalid type \"${expansion.i18n.constructor}\" for \"i18n\" property of ${expansion.sourceSpan.toString()}. Expected a \"Message\"`);\n }\n const message = expansion.i18n;\n const vars = {};\n const placeholders = {};\n // extract VARs from ICUs - we process them separately while\n // assembling resulting message via goog.getMsg function, since\n // we need to pass them to top-level goog.getMsg call\n Object.keys(message.placeholders).forEach(key => {\n const value = message.placeholders[key];\n if (key.startsWith(I18N_ICU_VAR_PREFIX)) {\n // Currently when the `plural` or `select` keywords in an ICU contain trailing spaces (e.g.\n // `{count, select , ...}`), these spaces are also included into the key names in ICU vars\n // (e.g. \"VAR_SELECT \"). These trailing spaces are not desirable, since they will later be\n // converted into `_` symbols while normalizing placeholder names, which might lead to\n // mismatches at runtime (i.e. placeholder will not be replaced with the correct value).\n const formattedKey = key.trim();\n const ast = this.bindingParser.parseInterpolationExpression(value.text, value.sourceSpan);\n vars[formattedKey] = new BoundText(ast, value.sourceSpan);\n } else {\n placeholders[key] = this._visitTextWithInterpolation(value.text, value.sourceSpan, null);\n }\n });\n return new Icu$1(vars, placeholders, expansion.sourceSpan, message);\n }\n visitExpansionCase(expansionCase) {\n return null;\n }\n visitComment(comment) {\n if (this.options.collectCommentNodes) {\n this.commentNodes.push(new Comment$1(comment.value || '', comment.sourceSpan));\n }\n return null;\n }\n visitBlockGroup(group, context) {\n const primaryBlock = group.blocks[0];\n // The HTML parser ensures that we don't hit this case, but we have an assertion just in case.\n if (!primaryBlock) {\n this.reportError('Block group must have at least one block.', group.sourceSpan);\n return null;\n }\n if (primaryBlock.name === 'defer' && this.options.enabledBlockTypes.has(primaryBlock.name)) {\n const {\n node,\n errors\n } = createDeferredBlock(group, this, this.bindingParser);\n this.errors.push(...errors);\n return node;\n }\n this.reportError(`Unrecognized block \"${primaryBlock.name}\".`, primaryBlock.sourceSpan);\n return null;\n }\n visitBlock(block, context) {}\n visitBlockParameter(parameter, context) {}\n // convert view engine `ParsedProperty` to a format suitable for IVY\n extractAttributes(elementName, properties, i18nPropsMeta) {\n const bound = [];\n const literal = [];\n properties.forEach(prop => {\n const i18n = i18nPropsMeta[prop.name];\n if (prop.isLiteral) {\n literal.push(new TextAttribute(prop.name, prop.expression.source || '', prop.sourceSpan, prop.keySpan, prop.valueSpan, i18n));\n } else {\n // Note that validation is skipped and property mapping is disabled\n // due to the fact that we need to make sure a given prop is not an\n // input of a directive and directive matching happens at runtime.\n const bep = this.bindingParser.createBoundElementProperty(elementName, prop, /* skipValidation */true, /* mapPropertyName */false);\n bound.push(BoundAttribute.fromBoundElementProperty(bep, i18n));\n }\n });\n return {\n bound,\n literal\n };\n }\n parseAttribute(isTemplateElement, attribute, matchableAttributes, parsedProperties, boundEvents, variables, references) {\n const name = normalizeAttributeName(attribute.name);\n const value = attribute.value;\n const srcSpan = attribute.sourceSpan;\n const absoluteOffset = attribute.valueSpan ? attribute.valueSpan.start.offset : srcSpan.start.offset;\n function createKeySpan(srcSpan, prefix, identifier) {\n // We need to adjust the start location for the keySpan to account for the removed 'data-'\n // prefix from `normalizeAttributeName`.\n const normalizationAdjustment = attribute.name.length - name.length;\n const keySpanStart = srcSpan.start.moveBy(prefix.length + normalizationAdjustment);\n const keySpanEnd = keySpanStart.moveBy(identifier.length);\n return new ParseSourceSpan(keySpanStart, keySpanEnd, keySpanStart, identifier);\n }\n const bindParts = name.match(BIND_NAME_REGEXP);\n if (bindParts) {\n if (bindParts[KW_BIND_IDX] != null) {\n const identifier = bindParts[IDENT_KW_IDX];\n const keySpan = createKeySpan(srcSpan, bindParts[KW_BIND_IDX], identifier);\n this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);\n } else if (bindParts[KW_LET_IDX]) {\n if (isTemplateElement) {\n const identifier = bindParts[IDENT_KW_IDX];\n const keySpan = createKeySpan(srcSpan, bindParts[KW_LET_IDX], identifier);\n this.parseVariable(identifier, value, srcSpan, keySpan, attribute.valueSpan, variables);\n } else {\n this.reportError(`\"let-\" is only supported on ng-template elements.`, srcSpan);\n }\n } else if (bindParts[KW_REF_IDX]) {\n const identifier = bindParts[IDENT_KW_IDX];\n const keySpan = createKeySpan(srcSpan, bindParts[KW_REF_IDX], identifier);\n this.parseReference(identifier, value, srcSpan, keySpan, attribute.valueSpan, references);\n } else if (bindParts[KW_ON_IDX]) {\n const events = [];\n const identifier = bindParts[IDENT_KW_IDX];\n const keySpan = createKeySpan(srcSpan, bindParts[KW_ON_IDX], identifier);\n this.bindingParser.parseEvent(identifier, value, /* isAssignmentEvent */false, srcSpan, attribute.valueSpan || srcSpan, matchableAttributes, events, keySpan);\n addEvents(events, boundEvents);\n } else if (bindParts[KW_BINDON_IDX]) {\n const identifier = bindParts[IDENT_KW_IDX];\n const keySpan = createKeySpan(srcSpan, bindParts[KW_BINDON_IDX], identifier);\n this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);\n this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);\n } else if (bindParts[KW_AT_IDX]) {\n const keySpan = createKeySpan(srcSpan, '', name);\n this.bindingParser.parseLiteralAttr(name, value, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);\n }\n return true;\n }\n // We didn't see a kw-prefixed property binding, but we have not yet checked\n // for the []/()/[()] syntax.\n let delims = null;\n if (name.startsWith(BINDING_DELIMS.BANANA_BOX.start)) {\n delims = BINDING_DELIMS.BANANA_BOX;\n } else if (name.startsWith(BINDING_DELIMS.PROPERTY.start)) {\n delims = BINDING_DELIMS.PROPERTY;\n } else if (name.startsWith(BINDING_DELIMS.EVENT.start)) {\n delims = BINDING_DELIMS.EVENT;\n }\n if (delims !== null &&\n // NOTE: older versions of the parser would match a start/end delimited\n // binding iff the property name was terminated by the ending delimiter\n // and the identifier in the binding was non-empty.\n // TODO(ayazhafiz): update this to handle malformed bindings.\n name.endsWith(delims.end) && name.length > delims.start.length + delims.end.length) {\n const identifier = name.substring(delims.start.length, name.length - delims.end.length);\n const keySpan = createKeySpan(srcSpan, delims.start, identifier);\n if (delims.start === BINDING_DELIMS.BANANA_BOX.start) {\n this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);\n this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);\n } else if (delims.start === BINDING_DELIMS.PROPERTY.start) {\n this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);\n } else {\n const events = [];\n this.bindingParser.parseEvent(identifier, value, /* isAssignmentEvent */false, srcSpan, attribute.valueSpan || srcSpan, matchableAttributes, events, keySpan);\n addEvents(events, boundEvents);\n }\n return true;\n }\n // No explicit binding found.\n const keySpan = createKeySpan(srcSpan, '' /* prefix */, name);\n const hasBinding = this.bindingParser.parsePropertyInterpolation(name, value, srcSpan, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan, attribute.valueTokens ?? null);\n return hasBinding;\n }\n _visitTextWithInterpolation(value, sourceSpan, interpolatedTokens, i18n) {\n const valueNoNgsp = replaceNgsp(value);\n const expr = this.bindingParser.parseInterpolation(valueNoNgsp, sourceSpan, interpolatedTokens);\n return expr ? new BoundText(expr, sourceSpan, i18n) : new Text$3(valueNoNgsp, sourceSpan);\n }\n parseVariable(identifier, value, sourceSpan, keySpan, valueSpan, variables) {\n if (identifier.indexOf('-') > -1) {\n this.reportError(`\"-\" is not allowed in variable names`, sourceSpan);\n } else if (identifier.length === 0) {\n this.reportError(`Variable does not have a name`, sourceSpan);\n }\n variables.push(new Variable(identifier, value, sourceSpan, keySpan, valueSpan));\n }\n parseReference(identifier, value, sourceSpan, keySpan, valueSpan, references) {\n if (identifier.indexOf('-') > -1) {\n this.reportError(`\"-\" is not allowed in reference names`, sourceSpan);\n } else if (identifier.length === 0) {\n this.reportError(`Reference does not have a name`, sourceSpan);\n } else if (references.some(reference => reference.name === identifier)) {\n this.reportError(`Reference \"#${identifier}\" is defined more than once`, sourceSpan);\n }\n references.push(new Reference(identifier, value, sourceSpan, keySpan, valueSpan));\n }\n parseAssignmentEvent(name, expression, sourceSpan, valueSpan, targetMatchableAttrs, boundEvents, keySpan) {\n const events = [];\n this.bindingParser.parseEvent(`${name}Change`, `${expression} =$event`, /* isAssignmentEvent */true, sourceSpan, valueSpan || sourceSpan, targetMatchableAttrs, events, keySpan);\n addEvents(events, boundEvents);\n }\n reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {\n this.errors.push(new ParseError(sourceSpan, message, level));\n }\n}\nclass NonBindableVisitor {\n visitElement(ast) {\n const preparsedElement = preparseElement(ast);\n if (preparsedElement.type === PreparsedElementType.SCRIPT || preparsedElement.type === PreparsedElementType.STYLE || preparsedElement.type === PreparsedElementType.STYLESHEET) {\n // Skipping