Subversion Repositories Sites.obs-saisons.fr

Rev

Blame | Last modification | View Log | RSS feed

/*
 * jQuery UI Accordion 1.8.5
 *
 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Accordion
 *
 * Depends:
 *      jquery.ui.core.js
 *      jquery.ui.widget.js
 */
(function( $, undefined ) {

$.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 self = this,
                        options = self.options;

                self.running = 0;

                self.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-fix
                        .children( "li" )
                                .addClass( "ui-accordion-li-fix" );

                self.headers = self.element.find( options.header )
                        .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" )
                        .bind( "mouseenter.accordion", function() {
                                if ( options.disabled ) {
                                        return;
                                }
                                $( this ).addClass( "ui-state-hover" );
                        })
                        .bind( "mouseleave.accordion", function() {
                                if ( options.disabled ) {
                                        return;
                                }
                                $( this ).removeClass( "ui-state-hover" );
                        })
                        .bind( "focus.accordion", function() {
                                if ( options.disabled ) {
                                        return;
                                }
                                $( this ).addClass( "ui-state-focus" );
                        })
                        .bind( "blur.accordion", function() {
                                if ( options.disabled ) {
                                        return;
                                }
                                $( this ).removeClass( "ui-state-focus" );
                        });

                self.headers.next()
                        .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" );

                if ( options.navigation ) {
                        var current = self.element.find( "a" ).filter( options.navigationFilter ).eq( 0 );
                        if ( current.length ) {
                                var header = current.closest( ".ui-accordion-header" );
                                if ( header.length ) {
                                        // anchor within header
                                        self.active = header;
                                } else {
                                        // anchor within content
                                        self.active = current.closest( ".ui-accordion-content" ).prev();
                                }
                        }
                }

                self.active = self._findActive( self.active || options.active )
                        .addClass( "ui-state-default ui-state-active" )
                        .toggleClass( "ui-corner-all ui-corner-top" );
                self.active.next().addClass( "ui-accordion-content-active" );

                self._createIcons();
                self.resize();
                
                // ARIA
                self.element.attr( "role", "tablist" );

                self.headers
                        .attr( "role", "tab" )
                        .bind( "keydown.accordion", function( event ) {
                                return self._keydown( event );
                        })
                        .next()
                                .attr( "role", "tabpanel" );

                self.headers
                        .not( self.active || "" )
                        .attr({
                                "aria-expanded": "false",
                                tabIndex: -1
                        })
                        .next()
                                .hide();

                // make sure at least one header is in the tab order
                if ( !self.active.length ) {
                        self.headers.eq( 0 ).attr( "tabIndex", 0 );
                } else {
                        self.active
                                .attr({
                                        "aria-expanded": "true",
                                        tabIndex: 0
                                });
                }

                // only need links in tab order for Safari
                if ( !$.browser.safari ) {
                        self.headers.find( "a" ).attr( "tabIndex", -1 );
                }

                if ( options.event ) {
                        self.headers.bind( options.event.split(" ").join(".accordion ") + ".accordion", function(event) {
                                self._clickHandler.call( self, event, this );
                                event.preventDefault();
                        });
                }
        },

        _createIcons: function() {
                var options = this.options;
                if ( options.icons ) {
                        $( "<span></span>" )
                                .addClass( "ui-icon " + options.icons.header )
                                .prependTo( this.headers );
                        this.active.children( ".ui-icon" )
                                .toggleClass(options.icons.header)
                                .toggleClass(options.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 options = this.options;

                this.element
                        .removeClass( "ui-accordion ui-widget ui-helper-reset" )
                        .removeAttr( "role" );

                this.headers
                        .unbind( ".accordion" )
                        .removeClass( "ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled 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 ui-accordion-disabled ui-state-disabled" );
                if ( options.autoHeight || options.fillHeight ) {
                        contents.css( "height", "" );
                }

                return $.Widget.prototype.destroy.call( 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();
                        }
                }
                // #5332 - opacity doesn't cascade to positioned elements in IE
                // so we need to add the disabled class to the headers and panels
                if ( key == "disabled" ) {
                        this.headers.add(this.headers.next())
                                [ value ? "addClass" : "removeClass" ](
                                        "ui-accordion-disabled ui-state-disabled" );
                }
        },

        _keydown: function( event ) {
                if ( this.options.disabled || event.altKey || event.ctrlKey ) {
                        return;
                }

                var keyCode = $.ui.keyCode,
                        length = this.headers.length,
                        currentIndex = this.headers.index( event.target ),
                        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 options = this.options,
                        maxHeight;

                if ( options.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 ( options.autoHeight ) {
                        maxHeight = 0;
                        this.headers.next()
                                .each(function() {
                                        maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
                                })
                                .height( maxHeight );
                }

                return this;
        },

        activate: function( index ) {
                // TODO this gets called on init, changing the option without an explicit call for that
                this.options.active = index;
                // call clickHandler with custom event
                var 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 separate target argument?
        _clickHandler: function( event, target ) {
                var options = this.options;
                if ( options.disabled ) {
                        return;
                }

                // called only when using activate(false) to close all parts programmatically
                if ( !event.target ) {
                        if ( !options.collapsible ) {
                                return;
                        }
                        this.active
                                .removeClass( "ui-state-active ui-corner-top" )
                                .addClass( "ui-state-default ui-corner-all" )
                                .children( ".ui-icon" )
                                        .removeClass( options.icons.headerSelected )
                                        .addClass( options.icons.header );
                        this.active.next().addClass( "ui-accordion-content-active" );
                        var toHide = this.active.next(),
                                data = {
                                        options: options,
                                        newHeader: $( [] ),
                                        oldHeader: options.active,
                                        newContent: $( [] ),
                                        oldContent: toHide
                                },
                                toShow = ( this.active = $( [] ) );
                        this._toggle( toShow, toHide, data );
                        return;
                }

                // get the click target
                var clicked = $( event.currentTarget || target ),
                        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?
                options.active = options.collapsible && clickedIsActive ?
                        false :
                        this.headers.index( clicked );

                // if animations are still active, or the active header is the target, ignore click
                if ( this.running || ( !options.collapsible && clickedIsActive ) ) {
                        return;
                }

                // switch classes
                this.active
                        .removeClass( "ui-state-active ui-corner-top" )
                        .addClass( "ui-state-default ui-corner-all" )
                        .children( ".ui-icon" )
                                .removeClass( options.icons.headerSelected )
                                .addClass( options.icons.header );
                if ( !clickedIsActive ) {
                        clicked
                                .removeClass( "ui-state-default ui-corner-all" )
                                .addClass( "ui-state-active ui-corner-top" )
                                .children( ".ui-icon" )
                                        .removeClass( options.icons.header )
                                        .addClass( options.icons.headerSelected );
                        clicked
                                .next()
                                .addClass( "ui-accordion-content-active" );
                }

                // find elements to show and hide
                var toShow = clicked.next(),
                        toHide = this.active.next(),
                        data = {
                                options: options,
                                newHeader: clickedIsActive && options.collapsible ? $([]) : clicked,
                                oldHeader: this.active,
                                newContent: clickedIsActive && options.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 self = this,
                        options = self.options;

                self.toShow = toShow;
                self.toHide = toHide;
                self.data = data;

                var complete = function() {
                        if ( !self ) {
                                return;
                        }
                        return self._completed.apply( self, arguments );
                };

                // trigger changestart event
                self._trigger( "changestart", null, self.data );

                // count elements to animate
                self.running = toHide.size() === 0 ? toShow.size() : toHide.size();

                if ( options.animated ) {
                        var animOptions = {};

                        if ( options.collapsible && clickedIsActive ) {
                                animOptions = {
                                        toShow: $( [] ),
                                        toHide: toHide,
                                        complete: complete,
                                        down: down,
                                        autoHeight: options.autoHeight || options.fillSpace
                                };
                        } else {
                                animOptions = {
                                        toShow: toShow,
                                        toHide: toHide,
                                        complete: complete,
                                        down: down,
                                        autoHeight: options.autoHeight || options.fillSpace
                                };
                        }

                        if ( !options.proxied ) {
                                options.proxied = options.animated;
                        }

                        if ( !options.proxiedDuration ) {
                                options.proxiedDuration = options.duration;
                        }

                        options.animated = $.isFunction( options.proxied ) ?
                                options.proxied( animOptions ) :
                                options.proxied;

                        options.duration = $.isFunction( options.proxiedDuration ) ?
                                options.proxiedDuration( animOptions ) :
                                options.proxiedDuration;

                        var animations = $.ui.accordion.animations,
                                duration = options.duration,
                                easing = options.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 ( options.collapsible && clickedIsActive ) {
                                toShow.toggle();
                        } else {
                                toHide.hide();
                                toShow.show();
                        }

                        complete( true );
                }

                // TODO assert that the blur and focus triggers are really necessary, remove otherwise
                toHide.prev()
                        .attr({
                                "aria-expanded": "false",
                                tabIndex: -1
                        })
                        .blur();
                toShow.prev()
                        .attr({
                                "aria-expanded": "true",
                                tabIndex: 0
                        })
                        .focus();
        },

        _completed: function( cancel ) {
                this.running = cancel ? 0 : --this.running;
                if ( this.running ) {
                        return;
                }

                if ( this.options.clearStyle ) {
                        this.toShow.add( this.toHide ).css({
                                height: "",
                                overflow: ""
                        });
                }

                // other classes are removed before the animation; this one needs to stay until completed
                this.toHide.removeClass( "ui-accordion-content-active" );

                this._trigger( "change", null, this.data );
        }
});

$.extend( $.ui.accordion, {
        version: "1.8.5",
        animations: {
                slide: function( options, additions ) {
                        options = $.extend({
                                easing: "swing",
                                duration: 300
                        }, options, additions );
                        if ( !options.toHide.size() ) {
                                options.toShow.animate({
                                        height: "show",
                                        paddingTop: "show",
                                        paddingBottom: "show"
                                }, options );
                                return;
                        }
                        if ( !options.toShow.size() ) {
                                options.toHide.animate({
                                        height: "hide",
                                        paddingTop: "hide",
                                        paddingBottom: "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 element
                        var 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 padding
                                        if ( 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,
                                                overflow: overflow
                                        });
                                        options.complete();
                                }
                        });
                },
                bounceslide: function( options ) {
                        this.slide( options, {
                                easing: options.down ? "easeOutBounce" : "swing",
                                duration: options.down ? 1000 : 200
                        });
                }
        }
});

})( jQuery );