helpers.js
5.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.uniqueSort = exports.compareDocumentPosition = exports.DocumentPosition = exports.removeSubsets = void 0;
var domhandler_1 = require("domhandler");
/**
* Given an array of nodes, remove any member that is contained by another.
*
* @category Helpers
* @param nodes Nodes to filter.
* @returns Remaining nodes that aren't subtrees of each other.
*/
function removeSubsets(nodes) {
var idx = nodes.length;
/*
* Check if each node (or one of its ancestors) is already contained in the
* array.
*/
while (--idx >= 0) {
var node = nodes[idx];
/*
* Remove the node if it is not unique.
* We are going through the array from the end, so we only
* have to check nodes that preceed the node under consideration in the array.
*/
if (idx > 0 && nodes.lastIndexOf(node, idx - 1) >= 0) {
nodes.splice(idx, 1);
continue;
}
for (var ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
if (nodes.includes(ancestor)) {
nodes.splice(idx, 1);
break;
}
}
}
return nodes;
}
exports.removeSubsets = removeSubsets;
/**
* @category Helpers
* @see {@link http://dom.spec.whatwg.org/#dom-node-comparedocumentposition}
*/
var DocumentPosition;
(function (DocumentPosition) {
DocumentPosition[DocumentPosition["DISCONNECTED"] = 1] = "DISCONNECTED";
DocumentPosition[DocumentPosition["PRECEDING"] = 2] = "PRECEDING";
DocumentPosition[DocumentPosition["FOLLOWING"] = 4] = "FOLLOWING";
DocumentPosition[DocumentPosition["CONTAINS"] = 8] = "CONTAINS";
DocumentPosition[DocumentPosition["CONTAINED_BY"] = 16] = "CONTAINED_BY";
})(DocumentPosition = exports.DocumentPosition || (exports.DocumentPosition = {}));
/**
* Compare the position of one node against another node in any other document.
* The return value is a bitmask with the values from {@link DocumentPosition}.
*
* Document order:
* > There is an ordering, document order, defined on all the nodes in the
* > document corresponding to the order in which the first character of the
* > XML representation of each node occurs in the XML representation of the
* > document after expansion of general entities. Thus, the document element
* > node will be the first node. Element nodes occur before their children.
* > Thus, document order orders element nodes in order of the occurrence of
* > their start-tag in the XML (after expansion of entities). The attribute
* > nodes of an element occur after the element and before its children. The
* > relative order of attribute nodes is implementation-dependent.
*
* Source:
* http://www.w3.org/TR/DOM-Level-3-Core/glossary.html#dt-document-order
*
* @category Helpers
* @param nodeA The first node to use in the comparison
* @param nodeB The second node to use in the comparison
* @returns A bitmask describing the input nodes' relative position.
*
* See http://dom.spec.whatwg.org/#dom-node-comparedocumentposition for
* a description of these values.
*/
function compareDocumentPosition(nodeA, nodeB) {
var aParents = [];
var bParents = [];
if (nodeA === nodeB) {
return 0;
}
var current = (0, domhandler_1.hasChildren)(nodeA) ? nodeA : nodeA.parent;
while (current) {
aParents.unshift(current);
current = current.parent;
}
current = (0, domhandler_1.hasChildren)(nodeB) ? nodeB : nodeB.parent;
while (current) {
bParents.unshift(current);
current = current.parent;
}
var maxIdx = Math.min(aParents.length, bParents.length);
var idx = 0;
while (idx < maxIdx && aParents[idx] === bParents[idx]) {
idx++;
}
if (idx === 0) {
return DocumentPosition.DISCONNECTED;
}
var sharedParent = aParents[idx - 1];
var siblings = sharedParent.children;
var aSibling = aParents[idx];
var bSibling = bParents[idx];
if (siblings.indexOf(aSibling) > siblings.indexOf(bSibling)) {
if (sharedParent === nodeB) {
return DocumentPosition.FOLLOWING | DocumentPosition.CONTAINED_BY;
}
return DocumentPosition.FOLLOWING;
}
if (sharedParent === nodeA) {
return DocumentPosition.PRECEDING | DocumentPosition.CONTAINS;
}
return DocumentPosition.PRECEDING;
}
exports.compareDocumentPosition = compareDocumentPosition;
/**
* Sort an array of nodes based on their relative position in the document and
* remove any duplicate nodes. If the array contains nodes that do not belong to
* the same document, sort order is unspecified.
*
* @category Helpers
* @param nodes Array of DOM nodes.
* @returns Collection of unique nodes, sorted in document order.
*/
function uniqueSort(nodes) {
nodes = nodes.filter(function (node, i, arr) { return !arr.includes(node, i + 1); });
nodes.sort(function (a, b) {
var relative = compareDocumentPosition(a, b);
if (relative & DocumentPosition.PRECEDING) {
return -1;
}
else if (relative & DocumentPosition.FOLLOWING) {
return 1;
}
return 0;
});
return nodes;
}
exports.uniqueSort = uniqueSort;
//# sourceMappingURL=helpers.js.map