Rev 162 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/** jQuery UI Accordion 1.8.1** Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)* Dual licensed under the MIT (MIT-LICENSE.txt)* and GPL (GPL-LICENSE.txt) licenses.** http://docs.jquery.com/UI/Accordion** Depends:* jquery.ui.core.js* jquery.ui.widget.js*/(function($) {$.widget("ui.accordion", {options: {active: 0,animated: 'slide',autoHeight: true,clearStyle: false,collapsible: false,event: "click",fillSpace: false,header: "> li > :first-child,> :not(li):even",icons: {header: "ui-icon-triangle-1-e",headerSelected: "ui-icon-triangle-1-s"},navigation: false,navigationFilter: function() {return this.href.toLowerCase() == location.href.toLowerCase();}},_create: function() {var o = this.options, self = this;this.running = 0;this.element.addClass("ui-accordion ui-widget ui-helper-reset");// in lack of child-selectors in CSS we need to mark top-LIs in a UL-accordion for some IE-fixif (this.element[0].nodeName == "UL") {this.element.children("li").addClass("ui-accordion-li-fix");}this.headers = this.element.find(o.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion", function(){ $(this).addClass('ui-state-hover'); }).bind("mouseleave.accordion", function(){ $(this).removeClass('ui-state-hover'); }).bind("focus.accordion", function(){ $(this).addClass('ui-state-focus'); }).bind("blur.accordion", function(){ $(this).removeClass('ui-state-focus'); });this.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");if ( o.navigation ) {var current = this.element.find("a").filter(o.navigationFilter);if ( current.length ) {var header = current.closest(".ui-accordion-header");if ( header.length ) {// anchor within headerthis.active = header;} else {// anchor within contentthis.active = current.closest(".ui-accordion-content").prev();}}}this.active = this._findActive(this.active || o.active).toggleClass("ui-state-default").toggleClass("ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");this.active.next().addClass('ui-accordion-content-active');//Append icon elementsthis._createIcons();this.resize();//ARIAthis.element.attr('role','tablist');this.headers.attr('role','tab').bind('keydown', function(event) { return self._keydown(event); }).next().attr('role','tabpanel');this.headers.not(this.active || "").attr('aria-expanded','false').attr("tabIndex", "-1").next().hide();// make sure at least one header is in the tab orderif (!this.active.length) {this.headers.eq(0).attr('tabIndex','0');} else {this.active.attr('aria-expanded','true').attr('tabIndex', '0');}// only need links in taborder for Safariif (!$.browser.safari)this.headers.find('a').attr('tabIndex','-1');if (o.event) {this.headers.bind((o.event) + ".accordion", function(event) {self._clickHandler.call(self, event, this);event.preventDefault();});}},_createIcons: function() {var o = this.options;if (o.icons) {$("<span/>").addClass("ui-icon " + o.icons.header).prependTo(this.headers);this.active.find(".ui-icon").toggleClass(o.icons.header).toggleClass(o.icons.headerSelected);this.element.addClass("ui-accordion-icons");}},_destroyIcons: function() {this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons");},destroy: function() {var o = this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role").unbind('.accordion').removeData('accordion');this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("tabIndex");this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();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");if (o.autoHeight || o.fillHeight) {contents.css("height", "");}return this;},_setOption: function(key, value) {$.Widget.prototype._setOption.apply(this, arguments);if (key == "active") {this.activate(value);}if (key == "icons") {this._destroyIcons();if (value) {this._createIcons();}}},_keydown: function(event) {var o = this.options, keyCode = $.ui.keyCode;if (o.disabled || event.altKey || event.ctrlKey)return;var length = this.headers.length;var currentIndex = this.headers.index(event.target);var toFocus = false;switch(event.keyCode) {case keyCode.RIGHT:case keyCode.DOWN:toFocus = this.headers[(currentIndex + 1) % length];break;case keyCode.LEFT:case keyCode.UP:toFocus = this.headers[(currentIndex - 1 + length) % length];break;case keyCode.SPACE:case keyCode.ENTER:this._clickHandler({ target: event.target }, event.target);event.preventDefault();}if (toFocus) {$(event.target).attr('tabIndex','-1');$(toFocus).attr('tabIndex','0');toFocus.focus();return false;}return true;},resize: function() {var o = this.options, maxHeight;if (o.fillSpace) {if($.browser.msie) { var defOverflow = this.element.parent().css('overflow'); this.element.parent().css('overflow', 'hidden'); }maxHeight = this.element.parent().height();if($.browser.msie) { this.element.parent().css('overflow', defOverflow); }this.headers.each(function() {maxHeight -= $(this).outerHeight(true);});this.headers.next().each(function() {$(this).height(Math.max(0, maxHeight - $(this).innerHeight() + $(this).height()));}).css('overflow', 'auto');} else if ( o.autoHeight ) {maxHeight = 0;this.headers.next().each(function() {maxHeight = Math.max(maxHeight, $(this).height());}).height(maxHeight);}return this;},activate: function(index) {// TODO this gets called on init, changing the option without an explicit call for thatthis.options.active = index;// call clickHandler with custom eventvar active = this._findActive(index)[0];this._clickHandler({ target: active }, active);return this;},_findActive: function(selector) {return selector? typeof selector == "number"? this.headers.filter(":eq(" + selector + ")"): this.headers.not(this.headers.not(selector)): selector === false? $([]): this.headers.filter(":eq(0)");},// TODO isn't event.target enough? why the seperate target argument?_clickHandler: function(event, target) {var o = this.options;if (o.disabled)return;// called only when using activate(false) to close all parts programmaticallyif (!event.target) {if (!o.collapsible)return;this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").find(".ui-icon").removeClass(o.icons.headerSelected).addClass(o.icons.header);this.active.next().addClass('ui-accordion-content-active');var toHide = this.active.next(),data = {options: o,newHeader: $([]),oldHeader: o.active,newContent: $([]),oldContent: toHide},toShow = (this.active = $([]));this._toggle(toShow, toHide, data);return;}// get the click targetvar clicked = $(event.currentTarget || target);var clickedIsActive = clicked[0] == this.active[0];// TODO the option is changed, is that correct?// TODO if it is correct, shouldn't that happen after determining that the click is valid?o.active = o.collapsible && clickedIsActive ? false : $('.ui-accordion-header', this.element).index(clicked);// if animations are still active, or the active header is the target, ignore clickif (this.running || (!o.collapsible && clickedIsActive)) {return;}// switch classesthis.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").find(".ui-icon").removeClass(o.icons.headerSelected).addClass(o.icons.header);if (!clickedIsActive) {clicked.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").find(".ui-icon").removeClass(o.icons.header).addClass(o.icons.headerSelected);clicked.next().addClass('ui-accordion-content-active');}// find elements to show and hidevar toShow = clicked.next(),toHide = this.active.next(),data = {options: o,newHeader: clickedIsActive && o.collapsible ? $([]) : clicked,oldHeader: this.active,newContent: clickedIsActive && o.collapsible ? $([]) : toShow,oldContent: toHide},down = this.headers.index( this.active[0] ) > this.headers.index( clicked[0] );this.active = clickedIsActive ? $([]) : clicked;this._toggle(toShow, toHide, data, clickedIsActive, down);return;},_toggle: function(toShow, toHide, data, clickedIsActive, down) {var o = this.options, self = this;this.toShow = toShow;this.toHide = toHide;this.data = data;var complete = function() { if(!self) return; return self._completed.apply(self, arguments); };// trigger changestart eventthis._trigger("changestart", null, this.data);// count elements to animatethis.running = toHide.size() === 0 ? toShow.size() : toHide.size();if (o.animated) {var animOptions = {};if ( o.collapsible && clickedIsActive ) {animOptions = {toShow: $([]),toHide: toHide,complete: complete,down: down,autoHeight: o.autoHeight || o.fillSpace};} else {animOptions = {toShow: toShow,toHide: toHide,complete: complete,down: down,autoHeight: o.autoHeight || o.fillSpace};}if (!o.proxied) {o.proxied = o.animated;}if (!o.proxiedDuration) {o.proxiedDuration = o.duration;}o.animated = $.isFunction(o.proxied) ?o.proxied(animOptions) : o.proxied;o.duration = $.isFunction(o.proxiedDuration) ?o.proxiedDuration(animOptions) : o.proxiedDuration;var animations = $.ui.accordion.animations,duration = o.duration,easing = o.animated;if (easing && !animations[easing] && !$.easing[easing]) {easing = 'slide';}if (!animations[easing]) {animations[easing] = function(options) {this.slide(options, {easing: easing,duration: duration || 700});};}animations[easing](animOptions);} else {if (o.collapsible && clickedIsActive) {toShow.toggle();} else {toHide.hide();toShow.show();}complete(true);}// TODO assert that the blur and focus triggers are really necessary, remove otherwisetoHide.prev().attr('aria-expanded','false').attr("tabIndex", "-1").blur();toShow.prev().attr('aria-expanded','true').attr("tabIndex", "0").focus();},_completed: function(cancel) {var o = this.options;this.running = cancel ? 0 : --this.running;if (this.running) return;if (o.clearStyle) {this.toShow.add(this.toHide).css({height: "",overflow: ""});}// other classes are removed before the animation; this one needs to stay until completedthis.toHide.removeClass("ui-accordion-content-active");this._trigger('change', null, this.data);}});$.extend($.ui.accordion, {version: "1.8.1",animations: {slide: function(options, additions) {options = $.extend({easing: "swing",duration: 300}, options, additions);if ( !options.toHide.size() ) {options.toShow.animate({height: "show"}, options);return;}if ( !options.toShow.size() ) {options.toHide.animate({height: "hide"}, options);return;}var overflow = options.toShow.css('overflow'),percentDone = 0,showProps = {},hideProps = {},fxAttrs = [ "height", "paddingTop", "paddingBottom" ],originalWidth;// fix width before calculating height of hidden elementvar s = options.toShow;originalWidth = s[0].style.width;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) );$.each(fxAttrs, function(i, prop) {hideProps[prop] = 'hide';var parts = ('' + $.css(options.toShow[0], prop)).match(/^([\d+-.]+)(.*)$/);showProps[prop] = {value: parts[1],unit: parts[2] || 'px'};});options.toShow.css({ height: 0, overflow: 'hidden' }).show();options.toHide.filter(":hidden").each(options.complete).end().filter(":visible").animate(hideProps,{step: function(now, settings) {// only calculate the percent when animating height// IE gets very inconsistent results when animating elements// with small values, which is common for paddingif (settings.prop == 'height') {percentDone = ( settings.end - settings.start === 0 ) ? 0 :(settings.now - settings.start) / (settings.end - settings.start);}options.toShow[0].style[settings.prop] =(percentDone * showProps[settings.prop].value) + showProps[settings.prop].unit;},duration: options.duration,easing: options.easing,complete: function() {if ( !options.autoHeight ) {options.toShow.css("height", "");}options.toShow.css("width", originalWidth);options.toShow.css({overflow: overflow});options.complete();}});},bounceslide: function(options) {this.slide(options, {easing: options.down ? "easeOutBounce" : "swing",duration: options.down ? 1000 : 200});}}});})(jQuery);