@lango-project/lango-js 0.1.1 → 0.1.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.
@@ -0,0 +1,3759 @@
1
+ 'use strict';
2
+
3
+ var conventions = require('./conventions');
4
+ var find = conventions.find;
5
+ var hasDefaultHTMLNamespace = conventions.hasDefaultHTMLNamespace;
6
+ var hasOwn = conventions.hasOwn;
7
+ var isHTMLMimeType = conventions.isHTMLMimeType;
8
+ var isHTMLRawTextElement = conventions.isHTMLRawTextElement;
9
+ var isHTMLVoidElement = conventions.isHTMLVoidElement;
10
+ var MIME_TYPE = conventions.MIME_TYPE;
11
+ var NAMESPACE = conventions.NAMESPACE;
12
+
13
+ /**
14
+ * Private DOM Constructor symbol
15
+ *
16
+ * Internal symbol used for construction of all classes whose constructors should be private.
17
+ * Currently used for checks in `Node`, `Document`, `Element`, `Attr`, `CharacterData`, `Text`, `Comment`,
18
+ * `CDATASection`, `DocumentType`, `Notation`, `Entity`, `EntityReference`, `DocumentFragment`, `ProcessingInstruction`
19
+ * so the constructor can't be used from outside the module.
20
+ */
21
+ var PDC = Symbol();
22
+
23
+ var errors = require('./errors');
24
+ var DOMException = errors.DOMException;
25
+ var DOMExceptionName = errors.DOMExceptionName;
26
+
27
+ var g = require('./grammar');
28
+
29
+ /**
30
+ * Checks if the given symbol equals the Private DOM Constructor symbol (PDC)
31
+ * and throws an Illegal constructor exception when the symbols don't match.
32
+ * This ensures that the constructor remains private and can't be used outside this module.
33
+ */
34
+ function checkSymbol(symbol) {
35
+ if (symbol !== PDC) {
36
+ throw new TypeError('Illegal constructor');
37
+ }
38
+ }
39
+
40
+ /**
41
+ * A prerequisite for `[].filter`, to drop elements that are empty.
42
+ *
43
+ * @param {string} input
44
+ * The string to be checked.
45
+ * @returns {boolean}
46
+ * Returns `true` if the input string is not empty, `false` otherwise.
47
+ */
48
+ function notEmptyString(input) {
49
+ return input !== '';
50
+ }
51
+ /**
52
+ * Splits a string on ASCII whitespace characters (U+0009 TAB, U+000A LF, U+000C FF, U+000D CR,
53
+ * U+0020 SPACE).
54
+ * It follows the definition from the infra specification from WHATWG.
55
+ *
56
+ * @param {string} input
57
+ * The string to be split.
58
+ * @returns {string[]}
59
+ * An array of the split strings. The array can be empty if the input string is empty or only
60
+ * contains whitespace characters.
61
+ * @see {@link https://infra.spec.whatwg.org/#split-on-ascii-whitespace}
62
+ * @see {@link https://infra.spec.whatwg.org/#ascii-whitespace}
63
+ */
64
+ function splitOnASCIIWhitespace(input) {
65
+ // U+0009 TAB, U+000A LF, U+000C FF, U+000D CR, U+0020 SPACE
66
+ return input ? input.split(/[\t\n\f\r ]+/).filter(notEmptyString) : [];
67
+ }
68
+
69
+ /**
70
+ * Adds element as a key to current if it is not already present.
71
+ *
72
+ * @param {Record<string, boolean | undefined>} current
73
+ * The current record object to which the element will be added as a key.
74
+ * The object's keys are string types and values are either boolean or undefined.
75
+ * @param {string} element
76
+ * The string to be added as a key to the current record.
77
+ * @returns {Record<string, boolean | undefined>}
78
+ * The updated record object after the addition of the new element.
79
+ */
80
+ function orderedSetReducer(current, element) {
81
+ if (!hasOwn(current, element)) {
82
+ current[element] = true;
83
+ }
84
+ return current;
85
+ }
86
+
87
+ /**
88
+ * Converts a string into an ordered set by splitting the input on ASCII whitespace and
89
+ * ensuring uniqueness of elements.
90
+ * This follows the definition of an ordered set from the infra specification by WHATWG.
91
+ *
92
+ * @param {string} input
93
+ * The input string to be transformed into an ordered set.
94
+ * @returns {string[]}
95
+ * An array of unique strings obtained from the input, preserving the original order.
96
+ * The array can be empty if the input string is empty or only contains whitespace characters.
97
+ * @see {@link https://infra.spec.whatwg.org/#ordered-set}
98
+ */
99
+ function toOrderedSet(input) {
100
+ if (!input) return [];
101
+ var list = splitOnASCIIWhitespace(input);
102
+ return Object.keys(list.reduce(orderedSetReducer, {}));
103
+ }
104
+
105
+ /**
106
+ * Uses `list.indexOf` to implement a function that behaves like `Array.prototype.includes`.
107
+ * This function is used in environments where `Array.prototype.includes` may not be available.
108
+ *
109
+ * @param {any[]} list
110
+ * The array in which to search for the element.
111
+ * @returns {function(any): boolean}
112
+ * A function that accepts an element and returns a boolean indicating whether the element is
113
+ * included in the provided list.
114
+ */
115
+ function arrayIncludes(list) {
116
+ return function (element) {
117
+ return list && list.indexOf(element) !== -1;
118
+ };
119
+ }
120
+
121
+ /**
122
+ * Validates a qualified name based on the criteria provided in the DOM specification by
123
+ * WHATWG.
124
+ *
125
+ * @param {string} qualifiedName
126
+ * The qualified name to be validated.
127
+ * @throws {DOMException}
128
+ * With code {@link DOMException.INVALID_CHARACTER_ERR} if the qualified name contains an
129
+ * invalid character.
130
+ * @see {@link https://dom.spec.whatwg.org/#validate}
131
+ */
132
+ function validateQualifiedName(qualifiedName) {
133
+ if (!g.QName_exact.test(qualifiedName)) {
134
+ throw new DOMException(DOMException.INVALID_CHARACTER_ERR, 'invalid character in qualified name "' + qualifiedName + '"');
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Validates a qualified name and the namespace associated with it,
140
+ * based on the criteria provided in the DOM specification by WHATWG.
141
+ *
142
+ * @param {string | null} namespace
143
+ * The namespace to be validated. It can be a string or null.
144
+ * @param {string} qualifiedName
145
+ * The qualified name to be validated.
146
+ * @returns {[namespace: string | null, prefix: string | null, localName: string]}
147
+ * Returns a tuple with the namespace,
148
+ * prefix and local name of the qualified name.
149
+ * @throws {DOMException}
150
+ * Throws a DOMException if the qualified name or the namespace is not valid.
151
+ * @see {@link https://dom.spec.whatwg.org/#validate-and-extract}
152
+ */
153
+ function validateAndExtract(namespace, qualifiedName) {
154
+ validateQualifiedName(qualifiedName);
155
+ namespace = namespace || null;
156
+ /**
157
+ * @type {string | null}
158
+ */
159
+ var prefix = null;
160
+ var localName = qualifiedName;
161
+ if (qualifiedName.indexOf(':') >= 0) {
162
+ var splitResult = qualifiedName.split(':');
163
+ prefix = splitResult[0];
164
+ localName = splitResult[1];
165
+ }
166
+ if (prefix !== null && namespace === null) {
167
+ throw new DOMException(DOMException.NAMESPACE_ERR, 'prefix is non-null and namespace is null');
168
+ }
169
+ if (prefix === 'xml' && namespace !== conventions.NAMESPACE.XML) {
170
+ throw new DOMException(DOMException.NAMESPACE_ERR, 'prefix is "xml" and namespace is not the XML namespace');
171
+ }
172
+ if ((prefix === 'xmlns' || qualifiedName === 'xmlns') && namespace !== conventions.NAMESPACE.XMLNS) {
173
+ throw new DOMException(
174
+ DOMException.NAMESPACE_ERR,
175
+ 'either qualifiedName or prefix is "xmlns" and namespace is not the XMLNS namespace'
176
+ );
177
+ }
178
+ if (namespace === conventions.NAMESPACE.XMLNS && prefix !== 'xmlns' && qualifiedName !== 'xmlns') {
179
+ throw new DOMException(
180
+ DOMException.NAMESPACE_ERR,
181
+ 'namespace is the XMLNS namespace and neither qualifiedName nor prefix is "xmlns"'
182
+ );
183
+ }
184
+ return [namespace, prefix, localName];
185
+ }
186
+
187
+ /**
188
+ * Copies properties from one object to another.
189
+ * It only copies the object's own (not inherited) properties.
190
+ *
191
+ * @param {Object} src
192
+ * The source object from which properties are copied.
193
+ * @param {Object} dest
194
+ * The destination object to which properties are copied.
195
+ */
196
+ function copy(src, dest) {
197
+ for (var p in src) {
198
+ if (hasOwn(src, p)) {
199
+ dest[p] = src[p];
200
+ }
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Extends a class with the properties and methods of a super class.
206
+ * It uses a form of prototypal inheritance, and establishes the `constructor` property
207
+ * correctly(?).
208
+ *
209
+ * It is not clear to the current maintainers if this implementation is making sense,
210
+ * since it creates an intermediate prototype function,
211
+ * which all properties of `Super` are copied onto using `_copy`.
212
+ *
213
+ * @param {Object} Class
214
+ * The class that is to be extended.
215
+ * @param {Object} Super
216
+ * The super class from which properties and methods are inherited.
217
+ * @private
218
+ */
219
+ function _extends(Class, Super) {
220
+ var pt = Class.prototype;
221
+ if (!(pt instanceof Super)) {
222
+ function t() {}
223
+ t.prototype = Super.prototype;
224
+ t = new t();
225
+ copy(pt, t);
226
+ Class.prototype = pt = t;
227
+ }
228
+ if (pt.constructor != Class) {
229
+ if (typeof Class != 'function') {
230
+ console.error('unknown Class:' + Class);
231
+ }
232
+ pt.constructor = Class;
233
+ }
234
+ }
235
+
236
+ var NodeType = {};
237
+ var ELEMENT_NODE = (NodeType.ELEMENT_NODE = 1);
238
+ var ATTRIBUTE_NODE = (NodeType.ATTRIBUTE_NODE = 2);
239
+ var TEXT_NODE = (NodeType.TEXT_NODE = 3);
240
+ var CDATA_SECTION_NODE = (NodeType.CDATA_SECTION_NODE = 4);
241
+ var ENTITY_REFERENCE_NODE = (NodeType.ENTITY_REFERENCE_NODE = 5);
242
+ var ENTITY_NODE = (NodeType.ENTITY_NODE = 6);
243
+ var PROCESSING_INSTRUCTION_NODE = (NodeType.PROCESSING_INSTRUCTION_NODE = 7);
244
+ var COMMENT_NODE = (NodeType.COMMENT_NODE = 8);
245
+ var DOCUMENT_NODE = (NodeType.DOCUMENT_NODE = 9);
246
+ var DOCUMENT_TYPE_NODE = (NodeType.DOCUMENT_TYPE_NODE = 10);
247
+ var DOCUMENT_FRAGMENT_NODE = (NodeType.DOCUMENT_FRAGMENT_NODE = 11);
248
+ var NOTATION_NODE = (NodeType.NOTATION_NODE = 12);
249
+
250
+ var DocumentPosition = conventions.freeze({
251
+ DOCUMENT_POSITION_DISCONNECTED: 1,
252
+ DOCUMENT_POSITION_PRECEDING: 2,
253
+ DOCUMENT_POSITION_FOLLOWING: 4,
254
+ DOCUMENT_POSITION_CONTAINS: 8,
255
+ DOCUMENT_POSITION_CONTAINED_BY: 16,
256
+ DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 32,
257
+ });
258
+
259
+ //helper functions for compareDocumentPosition
260
+ /**
261
+ * Finds the common ancestor in two parent chains.
262
+ *
263
+ * @param {Node[]} a
264
+ * The first parent chain.
265
+ * @param {Node[]} b
266
+ * The second parent chain.
267
+ * @returns {Node}
268
+ * The common ancestor node if it exists. If there is no common ancestor, the function will
269
+ * return `null`.
270
+ */
271
+ function commonAncestor(a, b) {
272
+ if (b.length < a.length) return commonAncestor(b, a);
273
+ var c = null;
274
+ for (var n in a) {
275
+ if (a[n] !== b[n]) return c;
276
+ c = a[n];
277
+ }
278
+ return c;
279
+ }
280
+
281
+ /**
282
+ * Assigns a unique identifier to a document to ensure consistency while comparing unrelated
283
+ * nodes.
284
+ *
285
+ * @param {Document} doc
286
+ * The document to which a unique identifier is to be assigned.
287
+ * @returns {string}
288
+ * The unique identifier of the document. If the document already had a unique identifier, the
289
+ * function will return the existing one.
290
+ */
291
+ function docGUID(doc) {
292
+ if (!doc.guid) doc.guid = Math.random();
293
+ return doc.guid;
294
+ }
295
+ //-- end of helper functions
296
+
297
+ /**
298
+ * The NodeList interface provides the abstraction of an ordered collection of nodes,
299
+ * without defining or constraining how this collection is implemented.
300
+ * NodeList objects in the DOM are live.
301
+ * The items in the NodeList are accessible via an integral index, starting from 0.
302
+ * You can also access the items of the NodeList with a `for...of` loop.
303
+ *
304
+ * @class NodeList
305
+ * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
306
+ * @constructs NodeList
307
+ */
308
+ function NodeList() {}
309
+ NodeList.prototype = {
310
+ /**
311
+ * The number of nodes in the list. The range of valid child node indices is 0 to length-1
312
+ * inclusive.
313
+ *
314
+ * @type {number}
315
+ */
316
+ length: 0,
317
+ /**
318
+ * Returns the item at `index`. If index is greater than or equal to the number of nodes in
319
+ * the list, this returns null.
320
+ *
321
+ * @param index
322
+ * Unsigned long Index into the collection.
323
+ * @returns {Node | null}
324
+ * The node at position `index` in the NodeList,
325
+ * or null if that is not a valid index.
326
+ */
327
+ item: function (index) {
328
+ return index >= 0 && index < this.length ? this[index] : null;
329
+ },
330
+ /**
331
+ * Returns a string representation of the NodeList.
332
+ *
333
+ * Accepts the same `options` object as `XMLSerializer.prototype.serializeToString`
334
+ * (`requireWellFormed`, `splitCDATASections`, `nodeFilter`). Passing a function is treated as
335
+ * a legacy `nodeFilter` for backward compatibility.
336
+ *
337
+ * @param {Object | function} [options]
338
+ * @param {boolean} [options.requireWellFormed=false]
339
+ * @param {boolean} [options.splitCDATASections=true]
340
+ * @param {function} [options.nodeFilter]
341
+ * @returns {string}
342
+ */
343
+ toString: function (options) {
344
+ var opts;
345
+ if (typeof options === 'function') {
346
+ opts = { requireWellFormed: false, splitCDATASections: true, nodeFilter: options };
347
+ } else if (!!options) {
348
+ opts = {
349
+ requireWellFormed: !!options.requireWellFormed,
350
+ splitCDATASections: options.splitCDATASections !== false,
351
+ nodeFilter: options.nodeFilter || null,
352
+ };
353
+ } else {
354
+ opts = { requireWellFormed: false, splitCDATASections: true, nodeFilter: null };
355
+ }
356
+ for (var buf = [], i = 0; i < this.length; i++) {
357
+ serializeToString(this[i], buf, null, opts);
358
+ }
359
+ return buf.join('');
360
+ },
361
+ /**
362
+ * Filters the NodeList based on a predicate.
363
+ *
364
+ * @param {function(Node): boolean} predicate
365
+ * - A predicate function to filter the NodeList.
366
+ * @returns {Node[]}
367
+ * An array of nodes that satisfy the predicate.
368
+ * @private
369
+ */
370
+ filter: function (predicate) {
371
+ return Array.prototype.filter.call(this, predicate);
372
+ },
373
+ /**
374
+ * Returns the first index at which a given node can be found in the NodeList, or -1 if it is
375
+ * not present.
376
+ *
377
+ * @param {Node} item
378
+ * - The Node item to locate in the NodeList.
379
+ * @returns {number}
380
+ * The first index of the node in the NodeList; -1 if not found.
381
+ * @private
382
+ */
383
+ indexOf: function (item) {
384
+ return Array.prototype.indexOf.call(this, item);
385
+ },
386
+ };
387
+ NodeList.prototype[Symbol.iterator] = function () {
388
+ var me = this;
389
+ var index = 0;
390
+
391
+ return {
392
+ next: function () {
393
+ if (index < me.length) {
394
+ return {
395
+ value: me[index++],
396
+ done: false,
397
+ };
398
+ } else {
399
+ return {
400
+ done: true,
401
+ };
402
+ }
403
+ },
404
+ return: function () {
405
+ return {
406
+ done: true,
407
+ };
408
+ },
409
+ };
410
+ };
411
+
412
+ /**
413
+ * Represents a live collection of nodes that is automatically updated when its associated
414
+ * document changes.
415
+ *
416
+ * @class LiveNodeList
417
+ * @param {Node} node
418
+ * The associated node.
419
+ * @param {function} refresh
420
+ * The function to refresh the live node list.
421
+ * @augments NodeList
422
+ * @constructs LiveNodeList
423
+ */
424
+ function LiveNodeList(node, refresh) {
425
+ this._node = node;
426
+ this._refresh = refresh;
427
+ _updateLiveList(this);
428
+ }
429
+ /**
430
+ * Updates the live node list.
431
+ *
432
+ * @param {LiveNodeList} list
433
+ * The live node list to update.
434
+ * @private
435
+ */
436
+ function _updateLiveList(list) {
437
+ var inc = list._node._inc || list._node.ownerDocument._inc;
438
+ if (list._inc !== inc) {
439
+ var ls = list._refresh(list._node);
440
+ __set__(list, 'length', ls.length);
441
+ if (!list.$$length || ls.length < list.$$length) {
442
+ for (var i = ls.length; i in list; i++) {
443
+ if (hasOwn(list, i)) {
444
+ delete list[i];
445
+ }
446
+ }
447
+ }
448
+ copy(ls, list);
449
+ list._inc = inc;
450
+ }
451
+ }
452
+ /**
453
+ * Returns the node at position `index` in the LiveNodeList, or null if that is not a valid
454
+ * index.
455
+ *
456
+ * @param {number} i
457
+ * Index into the collection.
458
+ * @returns {Node | null}
459
+ * The node at position `index` in the LiveNodeList, or null if that is not a valid index.
460
+ */
461
+ LiveNodeList.prototype.item = function (i) {
462
+ _updateLiveList(this);
463
+ return this[i] || null;
464
+ };
465
+
466
+ _extends(LiveNodeList, NodeList);
467
+
468
+ /**
469
+ * Objects implementing the NamedNodeMap interface are used to represent collections of nodes
470
+ * that can be accessed by name.
471
+ * Note that NamedNodeMap does not inherit from NodeList;
472
+ * NamedNodeMaps are not maintained in any particular order.
473
+ * Objects contained in an object implementing NamedNodeMap may also be accessed by an ordinal
474
+ * index,
475
+ * but this is simply to allow convenient enumeration of the contents of a NamedNodeMap,
476
+ * and does not imply that the DOM specifies an order to these Nodes.
477
+ * NamedNodeMap objects in the DOM are live.
478
+ * used for attributes or DocumentType entities
479
+ *
480
+ * This implementation only supports property indices, but does not support named properties,
481
+ * as specified in the living standard.
482
+ *
483
+ * @class NamedNodeMap
484
+ * @see https://dom.spec.whatwg.org/#interface-namednodemap
485
+ * @see https://webidl.spec.whatwg.org/#dfn-supported-property-names
486
+ * @constructs NamedNodeMap
487
+ */
488
+ /**
489
+ * A live collection of an element's attributes, keyed by name.
490
+ *
491
+ * The numbered entries and `length` are the sole authority for attribute order.
492
+ * A separate two-level null-prototype membership index (`namespaceURI` ->
493
+ * `localName` -> `Attr`, with the null/empty namespace held in its own bucket)
494
+ * lets the parse-time de-duplication in `setNamedItem` resolve an existing
495
+ * attribute in O(1) instead of scanning the list, so building an element with M
496
+ * attributes costs O(M) rather than O(M^2). The index never reorders attributes.
497
+ */
498
+ function NamedNodeMap() {
499
+ // namespaceURI (non-empty string) -> (localName -> Attr)
500
+ this._nsIndex = Object.create(null);
501
+ // localName -> Attr, for the null / empty-string namespace
502
+ this._noNsIndex = Object.create(null);
503
+ }
504
+ /**
505
+ * Returns the index of a node within the list.
506
+ *
507
+ * @param {Array} list
508
+ * The list of nodes.
509
+ * @param {Node} node
510
+ * The node to find.
511
+ * @returns {number}
512
+ * The index of the node within the list, or -1 if not found.
513
+ * @private
514
+ */
515
+ function _findNodeIndex(list, node) {
516
+ var i = 0;
517
+ while (i < list.length) {
518
+ if (list[i] === node) {
519
+ return i;
520
+ }
521
+ i++;
522
+ }
523
+ }
524
+ /**
525
+ * Returns the second-level index bucket (`localName` -> `Attr`) for a namespace,
526
+ * replicating `getNamedItemNS`'s falsy-namespace normalization: `null`,
527
+ * `undefined` and `''` all resolve to the dedicated null-namespace bucket, kept separate from
528
+ * any real URI (so a namespace URI equal to the string `"null"`
529
+ * cannot collide with the null namespace). Both levels are null-prototype objects, so an
530
+ * attribute named `__proto__` or `constructor` is an ordinary key.
531
+ *
532
+ * @param {NamedNodeMap} map
533
+ * @param {string | null | undefined} namespaceURI
534
+ * @param {boolean} create
535
+ * Create the bucket if it does not exist yet.
536
+ * @returns {Object | undefined}
537
+ * @private
538
+ */
539
+ function _nnmBucket(map, namespaceURI, create) {
540
+ if (!namespaceURI) {
541
+ return map._noNsIndex;
542
+ }
543
+ var bucket = map._nsIndex[namespaceURI];
544
+ if (!bucket && create) {
545
+ bucket = map._nsIndex[namespaceURI] = Object.create(null);
546
+ }
547
+ return bucket;
548
+ }
549
+ /**
550
+ * Looks up an attribute by namespace and local name through the membership index.
551
+ *
552
+ * @param {NamedNodeMap} map
553
+ * @param {string | null | undefined} namespaceURI
554
+ * @param {string} localName
555
+ * @returns {Attr | null}
556
+ * The matching attribute, or `null` when absent.
557
+ * @private
558
+ */
559
+ function _nnmIndexFind(map, namespaceURI, localName) {
560
+ var bucket = _nnmBucket(map, namespaceURI, false);
561
+ var found = bucket && bucket[localName];
562
+ return found ? found : null;
563
+ }
564
+ /**
565
+ * Records `attr` in the membership index under its namespace and local name,
566
+ * replacing any previous attribute with the same key.
567
+ *
568
+ * @param {NamedNodeMap} map
569
+ * @param {Attr} attr
570
+ * @private
571
+ */
572
+ function _nnmIndexAdd(map, attr) {
573
+ _nnmBucket(map, attr.namespaceURI, true)[attr.localName] = attr;
574
+ }
575
+ /**
576
+ * Removes `attr` from the membership index.
577
+ *
578
+ * @param {NamedNodeMap} map
579
+ * @param {Attr} attr
580
+ * @private
581
+ */
582
+ function _nnmIndexRemove(map, attr) {
583
+ var bucket = _nnmBucket(map, attr.namespaceURI, false);
584
+ if (bucket) {
585
+ delete bucket[attr.localName];
586
+ }
587
+ }
588
+ /**
589
+ * Adds a new attribute to the list and updates the owner element of the attribute.
590
+ *
591
+ * @param {Element} el
592
+ * The element which will become the owner of the new attribute.
593
+ * @param {NamedNodeMap} list
594
+ * The list to which the new attribute will be added.
595
+ * @param {Attr} newAttr
596
+ * The new attribute to be added.
597
+ * @param {Attr} oldAttr
598
+ * The old attribute to be replaced, or null if no attribute is to be replaced.
599
+ * @returns {void}
600
+ * @private
601
+ */
602
+ function _addNamedNode(el, list, newAttr, oldAttr) {
603
+ if (oldAttr) {
604
+ list[_findNodeIndex(list, oldAttr)] = newAttr;
605
+ } else {
606
+ list[list.length] = newAttr;
607
+ list.length++;
608
+ }
609
+ // Keep the membership index in sync with the ordered list. On replacement
610
+ // `oldAttr` shares `newAttr`'s (namespace, localName) key, so this overwrites
611
+ // its entry; on append it adds a new one.
612
+ _nnmIndexAdd(list, newAttr);
613
+ if (el) {
614
+ newAttr.ownerElement = el;
615
+ var doc = el.ownerDocument;
616
+ if (doc) {
617
+ oldAttr && _onRemoveAttribute(doc, el, oldAttr);
618
+ _onAddAttribute(doc, el, newAttr);
619
+ }
620
+ }
621
+ }
622
+ /**
623
+ * Removes an attribute from the list and updates the owner element of the attribute.
624
+ *
625
+ * @param {Element} el
626
+ * The element which is the current owner of the attribute.
627
+ * @param {NamedNodeMap} list
628
+ * The list from which the attribute will be removed.
629
+ * @param {Attr} attr
630
+ * The attribute to be removed.
631
+ * @returns {void}
632
+ * @private
633
+ */
634
+ function _removeNamedNode(el, list, attr) {
635
+ //console.log('remove attr:'+attr)
636
+ var i = _findNodeIndex(list, attr);
637
+ if (i >= 0) {
638
+ var lastIndex = list.length - 1;
639
+ while (i <= lastIndex) {
640
+ list[i] = list[++i];
641
+ }
642
+ list.length = lastIndex;
643
+ _nnmIndexRemove(list, attr);
644
+ if (el) {
645
+ var doc = el.ownerDocument;
646
+ if (doc) {
647
+ _onRemoveAttribute(doc, el, attr);
648
+ }
649
+ attr.ownerElement = null;
650
+ }
651
+ }
652
+ }
653
+ NamedNodeMap.prototype = {
654
+ length: 0,
655
+ item: NodeList.prototype.item,
656
+
657
+ /**
658
+ * Get an attribute by name. Note: Name is in lower case in case of HTML namespace and
659
+ * document.
660
+ *
661
+ * @param {string} localName
662
+ * The local name of the attribute.
663
+ * @returns {Attr | null}
664
+ * The attribute with the given local name, or null if no such attribute exists.
665
+ * @see https://dom.spec.whatwg.org/#concept-element-attributes-get-by-name
666
+ */
667
+ getNamedItem: function (localName) {
668
+ if (this._ownerElement && this._ownerElement._isInHTMLDocumentAndNamespace()) {
669
+ localName = localName.toLowerCase();
670
+ }
671
+ var i = 0;
672
+ while (i < this.length) {
673
+ var attr = this[i];
674
+ if (attr.nodeName === localName) {
675
+ return attr;
676
+ }
677
+ i++;
678
+ }
679
+ return null;
680
+ },
681
+
682
+ /**
683
+ * Set an attribute.
684
+ *
685
+ * @param {Attr} attr
686
+ * The attribute to set.
687
+ * @returns {Attr | null}
688
+ * The old attribute with the same local name and namespace URI as the new one, or null if no
689
+ * such attribute exists.
690
+ * @throws {DOMException}
691
+ * With code:
692
+ * - {@link INUSE_ATTRIBUTE_ERR} - If the attribute is already an attribute of another
693
+ * element.
694
+ * @see https://dom.spec.whatwg.org/#concept-element-attributes-set
695
+ */
696
+ setNamedItem: function (attr) {
697
+ var el = attr.ownerElement;
698
+ if (el && el !== this._ownerElement) {
699
+ throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR);
700
+ }
701
+ // Resolve any existing attribute with the same (namespace, localName)
702
+ // through the O(1) membership index rather than an O(M) scan — this is the
703
+ // parse-dedup hot path (`setAttributeNode` per attribute during parse).
704
+ var oldAttr = _nnmIndexFind(this, attr.namespaceURI, attr.localName);
705
+ if (oldAttr === attr) {
706
+ return attr;
707
+ }
708
+ _addNamedNode(this._ownerElement, this, attr, oldAttr);
709
+ return oldAttr;
710
+ },
711
+
712
+ /**
713
+ * Set an attribute, replacing an existing attribute with the same local name and namespace
714
+ * URI if one exists.
715
+ *
716
+ * @param {Attr} attr
717
+ * The attribute to set.
718
+ * @returns {Attr | null}
719
+ * The old attribute with the same local name and namespace URI as the new one, or null if no
720
+ * such attribute exists.
721
+ * @throws {DOMException}
722
+ * Throws a DOMException with the name "InUseAttributeError" if the attribute is already an
723
+ * attribute of another element.
724
+ * @see https://dom.spec.whatwg.org/#concept-element-attributes-set
725
+ */
726
+ setNamedItemNS: function (attr) {
727
+ return this.setNamedItem(attr);
728
+ },
729
+
730
+ /**
731
+ * Removes an attribute specified by the local name.
732
+ *
733
+ * @param {string} localName
734
+ * The local name of the attribute to be removed.
735
+ * @returns {Attr}
736
+ * The attribute node that was removed.
737
+ * @throws {DOMException}
738
+ * With code:
739
+ * - {@link DOMException.NOT_FOUND_ERR} if no attribute with the given name is found.
740
+ * @see https://dom.spec.whatwg.org/#dom-namednodemap-removenameditem
741
+ * @see https://dom.spec.whatwg.org/#concept-element-attributes-remove-by-name
742
+ */
743
+ removeNamedItem: function (localName) {
744
+ var attr = this.getNamedItem(localName);
745
+ if (!attr) {
746
+ throw new DOMException(DOMException.NOT_FOUND_ERR, localName);
747
+ }
748
+ _removeNamedNode(this._ownerElement, this, attr);
749
+ return attr;
750
+ },
751
+
752
+ /**
753
+ * Removes an attribute specified by the namespace and local name.
754
+ *
755
+ * @param {string | null} namespaceURI
756
+ * The namespace URI of the attribute to be removed.
757
+ * @param {string} localName
758
+ * The local name of the attribute to be removed.
759
+ * @returns {Attr}
760
+ * The attribute node that was removed.
761
+ * @throws {DOMException}
762
+ * With code:
763
+ * - {@link DOMException.NOT_FOUND_ERR} if no attribute with the given namespace URI and local
764
+ * name is found.
765
+ * @see https://dom.spec.whatwg.org/#dom-namednodemap-removenameditemns
766
+ * @see https://dom.spec.whatwg.org/#concept-element-attributes-remove-by-namespace
767
+ */
768
+ removeNamedItemNS: function (namespaceURI, localName) {
769
+ var attr = this.getNamedItemNS(namespaceURI, localName);
770
+ if (!attr) {
771
+ throw new DOMException(DOMException.NOT_FOUND_ERR, namespaceURI ? namespaceURI + ' : ' + localName : localName);
772
+ }
773
+ _removeNamedNode(this._ownerElement, this, attr);
774
+ return attr;
775
+ },
776
+
777
+ /**
778
+ * Get an attribute by namespace and local name.
779
+ *
780
+ * @param {string | null} namespaceURI
781
+ * The namespace URI of the attribute.
782
+ * @param {string} localName
783
+ * The local name of the attribute.
784
+ * @returns {Attr | null}
785
+ * The attribute with the given namespace URI and local name, or null if no such attribute
786
+ * exists.
787
+ * @see https://dom.spec.whatwg.org/#concept-element-attributes-get-by-namespace
788
+ */
789
+ getNamedItemNS: function (namespaceURI, localName) {
790
+ if (!namespaceURI) {
791
+ namespaceURI = null;
792
+ }
793
+ var i = 0;
794
+ while (i < this.length) {
795
+ var node = this[i];
796
+ if (node.localName === localName && node.namespaceURI === namespaceURI) {
797
+ return node;
798
+ }
799
+ i++;
800
+ }
801
+ return null;
802
+ },
803
+ };
804
+ NamedNodeMap.prototype[Symbol.iterator] = function () {
805
+ var me = this;
806
+ var index = 0;
807
+
808
+ return {
809
+ next: function () {
810
+ if (index < me.length) {
811
+ return {
812
+ value: me[index++],
813
+ done: false,
814
+ };
815
+ } else {
816
+ return {
817
+ done: true,
818
+ };
819
+ }
820
+ },
821
+ return: function () {
822
+ return {
823
+ done: true,
824
+ };
825
+ },
826
+ };
827
+ };
828
+
829
+ /**
830
+ * The DOMImplementation interface provides a number of methods for performing operations that
831
+ * are independent of any particular instance of the document object model.
832
+ *
833
+ * The DOMImplementation interface represents an object providing methods which are not
834
+ * dependent on any particular document.
835
+ * Such an object is returned by the `Document.implementation` property.
836
+ *
837
+ * **The individual methods describe the differences compared to the specs**.
838
+ *
839
+ * @class DOMImplementation
840
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation MDN
841
+ * @see https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490 DOM Level 1 Core
842
+ * (Initial)
843
+ * @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-102161490 DOM Level 2 Core
844
+ * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-102161490 DOM Level 3 Core
845
+ * @see https://dom.spec.whatwg.org/#domimplementation DOM Living Standard
846
+ * @constructs DOMImplementation
847
+ */
848
+ function DOMImplementation() {}
849
+
850
+ DOMImplementation.prototype = {
851
+ /**
852
+ * Test if the DOM implementation implements a specific feature and version, as specified in
853
+ * {@link https://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMFeatures DOM Features}.
854
+ *
855
+ * The DOMImplementation.hasFeature() method returns a Boolean flag indicating if a given
856
+ * feature is supported. The different implementations fairly diverged in what kind of
857
+ * features were reported. The latest version of the spec settled to force this method to
858
+ * always return true, where the functionality was accurate and in use.
859
+ *
860
+ * @deprecated
861
+ * It is deprecated and modern browsers return true in all cases.
862
+ * @function DOMImplementation#hasFeature
863
+ * @param {string} feature
864
+ * The name of the feature to test.
865
+ * @param {string} [version]
866
+ * This is the version number of the feature to test.
867
+ * @returns {boolean}
868
+ * Always returns true.
869
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/hasFeature MDN
870
+ * @see https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-5CED94D7 DOM Level 1 Core
871
+ * @see https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature DOM Living Standard
872
+ * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-5CED94D7 DOM Level 3 Core
873
+ */
874
+ hasFeature: function (feature, version) {
875
+ return true;
876
+ },
877
+ /**
878
+ * Creates a DOM Document object of the specified type with its document element. Note that
879
+ * based on the {@link DocumentType}
880
+ * given to create the document, the implementation may instantiate specialized
881
+ * {@link Document} objects that support additional features than the "Core", such as "HTML"
882
+ * {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#DOM2HTML DOM Level 2 HTML}.
883
+ * On the other hand, setting the {@link DocumentType} after the document was created makes
884
+ * this very unlikely to happen. Alternatively, specialized {@link Document} creation methods,
885
+ * such as createHTMLDocument
886
+ * {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#DOM2HTML DOM Level 2 HTML},
887
+ * can be used to obtain specific types of {@link Document} objects.
888
+ *
889
+ * __It behaves slightly different from the description in the living standard__:
890
+ * - There is no interface/class `XMLDocument`, it returns a `Document`
891
+ * instance (with it's `type` set to `'xml'`).
892
+ * - `encoding`, `mode`, `origin`, `url` fields are currently not declared.
893
+ *
894
+ * @function DOMImplementation.createDocument
895
+ * @param {string | null} namespaceURI
896
+ * The
897
+ * {@link https://www.w3.org/TR/DOM-Level-3-Core/glossary.html#dt-namespaceURI namespace URI}
898
+ * of the document element to create or null.
899
+ * @param {string | null} qualifiedName
900
+ * The
901
+ * {@link https://www.w3.org/TR/DOM-Level-3-Core/glossary.html#dt-qualifiedname qualified name}
902
+ * of the document element to be created or null.
903
+ * @param {DocumentType | null} [doctype=null]
904
+ * The type of document to be created or null. When doctype is not null, its
905
+ * {@link Node#ownerDocument} attribute is set to the document being created. Default is
906
+ * `null`
907
+ * @returns {Document}
908
+ * A new {@link Document} object with its document element. If the NamespaceURI,
909
+ * qualifiedName, and doctype are null, the returned {@link Document} is empty with no
910
+ * document element.
911
+ * @throws {DOMException}
912
+ * With code:
913
+ *
914
+ * - `INVALID_CHARACTER_ERR`: Raised if the specified qualified name is not an XML name
915
+ * according to {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#XML XML 1.0}.
916
+ * - `NAMESPACE_ERR`: Raised if the qualifiedName is malformed, if the qualifiedName has a
917
+ * prefix and the namespaceURI is null, or if the qualifiedName is null and the namespaceURI
918
+ * is different from null, or if the qualifiedName has a prefix that is "xml" and the
919
+ * namespaceURI is different from "{@link http://www.w3.org/XML/1998/namespace}"
920
+ * {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#Namespaces XML Namespaces},
921
+ * or if the DOM implementation does not support the "XML" feature but a non-null namespace
922
+ * URI was provided, since namespaces were defined by XML.
923
+ * - `WRONG_DOCUMENT_ERR`: Raised if doctype has already been used with a different document
924
+ * or was created from a different implementation.
925
+ * - `NOT_SUPPORTED_ERR`: May be raised if the implementation does not support the feature
926
+ * "XML" and the language exposed through the Document does not support XML Namespaces (such
927
+ * as {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#HTML40 HTML 4.01}).
928
+ * @since DOM Level 2.
929
+ * @see {@link #createHTMLDocument}
930
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createDocument MDN
931
+ * @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocument DOM Living Standard
932
+ * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Level-2-Core-DOM-createDocument DOM
933
+ * Level 3 Core
934
+ * @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#Level-2-Core-DOM-createDocument DOM
935
+ * Level 2 Core (initial)
936
+ */
937
+ createDocument: function (namespaceURI, qualifiedName, doctype) {
938
+ var contentType = MIME_TYPE.XML_APPLICATION;
939
+ if (namespaceURI === NAMESPACE.HTML) {
940
+ contentType = MIME_TYPE.XML_XHTML_APPLICATION;
941
+ } else if (namespaceURI === NAMESPACE.SVG) {
942
+ contentType = MIME_TYPE.XML_SVG_IMAGE;
943
+ }
944
+ var doc = new Document(PDC, { contentType: contentType });
945
+ doc.implementation = this;
946
+ doc.childNodes = new NodeList();
947
+ doc.doctype = doctype || null;
948
+ if (doctype) {
949
+ doc.appendChild(doctype);
950
+ }
951
+ if (qualifiedName) {
952
+ var root = doc.createElementNS(namespaceURI, qualifiedName);
953
+ doc.appendChild(root);
954
+ }
955
+ return doc;
956
+ },
957
+ /**
958
+ * Creates an empty DocumentType node. Entity declarations and notations are not made
959
+ * available. Entity reference expansions and default attribute additions do not occur.
960
+ *
961
+ * **This behavior is slightly different from the one in the specs**:
962
+ * - `encoding`, `mode`, `origin`, `url` fields are currently not declared.
963
+ * - `publicId` and `systemId` contain the raw data including any possible quotes,
964
+ * so they can always be serialized back to the original value
965
+ * - `internalSubset` contains the raw string between `[` and `]` if present,
966
+ * but is not parsed or validated in any form.
967
+ *
968
+ * @function DOMImplementation#createDocumentType
969
+ * @param {string} qualifiedName
970
+ * The {@link https://www.w3.org/TR/DOM-Level-3-Core/glossary.html#dt-qualifiedname qualified
971
+ * name} of the document type to be created.
972
+ * @param {string} [publicId]
973
+ * The external subset public identifier. Stored verbatim including surrounding quotes.
974
+ * When serialized with `requireWellFormed: true`, the serializer throws `InvalidStateError`
975
+ * if the value is non-empty and does not match the XML `PubidLiteral` production
976
+ * (W3C DOM Parsing §3.2.1.3; XML 1.0 production [12]). Creation-time validation is not
977
+ * enforced — deferred to a future breaking release.
978
+ * @param {string} [systemId]
979
+ * The external subset system identifier. Stored verbatim including surrounding quotes.
980
+ * When serialized with `requireWellFormed: true`, the serializer throws `InvalidStateError`
981
+ * if the value is non-empty and does not match the XML `SystemLiteral` production
982
+ * (W3C DOM Parsing §3.2.1.3; XML 1.0 production [11]). Creation-time validation is not
983
+ * enforced — deferred to a future breaking release.
984
+ * @param {string} [internalSubset]
985
+ * The internal subset or an empty string if it is not present. Stored verbatim.
986
+ * When serialized with `requireWellFormed: true`, the serializer throws `InvalidStateError`
987
+ * if the value contains `"]>"`. Creation-time validation is not enforced.
988
+ * @returns {DocumentType}
989
+ * A new {@link DocumentType} node with {@link Node#ownerDocument} set to null.
990
+ * @throws {DOMException}
991
+ * With code:
992
+ *
993
+ * - `INVALID_CHARACTER_ERR`: Raised if the specified qualified name is not an XML name
994
+ * according to {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#XML XML 1.0}.
995
+ * - `NAMESPACE_ERR`: Raised if the qualifiedName is malformed.
996
+ * - `NOT_SUPPORTED_ERR`: May be raised if the implementation does not support the feature
997
+ * "XML" and the language exposed through the Document does not support XML Namespaces (such
998
+ * as {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#HTML40 HTML 4.01}).
999
+ * @since DOM Level 2.
1000
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createDocumentType
1001
+ * MDN
1002
+ * @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype DOM Living
1003
+ * Standard
1004
+ * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Level-3-Core-DOM-createDocType DOM
1005
+ * Level 3 Core
1006
+ * @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#Level-2-Core-DOM-createDocType DOM
1007
+ * Level 2 Core
1008
+ * @see https://github.com/xmldom/xmldom/blob/master/CHANGELOG.md#050
1009
+ * @see https://www.w3.org/TR/DOM-Level-2-Core/#core-ID-Core-DocType-internalSubset
1010
+ * @prettierignore
1011
+ */
1012
+ createDocumentType: function (qualifiedName, publicId, systemId, internalSubset) {
1013
+ validateQualifiedName(qualifiedName);
1014
+ var node = new DocumentType(PDC);
1015
+ node.name = qualifiedName;
1016
+ node.nodeName = qualifiedName;
1017
+ node.publicId = publicId || '';
1018
+ node.systemId = systemId || '';
1019
+ node.internalSubset = internalSubset || '';
1020
+ node.childNodes = new NodeList();
1021
+
1022
+ return node;
1023
+ },
1024
+ /**
1025
+ * Returns an HTML document, that might already have a basic DOM structure.
1026
+ *
1027
+ * __It behaves slightly different from the description in the living standard__:
1028
+ * - If the first argument is `false` no initial nodes are added (steps 3-7 in the specs are
1029
+ * omitted)
1030
+ * - `encoding`, `mode`, `origin`, `url` fields are currently not declared.
1031
+ *
1032
+ * @param {string | false} [title]
1033
+ * A string containing the title to give the new HTML document.
1034
+ * @returns {Document}
1035
+ * The HTML document.
1036
+ * @since WHATWG Living Standard.
1037
+ * @see {@link #createDocument}
1038
+ * @see https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument
1039
+ * @see https://dom.spec.whatwg.org/#html-document
1040
+ */
1041
+ createHTMLDocument: function (title) {
1042
+ var doc = new Document(PDC, { contentType: MIME_TYPE.HTML });
1043
+ doc.implementation = this;
1044
+ doc.childNodes = new NodeList();
1045
+ if (title !== false) {
1046
+ doc.doctype = this.createDocumentType('html');
1047
+ doc.doctype.ownerDocument = doc;
1048
+ doc.appendChild(doc.doctype);
1049
+ var htmlNode = doc.createElement('html');
1050
+ doc.appendChild(htmlNode);
1051
+ var headNode = doc.createElement('head');
1052
+ htmlNode.appendChild(headNode);
1053
+ if (typeof title === 'string') {
1054
+ var titleNode = doc.createElement('title');
1055
+ titleNode.appendChild(doc.createTextNode(title));
1056
+ headNode.appendChild(titleNode);
1057
+ }
1058
+ htmlNode.appendChild(doc.createElement('body'));
1059
+ }
1060
+ return doc;
1061
+ },
1062
+ };
1063
+
1064
+ /**
1065
+ * The DOM Node interface is an abstract base class upon which many other DOM API objects are
1066
+ * based, thus letting those object types to be used similarly and often interchangeably. As an
1067
+ * abstract class, there is no such thing as a plain Node object. All objects that implement
1068
+ * Node functionality are based on one of its subclasses. Most notable are Document, Element,
1069
+ * and DocumentFragment.
1070
+ *
1071
+ * In addition, every kind of DOM node is represented by an interface based on Node. These
1072
+ * include Attr, CharacterData (which Text, Comment, CDATASection and ProcessingInstruction are
1073
+ * all based on), and DocumentType.
1074
+ *
1075
+ * In some cases, a particular feature of the base Node interface may not apply to one of its
1076
+ * child interfaces; in that case, the inheriting node may return null or throw an exception,
1077
+ * depending on circumstances. For example, attempting to add children to a node type that
1078
+ * cannot have children will throw an exception.
1079
+ *
1080
+ * **This behavior is slightly different from the in the specs**:
1081
+ * - unimplemented interfaces: `EventTarget`
1082
+ *
1083
+ * @class
1084
+ * @abstract
1085
+ * @param {Symbol} symbol
1086
+ * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
1087
+ * @see https://dom.spec.whatwg.org/#node
1088
+ * @prettierignore
1089
+ */
1090
+ function Node(symbol) {
1091
+ checkSymbol(symbol);
1092
+ }
1093
+
1094
+ Node.prototype = {
1095
+ /**
1096
+ * The first child of this node.
1097
+ *
1098
+ * @type {Node | null}
1099
+ */
1100
+ firstChild: null,
1101
+ /**
1102
+ * The last child of this node.
1103
+ *
1104
+ * @type {Node | null}
1105
+ */
1106
+ lastChild: null,
1107
+ /**
1108
+ * The previous sibling of this node.
1109
+ *
1110
+ * @type {Node | null}
1111
+ */
1112
+ previousSibling: null,
1113
+ /**
1114
+ * The next sibling of this node.
1115
+ *
1116
+ * @type {Node | null}
1117
+ */
1118
+ nextSibling: null,
1119
+ /**
1120
+ * The parent node of this node.
1121
+ *
1122
+ * @type {Node | null}
1123
+ */
1124
+ parentNode: null,
1125
+ /**
1126
+ * The parent element of this node.
1127
+ *
1128
+ * @type {Element | null}
1129
+ */
1130
+ get parentElement() {
1131
+ return this.parentNode && this.parentNode.nodeType === this.ELEMENT_NODE ? this.parentNode : null;
1132
+ },
1133
+ /**
1134
+ * The child nodes of this node.
1135
+ *
1136
+ * @type {NodeList}
1137
+ */
1138
+ childNodes: null,
1139
+ /**
1140
+ * The document object associated with this node.
1141
+ *
1142
+ * @type {Document | null}
1143
+ */
1144
+ ownerDocument: null,
1145
+ /**
1146
+ * The value of this node.
1147
+ *
1148
+ * @type {string | null}
1149
+ */
1150
+ nodeValue: null,
1151
+ /**
1152
+ * The namespace URI of this node.
1153
+ *
1154
+ * @type {string | null}
1155
+ */
1156
+ namespaceURI: null,
1157
+ /**
1158
+ * The prefix of the namespace for this node.
1159
+ *
1160
+ * @type {string | null}
1161
+ */
1162
+ prefix: null,
1163
+ /**
1164
+ * The local part of the qualified name of this node.
1165
+ *
1166
+ * @type {string | null}
1167
+ */
1168
+ localName: null,
1169
+ /**
1170
+ * The baseURI is currently always `about:blank`,
1171
+ * since that's what happens when you create a document from scratch.
1172
+ *
1173
+ * @type {'about:blank'}
1174
+ */
1175
+ baseURI: 'about:blank',
1176
+ /**
1177
+ * Is true if this node is part of a document.
1178
+ *
1179
+ * @type {boolean}
1180
+ */
1181
+ get isConnected() {
1182
+ var rootNode = this.getRootNode();
1183
+ return rootNode && rootNode.nodeType === rootNode.DOCUMENT_NODE;
1184
+ },
1185
+ /**
1186
+ * Checks whether `other` is an inclusive descendant of this node.
1187
+ *
1188
+ * @param {Node | null | undefined} other
1189
+ * The node to check.
1190
+ * @returns {boolean}
1191
+ * True if `other` is an inclusive descendant of this node; false otherwise.
1192
+ * @see https://dom.spec.whatwg.org/#dom-node-contains
1193
+ */
1194
+ contains: function (other) {
1195
+ if (!other) return false;
1196
+ var parent = other;
1197
+ do {
1198
+ if (this === parent) return true;
1199
+ parent = parent.parentNode;
1200
+ } while (parent);
1201
+ return false;
1202
+ },
1203
+ /**
1204
+ * @typedef GetRootNodeOptions
1205
+ * @property {boolean} [composed=false]
1206
+ */
1207
+ /**
1208
+ * Searches for the root node of this node.
1209
+ *
1210
+ * **This behavior is slightly different from the in the specs**:
1211
+ * - ignores `options.composed`, since `ShadowRoot`s are unsupported, always returns root.
1212
+ *
1213
+ * @param {GetRootNodeOptions} [options]
1214
+ * @returns {Node}
1215
+ * Root node.
1216
+ * @see https://dom.spec.whatwg.org/#dom-node-getrootnode
1217
+ * @see https://dom.spec.whatwg.org/#concept-shadow-including-root
1218
+ */
1219
+ getRootNode: function (options) {
1220
+ var parent = this;
1221
+ do {
1222
+ if (!parent.parentNode) {
1223
+ return parent;
1224
+ }
1225
+ parent = parent.parentNode;
1226
+ } while (parent);
1227
+ },
1228
+ /**
1229
+ * Checks whether the given node is equal to this node.
1230
+ *
1231
+ * Two nodes are equal when they have the same type, defining characteristics (for the type),
1232
+ * and the same childNodes. The comparison is iterative to avoid stack overflows on
1233
+ * deeply-nested trees. Attribute nodes of each Element pair are also pushed onto the stack
1234
+ * and compared the same way.
1235
+ *
1236
+ * @param {Node} [otherNode]
1237
+ * @returns {boolean}
1238
+ * @see https://dom.spec.whatwg.org/#concept-node-equals
1239
+ * @see ../docs/walk-dom.md.
1240
+ */
1241
+ isEqualNode: function (otherNode) {
1242
+ if (!otherNode) return false;
1243
+
1244
+ // Use an explicit {node, other} pair stack to avoid call-stack overflow on deep trees.
1245
+ // walkDOM cannot be used here — parallel two-tree traversal requires pairing
1246
+ // corresponding nodes at each step across both trees simultaneously.
1247
+ var stack = [{ node: this, other: otherNode }];
1248
+ while (stack.length > 0) {
1249
+ var pair = stack.pop();
1250
+ var node = pair.node;
1251
+ var other = pair.other;
1252
+
1253
+ if (node.nodeType !== other.nodeType) return false;
1254
+
1255
+ switch (node.nodeType) {
1256
+ case node.DOCUMENT_TYPE_NODE:
1257
+ if (node.name !== other.name) return false;
1258
+ if (node.publicId !== other.publicId) return false;
1259
+ if (node.systemId !== other.systemId) return false;
1260
+ break;
1261
+ case node.ELEMENT_NODE:
1262
+ if (node.namespaceURI !== other.namespaceURI) return false;
1263
+ if (node.prefix !== other.prefix) return false;
1264
+ if (node.localName !== other.localName) return false;
1265
+ if (node.attributes.length !== other.attributes.length) return false;
1266
+ for (var i = 0; i < node.attributes.length; i++) {
1267
+ var attr = node.attributes.item(i);
1268
+ var otherAttr = other.getAttributeNodeNS(attr.namespaceURI, attr.localName);
1269
+ if (!otherAttr) return false;
1270
+ stack.push({ node: attr, other: otherAttr });
1271
+ }
1272
+ break;
1273
+ case node.ATTRIBUTE_NODE:
1274
+ if (node.namespaceURI !== other.namespaceURI) return false;
1275
+ if (node.localName !== other.localName) return false;
1276
+ if (node.value !== other.value) return false;
1277
+ break;
1278
+ case node.PROCESSING_INSTRUCTION_NODE:
1279
+ if (node.target !== other.target || node.data !== other.data) return false;
1280
+ break;
1281
+ case node.TEXT_NODE:
1282
+ case node.CDATA_SECTION_NODE:
1283
+ case node.COMMENT_NODE:
1284
+ if (node.data !== other.data) return false;
1285
+ break;
1286
+ }
1287
+
1288
+ if (node.childNodes.length !== other.childNodes.length) return false;
1289
+
1290
+ // Push children in reverse order so index 0 is processed first (LIFO).
1291
+ for (var i = node.childNodes.length - 1; i >= 0; i--) {
1292
+ stack.push({ node: node.childNodes[i], other: other.childNodes[i] });
1293
+ }
1294
+ }
1295
+
1296
+ return true;
1297
+ },
1298
+ /**
1299
+ * Checks whether or not the given node is this node.
1300
+ *
1301
+ * @param {Node} [otherNode]
1302
+ */
1303
+ isSameNode: function (otherNode) {
1304
+ return this === otherNode;
1305
+ },
1306
+ /**
1307
+ * Inserts a node before a reference node as a child of this node.
1308
+ *
1309
+ * @param {Node} newChild
1310
+ * The new child node to be inserted.
1311
+ * @param {Node | null} refChild
1312
+ * The reference node before which newChild will be inserted.
1313
+ * @returns {Node}
1314
+ * The new child node successfully inserted.
1315
+ * @throws {DOMException}
1316
+ * Throws a DOMException if inserting the node would result in a DOM tree that is not
1317
+ * well-formed, or if `child` is provided but is not a child of `parent`.
1318
+ * See {@link _insertBefore} for more details.
1319
+ * @since Modified in DOM L2
1320
+ */
1321
+ insertBefore: function (newChild, refChild) {
1322
+ return _insertBefore(this, newChild, refChild);
1323
+ },
1324
+ /**
1325
+ * Replaces an old child node with a new child node within this node.
1326
+ *
1327
+ * @param {Node} newChild
1328
+ * The new node that is to replace the old node.
1329
+ * If it already exists in the DOM, it is removed from its original position.
1330
+ * @param {Node} oldChild
1331
+ * The existing child node to be replaced.
1332
+ * @returns {Node}
1333
+ * Returns the replaced child node.
1334
+ * @throws {DOMException}
1335
+ * Throws a DOMException if replacing the node would result in a DOM tree that is not
1336
+ * well-formed, or if `oldChild` is not a child of `this`.
1337
+ * This can also occur if the pre-replacement validity assertion fails.
1338
+ * See {@link _insertBefore}, {@link Node.removeChild}, and
1339
+ * {@link assertPreReplacementValidityInDocument} for more details.
1340
+ * @see https://dom.spec.whatwg.org/#concept-node-replace
1341
+ */
1342
+ replaceChild: function (newChild, oldChild) {
1343
+ _insertBefore(this, newChild, oldChild, assertPreReplacementValidityInDocument);
1344
+ if (oldChild) {
1345
+ this.removeChild(oldChild);
1346
+ }
1347
+ },
1348
+ /**
1349
+ * Removes an existing child node from this node.
1350
+ *
1351
+ * @param {Node} oldChild
1352
+ * The child node to be removed.
1353
+ * @returns {Node}
1354
+ * Returns the removed child node.
1355
+ * @throws {DOMException}
1356
+ * Throws a DOMException if `oldChild` is not a child of `this`.
1357
+ * See {@link _removeChild} for more details.
1358
+ */
1359
+ removeChild: function (oldChild) {
1360
+ return _removeChild(this, oldChild);
1361
+ },
1362
+ /**
1363
+ * Appends a child node to this node.
1364
+ *
1365
+ * @param {Node} newChild
1366
+ * The child node to be appended to this node.
1367
+ * If it already exists in the DOM, it is removed from its original position.
1368
+ * @returns {Node}
1369
+ * Returns the appended child node.
1370
+ * @throws {DOMException}
1371
+ * Throws a DOMException if appending the node would result in a DOM tree that is not
1372
+ * well-formed, or if `newChild` is not a valid Node.
1373
+ * See {@link insertBefore} for more details.
1374
+ */
1375
+ appendChild: function (newChild) {
1376
+ return this.insertBefore(newChild, null);
1377
+ },
1378
+ /**
1379
+ * Determines whether this node has any child nodes.
1380
+ *
1381
+ * @returns {boolean}
1382
+ * Returns true if this node has any child nodes, and false otherwise.
1383
+ */
1384
+ hasChildNodes: function () {
1385
+ return this.firstChild != null;
1386
+ },
1387
+ /**
1388
+ * Creates a copy of the calling node.
1389
+ *
1390
+ * @param {boolean} deep
1391
+ * If true, the contents of the node are recursively copied.
1392
+ * If false, only the node itself (and its attributes, if it is an element) are copied.
1393
+ * @returns {Node}
1394
+ * Returns the newly created copy of the node.
1395
+ * @throws {DOMException}
1396
+ * May throw a DOMException if operations within {@link Element#setAttributeNode} or
1397
+ * {@link Node#appendChild} (which are potentially invoked in this method) do not meet their
1398
+ * specific constraints.
1399
+ * @see {@link cloneNode}
1400
+ */
1401
+ cloneNode: function (deep) {
1402
+ return cloneNode(this.ownerDocument || this, this, deep);
1403
+ },
1404
+ /**
1405
+ * Puts the specified node and all of its subtree into a "normalized" form. In a normalized
1406
+ * subtree, no text nodes in the subtree are empty and there are no adjacent text nodes.
1407
+ *
1408
+ * Specifically, this method merges any adjacent text nodes (i.e., nodes for which `nodeType`
1409
+ * is `TEXT_NODE`) into a single node with the combined data. It also removes any empty text
1410
+ * nodes.
1411
+ *
1412
+ * This method iterativly traverses all child nodes to normalize all descendent nodes within
1413
+ * the subtree.
1414
+ *
1415
+ * @throws {DOMException}
1416
+ * May throw a DOMException if operations within removeChild or appendData (which are
1417
+ * potentially invoked in this method) do not meet their specific constraints.
1418
+ * @since Modified in DOM Level 2
1419
+ * @see {@link Node.removeChild}
1420
+ * @see {@link CharacterData.appendData}
1421
+ * @see ../docs/walk-dom.md.
1422
+ */
1423
+ normalize: function () {
1424
+ walkDOM(this, null, {
1425
+ enter: function (node) {
1426
+ // Merge adjacent text children of node before walkDOM schedules them.
1427
+ // walkDOM reads lastChild/previousSibling after enter returns, so the
1428
+ // surviving post-merge children are what it descends into.
1429
+ var child = node.firstChild;
1430
+ while (child) {
1431
+ var next = child.nextSibling;
1432
+ if (next !== null && next.nodeType === TEXT_NODE && child.nodeType === TEXT_NODE) {
1433
+ // Merge the whole run of adjacent text nodes at once: gather the
1434
+ // following text siblings' data, unlink them in a single pass, and
1435
+ // re-index the child list a single time. Per-sibling `removeChild`
1436
+ // (each an O(K) re-index) plus per-sibling `appendData` (each an O(K)
1437
+ // string rebuild) is O(K^2) over a long run of single-character text
1438
+ // nodes; this keeps it O(K). The first text node of the run survives
1439
+ // and carries the concatenated data, preserving node identity and
1440
+ // locator semantics.
1441
+ var tail = [];
1442
+ var sibling = next;
1443
+ while (sibling !== null && sibling.nodeType === TEXT_NODE) {
1444
+ tail.push(sibling.data);
1445
+ sibling = sibling.nextSibling;
1446
+ }
1447
+ // `sibling` is now the first non-text node after the run, or null.
1448
+ var removed = child.nextSibling;
1449
+ while (removed !== sibling) {
1450
+ var following = removed.nextSibling;
1451
+ removed.parentNode = null;
1452
+ removed.previousSibling = null;
1453
+ removed.nextSibling = null;
1454
+ removed = following;
1455
+ }
1456
+ child.nextSibling = sibling;
1457
+ if (sibling !== null) {
1458
+ sibling.previousSibling = child;
1459
+ } else {
1460
+ node.lastChild = child;
1461
+ }
1462
+ child.appendData(tail.join('')); // single O(K) string rebuild
1463
+ _onUpdateChild(node.ownerDocument, node); // single O(K) re-index
1464
+ child = sibling;
1465
+ } else {
1466
+ child = next;
1467
+ }
1468
+ }
1469
+ return true; // descend into surviving children
1470
+ },
1471
+ });
1472
+ },
1473
+ /**
1474
+ * Checks whether the DOM implementation implements a specific feature and its version.
1475
+ *
1476
+ * @deprecated
1477
+ * Since `DOMImplementation.hasFeature` is deprecated and always returns true.
1478
+ * @param {string} feature
1479
+ * The package name of the feature to test. This is the same name that can be passed to the
1480
+ * method `hasFeature` on `DOMImplementation`.
1481
+ * @param {string} version
1482
+ * This is the version number of the package name to test.
1483
+ * @returns {boolean}
1484
+ * Returns true in all cases in the current implementation.
1485
+ * @since Introduced in DOM Level 2
1486
+ * @see {@link DOMImplementation.hasFeature}
1487
+ */
1488
+ isSupported: function (feature, version) {
1489
+ return this.ownerDocument.implementation.hasFeature(feature, version);
1490
+ },
1491
+ /**
1492
+ * Look up the prefix associated to the given namespace URI, starting from this node.
1493
+ * **The default namespace declarations are ignored by this method.**
1494
+ * See Namespace Prefix Lookup for details on the algorithm used by this method.
1495
+ *
1496
+ * **This behavior is different from the in the specs**:
1497
+ * - no node type specific handling
1498
+ * - uses the internal attribute _nsMap for resolving namespaces that is updated when changing attributes
1499
+ *
1500
+ * @param {string | null} namespaceURI
1501
+ * The namespace URI for which to find the associated prefix.
1502
+ * @returns {string | null}
1503
+ * The associated prefix, if found; otherwise, null.
1504
+ * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespacePrefix
1505
+ * @see https://www.w3.org/TR/DOM-Level-3-Core/namespaces-algorithms.html#lookupNamespacePrefixAlgo
1506
+ * @see https://dom.spec.whatwg.org/#dom-node-lookupprefix
1507
+ * @see https://github.com/xmldom/xmldom/issues/322
1508
+ * @prettierignore
1509
+ */
1510
+ lookupPrefix: function (namespaceURI) {
1511
+ var el = this;
1512
+ while (el) {
1513
+ var map = el._nsMap;
1514
+ //console.dir(map)
1515
+ if (map) {
1516
+ for (var n in map) {
1517
+ if (hasOwn(map, n) && map[n] === namespaceURI) {
1518
+ return n;
1519
+ }
1520
+ }
1521
+ }
1522
+ el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
1523
+ }
1524
+ return null;
1525
+ },
1526
+ /**
1527
+ * This function is used to look up the namespace URI associated with the given prefix,
1528
+ * starting from this node.
1529
+ *
1530
+ * **This behavior is different from the in the specs**:
1531
+ * - no node type specific handling
1532
+ * - uses the internal attribute _nsMap for resolving namespaces that is updated when changing attributes
1533
+ *
1534
+ * @param {string | null} prefix
1535
+ * The prefix for which to find the associated namespace URI.
1536
+ * @returns {string | null}
1537
+ * The associated namespace URI, if found; otherwise, null.
1538
+ * @since DOM Level 3
1539
+ * @see https://dom.spec.whatwg.org/#dom-node-lookupnamespaceuri
1540
+ * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI
1541
+ * @prettierignore
1542
+ */
1543
+ lookupNamespaceURI: function (prefix) {
1544
+ var el = this;
1545
+ while (el) {
1546
+ var map = el._nsMap;
1547
+ //console.dir(map)
1548
+ if (map) {
1549
+ if (hasOwn(map, prefix)) {
1550
+ return map[prefix];
1551
+ }
1552
+ }
1553
+ el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
1554
+ }
1555
+ return null;
1556
+ },
1557
+ /**
1558
+ * Determines whether the given namespace URI is the default namespace.
1559
+ *
1560
+ * The function works by looking up the prefix associated with the given namespace URI. If no
1561
+ * prefix is found (i.e., the namespace URI is not registered in the namespace map of this
1562
+ * node or any of its ancestors), it returns `true`, implying the namespace URI is considered
1563
+ * the default.
1564
+ *
1565
+ * **This behavior is different from the in the specs**:
1566
+ * - no node type specific handling
1567
+ * - uses the internal attribute _nsMap for resolving namespaces that is updated when changing attributes
1568
+ *
1569
+ * @param {string | null} namespaceURI
1570
+ * The namespace URI to be checked.
1571
+ * @returns {boolean}
1572
+ * Returns true if the given namespace URI is the default namespace, false otherwise.
1573
+ * @since DOM Level 3
1574
+ * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-isDefaultNamespace
1575
+ * @see https://dom.spec.whatwg.org/#dom-node-isdefaultnamespace
1576
+ * @prettierignore
1577
+ */
1578
+ isDefaultNamespace: function (namespaceURI) {
1579
+ var prefix = this.lookupPrefix(namespaceURI);
1580
+ return prefix == null;
1581
+ },
1582
+ /**
1583
+ * Compares the reference node with a node with regard to their position in the document and
1584
+ * according to the document order.
1585
+ *
1586
+ * @param {Node} other
1587
+ * The node to compare the reference node to.
1588
+ * @returns {number}
1589
+ * Returns how the node is positioned relatively to the reference node according to the
1590
+ * bitmask. 0 if reference node and given node are the same.
1591
+ * @since DOM Level 3
1592
+ * @see https://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#Node3-compare
1593
+ * @see https://dom.spec.whatwg.org/#dom-node-comparedocumentposition
1594
+ */
1595
+ compareDocumentPosition: function (other) {
1596
+ if (this === other) return 0;
1597
+ var node1 = other;
1598
+ var node2 = this;
1599
+ var attr1 = null;
1600
+ var attr2 = null;
1601
+ if (node1 instanceof Attr) {
1602
+ attr1 = node1;
1603
+ node1 = attr1.ownerElement;
1604
+ }
1605
+ if (node2 instanceof Attr) {
1606
+ attr2 = node2;
1607
+ node2 = attr2.ownerElement;
1608
+ if (attr1 && node1 && node2 === node1) {
1609
+ for (var i = 0, attr; (attr = node2.attributes[i]); i++) {
1610
+ if (attr === attr1)
1611
+ return DocumentPosition.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC + DocumentPosition.DOCUMENT_POSITION_PRECEDING;
1612
+ if (attr === attr2)
1613
+ return DocumentPosition.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC + DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
1614
+ }
1615
+ }
1616
+ }
1617
+ if (!node1 || !node2 || node2.ownerDocument !== node1.ownerDocument) {
1618
+ return (
1619
+ DocumentPosition.DOCUMENT_POSITION_DISCONNECTED +
1620
+ DocumentPosition.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC +
1621
+ (docGUID(node2.ownerDocument) > docGUID(node1.ownerDocument)
1622
+ ? DocumentPosition.DOCUMENT_POSITION_FOLLOWING
1623
+ : DocumentPosition.DOCUMENT_POSITION_PRECEDING)
1624
+ );
1625
+ }
1626
+ if (attr2 && node1 === node2) {
1627
+ return DocumentPosition.DOCUMENT_POSITION_CONTAINS + DocumentPosition.DOCUMENT_POSITION_PRECEDING;
1628
+ }
1629
+ if (attr1 && node1 === node2) {
1630
+ return DocumentPosition.DOCUMENT_POSITION_CONTAINED_BY + DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
1631
+ }
1632
+
1633
+ var chain1 = [];
1634
+ var ancestor1 = node1.parentNode;
1635
+ while (ancestor1) {
1636
+ if (!attr2 && ancestor1 === node2) {
1637
+ return DocumentPosition.DOCUMENT_POSITION_CONTAINED_BY + DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
1638
+ }
1639
+ chain1.push(ancestor1);
1640
+ ancestor1 = ancestor1.parentNode;
1641
+ }
1642
+ chain1.reverse();
1643
+
1644
+ var chain2 = [];
1645
+ var ancestor2 = node2.parentNode;
1646
+ while (ancestor2) {
1647
+ if (!attr1 && ancestor2 === node1) {
1648
+ return DocumentPosition.DOCUMENT_POSITION_CONTAINS + DocumentPosition.DOCUMENT_POSITION_PRECEDING;
1649
+ }
1650
+ chain2.push(ancestor2);
1651
+ ancestor2 = ancestor2.parentNode;
1652
+ }
1653
+ chain2.reverse();
1654
+
1655
+ var ca = commonAncestor(chain1, chain2);
1656
+ for (var n in ca.childNodes) {
1657
+ var child = ca.childNodes[n];
1658
+ if (child === node2) return DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
1659
+ if (child === node1) return DocumentPosition.DOCUMENT_POSITION_PRECEDING;
1660
+ if (chain2.indexOf(child) >= 0) return DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
1661
+ if (chain1.indexOf(child) >= 0) return DocumentPosition.DOCUMENT_POSITION_PRECEDING;
1662
+ }
1663
+ return 0;
1664
+ },
1665
+ };
1666
+
1667
+ /**
1668
+ * Encodes special XML characters to their corresponding entities.
1669
+ *
1670
+ * @param {string} c
1671
+ * The character to be encoded.
1672
+ * @returns {string}
1673
+ * The encoded character.
1674
+ * @private
1675
+ */
1676
+ function _xmlEncoder(c) {
1677
+ return (
1678
+ (c == '<' && '&lt;') || (c == '>' && '&gt;') || (c == '&' && '&amp;') || (c == '"' && '&quot;') || '&#' + c.charCodeAt() + ';'
1679
+ );
1680
+ }
1681
+
1682
+ copy(NodeType, Node);
1683
+ copy(NodeType, Node.prototype);
1684
+ copy(DocumentPosition, Node);
1685
+ copy(DocumentPosition, Node.prototype);
1686
+
1687
+ /**
1688
+ * Visits every node in the subtree rooted at `node` in depth-first pre-order.
1689
+ *
1690
+ * Delegates to {@link walkDOM} for traversal. The `callback` is called on each node;
1691
+ * if it returns a truthy value, traversal stops immediately.
1692
+ *
1693
+ * @param {Node} node
1694
+ * Root of the subtree to visit.
1695
+ * @param {function(Node): *} callback
1696
+ * Called for each node. A truthy return value stops traversal early.
1697
+ */
1698
+ function _visitNode(node, callback) {
1699
+ walkDOM(node, null, {
1700
+ enter: function (n) {
1701
+ return callback(n) ? walkDOM.STOP : true;
1702
+ },
1703
+ });
1704
+ }
1705
+
1706
+ /**
1707
+ * Depth-first pre/post-order DOM tree walker.
1708
+ *
1709
+ * Visits every node in the subtree rooted at `node`. For each node:
1710
+ *
1711
+ * 1. Calls `callbacks.enter(node, context)` before descending into the node's children. The
1712
+ * return value becomes the `context` passed to each child's `enter` call and to the matching
1713
+ * `exit` call.
1714
+ * 2. If `enter` returns `null` or `undefined`, the node's children are skipped;
1715
+ * sibling traversal continues normally.
1716
+ * 3. If `enter` returns `walkDOM.STOP`, the entire traversal is aborted immediately — no
1717
+ * further `enter` or `exit` calls are made.
1718
+ * 4. `lastChild` and `previousSibling` are read **after** `enter` returns, so `enter` may
1719
+ * safely modify the node's own child list before the walker descends. Modifying siblings of
1720
+ * the current node or any other part of the tree produces unpredictable results: nodes already
1721
+ * queued on the stack are visited regardless of DOM changes, and newly inserted nodes outside
1722
+ * the current child list are never visited.
1723
+ * 5. Calls `callbacks.exit(node, context)` (if provided) after all of a node's children have
1724
+ * been visited, passing the same `context` that `enter`
1725
+ * returned for that node.
1726
+ *
1727
+ * This implementation uses an explicit stack and does not recurse — it is safe on arbitrarily
1728
+ * deep trees.
1729
+ *
1730
+ * @param {Node} node
1731
+ * Root of the subtree to walk.
1732
+ * @param {*} context
1733
+ * Initial context value passed to the root node's `enter`.
1734
+ * @param {{ enter: function(Node, *): *, exit?: function(Node, *): void }} callbacks
1735
+ * @returns {void | walkDOM.STOP}
1736
+ * @see ../docs/walk-dom.md.
1737
+ */
1738
+ function walkDOM(node, context, callbacks) {
1739
+ // Each stack frame is {node, context, phase}:
1740
+ // walkDOM.ENTER — call enter, then push children
1741
+ // walkDOM.EXIT — call exit
1742
+ var stack = [{ node: node, context: context, phase: walkDOM.ENTER }];
1743
+ while (stack.length > 0) {
1744
+ var frame = stack.pop();
1745
+ if (frame.phase === walkDOM.ENTER) {
1746
+ var childContext = callbacks.enter(frame.node, frame.context);
1747
+ if (childContext === walkDOM.STOP) {
1748
+ return walkDOM.STOP;
1749
+ }
1750
+ // Push exit frame before children so it fires after all children are processed (Last In First Out)
1751
+ stack.push({ node: frame.node, context: childContext, phase: walkDOM.EXIT });
1752
+ if (childContext === null || childContext === undefined) {
1753
+ continue; // skip children
1754
+ }
1755
+ // lastChild is read after enter returns, so enter may modify the child list.
1756
+ var child = frame.node.lastChild;
1757
+ // Traverse from lastChild backwards so that pushing onto the stack
1758
+ // naturally yields firstChild on top (processed first).
1759
+ while (child) {
1760
+ stack.push({ node: child, context: childContext, phase: walkDOM.ENTER });
1761
+ child = child.previousSibling;
1762
+ }
1763
+ } else {
1764
+ // frame.phase === walkDOM.EXIT
1765
+ if (callbacks.exit) {
1766
+ callbacks.exit(frame.node, frame.context);
1767
+ }
1768
+ }
1769
+ }
1770
+ }
1771
+
1772
+ /**
1773
+ * Sentinel value returned from a `walkDOM` `enter` callback to abort the entire traversal
1774
+ * immediately.
1775
+ *
1776
+ * @type {symbol}
1777
+ */
1778
+ walkDOM.STOP = Symbol('walkDOM.STOP');
1779
+ /**
1780
+ * Phase constant for a stack frame that has not yet been visited.
1781
+ * The `enter` callback is called and children are scheduled.
1782
+ *
1783
+ * @type {number}
1784
+ */
1785
+ walkDOM.ENTER = 0;
1786
+ /**
1787
+ * Phase constant for a stack frame whose subtree has been fully visited.
1788
+ * The `exit` callback is called.
1789
+ *
1790
+ * @type {number}
1791
+ */
1792
+ walkDOM.EXIT = 1;
1793
+
1794
+ /**
1795
+ * @typedef DocumentOptions
1796
+ * @property {string} [contentType=MIME_TYPE.XML_APPLICATION]
1797
+ */
1798
+ /**
1799
+ * The Document interface describes the common properties and methods for any kind of document.
1800
+ *
1801
+ * It should usually be created using `new DOMImplementation().createDocument(...)`
1802
+ * or `new DOMImplementation().createHTMLDocument(...)`.
1803
+ *
1804
+ * The constructor is considered a private API and offers to initially set the `contentType`
1805
+ * property via it's options parameter.
1806
+ *
1807
+ * @class
1808
+ * @param {Symbol} symbol
1809
+ * @param {DocumentOptions} [options]
1810
+ * @augments Node
1811
+ * @private
1812
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Document
1813
+ * @see https://dom.spec.whatwg.org/#interface-document
1814
+ */
1815
+ function Document(symbol, options) {
1816
+ checkSymbol(symbol);
1817
+
1818
+ var opt = options || {};
1819
+ this.ownerDocument = this;
1820
+ /**
1821
+ * The mime type of the document is determined at creation time and can not be modified.
1822
+ *
1823
+ * @type {string}
1824
+ * @see https://dom.spec.whatwg.org/#concept-document-content-type
1825
+ * @see {@link DOMImplementation}
1826
+ * @see {@link MIME_TYPE}
1827
+ * @readonly
1828
+ */
1829
+ this.contentType = opt.contentType || MIME_TYPE.XML_APPLICATION;
1830
+ /**
1831
+ * @type {'html' | 'xml'}
1832
+ * @see https://dom.spec.whatwg.org/#concept-document-type
1833
+ * @see {@link DOMImplementation}
1834
+ * @readonly
1835
+ */
1836
+ this.type = isHTMLMimeType(this.contentType) ? 'html' : 'xml';
1837
+ }
1838
+
1839
+ /**
1840
+ * Updates the namespace mapping of an element when a new attribute is added.
1841
+ *
1842
+ * @param {Document} doc
1843
+ * The document that the element belongs to.
1844
+ * @param {Element} el
1845
+ * The element to which the attribute is being added.
1846
+ * @param {Attr} newAttr
1847
+ * The new attribute being added.
1848
+ * @private
1849
+ */
1850
+ function _onAddAttribute(doc, el, newAttr) {
1851
+ doc && doc._inc++;
1852
+ var ns = newAttr.namespaceURI;
1853
+ if (ns === NAMESPACE.XMLNS) {
1854
+ //update namespace
1855
+ el._nsMap[newAttr.prefix ? newAttr.localName : ''] = newAttr.value;
1856
+ }
1857
+ }
1858
+
1859
+ /**
1860
+ * Updates the namespace mapping of an element when an attribute is removed.
1861
+ *
1862
+ * @param {Document} doc
1863
+ * The document that the element belongs to.
1864
+ * @param {Element} el
1865
+ * The element from which the attribute is being removed.
1866
+ * @param {Attr} newAttr
1867
+ * The attribute being removed.
1868
+ * @param {boolean} remove
1869
+ * Indicates whether the attribute is to be removed.
1870
+ * @private
1871
+ */
1872
+ function _onRemoveAttribute(doc, el, newAttr, remove) {
1873
+ doc && doc._inc++;
1874
+ var ns = newAttr.namespaceURI;
1875
+ if (ns === NAMESPACE.XMLNS) {
1876
+ //update namespace
1877
+ delete el._nsMap[newAttr.prefix ? newAttr.localName : ''];
1878
+ }
1879
+ }
1880
+
1881
+ /**
1882
+ * Updates `parent.childNodes`, adjusting the indexed items and its `length`.
1883
+ * If `newChild` is provided and has no nextSibling, it will be appended.
1884
+ * Otherwise, it's assumed that an item has been removed or inserted,
1885
+ * and `parent.firstNode` and its `.nextSibling` to re-indexing all child nodes of `parent`.
1886
+ *
1887
+ * @param {Document} doc
1888
+ * The parent document of `el`.
1889
+ * @param {Node} parent
1890
+ * The parent node whose childNodes list needs to be updated.
1891
+ * @param {Node} [newChild]
1892
+ * The new child node to be appended. If not provided, the function assumes a node has been
1893
+ * removed.
1894
+ * @private
1895
+ */
1896
+ function _onUpdateChild(doc, parent, newChild) {
1897
+ if (doc && doc._inc) {
1898
+ doc._inc++;
1899
+ var childNodes = parent.childNodes;
1900
+ // assumes nextSibling and previousSibling were already configured upfront
1901
+ if (newChild && !newChild.nextSibling) {
1902
+ // if an item has been appended, we only need to update the last index and the length
1903
+ childNodes[childNodes.length++] = newChild;
1904
+ } else {
1905
+ // otherwise we need to reindex all items,
1906
+ // which can take a while when processing nodes with a lot of children
1907
+ var child = parent.firstChild;
1908
+ var i = 0;
1909
+ while (child) {
1910
+ childNodes[i++] = child;
1911
+ child = child.nextSibling;
1912
+ }
1913
+ childNodes.length = i;
1914
+ delete childNodes[childNodes.length];
1915
+ }
1916
+ }
1917
+ }
1918
+
1919
+ /**
1920
+ * Removes the connections between `parentNode` and `child`
1921
+ * and any existing `child.previousSibling` or `child.nextSibling`.
1922
+ *
1923
+ * @param {Node} parentNode
1924
+ * The parent node from which the child node is to be removed.
1925
+ * @param {Node} child
1926
+ * The child node to be removed from the parentNode.
1927
+ * @returns {Node}
1928
+ * Returns the child node that was removed.
1929
+ * @throws {DOMException}
1930
+ * With code:
1931
+ * - {@link DOMException.NOT_FOUND_ERR} If the parentNode is not the parent of the child node.
1932
+ * @private
1933
+ * @see https://github.com/xmldom/xmldom/issues/135
1934
+ * @see https://github.com/xmldom/xmldom/issues/145
1935
+ */
1936
+ function _removeChild(parentNode, child) {
1937
+ if (parentNode !== child.parentNode) {
1938
+ throw new DOMException(DOMException.NOT_FOUND_ERR, "child's parent is not parent");
1939
+ }
1940
+ var oldPreviousSibling = child.previousSibling;
1941
+ var oldNextSibling = child.nextSibling;
1942
+ if (oldPreviousSibling) {
1943
+ oldPreviousSibling.nextSibling = oldNextSibling;
1944
+ } else {
1945
+ parentNode.firstChild = oldNextSibling;
1946
+ }
1947
+ if (oldNextSibling) {
1948
+ oldNextSibling.previousSibling = oldPreviousSibling;
1949
+ } else {
1950
+ parentNode.lastChild = oldPreviousSibling;
1951
+ }
1952
+ _onUpdateChild(parentNode.ownerDocument, parentNode);
1953
+ child.parentNode = null;
1954
+ child.previousSibling = null;
1955
+ child.nextSibling = null;
1956
+ return child;
1957
+ }
1958
+
1959
+ /**
1960
+ * Returns `true` if `node` can be a parent for insertion.
1961
+ *
1962
+ * @param {Node} node
1963
+ * @returns {boolean}
1964
+ */
1965
+ function hasValidParentNodeType(node) {
1966
+ return (
1967
+ node &&
1968
+ (node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE || node.nodeType === Node.ELEMENT_NODE)
1969
+ );
1970
+ }
1971
+
1972
+ /**
1973
+ * Returns `true` if `node` can be inserted according to it's `nodeType`.
1974
+ *
1975
+ * @param {Node} node
1976
+ * @returns {boolean}
1977
+ */
1978
+ function hasInsertableNodeType(node) {
1979
+ return (
1980
+ node &&
1981
+ (node.nodeType === Node.CDATA_SECTION_NODE ||
1982
+ node.nodeType === Node.COMMENT_NODE ||
1983
+ node.nodeType === Node.DOCUMENT_FRAGMENT_NODE ||
1984
+ node.nodeType === Node.DOCUMENT_TYPE_NODE ||
1985
+ node.nodeType === Node.ELEMENT_NODE ||
1986
+ node.nodeType === Node.PROCESSING_INSTRUCTION_NODE ||
1987
+ node.nodeType === Node.TEXT_NODE)
1988
+ );
1989
+ }
1990
+
1991
+ /**
1992
+ * Returns true if `node` is a DOCTYPE node.
1993
+ *
1994
+ * @param {Node} node
1995
+ * @returns {boolean}
1996
+ */
1997
+ function isDocTypeNode(node) {
1998
+ return node && node.nodeType === Node.DOCUMENT_TYPE_NODE;
1999
+ }
2000
+
2001
+ /**
2002
+ * Returns true if the node is an element.
2003
+ *
2004
+ * @param {Node} node
2005
+ * @returns {boolean}
2006
+ */
2007
+ function isElementNode(node) {
2008
+ return node && node.nodeType === Node.ELEMENT_NODE;
2009
+ }
2010
+ /**
2011
+ * Returns true if `node` is a text node.
2012
+ *
2013
+ * @param {Node} node
2014
+ * @returns {boolean}
2015
+ */
2016
+ function isTextNode(node) {
2017
+ return node && node.nodeType === Node.TEXT_NODE;
2018
+ }
2019
+
2020
+ /**
2021
+ * Check if en element node can be inserted before `child`, or at the end if child is falsy,
2022
+ * according to the presence and position of a doctype node on the same level.
2023
+ *
2024
+ * @param {Document} doc
2025
+ * The document node.
2026
+ * @param {Node} child
2027
+ * The node that would become the nextSibling if the element would be inserted.
2028
+ * @returns {boolean}
2029
+ * `true` if an element can be inserted before child.
2030
+ * @private
2031
+ */
2032
+ function isElementInsertionPossible(doc, child) {
2033
+ var parentChildNodes = doc.childNodes || [];
2034
+ if (find(parentChildNodes, isElementNode) || isDocTypeNode(child)) {
2035
+ return false;
2036
+ }
2037
+ var docTypeNode = find(parentChildNodes, isDocTypeNode);
2038
+ return !(child && docTypeNode && parentChildNodes.indexOf(docTypeNode) > parentChildNodes.indexOf(child));
2039
+ }
2040
+
2041
+ /**
2042
+ * Check if en element node can be inserted before `child`, or at the end if child is falsy,
2043
+ * according to the presence and position of a doctype node on the same level.
2044
+ *
2045
+ * @param {Node} doc
2046
+ * The document node.
2047
+ * @param {Node} child
2048
+ * The node that would become the nextSibling if the element would be inserted.
2049
+ * @returns {boolean}
2050
+ * `true` if an element can be inserted before child.
2051
+ * @private
2052
+ */
2053
+ function isElementReplacementPossible(doc, child) {
2054
+ var parentChildNodes = doc.childNodes || [];
2055
+
2056
+ function hasElementChildThatIsNotChild(node) {
2057
+ return isElementNode(node) && node !== child;
2058
+ }
2059
+
2060
+ if (find(parentChildNodes, hasElementChildThatIsNotChild)) {
2061
+ return false;
2062
+ }
2063
+ var docTypeNode = find(parentChildNodes, isDocTypeNode);
2064
+ return !(child && docTypeNode && parentChildNodes.indexOf(docTypeNode) > parentChildNodes.indexOf(child));
2065
+ }
2066
+
2067
+ /**
2068
+ * Asserts pre-insertion validity of a node into a parent before a child.
2069
+ * Throws errors for invalid node combinations that would result in an ill-formed DOM.
2070
+ *
2071
+ * @param {Node} parent
2072
+ * The parent node to insert `node` into.
2073
+ * @param {Node} node
2074
+ * The node to insert.
2075
+ * @param {Node | null} child
2076
+ * The node that should become the `nextSibling` of `node`. If null, no sibling is considered.
2077
+ * @throws {DOMException}
2078
+ * With code:
2079
+ * - {@link DOMException.HIERARCHY_REQUEST_ERR} If `parent` is not a Document,
2080
+ * DocumentFragment, or Element node.
2081
+ * - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is a host-including inclusive
2082
+ * ancestor of `parent`. (Currently not implemented)
2083
+ * - {@link DOMException.NOT_FOUND_ERR} If `child` is non-null and its `parent` is not
2084
+ * `parent`.
2085
+ * - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is not a DocumentFragment,
2086
+ * DocumentType, Element, or CharacterData node.
2087
+ * - {@link DOMException.HIERARCHY_REQUEST_ERR} If either `node` is a Text node and `parent` is
2088
+ * a document, or if `node` is a doctype and `parent` is not a document.
2089
+ * @private
2090
+ * @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
2091
+ * @see https://dom.spec.whatwg.org/#concept-node-replace
2092
+ */
2093
+ function assertPreInsertionValidity1to5(parent, node, child) {
2094
+ // 1. If `parent` is not a Document, DocumentFragment, or Element node, then throw a "HierarchyRequestError" DOMException.
2095
+ if (!hasValidParentNodeType(parent)) {
2096
+ throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Unexpected parent node type ' + parent.nodeType);
2097
+ }
2098
+ // 2. If `node` is a host-including inclusive ancestor of `parent`, then throw a "HierarchyRequestError" DOMException.
2099
+ // not implemented!
2100
+ // 3. If `child` is non-null and its parent is not `parent`, then throw a "NotFoundError" DOMException.
2101
+ if (child && child.parentNode !== parent) {
2102
+ throw new DOMException(DOMException.NOT_FOUND_ERR, 'child not in parent');
2103
+ }
2104
+ if (
2105
+ // 4. If `node` is not a DocumentFragment, DocumentType, Element, or CharacterData node, then throw a "HierarchyRequestError" DOMException.
2106
+ !hasInsertableNodeType(node) ||
2107
+ // 5. If either `node` is a Text node and `parent` is a document,
2108
+ // the sax parser currently adds top level text nodes, this will be fixed in 0.9.0
2109
+ // || (node.nodeType === Node.TEXT_NODE && parent.nodeType === Node.DOCUMENT_NODE)
2110
+ // or `node` is a doctype and `parent` is not a document, then throw a "HierarchyRequestError" DOMException.
2111
+ (isDocTypeNode(node) && parent.nodeType !== Node.DOCUMENT_NODE)
2112
+ ) {
2113
+ throw new DOMException(
2114
+ DOMException.HIERARCHY_REQUEST_ERR,
2115
+ 'Unexpected node type ' + node.nodeType + ' for parent node type ' + parent.nodeType
2116
+ );
2117
+ }
2118
+ }
2119
+
2120
+ /**
2121
+ * Asserts pre-insertion validity of a node into a document before a child.
2122
+ * Throws errors for invalid node combinations that would result in an ill-formed DOM.
2123
+ *
2124
+ * @param {Document} parent
2125
+ * The parent node to insert `node` into.
2126
+ * @param {Node} node
2127
+ * The node to insert.
2128
+ * @param {Node | undefined} child
2129
+ * The node that should become the `nextSibling` of `node`. If undefined, no sibling is
2130
+ * considered.
2131
+ * @returns {Node}
2132
+ * @throws {DOMException}
2133
+ * With code:
2134
+ * - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is a DocumentFragment with more than
2135
+ * one element child or has a Text node child.
2136
+ * - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is a DocumentFragment with one
2137
+ * element child and either `parent` has an element child, `child` is a doctype, or `child` is
2138
+ * non-null and a doctype is following `child`.
2139
+ * - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is an Element and `parent` has an
2140
+ * element child, `child` is a doctype, or `child` is non-null and a doctype is following
2141
+ * `child`.
2142
+ * - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is a DocumentType and `parent` has a
2143
+ * doctype child, `child` is non-null and an element is preceding `child`, or `child` is null
2144
+ * and `parent` has an element child.
2145
+ * @private
2146
+ * @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
2147
+ * @see https://dom.spec.whatwg.org/#concept-node-replace
2148
+ */
2149
+ function assertPreInsertionValidityInDocument(parent, node, child) {
2150
+ var parentChildNodes = parent.childNodes || [];
2151
+ var nodeChildNodes = node.childNodes || [];
2152
+
2153
+ // DocumentFragment
2154
+ if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
2155
+ var nodeChildElements = nodeChildNodes.filter(isElementNode);
2156
+ // If node has more than one element child or has a Text node child.
2157
+ if (nodeChildElements.length > 1 || find(nodeChildNodes, isTextNode)) {
2158
+ throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'More than one element or text in fragment');
2159
+ }
2160
+ // Otherwise, if `node` has one element child and either `parent` has an element child,
2161
+ // `child` is a doctype, or `child` is non-null and a doctype is following `child`.
2162
+ if (nodeChildElements.length === 1 && !isElementInsertionPossible(parent, child)) {
2163
+ throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Element in fragment can not be inserted before doctype');
2164
+ }
2165
+ }
2166
+ // Element
2167
+ if (isElementNode(node)) {
2168
+ // `parent` has an element child, `child` is a doctype,
2169
+ // or `child` is non-null and a doctype is following `child`.
2170
+ if (!isElementInsertionPossible(parent, child)) {
2171
+ throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Only one element can be added and only after doctype');
2172
+ }
2173
+ }
2174
+ // DocumentType
2175
+ if (isDocTypeNode(node)) {
2176
+ // `parent` has a doctype child,
2177
+ if (find(parentChildNodes, isDocTypeNode)) {
2178
+ throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Only one doctype is allowed');
2179
+ }
2180
+ var parentElementChild = find(parentChildNodes, isElementNode);
2181
+ // `child` is non-null and an element is preceding `child`,
2182
+ if (child && parentChildNodes.indexOf(parentElementChild) < parentChildNodes.indexOf(child)) {
2183
+ throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Doctype can only be inserted before an element');
2184
+ }
2185
+ // or `child` is null and `parent` has an element child.
2186
+ if (!child && parentElementChild) {
2187
+ throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Doctype can not be appended since element is present');
2188
+ }
2189
+ }
2190
+ }
2191
+
2192
+ /**
2193
+ * @param {Document} parent
2194
+ * The parent node to insert `node` into.
2195
+ * @param {Node} node
2196
+ * The node to insert.
2197
+ * @param {Node | undefined} child
2198
+ * the node that should become the `nextSibling` of `node`
2199
+ * @returns {Node}
2200
+ * @throws {DOMException}
2201
+ * For several node combinations that would create a DOM that is not well-formed.
2202
+ * @throws {DOMException}
2203
+ * If `child` is provided but is not a child of `parent`.
2204
+ * @private
2205
+ * @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
2206
+ * @see https://dom.spec.whatwg.org/#concept-node-replace
2207
+ */
2208
+ function assertPreReplacementValidityInDocument(parent, node, child) {
2209
+ var parentChildNodes = parent.childNodes || [];
2210
+ var nodeChildNodes = node.childNodes || [];
2211
+
2212
+ // DocumentFragment
2213
+ if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
2214
+ var nodeChildElements = nodeChildNodes.filter(isElementNode);
2215
+ // If `node` has more than one element child or has a Text node child.
2216
+ if (nodeChildElements.length > 1 || find(nodeChildNodes, isTextNode)) {
2217
+ throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'More than one element or text in fragment');
2218
+ }
2219
+ // Otherwise, if `node` has one element child and either `parent` has an element child that is not `child` or a doctype is following `child`.
2220
+ if (nodeChildElements.length === 1 && !isElementReplacementPossible(parent, child)) {
2221
+ throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Element in fragment can not be inserted before doctype');
2222
+ }
2223
+ }
2224
+ // Element
2225
+ if (isElementNode(node)) {
2226
+ // `parent` has an element child that is not `child` or a doctype is following `child`.
2227
+ if (!isElementReplacementPossible(parent, child)) {
2228
+ throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Only one element can be added and only after doctype');
2229
+ }
2230
+ }
2231
+ // DocumentType
2232
+ if (isDocTypeNode(node)) {
2233
+ function hasDoctypeChildThatIsNotChild(node) {
2234
+ return isDocTypeNode(node) && node !== child;
2235
+ }
2236
+
2237
+ // `parent` has a doctype child that is not `child`,
2238
+ if (find(parentChildNodes, hasDoctypeChildThatIsNotChild)) {
2239
+ throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Only one doctype is allowed');
2240
+ }
2241
+ var parentElementChild = find(parentChildNodes, isElementNode);
2242
+ // or an element is preceding `child`.
2243
+ if (child && parentChildNodes.indexOf(parentElementChild) < parentChildNodes.indexOf(child)) {
2244
+ throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Doctype can only be inserted before an element');
2245
+ }
2246
+ }
2247
+ }
2248
+
2249
+ /**
2250
+ * Inserts a node into a parent node before a child node.
2251
+ *
2252
+ * @param {Node} parent
2253
+ * The parent node to insert the node into.
2254
+ * @param {Node} node
2255
+ * The node to insert into the parent.
2256
+ * @param {Node | null} child
2257
+ * The node that should become the next sibling of the node.
2258
+ * If null, the function inserts the node at the end of the children of the parent node.
2259
+ * @param {Function} [_inDocumentAssertion]
2260
+ * An optional function to check pre-insertion validity if parent is a document node.
2261
+ * Defaults to {@link assertPreInsertionValidityInDocument}
2262
+ * @returns {Node}
2263
+ * Returns the inserted node.
2264
+ * @throws {DOMException}
2265
+ * Throws a DOMException if inserting the node would result in a DOM tree that is not
2266
+ * well-formed. See {@link assertPreInsertionValidity1to5},
2267
+ * {@link assertPreInsertionValidityInDocument}.
2268
+ * @throws {DOMException}
2269
+ * Throws a DOMException if child is provided but is not a child of the parent. See
2270
+ * {@link Node.removeChild}
2271
+ * @private
2272
+ * @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
2273
+ */
2274
+ function _insertBefore(parent, node, child, _inDocumentAssertion) {
2275
+ // To ensure pre-insertion validity of a node into a parent before a child, run these steps:
2276
+ assertPreInsertionValidity1to5(parent, node, child);
2277
+
2278
+ // If parent is a document, and any of the statements below, switched on the interface node implements,
2279
+ // are true, then throw a "HierarchyRequestError" DOMException.
2280
+ if (parent.nodeType === Node.DOCUMENT_NODE) {
2281
+ (_inDocumentAssertion || assertPreInsertionValidityInDocument)(parent, node, child);
2282
+ }
2283
+
2284
+ var cp = node.parentNode;
2285
+ if (cp) {
2286
+ cp.removeChild(node); //remove and update
2287
+ }
2288
+ if (node.nodeType === DOCUMENT_FRAGMENT_NODE) {
2289
+ var newFirst = node.firstChild;
2290
+ if (newFirst == null) {
2291
+ return node;
2292
+ }
2293
+ var newLast = node.lastChild;
2294
+ } else {
2295
+ newFirst = newLast = node;
2296
+ }
2297
+ var pre = child ? child.previousSibling : parent.lastChild;
2298
+
2299
+ newFirst.previousSibling = pre;
2300
+ newLast.nextSibling = child;
2301
+
2302
+ if (pre) {
2303
+ pre.nextSibling = newFirst;
2304
+ } else {
2305
+ parent.firstChild = newFirst;
2306
+ }
2307
+ if (child == null) {
2308
+ parent.lastChild = newLast;
2309
+ } else {
2310
+ child.previousSibling = newLast;
2311
+ }
2312
+ do {
2313
+ newFirst.parentNode = parent;
2314
+ } while (newFirst !== newLast && (newFirst = newFirst.nextSibling));
2315
+ _onUpdateChild(parent.ownerDocument || parent, parent, node);
2316
+ if (node.nodeType == DOCUMENT_FRAGMENT_NODE) {
2317
+ node.firstChild = node.lastChild = null;
2318
+ }
2319
+
2320
+ return node;
2321
+ }
2322
+
2323
+ Document.prototype = {
2324
+ /**
2325
+ * The implementation that created this document.
2326
+ *
2327
+ * @type DOMImplementation
2328
+ * @readonly
2329
+ */
2330
+ implementation: null,
2331
+ nodeName: '#document',
2332
+ nodeType: DOCUMENT_NODE,
2333
+ /**
2334
+ * The DocumentType node of the document.
2335
+ *
2336
+ * @type DocumentType
2337
+ * @readonly
2338
+ */
2339
+ doctype: null,
2340
+ documentElement: null,
2341
+ _inc: 1,
2342
+
2343
+ insertBefore: function (newChild, refChild) {
2344
+ //raises
2345
+ if (newChild.nodeType === DOCUMENT_FRAGMENT_NODE) {
2346
+ var child = newChild.firstChild;
2347
+ while (child) {
2348
+ var next = child.nextSibling;
2349
+ this.insertBefore(child, refChild);
2350
+ child = next;
2351
+ }
2352
+ return newChild;
2353
+ }
2354
+ _insertBefore(this, newChild, refChild);
2355
+ newChild.ownerDocument = this;
2356
+ if (this.documentElement === null && newChild.nodeType === ELEMENT_NODE) {
2357
+ this.documentElement = newChild;
2358
+ }
2359
+
2360
+ return newChild;
2361
+ },
2362
+ removeChild: function (oldChild) {
2363
+ var removed = _removeChild(this, oldChild);
2364
+ if (removed === this.documentElement) {
2365
+ this.documentElement = null;
2366
+ }
2367
+ return removed;
2368
+ },
2369
+ replaceChild: function (newChild, oldChild) {
2370
+ //raises
2371
+ _insertBefore(this, newChild, oldChild, assertPreReplacementValidityInDocument);
2372
+ newChild.ownerDocument = this;
2373
+ if (oldChild) {
2374
+ this.removeChild(oldChild);
2375
+ }
2376
+ if (isElementNode(newChild)) {
2377
+ this.documentElement = newChild;
2378
+ }
2379
+ },
2380
+ /**
2381
+ * Imports a node from another document into this document, creating a new copy owned by this
2382
+ * document. The source node and its subtree are not modified.
2383
+ *
2384
+ * @param {Node} importedNode
2385
+ * The node to import.
2386
+ * @param {boolean} deep
2387
+ * If true, the contents of the node are recursively imported.
2388
+ * If false, only the node itself (and its attributes, if it is an element) are imported.
2389
+ * @returns {Node}
2390
+ * Returns the newly created import of the node.
2391
+ * @see {@link importNode}
2392
+ * @see {@link https://dom.spec.whatwg.org/#dom-document-importnode}
2393
+ */
2394
+ importNode: function (importedNode, deep) {
2395
+ return importNode(this, importedNode, deep);
2396
+ },
2397
+ // Introduced in DOM Level 2:
2398
+ getElementById: function (id) {
2399
+ var rtv = null;
2400
+ _visitNode(this.documentElement, function (node) {
2401
+ if (node.nodeType == ELEMENT_NODE) {
2402
+ if (node.getAttribute('id') == id) {
2403
+ rtv = node;
2404
+ return true;
2405
+ }
2406
+ }
2407
+ });
2408
+ return rtv;
2409
+ },
2410
+
2411
+ /**
2412
+ * Creates a new `Element` that is owned by this `Document`.
2413
+ * In HTML Documents `localName` is the lower cased `tagName`,
2414
+ * otherwise no transformation is being applied.
2415
+ * When `contentType` implies the HTML namespace, it will be set as `namespaceURI`.
2416
+ *
2417
+ * __This implementation differs from the specification:__ - The provided name is not checked
2418
+ * against the `Name` production,
2419
+ * so no related error will be thrown.
2420
+ * - There is no interface `HTMLElement`, it is always an `Element`.
2421
+ * - There is no support for a second argument to indicate using custom elements.
2422
+ *
2423
+ * @param {string} tagName
2424
+ * @returns {Element}
2425
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement
2426
+ * @see https://dom.spec.whatwg.org/#dom-document-createelement
2427
+ * @see https://dom.spec.whatwg.org/#concept-create-element
2428
+ */
2429
+ createElement: function (tagName) {
2430
+ var node = new Element(PDC);
2431
+ node.ownerDocument = this;
2432
+ if (this.type === 'html') {
2433
+ tagName = tagName.toLowerCase();
2434
+ }
2435
+ if (hasDefaultHTMLNamespace(this.contentType)) {
2436
+ node.namespaceURI = NAMESPACE.HTML;
2437
+ }
2438
+ node.nodeName = tagName;
2439
+ node.tagName = tagName;
2440
+ node.localName = tagName;
2441
+ node.childNodes = new NodeList();
2442
+ var attrs = (node.attributes = new NamedNodeMap());
2443
+ attrs._ownerElement = node;
2444
+ return node;
2445
+ },
2446
+ /**
2447
+ * @returns {DocumentFragment}
2448
+ */
2449
+ createDocumentFragment: function () {
2450
+ var node = new DocumentFragment(PDC);
2451
+ node.ownerDocument = this;
2452
+ node.childNodes = new NodeList();
2453
+ return node;
2454
+ },
2455
+ /**
2456
+ * @param {string} data
2457
+ * @returns {Text}
2458
+ */
2459
+ createTextNode: function (data) {
2460
+ var node = new Text(PDC);
2461
+ node.ownerDocument = this;
2462
+ node.childNodes = new NodeList();
2463
+ node.appendData(data);
2464
+ return node;
2465
+ },
2466
+ /**
2467
+ * @param {string} data
2468
+ * @returns {Comment}
2469
+ * @see https://dom.spec.whatwg.org/#dom-document-createcomment
2470
+ * @see https://www.w3.org/TR/xml/#NT-Comment XML 1.0 production [15]
2471
+ * @see https://www.w3.org/TR/DOM-Parsing/#dfn-concept-serialize-xml §3.2.1.3
2472
+ *
2473
+ * Note: no validation is performed at creation time. When the resulting document is
2474
+ * serialized with `requireWellFormed: true`, the serializer throws `InvalidStateError`
2475
+ * if the comment data contains `--` anywhere, ends with `-`, or contains characters
2476
+ * outside the XML Char production (W3C DOM Parsing §3.2.1.3). Without that option the
2477
+ * data is emitted verbatim.
2478
+ */
2479
+ createComment: function (data) {
2480
+ var node = new Comment(PDC);
2481
+ node.ownerDocument = this;
2482
+ node.childNodes = new NodeList();
2483
+ node.appendData(data);
2484
+ return node;
2485
+ },
2486
+ /**
2487
+ * Returns a new CDATASection node whose data is `data`.
2488
+ *
2489
+ * __This implementation differs from the specification:__ - calling this method on an HTML
2490
+ * document does not throw `NotSupportedError`.
2491
+ *
2492
+ * @param {string} data
2493
+ * @returns {CDATASection}
2494
+ * @throws {DOMException}
2495
+ * With code `INVALID_CHARACTER_ERR` if `data` contains `"]]>"`.
2496
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createCDATASection
2497
+ * @see https://dom.spec.whatwg.org/#dom-document-createcdatasection
2498
+ */
2499
+ createCDATASection: function (data) {
2500
+ if (data.indexOf(']]>') !== -1) {
2501
+ throw new DOMException(DOMException.INVALID_CHARACTER_ERR, 'data contains "]]>"');
2502
+ }
2503
+ var node = new CDATASection(PDC);
2504
+ node.ownerDocument = this;
2505
+ node.childNodes = new NodeList();
2506
+ node.appendData(data);
2507
+ return node;
2508
+ },
2509
+ /**
2510
+ * Returns a ProcessingInstruction node whose target is target and data is data.
2511
+ *
2512
+ * __This behavior is slightly different from the in the specs__:
2513
+ * - it does not do any input validation on the arguments and doesn't throw
2514
+ * "InvalidCharacterError".
2515
+ *
2516
+ * Note: When the resulting document is serialized with `requireWellFormed: true`, the
2517
+ * serializer throws `InvalidStateError` if `.target` is not a valid XML `NCName` (a `Name`
2518
+ * with no colon) or is an ASCII case-insensitive match for `"xml"`, or if `.data` contains
2519
+ * `?>` or characters outside the XML Char production (W3C DOM Parsing §3.2.1.7). Without that
2520
+ * option the target and data are emitted verbatim.
2521
+ *
2522
+ * @param {string} target
2523
+ * @param {string} data
2524
+ * @returns {ProcessingInstruction}
2525
+ * @see https://developer.mozilla.org/docs/Web/API/Document/createProcessingInstruction
2526
+ * @see https://dom.spec.whatwg.org/#dom-document-createprocessinginstruction
2527
+ * @see https://www.w3.org/TR/DOM-Parsing/#dfn-concept-serialize-xml §3.2.1.7
2528
+ */
2529
+ createProcessingInstruction: function (target, data) {
2530
+ var node = new ProcessingInstruction(PDC);
2531
+ node.ownerDocument = this;
2532
+ node.childNodes = new NodeList();
2533
+ node.nodeName = node.target = target;
2534
+ node.nodeValue = node.data = data;
2535
+ return node;
2536
+ },
2537
+ /**
2538
+ * Creates an `Attr` node that is owned by this document.
2539
+ * In HTML Documents `localName` is the lower cased `name`,
2540
+ * otherwise no transformation is being applied.
2541
+ *
2542
+ * __This implementation differs from the specification:__ - The provided name is not checked
2543
+ * against the `Name` production,
2544
+ * so no related error will be thrown.
2545
+ *
2546
+ * @param {string} name
2547
+ * @returns {Attr}
2548
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createAttribute
2549
+ * @see https://dom.spec.whatwg.org/#dom-document-createattribute
2550
+ */
2551
+ createAttribute: function (name) {
2552
+ if (!g.QName_exact.test(name)) {
2553
+ throw new DOMException(DOMException.INVALID_CHARACTER_ERR, 'invalid character in name "' + name + '"');
2554
+ }
2555
+ if (this.type === 'html') {
2556
+ name = name.toLowerCase();
2557
+ }
2558
+ return this._createAttribute(name);
2559
+ },
2560
+ _createAttribute: function (name) {
2561
+ var node = new Attr(PDC);
2562
+ node.ownerDocument = this;
2563
+ node.childNodes = new NodeList();
2564
+ node.name = name;
2565
+ node.nodeName = name;
2566
+ node.localName = name;
2567
+ node.specified = true;
2568
+ return node;
2569
+ },
2570
+ /**
2571
+ * Creates an EntityReference object.
2572
+ * The current implementation does not fill the `childNodes` with those of the corresponding
2573
+ * `Entity`
2574
+ *
2575
+ * The `name` is validated against the XML `Name` production at creation time; an invalid name
2576
+ * throws `InvalidCharacterError`. When the resulting node is serialized with
2577
+ * `requireWellFormed: true`, the serializer re-validates `nodeName` against the XML `Name`
2578
+ * production and throws `InvalidStateError` if a later `nodeName` mutation made it invalid;
2579
+ * without that option the name is emitted verbatim.
2580
+ *
2581
+ * __This implementation differs from the specification:__ xmldom does not expand entities —
2582
+ * the parser resolves entity references inline and never constructs `EntityReference` nodes,
2583
+ * so this method is the only producer.
2584
+ *
2585
+ * @deprecated
2586
+ * In DOM Level 4.
2587
+ * @param {string} name
2588
+ * The name of the entity to reference. No namespace well-formedness checks are performed.
2589
+ * @returns {EntityReference}
2590
+ * @throws {DOMException}
2591
+ * With code `INVALID_CHARACTER_ERR` when `name` is not a valid XML `Name`.
2592
+ * @throws {DOMException}
2593
+ * with code `NOT_SUPPORTED_ERR` when the document is of type `html`
2594
+ * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-392B75AE
2595
+ */
2596
+ createEntityReference: function (name) {
2597
+ if (!g.Name_exact.test(name)) {
2598
+ throw new DOMException(DOMException.INVALID_CHARACTER_ERR, 'not a valid xml name "' + name + '"');
2599
+ }
2600
+ if (this.type === 'html') {
2601
+ throw new DOMException('document is an html document', DOMExceptionName.NotSupportedError);
2602
+ }
2603
+
2604
+ var node = new EntityReference(PDC);
2605
+ node.ownerDocument = this;
2606
+ node.childNodes = new NodeList();
2607
+ node.nodeName = name;
2608
+ return node;
2609
+ },
2610
+ // Introduced in DOM Level 2:
2611
+ /**
2612
+ * @param {string} namespaceURI
2613
+ * @param {string} qualifiedName
2614
+ * @returns {Element}
2615
+ */
2616
+ createElementNS: function (namespaceURI, qualifiedName) {
2617
+ var validated = validateAndExtract(namespaceURI, qualifiedName);
2618
+ var node = new Element(PDC);
2619
+ var attrs = (node.attributes = new NamedNodeMap());
2620
+ node.childNodes = new NodeList();
2621
+ node.ownerDocument = this;
2622
+ node.nodeName = qualifiedName;
2623
+ node.tagName = qualifiedName;
2624
+ node.namespaceURI = validated[0];
2625
+ node.prefix = validated[1];
2626
+ node.localName = validated[2];
2627
+ attrs._ownerElement = node;
2628
+ return node;
2629
+ },
2630
+ // Introduced in DOM Level 2:
2631
+ /**
2632
+ * @param {string} namespaceURI
2633
+ * @param {string} qualifiedName
2634
+ * @returns {Attr}
2635
+ */
2636
+ createAttributeNS: function (namespaceURI, qualifiedName) {
2637
+ var validated = validateAndExtract(namespaceURI, qualifiedName);
2638
+ var node = new Attr(PDC);
2639
+ node.ownerDocument = this;
2640
+ node.childNodes = new NodeList();
2641
+ node.nodeName = qualifiedName;
2642
+ node.name = qualifiedName;
2643
+ node.specified = true;
2644
+ node.namespaceURI = validated[0];
2645
+ node.prefix = validated[1];
2646
+ node.localName = validated[2];
2647
+ return node;
2648
+ },
2649
+ };
2650
+ _extends(Document, Node);
2651
+
2652
+ function Element(symbol) {
2653
+ checkSymbol(symbol);
2654
+
2655
+ this._nsMap = Object.create(null);
2656
+ }
2657
+ Element.prototype = {
2658
+ nodeType: ELEMENT_NODE,
2659
+ /**
2660
+ * The attributes of this element.
2661
+ *
2662
+ * @type {NamedNodeMap | null}
2663
+ */
2664
+ attributes: null,
2665
+ getQualifiedName: function () {
2666
+ return this.prefix ? this.prefix + ':' + this.localName : this.localName;
2667
+ },
2668
+ _isInHTMLDocumentAndNamespace: function () {
2669
+ return this.ownerDocument.type === 'html' && this.namespaceURI === NAMESPACE.HTML;
2670
+ },
2671
+ /**
2672
+ * Implementaton of Level2 Core function hasAttributes.
2673
+ *
2674
+ * @returns {boolean}
2675
+ * True if attribute list is not empty.
2676
+ * @see https://www.w3.org/TR/DOM-Level-2-Core/#core-ID-NodeHasAttrs
2677
+ */
2678
+ hasAttributes: function () {
2679
+ return !!(this.attributes && this.attributes.length);
2680
+ },
2681
+ hasAttribute: function (name) {
2682
+ return !!this.getAttributeNode(name);
2683
+ },
2684
+ /**
2685
+ * Returns element’s first attribute whose qualified name is `name`, and `null`
2686
+ * if there is no such attribute.
2687
+ *
2688
+ * @param {string} name
2689
+ * @returns {string | null}
2690
+ */
2691
+ getAttribute: function (name) {
2692
+ var attr = this.getAttributeNode(name);
2693
+ return attr ? attr.value : null;
2694
+ },
2695
+ getAttributeNode: function (name) {
2696
+ if (this._isInHTMLDocumentAndNamespace()) {
2697
+ name = name.toLowerCase();
2698
+ }
2699
+ return this.attributes.getNamedItem(name);
2700
+ },
2701
+ /**
2702
+ * Sets the value of element’s first attribute whose qualified name is qualifiedName to value.
2703
+ *
2704
+ * @param {string} name
2705
+ * @param {string} value
2706
+ */
2707
+ setAttribute: function (name, value) {
2708
+ if (this._isInHTMLDocumentAndNamespace()) {
2709
+ name = name.toLowerCase();
2710
+ }
2711
+ var attr = this.getAttributeNode(name);
2712
+ if (attr) {
2713
+ attr.value = attr.nodeValue = '' + value;
2714
+ } else {
2715
+ attr = this.ownerDocument._createAttribute(name);
2716
+ attr.value = attr.nodeValue = '' + value;
2717
+ this.setAttributeNode(attr);
2718
+ }
2719
+ },
2720
+ removeAttribute: function (name) {
2721
+ var attr = this.getAttributeNode(name);
2722
+ attr && this.removeAttributeNode(attr);
2723
+ },
2724
+ setAttributeNode: function (newAttr) {
2725
+ return this.attributes.setNamedItem(newAttr);
2726
+ },
2727
+ setAttributeNodeNS: function (newAttr) {
2728
+ return this.attributes.setNamedItemNS(newAttr);
2729
+ },
2730
+ removeAttributeNode: function (oldAttr) {
2731
+ //console.log(this == oldAttr.ownerElement)
2732
+ return this.attributes.removeNamedItem(oldAttr.nodeName);
2733
+ },
2734
+ //get real attribute name,and remove it by removeAttributeNode
2735
+ removeAttributeNS: function (namespaceURI, localName) {
2736
+ var old = this.getAttributeNodeNS(namespaceURI, localName);
2737
+ old && this.removeAttributeNode(old);
2738
+ },
2739
+
2740
+ hasAttributeNS: function (namespaceURI, localName) {
2741
+ return this.getAttributeNodeNS(namespaceURI, localName) != null;
2742
+ },
2743
+ /**
2744
+ * Returns element’s attribute whose namespace is `namespaceURI` and local name is
2745
+ * `localName`,
2746
+ * or `null` if there is no such attribute.
2747
+ *
2748
+ * @param {string} namespaceURI
2749
+ * @param {string} localName
2750
+ * @returns {string | null}
2751
+ */
2752
+ getAttributeNS: function (namespaceURI, localName) {
2753
+ var attr = this.getAttributeNodeNS(namespaceURI, localName);
2754
+ return attr ? attr.value : null;
2755
+ },
2756
+ /**
2757
+ * Sets the value of element’s attribute whose namespace is `namespaceURI` and local name is
2758
+ * `localName` to value.
2759
+ *
2760
+ * @param {string} namespaceURI
2761
+ * @param {string} qualifiedName
2762
+ * @param {string} value
2763
+ * @see https://dom.spec.whatwg.org/#dom-element-setattributens
2764
+ */
2765
+ setAttributeNS: function (namespaceURI, qualifiedName, value) {
2766
+ var validated = validateAndExtract(namespaceURI, qualifiedName);
2767
+ var localName = validated[2];
2768
+ var attr = this.getAttributeNodeNS(namespaceURI, localName);
2769
+ if (attr) {
2770
+ attr.value = attr.nodeValue = '' + value;
2771
+ } else {
2772
+ attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
2773
+ attr.value = attr.nodeValue = '' + value;
2774
+ this.setAttributeNode(attr);
2775
+ }
2776
+ },
2777
+ getAttributeNodeNS: function (namespaceURI, localName) {
2778
+ return this.attributes.getNamedItemNS(namespaceURI, localName);
2779
+ },
2780
+
2781
+ /**
2782
+ * Returns a LiveNodeList of all child elements which have **all** of the given class name(s).
2783
+ *
2784
+ * Returns an empty list if `classNames` is an empty string or only contains HTML white space
2785
+ * characters.
2786
+ *
2787
+ * Warning: This returns a live LiveNodeList.
2788
+ * Changes in the DOM will reflect in the array as the changes occur.
2789
+ * If an element selected by this array no longer qualifies for the selector,
2790
+ * it will automatically be removed. Be aware of this for iteration purposes.
2791
+ *
2792
+ * @param {string} classNames
2793
+ * Is a string representing the class name(s) to match; multiple class names are separated by
2794
+ * (ASCII-)whitespace.
2795
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByClassName
2796
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName
2797
+ * @see https://dom.spec.whatwg.org/#concept-getelementsbyclassname
2798
+ */
2799
+ getElementsByClassName: function (classNames) {
2800
+ var classNamesSet = toOrderedSet(classNames);
2801
+ return new LiveNodeList(this, function (base) {
2802
+ var ls = [];
2803
+ if (classNamesSet.length > 0) {
2804
+ _visitNode(base, function (node) {
2805
+ if (node !== base && node.nodeType === ELEMENT_NODE) {
2806
+ var nodeClassNames = node.getAttribute('class');
2807
+ // can be null if the attribute does not exist
2808
+ if (nodeClassNames) {
2809
+ // before splitting and iterating just compare them for the most common case
2810
+ var matches = classNames === nodeClassNames;
2811
+ if (!matches) {
2812
+ var nodeClassNamesSet = toOrderedSet(nodeClassNames);
2813
+ matches = classNamesSet.every(arrayIncludes(nodeClassNamesSet));
2814
+ }
2815
+ if (matches) {
2816
+ ls.push(node);
2817
+ }
2818
+ }
2819
+ }
2820
+ });
2821
+ }
2822
+ return ls;
2823
+ });
2824
+ },
2825
+
2826
+ /**
2827
+ * Returns a LiveNodeList of elements with the given qualifiedName.
2828
+ * Searching for all descendants can be done by passing `*` as `qualifiedName`.
2829
+ *
2830
+ * All descendants of the specified element are searched, but not the element itself.
2831
+ * The returned list is live, which means it updates itself with the DOM tree automatically.
2832
+ * Therefore, there is no need to call `Element.getElementsByTagName()`
2833
+ * with the same element and arguments repeatedly if the DOM changes in between calls.
2834
+ *
2835
+ * When called on an HTML element in an HTML document,
2836
+ * `getElementsByTagName` lower-cases the argument before searching for it.
2837
+ * This is undesirable when trying to match camel-cased SVG elements (such as
2838
+ * `<linearGradient>`) in an HTML document.
2839
+ * Instead, use `Element.getElementsByTagNameNS()`,
2840
+ * which preserves the capitalization of the tag name.
2841
+ *
2842
+ * `Element.getElementsByTagName` is similar to `Document.getElementsByTagName()`,
2843
+ * except that it only searches for elements that are descendants of the specified element.
2844
+ *
2845
+ * @param {string} qualifiedName
2846
+ * @returns {LiveNodeList}
2847
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByTagName
2848
+ * @see https://dom.spec.whatwg.org/#concept-getelementsbytagname
2849
+ */
2850
+ getElementsByTagName: function (qualifiedName) {
2851
+ var isHTMLDocument = (this.nodeType === DOCUMENT_NODE ? this : this.ownerDocument).type === 'html';
2852
+ var lowerQualifiedName = qualifiedName.toLowerCase();
2853
+ return new LiveNodeList(this, function (base) {
2854
+ var ls = [];
2855
+ _visitNode(base, function (node) {
2856
+ if (node === base || node.nodeType !== ELEMENT_NODE) {
2857
+ return;
2858
+ }
2859
+ if (qualifiedName === '*') {
2860
+ ls.push(node);
2861
+ } else {
2862
+ var nodeQualifiedName = node.getQualifiedName();
2863
+ var matchingQName = isHTMLDocument && node.namespaceURI === NAMESPACE.HTML ? lowerQualifiedName : qualifiedName;
2864
+ if (nodeQualifiedName === matchingQName) {
2865
+ ls.push(node);
2866
+ }
2867
+ }
2868
+ });
2869
+ return ls;
2870
+ });
2871
+ },
2872
+ getElementsByTagNameNS: function (namespaceURI, localName) {
2873
+ return new LiveNodeList(this, function (base) {
2874
+ var ls = [];
2875
+ _visitNode(base, function (node) {
2876
+ if (
2877
+ node !== base &&
2878
+ node.nodeType === ELEMENT_NODE &&
2879
+ (namespaceURI === '*' || node.namespaceURI === namespaceURI) &&
2880
+ (localName === '*' || node.localName == localName)
2881
+ ) {
2882
+ ls.push(node);
2883
+ }
2884
+ });
2885
+ return ls;
2886
+ });
2887
+ },
2888
+ };
2889
+ Document.prototype.getElementsByClassName = Element.prototype.getElementsByClassName;
2890
+ Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
2891
+ Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
2892
+
2893
+ _extends(Element, Node);
2894
+ function Attr(symbol) {
2895
+ checkSymbol(symbol);
2896
+
2897
+ this.namespaceURI = null;
2898
+ this.prefix = null;
2899
+ this.ownerElement = null;
2900
+ }
2901
+ Attr.prototype.nodeType = ATTRIBUTE_NODE;
2902
+ _extends(Attr, Node);
2903
+
2904
+ function CharacterData(symbol) {
2905
+ checkSymbol(symbol);
2906
+ }
2907
+ CharacterData.prototype = {
2908
+ data: '',
2909
+ substringData: function (offset, count) {
2910
+ return this.data.substring(offset, offset + count);
2911
+ },
2912
+ appendData: function (text) {
2913
+ text = this.data + text;
2914
+ this.nodeValue = this.data = text;
2915
+ this.length = text.length;
2916
+ },
2917
+ insertData: function (offset, text) {
2918
+ this.replaceData(offset, 0, text);
2919
+ },
2920
+ deleteData: function (offset, count) {
2921
+ this.replaceData(offset, count, '');
2922
+ },
2923
+ replaceData: function (offset, count, text) {
2924
+ var start = this.data.substring(0, offset);
2925
+ var end = this.data.substring(offset + count);
2926
+ text = start + text + end;
2927
+ this.nodeValue = this.data = text;
2928
+ this.length = text.length;
2929
+ },
2930
+ };
2931
+ _extends(CharacterData, Node);
2932
+ function Text(symbol) {
2933
+ checkSymbol(symbol);
2934
+ }
2935
+ Text.prototype = {
2936
+ nodeName: '#text',
2937
+ nodeType: TEXT_NODE,
2938
+ splitText: function (offset) {
2939
+ var text = this.data;
2940
+ var newText = text.substring(offset);
2941
+ text = text.substring(0, offset);
2942
+ this.data = this.nodeValue = text;
2943
+ this.length = text.length;
2944
+ var newNode = this.ownerDocument.createTextNode(newText);
2945
+ if (this.parentNode) {
2946
+ this.parentNode.insertBefore(newNode, this.nextSibling);
2947
+ }
2948
+ return newNode;
2949
+ },
2950
+ };
2951
+ _extends(Text, CharacterData);
2952
+ function Comment(symbol) {
2953
+ checkSymbol(symbol);
2954
+ }
2955
+ Comment.prototype = {
2956
+ nodeName: '#comment',
2957
+ nodeType: COMMENT_NODE,
2958
+ };
2959
+ _extends(Comment, CharacterData);
2960
+
2961
+ function CDATASection(symbol) {
2962
+ checkSymbol(symbol);
2963
+ }
2964
+ CDATASection.prototype = {
2965
+ nodeName: '#cdata-section',
2966
+ nodeType: CDATA_SECTION_NODE,
2967
+ };
2968
+ _extends(CDATASection, Text);
2969
+
2970
+ /**
2971
+ * @class DocumentType
2972
+ * @augments Node
2973
+ * @property {string} name
2974
+ * The doctype name, stored verbatim. Declared `readonly` by the WHATWG DOM spec; xmldom does
2975
+ * not enforce this constraint — direct property writes succeed and the written value is
2976
+ * serialized verbatim. When serialized with `requireWellFormed: true`, the serializer
2977
+ * validates the value against the XML `Name` production and throws `InvalidStateError` if it
2978
+ * does not match.
2979
+ * @property {string} publicId
2980
+ * The external subset public identifier, stored verbatim (including surrounding quotes).
2981
+ * Declared `readonly` by the WHATWG DOM spec; xmldom does not enforce this constraint —
2982
+ * direct property writes succeed and the written value is serialized verbatim.
2983
+ * When serialized with `requireWellFormed: true`, the serializer validates the value against
2984
+ * the XML `PubidLiteral` production and throws `InvalidStateError` if it does not match.
2985
+ * @property {string} systemId
2986
+ * The external subset system identifier, stored verbatim (including surrounding quotes).
2987
+ * Declared `readonly` by the WHATWG DOM spec; xmldom does not enforce this constraint —
2988
+ * direct property writes succeed and the written value is serialized verbatim.
2989
+ * When serialized with `requireWellFormed: true`, the serializer validates the value against
2990
+ * the XML `SystemLiteral` production and throws `InvalidStateError` if it does not match.
2991
+ * @property {string} internalSubset
2992
+ * The internal subset string (the raw content between `[` and `]`), or an empty string.
2993
+ * Declared `readonly` by the WHATWG DOM spec; xmldom does not enforce this constraint —
2994
+ * direct property writes succeed and the written value is serialized verbatim.
2995
+ * When serialized with `requireWellFormed: true`, the serializer throws `InvalidStateError`
2996
+ * if the value contains `"]>"`.
2997
+ * @see https://developer.mozilla.org/docs/Web/API/DocumentType MDN
2998
+ * @see https://dom.spec.whatwg.org/#interface-documenttype WHATWG DOM
2999
+ * @prettierignore
3000
+ */
3001
+ function DocumentType(symbol) {
3002
+ checkSymbol(symbol);
3003
+ }
3004
+ DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
3005
+ _extends(DocumentType, Node);
3006
+
3007
+ function Notation(symbol) {
3008
+ checkSymbol(symbol);
3009
+ }
3010
+ Notation.prototype.nodeType = NOTATION_NODE;
3011
+ _extends(Notation, Node);
3012
+
3013
+ function Entity(symbol) {
3014
+ checkSymbol(symbol);
3015
+ }
3016
+ Entity.prototype.nodeType = ENTITY_NODE;
3017
+ _extends(Entity, Node);
3018
+
3019
+ /**
3020
+ * Represents an EntityReference node, serialized as `&nodeName;`.
3021
+ *
3022
+ * `nodeName` is the referenced entity's name, stored verbatim. When serialized with
3023
+ * `requireWellFormed: true`, the serializer validates `nodeName` against the XML `Name`
3024
+ * production and throws `InvalidStateError` if it does not match; without that option the name
3025
+ * is emitted verbatim between `&` and `;`.
3026
+ *
3027
+ * __This implementation differs from the specification:__ xmldom does not expand entities —
3028
+ * the parser resolves entity references inline and never constructs `EntityReference` nodes,
3029
+ * so the only producer is {@link Document#createEntityReference}.
3030
+ *
3031
+ * @class
3032
+ * @see https://www.w3.org/TR/xml/#NT-Name
3033
+ */
3034
+ function EntityReference(symbol) {
3035
+ checkSymbol(symbol);
3036
+ }
3037
+ EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
3038
+ _extends(EntityReference, Node);
3039
+
3040
+ function DocumentFragment(symbol) {
3041
+ checkSymbol(symbol);
3042
+ }
3043
+ DocumentFragment.prototype.nodeName = '#document-fragment';
3044
+ DocumentFragment.prototype.nodeType = DOCUMENT_FRAGMENT_NODE;
3045
+ _extends(DocumentFragment, Node);
3046
+
3047
+ function ProcessingInstruction(symbol) {
3048
+ checkSymbol(symbol);
3049
+ }
3050
+ ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
3051
+ _extends(ProcessingInstruction, CharacterData);
3052
+ function XMLSerializer() {}
3053
+ /**
3054
+ * Returns the result of serializing `node` to XML.
3055
+ *
3056
+ * When `options.requireWellFormed` is `true`, the serializer throws `InvalidStateError` for
3057
+ * content that would produce ill-formed XML (e.g. CDATASection data containing `"]]>"`, Text
3058
+ * data containing characters outside the XML Char production, or a Document with no
3059
+ * `documentElement`).
3060
+ *
3061
+ * When `options.splitCDATASections` is `false`, CDATASection data is emitted verbatim even
3062
+ * when it contains `"]]>"`. When `true` (the default), `"]]>"` sequences are split across
3063
+ * concatenated CDATA sections — this behavior is **deprecated** and will be removed in the
3064
+ * next breaking release. Callers should migrate to `{ requireWellFormed: true }`, which throws
3065
+ * `InvalidStateError` instead of transforming.
3066
+ *
3067
+ * __This implementation differs from the specification:__ - CDATASection serialization is not
3068
+ * specified by W3C DOM Parsing or WHATWG DOM Parsing (see
3069
+ * {@link https://github.com/w3c/DOM-Parsing/issues/38 w3c/DOM-Parsing#38}).
3070
+ * When `splitCDATASections` is `true` (the default), `"]]>"` sequences in CDATASection data
3071
+ * are split across concatenated CDATA sections — this mechanism is derived from DOM Level 3
3072
+ * Core and is **deprecated**. The split mechanics will be removed in the next breaking
3073
+ * release. Callers that rely on this behavior should migrate to `{ requireWellFormed: true }`.
3074
+ * - W3C DOM Parsing §3.2.1.1 requires well-formedness checks on Element `localName`s,
3075
+ * prefixes,
3076
+ * and attribute serialization (duplicate attributes, namespace declarations, attribute value
3077
+ * characters) when `requireWellFormed` is `true`. Element and attribute qualified names (which
3078
+ * cover the namespace prefix) are validated against the XML `QName` production; the remaining
3079
+ * §3.2.1.1 checks (duplicate attributes, namespace-declaration consistency) and creation-time
3080
+ * name validation are **not implemented** in this release — see the tracking issue filed
3081
+ * against the next breaking milestone.
3082
+ *
3083
+ * @param {Node} node
3084
+ * @param {Object | function} [options]
3085
+ * Options object, or a legacy nodeFilter function (backward compatible).
3086
+ * @param {boolean} [options.requireWellFormed=false]
3087
+ * When `true`, throws `InvalidStateError` for content that would produce ill-formed XML.
3088
+ * @param {boolean} [options.splitCDATASections=true]
3089
+ * When `true` (default), splits `"]]>"` sequences in CDATASection data across concatenated
3090
+ * CDATA sections. **Deprecated** — will be removed in the next breaking release.
3091
+ * @param {function} [options.nodeFilter]
3092
+ * A filter function applied to each node before serialization.
3093
+ * @returns {string}
3094
+ * @throws {DOMException}
3095
+ * With name `InvalidStateError` when `requireWellFormed` is `true` and any of the following
3096
+ * conditions hold:
3097
+ * - an Element's qualified name (including any namespace prefix) is not a valid XML QName
3098
+ * - an attribute's qualified name (including a synthesized `xmlns:` namespace declaration) is
3099
+ * not a valid XML QName
3100
+ * - CDATASection data contains `"]]>"`
3101
+ * - Text data contains characters outside the XML Char production
3102
+ * - a Comment node's data contains `--` anywhere or ends with `-`
3103
+ * - a ProcessingInstruction's target is not a valid XML `NCName` (a `Name` with no colon) or is
3104
+ * an ASCII case-insensitive match for `"xml"`, or its data contains `?>` or characters outside
3105
+ * the XML Char production
3106
+ * - a DocumentType's `name` is not a valid XML `Name` (XML 1.0 production [5])
3107
+ * - a DocumentType's `publicId` is non-empty and does not match the XML `PubidLiteral`
3108
+ * production (W3C DOM Parsing §3.2.1.3; XML 1.0 production [12])
3109
+ * - a DocumentType's `systemId` is non-empty and does not match the XML `SystemLiteral`
3110
+ * production (W3C DOM Parsing §3.2.1.3; XML 1.0 production [11])
3111
+ * - a DocumentType's `internalSubset` contains `"]>"`
3112
+ * - an EntityReference's `nodeName` is not a valid XML `Name` (XML 1.0 production [5])
3113
+ * - the Document has no `documentElement`
3114
+ * @see https://developer.mozilla.org/docs/Web/API/XMLSerializer/serializeToString
3115
+ * @see https://html.spec.whatwg.org/#dom-xmlserializer-serializetostring
3116
+ * @see https://github.com/w3c/DOM-Parsing/issues/84
3117
+ * @prettierignore
3118
+ */
3119
+ XMLSerializer.prototype.serializeToString = function (node, options) {
3120
+ return nodeSerializeToString.call(node, options);
3121
+ };
3122
+ Node.prototype.toString = nodeSerializeToString;
3123
+ function nodeSerializeToString(options) {
3124
+ // Normalize the user-supplied options into a single internal opts object so that the
3125
+ // internal serializer always works with a consistent shape rather than positional flags.
3126
+ var opts;
3127
+ if (typeof options === 'function') {
3128
+ opts = { requireWellFormed: false, splitCDATASections: true, nodeFilter: options };
3129
+ } else if (options != null) {
3130
+ opts = {
3131
+ requireWellFormed: !!options.requireWellFormed,
3132
+ splitCDATASections: options.splitCDATASections !== false,
3133
+ nodeFilter: options.nodeFilter || null,
3134
+ };
3135
+ } else {
3136
+ opts = { requireWellFormed: false, splitCDATASections: true, nodeFilter: null };
3137
+ }
3138
+ var buf = [];
3139
+ var refNode = (this.nodeType === DOCUMENT_NODE && this.documentElement) || this;
3140
+ var prefix = refNode.prefix;
3141
+ var uri = refNode.namespaceURI;
3142
+
3143
+ if (uri && prefix == null) {
3144
+ var prefix = refNode.lookupPrefix(uri);
3145
+ if (prefix == null) {
3146
+ var visibleNamespaces = [
3147
+ { namespace: uri, prefix: null },
3148
+ //{namespace:uri,prefix:''}
3149
+ ];
3150
+ }
3151
+ }
3152
+ serializeToString(this, buf, visibleNamespaces, opts);
3153
+ return buf.join('');
3154
+ }
3155
+
3156
+ function needNamespaceDefine(node, isHTML, visibleNamespaces) {
3157
+ var prefix = node.prefix || '';
3158
+ var uri = node.namespaceURI;
3159
+ // According to [Namespaces in XML 1.0](https://www.w3.org/TR/REC-xml-names/#ns-using) ,
3160
+ // and more specifically https://www.w3.org/TR/REC-xml-names/#nsc-NoPrefixUndecl :
3161
+ // > In a namespace declaration for a prefix [...], the attribute value MUST NOT be empty.
3162
+ // in a similar manner [Namespaces in XML 1.1](https://www.w3.org/TR/xml-names11/#ns-using)
3163
+ // and more specifically https://www.w3.org/TR/xml-names11/#nsc-NSDeclared :
3164
+ // > [...] Furthermore, the attribute value [...] must not be an empty string.
3165
+ // so serializing empty namespace value like xmlns:ds="" would produce an invalid XML document.
3166
+ if (!uri) {
3167
+ return false;
3168
+ }
3169
+ if ((prefix === 'xml' && uri === NAMESPACE.XML) || uri === NAMESPACE.XMLNS) {
3170
+ return false;
3171
+ }
3172
+
3173
+ var i = visibleNamespaces.length;
3174
+ while (i--) {
3175
+ var ns = visibleNamespaces[i];
3176
+ // get namespace prefix
3177
+ if (ns.prefix === prefix) {
3178
+ return ns.namespace !== uri;
3179
+ }
3180
+ }
3181
+ return true;
3182
+ }
3183
+ /**
3184
+ * Literal whitespace other than space that appear in attribute values are serialized as
3185
+ * their entity references, so they will be preserved.
3186
+ * (In contrast to whitespace literals in the input which are normalized to spaces).
3187
+ *
3188
+ * Well-formed constraint: No < in Attribute Values:
3189
+ * > The replacement text of any entity referred to directly or indirectly
3190
+ * > in an attribute value must not contain a <.
3191
+ *
3192
+ * @see https://www.w3.org/TR/xml11/#CleanAttrVals
3193
+ * @see https://www.w3.org/TR/xml11/#NT-AttValue
3194
+ * @see https://www.w3.org/TR/xml11/#AVNormalize
3195
+ * @see https://w3c.github.io/DOM-Parsing/#serializing-an-element-s-attributes
3196
+ * @prettierignore
3197
+ */
3198
+ function addSerializedAttribute(buf, qualifiedName, value, requireWellFormed) {
3199
+ if (requireWellFormed && !g.QName_exact.test(qualifiedName)) {
3200
+ throw new DOMException(
3201
+ 'The attribute name "' + qualifiedName + '" is not a valid XML QName',
3202
+ DOMExceptionName.InvalidStateError
3203
+ );
3204
+ }
3205
+ buf.push(' ', qualifiedName, '="', value.replace(/[<>&"\t\n\r]/g, _xmlEncoder), '"');
3206
+ }
3207
+
3208
+ function serializeToString(node, buf, visibleNamespaces, opts) {
3209
+ if (!visibleNamespaces) {
3210
+ visibleNamespaces = [];
3211
+ }
3212
+ var nodeFilter = opts.nodeFilter;
3213
+ var requireWellFormed = opts.requireWellFormed;
3214
+ var splitCDATASections = opts.splitCDATASections;
3215
+ var doc = node.nodeType === DOCUMENT_NODE ? node : node.ownerDocument;
3216
+ var isHTML = doc.type === 'html';
3217
+
3218
+ walkDOM(
3219
+ node,
3220
+ { ns: visibleNamespaces },
3221
+ {
3222
+ enter: function (n, ctx) {
3223
+ var namespaces = ctx.ns;
3224
+
3225
+ if (nodeFilter) {
3226
+ n = nodeFilter(n);
3227
+ if (n) {
3228
+ if (typeof n == 'string') {
3229
+ buf.push(n);
3230
+ return null;
3231
+ }
3232
+ } else {
3233
+ return null;
3234
+ }
3235
+ }
3236
+
3237
+ switch (n.nodeType) {
3238
+ case ELEMENT_NODE:
3239
+ var attrs = n.attributes;
3240
+ var len = attrs.length;
3241
+ var nodeName = n.tagName;
3242
+
3243
+ var prefixedNodeName = nodeName;
3244
+ if (!isHTML && !n.prefix && n.namespaceURI) {
3245
+ var defaultNS;
3246
+ // lookup current default ns from `xmlns` attribute
3247
+ for (var ai = 0; ai < attrs.length; ai++) {
3248
+ if (attrs.item(ai).name === 'xmlns') {
3249
+ defaultNS = attrs.item(ai).value;
3250
+ break;
3251
+ }
3252
+ }
3253
+ if (!defaultNS) {
3254
+ // lookup current default ns in visibleNamespaces
3255
+ for (var nsi = namespaces.length - 1; nsi >= 0; nsi--) {
3256
+ var nsEntry = namespaces[nsi];
3257
+ if (nsEntry.prefix === '' && nsEntry.namespace === n.namespaceURI) {
3258
+ defaultNS = nsEntry.namespace;
3259
+ break;
3260
+ }
3261
+ }
3262
+ }
3263
+ if (defaultNS !== n.namespaceURI) {
3264
+ for (var nsi = namespaces.length - 1; nsi >= 0; nsi--) {
3265
+ var nsEntry = namespaces[nsi];
3266
+ if (nsEntry.namespace === n.namespaceURI) {
3267
+ if (nsEntry.prefix) {
3268
+ prefixedNodeName = nsEntry.prefix + ':' + nodeName;
3269
+ }
3270
+ break;
3271
+ }
3272
+ }
3273
+ }
3274
+ }
3275
+
3276
+ if (requireWellFormed && !g.QName_exact.test(prefixedNodeName)) {
3277
+ throw new DOMException(
3278
+ 'The element name "' + prefixedNodeName + '" is not a valid XML QName',
3279
+ DOMExceptionName.InvalidStateError
3280
+ );
3281
+ }
3282
+
3283
+ buf.push('<', prefixedNodeName);
3284
+
3285
+ // Build a fresh namespace snapshot for this element's children.
3286
+ // The slice prevents sibling elements from inheriting each other's declarations.
3287
+ var childNamespaces = namespaces.slice();
3288
+
3289
+ for (var i = 0; i < len; i++) {
3290
+ // add namespaces for attributes
3291
+ var attr = attrs.item(i);
3292
+ if (attr.prefix == 'xmlns') {
3293
+ childNamespaces.push({
3294
+ prefix: attr.localName,
3295
+ namespace: attr.value,
3296
+ });
3297
+ } else if (attr.nodeName == 'xmlns') {
3298
+ childNamespaces.push({ prefix: '', namespace: attr.value });
3299
+ }
3300
+ }
3301
+
3302
+ for (var i = 0; i < len; i++) {
3303
+ var attr = attrs.item(i);
3304
+ if (needNamespaceDefine(attr, isHTML, childNamespaces)) {
3305
+ var attrPrefix = attr.prefix || '';
3306
+ var uri = attr.namespaceURI;
3307
+ addSerializedAttribute(buf, attrPrefix ? 'xmlns:' + attrPrefix : 'xmlns', uri, requireWellFormed);
3308
+ childNamespaces.push({ prefix: attrPrefix, namespace: uri });
3309
+ }
3310
+ // Apply nodeFilter and serialize the attribute.
3311
+ var filteredAttr = nodeFilter ? nodeFilter(attr) : attr;
3312
+ if (filteredAttr) {
3313
+ if (typeof filteredAttr === 'string') {
3314
+ buf.push(filteredAttr);
3315
+ } else {
3316
+ addSerializedAttribute(buf, filteredAttr.name, filteredAttr.value, requireWellFormed);
3317
+ }
3318
+ }
3319
+ }
3320
+
3321
+ // add namespace for current node
3322
+ if (nodeName === prefixedNodeName && needNamespaceDefine(n, isHTML, childNamespaces)) {
3323
+ var nodePrefix = n.prefix || '';
3324
+ var uri = n.namespaceURI;
3325
+ addSerializedAttribute(buf, nodePrefix ? 'xmlns:' + nodePrefix : 'xmlns', uri, requireWellFormed);
3326
+ childNamespaces.push({ prefix: nodePrefix, namespace: uri });
3327
+ }
3328
+
3329
+ // in XML elements can be closed when they have no children
3330
+ var canCloseTag = !n.firstChild;
3331
+ if (canCloseTag && (isHTML || n.namespaceURI === NAMESPACE.HTML)) {
3332
+ // in HTML (doc or ns) only void elements can be closed right away
3333
+ canCloseTag = isHTMLVoidElement(nodeName);
3334
+ }
3335
+ if (canCloseTag) {
3336
+ buf.push('/>');
3337
+ // Self-closing: no children and no closing tag needed from exit.
3338
+ return null;
3339
+ }
3340
+
3341
+ buf.push('>');
3342
+
3343
+ // HTML raw text elements: serialize children as raw data without further descent.
3344
+ if (isHTML && isHTMLRawTextElement(nodeName)) {
3345
+ var child = n.firstChild;
3346
+ while (child) {
3347
+ if (child.data) {
3348
+ buf.push(child.data);
3349
+ } else {
3350
+ serializeToString(child, buf, childNamespaces.slice(), opts);
3351
+ }
3352
+ child = child.nextSibling;
3353
+ }
3354
+ buf.push('</', prefixedNodeName, '>');
3355
+ // Children handled manually above; prevent walkDOM from also traversing them.
3356
+ return null;
3357
+ }
3358
+
3359
+ // Return child context so walkDOM descends; exit will emit the closing tag.
3360
+ return { ns: childNamespaces, tag: prefixedNodeName };
3361
+ case DOCUMENT_NODE:
3362
+ case DOCUMENT_FRAGMENT_NODE:
3363
+ if (requireWellFormed && n.nodeType === DOCUMENT_NODE && n.documentElement == null) {
3364
+ throw new DOMException('The Document has no documentElement', DOMExceptionName.InvalidStateError);
3365
+ }
3366
+ // Pass namespaces through; each child element will slice independently.
3367
+ return { ns: namespaces };
3368
+ case ATTRIBUTE_NODE:
3369
+ addSerializedAttribute(buf, n.name, n.value, requireWellFormed);
3370
+ return null;
3371
+ case TEXT_NODE:
3372
+ /*
3373
+ * The ampersand character (&) and the left angle bracket (<) must not appear in their literal form,
3374
+ * except when used as markup delimiters, or within a comment, a processing instruction,
3375
+ * or a CDATA section.
3376
+ * If they are needed elsewhere, they must be escaped using either numeric character
3377
+ * references or the strings `&amp;` and `&lt;` respectively.
3378
+ * The right angle bracket (>) may be represented using the string " &gt; ",
3379
+ * and must, for compatibility, be escaped using either `&gt;`,
3380
+ * or a character reference when it appears in the string `]]>` in content,
3381
+ * when that string is not marking the end of a CDATA section.
3382
+ *
3383
+ * In the content of elements, character data is any string of characters which does not
3384
+ * contain the start-delimiter of any markup and does not include the CDATA-section-close
3385
+ * delimiter, `]]>`.
3386
+ *
3387
+ * @see https://www.w3.org/TR/xml/#NT-CharData
3388
+ * @see https://w3c.github.io/DOM-Parsing/#xml-serializing-a-text-node
3389
+ */
3390
+ if (requireWellFormed && g.InvalidChar.test(n.data)) {
3391
+ throw new DOMException(
3392
+ 'The Text node data contains characters outside the XML Char production',
3393
+ DOMExceptionName.InvalidStateError
3394
+ );
3395
+ }
3396
+ buf.push(n.data.replace(/[<&>]/g, _xmlEncoder));
3397
+ return null;
3398
+ case CDATA_SECTION_NODE:
3399
+ if (requireWellFormed && n.data.indexOf(']]>') !== -1) {
3400
+ throw new DOMException('The CDATASection data contains "]]>"', DOMExceptionName.InvalidStateError);
3401
+ }
3402
+ if (splitCDATASections) {
3403
+ buf.push(g.CDATA_START, n.data.replace(/]]>/g, ']]]]><![CDATA[>'), g.CDATA_END);
3404
+ } else {
3405
+ buf.push(g.CDATA_START, n.data, g.CDATA_END);
3406
+ }
3407
+ return null;
3408
+ case COMMENT_NODE:
3409
+ if (requireWellFormed) {
3410
+ if (g.InvalidChar.test(n.data)) {
3411
+ throw new DOMException(
3412
+ 'The comment node data contains characters outside the XML Char production',
3413
+ DOMExceptionName.InvalidStateError
3414
+ );
3415
+ }
3416
+ if (n.data.indexOf('--') !== -1 || n.data[n.data.length - 1] === '-') {
3417
+ throw new DOMException(
3418
+ 'The comment node data contains "--" or ends with "-"',
3419
+ DOMExceptionName.InvalidStateError
3420
+ );
3421
+ }
3422
+ }
3423
+ buf.push(g.COMMENT_START, n.data, g.COMMENT_END);
3424
+ return null;
3425
+ case DOCUMENT_TYPE_NODE:
3426
+ var pubid = n.publicId;
3427
+ var sysid = n.systemId;
3428
+ if (requireWellFormed) {
3429
+ if (!g.Name_exact.test(n.name)) {
3430
+ throw new DOMException(
3431
+ 'The doctype name "' + n.name + '" is not a valid XML Name',
3432
+ DOMExceptionName.InvalidStateError
3433
+ );
3434
+ }
3435
+ if (pubid && !g.PubidLiteral_match.test(pubid)) {
3436
+ throw new DOMException('DocumentType publicId is not a valid PubidLiteral', DOMExceptionName.InvalidStateError);
3437
+ }
3438
+ if (sysid && sysid !== '.' && !g.SystemLiteral_match.test(sysid)) {
3439
+ throw new DOMException('DocumentType systemId is not a valid SystemLiteral', DOMExceptionName.InvalidStateError);
3440
+ }
3441
+ if (n.internalSubset && n.internalSubset.indexOf(']>') !== -1) {
3442
+ throw new DOMException('DocumentType internalSubset contains "]>"', DOMExceptionName.InvalidStateError);
3443
+ }
3444
+ }
3445
+ buf.push(g.DOCTYPE_DECL_START, ' ', n.name);
3446
+ if (pubid) {
3447
+ buf.push(' ', g.PUBLIC, ' ', pubid);
3448
+ if (sysid && sysid !== '.') {
3449
+ buf.push(' ', sysid);
3450
+ }
3451
+ } else if (sysid && sysid !== '.') {
3452
+ buf.push(' ', g.SYSTEM, ' ', sysid);
3453
+ }
3454
+ if (n.internalSubset) {
3455
+ buf.push(' [', n.internalSubset, ']');
3456
+ }
3457
+ buf.push('>');
3458
+ return null;
3459
+ case PROCESSING_INSTRUCTION_NODE:
3460
+ if (requireWellFormed) {
3461
+ if (!g.NCName_exact.test(n.target) || n.target.toLowerCase() === 'xml') {
3462
+ throw new DOMException(
3463
+ 'The processing instruction target "' + n.target + '" is not a valid XML NCName or is reserved',
3464
+ DOMExceptionName.InvalidStateError
3465
+ );
3466
+ }
3467
+ if (g.InvalidChar.test(n.data)) {
3468
+ throw new DOMException(
3469
+ 'The ProcessingInstruction data contains characters outside the XML Char production',
3470
+ DOMExceptionName.InvalidStateError
3471
+ );
3472
+ }
3473
+ if (n.data.indexOf('?>') !== -1) {
3474
+ throw new DOMException('The ProcessingInstruction data contains "?>"', DOMExceptionName.InvalidStateError);
3475
+ }
3476
+ }
3477
+ buf.push('<?', n.target, ' ', n.data, '?>');
3478
+ return null;
3479
+ case ENTITY_REFERENCE_NODE:
3480
+ if (requireWellFormed && !g.Name_exact.test(n.nodeName)) {
3481
+ throw new DOMException(
3482
+ 'The entity reference name "' + n.nodeName + '" is not a valid XML Name',
3483
+ DOMExceptionName.InvalidStateError
3484
+ );
3485
+ }
3486
+ buf.push('&', n.nodeName, ';');
3487
+ return null;
3488
+ //case ENTITY_NODE:
3489
+ //case NOTATION_NODE:
3490
+ default:
3491
+ buf.push('??', n.nodeName);
3492
+ return null;
3493
+ }
3494
+ },
3495
+ exit: function (n, childCtx) {
3496
+ // Emit the closing tag for elements that were opened (not self-closed, not raw text).
3497
+ if (childCtx && childCtx.tag) {
3498
+ buf.push('</', childCtx.tag, '>');
3499
+ }
3500
+ },
3501
+ }
3502
+ );
3503
+ }
3504
+ /**
3505
+ * Imports a node from a different document into `doc`, creating a new copy.
3506
+ * Delegates to {@link walkDOM} for traversal. Each node in the subtree is shallow-cloned,
3507
+ * stamped with `doc` as its `ownerDocument`, and detached (`parentNode` set to `null`).
3508
+ * Children are imported recursively when `deep` is `true`; for {@link Attr} nodes `deep` is
3509
+ * always forced to `true`
3510
+ * because an attribute's value lives in a child text node.
3511
+ *
3512
+ * @param {Document} doc
3513
+ * The document that will own the imported node.
3514
+ * @param {Node} node
3515
+ * The node to import.
3516
+ * @param {boolean} deep
3517
+ * If `true`, descendants are imported recursively.
3518
+ * @returns {Node}
3519
+ * The newly imported node, now owned by `doc`.
3520
+ */
3521
+ function importNode(doc, node, deep) {
3522
+ var destRoot;
3523
+ walkDOM(node, null, {
3524
+ enter: function (srcNode, destParent) {
3525
+ // Shallow-clone the node and stamp it into the target document.
3526
+ var destNode = srcNode.cloneNode(false);
3527
+ destNode.ownerDocument = doc;
3528
+ destNode.parentNode = null;
3529
+ // capture as the root of the imported subtree or attach to parent.
3530
+ if (destParent === null) {
3531
+ destRoot = destNode;
3532
+ } else {
3533
+ destParent.appendChild(destNode);
3534
+ }
3535
+ // ATTRIBUTE_NODE must always be imported deeply: its value lives in a child text node.
3536
+ var shouldDeep = srcNode.nodeType === ATTRIBUTE_NODE || deep;
3537
+ return shouldDeep ? destNode : null;
3538
+ },
3539
+ });
3540
+ return destRoot;
3541
+ }
3542
+
3543
+ /**
3544
+ * Creates a copy of a node from an existing one.
3545
+ *
3546
+ * @param {Document} doc
3547
+ * The Document object representing the document that the new node will belong to.
3548
+ * @param {Node} node
3549
+ * The node to clone.
3550
+ * @param {boolean} deep
3551
+ * If true, the contents of the node are recursively copied.
3552
+ * If false, only the node itself (and its attributes, if it is an element) are copied.
3553
+ * @returns {Node}
3554
+ * Returns the newly created copy of the node.
3555
+ * @throws {DOMException}
3556
+ * May throw a DOMException if operations within setAttributeNode or appendChild (which are
3557
+ * potentially invoked in this function) do not meet their specific constraints.
3558
+ */
3559
+ function cloneNode(doc, node, deep) {
3560
+ var destRoot;
3561
+ walkDOM(node, null, {
3562
+ enter: function (srcNode, destParent) {
3563
+ // 1. Create a blank node of the same type and copy all scalar own properties.
3564
+ var destNode = new srcNode.constructor(PDC);
3565
+ for (var n in srcNode) {
3566
+ if (hasOwn(srcNode, n)) {
3567
+ var v = srcNode[n];
3568
+ if (typeof v != 'object') {
3569
+ if (v != destNode[n]) {
3570
+ destNode[n] = v;
3571
+ }
3572
+ }
3573
+ }
3574
+ }
3575
+ if (srcNode.childNodes) {
3576
+ destNode.childNodes = new NodeList();
3577
+ }
3578
+ destNode.ownerDocument = doc;
3579
+ // 2. Handle node-type-specific setup.
3580
+ // Attributes are not DOM children, so they are cloned inline here
3581
+ // rather than by walkDOM descent.
3582
+ // ATTRIBUTE_NODE forces deep=true so its own children are walked.
3583
+ var shouldDeep = deep;
3584
+ switch (destNode.nodeType) {
3585
+ case ELEMENT_NODE:
3586
+ var attrs = srcNode.attributes;
3587
+ var attrs2 = (destNode.attributes = new NamedNodeMap());
3588
+ var len = attrs.length;
3589
+ attrs2._ownerElement = destNode;
3590
+ for (var i = 0; i < len; i++) {
3591
+ destNode.setAttributeNode(cloneNode(doc, attrs.item(i), true));
3592
+ }
3593
+ break;
3594
+ case ATTRIBUTE_NODE:
3595
+ shouldDeep = true;
3596
+ }
3597
+ // 3. Attach to parent, or capture as the root of the cloned subtree.
3598
+ if (destParent !== null) {
3599
+ destParent.appendChild(destNode);
3600
+ } else {
3601
+ destRoot = destNode;
3602
+ }
3603
+ // 4. Return destNode as the context for children (causes walkDOM to descend),
3604
+ // or null to skip children (shallow clone).
3605
+ return shouldDeep ? destNode : null;
3606
+ },
3607
+ });
3608
+ return destRoot;
3609
+ }
3610
+
3611
+ function __set__(object, key, value) {
3612
+ object[key] = value;
3613
+ }
3614
+
3615
+ // Returns a new array of direct Element children.
3616
+ // Passed to LiveNodeList to implement ParentNode.children.
3617
+ // https://dom.spec.whatwg.org/#dom-parentnode-children
3618
+ function childrenRefresh(node) {
3619
+ var ls = [];
3620
+ var child = node.firstChild;
3621
+ while (child) {
3622
+ if (child.nodeType === ELEMENT_NODE) {
3623
+ ls.push(child);
3624
+ }
3625
+ child = child.nextSibling;
3626
+ }
3627
+ return ls;
3628
+ }
3629
+
3630
+ //do dynamic
3631
+ try {
3632
+ if (Object.defineProperty) {
3633
+ Object.defineProperty(LiveNodeList.prototype, 'length', {
3634
+ get: function () {
3635
+ _updateLiveList(this);
3636
+ return this.$$length;
3637
+ },
3638
+ });
3639
+
3640
+ /**
3641
+ * The text content of this node and its descendants.
3642
+ *
3643
+ * For {@link Element} and {@link DocumentFragment} nodes, returns the concatenation of the
3644
+ * `nodeValue` of every descendant text node, excluding processing instruction and comment
3645
+ * nodes. For all other node types, returns `nodeValue`.
3646
+ *
3647
+ * Setting `textContent` on an element or document fragment replaces all child nodes with a
3648
+ * single text node; on other nodes it sets `data`, `value`, and `nodeValue` directly.
3649
+ *
3650
+ * @type {string | null}
3651
+ * @see {@link https://dom.spec.whatwg.org/#dom-node-textcontent}
3652
+ */
3653
+ Object.defineProperty(Node.prototype, 'textContent', {
3654
+ get: function () {
3655
+ if (this.nodeType === ELEMENT_NODE || this.nodeType === DOCUMENT_FRAGMENT_NODE) {
3656
+ var buf = [];
3657
+ walkDOM(this, null, {
3658
+ enter: function (n) {
3659
+ if (n.nodeType === ELEMENT_NODE || n.nodeType === DOCUMENT_FRAGMENT_NODE) {
3660
+ return true; // enter children
3661
+ }
3662
+ if (n.nodeType === PROCESSING_INSTRUCTION_NODE || n.nodeType === COMMENT_NODE) {
3663
+ return null; // excluded from text content
3664
+ }
3665
+ buf.push(n.nodeValue);
3666
+ },
3667
+ });
3668
+ return buf.join('');
3669
+ }
3670
+ return this.nodeValue;
3671
+ },
3672
+
3673
+ set: function (data) {
3674
+ switch (this.nodeType) {
3675
+ case ELEMENT_NODE:
3676
+ case DOCUMENT_FRAGMENT_NODE:
3677
+ while (this.firstChild) {
3678
+ this.removeChild(this.firstChild);
3679
+ }
3680
+ if (data || String(data)) {
3681
+ this.appendChild(this.ownerDocument.createTextNode(data));
3682
+ }
3683
+ break;
3684
+
3685
+ default:
3686
+ this.data = data;
3687
+ this.value = data;
3688
+ this.nodeValue = data;
3689
+ }
3690
+ },
3691
+ });
3692
+
3693
+ Object.defineProperty(CharacterData.prototype, 'data', {
3694
+ get: function () {
3695
+ return this._data != null ? this._data : '';
3696
+ },
3697
+ set: function (v) {
3698
+ this._data = v;
3699
+ this.length = typeof v === 'string' ? v.length : 0;
3700
+ },
3701
+ });
3702
+
3703
+ Object.defineProperty(CharacterData.prototype, 'nodeValue', {
3704
+ get: function () {
3705
+ return this.data;
3706
+ },
3707
+ set: function (v) {
3708
+ this.data = v;
3709
+ },
3710
+ enumerable: true,
3711
+ configurable: true,
3712
+ });
3713
+
3714
+ Object.defineProperty(Element.prototype, 'children', {
3715
+ get: function () {
3716
+ return new LiveNodeList(this, childrenRefresh);
3717
+ },
3718
+ });
3719
+ Object.defineProperty(Document.prototype, 'children', {
3720
+ get: function () {
3721
+ return new LiveNodeList(this, childrenRefresh);
3722
+ },
3723
+ });
3724
+ Object.defineProperty(DocumentFragment.prototype, 'children', {
3725
+ get: function () {
3726
+ return new LiveNodeList(this, childrenRefresh);
3727
+ },
3728
+ });
3729
+
3730
+ __set__ = function (object, key, value) {
3731
+ //console.log(value)
3732
+ object['$$' + key] = value;
3733
+ };
3734
+ }
3735
+ } catch (e) {
3736
+ //ie8
3737
+ }
3738
+
3739
+ exports._updateLiveList = _updateLiveList;
3740
+ exports.Attr = Attr;
3741
+ exports.CDATASection = CDATASection;
3742
+ exports.CharacterData = CharacterData;
3743
+ exports.Comment = Comment;
3744
+ exports.Document = Document;
3745
+ exports.DocumentFragment = DocumentFragment;
3746
+ exports.DocumentType = DocumentType;
3747
+ exports.DOMImplementation = DOMImplementation;
3748
+ exports.Element = Element;
3749
+ exports.Entity = Entity;
3750
+ exports.EntityReference = EntityReference;
3751
+ exports.LiveNodeList = LiveNodeList;
3752
+ exports.NamedNodeMap = NamedNodeMap;
3753
+ exports.Node = Node;
3754
+ exports.NodeList = NodeList;
3755
+ exports.Notation = Notation;
3756
+ exports.Text = Text;
3757
+ exports.ProcessingInstruction = ProcessingInstruction;
3758
+ exports.walkDOM = walkDOM;
3759
+ exports.XMLSerializer = XMLSerializer;