From c740c0ec92aae1391c0d16eb43ccfefa51b86f31 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 11 Feb 2016 09:32:58 +0100 Subject: [PATCH] add stuff for walking the tree --- www/common/treesome.js | 215 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 www/common/treesome.js diff --git a/www/common/treesome.js b/www/common/treesome.js new file mode 100644 index 000000000..0bf912b5d --- /dev/null +++ b/www/common/treesome.js @@ -0,0 +1,215 @@ +define([], function () { + var tree = {}; + + tree.some = function (branch, predicate) { + // take the index of the last element in the current branch + var last = branch.childElementCount - 1; + + // it might be a leaf node + if (last < 0) { return false; } + + // otherwise it has children + while (last >= 0) { + // check from back to front + + // check the node's children (depth first) + // if the predicate tests true, return true + if (tree.some(branch.children[last], predicate)) { + return true; + } // otherwise none of the nodes inside it matched. + + // check the node itself + if (predicate(branch.children[last], last)) { + return true; + } + last--; + } + return false; + }; + + tree.someIncludingText = function (branch, predicate) { + // take the index of the last element in the current branch + var last = branch.childNodes.length - 1; + + // it might be a leaf node + if (last < 0) { return false; } + + // otherwise it has children + while (last >= 0) { + // check from back to front + + // check the node's children (depth first) + // if the predicate tests true, return true + if (tree.someIncludingText(branch.childNodes[last], predicate)) { + return true; + } // otherwise none of the nodes inside it matched. + + // check the node itself + if (predicate(branch.childNodes[last], last)) { + return true; + } + last--; + } + return false; + }; + + tree.findSameHierarchy = function (list, ancestor) { + var i = 0; + var success = true; + var last = list.length - 1; + var el; + + tree.someIncludingText(ancestor, function (e) { + // don't out of bounds + if (i > last) { + // unsuccessful + success = false; + return true; + } + + if (list[i] === (e.tagName||e.nodeName)) { + + if (i === last) { + el = e; + return true; + } + i++; + } else { + // hierarchy has changed, what should we do? + success = false; + return true; // terminate + } + }); + return success? el: false; + }; + + var indexOfNode = tree.indexOfNode = function (el) { + if (!(el && el.parentNode)) { + console.log("No parentNode found!"); + } + return Array.prototype.indexOf.call(el.parentNode.childNodes, el); + }; + + var siblingCount = tree.siblingCount = function (el) { + return el.parentNodes.childNodes.length; + }; + + var deepestNode = tree.deepestNode = function (el) { + var deepest = el.childNodes.length; + if (!deepest) { // no children + return el; // return the element + } else { + return deepestNode(el.childNodes[deepest - 1]); + } + }; + + var leftMostNode = tree.leftMostNode = function (el) { + var left = el.childNodes[0]; + if (el.childNodes.length) { + return leftMostNode(el.childNodes[0]); + } else { + return el; + } + }; + + var previousNode = tree.previousNode = function (el, root) { + if (!el || el === root) { return null; } + + var i = indexOfNode(el); + if (!el.parentNode) { return null; } + if (i === 0) { + + if (root && el.parentNode === root.childNodes[0]) { return null; } + return deepestNode(previousNode(el.parentNode)); + } else { + return el.parentNode.childNodes[i-1]; + } + }; + + /* includes text elements */ + var nextNode = tree.nextNode = function (el, root) { + if (!el || el === root) { + return null; + } + var i = indexOfNode(el) + 1, // the index of the next node + l = el.parentNode.childNodes.length; + if (i === l) { // out of bounds + return leftMostNode(nextNode(el.parentNode, root)); + } else { + return el.parentNode.childNodes[i]; + } + }; + + tree.recoverElement = function (el, root) { + var tags = tree.tagsUntilElement(el, root); + var found = tree.findSameHierarchy(tags, root); + var isSame = found === el; + console.log("found an equivalent node: %s", isSame); + return found; + }; + + tree.tagsUntilElement = function (el, ancestor) { + var list = []; + tree.someIncludingText(ancestor, function (e, index) { + list.push(e.tagName||e.nodeName); + return e === el; + }); + return list; + }; + + tree.distanceFromEnd = function (el, ancestor) { + var i = 0, success = false; + // FIXME can't be trusted if element is not found. + tree.some(ancestor, function (e, index) { + return (++i, e === el); + }); + return i; + }; + + tree.nthFromEnd = function (n, ancestor) { + var el = false, i = 0; + tree.some(ancestor, function (e) { + if (i > n) { + // terminate + return true; + } else { + return (++i === n) ? + ((el = e), true): + false; + } + }); + return el; + }; + + tree.contains = function (el, ancestor) { + ancestor = ancestor || inner; + return el && ancestor.contains && ancestor.contains(el); + }; + + tree.check = function (el, ancestor) { + return tree.nthFromEnd(Tree.distanceFromEnd(el, ancestor), ancestor) === el; + }; + + // TODO see if we can use this + tree.getNext = function (node, parent) { + if (node.firstChild) { return node.nextSibling; } + do { + if(node.nextSibling) { return node.nextSibling; } + node = node.parentNode; + } while (node && node !== parent); + }; + + // FIXME remove + tree.flatten = function (el, ancestor, cursorContainer) { + var list = []; + for (var el = ancestor; el = tree.getNext(next, ancestor);) { + if (cf || el === cursorContainer) { + cf = true; + list.push(el.tagName); + } + } + return list; + }; + + return tree; +});