Projet

Général

Profil

Paste
Télécharger (14,6 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / jquery_update / replace / ui / ui / jquery.ui.accordion.js @ 503b3f7b

1
/*!
2
 * jQuery UI Accordion 1.10.2
3
 * http://jqueryui.com
4
 *
5
 * Copyright 2013 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

    
17
var uid = 0,
18
        hideProps = {},
19
        showProps = {};
20

    
21
hideProps.height = hideProps.paddingTop = hideProps.paddingBottom =
22
        hideProps.borderTopWidth = hideProps.borderBottomWidth = "hide";
23
showProps.height = showProps.paddingTop = showProps.paddingBottom =
24
        showProps.borderTopWidth = showProps.borderBottomWidth = "show";
25

    
26
$.widget( "ui.accordion", {
27
        version: "1.10.2",
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-selected" )
106
                        .removeAttr( "aria-controls" )
107
                        .removeAttr( "tabIndex" )
108
                        .each(function() {
109
                                if ( /^ui-accordion/.test( this.id ) ) {
110
                                        this.removeAttribute( "id" );
111
                                }
112
                        });
113
                this._destroyIcons();
114

    
115
                // clean up content panels
116
                contents = this.headers.next()
117
                        .css( "display", "" )
118
                        .removeAttr( "role" )
119
                        .removeAttr( "aria-expanded" )
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
                /*jshint maxcomplexity:15*/
171
                if ( event.altKey || event.ctrlKey ) {
172
                        return;
173
                }
174

    
175
                var keyCode = $.ui.keyCode,
176
                        length = this.headers.length,
177
                        currentIndex = this.headers.index( event.target ),
178
                        toFocus = false;
179

    
180
                switch ( event.keyCode ) {
181
                        case keyCode.RIGHT:
182
                        case keyCode.DOWN:
183
                                toFocus = this.headers[ ( currentIndex + 1 ) % length ];
184
                                break;
185
                        case keyCode.LEFT:
186
                        case keyCode.UP:
187
                                toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
188
                                break;
189
                        case keyCode.SPACE:
190
                        case keyCode.ENTER:
191
                                this._eventHandler( event );
192
                                break;
193
                        case keyCode.HOME:
194
                                toFocus = this.headers[ 0 ];
195
                                break;
196
                        case keyCode.END:
197
                                toFocus = this.headers[ length - 1 ];
198
                                break;
199
                }
200

    
201
                if ( toFocus ) {
202
                        $( event.target ).attr( "tabIndex", -1 );
203
                        $( toFocus ).attr( "tabIndex", 0 );
204
                        toFocus.focus();
205
                        event.preventDefault();
206
                }
207
        },
208

    
209
        _panelKeyDown : function( event ) {
210
                if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
211
                        $( event.currentTarget ).prev().focus();
212
                }
213
        },
214

    
215
        refresh: function() {
216
                var options = this.options;
217
                this._processPanels();
218

    
219
                // was collapsed or no panel
220
                if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) {
221
                        options.active = false;
222
                        this.active = $();
223
                // active false only when collapsible is true
224
                } if ( options.active === false ) {
225
                        this._activate( 0 );
226
                // was active, but active panel is gone
227
                } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
228
                        // all remaining panel are disabled
229
                        if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) {
230
                                options.active = false;
231
                                this.active = $();
232
                        // activate previous panel
233
                        } else {
234
                                this._activate( Math.max( 0, options.active - 1 ) );
235
                        }
236
                // was active, active panel still exists
237
                } else {
238
                        // make sure active index is correct
239
                        options.active = this.headers.index( this.active );
240
                }
241

    
242
                this._destroyIcons();
243

    
244
                this._refresh();
245
        },
246

    
247
        _processPanels: function() {
248
                this.headers = this.element.find( this.options.header )
249
                        .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" );
250

    
251
                this.headers.next()
252
                        .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" )
253
                        .filter(":not(.ui-accordion-content-active)")
254
                        .hide();
255
        },
256

    
257
        _refresh: function() {
258
                var maxHeight,
259
                        options = this.options,
260
                        heightStyle = options.heightStyle,
261
                        parent = this.element.parent(),
262
                        accordionId = this.accordionId = "ui-accordion-" +
263
                                (this.element.attr( "id" ) || ++uid);
264

    
265
                this.active = this._findActive( options.active )
266
                        .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" )
267
                        .removeClass( "ui-corner-all" );
268
                this.active.next()
269
                        .addClass( "ui-accordion-content-active" )
270
                        .show();
271

    
272
                this.headers
273
                        .attr( "role", "tab" )
274
                        .each(function( i ) {
275
                                var header = $( this ),
276
                                        headerId = header.attr( "id" ),
277
                                        panel = header.next(),
278
                                        panelId = panel.attr( "id" );
279
                                if ( !headerId ) {
280
                                        headerId = accordionId + "-header-" + i;
281
                                        header.attr( "id", headerId );
282
                                }
283
                                if ( !panelId ) {
284
                                        panelId = accordionId + "-panel-" + i;
285
                                        panel.attr( "id", panelId );
286
                                }
287
                                header.attr( "aria-controls", panelId );
288
                                panel.attr( "aria-labelledby", headerId );
289
                        })
290
                        .next()
291
                                .attr( "role", "tabpanel" );
292

    
293
                this.headers
294
                        .not( this.active )
295
                        .attr({
296
                                "aria-selected": "false",
297
                                tabIndex: -1
298
                        })
299
                        .next()
300
                                .attr({
301
                                        "aria-expanded": "false",
302
                                        "aria-hidden": "true"
303
                                })
304
                                .hide();
305

    
306
                // make sure at least one header is in the tab order
307
                if ( !this.active.length ) {
308
                        this.headers.eq( 0 ).attr( "tabIndex", 0 );
309
                } else {
310
                        this.active.attr({
311
                                "aria-selected": "true",
312
                                tabIndex: 0
313
                        })
314
                        .next()
315
                                .attr({
316
                                        "aria-expanded": "true",
317
                                        "aria-hidden": "false"
318
                                });
319
                }
320

    
321
                this._createIcons();
322

    
323
                this._setupEvents( options.event );
324

    
325
                if ( heightStyle === "fill" ) {
326
                        maxHeight = parent.height();
327
                        this.element.siblings( ":visible" ).each(function() {
328
                                var elem = $( this ),
329
                                        position = elem.css( "position" );
330

    
331
                                if ( position === "absolute" || position === "fixed" ) {
332
                                        return;
333
                                }
334
                                maxHeight -= elem.outerHeight( true );
335
                        });
336

    
337
                        this.headers.each(function() {
338
                                maxHeight -= $( this ).outerHeight( true );
339
                        });
340

    
341
                        this.headers.next()
342
                                .each(function() {
343
                                        $( this ).height( Math.max( 0, maxHeight -
344
                                                $( this ).innerHeight() + $( this ).height() ) );
345
                                })
346
                                .css( "overflow", "auto" );
347
                } else if ( heightStyle === "auto" ) {
348
                        maxHeight = 0;
349
                        this.headers.next()
350
                                .each(function() {
351
                                        maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
352
                                })
353
                                .height( maxHeight );
354
                }
355
        },
356

    
357
        _activate: function( index ) {
358
                var active = this._findActive( index )[ 0 ];
359

    
360
                // trying to activate the already active panel
361
                if ( active === this.active[ 0 ] ) {
362
                        return;
363
                }
364

    
365
                // trying to collapse, simulate a click on the currently active header
366
                active = active || this.active[ 0 ];
367

    
368
                this._eventHandler({
369
                        target: active,
370
                        currentTarget: active,
371
                        preventDefault: $.noop
372
                });
373
        },
374

    
375
        _findActive: function( selector ) {
376
                return typeof selector === "number" ? this.headers.eq( selector ) : $();
377
        },
378

    
379
        _setupEvents: function( event ) {
380
                var events = {
381
                        keydown: "_keydown"
382
                };
383
                if ( event ) {
384
                        $.each( event.split(" "), function( index, eventName ) {
385
                                events[ eventName ] = "_eventHandler";
386
                        });
387
                }
388

    
389
                this._off( this.headers.add( this.headers.next() ) );
390
                this._on( this.headers, events );
391
                this._on( this.headers.next(), { keydown: "_panelKeyDown" });
392
                this._hoverable( this.headers );
393
                this._focusable( this.headers );
394
        },
395

    
396
        _eventHandler: function( event ) {
397
                var options = this.options,
398
                        active = this.active,
399
                        clicked = $( event.currentTarget ),
400
                        clickedIsActive = clicked[ 0 ] === active[ 0 ],
401
                        collapsing = clickedIsActive && options.collapsible,
402
                        toShow = collapsing ? $() : clicked.next(),
403
                        toHide = active.next(),
404
                        eventData = {
405
                                oldHeader: active,
406
                                oldPanel: toHide,
407
                                newHeader: collapsing ? $() : clicked,
408
                                newPanel: toShow
409
                        };
410

    
411
                event.preventDefault();
412

    
413
                if (
414
                                // click on active header, but not collapsible
415
                                ( clickedIsActive && !options.collapsible ) ||
416
                                // allow canceling activation
417
                                ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
418
                        return;
419
                }
420

    
421
                options.active = collapsing ? false : this.headers.index( clicked );
422

    
423
                // when the call to ._toggle() comes after the class changes
424
                // it causes a very odd bug in IE 8 (see #6720)
425
                this.active = clickedIsActive ? $() : clicked;
426
                this._toggle( eventData );
427

    
428
                // switch classes
429
                // corner classes on the previously active header stay after the animation
430
                active.removeClass( "ui-accordion-header-active ui-state-active" );
431
                if ( options.icons ) {
432
                        active.children( ".ui-accordion-header-icon" )
433
                                .removeClass( options.icons.activeHeader )
434
                                .addClass( options.icons.header );
435
                }
436

    
437
                if ( !clickedIsActive ) {
438
                        clicked
439
                                .removeClass( "ui-corner-all" )
440
                                .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" );
441
                        if ( options.icons ) {
442
                                clicked.children( ".ui-accordion-header-icon" )
443
                                        .removeClass( options.icons.header )
444
                                        .addClass( options.icons.activeHeader );
445
                        }
446

    
447
                        clicked
448
                                .next()
449
                                .addClass( "ui-accordion-content-active" );
450
                }
451
        },
452

    
453
        _toggle: function( data ) {
454
                var toShow = data.newPanel,
455
                        toHide = this.prevShow.length ? this.prevShow : data.oldPanel;
456

    
457
                // handle activating a panel during the animation for another activation
458
                this.prevShow.add( this.prevHide ).stop( true, true );
459
                this.prevShow = toShow;
460
                this.prevHide = toHide;
461

    
462
                if ( this.options.animate ) {
463
                        this._animate( toShow, toHide, data );
464
                } else {
465
                        toHide.hide();
466
                        toShow.show();
467
                        this._toggleComplete( data );
468
                }
469

    
470
                toHide.attr({
471
                        "aria-expanded": "false",
472
                        "aria-hidden": "true"
473
                });
474
                toHide.prev().attr( "aria-selected", "false" );
475
                // if we're switching panels, remove the old header from the tab order
476
                // if we're opening from collapsed state, remove the previous header from the tab order
477
                // if we're collapsing, then keep the collapsing header in the tab order
478
                if ( toShow.length && toHide.length ) {
479
                        toHide.prev().attr( "tabIndex", -1 );
480
                } else if ( toShow.length ) {
481
                        this.headers.filter(function() {
482
                                return $( this ).attr( "tabIndex" ) === 0;
483
                        })
484
                        .attr( "tabIndex", -1 );
485
                }
486

    
487
                toShow
488
                        .attr({
489
                                "aria-expanded": "true",
490
                                "aria-hidden": "false"
491
                        })
492
                        .prev()
493
                                .attr({
494
                                        "aria-selected": "true",
495
                                        tabIndex: 0
496
                                });
497
        },
498

    
499
        _animate: function( toShow, toHide, data ) {
500
                var total, easing, duration,
501
                        that = this,
502
                        adjust = 0,
503
                        down = toShow.length &&
504
                                ( !toHide.length || ( toShow.index() < toHide.index() ) ),
505
                        animate = this.options.animate || {},
506
                        options = down && animate.down || animate,
507
                        complete = function() {
508
                                that._toggleComplete( data );
509
                        };
510

    
511
                if ( typeof options === "number" ) {
512
                        duration = options;
513
                }
514
                if ( typeof options === "string" ) {
515
                        easing = options;
516
                }
517
                // fall back from options to animation in case of partial down settings
518
                easing = easing || options.easing || animate.easing;
519
                duration = duration || options.duration || animate.duration;
520

    
521
                if ( !toHide.length ) {
522
                        return toShow.animate( showProps, duration, easing, complete );
523
                }
524
                if ( !toShow.length ) {
525
                        return toHide.animate( hideProps, duration, easing, complete );
526
                }
527

    
528
                total = toShow.show().outerHeight();
529
                toHide.animate( hideProps, {
530
                        duration: duration,
531
                        easing: easing,
532
                        step: function( now, fx ) {
533
                                fx.now = Math.round( now );
534
                        }
535
                });
536
                toShow
537
                        .hide()
538
                        .animate( showProps, {
539
                                duration: duration,
540
                                easing: easing,
541
                                complete: complete,
542
                                step: function( now, fx ) {
543
                                        fx.now = Math.round( now );
544
                                        if ( fx.prop !== "height" ) {
545
                                                adjust += fx.now;
546
                                        } else if ( that.options.heightStyle !== "content" ) {
547
                                                fx.now = Math.round( total - toHide.outerHeight() - adjust );
548
                                                adjust = 0;
549
                                        }
550
                                }
551
                        });
552
        },
553

    
554
        _toggleComplete: function( data ) {
555
                var toHide = data.oldPanel;
556

    
557
                toHide
558
                        .removeClass( "ui-accordion-content-active" )
559
                        .prev()
560
                                .removeClass( "ui-corner-top" )
561
                                .addClass( "ui-corner-all" );
562

    
563
                // Work around for rendering bug in IE (#5421)
564
                if ( toHide.length ) {
565
                        toHide.parent()[0].className = toHide.parent()[0].className;
566
                }
567

    
568
                this._trigger( "activate", null, data );
569
        }
570
});
571

    
572
})( jQuery );