jOrgChart/jquery.jOrgChart.js

257 lines
8.3 KiB
JavaScript
Raw Normal View History

/**
* jQuery org-chart/tree plugin.
*
* Author: Wes Nolte
* http://twitter.com/wesnolte
*
* Based on the work of Mark Lee
* http://www.capricasoftware.co.uk
*
* Copyright (c) 2011 Wesley Nolte
* Dual licensed under the MIT and GPL licenses.
*
*/
2011-11-11 18:51:44 +01:00
(function($) {
$.fn.jOrgChart = function(options) {
var opts = $.extend({}, $.fn.jOrgChart.defaults, options);
var $appendTo = $(opts.chartElement);
2011-11-11 18:51:44 +01:00
// build the tree
$this = $(this);
var $container = $("<div class='" + opts.chartClass + "'/>");
if($this.is("ul")) {
buildNodes($this, $container, opts);
}
else if($this.is("li")) {
buildNode($this, $container, 0, opts);
}
$appendTo.append($container);
// add drag and drop if enabled
if(opts.dragAndDrop){
$('div.node').draggable({
cursor : 'move',
distance : 40,
helper : 'clone',
opacity : 0.8,
revert : 'invalid',
revertDuration : 100,
snap : 'div.node.expanded',
snapMode : 'inner',
stack : 'div.node'
});
$('div.node').droppable({
accept : '.node',
activeClass : 'drag-active',
hoverClass : 'drop-hover'
});
// Drag start event handler for nodes
$('div.node').bind("dragstart", function handleDragStart( event, ui ){
var sourceNode = $(this);
sourceNode.parentsUntil('.node-container')
.find('*')
.filter('.node')
.droppable('disable');
});
// Drag stop event handler for nodes
$('div.node').bind("dragstop", function handleDragStop( event, ui ){
/* reload the plugin */
$(opts.chartElement).children().remove();
$this.jOrgChart(opts);
});
// Drop event handler for nodes
$('div.node').bind("drop", function handleDropEvent( event, ui ) {
var targetID = $(this).data("tree-node");
var targetLi = $this.find("li").filter(function() { return $(this).data("tree-node") === targetID; } );
var targetUl = targetLi.children('ul');
var sourceID = ui.draggable.data("tree-node");
var sourceLi = $this.find("li").filter(function() { return $(this).data("tree-node") === sourceID; } );
var sourceUl = sourceLi.parent('ul');
if (targetUl.length > 0){
targetUl.append(sourceLi);
} else {
targetLi.append("<ul></ul>");
targetLi.children('ul').append(sourceLi);
}
//Removes any empty lists
if (sourceUl.children().length === 0){
sourceUl.remove();
}
}); // handleDropEvent
} // Drag and drop
2011-11-11 18:51:44 +01:00
};
// Option defaults
2011-11-11 18:51:44 +01:00
$.fn.jOrgChart.defaults = {
chartElement : 'body',
2011-11-11 18:51:44 +01:00
depth : -1,
chartClass : "jOrgChart",
dragAndDrop: false,
ignoreSpace: false
2011-11-11 18:51:44 +01:00
};
function buildNodes($list, $appendTo, opts) {
var $table = $("<table cellpadding='0' cellspacing='0' border='0'/>");
var $tbody = $("<tbody/>");
// Construct the node container(s)
var $nodeRow = $("<tr/>");
$list.children("li").each(function(i, elem) {
var $td = $("<td class='node-container'/>");
$td.attr("colspan", 2);
buildNode($(elem), $td, 0, opts);
$nodeRow.append($td);
});
$tbody.append($nodeRow);
$table.append($tbody);
$appendTo.append($table);
}
var nodeCount = 0;
// Method that recursively builds the tree
2011-11-11 18:51:44 +01:00
function buildNode($node, $appendTo, level, opts) {
var $table = $("<table cellpadding='0' cellspacing='0' border='0'/>");
var $tbody = $("<tbody/>");
// Construct the node container(s)
2011-11-11 18:51:44 +01:00
var $nodeRow = $("<tr/>").addClass("node-cells");
var $nodeCell = $("<td/>").addClass("node-cell").attr("colspan", 2);
var $childNodes = $node.children("ul:first").children("li");
var $nodeDiv;
2011-11-11 18:51:44 +01:00
if($childNodes.length > 1) {
$nodeCell.attr("colspan", $childNodes.length * 2);
}
// Draw the node
// Get the contents - any markup except li and ul allowed
var $nodeContent = $node.clone()
.children("ul,li")
.remove()
.end()
.html();
//Increaments the node count which is used to link the source list and the org chart
nodeCount++;
$node.data("tree-node", nodeCount);
$nodeDiv = $("<div>").addClass("node")
.data("tree-node", nodeCount)
.append($nodeContent);
// Expand and contract nodes
if ($childNodes.length > 0) {
$nodeDiv.click(function() {
var $this = $(this);
var $tr = $this.closest("tr");
2011-11-11 18:51:44 +01:00
if($tr.hasClass('contracted')){
$this.css('cursor','n-resize');
$tr.removeClass('contracted').addClass('expanded');
$tr.nextAll("tr").css(opts.ignoreSpace ? 'display' : 'visibility', '');
// Update the <li> appropriately so that if the tree redraws collapsed/non-collapsed nodes
// maintain their appearance
$node.removeClass('collapsed');
}else{
$this.css('cursor','s-resize');
$tr.removeClass('expanded').addClass('contracted');
if(opts.ignoreSpace) { $tr.nextAll("tr").css('display', 'none'); }
else { $tr.nextAll("tr").css('visibility', 'hidden'); }
$node.addClass('collapsed');
}
});
}
2011-11-11 18:51:44 +01:00
$nodeCell.append($nodeDiv);
$nodeRow.append($nodeCell);
$tbody.append($nodeRow);
if($childNodes.length > 0) {
// if it can be expanded then change the cursor
$nodeDiv.css('cursor','n-resize');
// recurse until leaves found (-1) or to the level specified
2011-11-11 18:51:44 +01:00
if(opts.depth == -1 || (level+1 < opts.depth)) {
var $downLineRow = $("<tr/>");
var $downLineCell = $("<td/>").attr("colspan", $childNodes.length*2);
$downLineRow.append($downLineCell);
// draw the connecting line from the parent node to the horizontal line
$downLine = $("<div></div>").addClass("line down");
$downLineCell.append($downLine);
2011-11-11 18:51:44 +01:00
$tbody.append($downLineRow);
// Draw the horizontal lines
var $linesRow = $("<tr/>");
$childNodes.each(function() {
var $left = $("<td>&nbsp;</td>").addClass("line left top");
var $right = $("<td>&nbsp;</td>").addClass("line right top");
2011-11-11 18:51:44 +01:00
$linesRow.append($left).append($right);
});
// horizontal line shouldn't extend beyond the first and last child branches
$linesRow.find("td:first")
.removeClass("top")
.end()
.find("td:last")
.removeClass("top");
2011-11-11 18:51:44 +01:00
$tbody.append($linesRow);
var $childNodesRow = $("<tr/>");
$childNodes.each(function() {
var $td = $("<td class='node-container'/>");
$td.attr("colspan", 2);
// recurse through children lists and items
2011-11-11 18:51:44 +01:00
buildNode($(this), $td, level+1, opts);
$childNodesRow.append($td);
});
2011-11-11 18:51:44 +01:00
}
$tbody.append($childNodesRow);
}
// any classes on the LI element get copied to the relevant node in the tree
// apart from the special 'collapsed' class, which collapses the sub-tree at this point
if ($node.attr('class') != undefined) {
var classList = $node.attr('class').split(/\s+/);
$.each(classList, function(index,item) {
if (item == 'collapsed') {
console.log($node);
if(opts.ignoreSpace) { $nodeRow.nextAll('tr').css('display', 'none'); }
else { $nodeRow.nextAll('tr').css('visibility', 'hidden'); }
$nodeRow.removeClass('expanded');
$nodeRow.addClass('contracted');
$nodeDiv.css('cursor','s-resize');
} else {
$nodeDiv.addClass(item);
}
});
}
2011-11-11 18:51:44 +01:00
$table.append($tbody);
$appendTo.append($table);
/* Prevent trees collapsing if a link inside a node is clicked */
$nodeDiv.children('a').click(function(e){
console.log(e);
e.stopPropagation();
});
2011-11-11 18:51:44 +01:00
};
})(jQuery);