web/data/jquery-treeview/jquery.treeview.js
changeset 10100 6718c03f8938
equal deleted inserted replaced
10099:12f4f2509a38 10100:6718c03f8938
       
     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 location.href.toLowerCase().indexOf(this.href.toLowerCase()) == 0;
       
   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);