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