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