No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 
 
 

512 líneas
14 KiB

  1. /*
  2. * jQuery UI Accordion 1.8
  3. *
  4. * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
  5. * Dual licensed under the MIT (MIT-LICENSE.txt)
  6. * and GPL (GPL-LICENSE.txt) licenses.
  7. *
  8. * http://docs.jquery.com/UI/Accordion
  9. *
  10. * Depends:
  11. * jquery.ui.core.js
  12. * jquery.ui.widget.js
  13. */
  14. (function($) {
  15. $.widget("ui.accordion", {
  16. options: {
  17. active: 0,
  18. animated: 'slide',
  19. autoHeight: true,
  20. clearStyle: false,
  21. collapsible: false,
  22. event: "click",
  23. fillSpace: false,
  24. header: "> li > :first-child,> :not(li):even",
  25. icons: {
  26. header: "ui-icon-triangle-1-e",
  27. headerSelected: "ui-icon-triangle-1-s"
  28. },
  29. navigation: false,
  30. navigationFilter: function() {
  31. return this.href.toLowerCase() == location.href.toLowerCase();
  32. }
  33. },
  34. _create: function() {
  35. var o = this.options, self = this;
  36. this.running = 0;
  37. this.element.addClass("ui-accordion ui-widget ui-helper-reset");
  38. // in lack of child-selectors in CSS we need to mark top-LIs in a UL-accordion for some IE-fix
  39. if (this.element[0].nodeName == "UL") {
  40. this.element.children("li").addClass("ui-accordion-li-fix");
  41. }
  42. this.headers = this.element.find(o.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all")
  43. .bind("mouseenter.accordion", function(){ $(this).addClass('ui-state-hover'); })
  44. .bind("mouseleave.accordion", function(){ $(this).removeClass('ui-state-hover'); })
  45. .bind("focus.accordion", function(){ $(this).addClass('ui-state-focus'); })
  46. .bind("blur.accordion", function(){ $(this).removeClass('ui-state-focus'); });
  47. this.headers
  48. .next()
  49. .addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");
  50. if ( o.navigation ) {
  51. var current = this.element.find("a").filter(o.navigationFilter);
  52. if ( current.length ) {
  53. var header = current.closest(".ui-accordion-header");
  54. if ( header.length ) {
  55. // anchor within header
  56. this.active = header;
  57. } else {
  58. // anchor within content
  59. this.active = current.closest(".ui-accordion-content").prev();
  60. }
  61. }
  62. }
  63. this.active = this._findActive(this.active || o.active).toggleClass("ui-state-default").toggleClass("ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");
  64. this.active.next().addClass('ui-accordion-content-active');
  65. //Append icon elements
  66. this._createIcons();
  67. // IE7-/Win - Extra vertical space in lists fixed
  68. if ($.browser.msie) {
  69. this.element.find('a').css('zoom', '1');
  70. }
  71. this.resize();
  72. //ARIA
  73. this.element.attr('role','tablist');
  74. this.headers
  75. .attr('role','tab')
  76. .bind('keydown', function(event) { return self._keydown(event); })
  77. .next()
  78. .attr('role','tabpanel');
  79. this.headers
  80. .not(this.active || "")
  81. .attr('aria-expanded','false')
  82. .attr("tabIndex", "-1")
  83. .next()
  84. .hide();
  85. // make sure at least one header is in the tab order
  86. if (!this.active.length) {
  87. this.headers.eq(0).attr('tabIndex','0');
  88. } else {
  89. this.active
  90. .attr('aria-expanded','true')
  91. .attr('tabIndex', '0');
  92. }
  93. // only need links in taborder for Safari
  94. if (!$.browser.safari)
  95. this.headers.find('a').attr('tabIndex','-1');
  96. if (o.event) {
  97. this.headers.bind((o.event) + ".accordion", function(event) {
  98. self._clickHandler.call(self, event, this);
  99. event.preventDefault();
  100. });
  101. }
  102. },
  103. _createIcons: function() {
  104. var o = this.options;
  105. if (o.icons) {
  106. $("<span/>").addClass("ui-icon " + o.icons.header).prependTo(this.headers);
  107. this.active.find(".ui-icon").toggleClass(o.icons.header).toggleClass(o.icons.headerSelected);
  108. this.element.addClass("ui-accordion-icons");
  109. }
  110. },
  111. _destroyIcons: function() {
  112. this.headers.children(".ui-icon").remove();
  113. this.element.removeClass("ui-accordion-icons");
  114. },
  115. destroy: function() {
  116. var o = this.options;
  117. this.element
  118. .removeClass("ui-accordion ui-widget ui-helper-reset")
  119. .removeAttr("role")
  120. .unbind('.accordion')
  121. .removeData('accordion');
  122. this.headers
  123. .unbind(".accordion")
  124. .removeClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-corner-top")
  125. .removeAttr("role").removeAttr("aria-expanded").removeAttr("tabindex");
  126. this.headers.find("a").removeAttr("tabindex");
  127. this._destroyIcons();
  128. var contents = this.headers.next().css("display", "").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active");
  129. if (o.autoHeight || o.fillHeight) {
  130. contents.css("height", "");
  131. }
  132. return this;
  133. },
  134. _setOption: function(key, value) {
  135. $.Widget.prototype._setOption.apply(this, arguments);
  136. if (key == "active") {
  137. this.activate(value);
  138. }
  139. if (key == "icons") {
  140. this._destroyIcons();
  141. if (value) {
  142. this._createIcons();
  143. }
  144. }
  145. },
  146. _keydown: function(event) {
  147. var o = this.options, keyCode = $.ui.keyCode;
  148. if (o.disabled || event.altKey || event.ctrlKey)
  149. return;
  150. var length = this.headers.length;
  151. var currentIndex = this.headers.index(event.target);
  152. var toFocus = false;
  153. switch(event.keyCode) {
  154. case keyCode.RIGHT:
  155. case keyCode.DOWN:
  156. toFocus = this.headers[(currentIndex + 1) % length];
  157. break;
  158. case keyCode.LEFT:
  159. case keyCode.UP:
  160. toFocus = this.headers[(currentIndex - 1 + length) % length];
  161. break;
  162. case keyCode.SPACE:
  163. case keyCode.ENTER:
  164. this._clickHandler({ target: event.target }, event.target);
  165. event.preventDefault();
  166. }
  167. if (toFocus) {
  168. $(event.target).attr('tabIndex','-1');
  169. $(toFocus).attr('tabIndex','0');
  170. toFocus.focus();
  171. return false;
  172. }
  173. return true;
  174. },
  175. resize: function() {
  176. var o = this.options, maxHeight;
  177. if (o.fillSpace) {
  178. if($.browser.msie) { var defOverflow = this.element.parent().css('overflow'); this.element.parent().css('overflow', 'hidden'); }
  179. maxHeight = this.element.parent().height();
  180. if($.browser.msie) { this.element.parent().css('overflow', defOverflow); }
  181. this.headers.each(function() {
  182. maxHeight -= $(this).outerHeight(true);
  183. });
  184. this.headers.next().each(function() {
  185. $(this).height(Math.max(0, maxHeight - $(this).innerHeight() + $(this).height()));
  186. }).css('overflow', 'auto');
  187. } else if ( o.autoHeight ) {
  188. maxHeight = 0;
  189. this.headers.next().each(function() {
  190. maxHeight = Math.max(maxHeight, $(this).height());
  191. }).height(maxHeight);
  192. }
  193. return this;
  194. },
  195. activate: function(index) {
  196. // TODO this gets called on init, changing the option without an explicit call for that
  197. this.options.active = index;
  198. // call clickHandler with custom event
  199. var active = this._findActive(index)[0];
  200. this._clickHandler({ target: active }, active);
  201. return this;
  202. },
  203. _findActive: function(selector) {
  204. return selector
  205. ? typeof selector == "number"
  206. ? this.headers.filter(":eq(" + selector + ")")
  207. : this.headers.not(this.headers.not(selector))
  208. : selector === false
  209. ? $([])
  210. : this.headers.filter(":eq(0)");
  211. },
  212. // TODO isn't event.target enough? why the seperate target argument?
  213. _clickHandler: function(event, target) {
  214. var o = this.options;
  215. if (o.disabled)
  216. return;
  217. // called only when using activate(false) to close all parts programmatically
  218. if (!event.target) {
  219. if (!o.collapsible)
  220. return;
  221. this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all")
  222. .find(".ui-icon").removeClass(o.icons.headerSelected).addClass(o.icons.header);
  223. this.active.next().addClass('ui-accordion-content-active');
  224. var toHide = this.active.next(),
  225. data = {
  226. options: o,
  227. newHeader: $([]),
  228. oldHeader: o.active,
  229. newContent: $([]),
  230. oldContent: toHide
  231. },
  232. toShow = (this.active = $([]));
  233. this._toggle(toShow, toHide, data);
  234. return;
  235. }
  236. // get the click target
  237. var clicked = $(event.currentTarget || target);
  238. var clickedIsActive = clicked[0] == this.active[0];
  239. // TODO the option is changed, is that correct?
  240. // TODO if it is correct, shouldn't that happen after determining that the click is valid?
  241. o.active = o.collapsible && clickedIsActive ? false : $('.ui-accordion-header', this.element).index(clicked);
  242. // if animations are still active, or the active header is the target, ignore click
  243. if (this.running || (!o.collapsible && clickedIsActive)) {
  244. return;
  245. }
  246. // switch classes
  247. this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all")
  248. .find(".ui-icon").removeClass(o.icons.headerSelected).addClass(o.icons.header);
  249. if (!clickedIsActive) {
  250. clicked.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top")
  251. .find(".ui-icon").removeClass(o.icons.header).addClass(o.icons.headerSelected);
  252. clicked.next().addClass('ui-accordion-content-active');
  253. }
  254. // find elements to show and hide
  255. var toShow = clicked.next(),
  256. toHide = this.active.next(),
  257. data = {
  258. options: o,
  259. newHeader: clickedIsActive && o.collapsible ? $([]) : clicked,
  260. oldHeader: this.active,
  261. newContent: clickedIsActive && o.collapsible ? $([]) : toShow,
  262. oldContent: toHide
  263. },
  264. down = this.headers.index( this.active[0] ) > this.headers.index( clicked[0] );
  265. this.active = clickedIsActive ? $([]) : clicked;
  266. this._toggle(toShow, toHide, data, clickedIsActive, down);
  267. return;
  268. },
  269. _toggle: function(toShow, toHide, data, clickedIsActive, down) {
  270. var o = this.options, self = this;
  271. this.toShow = toShow;
  272. this.toHide = toHide;
  273. this.data = data;
  274. var complete = function() { if(!self) return; return self._completed.apply(self, arguments); };
  275. // trigger changestart event
  276. this._trigger("changestart", null, this.data);
  277. // count elements to animate
  278. this.running = toHide.size() === 0 ? toShow.size() : toHide.size();
  279. if (o.animated) {
  280. var animOptions = {};
  281. if ( o.collapsible && clickedIsActive ) {
  282. animOptions = {
  283. toShow: $([]),
  284. toHide: toHide,
  285. complete: complete,
  286. down: down,
  287. autoHeight: o.autoHeight || o.fillSpace
  288. };
  289. } else {
  290. animOptions = {
  291. toShow: toShow,
  292. toHide: toHide,
  293. complete: complete,
  294. down: down,
  295. autoHeight: o.autoHeight || o.fillSpace
  296. };
  297. }
  298. if (!o.proxied) {
  299. o.proxied = o.animated;
  300. }
  301. if (!o.proxiedDuration) {
  302. o.proxiedDuration = o.duration;
  303. }
  304. o.animated = $.isFunction(o.proxied) ?
  305. o.proxied(animOptions) : o.proxied;
  306. o.duration = $.isFunction(o.proxiedDuration) ?
  307. o.proxiedDuration(animOptions) : o.proxiedDuration;
  308. var animations = $.ui.accordion.animations,
  309. duration = o.duration,
  310. easing = o.animated;
  311. if (easing && !animations[easing] && !$.easing[easing]) {
  312. easing = 'slide';
  313. }
  314. if (!animations[easing]) {
  315. animations[easing] = function(options) {
  316. this.slide(options, {
  317. easing: easing,
  318. duration: duration || 700
  319. });
  320. };
  321. }
  322. animations[easing](animOptions);
  323. } else {
  324. if (o.collapsible && clickedIsActive) {
  325. toShow.toggle();
  326. } else {
  327. toHide.hide();
  328. toShow.show();
  329. }
  330. complete(true);
  331. }
  332. // TODO assert that the blur and focus triggers are really necessary, remove otherwise
  333. toHide.prev().attr('aria-expanded','false').attr("tabIndex", "-1").blur();
  334. toShow.prev().attr('aria-expanded','true').attr("tabIndex", "0").focus();
  335. },
  336. _completed: function(cancel) {
  337. var o = this.options;
  338. this.running = cancel ? 0 : --this.running;
  339. if (this.running) return;
  340. if (o.clearStyle) {
  341. this.toShow.add(this.toHide).css({
  342. height: "",
  343. overflow: ""
  344. });
  345. }
  346. // other classes are removed before the animation; this one needs to stay until completed
  347. this.toHide.removeClass("ui-accordion-content-active");
  348. this._trigger('change', null, this.data);
  349. }
  350. });
  351. $.extend($.ui.accordion, {
  352. version: "1.8",
  353. animations: {
  354. slide: function(options, additions) {
  355. options = $.extend({
  356. easing: "swing",
  357. duration: 300
  358. }, options, additions);
  359. if ( !options.toHide.size() ) {
  360. options.toShow.animate({height: "show"}, options);
  361. return;
  362. }
  363. if ( !options.toShow.size() ) {
  364. options.toHide.animate({height: "hide"}, options);
  365. return;
  366. }
  367. var overflow = options.toShow.css('overflow'),
  368. percentDone = 0,
  369. showProps = {},
  370. hideProps = {},
  371. fxAttrs = [ "height", "paddingTop", "paddingBottom" ],
  372. originalWidth;
  373. // fix width before calculating height of hidden element
  374. var s = options.toShow;
  375. originalWidth = s[0].style.width;
  376. s.width( parseInt(s.parent().width(),10) - parseInt(s.css("paddingLeft"),10) - parseInt(s.css("paddingRight"),10) - (parseInt(s.css("borderLeftWidth"),10) || 0) - (parseInt(s.css("borderRightWidth"),10) || 0) );
  377. $.each(fxAttrs, function(i, prop) {
  378. hideProps[prop] = 'hide';
  379. var parts = ('' + $.css(options.toShow[0], prop)).match(/^([\d+-.]+)(.*)$/);
  380. showProps[prop] = {
  381. value: parts[1],
  382. unit: parts[2] || 'px'
  383. };
  384. });
  385. options.toShow.css({ height: 0, overflow: 'hidden' }).show();
  386. options.toHide.filter(":hidden").each(options.complete).end().filter(":visible").animate(hideProps,{
  387. step: function(now, settings) {
  388. // only calculate the percent when animating height
  389. // IE gets very inconsistent results when animating elements
  390. // with small values, which is common for padding
  391. if (settings.prop == 'height') {
  392. percentDone = ( settings.end - settings.start === 0 ) ? 0 :
  393. (settings.now - settings.start) / (settings.end - settings.start);
  394. }
  395. options.toShow[0].style[settings.prop] =
  396. (percentDone * showProps[settings.prop].value) + showProps[settings.prop].unit;
  397. },
  398. duration: options.duration,
  399. easing: options.easing,
  400. complete: function() {
  401. if ( !options.autoHeight ) {
  402. options.toShow.css("height", "");
  403. }
  404. options.toShow.css("width", originalWidth);
  405. options.toShow.css({overflow: overflow});
  406. options.complete();
  407. }
  408. });
  409. },
  410. bounceslide: function(options) {
  411. this.slide(options, {
  412. easing: options.down ? "easeOutBounce" : "swing",
  413. duration: options.down ? 1000 : 200
  414. });
  415. }
  416. }
  417. });
  418. })(jQuery);