[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

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


Generated : Sat Nov 23 08:20:01 2024 Cross-referenced by PHPXref