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