ui.accordion.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /*
  2. * Accordion 1.5 - jQuery menu widget
  3. *
  4. * Copyright (c) 2007 Jörn Zaefferer, Frank Marcia
  5. *
  6. * http://bassistance.de/jquery-plugins/jquery-plugin-accordion/
  7. *
  8. * Dual licensed under the MIT and GPL licenses:
  9. * http://www.opensource.org/licenses/mit-license.php
  10. * http://www.gnu.org/licenses/gpl.html
  11. *
  12. * Revision: $Id: jquery.accordion.js 2951 2007-08-28 07:21:13Z joern.zaefferer $
  13. *
  14. */
  15. (function($) {
  16. $.ui = $.ui || {}
  17. $.ui.accordion = {};
  18. $.extend($.ui.accordion, {
  19. defaults: {
  20. selectedClass: "selected",
  21. alwaysOpen: true,
  22. animated: 'slide',
  23. event: "click",
  24. header: "a"
  25. },
  26. animations: {
  27. slide: function(settings, additions) {
  28. settings = $.extend({
  29. easing: "swing",
  30. duration: 300
  31. }, settings, additions);
  32. if ( !settings.toHide.size() ) {
  33. settings.toShow.animate({height: "show"}, {
  34. duration: settings.duration,
  35. easing: settings.easing,
  36. complete: settings.finished
  37. });
  38. return;
  39. }
  40. var hideHeight = settings.toHide.height(),
  41. showHeight = settings.toShow.height(),
  42. difference = showHeight / hideHeight;
  43. settings.toShow.css({ height: 0, overflow: 'hidden' }).show();
  44. settings.toHide.filter(":hidden").each(settings.finished).end().filter(":visible").animate({height:"hide"},{
  45. step: function(n){
  46. settings.toShow.height(Math.ceil( (hideHeight - (n)) * difference ));
  47. },
  48. duration: settings.duration,
  49. easing: settings.easing,
  50. complete: settings.finished
  51. });
  52. },
  53. bounceslide: function(settings) {
  54. this.slide(settings, {
  55. easing: settings.down ? "bounceout" : "swing",
  56. duration: settings.down ? 1000 : 200
  57. });
  58. },
  59. easeslide: function(settings) {
  60. this.slide(settings, {
  61. easing: "easeinout",
  62. duration: 700
  63. })
  64. }
  65. }
  66. });
  67. $.fn.extend({
  68. nextUntil: function(expr) {
  69. var match = [];
  70. // We need to figure out which elements to push onto the array
  71. this.each(function(){
  72. // Traverse through the sibling nodes
  73. for( var i = this.nextSibling; i; i = i.nextSibling ) {
  74. // Make sure that we're only dealing with elements
  75. if ( i.nodeType != 1 ) continue;
  76. // If we find a match then we need to stop
  77. if ( $.filter( expr, [i] ).r.length ) break;
  78. // Otherwise, add it on to the stack
  79. match.push( i );
  80. }
  81. });
  82. return this.pushStack( match );
  83. },
  84. // the plugin method itself
  85. accordion: function(settings) {
  86. if ( !this.length )
  87. return this;
  88. // setup configuration
  89. settings = $.extend({}, $.ui.accordion.defaults, settings);
  90. if ( settings.navigation ) {
  91. var current = this.find("a").filter(function() { return this.href == location.href; });
  92. if ( current.length ) {
  93. if ( current.filter(settings.header).length ) {
  94. settings.active = current;
  95. } else {
  96. settings.active = current.parent().parent().prev();
  97. current.addClass("current");
  98. }
  99. }
  100. }
  101. // calculate active if not specified, using the first header
  102. var container = this,
  103. headers = container.find(settings.header),
  104. active = findActive(settings.active),
  105. running = 0;
  106. if ( settings.fillSpace ) {
  107. var maxHeight = this.parent().height();
  108. headers.each(function() {
  109. maxHeight -= $(this).outerHeight();
  110. });
  111. var maxPadding = 0;
  112. headers.nextUntil(settings.header).each(function() {
  113. maxPadding = Math.max(maxPadding, $(this).innerHeight() - $(this).height());
  114. }).height(maxHeight - maxPadding);
  115. } else if ( settings.autoheight ) {
  116. var maxHeight = 0;
  117. headers.nextUntil(settings.header).each(function() {
  118. maxHeight = Math.max(maxHeight, $(this).height());
  119. }).height(maxHeight);
  120. }
  121. headers
  122. .not(active || "")
  123. .nextUntil(settings.header)
  124. .hide();
  125. active.parent().andSelf().addClass(settings.selectedClass);
  126. function findActive(selector) {
  127. return selector != undefined
  128. ? typeof selector == "number"
  129. ? headers.filter(":eq(" + selector + ")")
  130. : headers.not(headers.not(selector))
  131. : selector === false
  132. ? $("<div>")
  133. : headers.filter(":eq(0)");
  134. }
  135. function toggle(toShow, toHide, data, clickedActive, down) {
  136. var finished = function(cancel) {
  137. running = cancel ? 0 : --running;
  138. if ( running )
  139. return;
  140. // trigger custom change event
  141. container.trigger("change", data);
  142. };
  143. // count elements to animate
  144. running = toHide.size() == 0 ? toShow.size() : toHide.size();
  145. if ( settings.animated ) {
  146. if ( !settings.alwaysOpen && clickedActive ) {
  147. toShow.slideToggle(settings.animated);
  148. finished(true);
  149. } else {
  150. $.ui.accordion.animations[settings.animated]({
  151. toShow: toShow,
  152. toHide: toHide,
  153. finished: finished,
  154. down: down
  155. });
  156. }
  157. } else {
  158. if ( !settings.alwaysOpen && clickedActive ) {
  159. toShow.toggle();
  160. } else {
  161. toHide.hide();
  162. toShow.show();
  163. }
  164. finished(true);
  165. }
  166. }
  167. function clickHandler(event) {
  168. // called only when using activate(false) to close all parts programmatically
  169. if ( !event.target && !settings.alwaysOpen ) {
  170. active.toggleClass(settings.selectedClass);
  171. var toHide = active.nextUntil(settings.header);
  172. var toShow = active = $([]);
  173. toggle( toShow, toHide );
  174. return;
  175. }
  176. // get the click target
  177. var clicked = $(event.target);
  178. // due to the event delegation model, we have to check if one
  179. // of the parent elements is our actual header, and find that
  180. if ( clicked.parents(settings.header).length )
  181. while ( !clicked.is(settings.header) )
  182. clicked = clicked.parent();
  183. var clickedActive = clicked[0] == active[0];
  184. // if animations are still active, or the active header is the target, ignore click
  185. if(running || (settings.alwaysOpen && clickedActive) || !clicked.is(settings.header))
  186. return;
  187. // switch classes
  188. active.parent().andSelf().toggleClass(settings.selectedClass);
  189. if ( !clickedActive ) {
  190. clicked.parent().andSelf().addClass(settings.selectedClass);
  191. }
  192. // find elements to show and hide
  193. var toShow = clicked.nextUntil(settings.header),
  194. toHide = active.nextUntil(settings.header),
  195. data = [clicked, active, toShow, toHide],
  196. down = headers.index( active[0] ) > headers.index( clicked[0] );
  197. active = clickedActive ? $([]) : clicked;
  198. toggle( toShow, toHide, data, clickedActive, down );
  199. return !toShow.length;
  200. };
  201. function activateHandler(event, index) {
  202. // IE manages to call activateHandler on normal clicks
  203. if ( arguments.length == 1 )
  204. return;
  205. // call clickHandler with custom event
  206. clickHandler({
  207. target: findActive(index)[0]
  208. });
  209. };
  210. return container
  211. .bind(settings.event, clickHandler)
  212. .bind("activate", activateHandler);
  213. },
  214. activate: function(index) {
  215. return this.trigger('activate', [index]);
  216. }
  217. });
  218. })(jQuery);