helpers.js 3.8 KB

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