[SPIP] +2.1.12
[velocampus/web/www.git] / www / plugins / auto / jquery_ui / javascript / jquery-ui-1.8.16 / ui / jquery.ui.accordion.js
1 /*
2 * jQuery UI Accordion 1.8.16
3 *
4 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
5 * Dual licensed under the MIT or GPL Version 2 licenses.
6 * http://jquery.org/license
7 *
8 * http://docs.jquery.com/UI/Accordion
9 *
10 * Depends:
11 * jquery.ui.core.js
12 * jquery.ui.widget.js
13 */
14 (function( $, undefined ) {
15
16 $.widget( "ui.accordion", {
17 options: {
18 active: 0,
19 animated: "slide",
20 autoHeight: true,
21 clearStyle: false,
22 collapsible: false,
23 event: "click",
24 fillSpace: false,
25 header: "> li > :first-child,> :not(li):even",
26 icons: {
27 header: "ui-icon-triangle-1-e",
28 headerSelected: "ui-icon-triangle-1-s"
29 },
30 navigation: false,
31 navigationFilter: function() {
32 return this.href.toLowerCase() === location.href.toLowerCase();
33 }
34 },
35
36 _create: function() {
37 var self = this,
38 options = self.options;
39
40 self.running = 0;
41
42 self.element
43 .addClass( "ui-accordion ui-widget ui-helper-reset" )
44 // in lack of child-selectors in CSS
45 // we need to mark top-LIs in a UL-accordion for some IE-fix
46 .children( "li" )
47 .addClass( "ui-accordion-li-fix" );
48
49 self.headers = self.element.find( options.header )
50 .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" )
51 .bind( "mouseenter.accordion", function() {
52 if ( options.disabled ) {
53 return;
54 }
55 $( this ).addClass( "ui-state-hover" );
56 })
57 .bind( "mouseleave.accordion", function() {
58 if ( options.disabled ) {
59 return;
60 }
61 $( this ).removeClass( "ui-state-hover" );
62 })
63 .bind( "focus.accordion", function() {
64 if ( options.disabled ) {
65 return;
66 }
67 $( this ).addClass( "ui-state-focus" );
68 })
69 .bind( "blur.accordion", function() {
70 if ( options.disabled ) {
71 return;
72 }
73 $( this ).removeClass( "ui-state-focus" );
74 });
75
76 self.headers.next()
77 .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" );
78
79 if ( options.navigation ) {
80 var current = self.element.find( "a" ).filter( options.navigationFilter ).eq( 0 );
81 if ( current.length ) {
82 var header = current.closest( ".ui-accordion-header" );
83 if ( header.length ) {
84 // anchor within header
85 self.active = header;
86 } else {
87 // anchor within content
88 self.active = current.closest( ".ui-accordion-content" ).prev();
89 }
90 }
91 }
92
93 self.active = self._findActive( self.active || options.active )
94 .addClass( "ui-state-default ui-state-active" )
95 .toggleClass( "ui-corner-all" )
96 .toggleClass( "ui-corner-top" );
97 self.active.next().addClass( "ui-accordion-content-active" );
98
99 self._createIcons();
100 self.resize();
101
102 // ARIA
103 self.element.attr( "role", "tablist" );
104
105 self.headers
106 .attr( "role", "tab" )
107 .bind( "keydown.accordion", function( event ) {
108 return self._keydown( event );
109 })
110 .next()
111 .attr( "role", "tabpanel" );
112
113 self.headers
114 .not( self.active || "" )
115 .attr({
116 "aria-expanded": "false",
117 "aria-selected": "false",
118 tabIndex: -1
119 })
120 .next()
121 .hide();
122
123 // make sure at least one header is in the tab order
124 if ( !self.active.length ) {
125 self.headers.eq( 0 ).attr( "tabIndex", 0 );
126 } else {
127 self.active
128 .attr({
129 "aria-expanded": "true",
130 "aria-selected": "true",
131 tabIndex: 0
132 });
133 }
134
135 // only need links in tab order for Safari
136 if ( !$.browser.safari ) {
137 self.headers.find( "a" ).attr( "tabIndex", -1 );
138 }
139
140 if ( options.event ) {
141 self.headers.bind( options.event.split(" ").join(".accordion ") + ".accordion", function(event) {
142 self._clickHandler.call( self, event, this );
143 event.preventDefault();
144 });
145 }
146 },
147
148 _createIcons: function() {
149 var options = this.options;
150 if ( options.icons ) {
151 $( "<span></span>" )
152 .addClass( "ui-icon " + options.icons.header )
153 .prependTo( this.headers );
154 this.active.children( ".ui-icon" )
155 .toggleClass(options.icons.header)
156 .toggleClass(options.icons.headerSelected);
157 this.element.addClass( "ui-accordion-icons" );
158 }
159 },
160
161 _destroyIcons: function() {
162 this.headers.children( ".ui-icon" ).remove();
163 this.element.removeClass( "ui-accordion-icons" );
164 },
165
166 destroy: function() {
167 var options = this.options;
168
169 this.element
170 .removeClass( "ui-accordion ui-widget ui-helper-reset" )
171 .removeAttr( "role" );
172
173 this.headers
174 .unbind( ".accordion" )
175 .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" )
176 .removeAttr( "role" )
177 .removeAttr( "aria-expanded" )
178 .removeAttr( "aria-selected" )
179 .removeAttr( "tabIndex" );
180
181 this.headers.find( "a" ).removeAttr( "tabIndex" );
182 this._destroyIcons();
183 var contents = this.headers.next()
184 .css( "display", "" )
185 .removeAttr( "role" )
186 .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled" );
187 if ( options.autoHeight || options.fillHeight ) {
188 contents.css( "height", "" );
189 }
190
191 return $.Widget.prototype.destroy.call( this );
192 },
193
194 _setOption: function( key, value ) {
195 $.Widget.prototype._setOption.apply( this, arguments );
196
197 if ( key == "active" ) {
198 this.activate( value );
199 }
200 if ( key == "icons" ) {
201 this._destroyIcons();
202 if ( value ) {
203 this._createIcons();
204 }
205 }
206 // #5332 - opacity doesn't cascade to positioned elements in IE
207 // so we need to add the disabled class to the headers and panels
208 if ( key == "disabled" ) {
209 this.headers.add(this.headers.next())
210 [ value ? "addClass" : "removeClass" ](
211 "ui-accordion-disabled ui-state-disabled" );
212 }
213 },
214
215 _keydown: function( event ) {
216 if ( this.options.disabled || event.altKey || event.ctrlKey ) {
217 return;
218 }
219
220 var keyCode = $.ui.keyCode,
221 length = this.headers.length,
222 currentIndex = this.headers.index( event.target ),
223 toFocus = false;
224
225 switch ( event.keyCode ) {
226 case keyCode.RIGHT:
227 case keyCode.DOWN:
228 toFocus = this.headers[ ( currentIndex + 1 ) % length ];
229 break;
230 case keyCode.LEFT:
231 case keyCode.UP:
232 toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
233 break;
234 case keyCode.SPACE:
235 case keyCode.ENTER:
236 this._clickHandler( { target: event.target }, event.target );
237 event.preventDefault();
238 }
239
240 if ( toFocus ) {
241 $( event.target ).attr( "tabIndex", -1 );
242 $( toFocus ).attr( "tabIndex", 0 );
243 toFocus.focus();
244 return false;
245 }
246
247 return true;
248 },
249
250 resize: function() {
251 var options = this.options,
252 maxHeight;
253
254 if ( options.fillSpace ) {
255 if ( $.browser.msie ) {
256 var defOverflow = this.element.parent().css( "overflow" );
257 this.element.parent().css( "overflow", "hidden");
258 }
259 maxHeight = this.element.parent().height();
260 if ($.browser.msie) {
261 this.element.parent().css( "overflow", defOverflow );
262 }
263
264 this.headers.each(function() {
265 maxHeight -= $( this ).outerHeight( true );
266 });
267
268 this.headers.next()
269 .each(function() {
270 $( this ).height( Math.max( 0, maxHeight -
271 $( this ).innerHeight() + $( this ).height() ) );
272 })
273 .css( "overflow", "auto" );
274 } else if ( options.autoHeight ) {
275 maxHeight = 0;
276 this.headers.next()
277 .each(function() {
278 maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
279 })
280 .height( maxHeight );
281 }
282
283 return this;
284 },
285
286 activate: function( index ) {
287 // TODO this gets called on init, changing the option without an explicit call for that
288 this.options.active = index;
289 // call clickHandler with custom event
290 var active = this._findActive( index )[ 0 ];
291 this._clickHandler( { target: active }, active );
292
293 return this;
294 },
295
296 _findActive: function( selector ) {
297 return selector
298 ? typeof selector === "number"
299 ? this.headers.filter( ":eq(" + selector + ")" )
300 : this.headers.not( this.headers.not( selector ) )
301 : selector === false
302 ? $( [] )
303 : this.headers.filter( ":eq(0)" );
304 },
305
306 // TODO isn't event.target enough? why the separate target argument?
307 _clickHandler: function( event, target ) {
308 var options = this.options;
309 if ( options.disabled ) {
310 return;
311 }
312
313 // called only when using activate(false) to close all parts programmatically
314 if ( !event.target ) {
315 if ( !options.collapsible ) {
316 return;
317 }
318 this.active
319 .removeClass( "ui-state-active ui-corner-top" )
320 .addClass( "ui-state-default ui-corner-all" )
321 .children( ".ui-icon" )
322 .removeClass( options.icons.headerSelected )
323 .addClass( options.icons.header );
324 this.active.next().addClass( "ui-accordion-content-active" );
325 var toHide = this.active.next(),
326 data = {
327 options: options,
328 newHeader: $( [] ),
329 oldHeader: options.active,
330 newContent: $( [] ),
331 oldContent: toHide
332 },
333 toShow = ( this.active = $( [] ) );
334 this._toggle( toShow, toHide, data );
335 return;
336 }
337
338 // get the click target
339 var clicked = $( event.currentTarget || target ),
340 clickedIsActive = clicked[0] === this.active[0];
341
342 // TODO the option is changed, is that correct?
343 // TODO if it is correct, shouldn't that happen after determining that the click is valid?
344 options.active = options.collapsible && clickedIsActive ?
345 false :
346 this.headers.index( clicked );
347
348 // if animations are still active, or the active header is the target, ignore click
349 if ( this.running || ( !options.collapsible && clickedIsActive ) ) {
350 return;
351 }
352
353 // find elements to show and hide
354 var active = this.active,
355 toShow = clicked.next(),
356 toHide = this.active.next(),
357 data = {
358 options: options,
359 newHeader: clickedIsActive && options.collapsible ? $([]) : clicked,
360 oldHeader: this.active,
361 newContent: clickedIsActive && options.collapsible ? $([]) : toShow,
362 oldContent: toHide
363 },
364 down = this.headers.index( this.active[0] ) > this.headers.index( clicked[0] );
365
366 // when the call to ._toggle() comes after the class changes
367 // it causes a very odd bug in IE 8 (see #6720)
368 this.active = clickedIsActive ? $([]) : clicked;
369 this._toggle( toShow, toHide, data, clickedIsActive, down );
370
371 // switch classes
372 active
373 .removeClass( "ui-state-active ui-corner-top" )
374 .addClass( "ui-state-default ui-corner-all" )
375 .children( ".ui-icon" )
376 .removeClass( options.icons.headerSelected )
377 .addClass( options.icons.header );
378 if ( !clickedIsActive ) {
379 clicked
380 .removeClass( "ui-state-default ui-corner-all" )
381 .addClass( "ui-state-active ui-corner-top" )
382 .children( ".ui-icon" )
383 .removeClass( options.icons.header )
384 .addClass( options.icons.headerSelected );
385 clicked
386 .next()
387 .addClass( "ui-accordion-content-active" );
388 }
389
390 return;
391 },
392
393 _toggle: function( toShow, toHide, data, clickedIsActive, down ) {
394 var self = this,
395 options = self.options;
396
397 self.toShow = toShow;
398 self.toHide = toHide;
399 self.data = data;
400
401 var complete = function() {
402 if ( !self ) {
403 return;
404 }
405 return self._completed.apply( self, arguments );
406 };
407
408 // trigger changestart event
409 self._trigger( "changestart", null, self.data );
410
411 // count elements to animate
412 self.running = toHide.size() === 0 ? toShow.size() : toHide.size();
413
414 if ( options.animated ) {
415 var animOptions = {};
416
417 if ( options.collapsible && clickedIsActive ) {
418 animOptions = {
419 toShow: $( [] ),
420 toHide: toHide,
421 complete: complete,
422 down: down,
423 autoHeight: options.autoHeight || options.fillSpace
424 };
425 } else {
426 animOptions = {
427 toShow: toShow,
428 toHide: toHide,
429 complete: complete,
430 down: down,
431 autoHeight: options.autoHeight || options.fillSpace
432 };
433 }
434
435 if ( !options.proxied ) {
436 options.proxied = options.animated;
437 }
438
439 if ( !options.proxiedDuration ) {
440 options.proxiedDuration = options.duration;
441 }
442
443 options.animated = $.isFunction( options.proxied ) ?
444 options.proxied( animOptions ) :
445 options.proxied;
446
447 options.duration = $.isFunction( options.proxiedDuration ) ?
448 options.proxiedDuration( animOptions ) :
449 options.proxiedDuration;
450
451 var animations = $.ui.accordion.animations,
452 duration = options.duration,
453 easing = options.animated;
454
455 if ( easing && !animations[ easing ] && !$.easing[ easing ] ) {
456 easing = "slide";
457 }
458 if ( !animations[ easing ] ) {
459 animations[ easing ] = function( options ) {
460 this.slide( options, {
461 easing: easing,
462 duration: duration || 700
463 });
464 };
465 }
466
467 animations[ easing ]( animOptions );
468 } else {
469 if ( options.collapsible && clickedIsActive ) {
470 toShow.toggle();
471 } else {
472 toHide.hide();
473 toShow.show();
474 }
475
476 complete( true );
477 }
478
479 // TODO assert that the blur and focus triggers are really necessary, remove otherwise
480 toHide.prev()
481 .attr({
482 "aria-expanded": "false",
483 "aria-selected": "false",
484 tabIndex: -1
485 })
486 .blur();
487 toShow.prev()
488 .attr({
489 "aria-expanded": "true",
490 "aria-selected": "true",
491 tabIndex: 0
492 })
493 .focus();
494 },
495
496 _completed: function( cancel ) {
497 this.running = cancel ? 0 : --this.running;
498 if ( this.running ) {
499 return;
500 }
501
502 if ( this.options.clearStyle ) {
503 this.toShow.add( this.toHide ).css({
504 height: "",
505 overflow: ""
506 });
507 }
508
509 // other classes are removed before the animation; this one needs to stay until completed
510 this.toHide.removeClass( "ui-accordion-content-active" );
511 // Work around for rendering bug in IE (#5421)
512 if ( this.toHide.length ) {
513 this.toHide.parent()[0].className = this.toHide.parent()[0].className;
514 }
515
516 this._trigger( "change", null, this.data );
517 }
518 });
519
520 $.extend( $.ui.accordion, {
521 version: "1.8.16",
522 animations: {
523 slide: function( options, additions ) {
524 options = $.extend({
525 easing: "swing",
526 duration: 300
527 }, options, additions );
528 if ( !options.toHide.size() ) {
529 options.toShow.animate({
530 height: "show",
531 paddingTop: "show",
532 paddingBottom: "show"
533 }, options );
534 return;
535 }
536 if ( !options.toShow.size() ) {
537 options.toHide.animate({
538 height: "hide",
539 paddingTop: "hide",
540 paddingBottom: "hide"
541 }, options );
542 return;
543 }
544 var overflow = options.toShow.css( "overflow" ),
545 percentDone = 0,
546 showProps = {},
547 hideProps = {},
548 fxAttrs = [ "height", "paddingTop", "paddingBottom" ],
549 originalWidth;
550 // fix width before calculating height of hidden element
551 var s = options.toShow;
552 originalWidth = s[0].style.width;
553 s.width( parseInt( s.parent().width(), 10 )
554 - parseInt( s.css( "paddingLeft" ), 10 )
555 - parseInt( s.css( "paddingRight" ), 10 )
556 - ( parseInt( s.css( "borderLeftWidth" ), 10 ) || 0 )
557 - ( parseInt( s.css( "borderRightWidth" ), 10) || 0 ) );
558
559 $.each( fxAttrs, function( i, prop ) {
560 hideProps[ prop ] = "hide";
561
562 var parts = ( "" + $.css( options.toShow[0], prop ) ).match( /^([\d+-.]+)(.*)$/ );
563 showProps[ prop ] = {
564 value: parts[ 1 ],
565 unit: parts[ 2 ] || "px"
566 };
567 });
568 options.toShow.css({ height: 0, overflow: "hidden" }).show();
569 options.toHide
570 .filter( ":hidden" )
571 .each( options.complete )
572 .end()
573 .filter( ":visible" )
574 .animate( hideProps, {
575 step: function( now, settings ) {
576 // only calculate the percent when animating height
577 // IE gets very inconsistent results when animating elements
578 // with small values, which is common for padding
579 if ( settings.prop == "height" ) {
580 percentDone = ( settings.end - settings.start === 0 ) ? 0 :
581 ( settings.now - settings.start ) / ( settings.end - settings.start );
582 }
583
584 options.toShow[ 0 ].style[ settings.prop ] =
585 ( percentDone * showProps[ settings.prop ].value )
586 + showProps[ settings.prop ].unit;
587 },
588 duration: options.duration,
589 easing: options.easing,
590 complete: function() {
591 if ( !options.autoHeight ) {
592 options.toShow.css( "height", "" );
593 }
594 options.toShow.css({
595 width: originalWidth,
596 overflow: overflow
597 });
598 options.complete();
599 }
600 });
601 },
602 bounceslide: function( options ) {
603 this.slide( options, {
604 easing: options.down ? "easeOutBounce" : "swing",
605 duration: options.down ? 1000 : 200
606 });
607 }
608 }
609 });
610
611 })( jQuery );