@xmldom/xmldom 0.9.8 → 0.9.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +234 -70
- package/index.d.ts +186 -17
- package/lib/dom.js +757 -338
- package/lib/grammar.js +14 -0
- package/package.json +15 -11
- package/readme.md +7 -0
package/lib/dom.js
CHANGED
|
@@ -330,14 +330,31 @@ NodeList.prototype = {
|
|
|
330
330
|
/**
|
|
331
331
|
* Returns a string representation of the NodeList.
|
|
332
332
|
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
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]
|
|
335
341
|
* @returns {string}
|
|
336
|
-
* A string representation of the NodeList.
|
|
337
342
|
*/
|
|
338
|
-
toString: function (
|
|
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
|
+
}
|
|
339
356
|
for (var buf = [], i = 0; i < this.length; i++) {
|
|
340
|
-
serializeToString(this[i], buf,
|
|
357
|
+
serializeToString(this[i], buf, null, opts);
|
|
341
358
|
}
|
|
342
359
|
return buf.join('');
|
|
343
360
|
},
|
|
@@ -866,11 +883,21 @@ DOMImplementation.prototype = {
|
|
|
866
883
|
* The {@link https://www.w3.org/TR/DOM-Level-3-Core/glossary.html#dt-qualifiedname qualified
|
|
867
884
|
* name} of the document type to be created.
|
|
868
885
|
* @param {string} [publicId]
|
|
869
|
-
* The external subset public identifier.
|
|
886
|
+
* The external subset public identifier. Stored verbatim including surrounding quotes.
|
|
887
|
+
* When serialized with `requireWellFormed: true`, the serializer throws `InvalidStateError`
|
|
888
|
+
* if the value is non-empty and does not match the XML `PubidLiteral` production
|
|
889
|
+
* (W3C DOM Parsing §3.2.1.3; XML 1.0 production [12]). Creation-time validation is not
|
|
890
|
+
* enforced — deferred to a future breaking release.
|
|
870
891
|
* @param {string} [systemId]
|
|
871
|
-
* The external subset system identifier.
|
|
892
|
+
* The external subset system identifier. Stored verbatim including surrounding quotes.
|
|
893
|
+
* When serialized with `requireWellFormed: true`, the serializer throws `InvalidStateError`
|
|
894
|
+
* if the value is non-empty and does not match the XML `SystemLiteral` production
|
|
895
|
+
* (W3C DOM Parsing §3.2.1.3; XML 1.0 production [11]). Creation-time validation is not
|
|
896
|
+
* enforced — deferred to a future breaking release.
|
|
872
897
|
* @param {string} [internalSubset]
|
|
873
|
-
*
|
|
898
|
+
* The internal subset or an empty string if it is not present. Stored verbatim.
|
|
899
|
+
* When serialized with `requireWellFormed: true`, the serializer throws `InvalidStateError`
|
|
900
|
+
* if the value contains `"]>"`. Creation-time validation is not enforced.
|
|
874
901
|
* @returns {DocumentType}
|
|
875
902
|
* A new {@link DocumentType} node with {@link Node#ownerDocument} set to null.
|
|
876
903
|
* @throws {DOMException}
|
|
@@ -1082,7 +1109,7 @@ Node.prototype = {
|
|
|
1082
1109
|
var parent = other;
|
|
1083
1110
|
do {
|
|
1084
1111
|
if (this === parent) return true;
|
|
1085
|
-
parent =
|
|
1112
|
+
parent = parent.parentNode;
|
|
1086
1113
|
} while (parent);
|
|
1087
1114
|
return false;
|
|
1088
1115
|
},
|
|
@@ -1114,56 +1141,68 @@ Node.prototype = {
|
|
|
1114
1141
|
/**
|
|
1115
1142
|
* Checks whether the given node is equal to this node.
|
|
1116
1143
|
*
|
|
1144
|
+
* Two nodes are equal when they have the same type, defining characteristics (for the type),
|
|
1145
|
+
* and the same childNodes. The comparison is iterative to avoid stack overflows on
|
|
1146
|
+
* deeply-nested trees. Attribute nodes of each Element pair are also pushed onto the stack
|
|
1147
|
+
* and compared the same way.
|
|
1148
|
+
*
|
|
1117
1149
|
* @param {Node} [otherNode]
|
|
1150
|
+
* @returns {boolean}
|
|
1118
1151
|
* @see https://dom.spec.whatwg.org/#concept-node-equals
|
|
1152
|
+
* @see ../docs/walk-dom.md.
|
|
1119
1153
|
*/
|
|
1120
1154
|
isEqualNode: function (otherNode) {
|
|
1121
1155
|
if (!otherNode) return false;
|
|
1122
1156
|
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
if (
|
|
1139
|
-
|
|
1157
|
+
// Use an explicit {node, other} pair stack to avoid call-stack overflow on deep trees.
|
|
1158
|
+
// walkDOM cannot be used here — parallel two-tree traversal requires pairing
|
|
1159
|
+
// corresponding nodes at each step across both trees simultaneously.
|
|
1160
|
+
var stack = [{ node: this, other: otherNode }];
|
|
1161
|
+
while (stack.length > 0) {
|
|
1162
|
+
var pair = stack.pop();
|
|
1163
|
+
var node = pair.node;
|
|
1164
|
+
var other = pair.other;
|
|
1165
|
+
|
|
1166
|
+
if (node.nodeType !== other.nodeType) return false;
|
|
1167
|
+
|
|
1168
|
+
switch (node.nodeType) {
|
|
1169
|
+
case node.DOCUMENT_TYPE_NODE:
|
|
1170
|
+
if (node.name !== other.name) return false;
|
|
1171
|
+
if (node.publicId !== other.publicId) return false;
|
|
1172
|
+
if (node.systemId !== other.systemId) return false;
|
|
1173
|
+
break;
|
|
1174
|
+
case node.ELEMENT_NODE:
|
|
1175
|
+
if (node.namespaceURI !== other.namespaceURI) return false;
|
|
1176
|
+
if (node.prefix !== other.prefix) return false;
|
|
1177
|
+
if (node.localName !== other.localName) return false;
|
|
1178
|
+
if (node.attributes.length !== other.attributes.length) return false;
|
|
1179
|
+
for (var i = 0; i < node.attributes.length; i++) {
|
|
1180
|
+
var attr = node.attributes.item(i);
|
|
1181
|
+
var otherAttr = other.getAttributeNodeNS(attr.namespaceURI, attr.localName);
|
|
1182
|
+
if (!otherAttr) return false;
|
|
1183
|
+
stack.push({ node: attr, other: otherAttr });
|
|
1140
1184
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
if (this.data !== otherNode.data) return false;
|
|
1157
|
-
break;
|
|
1158
|
-
}
|
|
1185
|
+
break;
|
|
1186
|
+
case node.ATTRIBUTE_NODE:
|
|
1187
|
+
if (node.namespaceURI !== other.namespaceURI) return false;
|
|
1188
|
+
if (node.localName !== other.localName) return false;
|
|
1189
|
+
if (node.value !== other.value) return false;
|
|
1190
|
+
break;
|
|
1191
|
+
case node.PROCESSING_INSTRUCTION_NODE:
|
|
1192
|
+
if (node.target !== other.target || node.data !== other.data) return false;
|
|
1193
|
+
break;
|
|
1194
|
+
case node.TEXT_NODE:
|
|
1195
|
+
case node.CDATA_SECTION_NODE:
|
|
1196
|
+
case node.COMMENT_NODE:
|
|
1197
|
+
if (node.data !== other.data) return false;
|
|
1198
|
+
break;
|
|
1199
|
+
}
|
|
1159
1200
|
|
|
1160
|
-
|
|
1161
|
-
return false;
|
|
1162
|
-
}
|
|
1201
|
+
if (node.childNodes.length !== other.childNodes.length) return false;
|
|
1163
1202
|
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1203
|
+
// Push children in reverse order so index 0 is processed first (LIFO).
|
|
1204
|
+
for (var i = node.childNodes.length - 1; i >= 0; i--) {
|
|
1205
|
+
stack.push({ node: node.childNodes[i], other: other.childNodes[i] });
|
|
1167
1206
|
}
|
|
1168
1207
|
}
|
|
1169
1208
|
|
|
@@ -1283,7 +1322,7 @@ Node.prototype = {
|
|
|
1283
1322
|
* is `TEXT_NODE`) into a single node with the combined data. It also removes any empty text
|
|
1284
1323
|
* nodes.
|
|
1285
1324
|
*
|
|
1286
|
-
* This method
|
|
1325
|
+
* This method iterativly traverses all child nodes to normalize all descendent nodes within
|
|
1287
1326
|
* the subtree.
|
|
1288
1327
|
*
|
|
1289
1328
|
* @throws {DOMException}
|
|
@@ -1292,19 +1331,28 @@ Node.prototype = {
|
|
|
1292
1331
|
* @since Modified in DOM Level 2
|
|
1293
1332
|
* @see {@link Node.removeChild}
|
|
1294
1333
|
* @see {@link CharacterData.appendData}
|
|
1334
|
+
* @see ../docs/walk-dom.md.
|
|
1295
1335
|
*/
|
|
1296
1336
|
normalize: function () {
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
child.
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1337
|
+
walkDOM(this, null, {
|
|
1338
|
+
enter: function (node) {
|
|
1339
|
+
// Merge adjacent text children of node before walkDOM schedules them.
|
|
1340
|
+
// walkDOM reads lastChild/previousSibling after enter returns, so the
|
|
1341
|
+
// surviving post-merge children are what it descends into.
|
|
1342
|
+
var child = node.firstChild;
|
|
1343
|
+
while (child) {
|
|
1344
|
+
var next = child.nextSibling;
|
|
1345
|
+
if (next !== null && next.nodeType === TEXT_NODE && child.nodeType === TEXT_NODE) {
|
|
1346
|
+
node.removeChild(next);
|
|
1347
|
+
child.appendData(next.data);
|
|
1348
|
+
// Do not advance child: re-check new nextSibling for another text run
|
|
1349
|
+
} else {
|
|
1350
|
+
child = next;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
return true; // descend into surviving children
|
|
1354
|
+
},
|
|
1355
|
+
});
|
|
1308
1356
|
},
|
|
1309
1357
|
/**
|
|
1310
1358
|
* Checks whether the DOM implementation implements a specific feature and its version.
|
|
@@ -1521,24 +1569,112 @@ copy(DocumentPosition, Node);
|
|
|
1521
1569
|
copy(DocumentPosition, Node.prototype);
|
|
1522
1570
|
|
|
1523
1571
|
/**
|
|
1524
|
-
*
|
|
1525
|
-
*
|
|
1526
|
-
* @
|
|
1527
|
-
*
|
|
1572
|
+
* Visits every node in the subtree rooted at `node` in depth-first pre-order.
|
|
1573
|
+
*
|
|
1574
|
+
* Delegates to {@link walkDOM} for traversal. The `callback` is called on each node;
|
|
1575
|
+
* if it returns a truthy value, traversal stops immediately.
|
|
1576
|
+
*
|
|
1577
|
+
* @param {Node} node
|
|
1578
|
+
* Root of the subtree to visit.
|
|
1579
|
+
* @param {function(Node): *} callback
|
|
1580
|
+
* Called for each node. A truthy return value stops traversal early.
|
|
1528
1581
|
*/
|
|
1529
1582
|
function _visitNode(node, callback) {
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1583
|
+
walkDOM(node, null, {
|
|
1584
|
+
enter: function (n) {
|
|
1585
|
+
return callback(n) ? walkDOM.STOP : true;
|
|
1586
|
+
},
|
|
1587
|
+
});
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
/**
|
|
1591
|
+
* Depth-first pre/post-order DOM tree walker.
|
|
1592
|
+
*
|
|
1593
|
+
* Visits every node in the subtree rooted at `node`. For each node:
|
|
1594
|
+
*
|
|
1595
|
+
* 1. Calls `callbacks.enter(node, context)` before descending into the node's children. The
|
|
1596
|
+
* return value becomes the `context` passed to each child's `enter` call and to the matching
|
|
1597
|
+
* `exit` call.
|
|
1598
|
+
* 2. If `enter` returns `null` or `undefined`, the node's children are skipped;
|
|
1599
|
+
* sibling traversal continues normally.
|
|
1600
|
+
* 3. If `enter` returns `walkDOM.STOP`, the entire traversal is aborted immediately — no
|
|
1601
|
+
* further `enter` or `exit` calls are made.
|
|
1602
|
+
* 4. `lastChild` and `previousSibling` are read **after** `enter` returns, so `enter` may
|
|
1603
|
+
* safely modify the node's own child list before the walker descends. Modifying siblings of
|
|
1604
|
+
* the current node or any other part of the tree produces unpredictable results: nodes already
|
|
1605
|
+
* queued on the stack are visited regardless of DOM changes, and newly inserted nodes outside
|
|
1606
|
+
* the current child list are never visited.
|
|
1607
|
+
* 5. Calls `callbacks.exit(node, context)` (if provided) after all of a node's children have
|
|
1608
|
+
* been visited, passing the same `context` that `enter`
|
|
1609
|
+
* returned for that node.
|
|
1610
|
+
*
|
|
1611
|
+
* This implementation uses an explicit stack and does not recurse — it is safe on arbitrarily
|
|
1612
|
+
* deep trees.
|
|
1613
|
+
*
|
|
1614
|
+
* @param {Node} node
|
|
1615
|
+
* Root of the subtree to walk.
|
|
1616
|
+
* @param {*} context
|
|
1617
|
+
* Initial context value passed to the root node's `enter`.
|
|
1618
|
+
* @param {{ enter: function(Node, *): *, exit?: function(Node, *): void }} callbacks
|
|
1619
|
+
* @returns {void | walkDOM.STOP}
|
|
1620
|
+
* @see ../docs/walk-dom.md.
|
|
1621
|
+
*/
|
|
1622
|
+
function walkDOM(node, context, callbacks) {
|
|
1623
|
+
// Each stack frame is {node, context, phase}:
|
|
1624
|
+
// walkDOM.ENTER — call enter, then push children
|
|
1625
|
+
// walkDOM.EXIT — call exit
|
|
1626
|
+
var stack = [{ node: node, context: context, phase: walkDOM.ENTER }];
|
|
1627
|
+
while (stack.length > 0) {
|
|
1628
|
+
var frame = stack.pop();
|
|
1629
|
+
if (frame.phase === walkDOM.ENTER) {
|
|
1630
|
+
var childContext = callbacks.enter(frame.node, frame.context);
|
|
1631
|
+
if (childContext === walkDOM.STOP) {
|
|
1632
|
+
return walkDOM.STOP;
|
|
1633
|
+
}
|
|
1634
|
+
// Push exit frame before children so it fires after all children are processed (Last In First Out)
|
|
1635
|
+
stack.push({ node: frame.node, context: childContext, phase: walkDOM.EXIT });
|
|
1636
|
+
if (childContext === null || childContext === undefined) {
|
|
1637
|
+
continue; // skip children
|
|
1537
1638
|
}
|
|
1538
|
-
|
|
1639
|
+
// lastChild is read after enter returns, so enter may modify the child list.
|
|
1640
|
+
var child = frame.node.lastChild;
|
|
1641
|
+
// Traverse from lastChild backwards so that pushing onto the stack
|
|
1642
|
+
// naturally yields firstChild on top (processed first).
|
|
1643
|
+
while (child) {
|
|
1644
|
+
stack.push({ node: child, context: childContext, phase: walkDOM.ENTER });
|
|
1645
|
+
child = child.previousSibling;
|
|
1646
|
+
}
|
|
1647
|
+
} else {
|
|
1648
|
+
// frame.phase === walkDOM.EXIT
|
|
1649
|
+
if (callbacks.exit) {
|
|
1650
|
+
callbacks.exit(frame.node, frame.context);
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1539
1653
|
}
|
|
1540
1654
|
}
|
|
1541
1655
|
|
|
1656
|
+
/**
|
|
1657
|
+
* Sentinel value returned from a `walkDOM` `enter` callback to abort the entire traversal
|
|
1658
|
+
* immediately.
|
|
1659
|
+
*
|
|
1660
|
+
* @type {symbol}
|
|
1661
|
+
*/
|
|
1662
|
+
walkDOM.STOP = Symbol('walkDOM.STOP');
|
|
1663
|
+
/**
|
|
1664
|
+
* Phase constant for a stack frame that has not yet been visited.
|
|
1665
|
+
* The `enter` callback is called and children are scheduled.
|
|
1666
|
+
*
|
|
1667
|
+
* @type {number}
|
|
1668
|
+
*/
|
|
1669
|
+
walkDOM.ENTER = 0;
|
|
1670
|
+
/**
|
|
1671
|
+
* Phase constant for a stack frame whose subtree has been fully visited.
|
|
1672
|
+
* The `exit` callback is called.
|
|
1673
|
+
*
|
|
1674
|
+
* @type {number}
|
|
1675
|
+
*/
|
|
1676
|
+
walkDOM.EXIT = 1;
|
|
1677
|
+
|
|
1542
1678
|
/**
|
|
1543
1679
|
* @typedef DocumentOptions
|
|
1544
1680
|
* @property {string} [contentType=MIME_TYPE.XML_APPLICATION]
|
|
@@ -2125,7 +2261,20 @@ Document.prototype = {
|
|
|
2125
2261
|
this.documentElement = newChild;
|
|
2126
2262
|
}
|
|
2127
2263
|
},
|
|
2128
|
-
|
|
2264
|
+
/**
|
|
2265
|
+
* Imports a node from another document into this document, creating a new copy owned by this
|
|
2266
|
+
* document. The source node and its subtree are not modified.
|
|
2267
|
+
*
|
|
2268
|
+
* @param {Node} importedNode
|
|
2269
|
+
* The node to import.
|
|
2270
|
+
* @param {boolean} deep
|
|
2271
|
+
* If true, the contents of the node are recursively imported.
|
|
2272
|
+
* If false, only the node itself (and its attributes, if it is an element) are imported.
|
|
2273
|
+
* @returns {Node}
|
|
2274
|
+
* Returns the newly created import of the node.
|
|
2275
|
+
* @see {@link importNode}
|
|
2276
|
+
* @see {@link https://dom.spec.whatwg.org/#dom-document-importnode}
|
|
2277
|
+
*/
|
|
2129
2278
|
importNode: function (importedNode, deep) {
|
|
2130
2279
|
return importNode(this, importedNode, deep);
|
|
2131
2280
|
},
|
|
@@ -2201,6 +2350,15 @@ Document.prototype = {
|
|
|
2201
2350
|
/**
|
|
2202
2351
|
* @param {string} data
|
|
2203
2352
|
* @returns {Comment}
|
|
2353
|
+
* @see https://dom.spec.whatwg.org/#dom-document-createcomment
|
|
2354
|
+
* @see https://www.w3.org/TR/xml/#NT-Comment XML 1.0 production [15]
|
|
2355
|
+
* @see https://www.w3.org/TR/DOM-Parsing/#dfn-concept-serialize-xml §3.2.1.3
|
|
2356
|
+
*
|
|
2357
|
+
* Note: no validation is performed at creation time. When the resulting document is
|
|
2358
|
+
* serialized with `requireWellFormed: true`, the serializer throws `InvalidStateError`
|
|
2359
|
+
* if the comment data contains `--` anywhere, ends with `-`, or contains characters
|
|
2360
|
+
* outside the XML Char production (W3C DOM Parsing §3.2.1.3). Without that option the
|
|
2361
|
+
* data is emitted verbatim.
|
|
2204
2362
|
*/
|
|
2205
2363
|
createComment: function (data) {
|
|
2206
2364
|
var node = new Comment(PDC);
|
|
@@ -2210,10 +2368,22 @@ Document.prototype = {
|
|
|
2210
2368
|
return node;
|
|
2211
2369
|
},
|
|
2212
2370
|
/**
|
|
2371
|
+
* Returns a new CDATASection node whose data is `data`.
|
|
2372
|
+
*
|
|
2373
|
+
* __This implementation differs from the specification:__ - calling this method on an HTML
|
|
2374
|
+
* document does not throw `NotSupportedError`.
|
|
2375
|
+
*
|
|
2213
2376
|
* @param {string} data
|
|
2214
2377
|
* @returns {CDATASection}
|
|
2378
|
+
* @throws {DOMException}
|
|
2379
|
+
* With code `INVALID_CHARACTER_ERR` if `data` contains `"]]>"`.
|
|
2380
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createCDATASection
|
|
2381
|
+
* @see https://dom.spec.whatwg.org/#dom-document-createcdatasection
|
|
2215
2382
|
*/
|
|
2216
2383
|
createCDATASection: function (data) {
|
|
2384
|
+
if (data.indexOf(']]>') !== -1) {
|
|
2385
|
+
throw new DOMException(DOMException.INVALID_CHARACTER_ERR, 'data contains "]]>"');
|
|
2386
|
+
}
|
|
2217
2387
|
var node = new CDATASection(PDC);
|
|
2218
2388
|
node.ownerDocument = this;
|
|
2219
2389
|
node.childNodes = new NodeList();
|
|
@@ -2221,9 +2391,24 @@ Document.prototype = {
|
|
|
2221
2391
|
return node;
|
|
2222
2392
|
},
|
|
2223
2393
|
/**
|
|
2394
|
+
* Returns a ProcessingInstruction node whose target is target and data is data.
|
|
2395
|
+
*
|
|
2396
|
+
* __This behavior is slightly different from the in the specs__:
|
|
2397
|
+
* - it does not do any input validation on the arguments and doesn't throw
|
|
2398
|
+
* "InvalidCharacterError".
|
|
2399
|
+
*
|
|
2400
|
+
* Note: When the resulting document is serialized with `requireWellFormed: true`, the
|
|
2401
|
+
* serializer throws `InvalidStateError` if `.target` contains `:` or is an ASCII
|
|
2402
|
+
* case-insensitive match for `"xml"`, or if `.data` contains `?>` or characters outside the
|
|
2403
|
+
* XML Char production (W3C DOM Parsing §3.2.1.7). Without that option the data is emitted
|
|
2404
|
+
* verbatim.
|
|
2405
|
+
*
|
|
2224
2406
|
* @param {string} target
|
|
2225
2407
|
* @param {string} data
|
|
2226
2408
|
* @returns {ProcessingInstruction}
|
|
2409
|
+
* @see https://developer.mozilla.org/docs/Web/API/Document/createProcessingInstruction
|
|
2410
|
+
* @see https://dom.spec.whatwg.org/#dom-document-createprocessinginstruction
|
|
2411
|
+
* @see https://www.w3.org/TR/DOM-Parsing/#dfn-concept-serialize-xml §3.2.1.7
|
|
2227
2412
|
*/
|
|
2228
2413
|
createProcessingInstruction: function (target, data) {
|
|
2229
2414
|
var node = new ProcessingInstruction(PDC);
|
|
@@ -2656,6 +2841,31 @@ CDATASection.prototype = {
|
|
|
2656
2841
|
};
|
|
2657
2842
|
_extends(CDATASection, Text);
|
|
2658
2843
|
|
|
2844
|
+
/**
|
|
2845
|
+
* @class DocumentType
|
|
2846
|
+
* @augments Node
|
|
2847
|
+
* @property {string} publicId
|
|
2848
|
+
* The external subset public identifier, stored verbatim (including surrounding quotes).
|
|
2849
|
+
* Declared `readonly` by the WHATWG DOM spec; xmldom does not enforce this constraint —
|
|
2850
|
+
* direct property writes succeed and the written value is serialized verbatim.
|
|
2851
|
+
* When serialized with `requireWellFormed: true`, the serializer validates the value against
|
|
2852
|
+
* the XML `PubidLiteral` production and throws `InvalidStateError` if it does not match.
|
|
2853
|
+
* @property {string} systemId
|
|
2854
|
+
* The external subset system identifier, stored verbatim (including surrounding quotes).
|
|
2855
|
+
* Declared `readonly` by the WHATWG DOM spec; xmldom does not enforce this constraint —
|
|
2856
|
+
* direct property writes succeed and the written value is serialized verbatim.
|
|
2857
|
+
* When serialized with `requireWellFormed: true`, the serializer validates the value against
|
|
2858
|
+
* the XML `SystemLiteral` production and throws `InvalidStateError` if it does not match.
|
|
2859
|
+
* @property {string} internalSubset
|
|
2860
|
+
* The internal subset string (the raw content between `[` and `]`), or an empty string.
|
|
2861
|
+
* Declared `readonly` by the WHATWG DOM spec; xmldom does not enforce this constraint —
|
|
2862
|
+
* direct property writes succeed and the written value is serialized verbatim.
|
|
2863
|
+
* When serialized with `requireWellFormed: true`, the serializer throws `InvalidStateError`
|
|
2864
|
+
* if the value contains `"]>"`.
|
|
2865
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/DocumentType MDN
|
|
2866
|
+
* @see https://dom.spec.whatwg.org/#interface-documenttype WHATWG DOM
|
|
2867
|
+
* @prettierignore
|
|
2868
|
+
*/
|
|
2659
2869
|
function DocumentType(symbol) {
|
|
2660
2870
|
checkSymbol(symbol);
|
|
2661
2871
|
}
|
|
@@ -2693,11 +2903,82 @@ function ProcessingInstruction(symbol) {
|
|
|
2693
2903
|
ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
|
|
2694
2904
|
_extends(ProcessingInstruction, CharacterData);
|
|
2695
2905
|
function XMLSerializer() {}
|
|
2696
|
-
|
|
2697
|
-
|
|
2906
|
+
/**
|
|
2907
|
+
* Returns the result of serializing `node` to XML.
|
|
2908
|
+
*
|
|
2909
|
+
* When `options.requireWellFormed` is `true`, the serializer throws `InvalidStateError` for
|
|
2910
|
+
* content that would produce ill-formed XML (e.g. CDATASection data containing `"]]>"`, Text
|
|
2911
|
+
* data containing characters outside the XML Char production, or a Document with no
|
|
2912
|
+
* `documentElement`).
|
|
2913
|
+
*
|
|
2914
|
+
* When `options.splitCDATASections` is `false`, CDATASection data is emitted verbatim even
|
|
2915
|
+
* when it contains `"]]>"`. When `true` (the default), `"]]>"` sequences are split across
|
|
2916
|
+
* concatenated CDATA sections — this behavior is **deprecated** and will be removed in the
|
|
2917
|
+
* next breaking release. Callers should migrate to `{ requireWellFormed: true }`, which throws
|
|
2918
|
+
* `InvalidStateError` instead of transforming.
|
|
2919
|
+
*
|
|
2920
|
+
* __This implementation differs from the specification:__ - CDATASection serialization is not
|
|
2921
|
+
* specified by W3C DOM Parsing or WHATWG DOM Parsing (see
|
|
2922
|
+
* {@link https://github.com/w3c/DOM-Parsing/issues/38 w3c/DOM-Parsing#38}).
|
|
2923
|
+
* When `splitCDATASections` is `true` (the default), `"]]>"` sequences in CDATASection data
|
|
2924
|
+
* are split across concatenated CDATA sections — this mechanism is derived from DOM Level 3
|
|
2925
|
+
* Core and is **deprecated**. The split mechanics will be removed in the next breaking
|
|
2926
|
+
* release. Callers that rely on this behavior should migrate to `{ requireWellFormed: true }`.
|
|
2927
|
+
* - W3C DOM Parsing §3.2.1.1 requires well-formedness checks on Element `localName`s,
|
|
2928
|
+
* prefixes,
|
|
2929
|
+
* and attribute serialization (duplicate attributes, namespace declarations, attribute value
|
|
2930
|
+
* characters) when `requireWellFormed` is `true`. These checks are **not implemented** in this
|
|
2931
|
+
* release — see the tracking issue filed against the next breaking milestone.
|
|
2932
|
+
*
|
|
2933
|
+
* @param {Node} node
|
|
2934
|
+
* @param {Object | function} [options]
|
|
2935
|
+
* Options object, or a legacy nodeFilter function (backward compatible).
|
|
2936
|
+
* @param {boolean} [options.requireWellFormed=false]
|
|
2937
|
+
* When `true`, throws `InvalidStateError` for content that would produce ill-formed XML.
|
|
2938
|
+
* @param {boolean} [options.splitCDATASections=true]
|
|
2939
|
+
* When `true` (default), splits `"]]>"` sequences in CDATASection data across concatenated
|
|
2940
|
+
* CDATA sections. **Deprecated** — will be removed in the next breaking release.
|
|
2941
|
+
* @param {function} [options.nodeFilter]
|
|
2942
|
+
* A filter function applied to each node before serialization.
|
|
2943
|
+
* @returns {string}
|
|
2944
|
+
* @throws {DOMException}
|
|
2945
|
+
* With name `InvalidStateError` when `requireWellFormed` is `true` and any of the following
|
|
2946
|
+
* conditions hold:
|
|
2947
|
+
* - CDATASection data contains `"]]>"`
|
|
2948
|
+
* - Text data contains characters outside the XML Char production
|
|
2949
|
+
* - a Comment node's data contains `--` anywhere or ends with `-`
|
|
2950
|
+
* - a ProcessingInstruction's target contains `:` or is an ASCII case-insensitive match for
|
|
2951
|
+
* `"xml"`, or its data contains `?>` or characters outside the XML Char production
|
|
2952
|
+
* - a DocumentType's `publicId` is non-empty and does not match the XML `PubidLiteral`
|
|
2953
|
+
* production (W3C DOM Parsing §3.2.1.3; XML 1.0 production [12])
|
|
2954
|
+
* - a DocumentType's `systemId` is non-empty and does not match the XML `SystemLiteral`
|
|
2955
|
+
* production (W3C DOM Parsing §3.2.1.3; XML 1.0 production [11])
|
|
2956
|
+
* - a DocumentType's `internalSubset` contains `"]>"`
|
|
2957
|
+
* - the Document has no `documentElement`
|
|
2958
|
+
* @see https://developer.mozilla.org/docs/Web/API/XMLSerializer/serializeToString
|
|
2959
|
+
* @see https://html.spec.whatwg.org/#dom-xmlserializer-serializetostring
|
|
2960
|
+
* @see https://github.com/w3c/DOM-Parsing/issues/84
|
|
2961
|
+
* @prettierignore
|
|
2962
|
+
*/
|
|
2963
|
+
XMLSerializer.prototype.serializeToString = function (node, options) {
|
|
2964
|
+
return nodeSerializeToString.call(node, options);
|
|
2698
2965
|
};
|
|
2699
2966
|
Node.prototype.toString = nodeSerializeToString;
|
|
2700
|
-
function nodeSerializeToString(
|
|
2967
|
+
function nodeSerializeToString(options) {
|
|
2968
|
+
// Normalize the user-supplied options into a single internal opts object so that the
|
|
2969
|
+
// internal serializer always works with a consistent shape rather than positional flags.
|
|
2970
|
+
var opts;
|
|
2971
|
+
if (typeof options === 'function') {
|
|
2972
|
+
opts = { requireWellFormed: false, splitCDATASections: true, nodeFilter: options };
|
|
2973
|
+
} else if (options != null) {
|
|
2974
|
+
opts = {
|
|
2975
|
+
requireWellFormed: !!options.requireWellFormed,
|
|
2976
|
+
splitCDATASections: options.splitCDATASections !== false,
|
|
2977
|
+
nodeFilter: options.nodeFilter || null,
|
|
2978
|
+
};
|
|
2979
|
+
} else {
|
|
2980
|
+
opts = { requireWellFormed: false, splitCDATASections: true, nodeFilter: null };
|
|
2981
|
+
}
|
|
2701
2982
|
var buf = [];
|
|
2702
2983
|
var refNode = (this.nodeType === DOCUMENT_NODE && this.documentElement) || this;
|
|
2703
2984
|
var prefix = refNode.prefix;
|
|
@@ -2712,7 +2993,7 @@ function nodeSerializeToString(nodeFilter) {
|
|
|
2712
2993
|
];
|
|
2713
2994
|
}
|
|
2714
2995
|
}
|
|
2715
|
-
serializeToString(this, buf,
|
|
2996
|
+
serializeToString(this, buf, visibleNamespaces, opts);
|
|
2716
2997
|
return buf.join('');
|
|
2717
2998
|
}
|
|
2718
2999
|
|
|
@@ -2762,235 +3043,317 @@ function addSerializedAttribute(buf, qualifiedName, value) {
|
|
|
2762
3043
|
buf.push(' ', qualifiedName, '="', value.replace(/[<>&"\t\n\r]/g, _xmlEncoder), '"');
|
|
2763
3044
|
}
|
|
2764
3045
|
|
|
2765
|
-
function serializeToString(node, buf,
|
|
3046
|
+
function serializeToString(node, buf, visibleNamespaces, opts) {
|
|
2766
3047
|
if (!visibleNamespaces) {
|
|
2767
3048
|
visibleNamespaces = [];
|
|
2768
3049
|
}
|
|
3050
|
+
var nodeFilter = opts.nodeFilter;
|
|
3051
|
+
var requireWellFormed = opts.requireWellFormed;
|
|
3052
|
+
var splitCDATASections = opts.splitCDATASections;
|
|
2769
3053
|
var doc = node.nodeType === DOCUMENT_NODE ? node : node.ownerDocument;
|
|
2770
3054
|
var isHTML = doc.type === 'html';
|
|
2771
3055
|
|
|
2772
|
-
|
|
2773
|
-
node
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
switch (node.nodeType) {
|
|
2786
|
-
case ELEMENT_NODE:
|
|
2787
|
-
var attrs = node.attributes;
|
|
2788
|
-
var len = attrs.length;
|
|
2789
|
-
var child = node.firstChild;
|
|
2790
|
-
var nodeName = node.tagName;
|
|
2791
|
-
|
|
2792
|
-
var prefixedNodeName = nodeName;
|
|
2793
|
-
if (!isHTML && !node.prefix && node.namespaceURI) {
|
|
2794
|
-
var defaultNS;
|
|
2795
|
-
// lookup current default ns from `xmlns` attribute
|
|
2796
|
-
for (var ai = 0; ai < attrs.length; ai++) {
|
|
2797
|
-
if (attrs.item(ai).name === 'xmlns') {
|
|
2798
|
-
defaultNS = attrs.item(ai).value;
|
|
2799
|
-
break;
|
|
2800
|
-
}
|
|
2801
|
-
}
|
|
2802
|
-
if (!defaultNS) {
|
|
2803
|
-
// lookup current default ns in visibleNamespaces
|
|
2804
|
-
for (var nsi = visibleNamespaces.length - 1; nsi >= 0; nsi--) {
|
|
2805
|
-
var namespace = visibleNamespaces[nsi];
|
|
2806
|
-
if (namespace.prefix === '' && namespace.namespace === node.namespaceURI) {
|
|
2807
|
-
defaultNS = namespace.namespace;
|
|
2808
|
-
break;
|
|
3056
|
+
walkDOM(
|
|
3057
|
+
node,
|
|
3058
|
+
{ ns: visibleNamespaces },
|
|
3059
|
+
{
|
|
3060
|
+
enter: function (n, ctx) {
|
|
3061
|
+
var namespaces = ctx.ns;
|
|
3062
|
+
|
|
3063
|
+
if (nodeFilter) {
|
|
3064
|
+
n = nodeFilter(n);
|
|
3065
|
+
if (n) {
|
|
3066
|
+
if (typeof n == 'string') {
|
|
3067
|
+
buf.push(n);
|
|
3068
|
+
return null;
|
|
2809
3069
|
}
|
|
3070
|
+
} else {
|
|
3071
|
+
return null;
|
|
2810
3072
|
}
|
|
2811
3073
|
}
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
3074
|
+
|
|
3075
|
+
switch (n.nodeType) {
|
|
3076
|
+
case ELEMENT_NODE:
|
|
3077
|
+
var attrs = n.attributes;
|
|
3078
|
+
var len = attrs.length;
|
|
3079
|
+
var nodeName = n.tagName;
|
|
3080
|
+
|
|
3081
|
+
var prefixedNodeName = nodeName;
|
|
3082
|
+
if (!isHTML && !n.prefix && n.namespaceURI) {
|
|
3083
|
+
var defaultNS;
|
|
3084
|
+
// lookup current default ns from `xmlns` attribute
|
|
3085
|
+
for (var ai = 0; ai < attrs.length; ai++) {
|
|
3086
|
+
if (attrs.item(ai).name === 'xmlns') {
|
|
3087
|
+
defaultNS = attrs.item(ai).value;
|
|
3088
|
+
break;
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
if (!defaultNS) {
|
|
3092
|
+
// lookup current default ns in visibleNamespaces
|
|
3093
|
+
for (var nsi = namespaces.length - 1; nsi >= 0; nsi--) {
|
|
3094
|
+
var nsEntry = namespaces[nsi];
|
|
3095
|
+
if (nsEntry.prefix === '' && nsEntry.namespace === n.namespaceURI) {
|
|
3096
|
+
defaultNS = nsEntry.namespace;
|
|
3097
|
+
break;
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
if (defaultNS !== n.namespaceURI) {
|
|
3102
|
+
for (var nsi = namespaces.length - 1; nsi >= 0; nsi--) {
|
|
3103
|
+
var nsEntry = namespaces[nsi];
|
|
3104
|
+
if (nsEntry.namespace === n.namespaceURI) {
|
|
3105
|
+
if (nsEntry.prefix) {
|
|
3106
|
+
prefixedNodeName = nsEntry.prefix + ':' + nodeName;
|
|
3107
|
+
}
|
|
3108
|
+
break;
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
2818
3111
|
}
|
|
2819
|
-
break;
|
|
2820
3112
|
}
|
|
2821
|
-
}
|
|
2822
|
-
}
|
|
2823
|
-
}
|
|
2824
3113
|
|
|
2825
|
-
|
|
3114
|
+
buf.push('<', prefixedNodeName);
|
|
3115
|
+
|
|
3116
|
+
// Build a fresh namespace snapshot for this element's children.
|
|
3117
|
+
// The slice prevents sibling elements from inheriting each other's declarations.
|
|
3118
|
+
var childNamespaces = namespaces.slice();
|
|
3119
|
+
|
|
3120
|
+
for (var i = 0; i < len; i++) {
|
|
3121
|
+
// add namespaces for attributes
|
|
3122
|
+
var attr = attrs.item(i);
|
|
3123
|
+
if (attr.prefix == 'xmlns') {
|
|
3124
|
+
childNamespaces.push({
|
|
3125
|
+
prefix: attr.localName,
|
|
3126
|
+
namespace: attr.value,
|
|
3127
|
+
});
|
|
3128
|
+
} else if (attr.nodeName == 'xmlns') {
|
|
3129
|
+
childNamespaces.push({ prefix: '', namespace: attr.value });
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
2826
3132
|
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
3133
|
+
for (var i = 0; i < len; i++) {
|
|
3134
|
+
var attr = attrs.item(i);
|
|
3135
|
+
if (needNamespaceDefine(attr, isHTML, childNamespaces)) {
|
|
3136
|
+
var attrPrefix = attr.prefix || '';
|
|
3137
|
+
var uri = attr.namespaceURI;
|
|
3138
|
+
addSerializedAttribute(buf, attrPrefix ? 'xmlns:' + attrPrefix : 'xmlns', uri);
|
|
3139
|
+
childNamespaces.push({ prefix: attrPrefix, namespace: uri });
|
|
3140
|
+
}
|
|
3141
|
+
// Apply nodeFilter and serialize the attribute.
|
|
3142
|
+
var filteredAttr = nodeFilter ? nodeFilter(attr) : attr;
|
|
3143
|
+
if (filteredAttr) {
|
|
3144
|
+
if (typeof filteredAttr === 'string') {
|
|
3145
|
+
buf.push(filteredAttr);
|
|
3146
|
+
} else {
|
|
3147
|
+
addSerializedAttribute(buf, filteredAttr.name, filteredAttr.value);
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
2839
3151
|
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
}
|
|
2848
|
-
serializeToString(attr, buf, nodeFilter, visibleNamespaces);
|
|
2849
|
-
}
|
|
3152
|
+
// add namespace for current node
|
|
3153
|
+
if (nodeName === prefixedNodeName && needNamespaceDefine(n, isHTML, childNamespaces)) {
|
|
3154
|
+
var nodePrefix = n.prefix || '';
|
|
3155
|
+
var uri = n.namespaceURI;
|
|
3156
|
+
addSerializedAttribute(buf, nodePrefix ? 'xmlns:' + nodePrefix : 'xmlns', uri);
|
|
3157
|
+
childNamespaces.push({ prefix: nodePrefix, namespace: uri });
|
|
3158
|
+
}
|
|
2850
3159
|
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
3160
|
+
// in XML elements can be closed when they have no children
|
|
3161
|
+
var canCloseTag = !n.firstChild;
|
|
3162
|
+
if (canCloseTag && (isHTML || n.namespaceURI === NAMESPACE.HTML)) {
|
|
3163
|
+
// in HTML (doc or ns) only void elements can be closed right away
|
|
3164
|
+
canCloseTag = isHTMLVoidElement(nodeName);
|
|
3165
|
+
}
|
|
3166
|
+
if (canCloseTag) {
|
|
3167
|
+
buf.push('/>');
|
|
3168
|
+
// Self-closing: no children and no closing tag needed from exit.
|
|
3169
|
+
return null;
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
buf.push('>');
|
|
3173
|
+
|
|
3174
|
+
// HTML raw text elements: serialize children as raw data without further descent.
|
|
3175
|
+
if (isHTML && isHTMLRawTextElement(nodeName)) {
|
|
3176
|
+
var child = n.firstChild;
|
|
3177
|
+
while (child) {
|
|
3178
|
+
if (child.data) {
|
|
3179
|
+
buf.push(child.data);
|
|
3180
|
+
} else {
|
|
3181
|
+
serializeToString(child, buf, childNamespaces.slice(), opts);
|
|
3182
|
+
}
|
|
3183
|
+
child = child.nextSibling;
|
|
3184
|
+
}
|
|
3185
|
+
buf.push('</', prefixedNodeName, '>');
|
|
3186
|
+
// Children handled manually above; prevent walkDOM from also traversing them.
|
|
3187
|
+
return null;
|
|
3188
|
+
}
|
|
3189
|
+
|
|
3190
|
+
// Return child context so walkDOM descends; exit will emit the closing tag.
|
|
3191
|
+
return { ns: childNamespaces, tag: prefixedNodeName };
|
|
3192
|
+
case DOCUMENT_NODE:
|
|
3193
|
+
case DOCUMENT_FRAGMENT_NODE:
|
|
3194
|
+
if (requireWellFormed && n.nodeType === DOCUMENT_NODE && n.documentElement == null) {
|
|
3195
|
+
throw new DOMException('The Document has no documentElement', DOMExceptionName.InvalidStateError);
|
|
3196
|
+
}
|
|
3197
|
+
// Pass namespaces through; each child element will slice independently.
|
|
3198
|
+
return { ns: namespaces };
|
|
3199
|
+
case ATTRIBUTE_NODE:
|
|
3200
|
+
addSerializedAttribute(buf, n.name, n.value);
|
|
3201
|
+
return null;
|
|
3202
|
+
case TEXT_NODE:
|
|
3203
|
+
/*
|
|
3204
|
+
* The ampersand character (&) and the left angle bracket (<) must not appear in their literal form,
|
|
3205
|
+
* except when used as markup delimiters, or within a comment, a processing instruction,
|
|
3206
|
+
* or a CDATA section.
|
|
3207
|
+
* If they are needed elsewhere, they must be escaped using either numeric character
|
|
3208
|
+
* references or the strings `&` and `<` respectively.
|
|
3209
|
+
* The right angle bracket (>) may be represented using the string " > ",
|
|
3210
|
+
* and must, for compatibility, be escaped using either `>`,
|
|
3211
|
+
* or a character reference when it appears in the string `]]>` in content,
|
|
3212
|
+
* when that string is not marking the end of a CDATA section.
|
|
3213
|
+
*
|
|
3214
|
+
* In the content of elements, character data is any string of characters which does not
|
|
3215
|
+
* contain the start-delimiter of any markup and does not include the CDATA-section-close
|
|
3216
|
+
* delimiter, `]]>`.
|
|
3217
|
+
*
|
|
3218
|
+
* @see https://www.w3.org/TR/xml/#NT-CharData
|
|
3219
|
+
* @see https://w3c.github.io/DOM-Parsing/#xml-serializing-a-text-node
|
|
3220
|
+
*/
|
|
3221
|
+
if (requireWellFormed && g.InvalidChar.test(n.data)) {
|
|
3222
|
+
throw new DOMException(
|
|
3223
|
+
'The Text node data contains characters outside the XML Char production',
|
|
3224
|
+
DOMExceptionName.InvalidStateError
|
|
3225
|
+
);
|
|
3226
|
+
}
|
|
3227
|
+
buf.push(n.data.replace(/[<&>]/g, _xmlEncoder));
|
|
3228
|
+
return null;
|
|
3229
|
+
case CDATA_SECTION_NODE:
|
|
3230
|
+
if (requireWellFormed && n.data.indexOf(']]>') !== -1) {
|
|
3231
|
+
throw new DOMException('The CDATASection data contains "]]>"', DOMExceptionName.InvalidStateError);
|
|
3232
|
+
}
|
|
3233
|
+
if (splitCDATASections) {
|
|
3234
|
+
buf.push(g.CDATA_START, n.data.replace(/]]>/g, ']]]]><![CDATA[>'), g.CDATA_END);
|
|
2873
3235
|
} else {
|
|
2874
|
-
|
|
3236
|
+
buf.push(g.CDATA_START, n.data, g.CDATA_END);
|
|
2875
3237
|
}
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
3238
|
+
return null;
|
|
3239
|
+
case COMMENT_NODE:
|
|
3240
|
+
if (requireWellFormed) {
|
|
3241
|
+
if (g.InvalidChar.test(n.data)) {
|
|
3242
|
+
throw new DOMException(
|
|
3243
|
+
'The comment node data contains characters outside the XML Char production',
|
|
3244
|
+
DOMExceptionName.InvalidStateError
|
|
3245
|
+
);
|
|
3246
|
+
}
|
|
3247
|
+
if (n.data.indexOf('--') !== -1 || n.data[n.data.length - 1] === '-') {
|
|
3248
|
+
throw new DOMException(
|
|
3249
|
+
'The comment node data contains "--" or ends with "-"',
|
|
3250
|
+
DOMExceptionName.InvalidStateError
|
|
3251
|
+
);
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
buf.push(g.COMMENT_START, n.data, g.COMMENT_END);
|
|
3255
|
+
return null;
|
|
3256
|
+
case DOCUMENT_TYPE_NODE:
|
|
3257
|
+
var pubid = n.publicId;
|
|
3258
|
+
var sysid = n.systemId;
|
|
3259
|
+
if (requireWellFormed) {
|
|
3260
|
+
if (pubid && !g.PubidLiteral_match.test(pubid)) {
|
|
3261
|
+
throw new DOMException('DocumentType publicId is not a valid PubidLiteral', DOMExceptionName.InvalidStateError);
|
|
3262
|
+
}
|
|
3263
|
+
if (sysid && sysid !== '.' && !g.SystemLiteral_match.test(sysid)) {
|
|
3264
|
+
throw new DOMException('DocumentType systemId is not a valid SystemLiteral', DOMExceptionName.InvalidStateError);
|
|
3265
|
+
}
|
|
3266
|
+
if (n.internalSubset && n.internalSubset.indexOf(']>') !== -1) {
|
|
3267
|
+
throw new DOMException('DocumentType internalSubset contains "]>"', DOMExceptionName.InvalidStateError);
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
buf.push(g.DOCTYPE_DECL_START, ' ', n.name);
|
|
3271
|
+
if (pubid) {
|
|
3272
|
+
buf.push(' ', g.PUBLIC, ' ', pubid);
|
|
3273
|
+
if (sysid && sysid !== '.') {
|
|
3274
|
+
buf.push(' ', sysid);
|
|
3275
|
+
}
|
|
3276
|
+
} else if (sysid && sysid !== '.') {
|
|
3277
|
+
buf.push(' ', g.SYSTEM, ' ', sysid);
|
|
3278
|
+
}
|
|
3279
|
+
if (n.internalSubset) {
|
|
3280
|
+
buf.push(' [', n.internalSubset, ']');
|
|
3281
|
+
}
|
|
3282
|
+
buf.push('>');
|
|
3283
|
+
return null;
|
|
3284
|
+
case PROCESSING_INSTRUCTION_NODE:
|
|
3285
|
+
if (requireWellFormed) {
|
|
3286
|
+
if (n.target.indexOf(':') !== -1 || n.target.toLowerCase() === 'xml') {
|
|
3287
|
+
throw new DOMException('The ProcessingInstruction target is not well-formed', DOMExceptionName.InvalidStateError);
|
|
3288
|
+
}
|
|
3289
|
+
if (g.InvalidChar.test(n.data)) {
|
|
3290
|
+
throw new DOMException(
|
|
3291
|
+
'The ProcessingInstruction data contains characters outside the XML Char production',
|
|
3292
|
+
DOMExceptionName.InvalidStateError
|
|
3293
|
+
);
|
|
3294
|
+
}
|
|
3295
|
+
if (n.data.indexOf('?>') !== -1) {
|
|
3296
|
+
throw new DOMException('The ProcessingInstruction data contains "?>"', DOMExceptionName.InvalidStateError);
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
buf.push('<?', n.target, ' ', n.data, '?>');
|
|
3300
|
+
return null;
|
|
3301
|
+
case ENTITY_REFERENCE_NODE:
|
|
3302
|
+
buf.push('&', n.nodeName, ';');
|
|
3303
|
+
return null;
|
|
3304
|
+
//case ENTITY_NODE:
|
|
3305
|
+
//case NOTATION_NODE:
|
|
3306
|
+
default:
|
|
3307
|
+
buf.push('??', n.nodeName);
|
|
3308
|
+
return null;
|
|
2883
3309
|
}
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
case DOCUMENT_NODE:
|
|
2890
|
-
case DOCUMENT_FRAGMENT_NODE:
|
|
2891
|
-
var child = node.firstChild;
|
|
2892
|
-
while (child) {
|
|
2893
|
-
serializeToString(child, buf, nodeFilter, visibleNamespaces.slice());
|
|
2894
|
-
child = child.nextSibling;
|
|
2895
|
-
}
|
|
2896
|
-
return;
|
|
2897
|
-
case ATTRIBUTE_NODE:
|
|
2898
|
-
return addSerializedAttribute(buf, node.name, node.value);
|
|
2899
|
-
case TEXT_NODE:
|
|
2900
|
-
/*
|
|
2901
|
-
* The ampersand character (&) and the left angle bracket (<) must not appear in their literal form,
|
|
2902
|
-
* except when used as markup delimiters, or within a comment, a processing instruction,
|
|
2903
|
-
* or a CDATA section.
|
|
2904
|
-
* If they are needed elsewhere, they must be escaped using either numeric character
|
|
2905
|
-
* references or the strings `&` and `<` respectively.
|
|
2906
|
-
* The right angle bracket (>) may be represented using the string " > ",
|
|
2907
|
-
* and must, for compatibility, be escaped using either `>`,
|
|
2908
|
-
* or a character reference when it appears in the string `]]>` in content,
|
|
2909
|
-
* when that string is not marking the end of a CDATA section.
|
|
2910
|
-
*
|
|
2911
|
-
* In the content of elements, character data is any string of characters which does not
|
|
2912
|
-
* contain the start-delimiter of any markup and does not include the CDATA-section-close
|
|
2913
|
-
* delimiter, `]]>`.
|
|
2914
|
-
*
|
|
2915
|
-
* @see https://www.w3.org/TR/xml/#NT-CharData
|
|
2916
|
-
* @see https://w3c.github.io/DOM-Parsing/#xml-serializing-a-text-node
|
|
2917
|
-
*/
|
|
2918
|
-
return buf.push(node.data.replace(/[<&>]/g, _xmlEncoder));
|
|
2919
|
-
case CDATA_SECTION_NODE:
|
|
2920
|
-
return buf.push(g.CDATA_START, node.data, g.CDATA_END);
|
|
2921
|
-
case COMMENT_NODE:
|
|
2922
|
-
return buf.push(g.COMMENT_START, node.data, g.COMMENT_END);
|
|
2923
|
-
case DOCUMENT_TYPE_NODE:
|
|
2924
|
-
var pubid = node.publicId;
|
|
2925
|
-
var sysid = node.systemId;
|
|
2926
|
-
buf.push(g.DOCTYPE_DECL_START, ' ', node.name);
|
|
2927
|
-
if (pubid) {
|
|
2928
|
-
buf.push(' ', g.PUBLIC, ' ', pubid);
|
|
2929
|
-
if (sysid && sysid !== '.') {
|
|
2930
|
-
buf.push(' ', sysid);
|
|
3310
|
+
},
|
|
3311
|
+
exit: function (n, childCtx) {
|
|
3312
|
+
// Emit the closing tag for elements that were opened (not self-closed, not raw text).
|
|
3313
|
+
if (childCtx && childCtx.tag) {
|
|
3314
|
+
buf.push('</', childCtx.tag, '>');
|
|
2931
3315
|
}
|
|
2932
|
-
}
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
if (node.internalSubset) {
|
|
2936
|
-
buf.push(' [', node.internalSubset, ']');
|
|
2937
|
-
}
|
|
2938
|
-
buf.push('>');
|
|
2939
|
-
return;
|
|
2940
|
-
case PROCESSING_INSTRUCTION_NODE:
|
|
2941
|
-
return buf.push('<?', node.target, ' ', node.data, '?>');
|
|
2942
|
-
case ENTITY_REFERENCE_NODE:
|
|
2943
|
-
return buf.push('&', node.nodeName, ';');
|
|
2944
|
-
//case ENTITY_NODE:
|
|
2945
|
-
//case NOTATION_NODE:
|
|
2946
|
-
default:
|
|
2947
|
-
buf.push('??', node.nodeName);
|
|
2948
|
-
}
|
|
3316
|
+
},
|
|
3317
|
+
}
|
|
3318
|
+
);
|
|
2949
3319
|
}
|
|
3320
|
+
/**
|
|
3321
|
+
* Imports a node from a different document into `doc`, creating a new copy.
|
|
3322
|
+
* Delegates to {@link walkDOM} for traversal. Each node in the subtree is shallow-cloned,
|
|
3323
|
+
* stamped with `doc` as its `ownerDocument`, and detached (`parentNode` set to `null`).
|
|
3324
|
+
* Children are imported recursively when `deep` is `true`; for {@link Attr} nodes `deep` is
|
|
3325
|
+
* always forced to `true`
|
|
3326
|
+
* because an attribute's value lives in a child text node.
|
|
3327
|
+
*
|
|
3328
|
+
* @param {Document} doc
|
|
3329
|
+
* The document that will own the imported node.
|
|
3330
|
+
* @param {Node} node
|
|
3331
|
+
* The node to import.
|
|
3332
|
+
* @param {boolean} deep
|
|
3333
|
+
* If `true`, descendants are imported recursively.
|
|
3334
|
+
* @returns {Node}
|
|
3335
|
+
* The newly imported node, now owned by `doc`.
|
|
3336
|
+
*/
|
|
2950
3337
|
function importNode(doc, node, deep) {
|
|
2951
|
-
var
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
//case COMMENT_NODE:
|
|
2971
|
-
// deep = false;
|
|
2972
|
-
// break;
|
|
2973
|
-
//case DOCUMENT_NODE:
|
|
2974
|
-
//case DOCUMENT_TYPE_NODE:
|
|
2975
|
-
//cannot be imported.
|
|
2976
|
-
//case ENTITY_NODE:
|
|
2977
|
-
//case NOTATION_NODE:
|
|
2978
|
-
//can not hit in level3
|
|
2979
|
-
//default:throw e;
|
|
2980
|
-
}
|
|
2981
|
-
if (!node2) {
|
|
2982
|
-
node2 = node.cloneNode(false); //false
|
|
2983
|
-
}
|
|
2984
|
-
node2.ownerDocument = doc;
|
|
2985
|
-
node2.parentNode = null;
|
|
2986
|
-
if (deep) {
|
|
2987
|
-
var child = node.firstChild;
|
|
2988
|
-
while (child) {
|
|
2989
|
-
node2.appendChild(importNode(doc, child, deep));
|
|
2990
|
-
child = child.nextSibling;
|
|
2991
|
-
}
|
|
2992
|
-
}
|
|
2993
|
-
return node2;
|
|
3338
|
+
var destRoot;
|
|
3339
|
+
walkDOM(node, null, {
|
|
3340
|
+
enter: function (srcNode, destParent) {
|
|
3341
|
+
// Shallow-clone the node and stamp it into the target document.
|
|
3342
|
+
var destNode = srcNode.cloneNode(false);
|
|
3343
|
+
destNode.ownerDocument = doc;
|
|
3344
|
+
destNode.parentNode = null;
|
|
3345
|
+
// capture as the root of the imported subtree or attach to parent.
|
|
3346
|
+
if (destParent === null) {
|
|
3347
|
+
destRoot = destNode;
|
|
3348
|
+
} else {
|
|
3349
|
+
destParent.appendChild(destNode);
|
|
3350
|
+
}
|
|
3351
|
+
// ATTRIBUTE_NODE must always be imported deeply: its value lives in a child text node.
|
|
3352
|
+
var shouldDeep = srcNode.nodeType === ATTRIBUTE_NODE || deep;
|
|
3353
|
+
return shouldDeep ? destNode : null;
|
|
3354
|
+
},
|
|
3355
|
+
});
|
|
3356
|
+
return destRoot;
|
|
2994
3357
|
}
|
|
2995
3358
|
|
|
2996
3359
|
/**
|
|
@@ -3010,47 +3373,76 @@ function importNode(doc, node, deep) {
|
|
|
3010
3373
|
* potentially invoked in this function) do not meet their specific constraints.
|
|
3011
3374
|
*/
|
|
3012
3375
|
function cloneNode(doc, node, deep) {
|
|
3013
|
-
var
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3376
|
+
var destRoot;
|
|
3377
|
+
walkDOM(node, null, {
|
|
3378
|
+
enter: function (srcNode, destParent) {
|
|
3379
|
+
// 1. Create a blank node of the same type and copy all scalar own properties.
|
|
3380
|
+
var destNode = new srcNode.constructor(PDC);
|
|
3381
|
+
for (var n in srcNode) {
|
|
3382
|
+
if (hasOwn(srcNode, n)) {
|
|
3383
|
+
var v = srcNode[n];
|
|
3384
|
+
if (typeof v != 'object') {
|
|
3385
|
+
if (v != destNode[n]) {
|
|
3386
|
+
destNode[n] = v;
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3020
3389
|
}
|
|
3021
3390
|
}
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
if (node.childNodes) {
|
|
3025
|
-
node2.childNodes = new NodeList();
|
|
3026
|
-
}
|
|
3027
|
-
node2.ownerDocument = doc;
|
|
3028
|
-
switch (node2.nodeType) {
|
|
3029
|
-
case ELEMENT_NODE:
|
|
3030
|
-
var attrs = node.attributes;
|
|
3031
|
-
var attrs2 = (node2.attributes = new NamedNodeMap());
|
|
3032
|
-
var len = attrs.length;
|
|
3033
|
-
attrs2._ownerElement = node2;
|
|
3034
|
-
for (var i = 0; i < len; i++) {
|
|
3035
|
-
node2.setAttributeNode(cloneNode(doc, attrs.item(i), true));
|
|
3391
|
+
if (srcNode.childNodes) {
|
|
3392
|
+
destNode.childNodes = new NodeList();
|
|
3036
3393
|
}
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3394
|
+
destNode.ownerDocument = doc;
|
|
3395
|
+
// 2. Handle node-type-specific setup.
|
|
3396
|
+
// Attributes are not DOM children, so they are cloned inline here
|
|
3397
|
+
// rather than by walkDOM descent.
|
|
3398
|
+
// ATTRIBUTE_NODE forces deep=true so its own children are walked.
|
|
3399
|
+
var shouldDeep = deep;
|
|
3400
|
+
switch (destNode.nodeType) {
|
|
3401
|
+
case ELEMENT_NODE:
|
|
3402
|
+
var attrs = srcNode.attributes;
|
|
3403
|
+
var attrs2 = (destNode.attributes = new NamedNodeMap());
|
|
3404
|
+
var len = attrs.length;
|
|
3405
|
+
attrs2._ownerElement = destNode;
|
|
3406
|
+
for (var i = 0; i < len; i++) {
|
|
3407
|
+
destNode.setAttributeNode(cloneNode(doc, attrs.item(i), true));
|
|
3408
|
+
}
|
|
3409
|
+
break;
|
|
3410
|
+
case ATTRIBUTE_NODE:
|
|
3411
|
+
shouldDeep = true;
|
|
3412
|
+
}
|
|
3413
|
+
// 3. Attach to parent, or capture as the root of the cloned subtree.
|
|
3414
|
+
if (destParent !== null) {
|
|
3415
|
+
destParent.appendChild(destNode);
|
|
3416
|
+
} else {
|
|
3417
|
+
destRoot = destNode;
|
|
3418
|
+
}
|
|
3419
|
+
// 4. Return destNode as the context for children (causes walkDOM to descend),
|
|
3420
|
+
// or null to skip children (shallow clone).
|
|
3421
|
+
return shouldDeep ? destNode : null;
|
|
3422
|
+
},
|
|
3423
|
+
});
|
|
3424
|
+
return destRoot;
|
|
3049
3425
|
}
|
|
3050
3426
|
|
|
3051
3427
|
function __set__(object, key, value) {
|
|
3052
3428
|
object[key] = value;
|
|
3053
3429
|
}
|
|
3430
|
+
|
|
3431
|
+
// Returns a new array of direct Element children.
|
|
3432
|
+
// Passed to LiveNodeList to implement ParentNode.children.
|
|
3433
|
+
// https://dom.spec.whatwg.org/#dom-parentnode-children
|
|
3434
|
+
function childrenRefresh(node) {
|
|
3435
|
+
var ls = [];
|
|
3436
|
+
var child = node.firstChild;
|
|
3437
|
+
while (child) {
|
|
3438
|
+
if (child.nodeType === ELEMENT_NODE) {
|
|
3439
|
+
ls.push(child);
|
|
3440
|
+
}
|
|
3441
|
+
child = child.nextSibling;
|
|
3442
|
+
}
|
|
3443
|
+
return ls;
|
|
3444
|
+
}
|
|
3445
|
+
|
|
3054
3446
|
//do dynamic
|
|
3055
3447
|
try {
|
|
3056
3448
|
if (Object.defineProperty) {
|
|
@@ -3061,9 +3453,37 @@ try {
|
|
|
3061
3453
|
},
|
|
3062
3454
|
});
|
|
3063
3455
|
|
|
3456
|
+
/**
|
|
3457
|
+
* The text content of this node and its descendants.
|
|
3458
|
+
*
|
|
3459
|
+
* For {@link Element} and {@link DocumentFragment} nodes, returns the concatenation of the
|
|
3460
|
+
* `nodeValue` of every descendant text node, excluding processing instruction and comment
|
|
3461
|
+
* nodes. For all other node types, returns `nodeValue`.
|
|
3462
|
+
*
|
|
3463
|
+
* Setting `textContent` on an element or document fragment replaces all child nodes with a
|
|
3464
|
+
* single text node; on other nodes it sets `data`, `value`, and `nodeValue` directly.
|
|
3465
|
+
*
|
|
3466
|
+
* @type {string | null}
|
|
3467
|
+
* @see {@link https://dom.spec.whatwg.org/#dom-node-textcontent}
|
|
3468
|
+
*/
|
|
3064
3469
|
Object.defineProperty(Node.prototype, 'textContent', {
|
|
3065
3470
|
get: function () {
|
|
3066
|
-
|
|
3471
|
+
if (this.nodeType === ELEMENT_NODE || this.nodeType === DOCUMENT_FRAGMENT_NODE) {
|
|
3472
|
+
var buf = [];
|
|
3473
|
+
walkDOM(this, null, {
|
|
3474
|
+
enter: function (n) {
|
|
3475
|
+
if (n.nodeType === ELEMENT_NODE || n.nodeType === DOCUMENT_FRAGMENT_NODE) {
|
|
3476
|
+
return true; // enter children
|
|
3477
|
+
}
|
|
3478
|
+
if (n.nodeType === PROCESSING_INSTRUCTION_NODE || n.nodeType === COMMENT_NODE) {
|
|
3479
|
+
return null; // excluded from text content
|
|
3480
|
+
}
|
|
3481
|
+
buf.push(n.nodeValue);
|
|
3482
|
+
},
|
|
3483
|
+
});
|
|
3484
|
+
return buf.join('');
|
|
3485
|
+
}
|
|
3486
|
+
return this.nodeValue;
|
|
3067
3487
|
},
|
|
3068
3488
|
|
|
3069
3489
|
set: function (data) {
|
|
@@ -3086,23 +3506,21 @@ try {
|
|
|
3086
3506
|
},
|
|
3087
3507
|
});
|
|
3088
3508
|
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
}
|
|
3105
|
-
}
|
|
3509
|
+
Object.defineProperty(Element.prototype, 'children', {
|
|
3510
|
+
get: function () {
|
|
3511
|
+
return new LiveNodeList(this, childrenRefresh);
|
|
3512
|
+
},
|
|
3513
|
+
});
|
|
3514
|
+
Object.defineProperty(Document.prototype, 'children', {
|
|
3515
|
+
get: function () {
|
|
3516
|
+
return new LiveNodeList(this, childrenRefresh);
|
|
3517
|
+
},
|
|
3518
|
+
});
|
|
3519
|
+
Object.defineProperty(DocumentFragment.prototype, 'children', {
|
|
3520
|
+
get: function () {
|
|
3521
|
+
return new LiveNodeList(this, childrenRefresh);
|
|
3522
|
+
},
|
|
3523
|
+
});
|
|
3106
3524
|
|
|
3107
3525
|
__set__ = function (object, key, value) {
|
|
3108
3526
|
//console.log(value)
|
|
@@ -3132,4 +3550,5 @@ exports.NodeList = NodeList;
|
|
|
3132
3550
|
exports.Notation = Notation;
|
|
3133
3551
|
exports.Text = Text;
|
|
3134
3552
|
exports.ProcessingInstruction = ProcessingInstruction;
|
|
3553
|
+
exports.walkDOM = walkDOM;
|
|
3135
3554
|
exports.XMLSerializer = XMLSerializer;
|