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);
  };
  import { isPresent, isBlank } from 'angular2/src/facade/lang';
  import { ListWrapper } from 'angular2/src/facade/collection';
  import { HtmlAttrAst, HtmlTextAst, HtmlCommentAst, HtmlElementAst } from './html_ast';
  import { Injectable } from 'angular2/src/core/di';
  import { HtmlTokenType, tokenizeHtml } from './html_lexer';
  import { ParseError, ParseSourceSpan } from './parse_util';
  import { getHtmlTagDefinition, getNsPrefix, mergeNsAndName } from './html_tags';
  export class HtmlTreeError extends ParseError {
      constructor(elementName, span, msg) {
          super(span, msg);
          this.elementName = elementName;
      }
      static create(elementName, span, msg) {
          return new HtmlTreeError(elementName, span, msg);
      }
  }
  export class HtmlParseTreeResult {
      constructor(rootNodes, errors) {
          this.rootNodes = rootNodes;
          this.errors = errors;
      }
  }
  export let HtmlParser = class HtmlParser {
      parse(sourceContent, sourceUrl) {
          var tokensAndErrors = tokenizeHtml(sourceContent, sourceUrl);
          var treeAndErrors = new TreeBuilder(tokensAndErrors.tokens).build();
          return new HtmlParseTreeResult(treeAndErrors.rootNodes, tokensAndErrors.errors
              .concat(treeAndErrors.errors));
      }
  };
  HtmlParser = __decorate([
      Injectable(), 
      __metadata('design:paramtypes', [])
  ], HtmlParser);
  class TreeBuilder {
      constructor(tokens) {
          this.tokens = tokens;
          this.index = -1;
          this.rootNodes = [];
          this.errors = [];
          this.elementStack = [];
          this._advance();
      }
      build() {
          while (this.peek.type !== HtmlTokenType.EOF) {
              if (this.peek.type === HtmlTokenType.TAG_OPEN_START) {
                  this._consumeStartTag(this._advance());
              }
              else if (this.peek.type === HtmlTokenType.TAG_CLOSE) {
                  this._consumeEndTag(this._advance());
              }
              else if (this.peek.type === HtmlTokenType.CDATA_START) {
                  this._closeVoidElement();
                  this._consumeCdata(this._advance());
              }
              else if (this.peek.type === HtmlTokenType.COMMENT_START) {
                  this._closeVoidElement();
                  this._consumeComment(this._advance());
              }
              else if (this.peek.type === HtmlTokenType.TEXT ||
                  this.peek.type === HtmlTokenType.RAW_TEXT ||
                  this.peek.type === HtmlTokenType.ESCAPABLE_RAW_TEXT) {
                  this._closeVoidElement();
                  this._consumeText(this._advance());
              }
              else {
                  // Skip all other tokens...
                  this._advance();
              }
          }
          return new HtmlParseTreeResult(this.rootNodes, this.errors);
      }
      _advance() {
          var 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(HtmlTokenType.CDATA_END);
      }
      _consumeComment(token) {
          var text = this._advanceIf(HtmlTokenType.RAW_TEXT);
          this._advanceIf(HtmlTokenType.COMMENT_END);
          var value = isPresent(text) ? text.parts[0].trim() : null;
          this._addToParent(new HtmlCommentAst(value, token.sourceSpan));
      }
      _consumeText(token) {
          let text = token.parts[0];
          if (text.length > 0 && text[0] == '\n') {
              let parent = this._getParentElement();
              if (isPresent(parent) && parent.children.length == 0 &&
                  getHtmlTagDefinition(parent.name).ignoreFirstLf) {
                  text = text.substring(1);
              }
          }
          if (text.length > 0) {
              this._addToParent(new HtmlTextAst(text, token.sourceSpan));
          }
      }
      _closeVoidElement() {
          if (this.elementStack.length > 0) {
              let el = ListWrapper.last(this.elementStack);
              if (getHtmlTagDefinition(el.name).isVoid) {
                  this.elementStack.pop();
              }
          }
      }
      _consumeStartTag(startTagToken) {
          var prefix = startTagToken.parts[0];
          var name = startTagToken.parts[1];
          var attrs = [];
          while (this.peek.type === HtmlTokenType.ATTR_NAME) {
              attrs.push(this._consumeAttr(this._advance()));
          }
          var fullName = getElementFullName(prefix, name, this._getParentElement());
          var 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 === HtmlTokenType.TAG_OPEN_END_VOID) {
              this._advance();
              selfClosing = true;
              if (getNsPrefix(fullName) == null && !getHtmlTagDefinition(fullName).isVoid) {
                  this.errors.push(HtmlTreeError.create(fullName, startTagToken.sourceSpan, `Only void and foreign elements can be self closed "${startTagToken.parts[1]}"`));
              }
          }
          else if (this.peek.type === HtmlTokenType.TAG_OPEN_END) {
              this._advance();
              selfClosing = false;
          }
          var end = this.peek.sourceSpan.start;
          let span = new ParseSourceSpan(startTagToken.sourceSpan.start, end);
          var el = new HtmlElementAst(fullName, attrs, [], span, span, null);
          this._pushElement(el);
          if (selfClosing) {
              this._popElement(fullName);
              el.endSourceSpan = span;
          }
      }
      _pushElement(el) {
          if (this.elementStack.length > 0) {
              var parentEl = ListWrapper.last(this.elementStack);
              if (getHtmlTagDefinition(parentEl.name).isClosedByChild(el.name)) {
                  this.elementStack.pop();
              }
          }
          var tagDef = getHtmlTagDefinition(el.name);
          var parentEl = this._getParentElement();
          if (tagDef.requireExtraParent(isPresent(parentEl) ? parentEl.name : null)) {
              var newParent = new HtmlElementAst(tagDef.parentToAdd, [], [el], el.sourceSpan, el.startSourceSpan, el.endSourceSpan);
              this._addToParent(newParent);
              this.elementStack.push(newParent);
              this.elementStack.push(el);
          }
          else {
              this._addToParent(el);
              this.elementStack.push(el);
          }
      }
      _consumeEndTag(endTagToken) {
          var fullName = getElementFullName(endTagToken.parts[0], endTagToken.parts[1], this._getParentElement());
          this._getParentElement().endSourceSpan = endTagToken.sourceSpan;
          if (getHtmlTagDefinition(fullName).isVoid) {
              this.errors.push(HtmlTreeError.create(fullName, endTagToken.sourceSpan, `Void elements do not have end tags "${endTagToken.parts[1]}"`));
          }
          else if (!this._popElement(fullName)) {
              this.errors.push(HtmlTreeError.create(fullName, endTagToken.sourceSpan, `Unexpected closing tag "${endTagToken.parts[1]}"`));
          }
      }
      _popElement(fullName) {
          for (let stackIndex = this.elementStack.length - 1; stackIndex >= 0; stackIndex--) {
              let el = this.elementStack[stackIndex];
              if (el.name == fullName) {
                  ListWrapper.splice(this.elementStack, stackIndex, this.elementStack.length - stackIndex);
                  return true;
              }
              if (!getHtmlTagDefinition(el.name).closedByParent) {
                  return false;
              }
          }
          return false;
      }
      _consumeAttr(attrName) {
          var fullName = mergeNsAndName(attrName.parts[0], attrName.parts[1]);
          var end = attrName.sourceSpan.end;
          var value = '';
          if (this.peek.type === HtmlTokenType.ATTR_VALUE) {
              var valueToken = this._advance();
              value = valueToken.parts[0];
              end = valueToken.sourceSpan.end;
          }
          return new HtmlAttrAst(fullName, value, new ParseSourceSpan(attrName.sourceSpan.start, end));
      }
      _getParentElement() {
          return this.elementStack.length > 0 ? ListWrapper.last(this.elementStack) : null;
      }
      _addToParent(node) {
          var parent = this._getParentElement();
          if (isPresent(parent)) {
              parent.children.push(node);
          }
          else {
              this.rootNodes.push(node);
          }
      }
  }
  function getElementFullName(prefix, localName, parentElement) {
      if (isBlank(prefix)) {
          prefix = getHtmlTagDefinition(localName).implicitNamespacePrefix;
          if (isBlank(prefix) && isPresent(parentElement)) {
              prefix = getNsPrefix(parentElement.name);
          }
      }
      return mergeNsAndName(prefix, localName);
  }
  