@xmldom/xmldom 0.9.0-beta.9 → 0.9.1
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/CHANGELOG.md +95 -0
- package/index.d.ts +1242 -121
- package/lib/.eslintrc.yml +1 -1
- package/lib/conventions.js +108 -77
- package/lib/dom-parser.js +98 -87
- package/lib/dom.js +1055 -397
- package/lib/entities.js +14 -9
- package/lib/errors.js +202 -0
- package/lib/grammar.js +21 -3
- package/lib/index.js +23 -3
- package/lib/sax.js +157 -115
- package/package.json +73 -70
- package/readme.md +11 -3
package/lib/sax.js
CHANGED
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
var conventions = require('./conventions');
|
|
4
4
|
var g = require('./grammar');
|
|
5
|
+
var errors = require('./errors');
|
|
5
6
|
|
|
6
7
|
var isHTMLEscapableRawTextElement = conventions.isHTMLEscapableRawTextElement;
|
|
7
8
|
var isHTMLMimeType = conventions.isHTMLMimeType;
|
|
8
9
|
var isHTMLRawTextElement = conventions.isHTMLRawTextElement;
|
|
10
|
+
var hasOwn = conventions.hasOwn;
|
|
9
11
|
var NAMESPACE = conventions.NAMESPACE;
|
|
10
|
-
var ParseError =
|
|
12
|
+
var ParseError = errors.ParseError;
|
|
13
|
+
var DOMException = errors.DOMException;
|
|
11
14
|
|
|
12
15
|
//var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',')
|
|
13
16
|
|
|
@@ -28,14 +31,26 @@ XMLReader.prototype = {
|
|
|
28
31
|
parse: function (source, defaultNSMap, entityMap) {
|
|
29
32
|
var domBuilder = this.domBuilder;
|
|
30
33
|
domBuilder.startDocument();
|
|
31
|
-
_copy(defaultNSMap, (defaultNSMap =
|
|
34
|
+
_copy(defaultNSMap, (defaultNSMap = Object.create(null)));
|
|
32
35
|
parse(source, defaultNSMap, entityMap, domBuilder, this.errorHandler);
|
|
33
36
|
domBuilder.endDocument();
|
|
34
37
|
},
|
|
35
38
|
};
|
|
36
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Detecting everything that might be a reference,
|
|
42
|
+
* including those without ending `;`, since those are allowed in HTML.
|
|
43
|
+
* The entityReplacer takes care of verifying and transforming each occurrence,
|
|
44
|
+
* and reports to the errorHandler on those that are not OK,
|
|
45
|
+
* depending on the context.
|
|
46
|
+
*/
|
|
47
|
+
var ENTITY_REG = /&#?\w+;?/g;
|
|
48
|
+
|
|
37
49
|
function parse(source, defaultNSMapCopy, entityMap, domBuilder, errorHandler) {
|
|
38
50
|
var isHTML = isHTMLMimeType(domBuilder.mimeType);
|
|
51
|
+
if (source.indexOf(g.UNICODE_REPLACEMENT_CHARACTER) >= 0) {
|
|
52
|
+
return errorHandler.fatalError('Unicode replacement character detected, source encoding issues?');
|
|
53
|
+
}
|
|
39
54
|
|
|
40
55
|
function fixedFromCharCode(code) {
|
|
41
56
|
// String.prototype.fromCharCode does not supports
|
|
@@ -52,8 +67,18 @@ function parse(source, defaultNSMapCopy, entityMap, domBuilder, errorHandler) {
|
|
|
52
67
|
}
|
|
53
68
|
|
|
54
69
|
function entityReplacer(a) {
|
|
55
|
-
var
|
|
56
|
-
if (
|
|
70
|
+
var complete = a[a.length - 1] === ';' ? a : a + ';';
|
|
71
|
+
if (!isHTML && complete !== a) {
|
|
72
|
+
errorHandler.error('EntityRef: expecting ;');
|
|
73
|
+
return a;
|
|
74
|
+
}
|
|
75
|
+
var match = g.Reference.exec(complete);
|
|
76
|
+
if (!match || match[0].length !== complete.length) {
|
|
77
|
+
errorHandler.error('entity not matching Reference production: ' + a);
|
|
78
|
+
return a;
|
|
79
|
+
}
|
|
80
|
+
var k = complete.slice(1, -1);
|
|
81
|
+
if (hasOwn(entityMap, k)) {
|
|
57
82
|
return entityMap[k];
|
|
58
83
|
} else if (k.charAt(0) === '#') {
|
|
59
84
|
return fixedFromCharCode(parseInt(k.substr(1).replace('x', '0x')));
|
|
@@ -66,7 +91,7 @@ function parse(source, defaultNSMapCopy, entityMap, domBuilder, errorHandler) {
|
|
|
66
91
|
function appendText(end) {
|
|
67
92
|
//has some bugs
|
|
68
93
|
if (end > start) {
|
|
69
|
-
var xt = source.substring(start, end).replace(
|
|
94
|
+
var xt = source.substring(start, end).replace(ENTITY_REG, entityReplacer);
|
|
70
95
|
locator && position(start);
|
|
71
96
|
domBuilder.characters(xt, 0, end - start);
|
|
72
97
|
start = end;
|
|
@@ -88,70 +113,83 @@ function parse(source, defaultNSMapCopy, entityMap, domBuilder, errorHandler) {
|
|
|
88
113
|
var locator = domBuilder.locator;
|
|
89
114
|
|
|
90
115
|
var parseStack = [{ currentNSMap: defaultNSMapCopy }];
|
|
91
|
-
var
|
|
116
|
+
var unclosedTags = [];
|
|
92
117
|
var start = 0;
|
|
93
118
|
while (true) {
|
|
94
119
|
try {
|
|
95
120
|
var tagStart = source.indexOf('<', start);
|
|
96
121
|
if (tagStart < 0) {
|
|
97
|
-
if (!
|
|
122
|
+
if (!isHTML && unclosedTags.length > 0) {
|
|
123
|
+
return errorHandler.fatalError('unclosed xml tag(s): ' + unclosedTags.join(', '));
|
|
124
|
+
}
|
|
125
|
+
if (!source.substring(start).match(/^\s*$/)) {
|
|
98
126
|
var doc = domBuilder.doc;
|
|
99
127
|
var text = doc.createTextNode(source.substr(start));
|
|
128
|
+
if (doc.documentElement) {
|
|
129
|
+
return errorHandler.error('Extra content at the end of the document');
|
|
130
|
+
}
|
|
100
131
|
doc.appendChild(text);
|
|
101
132
|
domBuilder.currentElement = text;
|
|
102
133
|
}
|
|
103
134
|
return;
|
|
104
135
|
}
|
|
105
136
|
if (tagStart > start) {
|
|
137
|
+
var fromSource = source.substring(start, tagStart);
|
|
138
|
+
if (!isHTML && unclosedTags.length === 0) {
|
|
139
|
+
fromSource = fromSource.replace(new RegExp(g.S_OPT.source, 'g'), '');
|
|
140
|
+
fromSource && errorHandler.error("Unexpected content outside root element: '" + fromSource + "'");
|
|
141
|
+
}
|
|
106
142
|
appendText(tagStart);
|
|
107
143
|
}
|
|
108
144
|
switch (source.charAt(tagStart + 1)) {
|
|
109
145
|
case '/':
|
|
110
|
-
var
|
|
111
|
-
var end = source.indexOf('>', tagStart + 3);
|
|
146
|
+
var end = source.indexOf('>', tagStart + 2);
|
|
112
147
|
var tagNameRaw = source.substring(tagStart + 2, end > 0 ? end : undefined);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (
|
|
118
|
-
errorHandler.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
148
|
+
if (!tagNameRaw) {
|
|
149
|
+
return errorHandler.fatalError('end tag name missing');
|
|
150
|
+
}
|
|
151
|
+
var tagNameMatch = end > 0 && g.reg('^', g.QName_group, g.S_OPT, '$').exec(tagNameRaw);
|
|
152
|
+
if (!tagNameMatch) {
|
|
153
|
+
return errorHandler.fatalError('end tag name contains invalid characters: "' + tagNameRaw + '"');
|
|
154
|
+
}
|
|
155
|
+
if (!domBuilder.currentElement && !domBuilder.doc.documentElement) {
|
|
156
|
+
// not enough information to provide a helpful error message,
|
|
157
|
+
// but parsing will throw since there is no root element
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
var currentTagName =
|
|
161
|
+
unclosedTags[unclosedTags.length - 1] ||
|
|
162
|
+
domBuilder.currentElement.tagName ||
|
|
163
|
+
domBuilder.doc.documentElement.tagName ||
|
|
164
|
+
'';
|
|
165
|
+
if (currentTagName !== tagNameMatch[1]) {
|
|
166
|
+
var tagNameLower = tagNameMatch[1].toLowerCase();
|
|
167
|
+
if (!isHTML || currentTagName.toLowerCase() !== tagNameLower) {
|
|
168
|
+
return errorHandler.fatalError('Opening and ending tag mismatch: "' + currentTagName + '" != "' + tagNameRaw + '"');
|
|
169
|
+
}
|
|
122
170
|
}
|
|
171
|
+
var config = parseStack.pop();
|
|
172
|
+
unclosedTags.pop();
|
|
123
173
|
var localNSMap = config.localNSMap;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
for (var prefix in localNSMap) {
|
|
130
|
-
if (Object.prototype.hasOwnProperty.call(localNSMap, prefix)) {
|
|
131
|
-
domBuilder.endPrefixMapping(prefix);
|
|
132
|
-
}
|
|
174
|
+
domBuilder.endElement(config.uri, config.localName, currentTagName);
|
|
175
|
+
if (localNSMap) {
|
|
176
|
+
for (var prefix in localNSMap) {
|
|
177
|
+
if (hasOwn(localNSMap, prefix)) {
|
|
178
|
+
domBuilder.endPrefixMapping(prefix);
|
|
133
179
|
}
|
|
134
180
|
}
|
|
135
|
-
if (!endMatch) {
|
|
136
|
-
// No known test case
|
|
137
|
-
return errorHandler.fatalError(
|
|
138
|
-
'end tag name: ' + tagName + ' is not match the current start tagName:' + config.tagName
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
} else {
|
|
142
|
-
parseStack.push(config);
|
|
143
181
|
}
|
|
144
182
|
|
|
145
183
|
end++;
|
|
146
184
|
break;
|
|
147
|
-
// end
|
|
185
|
+
// end element
|
|
148
186
|
case '?': // <?...?>
|
|
149
187
|
locator && position(tagStart);
|
|
150
188
|
end = parseProcessingInstruction(source, tagStart, domBuilder, errorHandler);
|
|
151
189
|
break;
|
|
152
190
|
case '!': // <!doctype,<![CDATA,<!--
|
|
153
191
|
locator && position(tagStart);
|
|
154
|
-
end = parseDoctypeCommentOrCData(source, tagStart, domBuilder, errorHandler);
|
|
192
|
+
end = parseDoctypeCommentOrCData(source, tagStart, domBuilder, errorHandler, isHTML);
|
|
155
193
|
break;
|
|
156
194
|
default:
|
|
157
195
|
locator && position(tagStart);
|
|
@@ -161,10 +199,11 @@ function parse(source, defaultNSMapCopy, entityMap, domBuilder, errorHandler) {
|
|
|
161
199
|
var end = parseElementStartPart(source, tagStart, el, currentNSMap, entityReplacer, errorHandler, isHTML);
|
|
162
200
|
var len = el.length;
|
|
163
201
|
|
|
164
|
-
if (!el.closed
|
|
165
|
-
el.
|
|
166
|
-
|
|
167
|
-
|
|
202
|
+
if (!el.closed) {
|
|
203
|
+
if (isHTML && conventions.isHTMLVoidElement(el.tagName)) {
|
|
204
|
+
el.closed = true;
|
|
205
|
+
} else {
|
|
206
|
+
unclosedTags.push(el.tagName);
|
|
168
207
|
}
|
|
169
208
|
}
|
|
170
209
|
if (locator && len) {
|
|
@@ -195,6 +234,8 @@ function parse(source, defaultNSMapCopy, entityMap, domBuilder, errorHandler) {
|
|
|
195
234
|
} catch (e) {
|
|
196
235
|
if (e instanceof ParseError) {
|
|
197
236
|
throw e;
|
|
237
|
+
} else if (e instanceof DOMException) {
|
|
238
|
+
throw new ParseError(e.name + ': ' + e.message, domBuilder.locator, e);
|
|
198
239
|
}
|
|
199
240
|
errorHandler.error('element parse error: ' + e);
|
|
200
241
|
end = -1;
|
|
@@ -202,7 +243,7 @@ function parse(source, defaultNSMapCopy, entityMap, domBuilder, errorHandler) {
|
|
|
202
243
|
if (end > start) {
|
|
203
244
|
start = end;
|
|
204
245
|
} else {
|
|
205
|
-
//
|
|
246
|
+
//Possible sax fallback here, risk of positional error
|
|
206
247
|
appendText(Math.max(tagStart, start) + 1);
|
|
207
248
|
}
|
|
208
249
|
}
|
|
@@ -215,8 +256,9 @@ function copyLocator(f, t) {
|
|
|
215
256
|
}
|
|
216
257
|
|
|
217
258
|
/**
|
|
218
|
-
* @
|
|
219
|
-
*
|
|
259
|
+
* @returns
|
|
260
|
+
* end of the elementStartPart(end of elementEndPart for selfClosed el)
|
|
261
|
+
* @see {@link #appendElement}
|
|
220
262
|
*/
|
|
221
263
|
function parseElementStartPart(source, start, el, currentNSMap, entityReplacer, errorHandler, isHTML) {
|
|
222
264
|
/**
|
|
@@ -225,16 +267,19 @@ function parseElementStartPart(source, start, el, currentNSMap, entityReplacer,
|
|
|
225
267
|
* @param {number} startIndex
|
|
226
268
|
*/
|
|
227
269
|
function addAttribute(qname, value, startIndex) {
|
|
228
|
-
if (el.attributeNames
|
|
270
|
+
if (hasOwn(el.attributeNames, qname)) {
|
|
229
271
|
return errorHandler.fatalError('Attribute ' + qname + ' redefined');
|
|
230
272
|
}
|
|
273
|
+
if (!isHTML && value.indexOf('<') >= 0) {
|
|
274
|
+
return errorHandler.fatalError("Unescaped '<' not allowed in attributes values");
|
|
275
|
+
}
|
|
231
276
|
el.addValue(
|
|
232
277
|
qname,
|
|
233
278
|
// @see https://www.w3.org/TR/xml/#AVNormalize
|
|
234
279
|
// since the xmldom sax parser does not "interpret" DTD the following is not implemented:
|
|
235
280
|
// - recursive replacement of (DTD) entity references
|
|
236
281
|
// - trimming and collapsing multiple spaces into a single one for attributes that are not of type CDATA
|
|
237
|
-
value.replace(/[\t\n\r]/g, ' ').replace(
|
|
282
|
+
value.replace(/[\t\n\r]/g, ' ').replace(ENTITY_REG, entityReplacer),
|
|
238
283
|
startIndex
|
|
239
284
|
);
|
|
240
285
|
}
|
|
@@ -346,7 +391,9 @@ function parseElementStartPart(source, start, el, currentNSMap, entityReplacer,
|
|
|
346
391
|
}
|
|
347
392
|
break;
|
|
348
393
|
case S_EQ:
|
|
349
|
-
|
|
394
|
+
if (!isHTML) {
|
|
395
|
+
return errorHandler.fatalError('AttValue: \' or " expected');
|
|
396
|
+
}
|
|
350
397
|
}
|
|
351
398
|
return p;
|
|
352
399
|
/*xml space '\x20' | #x9 | #xD | #xA; */
|
|
@@ -409,18 +456,17 @@ function parseElementStartPart(source, start, el, currentNSMap, entityReplacer,
|
|
|
409
456
|
}
|
|
410
457
|
}
|
|
411
458
|
} //end outer switch
|
|
412
|
-
//console.log('p++',p)
|
|
413
459
|
p++;
|
|
414
460
|
}
|
|
415
461
|
}
|
|
416
462
|
|
|
417
463
|
/**
|
|
418
|
-
* @
|
|
464
|
+
* @returns
|
|
465
|
+
* `true` if a new namespace has been defined.
|
|
419
466
|
*/
|
|
420
467
|
function appendElement(el, domBuilder, currentNSMap) {
|
|
421
468
|
var tagName = el.tagName;
|
|
422
469
|
var localNSMap = null;
|
|
423
|
-
//var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
|
|
424
470
|
var i = el.length;
|
|
425
471
|
while (i--) {
|
|
426
472
|
var a = el[i];
|
|
@@ -442,10 +488,8 @@ function appendElement(el, domBuilder, currentNSMap) {
|
|
|
442
488
|
if (nsPrefix !== false) {
|
|
443
489
|
//hack!!
|
|
444
490
|
if (localNSMap == null) {
|
|
445
|
-
localNSMap =
|
|
446
|
-
|
|
447
|
-
_copy(currentNSMap, (currentNSMap = {}));
|
|
448
|
-
//console.log(currentNSMap,1)
|
|
491
|
+
localNSMap = Object.create(null);
|
|
492
|
+
_copy(currentNSMap, (currentNSMap = Object.create(null)));
|
|
449
493
|
}
|
|
450
494
|
currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
|
|
451
495
|
a.uri = NAMESPACE.XMLNS;
|
|
@@ -482,7 +526,7 @@ function appendElement(el, domBuilder, currentNSMap) {
|
|
|
482
526
|
domBuilder.endElement(ns, localName, tagName);
|
|
483
527
|
if (localNSMap) {
|
|
484
528
|
for (prefix in localNSMap) {
|
|
485
|
-
if (
|
|
529
|
+
if (hasOwn(localNSMap, prefix)) {
|
|
486
530
|
domBuilder.endPrefixMapping(prefix);
|
|
487
531
|
}
|
|
488
532
|
}
|
|
@@ -506,7 +550,7 @@ function parseHtmlSpecialContent(source, elStartEnd, tagName, entityReplacer, do
|
|
|
506
550
|
var text = source.substring(elStartEnd + 1, elEndStart);
|
|
507
551
|
|
|
508
552
|
if (isEscapableRaw) {
|
|
509
|
-
text = text.replace(
|
|
553
|
+
text = text.replace(ENTITY_REG, entityReplacer);
|
|
510
554
|
}
|
|
511
555
|
domBuilder.characters(text, 0, text.length);
|
|
512
556
|
return elEndStart;
|
|
@@ -514,25 +558,9 @@ function parseHtmlSpecialContent(source, elStartEnd, tagName, entityReplacer, do
|
|
|
514
558
|
return elStartEnd + 1;
|
|
515
559
|
}
|
|
516
560
|
|
|
517
|
-
function fixSelfClosed(source, elStartEnd, tagName, closeMap) {
|
|
518
|
-
//if(tagName in closeMap){
|
|
519
|
-
var pos = closeMap[tagName];
|
|
520
|
-
if (pos == null) {
|
|
521
|
-
//console.log(tagName)
|
|
522
|
-
pos = source.lastIndexOf('</' + tagName + '>');
|
|
523
|
-
if (pos < elStartEnd) {
|
|
524
|
-
//忘记闭合
|
|
525
|
-
pos = source.lastIndexOf('</' + tagName);
|
|
526
|
-
}
|
|
527
|
-
closeMap[tagName] = pos;
|
|
528
|
-
}
|
|
529
|
-
return pos < elStartEnd;
|
|
530
|
-
//}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
561
|
function _copy(source, target) {
|
|
534
562
|
for (var n in source) {
|
|
535
|
-
if (
|
|
563
|
+
if (hasOwn(source, n)) {
|
|
536
564
|
target[n] = source[n];
|
|
537
565
|
}
|
|
538
566
|
}
|
|
@@ -540,23 +568,28 @@ function _copy(source, target) {
|
|
|
540
568
|
|
|
541
569
|
/**
|
|
542
570
|
* @typedef ParseUtils
|
|
543
|
-
* @property {function(relativeIndex: number?): string | undefined} char
|
|
544
|
-
*
|
|
545
|
-
* @property {function(): number} getIndex
|
|
546
|
-
*
|
|
547
|
-
*
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
551
|
-
*
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
* @property {function():
|
|
556
|
-
*
|
|
557
|
-
*
|
|
558
|
-
*
|
|
559
|
-
* @
|
|
571
|
+
* @property {function(relativeIndex: number?): string | undefined} char
|
|
572
|
+
* Provides look ahead access to a singe character relative to the current index.
|
|
573
|
+
* @property {function(): number} getIndex
|
|
574
|
+
* Provides read-only access to the current index.
|
|
575
|
+
* @property {function(reg: RegExp): string | null} getMatch
|
|
576
|
+
* Applies the provided regular expression enforcing that it starts at the current index and
|
|
577
|
+
* returns the complete matching string,
|
|
578
|
+
* and moves the current index by the length of the matching string.
|
|
579
|
+
* @property {function(): string} getSource
|
|
580
|
+
* Provides read-only access to the complete source.
|
|
581
|
+
* @property {function(places: number?): void} skip
|
|
582
|
+
* moves the current index by places (defaults to 1)
|
|
583
|
+
* @property {function(): number} skipBlanks
|
|
584
|
+
* Moves the current index by the amount of white space that directly follows the current index
|
|
585
|
+
* and returns the amount of whitespace chars skipped (0..n),
|
|
586
|
+
* or -1 if the end of the source was reached.
|
|
587
|
+
* @property {function(): string} substringFromIndex
|
|
588
|
+
* creates a substring from the current index to the end of `source`
|
|
589
|
+
* @property {function(compareWith: string): boolean} substringStartsWith
|
|
590
|
+
* Checks if source contains `compareWith`,
|
|
591
|
+
* starting from the current index.
|
|
592
|
+
* @see {@link parseUtils}
|
|
560
593
|
*/
|
|
561
594
|
|
|
562
595
|
/**
|
|
@@ -656,8 +689,13 @@ function parseDoctypeInternalSubset(p, errorHandler) {
|
|
|
656
689
|
if (p.char() === '[') {
|
|
657
690
|
p.skip(1);
|
|
658
691
|
var intSubsetStart = p.getIndex();
|
|
659
|
-
p.skipBlanks();
|
|
660
692
|
while (p.getIndex() < source.length) {
|
|
693
|
+
p.skipBlanks();
|
|
694
|
+
if (p.char() === ']') {
|
|
695
|
+
var internalSubset = source.substring(intSubsetStart, p.getIndex());
|
|
696
|
+
p.skip(1);
|
|
697
|
+
return internalSubset;
|
|
698
|
+
}
|
|
661
699
|
var current = null;
|
|
662
700
|
// Only in external subset
|
|
663
701
|
// if (char() === '<' && char(1) === '!' && char(2) === '[') {
|
|
@@ -665,20 +703,20 @@ function parseDoctypeInternalSubset(p, errorHandler) {
|
|
|
665
703
|
// } else
|
|
666
704
|
if (p.char() === '<' && p.char(1) === '!') {
|
|
667
705
|
switch (p.char(2)) {
|
|
668
|
-
case 'E':
|
|
706
|
+
case 'E': // ELEMENT | ENTITY
|
|
669
707
|
if (p.char(3) === 'L') {
|
|
670
708
|
current = p.getMatch(g.elementdecl);
|
|
671
709
|
} else if (p.char(3) === 'N') {
|
|
672
710
|
current = p.getMatch(g.EntityDecl);
|
|
673
711
|
}
|
|
674
712
|
break;
|
|
675
|
-
case 'A':
|
|
713
|
+
case 'A': // ATTRIBUTE
|
|
676
714
|
current = p.getMatch(g.AttlistDecl);
|
|
677
715
|
break;
|
|
678
|
-
case 'N':
|
|
716
|
+
case 'N': // NOTATION
|
|
679
717
|
current = p.getMatch(g.NotationDecl);
|
|
680
718
|
break;
|
|
681
|
-
case '-':
|
|
719
|
+
case '-': // COMMENT
|
|
682
720
|
current = p.getMatch(g.Comment);
|
|
683
721
|
break;
|
|
684
722
|
}
|
|
@@ -692,13 +730,6 @@ function parseDoctypeInternalSubset(p, errorHandler) {
|
|
|
692
730
|
if (!current) {
|
|
693
731
|
return errorHandler.fatalError('Error in internal subset at position ' + p.getIndex());
|
|
694
732
|
}
|
|
695
|
-
p.skipBlanks();
|
|
696
|
-
if (p.char() === ']') {
|
|
697
|
-
var internalSubset = source.substring(intSubsetStart, p.getIndex());
|
|
698
|
-
p.skip(1);
|
|
699
|
-
return internalSubset;
|
|
700
|
-
}
|
|
701
|
-
p.skipBlanks();
|
|
702
733
|
}
|
|
703
734
|
return errorHandler.fatalError('doctype internal subset is not well-formed, missing ]');
|
|
704
735
|
}
|
|
@@ -706,14 +737,20 @@ function parseDoctypeInternalSubset(p, errorHandler) {
|
|
|
706
737
|
|
|
707
738
|
/**
|
|
708
739
|
* Called when the parser encounters an element starting with '<!'.
|
|
709
|
-
*
|
|
710
|
-
* @param {
|
|
740
|
+
*
|
|
741
|
+
* @param {string} source
|
|
742
|
+
* The xml.
|
|
743
|
+
* @param {number} start
|
|
744
|
+
* the start index of the '<!'
|
|
711
745
|
* @param {DOMHandler} domBuilder
|
|
712
746
|
* @param {DOMHandler} errorHandler
|
|
713
|
-
* @
|
|
714
|
-
* @
|
|
747
|
+
* @param {boolean} isHTML
|
|
748
|
+
* @returns {number | never}
|
|
749
|
+
* The end index of the element.
|
|
750
|
+
* @throws {ParseError}
|
|
751
|
+
* In case the element is not well-formed.
|
|
715
752
|
*/
|
|
716
|
-
function parseDoctypeCommentOrCData(source, start, domBuilder, errorHandler) {
|
|
753
|
+
function parseDoctypeCommentOrCData(source, start, domBuilder, errorHandler, isHTML) {
|
|
717
754
|
var p = parseUtils(source, start);
|
|
718
755
|
|
|
719
756
|
switch (p.char(2)) {
|
|
@@ -730,6 +767,9 @@ function parseDoctypeCommentOrCData(source, start, domBuilder, errorHandler) {
|
|
|
730
767
|
// should be CDATA
|
|
731
768
|
var cdata = p.getMatch(g.CDSect);
|
|
732
769
|
if (cdata) {
|
|
770
|
+
if (!isHTML && !domBuilder.currentElement) {
|
|
771
|
+
return errorHandler.fatalError('CDATA outside of element');
|
|
772
|
+
}
|
|
733
773
|
domBuilder.startCDATA();
|
|
734
774
|
domBuilder.characters(cdata, g.CDATA_START.length, cdata.length - g.CDATA_START.length - g.CDATA_END.length);
|
|
735
775
|
domBuilder.endCDATA();
|
|
@@ -739,13 +779,9 @@ function parseDoctypeCommentOrCData(source, start, domBuilder, errorHandler) {
|
|
|
739
779
|
}
|
|
740
780
|
case 'D': {
|
|
741
781
|
// should be DOCTYPE
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
systemId: undefined,
|
|
746
|
-
internalSubset: undefined,
|
|
747
|
-
};
|
|
748
|
-
|
|
782
|
+
if (domBuilder.doc && domBuilder.doc.documentElement) {
|
|
783
|
+
return errorHandler.fatalError('Doctype not allowed inside or after documentElement at position ' + p.getIndex());
|
|
784
|
+
}
|
|
749
785
|
if (!p.substringStartsWith(g.DOCTYPE_DECL_START)) {
|
|
750
786
|
return errorHandler.fatalError('Expected ' + g.DOCTYPE_DECL_START + ' at position ' + p.getIndex());
|
|
751
787
|
}
|
|
@@ -754,6 +790,12 @@ function parseDoctypeCommentOrCData(source, start, domBuilder, errorHandler) {
|
|
|
754
790
|
return errorHandler.fatalError('Expected whitespace after ' + g.DOCTYPE_DECL_START + ' at position ' + p.getIndex());
|
|
755
791
|
}
|
|
756
792
|
|
|
793
|
+
var doctype = {
|
|
794
|
+
name: undefined,
|
|
795
|
+
publicId: undefined,
|
|
796
|
+
systemId: undefined,
|
|
797
|
+
internalSubset: undefined,
|
|
798
|
+
};
|
|
757
799
|
// Parse the DOCTYPE name
|
|
758
800
|
doctype.name = p.getMatch(g.Name);
|
|
759
801
|
if (!doctype.name)
|
|
@@ -811,7 +853,7 @@ function parseProcessingInstruction(source, start, domBuilder, errorHandler) {
|
|
|
811
853
|
}
|
|
812
854
|
|
|
813
855
|
function ElementAttributes() {
|
|
814
|
-
this.attributeNames =
|
|
856
|
+
this.attributeNames = Object.create(null);
|
|
815
857
|
}
|
|
816
858
|
|
|
817
859
|
ElementAttributes.prototype = {
|
package/package.json
CHANGED
|
@@ -1,72 +1,75 @@
|
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
2
|
+
"name": "@xmldom/xmldom",
|
|
3
|
+
"version": "0.9.1",
|
|
4
|
+
"description": "A pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"w3c",
|
|
7
|
+
"dom",
|
|
8
|
+
"xml",
|
|
9
|
+
"parser",
|
|
10
|
+
"javascript",
|
|
11
|
+
"DOMParser",
|
|
12
|
+
"XMLSerializer",
|
|
13
|
+
"ponyfill"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/xmldom/xmldom",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git://github.com/xmldom/xmldom.git"
|
|
19
|
+
},
|
|
20
|
+
"main": "lib/index.js",
|
|
21
|
+
"types": "index.d.ts",
|
|
22
|
+
"files": [
|
|
23
|
+
"CHANGELOG.md",
|
|
24
|
+
"LICENSE",
|
|
25
|
+
"readme.md",
|
|
26
|
+
"SECURITY.md",
|
|
27
|
+
"index.d.ts",
|
|
28
|
+
"lib"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"lint": "eslint examples lib test",
|
|
32
|
+
"format": "prettier --write examples lib test index.d.ts",
|
|
33
|
+
"changelog": "auto-changelog --unreleased-only",
|
|
34
|
+
"start": "nodemon --watch package.json --watch lib --watch test --exec 'npm --silent run test && npm --silent run lint'",
|
|
35
|
+
"test": "jest",
|
|
36
|
+
"fuzz": "jest --config=./jest.fuzz.config.js",
|
|
37
|
+
"test:types": "cd examples/typescript-node-es6 && ./pretest.sh 3 && ./pretest.sh 4 && ./pretest.sh 5 && node dist/index.js",
|
|
38
|
+
"testrelease": "npm test && eslint lib",
|
|
39
|
+
"version": "./changelog-has-version.sh",
|
|
40
|
+
"release": "np --no-yarn --test-script testrelease"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=14.0.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@homer0/prettier-plugin-jsdoc": "9.0.2",
|
|
47
|
+
"@jazzer.js/core": "2.1.0",
|
|
48
|
+
"@jazzer.js/jest-runner": "2.1.0",
|
|
49
|
+
"auto-changelog": "2.4.0",
|
|
50
|
+
"eslint": "8.57.0",
|
|
51
|
+
"eslint-config-prettier": "9.1.0",
|
|
52
|
+
"eslint-plugin-anti-trojan-source": "1.1.1",
|
|
53
|
+
"eslint-plugin-es5": "1.5.0",
|
|
54
|
+
"eslint-plugin-n": "17.10.2",
|
|
55
|
+
"eslint-plugin-prettier": "5.2.1",
|
|
56
|
+
"get-stream": "6.0.1",
|
|
57
|
+
"jest": "29.7.0",
|
|
58
|
+
"nodemon": "3.1.4",
|
|
59
|
+
"np": "8.0.4",
|
|
60
|
+
"prettier": "3.3.3",
|
|
61
|
+
"rxjs": "7.8.1",
|
|
62
|
+
"xmltest": "2.0.1",
|
|
63
|
+
"yauzl": "3.1.3"
|
|
64
|
+
},
|
|
65
|
+
"bugs": {
|
|
66
|
+
"url": "https://github.com/xmldom/xmldom/issues"
|
|
67
|
+
},
|
|
68
|
+
"license": "MIT",
|
|
69
|
+
"auto-changelog": {
|
|
70
|
+
"prepend": true,
|
|
71
|
+
"remote": "origin",
|
|
72
|
+
"tagPrefix": "",
|
|
73
|
+
"template": "./auto-changelog.hbs"
|
|
74
|
+
}
|
|
72
75
|
}
|