| Leo Repp | 58b9f11 | 2021-11-22 11:57:47 +0100 | [diff] [blame^] | 1 | // removeSubsets |
| 2 | // Given an array of nodes, remove any member that is contained by another. |
| 3 | exports.removeSubsets = function(nodes) { |
| 4 | var idx = nodes.length, node, ancestor, replace; |
| 5 | |
| 6 | // Check if each node (or one of its ancestors) is already contained in the |
| 7 | // array. |
| 8 | while (--idx > -1) { |
| 9 | node = ancestor = nodes[idx]; |
| 10 | |
| 11 | // Temporarily remove the node under consideration |
| 12 | nodes[idx] = null; |
| 13 | replace = true; |
| 14 | |
| 15 | while (ancestor) { |
| 16 | if (nodes.indexOf(ancestor) > -1) { |
| 17 | replace = false; |
| 18 | nodes.splice(idx, 1); |
| 19 | break; |
| 20 | } |
| 21 | ancestor = ancestor.parent; |
| 22 | } |
| 23 | |
| 24 | // If the node has been found to be unique, re-insert it. |
| 25 | if (replace) { |
| 26 | nodes[idx] = node; |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | return nodes; |
| 31 | }; |
| 32 | |
| 33 | // Source: http://dom.spec.whatwg.org/#dom-node-comparedocumentposition |
| 34 | var POSITION = { |
| 35 | DISCONNECTED: 1, |
| 36 | PRECEDING: 2, |
| 37 | FOLLOWING: 4, |
| 38 | CONTAINS: 8, |
| 39 | CONTAINED_BY: 16 |
| 40 | }; |
| 41 | |
| 42 | // Compare the position of one node against another node in any other document. |
| 43 | // The return value is a bitmask with the following values: |
| 44 | // |
| 45 | // document order: |
| 46 | // > There is an ordering, document order, defined on all the nodes in the |
| 47 | // > document corresponding to the order in which the first character of the |
| 48 | // > XML representation of each node occurs in the XML representation of the |
| 49 | // > document after expansion of general entities. Thus, the document element |
| 50 | // > node will be the first node. Element nodes occur before their children. |
| 51 | // > Thus, document order orders element nodes in order of the occurrence of |
| 52 | // > their start-tag in the XML (after expansion of entities). The attribute |
| 53 | // > nodes of an element occur after the element and before its children. The |
| 54 | // > relative order of attribute nodes is implementation-dependent./ |
| 55 | // Source: |
| 56 | // http://www.w3.org/TR/DOM-Level-3-Core/glossary.html#dt-document-order |
| 57 | // |
| 58 | // @argument {Node} nodaA The first node to use in the comparison |
| 59 | // @argument {Node} nodeB The second node to use in the comparison |
| 60 | // |
| 61 | // @return {Number} A bitmask describing the input nodes' relative position. |
| 62 | // See http://dom.spec.whatwg.org/#dom-node-comparedocumentposition for |
| 63 | // a description of these values. |
| 64 | var comparePos = exports.compareDocumentPosition = function(nodeA, nodeB) { |
| 65 | var aParents = []; |
| 66 | var bParents = []; |
| 67 | var current, sharedParent, siblings, aSibling, bSibling, idx; |
| 68 | |
| 69 | if (nodeA === nodeB) { |
| 70 | return 0; |
| 71 | } |
| 72 | |
| 73 | current = nodeA; |
| 74 | while (current) { |
| 75 | aParents.unshift(current); |
| 76 | current = current.parent; |
| 77 | } |
| 78 | current = nodeB; |
| 79 | while (current) { |
| 80 | bParents.unshift(current); |
| 81 | current = current.parent; |
| 82 | } |
| 83 | |
| 84 | idx = 0; |
| 85 | while (aParents[idx] === bParents[idx]) { |
| 86 | idx++; |
| 87 | } |
| 88 | |
| 89 | if (idx === 0) { |
| 90 | return POSITION.DISCONNECTED; |
| 91 | } |
| 92 | |
| 93 | sharedParent = aParents[idx - 1]; |
| 94 | siblings = sharedParent.children; |
| 95 | aSibling = aParents[idx]; |
| 96 | bSibling = bParents[idx]; |
| 97 | |
| 98 | if (siblings.indexOf(aSibling) > siblings.indexOf(bSibling)) { |
| 99 | if (sharedParent === nodeB) { |
| 100 | return POSITION.FOLLOWING | POSITION.CONTAINED_BY; |
| 101 | } |
| 102 | return POSITION.FOLLOWING; |
| 103 | } else { |
| 104 | if (sharedParent === nodeA) { |
| 105 | return POSITION.PRECEDING | POSITION.CONTAINS; |
| 106 | } |
| 107 | return POSITION.PRECEDING; |
| 108 | } |
| 109 | }; |
| 110 | |
| 111 | // Sort an array of nodes based on their relative position in the document and |
| 112 | // remove any duplicate nodes. If the array contains nodes that do not belong |
| 113 | // to the same document, sort order is unspecified. |
| 114 | // |
| 115 | // @argument {Array} nodes Array of DOM nodes |
| 116 | // |
| 117 | // @returns {Array} collection of unique nodes, sorted in document order |
| 118 | exports.uniqueSort = function(nodes) { |
| 119 | var idx = nodes.length, node, position; |
| 120 | |
| 121 | nodes = nodes.slice(); |
| 122 | |
| 123 | while (--idx > -1) { |
| 124 | node = nodes[idx]; |
| 125 | position = nodes.indexOf(node); |
| 126 | if (position > -1 && position < idx) { |
| 127 | nodes.splice(idx, 1); |
| 128 | } |
| 129 | } |
| 130 | nodes.sort(function(a, b) { |
| 131 | var relative = comparePos(a, b); |
| 132 | if (relative & POSITION.PRECEDING) { |
| 133 | return -1; |
| 134 | } else if (relative & POSITION.FOLLOWING) { |
| 135 | return 1; |
| 136 | } |
| 137 | return 0; |
| 138 | }); |
| 139 | |
| 140 | return nodes; |
| 141 | }; |