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