[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

   1  /*!
   2   * jQuery UI Accordion 1.12.1
   3   * http://jqueryui.com
   4   *
   5   * Copyright jQuery Foundation and other contributors
   6   * Released under the MIT license.
   7   * http://jquery.org/license
   8   */
   9  
  10  //>>label: Accordion
  11  //>>group: Widgets
  12  // jscs:disable maximumLineLength
  13  //>>description: Displays collapsible content panels for presenting information in a limited amount of space.
  14  // jscs:enable maximumLineLength
  15  //>>docs: http://api.jqueryui.com/accordion/
  16  //>>demos: http://jqueryui.com/accordion/
  17  //>>css.structure: ../../themes/base/core.css
  18  //>>css.structure: ../../themes/base/accordion.css
  19  //>>css.theme: ../../themes/base/theme.css
  20  
  21  ( function( factory ) {
  22      if ( typeof define === "function" && define.amd ) {
  23  
  24          // AMD. Register as an anonymous module.
  25          define( [
  26              "jquery",
  27              "./core"
  28          ], factory );
  29      } else {
  30  
  31          // Browser globals
  32          factory( jQuery );
  33      }
  34  }( function( $ ) {
  35  
  36  return $.widget( "ui.accordion", {
  37      version: "1.12.1",
  38      options: {
  39          active: 0,
  40          animate: {},
  41          classes: {
  42              "ui-accordion-header": "ui-corner-top",
  43              "ui-accordion-header-collapsed": "ui-corner-all",
  44              "ui-accordion-content": "ui-corner-bottom"
  45          },
  46          collapsible: false,
  47          event: "click",
  48          header: "> li > :first-child, > :not(li):even",
  49          heightStyle: "auto",
  50          icons: {
  51              activeHeader: "ui-icon-triangle-1-s",
  52              header: "ui-icon-triangle-1-e"
  53          },
  54  
  55          // Callbacks
  56          activate: null,
  57          beforeActivate: null
  58      },
  59  
  60      hideProps: {
  61          borderTopWidth: "hide",
  62          borderBottomWidth: "hide",
  63          paddingTop: "hide",
  64          paddingBottom: "hide",
  65          height: "hide"
  66      },
  67  
  68      showProps: {
  69          borderTopWidth: "show",
  70          borderBottomWidth: "show",
  71          paddingTop: "show",
  72          paddingBottom: "show",
  73          height: "show"
  74      },
  75  
  76      _create: function() {
  77          var options = this.options;
  78  
  79          this.prevShow = this.prevHide = $();
  80          this._addClass( "ui-accordion", "ui-widget ui-helper-reset" );
  81          this.element.attr( "role", "tablist" );
  82  
  83          // Don't allow collapsible: false and active: false / null
  84          if ( !options.collapsible && ( options.active === false || options.active == null ) ) {
  85              options.active = 0;
  86          }
  87  
  88          this._processPanels();
  89  
  90          // handle negative values
  91          if ( options.active < 0 ) {
  92              options.active += this.headers.length;
  93          }
  94          this._refresh();
  95      },
  96  
  97      _getCreateEventData: function() {
  98          return {
  99              header: this.active,
 100              panel: !this.active.length ? $() : this.active.next()
 101          };
 102      },
 103  
 104      _createIcons: function() {
 105          var icon, children,
 106              icons = this.options.icons;
 107  
 108          if ( icons ) {
 109              icon = $( "<span>" );
 110              this._addClass( icon, "ui-accordion-header-icon", "ui-icon " + icons.header );
 111              icon.prependTo( this.headers );
 112              children = this.active.children( ".ui-accordion-header-icon" );
 113              this._removeClass( children, icons.header )
 114                  ._addClass( children, null, icons.activeHeader )
 115                  ._addClass( this.headers, "ui-accordion-icons" );
 116          }
 117      },
 118  
 119      _destroyIcons: function() {
 120          this._removeClass( this.headers, "ui-accordion-icons" );
 121          this.headers.children( ".ui-accordion-header-icon" ).remove();
 122      },
 123  
 124      _destroy: function() {
 125          var contents;
 126  
 127          // Clean up main element
 128          this.element.removeAttr( "role" );
 129  
 130          // Clean up headers
 131          this.headers
 132              .removeAttr( "role aria-expanded aria-selected aria-controls tabIndex" )
 133              .removeUniqueId();
 134  
 135          this._destroyIcons();
 136  
 137          // Clean up content panels
 138          contents = this.headers.next()
 139              .css( "display", "" )
 140              .removeAttr( "role aria-hidden aria-labelledby" )
 141              .removeUniqueId();
 142  
 143          if ( this.options.heightStyle !== "content" ) {
 144              contents.css( "height", "" );
 145          }
 146      },
 147  
 148      _setOption: function( key, value ) {
 149          if ( key === "active" ) {
 150  
 151              // _activate() will handle invalid values and update this.options
 152              this._activate( value );
 153              return;
 154          }
 155  
 156          if ( key === "event" ) {
 157              if ( this.options.event ) {
 158                  this._off( this.headers, this.options.event );
 159              }
 160              this._setupEvents( value );
 161          }
 162  
 163          this._super( key, value );
 164  
 165          // Setting collapsible: false while collapsed; open first panel
 166          if ( key === "collapsible" && !value && this.options.active === false ) {
 167              this._activate( 0 );
 168          }
 169  
 170          if ( key === "icons" ) {
 171              this._destroyIcons();
 172              if ( value ) {
 173                  this._createIcons();
 174              }
 175          }
 176      },
 177  
 178      _setOptionDisabled: function( value ) {
 179          this._super( value );
 180  
 181          this.element.attr( "aria-disabled", value );
 182  
 183          // Support: IE8 Only
 184          // #5332 / #6059 - opacity doesn't cascade to positioned elements in IE
 185          // so we need to add the disabled class to the headers and panels
 186          this._toggleClass( null, "ui-state-disabled", !!value );
 187          this._toggleClass( this.headers.add( this.headers.next() ), null, "ui-state-disabled",
 188              !!value );
 189      },
 190  
 191      _keydown: function( event ) {
 192          if ( event.altKey || event.ctrlKey ) {
 193              return;
 194          }
 195  
 196          var keyCode = $.ui.keyCode,
 197              length = this.headers.length,
 198              currentIndex = this.headers.index( event.target ),
 199              toFocus = false;
 200  
 201          switch ( event.keyCode ) {
 202          case keyCode.RIGHT:
 203          case keyCode.DOWN:
 204              toFocus = this.headers[ ( currentIndex + 1 ) % length ];
 205              break;
 206          case keyCode.LEFT:
 207          case keyCode.UP:
 208              toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
 209              break;
 210          case keyCode.SPACE:
 211          case keyCode.ENTER:
 212              this._eventHandler( event );
 213              break;
 214          case keyCode.HOME:
 215              toFocus = this.headers[ 0 ];
 216              break;
 217          case keyCode.END:
 218              toFocus = this.headers[ length - 1 ];
 219              break;
 220          }
 221  
 222          if ( toFocus ) {
 223              $( event.target ).attr( "tabIndex", -1 );
 224              $( toFocus ).attr( "tabIndex", 0 );
 225              $( toFocus ).trigger( "focus" );
 226              event.preventDefault();
 227          }
 228      },
 229  
 230      _panelKeyDown: function( event ) {
 231          if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
 232              $( event.currentTarget ).prev().trigger( "focus" );
 233          }
 234      },
 235  
 236      refresh: function() {
 237          var options = this.options;
 238          this._processPanels();
 239  
 240          // Was collapsed or no panel
 241          if ( ( options.active === false && options.collapsible === true ) ||
 242                  !this.headers.length ) {
 243              options.active = false;
 244              this.active = $();
 245  
 246          // active false only when collapsible is true
 247          } else if ( options.active === false ) {
 248              this._activate( 0 );
 249  
 250          // was active, but active panel is gone
 251          } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
 252  
 253              // all remaining panel are disabled
 254              if ( this.headers.length === this.headers.find( ".ui-state-disabled" ).length ) {
 255                  options.active = false;
 256                  this.active = $();
 257  
 258              // activate previous panel
 259              } else {
 260                  this._activate( Math.max( 0, options.active - 1 ) );
 261              }
 262  
 263          // was active, active panel still exists
 264          } else {
 265  
 266              // make sure active index is correct
 267              options.active = this.headers.index( this.active );
 268          }
 269  
 270          this._destroyIcons();
 271  
 272          this._refresh();
 273      },
 274  
 275      _processPanels: function() {
 276          var prevHeaders = this.headers,
 277              prevPanels = this.panels;
 278  
 279          this.headers = this.element.find( this.options.header );
 280          this._addClass( this.headers, "ui-accordion-header ui-accordion-header-collapsed",
 281              "ui-state-default" );
 282  
 283          this.panels = this.headers.next().filter( ":not(.ui-accordion-content-active)" ).hide();
 284          this._addClass( this.panels, "ui-accordion-content", "ui-helper-reset ui-widget-content" );
 285  
 286          // Avoid memory leaks (#10056)
 287          if ( prevPanels ) {
 288              this._off( prevHeaders.not( this.headers ) );
 289              this._off( prevPanels.not( this.panels ) );
 290          }
 291      },
 292  
 293      _refresh: function() {
 294          var maxHeight,
 295              options = this.options,
 296              heightStyle = options.heightStyle,
 297              parent = this.element.parent();
 298  
 299          this.active = this._findActive( options.active );
 300          this._addClass( this.active, "ui-accordion-header-active", "ui-state-active" )
 301              ._removeClass( this.active, "ui-accordion-header-collapsed" );
 302          this._addClass( this.active.next(), "ui-accordion-content-active" );
 303          this.active.next().show();
 304  
 305          this.headers
 306              .attr( "role", "tab" )
 307              .each( function() {
 308                  var header = $( this ),
 309                      headerId = header.uniqueId().attr( "id" ),
 310                      panel = header.next(),
 311                      panelId = panel.uniqueId().attr( "id" );
 312                  header.attr( "aria-controls", panelId );
 313                  panel.attr( "aria-labelledby", headerId );
 314              } )
 315              .next()
 316                  .attr( "role", "tabpanel" );
 317  
 318          this.headers
 319              .not( this.active )
 320                  .attr( {
 321                      "aria-selected": "false",
 322                      "aria-expanded": "false",
 323                      tabIndex: -1
 324                  } )
 325                  .next()
 326                      .attr( {
 327                          "aria-hidden": "true"
 328                      } )
 329                      .hide();
 330  
 331          // Make sure at least one header is in the tab order
 332          if ( !this.active.length ) {
 333              this.headers.eq( 0 ).attr( "tabIndex", 0 );
 334          } else {
 335              this.active.attr( {
 336                  "aria-selected": "true",
 337                  "aria-expanded": "true",
 338                  tabIndex: 0
 339              } )
 340                  .next()
 341                      .attr( {
 342                          "aria-hidden": "false"
 343                      } );
 344          }
 345  
 346          this._createIcons();
 347  
 348          this._setupEvents( options.event );
 349  
 350          if ( heightStyle === "fill" ) {
 351              maxHeight = parent.height();
 352              this.element.siblings( ":visible" ).each( function() {
 353                  var elem = $( this ),
 354                      position = elem.css( "position" );
 355  
 356                  if ( position === "absolute" || position === "fixed" ) {
 357                      return;
 358                  }
 359                  maxHeight -= elem.outerHeight( true );
 360              } );
 361  
 362              this.headers.each( function() {
 363                  maxHeight -= $( this ).outerHeight( true );
 364              } );
 365  
 366              this.headers.next()
 367                  .each( function() {
 368                      $( this ).height( Math.max( 0, maxHeight -
 369                          $( this ).innerHeight() + $( this ).height() ) );
 370                  } )
 371                  .css( "overflow", "auto" );
 372          } else if ( heightStyle === "auto" ) {
 373              maxHeight = 0;
 374              this.headers.next()
 375                  .each( function() {
 376                      var isVisible = $( this ).is( ":visible" );
 377                      if ( !isVisible ) {
 378                          $( this ).show();
 379                      }
 380                      maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
 381                      if ( !isVisible ) {
 382                          $( this ).hide();
 383                      }
 384                  } )
 385                  .height( maxHeight );
 386          }
 387      },
 388  
 389      _activate: function( index ) {
 390          var active = this._findActive( index )[ 0 ];
 391  
 392          // Trying to activate the already active panel
 393          if ( active === this.active[ 0 ] ) {
 394              return;
 395          }
 396  
 397          // Trying to collapse, simulate a click on the currently active header
 398          active = active || this.active[ 0 ];
 399  
 400          this._eventHandler( {
 401              target: active,
 402              currentTarget: active,
 403              preventDefault: $.noop
 404          } );
 405      },
 406  
 407      _findActive: function( selector ) {
 408          return typeof selector === "number" ? this.headers.eq( selector ) : $();
 409      },
 410  
 411      _setupEvents: function( event ) {
 412          var events = {
 413              keydown: "_keydown"
 414          };
 415          if ( event ) {
 416              $.each( event.split( " " ), function( index, eventName ) {
 417                  events[ eventName ] = "_eventHandler";
 418              } );
 419          }
 420  
 421          this._off( this.headers.add( this.headers.next() ) );
 422          this._on( this.headers, events );
 423          this._on( this.headers.next(), { keydown: "_panelKeyDown" } );
 424          this._hoverable( this.headers );
 425          this._focusable( this.headers );
 426      },
 427  
 428      _eventHandler: function( event ) {
 429          var activeChildren, clickedChildren,
 430              options = this.options,
 431              active = this.active,
 432              clicked = $( event.currentTarget ),
 433              clickedIsActive = clicked[ 0 ] === active[ 0 ],
 434              collapsing = clickedIsActive && options.collapsible,
 435              toShow = collapsing ? $() : clicked.next(),
 436              toHide = active.next(),
 437              eventData = {
 438                  oldHeader: active,
 439                  oldPanel: toHide,
 440                  newHeader: collapsing ? $() : clicked,
 441                  newPanel: toShow
 442              };
 443  
 444          event.preventDefault();
 445  
 446          if (
 447  
 448                  // click on active header, but not collapsible
 449                  ( clickedIsActive && !options.collapsible ) ||
 450  
 451                  // allow canceling activation
 452                  ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
 453              return;
 454          }
 455  
 456          options.active = collapsing ? false : this.headers.index( clicked );
 457  
 458          // When the call to ._toggle() comes after the class changes
 459          // it causes a very odd bug in IE 8 (see #6720)
 460          this.active = clickedIsActive ? $() : clicked;
 461          this._toggle( eventData );
 462  
 463          // Switch classes
 464          // corner classes on the previously active header stay after the animation
 465          this._removeClass( active, "ui-accordion-header-active", "ui-state-active" );
 466          if ( options.icons ) {
 467              activeChildren = active.children( ".ui-accordion-header-icon" );
 468              this._removeClass( activeChildren, null, options.icons.activeHeader )
 469                  ._addClass( activeChildren, null, options.icons.header );
 470          }
 471  
 472          if ( !clickedIsActive ) {
 473              this._removeClass( clicked, "ui-accordion-header-collapsed" )
 474                  ._addClass( clicked, "ui-accordion-header-active", "ui-state-active" );
 475              if ( options.icons ) {
 476                  clickedChildren = clicked.children( ".ui-accordion-header-icon" );
 477                  this._removeClass( clickedChildren, null, options.icons.header )
 478                      ._addClass( clickedChildren, null, options.icons.activeHeader );
 479              }
 480  
 481              this._addClass( clicked.next(), "ui-accordion-content-active" );
 482          }
 483      },
 484  
 485      _toggle: function( data ) {
 486          var toShow = data.newPanel,
 487              toHide = this.prevShow.length ? this.prevShow : data.oldPanel;
 488  
 489          // Handle activating a panel during the animation for another activation
 490          this.prevShow.add( this.prevHide ).stop( true, true );
 491          this.prevShow = toShow;
 492          this.prevHide = toHide;
 493  
 494          if ( this.options.animate ) {
 495              this._animate( toShow, toHide, data );
 496          } else {
 497              toHide.hide();
 498              toShow.show();
 499              this._toggleComplete( data );
 500          }
 501  
 502          toHide.attr( {
 503              "aria-hidden": "true"
 504          } );
 505          toHide.prev().attr( {
 506              "aria-selected": "false",
 507              "aria-expanded": "false"
 508          } );
 509  
 510          // if we're switching panels, remove the old header from the tab order
 511          // if we're opening from collapsed state, remove the previous header from the tab order
 512          // if we're collapsing, then keep the collapsing header in the tab order
 513          if ( toShow.length && toHide.length ) {
 514              toHide.prev().attr( {
 515                  "tabIndex": -1,
 516                  "aria-expanded": "false"
 517              } );
 518          } else if ( toShow.length ) {
 519              this.headers.filter( function() {
 520                  return parseInt( $( this ).attr( "tabIndex" ), 10 ) === 0;
 521              } )
 522                  .attr( "tabIndex", -1 );
 523          }
 524  
 525          toShow
 526              .attr( "aria-hidden", "false" )
 527              .prev()
 528                  .attr( {
 529                      "aria-selected": "true",
 530                      "aria-expanded": "true",
 531                      tabIndex: 0
 532                  } );
 533      },
 534  
 535      _animate: function( toShow, toHide, data ) {
 536          var total, easing, duration,
 537              that = this,
 538              adjust = 0,
 539              boxSizing = toShow.css( "box-sizing" ),
 540              down = toShow.length &&
 541                  ( !toHide.length || ( toShow.index() < toHide.index() ) ),
 542              animate = this.options.animate || {},
 543              options = down && animate.down || animate,
 544              complete = function() {
 545                  that._toggleComplete( data );
 546              };
 547  
 548          if ( typeof options === "number" ) {
 549              duration = options;
 550          }
 551          if ( typeof options === "string" ) {
 552              easing = options;
 553          }
 554  
 555          // fall back from options to animation in case of partial down settings
 556          easing = easing || options.easing || animate.easing;
 557          duration = duration || options.duration || animate.duration;
 558  
 559          if ( !toHide.length ) {
 560              return toShow.animate( this.showProps, duration, easing, complete );
 561          }
 562          if ( !toShow.length ) {
 563              return toHide.animate( this.hideProps, duration, easing, complete );
 564          }
 565  
 566          total = toShow.show().outerHeight();
 567          toHide.animate( this.hideProps, {
 568              duration: duration,
 569              easing: easing,
 570              step: function( now, fx ) {
 571                  fx.now = Math.round( now );
 572              }
 573          } );
 574          toShow
 575              .hide()
 576              .animate( this.showProps, {
 577                  duration: duration,
 578                  easing: easing,
 579                  complete: complete,
 580                  step: function( now, fx ) {
 581                      fx.now = Math.round( now );
 582                      if ( fx.prop !== "height" ) {
 583                          if ( boxSizing === "content-box" ) {
 584                              adjust += fx.now;
 585                          }
 586                      } else if ( that.options.heightStyle !== "content" ) {
 587                          fx.now = Math.round( total - toHide.outerHeight() - adjust );
 588                          adjust = 0;
 589                      }
 590                  }
 591              } );
 592      },
 593  
 594      _toggleComplete: function( data ) {
 595          var toHide = data.oldPanel,
 596              prev = toHide.prev();
 597  
 598          this._removeClass( toHide, "ui-accordion-content-active" );
 599          this._removeClass( prev, "ui-accordion-header-active" )
 600              ._addClass( prev, "ui-accordion-header-collapsed" );
 601  
 602          // Work around for rendering bug in IE (#5421)
 603          if ( toHide.length ) {
 604              toHide.parent()[ 0 ].className = toHide.parent()[ 0 ].className;
 605          }
 606          this._trigger( "activate", null, data );
 607      }
 608  } );
 609  
 610  } ) );


Generated: Mon Nov 30 01:00:03 2020 Cross-referenced by PHPXref 0.7.1