parse5 3.0.2 → 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.
- package/lib/extensions/location_info/open_element_stack_mixin.js +34 -0
- package/lib/extensions/location_info/parser_mixin.js +213 -0
- package/lib/extensions/location_info/tokenizer_mixin.js +117 -0
- package/lib/extensions/position_tracking/preprocessor_mixin.js +72 -0
- package/lib/index.d.ts +3 -3
- package/lib/parser/index.js +3 -3
- package/lib/parser/parser_stream.js +1 -1
- package/lib/sax/index.js +6 -1
- package/lib/serializer/index.js +1 -1
- package/lib/tokenizer/index.js +2 -6
- package/lib/tokenizer/preprocessor.js +0 -8
- package/lib/{common → utils}/merge_options.js +0 -0
- package/lib/utils/mixin.js +18 -0
- package/package.json +2 -2
- package/lib/location_info/parser_mixin.js +0 -220
- package/lib/location_info/tokenizer_mixin.js +0 -148
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var Mixin = require('../../utils/mixin'),
|
|
4
|
+
inherits = require('util').inherits;
|
|
5
|
+
|
|
6
|
+
var LocationInfoOpenElementStackMixin = module.exports = function (stack, options) {
|
|
7
|
+
Mixin.call(this, stack);
|
|
8
|
+
|
|
9
|
+
this.onItemPop = options.onItemPop;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
inherits(LocationInfoOpenElementStackMixin, Mixin);
|
|
13
|
+
|
|
14
|
+
LocationInfoOpenElementStackMixin.prototype._getOverriddenMethods = function (mxn, orig) {
|
|
15
|
+
return {
|
|
16
|
+
pop: function () {
|
|
17
|
+
mxn.onItemPop(this.current);
|
|
18
|
+
orig.pop.call(this);
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
popAllUpToHtmlElement: function () {
|
|
22
|
+
for (var i = this.stackTop; i > 0; i--)
|
|
23
|
+
mxn.onItemPop(this.items[i]);
|
|
24
|
+
|
|
25
|
+
orig.popAllUpToHtmlElement.call(this);
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
remove: function (element) {
|
|
29
|
+
mxn.onItemPop(this.current);
|
|
30
|
+
orig.remove.call(this, element);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var Mixin = require('../../utils/mixin'),
|
|
4
|
+
Tokenizer = require('../../tokenizer'),
|
|
5
|
+
LocationInfoTokenizerMixin = require('./tokenizer_mixin'),
|
|
6
|
+
PositionTrackingPreprocessorMixin = require('../position_tracking/preprocessor_mixin'),
|
|
7
|
+
LocationInfoOpenElementStackMixin = require('./open_element_stack_mixin'),
|
|
8
|
+
HTML = require('../../common/html'),
|
|
9
|
+
inherits = require('util').inherits;
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
//Aliases
|
|
13
|
+
var $ = HTML.TAG_NAMES;
|
|
14
|
+
|
|
15
|
+
var LocationInfoParserMixin = module.exports = function (parser) {
|
|
16
|
+
Mixin.call(this, parser);
|
|
17
|
+
|
|
18
|
+
this.parser = parser;
|
|
19
|
+
this.posTracker = null;
|
|
20
|
+
this.lastStartTagToken = null;
|
|
21
|
+
this.lastFosterParentingLocation = null;
|
|
22
|
+
this.currentToken = null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
inherits(LocationInfoParserMixin, Mixin);
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
LocationInfoParserMixin.prototype._setStartLocation = function (element) {
|
|
29
|
+
if (this.lastStartTagToken) {
|
|
30
|
+
element.__location = Object.create(this.lastStartTagToken.location);
|
|
31
|
+
element.__location.startTag = this.lastStartTagToken.location;
|
|
32
|
+
}
|
|
33
|
+
else
|
|
34
|
+
element.__location = null;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
LocationInfoParserMixin.prototype._setEndLocation = function (element, closingToken) {
|
|
38
|
+
var loc = element.__location;
|
|
39
|
+
|
|
40
|
+
if (loc) {
|
|
41
|
+
if (closingToken.location) {
|
|
42
|
+
var ctLoc = closingToken.location,
|
|
43
|
+
tn = this.parser.treeAdapter.getTagName(element);
|
|
44
|
+
|
|
45
|
+
// NOTE: For cases like <p> <p> </p> - First 'p' closes without a closing
|
|
46
|
+
// tag and for cases like <td> <p> </td> - 'p' closes without a closing tag.
|
|
47
|
+
var isClosingEndTag = closingToken.type === Tokenizer.END_TAG_TOKEN && tn === closingToken.tagName;
|
|
48
|
+
|
|
49
|
+
if (isClosingEndTag) {
|
|
50
|
+
loc.endTag = Object.create(ctLoc);
|
|
51
|
+
loc.endOffset = ctLoc.endOffset;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
else
|
|
55
|
+
loc.endOffset = ctLoc.startOffset;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
else if (closingToken.type === Tokenizer.EOF_TOKEN)
|
|
59
|
+
loc.endOffset = this.posTracker.offset;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
LocationInfoParserMixin.prototype._getOverriddenMethods = function (mxn, orig) {
|
|
64
|
+
return {
|
|
65
|
+
_bootstrap: function (document, fragmentContext) {
|
|
66
|
+
orig._bootstrap.call(this, document, fragmentContext);
|
|
67
|
+
|
|
68
|
+
mxn.lastStartTagToken = null;
|
|
69
|
+
mxn.lastFosterParentingLocation = null;
|
|
70
|
+
mxn.currentToken = null;
|
|
71
|
+
mxn.posTracker = new PositionTrackingPreprocessorMixin(this.tokenizer.preprocessor);
|
|
72
|
+
|
|
73
|
+
new LocationInfoTokenizerMixin(this.tokenizer);
|
|
74
|
+
|
|
75
|
+
new LocationInfoOpenElementStackMixin(this.openElements, {
|
|
76
|
+
onItemPop: function (element) {
|
|
77
|
+
mxn._setEndLocation(element, mxn.currentToken);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
_runParsingLoop: function (scriptHandler) {
|
|
83
|
+
orig._runParsingLoop.call(this, scriptHandler);
|
|
84
|
+
|
|
85
|
+
// NOTE: generate location info for elements
|
|
86
|
+
// that remains on open element stack
|
|
87
|
+
for (var i = this.openElements.stackTop; i >= 0; i--)
|
|
88
|
+
mxn._setEndLocation(this.openElements.items[i], mxn.currentToken);
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
//Token processing
|
|
93
|
+
_processTokenInForeignContent: function (token) {
|
|
94
|
+
mxn.currentToken = token;
|
|
95
|
+
orig._processTokenInForeignContent.call(this, token);
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
_processToken: function (token) {
|
|
99
|
+
mxn.currentToken = token;
|
|
100
|
+
orig._processToken.call(this, token);
|
|
101
|
+
|
|
102
|
+
//NOTE: <body> and <html> are never popped from the stack, so we need to updated
|
|
103
|
+
//their end location explicitly.
|
|
104
|
+
var requireExplicitUpdate = token.type === Tokenizer.END_TAG_TOKEN &&
|
|
105
|
+
(token.tagName === $.HTML ||
|
|
106
|
+
token.tagName === $.BODY && this.openElements.hasInScope($.BODY));
|
|
107
|
+
|
|
108
|
+
if (requireExplicitUpdate) {
|
|
109
|
+
for (var i = this.openElements.stackTop; i >= 0; i--) {
|
|
110
|
+
var element = this.openElements.items[i];
|
|
111
|
+
|
|
112
|
+
if (this.treeAdapter.getTagName(element) === token.tagName) {
|
|
113
|
+
mxn._setEndLocation(element, token);
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
//Doctype
|
|
122
|
+
_setDocumentType: function (token) {
|
|
123
|
+
orig._setDocumentType.call(this, token);
|
|
124
|
+
|
|
125
|
+
var documentChildren = this.treeAdapter.getChildNodes(this.document),
|
|
126
|
+
cnLength = documentChildren.length;
|
|
127
|
+
|
|
128
|
+
for (var i = 0; i < cnLength; i++) {
|
|
129
|
+
var node = documentChildren[i];
|
|
130
|
+
|
|
131
|
+
if (this.treeAdapter.isDocumentTypeNode(node)) {
|
|
132
|
+
node.__location = token.location;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
//Elements
|
|
140
|
+
_attachElementToTree: function (element) {
|
|
141
|
+
//NOTE: _attachElementToTree is called from _appendElement, _insertElement and _insertTemplate methods.
|
|
142
|
+
//So we will use token location stored in this methods for the element.
|
|
143
|
+
mxn._setStartLocation(element);
|
|
144
|
+
mxn.lastStartTagToken = null;
|
|
145
|
+
orig._attachElementToTree.call(this, element);
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
_appendElement: function (token, namespaceURI) {
|
|
149
|
+
mxn.lastStartTagToken = token;
|
|
150
|
+
orig._appendElement.call(this, token, namespaceURI);
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
_insertElement: function (token, namespaceURI) {
|
|
154
|
+
mxn.lastStartTagToken = token;
|
|
155
|
+
orig._insertElement.call(this, token, namespaceURI);
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
_insertTemplate: function (token) {
|
|
159
|
+
mxn.lastStartTagToken = token;
|
|
160
|
+
orig._insertTemplate.call(this, token);
|
|
161
|
+
|
|
162
|
+
var tmplContent = this.treeAdapter.getTemplateContent(this.openElements.current);
|
|
163
|
+
|
|
164
|
+
tmplContent.__location = null;
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
_insertFakeRootElement: function () {
|
|
168
|
+
orig._insertFakeRootElement.call(this);
|
|
169
|
+
this.openElements.current.__location = null;
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
//Comments
|
|
173
|
+
_appendCommentNode: function (token, parent) {
|
|
174
|
+
orig._appendCommentNode.call(this, token, parent);
|
|
175
|
+
|
|
176
|
+
var children = this.treeAdapter.getChildNodes(parent),
|
|
177
|
+
commentNode = children[children.length - 1];
|
|
178
|
+
|
|
179
|
+
commentNode.__location = token.location;
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
//Text
|
|
183
|
+
_findFosterParentingLocation: function () {
|
|
184
|
+
//NOTE: store last foster parenting location, so we will be able to find inserted text
|
|
185
|
+
//in case of foster parenting
|
|
186
|
+
mxn.lastFosterParentingLocation = orig._findFosterParentingLocation.call(this);
|
|
187
|
+
|
|
188
|
+
return mxn.lastFosterParentingLocation;
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
_insertCharacters: function (token) {
|
|
192
|
+
orig._insertCharacters.call(this, token);
|
|
193
|
+
|
|
194
|
+
var hasFosterParent = this._shouldFosterParentOnInsertion(),
|
|
195
|
+
parent = hasFosterParent && mxn.lastFosterParentingLocation.parent ||
|
|
196
|
+
this.openElements.currentTmplContent ||
|
|
197
|
+
this.openElements.current,
|
|
198
|
+
siblings = this.treeAdapter.getChildNodes(parent),
|
|
199
|
+
textNodeIdx = hasFosterParent && mxn.lastFosterParentingLocation.beforeElement ?
|
|
200
|
+
siblings.indexOf(mxn.lastFosterParentingLocation.beforeElement) - 1 :
|
|
201
|
+
siblings.length - 1,
|
|
202
|
+
textNode = siblings[textNodeIdx];
|
|
203
|
+
|
|
204
|
+
//NOTE: if we have location assigned by another token, then just update end position
|
|
205
|
+
if (textNode.__location)
|
|
206
|
+
textNode.__location.endOffset = token.location.endOffset;
|
|
207
|
+
|
|
208
|
+
else
|
|
209
|
+
textNode.__location = token.location;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
|
+
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var Mixin = require('../../utils/mixin'),
|
|
4
|
+
Tokenizer = require('../../tokenizer'),
|
|
5
|
+
PositionTrackingPreprocessorMixin = require('../position_tracking/preprocessor_mixin'),
|
|
6
|
+
inherits = require('util').inherits;
|
|
7
|
+
|
|
8
|
+
var LocationInfoTokenizerMixin = module.exports = function (tokenizer) {
|
|
9
|
+
Mixin.call(this, tokenizer);
|
|
10
|
+
|
|
11
|
+
this.tokenizer = tokenizer;
|
|
12
|
+
this.posTracker = new PositionTrackingPreprocessorMixin(tokenizer.preprocessor);
|
|
13
|
+
this.currentAttrLocation = null;
|
|
14
|
+
this.currentTokenLocation = null;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
inherits(LocationInfoTokenizerMixin, Mixin);
|
|
18
|
+
|
|
19
|
+
LocationInfoTokenizerMixin.prototype._getCurrentLocation = function () {
|
|
20
|
+
return {
|
|
21
|
+
line: this.posTracker.line,
|
|
22
|
+
col: this.posTracker.col,
|
|
23
|
+
startOffset: this.posTracker.offset,
|
|
24
|
+
endOffset: -1
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
LocationInfoTokenizerMixin.prototype._attachCurrentAttrLocationInfo = function () {
|
|
29
|
+
this.currentAttrLocation.endOffset = this.posTracker.offset;
|
|
30
|
+
|
|
31
|
+
var currentToken = this.tokenizer.currentToken,
|
|
32
|
+
currentAttr = this.tokenizer.currentAttr;
|
|
33
|
+
|
|
34
|
+
if (!currentToken.location.attrs)
|
|
35
|
+
currentToken.location.attrs = Object.create(null);
|
|
36
|
+
|
|
37
|
+
currentToken.location.attrs[currentAttr.name] = this.currentAttrLocation;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
LocationInfoTokenizerMixin.prototype._getOverriddenMethods = function (mxn, orig) {
|
|
41
|
+
var methods = {
|
|
42
|
+
_createStartTagToken: function () {
|
|
43
|
+
orig._createStartTagToken.call(this);
|
|
44
|
+
this.currentToken.location = mxn.currentTokenLocation;
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
_createEndTagToken: function () {
|
|
48
|
+
orig._createEndTagToken.call(this);
|
|
49
|
+
this.currentToken.location = mxn.currentTokenLocation;
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
_createCommentToken: function () {
|
|
53
|
+
orig._createCommentToken.call(this);
|
|
54
|
+
this.currentToken.location = mxn.currentTokenLocation;
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
_createDoctypeToken: function (initialName) {
|
|
58
|
+
orig._createDoctypeToken.call(this, initialName);
|
|
59
|
+
this.currentToken.location = mxn.currentTokenLocation;
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
_createCharacterToken: function (type, ch) {
|
|
63
|
+
orig._createCharacterToken.call(this, type, ch);
|
|
64
|
+
this.currentCharacterToken.location = mxn.currentTokenLocation;
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
_createAttr: function (attrNameFirstCh) {
|
|
68
|
+
orig._createAttr.call(this, attrNameFirstCh);
|
|
69
|
+
mxn.currentAttrLocation = mxn._getCurrentLocation();
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
_leaveAttrName: function (toState) {
|
|
73
|
+
orig._leaveAttrName.call(this, toState);
|
|
74
|
+
mxn._attachCurrentAttrLocationInfo();
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
_leaveAttrValue: function (toState) {
|
|
78
|
+
orig._leaveAttrValue.call(this, toState);
|
|
79
|
+
mxn._attachCurrentAttrLocationInfo();
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
_emitCurrentToken: function () {
|
|
83
|
+
//NOTE: if we have pending character token make it's end location equal to the
|
|
84
|
+
//current token's start location.
|
|
85
|
+
if (this.currentCharacterToken)
|
|
86
|
+
this.currentCharacterToken.location.endOffset = this.currentToken.location.startOffset;
|
|
87
|
+
|
|
88
|
+
this.currentToken.location.endOffset = mxn.posTracker.offset + 1;
|
|
89
|
+
orig._emitCurrentToken.call(this);
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
_emitCurrentCharacterToken: function () {
|
|
93
|
+
//NOTE: if we have character token and it's location wasn't set in the _emitCurrentToken(),
|
|
94
|
+
//then set it's location at the current preprocessor position.
|
|
95
|
+
//We don't need to increment preprocessor position, since character token
|
|
96
|
+
//emission is always forced by the start of the next character token here.
|
|
97
|
+
//So, we already have advanced position.
|
|
98
|
+
if (this.currentCharacterToken && this.currentCharacterToken.location.endOffset === -1)
|
|
99
|
+
this.currentCharacterToken.location.endOffset = mxn.posTracker.offset;
|
|
100
|
+
|
|
101
|
+
orig._emitCurrentCharacterToken.call(this);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
//NOTE: patch initial states for each mode to obtain token start position
|
|
106
|
+
Object.keys(Tokenizer.MODE).forEach(function (modeName) {
|
|
107
|
+
var state = Tokenizer.MODE[modeName];
|
|
108
|
+
|
|
109
|
+
methods[state] = function (cp) {
|
|
110
|
+
mxn.currentTokenLocation = mxn._getCurrentLocation();
|
|
111
|
+
orig[state].call(this, cp);
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return methods;
|
|
116
|
+
};
|
|
117
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var Mixin = require('../../utils/mixin'),
|
|
4
|
+
inherits = require('util').inherits,
|
|
5
|
+
UNICODE = require('../../common/unicode');
|
|
6
|
+
|
|
7
|
+
//Aliases
|
|
8
|
+
var $ = UNICODE.CODE_POINTS;
|
|
9
|
+
|
|
10
|
+
var PositionTrackingPreprocessorMixin = module.exports = function (preprocessor) {
|
|
11
|
+
// NOTE: avoid installing tracker twice
|
|
12
|
+
if (!preprocessor.__locTracker) {
|
|
13
|
+
preprocessor.__locTracker = this;
|
|
14
|
+
|
|
15
|
+
Mixin.call(this, preprocessor);
|
|
16
|
+
|
|
17
|
+
this.preprocessor = preprocessor;
|
|
18
|
+
this.isEol = false;
|
|
19
|
+
this.lineStartPos = 0;
|
|
20
|
+
this.droppedBufferSize = 0;
|
|
21
|
+
|
|
22
|
+
this.col = -1;
|
|
23
|
+
this.line = 1;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return preprocessor.__locTracker;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
inherits(PositionTrackingPreprocessorMixin, Mixin);
|
|
30
|
+
|
|
31
|
+
Object.defineProperty(PositionTrackingPreprocessorMixin.prototype, 'offset', {
|
|
32
|
+
get: function () {
|
|
33
|
+
return this.droppedBufferSize + this.preprocessor.pos;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
PositionTrackingPreprocessorMixin.prototype._getOverriddenMethods = function (mxn, orig) {
|
|
38
|
+
return {
|
|
39
|
+
advance: function () {
|
|
40
|
+
var cp = orig.advance.call(this);
|
|
41
|
+
|
|
42
|
+
//NOTE: LF should be in the last column of the line
|
|
43
|
+
if (mxn.isEol) {
|
|
44
|
+
mxn.isEol = false;
|
|
45
|
+
mxn.line++;
|
|
46
|
+
mxn.lineStartPos = mxn.offset;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (cp === $.LINE_FEED)
|
|
50
|
+
mxn.isEol = true;
|
|
51
|
+
|
|
52
|
+
mxn.col = mxn.offset - mxn.lineStartPos + 1;
|
|
53
|
+
|
|
54
|
+
return cp;
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
retreat: function () {
|
|
58
|
+
orig.retreat.call(this);
|
|
59
|
+
mxn.isEol = false;
|
|
60
|
+
|
|
61
|
+
mxn.col = mxn.offset - mxn.lineStartPos + 1;
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
dropParsedChunk: function () {
|
|
65
|
+
var prevPos = this.pos;
|
|
66
|
+
|
|
67
|
+
orig.dropParsedChunk.call(this);
|
|
68
|
+
|
|
69
|
+
mxn.droppedBufferSize += prevPos - this.pos;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
};
|
package/lib/index.d.ts
CHANGED
|
@@ -54,9 +54,9 @@ declare namespace MarkupData {
|
|
|
54
54
|
declare namespace Options {
|
|
55
55
|
export interface ParserOptions {
|
|
56
56
|
/**
|
|
57
|
-
* Enables source code location information
|
|
58
|
-
*
|
|
59
|
-
* If the element was implicitly created by the parser
|
|
57
|
+
* Enables source code location information. When enabled, each node (except the root node) will have a `__location` property.
|
|
58
|
+
* If the node is not an empty element, `__location` will be a {@link MarkupData.ElementLocation} object, otherwise it will be {@link MarkupData.Location}.
|
|
59
|
+
* If the element was implicitly created by the parser (as part of [tree correction](https://html.spec.whatwg.org/multipage/syntax.html#an-introduction-to-error-handling-and-strange-cases-in-the-parser)), its `__location` property will be `undefined`.
|
|
60
60
|
*
|
|
61
61
|
* **Default:** `false`
|
|
62
62
|
*/
|
package/lib/parser/index.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
var Tokenizer = require('../tokenizer'),
|
|
4
4
|
OpenElementStack = require('./open_element_stack'),
|
|
5
5
|
FormattingElementList = require('./formatting_element_list'),
|
|
6
|
-
|
|
6
|
+
LocationInfoParserMixin = require('../extensions/location_info/parser_mixin'),
|
|
7
7
|
defaultTreeAdapter = require('../tree_adapters/default'),
|
|
8
|
+
mergeOptions = require('../utils/merge_options'),
|
|
8
9
|
doctype = require('../common/doctype'),
|
|
9
10
|
foreignContent = require('../common/foreign_content'),
|
|
10
|
-
mergeOptions = require('../common/merge_options'),
|
|
11
11
|
UNICODE = require('../common/unicode'),
|
|
12
12
|
HTML = require('../common/html');
|
|
13
13
|
|
|
@@ -310,7 +310,7 @@ var Parser = module.exports = function (options) {
|
|
|
310
310
|
this.pendingScript = null;
|
|
311
311
|
|
|
312
312
|
if (this.options.locationInfo)
|
|
313
|
-
|
|
313
|
+
new LocationInfoParserMixin(this);
|
|
314
314
|
};
|
|
315
315
|
|
|
316
316
|
// API
|
|
@@ -35,7 +35,7 @@ ParserStream.prototype._write = function (chunk, encoding, callback) {
|
|
|
35
35
|
|
|
36
36
|
ParserStream.prototype.end = function (chunk, encoding, callback) {
|
|
37
37
|
this.lastChunkWritten = true;
|
|
38
|
-
WritableStream.prototype.end.call(this, chunk, encoding, callback);
|
|
38
|
+
WritableStream.prototype.end.call(this, chunk || '', encoding, callback);
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
//Scriptable parser implementation
|
package/lib/sax/index.js
CHANGED
|
@@ -4,8 +4,9 @@ var TransformStream = require('stream').Transform,
|
|
|
4
4
|
DevNullStream = require('./dev_null_stream'),
|
|
5
5
|
inherits = require('util').inherits,
|
|
6
6
|
Tokenizer = require('../tokenizer'),
|
|
7
|
+
LocationInfoTokenizerMixin = require('../extensions/location_info/tokenizer_mixin'),
|
|
7
8
|
ParserFeedbackSimulator = require('./parser_feedback_simulator'),
|
|
8
|
-
mergeOptions = require('../
|
|
9
|
+
mergeOptions = require('../utils/merge_options');
|
|
9
10
|
|
|
10
11
|
var DEFAULT_OPTIONS = {
|
|
11
12
|
locationInfo: false
|
|
@@ -17,6 +18,10 @@ var SAXParser = module.exports = function (options) {
|
|
|
17
18
|
this.options = mergeOptions(DEFAULT_OPTIONS, options);
|
|
18
19
|
|
|
19
20
|
this.tokenizer = new Tokenizer(options);
|
|
21
|
+
|
|
22
|
+
if (this.options.locationInfo)
|
|
23
|
+
new LocationInfoTokenizerMixin(this.tokenizer);
|
|
24
|
+
|
|
20
25
|
this.parserFeedbackSimulator = new ParserFeedbackSimulator(this.tokenizer);
|
|
21
26
|
|
|
22
27
|
this.pendingText = null;
|
package/lib/serializer/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var defaultTreeAdapter = require('../tree_adapters/default'),
|
|
4
|
+
mergeOptions = require('../utils/merge_options'),
|
|
4
5
|
doctype = require('../common/doctype'),
|
|
5
|
-
mergeOptions = require('../common/merge_options'),
|
|
6
6
|
HTML = require('../common/html');
|
|
7
7
|
|
|
8
8
|
//Aliases
|
package/lib/tokenizer/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var Preprocessor = require('./preprocessor'),
|
|
4
|
-
locationInfoMixin = require('../location_info/tokenizer_mixin'),
|
|
5
4
|
UNICODE = require('../common/unicode'),
|
|
6
5
|
neTree = require('./named_entity_data');
|
|
7
6
|
|
|
@@ -173,7 +172,7 @@ function findNamedEntityTreeBranch(nodeIx, cp) {
|
|
|
173
172
|
|
|
174
173
|
|
|
175
174
|
//Tokenizer
|
|
176
|
-
var Tokenizer = module.exports = function (
|
|
175
|
+
var Tokenizer = module.exports = function () {
|
|
177
176
|
this.preprocessor = new Preprocessor();
|
|
178
177
|
|
|
179
178
|
this.tokenQueue = [];
|
|
@@ -193,9 +192,6 @@ var Tokenizer = module.exports = function (options) {
|
|
|
193
192
|
this.currentCharacterToken = null;
|
|
194
193
|
this.currentToken = null;
|
|
195
194
|
this.currentAttr = null;
|
|
196
|
-
|
|
197
|
-
if (options && options.locationInfo)
|
|
198
|
-
locationInfoMixin.assign(this);
|
|
199
195
|
};
|
|
200
196
|
|
|
201
197
|
//Token types
|
|
@@ -210,7 +206,7 @@ Tokenizer.EOF_TOKEN = 'EOF_TOKEN';
|
|
|
210
206
|
Tokenizer.HIBERNATION_TOKEN = 'HIBERNATION_TOKEN';
|
|
211
207
|
|
|
212
208
|
//Tokenizer initial states for different modes
|
|
213
|
-
Tokenizer.MODE =
|
|
209
|
+
Tokenizer.MODE = {
|
|
214
210
|
DATA: DATA_STATE,
|
|
215
211
|
RCDATA: RCDATA_STATE,
|
|
216
212
|
RAWTEXT: RAWTEXT_STATE,
|
|
@@ -32,7 +32,6 @@ var Preprocessor = module.exports = function () {
|
|
|
32
32
|
this.pos = -1;
|
|
33
33
|
this.lastGapPos = -1;
|
|
34
34
|
this.lastCharPos = -1;
|
|
35
|
-
this.droppedBufferSize = 0;
|
|
36
35
|
|
|
37
36
|
this.gapStack = [];
|
|
38
37
|
|
|
@@ -43,16 +42,9 @@ var Preprocessor = module.exports = function () {
|
|
|
43
42
|
this.bufferWaterline = DEFAULT_BUFFER_WATERLINE;
|
|
44
43
|
};
|
|
45
44
|
|
|
46
|
-
Object.defineProperty(Preprocessor.prototype, 'sourcePos', {
|
|
47
|
-
get: function () {
|
|
48
|
-
return this.droppedBufferSize + this.pos;
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
45
|
Preprocessor.prototype.dropParsedChunk = function () {
|
|
53
46
|
if (this.pos > this.bufferWaterline) {
|
|
54
47
|
this.lastCharPos -= this.pos;
|
|
55
|
-
this.droppedBufferSize += this.pos;
|
|
56
48
|
this.html = this.html.substring(this.pos);
|
|
57
49
|
this.pos = 0;
|
|
58
50
|
this.lastGapPos = -1;
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var Mixin = module.exports = function (host) {
|
|
4
|
+
var originalMethods = {},
|
|
5
|
+
overriddenMethods = this._getOverriddenMethods(this, originalMethods);
|
|
6
|
+
|
|
7
|
+
Object.keys(overriddenMethods).forEach(function (key) {
|
|
8
|
+
if (typeof overriddenMethods[key] === 'function') {
|
|
9
|
+
originalMethods[key] = host[key];
|
|
10
|
+
host[key] = overriddenMethods[key];
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
Mixin.prototype._getOverriddenMethods = function () {
|
|
16
|
+
throw new Error('Not implemented');
|
|
17
|
+
};
|
|
18
|
+
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "parse5",
|
|
3
3
|
"description": "HTML parsing/serialization toolset for Node.js. WHATWG HTML Living Standard (aka HTML5)-compliant.",
|
|
4
|
-
"version": "3.0.
|
|
4
|
+
"version": "3.0.3",
|
|
5
5
|
"author": "Ivan Nikulin <ifaaan@gmail.com> (https://github.com/inikulin)",
|
|
6
6
|
"contributors": "https://github.com/inikulin/parse5/graphs/contributors",
|
|
7
7
|
"homepage": "https://github.com/inikulin/parse5",
|
|
@@ -58,6 +58,6 @@
|
|
|
58
58
|
"lib"
|
|
59
59
|
],
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@types/node": "
|
|
61
|
+
"@types/node": "*"
|
|
62
62
|
}
|
|
63
63
|
}
|
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var OpenElementStack = require('../parser/open_element_stack'),
|
|
4
|
-
Tokenizer = require('../tokenizer'),
|
|
5
|
-
HTML = require('../common/html');
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
//Aliases
|
|
9
|
-
var $ = HTML.TAG_NAMES;
|
|
10
|
-
|
|
11
|
-
exports.assign = function (parser) {
|
|
12
|
-
//NOTE: obtain Parser proto this way to avoid module circular references
|
|
13
|
-
var parserProto = Object.getPrototypeOf(parser),
|
|
14
|
-
treeAdapter = parser.treeAdapter,
|
|
15
|
-
attachableElementLocation = null,
|
|
16
|
-
lastFosterParentingLocation = null,
|
|
17
|
-
currentToken = null;
|
|
18
|
-
|
|
19
|
-
function setEndLocation(element, closingToken) {
|
|
20
|
-
var loc = element.__location;
|
|
21
|
-
|
|
22
|
-
if (!loc)
|
|
23
|
-
return;
|
|
24
|
-
|
|
25
|
-
if (!loc.startTag) {
|
|
26
|
-
loc.startTag = {
|
|
27
|
-
line: loc.line,
|
|
28
|
-
col: loc.col,
|
|
29
|
-
startOffset: loc.startOffset,
|
|
30
|
-
endOffset: loc.endOffset
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
if (loc.attrs)
|
|
34
|
-
loc.startTag.attrs = loc.attrs;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (closingToken.location) {
|
|
38
|
-
var ctLocation = closingToken.location,
|
|
39
|
-
tn = treeAdapter.getTagName(element),
|
|
40
|
-
// NOTE: For cases like <p> <p> </p> - First 'p' closes without a closing tag and
|
|
41
|
-
// for cases like <td> <p> </td> - 'p' closes without a closing tag
|
|
42
|
-
isClosingEndTag = closingToken.type === Tokenizer.END_TAG_TOKEN &&
|
|
43
|
-
tn === closingToken.tagName;
|
|
44
|
-
|
|
45
|
-
if (isClosingEndTag) {
|
|
46
|
-
loc.endTag = {
|
|
47
|
-
line: ctLocation.line,
|
|
48
|
-
col: ctLocation.col,
|
|
49
|
-
startOffset: ctLocation.startOffset,
|
|
50
|
-
endOffset: ctLocation.endOffset
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (isClosingEndTag)
|
|
55
|
-
loc.endOffset = ctLocation.endOffset;
|
|
56
|
-
else
|
|
57
|
-
loc.endOffset = ctLocation.startOffset;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
else if (closingToken.type === Tokenizer.EOF_TOKEN)
|
|
61
|
-
loc.endOffset = parser.tokenizer.preprocessor.sourcePos;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
//NOTE: patch _bootstrap method
|
|
65
|
-
parser._bootstrap = function (document, fragmentContext) {
|
|
66
|
-
parserProto._bootstrap.call(this, document, fragmentContext);
|
|
67
|
-
|
|
68
|
-
attachableElementLocation = null;
|
|
69
|
-
lastFosterParentingLocation = null;
|
|
70
|
-
currentToken = null;
|
|
71
|
-
|
|
72
|
-
//OpenElementStack
|
|
73
|
-
parser.openElements.pop = function () {
|
|
74
|
-
setEndLocation(this.current, currentToken);
|
|
75
|
-
OpenElementStack.prototype.pop.call(this);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
parser.openElements.popAllUpToHtmlElement = function () {
|
|
79
|
-
for (var i = this.stackTop; i > 0; i--)
|
|
80
|
-
setEndLocation(this.items[i], currentToken);
|
|
81
|
-
|
|
82
|
-
OpenElementStack.prototype.popAllUpToHtmlElement.call(this);
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
parser.openElements.remove = function (element) {
|
|
86
|
-
setEndLocation(element, currentToken);
|
|
87
|
-
OpenElementStack.prototype.remove.call(this, element);
|
|
88
|
-
};
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
parser._runParsingLoop = function (scriptHandler) {
|
|
92
|
-
parserProto._runParsingLoop.call(this, scriptHandler);
|
|
93
|
-
|
|
94
|
-
// NOTE: generate location info for elements
|
|
95
|
-
// that remains on open element stack
|
|
96
|
-
for (var i = parser.openElements.stackTop; i >= 0; i--)
|
|
97
|
-
setEndLocation(parser.openElements.items[i], currentToken);
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
//Token processing
|
|
102
|
-
parser._processTokenInForeignContent = function (token) {
|
|
103
|
-
currentToken = token;
|
|
104
|
-
parserProto._processTokenInForeignContent.call(this, token);
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
parser._processToken = function (token) {
|
|
108
|
-
currentToken = token;
|
|
109
|
-
parserProto._processToken.call(this, token);
|
|
110
|
-
|
|
111
|
-
//NOTE: <body> and <html> are never popped from the stack, so we need to updated
|
|
112
|
-
//their end location explicitly.
|
|
113
|
-
if (token.type === Tokenizer.END_TAG_TOKEN &&
|
|
114
|
-
(token.tagName === $.HTML ||
|
|
115
|
-
token.tagName === $.BODY && this.openElements.hasInScope($.BODY))) {
|
|
116
|
-
for (var i = this.openElements.stackTop; i >= 0; i--) {
|
|
117
|
-
var element = this.openElements.items[i];
|
|
118
|
-
|
|
119
|
-
if (this.treeAdapter.getTagName(element) === token.tagName) {
|
|
120
|
-
setEndLocation(element, token);
|
|
121
|
-
break;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
//Doctype
|
|
129
|
-
parser._setDocumentType = function (token) {
|
|
130
|
-
parserProto._setDocumentType.call(this, token);
|
|
131
|
-
|
|
132
|
-
var documentChildren = this.treeAdapter.getChildNodes(this.document),
|
|
133
|
-
cnLength = documentChildren.length;
|
|
134
|
-
|
|
135
|
-
for (var i = 0; i < cnLength; i++) {
|
|
136
|
-
var node = documentChildren[i];
|
|
137
|
-
|
|
138
|
-
if (this.treeAdapter.isDocumentTypeNode(node)) {
|
|
139
|
-
node.__location = token.location;
|
|
140
|
-
break;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
//Elements
|
|
147
|
-
parser._attachElementToTree = function (element) {
|
|
148
|
-
//NOTE: _attachElementToTree is called from _appendElement, _insertElement and _insertTemplate methods.
|
|
149
|
-
//So we will use token location stored in this methods for the element.
|
|
150
|
-
element.__location = attachableElementLocation || null;
|
|
151
|
-
attachableElementLocation = null;
|
|
152
|
-
parserProto._attachElementToTree.call(this, element);
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
parser._appendElement = function (token, namespaceURI) {
|
|
156
|
-
attachableElementLocation = token.location;
|
|
157
|
-
parserProto._appendElement.call(this, token, namespaceURI);
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
parser._insertElement = function (token, namespaceURI) {
|
|
161
|
-
attachableElementLocation = token.location;
|
|
162
|
-
parserProto._insertElement.call(this, token, namespaceURI);
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
parser._insertTemplate = function (token) {
|
|
166
|
-
attachableElementLocation = token.location;
|
|
167
|
-
parserProto._insertTemplate.call(this, token);
|
|
168
|
-
|
|
169
|
-
var tmplContent = this.treeAdapter.getTemplateContent(this.openElements.current);
|
|
170
|
-
|
|
171
|
-
tmplContent.__location = null;
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
parser._insertFakeRootElement = function () {
|
|
175
|
-
parserProto._insertFakeRootElement.call(this);
|
|
176
|
-
this.openElements.current.__location = null;
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
//Comments
|
|
181
|
-
parser._appendCommentNode = function (token, parent) {
|
|
182
|
-
parserProto._appendCommentNode.call(this, token, parent);
|
|
183
|
-
|
|
184
|
-
var children = this.treeAdapter.getChildNodes(parent),
|
|
185
|
-
commentNode = children[children.length - 1];
|
|
186
|
-
|
|
187
|
-
commentNode.__location = token.location;
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
//Text
|
|
192
|
-
parser._findFosterParentingLocation = function () {
|
|
193
|
-
//NOTE: store last foster parenting location, so we will be able to find inserted text
|
|
194
|
-
//in case of foster parenting
|
|
195
|
-
lastFosterParentingLocation = parserProto._findFosterParentingLocation.call(this);
|
|
196
|
-
return lastFosterParentingLocation;
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
parser._insertCharacters = function (token) {
|
|
200
|
-
parserProto._insertCharacters.call(this, token);
|
|
201
|
-
|
|
202
|
-
var hasFosterParent = this._shouldFosterParentOnInsertion(),
|
|
203
|
-
parent = hasFosterParent && lastFosterParentingLocation.parent ||
|
|
204
|
-
this.openElements.currentTmplContent ||
|
|
205
|
-
this.openElements.current,
|
|
206
|
-
siblings = this.treeAdapter.getChildNodes(parent),
|
|
207
|
-
textNodeIdx = hasFosterParent && lastFosterParentingLocation.beforeElement ?
|
|
208
|
-
siblings.indexOf(lastFosterParentingLocation.beforeElement) - 1 :
|
|
209
|
-
siblings.length - 1,
|
|
210
|
-
textNode = siblings[textNodeIdx];
|
|
211
|
-
|
|
212
|
-
//NOTE: if we have location assigned by another token, then just update end position
|
|
213
|
-
if (textNode.__location)
|
|
214
|
-
textNode.__location.endOffset = token.location.endOffset;
|
|
215
|
-
|
|
216
|
-
else
|
|
217
|
-
textNode.__location = token.location;
|
|
218
|
-
};
|
|
219
|
-
};
|
|
220
|
-
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var UNICODE = require('../common/unicode');
|
|
4
|
-
|
|
5
|
-
//Aliases
|
|
6
|
-
var $ = UNICODE.CODE_POINTS;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
exports.assign = function (tokenizer) {
|
|
10
|
-
//NOTE: obtain Tokenizer proto this way to avoid module circular references
|
|
11
|
-
var tokenizerProto = Object.getPrototypeOf(tokenizer),
|
|
12
|
-
tokenStartOffset = -1,
|
|
13
|
-
tokenCol = -1,
|
|
14
|
-
tokenLine = 1,
|
|
15
|
-
isEol = false,
|
|
16
|
-
lineStartPos = 0,
|
|
17
|
-
col = -1,
|
|
18
|
-
line = 1;
|
|
19
|
-
|
|
20
|
-
function attachLocationInfo(token) {
|
|
21
|
-
token.location = {
|
|
22
|
-
line: tokenLine,
|
|
23
|
-
col: tokenCol,
|
|
24
|
-
startOffset: tokenStartOffset,
|
|
25
|
-
endOffset: -1
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
//NOTE: patch consumption method to track line/col information
|
|
30
|
-
tokenizer._consume = function () {
|
|
31
|
-
var cp = tokenizerProto._consume.call(this);
|
|
32
|
-
|
|
33
|
-
//NOTE: LF should be in the last column of the line
|
|
34
|
-
if (isEol) {
|
|
35
|
-
isEol = false;
|
|
36
|
-
line++;
|
|
37
|
-
lineStartPos = this.preprocessor.sourcePos;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (cp === $.LINE_FEED)
|
|
41
|
-
isEol = true;
|
|
42
|
-
|
|
43
|
-
col = this.preprocessor.sourcePos - lineStartPos + 1;
|
|
44
|
-
|
|
45
|
-
return cp;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
tokenizer._unconsume = function () {
|
|
49
|
-
tokenizerProto._unconsume.call(this);
|
|
50
|
-
isEol = false;
|
|
51
|
-
|
|
52
|
-
col = this.preprocessor.sourcePos - lineStartPos + 1;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
//NOTE: patch token creation methods and attach location objects
|
|
56
|
-
tokenizer._createStartTagToken = function () {
|
|
57
|
-
tokenizerProto._createStartTagToken.call(this);
|
|
58
|
-
attachLocationInfo(this.currentToken);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
tokenizer._createEndTagToken = function () {
|
|
62
|
-
tokenizerProto._createEndTagToken.call(this);
|
|
63
|
-
attachLocationInfo(this.currentToken);
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
tokenizer._createCommentToken = function () {
|
|
67
|
-
tokenizerProto._createCommentToken.call(this);
|
|
68
|
-
attachLocationInfo(this.currentToken);
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
tokenizer._createDoctypeToken = function (initialName) {
|
|
72
|
-
tokenizerProto._createDoctypeToken.call(this, initialName);
|
|
73
|
-
attachLocationInfo(this.currentToken);
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
tokenizer._createCharacterToken = function (type, ch) {
|
|
77
|
-
tokenizerProto._createCharacterToken.call(this, type, ch);
|
|
78
|
-
attachLocationInfo(this.currentCharacterToken);
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
tokenizer._createAttr = function (attrNameFirstCh) {
|
|
82
|
-
tokenizerProto._createAttr.call(this, attrNameFirstCh);
|
|
83
|
-
this.currentAttrLocation = {
|
|
84
|
-
line: line,
|
|
85
|
-
col: col,
|
|
86
|
-
startOffset: this.preprocessor.sourcePos,
|
|
87
|
-
endOffset: -1
|
|
88
|
-
};
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
tokenizer._leaveAttrName = function (toState) {
|
|
92
|
-
tokenizerProto._leaveAttrName.call(this, toState);
|
|
93
|
-
this._attachCurrentAttrLocationInfo();
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
tokenizer._leaveAttrValue = function (toState) {
|
|
97
|
-
tokenizerProto._leaveAttrValue.call(this, toState);
|
|
98
|
-
this._attachCurrentAttrLocationInfo();
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
tokenizer._attachCurrentAttrLocationInfo = function () {
|
|
102
|
-
this.currentAttrLocation.endOffset = this.preprocessor.sourcePos;
|
|
103
|
-
|
|
104
|
-
if (!this.currentToken.location.attrs)
|
|
105
|
-
this.currentToken.location.attrs = Object.create(null);
|
|
106
|
-
|
|
107
|
-
this.currentToken.location.attrs[this.currentAttr.name] = this.currentAttrLocation;
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
//NOTE: patch token emission methods to determine end location
|
|
111
|
-
tokenizer._emitCurrentToken = function () {
|
|
112
|
-
//NOTE: if we have pending character token make it's end location equal to the
|
|
113
|
-
//current token's start location.
|
|
114
|
-
if (this.currentCharacterToken)
|
|
115
|
-
this.currentCharacterToken.location.endOffset = this.currentToken.location.startOffset;
|
|
116
|
-
|
|
117
|
-
this.currentToken.location.endOffset = this.preprocessor.sourcePos + 1;
|
|
118
|
-
tokenizerProto._emitCurrentToken.call(this);
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
tokenizer._emitCurrentCharacterToken = function () {
|
|
122
|
-
//NOTE: if we have character token and it's location wasn't set in the _emitCurrentToken(),
|
|
123
|
-
//then set it's location at the current preprocessor position.
|
|
124
|
-
//We don't need to increment preprocessor position, since character token
|
|
125
|
-
//emission is always forced by the start of the next character token here.
|
|
126
|
-
//So, we already have advanced position.
|
|
127
|
-
if (this.currentCharacterToken && this.currentCharacterToken.location.endOffset === -1)
|
|
128
|
-
this.currentCharacterToken.location.endOffset = this.preprocessor.sourcePos;
|
|
129
|
-
|
|
130
|
-
tokenizerProto._emitCurrentCharacterToken.call(this);
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
//NOTE: patch initial states for each mode to obtain token start position
|
|
134
|
-
Object.keys(tokenizerProto.MODE)
|
|
135
|
-
|
|
136
|
-
.map(function (modeName) {
|
|
137
|
-
return tokenizerProto.MODE[modeName];
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
.forEach(function (state) {
|
|
141
|
-
tokenizer[state] = function (cp) {
|
|
142
|
-
tokenStartOffset = this.preprocessor.sourcePos;
|
|
143
|
-
tokenLine = line;
|
|
144
|
-
tokenCol = col;
|
|
145
|
-
tokenizerProto[state].call(this, cp);
|
|
146
|
-
};
|
|
147
|
-
});
|
|
148
|
-
};
|