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