UPDATE 4.4.0.0
[phpmyadmin.git] / js / jquery / src / jquery-ui / tabs.js
blobab6305bf38f6d6a1e4e2747cb12a19fd05f163f1
1 /*!
2  * jQuery UI Tabs 1.11.2
3  * http://jqueryui.com
4  *
5  * Copyright 2014 jQuery Foundation and other contributors
6  * Released under the MIT license.
7  * http://jquery.org/license
8  *
9  * http://api.jqueryui.com/tabs/
10  */
11 (function( factory ) {
12         if ( typeof define === "function" && define.amd ) {
14                 // AMD. Register as an anonymous module.
15                 define([
16                         "jquery",
17                         "./core",
18                         "./widget"
19                 ], factory );
20         } else {
22                 // Browser globals
23                 factory( jQuery );
24         }
25 }(function( $ ) {
27 return $.widget( "ui.tabs", {
28         version: "1.11.2",
29         delay: 300,
30         options: {
31                 active: null,
32                 collapsible: false,
33                 event: "click",
34                 heightStyle: "content",
35                 hide: null,
36                 show: null,
38                 // callbacks
39                 activate: null,
40                 beforeActivate: null,
41                 beforeLoad: null,
42                 load: null
43         },
45         _isLocal: (function() {
46                 var rhash = /#.*$/;
48                 return function( anchor ) {
49                         var anchorUrl, locationUrl;
51                         // support: IE7
52                         // IE7 doesn't normalize the href property when set via script (#9317)
53                         anchor = anchor.cloneNode( false );
55                         anchorUrl = anchor.href.replace( rhash, "" );
56                         locationUrl = location.href.replace( rhash, "" );
58                         // decoding may throw an error if the URL isn't UTF-8 (#9518)
59                         try {
60                                 anchorUrl = decodeURIComponent( anchorUrl );
61                         } catch ( error ) {}
62                         try {
63                                 locationUrl = decodeURIComponent( locationUrl );
64                         } catch ( error ) {}
66                         return anchor.hash.length > 1 && anchorUrl === locationUrl;
67                 };
68         })(),
70         _create: function() {
71                 var that = this,
72                         options = this.options;
74                 this.running = false;
76                 this.element
77                         .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" )
78                         .toggleClass( "ui-tabs-collapsible", options.collapsible );
80                 this._processTabs();
81                 options.active = this._initialActive();
83                 // Take disabling tabs via class attribute from HTML
84                 // into account and update option properly.
85                 if ( $.isArray( options.disabled ) ) {
86                         options.disabled = $.unique( options.disabled.concat(
87                                 $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
88                                         return that.tabs.index( li );
89                                 })
90                         ) ).sort();
91                 }
93                 // check for length avoids error when initializing empty list
94                 if ( this.options.active !== false && this.anchors.length ) {
95                         this.active = this._findActive( options.active );
96                 } else {
97                         this.active = $();
98                 }
100                 this._refresh();
102                 if ( this.active.length ) {
103                         this.load( options.active );
104                 }
105         },
107         _initialActive: function() {
108                 var active = this.options.active,
109                         collapsible = this.options.collapsible,
110                         locationHash = location.hash.substring( 1 );
112                 if ( active === null ) {
113                         // check the fragment identifier in the URL
114                         if ( locationHash ) {
115                                 this.tabs.each(function( i, tab ) {
116                                         if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
117                                                 active = i;
118                                                 return false;
119                                         }
120                                 });
121                         }
123                         // check for a tab marked active via a class
124                         if ( active === null ) {
125                                 active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
126                         }
128                         // no active tab, set to false
129                         if ( active === null || active === -1 ) {
130                                 active = this.tabs.length ? 0 : false;
131                         }
132                 }
134                 // handle numbers: negative, out of range
135                 if ( active !== false ) {
136                         active = this.tabs.index( this.tabs.eq( active ) );
137                         if ( active === -1 ) {
138                                 active = collapsible ? false : 0;
139                         }
140                 }
142                 // don't allow collapsible: false and active: false
143                 if ( !collapsible && active === false && this.anchors.length ) {
144                         active = 0;
145                 }
147                 return active;
148         },
150         _getCreateEventData: function() {
151                 return {
152                         tab: this.active,
153                         panel: !this.active.length ? $() : this._getPanelForTab( this.active )
154                 };
155         },
157         _tabKeydown: function( event ) {
158                 var focusedTab = $( this.document[0].activeElement ).closest( "li" ),
159                         selectedIndex = this.tabs.index( focusedTab ),
160                         goingForward = true;
162                 if ( this._handlePageNav( event ) ) {
163                         return;
164                 }
166                 switch ( event.keyCode ) {
167                         case $.ui.keyCode.RIGHT:
168                         case $.ui.keyCode.DOWN:
169                                 selectedIndex++;
170                                 break;
171                         case $.ui.keyCode.UP:
172                         case $.ui.keyCode.LEFT:
173                                 goingForward = false;
174                                 selectedIndex--;
175                                 break;
176                         case $.ui.keyCode.END:
177                                 selectedIndex = this.anchors.length - 1;
178                                 break;
179                         case $.ui.keyCode.HOME:
180                                 selectedIndex = 0;
181                                 break;
182                         case $.ui.keyCode.SPACE:
183                                 // Activate only, no collapsing
184                                 event.preventDefault();
185                                 clearTimeout( this.activating );
186                                 this._activate( selectedIndex );
187                                 return;
188                         case $.ui.keyCode.ENTER:
189                                 // Toggle (cancel delayed activation, allow collapsing)
190                                 event.preventDefault();
191                                 clearTimeout( this.activating );
192                                 // Determine if we should collapse or activate
193                                 this._activate( selectedIndex === this.options.active ? false : selectedIndex );
194                                 return;
195                         default:
196                                 return;
197                 }
199                 // Focus the appropriate tab, based on which key was pressed
200                 event.preventDefault();
201                 clearTimeout( this.activating );
202                 selectedIndex = this._focusNextTab( selectedIndex, goingForward );
204                 // Navigating with control key will prevent automatic activation
205                 if ( !event.ctrlKey ) {
206                         // Update aria-selected immediately so that AT think the tab is already selected.
207                         // Otherwise AT may confuse the user by stating that they need to activate the tab,
208                         // but the tab will already be activated by the time the announcement finishes.
209                         focusedTab.attr( "aria-selected", "false" );
210                         this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
212                         this.activating = this._delay(function() {
213                                 this.option( "active", selectedIndex );
214                         }, this.delay );
215                 }
216         },
218         _panelKeydown: function( event ) {
219                 if ( this._handlePageNav( event ) ) {
220                         return;
221                 }
223                 // Ctrl+up moves focus to the current tab
224                 if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
225                         event.preventDefault();
226                         this.active.focus();
227                 }
228         },
230         // Alt+page up/down moves focus to the previous/next tab (and activates)
231         _handlePageNav: function( event ) {
232                 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
233                         this._activate( this._focusNextTab( this.options.active - 1, false ) );
234                         return true;
235                 }
236                 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
237                         this._activate( this._focusNextTab( this.options.active + 1, true ) );
238                         return true;
239                 }
240         },
242         _findNextTab: function( index, goingForward ) {
243                 var lastTabIndex = this.tabs.length - 1;
245                 function constrain() {
246                         if ( index > lastTabIndex ) {
247                                 index = 0;
248                         }
249                         if ( index < 0 ) {
250                                 index = lastTabIndex;
251                         }
252                         return index;
253                 }
255                 while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
256                         index = goingForward ? index + 1 : index - 1;
257                 }
259                 return index;
260         },
262         _focusNextTab: function( index, goingForward ) {
263                 index = this._findNextTab( index, goingForward );
264                 this.tabs.eq( index ).focus();
265                 return index;
266         },
268         _setOption: function( key, value ) {
269                 if ( key === "active" ) {
270                         // _activate() will handle invalid values and update this.options
271                         this._activate( value );
272                         return;
273                 }
275                 if ( key === "disabled" ) {
276                         // don't use the widget factory's disabled handling
277                         this._setupDisabled( value );
278                         return;
279                 }
281                 this._super( key, value);
283                 if ( key === "collapsible" ) {
284                         this.element.toggleClass( "ui-tabs-collapsible", value );
285                         // Setting collapsible: false while collapsed; open first panel
286                         if ( !value && this.options.active === false ) {
287                                 this._activate( 0 );
288                         }
289                 }
291                 if ( key === "event" ) {
292                         this._setupEvents( value );
293                 }
295                 if ( key === "heightStyle" ) {
296                         this._setupHeightStyle( value );
297                 }
298         },
300         _sanitizeSelector: function( hash ) {
301                 return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
302         },
304         refresh: function() {
305                 var options = this.options,
306                         lis = this.tablist.children( ":has(a[href])" );
308                 // get disabled tabs from class attribute from HTML
309                 // this will get converted to a boolean if needed in _refresh()
310                 options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
311                         return lis.index( tab );
312                 });
314                 this._processTabs();
316                 // was collapsed or no tabs
317                 if ( options.active === false || !this.anchors.length ) {
318                         options.active = false;
319                         this.active = $();
320                 // was active, but active tab is gone
321                 } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
322                         // all remaining tabs are disabled
323                         if ( this.tabs.length === options.disabled.length ) {
324                                 options.active = false;
325                                 this.active = $();
326                         // activate previous tab
327                         } else {
328                                 this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
329                         }
330                 // was active, active tab still exists
331                 } else {
332                         // make sure active index is correct
333                         options.active = this.tabs.index( this.active );
334                 }
336                 this._refresh();
337         },
339         _refresh: function() {
340                 this._setupDisabled( this.options.disabled );
341                 this._setupEvents( this.options.event );
342                 this._setupHeightStyle( this.options.heightStyle );
344                 this.tabs.not( this.active ).attr({
345                         "aria-selected": "false",
346                         "aria-expanded": "false",
347                         tabIndex: -1
348                 });
349                 this.panels.not( this._getPanelForTab( this.active ) )
350                         .hide()
351                         .attr({
352                                 "aria-hidden": "true"
353                         });
355                 // Make sure one tab is in the tab order
356                 if ( !this.active.length ) {
357                         this.tabs.eq( 0 ).attr( "tabIndex", 0 );
358                 } else {
359                         this.active
360                                 .addClass( "ui-tabs-active ui-state-active" )
361                                 .attr({
362                                         "aria-selected": "true",
363                                         "aria-expanded": "true",
364                                         tabIndex: 0
365                                 });
366                         this._getPanelForTab( this.active )
367                                 .show()
368                                 .attr({
369                                         "aria-hidden": "false"
370                                 });
371                 }
372         },
374         _processTabs: function() {
375                 var that = this,
376                         prevTabs = this.tabs,
377                         prevAnchors = this.anchors,
378                         prevPanels = this.panels;
380                 this.tablist = this._getList()
381                         .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
382                         .attr( "role", "tablist" )
384                         // Prevent users from focusing disabled tabs via click
385                         .delegate( "> li", "mousedown" + this.eventNamespace, function( event ) {
386                                 if ( $( this ).is( ".ui-state-disabled" ) ) {
387                                         event.preventDefault();
388                                 }
389                         })
391                         // support: IE <9
392                         // Preventing the default action in mousedown doesn't prevent IE
393                         // from focusing the element, so if the anchor gets focused, blur.
394                         // We don't have to worry about focusing the previously focused
395                         // element since clicking on a non-focusable element should focus
396                         // the body anyway.
397                         .delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() {
398                                 if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
399                                         this.blur();
400                                 }
401                         });
403                 this.tabs = this.tablist.find( "> li:has(a[href])" )
404                         .addClass( "ui-state-default ui-corner-top" )
405                         .attr({
406                                 role: "tab",
407                                 tabIndex: -1
408                         });
410                 this.anchors = this.tabs.map(function() {
411                                 return $( "a", this )[ 0 ];
412                         })
413                         .addClass( "ui-tabs-anchor" )
414                         .attr({
415                                 role: "presentation",
416                                 tabIndex: -1
417                         });
419                 this.panels = $();
421                 this.anchors.each(function( i, anchor ) {
422                         var selector, panel, panelId,
423                                 anchorId = $( anchor ).uniqueId().attr( "id" ),
424                                 tab = $( anchor ).closest( "li" ),
425                                 originalAriaControls = tab.attr( "aria-controls" );
427                         // inline tab
428                         if ( that._isLocal( anchor ) ) {
429                                 selector = anchor.hash;
430                                 panelId = selector.substring( 1 );
431                                 panel = that.element.find( that._sanitizeSelector( selector ) );
432                         // remote tab
433                         } else {
434                                 // If the tab doesn't already have aria-controls,
435                                 // generate an id by using a throw-away element
436                                 panelId = tab.attr( "aria-controls" ) || $( {} ).uniqueId()[ 0 ].id;
437                                 selector = "#" + panelId;
438                                 panel = that.element.find( selector );
439                                 if ( !panel.length ) {
440                                         panel = that._createPanel( panelId );
441                                         panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
442                                 }
443                                 panel.attr( "aria-live", "polite" );
444                         }
446                         if ( panel.length) {
447                                 that.panels = that.panels.add( panel );
448                         }
449                         if ( originalAriaControls ) {
450                                 tab.data( "ui-tabs-aria-controls", originalAriaControls );
451                         }
452                         tab.attr({
453                                 "aria-controls": panelId,
454                                 "aria-labelledby": anchorId
455                         });
456                         panel.attr( "aria-labelledby", anchorId );
457                 });
459                 this.panels
460                         .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
461                         .attr( "role", "tabpanel" );
463                 // Avoid memory leaks (#10056)
464                 if ( prevTabs ) {
465                         this._off( prevTabs.not( this.tabs ) );
466                         this._off( prevAnchors.not( this.anchors ) );
467                         this._off( prevPanels.not( this.panels ) );
468                 }
469         },
471         // allow overriding how to find the list for rare usage scenarios (#7715)
472         _getList: function() {
473                 return this.tablist || this.element.find( "ol,ul" ).eq( 0 );
474         },
476         _createPanel: function( id ) {
477                 return $( "<div>" )
478                         .attr( "id", id )
479                         .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
480                         .data( "ui-tabs-destroy", true );
481         },
483         _setupDisabled: function( disabled ) {
484                 if ( $.isArray( disabled ) ) {
485                         if ( !disabled.length ) {
486                                 disabled = false;
487                         } else if ( disabled.length === this.anchors.length ) {
488                                 disabled = true;
489                         }
490                 }
492                 // disable tabs
493                 for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) {
494                         if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
495                                 $( li )
496                                         .addClass( "ui-state-disabled" )
497                                         .attr( "aria-disabled", "true" );
498                         } else {
499                                 $( li )
500                                         .removeClass( "ui-state-disabled" )
501                                         .removeAttr( "aria-disabled" );
502                         }
503                 }
505                 this.options.disabled = disabled;
506         },
508         _setupEvents: function( event ) {
509                 var events = {};
510                 if ( event ) {
511                         $.each( event.split(" "), function( index, eventName ) {
512                                 events[ eventName ] = "_eventHandler";
513                         });
514                 }
516                 this._off( this.anchors.add( this.tabs ).add( this.panels ) );
517                 // Always prevent the default action, even when disabled
518                 this._on( true, this.anchors, {
519                         click: function( event ) {
520                                 event.preventDefault();
521                         }
522                 });
523                 this._on( this.anchors, events );
524                 this._on( this.tabs, { keydown: "_tabKeydown" } );
525                 this._on( this.panels, { keydown: "_panelKeydown" } );
527                 this._focusable( this.tabs );
528                 this._hoverable( this.tabs );
529         },
531         _setupHeightStyle: function( heightStyle ) {
532                 var maxHeight,
533                         parent = this.element.parent();
535                 if ( heightStyle === "fill" ) {
536                         maxHeight = parent.height();
537                         maxHeight -= this.element.outerHeight() - this.element.height();
539                         this.element.siblings( ":visible" ).each(function() {
540                                 var elem = $( this ),
541                                         position = elem.css( "position" );
543                                 if ( position === "absolute" || position === "fixed" ) {
544                                         return;
545                                 }
546                                 maxHeight -= elem.outerHeight( true );
547                         });
549                         this.element.children().not( this.panels ).each(function() {
550                                 maxHeight -= $( this ).outerHeight( true );
551                         });
553                         this.panels.each(function() {
554                                 $( this ).height( Math.max( 0, maxHeight -
555                                         $( this ).innerHeight() + $( this ).height() ) );
556                         })
557                         .css( "overflow", "auto" );
558                 } else if ( heightStyle === "auto" ) {
559                         maxHeight = 0;
560                         this.panels.each(function() {
561                                 maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
562                         }).height( maxHeight );
563                 }
564         },
566         _eventHandler: function( event ) {
567                 var options = this.options,
568                         active = this.active,
569                         anchor = $( event.currentTarget ),
570                         tab = anchor.closest( "li" ),
571                         clickedIsActive = tab[ 0 ] === active[ 0 ],
572                         collapsing = clickedIsActive && options.collapsible,
573                         toShow = collapsing ? $() : this._getPanelForTab( tab ),
574                         toHide = !active.length ? $() : this._getPanelForTab( active ),
575                         eventData = {
576                                 oldTab: active,
577                                 oldPanel: toHide,
578                                 newTab: collapsing ? $() : tab,
579                                 newPanel: toShow
580                         };
582                 event.preventDefault();
584                 if ( tab.hasClass( "ui-state-disabled" ) ||
585                                 // tab is already loading
586                                 tab.hasClass( "ui-tabs-loading" ) ||
587                                 // can't switch durning an animation
588                                 this.running ||
589                                 // click on active header, but not collapsible
590                                 ( clickedIsActive && !options.collapsible ) ||
591                                 // allow canceling activation
592                                 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
593                         return;
594                 }
596                 options.active = collapsing ? false : this.tabs.index( tab );
598                 this.active = clickedIsActive ? $() : tab;
599                 if ( this.xhr ) {
600                         this.xhr.abort();
601                 }
603                 if ( !toHide.length && !toShow.length ) {
604                         $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
605                 }
607                 if ( toShow.length ) {
608                         this.load( this.tabs.index( tab ), event );
609                 }
610                 this._toggle( event, eventData );
611         },
613         // handles show/hide for selecting tabs
614         _toggle: function( event, eventData ) {
615                 var that = this,
616                         toShow = eventData.newPanel,
617                         toHide = eventData.oldPanel;
619                 this.running = true;
621                 function complete() {
622                         that.running = false;
623                         that._trigger( "activate", event, eventData );
624                 }
626                 function show() {
627                         eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
629                         if ( toShow.length && that.options.show ) {
630                                 that._show( toShow, that.options.show, complete );
631                         } else {
632                                 toShow.show();
633                                 complete();
634                         }
635                 }
637                 // start out by hiding, then showing, then completing
638                 if ( toHide.length && this.options.hide ) {
639                         this._hide( toHide, this.options.hide, function() {
640                                 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
641                                 show();
642                         });
643                 } else {
644                         eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
645                         toHide.hide();
646                         show();
647                 }
649                 toHide.attr( "aria-hidden", "true" );
650                 eventData.oldTab.attr({
651                         "aria-selected": "false",
652                         "aria-expanded": "false"
653                 });
654                 // If we're switching tabs, remove the old tab from the tab order.
655                 // If we're opening from collapsed state, remove the previous tab from the tab order.
656                 // If we're collapsing, then keep the collapsing tab in the tab order.
657                 if ( toShow.length && toHide.length ) {
658                         eventData.oldTab.attr( "tabIndex", -1 );
659                 } else if ( toShow.length ) {
660                         this.tabs.filter(function() {
661                                 return $( this ).attr( "tabIndex" ) === 0;
662                         })
663                         .attr( "tabIndex", -1 );
664                 }
666                 toShow.attr( "aria-hidden", "false" );
667                 eventData.newTab.attr({
668                         "aria-selected": "true",
669                         "aria-expanded": "true",
670                         tabIndex: 0
671                 });
672         },
674         _activate: function( index ) {
675                 var anchor,
676                         active = this._findActive( index );
678                 // trying to activate the already active panel
679                 if ( active[ 0 ] === this.active[ 0 ] ) {
680                         return;
681                 }
683                 // trying to collapse, simulate a click on the current active header
684                 if ( !active.length ) {
685                         active = this.active;
686                 }
688                 anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
689                 this._eventHandler({
690                         target: anchor,
691                         currentTarget: anchor,
692                         preventDefault: $.noop
693                 });
694         },
696         _findActive: function( index ) {
697                 return index === false ? $() : this.tabs.eq( index );
698         },
700         _getIndex: function( index ) {
701                 // meta-function to give users option to provide a href string instead of a numerical index.
702                 if ( typeof index === "string" ) {
703                         index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) );
704                 }
706                 return index;
707         },
709         _destroy: function() {
710                 if ( this.xhr ) {
711                         this.xhr.abort();
712                 }
714                 this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" );
716                 this.tablist
717                         .removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
718                         .removeAttr( "role" );
720                 this.anchors
721                         .removeClass( "ui-tabs-anchor" )
722                         .removeAttr( "role" )
723                         .removeAttr( "tabIndex" )
724                         .removeUniqueId();
726                 this.tablist.unbind( this.eventNamespace );
728                 this.tabs.add( this.panels ).each(function() {
729                         if ( $.data( this, "ui-tabs-destroy" ) ) {
730                                 $( this ).remove();
731                         } else {
732                                 $( this )
733                                         .removeClass( "ui-state-default ui-state-active ui-state-disabled " +
734                                                 "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" )
735                                         .removeAttr( "tabIndex" )
736                                         .removeAttr( "aria-live" )
737                                         .removeAttr( "aria-busy" )
738                                         .removeAttr( "aria-selected" )
739                                         .removeAttr( "aria-labelledby" )
740                                         .removeAttr( "aria-hidden" )
741                                         .removeAttr( "aria-expanded" )
742                                         .removeAttr( "role" );
743                         }
744                 });
746                 this.tabs.each(function() {
747                         var li = $( this ),
748                                 prev = li.data( "ui-tabs-aria-controls" );
749                         if ( prev ) {
750                                 li
751                                         .attr( "aria-controls", prev )
752                                         .removeData( "ui-tabs-aria-controls" );
753                         } else {
754                                 li.removeAttr( "aria-controls" );
755                         }
756                 });
758                 this.panels.show();
760                 if ( this.options.heightStyle !== "content" ) {
761                         this.panels.css( "height", "" );
762                 }
763         },
765         enable: function( index ) {
766                 var disabled = this.options.disabled;
767                 if ( disabled === false ) {
768                         return;
769                 }
771                 if ( index === undefined ) {
772                         disabled = false;
773                 } else {
774                         index = this._getIndex( index );
775                         if ( $.isArray( disabled ) ) {
776                                 disabled = $.map( disabled, function( num ) {
777                                         return num !== index ? num : null;
778                                 });
779                         } else {
780                                 disabled = $.map( this.tabs, function( li, num ) {
781                                         return num !== index ? num : null;
782                                 });
783                         }
784                 }
785                 this._setupDisabled( disabled );
786         },
788         disable: function( index ) {
789                 var disabled = this.options.disabled;
790                 if ( disabled === true ) {
791                         return;
792                 }
794                 if ( index === undefined ) {
795                         disabled = true;
796                 } else {
797                         index = this._getIndex( index );
798                         if ( $.inArray( index, disabled ) !== -1 ) {
799                                 return;
800                         }
801                         if ( $.isArray( disabled ) ) {
802                                 disabled = $.merge( [ index ], disabled ).sort();
803                         } else {
804                                 disabled = [ index ];
805                         }
806                 }
807                 this._setupDisabled( disabled );
808         },
810         load: function( index, event ) {
811                 index = this._getIndex( index );
812                 var that = this,
813                         tab = this.tabs.eq( index ),
814                         anchor = tab.find( ".ui-tabs-anchor" ),
815                         panel = this._getPanelForTab( tab ),
816                         eventData = {
817                                 tab: tab,
818                                 panel: panel
819                         };
821                 // not remote
822                 if ( this._isLocal( anchor[ 0 ] ) ) {
823                         return;
824                 }
826                 this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
828                 // support: jQuery <1.8
829                 // jQuery <1.8 returns false if the request is canceled in beforeSend,
830                 // but as of 1.8, $.ajax() always returns a jqXHR object.
831                 if ( this.xhr && this.xhr.statusText !== "canceled" ) {
832                         tab.addClass( "ui-tabs-loading" );
833                         panel.attr( "aria-busy", "true" );
835                         this.xhr
836                                 .success(function( response ) {
837                                         // support: jQuery <1.8
838                                         // http://bugs.jquery.com/ticket/11778
839                                         setTimeout(function() {
840                                                 panel.html( response );
841                                                 that._trigger( "load", event, eventData );
842                                         }, 1 );
843                                 })
844                                 .complete(function( jqXHR, status ) {
845                                         // support: jQuery <1.8
846                                         // http://bugs.jquery.com/ticket/11778
847                                         setTimeout(function() {
848                                                 if ( status === "abort" ) {
849                                                         that.panels.stop( false, true );
850                                                 }
852                                                 tab.removeClass( "ui-tabs-loading" );
853                                                 panel.removeAttr( "aria-busy" );
855                                                 if ( jqXHR === that.xhr ) {
856                                                         delete that.xhr;
857                                                 }
858                                         }, 1 );
859                                 });
860                 }
861         },
863         _ajaxSettings: function( anchor, event, eventData ) {
864                 var that = this;
865                 return {
866                         url: anchor.attr( "href" ),
867                         beforeSend: function( jqXHR, settings ) {
868                                 return that._trigger( "beforeLoad", event,
869                                         $.extend( { jqXHR: jqXHR, ajaxSettings: settings }, eventData ) );
870                         }
871                 };
872         },
874         _getPanelForTab: function( tab ) {
875                 var id = $( tab ).attr( "aria-controls" );
876                 return this.element.find( this._sanitizeSelector( "#" + id ) );
877         }
880 }));