danielebarchiesi@0: danielebarchiesi@0: /* danielebarchiesi@0: * jQuery treeTable Plugin 2.3.0 danielebarchiesi@0: * http://ludo.cubicphuse.nl/jquery-plugins/treeTable/ danielebarchiesi@0: * danielebarchiesi@0: * Copyright 2010, Ludo van den Boom danielebarchiesi@0: * Dual licensed under the MIT or GPL Version 2 licenses. danielebarchiesi@0: */ danielebarchiesi@0: (function($) { danielebarchiesi@0: // Helps to make options available to all functions danielebarchiesi@0: // TODO: This gives problems when there are both expandable and non-expandable danielebarchiesi@0: // trees on a page. The options shouldn't be global to all these instances! danielebarchiesi@0: var options; danielebarchiesi@0: var defaultPaddingLeft; danielebarchiesi@0: danielebarchiesi@0: $.fn.treeTable = function(opts) { danielebarchiesi@0: options = $.extend({}, $.fn.treeTable.defaults, opts); danielebarchiesi@0: danielebarchiesi@0: return this.each(function() { danielebarchiesi@0: $(this).addClass("treeTable").find("tbody tr").each(function() { danielebarchiesi@0: // Initialize root nodes only if possible danielebarchiesi@0: if(!options.expandable || $(this)[0].className.search(options.childPrefix) == -1) { danielebarchiesi@0: // To optimize performance of indentation, I retrieve the padding-left danielebarchiesi@0: // value of the first root node. This way I only have to call +css+ danielebarchiesi@0: // once. danielebarchiesi@0: if (isNaN(defaultPaddingLeft)) { danielebarchiesi@0: defaultPaddingLeft = parseInt($($(this).children("td")[options.treeColumn]).css('padding-left'), 10); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: initialize($(this)); danielebarchiesi@0: } else if(options.initialState == "collapsed") { danielebarchiesi@0: this.style.display = "none"; // Performance! $(this).hide() is slow... danielebarchiesi@0: } danielebarchiesi@0: }); danielebarchiesi@0: }); danielebarchiesi@0: }; danielebarchiesi@0: danielebarchiesi@0: $.fn.treeTable.defaults = { danielebarchiesi@0: childPrefix: "child-of-", danielebarchiesi@0: clickableNodeNames: false, danielebarchiesi@0: expandable: true, danielebarchiesi@0: indent: 19, danielebarchiesi@0: initialState: "collapsed", danielebarchiesi@0: treeColumn: 0 danielebarchiesi@0: }; danielebarchiesi@0: danielebarchiesi@0: // Recursively hide all node's children in a tree danielebarchiesi@0: $.fn.collapse = function() { danielebarchiesi@0: $(this).addClass("collapsed"); danielebarchiesi@0: danielebarchiesi@0: childrenOf($(this)).each(function() { danielebarchiesi@0: if(!$(this).hasClass("collapsed")) { danielebarchiesi@0: $(this).collapse(); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: this.style.display = "none"; // Performance! $(this).hide() is slow... danielebarchiesi@0: }); danielebarchiesi@0: danielebarchiesi@0: return this; danielebarchiesi@0: }; danielebarchiesi@0: danielebarchiesi@0: // Recursively show all node's children in a tree danielebarchiesi@0: $.fn.expand = function() { danielebarchiesi@0: $(this).removeClass("collapsed").addClass("expanded"); danielebarchiesi@0: danielebarchiesi@0: childrenOf($(this)).each(function() { danielebarchiesi@0: initialize($(this)); danielebarchiesi@0: danielebarchiesi@0: if($(this).is(".expanded.parent")) { danielebarchiesi@0: $(this).expand(); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: // this.style.display = "table-row"; // Unfortunately this is not possible with IE :-( danielebarchiesi@0: $(this).show(); danielebarchiesi@0: }); danielebarchiesi@0: danielebarchiesi@0: return this; danielebarchiesi@0: }; danielebarchiesi@0: danielebarchiesi@0: // Reveal a node by expanding all ancestors danielebarchiesi@0: $.fn.reveal = function() { danielebarchiesi@0: $(ancestorsOf($(this)).reverse()).each(function() { danielebarchiesi@0: initialize($(this)); danielebarchiesi@0: $(this).expand().show(); danielebarchiesi@0: }); danielebarchiesi@0: danielebarchiesi@0: return this; danielebarchiesi@0: }; danielebarchiesi@0: danielebarchiesi@0: // Add an entire branch to +destination+ danielebarchiesi@0: $.fn.appendBranchTo = function(destination) { danielebarchiesi@0: var node = $(this); danielebarchiesi@0: var parent = parentOf(node); danielebarchiesi@0: danielebarchiesi@0: var ancestorNames = $.map(ancestorsOf($(destination)), function(a) { return a.id; }); danielebarchiesi@0: danielebarchiesi@0: // Conditions: danielebarchiesi@0: // 1: +node+ should not be inserted in a location in a branch if this would danielebarchiesi@0: // result in +node+ being an ancestor of itself. danielebarchiesi@0: // 2: +node+ should not have a parent OR the destination should not be the danielebarchiesi@0: // same as +node+'s current parent (this last condition prevents +node+ danielebarchiesi@0: // from being moved to the same location where it already is). danielebarchiesi@0: // 3: +node+ should not be inserted as a child of +node+ itself. danielebarchiesi@0: if($.inArray(node[0].id, ancestorNames) == -1 && (!parent || (destination.id != parent[0].id)) && destination.id != node[0].id) { danielebarchiesi@0: indent(node, ancestorsOf(node).length * options.indent * -1); // Remove indentation danielebarchiesi@0: danielebarchiesi@0: if(parent) { node.removeClass(options.childPrefix + parent[0].id); } danielebarchiesi@0: danielebarchiesi@0: node.addClass(options.childPrefix + destination.id); danielebarchiesi@0: move(node, destination); // Recursively move nodes to new location danielebarchiesi@0: indent(node, ancestorsOf(node).length * options.indent); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: return this; danielebarchiesi@0: }; danielebarchiesi@0: danielebarchiesi@0: // Add reverse() function from JS Arrays danielebarchiesi@0: $.fn.reverse = function() { danielebarchiesi@0: return this.pushStack(this.get().reverse(), arguments); danielebarchiesi@0: }; danielebarchiesi@0: danielebarchiesi@0: // Toggle an entire branch danielebarchiesi@0: $.fn.toggleBranch = function() { danielebarchiesi@0: if($(this).hasClass("collapsed")) { danielebarchiesi@0: $(this).expand(); danielebarchiesi@0: } else { danielebarchiesi@0: $(this).removeClass("expanded").collapse(); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: return this; danielebarchiesi@0: }; danielebarchiesi@0: danielebarchiesi@0: // === Private functions danielebarchiesi@0: danielebarchiesi@0: function ancestorsOf(node) { danielebarchiesi@0: var ancestors = []; danielebarchiesi@0: while(node = parentOf(node)) { danielebarchiesi@0: ancestors[ancestors.length] = node[0]; danielebarchiesi@0: } danielebarchiesi@0: return ancestors; danielebarchiesi@0: }; danielebarchiesi@0: danielebarchiesi@0: function childrenOf(node) { danielebarchiesi@0: return $(node).siblings("tr." + options.childPrefix + node[0].id); danielebarchiesi@0: }; danielebarchiesi@0: danielebarchiesi@0: function getPaddingLeft(node) { danielebarchiesi@0: var paddingLeft = parseInt(node[0].style.paddingLeft, 10); danielebarchiesi@0: return (isNaN(paddingLeft)) ? defaultPaddingLeft : paddingLeft; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: function indent(node, value) { danielebarchiesi@0: var cell = $(node.children("td")[options.treeColumn]); danielebarchiesi@0: cell[0].style.paddingLeft = getPaddingLeft(cell) + value + "px"; danielebarchiesi@0: danielebarchiesi@0: childrenOf(node).each(function() { danielebarchiesi@0: indent($(this), value); danielebarchiesi@0: }); danielebarchiesi@0: }; danielebarchiesi@0: danielebarchiesi@0: function initialize(node) { danielebarchiesi@0: if(!node.hasClass("initialized")) { danielebarchiesi@0: node.addClass("initialized"); danielebarchiesi@0: danielebarchiesi@0: var childNodes = childrenOf(node); danielebarchiesi@0: danielebarchiesi@0: if(!node.hasClass("parent") && childNodes.length > 0) { danielebarchiesi@0: node.addClass("parent"); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: if(node.hasClass("parent")) { danielebarchiesi@0: var cell = $(node.children("td")[options.treeColumn]); danielebarchiesi@0: var padding = getPaddingLeft(cell) + options.indent; danielebarchiesi@0: danielebarchiesi@0: childNodes.each(function() { danielebarchiesi@0: $(this).children("td")[options.treeColumn].style.paddingLeft = padding + "px"; danielebarchiesi@0: }); danielebarchiesi@0: danielebarchiesi@0: if(options.expandable) { danielebarchiesi@0: cell.prepend(''); danielebarchiesi@0: $(cell[0].firstChild).click(function() { node.toggleBranch(); }); danielebarchiesi@0: danielebarchiesi@0: if(options.clickableNodeNames) { danielebarchiesi@0: cell[0].style.cursor = "pointer"; danielebarchiesi@0: $(cell).click(function(e) { danielebarchiesi@0: // Don't double-toggle if the click is on the existing expander icon danielebarchiesi@0: if (e.target.className != 'expander') { danielebarchiesi@0: node.toggleBranch(); danielebarchiesi@0: } danielebarchiesi@0: }); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: // Check for a class set explicitly by the user, otherwise set the default class danielebarchiesi@0: if(!(node.hasClass("expanded") || node.hasClass("collapsed"))) { danielebarchiesi@0: node.addClass(options.initialState); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: if(node.hasClass("expanded")) { danielebarchiesi@0: node.expand(); danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: }; danielebarchiesi@0: danielebarchiesi@0: function move(node, destination) { danielebarchiesi@0: node.insertAfter(destination); danielebarchiesi@0: childrenOf(node).reverse().each(function() { move($(this), node[0]); }); danielebarchiesi@0: }; danielebarchiesi@0: danielebarchiesi@0: function parentOf(node) { danielebarchiesi@0: var classNames = node[0].className.split(' '); danielebarchiesi@0: danielebarchiesi@0: for(key in classNames) { danielebarchiesi@0: if(classNames[key].match(options.childPrefix)) { danielebarchiesi@0: return $(node).siblings("#" + classNames[key].substring(options.childPrefix.length)); danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: }; danielebarchiesi@0: })(jQuery);