|
|
/* * Treeview 1.4.1 - jQuery plugin to hide and show branches of a tree * * http://bassistance.de/jquery-plugins/jquery-plugin-treeview/
* http://docs.jquery.com/Plugins/Treeview
* * Copyright (c) 2007 Jörn Zaefferer * * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
* * Revision: $Id: jquery.treeview.js 5759 2008-07-01 07:50:28Z joern.zaefferer $ * */
;(function($) {
// TODO rewrite as a widget, removing all the extra plugins
$.extend($.fn, { swapClass: function(c1, c2) { var c1Elements = this.filter('.' + c1); this.filter('.' + c2).removeClass(c2).addClass(c1); c1Elements.removeClass(c1).addClass(c2); return this; }, replaceClass: function(c1, c2) { return this.filter('.' + c1).removeClass(c1).addClass(c2).end(); }, 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) { if (!settings.prerendered) { // mark last tree items
this.filter(":last-child:not(ul)").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) { // TODO use event delegation
this.filter(":has(>ul):not(:has(>a))").find(">span").unbind("click.treeview").bind("click.treeview", function(event) { // don't handle click events on children, eg. checkboxes
if ( this == event.target ) toggler.apply($(this).next()); }).add( $("a", this) ).hoverClass(); if (!settings.prerendered) { // 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); // create hitarea if not present
var hitarea = this.find("div." + CLASSES.hitarea); if (!hitarea.length) hitarea = this.prepend("<div class=\"" + CLASSES.hitarea + "\"/>").find("div." + CLASSES.hitarea); hitarea.removeClass().addClass(CLASSES.hitarea).each(function() { var classes = ""; $.each($(this).parent().attr("class").split(" "), function() { classes += this + "-hitarea "; }); $(this).addClass( classes ); }) } // apply event to hitarea
this.find("div." + CLASSES.hitarea).click( toggler ); }, treeview: function(settings) { settings = $.extend({ cookieId: "treeview" }, settings); 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
$("a:eq(0)", control).click( handler(CLASSES.collapsable) ); // click on second to expand tree
$("a:eq(1)", control).click( handler(CLASSES.expandable) ); // click on third to toggle tree
$("a:eq(2)", control).click( handler() ); } // handle toggle event
function toggler() { $(this) .parent() // swap classes for hitarea
.find(">.hitarea") .swapClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea ) .swapClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea ) .end() // swap classes for parent li
.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() // swap classes for hitarea
.find(">.hitarea") .replaceClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea ) .replaceClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea ) .end() .replaceClass( CLASSES.collapsable, CLASSES.expandable ) .replaceClass( CLASSES.lastCollapsable, CLASSES.lastExpandable ) .find( ">ul" ) .heightHide( settings.animated, settings.toggle ); } } this.data("toggler", toggler); 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(settings.cookieId, data.join(""), settings.cookieOptions ); } function deserialize() { var stored = $.cookie(settings.cookieId); 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); 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.toLowerCase() == location.href.toLowerCase(); }); if ( current.length ) { // TODO update the open/closed classes
var items = current.addClass("selected").parents("ul, li").add( current.next() ).show(); if (settings.prerendered) { // if prerendered is on, replicate the basic class swapping
items.filter("li") .swapClass( CLASSES.collapsable, CLASSES.expandable ) .swapClass( CLASSES.lastCollapsable, CLASSES.lastExpandable ) .find(">.hitarea") .swapClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea ) .swapClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea ); } } break; } branches.applyClasses(settings, toggler); // if control option is set, create the treecontroller and show it
if ( settings.control ) { treeController(this, settings.control); $(settings.control).show(); } return this; } }); // classes used by the plugin
// need to be styled via external stylesheet, see first example
$.treeview = {}; var CLASSES = ($.treeview.classes = { open: "open", closed: "closed", expandable: "expandable", expandableHitarea: "expandable-hitarea", lastExpandableHitarea: "lastExpandable-hitarea", collapsable: "collapsable", collapsableHitarea: "collapsable-hitarea", lastCollapsableHitarea: "lastCollapsable-hitarea", lastCollapsable: "lastCollapsable", lastExpandable: "lastExpandable", last: "last", hitarea: "hitarea" }); })(jQuery);
|