parse5 2.2.3 → 3.0.3

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.
Files changed (33) hide show
  1. package/LICENSE +19 -19
  2. package/README.md +40 -40
  3. package/lib/common/doctype.js +156 -137
  4. package/lib/common/foreign_content.js +1 -1
  5. package/lib/common/html.js +272 -266
  6. package/lib/common/unicode.js +47 -47
  7. package/lib/extensions/location_info/open_element_stack_mixin.js +34 -0
  8. package/lib/extensions/location_info/parser_mixin.js +213 -0
  9. package/lib/extensions/location_info/tokenizer_mixin.js +117 -0
  10. package/lib/extensions/position_tracking/preprocessor_mixin.js +72 -0
  11. package/lib/index.d.ts +1077 -0
  12. package/lib/index.js +44 -108
  13. package/lib/parser/formatting_element_list.js +167 -167
  14. package/lib/parser/index.js +2819 -2817
  15. package/lib/parser/open_element_stack.js +395 -395
  16. package/lib/parser/parser_stream.js +76 -0
  17. package/lib/parser/plain_text_conversion_stream.js +20 -0
  18. package/lib/sax/dev_null_stream.js +14 -14
  19. package/lib/sax/index.js +7 -119
  20. package/lib/serializer/index.js +162 -180
  21. package/lib/serializer/{stream.js → serializer_stream.js} +28 -49
  22. package/lib/tokenizer/index.js +65 -22
  23. package/lib/tokenizer/named_entity_data.js +5 -0
  24. package/lib/tokenizer/preprocessor.js +0 -8
  25. package/lib/tree_adapters/default.js +209 -578
  26. package/lib/tree_adapters/htmlparser2.js +338 -327
  27. package/lib/{common → utils}/merge_options.js +13 -13
  28. package/lib/utils/mixin.js +18 -0
  29. package/package.json +13 -14
  30. package/lib/location_info/parser_mixin.js +0 -217
  31. package/lib/location_info/tokenizer_mixin.js +0 -169
  32. package/lib/parser/stream.js +0 -140
  33. package/lib/tokenizer/named_entity_trie.js +0 -6
package/lib/index.js CHANGED
@@ -1,108 +1,44 @@
1
- 'use strict';
2
-
3
- var Parser = require('./parser'),
4
- Serializer = require('./serializer');
5
-
6
- /** @namespace parse5 */
7
-
8
- /**
9
- * Parses an HTML string.
10
- * @function parse
11
- * @memberof parse5
12
- * @instance
13
- * @param {string} html - Input HTML string.
14
- * @param {ParserOptions} [options] - Parsing options.
15
- * @returns {ASTNode<Document>} document
16
- * @example
17
- * var parse5 = require('parse5');
18
- *
19
- * var document = parse5.parse('<!DOCTYPE html><html><head></head><body>Hi there!</body></html>');
20
- */
21
- exports.parse = function parse(html, options) {
22
- var parser = new Parser(options);
23
-
24
- return parser.parse(html);
25
- };
26
-
27
- /**
28
- * Parses an HTML fragment.
29
- * @function parseFragment
30
- * @memberof parse5
31
- * @instance
32
- * @param {ASTNode} [fragmentContext] - Parsing context element. If specified, given fragment
33
- * will be parsed as if it was set to the context element's `innerHTML` property.
34
- * @param {string} html - Input HTML fragment string.
35
- * @param {ParserOptions} [options] - Parsing options.
36
- * @returns {ASTNode<DocumentFragment>} documentFragment
37
- * @example
38
- * var parse5 = require('parse5');
39
- *
40
- * var documentFragment = parse5.parseFragment('<table></table>');
41
- *
42
- * // Parses the html fragment in the context of the parsed <table> element.
43
- * var trFragment = parser.parseFragment(documentFragment.childNodes[0], '<tr><td>Shake it, baby</td></tr>');
44
- */
45
- exports.parseFragment = function parseFragment(fragmentContext, html, options) {
46
- if (typeof fragmentContext === 'string') {
47
- options = html;
48
- html = fragmentContext;
49
- fragmentContext = null;
50
- }
51
-
52
- var parser = new Parser(options);
53
-
54
- return parser.parseFragment(html, fragmentContext);
55
- };
56
-
57
- /**
58
- * Serializes an AST node to an HTML string.
59
- * @function serialize
60
- * @memberof parse5
61
- * @instance
62
- * @param {ASTNode} node - Node to serialize.
63
- * @param {SerializerOptions} [options] - Serialization options.
64
- * @returns {String} html
65
- * @example
66
- * var parse5 = require('parse5');
67
- *
68
- * var document = parse5.parse('<!DOCTYPE html><html><head></head><body>Hi there!</body></html>');
69
- *
70
- * // Serializes a document.
71
- * var html = parse5.serialize(document);
72
- *
73
- * // Serializes the <body> element content.
74
- * var bodyInnerHtml = parse5.serialize(document.childNodes[0].childNodes[1]);
75
- */
76
- exports.serialize = function (node, options) {
77
- var serializer = new Serializer(node, options);
78
-
79
- return serializer.serialize();
80
- };
81
-
82
- /**
83
- * Provides built-in tree adapters that can be used for parsing and serialization.
84
- * @var treeAdapters
85
- * @memberof parse5
86
- * @instance
87
- * @property {TreeAdapter} default - Default tree format for parse5.
88
- * @property {TreeAdapter} htmlparser2 - Quite popular [htmlparser2](https://github.com/fb55/htmlparser2) tree format
89
- * (e.g. used by [cheerio](https://github.com/MatthewMueller/cheerio) and [jsdom](https://github.com/tmpvar/jsdom)).
90
- * @example
91
- * var parse5 = require('parse5');
92
- *
93
- * // Uses the default tree adapter for parsing.
94
- * var document = parse5.parse('<div></div>', { treeAdapter: parse5.treeAdapters.default });
95
- *
96
- * // Uses the htmlparser2 tree adapter with the SerializerStream.
97
- * var serializer = new parse5.SerializerStream(node, { treeAdapter: parse5.treeAdapters.htmlparser2 });
98
- */
99
- exports.treeAdapters = {
100
- default: require('./tree_adapters/default'),
101
- htmlparser2: require('./tree_adapters/htmlparser2')
102
- };
103
-
104
-
105
- // Streaming
106
- exports.ParserStream = require('./parser/stream');
107
- exports.SerializerStream = require('./serializer/stream');
108
- exports.SAXParser = require('./sax');
1
+ 'use strict';
2
+
3
+ var Parser = require('./parser'),
4
+ Serializer = require('./serializer');
5
+
6
+
7
+ // Shorthands
8
+ exports.parse = function parse(html, options) {
9
+ var parser = new Parser(options);
10
+
11
+ return parser.parse(html);
12
+ };
13
+
14
+ exports.parseFragment = function parseFragment(fragmentContext, html, options) {
15
+ if (typeof fragmentContext === 'string') {
16
+ options = html;
17
+ html = fragmentContext;
18
+ fragmentContext = null;
19
+ }
20
+
21
+ var parser = new Parser(options);
22
+
23
+ return parser.parseFragment(html, fragmentContext);
24
+ };
25
+
26
+ exports.serialize = function (node, options) {
27
+ var serializer = new Serializer(node, options);
28
+
29
+ return serializer.serialize();
30
+ };
31
+
32
+
33
+ // Tree adapters
34
+ exports.treeAdapters = {
35
+ default: require('./tree_adapters/default'),
36
+ htmlparser2: require('./tree_adapters/htmlparser2')
37
+ };
38
+
39
+
40
+ // Streaming
41
+ exports.ParserStream = require('./parser/parser_stream');
42
+ exports.PlainTextConversionStream = require('./parser/plain_text_conversion_stream');
43
+ exports.SerializerStream = require('./serializer/serializer_stream');
44
+ exports.SAXParser = require('./sax');
@@ -1,167 +1,167 @@
1
- 'use strict';
2
-
3
- //Const
4
- var NOAH_ARK_CAPACITY = 3;
5
-
6
- //List of formatting elements
7
- var FormattingElementList = module.exports = function (treeAdapter) {
8
- this.length = 0;
9
- this.entries = [];
10
- this.treeAdapter = treeAdapter;
11
- this.bookmark = null;
12
- };
13
-
14
- //Entry types
15
- FormattingElementList.MARKER_ENTRY = 'MARKER_ENTRY';
16
- FormattingElementList.ELEMENT_ENTRY = 'ELEMENT_ENTRY';
17
-
18
- //Noah Ark's condition
19
- //OPTIMIZATION: at first we try to find possible candidates for exclusion using
20
- //lightweight heuristics without thorough attributes check.
21
- FormattingElementList.prototype._getNoahArkConditionCandidates = function (newElement) {
22
- var candidates = [];
23
-
24
- if (this.length >= NOAH_ARK_CAPACITY) {
25
- var neAttrsLength = this.treeAdapter.getAttrList(newElement).length,
26
- neTagName = this.treeAdapter.getTagName(newElement),
27
- neNamespaceURI = this.treeAdapter.getNamespaceURI(newElement);
28
-
29
- for (var i = this.length - 1; i >= 0; i--) {
30
- var entry = this.entries[i];
31
-
32
- if (entry.type === FormattingElementList.MARKER_ENTRY)
33
- break;
34
-
35
- var element = entry.element,
36
- elementAttrs = this.treeAdapter.getAttrList(element),
37
- isCandidate = this.treeAdapter.getTagName(element) === neTagName &&
38
- this.treeAdapter.getNamespaceURI(element) === neNamespaceURI &&
39
- elementAttrs.length === neAttrsLength;
40
-
41
- if (isCandidate)
42
- candidates.push({idx: i, attrs: elementAttrs});
43
- }
44
- }
45
-
46
- return candidates.length < NOAH_ARK_CAPACITY ? [] : candidates;
47
- };
48
-
49
- FormattingElementList.prototype._ensureNoahArkCondition = function (newElement) {
50
- var candidates = this._getNoahArkConditionCandidates(newElement),
51
- cLength = candidates.length;
52
-
53
- if (cLength) {
54
- var neAttrs = this.treeAdapter.getAttrList(newElement),
55
- neAttrsLength = neAttrs.length,
56
- neAttrsMap = {};
57
-
58
- //NOTE: build attrs map for the new element so we can perform fast lookups
59
- for (var i = 0; i < neAttrsLength; i++) {
60
- var neAttr = neAttrs[i];
61
-
62
- neAttrsMap[neAttr.name] = neAttr.value;
63
- }
64
-
65
- for (i = 0; i < neAttrsLength; i++) {
66
- for (var j = 0; j < cLength; j++) {
67
- var cAttr = candidates[j].attrs[i];
68
-
69
- if (neAttrsMap[cAttr.name] !== cAttr.value) {
70
- candidates.splice(j, 1);
71
- cLength--;
72
- }
73
-
74
- if (candidates.length < NOAH_ARK_CAPACITY)
75
- return;
76
- }
77
- }
78
-
79
- //NOTE: remove bottommost candidates until Noah's Ark condition will not be met
80
- for (i = cLength - 1; i >= NOAH_ARK_CAPACITY - 1; i--) {
81
- this.entries.splice(candidates[i].idx, 1);
82
- this.length--;
83
- }
84
- }
85
- };
86
-
87
- //Mutations
88
- FormattingElementList.prototype.insertMarker = function () {
89
- this.entries.push({type: FormattingElementList.MARKER_ENTRY});
90
- this.length++;
91
- };
92
-
93
- FormattingElementList.prototype.pushElement = function (element, token) {
94
- this._ensureNoahArkCondition(element);
95
-
96
- this.entries.push({
97
- type: FormattingElementList.ELEMENT_ENTRY,
98
- element: element,
99
- token: token
100
- });
101
-
102
- this.length++;
103
- };
104
-
105
- FormattingElementList.prototype.insertElementAfterBookmark = function (element, token) {
106
- var bookmarkIdx = this.length - 1;
107
-
108
- for (; bookmarkIdx >= 0; bookmarkIdx--) {
109
- if (this.entries[bookmarkIdx] === this.bookmark)
110
- break;
111
- }
112
-
113
- this.entries.splice(bookmarkIdx + 1, 0, {
114
- type: FormattingElementList.ELEMENT_ENTRY,
115
- element: element,
116
- token: token
117
- });
118
-
119
- this.length++;
120
- };
121
-
122
- FormattingElementList.prototype.removeEntry = function (entry) {
123
- for (var i = this.length - 1; i >= 0; i--) {
124
- if (this.entries[i] === entry) {
125
- this.entries.splice(i, 1);
126
- this.length--;
127
- break;
128
- }
129
- }
130
- };
131
-
132
- FormattingElementList.prototype.clearToLastMarker = function () {
133
- while (this.length) {
134
- var entry = this.entries.pop();
135
-
136
- this.length--;
137
-
138
- if (entry.type === FormattingElementList.MARKER_ENTRY)
139
- break;
140
- }
141
- };
142
-
143
- //Search
144
- FormattingElementList.prototype.getElementEntryInScopeWithTagName = function (tagName) {
145
- for (var i = this.length - 1; i >= 0; i--) {
146
- var entry = this.entries[i];
147
-
148
- if (entry.type === FormattingElementList.MARKER_ENTRY)
149
- return null;
150
-
151
- if (this.treeAdapter.getTagName(entry.element) === tagName)
152
- return entry;
153
- }
154
-
155
- return null;
156
- };
157
-
158
- FormattingElementList.prototype.getElementEntry = function (element) {
159
- for (var i = this.length - 1; i >= 0; i--) {
160
- var entry = this.entries[i];
161
-
162
- if (entry.type === FormattingElementList.ELEMENT_ENTRY && entry.element === element)
163
- return entry;
164
- }
165
-
166
- return null;
167
- };
1
+ 'use strict';
2
+
3
+ //Const
4
+ var NOAH_ARK_CAPACITY = 3;
5
+
6
+ //List of formatting elements
7
+ var FormattingElementList = module.exports = function (treeAdapter) {
8
+ this.length = 0;
9
+ this.entries = [];
10
+ this.treeAdapter = treeAdapter;
11
+ this.bookmark = null;
12
+ };
13
+
14
+ //Entry types
15
+ FormattingElementList.MARKER_ENTRY = 'MARKER_ENTRY';
16
+ FormattingElementList.ELEMENT_ENTRY = 'ELEMENT_ENTRY';
17
+
18
+ //Noah Ark's condition
19
+ //OPTIMIZATION: at first we try to find possible candidates for exclusion using
20
+ //lightweight heuristics without thorough attributes check.
21
+ FormattingElementList.prototype._getNoahArkConditionCandidates = function (newElement) {
22
+ var candidates = [];
23
+
24
+ if (this.length >= NOAH_ARK_CAPACITY) {
25
+ var neAttrsLength = this.treeAdapter.getAttrList(newElement).length,
26
+ neTagName = this.treeAdapter.getTagName(newElement),
27
+ neNamespaceURI = this.treeAdapter.getNamespaceURI(newElement);
28
+
29
+ for (var i = this.length - 1; i >= 0; i--) {
30
+ var entry = this.entries[i];
31
+
32
+ if (entry.type === FormattingElementList.MARKER_ENTRY)
33
+ break;
34
+
35
+ var element = entry.element,
36
+ elementAttrs = this.treeAdapter.getAttrList(element),
37
+ isCandidate = this.treeAdapter.getTagName(element) === neTagName &&
38
+ this.treeAdapter.getNamespaceURI(element) === neNamespaceURI &&
39
+ elementAttrs.length === neAttrsLength;
40
+
41
+ if (isCandidate)
42
+ candidates.push({idx: i, attrs: elementAttrs});
43
+ }
44
+ }
45
+
46
+ return candidates.length < NOAH_ARK_CAPACITY ? [] : candidates;
47
+ };
48
+
49
+ FormattingElementList.prototype._ensureNoahArkCondition = function (newElement) {
50
+ var candidates = this._getNoahArkConditionCandidates(newElement),
51
+ cLength = candidates.length;
52
+
53
+ if (cLength) {
54
+ var neAttrs = this.treeAdapter.getAttrList(newElement),
55
+ neAttrsLength = neAttrs.length,
56
+ neAttrsMap = Object.create(null);
57
+
58
+ //NOTE: build attrs map for the new element so we can perform fast lookups
59
+ for (var i = 0; i < neAttrsLength; i++) {
60
+ var neAttr = neAttrs[i];
61
+
62
+ neAttrsMap[neAttr.name] = neAttr.value;
63
+ }
64
+
65
+ for (i = 0; i < neAttrsLength; i++) {
66
+ for (var j = 0; j < cLength; j++) {
67
+ var cAttr = candidates[j].attrs[i];
68
+
69
+ if (neAttrsMap[cAttr.name] !== cAttr.value) {
70
+ candidates.splice(j, 1);
71
+ cLength--;
72
+ }
73
+
74
+ if (candidates.length < NOAH_ARK_CAPACITY)
75
+ return;
76
+ }
77
+ }
78
+
79
+ //NOTE: remove bottommost candidates until Noah's Ark condition will not be met
80
+ for (i = cLength - 1; i >= NOAH_ARK_CAPACITY - 1; i--) {
81
+ this.entries.splice(candidates[i].idx, 1);
82
+ this.length--;
83
+ }
84
+ }
85
+ };
86
+
87
+ //Mutations
88
+ FormattingElementList.prototype.insertMarker = function () {
89
+ this.entries.push({type: FormattingElementList.MARKER_ENTRY});
90
+ this.length++;
91
+ };
92
+
93
+ FormattingElementList.prototype.pushElement = function (element, token) {
94
+ this._ensureNoahArkCondition(element);
95
+
96
+ this.entries.push({
97
+ type: FormattingElementList.ELEMENT_ENTRY,
98
+ element: element,
99
+ token: token
100
+ });
101
+
102
+ this.length++;
103
+ };
104
+
105
+ FormattingElementList.prototype.insertElementAfterBookmark = function (element, token) {
106
+ var bookmarkIdx = this.length - 1;
107
+
108
+ for (; bookmarkIdx >= 0; bookmarkIdx--) {
109
+ if (this.entries[bookmarkIdx] === this.bookmark)
110
+ break;
111
+ }
112
+
113
+ this.entries.splice(bookmarkIdx + 1, 0, {
114
+ type: FormattingElementList.ELEMENT_ENTRY,
115
+ element: element,
116
+ token: token
117
+ });
118
+
119
+ this.length++;
120
+ };
121
+
122
+ FormattingElementList.prototype.removeEntry = function (entry) {
123
+ for (var i = this.length - 1; i >= 0; i--) {
124
+ if (this.entries[i] === entry) {
125
+ this.entries.splice(i, 1);
126
+ this.length--;
127
+ break;
128
+ }
129
+ }
130
+ };
131
+
132
+ FormattingElementList.prototype.clearToLastMarker = function () {
133
+ while (this.length) {
134
+ var entry = this.entries.pop();
135
+
136
+ this.length--;
137
+
138
+ if (entry.type === FormattingElementList.MARKER_ENTRY)
139
+ break;
140
+ }
141
+ };
142
+
143
+ //Search
144
+ FormattingElementList.prototype.getElementEntryInScopeWithTagName = function (tagName) {
145
+ for (var i = this.length - 1; i >= 0; i--) {
146
+ var entry = this.entries[i];
147
+
148
+ if (entry.type === FormattingElementList.MARKER_ENTRY)
149
+ return null;
150
+
151
+ if (this.treeAdapter.getTagName(entry.element) === tagName)
152
+ return entry;
153
+ }
154
+
155
+ return null;
156
+ };
157
+
158
+ FormattingElementList.prototype.getElementEntry = function (element) {
159
+ for (var i = this.length - 1; i >= 0; i--) {
160
+ var entry = this.entries[i];
161
+
162
+ if (entry.type === FormattingElementList.ELEMENT_ENTRY && entry.element === element)
163
+ return entry;
164
+ }
165
+
166
+ return null;
167
+ };