text: /**
   * @license
   * Copyright Google Inc. 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 __extends = (this && this.__extends) || function (d, b) {
      for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
      function __() { this.constructor = d; }
      d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  };
  import { Inject, Injectable, OpaqueToken, Optional, SecurityContext } from '@angular/core';
  import { removeIdentifierDuplicates } from '../compile_metadata';
  import { EmptyExpr, RecursiveAstVisitor } from '../expression_parser/ast';
  import { Parser } from '../expression_parser/parser';
  import { StringMapWrapper } from '../facade/collection';
  import { isBlank, isPresent, isString } from '../facade/lang';
  import { I18NHtmlParser } from '../i18n/i18n_html_parser';
  import { Identifiers, identifierToken, resolveIdentifierToken } from '../identifiers';
  import * as html from '../ml_parser/ast';
  import { ParseTreeResult } from '../ml_parser/html_parser';
  import { expandNodes } from '../ml_parser/icu_ast_expander';
  import { InterpolationConfig } from '../ml_parser/interpolation_config';
  import { mergeNsAndName, splitNsName } from '../ml_parser/tags';
  import { ParseError, ParseErrorLevel, ParseSourceSpan } from '../parse_util';
  import { Console, MAX_INTERPOLATION_VALUES } from '../private_import_core';
  import { ProviderElementContext, ProviderViewContext } from '../provider_analyzer';
  import { ElementSchemaRegistry } from '../schema/element_schema_registry';
  import { CssSelector, SelectorMatcher } from '../selector';
  import { isStyleUrlResolvable } from '../style_url_resolver';
  import { splitAtColon } from '../util';
  import { AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ReferenceAst, TextAst, VariableAst, templateVisitAll } from './template_ast';
  import { PreparsedElementType, preparseElement } from './template_preparser';
  // Group 1 = "bind-"
  // Group 2 = "let-"
  // Group 3 = "ref-/#"
  // Group 4 = "on-"
  // Group 5 = "bindon-"
  // Group 6 = "@"
  // Group 7 = the identifier after "bind-", "let-", "ref-/#", "on-", "bindon-" or "@"
  // Group 8 = identifier inside [()]
  // Group 9 = identifier inside []
  // Group 10 = identifier inside ()
  var BIND_NAME_REGEXP = /^(?:(?:(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/;
  var KW_BIND_IDX = 1;
  var KW_LET_IDX = 2;
  var KW_REF_IDX = 3;
  var KW_ON_IDX = 4;
  var KW_BINDON_IDX = 5;
  var KW_AT_IDX = 6;
  var IDENT_KW_IDX = 7;
  var IDENT_BANANA_BOX_IDX = 8;
  var IDENT_PROPERTY_IDX = 9;
  var IDENT_EVENT_IDX = 10;
  var ANIMATE_PROP_PREFIX = 'animate-';
  var TEMPLATE_ELEMENT = 'template';
  var TEMPLATE_ATTR = 'template';
  var TEMPLATE_ATTR_PREFIX = '*';
  var CLASS_ATTR = 'class';
  var PROPERTY_PARTS_SEPARATOR = '.';
  var ATTRIBUTE_PREFIX = 'attr';
  var CLASS_PREFIX = 'class';
  var STYLE_PREFIX = 'style';
  var TEXT_CSS_SELECTOR = CssSelector.parse('*')[0];
  /**
   * Provides an array of {@link TemplateAstVisitor}s which will be used to transform
   * parsed templates before compilation is invoked, allowing custom expression syntax
   * and other advanced transformations.
   *
   * This is currently an internal-only feature and not meant for general use.
   */
  export var TEMPLATE_TRANSFORMS = new OpaqueToken('TemplateTransforms');
  export var TemplateParseError = (function (_super) {
      __extends(TemplateParseError, _super);
      function TemplateParseError(message, span, level) {
          _super.call(this, span, message, level);
      }
      return TemplateParseError;
  }(ParseError));
  export var TemplateParseResult = (function () {
      function TemplateParseResult(templateAst, errors) {
          this.templateAst = templateAst;
          this.errors = errors;
      }
      return TemplateParseResult;
  }());
  export var TemplateParser = (function () {
      function TemplateParser(_exprParser, _schemaRegistry, _htmlParser, _console, transforms) {
          this._exprParser = _exprParser;
          this._schemaRegistry = _schemaRegistry;
          this._htmlParser = _htmlParser;
          this._console = _console;
          this.transforms = transforms;
      }
      TemplateParser.prototype.parse = function (component, template, directives, pipes, schemas, templateUrl) {
          var result = this.tryParse(component, template, directives, pipes, schemas, templateUrl);
          var warnings = result.errors.filter(function (error) { return error.level === ParseErrorLevel.WARNING; });
          var errors = result.errors.filter(function (error) { return error.level === ParseErrorLevel.FATAL; });
          if (warnings.length > 0) {
              this._console.warn("Template parse warnings:\n" + warnings.join('\n'));
          }
          if (errors.length > 0) {
              var errorString = errors.join('\n');
              throw new Error("Template parse errors:\n" + errorString);
          }
          return result.templateAst;
      };
      TemplateParser.prototype.tryParse = function (component, template, directives, pipes, schemas, templateUrl) {
          var interpolationConfig;
          if (component.template) {
              interpolationConfig = InterpolationConfig.fromArray(component.template.interpolation);
          }
          var htmlAstWithErrors = this._htmlParser.parse(template, templateUrl, true, interpolationConfig);
          var errors = htmlAstWithErrors.errors;
          var result;
          if (errors.length == 0) {
              // Transform ICU messages to angular directives
              var expandedHtmlAst = expandNodes(htmlAstWithErrors.rootNodes);
              errors.push.apply(errors, expandedHtmlAst.errors);
              htmlAstWithErrors = new ParseTreeResult(expandedHtmlAst.nodes, errors);
          }
          if (htmlAstWithErrors.rootNodes.length > 0) {
              var uniqDirectives = removeIdentifierDuplicates(directives);
              var uniqPipes = removeIdentifierDuplicates(pipes);
              var providerViewContext = new ProviderViewContext(component, htmlAstWithErrors.rootNodes[0].sourceSpan);
              var parseVisitor = new TemplateParseVisitor(providerViewContext, uniqDirectives, uniqPipes, schemas, this._exprParser, this._schemaRegistry);
              result = html.visitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT);
              errors.push.apply(errors, parseVisitor.errors.concat(providerViewContext.errors));
          }
          else {
              result = [];
          }
          this._assertNoReferenceDuplicationOnTemplate(result, errors);
          if (errors.length > 0) {
              return new TemplateParseResult(result, errors);
          }
          if (isPresent(this.transforms)) {
              this.transforms.forEach(function (transform) { result = templateVisitAll(transform, result); });
          }
          return new TemplateParseResult(result, errors);
      };
      /** @internal */
      TemplateParser.prototype._assertNoReferenceDuplicationOnTemplate = function (result, errors) {
          var existingReferences = [];
          result.filter(function (element) { return !!element.references; })
              .forEach(function (element) { return element.references.forEach(function (reference) {
              var name = reference.name;
              if (existingReferences.indexOf(name) < 0) {
                  existingReferences.push(name);
              }
              else {
                  var error = new TemplateParseError("Reference \"#" + name + "\" is defined several times", reference.sourceSpan, ParseErrorLevel.FATAL);
                  errors.push(error);
              }
          }); });
      };
      TemplateParser.decorators = [
          { type: Injectable },
      ];
      /** @nocollapse */
      TemplateParser.ctorParameters = [
          { type: Parser, },
          { type: ElementSchemaRegistry, },
          { type: I18NHtmlParser, },
          { type: Console, },
          { type: Array, decorators: [{ type: Optional }, { type: Inject, args: [TEMPLATE_TRANSFORMS,] },] },
      ];
      return TemplateParser;
  }());
  var TemplateParseVisitor = (function () {
      function TemplateParseVisitor(providerViewContext, directives, pipes, _schemas, _exprParser, _schemaRegistry) {
          var _this = this;
          this.providerViewContext = providerViewContext;
          this._schemas = _schemas;
          this._exprParser = _exprParser;
          this._schemaRegistry = _schemaRegistry;
          this.selectorMatcher = new SelectorMatcher();
          this.errors = [];
          this.directivesIndex = new Map();
          this.ngContentCount = 0;
          this.pipesByName = new Map();
          var tempMeta = providerViewContext.component.template;
          if (tempMeta && tempMeta.interpolation) {
              this._interpolationConfig = {
                  start: tempMeta.interpolation[0],
                  end: tempMeta.interpolation[1]
              };
          }
          directives.forEach(function (directive, index) {
              var selector = CssSelector.parse(directive.selector);
              _this.selectorMatcher.addSelectables(selector, directive);
              _this.directivesIndex.set(directive, index);
          });
          pipes.forEach(function (pipe) { return _this.pipesByName.set(pipe.name, pipe); });
      }
      TemplateParseVisitor.prototype._reportError = function (message, sourceSpan, level) {
          if (level === void 0) { level = ParseErrorLevel.FATAL; }
          this.errors.push(new TemplateParseError(message, sourceSpan, level));
      };
      TemplateParseVisitor.prototype._reportParserErrors = function (errors, sourceSpan) {
          for (var _i = 0, errors_1 = errors; _i < errors_1.length; _i++) {
              var error = errors_1[_i];
              this._reportError(error.message, sourceSpan);
          }
      };
      TemplateParseVisitor.prototype._parseInterpolation = function (value, sourceSpan) {
          var sourceInfo = sourceSpan.start.toString();
          try {
              var ast = this._exprParser.parseInterpolation(value, sourceInfo, this._interpolationConfig);
              if (ast)
                  this._reportParserErrors(ast.errors, sourceSpan);
              this._checkPipes(ast, sourceSpan);
              if (isPresent(ast) &&
                  ast.ast.expressions.length > MAX_INTERPOLATION_VALUES) {
                  throw new Error("Only support at most " + MAX_INTERPOLATION_VALUES + " interpolation values!");
              }
              return ast;
          }
          catch (e) {
              this._reportError("" + e, sourceSpan);
              return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
          }
      };
      TemplateParseVisitor.prototype._parseAction = function (value, sourceSpan) {
          var sourceInfo = sourceSpan.start.toString();
          try {
              var ast = this._exprParser.parseAction(value, sourceInfo, this._interpolationConfig);
              if (ast) {
                  this._reportParserErrors(ast.errors, sourceSpan);
              }
              if (!ast || ast.ast instanceof EmptyExpr) {
                  this._reportError("Empty expressions are not allowed", sourceSpan);
                  return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
              }
              this._checkPipes(ast, sourceSpan);
              return ast;
          }
          catch (e) {
              this._reportError("" + e, sourceSpan);
              return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
          }
      };
      TemplateParseVisitor.prototype._parseBinding = function (value, sourceSpan) {
          var sourceInfo = sourceSpan.start.toString();
          try {
              var ast = this._exprParser.parseBinding(value, sourceInfo, this._interpolationConfig);
              if (ast)
                  this._reportParserErrors(ast.errors, sourceSpan);
              this._checkPipes(ast, sourceSpan);
              return ast;
          }
          catch (e) {
              this._reportError("" + e, sourceSpan);
              return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
          }
      };
      TemplateParseVisitor.prototype._parseTemplateBindings = function (value, sourceSpan) {
          var _this = this;
          var sourceInfo = sourceSpan.start.toString();
          try {
              var bindingsResult = this._exprParser.parseTemplateBindings(value, sourceInfo);
              this._reportParserErrors(bindingsResult.errors, sourceSpan);
              bindingsResult.templateBindings.forEach(function (binding) {
                  if (isPresent(binding.expression)) {
                      _this._checkPipes(binding.expression, sourceSpan);
                  }
              });
              bindingsResult.warnings.forEach(function (warning) { _this._reportError(warning, sourceSpan, ParseErrorLevel.WARNING); });
              return bindingsResult.templateBindings;
          }
          catch (e) {
              this._reportError("" + e, sourceSpan);
              return [];
          }
      };
      TemplateParseVisitor.prototype._checkPipes = function (ast, sourceSpan) {
          var _this = this;
          if (isPresent(ast)) {
              var collector = new PipeCollector();
              ast.visit(collector);
              collector.pipes.forEach(function (pipeName) {
                  if (!_this.pipesByName.has(pipeName)) {
                      _this._reportError("The pipe '" + pipeName + "' could not be found", sourceSpan);
                  }
              });
          }
      };
      TemplateParseVisitor.prototype.visitExpansion = function (expansion, context) { return null; };
      TemplateParseVisitor.prototype.visitExpansionCase = function (expansionCase, context) { return null; };
      TemplateParseVisitor.prototype.visitText = function (text, parent) {
          var ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR);
          var expr = this._parseInterpolation(text.value, text.sourceSpan);
          if (isPresent(expr)) {
              return new BoundTextAst(expr, ngContentIndex, text.sourceSpan);
          }
          else {
              return new TextAst(text.value, ngContentIndex, text.sourceSpan);
          }
      };
      TemplateParseVisitor.prototype.visitAttribute = function (attribute, context) {
          return new AttrAst(attribute.name, attribute.value, attribute.sourceSpan);
      };
      TemplateParseVisitor.prototype.visitComment = function (comment, context) { return null; };
      TemplateParseVisitor.prototype.visitElement = function (element, parent) {
          var _this = this;
          var nodeName = element.name;
          var preparsedElement = preparseElement(element);
          if (preparsedElement.type === PreparsedElementType.SCRIPT ||
              preparsedElement.type === PreparsedElementType.STYLE) {
              // Skipping <script> for security reasons
              // Skipping <style> as we already processed them
              // in the StyleCompiler
              return null;
          }
          if (preparsedElement.type === PreparsedElementType.STYLESHEET &&
              isStyleUrlResolvable(preparsedElement.hrefAttr)) {
              // Skipping stylesheets with either relative urls or package scheme as we already processed
              // them in the StyleCompiler
              return null;
          }
          var matchableAttrs = [];
          var elementOrDirectiveProps = [];
          var elementOrDirectiveRefs = [];
          var elementVars = [];
          var animationProps = [];
          var events = [];
          var templateElementOrDirectiveProps = [];
          var templateMatchableAttrs = [];
          var templateElementVars = [];
          var hasInlineTemplates = false;
          var attrs = [];
          var lcElName = splitNsName(nodeName.toLowerCase())[1];
          var isTemplateElement = lcElName == TEMPLATE_ELEMENT;
          element.attrs.forEach(function (attr) {
              var hasBinding = _this._parseAttr(isTemplateElement, attr, matchableAttrs, elementOrDirectiveProps, animationProps, events, elementOrDirectiveRefs, elementVars);
              var hasTemplateBinding = _this._parseInlineTemplateBinding(attr, templateMatchableAttrs, templateElementOrDirectiveProps, templateElementVars);
              if (hasTemplateBinding && hasInlineTemplates) {
                  _this._reportError("Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with *", attr.sourceSpan);
              }
              if (!hasBinding && !hasTemplateBinding) {
                  // don't include the bindings as attributes as well in the AST
                  attrs.push(_this.visitAttribute(attr, null));
                  matchableAttrs.push([attr.name, attr.value]);
              }
              if (hasTemplateBinding) {
                  hasInlineTemplates = true;
              }
          });
          var elementCssSelector = createElementCssSelector(nodeName, matchableAttrs);
          var _a = this._parseDirectives(this.selectorMatcher, elementCssSelector), directiveMetas = _a.directives, matchElement = _a.matchElement;
          var references = [];
          var directiveAsts = this._createDirectiveAsts(isTemplateElement, element.name, directiveMetas, elementOrDirectiveProps, elementOrDirectiveRefs, element.sourceSpan, references);
          var elementProps = this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directiveAsts)
              .concat(animationProps);
          var isViewRoot = parent.isTemplateElement || hasInlineTemplates;
          var providerContext = new ProviderElementContext(this.providerViewContext, parent.providerContext, isViewRoot, directiveAsts, attrs, references, element.sourceSpan);
          var children = html.visitAll(preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children, ElementContext.create(isTemplateElement, directiveAsts, isTemplateElement ? parent.providerContext : providerContext));
          providerContext.afterElement();
          // Override the actual selector when the `ngProjectAs` attribute is provided
          var projectionSelector = isPresent(preparsedElement.projectAs) ?
              CssSelector.parse(preparsedElement.projectAs)[0] :
              elementCssSelector;
          var ngContentIndex = parent.findNgContentIndex(projectionSelector);
          var parsedElement;
          if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
              if (isPresent(element.children) && element.children.length > 0) {
                  this._reportError("<ng-content> element cannot have content. <ng-content> must be immediately followed by </ng-content>", element.sourceSpan);
              }
              parsedElement = new NgContentAst(this.ngContentCount++, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
          }
          else if (isTemplateElement) {
              this._assertAllEventsPublishedByDirectives(directiveAsts, events);
              this._assertNoComponentsNorElementBindingsOnTemplate(directiveAsts, elementProps, element.sourceSpan);
              parsedElement = new EmbeddedTemplateAst(attrs, events, references, elementVars, providerContext.transformedDirectiveAsts, providerContext.transformProviders, providerContext.transformedHasViewContainer, children, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
          }
          else {
              this._assertElementExists(matchElement, element);
              this._assertOnlyOneComponent(directiveAsts, element.sourceSpan);
              var ngContentIndex_1 = hasInlineTemplates ? null : parent.findNgContentIndex(projectionSelector);
              parsedElement = new ElementAst(nodeName, attrs, elementProps, events, references, providerContext.transformedDirectiveAsts, providerContext.transformProviders, providerContext.transformedHasViewContainer, children, hasInlineTemplates ? null : ngContentIndex_1, element.sourceSpan);
          }
          if (hasInlineTemplates) {
              var templateCssSelector = createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs);
              var templateDirectiveMetas = this._parseDirectives(this.selectorMatcher, templateCssSelector).directives;
              var templateDirectiveAsts = this._createDirectiveAsts(true, element.name, templateDirectiveMetas, templateElementOrDirectiveProps, [], element.sourceSpan, []);
              var templateElementProps = this._createElementPropertyAsts(element.name, templateElementOrDirectiveProps, templateDirectiveAsts);
              this._assertNoComponentsNorElementBindingsOnTemplate(templateDirectiveAsts, templateElementProps, element.sourceSpan);
              var templateProviderContext = new ProviderElementContext(this.providerViewContext, parent.providerContext, parent.isTemplateElement, templateDirectiveAsts, [], [], element.sourceSpan);
              templateProviderContext.afterElement();
              parsedElement = new EmbeddedTemplateAst([], [], [], templateElementVars, templateProviderContext.transformedDirectiveAsts, templateProviderContext.transformProviders, templateProviderContext.transformedHasViewContainer, [parsedElement], ngContentIndex, element.sourceSpan);
          }
          return parsedElement;
      };
      TemplateParseVisitor.prototype._parseInlineTemplateBinding = function (attr, targetMatchableAttrs, targetProps, targetVars) {
          var templateBindingsSource = null;
          if (this._normalizeAttributeName(attr.name) == TEMPLATE_ATTR) {
              templateBindingsSource = attr.value;
          }
          else if (attr.name.startsWith(TEMPLATE_ATTR_PREFIX)) {
              var key = attr.name.substring(TEMPLATE_ATTR_PREFIX.length); // remove the star
              templateBindingsSource = (attr.value.length == 0) ? key : key + ' ' + attr.value;
          }
          if (isPresent(templateBindingsSource)) {
              var bindings = this._parseTemplateBindings(templateBindingsSource, attr.sourceSpan);
              for (var i = 0; i < bindings.length; i++) {
                  var binding = bindings[i];
                  if (binding.keyIsVar) {
                      targetVars.push(new VariableAst(binding.key, binding.name, attr.sourceSpan));
                  }
                  else if (isPresent(binding.expression)) {
                      this._parsePropertyAst(binding.key, binding.expression, attr.sourceSpan, targetMatchableAttrs, targetProps);
                  }
                  else {
                      targetMatchableAttrs.push([binding.key, '']);
                      this._parseLiteralAttr(binding.key, null, attr.sourceSpan, targetProps);
                  }
              }
              return true;
          }
          return false;
      };
      TemplateParseVisitor.prototype._parseAttr = function (isTemplateElement, attr, targetMatchableAttrs, targetProps, targetAnimationProps, targetEvents, targetRefs, targetVars) {
          var name = this._normalizeAttributeName(attr.name);
          var value = attr.value;
          var srcSpan = attr.sourceSpan;
          var bindParts = name.match(BIND_NAME_REGEXP);
          var hasBinding = false;
          if (bindParts !== null) {
              hasBinding = true;
              if (isPresent(bindParts[KW_BIND_IDX])) {
                  this._parsePropertyOrAnimation(bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetProps, targetAnimationProps);
              }
              else if (bindParts[KW_LET_IDX]) {
                  if (isTemplateElement) {
                      var identifier = bindParts[IDENT_KW_IDX];
                      this._parseVariable(identifier, value, srcSpan, targetVars);
                  }
                  else {
                      this._reportError("\"let-\" is only supported on template elements.", srcSpan);
                  }
              }
              else if (bindParts[KW_REF_IDX]) {
                  var identifier = bindParts[IDENT_KW_IDX];
                  this._parseReference(identifier, value, srcSpan, targetRefs);
              }
              else if (bindParts[KW_ON_IDX]) {
                  this._parseEvent(bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
              }
              else if (bindParts[KW_BINDON_IDX]) {
                  this._parsePropertyOrAnimation(bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetProps, targetAnimationProps);
                  this._parseAssignmentEvent(bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
              }
              else if (bindParts[KW_AT_IDX]) {
                  if (name[0] == '@' && isPresent(value) && value.length > 0) {
                      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.", srcSpan, ParseErrorLevel.FATAL);
                  }
                  this._parseAnimation(bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetAnimationProps);
              }
              else if (bindParts[IDENT_BANANA_BOX_IDX]) {
                  this._parsePropertyOrAnimation(bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, targetMatchableAttrs, targetProps, targetAnimationProps);
                  this._parseAssignmentEvent(bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
              }
              else if (bindParts[IDENT_PROPERTY_IDX]) {
                  this._parsePropertyOrAnimation(bindParts[IDENT_PROPERTY_IDX], value, srcSpan, targetMatchableAttrs, targetProps, targetAnimationProps);
              }
              else if (bindParts[IDENT_EVENT_IDX]) {
                  this._parseEvent(bindParts[IDENT_EVENT_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
              }
          }
          else {
              hasBinding =
                  this._parsePropertyInterpolation(name, value, srcSpan, targetMatchableAttrs, targetProps);
          }
          if (!hasBinding) {
              this._parseLiteralAttr(name, value, srcSpan, targetProps);
          }
          return hasBinding;
      };
      TemplateParseVisitor.prototype._normalizeAttributeName = function (attrName) {
          return /^data-/i.test(attrName) ? attrName.substring(5) : attrName;
      };
      TemplateParseVisitor.prototype._parseVariable = function (identifier, value, sourceSpan, targetVars) {
          if (identifier.indexOf('-') > -1) {
              this._reportError("\"-\" is not allowed in variable names", sourceSpan);
          }
          targetVars.push(new VariableAst(identifier, value, sourceSpan));
      };
      TemplateParseVisitor.prototype._parseReference = function (identifier, value, sourceSpan, targetRefs) {
          if (identifier.indexOf('-') > -1) {
              this._reportError("\"-\" is not allowed in reference names", sourceSpan);
          }
          targetRefs.push(new ElementOrDirectiveRef(identifier, value, sourceSpan));
      };
      TemplateParseVisitor.prototype._parsePropertyOrAnimation = function (name, expression, sourceSpan, targetMatchableAttrs, targetProps, targetAnimationProps) {
          var animatePropLength = ANIMATE_PROP_PREFIX.length;
          var isAnimationProp = name[0] == '@';
          var animationPrefixLength = 1;
          if (name.substring(0, animatePropLength) == ANIMATE_PROP_PREFIX) {
              isAnimationProp = true;
              animationPrefixLength = animatePropLength;
          }
          if (isAnimationProp) {
              this._parseAnimation(name.substr(animationPrefixLength), expression, sourceSpan, targetMatchableAttrs, targetAnimationProps);
          }
          else {
              this._parsePropertyAst(name, this._parseBinding(expression, sourceSpan), sourceSpan, targetMatchableAttrs, targetProps);
          }
      };
      TemplateParseVisitor.prototype._parseAnimation = function (name, expression, sourceSpan, targetMatchableAttrs, targetAnimationProps) {
          // 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
          if (!isPresent(expression) || expression.length == 0) {
              expression = 'null';
          }
          var ast = this._parseBinding(expression, sourceSpan);
          targetMatchableAttrs.push([name, ast.source]);
          targetAnimationProps.push(new BoundElementPropertyAst(name, PropertyBindingType.Animation, SecurityContext.NONE, ast, null, sourceSpan));
      };
      TemplateParseVisitor.prototype._parsePropertyInterpolation = function (name, value, sourceSpan, targetMatchableAttrs, targetProps) {
          var expr = this._parseInterpolation(value, sourceSpan);
          if (isPresent(expr)) {
              this._parsePropertyAst(name, expr, sourceSpan, targetMatchableAttrs, targetProps);
              return true;
          }
          return false;
      };
      TemplateParseVisitor.prototype._parsePropertyAst = function (name, ast, sourceSpan, targetMatchableAttrs, targetProps) {
          targetMatchableAttrs.push([name, ast.source]);
          targetProps.push(new BoundElementOrDirectiveProperty(name, ast, false, sourceSpan));
      };
      TemplateParseVisitor.prototype._parseAssignmentEvent = function (name, expression, sourceSpan, targetMatchableAttrs, targetEvents) {
          this._parseEvent(name + "Change", expression + "=$event", sourceSpan, targetMatchableAttrs, targetEvents);
      };
      TemplateParseVisitor.prototype._parseEvent = function (name, expression, sourceSpan, targetMatchableAttrs, targetEvents) {
          // long format: 'target: eventName'
          var parts = splitAtColon(name, [null, name]);
          var target = parts[0];
          var eventName = parts[1];
          var ast = this._parseAction(expression, sourceSpan);
          targetMatchableAttrs.push([name, ast.source]);
          targetEvents.push(new BoundEventAst(eventName, target, ast, sourceSpan));
          // Don't detect directives for event names for now,
          // so don't add the event name to the matchableAttrs
      };
      TemplateParseVisitor.prototype._parseLiteralAttr = function (name, value, sourceSpan, targetProps) {
          targetProps.push(new BoundElementOrDirectiveProperty(name, this._exprParser.wrapLiteralPrimitive(value, ''), true, sourceSpan));
      };
      TemplateParseVisitor.prototype._parseDirectives = function (selectorMatcher, elementCssSelector) {
          var _this = this;
          // Need to sort the directives so that we get consistent results throughout,
          // as selectorMatcher uses Maps inside.
          // Also deduplicate directives as they might match more than one time!
          var directives = new Array(this.directivesIndex.size);
          // Whether any directive selector matches on the element name
          var matchElement = false;
          selectorMatcher.match(elementCssSelector, function (selector, directive) {
              directives[_this.directivesIndex.get(directive)] = directive;
              matchElement = matchElement || selector.hasElementSelector();
          });
          return {
              directives: directives.filter(function (dir) { return !!dir; }),
              matchElement: matchElement,
          };
      };
      TemplateParseVisitor.prototype._createDirectiveAsts = function (isTemplateElement, elementName, directives, props, elementOrDirectiveRefs, elementSourceSpan, targetReferences) {
          var _this = this;
          var matchedReferences = new Set();
          var component = null;
          var directiveAsts = directives.map(function (directive) {
              var sourceSpan = new ParseSourceSpan(elementSourceSpan.start, elementSourceSpan.end, "Directive " + directive.type.name);
              if (directive.isComponent) {
                  component = directive;
              }
              var hostProperties = [];
              var hostEvents = [];
              var directiveProperties = [];
              _this._createDirectiveHostPropertyAsts(elementName, directive.hostProperties, sourceSpan, hostProperties);
              _this._createDirectiveHostEventAsts(directive.hostListeners, sourceSpan, hostEvents);
              _this._createDirectivePropertyAsts(directive.inputs, props, directiveProperties);
              elementOrDirectiveRefs.forEach(function (elOrDirRef) {
                  if ((elOrDirRef.value.length === 0 && directive.isComponent) ||
                      (directive.exportAs == elOrDirRef.value)) {
                      targetReferences.push(new ReferenceAst(elOrDirRef.name, identifierToken(directive.type), elOrDirRef.sourceSpan));
                      matchedReferences.add(elOrDirRef.name);
                  }
              });
              return new DirectiveAst(directive, directiveProperties, hostProperties, hostEvents, sourceSpan);
          });
          elementOrDirectiveRefs.forEach(function (elOrDirRef) {
              if (elOrDirRef.value.length > 0) {
                  if (!matchedReferences.has(elOrDirRef.name)) {
                      _this._reportError("There is no directive with \"exportAs\" set to \"" + elOrDirRef.value + "\"", elOrDirRef.sourceSpan);
                  }
              }
              else if (!component) {
                  var refToken = null;
                  if (isTemplateElement) {
                      refToken = resolveIdentifierToken(Identifiers.TemplateRef);
                  }
                  targetReferences.push(new ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.sourceSpan));
              }
          }); // fix syntax highlighting issue: `
          return directiveAsts;
      };
      TemplateParseVisitor.prototype._createDirectiveHostPropertyAsts = function (elementName, hostProps, sourceSpan, targetPropertyAsts) {
          var _this = this;
          if (hostProps) {
              StringMapWrapper.forEach(hostProps, function (expression, propName) {
                  if (isString(expression)) {
                      var exprAst = _this._parseBinding(expression, sourceSpan);
                      targetPropertyAsts.push(_this._createElementPropertyAst(elementName, propName, exprAst, 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);
                  }
              });
          }
      };
      TemplateParseVisitor.prototype._createDirectiveHostEventAsts = function (hostListeners, sourceSpan, targetEventAsts) {
          var _this = this;
          if (hostListeners) {
              StringMapWrapper.forEach(hostListeners, function (expression, propName) {
                  if (isString(expression)) {
                      _this._parseEvent(propName, expression, sourceSpan, [], targetEventAsts);
                  }
                  else {
                      _this._reportError("Value of the host listener \"" + propName + "\" needs to be a string representing an expression but got \"" + expression + "\" (" + typeof expression + ")", sourceSpan);
                  }
              });
          }
      };
      TemplateParseVisitor.prototype._createDirectivePropertyAsts = function (directiveProperties, boundProps, targetBoundDirectiveProps) {
          if (directiveProperties) {
              var boundPropsByName_1 = new Map();
              boundProps.forEach(function (boundProp) {
                  var prevValue = boundPropsByName_1.get(boundProp.name);
                  if (isBlank(prevValue) || prevValue.isLiteral) {
                      // give [a]="b" a higher precedence than a="b" on the same element
                      boundPropsByName_1.set(boundProp.name, boundProp);
                  }
              });
              StringMapWrapper.forEach(directiveProperties, function (elProp, dirProp) {
                  var boundProp = boundPropsByName_1.get(elProp);
                  // Bindings are optional, so this binding only needs to be set up if an expression is given.
                  if (boundProp) {
                      targetBoundDirectiveProps.push(new BoundDirectivePropertyAst(dirProp, boundProp.name, boundProp.expression, boundProp.sourceSpan));
                  }
              });
          }
      };
      TemplateParseVisitor.prototype._createElementPropertyAsts = function (elementName, props, directives) {
          var _this = this;
          var boundElementProps = [];
          var boundDirectivePropsIndex = new Map();
          directives.forEach(function (directive) {
              directive.inputs.forEach(function (prop) {
                  boundDirectivePropsIndex.set(prop.templateName, prop);
              });
          });
          props.forEach(function (prop) {
              if (!prop.isLiteral && isBlank(boundDirectivePropsIndex.get(prop.name))) {
                  boundElementProps.push(_this._createElementPropertyAst(elementName, prop.name, prop.expression, prop.sourceSpan));
              }
          });
          return boundElementProps;
      };
      TemplateParseVisitor.prototype._createElementPropertyAst = function (elementName, name, ast, sourceSpan) {
          var unit = null;
          var bindingType;
          var boundPropertyName;
          var parts = name.split(PROPERTY_PARTS_SEPARATOR);
          var securityContext;
          if (parts.length === 1) {
              var partValue = parts[0];
              if (partValue[0] == '@') {
                  boundPropertyName = partValue.substr(1);
                  bindingType = PropertyBindingType.Animation;
                  securityContext = SecurityContext.NONE;
              }
              else {
                  boundPropertyName = this._schemaRegistry.getMappedPropName(partValue);
                  securityContext = this._schemaRegistry.securityContext(elementName, boundPropertyName);
                  bindingType = PropertyBindingType.Property;
                  this._assertNoEventBinding(boundPropertyName, sourceSpan, false);
                  if (!this._schemaRegistry.hasProperty(elementName, boundPropertyName, this._schemas)) {
                      var errorMsg = "Can't bind to '" + boundPropertyName + "' since it isn't a known property of '" + elementName + "'.";
                      if (elementName.indexOf('-') > -1) {
                          errorMsg +=
                              ("\n1. If '" + elementName + "' is an Angular component and it has '" + boundPropertyName + "' input, then verify that it is part of this module.") +
                                  ("\n2. If '" + elementName + "' is a Web Component then add \"CUSTOM_ELEMENTS_SCHEMA\" to the '@NgModule.schemas' of this component to suppress this message.\n");
                      }
                      this._reportError(errorMsg, sourceSpan);
                  }
              }
          }
          else {
              if (parts[0] == ATTRIBUTE_PREFIX) {
                  boundPropertyName = parts[1];
                  this._assertNoEventBinding(boundPropertyName, sourceSpan, true);
                  // NB: For security purposes, use the mapped property name, not the attribute name.
                  var mapPropName = this._schemaRegistry.getMappedPropName(boundPropertyName);
                  securityContext = this._schemaRegistry.securityContext(elementName, mapPropName);
                  var nsSeparatorIdx = boundPropertyName.indexOf(':');
                  if (nsSeparatorIdx > -1) {
                      var ns = boundPropertyName.substring(0, nsSeparatorIdx);
                      var name_1 = boundPropertyName.substring(nsSeparatorIdx + 1);
                      boundPropertyName = mergeNsAndName(ns, name_1);
                  }
                  bindingType = PropertyBindingType.Attribute;
              }
              else if (parts[0] == CLASS_PREFIX) {
                  boundPropertyName = parts[1];
                  bindingType = PropertyBindingType.Class;
                  securityContext = SecurityContext.NONE;
              }
              else if (parts[0] == STYLE_PREFIX) {
                  unit = parts.length > 2 ? parts[2] : null;
                  boundPropertyName = parts[1];
                  bindingType = PropertyBindingType.Style;
                  securityContext = SecurityContext.STYLE;
              }
              else {
                  this._reportError("Invalid property name '" + name + "'", sourceSpan);
                  bindingType = null;
                  securityContext = null;
              }
          }
          return new BoundElementPropertyAst(boundPropertyName, bindingType, securityContext, ast, unit, sourceSpan);
      };
      /**
       * @param propName the name of the property / attribute
       * @param sourceSpan
       * @param isAttr true when binding to an attribute
       * @private
       */
      TemplateParseVisitor.prototype._assertNoEventBinding = function (propName, sourceSpan, isAttr) {
          if (propName.toLowerCase().startsWith('on')) {
              var msg = ("Binding to event attribute '" + propName + "' is disallowed for security reasons, ") +
                  ("please use (" + propName.slice(2) + ")=...");
              if (!isAttr) {
                  msg +=
                      ("\nIf '" + propName + "' is a directive input, make sure the directive is imported by the") +
                          " current module.";
              }
              this._reportError(msg, sourceSpan, ParseErrorLevel.FATAL);
          }
      };
      TemplateParseVisitor.prototype._findComponentDirectiveNames = function (directives) {
          var componentTypeNames = [];
          directives.forEach(function (directive) {
              var typeName = directive.directive.type.name;
              if (directive.directive.isComponent) {
                  componentTypeNames.push(typeName);
              }
          });
          return componentTypeNames;
      };
      TemplateParseVisitor.prototype._assertOnlyOneComponent = function (directives, sourceSpan) {
          var componentTypeNames = this._findComponentDirectiveNames(directives);
          if (componentTypeNames.length > 1) {
              this._reportError("More than one component: " + componentTypeNames.join(','), sourceSpan);
          }
      };
      /**
       * Make sure that non-angular tags conform to the schemas.
       *
       * Note: An element is considered an angular tag when at least one directive selector matches the
       * tag name.
       *
       * @param matchElement Whether any directive has matched on the tag name
       * @param element the html element
       */
      TemplateParseVisitor.prototype._assertElementExists = function (matchElement, element) {
          var elName = element.name.replace(/^:xhtml:/, '');
          if (!matchElement && !this._schemaRegistry.hasElement(elName, this._schemas)) {
              var errorMsg = ("'" + elName + "' is not a known element:\n") +
                  ("1. If '" + elName + "' is an Angular component, then verify that it is part of this module.\n") +
                  ("2. If '" + elName + "' is a Web Component then add \"CUSTOM_ELEMENTS_SCHEMA\" to the '@NgModule.schemas' of this component to suppress this message.");
              this._reportError(errorMsg, element.sourceSpan);
          }
      };
      TemplateParseVisitor.prototype._assertNoComponentsNorElementBindingsOnTemplate = function (directives, elementProps, sourceSpan) {
          var _this = this;
          var componentTypeNames = this._findComponentDirectiveNames(directives);
          if (componentTypeNames.length > 0) {
              this._reportError("Components on an embedded template: " + componentTypeNames.join(','), sourceSpan);
          }
          elementProps.forEach(function (prop) {
              _this._reportError("Property binding " + prop.name + " not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the \"directives\" section.", sourceSpan);
          });
      };
      TemplateParseVisitor.prototype._assertAllEventsPublishedByDirectives = function (directives, events) {
          var _this = this;
          var allDirectiveEvents = new Set();
          directives.forEach(function (directive) {
              StringMapWrapper.forEach(directive.directive.outputs, function (eventName) {
                  allDirectiveEvents.add(eventName);
              });
          });
          events.forEach(function (event) {
              if (isPresent(event.target) || !allDirectiveEvents.has(event.name)) {
                  _this._reportError("Event binding " + event.fullName + " not emitted by any directive on an embedded template. Make sure that the event name is spelled correctly and all directives are listed in the \"directives\" section.", event.sourceSpan);
              }
          });
      };
      return TemplateParseVisitor;
  }());
  var NonBindableVisitor = (function () {
      function NonBindableVisitor() {
      }
      NonBindableVisitor.prototype.visitElement = function (ast, parent) {
          var preparsedElement = preparseElement(ast);
          if (preparsedElement.type === PreparsedElementType.SCRIPT ||
              preparsedElement.type === PreparsedElementType.STYLE ||
              preparsedElement.type === PreparsedElementType.STYLESHEET) {
              // Skipping <script> for security reasons
              // Skipping <style> and stylesheets as we already processed them
              // in the StyleCompiler
              return null;
          }
          var attrNameAndValues = ast.attrs.map(function (attrAst) { return [attrAst.name, attrAst.value]; });
          var selector = createElementCssSelector(ast.name, attrNameAndValues);
          var ngContentIndex = parent.findNgContentIndex(selector);
          var children = html.visitAll(this, ast.children, EMPTY_ELEMENT_CONTEXT);
          return new ElementAst(ast.name, html.visitAll(this, ast.attrs), [], [], [], [], [], false, children, ngContentIndex, ast.sourceSpan);
      };
      NonBindableVisitor.prototype.visitComment = function (comment, context) { return null; };
      NonBindableVisitor.prototype.visitAttribute = function (attribute, context) {
          return new AttrAst(attribute.name, attribute.value, attribute.sourceSpan);
      };
      NonBindableVisitor.prototype.visitText = function (text, parent) {
          var ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR);
          return new TextAst(text.value, ngContentIndex, text.sourceSpan);
      };
      NonBindableVisitor.prototype.visitExpansion = function (expansion, context) { return expansion; };
      NonBindableVisitor.prototype.visitExpansionCase = function (expansionCase, context) { return expansionCase; };
      return NonBindableVisitor;
  }());
  var BoundElementOrDirectiveProperty = (function () {
      function BoundElementOrDirectiveProperty(name, expression, isLiteral, sourceSpan) {
          this.name = name;
          this.expression = expression;
          this.isLiteral = isLiteral;
          this.sourceSpan = sourceSpan;
      }
      return BoundElementOrDirectiveProperty;
  }());
  var ElementOrDirectiveRef = (function () {
      function ElementOrDirectiveRef(name, value, sourceSpan) {
          this.name = name;
          this.value = value;
          this.sourceSpan = sourceSpan;
      }
      return ElementOrDirectiveRef;
  }());
  export function splitClasses(classAttrValue) {
      return classAttrValue.trim().split(/\s+/g);
  }
  var ElementContext = (function () {
      function ElementContext(isTemplateElement, _ngContentIndexMatcher, _wildcardNgContentIndex, providerContext) {
          this.isTemplateElement = isTemplateElement;
          this._ngContentIndexMatcher = _ngContentIndexMatcher;
          this._wildcardNgContentIndex = _wildcardNgContentIndex;
          this.providerContext = providerContext;
      }
      ElementContext.create = function (isTemplateElement, directives, providerContext) {
          var matcher = new SelectorMatcher();
          var wildcardNgContentIndex = null;
          var component = directives.find(function (directive) { return directive.directive.isComponent; });
          if (component) {
              var ngContentSelectors = component.directive.template.ngContentSelectors;
              for (var i = 0; i < ngContentSelectors.length; i++) {
                  var selector = ngContentSelectors[i];
                  if (selector === '*') {
                      wildcardNgContentIndex = i;
                  }
                  else {
                      matcher.addSelectables(CssSelector.parse(ngContentSelectors[i]), i);
                  }
              }
          }
          return new ElementContext(isTemplateElement, matcher, wildcardNgContentIndex, providerContext);
      };
      ElementContext.prototype.findNgContentIndex = function (selector) {
          var ngContentIndices = [];
          this._ngContentIndexMatcher.match(selector, function (selector, ngContentIndex) { ngContentIndices.push(ngContentIndex); });
          ngContentIndices.sort();
          if (isPresent(this._wildcardNgContentIndex)) {
              ngContentIndices.push(this._wildcardNgContentIndex);
          }
          return ngContentIndices.length > 0 ? ngContentIndices[0] : null;
      };
      return ElementContext;
  }());
  function createElementCssSelector(elementName, matchableAttrs) {
      var cssSelector = new CssSelector();
      var elNameNoNs = splitNsName(elementName)[1];
      cssSelector.setElement(elNameNoNs);
      for (var i = 0; i < matchableAttrs.length; i++) {
          var attrName = matchableAttrs[i][0];
          var attrNameNoNs = splitNsName(attrName)[1];
          var attrValue = matchableAttrs[i][1];
          cssSelector.addAttribute(attrNameNoNs, attrValue);
          if (attrName.toLowerCase() == CLASS_ATTR) {
              var classes = splitClasses(attrValue);
              classes.forEach(function (className) { return cssSelector.addClassName(className); });
          }
      }
      return cssSelector;
  }
  var EMPTY_ELEMENT_CONTEXT = new ElementContext(true, new SelectorMatcher(), null, null);
  var NON_BINDABLE_VISITOR = new NonBindableVisitor();
  export var PipeCollector = (function (_super) {
      __extends(PipeCollector, _super);
      function PipeCollector() {
          _super.apply(this, arguments);
          this.pipes = new Set();
      }
      PipeCollector.prototype.visitPipe = function (ast, context) {
          this.pipes.add(ast.name);
          ast.exp.visit(this);
          this.visitAll(ast.args, context);
          return null;
      };
      return PipeCollector;
  }(RecursiveAstVisitor));
  //# sourceMappingURL=template_parser.js.map