jquery.treeview.js 7.8 KB

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