first commit
[step2_drupal.git] / navigate / navigate_menu / jquery.treeview.js
blobcfd93f8614fbbe0f27177491497fc323170f517b
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,v 1.1 2008/10/27 17:51:05 stompeers Exp $
14  *
15  */
17 ;(function($) {
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                                 // HACK by stompeers, so that the hitareas won't be duplicated when using Navigate's Cache
83                                 if (!$(this).find('.hitarea').length > 0) {
84                                         this.prepend("<div class=\"" + CLASSES.hitarea + "\"/>").find("div." + CLASSES.hitarea).each(function() {
85                                                 var classes = "";
86                                                 $.each($(this).parent().attr("class").split(" "), function() {
87                                                         classes += this + "-hitarea ";
88                                                 });
89                                                 $(this).addClass( classes );
90                                         });
91                                 }
92                         }
93                         
94                         // apply event to hitarea
95                         this.find("div." + CLASSES.hitarea).click( toggler );
96                 },
97                 treeview: function(settings) {
98                         
99                         settings = $.extend({
100                                 cookieId: "treeview"
101                         }, settings);
102                         
103                         if (settings.add) {
104                                 return this.trigger("add", [settings.add]);
105                         }
106                         
107                         if ( settings.toggle ) {
108                                 var callback = settings.toggle;
109                                 settings.toggle = function() {
110                                         return callback.apply($(this).parent()[0], arguments);
111                                 };
112                         }
113                 
114                         // factory for treecontroller
115                         function treeController(tree, control) {
116                                 // factory for click handlers
117                                 function handler(filter) {
118                                         return function() {
119                                                 // reuse toggle event handler, applying the elements to toggle
120                                                 // start searching for all hitareas
121                                                 toggler.apply( $("div." + CLASSES.hitarea, tree).filter(function() {
122                                                         // for plain toggle, no filter is provided, otherwise we need to check the parent element
123                                                         return filter ? $(this).parent("." + filter).length : true;
124                                                 }) );
125                                                 return false;
126                                         };
127                                 }
128                                 // click on first element to collapse tree
129                                 $("a:eq(0)", control).click( handler(CLASSES.collapsable) );
130                                 // click on second to expand tree
131                                 $("a:eq(1)", control).click( handler(CLASSES.expandable) );
132                                 // click on third to toggle tree
133                                 $("a:eq(2)", control).click( handler() ); 
134                         }
135                 
136                         // handle toggle event
137                         function toggler() {
138                                 $(this)
139                                         .parent()
140                                         // swap classes for hitarea
141                                         .find(">.hitarea")
142                                                 .swapClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea )
143                                                 .swapClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea )
144                                         .end()
145                                         // swap classes for parent li
146                                         .swapClass( CLASSES.collapsable, CLASSES.expandable )
147                                         .swapClass( CLASSES.lastCollapsable, CLASSES.lastExpandable )
148                                         // find child lists
149                                         .find( ">ul" )
150                                         // toggle them
151                                         .heightToggle( settings.animated, settings.toggle );
152                                 if ( settings.unique ) {
153                                         $(this).parent()
154                                                 .siblings()
155                                                 // swap classes for hitarea
156                                                 .find(">.hitarea")
157                                                         .replaceClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea )
158                                                         .replaceClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea )
159                                                 .end()
160                                                 .replaceClass( CLASSES.collapsable, CLASSES.expandable )
161                                                 .replaceClass( CLASSES.lastCollapsable, CLASSES.lastExpandable )
162                                                 .find( ">ul" )
163                                                 .heightHide( settings.animated, settings.toggle );
164                                 }
165                         }
166                         
167                         function serialize() {
168                                 function binary(arg) {
169                                         return arg ? 1 : 0;
170                                 }
171                                 var data = [];
172                                 branches.each(function(i, e) {
173                                         data[i] = $(e).is(":has(>ul:visible)") ? 1 : 0;
174                                 });
175                                 var options = new Array();
176                                 // HACK by stompeers to save state of menu across all pages
177                                 options.path = '/';
178                                 $.cookie(settings.cookieId, data.join(""), options );
179                         }
180                         
181                         function deserialize() {
182                                 var stored = $.cookie(settings.cookieId);
183                                 if ( stored ) {
184                                         var data = stored.split("");
185                                         branches.each(function(i, e) {
186                                                 $(e).find(">ul")[ parseInt(data[i]) ? "show" : "hide" ]();
187                                         });
188                                 }
189                         }
190                         
191                         // add treeview class to activate styles
192                         this.addClass("treeview");
193                         
194                         // prepare branches and find all tree items with child lists
195                         var branches = this.find("li").prepareBranches(settings);
196                         
197                         switch(settings.persist) {
198                         case "cookie":
199                                 var toggleCallback = settings.toggle;
200                                 settings.toggle = function() {
201                                         serialize();
202                                         if (toggleCallback) {
203                                                 toggleCallback.apply(this, arguments);
204                                         }
205                                 };
206                                 deserialize();
207                                 break;
208                         case "location":
209                                 var current = this.find("a").filter(function() { return this.href.toLowerCase() == location.href.toLowerCase(); });
210                                 if ( current.length ) {
211                                         current.addClass("selected").parents("ul, li").add( current.next() ).show();
212                                 }
213                                 break;
214                         }
215                         
216                         branches.applyClasses(settings, toggler);
217                                 
218                         // if control option is set, create the treecontroller and show it
219                         if ( settings.control ) {
220                                 treeController(this, settings.control);
221                                 $(settings.control).show();
222                         }
223                         
224                         return this.bind("add", function(event, branches) {
225                                 $(branches).prev()
226                                         .removeClass(CLASSES.last)
227                                         .removeClass(CLASSES.lastCollapsable)
228                                         .removeClass(CLASSES.lastExpandable)
229                                 .find(">.hitarea")
230                                         .removeClass(CLASSES.lastCollapsableHitarea)
231                                         .removeClass(CLASSES.lastExpandableHitarea);
232                                 $(branches).find("li").andSelf().prepareBranches(settings).applyClasses(settings, toggler);
233                         });
234                 }
235         });
236         
237         // classes used by the plugin
238         // need to be styled via external stylesheet, see first example
239         var CLASSES = $.fn.treeview.classes = {
240                 open: "open",
241                 closed: "closed",
242                 expandable: "expandable",
243                 expandableHitarea: "expandable-hitarea",
244                 lastExpandableHitarea: "lastExpandable-hitarea",
245                 collapsable: "collapsable",
246                 collapsableHitarea: "collapsable-hitarea",
247                 lastCollapsableHitarea: "lastCollapsable-hitarea",
248                 lastCollapsable: "lastCollapsable",
249                 lastExpandable: "lastExpandable",
250                 last: "last",
251                 hitarea: "hitarea"
252         };
253         
254         // provide backwards compability
255         $.fn.Treeview = $.fn.treeview;
256         
257 })(jQuery);