1*eb8dc403SDave Cobbley/* 2*eb8dc403SDave Cobbley * jQuery treetable Plugin 3.1.0 3*eb8dc403SDave Cobbley * http://ludo.cubicphuse.nl/jquery-treetable 4*eb8dc403SDave Cobbley * 5*eb8dc403SDave Cobbley * Copyright 2013, Ludo van den Boom 6*eb8dc403SDave Cobbley * Dual licensed under the MIT or GPL Version 2 licenses. 7*eb8dc403SDave Cobbley */ 8*eb8dc403SDave Cobbley(function() { 9*eb8dc403SDave Cobbley var $, Node, Tree, methods; 10*eb8dc403SDave Cobbley 11*eb8dc403SDave Cobbley $ = jQuery; 12*eb8dc403SDave Cobbley 13*eb8dc403SDave Cobbley Node = (function() { 14*eb8dc403SDave Cobbley function Node(row, tree, settings) { 15*eb8dc403SDave Cobbley var parentId; 16*eb8dc403SDave Cobbley 17*eb8dc403SDave Cobbley this.row = row; 18*eb8dc403SDave Cobbley this.tree = tree; 19*eb8dc403SDave Cobbley this.settings = settings; 20*eb8dc403SDave Cobbley 21*eb8dc403SDave Cobbley // TODO Ensure id/parentId is always a string (not int) 22*eb8dc403SDave Cobbley this.id = this.row.data(this.settings.nodeIdAttr); 23*eb8dc403SDave Cobbley 24*eb8dc403SDave Cobbley // TODO Move this to a setParentId function? 25*eb8dc403SDave Cobbley parentId = this.row.data(this.settings.parentIdAttr); 26*eb8dc403SDave Cobbley if (parentId != null && parentId !== "") { 27*eb8dc403SDave Cobbley this.parentId = parentId; 28*eb8dc403SDave Cobbley } 29*eb8dc403SDave Cobbley 30*eb8dc403SDave Cobbley this.treeCell = $(this.row.children(this.settings.columnElType)[this.settings.column]); 31*eb8dc403SDave Cobbley this.expander = $(this.settings.expanderTemplate); 32*eb8dc403SDave Cobbley this.indenter = $(this.settings.indenterTemplate); 33*eb8dc403SDave Cobbley this.children = []; 34*eb8dc403SDave Cobbley this.initialized = false; 35*eb8dc403SDave Cobbley this.treeCell.prepend(this.indenter); 36*eb8dc403SDave Cobbley } 37*eb8dc403SDave Cobbley 38*eb8dc403SDave Cobbley Node.prototype.addChild = function(child) { 39*eb8dc403SDave Cobbley return this.children.push(child); 40*eb8dc403SDave Cobbley }; 41*eb8dc403SDave Cobbley 42*eb8dc403SDave Cobbley Node.prototype.ancestors = function() { 43*eb8dc403SDave Cobbley var ancestors, node; 44*eb8dc403SDave Cobbley node = this; 45*eb8dc403SDave Cobbley ancestors = []; 46*eb8dc403SDave Cobbley while (node = node.parentNode()) { 47*eb8dc403SDave Cobbley ancestors.push(node); 48*eb8dc403SDave Cobbley } 49*eb8dc403SDave Cobbley return ancestors; 50*eb8dc403SDave Cobbley }; 51*eb8dc403SDave Cobbley 52*eb8dc403SDave Cobbley Node.prototype.collapse = function() { 53*eb8dc403SDave Cobbley if (this.collapsed()) { 54*eb8dc403SDave Cobbley return this; 55*eb8dc403SDave Cobbley } 56*eb8dc403SDave Cobbley 57*eb8dc403SDave Cobbley this.row.removeClass("expanded").addClass("collapsed"); 58*eb8dc403SDave Cobbley 59*eb8dc403SDave Cobbley this._hideChildren(); 60*eb8dc403SDave Cobbley this.expander.attr("title", this.settings.stringExpand); 61*eb8dc403SDave Cobbley 62*eb8dc403SDave Cobbley if (this.initialized && this.settings.onNodeCollapse != null) { 63*eb8dc403SDave Cobbley this.settings.onNodeCollapse.apply(this); 64*eb8dc403SDave Cobbley } 65*eb8dc403SDave Cobbley 66*eb8dc403SDave Cobbley return this; 67*eb8dc403SDave Cobbley }; 68*eb8dc403SDave Cobbley 69*eb8dc403SDave Cobbley Node.prototype.collapsed = function() { 70*eb8dc403SDave Cobbley return this.row.hasClass("collapsed"); 71*eb8dc403SDave Cobbley }; 72*eb8dc403SDave Cobbley 73*eb8dc403SDave Cobbley // TODO destroy: remove event handlers, expander, indenter, etc. 74*eb8dc403SDave Cobbley 75*eb8dc403SDave Cobbley Node.prototype.expand = function() { 76*eb8dc403SDave Cobbley if (this.expanded()) { 77*eb8dc403SDave Cobbley return this; 78*eb8dc403SDave Cobbley } 79*eb8dc403SDave Cobbley 80*eb8dc403SDave Cobbley this.row.removeClass("collapsed").addClass("expanded"); 81*eb8dc403SDave Cobbley 82*eb8dc403SDave Cobbley if (this.initialized && this.settings.onNodeExpand != null) { 83*eb8dc403SDave Cobbley this.settings.onNodeExpand.apply(this); 84*eb8dc403SDave Cobbley } 85*eb8dc403SDave Cobbley 86*eb8dc403SDave Cobbley if ($(this.row).is(":visible")) { 87*eb8dc403SDave Cobbley this._showChildren(); 88*eb8dc403SDave Cobbley } 89*eb8dc403SDave Cobbley 90*eb8dc403SDave Cobbley this.expander.attr("title", this.settings.stringCollapse); 91*eb8dc403SDave Cobbley 92*eb8dc403SDave Cobbley return this; 93*eb8dc403SDave Cobbley }; 94*eb8dc403SDave Cobbley 95*eb8dc403SDave Cobbley Node.prototype.expanded = function() { 96*eb8dc403SDave Cobbley return this.row.hasClass("expanded"); 97*eb8dc403SDave Cobbley }; 98*eb8dc403SDave Cobbley 99*eb8dc403SDave Cobbley Node.prototype.hide = function() { 100*eb8dc403SDave Cobbley this._hideChildren(); 101*eb8dc403SDave Cobbley this.row.hide(); 102*eb8dc403SDave Cobbley return this; 103*eb8dc403SDave Cobbley }; 104*eb8dc403SDave Cobbley 105*eb8dc403SDave Cobbley Node.prototype.isBranchNode = function() { 106*eb8dc403SDave Cobbley if(this.children.length > 0 || this.row.data(this.settings.branchAttr) === true) { 107*eb8dc403SDave Cobbley return true; 108*eb8dc403SDave Cobbley } else { 109*eb8dc403SDave Cobbley return false; 110*eb8dc403SDave Cobbley } 111*eb8dc403SDave Cobbley }; 112*eb8dc403SDave Cobbley 113*eb8dc403SDave Cobbley Node.prototype.updateBranchLeafClass = function(){ 114*eb8dc403SDave Cobbley this.row.removeClass('branch'); 115*eb8dc403SDave Cobbley this.row.removeClass('leaf'); 116*eb8dc403SDave Cobbley this.row.addClass(this.isBranchNode() ? 'branch' : 'leaf'); 117*eb8dc403SDave Cobbley }; 118*eb8dc403SDave Cobbley 119*eb8dc403SDave Cobbley Node.prototype.level = function() { 120*eb8dc403SDave Cobbley return this.ancestors().length; 121*eb8dc403SDave Cobbley }; 122*eb8dc403SDave Cobbley 123*eb8dc403SDave Cobbley Node.prototype.parentNode = function() { 124*eb8dc403SDave Cobbley if (this.parentId != null) { 125*eb8dc403SDave Cobbley return this.tree[this.parentId]; 126*eb8dc403SDave Cobbley } else { 127*eb8dc403SDave Cobbley return null; 128*eb8dc403SDave Cobbley } 129*eb8dc403SDave Cobbley }; 130*eb8dc403SDave Cobbley 131*eb8dc403SDave Cobbley Node.prototype.removeChild = function(child) { 132*eb8dc403SDave Cobbley var i = $.inArray(child, this.children); 133*eb8dc403SDave Cobbley return this.children.splice(i, 1) 134*eb8dc403SDave Cobbley }; 135*eb8dc403SDave Cobbley 136*eb8dc403SDave Cobbley Node.prototype.render = function() { 137*eb8dc403SDave Cobbley var handler, 138*eb8dc403SDave Cobbley settings = this.settings, 139*eb8dc403SDave Cobbley target; 140*eb8dc403SDave Cobbley 141*eb8dc403SDave Cobbley if (settings.expandable === true && this.isBranchNode()) { 142*eb8dc403SDave Cobbley handler = function(e) { 143*eb8dc403SDave Cobbley $(this).parents("table").treetable("node", $(this).parents("tr").data(settings.nodeIdAttr)).toggle(); 144*eb8dc403SDave Cobbley return e.preventDefault(); 145*eb8dc403SDave Cobbley }; 146*eb8dc403SDave Cobbley 147*eb8dc403SDave Cobbley this.indenter.html(this.expander); 148*eb8dc403SDave Cobbley target = settings.clickableNodeNames === true ? this.treeCell : this.expander; 149*eb8dc403SDave Cobbley 150*eb8dc403SDave Cobbley target.off("click.treetable").on("click.treetable", handler); 151*eb8dc403SDave Cobbley target.off("keydown.treetable").on("keydown.treetable", function(e) { 152*eb8dc403SDave Cobbley if (e.keyCode == 13) { 153*eb8dc403SDave Cobbley handler.apply(this, [e]); 154*eb8dc403SDave Cobbley } 155*eb8dc403SDave Cobbley }); 156*eb8dc403SDave Cobbley } 157*eb8dc403SDave Cobbley 158*eb8dc403SDave Cobbley this.indenter[0].style.paddingLeft = "" + (this.level() * settings.indent) + "px"; 159*eb8dc403SDave Cobbley 160*eb8dc403SDave Cobbley return this; 161*eb8dc403SDave Cobbley }; 162*eb8dc403SDave Cobbley 163*eb8dc403SDave Cobbley Node.prototype.reveal = function() { 164*eb8dc403SDave Cobbley if (this.parentId != null) { 165*eb8dc403SDave Cobbley this.parentNode().reveal(); 166*eb8dc403SDave Cobbley } 167*eb8dc403SDave Cobbley return this.expand(); 168*eb8dc403SDave Cobbley }; 169*eb8dc403SDave Cobbley 170*eb8dc403SDave Cobbley Node.prototype.setParent = function(node) { 171*eb8dc403SDave Cobbley if (this.parentId != null) { 172*eb8dc403SDave Cobbley this.tree[this.parentId].removeChild(this); 173*eb8dc403SDave Cobbley } 174*eb8dc403SDave Cobbley this.parentId = node.id; 175*eb8dc403SDave Cobbley this.row.data(this.settings.parentIdAttr, node.id); 176*eb8dc403SDave Cobbley return node.addChild(this); 177*eb8dc403SDave Cobbley }; 178*eb8dc403SDave Cobbley 179*eb8dc403SDave Cobbley Node.prototype.show = function() { 180*eb8dc403SDave Cobbley if (!this.initialized) { 181*eb8dc403SDave Cobbley this._initialize(); 182*eb8dc403SDave Cobbley } 183*eb8dc403SDave Cobbley this.row.show(); 184*eb8dc403SDave Cobbley if (this.expanded()) { 185*eb8dc403SDave Cobbley this._showChildren(); 186*eb8dc403SDave Cobbley } 187*eb8dc403SDave Cobbley return this; 188*eb8dc403SDave Cobbley }; 189*eb8dc403SDave Cobbley 190*eb8dc403SDave Cobbley Node.prototype.toggle = function() { 191*eb8dc403SDave Cobbley if (this.expanded()) { 192*eb8dc403SDave Cobbley this.collapse(); 193*eb8dc403SDave Cobbley } else { 194*eb8dc403SDave Cobbley this.expand(); 195*eb8dc403SDave Cobbley } 196*eb8dc403SDave Cobbley return this; 197*eb8dc403SDave Cobbley }; 198*eb8dc403SDave Cobbley 199*eb8dc403SDave Cobbley Node.prototype._hideChildren = function() { 200*eb8dc403SDave Cobbley var child, _i, _len, _ref, _results; 201*eb8dc403SDave Cobbley _ref = this.children; 202*eb8dc403SDave Cobbley _results = []; 203*eb8dc403SDave Cobbley for (_i = 0, _len = _ref.length; _i < _len; _i++) { 204*eb8dc403SDave Cobbley child = _ref[_i]; 205*eb8dc403SDave Cobbley _results.push(child.hide()); 206*eb8dc403SDave Cobbley } 207*eb8dc403SDave Cobbley return _results; 208*eb8dc403SDave Cobbley }; 209*eb8dc403SDave Cobbley 210*eb8dc403SDave Cobbley Node.prototype._initialize = function() { 211*eb8dc403SDave Cobbley var settings = this.settings; 212*eb8dc403SDave Cobbley 213*eb8dc403SDave Cobbley this.render(); 214*eb8dc403SDave Cobbley 215*eb8dc403SDave Cobbley if (settings.expandable === true && settings.initialState === "collapsed") { 216*eb8dc403SDave Cobbley this.collapse(); 217*eb8dc403SDave Cobbley } else { 218*eb8dc403SDave Cobbley this.expand(); 219*eb8dc403SDave Cobbley } 220*eb8dc403SDave Cobbley 221*eb8dc403SDave Cobbley if (settings.onNodeInitialized != null) { 222*eb8dc403SDave Cobbley settings.onNodeInitialized.apply(this); 223*eb8dc403SDave Cobbley } 224*eb8dc403SDave Cobbley 225*eb8dc403SDave Cobbley return this.initialized = true; 226*eb8dc403SDave Cobbley }; 227*eb8dc403SDave Cobbley 228*eb8dc403SDave Cobbley Node.prototype._showChildren = function() { 229*eb8dc403SDave Cobbley var child, _i, _len, _ref, _results; 230*eb8dc403SDave Cobbley _ref = this.children; 231*eb8dc403SDave Cobbley _results = []; 232*eb8dc403SDave Cobbley for (_i = 0, _len = _ref.length; _i < _len; _i++) { 233*eb8dc403SDave Cobbley child = _ref[_i]; 234*eb8dc403SDave Cobbley _results.push(child.show()); 235*eb8dc403SDave Cobbley } 236*eb8dc403SDave Cobbley return _results; 237*eb8dc403SDave Cobbley }; 238*eb8dc403SDave Cobbley 239*eb8dc403SDave Cobbley return Node; 240*eb8dc403SDave Cobbley })(); 241*eb8dc403SDave Cobbley 242*eb8dc403SDave Cobbley Tree = (function() { 243*eb8dc403SDave Cobbley function Tree(table, settings) { 244*eb8dc403SDave Cobbley this.table = table; 245*eb8dc403SDave Cobbley this.settings = settings; 246*eb8dc403SDave Cobbley this.tree = {}; 247*eb8dc403SDave Cobbley 248*eb8dc403SDave Cobbley // Cache the nodes and roots in simple arrays for quick access/iteration 249*eb8dc403SDave Cobbley this.nodes = []; 250*eb8dc403SDave Cobbley this.roots = []; 251*eb8dc403SDave Cobbley } 252*eb8dc403SDave Cobbley 253*eb8dc403SDave Cobbley Tree.prototype.collapseAll = function() { 254*eb8dc403SDave Cobbley var node, _i, _len, _ref, _results; 255*eb8dc403SDave Cobbley _ref = this.nodes; 256*eb8dc403SDave Cobbley _results = []; 257*eb8dc403SDave Cobbley for (_i = 0, _len = _ref.length; _i < _len; _i++) { 258*eb8dc403SDave Cobbley node = _ref[_i]; 259*eb8dc403SDave Cobbley _results.push(node.collapse()); 260*eb8dc403SDave Cobbley } 261*eb8dc403SDave Cobbley return _results; 262*eb8dc403SDave Cobbley }; 263*eb8dc403SDave Cobbley 264*eb8dc403SDave Cobbley Tree.prototype.expandAll = function() { 265*eb8dc403SDave Cobbley var node, _i, _len, _ref, _results; 266*eb8dc403SDave Cobbley _ref = this.nodes; 267*eb8dc403SDave Cobbley _results = []; 268*eb8dc403SDave Cobbley for (_i = 0, _len = _ref.length; _i < _len; _i++) { 269*eb8dc403SDave Cobbley node = _ref[_i]; 270*eb8dc403SDave Cobbley _results.push(node.expand()); 271*eb8dc403SDave Cobbley } 272*eb8dc403SDave Cobbley return _results; 273*eb8dc403SDave Cobbley }; 274*eb8dc403SDave Cobbley 275*eb8dc403SDave Cobbley Tree.prototype.findLastNode = function (node) { 276*eb8dc403SDave Cobbley if (node.children.length > 0) { 277*eb8dc403SDave Cobbley return this.findLastNode(node.children[node.children.length - 1]); 278*eb8dc403SDave Cobbley } else { 279*eb8dc403SDave Cobbley return node; 280*eb8dc403SDave Cobbley } 281*eb8dc403SDave Cobbley }; 282*eb8dc403SDave Cobbley 283*eb8dc403SDave Cobbley Tree.prototype.loadRows = function(rows) { 284*eb8dc403SDave Cobbley var node, row, i; 285*eb8dc403SDave Cobbley 286*eb8dc403SDave Cobbley if (rows != null) { 287*eb8dc403SDave Cobbley for (i = 0; i < rows.length; i++) { 288*eb8dc403SDave Cobbley row = $(rows[i]); 289*eb8dc403SDave Cobbley 290*eb8dc403SDave Cobbley if (row.data(this.settings.nodeIdAttr) != null) { 291*eb8dc403SDave Cobbley node = new Node(row, this.tree, this.settings); 292*eb8dc403SDave Cobbley this.nodes.push(node); 293*eb8dc403SDave Cobbley this.tree[node.id] = node; 294*eb8dc403SDave Cobbley 295*eb8dc403SDave Cobbley if (node.parentId != null) { 296*eb8dc403SDave Cobbley this.tree[node.parentId].addChild(node); 297*eb8dc403SDave Cobbley } else { 298*eb8dc403SDave Cobbley this.roots.push(node); 299*eb8dc403SDave Cobbley } 300*eb8dc403SDave Cobbley } 301*eb8dc403SDave Cobbley } 302*eb8dc403SDave Cobbley } 303*eb8dc403SDave Cobbley 304*eb8dc403SDave Cobbley for (i = 0; i < this.nodes.length; i++) { 305*eb8dc403SDave Cobbley node = this.nodes[i].updateBranchLeafClass(); 306*eb8dc403SDave Cobbley } 307*eb8dc403SDave Cobbley 308*eb8dc403SDave Cobbley return this; 309*eb8dc403SDave Cobbley }; 310*eb8dc403SDave Cobbley 311*eb8dc403SDave Cobbley Tree.prototype.move = function(node, destination) { 312*eb8dc403SDave Cobbley // Conditions: 313*eb8dc403SDave Cobbley // 1: +node+ should not be inserted as a child of +node+ itself. 314*eb8dc403SDave Cobbley // 2: +destination+ should not be the same as +node+'s current parent (this 315*eb8dc403SDave Cobbley // prevents +node+ from being moved to the same location where it already 316*eb8dc403SDave Cobbley // is). 317*eb8dc403SDave Cobbley // 3: +node+ should not be inserted in a location in a branch if this would 318*eb8dc403SDave Cobbley // result in +node+ being an ancestor of itself. 319*eb8dc403SDave Cobbley var nodeParent = node.parentNode(); 320*eb8dc403SDave Cobbley if (node !== destination && destination.id !== node.parentId && $.inArray(node, destination.ancestors()) === -1) { 321*eb8dc403SDave Cobbley node.setParent(destination); 322*eb8dc403SDave Cobbley this._moveRows(node, destination); 323*eb8dc403SDave Cobbley 324*eb8dc403SDave Cobbley // Re-render parentNode if this is its first child node, and therefore 325*eb8dc403SDave Cobbley // doesn't have the expander yet. 326*eb8dc403SDave Cobbley if (node.parentNode().children.length === 1) { 327*eb8dc403SDave Cobbley node.parentNode().render(); 328*eb8dc403SDave Cobbley } 329*eb8dc403SDave Cobbley } 330*eb8dc403SDave Cobbley 331*eb8dc403SDave Cobbley if(nodeParent){ 332*eb8dc403SDave Cobbley nodeParent.updateBranchLeafClass(); 333*eb8dc403SDave Cobbley } 334*eb8dc403SDave Cobbley if(node.parentNode()){ 335*eb8dc403SDave Cobbley node.parentNode().updateBranchLeafClass(); 336*eb8dc403SDave Cobbley } 337*eb8dc403SDave Cobbley node.updateBranchLeafClass(); 338*eb8dc403SDave Cobbley return this; 339*eb8dc403SDave Cobbley }; 340*eb8dc403SDave Cobbley 341*eb8dc403SDave Cobbley Tree.prototype.removeNode = function(node) { 342*eb8dc403SDave Cobbley // Recursively remove all descendants of +node+ 343*eb8dc403SDave Cobbley this.unloadBranch(node); 344*eb8dc403SDave Cobbley 345*eb8dc403SDave Cobbley // Remove node from DOM (<tr>) 346*eb8dc403SDave Cobbley node.row.remove(); 347*eb8dc403SDave Cobbley 348*eb8dc403SDave Cobbley // Clean up Tree object (so Node objects are GC-ed) 349*eb8dc403SDave Cobbley delete this.tree[node.id]; 350*eb8dc403SDave Cobbley this.nodes.splice($.inArray(node, this.nodes), 1); 351*eb8dc403SDave Cobbley } 352*eb8dc403SDave Cobbley 353*eb8dc403SDave Cobbley Tree.prototype.render = function() { 354*eb8dc403SDave Cobbley var root, _i, _len, _ref; 355*eb8dc403SDave Cobbley _ref = this.roots; 356*eb8dc403SDave Cobbley for (_i = 0, _len = _ref.length; _i < _len; _i++) { 357*eb8dc403SDave Cobbley root = _ref[_i]; 358*eb8dc403SDave Cobbley 359*eb8dc403SDave Cobbley // Naming is confusing (show/render). I do not call render on node from 360*eb8dc403SDave Cobbley // here. 361*eb8dc403SDave Cobbley root.show(); 362*eb8dc403SDave Cobbley } 363*eb8dc403SDave Cobbley return this; 364*eb8dc403SDave Cobbley }; 365*eb8dc403SDave Cobbley 366*eb8dc403SDave Cobbley Tree.prototype.sortBranch = function(node, sortFun) { 367*eb8dc403SDave Cobbley // First sort internal array of children 368*eb8dc403SDave Cobbley node.children.sort(sortFun); 369*eb8dc403SDave Cobbley 370*eb8dc403SDave Cobbley // Next render rows in correct order on page 371*eb8dc403SDave Cobbley this._sortChildRows(node); 372*eb8dc403SDave Cobbley 373*eb8dc403SDave Cobbley return this; 374*eb8dc403SDave Cobbley }; 375*eb8dc403SDave Cobbley 376*eb8dc403SDave Cobbley Tree.prototype.unloadBranch = function(node) { 377*eb8dc403SDave Cobbley var children, i; 378*eb8dc403SDave Cobbley 379*eb8dc403SDave Cobbley for (i = 0; i < node.children.length; i++) { 380*eb8dc403SDave Cobbley this.removeNode(node.children[i]); 381*eb8dc403SDave Cobbley } 382*eb8dc403SDave Cobbley 383*eb8dc403SDave Cobbley // Reset node's collection of children 384*eb8dc403SDave Cobbley node.children = []; 385*eb8dc403SDave Cobbley 386*eb8dc403SDave Cobbley node.updateBranchLeafClass(); 387*eb8dc403SDave Cobbley 388*eb8dc403SDave Cobbley return this; 389*eb8dc403SDave Cobbley }; 390*eb8dc403SDave Cobbley 391*eb8dc403SDave Cobbley Tree.prototype._moveRows = function(node, destination) { 392*eb8dc403SDave Cobbley var children = node.children, i; 393*eb8dc403SDave Cobbley 394*eb8dc403SDave Cobbley node.row.insertAfter(destination.row); 395*eb8dc403SDave Cobbley node.render(); 396*eb8dc403SDave Cobbley 397*eb8dc403SDave Cobbley // Loop backwards through children to have them end up on UI in correct 398*eb8dc403SDave Cobbley // order (see #112) 399*eb8dc403SDave Cobbley for (i = children.length - 1; i >= 0; i--) { 400*eb8dc403SDave Cobbley this._moveRows(children[i], node); 401*eb8dc403SDave Cobbley } 402*eb8dc403SDave Cobbley }; 403*eb8dc403SDave Cobbley 404*eb8dc403SDave Cobbley // Special _moveRows case, move children to itself to force sorting 405*eb8dc403SDave Cobbley Tree.prototype._sortChildRows = function(parentNode) { 406*eb8dc403SDave Cobbley return this._moveRows(parentNode, parentNode); 407*eb8dc403SDave Cobbley }; 408*eb8dc403SDave Cobbley 409*eb8dc403SDave Cobbley return Tree; 410*eb8dc403SDave Cobbley })(); 411*eb8dc403SDave Cobbley 412*eb8dc403SDave Cobbley // jQuery Plugin 413*eb8dc403SDave Cobbley methods = { 414*eb8dc403SDave Cobbley init: function(options, force) { 415*eb8dc403SDave Cobbley var settings; 416*eb8dc403SDave Cobbley 417*eb8dc403SDave Cobbley settings = $.extend({ 418*eb8dc403SDave Cobbley branchAttr: "ttBranch", 419*eb8dc403SDave Cobbley clickableNodeNames: false, 420*eb8dc403SDave Cobbley column: 0, 421*eb8dc403SDave Cobbley columnElType: "td", // i.e. 'td', 'th' or 'td,th' 422*eb8dc403SDave Cobbley expandable: false, 423*eb8dc403SDave Cobbley expanderTemplate: "<a href='#'> </a>", 424*eb8dc403SDave Cobbley indent: 10, 425*eb8dc403SDave Cobbley indenterTemplate: "<span class='indenter'></span>", 426*eb8dc403SDave Cobbley initialState: "collapsed", 427*eb8dc403SDave Cobbley nodeIdAttr: "ttId", // maps to data-tt-id 428*eb8dc403SDave Cobbley parentIdAttr: "ttParentId", // maps to data-tt-parent-id 429*eb8dc403SDave Cobbley stringExpand: "Expand", 430*eb8dc403SDave Cobbley stringCollapse: "Collapse", 431*eb8dc403SDave Cobbley 432*eb8dc403SDave Cobbley // Events 433*eb8dc403SDave Cobbley onInitialized: null, 434*eb8dc403SDave Cobbley onNodeCollapse: null, 435*eb8dc403SDave Cobbley onNodeExpand: null, 436*eb8dc403SDave Cobbley onNodeInitialized: null 437*eb8dc403SDave Cobbley }, options); 438*eb8dc403SDave Cobbley 439*eb8dc403SDave Cobbley return this.each(function() { 440*eb8dc403SDave Cobbley var el = $(this), tree; 441*eb8dc403SDave Cobbley 442*eb8dc403SDave Cobbley if (force || el.data("treetable") === undefined) { 443*eb8dc403SDave Cobbley tree = new Tree(this, settings); 444*eb8dc403SDave Cobbley tree.loadRows(this.rows).render(); 445*eb8dc403SDave Cobbley 446*eb8dc403SDave Cobbley el.addClass("treetable").data("treetable", tree); 447*eb8dc403SDave Cobbley 448*eb8dc403SDave Cobbley if (settings.onInitialized != null) { 449*eb8dc403SDave Cobbley settings.onInitialized.apply(tree); 450*eb8dc403SDave Cobbley } 451*eb8dc403SDave Cobbley } 452*eb8dc403SDave Cobbley 453*eb8dc403SDave Cobbley return el; 454*eb8dc403SDave Cobbley }); 455*eb8dc403SDave Cobbley }, 456*eb8dc403SDave Cobbley 457*eb8dc403SDave Cobbley destroy: function() { 458*eb8dc403SDave Cobbley return this.each(function() { 459*eb8dc403SDave Cobbley return $(this).removeData("treetable").removeClass("treetable"); 460*eb8dc403SDave Cobbley }); 461*eb8dc403SDave Cobbley }, 462*eb8dc403SDave Cobbley 463*eb8dc403SDave Cobbley collapseAll: function() { 464*eb8dc403SDave Cobbley this.data("treetable").collapseAll(); 465*eb8dc403SDave Cobbley return this; 466*eb8dc403SDave Cobbley }, 467*eb8dc403SDave Cobbley 468*eb8dc403SDave Cobbley collapseNode: function(id) { 469*eb8dc403SDave Cobbley var node = this.data("treetable").tree[id]; 470*eb8dc403SDave Cobbley 471*eb8dc403SDave Cobbley if (node) { 472*eb8dc403SDave Cobbley node.collapse(); 473*eb8dc403SDave Cobbley } else { 474*eb8dc403SDave Cobbley throw new Error("Unknown node '" + id + "'"); 475*eb8dc403SDave Cobbley } 476*eb8dc403SDave Cobbley 477*eb8dc403SDave Cobbley return this; 478*eb8dc403SDave Cobbley }, 479*eb8dc403SDave Cobbley 480*eb8dc403SDave Cobbley expandAll: function() { 481*eb8dc403SDave Cobbley this.data("treetable").expandAll(); 482*eb8dc403SDave Cobbley return this; 483*eb8dc403SDave Cobbley }, 484*eb8dc403SDave Cobbley 485*eb8dc403SDave Cobbley expandNode: function(id) { 486*eb8dc403SDave Cobbley var node = this.data("treetable").tree[id]; 487*eb8dc403SDave Cobbley 488*eb8dc403SDave Cobbley if (node) { 489*eb8dc403SDave Cobbley if (!node.initialized) { 490*eb8dc403SDave Cobbley node._initialize(); 491*eb8dc403SDave Cobbley } 492*eb8dc403SDave Cobbley 493*eb8dc403SDave Cobbley node.expand(); 494*eb8dc403SDave Cobbley } else { 495*eb8dc403SDave Cobbley throw new Error("Unknown node '" + id + "'"); 496*eb8dc403SDave Cobbley } 497*eb8dc403SDave Cobbley 498*eb8dc403SDave Cobbley return this; 499*eb8dc403SDave Cobbley }, 500*eb8dc403SDave Cobbley 501*eb8dc403SDave Cobbley loadBranch: function(node, rows) { 502*eb8dc403SDave Cobbley var settings = this.data("treetable").settings, 503*eb8dc403SDave Cobbley tree = this.data("treetable").tree; 504*eb8dc403SDave Cobbley 505*eb8dc403SDave Cobbley // TODO Switch to $.parseHTML 506*eb8dc403SDave Cobbley rows = $(rows); 507*eb8dc403SDave Cobbley 508*eb8dc403SDave Cobbley if (node == null) { // Inserting new root nodes 509*eb8dc403SDave Cobbley this.append(rows); 510*eb8dc403SDave Cobbley } else { 511*eb8dc403SDave Cobbley var lastNode = this.data("treetable").findLastNode(node); 512*eb8dc403SDave Cobbley rows.insertAfter(lastNode.row); 513*eb8dc403SDave Cobbley } 514*eb8dc403SDave Cobbley 515*eb8dc403SDave Cobbley this.data("treetable").loadRows(rows); 516*eb8dc403SDave Cobbley 517*eb8dc403SDave Cobbley // Make sure nodes are properly initialized 518*eb8dc403SDave Cobbley rows.filter("tr").each(function() { 519*eb8dc403SDave Cobbley tree[$(this).data(settings.nodeIdAttr)].show(); 520*eb8dc403SDave Cobbley }); 521*eb8dc403SDave Cobbley 522*eb8dc403SDave Cobbley if (node != null) { 523*eb8dc403SDave Cobbley // Re-render parent to ensure expander icon is shown (#79) 524*eb8dc403SDave Cobbley node.render().expand(); 525*eb8dc403SDave Cobbley } 526*eb8dc403SDave Cobbley 527*eb8dc403SDave Cobbley return this; 528*eb8dc403SDave Cobbley }, 529*eb8dc403SDave Cobbley 530*eb8dc403SDave Cobbley move: function(nodeId, destinationId) { 531*eb8dc403SDave Cobbley var destination, node; 532*eb8dc403SDave Cobbley 533*eb8dc403SDave Cobbley node = this.data("treetable").tree[nodeId]; 534*eb8dc403SDave Cobbley destination = this.data("treetable").tree[destinationId]; 535*eb8dc403SDave Cobbley this.data("treetable").move(node, destination); 536*eb8dc403SDave Cobbley 537*eb8dc403SDave Cobbley return this; 538*eb8dc403SDave Cobbley }, 539*eb8dc403SDave Cobbley 540*eb8dc403SDave Cobbley node: function(id) { 541*eb8dc403SDave Cobbley return this.data("treetable").tree[id]; 542*eb8dc403SDave Cobbley }, 543*eb8dc403SDave Cobbley 544*eb8dc403SDave Cobbley removeNode: function(id) { 545*eb8dc403SDave Cobbley var node = this.data("treetable").tree[id]; 546*eb8dc403SDave Cobbley 547*eb8dc403SDave Cobbley if (node) { 548*eb8dc403SDave Cobbley this.data("treetable").removeNode(node); 549*eb8dc403SDave Cobbley } else { 550*eb8dc403SDave Cobbley throw new Error("Unknown node '" + id + "'"); 551*eb8dc403SDave Cobbley } 552*eb8dc403SDave Cobbley 553*eb8dc403SDave Cobbley return this; 554*eb8dc403SDave Cobbley }, 555*eb8dc403SDave Cobbley 556*eb8dc403SDave Cobbley reveal: function(id) { 557*eb8dc403SDave Cobbley var node = this.data("treetable").tree[id]; 558*eb8dc403SDave Cobbley 559*eb8dc403SDave Cobbley if (node) { 560*eb8dc403SDave Cobbley node.reveal(); 561*eb8dc403SDave Cobbley } else { 562*eb8dc403SDave Cobbley throw new Error("Unknown node '" + id + "'"); 563*eb8dc403SDave Cobbley } 564*eb8dc403SDave Cobbley 565*eb8dc403SDave Cobbley return this; 566*eb8dc403SDave Cobbley }, 567*eb8dc403SDave Cobbley 568*eb8dc403SDave Cobbley sortBranch: function(node, columnOrFunction) { 569*eb8dc403SDave Cobbley var settings = this.data("treetable").settings, 570*eb8dc403SDave Cobbley prepValue, 571*eb8dc403SDave Cobbley sortFun; 572*eb8dc403SDave Cobbley 573*eb8dc403SDave Cobbley columnOrFunction = columnOrFunction || settings.column; 574*eb8dc403SDave Cobbley sortFun = columnOrFunction; 575*eb8dc403SDave Cobbley 576*eb8dc403SDave Cobbley if ($.isNumeric(columnOrFunction)) { 577*eb8dc403SDave Cobbley sortFun = function(a, b) { 578*eb8dc403SDave Cobbley var extractValue, valA, valB; 579*eb8dc403SDave Cobbley 580*eb8dc403SDave Cobbley extractValue = function(node) { 581*eb8dc403SDave Cobbley var val = node.row.find("td:eq(" + columnOrFunction + ")").text(); 582*eb8dc403SDave Cobbley // Ignore trailing/leading whitespace and use uppercase values for 583*eb8dc403SDave Cobbley // case insensitive ordering 584*eb8dc403SDave Cobbley return $.trim(val).toUpperCase(); 585*eb8dc403SDave Cobbley } 586*eb8dc403SDave Cobbley 587*eb8dc403SDave Cobbley valA = extractValue(a); 588*eb8dc403SDave Cobbley valB = extractValue(b); 589*eb8dc403SDave Cobbley 590*eb8dc403SDave Cobbley if (valA < valB) return -1; 591*eb8dc403SDave Cobbley if (valA > valB) return 1; 592*eb8dc403SDave Cobbley return 0; 593*eb8dc403SDave Cobbley }; 594*eb8dc403SDave Cobbley } 595*eb8dc403SDave Cobbley 596*eb8dc403SDave Cobbley this.data("treetable").sortBranch(node, sortFun); 597*eb8dc403SDave Cobbley return this; 598*eb8dc403SDave Cobbley }, 599*eb8dc403SDave Cobbley 600*eb8dc403SDave Cobbley unloadBranch: function(node) { 601*eb8dc403SDave Cobbley this.data("treetable").unloadBranch(node); 602*eb8dc403SDave Cobbley return this; 603*eb8dc403SDave Cobbley } 604*eb8dc403SDave Cobbley }; 605*eb8dc403SDave Cobbley 606*eb8dc403SDave Cobbley $.fn.treetable = function(method) { 607*eb8dc403SDave Cobbley if (methods[method]) { 608*eb8dc403SDave Cobbley return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); 609*eb8dc403SDave Cobbley } else if (typeof method === 'object' || !method) { 610*eb8dc403SDave Cobbley return methods.init.apply(this, arguments); 611*eb8dc403SDave Cobbley } else { 612*eb8dc403SDave Cobbley return $.error("Method " + method + " does not exist on jQuery.treetable"); 613*eb8dc403SDave Cobbley } 614*eb8dc403SDave Cobbley }; 615*eb8dc403SDave Cobbley 616*eb8dc403SDave Cobbley // Expose classes to world 617*eb8dc403SDave Cobbley this.TreeTable || (this.TreeTable = {}); 618*eb8dc403SDave Cobbley this.TreeTable.Node = Node; 619*eb8dc403SDave Cobbley this.TreeTable.Tree = Tree; 620*eb8dc403SDave Cobbley}).call(this); 621