UNPKG

angular2

Version:

Angular 2 - a web framework for modern web apps

320 lines (319 loc) 14 kB
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, HtmlExpansionAst, HtmlExpansionCaseAst } from './html_ast'; import { Injectable } from 'angular2/src/core/di'; import { HtmlToken, 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, parseExpansionForms = false) { var tokensAndErrors = tokenizeHtml(sourceContent, sourceUrl, parseExpansionForms); 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 if (this.peek.type === HtmlTokenType.EXPANSION_FORM_START) { this._consumeExpansion(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)); } _consumeExpansion(token) { let switchValue = this._advance(); let type = this._advance(); let cases = []; // read = while (this.peek.type === HtmlTokenType.EXPANSION_CASE_VALUE) { let expCase = this._parseExpansionCase(); if (isBlank(expCase)) return; // error cases.push(expCase); } // read the final } if (this.peek.type !== HtmlTokenType.EXPANSION_FORM_END) { this.errors.push(HtmlTreeError.create(null, this.peek.sourceSpan, `Invalid expansion form. Missing '}'.`)); return; } this._advance(); let mainSourceSpan = new ParseSourceSpan(token.sourceSpan.start, this.peek.sourceSpan.end); this._addToParent(new HtmlExpansionAst(switchValue.parts[0], type.parts[0], cases, mainSourceSpan, switchValue.sourceSpan)); } _parseExpansionCase() { let value = this._advance(); // read { if (this.peek.type !== HtmlTokenType.EXPANSION_CASE_EXP_START) { this.errors.push(HtmlTreeError.create(null, this.peek.sourceSpan, `Invalid expansion form. Missing '{'.,`)); return null; } // read until } let start = this._advance(); let exp = this._collectExpansionExpTokens(start); if (isBlank(exp)) return null; let end = this._advance(); exp.push(new HtmlToken(HtmlTokenType.EOF, [], end.sourceSpan)); // parse everything in between { and } let parsedExp = new TreeBuilder(exp).build(); if (parsedExp.errors.length > 0) { this.errors = this.errors.concat(parsedExp.errors); return null; } let sourceSpan = new ParseSourceSpan(value.sourceSpan.start, end.sourceSpan.end); let expSourceSpan = new ParseSourceSpan(start.sourceSpan.start, end.sourceSpan.end); return new HtmlExpansionCaseAst(value.parts[0], parsedExp.rootNodes, sourceSpan, value.sourceSpan, expSourceSpan); } _collectExpansionExpTokens(start) { let exp = []; let expansionFormStack = [HtmlTokenType.EXPANSION_CASE_EXP_START]; while (true) { if (this.peek.type === HtmlTokenType.EXPANSION_FORM_START || this.peek.type === HtmlTokenType.EXPANSION_CASE_EXP_START) { expansionFormStack.push(this.peek.type); } if (this.peek.type === HtmlTokenType.EXPANSION_CASE_EXP_END) { if (lastOnStack(expansionFormStack, HtmlTokenType.EXPANSION_CASE_EXP_START)) { expansionFormStack.pop(); if (expansionFormStack.length == 0) return exp; } else { this.errors.push(HtmlTreeError.create(null, start.sourceSpan, `Invalid expansion form. Missing '}'.`)); return null; } } if (this.peek.type === HtmlTokenType.EXPANSION_FORM_END) { if (lastOnStack(expansionFormStack, HtmlTokenType.EXPANSION_FORM_START)) { expansionFormStack.pop(); } else { this.errors.push(HtmlTreeError.create(null, start.sourceSpan, `Invalid expansion form. Missing '}'.`)); return null; } } if (this.peek.type === HtmlTokenType.EOF) { this.errors.push(HtmlTreeError.create(null, start.sourceSpan, `Invalid expansion form. Missing '}'.`)); return null; } exp.push(this._advance()); } } _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); } function lastOnStack(stack, element) { return stack.length > 0 && stack[stack.length - 1] === element; }