UNPKG

3.11 kBJavaScriptView Raw
1var _ = require('lodash');
2
3function decorateTree(tree) {
4 tree.lca = function lowestCommonAncestor(nodes) {
5 var parentNodesInCommon = _.chain(nodes)
6 .map(function (node) {
7 if(!node || !_.isFunction(node.getPath)) {
8 throw new Error('Cannot calculate lca with a null node');
9 }
10 return node.getPath();
11 })
12 .reduce(function (acc, nextPath) {
13 return _.intersection(acc, nextPath)
14 })
15 .value();
16 return parentNodesInCommon.pop();
17 };
18
19 tree.pathBetween = function pathBetweenNodes(from, to) {
20
21 var lca, fromPath, toPath, fromLcaIdx, toLcaIdx, pathBetween;
22
23 // find the lowest commen ancestor
24 lca = tree.lca([from, to]);
25
26 // get the full path from -> root, and reverse it
27 fromPath = _(from.getPath().reverse());
28
29 // get the full path to -> root
30 toPath = _(to.getPath());
31
32 // find the index of lca in fromPath and toPath
33 fromLcaIdx = fromPath.findIndex(lca);
34 toLcaIdx = toPath.findIndex(lca);
35
36 // slice and combine the arrays to get the path between
37 pathBetween = fromPath.slice(0, fromLcaIdx).concat(toPath.slice(toLcaIdx).value());
38
39 return pathBetween.value();
40 };
41}
42
43function addPrototypeDecorations(tree) {
44 var prototree = Object.getPrototypeOf(tree);
45
46 if(!shouldDecorateTreePrototype(prototree)) {
47 return;
48 }
49
50 prototree.depth = function calculateEffectiveNodeDepth(includeCompressedNodes) {
51 var path = this.getPath()
52 , depth = path.length - 1
53 , compressedDepth;
54
55 if(includeCompressedNodes) {
56 compressedDepth = _.reduce(path, function (acc, n) {
57 return acc + (n.compressedNodes ? n.compressedNodes.length : 0);
58 }, 0);
59 depth += compressedDepth;
60 }
61
62 return depth;
63 };
64
65 prototree.pathTo = function(to) {
66 return tree.pathBetween(this, to);
67 };
68
69 prototree.leafNodes = function findAllLeafNodes() {
70 return this.all(function (node) { return !node.hasChildren(); });
71 };
72
73 prototree.lcaWith = function(otherNodes) {
74 var nodes = _.clone(otherNodes);
75 nodes.push(this);
76 return tree.lca(nodes);
77 };
78
79 prototree.filterWalk = function (callback) {
80 function filterWalkRecursive(node) {
81 var evaluateChildren = callback(node);
82 if(evaluateChildren && _.isArray(node.children)) {
83 _.forEach(node.children, filterWalkRecursive)
84 }
85 }
86 return filterWalkRecursive(this);
87 }
88}
89
90function shouldDecorateTreePrototype(prototype) {
91 return !(
92 _.isFunction(prototype.depth) &&
93 _.isFunction(prototype.pathTo) &&
94 _.isFunction(prototype.leafNodes) &&
95 _.isFunction(prototype.lcaWith)
96 )
97}
98
99function indexTree(tree, attrs) {
100 tree.indices = _.chain(attrs)
101 .map(function (attr) {
102 var result = {_attr: attr};
103 tree.walk(function (node) {
104 var key = node.model[attr];
105 if(! _.isUndefined(key) ) {
106 result[key] = node;
107 }
108 });
109 return result;
110 })
111 .indexBy('_attr')
112 .value();
113}
114
115module.exports = {
116 decorateTree: decorateTree,
117 addPrototypeDecorations: addPrototypeDecorations,
118 indexTree: indexTree
119};
\No newline at end of file