forked from angular/angular
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhtml_parser.ts
More file actions
135 lines (123 loc) · 4.2 KB
/
Copy pathhtml_parser.ts
File metadata and controls
135 lines (123 loc) · 4.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import {
isPresent,
StringWrapper,
stringify,
assertionsEnabled,
StringJoiner
} from 'angular2/src/core/facade/lang';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {
HtmlAst,
HtmlAttrAst,
HtmlTextAst,
HtmlElementAst,
HtmlAstVisitor,
htmlVisitAll
} from './html_ast';
import {escapeDoubleQuoteString} from './util';
import {Injectable} from 'angular2/src/core/di';
const NG_NON_BINDABLE = 'ng-non-bindable';
@Injectable()
export class HtmlParser {
parse(template: string, sourceInfo: string): HtmlAst[] {
var root = DOM.createTemplate(template);
return parseChildNodes(root, sourceInfo);
}
unparse(nodes: HtmlAst[]): string {
var visitor = new UnparseVisitor();
var parts = [];
htmlVisitAll(visitor, nodes, parts);
return parts.join('');
}
}
function parseText(text: Text, indexInParent: number, parentSourceInfo: string): HtmlTextAst {
// TODO(tbosch): add source row/column source info from parse5 / package:html
var value = DOM.getText(text);
return new HtmlTextAst(value,
`${parentSourceInfo} > #text(${value}):nth-child(${indexInParent})`);
}
function parseAttr(element: Element, parentSourceInfo: string, attrName: string, attrValue: string):
HtmlAttrAst {
// TODO(tbosch): add source row/column source info from parse5 / package:html
return new HtmlAttrAst(attrName, attrValue, `${parentSourceInfo}[${attrName}=${attrValue}]`);
}
function parseElement(element: Element, indexInParent: number, parentSourceInfo: string):
HtmlElementAst {
// normalize nodename always as lower case so that following build steps
// can rely on this
var nodeName = DOM.nodeName(element).toLowerCase();
// TODO(tbosch): add source row/column source info from parse5 / package:html
var sourceInfo = `${parentSourceInfo} > ${nodeName}:nth-child(${indexInParent})`;
var attrs = parseAttrs(element, sourceInfo);
var childNodes;
if (ignoreChildren(attrs)) {
childNodes = [];
} else {
childNodes = parseChildNodes(element, sourceInfo);
}
return new HtmlElementAst(nodeName, attrs, childNodes, sourceInfo);
}
function parseAttrs(element: Element, elementSourceInfo: string): HtmlAttrAst[] {
// Note: sort the attributes early in the pipeline to get
// consistent results throughout the pipeline, as attribute order is not defined
// in DOM parsers!
var attrMap = DOM.attributeMap(element);
var attrList: string[][] = [];
attrMap.forEach((value, name) => attrList.push([name, value]));
attrList.sort((entry1, entry2) => StringWrapper.compare(entry1[0], entry2[0]));
return attrList.map(entry => parseAttr(element, elementSourceInfo, entry[0], entry[1]));
}
function parseChildNodes(element: Element, parentSourceInfo: string): HtmlAst[] {
var root = DOM.templateAwareRoot(element);
var childNodes = DOM.childNodesAsList(root);
var result = [];
var index = 0;
childNodes.forEach(childNode => {
var childResult = null;
if (DOM.isTextNode(childNode)) {
var text = <Text>childNode;
childResult = parseText(text, index, parentSourceInfo);
} else if (DOM.isElementNode(childNode)) {
var el = <Element>childNode;
childResult = parseElement(el, index, parentSourceInfo);
}
if (isPresent(childResult)) {
// Won't have a childResult for e.g. comment nodes
result.push(childResult);
}
index++;
});
return result;
}
function ignoreChildren(attrs: HtmlAttrAst[]): boolean {
for (var i = 0; i < attrs.length; i++) {
var a = attrs[i];
if (a.name == NG_NON_BINDABLE) {
return true;
}
}
return false;
}
class UnparseVisitor implements HtmlAstVisitor {
visitElement(ast: HtmlElementAst, parts: string[]): any {
parts.push(`<${ast.name}`);
var attrs = [];
htmlVisitAll(this, ast.attrs, attrs);
if (ast.attrs.length > 0) {
parts.push(' ');
parts.push(attrs.join(' '));
}
parts.push(`>`);
htmlVisitAll(this, ast.children, parts);
parts.push(`</${ast.name}>`);
return null;
}
visitAttr(ast: HtmlAttrAst, parts: string[]): any {
parts.push(`${ast.name}=${escapeDoubleQuoteString(ast.value)}`);
return null;
}
visitText(ast: HtmlTextAst, parts: string[]): any {
parts.push(ast.value);
return null;
}
}