Skip to content

Commit

Permalink
Imports the project to github.
Browse files Browse the repository at this point in the history
  • Loading branch information
joaonuno committed Jun 19, 2013
1 parent c2bc358 commit a571de2
Show file tree
Hide file tree
Showing 4 changed files with 645 additions and 0 deletions.
26 changes: 26 additions & 0 deletions .jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"bitwise": true,
"curly": true,
"eqeqeq": true,
"forin": true,
"immed": true,
"indent": 2,
"latedef": true,
"newcap": true,
"noarg": true,
"noempty": true,
"nonew": true,
"undef": true,
"unused": true,
"strict": true,
"trailing": true,
"maxlen": 120,
"browser": true,
"devel": true,
"node": true,
"white": true,
"onevar": true,
"globals": {
"require": false
}
}
204 changes: 204 additions & 0 deletions src/TreeModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
(function () {
/* global define */
'use strict';

var walkStrategies;

walkStrategies = {};

function TreeModel(config) {
config = config || {};
this.config = config;
this.config.childrenPropertyName = config.childrenPropertyName || 'children';
this.config.modelComparatorFn = config.modelComparatorFn || function () {
return 0;
};
}

TreeModel.prototype.parse = function (model) {
var i, childCount, node;

if (!(model instanceof Object)) {
throw new TypeError('Model must be of type object.');
}

node = new Node(this.config, model);
if (model[this.config.childrenPropertyName] instanceof Array) {
model[this.config.childrenPropertyName].sort(this.config.modelComparatorFn);
for (i = 0, childCount = model[this.config.childrenPropertyName].length; i < childCount; i++) {
_addChildToNode(node, this.parse(model[this.config.childrenPropertyName][i]));
}
}
return node;
};

function _addChildToNode(node, child) {
child.parent = node;
node.children.push(child);
return child;
}

function Node(config, model) {
this.config = config;
this.model = model;
this.children = [];
}

Node.prototype.isRoot = function () {
return this.parent === undefined;
};

Node.prototype.addChild = function (child) {
var index;
child.parent = this;
if (!(this.model[this.config.childrenPropertyName] instanceof Array)) {
this.model[this.config.childrenPropertyName] = [];
}
this.model[this.config.childrenPropertyName].push(child.model);
// TODO Refactor this to avoid using sort
this.model[this.config.childrenPropertyName].sort(this.config.modelComparatorFn);
index = this.model[this.config.childrenPropertyName].lastIndexOf(child);
this.children.splice(index, 0, child);
return child;
};

Node.prototype.getPath = function () {
var path = [];
(function addToPath(node) {
path.unshift(node);
if (!node.isRoot()) {
addToPath(node.parent);
}
})(this);
return path;
};

/**
* Parse the arguments of traversal functions. These functions can take one optional
* first argument which is an options object. If present, this object will be stored
* in args.options. The only mandatory argument is the callback function which can
* appear in the first or second position (if an options object is given). This
* function will be saved to args.fn. The last optional argument is the context on
* which the callback function will be called. It will be available in args.ctx.
*
* @return Parsed arguments.
*/
function parseArgs() {
var args = {};
if (arguments.length === 1) {
args.fn = arguments[0];
} else if (arguments.length === 2) {
if (typeof arguments[0] === 'function') {
args.fn = arguments[0];
args.ctx = arguments[1];
} else {
args.options = arguments[0];
args.fn = arguments[1];
}
} else {
args.options = arguments[0];
args.fn = arguments[1];
args.ctx = arguments[2];
}
args.options = args.options || {};
if (!args.options.strategy) {
args.options.strategy = 'depthFirstPreOrder';
}
if (!walkStrategies[args.options.strategy]) {
throw new Error('Unknown tree walk strategy.');
}
return args;
}

Node.prototype.walk = function () {
var args;
args = parseArgs.apply(this, arguments);
walkStrategies[args.options.strategy].call(this, args.fn, args.ctx);
};

walkStrategies.depthFirstPreOrder = function depthFirstPreOrder(callback, context) {
var i, childCount, keepGoing;
keepGoing = callback.call(context, this);
for (i = 0, childCount = this.children.length; i < childCount; i++) {
if (keepGoing === false) {
return false;
}
keepGoing = depthFirstPreOrder.call(this.children[i], callback, context);
}
return keepGoing;
};

walkStrategies.depthFirstPostOrder = function depthFirstPostOrder(callback, context) {
var i, childCount, keepGoing;
for (i = 0, childCount = this.children.length; i < childCount; i++) {
keepGoing = depthFirstPostOrder.call(this.children[i], callback, context);
if (keepGoing === false) {
return false;
}
}
keepGoing = callback.call(context, this);
return keepGoing;
};

walkStrategies.breadthFirst = function breadthFirst(callback, context) {
var queue = [this];
(function processQueue() {
var i, childCount, node;
if (queue.length === 0) {
return;
}
node = queue.shift();
for (i = 0, childCount = node.children.length; i < childCount; i++) {
queue.push(node.children[i]);
}
if (callback.call(context, node) !== false) {
processQueue();
}
})();
};

Node.prototype.all = function () {
var args, all = [];
args = parseArgs.apply(this, arguments);
walkStrategies[args.options.strategy].call(this, function (node) {
if (args.fn.call(args.ctx, node)) {
all.push(node);
}
}, args.ctx);
return all;
};

Node.prototype.first = function () {
var args, first;
args = parseArgs.apply(this, arguments);
walkStrategies[args.options.strategy].call(this, function (node) {
if (args.fn.call(args.ctx, node)) {
first = node;
return false;
}
}, args.ctx);
return first;
};

Node.prototype.drop = function () {
var indexOfChild;
if (!this.isRoot()) {
indexOfChild = this.parent.children.indexOf(this);
this.parent.children.splice(indexOfChild, 1);
this.parent.model.children.splice(indexOfChild, 1);
this.parent = undefined;
delete this.parent;
}
return this;
};

if (typeof exports === 'object') {
module.exports = TreeModel;
}
else if (typeof define === 'function' && define.amd) {
define(TreeModel);
}
else {
this.TreeModel = TreeModel;
}
}).call(this);
3 changes: 3 additions & 0 deletions test/mocha.opts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
--reporter spec
--bail
--check-leaks
Loading

0 comments on commit a571de2

Please sign in to comment.