integration/back-office/lib/jquery-ui/ui/jquery.ui.tabs.js
changeset 1 b95aebb070b5
equal deleted inserted replaced
0:a51471894a9e 1:b95aebb070b5
       
     1 /*!
       
     2  * jQuery UI Tabs 1.10.3
       
     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.3",
       
    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 );