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