(function($) { // classes used by the plugin // need to be styled via external stylesheet, see first example var CLASSES = { open: "open", closed: "closed", expandable: "expandable", collapsable: "collapsable", lastCollapsable: "lastCollapsable", lastExpandable: "lastExpandable", last: "last", hitarea: "hitarea" }; // styles for hitareas var hitareaCSS = { height: 15, width: 15, marginLeft: "-15px", "float": "left", cursor: "pointer" }; // ie specific styles for hitareas if( $.browser.msie ) $.extend( hitareaCSS, { background: "#fff", filter: "alpha(opacity=0)", display: "inline" }); $.extend($.fn, { swapClass: function(c1, c2) { return this.each(function() { var $this = $(this); if ( $.className.has(this, c1) ) $this.removeClass(c1).addClass(c2); else if ( $.className.has(this, c2) ) $this.removeClass(c2).addClass(c1); }); }, replaceClass: function(c1, c2) { return this.each(function() { var $this = $(this); if ( $.className.has(this, c1) ) $this.removeClass(c1).addClass(c2); }); }, hoverClass: function(className) { className = className || "hover"; return this.hover(function() { $(this).addClass(className); }, function() { $(this).removeClass(className); }); }, heightToggle: function(animated, callback) { animated ? this.animate({ height: "toggle" }, animated, callback) : this.each(function(){ jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ](); if(callback) callback.apply(this, arguments); }); }, heightHide: function(animated, callback) { if (animated) { this.animate({ height: "hide" }, animated, callback) } else { this.hide(); if (callback) this.each(callback); } }, prepareBranches: function(settings) { // mark last tree items this.filter(":last-child").addClass(CLASSES.last); // collapse whole tree, or only those marked as closed, anyway except those marked as open this.filter((settings.collapsed ? "" : "." + CLASSES.closed) + ":not(." + CLASSES.open + ")").find(">ul").hide(); // return all items with sublists return this.filter(":has(>ul)"); }, applyClasses: function(settings, toggler) { this.filter(":has(ul):not(:has(>a))").find(">span").click(function(event) { if ( this == event.target ) { toggler.apply($(this).next()); } }).add( $("a", this) ).hoverClass(); // handle closed ones first this.filter(":has(>ul:hidden)") .addClass(CLASSES.expandable) .replaceClass(CLASSES.last, CLASSES.lastExpandable); // handle open ones this.not(":has(>ul:hidden)") .addClass(CLASSES.collapsable) .replaceClass(CLASSES.last, CLASSES.lastCollapsable); // append hitarea this.prepend("
") // find hitarea .find("div." + CLASSES.hitarea) // apply styles to hitarea .css(hitareaCSS) // apply click event to hitarea .click( toggler ); }, // 3497 original code has changed case of Treeview. Changed back. jmg Treeview: function(settings) { // currently no defaults necessary, all implicit settings = $.extend({}, settings); if (settings.add) { return this.trigger("add", [settings.add]); } if (settings.toggle ) { var callback = settings.toggle; settings.toggle = function() { return callback.apply($(this).parent()[0], arguments); } } // factory for treecontroller function treeController(tree, control) { // factory for click handlers function handler(filter) { return function() { // reuse toggle event handler, applying the elements to toggle // start searching for all hitareas toggler.apply( $("div." + CLASSES.hitarea, tree).filter(function() { // for plain toggle, no filter is provided, otherwise we need to check the parent element return filter ? $(this).parent("." + filter).length : true; }) ); return false; } } // click on first element to collapse tree $(":eq(0)", control).click( handler(CLASSES.collapsable) ); // click on second to expand tree $(":eq(1)", control).click( handler(CLASSES.expandable) ); // click on third to toggle tree $(":eq(2)", control).click( handler() ); } // handle toggle event function toggler() { // this refers to hitareas, we need to find the parent lis first $(this).parent() // swap classes .swapClass( CLASSES.collapsable, CLASSES.expandable ) .swapClass( CLASSES.lastCollapsable, CLASSES.lastExpandable ) // find child lists .find( ">ul" ) // toggle them .heightToggle( settings.animated, settings.toggle ); if ( settings.unique ) { $(this).parent() .siblings() .replaceClass( CLASSES.collapsable, CLASSES.expandable ) .replaceClass( CLASSES.lastCollapsable, CLASSES.lastExpandable ) .find( ">ul" ) .heightHide( settings.animated, settings.toggle ); } } function serialize() { function binary(arg) { return arg ? 1 : 0; } var data = []; branches.each(function(i, e) { data[i] = $(e).is(":has(>ul:visible)") ? 1 : 0; }); $.cookie("treeview", data.join("") ); } function deserialize() { var stored = $.cookie("treeview"); if ( stored ) { var data = stored.split(""); branches.each(function(i, e) { $(e).find(">ul")[ parseInt(data[i]) ? "show" : "hide" ](); }); } } // add treeview class to activate styles this.addClass("treeview"); // prepare branches and find all tree items with child lists var branches = this.find("li").prepareBranches(settings);//3497 original code //var branches = $("li[>ul]:not([div])", this);//jim's fix for recursion..nfg for 3497 //var branches = this.find("li[>ul]:not([div])"); //jmg change to 3497 for recursion..nfg??........................................ switch(settings.persist) { case "cookie": var toggleCallback = settings.toggle; settings.toggle = function() { serialize(); if (toggleCallback) { toggleCallback.apply(this, arguments); } }; deserialize(); break; case "location": var current = this.find("a").filter(function() { return this.href == location.href; }); if ( current.length ) { current.addClass("selected").parents("ul, li").add( current.next() ).show(); } break; } branches.applyClasses(settings, toggler); // if control option is set, create the treecontroller if ( settings.control ) treeController(this, settings.control); return this.bind("add", function(event, branches) { $(branches).prev().removeClass(CLASSES.last).removeClass(CLASSES.lastCollapsable).removeClass(CLASSES.lastExpandable); $(branches).find("li").andSelf().prepareBranches(settings).applyClasses(settings, toggler); }); } }); })(jQuery);