1 | /* |
---|---|
2 | * Treeview 1.4.2 - jQuery plugin to hide and show branches of a tree |
3 | * |
4 | * http://bassistance.de/jquery-plugins/jquery-plugin-treeview/ |
5 | * |
6 | * Copyright Jörn Zaefferer |
7 | * Released under the MIT license: |
8 | * http://www.opensource.org/licenses/mit-license.php |
9 | */ |
10 | |
11 | ;(function($) { |
12 | |
13 | // TODO rewrite as a widget, removing all the extra plugins |
14 | $.extend($.fn, { |
15 | swapClass: function(c1, c2) { |
16 | var c1Elements = this.filter('.' + c1); |
17 | this.filter('.' + c2).removeClass(c2).addClass(c1); |
18 | c1Elements.removeClass(c1).addClass(c2); |
19 | return this; |
20 | }, |
21 | replaceClass: function(c1, c2) { |
22 | return this.filter('.' + c1).removeClass(c1).addClass(c2).end(); |
23 | }, |
24 | hoverClass: function(className) { |
25 | className = className || "hover"; |
26 | return this.hover(function() { |
27 | $(this).addClass(className); |
28 | }, function() { |
29 | $(this).removeClass(className); |
30 | }); |
31 | }, |
32 | heightToggle: function(animated, callback) { |
33 | animated ? |
34 | this.animate({ height: "toggle" }, animated, callback) : |
35 | this.each(function(){ |
36 | jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ](); |
37 | if(callback) |
38 | callback.apply(this, arguments); |
39 | }); |
40 | }, |
41 | heightHide: function(animated, callback) { |
42 | if (animated) { |
43 | this.animate({ height: "hide" }, animated, callback); |
44 | } else { |
45 | this.hide(); |
46 | if (callback) |
47 | this.each(callback); |
48 | } |
49 | }, |
50 | prepareBranches: function(settings) { |
51 | if (!settings.prerendered) { |
52 | // mark last tree items |
53 | this.filter(":last-child:not(ul)").addClass(CLASSES.last); |
54 | // collapse whole tree, or only those marked as closed, anyway except those marked as open |
55 | this.filter((settings.collapsed ? "" : "." + CLASSES.closed) + ":not(." + CLASSES.open + ")").find(">ul").hide(); |
56 | } |
57 | // return all items with sublists |
58 | return this.filter(":has(>ul)"); |
59 | }, |
60 | applyClasses: function(settings, toggler) { |
61 | // TODO use event delegation |
62 | this.filter(":has(>ul):not(:has(>a))").find(">span").unbind("click.treeview").bind("click.treeview", function(event) { |
63 | // don't handle click events on children, eg. checkboxes |
64 | if ( this == event.target ) |
65 | toggler.apply($(this).next()); |
66 | }).add( $("a", this) ).hoverClass(); |
67 | |
68 | if (!settings.prerendered) { |
69 | // handle closed ones first |
70 | this.filter(":has(>ul:hidden)") |
71 | .addClass(CLASSES.expandable) |
72 | .replaceClass(CLASSES.last, CLASSES.lastExpandable); |
73 | |
74 | // handle open ones |
75 | this.not(":has(>ul:hidden)") |
76 | .addClass(CLASSES.collapsable) |
77 | .replaceClass(CLASSES.last, CLASSES.lastCollapsable); |
78 | |
79 | // create hitarea if not present |
80 | var hitarea = this.find("div." + CLASSES.hitarea); |
81 | if (!hitarea.length) |
82 | hitarea = this.prepend("<div class=\"" + CLASSES.hitarea + "\"/>").find("div." + CLASSES.hitarea); |
83 | hitarea.removeClass().addClass(CLASSES.hitarea).each(function() { |
84 | var classes = ""; |
85 | $.each($(this).parent().attr("class").split(" "), function() { |
86 | classes += this + "-hitarea "; |
87 | }); |
88 | $(this).addClass( classes ); |
89 | }) |
90 | } |
91 | |
92 | // apply event to hitarea |
93 | this.find("div." + CLASSES.hitarea).click( toggler ); |
94 | }, |
95 | treeview: function(settings) { |
96 | |
97 | settings = $.extend({ |
98 | cookieId: "treeview" |
99 | }, settings); |
100 | |
101 | if ( settings.toggle ) { |
102 | var callback = settings.toggle; |
103 | settings.toggle = function() { |
104 | return callback.apply($(this).parent()[0], arguments); |
105 | }; |
106 | } |
107 | |
108 | // factory for treecontroller |
109 | function treeController(tree, control) { |
110 | // factory for click handlers |
111 | function handler(filter) { |
112 | return function() { |
113 | // reuse toggle event handler, applying the elements to toggle |
114 | // start searching for all hitareas |
115 | toggler.apply( $("div." + CLASSES.hitarea, tree).filter(function() { |
116 | // for plain toggle, no filter is provided, otherwise we need to check the parent element |
117 | return filter ? $(this).parent("." + filter).length : true; |
118 | }) ); |
119 | return false; |
120 | }; |
121 | } |
122 | // click on first element to collapse tree |
123 | $("a:eq(0)", control).click( handler(CLASSES.collapsable) ); |
124 | // click on second to expand tree |
125 | $("a:eq(1)", control).click( handler(CLASSES.expandable) ); |
126 | // click on third to toggle tree |
127 | $("a:eq(2)", control).click( handler() ); |
128 | } |
129 | |
130 | // handle toggle event |
131 | function toggler() { |
132 | $(this) |
133 | .parent() |
134 | // swap classes for hitarea |
135 | .find(">.hitarea") |
136 | .swapClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea ) |
137 | .swapClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea ) |
138 | .end() |
139 | // swap classes for parent li |
140 | .swapClass( CLASSES.collapsable, CLASSES.expandable ) |
141 | .swapClass( CLASSES.lastCollapsable, CLASSES.lastExpandable ) |
142 | // find child lists |
143 | .find( ">ul" ) |
144 | // toggle them |
145 | .heightToggle( settings.animated, settings.toggle ); |
146 | if ( settings.unique ) { |
147 | $(this).parent() |
148 | .siblings() |
149 | // swap classes for hitarea |
150 | .find(">.hitarea") |
151 | .replaceClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea ) |
152 | .replaceClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea ) |
153 | .end() |
154 | .replaceClass( CLASSES.collapsable, CLASSES.expandable ) |
155 | .replaceClass( CLASSES.lastCollapsable, CLASSES.lastExpandable ) |
156 | .find( ">ul" ) |
157 | .heightHide( settings.animated, settings.toggle ); |
158 | } |
159 | } |
160 | this.data("toggler", toggler); |
161 | |
162 | function serialize() { |
163 | function binary(arg) { |
164 | return arg ? 1 : 0; |
165 | } |
166 | var data = []; |
167 | branches.each(function(i, e) { |
168 | data[i] = $(e).is(":has(>ul:visible)") ? 1 : 0; |
169 | }); |
170 | $.cookie(settings.cookieId, data.join(""), settings.cookieOptions ); |
171 | } |
172 | |
173 | function deserialize() { |
174 | var stored = $.cookie(settings.cookieId); |
175 | if ( stored ) { |
176 | var data = stored.split(""); |
177 | branches.each(function(i, e) { |
178 | $(e).find(">ul")[ parseInt(data[i]) ? "show" : "hide" ](); |
179 | }); |
180 | } |
181 | } |
182 | |
183 | // add treeview class to activate styles |
184 | this.addClass("treeview"); |
185 | |
186 | // prepare branches and find all tree items with child lists |
187 | var branches = this.find("li").prepareBranches(settings); |
188 | |
189 | switch(settings.persist) { |
190 | case "cookie": |
191 | var toggleCallback = settings.toggle; |
192 | settings.toggle = function() { |
193 | serialize(); |
194 | if (toggleCallback) { |
195 | toggleCallback.apply(this, arguments); |
196 | } |
197 | }; |
198 | deserialize(); |
199 | break; |
200 | case "location": |
201 | var current = this.find("a").filter(function() { |
202 | return location.href.toLowerCase().indexOf(this.href.toLowerCase()) == 0; |
203 | }); |
204 | if ( current.length ) { |
205 | // TODO update the open/closed classes |
206 | var items = current.addClass("selected").parents("ul, li").add( current.next() ).show(); |
207 | if (settings.prerendered) { |
208 | // if prerendered is on, replicate the basic class swapping |
209 | items.filter("li") |
210 | .swapClass( CLASSES.collapsable, CLASSES.expandable ) |
211 | .swapClass( CLASSES.lastCollapsable, CLASSES.lastExpandable ) |
212 | .find(">.hitarea") |
213 | .swapClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea ) |
214 | .swapClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea ); |
215 | } |
216 | } |
217 | break; |
218 | } |
219 | |
220 | branches.applyClasses(settings, toggler); |
221 | |
222 | // if control option is set, create the treecontroller and show it |
223 | if ( settings.control ) { |
224 | treeController(this, settings.control); |
225 | $(settings.control).show(); |
226 | } |
227 | |
228 | return this; |
229 | } |
230 | }); |
231 | |
232 | // classes used by the plugin |
233 | // need to be styled via external stylesheet, see first example |
234 | $.treeview = {}; |
235 | var CLASSES = ($.treeview.classes = { |
236 | open: "open", |
237 | closed: "closed", |
238 | expandable: "expandable", |
239 | expandableHitarea: "expandable-hitarea", |
240 | lastExpandableHitarea: "lastExpandable-hitarea", |
241 | collapsable: "collapsable", |
242 | collapsableHitarea: "collapsable-hitarea", |
243 | lastCollapsableHitarea: "lastCollapsable-hitarea", |
244 | lastCollapsable: "lastCollapsable", |
245 | lastExpandable: "lastExpandable", |
246 | last: "last", |
247 | hitarea: "hitarea" |
248 | }); |
249 | |
250 | })(jQuery); |
251 |
Members