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