[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

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


Generated: Mon Oct 14 01:00:02 2024 Cross-referenced by PHPXref 0.7.1