diff --git a/README.md b/README.md index 20543f7..d7fc7d1 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,11 @@ know the name of the 'children' attribute). }; var tree = Arborel.parse(wikipediaJsCategory, 'subcategories'); + +Also several children (or even the whole tree) can be added at the same time (syntax is similar as parse) + + var tree = new Arboreal(); + tree.children[1].appendChildren({category:'C#', subitems:[{category:'WPF'}]}, "subitems" ); ### Traversal @@ -98,7 +103,15 @@ rewritten as: for (i = 1; i <= this.depth; i++) depth += ">>"; console.info([depth, this.data.category].join(" ")); } - + +Arboreal object can be iterated up to the root using method 'bubbleUp': + + tree.children[2].children[0].bubbleUp(iterator) + + // => >>>> JavaScript dialect engines + // >> JavaScript programming languages family + // JavaScript + ### Search In order to search for a single node into an arboreal object, one can use the `find` @@ -122,7 +135,7 @@ it will try to find a node by id. While traversing a tree, nodes can be deleted by calling the `remove` method on the node object bound to the iterator function. - tree.length + tree.getLength(); // => 7 @@ -133,7 +146,7 @@ method on the node object bound to the iterator function. } }); - tree.length; + tree.getLength(); // 5 diff --git a/lib/arboreal.js b/lib/arboreal.js index 0135c0c..5836453 100644 --- a/lib/arboreal.js +++ b/lib/arboreal.js @@ -1,8 +1,13 @@ -!function () { - 'use strict'; +(function () { function include (array, item) { - return array.indexOf(item) > -1; + return indexOf(array, item) > -1; + } + function indexOf(array, item) { + for (var i = 0, j = array.length; i < j; i++) { + if (array[i] === item) { return i; } + } + return -1; } function _traverseDown (context, iterator) { @@ -41,6 +46,14 @@ } } + function _bubbleUp (context, iterator) { + var node; + + while (context) { + if ( iterator(context) === false ) return; + context = context.parent; + } + } function _traverse (context, iterator, callback) { var visited = [], @@ -96,7 +109,7 @@ this.children = []; } - Arboreal.parse = function (object, childrenAttr) { + Arboreal.parse = function (object, childrenAttr, parent) { var root, getNodeData = function (node) { var attr, nodeData = {}; for (attr in node) { @@ -119,9 +132,9 @@ walkDown(node[childrenAttr][i], newNode); } } - })(object); + })(object, parent); - return root; + return root || parent; }; @@ -131,6 +144,10 @@ return this; }; + Arboreal.prototype.appendChildren = function (data, childrenAttr) { + return Arboreal.parse(data, childrenAttr, this); + }; + Arboreal.prototype.removeChild = function (arg) { if (typeof arg === 'number' && this.children[arg]) { return this.children.splice(arg, 1).shift(); @@ -171,6 +188,10 @@ _traverse(this, iterator, _traverseDown); }; + Arboreal.prototype.bubbleUp = function (iterator) { + _traverse(this, iterator, _bubbleUp); + }; + Arboreal.prototype.toString = function () { var lines = []; @@ -235,16 +256,15 @@ }); return nodeList; }; - - Arboreal.prototype.__defineGetter__("length", function () { + + Arboreal.prototype.getLength = function () { return this.toArray().length; - }); - - + }; + if (typeof module !== 'undefined' && module.exports) { module.exports = Arboreal; } else { this.Arboreal = Arboreal; } -}(this); +}(this)); diff --git a/test/specs/arboreal-spec.js b/test/specs/arboreal-spec.js index 3be1055..fe171ab 100644 --- a/test/specs/arboreal-spec.js +++ b/test/specs/arboreal-spec.js @@ -28,7 +28,7 @@ describe("Arboreal", function () { }); - it("::parse()", function () { + describe("::parse()", function () { var data = { category: 'JavaScript', subcategories: [ @@ -43,8 +43,29 @@ describe("Arboreal", function () { {category: 'JavaScript based HTML editors'} ] }; - var tree = Arboreal.parse(data, 'subcategories'); - expect(tree.length).toBe(7); + it("default parent", function () { + var tree = Arboreal.parse(data, 'subcategories'); + expect(tree.getLength()).toBe(7); + }); + it("Arboreal parent", function () { + var subData = { + category: 'C#', + subcategories: [ + {category: 'WinForms'}, + {category: 'WPF', + subcategories: [{ + category: 'XAML markup' + }] + }, + + ] + }; + var tree = Arboreal.parse(data, 'subcategories'); + var biggerTree = Arboreal.parse(subData, 'subcategories', tree.children[1]); + expect(biggerTree.children[0].getLength()).toBe(4); + expect(tree.getLength()).toBe(11); + expect(tree.children[1].children[0].data.category).toBe('C#'); + }); }); it("#appendChild(null, 'bla')", function () { @@ -54,7 +75,24 @@ describe("Arboreal", function () { expect(tree.children[0].id).toBe("myId"); expect(tree.children[0].parent).toEqual(tree); }); - + + it("#appendChildren to root", function () { + var tree = new Arboreal(); + tree.appendChildren({testAttr:'subroot', subitems:[{testAttr:'firstChild'}]}, "subitems" ); + expect(tree.getLength()).toBe(3); + expect(tree.children[0].data.testAttr).toBe("subroot"); + expect(tree.children[0].children[0].data.testAttr).toEqual('firstChild'); + }); + + it("#appendChildren to children", function () { + var tree = new Arboreal(); + tree.appendChild().appendChild() + tree.children[1].appendChildren({testAttr:'secondchild', subitems:[{testAttr:'third child'}]}, "subitems"); + expect(tree.getLength()).toBe(5); + expect(tree.children[1].children[0].data.testAttr).toBe("secondchild"); + expect(tree.children[1].children[0].children[0].data.testAttr).toEqual('third child'); + }); + it("#removeChild()", function () { var tree = new Arboreal(), thirdChild, @@ -131,7 +169,40 @@ describe("Arboreal", function () { }); expect(treeIds.length).toBe(3); }); + + it("#bubbleUp", function () { + var tree = new Arboreal(), + callbackCounter=0, + treeIds = [], + spy1 = jasmine.createSpy(), + spy2 = jasmine.createSpy(), + appendId = function (node) { + treeIds.push(node.id); + }; + + + appendSomeChildren(tree); + tree.bubbleUp(spy1); + //should iterate over itself + expect(spy1.callCount).toBe(1); + + //should bubble up to the root + tree.children[0].children[1].bubbleUp(spy2); + expect(spy2.callCount).toBe(1+1+1); + + //should bubble up in the right order + tree.children[0].children[1].bubbleUp(appendId); + expect(treeIds.shift()).toBe("0/0/1"); + + treeIds = []; + //should break when iterator returns a falsy value + tree.bubbleUp(function (node) { + appendId(node); + if (treeIds.length === 2) return false; + }); + expect(treeIds.length).toBe(1); + }); it("#toArray", function () { var tree = new Arboreal();