@xmldom/xmldom 0.9.0-beta.1 → 0.9.0-beta.10
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 +286 -8
- package/SECURITY.md +8 -8
- package/index.d.ts +369 -21
- package/lib/.eslintrc.yml +1 -0
- package/lib/conventions.js +192 -112
- package/lib/dom-parser.js +301 -232
- package/lib/dom.js +1465 -871
- package/lib/entities.js +2150 -254
- package/lib/grammar.js +516 -0
- package/lib/index.js +19 -5
- package/lib/sax.js +681 -472
- package/package.json +15 -11
- package/readme.md +31 -42
package/lib/dom-parser.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
'use strict'
|
|
1
|
+
'use strict';
|
|
2
2
|
|
|
3
|
-
var conventions = require(
|
|
4
|
-
var dom = require('./dom')
|
|
3
|
+
var conventions = require('./conventions');
|
|
4
|
+
var dom = require('./dom');
|
|
5
5
|
var entities = require('./entities');
|
|
6
6
|
var sax = require('./sax');
|
|
7
7
|
|
|
8
8
|
var DOMImplementation = dom.DOMImplementation;
|
|
9
9
|
|
|
10
|
+
var hasDefaultHTMLNamespace = conventions.hasDefaultHTMLNamespace;
|
|
11
|
+
var isHTMLMimeType = conventions.isHTMLMimeType;
|
|
12
|
+
var isValidMimeType = conventions.isValidMimeType;
|
|
10
13
|
var MIME_TYPE = conventions.MIME_TYPE;
|
|
11
14
|
var NAMESPACE = conventions.NAMESPACE;
|
|
15
|
+
var ParseError = conventions.ParseError;
|
|
12
16
|
|
|
13
|
-
var ParseError = sax.ParseError;
|
|
14
17
|
var XMLReader = sax.XMLReader;
|
|
15
18
|
|
|
16
19
|
/**
|
|
@@ -25,19 +28,18 @@ var XMLReader = sax.XMLReader;
|
|
|
25
28
|
* > as if it normalized all line breaks in external parsed entities (including the document entity)
|
|
26
29
|
* > on input, before parsing, by translating all of the following to a single #xA character:
|
|
27
30
|
* >
|
|
28
|
-
* > 1. the two-character sequence #xD #xA
|
|
29
|
-
* > 2. the two-character sequence #xD #x85
|
|
30
|
-
* > 3. the single character #x85
|
|
31
|
-
* > 4. the single character #x2028
|
|
31
|
+
* > 1. the two-character sequence #xD #xA,
|
|
32
|
+
* > 2. the two-character sequence #xD #x85,
|
|
33
|
+
* > 3. the single character #x85,
|
|
34
|
+
* > 4. the single character #x2028,
|
|
32
35
|
* > 5. any #xD character that is not immediately followed by #xA or #x85.
|
|
33
36
|
*
|
|
34
37
|
* @param {string} input
|
|
35
38
|
* @returns {string}
|
|
39
|
+
* @prettierignore
|
|
36
40
|
*/
|
|
37
41
|
function normalizeLineEndings(input) {
|
|
38
|
-
return input
|
|
39
|
-
.replace(/\r[\n\u0085]/g, '\n')
|
|
40
|
-
.replace(/[\r\u0085\u2028]/g, '\n')
|
|
42
|
+
return input.replace(/\r[\n\u0085]/g, '\n').replace(/[\r\u0085\u2028]/g, '\n');
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
/**
|
|
@@ -48,74 +50,104 @@ function normalizeLineEndings(input) {
|
|
|
48
50
|
|
|
49
51
|
/**
|
|
50
52
|
* @typedef DOMParserOptions
|
|
51
|
-
* @property {typeof
|
|
52
|
-
* The method to use instead of `
|
|
53
|
-
*
|
|
53
|
+
* @property {typeof assign} [assign]
|
|
54
|
+
* The method to use instead of `conventions.assign`, which is used to copy values from
|
|
55
|
+
* `options` before they are used for parsing.
|
|
54
56
|
* @property {typeof DOMHandler} [domHandler]
|
|
55
|
-
* For internal testing: The class for creating an instance for handling events from the SAX
|
|
56
|
-
*
|
|
57
|
+
* For internal testing: The class for creating an instance for handling events from the SAX
|
|
58
|
+
* parser.
|
|
59
|
+
* *****Warning: By configuring a faulty implementation, the specified behavior can completely
|
|
60
|
+
* be broken.*****.
|
|
57
61
|
* @property {Function} [errorHandler]
|
|
62
|
+
* DEPRECATED! use `onError` instead.
|
|
63
|
+
* @property {function(level:ErrorLevel, message:string, context: DOMHandler):void}
|
|
64
|
+
* [onError]
|
|
65
|
+
* A function that is invoked for every error that occurs during parsing.
|
|
66
|
+
*
|
|
67
|
+
* If it is not provided, all errors are reported to `console.error`
|
|
68
|
+
* and only `fatalError`s are thrown as a `ParseError`,
|
|
69
|
+
* which prevents any further processing.
|
|
70
|
+
* If the provided method throws, a `ParserError` is thrown,
|
|
71
|
+
* which prevents any further processing.
|
|
72
|
+
*
|
|
73
|
+
* Be aware that many `warning`s are considered an error that prevents further processing in
|
|
74
|
+
* most implementations.
|
|
58
75
|
* @property {boolean} [locator=true]
|
|
59
|
-
* Configures if the nodes created during parsing
|
|
60
|
-
*
|
|
61
|
-
* describing their location in the XML string.
|
|
76
|
+
* Configures if the nodes created during parsing will have a `lineNumber` and a `columnNumber`
|
|
77
|
+
* attribute describing their location in the XML string.
|
|
62
78
|
* Default is true.
|
|
63
79
|
* @property {(string) => string} [normalizeLineEndings]
|
|
64
80
|
* used to replace line endings before parsing, defaults to `normalizeLineEndings`
|
|
65
|
-
* @property {
|
|
81
|
+
* @property {Object} [xmlns]
|
|
66
82
|
* The XML namespaces that should be assumed when parsing.
|
|
67
83
|
* The default namespace can be provided by the key that is the empty string.
|
|
68
84
|
* When the `mimeType` for HTML, XHTML or SVG are passed to `parseFromString`,
|
|
69
85
|
* the default namespace that will be used,
|
|
70
86
|
* will be overridden according to the specification.
|
|
71
|
-
*
|
|
72
|
-
* @see normalizeLineEndings
|
|
87
|
+
* @see {@link normalizeLineEndings}
|
|
73
88
|
*/
|
|
74
89
|
|
|
75
90
|
/**
|
|
76
|
-
* The DOMParser interface provides the ability to parse XML or HTML source code
|
|
77
|
-
*
|
|
91
|
+
* The DOMParser interface provides the ability to parse XML or HTML source code from a string
|
|
92
|
+
* into a DOM `Document`.
|
|
78
93
|
*
|
|
79
|
-
*
|
|
80
|
-
* to control the behavior
|
|
94
|
+
* ***xmldom is different from the spec in that it allows an `options` parameter,
|
|
95
|
+
* to control the behavior***.
|
|
81
96
|
*
|
|
97
|
+
* @class
|
|
82
98
|
* @param {DOMParserOptions} [options]
|
|
83
|
-
* @constructor
|
|
84
|
-
*
|
|
85
99
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser
|
|
86
100
|
* @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-parsing-and-serialization
|
|
87
101
|
*/
|
|
88
|
-
function DOMParser(options){
|
|
89
|
-
|
|
90
|
-
options = options || {locator:true};
|
|
102
|
+
function DOMParser(options) {
|
|
103
|
+
options = options || { locator: true };
|
|
91
104
|
|
|
92
105
|
/**
|
|
93
|
-
* The method to use instead of `
|
|
94
|
-
*
|
|
106
|
+
* The method to use instead of `conventions.assign`, which is used to copy values from
|
|
107
|
+
* `options`
|
|
108
|
+
* before they are used for parsing.
|
|
95
109
|
*
|
|
96
|
-
* @type {
|
|
97
|
-
* @readonly
|
|
110
|
+
* @type {conventions.assign}
|
|
98
111
|
* @private
|
|
99
|
-
* @see conventions.assign
|
|
112
|
+
* @see {@link conventions.assign}
|
|
113
|
+
* @readonly
|
|
100
114
|
*/
|
|
101
|
-
this.assign = options.assign ||
|
|
115
|
+
this.assign = options.assign || conventions.assign;
|
|
102
116
|
|
|
103
117
|
/**
|
|
104
|
-
* For internal testing: The class for creating an instance for handling events from the SAX
|
|
105
|
-
*
|
|
118
|
+
* For internal testing: The class for creating an instance for handling events from the SAX
|
|
119
|
+
* parser.
|
|
120
|
+
* *****Warning: By configuring a faulty implementation, the specified behavior can completely
|
|
121
|
+
* be broken*****.
|
|
106
122
|
*
|
|
107
123
|
* @type {typeof DOMHandler}
|
|
108
|
-
* @readonly
|
|
109
124
|
* @private
|
|
125
|
+
* @readonly
|
|
110
126
|
*/
|
|
111
|
-
this.domHandler = options.domHandler || DOMHandler
|
|
127
|
+
this.domHandler = options.domHandler || DOMHandler;
|
|
112
128
|
|
|
113
129
|
/**
|
|
114
|
-
* A function that
|
|
115
|
-
*
|
|
116
|
-
*
|
|
130
|
+
* A function that is invoked for every error that occurs during parsing.
|
|
131
|
+
*
|
|
132
|
+
* If it is not provided, all errors are reported to `console.error`
|
|
133
|
+
* and only `fatalError`s are thrown as a `ParseError`,
|
|
134
|
+
* which prevents any further processing.
|
|
135
|
+
* If the provided method throws, a `ParserError` is thrown,
|
|
136
|
+
* which prevents any further processing.
|
|
137
|
+
*
|
|
138
|
+
* Be aware that many `warning`s are considered an error that prevents further processing in
|
|
139
|
+
* most implementations.
|
|
140
|
+
*
|
|
141
|
+
* @type {function(level:ErrorLevel, message:string, context: DOMHandler):void}
|
|
142
|
+
* @see {@link onErrorStopParsing}
|
|
143
|
+
* @see {@link onWarningStopParsing}
|
|
117
144
|
*/
|
|
118
|
-
this.
|
|
145
|
+
this.onError = options.onError || options.errorHandler;
|
|
146
|
+
if (options.errorHandler && typeof options.errorHandler !== 'function') {
|
|
147
|
+
throw new TypeError('errorHandler object is no longer supported, switch to onError!');
|
|
148
|
+
} else if (options.errorHandler) {
|
|
149
|
+
options.errorHandler('warning', 'The `errorHandler` option has been deprecated, use `onError` instead!', this);
|
|
150
|
+
}
|
|
119
151
|
|
|
120
152
|
/**
|
|
121
153
|
* used to replace line endings before parsing, defaults to `normalizeLineEndings`
|
|
@@ -123,164 +155,151 @@ function DOMParser(options){
|
|
|
123
155
|
* @type {(string) => string}
|
|
124
156
|
* @readonly
|
|
125
157
|
*/
|
|
126
|
-
this.normalizeLineEndings = options.normalizeLineEndings || normalizeLineEndings
|
|
158
|
+
this.normalizeLineEndings = options.normalizeLineEndings || normalizeLineEndings;
|
|
127
159
|
|
|
128
160
|
/**
|
|
129
|
-
* Configures if the nodes created during parsing
|
|
130
|
-
*
|
|
131
|
-
* describing their location in the XML string.
|
|
161
|
+
* Configures if the nodes created during parsing will have a `lineNumber` and a
|
|
162
|
+
* `columnNumber`
|
|
163
|
+
* attribute describing their location in the XML string.
|
|
132
164
|
* Default is true.
|
|
165
|
+
*
|
|
133
166
|
* @type {boolean}
|
|
134
167
|
* @readonly
|
|
135
168
|
*/
|
|
136
|
-
this.locator = !!options.locator
|
|
169
|
+
this.locator = !!options.locator;
|
|
137
170
|
|
|
138
171
|
/**
|
|
139
172
|
* The default namespace can be provided by the key that is the empty string.
|
|
140
173
|
* When the `mimeType` for HTML, XHTML or SVG are passed to `parseFromString`,
|
|
141
174
|
* the default namespace that will be used,
|
|
142
175
|
* will be overridden according to the specification.
|
|
143
|
-
*
|
|
176
|
+
*
|
|
177
|
+
* @type {Readonly<Object>}
|
|
144
178
|
* @readonly
|
|
145
179
|
*/
|
|
146
|
-
this.xmlns = options.xmlns || {}
|
|
180
|
+
this.xmlns = options.xmlns || {};
|
|
147
181
|
}
|
|
148
182
|
|
|
149
183
|
/**
|
|
150
|
-
* Parses `source` using the options in the way configured by the `DOMParserOptions` of `this`
|
|
151
|
-
* If `mimeType` is `text/html` an HTML `Document` is created,
|
|
184
|
+
* Parses `source` using the options in the way configured by the `DOMParserOptions` of `this`
|
|
185
|
+
* `DOMParser`. If `mimeType` is `text/html` an HTML `Document` is created,
|
|
186
|
+
* otherwise an XML `Document` is created.
|
|
152
187
|
*
|
|
153
|
-
* __It behaves
|
|
154
|
-
* -
|
|
155
|
-
* -
|
|
156
|
-
*
|
|
157
|
-
* -
|
|
158
|
-
* -
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
* - If no `ParserError` is thrown, this method returns the `DOMHandler.doc`,
|
|
163
|
-
* which most likely is the `Document` that has been created during parsing, or `undefined`.
|
|
164
|
-
* __**Warning: By configuring a faulty DOMHandler implementation,
|
|
165
|
-
* the specified behavior can completely be broken.**__
|
|
188
|
+
* __It behaves different from the description in the living standard__:
|
|
189
|
+
* - Uses the `options` passed to the `DOMParser` constructor to modify the behavior.
|
|
190
|
+
* - Any unexpected input is reported to `onError` with either a `warning`,
|
|
191
|
+
* `error` or `fatalError` level.
|
|
192
|
+
* - Any `fatalError` throws a `ParseError` which prevents further processing.
|
|
193
|
+
* - Any error thrown by `onError` is converted to a `ParseError` which prevents further
|
|
194
|
+
* processing - If no `Document` was created during parsing it is reported as a `fatalError`.
|
|
195
|
+
* *****Warning: By configuring a faulty DOMHandler implementation,
|
|
196
|
+
* the specified behavior can completely be broken*****.
|
|
166
197
|
*
|
|
167
|
-
* @param {string} source
|
|
198
|
+
* @param {string} source
|
|
199
|
+
* The XML mime type only allows string input!
|
|
168
200
|
* @param {string} [mimeType='application/xml']
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
* @returns
|
|
172
|
-
* @throws ParseError
|
|
173
|
-
*
|
|
201
|
+
* the mimeType or contentType of the document to be created determines the `type` of document
|
|
202
|
+
* created (XML or HTML)
|
|
203
|
+
* @returns The `Document` node.
|
|
204
|
+
* @throws {ParseError}
|
|
205
|
+
* for any `fatalError` or anything that is thrown by `onError`
|
|
206
|
+
* @throws {TypeError}
|
|
207
|
+
* for any invalid `mimeType`
|
|
174
208
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString
|
|
175
209
|
* @see https://html.spec.whatwg.org/#dom-domparser-parsefromstring-dev
|
|
176
210
|
*/
|
|
177
211
|
DOMParser.prototype.parseFromString = function (source, mimeType) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
212
|
+
if (!isValidMimeType(mimeType)) {
|
|
213
|
+
throw new TypeError('DOMParser.parseFromString: the provided mimeType "' + mimeType + '" is not valid.');
|
|
214
|
+
}
|
|
215
|
+
var defaultNSMap = this.assign({}, this.xmlns);
|
|
216
|
+
var entityMap = entities.XML_ENTITIES;
|
|
217
|
+
var defaultNamespace = defaultNSMap[''] || null;
|
|
218
|
+
if (hasDefaultHTMLNamespace(mimeType)) {
|
|
219
|
+
entityMap = entities.HTML_ENTITIES;
|
|
220
|
+
defaultNamespace = NAMESPACE.HTML;
|
|
184
221
|
} else if (mimeType === MIME_TYPE.XML_SVG_IMAGE) {
|
|
185
|
-
defaultNamespace = NAMESPACE.SVG
|
|
222
|
+
defaultNamespace = NAMESPACE.SVG;
|
|
186
223
|
}
|
|
187
|
-
defaultNSMap[''] = defaultNamespace
|
|
188
|
-
defaultNSMap.xml = defaultNSMap.xml || NAMESPACE.XML
|
|
224
|
+
defaultNSMap[''] = defaultNamespace;
|
|
225
|
+
defaultNSMap.xml = defaultNSMap.xml || NAMESPACE.XML;
|
|
189
226
|
|
|
190
227
|
var domBuilder = new this.domHandler({
|
|
191
228
|
mimeType: mimeType,
|
|
192
229
|
defaultNamespace: defaultNamespace,
|
|
193
|
-
|
|
230
|
+
onError: this.onError,
|
|
231
|
+
});
|
|
194
232
|
var locator = this.locator ? {} : undefined;
|
|
195
233
|
if (this.locator) {
|
|
196
|
-
domBuilder.setDocumentLocator(locator)
|
|
234
|
+
domBuilder.setDocumentLocator(locator);
|
|
197
235
|
}
|
|
198
236
|
|
|
199
|
-
var sax = new XMLReader()
|
|
200
|
-
sax.errorHandler =
|
|
201
|
-
sax.domBuilder = domBuilder
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
sax.errorHandler.error('invalid doc source')
|
|
237
|
+
var sax = new XMLReader();
|
|
238
|
+
sax.errorHandler = domBuilder;
|
|
239
|
+
sax.domBuilder = domBuilder;
|
|
240
|
+
var isXml = !conventions.isHTMLMimeType(mimeType);
|
|
241
|
+
if (isXml && typeof source !== 'string') {
|
|
242
|
+
sax.errorHandler.fatalError('source is not a string');
|
|
206
243
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if(!errorImpl){
|
|
211
|
-
if(domBuilder instanceof DOMHandler){
|
|
212
|
-
return domBuilder;
|
|
213
|
-
}
|
|
214
|
-
errorImpl = domBuilder ;
|
|
215
|
-
}
|
|
216
|
-
var errorHandler = {}
|
|
217
|
-
var isCallback = errorImpl instanceof Function;
|
|
218
|
-
locator = locator||{}
|
|
219
|
-
function build(key){
|
|
220
|
-
var fn = errorImpl[key];
|
|
221
|
-
if(!fn && isCallback){
|
|
222
|
-
fn = errorImpl.length == 2?function(msg){errorImpl(key,msg)}:errorImpl;
|
|
223
|
-
}
|
|
224
|
-
errorHandler[key] = fn && function(msg){
|
|
225
|
-
fn('[xmldom '+key+']\t'+msg+_locator(locator));
|
|
226
|
-
}||function(){};
|
|
244
|
+
sax.parse(this.normalizeLineEndings(String(source)), defaultNSMap, entityMap);
|
|
245
|
+
if (!domBuilder.doc.documentElement) {
|
|
246
|
+
sax.errorHandler.fatalError('missing root element');
|
|
227
247
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
build('fatalError');
|
|
231
|
-
return errorHandler;
|
|
232
|
-
}
|
|
248
|
+
return domBuilder.doc;
|
|
249
|
+
};
|
|
233
250
|
|
|
234
251
|
/**
|
|
235
252
|
* @typedef DOMHandlerOptions
|
|
236
253
|
* @property {string} [mimeType=MIME_TYPE.XML_APPLICATION]
|
|
237
|
-
* @property {string|null} [defaultNamespace=null]
|
|
254
|
+
* @property {string | null} [defaultNamespace=null]
|
|
238
255
|
*/
|
|
239
256
|
/**
|
|
240
|
-
* The class that is used to handle events from the SAX parser to create the related DOM
|
|
257
|
+
* The class that is used to handle events from the SAX parser to create the related DOM
|
|
258
|
+
* elements.
|
|
241
259
|
*
|
|
242
260
|
* Some methods are only implemented as an empty function,
|
|
243
261
|
* since they are (at least currently) not relevant for xmldom.
|
|
244
262
|
*
|
|
245
|
-
* @
|
|
263
|
+
* @class
|
|
246
264
|
* @param {DOMHandlerOptions} [options]
|
|
247
265
|
* @see http://www.saxproject.org/apidoc/org/xml/sax/ext/DefaultHandler2.html
|
|
248
266
|
*/
|
|
249
267
|
function DOMHandler(options) {
|
|
250
|
-
var opt = options || {}
|
|
268
|
+
var opt = options || {};
|
|
251
269
|
/**
|
|
252
270
|
* The mime type is used to determine if the DOM handler will create an XML or HTML document.
|
|
253
271
|
* Only if it is set to `text/html` it will create an HTML document.
|
|
254
272
|
* It defaults to MIME_TYPE.XML_APPLICATION.
|
|
255
273
|
*
|
|
256
274
|
* @type {string}
|
|
275
|
+
* @see {@link MIME_TYPE}
|
|
257
276
|
* @readonly
|
|
258
|
-
* @see MIME_TYPE
|
|
259
277
|
*/
|
|
260
|
-
this.mimeType = opt.mimeType || MIME_TYPE.XML_APPLICATION
|
|
278
|
+
this.mimeType = opt.mimeType || MIME_TYPE.XML_APPLICATION;
|
|
261
279
|
|
|
262
280
|
/**
|
|
263
281
|
* The namespace to use to create an XML document.
|
|
264
282
|
* For the following reasons this is required:
|
|
265
283
|
* - The SAX API for `startDocument` doesn't offer any way to pass a namespace,
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
284
|
+
* since at that point there is no way for the parser to know what the default namespace from
|
|
285
|
+
* the document will be.
|
|
286
|
+
* - When creating using `DOMImplementation.createDocument` it is required to pass a
|
|
287
|
+
* namespace,
|
|
288
|
+
* to determine the correct `Document.contentType`, which should match `this.mimeType`.
|
|
269
289
|
* - When parsing an XML document with the `application/xhtml+xml` mimeType,
|
|
270
|
-
*
|
|
290
|
+
* the HTML namespace needs to be the default namespace.
|
|
271
291
|
*
|
|
272
|
-
* @type {string|null}
|
|
273
|
-
* @readonly
|
|
292
|
+
* @type {string | null}
|
|
274
293
|
* @private
|
|
294
|
+
* @readonly
|
|
275
295
|
*/
|
|
276
|
-
this.defaultNamespace = opt.defaultNamespace || null
|
|
296
|
+
this.defaultNamespace = opt.defaultNamespace || null;
|
|
277
297
|
|
|
278
298
|
/**
|
|
279
|
-
* @private
|
|
280
299
|
* @type {boolean}
|
|
300
|
+
* @private
|
|
281
301
|
*/
|
|
282
|
-
this.cdata = false
|
|
283
|
-
|
|
302
|
+
this.cdata = false;
|
|
284
303
|
|
|
285
304
|
/**
|
|
286
305
|
* The last `Element` that was created by `startElement`.
|
|
@@ -291,7 +310,7 @@ function DOMHandler(options) {
|
|
|
291
310
|
* @type {Element | Node | undefined}
|
|
292
311
|
* @private
|
|
293
312
|
*/
|
|
294
|
-
this.currentElement = undefined
|
|
313
|
+
this.currentElement = undefined;
|
|
295
314
|
|
|
296
315
|
/**
|
|
297
316
|
* The Document that is created as part of `startDocument`,
|
|
@@ -300,25 +319,32 @@ function DOMHandler(options) {
|
|
|
300
319
|
* @type {Document | undefined}
|
|
301
320
|
* @readonly
|
|
302
321
|
*/
|
|
303
|
-
this.doc = undefined
|
|
322
|
+
this.doc = undefined;
|
|
304
323
|
|
|
305
324
|
/**
|
|
306
325
|
* The locator is stored as part of setDocumentLocator.
|
|
307
|
-
* It is controlled and mutated by the SAX parser
|
|
308
|
-
* to store the current parsing position.
|
|
326
|
+
* It is controlled and mutated by the SAX parser to store the current parsing position.
|
|
309
327
|
* It is used by DOMHandler to set `columnNumber` and `lineNumber`
|
|
310
328
|
* on the DOM nodes.
|
|
311
329
|
*
|
|
312
330
|
* @type {Readonly<Locator> | undefined}
|
|
313
|
-
* @readonly (the sax parser currently sometimes set's it)
|
|
314
331
|
* @private
|
|
332
|
+
* @readonly (the
|
|
333
|
+
* sax parser currently sometimes set's it)
|
|
334
|
+
*/
|
|
335
|
+
this.locator = undefined;
|
|
336
|
+
/**
|
|
337
|
+
* @type {function (level:ErrorLevel ,message:string, context:DOMHandler):void}
|
|
338
|
+
* @readonly
|
|
315
339
|
*/
|
|
316
|
-
this.
|
|
340
|
+
this.onError = opt.onError;
|
|
317
341
|
}
|
|
318
|
-
|
|
342
|
+
|
|
343
|
+
function position(locator, node) {
|
|
319
344
|
node.lineNumber = locator.lineNumber;
|
|
320
345
|
node.columnNumber = locator.columnNumber;
|
|
321
346
|
}
|
|
347
|
+
|
|
322
348
|
DOMHandler.prototype = {
|
|
323
349
|
/**
|
|
324
350
|
* Either creates an XML or an HTML document and stores it under `this.doc`.
|
|
@@ -326,69 +352,61 @@ DOMHandler.prototype = {
|
|
|
326
352
|
* and it will not contain any `childNodes`.
|
|
327
353
|
* If it is an HTML document, it will be created without any `childNodes`.
|
|
328
354
|
*
|
|
329
|
-
* @see
|
|
355
|
+
* @see http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
|
|
330
356
|
*/
|
|
331
|
-
startDocument
|
|
332
|
-
var impl = new DOMImplementation()
|
|
333
|
-
this.doc =
|
|
334
|
-
? impl.createHTMLDocument(false)
|
|
335
|
-
: impl.createDocument(this.defaultNamespace, '')
|
|
357
|
+
startDocument: function () {
|
|
358
|
+
var impl = new DOMImplementation();
|
|
359
|
+
this.doc = isHTMLMimeType(this.mimeType) ? impl.createHTMLDocument(false) : impl.createDocument(this.defaultNamespace, '');
|
|
336
360
|
},
|
|
337
|
-
startElement:function(namespaceURI, localName, qName, attrs) {
|
|
361
|
+
startElement: function (namespaceURI, localName, qName, attrs) {
|
|
338
362
|
var doc = this.doc;
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
this.locator && position(this.locator,el)
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
363
|
+
var el = doc.createElementNS(namespaceURI, qName || localName);
|
|
364
|
+
var len = attrs.length;
|
|
365
|
+
appendElement(this, el);
|
|
366
|
+
this.currentElement = el;
|
|
367
|
+
|
|
368
|
+
this.locator && position(this.locator, el);
|
|
369
|
+
for (var i = 0; i < len; i++) {
|
|
370
|
+
var namespaceURI = attrs.getURI(i);
|
|
371
|
+
var value = attrs.getValue(i);
|
|
372
|
+
var qName = attrs.getQName(i);
|
|
349
373
|
var attr = doc.createAttributeNS(namespaceURI, qName);
|
|
350
|
-
this.locator &&position(attrs.getLocator(i),attr);
|
|
374
|
+
this.locator && position(attrs.getLocator(i), attr);
|
|
351
375
|
attr.value = attr.nodeValue = value;
|
|
352
|
-
el.setAttributeNode(attr)
|
|
353
|
-
|
|
354
|
-
},
|
|
355
|
-
endElement:function(namespaceURI, localName, qName) {
|
|
356
|
-
var current = this.currentElement
|
|
357
|
-
var tagName = current.tagName;
|
|
358
|
-
this.currentElement = current.parentNode;
|
|
359
|
-
},
|
|
360
|
-
startPrefixMapping:function(prefix, uri) {
|
|
361
|
-
},
|
|
362
|
-
endPrefixMapping:function(prefix) {
|
|
376
|
+
el.setAttributeNode(attr);
|
|
377
|
+
}
|
|
363
378
|
},
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
this.locator && position(this.locator,ins)
|
|
367
|
-
appendElement(this, ins);
|
|
379
|
+
endElement: function (namespaceURI, localName, qName) {
|
|
380
|
+
this.currentElement = this.currentElement.parentNode;
|
|
368
381
|
},
|
|
369
|
-
|
|
382
|
+
startPrefixMapping: function (prefix, uri) {},
|
|
383
|
+
endPrefixMapping: function (prefix) {},
|
|
384
|
+
processingInstruction: function (target, data) {
|
|
385
|
+
var ins = this.doc.createProcessingInstruction(target, data);
|
|
386
|
+
this.locator && position(this.locator, ins);
|
|
387
|
+
appendElement(this, ins);
|
|
370
388
|
},
|
|
371
|
-
|
|
372
|
-
|
|
389
|
+
ignorableWhitespace: function (ch, start, length) {},
|
|
390
|
+
characters: function (chars, start, length) {
|
|
391
|
+
chars = _toString.apply(this, arguments);
|
|
373
392
|
//console.log(chars)
|
|
374
|
-
if(chars){
|
|
393
|
+
if (chars) {
|
|
375
394
|
if (this.cdata) {
|
|
376
395
|
var charNode = this.doc.createCDATASection(chars);
|
|
377
396
|
} else {
|
|
378
397
|
var charNode = this.doc.createTextNode(chars);
|
|
379
398
|
}
|
|
380
|
-
if(this.currentElement){
|
|
399
|
+
if (this.currentElement) {
|
|
381
400
|
this.currentElement.appendChild(charNode);
|
|
382
|
-
}else if(/^\s*$/.test(chars)){
|
|
401
|
+
} else if (/^\s*$/.test(chars)) {
|
|
383
402
|
this.doc.appendChild(charNode);
|
|
384
403
|
//process xml
|
|
385
404
|
}
|
|
386
|
-
this.locator && position(this.locator,charNode)
|
|
405
|
+
this.locator && position(this.locator, charNode);
|
|
387
406
|
}
|
|
388
407
|
},
|
|
389
|
-
skippedEntity:function(name) {
|
|
390
|
-
|
|
391
|
-
endDocument:function() {
|
|
408
|
+
skippedEntity: function (name) {},
|
|
409
|
+
endDocument: function () {
|
|
392
410
|
this.doc.normalize();
|
|
393
411
|
},
|
|
394
412
|
/**
|
|
@@ -397,62 +415,85 @@ DOMHandler.prototype = {
|
|
|
397
415
|
*
|
|
398
416
|
* @param {Locator} locator
|
|
399
417
|
*/
|
|
400
|
-
setDocumentLocator:function (locator) {
|
|
418
|
+
setDocumentLocator: function (locator) {
|
|
401
419
|
if (locator) {
|
|
402
|
-
locator.lineNumber = 0
|
|
420
|
+
locator.lineNumber = 0;
|
|
403
421
|
}
|
|
404
|
-
this.locator = locator
|
|
422
|
+
this.locator = locator;
|
|
405
423
|
},
|
|
406
424
|
//LexicalHandler
|
|
407
|
-
comment:function(chars, start, length) {
|
|
408
|
-
chars = _toString.apply(this,arguments)
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
425
|
+
comment: function (chars, start, length) {
|
|
426
|
+
chars = _toString.apply(this, arguments);
|
|
427
|
+
var comm = this.doc.createComment(chars);
|
|
428
|
+
this.locator && position(this.locator, comm);
|
|
429
|
+
appendElement(this, comm);
|
|
412
430
|
},
|
|
413
431
|
|
|
414
|
-
startCDATA:function() {
|
|
415
|
-
|
|
416
|
-
|
|
432
|
+
startCDATA: function () {
|
|
433
|
+
//used in characters() methods
|
|
434
|
+
this.cdata = true;
|
|
417
435
|
},
|
|
418
|
-
endCDATA:function() {
|
|
419
|
-
|
|
436
|
+
endCDATA: function () {
|
|
437
|
+
this.cdata = false;
|
|
420
438
|
},
|
|
421
439
|
|
|
422
|
-
startDTD:function(name, publicId, systemId) {
|
|
440
|
+
startDTD: function (name, publicId, systemId, internalSubset) {
|
|
423
441
|
var impl = this.doc.implementation;
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
442
|
+
if (impl && impl.createDocumentType) {
|
|
443
|
+
var dt = impl.createDocumentType(name, publicId, systemId, internalSubset);
|
|
444
|
+
this.locator && position(this.locator, dt);
|
|
445
|
+
appendElement(this, dt);
|
|
446
|
+
this.doc.doctype = dt;
|
|
447
|
+
}
|
|
448
|
+
},
|
|
449
|
+
reportError: function (level, message) {
|
|
450
|
+
if (typeof this.onError === 'function') {
|
|
451
|
+
try {
|
|
452
|
+
this.onError(level, message, this);
|
|
453
|
+
} catch (e) {
|
|
454
|
+
throw new ParseError('Reporting ' + level + ' "' + message + '" caused ' + e, this.locator);
|
|
455
|
+
}
|
|
456
|
+
} else {
|
|
457
|
+
console.error('[xmldom ' + level + ']\t' + message, _locator(this.locator));
|
|
458
|
+
}
|
|
430
459
|
},
|
|
431
460
|
/**
|
|
432
|
-
* @see org
|
|
433
|
-
* @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
|
|
461
|
+
* @see http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
|
|
434
462
|
*/
|
|
435
|
-
warning:function(
|
|
436
|
-
|
|
463
|
+
warning: function (message) {
|
|
464
|
+
this.reportError('warning', message);
|
|
437
465
|
},
|
|
438
|
-
error:function(
|
|
439
|
-
|
|
466
|
+
error: function (message) {
|
|
467
|
+
this.reportError('error', message);
|
|
440
468
|
},
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
469
|
+
/**
|
|
470
|
+
* This function reports a fatal error and throws a ParseError.
|
|
471
|
+
*
|
|
472
|
+
* @param {string} message
|
|
473
|
+
* - The message to be used for reporting and throwing the error.
|
|
474
|
+
* @returns {never} This function always throws an error and never returns a value.
|
|
475
|
+
* @throws {ParseError}
|
|
476
|
+
* Always throws a ParseError with the provided message.
|
|
477
|
+
*/
|
|
478
|
+
fatalError: function (message) {
|
|
479
|
+
this.reportError('fatalError', message);
|
|
480
|
+
throw new ParseError(message, this.locator);
|
|
481
|
+
},
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
function _locator(l) {
|
|
485
|
+
if (l) {
|
|
486
|
+
return '\n@#[line:' + l.lineNumber + ',col:' + l.columnNumber + ']';
|
|
448
487
|
}
|
|
449
488
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
489
|
+
|
|
490
|
+
function _toString(chars, start, length) {
|
|
491
|
+
if (typeof chars == 'string') {
|
|
492
|
+
return chars.substr(start, length);
|
|
493
|
+
} else {
|
|
494
|
+
//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
|
|
495
|
+
if (chars.length >= start + length || start) {
|
|
496
|
+
return new java.lang.String(chars, start, length) + '';
|
|
456
497
|
}
|
|
457
498
|
return chars;
|
|
458
499
|
}
|
|
@@ -489,19 +530,47 @@ function _toString(chars,start,length){
|
|
|
489
530
|
* #notationDecl(name, publicId, systemId) {};
|
|
490
531
|
* #unparsedEntityDecl(name, publicId, systemId, notationName) {};
|
|
491
532
|
*/
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
533
|
+
'endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl'.replace(
|
|
534
|
+
/\w+/g,
|
|
535
|
+
function (key) {
|
|
536
|
+
DOMHandler.prototype[key] = function () {
|
|
537
|
+
return null;
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
);
|
|
495
541
|
|
|
496
542
|
/* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */
|
|
497
|
-
function appendElement
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
}
|
|
543
|
+
function appendElement(handler, node) {
|
|
544
|
+
if (!handler.currentElement) {
|
|
545
|
+
handler.doc.appendChild(node);
|
|
546
|
+
} else {
|
|
547
|
+
handler.currentElement.appendChild(node);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* A method that prevents any further parsing when an `error`
|
|
553
|
+
* with level `error` is reported during parsing.
|
|
554
|
+
*
|
|
555
|
+
* @see {@link DOMParserOptions.onError}
|
|
556
|
+
* @see {@link onWarningStopParsing}
|
|
557
|
+
*/
|
|
558
|
+
function onErrorStopParsing(level) {
|
|
559
|
+
if (level === 'error') throw 'onErrorStopParsing';
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* A method that prevents any further parsing when any `error` is reported during parsing.
|
|
564
|
+
*
|
|
565
|
+
* @see {@link DOMParserOptions.onError}
|
|
566
|
+
* @see {@link onErrorStopParsing}
|
|
567
|
+
*/
|
|
568
|
+
function onWarningStopParsing() {
|
|
569
|
+
throw 'onWarningStopParsing';
|
|
570
|
+
}
|
|
504
571
|
|
|
505
572
|
exports.__DOMHandler = DOMHandler;
|
|
506
|
-
exports.normalizeLineEndings = normalizeLineEndings;
|
|
507
573
|
exports.DOMParser = DOMParser;
|
|
574
|
+
exports.normalizeLineEndings = normalizeLineEndings;
|
|
575
|
+
exports.onErrorStopParsing = onErrorStopParsing;
|
|
576
|
+
exports.onWarningStopParsing = onWarningStopParsing;
|