| 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