var Task = function (options) { var _options = options || {}; var _description = _options.description || ""; var _assignedTo = _options.assignedTo || ""; var _duration = _options.duration || 0.0; var _tasks = []; return { description: _description, assignedTo: _assignedTo, duration: _duration, tasks: _tasks }; }; var TreeNode = function (options) { var _options = options || {}; var _value = _options.value || {}; var _children = _options.children || []; var _parent = _options.parent || null; // Public methods var isRoot = function () { return (_parent == null); }; var level = function () { return isRoot() ? 0 : _parent.level() + 1; }; var addChild = function (value) { var childNode = new TreeNode({ value: value, parent: this }); _children.push(childNode); return childNode; }; return { value: _value, children: _children, parent: _parent, // public methods isRoot: isRoot, level: level, addChild: addChild }; }; var treeFuncs = function () { var buildTaskTree = function (sourceTasks) { var tree = new TreeNode({ value: "ROOT" }); var taskStack = []; var treeNodeTargetStack = []; taskStack.push(sourceTasks); treeNodeTargetStack.push(tree); while (taskStack.length > 0) { var currentTask = taskStack.pop(); var targetNode = treeNodeTargetStack.pop(); var length = currentTask.length; for (var i = 0; i < length; i++) { var task = currentTask[i]; var childNode = targetNode.addChild(task); if (task.tasks.length > 0) { taskStack.push(task.tasks); treeNodeTargetStack.push(childNode); } } } return tree; }; var traverseAndApply = function (treeNode, func, functionArgs) { var treeNodeStack = []; var rootArray = []; rootArray.push(treeNode); treeNodeStack.push(rootArray); while (treeNodeStack.length > 0) { var currentNodeList = treeNodeStack.pop(); var length = currentNodeList.length; for (var i = 0; i < length; i++) { var currentNode = currentNodeList[i]; if (currentNode.parent != null) { func(currentNode.value, currentNode, functionArgs); } if (currentNode.children.length > 0) { treeNodeStack.push(currentNode.children); } } } }; return { buildTaskTree: buildTaskTree, traverseAndApply: traverseAndApply }; }; var Controller = function () { var _tasks = []; var _filterResultTasks = []; var _treeFuncs = new treeFuncs(); var initTasks = function () { // 4 "Level 1 _tasks.push(new Task({ description: "Discuss Product Concept", assignedTo: "Jackie Chan; Frodo; Sauroman", duration: 4 })); _tasks.push(new Task({ description: "Create Mockups", assignedTo: "Frodo; Gimili;", duration: 8 })); _tasks.push(new Task({ description: "Setup POC environment", assignedTo: "Frodo; Gandalf", duration: 12 })); _tasks.push(new Task({ description: "POC Development", assignedTo: "Gandalf; Ilsildor; Aragorn", duration: 46 })); // Level 2 _tasks[1].tasks.push(new Task({ description: "Paper walk through", assignedTo: "Gimili;", duration: 6 })); _tasks[1].tasks.push(new Task({ description: "Convert to HTML", assignedTo: "Gimili;", duration: 2 })); _tasks[2].tasks.push(new Task({ description: "Determine SQL or NoSQL database", assignedTo: "Gandalf; Frodo", duration: 2 })); _tasks[2].tasks.push(new Task({ description: "Setup Heroku", assignedTo: "Gandalf; Frodo", duration: 5 })); _tasks[2].tasks.push(new Task({ description: "Setup Nodejs", assignedTo: "Gandalf; Frodo", duration: 5 })); _tasks[3].tasks.push(new Task({ description: "Create Backlog", assignedTo: "Ilsildor;", duration: 10 })); _tasks[3].tasks.push(new Task({ description: "TDD Sprint", assignedTo: "Aragorn;", duration: 10 })); _tasks[3].tasks.push(new Task({ description: "QA Review", assignedTo: "Gandalf;", duration: 10 })); // Level 3 for 'Create Backlog' _tasks[3].tasks[0].tasks.push(new Task({ description: "Confirm users stories", assignedTo: "Isildor; Jackie Chan", duration: 2 })); _tasks[3].tasks[0].tasks.push(new Task({ description: "Check Team schedule", assignedTo: "Isildor; ", duration: 3 })); // Level 3 for 'TDD Sprint' _tasks[3].tasks[1].tasks.push(new Task({ description: "Confirm users stories", assignedTo: "Isildor; Jackie Chan", duration: 2 })); _tasks[3].tasks[0].tasks.push(new Task({ description: "Check Team schedule", assignedTo: "Isildor; ", duration: 3 })); }; var filterTopLevel = function(){ var treeNode = _treeFuncs.buildTaskTree(_tasks); var matchTopLevelArgs = {}; matchTopLevelArgs.results = [] var matchTopLevelFunc = function(value, node, funcArgs){ if(node.level() == 1){ funcArgs.results.push(value); } }; _treeFuncs.traverseAndApply(treeNode, matchTopLevelFunc, matchTopLevelArgs); var template = Handlebars.compile($("#filterDisplay").html()); var resultHTML = template({ tasks: matchTopLevelArgs.results }); $("#filterResultDisplay").html(resultHTML); }; var filterFrodoTasks = function(){ var treeNode = _treeFuncs.buildTaskTree(_tasks); var frodoTasksArgs = {}; frodoTasksArgs.results = []; var frodoTasksFunc = function(value, node, funcArgs){ if((value.assignedTo.indexOf("Frodo") > -1) & (node.level() != 1)){ funcArgs.results.push(value); } }; _treeFuncs.traverseAndApply(treeNode, frodoTasksFunc, frodoTasksArgs); var template = Handlebars.compile($("#filterDisplay").html()); var resultHTML = template({ tasks: frodoTasksArgs.results }); $("#filterResultDisplay").html(resultHTML); }; return { tasks: _tasks, filterResultTasks: _filterResultTasks, initTasks: initTasks, // Filters filterTopLevel: filterTopLevel, filterFrodoTasks: filterFrodoTasks }; }; $(document).ready(function () { var controller = new Controller(); controller.initTasks(); var projectSource = $("#projectDisplay").html(); Handlebars.registerPartial("taskDisplay", $("#taskDisplay").html()); var template = Handlebars.compile(projectSource); var resultHTML = template({ tasks: controller.tasks }); $("#projectPlan").html(resultHTML); window["controller"] = controller; });
<div style="padding: 8px;"> <div id="apparea" role="tabpanel"> <ul class="nav nav-tabs" role="tablist"> <li role="presentation" class="active"><a href="#projectlist" aria-controls="projectplan" role="tab" data-toggle="tab">Project Plan</a> </li> <li role="presentation"><a href="#filters" aria-controls="filters" role="tab" data-toggle="tab">Filtering Examples</a> </li> </ul> <div class="tab-content demo"> <div role="tabpanel" class="tab-pane active" id="projectlist"> <h3>Project Plan</h3> <div id="projectPlan"></div> </div> <div role="tabpanel" class="tab-pane active" id="filters"> <div id="filterResultDisplay" class="filter-results"> </div> <p> <button type="button" onclick="controller.filterTopLevel();">Filter Top Level</button> </p> <p> <button type="button" onclick="controller.filterFrodoTasks();">Filter Frodo's Tasks</button> </p> </div> </div> </div> </div> <script id="projectDisplay" type="text/x-handlebars-template"> <ul class = "project-list"> {{> taskDisplay}} </ul> </script> <script id="taskDisplay" type="text/x-handlebars-template"> {{#each tasks}} <li class = "list-item"> <span class = "task-name">{{description}}</span> | Team: {{assignedTo}} | Duration: {{duration}} (hrs) {{#if tasks}} <ul class="project-list"> {{> taskDisplay}} </ul> {{/if}} </li > {{/each}} </script> <script id="filterDisplay" type="text/x-handlebars-template"> <ul class = "project-list"> {{#each tasks}} <li> <span class = "task-name">{{description}}</span> | Team: {{assignedTo}} | Duration: {{duration}} (hrs) </li> {{/each}} </ul> </script>
div.demo { margin-top: 8px; } div.filter-results{ border-color: #ddd; border-style: solid; border-width: 1px; margin-bottom: 6px; min-height: 200px; overflow: scroll; } li.list-item { margin-top: 4px; } span.task-name { font-weight: bold; font-style: italic; } ul.project-list { list-style-type: none; margin-bottom: 8px; margin-left: 12px; margin-top: 8px; text-decoration: none; }