text: var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
      var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
      if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
      else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
      return c > 3 && r && Object.defineProperty(target, key, r), r;
  };
  var __metadata = (this && this.__metadata) || function (k, v) {
      if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
  };
  var __param = (this && this.__param) || function (paramIndex, decorator) {
      return function (target, key) { decorator(target, key, paramIndex); }
  };
  import { ListWrapper, StringMapWrapper, SetWrapper } from 'angular2/src/facade/collection';
  import { RegExpWrapper, isPresent, StringWrapper, isBlank } from 'angular2/src/facade/lang';
  import { Injectable, Inject, OpaqueToken, Optional } from 'angular2/core';
  import { CONST_EXPR } from 'angular2/src/facade/lang';
  import { BaseException } from 'angular2/src/facade/exceptions';
  import { Parser } from 'angular2/src/core/change_detection/change_detection';
  import { HtmlParser } from './html_parser';
  import { splitNsName } from './html_tags';
  import { ParseError } from './parse_util';
  import { RecursiveAstVisitor } from 'angular2/src/core/change_detection/parser/ast';
  import { ElementAst, BoundElementPropertyAst, BoundEventAst, VariableAst, templateVisitAll, TextAst, BoundTextAst, EmbeddedTemplateAst, AttrAst, NgContentAst, PropertyBindingType, DirectiveAst, BoundDirectivePropertyAst } from './template_ast';
  import { CssSelector, SelectorMatcher } from 'angular2/src/compiler/selector';
  import { ElementSchemaRegistry } from 'angular2/src/compiler/schema/element_schema_registry';
  import { preparseElement, PreparsedElementType } from './template_preparser';
  import { isStyleUrlResolvable } from './style_url_resolver';
  import { htmlVisitAll } from './html_ast';
  import { splitAtColon } from './util';
  // Group 1 = "bind-"
  // Group 2 = "var-" or "#"
  // Group 3 = "on-"
  // Group 4 = "bindon-"
  // Group 5 = the identifier after "bind-", "var-/#", or "on-"
  // Group 6 = identifier inside [()]
  // Group 7 = identifier inside []
  // Group 8 = identifier inside ()
  var BIND_NAME_REGEXP = /^(?:(?:(?:(bind-)|(var-|#)|(on-)|(bindon-))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/g;
  const TEMPLATE_ELEMENT = 'template';
  const TEMPLATE_ATTR = 'template';
  const TEMPLATE_ATTR_PREFIX = '*';
  const CLASS_ATTR = 'class';
  var PROPERTY_PARTS_SEPARATOR = '.';
  const ATTRIBUTE_PREFIX = 'attr';
  const CLASS_PREFIX = 'class';
  const 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 const TEMPLATE_TRANSFORMS = CONST_EXPR(new OpaqueToken('TemplateTransforms'));
  export class TemplateParseError extends ParseError {
      constructor(message, location) {
          super(location, message);
      }
  }
  export let TemplateParser = class {
      constructor(_exprParser, _schemaRegistry, _htmlParser, transforms) {
          this._exprParser = _exprParser;
          this._schemaRegistry = _schemaRegistry;
          this._htmlParser = _htmlParser;
          this.transforms = transforms;
      }
      parse(template, directives, pipes, templateUrl) {
          var parseVisitor = new TemplateParseVisitor(directives, pipes, this._exprParser, this._schemaRegistry);
          var htmlAstWithErrors = this._htmlParser.parse(template, templateUrl);
          var result = htmlVisitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_COMPONENT);
          var errors = htmlAstWithErrors.errors.concat(parseVisitor.errors);
          if (errors.length > 0) {
              var errorString = errors.join('\n');
              throw new BaseException(`Template parse errors:\n${errorString}`);
          }
          if (isPresent(this.transforms)) {
              this.transforms.forEach((transform) => { result = templateVisitAll(transform, result); });
          }
          return result;
      }
  };
  TemplateParser = __decorate([
      Injectable(),
      __param(3, Optional()),
      __param(3, Inject(TEMPLATE_TRANSFORMS)), 
      __metadata('design:paramtypes', [Parser, ElementSchemaRegistry, HtmlParser, Array])
  ], TemplateParser);
  class TemplateParseVisitor {
      constructor(directives, pipes, _exprParser, _schemaRegistry) {
          this._exprParser = _exprParser;
          this._schemaRegistry = _schemaRegistry;
          this.errors = [];
          this.directivesIndex = new Map();
          this.ngContentCount = 0;
          this.selectorMatcher = new SelectorMatcher();
          ListWrapper.forEachWithIndex(directives, (directive, index) => {
              var selector = CssSelector.parse(directive.selector);
              this.selectorMatcher.addSelectables(selector, directive);
              this.directivesIndex.set(directive, index);
          });
          this.pipesByName = new Map();
          pipes.forEach(pipe => this.pipesByName.set(pipe.name, pipe));
      }
      _reportError(message, sourceSpan) {
          this.errors.push(new TemplateParseError(message, sourceSpan.start));
      }
      _parseInterpolation(value, sourceSpan) {
          var sourceInfo = sourceSpan.start.toString();
          try {
              var ast = this._exprParser.parseInterpolation(value, sourceInfo);
              this._checkPipes(ast, sourceSpan);
              return ast;
          }
          catch (e) {
              this._reportError(`${e}`, sourceSpan);
              return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
          }
      }
      _parseAction(value, sourceSpan) {
          var sourceInfo = sourceSpan.start.toString();
          try {
              var ast = this._exprParser.parseAction(value, sourceInfo);
              this._checkPipes(ast, sourceSpan);
              return ast;
          }
          catch (e) {
              this._reportError(`${e}`, sourceSpan);
              return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
          }
      }
      _parseBinding(value, sourceSpan) {
          var sourceInfo = sourceSpan.start.toString();
          try {
              var ast = this._exprParser.parseBinding(value, sourceInfo);
              this._checkPipes(ast, sourceSpan);
              return ast;
          }
          catch (e) {
              this._reportError(`${e}`, sourceSpan);
              return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
          }
      }
      _parseTemplateBindings(value, sourceSpan) {
          var sourceInfo = sourceSpan.start.toString();
          try {
              var bindings = this._exprParser.parseTemplateBindings(value, sourceInfo);
              bindings.forEach((binding) => {
                  if (isPresent(binding.expression)) {
                      this._checkPipes(binding.expression, sourceSpan);
                  }
              });
              return bindings;
          }
          catch (e) {
              this._reportError(`${e}`, sourceSpan);
              return [];
          }
      }
      _checkPipes(ast, sourceSpan) {
          if (isPresent(ast)) {
              var collector = new PipeCollector();
              ast.visit(collector);
              collector.pipes.forEach((pipeName) => {
                  if (!this.pipesByName.has(pipeName)) {
                      this._reportError(`The pipe '${pipeName}' could not be found`, sourceSpan);
                  }
              });
          }
      }
      visitText(ast, component) {
          var ngContentIndex = component.findNgContentIndex(TEXT_CSS_SELECTOR);
          var expr = this._parseInterpolation(ast.value, ast.sourceSpan);
          if (isPresent(expr)) {
              return new BoundTextAst(expr, ngContentIndex, ast.sourceSpan);
          }
          else {
              return new TextAst(ast.value, ngContentIndex, ast.sourceSpan);
          }
      }
      visitAttr(ast, contex) {
          return new AttrAst(ast.name, ast.value, ast.sourceSpan);
      }
      visitElement(element, component) {
          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 vars = [];
          var events = [];
          var templateElementOrDirectiveProps = [];
          var templateVars = [];
          var templateMatchableAttrs = [];
          var hasInlineTemplates = false;
          var attrs = [];
          element.attrs.forEach(attr => {
              var hasBinding = this._parseAttr(attr, matchableAttrs, elementOrDirectiveProps, events, vars);
              var hasTemplateBinding = this._parseInlineTemplateBinding(attr, templateMatchableAttrs, templateElementOrDirectiveProps, templateVars);
              if (!hasBinding && !hasTemplateBinding) {
                  // don't include the bindings as attributes as well in the AST
                  attrs.push(this.visitAttr(attr, null));
                  matchableAttrs.push([attr.name, attr.value]);
              }
              if (hasTemplateBinding) {
                  hasInlineTemplates = true;
              }
          });
          var lcElName = splitNsName(nodeName.toLowerCase())[1];
          var isTemplateElement = lcElName == TEMPLATE_ELEMENT;
          var elementCssSelector = createElementCssSelector(nodeName, matchableAttrs);
          var directives = this._createDirectiveAsts(element.name, this._parseDirectives(this.selectorMatcher, elementCssSelector), elementOrDirectiveProps, isTemplateElement ? [] : vars, element.sourceSpan);
          var elementProps = this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directives);
          var children = htmlVisitAll(preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children, Component.create(directives));
          var elementNgContentIndex = hasInlineTemplates ? null : component.findNgContentIndex(elementCssSelector);
          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++, elementNgContentIndex, element.sourceSpan);
          }
          else if (isTemplateElement) {
              this._assertAllEventsPublishedByDirectives(directives, events);
              this._assertNoComponentsNorElementBindingsOnTemplate(directives, elementProps, element.sourceSpan);
              parsedElement = new EmbeddedTemplateAst(attrs, events, vars, directives, children, elementNgContentIndex, element.sourceSpan);
          }
          else {
              this._assertOnlyOneComponent(directives, element.sourceSpan);
              var elementExportAsVars = vars.filter(varAst => varAst.value.length === 0);
              parsedElement =
                  new ElementAst(nodeName, attrs, elementProps, events, elementExportAsVars, directives, children, elementNgContentIndex, element.sourceSpan);
          }
          if (hasInlineTemplates) {
              var templateCssSelector = createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs);
              var templateDirectives = this._createDirectiveAsts(element.name, this._parseDirectives(this.selectorMatcher, templateCssSelector), templateElementOrDirectiveProps, [], element.sourceSpan);
              var templateElementProps = this._createElementPropertyAsts(element.name, templateElementOrDirectiveProps, templateDirectives);
              this._assertNoComponentsNorElementBindingsOnTemplate(templateDirectives, templateElementProps, element.sourceSpan);
              parsedElement = new EmbeddedTemplateAst([], [], templateVars, templateDirectives, [parsedElement], component.findNgContentIndex(templateCssSelector), element.sourceSpan);
          }
          return parsedElement;
      }
      _parseInlineTemplateBinding(attr, targetMatchableAttrs, targetProps, targetVars) {
          var templateBindingsSource = null;
          if (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));
                      targetMatchableAttrs.push([binding.key, binding.name]);
                  }
                  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;
      }
      _parseAttr(attr, targetMatchableAttrs, targetProps, targetEvents, targetVars) {
          var attrName = this._normalizeAttributeName(attr.name);
          var attrValue = attr.value;
          var bindParts = RegExpWrapper.firstMatch(BIND_NAME_REGEXP, attrName);
          var hasBinding = false;
          if (isPresent(bindParts)) {
              hasBinding = true;
              if (isPresent(bindParts[1])) {
                  this._parseProperty(bindParts[5], attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps);
              }
              else if (isPresent(bindParts[2])) {
                  var identifier = bindParts[5];
                  this._parseVariable(identifier, attrValue, attr.sourceSpan, targetVars);
              }
              else if (isPresent(bindParts[3])) {
                  this._parseEvent(bindParts[5], attrValue, attr.sourceSpan, targetMatchableAttrs, targetEvents);
              }
              else if (isPresent(bindParts[4])) {
                  this._parseProperty(bindParts[5], attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps);
                  this._parseAssignmentEvent(bindParts[5], attrValue, attr.sourceSpan, targetMatchableAttrs, targetEvents);
              }
              else if (isPresent(bindParts[6])) {
                  this._parseProperty(bindParts[6], attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps);
                  this._parseAssignmentEvent(bindParts[6], attrValue, attr.sourceSpan, targetMatchableAttrs, targetEvents);
              }
              else if (isPresent(bindParts[7])) {
                  this._parseProperty(bindParts[7], attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps);
              }
              else if (isPresent(bindParts[8])) {
                  this._parseEvent(bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs, targetEvents);
              }
          }
          else {
              hasBinding = this._parsePropertyInterpolation(attrName, attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps);
          }
          if (!hasBinding) {
              this._parseLiteralAttr(attrName, attrValue, attr.sourceSpan, targetProps);
          }
          return hasBinding;
      }
      _normalizeAttributeName(attrName) {
          return attrName.toLowerCase().startsWith('data-') ? attrName.substring(5) : attrName;
      }
      _parseVariable(identifier, value, sourceSpan, targetVars) {
          if (identifier.indexOf('-') > -1) {
              this._reportError(`"-" is not allowed in variable names`, sourceSpan);
          }
          targetVars.push(new VariableAst(identifier, value, sourceSpan));
      }
      _parseProperty(name, expression, sourceSpan, targetMatchableAttrs, targetProps) {
          this._parsePropertyAst(name, this._parseBinding(expression, sourceSpan), sourceSpan, targetMatchableAttrs, targetProps);
      }
      _parsePropertyInterpolation(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;
      }
      _parsePropertyAst(name, ast, sourceSpan, targetMatchableAttrs, targetProps) {
          targetMatchableAttrs.push([name, ast.source]);
          targetProps.push(new BoundElementOrDirectiveProperty(name, ast, false, sourceSpan));
      }
      _parseAssignmentEvent(name, expression, sourceSpan, targetMatchableAttrs, targetEvents) {
          this._parseEvent(`${name}Change`, `${expression}=$event`, sourceSpan, targetMatchableAttrs, targetEvents);
      }
      _parseEvent(name, expression, sourceSpan, targetMatchableAttrs, targetEvents) {
          // long format: 'target: eventName'
          var parts = splitAtColon(name, [null, name]);
          var target = parts[0];
          var eventName = parts[1];
          targetEvents.push(new BoundEventAst(eventName, target, this._parseAction(expression, sourceSpan), sourceSpan));
          // Don't detect directives for event names for now,
          // so don't add the event name to the matchableAttrs
      }
      _parseLiteralAttr(name, value, sourceSpan, targetProps) {
          targetProps.push(new BoundElementOrDirectiveProperty(name, this._exprParser.wrapLiteralPrimitive(value, ''), true, sourceSpan));
      }
      _parseDirectives(selectorMatcher, elementCssSelector) {
          var directives = [];
          selectorMatcher.match(elementCssSelector, (selector, directive) => { directives.push(directive); });
          // Need to sort the directives so that we get consistent results throughout,
          // as selectorMatcher uses Maps inside.
          // Also need to make components the first directive in the array
          ListWrapper.sort(directives, (dir1, dir2) => {
              var dir1Comp = dir1.isComponent;
              var dir2Comp = dir2.isComponent;
              if (dir1Comp && !dir2Comp) {
                  return -1;
              }
              else if (!dir1Comp && dir2Comp) {
                  return 1;
              }
              else {
                  return this.directivesIndex.get(dir1) - this.directivesIndex.get(dir2);
              }
          });
          return directives;
      }
      _createDirectiveAsts(elementName, directives, props, possibleExportAsVars, sourceSpan) {
          var matchedVariables = new Set();
          var directiveAsts = directives.map((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);
              var exportAsVars = [];
              possibleExportAsVars.forEach((varAst) => {
                  if ((varAst.value.length === 0 && directive.isComponent) ||
                      (directive.exportAs == varAst.value)) {
                      exportAsVars.push(varAst);
                      matchedVariables.add(varAst.name);
                  }
              });
              return new DirectiveAst(directive, directiveProperties, hostProperties, hostEvents, exportAsVars, sourceSpan);
          });
          possibleExportAsVars.forEach((varAst) => {
              if (varAst.value.length > 0 && !SetWrapper.has(matchedVariables, varAst.name)) {
                  this._reportError(`There is no directive with "exportAs" set to "${varAst.value}"`, varAst.sourceSpan);
              }
          });
          return directiveAsts;
      }
      _createDirectiveHostPropertyAsts(elementName, hostProps, sourceSpan, targetPropertyAsts) {
          if (isPresent(hostProps)) {
              StringMapWrapper.forEach(hostProps, (expression, propName) => {
                  var exprAst = this._parseBinding(expression, sourceSpan);
                  targetPropertyAsts.push(this._createElementPropertyAst(elementName, propName, exprAst, sourceSpan));
              });
          }
      }
      _createDirectiveHostEventAsts(hostListeners, sourceSpan, targetEventAsts) {
          if (isPresent(hostListeners)) {
              StringMapWrapper.forEach(hostListeners, (expression, propName) => {
                  this._parseEvent(propName, expression, sourceSpan, [], targetEventAsts);
              });
          }
      }
      _createDirectivePropertyAsts(directiveProperties, boundProps, targetBoundDirectiveProps) {
          if (isPresent(directiveProperties)) {
              var boundPropsByName = new Map();
              boundProps.forEach(boundProp => {
                  var prevValue = boundPropsByName.get(boundProp.name);
                  if (isBlank(prevValue) || prevValue.isLiteral) {
                      // give [a]="b" a higher precedence than a="b" on the same element
                      boundPropsByName.set(boundProp.name, boundProp);
                  }
              });
              StringMapWrapper.forEach(directiveProperties, (elProp, dirProp) => {
                  var boundProp = boundPropsByName.get(elProp);
                  // Bindings are optional, so this binding only needs to be set up if an expression is given.
                  if (isPresent(boundProp)) {
                      targetBoundDirectiveProps.push(new BoundDirectivePropertyAst(dirProp, boundProp.name, boundProp.expression, boundProp.sourceSpan));
                  }
              });
          }
      }
      _createElementPropertyAsts(elementName, props, directives) {
          var boundElementProps = [];
          var boundDirectivePropsIndex = new Map();
          directives.forEach((directive) => {
              directive.inputs.forEach((prop) => {
                  boundDirectivePropsIndex.set(prop.templateName, prop);
              });
          });
          props.forEach((prop) => {
              if (!prop.isLiteral && isBlank(boundDirectivePropsIndex.get(prop.name))) {
                  boundElementProps.push(this._createElementPropertyAst(elementName, prop.name, prop.expression, prop.sourceSpan));
              }
          });
          return boundElementProps;
      }
      _createElementPropertyAst(elementName, name, ast, sourceSpan) {
          var unit = null;
          var bindingType;
          var boundPropertyName;
          var parts = name.split(PROPERTY_PARTS_SEPARATOR);
          if (parts.length === 1) {
              boundPropertyName = this._schemaRegistry.getMappedPropName(parts[0]);
              bindingType = PropertyBindingType.Property;
              if (!this._schemaRegistry.hasProperty(elementName, boundPropertyName)) {
                  this._reportError(`Can't bind to '${boundPropertyName}' since it isn't a known native property`, sourceSpan);
              }
          }
          else {
              if (parts[0] == ATTRIBUTE_PREFIX) {
                  boundPropertyName = parts[1];
                  bindingType = PropertyBindingType.Attribute;
              }
              else if (parts[0] == CLASS_PREFIX) {
                  boundPropertyName = parts[1];
                  bindingType = PropertyBindingType.Class;
              }
              else if (parts[0] == STYLE_PREFIX) {
                  unit = parts.length > 2 ? parts[2] : null;
                  boundPropertyName = parts[1];
                  bindingType = PropertyBindingType.Style;
              }
              else {
                  this._reportError(`Invalid property name '${name}'`, sourceSpan);
                  bindingType = null;
              }
          }
          return new BoundElementPropertyAst(boundPropertyName, bindingType, ast, unit, sourceSpan);
      }
      _findComponentDirectiveNames(directives) {
          var componentTypeNames = [];
          directives.forEach(directive => {
              var typeName = directive.directive.type.name;
              if (directive.directive.isComponent) {
                  componentTypeNames.push(typeName);
              }
          });
          return componentTypeNames;
      }
      _assertOnlyOneComponent(directives, sourceSpan) {
          var componentTypeNames = this._findComponentDirectiveNames(directives);
          if (componentTypeNames.length > 1) {
              this._reportError(`More than one component: ${componentTypeNames.join(',')}`, sourceSpan);
          }
      }
      _assertNoComponentsNorElementBindingsOnTemplate(directives, elementProps, sourceSpan) {
          var componentTypeNames = this._findComponentDirectiveNames(directives);
          if (componentTypeNames.length > 0) {
              this._reportError(`Components on an embedded template: ${componentTypeNames.join(',')}`, sourceSpan);
          }
          elementProps.forEach(prop => {
              this._reportError(`Property binding ${prop.name} not used by any directive on an embedded template`, sourceSpan);
          });
      }
      _assertAllEventsPublishedByDirectives(directives, events) {
          var allDirectiveEvents = new Set();
          directives.forEach(directive => {
              StringMapWrapper.forEach(directive.directive.outputs, (eventName, _) => { allDirectiveEvents.add(eventName); });
          });
          events.forEach(event => {
              if (isPresent(event.target) || !SetWrapper.has(allDirectiveEvents, event.name)) {
                  this._reportError(`Event binding ${event.fullName} not emitted by any directive on an embedded template`, event.sourceSpan);
              }
          });
      }
  }
  class NonBindableVisitor {
      visitElement(ast, component) {
          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(attrAst => [attrAst.name, attrAst.value]);
          var selector = createElementCssSelector(ast.name, attrNameAndValues);
          var ngContentIndex = component.findNgContentIndex(selector);
          var children = htmlVisitAll(this, ast.children, EMPTY_COMPONENT);
          return new ElementAst(ast.name, htmlVisitAll(this, ast.attrs), [], [], [], [], children, ngContentIndex, ast.sourceSpan);
      }
      visitAttr(ast, context) {
          return new AttrAst(ast.name, ast.value, ast.sourceSpan);
      }
      visitText(ast, component) {
          var ngContentIndex = component.findNgContentIndex(TEXT_CSS_SELECTOR);
          return new TextAst(ast.value, ngContentIndex, ast.sourceSpan);
      }
  }
  class BoundElementOrDirectiveProperty {
      constructor(name, expression, isLiteral, sourceSpan) {
          this.name = name;
          this.expression = expression;
          this.isLiteral = isLiteral;
          this.sourceSpan = sourceSpan;
      }
  }
  export function splitClasses(classAttrValue) {
      return StringWrapper.split(classAttrValue.trim(), /\s+/g);
  }
  class Component {
      constructor(ngContentIndexMatcher, wildcardNgContentIndex) {
          this.ngContentIndexMatcher = ngContentIndexMatcher;
          this.wildcardNgContentIndex = wildcardNgContentIndex;
      }
      static create(directives) {
          if (directives.length === 0 || !directives[0].directive.isComponent) {
              return EMPTY_COMPONENT;
          }
          var matcher = new SelectorMatcher();
          var ngContentSelectors = directives[0].directive.template.ngContentSelectors;
          var wildcardNgContentIndex = null;
          for (var i = 0; i < ngContentSelectors.length; i++) {
              var selector = ngContentSelectors[i];
              if (StringWrapper.equals(selector, '*')) {
                  wildcardNgContentIndex = i;
              }
              else {
                  matcher.addSelectables(CssSelector.parse(ngContentSelectors[i]), i);
              }
          }
          return new Component(matcher, wildcardNgContentIndex);
      }
      findNgContentIndex(selector) {
          var ngContentIndices = [];
          this.ngContentIndexMatcher.match(selector, (selector, ngContentIndex) => { ngContentIndices.push(ngContentIndex); });
          ListWrapper.sort(ngContentIndices);
          if (isPresent(this.wildcardNgContentIndex)) {
              ngContentIndices.push(this.wildcardNgContentIndex);
          }
          return ngContentIndices.length > 0 ? ngContentIndices[0] : null;
      }
  }
  function createElementCssSelector(elementName, matchableAttrs) {
      var cssSelector = new CssSelector();
      let elNameNoNs = splitNsName(elementName)[1];
      cssSelector.setElement(elNameNoNs);
      for (var i = 0; i < matchableAttrs.length; i++) {
          let attrName = matchableAttrs[i][0];
          let attrNameNoNs = splitNsName(attrName)[1];
          let attrValue = matchableAttrs[i][1];
          cssSelector.addAttribute(attrNameNoNs, attrValue);
          if (attrName.toLowerCase() == CLASS_ATTR) {
              var classes = splitClasses(attrValue);
              classes.forEach(className => cssSelector.addClassName(className));
          }
      }
      return cssSelector;
  }
  var EMPTY_COMPONENT = new Component(new SelectorMatcher(), null);
  var NON_BINDABLE_VISITOR = new NonBindableVisitor();
  export class PipeCollector extends RecursiveAstVisitor {
      constructor(...args) {
          super(...args);
          this.pipes = new Set();
      }
      visitPipe(ast) {
          this.pipes.add(ast.name);
          ast.exp.visit(this);
          this.visitAll(ast.args);
          return null;
      }
  }
  