[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/js/jquery/ui/ -> tabs.js (source)

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


Generated : Tue Dec 17 08:20:01 2024 Cross-referenced by PHPXref