[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/resources/lib/jquery.ui/ -> jquery.ui.tabs.js (source)

   1  /*!
   2   * jQuery UI Tabs 1.9.2
   3   * http://jqueryui.com
   4   *
   5   * Copyright 2012 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          anchor.href.replace( rhash, "" ) ===
  27              location.href.replace( rhash, "" )
  28                  // support: Safari 5.1
  29                  // Safari 5.1 doesn't encode spaces in window.location
  30                  // but it does encode spaces from anchors (#8777)
  31                  .replace( /\s/g, "%20" );
  32  }
  33  
  34  $.widget( "ui.tabs", {
  35      version: "1.9.2",
  36      delay: 300,
  37      options: {
  38          active: null,
  39          collapsible: false,
  40          event: "click",
  41          heightStyle: "content",
  42          hide: null,
  43          show: null,
  44  
  45          // callbacks
  46          activate: null,
  47          beforeActivate: null,
  48          beforeLoad: null,
  49          load: null
  50      },
  51  
  52      _create: function() {
  53          var that = this,
  54              options = this.options,
  55              active = options.active,
  56              locationHash = location.hash.substring( 1 );
  57  
  58          this.running = false;
  59  
  60          this.element
  61              .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" )
  62              .toggleClass( "ui-tabs-collapsible", options.collapsible )
  63              // Prevent users from focusing disabled tabs via click
  64              .delegate( ".ui-tabs-nav > li", "mousedown" + this.eventNamespace, function( event ) {
  65                  if ( $( this ).is( ".ui-state-disabled" ) ) {
  66                      event.preventDefault();
  67                  }
  68              })
  69              // support: IE <9
  70              // Preventing the default action in mousedown doesn't prevent IE
  71              // from focusing the element, so if the anchor gets focused, blur.
  72              // We don't have to worry about focusing the previously focused
  73              // element since clicking on a non-focusable element should focus
  74              // the body anyway.
  75              .delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() {
  76                  if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
  77                      this.blur();
  78                  }
  79              });
  80  
  81          this._processTabs();
  82  
  83          if ( active === null ) {
  84              // check the fragment identifier in the URL
  85              if ( locationHash ) {
  86                  this.tabs.each(function( i, tab ) {
  87                      if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
  88                          active = i;
  89                          return false;
  90                      }
  91                  });
  92              }
  93  
  94              // check for a tab marked active via a class
  95              if ( active === null ) {
  96                  active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
  97              }
  98  
  99              // no active tab, set to false
 100              if ( active === null || active === -1 ) {
 101                  active = this.tabs.length ? 0 : false;
 102              }
 103          }
 104  
 105          // handle numbers: negative, out of range
 106          if ( active !== false ) {
 107              active = this.tabs.index( this.tabs.eq( active ) );
 108              if ( active === -1 ) {
 109                  active = options.collapsible ? false : 0;
 110              }
 111          }
 112          options.active = active;
 113  
 114          // don't allow collapsible: false and active: false
 115          if ( !options.collapsible && options.active === false && this.anchors.length ) {
 116              options.active = 0;
 117          }
 118  
 119          // Take disabling tabs via class attribute from HTML
 120          // into account and update option properly.
 121          if ( $.isArray( options.disabled ) ) {
 122              options.disabled = $.unique( options.disabled.concat(
 123                  $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
 124                      return that.tabs.index( li );
 125                  })
 126              ) ).sort();
 127          }
 128  
 129          // check for length avoids error when initializing empty list
 130          if ( this.options.active !== false && this.anchors.length ) {
 131              this.active = this._findActive( this.options.active );
 132          } else {
 133              this.active = $();
 134          }
 135  
 136          this._refresh();
 137  
 138          if ( this.active.length ) {
 139              this.load( options.active );
 140          }
 141      },
 142  
 143      _getCreateEventData: function() {
 144          return {
 145              tab: this.active,
 146              panel: !this.active.length ? $() : this._getPanelForTab( this.active )
 147          };
 148      },
 149  
 150      _tabKeydown: function( event ) {
 151          var focusedTab = $( this.document[0].activeElement ).closest( "li" ),
 152              selectedIndex = this.tabs.index( focusedTab ),
 153              goingForward = true;
 154  
 155          if ( this._handlePageNav( event ) ) {
 156              return;
 157          }
 158  
 159          switch ( event.keyCode ) {
 160              case $.ui.keyCode.RIGHT:
 161              case $.ui.keyCode.DOWN:
 162                  selectedIndex++;
 163                  break;
 164              case $.ui.keyCode.UP:
 165              case $.ui.keyCode.LEFT:
 166                  goingForward = false;
 167                  selectedIndex--;
 168                  break;
 169              case $.ui.keyCode.END:
 170                  selectedIndex = this.anchors.length - 1;
 171                  break;
 172              case $.ui.keyCode.HOME:
 173                  selectedIndex = 0;
 174                  break;
 175              case $.ui.keyCode.SPACE:
 176                  // Activate only, no collapsing
 177                  event.preventDefault();
 178                  clearTimeout( this.activating );
 179                  this._activate( selectedIndex );
 180                  return;
 181              case $.ui.keyCode.ENTER:
 182                  // Toggle (cancel delayed activation, allow collapsing)
 183                  event.preventDefault();
 184                  clearTimeout( this.activating );
 185                  // Determine if we should collapse or activate
 186                  this._activate( selectedIndex === this.options.active ? false : selectedIndex );
 187                  return;
 188              default:
 189                  return;
 190          }
 191  
 192          // Focus the appropriate tab, based on which key was pressed
 193          event.preventDefault();
 194          clearTimeout( this.activating );
 195          selectedIndex = this._focusNextTab( selectedIndex, goingForward );
 196  
 197          // Navigating with control key will prevent automatic activation
 198          if ( !event.ctrlKey ) {
 199              // Update aria-selected immediately so that AT think the tab is already selected.
 200              // Otherwise AT may confuse the user by stating that they need to activate the tab,
 201              // but the tab will already be activated by the time the announcement finishes.
 202              focusedTab.attr( "aria-selected", "false" );
 203              this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
 204  
 205              this.activating = this._delay(function() {
 206                  this.option( "active", selectedIndex );
 207              }, this.delay );
 208          }
 209      },
 210  
 211      _panelKeydown: function( event ) {
 212          if ( this._handlePageNav( event ) ) {
 213              return;
 214          }
 215  
 216          // Ctrl+up moves focus to the current tab
 217          if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
 218              event.preventDefault();
 219              this.active.focus();
 220          }
 221      },
 222  
 223      // Alt+page up/down moves focus to the previous/next tab (and activates)
 224      _handlePageNav: function( event ) {
 225          if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
 226              this._activate( this._focusNextTab( this.options.active - 1, false ) );
 227              return true;
 228          }
 229          if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
 230              this._activate( this._focusNextTab( this.options.active + 1, true ) );
 231              return true;
 232          }
 233      },
 234  
 235      _findNextTab: function( index, goingForward ) {
 236          var lastTabIndex = this.tabs.length - 1;
 237  
 238  		function constrain() {
 239              if ( index > lastTabIndex ) {
 240                  index = 0;
 241              }
 242              if ( index < 0 ) {
 243                  index = lastTabIndex;
 244              }
 245              return index;
 246          }
 247  
 248          while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
 249              index = goingForward ? index + 1 : index - 1;
 250          }
 251  
 252          return index;
 253      },
 254  
 255      _focusNextTab: function( index, goingForward ) {
 256          index = this._findNextTab( index, goingForward );
 257          this.tabs.eq( index ).focus();
 258          return index;
 259      },
 260  
 261      _setOption: function( key, value ) {
 262          if ( key === "active" ) {
 263              // _activate() will handle invalid values and update this.options
 264              this._activate( value );
 265              return;
 266          }
 267  
 268          if ( key === "disabled" ) {
 269              // don't use the widget factory's disabled handling
 270              this._setupDisabled( value );
 271              return;
 272          }
 273  
 274          this._super( key, value);
 275  
 276          if ( key === "collapsible" ) {
 277              this.element.toggleClass( "ui-tabs-collapsible", value );
 278              // Setting collapsible: false while collapsed; open first panel
 279              if ( !value && this.options.active === false ) {
 280                  this._activate( 0 );
 281              }
 282          }
 283  
 284          if ( key === "event" ) {
 285              this._setupEvents( value );
 286          }
 287  
 288          if ( key === "heightStyle" ) {
 289              this._setupHeightStyle( value );
 290          }
 291      },
 292  
 293      _tabId: function( tab ) {
 294          return tab.attr( "aria-controls" ) || "ui-tabs-" + getNextTabId();
 295      },
 296  
 297      _sanitizeSelector: function( hash ) {
 298          return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
 299      },
 300  
 301      refresh: function() {
 302          var options = this.options,
 303              lis = this.tablist.children( ":has(a[href])" );
 304  
 305          // get disabled tabs from class attribute from HTML
 306          // this will get converted to a boolean if needed in _refresh()
 307          options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
 308              return lis.index( tab );
 309          });
 310  
 311          this._processTabs();
 312  
 313          // was collapsed or no tabs
 314          if ( options.active === false || !this.anchors.length ) {
 315              options.active = false;
 316              this.active = $();
 317          // was active, but active tab is gone
 318          } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
 319              // all remaining tabs are disabled
 320              if ( this.tabs.length === options.disabled.length ) {
 321                  options.active = false;
 322                  this.active = $();
 323              // activate previous tab
 324              } else {
 325                  this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
 326              }
 327          // was active, active tab still exists
 328          } else {
 329              // make sure active index is correct
 330              options.active = this.tabs.index( this.active );
 331          }
 332  
 333          this._refresh();
 334      },
 335  
 336      _refresh: function() {
 337          this._setupDisabled( this.options.disabled );
 338          this._setupEvents( this.options.event );
 339          this._setupHeightStyle( this.options.heightStyle );
 340  
 341          this.tabs.not( this.active ).attr({
 342              "aria-selected": "false",
 343              tabIndex: -1
 344          });
 345          this.panels.not( this._getPanelForTab( this.active ) )
 346              .hide()
 347              .attr({
 348                  "aria-expanded": "false",
 349                  "aria-hidden": "true"
 350              });
 351  
 352          // Make sure one tab is in the tab order
 353          if ( !this.active.length ) {
 354              this.tabs.eq( 0 ).attr( "tabIndex", 0 );
 355          } else {
 356              this.active
 357                  .addClass( "ui-tabs-active ui-state-active" )
 358                  .attr({
 359                      "aria-selected": "true",
 360                      tabIndex: 0
 361                  });
 362              this._getPanelForTab( this.active )
 363                  .show()
 364                  .attr({
 365                      "aria-expanded": "true",
 366                      "aria-hidden": "false"
 367                  });
 368          }
 369      },
 370  
 371      _processTabs: function() {
 372          var that = this;
 373  
 374          this.tablist = this._getList()
 375              .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
 376              .attr( "role", "tablist" );
 377  
 378          this.tabs = this.tablist.find( "> li:has(a[href])" )
 379              .addClass( "ui-state-default ui-corner-top" )
 380              .attr({
 381                  role: "tab",
 382                  tabIndex: -1
 383              });
 384  
 385          this.anchors = this.tabs.map(function() {
 386                  return $( "a", this )[ 0 ];
 387              })
 388              .addClass( "ui-tabs-anchor" )
 389              .attr({
 390                  role: "presentation",
 391                  tabIndex: -1
 392              });
 393  
 394          this.panels = $();
 395  
 396          this.anchors.each(function( i, anchor ) {
 397              var selector, panel, panelId,
 398                  anchorId = $( anchor ).uniqueId().attr( "id" ),
 399                  tab = $( anchor ).closest( "li" ),
 400                  originalAriaControls = tab.attr( "aria-controls" );
 401  
 402              // inline tab
 403              if ( isLocal( anchor ) ) {
 404                  selector = anchor.hash;
 405                  panel = that.element.find( that._sanitizeSelector( selector ) );
 406              // remote tab
 407              } else {
 408                  panelId = that._tabId( tab );
 409                  selector = "#" + panelId;
 410                  panel = that.element.find( selector );
 411                  if ( !panel.length ) {
 412                      panel = that._createPanel( panelId );
 413                      panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
 414                  }
 415                  panel.attr( "aria-live", "polite" );
 416              }
 417  
 418              if ( panel.length) {
 419                  that.panels = that.panels.add( panel );
 420              }
 421              if ( originalAriaControls ) {
 422                  tab.data( "ui-tabs-aria-controls", originalAriaControls );
 423              }
 424              tab.attr({
 425                  "aria-controls": selector.substring( 1 ),
 426                  "aria-labelledby": anchorId
 427              });
 428              panel.attr( "aria-labelledby", anchorId );
 429          });
 430  
 431          this.panels
 432              .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
 433              .attr( "role", "tabpanel" );
 434      },
 435  
 436      // allow overriding how to find the list for rare usage scenarios (#7715)
 437      _getList: function() {
 438          return this.element.find( "ol,ul" ).eq( 0 );
 439      },
 440  
 441      _createPanel: function( id ) {
 442          return $( "<div>" )
 443              .attr( "id", id )
 444              .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
 445              .data( "ui-tabs-destroy", true );
 446      },
 447  
 448      _setupDisabled: function( disabled ) {
 449          if ( $.isArray( disabled ) ) {
 450              if ( !disabled.length ) {
 451                  disabled = false;
 452              } else if ( disabled.length === this.anchors.length ) {
 453                  disabled = true;
 454              }
 455          }
 456  
 457          // disable tabs
 458          for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) {
 459              if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
 460                  $( li )
 461                      .addClass( "ui-state-disabled" )
 462                      .attr( "aria-disabled", "true" );
 463              } else {
 464                  $( li )
 465                      .removeClass( "ui-state-disabled" )
 466                      .removeAttr( "aria-disabled" );
 467              }
 468          }
 469  
 470          this.options.disabled = disabled;
 471      },
 472  
 473      _setupEvents: function( event ) {
 474          var events = {
 475              click: function( event ) {
 476                  event.preventDefault();
 477              }
 478          };
 479          if ( event ) {
 480              $.each( event.split(" "), function( index, eventName ) {
 481                  events[ eventName ] = "_eventHandler";
 482              });
 483          }
 484  
 485          this._off( this.anchors.add( this.tabs ).add( this.panels ) );
 486          this._on( this.anchors, events );
 487          this._on( this.tabs, { keydown: "_tabKeydown" } );
 488          this._on( this.panels, { keydown: "_panelKeydown" } );
 489  
 490          this._focusable( this.tabs );
 491          this._hoverable( this.tabs );
 492      },
 493  
 494      _setupHeightStyle: function( heightStyle ) {
 495          var maxHeight, overflow,
 496              parent = this.element.parent();
 497  
 498          if ( heightStyle === "fill" ) {
 499              // IE 6 treats height like minHeight, so we need to turn off overflow
 500              // in order to get a reliable height
 501              // we use the minHeight support test because we assume that only
 502              // browsers that don't support minHeight will treat height as minHeight
 503              if ( !$.support.minHeight ) {
 504                  overflow = parent.css( "overflow" );
 505                  parent.css( "overflow", "hidden");
 506              }
 507              maxHeight = parent.height();
 508              this.element.siblings( ":visible" ).each(function() {
 509                  var elem = $( this ),
 510                      position = elem.css( "position" );
 511  
 512                  if ( position === "absolute" || position === "fixed" ) {
 513                      return;
 514                  }
 515                  maxHeight -= elem.outerHeight( true );
 516              });
 517              if ( overflow ) {
 518                  parent.css( "overflow", overflow );
 519              }
 520  
 521              this.element.children().not( this.panels ).each(function() {
 522                  maxHeight -= $( this ).outerHeight( true );
 523              });
 524  
 525              this.panels.each(function() {
 526                  $( this ).height( Math.max( 0, maxHeight -
 527                      $( this ).innerHeight() + $( this ).height() ) );
 528              })
 529              .css( "overflow", "auto" );
 530          } else if ( heightStyle === "auto" ) {
 531              maxHeight = 0;
 532              this.panels.each(function() {
 533                  maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
 534              }).height( maxHeight );
 535          }
 536      },
 537  
 538      _eventHandler: function( event ) {
 539          var options = this.options,
 540              active = this.active,
 541              anchor = $( event.currentTarget ),
 542              tab = anchor.closest( "li" ),
 543              clickedIsActive = tab[ 0 ] === active[ 0 ],
 544              collapsing = clickedIsActive && options.collapsible,
 545              toShow = collapsing ? $() : this._getPanelForTab( tab ),
 546              toHide = !active.length ? $() : this._getPanelForTab( active ),
 547              eventData = {
 548                  oldTab: active,
 549                  oldPanel: toHide,
 550                  newTab: collapsing ? $() : tab,
 551                  newPanel: toShow
 552              };
 553  
 554          event.preventDefault();
 555  
 556          if ( tab.hasClass( "ui-state-disabled" ) ||
 557                  // tab is already loading
 558                  tab.hasClass( "ui-tabs-loading" ) ||
 559                  // can't switch durning an animation
 560                  this.running ||
 561                  // click on active header, but not collapsible
 562                  ( clickedIsActive && !options.collapsible ) ||
 563                  // allow canceling activation
 564                  ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
 565              return;
 566          }
 567  
 568          options.active = collapsing ? false : this.tabs.index( tab );
 569  
 570          this.active = clickedIsActive ? $() : tab;
 571          if ( this.xhr ) {
 572              this.xhr.abort();
 573          }
 574  
 575          if ( !toHide.length && !toShow.length ) {
 576              $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
 577          }
 578  
 579          if ( toShow.length ) {
 580              this.load( this.tabs.index( tab ), event );
 581          }
 582          this._toggle( event, eventData );
 583      },
 584  
 585      // handles show/hide for selecting tabs
 586      _toggle: function( event, eventData ) {
 587          var that = this,
 588              toShow = eventData.newPanel,
 589              toHide = eventData.oldPanel;
 590  
 591          this.running = true;
 592  
 593  		function complete() {
 594              that.running = false;
 595              that._trigger( "activate", event, eventData );
 596          }
 597  
 598  		function show() {
 599              eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
 600  
 601              if ( toShow.length && that.options.show ) {
 602                  that._show( toShow, that.options.show, complete );
 603              } else {
 604                  toShow.show();
 605                  complete();
 606              }
 607          }
 608  
 609          // start out by hiding, then showing, then completing
 610          if ( toHide.length && this.options.hide ) {
 611              this._hide( toHide, this.options.hide, function() {
 612                  eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
 613                  show();
 614              });
 615          } else {
 616              eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
 617              toHide.hide();
 618              show();
 619          }
 620  
 621          toHide.attr({
 622              "aria-expanded": "false",
 623              "aria-hidden": "true"
 624          });
 625          eventData.oldTab.attr( "aria-selected", "false" );
 626          // If we're switching tabs, remove the old tab from the tab order.
 627          // If we're opening from collapsed state, remove the previous tab from the tab order.
 628          // If we're collapsing, then keep the collapsing tab in the tab order.
 629          if ( toShow.length && toHide.length ) {
 630              eventData.oldTab.attr( "tabIndex", -1 );
 631          } else if ( toShow.length ) {
 632              this.tabs.filter(function() {
 633                  return $( this ).attr( "tabIndex" ) === 0;
 634              })
 635              .attr( "tabIndex", -1 );
 636          }
 637  
 638          toShow.attr({
 639              "aria-expanded": "true",
 640              "aria-hidden": "false"
 641          });
 642          eventData.newTab.attr({
 643              "aria-selected": "true",
 644              tabIndex: 0
 645          });
 646      },
 647  
 648      _activate: function( index ) {
 649          var anchor,
 650              active = this._findActive( index );
 651  
 652          // trying to activate the already active panel
 653          if ( active[ 0 ] === this.active[ 0 ] ) {
 654              return;
 655          }
 656  
 657          // trying to collapse, simulate a click on the current active header
 658          if ( !active.length ) {
 659              active = this.active;
 660          }
 661  
 662          anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
 663          this._eventHandler({
 664              target: anchor,
 665              currentTarget: anchor,
 666              preventDefault: $.noop
 667          });
 668      },
 669  
 670      _findActive: function( index ) {
 671          return index === false ? $() : this.tabs.eq( index );
 672      },
 673  
 674      _getIndex: function( index ) {
 675          // meta-function to give users option to provide a href string instead of a numerical index.
 676          if ( typeof index === "string" ) {
 677              index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) );
 678          }
 679  
 680          return index;
 681      },
 682  
 683      _destroy: function() {
 684          if ( this.xhr ) {
 685              this.xhr.abort();
 686          }
 687  
 688          this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" );
 689  
 690          this.tablist
 691              .removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
 692              .removeAttr( "role" );
 693  
 694          this.anchors
 695              .removeClass( "ui-tabs-anchor" )
 696              .removeAttr( "role" )
 697              .removeAttr( "tabIndex" )
 698              .removeData( "href.tabs" )
 699              .removeData( "load.tabs" )
 700              .removeUniqueId();
 701  
 702          this.tabs.add( this.panels ).each(function() {
 703              if ( $.data( this, "ui-tabs-destroy" ) ) {
 704                  $( this ).remove();
 705              } else {
 706                  $( this )
 707                      .removeClass( "ui-state-default ui-state-active ui-state-disabled " +
 708                          "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" )
 709                      .removeAttr( "tabIndex" )
 710                      .removeAttr( "aria-live" )
 711                      .removeAttr( "aria-busy" )
 712                      .removeAttr( "aria-selected" )
 713                      .removeAttr( "aria-labelledby" )
 714                      .removeAttr( "aria-hidden" )
 715                      .removeAttr( "aria-expanded" )
 716                      .removeAttr( "role" );
 717              }
 718          });
 719  
 720          this.tabs.each(function() {
 721              var li = $( this ),
 722                  prev = li.data( "ui-tabs-aria-controls" );
 723              if ( prev ) {
 724                  li.attr( "aria-controls", prev );
 725              } else {
 726                  li.removeAttr( "aria-controls" );
 727              }
 728          });
 729  
 730          this.panels.show();
 731  
 732          if ( this.options.heightStyle !== "content" ) {
 733              this.panels.css( "height", "" );
 734          }
 735      },
 736  
 737      enable: function( index ) {
 738          var disabled = this.options.disabled;
 739          if ( disabled === false ) {
 740              return;
 741          }
 742  
 743          if ( index === undefined ) {
 744              disabled = false;
 745          } else {
 746              index = this._getIndex( index );
 747              if ( $.isArray( disabled ) ) {
 748                  disabled = $.map( disabled, function( num ) {
 749                      return num !== index ? num : null;
 750                  });
 751              } else {
 752                  disabled = $.map( this.tabs, function( li, num ) {
 753                      return num !== index ? num : null;
 754                  });
 755              }
 756          }
 757          this._setupDisabled( disabled );
 758      },
 759  
 760      disable: function( index ) {
 761          var disabled = this.options.disabled;
 762          if ( disabled === true ) {
 763              return;
 764          }
 765  
 766          if ( index === undefined ) {
 767              disabled = true;
 768          } else {
 769              index = this._getIndex( index );
 770              if ( $.inArray( index, disabled ) !== -1 ) {
 771                  return;
 772              }
 773              if ( $.isArray( disabled ) ) {
 774                  disabled = $.merge( [ index ], disabled ).sort();
 775              } else {
 776                  disabled = [ index ];
 777              }
 778          }
 779          this._setupDisabled( disabled );
 780      },
 781  
 782      load: function( index, event ) {
 783          index = this._getIndex( index );
 784          var that = this,
 785              tab = this.tabs.eq( index ),
 786              anchor = tab.find( ".ui-tabs-anchor" ),
 787              panel = this._getPanelForTab( tab ),
 788              eventData = {
 789                  tab: tab,
 790                  panel: panel
 791              };
 792  
 793          // not remote
 794          if ( isLocal( anchor[ 0 ] ) ) {
 795              return;
 796          }
 797  
 798          this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
 799  
 800          // support: jQuery <1.8
 801          // jQuery <1.8 returns false if the request is canceled in beforeSend,
 802          // but as of 1.8, $.ajax() always returns a jqXHR object.
 803          if ( this.xhr && this.xhr.statusText !== "canceled" ) {
 804              tab.addClass( "ui-tabs-loading" );
 805              panel.attr( "aria-busy", "true" );
 806  
 807              this.xhr
 808                  .success(function( response ) {
 809                      // support: jQuery <1.8
 810                      // http://bugs.jquery.com/ticket/11778
 811                      setTimeout(function() {
 812                          panel.html( response );
 813                          that._trigger( "load", event, eventData );
 814                      }, 1 );
 815                  })
 816                  .complete(function( jqXHR, status ) {
 817                      // support: jQuery <1.8
 818                      // http://bugs.jquery.com/ticket/11778
 819                      setTimeout(function() {
 820                          if ( status === "abort" ) {
 821                              that.panels.stop( false, true );
 822                          }
 823  
 824                          tab.removeClass( "ui-tabs-loading" );
 825                          panel.removeAttr( "aria-busy" );
 826  
 827                          if ( jqXHR === that.xhr ) {
 828                              delete that.xhr;
 829                          }
 830                      }, 1 );
 831                  });
 832          }
 833      },
 834  
 835      // TODO: Remove this function in 1.10 when ajaxOptions is removed
 836      _ajaxSettings: function( anchor, event, eventData ) {
 837          var that = this;
 838          return {
 839              url: anchor.attr( "href" ),
 840              beforeSend: function( jqXHR, settings ) {
 841                  return that._trigger( "beforeLoad", event,
 842                      $.extend( { jqXHR : jqXHR, ajaxSettings: settings }, eventData ) );
 843              }
 844          };
 845      },
 846  
 847      _getPanelForTab: function( tab ) {
 848          var id = $( tab ).attr( "aria-controls" );
 849          return this.element.find( this._sanitizeSelector( "#" + id ) );
 850      }
 851  });
 852  
 853  // DEPRECATED
 854  if ( $.uiBackCompat !== false ) {
 855  
 856      // helper method for a lot of the back compat extensions
 857      $.ui.tabs.prototype._ui = function( tab, panel ) {
 858          return {
 859              tab: tab,
 860              panel: panel,
 861              index: this.anchors.index( tab )
 862          };
 863      };
 864  
 865      // url method
 866      $.widget( "ui.tabs", $.ui.tabs, {
 867          url: function( index, url ) {
 868              this.anchors.eq( index ).attr( "href", url );
 869          }
 870      });
 871  
 872      // TODO: Remove _ajaxSettings() method when removing this extension
 873      // ajaxOptions and cache options
 874      $.widget( "ui.tabs", $.ui.tabs, {
 875          options: {
 876              ajaxOptions: null,
 877              cache: false
 878          },
 879  
 880          _create: function() {
 881              this._super();
 882  
 883              var that = this;
 884  
 885              this._on({ tabsbeforeload: function( event, ui ) {
 886                  // tab is already cached
 887                  if ( $.data( ui.tab[ 0 ], "cache.tabs" ) ) {
 888                      event.preventDefault();
 889                      return;
 890                  }
 891  
 892                  ui.jqXHR.success(function() {
 893                      if ( that.options.cache ) {
 894                          $.data( ui.tab[ 0 ], "cache.tabs", true );
 895                      }
 896                  });
 897              }});
 898          },
 899  
 900          _ajaxSettings: function( anchor, event, ui ) {
 901              var ajaxOptions = this.options.ajaxOptions;
 902              return $.extend( {}, ajaxOptions, {
 903                  error: function( xhr, status ) {
 904                      try {
 905                          // Passing index avoid a race condition when this method is
 906                          // called after the user has selected another tab.
 907                          // Pass the anchor that initiated this request allows
 908                          // loadError to manipulate the tab content panel via $(a.hash)
 909                          ajaxOptions.error(
 910                              xhr, status, ui.tab.closest( "li" ).index(), ui.tab[ 0 ] );
 911                      }
 912                      catch ( error ) {}
 913                  }
 914              }, this._superApply( arguments ) );
 915          },
 916  
 917          _setOption: function( key, value ) {
 918              // reset cache if switching from cached to not cached
 919              if ( key === "cache" && value === false ) {
 920                  this.anchors.removeData( "cache.tabs" );
 921              }
 922              this._super( key, value );
 923          },
 924  
 925          _destroy: function() {
 926              this.anchors.removeData( "cache.tabs" );
 927              this._super();
 928          },
 929  
 930          url: function( index ){
 931              this.anchors.eq( index ).removeData( "cache.tabs" );
 932              this._superApply( arguments );
 933          }
 934      });
 935  
 936      // abort method
 937      $.widget( "ui.tabs", $.ui.tabs, {
 938          abort: function() {
 939              if ( this.xhr ) {
 940                  this.xhr.abort();
 941              }
 942          }
 943      });
 944  
 945      // spinner
 946      $.widget( "ui.tabs", $.ui.tabs, {
 947          options: {
 948              spinner: "<em>Loading&#8230;</em>"
 949          },
 950          _create: function() {
 951              this._super();
 952              this._on({
 953                  tabsbeforeload: function( event, ui ) {
 954                      // Don't react to nested tabs or tabs that don't use a spinner
 955                      if ( event.target !== this.element[ 0 ] ||
 956                              !this.options.spinner ) {
 957                          return;
 958                      }
 959  
 960                      var span = ui.tab.find( "span" ),
 961                          html = span.html();
 962                      span.html( this.options.spinner );
 963                      ui.jqXHR.complete(function() {
 964                          span.html( html );
 965                      });
 966                  }
 967              });
 968          }
 969      });
 970  
 971      // enable/disable events
 972      $.widget( "ui.tabs", $.ui.tabs, {
 973          options: {
 974              enable: null,
 975              disable: null
 976          },
 977  
 978          enable: function( index ) {
 979              var options = this.options,
 980                  trigger;
 981  
 982              if ( index && options.disabled === true ||
 983                      ( $.isArray( options.disabled ) && $.inArray( index, options.disabled ) !== -1 ) ) {
 984                  trigger = true;
 985              }
 986  
 987              this._superApply( arguments );
 988  
 989              if ( trigger ) {
 990                  this._trigger( "enable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
 991              }
 992          },
 993  
 994          disable: function( index ) {
 995              var options = this.options,
 996                  trigger;
 997  
 998              if ( index && options.disabled === false ||
 999                      ( $.isArray( options.disabled ) && $.inArray( index, options.disabled ) === -1 ) ) {
1000                  trigger = true;
1001              }
1002  
1003              this._superApply( arguments );
1004  
1005              if ( trigger ) {
1006                  this._trigger( "disable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
1007              }
1008          }
1009      });
1010  
1011      // add/remove methods and events
1012      $.widget( "ui.tabs", $.ui.tabs, {
1013          options: {
1014              add: null,
1015              remove: null,
1016              tabTemplate: "<li><a href='#{href}'><span>#{label}</span></a></li>"
1017          },
1018  
1019          add: function( url, label, index ) {
1020              if ( index === undefined ) {
1021                  index = this.anchors.length;
1022              }
1023  
1024              var doInsertAfter, panel,
1025                  options = this.options,
1026                  li = $( options.tabTemplate
1027                      .replace( /#\{href\}/g, url )
1028                      .replace( /#\{label\}/g, label ) ),
1029                  id = !url.indexOf( "#" ) ?
1030                      url.replace( "#", "" ) :
1031                      this._tabId( li );
1032  
1033              li.addClass( "ui-state-default ui-corner-top" ).data( "ui-tabs-destroy", true );
1034              li.attr( "aria-controls", id );
1035  
1036              doInsertAfter = index >= this.tabs.length;
1037  
1038              // try to find an existing element before creating a new one
1039              panel = this.element.find( "#" + id );
1040              if ( !panel.length ) {
1041                  panel = this._createPanel( id );
1042                  if ( doInsertAfter ) {
1043                      if ( index > 0 ) {
1044                          panel.insertAfter( this.panels.eq( -1 ) );
1045                      } else {
1046                          panel.appendTo( this.element );
1047                      }
1048                  } else {
1049                      panel.insertBefore( this.panels[ index ] );
1050                  }
1051              }
1052              panel.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ).hide();
1053  
1054              if ( doInsertAfter ) {
1055                  li.appendTo( this.tablist );
1056              } else {
1057                  li.insertBefore( this.tabs[ index ] );
1058              }
1059  
1060              options.disabled = $.map( options.disabled, function( n ) {
1061                  return n >= index ? ++n : n;
1062              });
1063  
1064              this.refresh();
1065              if ( this.tabs.length === 1 && options.active === false ) {
1066                  this.option( "active", 0 );
1067              }
1068  
1069              this._trigger( "add", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
1070              return this;
1071          },
1072  
1073          remove: function( index ) {
1074              index = this._getIndex( index );
1075              var options = this.options,
1076                  tab = this.tabs.eq( index ).remove(),
1077                  panel = this._getPanelForTab( tab ).remove();
1078  
1079              // If selected tab was removed focus tab to the right or
1080              // in case the last tab was removed the tab to the left.
1081              // We check for more than 2 tabs, because if there are only 2,
1082              // then when we remove this tab, there will only be one tab left
1083              // so we don't need to detect which tab to activate.
1084              if ( tab.hasClass( "ui-tabs-active" ) && this.anchors.length > 2 ) {
1085                  this._activate( index + ( index + 1 < this.anchors.length ? 1 : -1 ) );
1086              }
1087  
1088              options.disabled = $.map(
1089                  $.grep( options.disabled, function( n ) {
1090                      return n !== index;
1091                  }),
1092                  function( n ) {
1093                      return n >= index ? --n : n;
1094                  });
1095  
1096              this.refresh();
1097  
1098              this._trigger( "remove", null, this._ui( tab.find( "a" )[ 0 ], panel[ 0 ] ) );
1099              return this;
1100          }
1101      });
1102  
1103      // length method
1104      $.widget( "ui.tabs", $.ui.tabs, {
1105          length: function() {
1106              return this.anchors.length;
1107          }
1108      });
1109  
1110      // panel ids (idPrefix option + title attribute)
1111      $.widget( "ui.tabs", $.ui.tabs, {
1112          options: {
1113              idPrefix: "ui-tabs-"
1114          },
1115  
1116          _tabId: function( tab ) {
1117              var a = tab.is( "li" ) ? tab.find( "a[href]" ) : tab;
1118              a = a[0];
1119              return $( a ).closest( "li" ).attr( "aria-controls" ) ||
1120                  a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF\-]/g, "" ) ||
1121                  this.options.idPrefix + getNextTabId();
1122          }
1123      });
1124  
1125      // _createPanel method
1126      $.widget( "ui.tabs", $.ui.tabs, {
1127          options: {
1128              panelTemplate: "<div></div>"
1129          },
1130  
1131          _createPanel: function( id ) {
1132              return $( this.options.panelTemplate )
1133                  .attr( "id", id )
1134                  .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
1135                  .data( "ui-tabs-destroy", true );
1136          }
1137      });
1138  
1139      // selected option
1140      $.widget( "ui.tabs", $.ui.tabs, {
1141          _create: function() {
1142              var options = this.options;
1143              if ( options.active === null && options.selected !== undefined ) {
1144                  options.active = options.selected === -1 ? false : options.selected;
1145              }
1146              this._super();
1147              options.selected = options.active;
1148              if ( options.selected === false ) {
1149                  options.selected = -1;
1150              }
1151          },
1152  
1153          _setOption: function( key, value ) {
1154              if ( key !== "selected" ) {
1155                  return this._super( key, value );
1156              }
1157  
1158              var options = this.options;
1159              this._super( "active", value === -1 ? false : value );
1160              options.selected = options.active;
1161              if ( options.selected === false ) {
1162                  options.selected = -1;
1163              }
1164          },
1165  
1166          _eventHandler: function() {
1167              this._superApply( arguments );
1168              this.options.selected = this.options.active;
1169              if ( this.options.selected === false ) {
1170                  this.options.selected = -1;
1171              }
1172          }
1173      });
1174  
1175      // show and select event
1176      $.widget( "ui.tabs", $.ui.tabs, {
1177          options: {
1178              show: null,
1179              select: null
1180          },
1181          _create: function() {
1182              this._super();
1183              if ( this.options.active !== false ) {
1184                  this._trigger( "show", null, this._ui(
1185                      this.active.find( ".ui-tabs-anchor" )[ 0 ],
1186                      this._getPanelForTab( this.active )[ 0 ] ) );
1187              }
1188          },
1189          _trigger: function( type, event, data ) {
1190              var tab, panel,
1191                  ret = this._superApply( arguments );
1192  
1193              if ( !ret ) {
1194                  return false;
1195              }
1196  
1197              if ( type === "beforeActivate" ) {
1198                  tab = data.newTab.length ? data.newTab : data.oldTab;
1199                  panel = data.newPanel.length ? data.newPanel : data.oldPanel;
1200                  ret = this._super( "select", event, {
1201                      tab: tab.find( ".ui-tabs-anchor" )[ 0],
1202                      panel: panel[ 0 ],
1203                      index: tab.closest( "li" ).index()
1204                  });
1205              } else if ( type === "activate" && data.newTab.length ) {
1206                  ret = this._super( "show", event, {
1207                      tab: data.newTab.find( ".ui-tabs-anchor" )[ 0 ],
1208                      panel: data.newPanel[ 0 ],
1209                      index: data.newTab.closest( "li" ).index()
1210                  });
1211              }
1212              return ret;
1213          }
1214      });
1215  
1216      // select method
1217      $.widget( "ui.tabs", $.ui.tabs, {
1218          select: function( index ) {
1219              index = this._getIndex( index );
1220              if ( index === -1 ) {
1221                  if ( this.options.collapsible && this.options.selected !== -1 ) {
1222                      index = this.options.selected;
1223                  } else {
1224                      return;
1225                  }
1226              }
1227              this.anchors.eq( index ).trigger( this.options.event + this.eventNamespace );
1228          }
1229      });
1230  
1231      // cookie option
1232      (function() {
1233  
1234      var listId = 0;
1235  
1236      $.widget( "ui.tabs", $.ui.tabs, {
1237          options: {
1238              cookie: null // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
1239          },
1240          _create: function() {
1241              var options = this.options,
1242                  active;
1243              if ( options.active == null && options.cookie ) {
1244                  active = parseInt( this._cookie(), 10 );
1245                  if ( active === -1 ) {
1246                      active = false;
1247                  }
1248                  options.active = active;
1249              }
1250              this._super();
1251          },
1252          _cookie: function( active ) {
1253              var cookie = [ this.cookie ||
1254                  ( this.cookie = this.options.cookie.name || "ui-tabs-" + (++listId) ) ];
1255              if ( arguments.length ) {
1256                  cookie.push( active === false ? -1 : active );
1257                  cookie.push( this.options.cookie );
1258              }
1259              return $.cookie.apply( null, cookie );
1260          },
1261          _refresh: function() {
1262              this._super();
1263              if ( this.options.cookie ) {
1264                  this._cookie( this.options.active, this.options.cookie );
1265              }
1266          },
1267          _eventHandler: function() {
1268              this._superApply( arguments );
1269              if ( this.options.cookie ) {
1270                  this._cookie( this.options.active, this.options.cookie );
1271              }
1272          },
1273          _destroy: function() {
1274              this._super();
1275              if ( this.options.cookie ) {
1276                  this._cookie( null, this.options.cookie );
1277              }
1278          }
1279      });
1280  
1281      })();
1282  
1283      // load event
1284      $.widget( "ui.tabs", $.ui.tabs, {
1285          _trigger: function( type, event, data ) {
1286              var _data = $.extend( {}, data );
1287              if ( type === "load" ) {
1288                  _data.panel = _data.panel[ 0 ];
1289                  _data.tab = _data.tab.find( ".ui-tabs-anchor" )[ 0 ];
1290              }
1291              return this._super( type, event, _data );
1292          }
1293      });
1294  
1295      // fx option
1296      // The new animation options (show, hide) conflict with the old show callback.
1297      // The old fx option wins over show/hide anyway (always favor back-compat).
1298      // If a user wants to use the new animation API, they must give up the old API.
1299      $.widget( "ui.tabs", $.ui.tabs, {
1300          options: {
1301              fx: null // e.g. { height: "toggle", opacity: "toggle", duration: 200 }
1302          },
1303  
1304          _getFx: function() {
1305              var hide, show,
1306                  fx = this.options.fx;
1307  
1308              if ( fx ) {
1309                  if ( $.isArray( fx ) ) {
1310                      hide = fx[ 0 ];
1311                      show = fx[ 1 ];
1312                  } else {
1313                      hide = show = fx;
1314                  }
1315              }
1316  
1317              return fx ? { show: show, hide: hide } : null;
1318          },
1319  
1320          _toggle: function( event, eventData ) {
1321              var that = this,
1322                  toShow = eventData.newPanel,
1323                  toHide = eventData.oldPanel,
1324                  fx = this._getFx();
1325  
1326              if ( !fx ) {
1327                  return this._super( event, eventData );
1328              }
1329  
1330              that.running = true;
1331  
1332  			function complete() {
1333                  that.running = false;
1334                  that._trigger( "activate", event, eventData );
1335              }
1336  
1337  			function show() {
1338                  eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
1339  
1340                  if ( toShow.length && fx.show ) {
1341                      toShow
1342                          .animate( fx.show, fx.show.duration, function() {
1343                              complete();
1344                          });
1345                  } else {
1346                      toShow.show();
1347                      complete();
1348                  }
1349              }
1350  
1351              // start out by hiding, then showing, then completing
1352              if ( toHide.length && fx.hide ) {
1353                  toHide.animate( fx.hide, fx.hide.duration, function() {
1354                      eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
1355                      show();
1356                  });
1357              } else {
1358                  eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
1359                  toHide.hide();
1360                  show();
1361              }
1362          }
1363      });
1364  }
1365  
1366  })( jQuery );


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1