and a note on manual changes in dataTables.bootstrap.css
[myslice.git] / third-party / jquery-ui-1.10.2 / ui / jquery.ui.accordion.js
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 );