@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/lib/dom-parser.js CHANGED
@@ -1,16 +1,19 @@
1
- 'use strict'
1
+ 'use strict';
2
2
 
3
- var conventions = require("./conventions");
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 conventions.assign} [assign=Object.assign || conventions.assign]
52
- * The method to use instead of `Object.assign` (or if not available `conventions.assign`),
53
- * which is used to copy values from the options before they are used for parsing.
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 parser.
56
- * Warning: By configuring a faulty implementation, the specified behavior can completely be broken.
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
- * will have a `lineNumber` and a `columnNumber` attribute
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 {object} [xmlns]
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
- * from a string into a DOM `Document`.
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
- * _xmldom is different from the spec in that it allows an `options` parameter,
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 `Object.assign` (or if not available `conventions.assign`),
94
- * which is used to copy values from the options before they are used for parsing.
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 {function (target: object, source: object | null | undefined): object}
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 || Object.assign || conventions.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 parser.
105
- * __**Warning: By configuring a faulty implementation, the specified behavior can completely be broken.**__
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 can be invoked as the errorHandler instead of the default ones.
115
- * @type {Function | undefined}
116
- * @readonly
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.errorHandler = options.errorHandler;
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
- * will have a `lineNumber` and a `columnNumber` attribute
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
- * @type {Readonly<object>}
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` `DOMParser`.
151
- * If `mimeType` is `text/html` an HTML `Document` is created, otherwise an XML `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 very different from the description in the living standard__:
154
- * - Only allows the first argument to be a string (calls `error` handler otherwise.)
155
- * - The second parameter is optional (defaults to `application/xml`) and can be any string,
156
- * no `TypeError` will be thrown for values not listed in the spec.
157
- * - Uses the `options` passed to the `DOMParser` constructor to modify the behavior/implementation.
158
- * - Instead of creating a Document containing the error message,
159
- * it triggers `errorHandler`(s) when unexpected input is found, which means it can return `undefined`.
160
- * All error handlers can throw an `Error`, by default only the `fatalError` handler throws (a `ParserError`).
161
- * - All errors thrown during the parsing that are not a `ParserError` are caught and reported using the `error` handler.
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 Only string input is possible!
198
+ * @param {string} source
199
+ * The XML mime type only allows string input!
168
200
  * @param {string} [mimeType='application/xml']
169
- * the mimeType or contentType of the document to be created
170
- * determines the `type` of document created (XML or HTML)
171
- * @returns {Document | undefined}
172
- * @throws ParseError for specific errors depending on the configured `errorHandler`s and/or `domBuilder`
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
- var defaultNSMap = this.assign({}, this.xmlns)
179
- var entityMap = entities.XML_ENTITIES
180
- var defaultNamespace = defaultNSMap[''] || null
181
- if (MIME_TYPE.hasDefaultHTMLNamespace(mimeType)) {
182
- entityMap = entities.HTML_ENTITIES
183
- defaultNamespace = NAMESPACE.HTML
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 = buildErrorHandler(this.errorHandler, domBuilder, locator)
201
- sax.domBuilder = domBuilder
202
- if (source && typeof source === 'string') {
203
- sax.parse(this.normalizeLineEndings(source), defaultNSMap, entityMap)
204
- } else {
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
- return domBuilder.doc
208
- }
209
- function buildErrorHandler(errorImpl,domBuilder,locator){
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
- build('warning');
229
- build('error');
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 elements.
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
- * @constructor
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
- * since at that point there is no way for the parser to know what the default namespace from the document will be.
267
- * - When creating using `DOMImplementation.createDocument` it is required to pass a namespace,
268
- * to determine the correct `Document.contentType`, which should match `this.mimeType`.
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
- * the HTML namespace needs to be the default namespace.
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.locator = undefined
340
+ this.onError = opt.onError;
317
341
  }
318
- function position(locator,node){
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 http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
355
+ * @see http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
330
356
  */
331
- startDocument : function() {
332
- var impl = new DOMImplementation()
333
- this.doc = MIME_TYPE.isHTML(this.mimeType)
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
- var el = doc.createElementNS(namespaceURI, qName||localName);
340
- var len = attrs.length;
341
- appendElement(this, el);
342
- this.currentElement = el;
343
-
344
- this.locator && position(this.locator,el)
345
- for (var i = 0 ; i < len; i++) {
346
- var namespaceURI = attrs.getURI(i);
347
- var value = attrs.getValue(i);
348
- var qName = attrs.getQName(i);
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
- processingInstruction:function(target, data) {
365
- var ins = this.doc.createProcessingInstruction(target, data);
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
- ignorableWhitespace:function(ch, start, length) {
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
- characters:function(chars, start, length) {
372
- chars = _toString.apply(this,arguments)
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
- var comm = this.doc.createComment(chars);
410
- this.locator && position(this.locator,comm)
411
- appendElement(this, comm);
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
- //used in characters() methods
416
- this.cdata = true;
432
+ startCDATA: function () {
433
+ //used in characters() methods
434
+ this.cdata = true;
417
435
  },
418
- endCDATA:function() {
419
- this.cdata = false;
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
- if (impl && impl.createDocumentType) {
425
- var dt = impl.createDocumentType(name, publicId, systemId);
426
- this.locator && position(this.locator,dt)
427
- appendElement(this, dt);
428
- this.doc.doctype = dt;
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.xml.sax.ErrorHandler
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(error) {
436
- console.warn('[xmldom warning]\t'+error,_locator(this.locator));
463
+ warning: function (message) {
464
+ this.reportError('warning', message);
437
465
  },
438
- error:function(error) {
439
- console.error('[xmldom error]\t'+error,_locator(this.locator));
466
+ error: function (message) {
467
+ this.reportError('error', message);
440
468
  },
441
- fatalError:function(error) {
442
- throw new ParseError(error, this.locator);
443
- }
444
- }
445
- function _locator(l){
446
- if(l){
447
- return '\n@#[line:'+l.lineNumber+',col:'+l.columnNumber+']'
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
- function _toString(chars,start,length){
451
- if(typeof chars == 'string'){
452
- return chars.substr(start,length)
453
- }else{//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
454
- if(chars.length >= start+length || start){
455
- return new java.lang.String(chars,start,length)+'';
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
- "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){
493
- DOMHandler.prototype[key] = function(){return null}
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 (hander,node) {
498
- if (!hander.currentElement) {
499
- hander.doc.appendChild(node);
500
- } else {
501
- hander.currentElement.appendChild(node);
502
- }
503
- }//appendChild and setAttributeNS are preformance key
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;