1 | /** |
---|
2 | * JQuery Organisation Chart Plugin. |
---|
3 | * |
---|
4 | * Author: Mark Lee |
---|
5 | * Copyright (C)2013-2015 Caprica Software Limited |
---|
6 | * http://www.capricasoftware.co.uk |
---|
7 | * |
---|
8 | * Contributions by: Paul Lautman <paul.lautman at gmail.com> |
---|
9 | * |
---|
10 | * This software is licensed under the Creative Commons Attribution-ShareAlike 3.0 License, |
---|
11 | * see here for license terms: |
---|
12 | * |
---|
13 | * http://creativecommons.org/licenses/by-sa/3.0 |
---|
14 | */ |
---|
15 | (function($) { |
---|
16 | |
---|
17 | $.fn.orgChart = function(options) { |
---|
18 | var opts = $.extend({}, $.fn.orgChart.defaults, options); |
---|
19 | |
---|
20 | return this.each(function() { |
---|
21 | var $chartSource = $(this); |
---|
22 | // Clone the source list hierarchy so levels can be non-destructively removed if needed |
---|
23 | // before creating the chart |
---|
24 | $this = $chartSource.clone(); |
---|
25 | if (opts.levels > -1) { |
---|
26 | $this.find("ul").andSelf().filter(function() {return $chartSource.parents("ul").length+1 > opts.levels;}).remove(); |
---|
27 | } |
---|
28 | // Store the original element |
---|
29 | $this.data("chart-source", $chartSource); |
---|
30 | // Build the chart... |
---|
31 | var $container = $("<div class='" + opts.chartClass + "'/>"); |
---|
32 | if (opts.interactive) { |
---|
33 | $container.addClass("interactive"); |
---|
34 | } |
---|
35 | // The chart may be sourced from either a "ul", or an "li" element... |
---|
36 | var $root; |
---|
37 | if ($this.is("ul")) { |
---|
38 | $root = $this.find("li:first"); |
---|
39 | } |
---|
40 | else if ($this.is("li")) { |
---|
41 | $root = $this; |
---|
42 | } |
---|
43 | if ($root) { |
---|
44 | buildNode($root, $container, 0, 0, opts); |
---|
45 | // Special case for any hyperlink anchor in the chart to prevent the click on the node itself from propagating |
---|
46 | $container.find("div.node a").click(function(evt) { |
---|
47 | evt.stopImmediatePropagation(); |
---|
48 | }); |
---|
49 | if(opts.replace) { |
---|
50 | opts.container.empty(); |
---|
51 | } |
---|
52 | opts.container.append($container); |
---|
53 | } |
---|
54 | }); |
---|
55 | }; |
---|
56 | |
---|
57 | $.fn.orgChart.defaults = { |
---|
58 | container : $("body"), |
---|
59 | depth : -1, |
---|
60 | levels : -1, |
---|
61 | showLevels : -1, |
---|
62 | stack : false, |
---|
63 | chartClass : "orgChart", |
---|
64 | hoverClass : "hover", |
---|
65 | nodeText : function($node) {return $node.clone().children("ul,li").remove().end().html();}, |
---|
66 | interactive: false, |
---|
67 | fade : false, |
---|
68 | speed : "slow", |
---|
69 | nodeClicked: function($node) {}, |
---|
70 | copyClasses: true, |
---|
71 | copyData : true, |
---|
72 | copyStyles : true, |
---|
73 | copyTitle : true, |
---|
74 | replace : true |
---|
75 | }; |
---|
76 | |
---|
77 | function buildNode($node, $appendTo, level, index, opts) { |
---|
78 | var $table = $("<table cellpadding='0' cellspacing='0' border='0'/>"); |
---|
79 | var $tbody = $("<tbody/>"); |
---|
80 | |
---|
81 | // Make this node... |
---|
82 | var $nodeRow = $("<tr/>").addClass("nodes"); |
---|
83 | var $nodeCell = $("<td/>").addClass("node").attr("colspan", 2); |
---|
84 | var $childNodes = $node.children("ul:first").children("li"); |
---|
85 | if ($childNodes.length > 1) { |
---|
86 | $nodeCell.attr("colspan", $childNodes.length*2); |
---|
87 | } |
---|
88 | |
---|
89 | var $adjunct = $node.children("adjunct").eq(0); |
---|
90 | if ($adjunct.length > 0) { |
---|
91 | var $adjunctDiv = $("<div>").addClass("adjunct node").addClass("level"+level).addClass("node"+index).addClass("level"+level+"-node"+index).append(opts.nodeText($adjunct)); |
---|
92 | $adjunctDiv.appendTo($nodeCell); |
---|
93 | var $linkDiv = $("<div>").addClass("adjunct-link"); |
---|
94 | $linkDiv.appendTo($nodeCell); |
---|
95 | $adjunct.remove(); |
---|
96 | } |
---|
97 | |
---|
98 | var $heading = $("<h2>").html(opts.nodeText($node)); |
---|
99 | var $nodeDiv = $("<div>").addClass("node").addClass("level"+level).addClass("node"+index).addClass("level"+level+"-node"+index).append($heading); |
---|
100 | |
---|
101 | // Copy classes from the source list to the chart node |
---|
102 | if (opts.copyClasses) { |
---|
103 | $nodeDiv.addClass($node.attr("class")); |
---|
104 | } |
---|
105 | |
---|
106 | // Copy data from the source list to the chart node |
---|
107 | if (opts.copyData) { |
---|
108 | $nodeDiv.data($node.data()); |
---|
109 | } |
---|
110 | |
---|
111 | // Copy CSS styles from the source list to the chart node |
---|
112 | if (opts.copyStyles) { |
---|
113 | $nodeDiv.attr("style", $node.attr("style")); |
---|
114 | } |
---|
115 | |
---|
116 | // Copy the title attribute from the source list to the chart node |
---|
117 | if (opts.copyTitle) { |
---|
118 | $nodeDiv.attr("title", $node.attr("title")); |
---|
119 | } |
---|
120 | |
---|
121 | $nodeDiv.data("orgchart-level", level).data("orgchart-node", $node); |
---|
122 | |
---|
123 | $nodeCell.append($nodeDiv); |
---|
124 | $nodeRow.append($nodeCell); |
---|
125 | $tbody.append($nodeRow); |
---|
126 | |
---|
127 | $nodeDiv.click(function() { |
---|
128 | var $this = $(this); |
---|
129 | opts.nodeClicked($this.data("orgchart-node"), $this); |
---|
130 | if (opts.interactive) { |
---|
131 | var $row = $this.closest("tr"); |
---|
132 | if ($row.next("tr").is(":visible")) { |
---|
133 | if (opts.fade) { |
---|
134 | $row.nextAll("tr").fadeOut(opts.speed); |
---|
135 | } |
---|
136 | else { |
---|
137 | $row.nextAll("tr").hide(); |
---|
138 | } |
---|
139 | $this.removeClass("shownChildren").addClass("hiddenChildren"); |
---|
140 | } |
---|
141 | else { |
---|
142 | $this.removeClass("hiddenChildren").addClass("shownChildren"); |
---|
143 | if (opts.fade) { |
---|
144 | $row.nextAll("tr").fadeIn(opts.speed); |
---|
145 | } |
---|
146 | else { |
---|
147 | $row.nextAll("tr").show(); |
---|
148 | } |
---|
149 | } |
---|
150 | } |
---|
151 | }); |
---|
152 | |
---|
153 | if ($childNodes.length > 0) { |
---|
154 | if (opts.depth == -1 || level+1 < opts.depth) { |
---|
155 | var $downLineRow = $("<tr/>").addClass("lines"); |
---|
156 | var $downLineCell = $("<td/>").attr("colspan", $childNodes.length*2); |
---|
157 | $downLineRow.append($downLineCell); |
---|
158 | |
---|
159 | var $downLineTable = $("<table cellpadding='0' cellspacing='0' border='0'>"); |
---|
160 | $downLineTable.append("<tbody>"); |
---|
161 | var $downLineLine = $("<tr/>").addClass("lines x"); |
---|
162 | var $downLeft = $("<td>").addClass("line left"); |
---|
163 | var $downRight = $("<td>").addClass("line right"); |
---|
164 | $downLineLine.append($downLeft).append($downRight); |
---|
165 | $downLineTable.children("tbody").append($downLineLine); |
---|
166 | $downLineCell.append($downLineTable); |
---|
167 | |
---|
168 | $tbody.append($downLineRow); |
---|
169 | |
---|
170 | if ($childNodes.length > 0) { |
---|
171 | $nodeDiv.addClass("hasChildren"); |
---|
172 | if (opts.showLevels == -1 || level < opts.showLevels-1) { |
---|
173 | $nodeDiv.addClass("shownChildren"); |
---|
174 | } |
---|
175 | else { |
---|
176 | $nodeDiv.addClass("hiddenChildren"); |
---|
177 | } |
---|
178 | if (opts.interactive) { |
---|
179 | $nodeDiv.hover(function() {$(this).addClass(opts.hoverClass);}, function() {$(this).removeClass(opts.hoverClass)}); |
---|
180 | } |
---|
181 | } |
---|
182 | |
---|
183 | // Recursively make child nodes... |
---|
184 | var $linesRow = $("<tr/>").addClass("lines v"); |
---|
185 | $childNodes.each(function() { |
---|
186 | var $left = $("<td/>").addClass("line left top"); |
---|
187 | var $right = $("<td/>").addClass("line right top"); |
---|
188 | $linesRow.append($left).append($right); |
---|
189 | }); |
---|
190 | $linesRow.find("td:first").removeClass("top"); |
---|
191 | $linesRow.find("td:last").removeClass("top"); |
---|
192 | $tbody.append($linesRow); |
---|
193 | var $childNodesRow = $("<tr/>"); |
---|
194 | $childNodes.each(function(index) { |
---|
195 | var $td = $("<td/>"); |
---|
196 | $td.attr("colspan", 2); |
---|
197 | buildNode($(this), $td, level+1, index, opts); |
---|
198 | $childNodesRow.append($td); |
---|
199 | }); |
---|
200 | $tbody.append($childNodesRow); |
---|
201 | } |
---|
202 | else if (opts.stack) { |
---|
203 | var $stackNodes = $childNodes.clone(); |
---|
204 | var $list = $("<ul class='stack'>").append($stackNodes).addClass("level"+level).addClass("node"+index).addClass("level"+level+"-node"+index); |
---|
205 | var $stackContainer = $("<div class='stack-container'>").append($list); |
---|
206 | $nodeDiv.after($stackContainer); |
---|
207 | } |
---|
208 | } |
---|
209 | |
---|
210 | if (opts.showLevels > -1 && level >= opts.showLevels-1) { |
---|
211 | $nodeRow.nextAll("tr").hide(); |
---|
212 | } |
---|
213 | |
---|
214 | $table.append($tbody); |
---|
215 | $appendTo.append($table); |
---|
216 | }; |
---|
217 | |
---|
218 | })(jQuery); |
---|