source: trunk/themes/default/js/ui/jquery.ui.accordion.js @ 28780

Last change on this file since 28780 was 28780, checked in by rvelices, 10 years ago

upgrade jquery ui from 1.10.1 to 1.10.4

File size: 14.6 KB
Line 
1/*!
2 * jQuery UI Accordion 1.10.4
3 * http://jqueryui.com
4 *
5 * Copyright 2014 jQuery Foundation and other contributors
6 * Released under the MIT license.
7 * http://jquery.org/license
8 *
9 * http://api.jqueryui.com/accordion/
10 *
11 * Depends:
12 *      jquery.ui.core.js
13 *      jquery.ui.widget.js
14 */
15(function( $, undefined ) {
16
17var uid = 0,
18        hideProps = {},
19        showProps = {};
20
21hideProps.height = hideProps.paddingTop = hideProps.paddingBottom =
22        hideProps.borderTopWidth = hideProps.borderBottomWidth = "hide";
23showProps.height = showProps.paddingTop = showProps.paddingBottom =
24        showProps.borderTopWidth = showProps.borderBottomWidth = "show";
25
26$.widget( "ui.accordion", {
27        version: "1.10.4",
28        options: {
29                active: 0,
30                animate: {},
31                collapsible: false,
32                event: "click",
33                header: "> li > :first-child,> :not(li):even",
34                heightStyle: "auto",
35                icons: {
36                        activeHeader: "ui-icon-triangle-1-s",
37                        header: "ui-icon-triangle-1-e"
38                },
39
40                // callbacks
41                activate: null,
42                beforeActivate: null
43        },
44
45        _create: function() {
46                var options = this.options;
47                this.prevShow = this.prevHide = $();
48                this.element.addClass( "ui-accordion ui-widget ui-helper-reset" )
49                        // ARIA
50                        .attr( "role", "tablist" );
51
52                // don't allow collapsible: false and active: false / null
53                if ( !options.collapsible && (options.active === false || options.active == null) ) {
54                        options.active = 0;
55                }
56
57                this._processPanels();
58                // handle negative values
59                if ( options.active < 0 ) {
60                        options.active += this.headers.length;
61                }
62                this._refresh();
63        },
64
65        _getCreateEventData: function() {
66                return {
67                        header: this.active,
68                        panel: !this.active.length ? $() : this.active.next(),
69                        content: !this.active.length ? $() : this.active.next()
70                };
71        },
72
73        _createIcons: function() {
74                var icons = this.options.icons;
75                if ( icons ) {
76                        $( "<span>" )
77                                .addClass( "ui-accordion-header-icon ui-icon " + icons.header )
78                                .prependTo( this.headers );
79                        this.active.children( ".ui-accordion-header-icon" )
80                                .removeClass( icons.header )
81                                .addClass( icons.activeHeader );
82                        this.headers.addClass( "ui-accordion-icons" );
83                }
84        },
85
86        _destroyIcons: function() {
87                this.headers
88                        .removeClass( "ui-accordion-icons" )
89                        .children( ".ui-accordion-header-icon" )
90                                .remove();
91        },
92
93        _destroy: function() {
94                var contents;
95
96                // clean up main element
97                this.element
98                        .removeClass( "ui-accordion ui-widget ui-helper-reset" )
99                        .removeAttr( "role" );
100
101                // clean up headers
102                this.headers
103                        .removeClass( "ui-accordion-header ui-accordion-header-active ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top" )
104                        .removeAttr( "role" )
105                        .removeAttr( "aria-expanded" )
106                        .removeAttr( "aria-selected" )
107                        .removeAttr( "aria-controls" )
108                        .removeAttr( "tabIndex" )
109                        .each(function() {
110                                if ( /^ui-accordion/.test( this.id ) ) {
111                                        this.removeAttribute( "id" );
112                                }
113                        });
114                this._destroyIcons();
115
116                // clean up content panels
117                contents = this.headers.next()
118                        .css( "display", "" )
119                        .removeAttr( "role" )
120                        .removeAttr( "aria-hidden" )
121                        .removeAttr( "aria-labelledby" )
122                        .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled" )
123                        .each(function() {
124                                if ( /^ui-accordion/.test( this.id ) ) {
125                                        this.removeAttribute( "id" );
126                                }
127                        });
128                if ( this.options.heightStyle !== "content" ) {
129                        contents.css( "height", "" );
130                }
131        },
132
133        _setOption: function( key, value ) {
134                if ( key === "active" ) {
135                        // _activate() will handle invalid values and update this.options
136                        this._activate( value );
137                        return;
138                }
139
140                if ( key === "event" ) {
141                        if ( this.options.event ) {
142                                this._off( this.headers, this.options.event );
143                        }
144                        this._setupEvents( value );
145                }
146
147                this._super( key, value );
148
149                // setting collapsible: false while collapsed; open first panel
150                if ( key === "collapsible" && !value && this.options.active === false ) {
151                        this._activate( 0 );
152                }
153
154                if ( key === "icons" ) {
155                        this._destroyIcons();
156                        if ( value ) {
157                                this._createIcons();
158                        }
159                }
160
161                // #5332 - opacity doesn't cascade to positioned elements in IE
162                // so we need to add the disabled class to the headers and panels
163                if ( key === "disabled" ) {
164                        this.headers.add( this.headers.next() )
165                                .toggleClass( "ui-state-disabled", !!value );
166                }
167        },
168
169        _keydown: function( event ) {
170                if ( event.altKey || event.ctrlKey ) {
171                        return;
172                }
173
174                var keyCode = $.ui.keyCode,
175                        length = this.headers.length,
176                        currentIndex = this.headers.index( event.target ),
177                        toFocus = false;
178
179                switch ( event.keyCode ) {
180                        case keyCode.RIGHT:
181                        case keyCode.DOWN:
182                                toFocus = this.headers[ ( currentIndex + 1 ) % length ];
183                                break;
184                        case keyCode.LEFT:
185                        case keyCode.UP:
186                                toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
187                                break;
188                        case keyCode.SPACE:
189                        case keyCode.ENTER:
190                                this._eventHandler( event );
191                                break;
192                        case keyCode.HOME:
193                                toFocus = this.headers[ 0 ];
194                                break;
195                        case keyCode.END:
196                                toFocus = this.headers[ length - 1 ];
197                                break;
198                }
199
200                if ( toFocus ) {
201                        $( event.target ).attr( "tabIndex", -1 );
202                        $( toFocus ).attr( "tabIndex", 0 );
203                        toFocus.focus();
204                        event.preventDefault();
205                }
206        },
207
208        _panelKeyDown : function( event ) {
209                if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
210                        $( event.currentTarget ).prev().focus();
211                }
212        },
213
214        refresh: function() {
215                var options = this.options;
216                this._processPanels();
217
218                // was collapsed or no panel
219                if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) {
220                        options.active = false;
221                        this.active = $();
222                // active false only when collapsible is true
223                } else if ( options.active === false ) {
224                        this._activate( 0 );
225                // was active, but active panel is gone
226                } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
227                        // all remaining panel are disabled
228                        if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) {
229                                options.active = false;
230                                this.active = $();
231                        // activate previous panel
232                        } else {
233                                this._activate( Math.max( 0, options.active - 1 ) );
234                        }
235                // was active, active panel still exists
236                } else {
237                        // make sure active index is correct
238                        options.active = this.headers.index( this.active );
239                }
240
241                this._destroyIcons();
242
243                this._refresh();
244        },
245
246        _processPanels: function() {
247                this.headers = this.element.find( this.options.header )
248                        .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" );
249
250                this.headers.next()
251                        .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" )
252                        .filter(":not(.ui-accordion-content-active)")
253                        .hide();
254        },
255
256        _refresh: function() {
257                var maxHeight,
258                        options = this.options,
259                        heightStyle = options.heightStyle,
260                        parent = this.element.parent(),
261                        accordionId = this.accordionId = "ui-accordion-" +
262                                (this.element.attr( "id" ) || ++uid);
263
264                this.active = this._findActive( options.active )
265                        .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" )
266                        .removeClass( "ui-corner-all" );
267                this.active.next()
268                        .addClass( "ui-accordion-content-active" )
269                        .show();
270
271                this.headers
272                        .attr( "role", "tab" )
273                        .each(function( i ) {
274                                var header = $( this ),
275                                        headerId = header.attr( "id" ),
276                                        panel = header.next(),
277                                        panelId = panel.attr( "id" );
278                                if ( !headerId ) {
279                                        headerId = accordionId + "-header-" + i;
280                                        header.attr( "id", headerId );
281                                }
282                                if ( !panelId ) {
283                                        panelId = accordionId + "-panel-" + i;
284                                        panel.attr( "id", panelId );
285                                }
286                                header.attr( "aria-controls", panelId );
287                                panel.attr( "aria-labelledby", headerId );
288                        })
289                        .next()
290                                .attr( "role", "tabpanel" );
291
292                this.headers
293                        .not( this.active )
294                        .attr({
295                                "aria-selected": "false",
296                                "aria-expanded": "false",
297                                tabIndex: -1
298                        })
299                        .next()
300                                .attr({
301                                        "aria-hidden": "true"
302                                })
303                                .hide();
304
305                // make sure at least one header is in the tab order
306                if ( !this.active.length ) {
307                        this.headers.eq( 0 ).attr( "tabIndex", 0 );
308                } else {
309                        this.active.attr({
310                                "aria-selected": "true",
311                                "aria-expanded": "true",
312                                tabIndex: 0
313                        })
314                        .next()
315                                .attr({
316                                        "aria-hidden": "false"
317                                });
318                }
319
320                this._createIcons();
321
322                this._setupEvents( options.event );
323
324                if ( heightStyle === "fill" ) {
325                        maxHeight = parent.height();
326                        this.element.siblings( ":visible" ).each(function() {
327                                var elem = $( this ),
328                                        position = elem.css( "position" );
329
330                                if ( position === "absolute" || position === "fixed" ) {
331                                        return;
332                                }
333                                maxHeight -= elem.outerHeight( true );
334                        });
335
336                        this.headers.each(function() {
337                                maxHeight -= $( this ).outerHeight( true );
338                        });
339
340                        this.headers.next()
341                                .each(function() {
342                                        $( this ).height( Math.max( 0, maxHeight -
343                                                $( this ).innerHeight() + $( this ).height() ) );
344                                })
345                                .css( "overflow", "auto" );
346                } else if ( heightStyle === "auto" ) {
347                        maxHeight = 0;
348                        this.headers.next()
349                                .each(function() {
350                                        maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
351                                })
352                                .height( maxHeight );
353                }
354        },
355
356        _activate: function( index ) {
357                var active = this._findActive( index )[ 0 ];
358
359                // trying to activate the already active panel
360                if ( active === this.active[ 0 ] ) {
361                        return;
362                }
363
364                // trying to collapse, simulate a click on the currently active header
365                active = active || this.active[ 0 ];
366
367                this._eventHandler({
368                        target: active,
369                        currentTarget: active,
370                        preventDefault: $.noop
371                });
372        },
373
374        _findActive: function( selector ) {
375                return typeof selector === "number" ? this.headers.eq( selector ) : $();
376        },
377
378        _setupEvents: function( event ) {
379                var events = {
380                        keydown: "_keydown"
381                };
382                if ( event ) {
383                        $.each( event.split(" "), function( index, eventName ) {
384                                events[ eventName ] = "_eventHandler";
385                        });
386                }
387
388                this._off( this.headers.add( this.headers.next() ) );
389                this._on( this.headers, events );
390                this._on( this.headers.next(), { keydown: "_panelKeyDown" });
391                this._hoverable( this.headers );
392                this._focusable( this.headers );
393        },
394
395        _eventHandler: function( event ) {
396                var options = this.options,
397                        active = this.active,
398                        clicked = $( event.currentTarget ),
399                        clickedIsActive = clicked[ 0 ] === active[ 0 ],
400                        collapsing = clickedIsActive && options.collapsible,
401                        toShow = collapsing ? $() : clicked.next(),
402                        toHide = active.next(),
403                        eventData = {
404                                oldHeader: active,
405                                oldPanel: toHide,
406                                newHeader: collapsing ? $() : clicked,
407                                newPanel: toShow
408                        };
409
410                event.preventDefault();
411
412                if (
413                                // click on active header, but not collapsible
414                                ( clickedIsActive && !options.collapsible ) ||
415                                // allow canceling activation
416                                ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
417                        return;
418                }
419
420                options.active = collapsing ? false : this.headers.index( clicked );
421
422                // when the call to ._toggle() comes after the class changes
423                // it causes a very odd bug in IE 8 (see #6720)
424                this.active = clickedIsActive ? $() : clicked;
425                this._toggle( eventData );
426
427                // switch classes
428                // corner classes on the previously active header stay after the animation
429                active.removeClass( "ui-accordion-header-active ui-state-active" );
430                if ( options.icons ) {
431                        active.children( ".ui-accordion-header-icon" )
432                                .removeClass( options.icons.activeHeader )
433                                .addClass( options.icons.header );
434                }
435
436                if ( !clickedIsActive ) {
437                        clicked
438                                .removeClass( "ui-corner-all" )
439                                .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" );
440                        if ( options.icons ) {
441                                clicked.children( ".ui-accordion-header-icon" )
442                                        .removeClass( options.icons.header )
443                                        .addClass( options.icons.activeHeader );
444                        }
445
446                        clicked
447                                .next()
448                                .addClass( "ui-accordion-content-active" );
449                }
450        },
451
452        _toggle: function( data ) {
453                var toShow = data.newPanel,
454                        toHide = this.prevShow.length ? this.prevShow : data.oldPanel;
455
456                // handle activating a panel during the animation for another activation
457                this.prevShow.add( this.prevHide ).stop( true, true );
458                this.prevShow = toShow;
459                this.prevHide = toHide;
460
461                if ( this.options.animate ) {
462                        this._animate( toShow, toHide, data );
463                } else {
464                        toHide.hide();
465                        toShow.show();
466                        this._toggleComplete( data );
467                }
468
469                toHide.attr({
470                        "aria-hidden": "true"
471                });
472                toHide.prev().attr( "aria-selected", "false" );
473                // if we're switching panels, remove the old header from the tab order
474                // if we're opening from collapsed state, remove the previous header from the tab order
475                // if we're collapsing, then keep the collapsing header in the tab order
476                if ( toShow.length && toHide.length ) {
477                        toHide.prev().attr({
478                                "tabIndex": -1,
479                                "aria-expanded": "false"
480                        });
481                } else if ( toShow.length ) {
482                        this.headers.filter(function() {
483                                return $( this ).attr( "tabIndex" ) === 0;
484                        })
485                        .attr( "tabIndex", -1 );
486                }
487
488                toShow
489                        .attr( "aria-hidden", "false" )
490                        .prev()
491                                .attr({
492                                        "aria-selected": "true",
493                                        tabIndex: 0,
494                                        "aria-expanded": "true"
495                                });
496        },
497
498        _animate: function( toShow, toHide, data ) {
499                var total, easing, duration,
500                        that = this,
501                        adjust = 0,
502                        down = toShow.length &&
503                                ( !toHide.length || ( toShow.index() < toHide.index() ) ),
504                        animate = this.options.animate || {},
505                        options = down && animate.down || animate,
506                        complete = function() {
507                                that._toggleComplete( data );
508                        };
509
510                if ( typeof options === "number" ) {
511                        duration = options;
512                }
513                if ( typeof options === "string" ) {
514                        easing = options;
515                }
516                // fall back from options to animation in case of partial down settings
517                easing = easing || options.easing || animate.easing;
518                duration = duration || options.duration || animate.duration;
519
520                if ( !toHide.length ) {
521                        return toShow.animate( showProps, duration, easing, complete );
522                }
523                if ( !toShow.length ) {
524                        return toHide.animate( hideProps, duration, easing, complete );
525                }
526
527                total = toShow.show().outerHeight();
528                toHide.animate( hideProps, {
529                        duration: duration,
530                        easing: easing,
531                        step: function( now, fx ) {
532                                fx.now = Math.round( now );
533                        }
534                });
535                toShow
536                        .hide()
537                        .animate( showProps, {
538                                duration: duration,
539                                easing: easing,
540                                complete: complete,
541                                step: function( now, fx ) {
542                                        fx.now = Math.round( now );
543                                        if ( fx.prop !== "height" ) {
544                                                adjust += fx.now;
545                                        } else if ( that.options.heightStyle !== "content" ) {
546                                                fx.now = Math.round( total - toHide.outerHeight() - adjust );
547                                                adjust = 0;
548                                        }
549                                }
550                        });
551        },
552
553        _toggleComplete: function( data ) {
554                var toHide = data.oldPanel;
555
556                toHide
557                        .removeClass( "ui-accordion-content-active" )
558                        .prev()
559                                .removeClass( "ui-corner-top" )
560                                .addClass( "ui-corner-all" );
561
562                // Work around for rendering bug in IE (#5421)
563                if ( toHide.length ) {
564                        toHide.parent()[0].className = toHide.parent()[0].className;
565                }
566                this._trigger( "activate", null, data );
567        }
568});
569
570})( jQuery );
Note: See TracBrowser for help on using the repository browser.