parse5 0.6.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.npmignore +3 -1
- package/LICENSE +1 -1
- package/README.md +166 -77
- package/index.js +7 -0
- package/lib/{html.js → const/html.js} +0 -0
- package/lib/{unicode.js → const/unicode.js} +0 -0
- package/lib/{named_entity_trie.js → tokenization_stage/named_entity_trie.js} +1 -1
- package/lib/tokenization_stage/parser_simulator.js +2 -0
- package/lib/{preprocessor.js → tokenization_stage/preprocessor.js} +5 -5
- package/lib/{tokenizer.js → tokenization_stage/tokenizer.js} +37 -37
- package/lib/{default_tree_adapter.js → tree_adapters/default.js} +47 -0
- package/lib/tree_adapters/htmlparser2.js +267 -0
- package/lib/{formatting_element_list.js → tree_construction_stage/formatting_element_list.js} +1 -1
- package/lib/{open_element_stack.js → tree_construction_stage/open_element_stack.js} +2 -2
- package/lib/{parser.js → tree_construction_stage/parser.js} +8 -8
- package/lib/tree_construction_stage/tree_serializer.js +165 -0
- package/package.json +33 -26
- package/test/data/tokenizer/contentModelFlags.test +0 -75
- package/test/data/tokenizer/domjs.test +0 -90
- package/test/data/tokenizer/entities.test +0 -283
- package/test/data/tokenizer/escapeFlag.test +0 -33
- package/test/data/tokenizer/namedEntities.test +0 -44189
- package/test/data/tokenizer/numericEntities.test +0 -1313
- package/test/data/tokenizer/pendingSpecChanges.test +0 -7
- package/test/data/tokenizer/test1.test +0 -196
- package/test/data/tokenizer/test2.test +0 -179
- package/test/data/tokenizer/test3.test +0 -6047
- package/test/data/tokenizer/test4.test +0 -344
- package/test/data/tokenizer/unicodeChars.test +0 -1295
- package/test/data/tokenizer/unicodeCharsProblematic.test +0 -27
- package/test/data/tree-construction/adoption01.dat +0 -275
- package/test/data/tree-construction/adoption02.dat +0 -31
- package/test/data/tree-construction/comments01.dat +0 -146
- package/test/data/tree-construction/doctype01.dat +0 -370
- package/test/data/tree-construction/domjs-unsafe.dat +0 -0
- package/test/data/tree-construction/entities01.dat +0 -603
- package/test/data/tree-construction/entities02.dat +0 -249
- package/test/data/tree-construction/html5test-com.dat +0 -246
- package/test/data/tree-construction/inbody01.dat +0 -43
- package/test/data/tree-construction/isindex.dat +0 -40
- package/test/data/tree-construction/main-element.dat +0 -29
- package/test/data/tree-construction/pending-spec-changes-plain-text-unsafe.dat +0 -0
- package/test/data/tree-construction/pending-spec-changes.dat +0 -52
- package/test/data/tree-construction/plain-text-unsafe.dat +0 -0
- package/test/data/tree-construction/scriptdata01.dat +0 -308
- package/test/data/tree-construction/tables01.dat +0 -212
- package/test/data/tree-construction/tests1.dat +0 -1952
- package/test/data/tree-construction/tests10.dat +0 -799
- package/test/data/tree-construction/tests11.dat +0 -482
- package/test/data/tree-construction/tests12.dat +0 -62
- package/test/data/tree-construction/tests14.dat +0 -74
- package/test/data/tree-construction/tests15.dat +0 -208
- package/test/data/tree-construction/tests16.dat +0 -2299
- package/test/data/tree-construction/tests17.dat +0 -153
- package/test/data/tree-construction/tests18.dat +0 -269
- package/test/data/tree-construction/tests19.dat +0 -1249
- package/test/data/tree-construction/tests2.dat +0 -763
- package/test/data/tree-construction/tests20.dat +0 -455
- package/test/data/tree-construction/tests21.dat +0 -257
- package/test/data/tree-construction/tests22.dat +0 -157
- package/test/data/tree-construction/tests23.dat +0 -155
- package/test/data/tree-construction/tests24.dat +0 -79
- package/test/data/tree-construction/tests25.dat +0 -219
- package/test/data/tree-construction/tests26.dat +0 -313
- package/test/data/tree-construction/tests3.dat +0 -305
- package/test/data/tree-construction/tests4.dat +0 -59
- package/test/data/tree-construction/tests5.dat +0 -191
- package/test/data/tree-construction/tests6.dat +0 -663
- package/test/data/tree-construction/tests7.dat +0 -390
- package/test/data/tree-construction/tests8.dat +0 -148
- package/test/data/tree-construction/tests9.dat +0 -457
- package/test/data/tree-construction/tests_innerHTML_1.dat +0 -741
- package/test/data/tree-construction/tricky01.dat +0 -261
- package/test/data/tree-construction/webkit01.dat +0 -594
- package/test/data/tree-construction/webkit02.dat +0 -94
- package/test/fixtures/formatting_element_list_test.js +0 -267
- package/test/fixtures/open_element_stack_test.js +0 -495
- package/test/fixtures/parser_test.js +0 -160
- package/test/fixtures/tokenizer_test.js +0 -178
- package/test/run_tests.js +0 -10
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//Node construction
|
|
1
2
|
exports.createDocument = function () {
|
|
2
3
|
return {
|
|
3
4
|
nodeName: '#document',
|
|
@@ -41,6 +42,8 @@ var createTextNode = function (value) {
|
|
|
41
42
|
}
|
|
42
43
|
};
|
|
43
44
|
|
|
45
|
+
|
|
46
|
+
//Tree mutation
|
|
44
47
|
exports.setDocumentType = function (document, name, publicId, systemId) {
|
|
45
48
|
var doctypeNode = null;
|
|
46
49
|
|
|
@@ -130,10 +133,16 @@ exports.adoptAttributes = function (recipientNode, attrs) {
|
|
|
130
133
|
}
|
|
131
134
|
};
|
|
132
135
|
|
|
136
|
+
|
|
137
|
+
//Tree traversing
|
|
133
138
|
exports.getFirstChild = function (node) {
|
|
134
139
|
return node.childNodes[0];
|
|
135
140
|
};
|
|
136
141
|
|
|
142
|
+
exports.getChildNodes = function (node) {
|
|
143
|
+
return node.childNodes;
|
|
144
|
+
};
|
|
145
|
+
|
|
137
146
|
exports.getParentNode = function (node) {
|
|
138
147
|
return node.parentNode;
|
|
139
148
|
};
|
|
@@ -142,10 +151,48 @@ exports.getAttrList = function (node) {
|
|
|
142
151
|
return node.attrs;
|
|
143
152
|
};
|
|
144
153
|
|
|
154
|
+
//Node data
|
|
145
155
|
exports.getTagName = function (element) {
|
|
146
156
|
return element.tagName;
|
|
147
157
|
};
|
|
148
158
|
|
|
149
159
|
exports.getNamespaceURI = function (element) {
|
|
150
160
|
return element.namespaceURI;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
exports.getTextNodeContent = function (textNode) {
|
|
164
|
+
return textNode.value;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
exports.getCommentNodeContent = function (commentNode) {
|
|
168
|
+
return commentNode.data;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
exports.getDocumentTypeNodeName = function (doctypeNode) {
|
|
172
|
+
return doctypeNode.name;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
exports.getDocumentTypeNodePublicId = function (doctypeNode) {
|
|
176
|
+
return doctypeNode.publicId;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
exports.getDocumentTypeNodeSystemId = function (doctypeNode) {
|
|
180
|
+
return doctypeNode.systemId;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
//Node types
|
|
184
|
+
exports.isTextNode = function (node) {
|
|
185
|
+
return node.nodeName === '#text';
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
exports.isCommentNode = function (node) {
|
|
189
|
+
return node.nodeName === '#comment';
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
exports.isDocumentTypeNode = function (node) {
|
|
193
|
+
return node.nodeName === '#documentType';
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
exports.isElementNode = function (node) {
|
|
197
|
+
return !!node.tagName;
|
|
151
198
|
};
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
//Node construction
|
|
2
|
+
exports.createDocument =
|
|
3
|
+
exports.createDocumentFragment = function () {
|
|
4
|
+
return {
|
|
5
|
+
type: 'root',
|
|
6
|
+
name: 'root',
|
|
7
|
+
parent: null,
|
|
8
|
+
prev: null,
|
|
9
|
+
next: null,
|
|
10
|
+
children: []
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
exports.createElement = function (tagName, namespaceURI, attrs) {
|
|
15
|
+
var attribs = {},
|
|
16
|
+
attribsNamespace = {},
|
|
17
|
+
attribsPrefix = {};
|
|
18
|
+
|
|
19
|
+
for (var i = 0; i < attrs.length; i++) {
|
|
20
|
+
var attrName = attrs[i].name;
|
|
21
|
+
|
|
22
|
+
attribs[attrName] = attrs[i].value;
|
|
23
|
+
attribsNamespace[attrName] = attrs[i].namespace;
|
|
24
|
+
attribsPrefix[attrName] = attrs[i].prefix;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
type: tagName === 'script' || tagName === 'style' ? tagName : 'tag',
|
|
29
|
+
name: tagName,
|
|
30
|
+
namespace: namespaceURI,
|
|
31
|
+
attribs: attribs,
|
|
32
|
+
'x-attribsNamespace': attribsNamespace,
|
|
33
|
+
'x-attribsPrefix': attribsPrefix,
|
|
34
|
+
children: [],
|
|
35
|
+
parent: null,
|
|
36
|
+
prev: null,
|
|
37
|
+
next: null
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
exports.createCommentNode = function (data) {
|
|
42
|
+
return {
|
|
43
|
+
type: 'comment',
|
|
44
|
+
data: data,
|
|
45
|
+
parent: null,
|
|
46
|
+
prev: null,
|
|
47
|
+
next: null
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
var createTextNode = function (value) {
|
|
52
|
+
return {
|
|
53
|
+
type: 'text',
|
|
54
|
+
data: value,
|
|
55
|
+
parent: null,
|
|
56
|
+
prev: null,
|
|
57
|
+
next: null
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
//Tree mutation
|
|
63
|
+
exports.setDocumentType = function (document, name, publicId, systemId) {
|
|
64
|
+
var data = '!DOCTYPE';
|
|
65
|
+
|
|
66
|
+
if (name)
|
|
67
|
+
data += ' ' + name;
|
|
68
|
+
|
|
69
|
+
if (publicId)
|
|
70
|
+
data += ' PUBLIC "' + publicId + '"';
|
|
71
|
+
|
|
72
|
+
if (systemId)
|
|
73
|
+
data += ' "' + systemId + '"';
|
|
74
|
+
|
|
75
|
+
var doctypeNode = null;
|
|
76
|
+
|
|
77
|
+
for (var i = 0; i < document.children.length; i++) {
|
|
78
|
+
if (document.children[i].type === 'directive' && document.children[i].name === '!doctype') {
|
|
79
|
+
doctypeNode = document.children[i];
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (doctypeNode) {
|
|
85
|
+
doctypeNode.data = data;
|
|
86
|
+
doctypeNode['x-name'] = name;
|
|
87
|
+
doctypeNode['x-publicId'] = publicId;
|
|
88
|
+
doctypeNode['x-systemId'] = systemId;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
else {
|
|
92
|
+
appendChild(document, {
|
|
93
|
+
type: 'directive',
|
|
94
|
+
name: '!doctype',
|
|
95
|
+
data: data,
|
|
96
|
+
'x-name': name,
|
|
97
|
+
'x-publicId': publicId,
|
|
98
|
+
'x-systemId': systemId
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
exports.setQuirksMode = function (document) {
|
|
105
|
+
document.quirksMode = true;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
exports.isQuirksMode = function (document) {
|
|
109
|
+
return document.quirksMode;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
var appendChild = exports.appendChild = function (parentNode, newNode) {
|
|
113
|
+
var prev = parentNode.children[parentNode.children.length - 1];
|
|
114
|
+
|
|
115
|
+
if (prev) {
|
|
116
|
+
prev.next = newNode;
|
|
117
|
+
newNode.prev = prev;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
parentNode.children.push(newNode);
|
|
121
|
+
newNode.parent = parentNode;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
var insertBefore = exports.insertBefore = function (parentNode, newNode, referenceNode) {
|
|
125
|
+
var insertionIdx = parentNode.children.indexOf(referenceNode),
|
|
126
|
+
prev = referenceNode.prev;
|
|
127
|
+
|
|
128
|
+
if (prev) {
|
|
129
|
+
prev.next = newNode;
|
|
130
|
+
newNode.prev = prev;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
referenceNode.prev = newNode;
|
|
134
|
+
newNode.next = referenceNode;
|
|
135
|
+
|
|
136
|
+
parentNode.children.splice(insertionIdx, 0, newNode);
|
|
137
|
+
newNode.parent = parentNode;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
exports.detachNode = function (node) {
|
|
141
|
+
if (node.parent) {
|
|
142
|
+
var idx = node.parent.children.indexOf(node),
|
|
143
|
+
prev = node.prev,
|
|
144
|
+
next = node.next;
|
|
145
|
+
|
|
146
|
+
node.prev = null;
|
|
147
|
+
node.next = null;
|
|
148
|
+
|
|
149
|
+
if (prev)
|
|
150
|
+
prev.next = next;
|
|
151
|
+
|
|
152
|
+
if (next)
|
|
153
|
+
next.prev = prev;
|
|
154
|
+
|
|
155
|
+
node.parent.children.splice(idx, 1);
|
|
156
|
+
node.parent = null;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
exports.insertText = function (parentNode, text) {
|
|
161
|
+
var lastChild = parentNode.children[parentNode.children.length - 1];
|
|
162
|
+
|
|
163
|
+
if (lastChild && lastChild.type === 'text')
|
|
164
|
+
lastChild.data += text;
|
|
165
|
+
else
|
|
166
|
+
appendChild(parentNode, createTextNode(text));
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
exports.insertTextBefore = function (parentNode, text, referenceNode) {
|
|
170
|
+
var prevNode = parentNode.children[parentNode.children.indexOf(referenceNode) - 1];
|
|
171
|
+
|
|
172
|
+
if (prevNode && prevNode.type === 'text')
|
|
173
|
+
prevNode.data += text;
|
|
174
|
+
else
|
|
175
|
+
insertBefore(parentNode, createTextNode(text), referenceNode);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
exports.adoptAttributes = function (recipientNode, attrs) {
|
|
179
|
+
for (var i = 0; i < attrs.length; i++) {
|
|
180
|
+
var attrName = attrs[i].name;
|
|
181
|
+
|
|
182
|
+
if (typeof recipientNode.attribs[attrName] === 'undefined') {
|
|
183
|
+
recipientNode.attribs[attrName] = attrs[i].value;
|
|
184
|
+
recipientNode['x-attribsNamespace'][attrName] = attrs[i].namespace;
|
|
185
|
+
recipientNode['x-attribsPrefix'][attrName] = attrs[i].prefix;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
//Tree traversing
|
|
192
|
+
exports.getFirstChild = function (node) {
|
|
193
|
+
return node.children[0];
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
exports.getChildNodes = function (node) {
|
|
197
|
+
return node.children;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
exports.getParentNode = function (node) {
|
|
201
|
+
return node.parent;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
exports.getAttrList = function (node) {
|
|
205
|
+
var attrList = [];
|
|
206
|
+
|
|
207
|
+
for (var name in node.attribs) {
|
|
208
|
+
if (node.attribs.hasOwnProperty(name)) {
|
|
209
|
+
attrList.push({
|
|
210
|
+
name: name,
|
|
211
|
+
value: node.attribs[name],
|
|
212
|
+
namespace: node['x-attribsNamespace'][name],
|
|
213
|
+
prefix: node['x-attribsPrefix'][name]
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return attrList;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
//Node data
|
|
223
|
+
exports.getTagName = function (element) {
|
|
224
|
+
return element.name;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
exports.getNamespaceURI = function (element) {
|
|
228
|
+
return element.namespace;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
exports.getTextNodeContent = function (textNode) {
|
|
232
|
+
return textNode.data;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
exports.getCommentNodeContent = function (commentNode) {
|
|
236
|
+
return commentNode.data;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
exports.getDocumentTypeNodeName = function (doctypeNode) {
|
|
240
|
+
return doctypeNode['x-name'];
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
exports.getDocumentTypeNodePublicId = function (doctypeNode) {
|
|
244
|
+
return doctypeNode['x-publicId'];
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
exports.getDocumentTypeNodeSystemId = function (doctypeNode) {
|
|
248
|
+
return doctypeNode['x-systemId'];
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
//Node types
|
|
253
|
+
exports.isTextNode = function (node) {
|
|
254
|
+
return node.type === 'text';
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
exports.isCommentNode = function (node) {
|
|
258
|
+
return node.type === 'comment';
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
exports.isDocumentTypeNode = function (node) {
|
|
262
|
+
return node.type === 'directive' && node.name === '!doctype';
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
exports.isElementNode = function (node) {
|
|
266
|
+
return !!node.attribs;
|
|
267
|
+
};
|
package/lib/{formatting_element_list.js → tree_construction_stage/formatting_element_list.js}
RENAMED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
var NOAH_ARK_CAPACITY = 3;
|
|
3
3
|
|
|
4
4
|
//List of formatting elements
|
|
5
|
-
var FormattingElementList = exports
|
|
5
|
+
var FormattingElementList = module.exports = function (treeAdapter) {
|
|
6
6
|
this.length = 0;
|
|
7
7
|
this.entries = [];
|
|
8
8
|
this.treeAdapter = treeAdapter;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var HTML = require('
|
|
1
|
+
var HTML = require('../const/html');
|
|
2
2
|
|
|
3
3
|
//Aliases
|
|
4
4
|
var $ = HTML.TAG_NAMES,
|
|
@@ -75,7 +75,7 @@ function isScopingElement(tn, ns) {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
//Stack of open elements
|
|
78
|
-
var OpenElementStack = exports
|
|
78
|
+
var OpenElementStack = module.exports = function (document, treeAdapter) {
|
|
79
79
|
this.stackTop = -1;
|
|
80
80
|
this.items = [];
|
|
81
81
|
this.current = document;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
var Tokenizer = require('
|
|
2
|
-
OpenElementStack = require('./open_element_stack')
|
|
3
|
-
FormattingElementList = require('./formatting_element_list')
|
|
4
|
-
defaultTreeAdapter = require('
|
|
5
|
-
|
|
6
|
-
HTML = require('
|
|
1
|
+
var Tokenizer = require('../tokenization_stage/tokenizer'),
|
|
2
|
+
OpenElementStack = require('./open_element_stack'),
|
|
3
|
+
FormattingElementList = require('./formatting_element_list'),
|
|
4
|
+
defaultTreeAdapter = require('../tree_adapters/default'),
|
|
5
|
+
UNICODE = require('../const/unicode'),
|
|
6
|
+
HTML = require('../const/html');
|
|
7
7
|
|
|
8
8
|
//Aliases
|
|
9
9
|
var $ = HTML.TAG_NAMES,
|
|
@@ -757,7 +757,7 @@ function getSearchableIndexInputAttrs(isindexStartTagToken) {
|
|
|
757
757
|
}
|
|
758
758
|
|
|
759
759
|
//Parser
|
|
760
|
-
var Parser = exports
|
|
760
|
+
var Parser = module.exports = function (treeAdapter) {
|
|
761
761
|
this.treeAdapter = treeAdapter || defaultTreeAdapter;
|
|
762
762
|
};
|
|
763
763
|
|
|
@@ -2971,7 +2971,7 @@ function startTagAfterAfterFrameset(p, token) {
|
|
|
2971
2971
|
//12.2.5.5 The rules for parsing tokens in foreign content
|
|
2972
2972
|
//------------------------------------------------------------------
|
|
2973
2973
|
function nullCharacterInForeignContent(p, token) {
|
|
2974
|
-
token.chars =
|
|
2974
|
+
token.chars = UNICODE.REPLACEMENT_CHARACTER;
|
|
2975
2975
|
p._insertCharacters(token);
|
|
2976
2976
|
}
|
|
2977
2977
|
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
var defaultTreeAdapter = require('../tree_adapters/default'),
|
|
2
|
+
HTML = require('../const/html');
|
|
3
|
+
|
|
4
|
+
//Aliases
|
|
5
|
+
var $ = HTML.TAG_NAMES,
|
|
6
|
+
NS = HTML.NAMESPACES;
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
//Escaping regexes
|
|
10
|
+
var AMP_REGEX = /&/g,
|
|
11
|
+
NBSP_REGEX = /\u00a0/g,
|
|
12
|
+
DOUBLE_QUOTE_REGEX = /"/g,
|
|
13
|
+
LT_REGEX = /</g,
|
|
14
|
+
GT_REGEX = />/g;
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
//Escape string
|
|
18
|
+
function escapeString(str, attrMode) {
|
|
19
|
+
str = str
|
|
20
|
+
.replace(AMP_REGEX, '&')
|
|
21
|
+
.replace(NBSP_REGEX, ' ');
|
|
22
|
+
|
|
23
|
+
if (attrMode)
|
|
24
|
+
str = str.replace(DOUBLE_QUOTE_REGEX, '"');
|
|
25
|
+
|
|
26
|
+
else {
|
|
27
|
+
str = str
|
|
28
|
+
.replace(LT_REGEX, '<')
|
|
29
|
+
.replace(GT_REGEX, '>');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return str;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
//TreeSerializer
|
|
37
|
+
var TreeSerializer = module.exports = function (treeAdapter) {
|
|
38
|
+
this.treeAdapter = treeAdapter || defaultTreeAdapter;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
//API
|
|
43
|
+
TreeSerializer.prototype.serialize = function (node) {
|
|
44
|
+
this.html = '';
|
|
45
|
+
this._serializeChildNodes(node);
|
|
46
|
+
|
|
47
|
+
return this.html;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
//Internals
|
|
52
|
+
TreeSerializer.prototype._serializeChildNodes = function (parentNode) {
|
|
53
|
+
var childNodes = this.treeAdapter.getChildNodes(parentNode);
|
|
54
|
+
|
|
55
|
+
if (childNodes) {
|
|
56
|
+
for (var i = 0, cnLength = childNodes.length; i < cnLength; i++) {
|
|
57
|
+
var currentNode = childNodes[i];
|
|
58
|
+
|
|
59
|
+
if (this.treeAdapter.isElementNode(currentNode))
|
|
60
|
+
this._serializeElement(currentNode);
|
|
61
|
+
|
|
62
|
+
else if (this.treeAdapter.isTextNode(currentNode))
|
|
63
|
+
this._serializeTextNode(currentNode);
|
|
64
|
+
|
|
65
|
+
else if (this.treeAdapter.isCommentNode(currentNode))
|
|
66
|
+
this._serializeCommentNode(currentNode);
|
|
67
|
+
|
|
68
|
+
else if (this.treeAdapter.isDocumentTypeNode(currentNode))
|
|
69
|
+
this._serializeDocumentTypeNode(currentNode);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
TreeSerializer.prototype._serializeElement = function (node) {
|
|
75
|
+
var tn = this.treeAdapter.getTagName(node),
|
|
76
|
+
ns = this.treeAdapter.getNamespaceURI(node),
|
|
77
|
+
qualifiedTn = (ns === NS.HTML || ns === NS.SVG || ns === NS.MATHML) ? tn : (ns + ':' + tn);
|
|
78
|
+
|
|
79
|
+
this.html += '<' + qualifiedTn;
|
|
80
|
+
this._serializeAttributes(node);
|
|
81
|
+
this.html += '>';
|
|
82
|
+
|
|
83
|
+
if (tn !== $.AREA && tn !== $.BASE && tn !== $.BASEFONT && tn !== $.BGSOUND && tn !== $.BR && tn !== $.BR &&
|
|
84
|
+
tn !== $.COL && tn !== $.EMBED && tn !== $.FRAME && tn !== $.HR && tn !== $.IMG && tn !== $.INPUT &&
|
|
85
|
+
tn !== $.KEYGEN && tn !== $.LINK && tn !== $.MENUITEM && tn !== $.META && tn !== $.PARAM && tn !== $.SOURCE &&
|
|
86
|
+
tn !== $.TRACK && tn !== $.WBR) {
|
|
87
|
+
|
|
88
|
+
if (tn === $.PRE || tn === $.TEXTAREA || tn === $.LISTING) {
|
|
89
|
+
var firstChild = this.treeAdapter.getFirstChild(node);
|
|
90
|
+
|
|
91
|
+
if (firstChild && this.treeAdapter.isTextNode(firstChild))
|
|
92
|
+
this.html += '\n';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this._serializeChildNodes(node);
|
|
96
|
+
this.html += '</' + qualifiedTn + '>';
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
TreeSerializer.prototype._serializeAttributes = function (node) {
|
|
101
|
+
var attrs = this.treeAdapter.getAttrList(node);
|
|
102
|
+
|
|
103
|
+
for (var i = 0, attrsLength = attrs.length; i < attrsLength; i++) {
|
|
104
|
+
var attr = attrs[i];
|
|
105
|
+
|
|
106
|
+
this.html += ' ';
|
|
107
|
+
|
|
108
|
+
if (!attr.namespace)
|
|
109
|
+
this.html += attr.name;
|
|
110
|
+
|
|
111
|
+
else if (attr.namespace === NS.XML)
|
|
112
|
+
this.html += 'xml:' + attr.name;
|
|
113
|
+
|
|
114
|
+
else if (attr.namespace === NS.XMLNS) {
|
|
115
|
+
if (attr.name !== 'xmlns')
|
|
116
|
+
this.html += 'xmlns:';
|
|
117
|
+
|
|
118
|
+
this.html += attr.name;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
else if (attr.namespace === NS.XLINK)
|
|
122
|
+
this.html += 'xlink:' + attr.name;
|
|
123
|
+
|
|
124
|
+
else
|
|
125
|
+
this.html += attr.namespace + ':' + attr.name;
|
|
126
|
+
|
|
127
|
+
this.html += '="' + escapeString(attr.value, true) + '"';
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
TreeSerializer.prototype._serializeTextNode = function (node) {
|
|
132
|
+
var parent = this.treeAdapter.getParentNode(node),
|
|
133
|
+
parentTn = parent && this.treeAdapter.getTagName(parent),
|
|
134
|
+
content = this.treeAdapter.getTextNodeContent(node);
|
|
135
|
+
|
|
136
|
+
if (parentTn === $.STYLE || parentTn === $.SCRIPT || parentTn === $.XMP || parentTn === $.IFRAME ||
|
|
137
|
+
parentTn === $.NOEMBED || parentTn === $.NOFRAMES || parentTn === $.PLAINTEXT || parentTn === $.NOSCRIPT) {
|
|
138
|
+
this.html += content;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
else
|
|
142
|
+
this.html += escapeString(content, false);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
TreeSerializer.prototype._serializeCommentNode = function (node) {
|
|
146
|
+
this.html += '<!--' + this.treeAdapter.getCommentNodeContent(node) + '-->';
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
TreeSerializer.prototype._serializeDocumentTypeNode = function (node) {
|
|
150
|
+
var name = this.treeAdapter.getDocumentTypeNodeName(node),
|
|
151
|
+
publicId = this.treeAdapter.getDocumentTypeNodePublicId(node),
|
|
152
|
+
systemId = this.treeAdapter.getDocumentTypeNodeSystemId(node);
|
|
153
|
+
|
|
154
|
+
this.html += '<!DOCTYPE ' + name;
|
|
155
|
+
|
|
156
|
+
if (publicId !== null)
|
|
157
|
+
this.html += ' PUBLIC "' + publicId + '"';
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
if (systemId !== null)
|
|
161
|
+
this.html += ' "' + systemId + '"';
|
|
162
|
+
|
|
163
|
+
this.html += '>';
|
|
164
|
+
};
|
|
165
|
+
|
package/package.json
CHANGED
|
@@ -1,28 +1,35 @@
|
|
|
1
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
|
-
|
|
2
|
+
"name": "parse5",
|
|
3
|
+
"description": "Fast full-featured HTML parser for Node. Based on WHATWG HTML5 specification.",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"author": "Ivan Nikulin (ifaaan@gmail.com, https://github.com/inikulin)",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"html",
|
|
8
|
+
"parser",
|
|
9
|
+
"html5",
|
|
10
|
+
"WHATWG",
|
|
11
|
+
"specification",
|
|
12
|
+
"fast",
|
|
13
|
+
"html parser",
|
|
14
|
+
"html5 parser",
|
|
15
|
+
"htmlparser",
|
|
16
|
+
"parse5",
|
|
17
|
+
"serializer",
|
|
18
|
+
"html serializer",
|
|
19
|
+
"htmlserializer"
|
|
20
|
+
],
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git://github.com/inikulin/parse5.git"
|
|
24
|
+
},
|
|
25
|
+
"main": "./index.js",
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"nodeunit": "0.8.0"
|
|
28
|
+
},
|
|
29
|
+
"licenses": [
|
|
30
|
+
{
|
|
31
|
+
"type": "MIT",
|
|
32
|
+
"url": "https://raw.github.com/inikulin/parse5/master/LICENSE"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
28
35
|
}
|