jquery.jOrgChart.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /**
  2. * jQuery org-chart/tree plugin.
  3. *
  4. * Author: Wes Nolte
  5. * http://twitter.com/wesnolte
  6. *
  7. * Based on the work of Mark Lee
  8. * http://www.capricasoftware.co.uk
  9. *
  10. * Copyright (c) 2011 Wesley Nolte
  11. * Dual licensed under the MIT and GPL licenses.
  12. *
  13. */
  14. (function($) {
  15. $.fn.jOrgChart = function(options) {
  16. var opts = $.extend({}, $.fn.jOrgChart.defaults, options);
  17. var $appendTo = $(opts.chartElement);
  18. // build the tree
  19. $this = $(this);
  20. var $container = $("<div class='" + opts.chartClass + "'/>");
  21. if($this.is("ul")) {
  22. buildNode($this.find("li:first"), $container, 0, opts);
  23. }
  24. else if($this.is("li")) {
  25. buildNode($this, $container, 0, opts);
  26. }
  27. $appendTo.append($container);
  28. // add drag and drop if enabled
  29. if(opts.dragAndDrop){
  30. $('div.node').draggable({
  31. cursor : 'move',
  32. distance : 40,
  33. helper : 'clone',
  34. opacity : 0.8,
  35. revert : 'invalid',
  36. revertDuration : 100,
  37. snap : 'div.node.expanded',
  38. snapMode : 'inner',
  39. stack : 'div.node'
  40. });
  41. $('div.node').droppable({
  42. accept : '.node',
  43. activeClass : 'drag-active',
  44. hoverClass : 'drop-hover'
  45. });
  46. // Drag start event handler for nodes
  47. $('div.node').bind("dragstart", function handleDragStart( event, ui ){
  48. var sourceNode = $(this);
  49. sourceNode.parentsUntil('.node-container')
  50. .find('*')
  51. .filter('.node')
  52. .droppable('disable');
  53. });
  54. // Drag stop event handler for nodes
  55. $('div.node').bind("dragstop", function handleDragStop( event, ui ){
  56. /* reload the plugin */
  57. $(opts.chartElement).children().remove();
  58. $this.jOrgChart(opts);
  59. });
  60. // Drop event handler for nodes
  61. $('div.node').bind("drop", function handleDropEvent( event, ui ) {
  62. var targetID = $(this).data("tree-node");
  63. var targetLi = $this.find("li").filter(function() { return $(this).data("tree-node") === targetID; } );
  64. var targetUl = targetLi.children('ul');
  65. var sourceID = ui.draggable.data("tree-node");
  66. var sourceLi = $this.find("li").filter(function() { return $(this).data("tree-node") === sourceID; } );
  67. var sourceUl = sourceLi.parent('ul');
  68. if (targetUl.length > 0){
  69. targetUl.append(sourceLi);
  70. } else {
  71. targetLi.append("<ul></ul>");
  72. targetLi.children('ul').append(sourceLi);
  73. }
  74. //Removes any empty lists
  75. if (sourceUl.children().length === 0){
  76. sourceUl.remove();
  77. }
  78. }); // handleDropEvent
  79. } // Drag and drop
  80. };
  81. // Option defaults
  82. $.fn.jOrgChart.defaults = {
  83. chartElement : 'body',
  84. depth : -1,
  85. chartClass : "jOrgChart",
  86. dragAndDrop: false
  87. };
  88. var nodeCount = 0;
  89. // Method that recursively builds the tree
  90. function buildNode($node, $appendTo, level, opts) {
  91. var $table = $("<table cellpadding='0' cellspacing='0' border='0'/>");
  92. var $tbody = $("<tbody/>");
  93. // Construct the node container(s)
  94. var $nodeRow = $("<tr/>").addClass("node-cells");
  95. var $nodeCell = $("<td/>").addClass("node-cell").attr("colspan", 2);
  96. var $childNodes = $node.children("ul:first").children("li");
  97. var $nodeDiv;
  98. if($childNodes.length > 1) {
  99. $nodeCell.attr("colspan", $childNodes.length * 2);
  100. }
  101. // Draw the node
  102. // Get the contents - any markup except li and ul allowed
  103. var $nodeContent = $node.clone()
  104. .children("ul,li")
  105. .remove()
  106. .end()
  107. .html();
  108. //Increaments the node count which is used to link the source list and the org chart
  109. nodeCount++;
  110. $node.data("tree-node", nodeCount);
  111. $nodeDiv = $("<div>").addClass("node")
  112. .data("tree-node", nodeCount)
  113. .append($nodeContent);
  114. // Expand and contract nodes
  115. if ($childNodes.length > 0) {
  116. $nodeDiv.click(function() {
  117. var $this = $(this);
  118. var $tr = $this.closest("tr");
  119. if($tr.hasClass('contracted')){
  120. $this.css('cursor','n-resize');
  121. $tr.removeClass('contracted').addClass('expanded');
  122. $tr.nextAll("tr").css('visibility', '');
  123. // Update the <li> appropriately so that if the tree redraws collapsed/non-collapsed nodes
  124. // maintain their appearance
  125. $node.removeClass('collapsed');
  126. }else{
  127. $this.css('cursor','s-resize');
  128. $tr.removeClass('expanded').addClass('contracted');
  129. $tr.nextAll("tr").css('visibility', 'hidden');
  130. $node.addClass('collapsed');
  131. }
  132. });
  133. }
  134. $nodeCell.append($nodeDiv);
  135. $nodeRow.append($nodeCell);
  136. $tbody.append($nodeRow);
  137. if($childNodes.length > 0) {
  138. // if it can be expanded then change the cursor
  139. $nodeDiv.css('cursor','n-resize');
  140. // recurse until leaves found (-1) or to the level specified
  141. if(opts.depth == -1 || (level+1 < opts.depth)) {
  142. var $downLineRow = $("<tr/>");
  143. var $downLineCell = $("<td/>").attr("colspan", $childNodes.length*2);
  144. $downLineRow.append($downLineCell);
  145. // draw the connecting line from the parent node to the horizontal line
  146. $downLine = $("<div></div>").addClass("line down");
  147. $downLineCell.append($downLine);
  148. $tbody.append($downLineRow);
  149. // Draw the horizontal lines
  150. var $linesRow = $("<tr/>");
  151. $childNodes.each(function() {
  152. var $left = $("<td>&nbsp;</td>").addClass("line left top");
  153. var $right = $("<td>&nbsp;</td>").addClass("line right top");
  154. $linesRow.append($left).append($right);
  155. });
  156. // horizontal line shouldn't extend beyond the first and last child branches
  157. $linesRow.find("td:first")
  158. .removeClass("top")
  159. .end()
  160. .find("td:last")
  161. .removeClass("top");
  162. $tbody.append($linesRow);
  163. var $childNodesRow = $("<tr/>");
  164. $childNodes.each(function() {
  165. var $td = $("<td class='node-container'/>");
  166. $td.attr("colspan", 2);
  167. // recurse through children lists and items
  168. buildNode($(this), $td, level+1, opts);
  169. $childNodesRow.append($td);
  170. });
  171. }
  172. $tbody.append($childNodesRow);
  173. }
  174. // any classes on the LI element get copied to the relevant node in the tree
  175. // apart from the special 'collapsed' class, which collapses the sub-tree at this point
  176. if ($node.attr('class') != undefined) {
  177. var classList = $node.attr('class').split(/\s+/);
  178. $.each(classList, function(index,item) {
  179. if (item == 'collapsed') {
  180. console.log($node);
  181. $nodeRow.nextAll('tr').css('visibility', 'hidden');
  182. $nodeRow.removeClass('expanded');
  183. $nodeRow.addClass('contracted');
  184. $nodeDiv.css('cursor','s-resize');
  185. } else {
  186. $nodeDiv.addClass(item);
  187. }
  188. });
  189. }
  190. $table.append($tbody);
  191. $appendTo.append($table);
  192. /* Prevent trees collapsing if a link inside a node is clicked */
  193. $nodeDiv.children('a').click(function(e){
  194. console.log(e);
  195. e.stopPropagation();
  196. });
  197. };
  198. })(jQuery);