parse5 6.0.1 → 7.3.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.
Files changed (86) hide show
  1. package/README.md +2 -2
  2. package/dist/cjs/common/doctype.d.ts +4 -0
  3. package/dist/cjs/common/doctype.js +118 -0
  4. package/dist/cjs/common/error-codes.d.ts +67 -0
  5. package/dist/cjs/common/error-codes.js +66 -0
  6. package/dist/cjs/common/foreign-content.d.ts +9 -0
  7. package/dist/cjs/common/foreign-content.js +237 -0
  8. package/dist/cjs/common/html.d.ts +289 -0
  9. package/dist/cjs/common/html.js +528 -0
  10. package/dist/cjs/common/token.d.ts +84 -0
  11. package/dist/cjs/common/token.js +24 -0
  12. package/dist/cjs/common/unicode.d.ts +42 -0
  13. package/dist/cjs/common/unicode.js +70 -0
  14. package/dist/cjs/index.d.ts +71 -0
  15. package/dist/cjs/index.js +54 -0
  16. package/dist/cjs/package.json +1 -0
  17. package/dist/cjs/parser/formatting-element-list.d.ts +36 -0
  18. package/dist/cjs/parser/formatting-element-list.js +114 -0
  19. package/dist/cjs/parser/index.d.ts +221 -0
  20. package/dist/cjs/parser/index.js +3240 -0
  21. package/dist/cjs/parser/open-element-stack.d.ts +53 -0
  22. package/dist/cjs/parser/open-element-stack.js +328 -0
  23. package/dist/cjs/serializer/index.d.ts +60 -0
  24. package/dist/cjs/serializer/index.js +172 -0
  25. package/dist/cjs/tokenizer/index.d.ts +247 -0
  26. package/dist/cjs/tokenizer/index.js +2714 -0
  27. package/dist/cjs/tokenizer/preprocessor.d.ts +36 -0
  28. package/dist/cjs/tokenizer/preprocessor.js +200 -0
  29. package/dist/cjs/tree-adapters/default.d.ts +84 -0
  30. package/dist/cjs/tree-adapters/default.js +176 -0
  31. package/dist/cjs/tree-adapters/interface.d.ts +255 -0
  32. package/dist/cjs/tree-adapters/interface.js +2 -0
  33. package/dist/common/doctype.d.ts +4 -0
  34. package/{lib → dist}/common/doctype.js +24 -72
  35. package/dist/common/error-codes.d.ts +67 -0
  36. package/dist/common/error-codes.js +63 -0
  37. package/dist/common/foreign-content.d.ts +9 -0
  38. package/dist/common/foreign-content.js +228 -0
  39. package/dist/common/html.d.ts +289 -0
  40. package/dist/common/html.js +523 -0
  41. package/dist/common/token.d.ts +84 -0
  42. package/dist/common/token.js +20 -0
  43. package/dist/common/unicode.d.ts +42 -0
  44. package/dist/common/unicode.js +62 -0
  45. package/dist/index.d.ts +71 -0
  46. package/dist/index.js +42 -0
  47. package/dist/parser/formatting-element-list.d.ts +36 -0
  48. package/dist/parser/formatting-element-list.js +110 -0
  49. package/dist/parser/index.d.ts +221 -0
  50. package/dist/parser/index.js +3245 -0
  51. package/dist/parser/open-element-stack.d.ts +53 -0
  52. package/dist/parser/open-element-stack.js +324 -0
  53. package/dist/serializer/index.d.ts +60 -0
  54. package/dist/serializer/index.js +168 -0
  55. package/dist/tokenizer/index.d.ts +247 -0
  56. package/dist/tokenizer/index.js +2710 -0
  57. package/dist/tokenizer/preprocessor.d.ts +36 -0
  58. package/dist/tokenizer/preprocessor.js +196 -0
  59. package/dist/tree-adapters/default.d.ts +84 -0
  60. package/dist/tree-adapters/default.js +173 -0
  61. package/dist/tree-adapters/interface.d.ts +255 -0
  62. package/dist/tree-adapters/interface.js +1 -0
  63. package/package.json +21 -6
  64. package/lib/common/error-codes.js +0 -65
  65. package/lib/common/foreign-content.js +0 -265
  66. package/lib/common/html.js +0 -272
  67. package/lib/common/unicode.js +0 -109
  68. package/lib/extensions/error-reporting/mixin-base.js +0 -43
  69. package/lib/extensions/error-reporting/parser-mixin.js +0 -52
  70. package/lib/extensions/error-reporting/preprocessor-mixin.js +0 -24
  71. package/lib/extensions/error-reporting/tokenizer-mixin.js +0 -17
  72. package/lib/extensions/location-info/open-element-stack-mixin.js +0 -35
  73. package/lib/extensions/location-info/parser-mixin.js +0 -223
  74. package/lib/extensions/location-info/tokenizer-mixin.js +0 -146
  75. package/lib/extensions/position-tracking/preprocessor-mixin.js +0 -64
  76. package/lib/index.js +0 -29
  77. package/lib/parser/formatting-element-list.js +0 -181
  78. package/lib/parser/index.js +0 -2956
  79. package/lib/parser/open-element-stack.js +0 -482
  80. package/lib/serializer/index.js +0 -176
  81. package/lib/tokenizer/index.js +0 -2196
  82. package/lib/tokenizer/named-entity-data.js +0 -5
  83. package/lib/tokenizer/preprocessor.js +0 -159
  84. package/lib/tree-adapters/default.js +0 -221
  85. package/lib/utils/merge-options.js +0 -13
  86. package/lib/utils/mixin.js +0 -39
@@ -1,223 +0,0 @@
1
- 'use strict';
2
-
3
- const Mixin = require('../../utils/mixin');
4
- const Tokenizer = require('../../tokenizer');
5
- const LocationInfoTokenizerMixin = require('./tokenizer-mixin');
6
- const LocationInfoOpenElementStackMixin = require('./open-element-stack-mixin');
7
- const HTML = require('../../common/html');
8
-
9
- //Aliases
10
- const $ = HTML.TAG_NAMES;
11
-
12
- class LocationInfoParserMixin extends Mixin {
13
- constructor(parser) {
14
- super(parser);
15
-
16
- this.parser = parser;
17
- this.treeAdapter = this.parser.treeAdapter;
18
- this.posTracker = null;
19
- this.lastStartTagToken = null;
20
- this.lastFosterParentingLocation = null;
21
- this.currentToken = null;
22
- }
23
-
24
- _setStartLocation(element) {
25
- let loc = null;
26
-
27
- if (this.lastStartTagToken) {
28
- loc = Object.assign({}, this.lastStartTagToken.location);
29
- loc.startTag = this.lastStartTagToken.location;
30
- }
31
-
32
- this.treeAdapter.setNodeSourceCodeLocation(element, loc);
33
- }
34
-
35
- _setEndLocation(element, closingToken) {
36
- const loc = this.treeAdapter.getNodeSourceCodeLocation(element);
37
-
38
- if (loc) {
39
- if (closingToken.location) {
40
- const ctLoc = closingToken.location;
41
- const tn = this.treeAdapter.getTagName(element);
42
-
43
- // NOTE: For cases like <p> <p> </p> - First 'p' closes without a closing
44
- // tag and for cases like <td> <p> </td> - 'p' closes without a closing tag.
45
- const isClosingEndTag = closingToken.type === Tokenizer.END_TAG_TOKEN && tn === closingToken.tagName;
46
- const endLoc = {};
47
- if (isClosingEndTag) {
48
- endLoc.endTag = Object.assign({}, ctLoc);
49
- endLoc.endLine = ctLoc.endLine;
50
- endLoc.endCol = ctLoc.endCol;
51
- endLoc.endOffset = ctLoc.endOffset;
52
- } else {
53
- endLoc.endLine = ctLoc.startLine;
54
- endLoc.endCol = ctLoc.startCol;
55
- endLoc.endOffset = ctLoc.startOffset;
56
- }
57
-
58
- this.treeAdapter.updateNodeSourceCodeLocation(element, endLoc);
59
- }
60
- }
61
- }
62
-
63
- _getOverriddenMethods(mxn, orig) {
64
- return {
65
- _bootstrap(document, fragmentContext) {
66
- orig._bootstrap.call(this, document, fragmentContext);
67
-
68
- mxn.lastStartTagToken = null;
69
- mxn.lastFosterParentingLocation = null;
70
- mxn.currentToken = null;
71
-
72
- const tokenizerMixin = Mixin.install(this.tokenizer, LocationInfoTokenizerMixin);
73
-
74
- mxn.posTracker = tokenizerMixin.posTracker;
75
-
76
- Mixin.install(this.openElements, LocationInfoOpenElementStackMixin, {
77
- onItemPop: function(element) {
78
- mxn._setEndLocation(element, mxn.currentToken);
79
- }
80
- });
81
- },
82
-
83
- _runParsingLoop(scriptHandler) {
84
- orig._runParsingLoop.call(this, scriptHandler);
85
-
86
- // NOTE: generate location info for elements
87
- // that remains on open element stack
88
- for (let i = this.openElements.stackTop; i >= 0; i--) {
89
- mxn._setEndLocation(this.openElements.items[i], mxn.currentToken);
90
- }
91
- },
92
-
93
- //Token processing
94
- _processTokenInForeignContent(token) {
95
- mxn.currentToken = token;
96
- orig._processTokenInForeignContent.call(this, token);
97
- },
98
-
99
- _processToken(token) {
100
- mxn.currentToken = token;
101
- orig._processToken.call(this, token);
102
-
103
- //NOTE: <body> and <html> are never popped from the stack, so we need to updated
104
- //their end location explicitly.
105
- const requireExplicitUpdate =
106
- token.type === Tokenizer.END_TAG_TOKEN &&
107
- (token.tagName === $.HTML || (token.tagName === $.BODY && this.openElements.hasInScope($.BODY)));
108
-
109
- if (requireExplicitUpdate) {
110
- for (let i = this.openElements.stackTop; i >= 0; i--) {
111
- const element = this.openElements.items[i];
112
-
113
- if (this.treeAdapter.getTagName(element) === token.tagName) {
114
- mxn._setEndLocation(element, token);
115
- break;
116
- }
117
- }
118
- }
119
- },
120
-
121
- //Doctype
122
- _setDocumentType(token) {
123
- orig._setDocumentType.call(this, token);
124
-
125
- const documentChildren = this.treeAdapter.getChildNodes(this.document);
126
- const cnLength = documentChildren.length;
127
-
128
- for (let i = 0; i < cnLength; i++) {
129
- const node = documentChildren[i];
130
-
131
- if (this.treeAdapter.isDocumentTypeNode(node)) {
132
- this.treeAdapter.setNodeSourceCodeLocation(node, token.location);
133
- break;
134
- }
135
- }
136
- },
137
-
138
- //Elements
139
- _attachElementToTree(element) {
140
- //NOTE: _attachElementToTree is called from _appendElement, _insertElement and _insertTemplate methods.
141
- //So we will use token location stored in this methods for the element.
142
- mxn._setStartLocation(element);
143
- mxn.lastStartTagToken = null;
144
- orig._attachElementToTree.call(this, element);
145
- },
146
-
147
- _appendElement(token, namespaceURI) {
148
- mxn.lastStartTagToken = token;
149
- orig._appendElement.call(this, token, namespaceURI);
150
- },
151
-
152
- _insertElement(token, namespaceURI) {
153
- mxn.lastStartTagToken = token;
154
- orig._insertElement.call(this, token, namespaceURI);
155
- },
156
-
157
- _insertTemplate(token) {
158
- mxn.lastStartTagToken = token;
159
- orig._insertTemplate.call(this, token);
160
-
161
- const tmplContent = this.treeAdapter.getTemplateContent(this.openElements.current);
162
-
163
- this.treeAdapter.setNodeSourceCodeLocation(tmplContent, null);
164
- },
165
-
166
- _insertFakeRootElement() {
167
- orig._insertFakeRootElement.call(this);
168
- this.treeAdapter.setNodeSourceCodeLocation(this.openElements.current, null);
169
- },
170
-
171
- //Comments
172
- _appendCommentNode(token, parent) {
173
- orig._appendCommentNode.call(this, token, parent);
174
-
175
- const children = this.treeAdapter.getChildNodes(parent);
176
- const commentNode = children[children.length - 1];
177
-
178
- this.treeAdapter.setNodeSourceCodeLocation(commentNode, token.location);
179
- },
180
-
181
- //Text
182
- _findFosterParentingLocation() {
183
- //NOTE: store last foster parenting location, so we will be able to find inserted text
184
- //in case of foster parenting
185
- mxn.lastFosterParentingLocation = orig._findFosterParentingLocation.call(this);
186
-
187
- return mxn.lastFosterParentingLocation;
188
- },
189
-
190
- _insertCharacters(token) {
191
- orig._insertCharacters.call(this, token);
192
-
193
- const hasFosterParent = this._shouldFosterParentOnInsertion();
194
-
195
- const parent =
196
- (hasFosterParent && mxn.lastFosterParentingLocation.parent) ||
197
- this.openElements.currentTmplContent ||
198
- this.openElements.current;
199
-
200
- const siblings = this.treeAdapter.getChildNodes(parent);
201
-
202
- const textNodeIdx =
203
- hasFosterParent && mxn.lastFosterParentingLocation.beforeElement
204
- ? siblings.indexOf(mxn.lastFosterParentingLocation.beforeElement) - 1
205
- : siblings.length - 1;
206
-
207
- const textNode = siblings[textNodeIdx];
208
-
209
- //NOTE: if we have location assigned by another token, then just update end position
210
- const tnLoc = this.treeAdapter.getNodeSourceCodeLocation(textNode);
211
-
212
- if (tnLoc) {
213
- const { endLine, endCol, endOffset } = token.location;
214
- this.treeAdapter.updateNodeSourceCodeLocation(textNode, { endLine, endCol, endOffset });
215
- } else {
216
- this.treeAdapter.setNodeSourceCodeLocation(textNode, token.location);
217
- }
218
- }
219
- };
220
- }
221
- }
222
-
223
- module.exports = LocationInfoParserMixin;
@@ -1,146 +0,0 @@
1
- 'use strict';
2
-
3
- const Mixin = require('../../utils/mixin');
4
- const Tokenizer = require('../../tokenizer');
5
- const PositionTrackingPreprocessorMixin = require('../position-tracking/preprocessor-mixin');
6
-
7
- class LocationInfoTokenizerMixin extends Mixin {
8
- constructor(tokenizer) {
9
- super(tokenizer);
10
-
11
- this.tokenizer = tokenizer;
12
- this.posTracker = Mixin.install(tokenizer.preprocessor, PositionTrackingPreprocessorMixin);
13
- this.currentAttrLocation = null;
14
- this.ctLoc = null;
15
- }
16
-
17
- _getCurrentLocation() {
18
- return {
19
- startLine: this.posTracker.line,
20
- startCol: this.posTracker.col,
21
- startOffset: this.posTracker.offset,
22
- endLine: -1,
23
- endCol: -1,
24
- endOffset: -1
25
- };
26
- }
27
-
28
- _attachCurrentAttrLocationInfo() {
29
- this.currentAttrLocation.endLine = this.posTracker.line;
30
- this.currentAttrLocation.endCol = this.posTracker.col;
31
- this.currentAttrLocation.endOffset = this.posTracker.offset;
32
-
33
- const currentToken = this.tokenizer.currentToken;
34
- const currentAttr = this.tokenizer.currentAttr;
35
-
36
- if (!currentToken.location.attrs) {
37
- currentToken.location.attrs = Object.create(null);
38
- }
39
-
40
- currentToken.location.attrs[currentAttr.name] = this.currentAttrLocation;
41
- }
42
-
43
- _getOverriddenMethods(mxn, orig) {
44
- const methods = {
45
- _createStartTagToken() {
46
- orig._createStartTagToken.call(this);
47
- this.currentToken.location = mxn.ctLoc;
48
- },
49
-
50
- _createEndTagToken() {
51
- orig._createEndTagToken.call(this);
52
- this.currentToken.location = mxn.ctLoc;
53
- },
54
-
55
- _createCommentToken() {
56
- orig._createCommentToken.call(this);
57
- this.currentToken.location = mxn.ctLoc;
58
- },
59
-
60
- _createDoctypeToken(initialName) {
61
- orig._createDoctypeToken.call(this, initialName);
62
- this.currentToken.location = mxn.ctLoc;
63
- },
64
-
65
- _createCharacterToken(type, ch) {
66
- orig._createCharacterToken.call(this, type, ch);
67
- this.currentCharacterToken.location = mxn.ctLoc;
68
- },
69
-
70
- _createEOFToken() {
71
- orig._createEOFToken.call(this);
72
- this.currentToken.location = mxn._getCurrentLocation();
73
- },
74
-
75
- _createAttr(attrNameFirstCh) {
76
- orig._createAttr.call(this, attrNameFirstCh);
77
- mxn.currentAttrLocation = mxn._getCurrentLocation();
78
- },
79
-
80
- _leaveAttrName(toState) {
81
- orig._leaveAttrName.call(this, toState);
82
- mxn._attachCurrentAttrLocationInfo();
83
- },
84
-
85
- _leaveAttrValue(toState) {
86
- orig._leaveAttrValue.call(this, toState);
87
- mxn._attachCurrentAttrLocationInfo();
88
- },
89
-
90
- _emitCurrentToken() {
91
- const ctLoc = this.currentToken.location;
92
-
93
- //NOTE: if we have pending character token make it's end location equal to the
94
- //current token's start location.
95
- if (this.currentCharacterToken) {
96
- this.currentCharacterToken.location.endLine = ctLoc.startLine;
97
- this.currentCharacterToken.location.endCol = ctLoc.startCol;
98
- this.currentCharacterToken.location.endOffset = ctLoc.startOffset;
99
- }
100
-
101
- if (this.currentToken.type === Tokenizer.EOF_TOKEN) {
102
- ctLoc.endLine = ctLoc.startLine;
103
- ctLoc.endCol = ctLoc.startCol;
104
- ctLoc.endOffset = ctLoc.startOffset;
105
- } else {
106
- ctLoc.endLine = mxn.posTracker.line;
107
- ctLoc.endCol = mxn.posTracker.col + 1;
108
- ctLoc.endOffset = mxn.posTracker.offset + 1;
109
- }
110
-
111
- orig._emitCurrentToken.call(this);
112
- },
113
-
114
- _emitCurrentCharacterToken() {
115
- const ctLoc = this.currentCharacterToken && this.currentCharacterToken.location;
116
-
117
- //NOTE: if we have character token and it's location wasn't set in the _emitCurrentToken(),
118
- //then set it's location at the current preprocessor position.
119
- //We don't need to increment preprocessor position, since character token
120
- //emission is always forced by the start of the next character token here.
121
- //So, we already have advanced position.
122
- if (ctLoc && ctLoc.endOffset === -1) {
123
- ctLoc.endLine = mxn.posTracker.line;
124
- ctLoc.endCol = mxn.posTracker.col;
125
- ctLoc.endOffset = mxn.posTracker.offset;
126
- }
127
-
128
- orig._emitCurrentCharacterToken.call(this);
129
- }
130
- };
131
-
132
- //NOTE: patch initial states for each mode to obtain token start position
133
- Object.keys(Tokenizer.MODE).forEach(modeName => {
134
- const state = Tokenizer.MODE[modeName];
135
-
136
- methods[state] = function(cp) {
137
- mxn.ctLoc = mxn._getCurrentLocation();
138
- orig[state].call(this, cp);
139
- };
140
- });
141
-
142
- return methods;
143
- }
144
- }
145
-
146
- module.exports = LocationInfoTokenizerMixin;
@@ -1,64 +0,0 @@
1
- 'use strict';
2
-
3
- const Mixin = require('../../utils/mixin');
4
-
5
- class PositionTrackingPreprocessorMixin extends Mixin {
6
- constructor(preprocessor) {
7
- super(preprocessor);
8
-
9
- this.preprocessor = preprocessor;
10
- this.isEol = false;
11
- this.lineStartPos = 0;
12
- this.droppedBufferSize = 0;
13
-
14
- this.offset = 0;
15
- this.col = 0;
16
- this.line = 1;
17
- }
18
-
19
- _getOverriddenMethods(mxn, orig) {
20
- return {
21
- advance() {
22
- const pos = this.pos + 1;
23
- const ch = this.html[pos];
24
-
25
- //NOTE: LF should be in the last column of the line
26
- if (mxn.isEol) {
27
- mxn.isEol = false;
28
- mxn.line++;
29
- mxn.lineStartPos = pos;
30
- }
31
-
32
- if (ch === '\n' || (ch === '\r' && this.html[pos + 1] !== '\n')) {
33
- mxn.isEol = true;
34
- }
35
-
36
- mxn.col = pos - mxn.lineStartPos + 1;
37
- mxn.offset = mxn.droppedBufferSize + pos;
38
-
39
- return orig.advance.call(this);
40
- },
41
-
42
- retreat() {
43
- orig.retreat.call(this);
44
-
45
- mxn.isEol = false;
46
- mxn.col = this.pos - mxn.lineStartPos + 1;
47
- },
48
-
49
- dropParsedChunk() {
50
- const prevPos = this.pos;
51
-
52
- orig.dropParsedChunk.call(this);
53
-
54
- const reduction = prevPos - this.pos;
55
-
56
- mxn.lineStartPos -= reduction;
57
- mxn.droppedBufferSize += reduction;
58
- mxn.offset = mxn.droppedBufferSize + this.pos;
59
- }
60
- };
61
- }
62
- }
63
-
64
- module.exports = PositionTrackingPreprocessorMixin;
package/lib/index.js DELETED
@@ -1,29 +0,0 @@
1
- 'use strict';
2
-
3
- const Parser = require('./parser');
4
- const Serializer = require('./serializer');
5
-
6
- // Shorthands
7
- exports.parse = function parse(html, options) {
8
- const parser = new Parser(options);
9
-
10
- return parser.parse(html);
11
- };
12
-
13
- exports.parseFragment = function parseFragment(fragmentContext, html, options) {
14
- if (typeof fragmentContext === 'string') {
15
- options = html;
16
- html = fragmentContext;
17
- fragmentContext = null;
18
- }
19
-
20
- const parser = new Parser(options);
21
-
22
- return parser.parseFragment(html, fragmentContext);
23
- };
24
-
25
- exports.serialize = function(node, options) {
26
- const serializer = new Serializer(node, options);
27
-
28
- return serializer.serialize();
29
- };
@@ -1,181 +0,0 @@
1
- 'use strict';
2
-
3
- //Const
4
- const NOAH_ARK_CAPACITY = 3;
5
-
6
- //List of formatting elements
7
- class FormattingElementList {
8
- constructor(treeAdapter) {
9
- this.length = 0;
10
- this.entries = [];
11
- this.treeAdapter = treeAdapter;
12
- this.bookmark = null;
13
- }
14
-
15
- //Noah Ark's condition
16
- //OPTIMIZATION: at first we try to find possible candidates for exclusion using
17
- //lightweight heuristics without thorough attributes check.
18
- _getNoahArkConditionCandidates(newElement) {
19
- const candidates = [];
20
-
21
- if (this.length >= NOAH_ARK_CAPACITY) {
22
- const neAttrsLength = this.treeAdapter.getAttrList(newElement).length;
23
- const neTagName = this.treeAdapter.getTagName(newElement);
24
- const neNamespaceURI = this.treeAdapter.getNamespaceURI(newElement);
25
-
26
- for (let i = this.length - 1; i >= 0; i--) {
27
- const entry = this.entries[i];
28
-
29
- if (entry.type === FormattingElementList.MARKER_ENTRY) {
30
- break;
31
- }
32
-
33
- const element = entry.element;
34
- const elementAttrs = this.treeAdapter.getAttrList(element);
35
-
36
- const isCandidate =
37
- 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
-
47
- return candidates.length < NOAH_ARK_CAPACITY ? [] : candidates;
48
- }
49
-
50
- _ensureNoahArkCondition(newElement) {
51
- const candidates = this._getNoahArkConditionCandidates(newElement);
52
- let cLength = candidates.length;
53
-
54
- if (cLength) {
55
- const neAttrs = this.treeAdapter.getAttrList(newElement);
56
- const neAttrsLength = neAttrs.length;
57
- const neAttrsMap = Object.create(null);
58
-
59
- //NOTE: build attrs map for the new element so we can perform fast lookups
60
- for (let i = 0; i < neAttrsLength; i++) {
61
- const neAttr = neAttrs[i];
62
-
63
- neAttrsMap[neAttr.name] = neAttr.value;
64
- }
65
-
66
- for (let i = 0; i < neAttrsLength; i++) {
67
- for (let j = 0; j < cLength; j++) {
68
- const cAttr = candidates[j].attrs[i];
69
-
70
- if (neAttrsMap[cAttr.name] !== cAttr.value) {
71
- candidates.splice(j, 1);
72
- cLength--;
73
- }
74
-
75
- if (candidates.length < NOAH_ARK_CAPACITY) {
76
- return;
77
- }
78
- }
79
- }
80
-
81
- //NOTE: remove bottommost candidates until Noah's Ark condition will not be met
82
- for (let i = cLength - 1; i >= NOAH_ARK_CAPACITY - 1; i--) {
83
- this.entries.splice(candidates[i].idx, 1);
84
- this.length--;
85
- }
86
- }
87
- }
88
-
89
- //Mutations
90
- insertMarker() {
91
- this.entries.push({ type: FormattingElementList.MARKER_ENTRY });
92
- this.length++;
93
- }
94
-
95
- pushElement(element, token) {
96
- this._ensureNoahArkCondition(element);
97
-
98
- this.entries.push({
99
- type: FormattingElementList.ELEMENT_ENTRY,
100
- element: element,
101
- token: token
102
- });
103
-
104
- this.length++;
105
- }
106
-
107
- insertElementAfterBookmark(element, token) {
108
- let bookmarkIdx = this.length - 1;
109
-
110
- for (; bookmarkIdx >= 0; bookmarkIdx--) {
111
- if (this.entries[bookmarkIdx] === this.bookmark) {
112
- break;
113
- }
114
- }
115
-
116
- this.entries.splice(bookmarkIdx + 1, 0, {
117
- type: FormattingElementList.ELEMENT_ENTRY,
118
- element: element,
119
- token: token
120
- });
121
-
122
- this.length++;
123
- }
124
-
125
- removeEntry(entry) {
126
- for (let i = this.length - 1; i >= 0; i--) {
127
- if (this.entries[i] === entry) {
128
- this.entries.splice(i, 1);
129
- this.length--;
130
- break;
131
- }
132
- }
133
- }
134
-
135
- clearToLastMarker() {
136
- while (this.length) {
137
- const entry = this.entries.pop();
138
-
139
- this.length--;
140
-
141
- if (entry.type === FormattingElementList.MARKER_ENTRY) {
142
- break;
143
- }
144
- }
145
- }
146
-
147
- //Search
148
- getElementEntryInScopeWithTagName(tagName) {
149
- for (let i = this.length - 1; i >= 0; i--) {
150
- const entry = this.entries[i];
151
-
152
- if (entry.type === FormattingElementList.MARKER_ENTRY) {
153
- return null;
154
- }
155
-
156
- if (this.treeAdapter.getTagName(entry.element) === tagName) {
157
- return entry;
158
- }
159
- }
160
-
161
- return null;
162
- }
163
-
164
- getElementEntry(element) {
165
- for (let i = this.length - 1; i >= 0; i--) {
166
- const entry = this.entries[i];
167
-
168
- if (entry.type === FormattingElementList.ELEMENT_ENTRY && entry.element === element) {
169
- return entry;
170
- }
171
- }
172
-
173
- return null;
174
- }
175
- }
176
-
177
- //Entry types
178
- FormattingElementList.MARKER_ENTRY = 'MARKER_ENTRY';
179
- FormattingElementList.ELEMENT_ENTRY = 'ELEMENT_ENTRY';
180
-
181
- module.exports = FormattingElementList;