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