[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-admin/js/ -> common.js (source)

   1  /**
   2   * @output wp-admin/js/common.js
   3   */
   4  
   5  /* global setUserSetting, ajaxurl, alert, confirm, pagenow */
   6  /* global columns, screenMeta */
   7  
   8  /**
   9   *  Adds common WordPress functionality to the window.
  10   *
  11   *  @param {jQuery} $        jQuery object.
  12   *  @param {Object} window   The window object.
  13   *  @param {mixed} undefined Unused.
  14   */
  15  ( function( $, window, undefined ) {
  16      var $document = $( document ),
  17          $window = $( window ),
  18          $body = $( document.body ),
  19          __ = wp.i18n.__,
  20          sprintf = wp.i18n.sprintf;
  21  
  22  /**
  23   * Throws an error for a deprecated property.
  24   *
  25   * @since 5.5.1
  26   *
  27   * @param {string} propName    The property that was used.
  28   * @param {string} version     The version of WordPress that deprecated the property.
  29   * @param {string} replacement The property that should have been used.
  30   */
  31  function deprecatedProperty( propName, version, replacement ) {
  32      var message;
  33  
  34      if ( 'undefined' !== typeof replacement ) {
  35          message = sprintf(
  36              /* translators: 1: Deprecated property name, 2: Version number, 3: Alternative property name. */
  37              __( '%1$s is deprecated since version %2$s! Use %3$s instead.' ),
  38              propName,
  39              version,
  40              replacement
  41          );
  42      } else {
  43          message = sprintf(
  44              /* translators: 1: Deprecated property name, 2: Version number. */
  45              __( '%1$s is deprecated since version %2$s with no alternative available.' ),
  46              propName,
  47              version
  48          );
  49      }
  50  
  51      window.console.warn( message );
  52  }
  53  
  54  /**
  55   * Deprecate all properties on an object.
  56   *
  57   * @since 5.5.1
  58   * @since 5.6.0 Added the `version` parameter.
  59   *
  60   * @param {string} name       The name of the object, i.e. commonL10n.
  61   * @param {object} l10nObject The object to deprecate the properties on.
  62   * @param {string} version    The version of WordPress that deprecated the property.
  63   *
  64   * @return {object} The object with all its properties deprecated.
  65   */
  66  function deprecateL10nObject( name, l10nObject, version ) {
  67      var deprecatedObject = {};
  68  
  69      Object.keys( l10nObject ).forEach( function( key ) {
  70          var prop = l10nObject[ key ];
  71          var propName = name + '.' + key;
  72  
  73          if ( 'object' === typeof prop ) {
  74              Object.defineProperty( deprecatedObject, key, { get: function() {
  75                  deprecatedProperty( propName, version, prop.alternative );
  76                  return prop.func();
  77              } } );
  78          } else {
  79              Object.defineProperty( deprecatedObject, key, { get: function() {
  80                  deprecatedProperty( propName, version, 'wp.i18n' );
  81                  return prop;
  82              } } );
  83          }
  84      } );
  85  
  86      return deprecatedObject;
  87  }
  88  
  89  window.wp.deprecateL10nObject = deprecateL10nObject;
  90  
  91  /**
  92   * Removed in 5.5.0, needed for back-compatibility.
  93   *
  94   * @since 2.6.0
  95   * @deprecated 5.5.0
  96   */
  97  window.commonL10n = window.commonL10n || {
  98      warnDelete: '',
  99      dismiss: '',
 100      collapseMenu: '',
 101      expandMenu: ''
 102  };
 103  
 104  window.commonL10n = deprecateL10nObject( 'commonL10n', window.commonL10n, '5.5.0' );
 105  
 106  /**
 107   * Removed in 5.5.0, needed for back-compatibility.
 108   *
 109   * @since 3.3.0
 110   * @deprecated 5.5.0
 111   */
 112  window.wpPointerL10n = window.wpPointerL10n || {
 113      dismiss: ''
 114  };
 115  
 116  window.wpPointerL10n = deprecateL10nObject( 'wpPointerL10n', window.wpPointerL10n, '5.5.0' );
 117  
 118  /**
 119   * Removed in 5.5.0, needed for back-compatibility.
 120   *
 121   * @since 4.3.0
 122   * @deprecated 5.5.0
 123   */
 124  window.userProfileL10n = window.userProfileL10n || {
 125      warn: '',
 126      warnWeak: '',
 127      show: '',
 128      hide: '',
 129      cancel: '',
 130      ariaShow: '',
 131      ariaHide: ''
 132  };
 133  
 134  window.userProfileL10n = deprecateL10nObject( 'userProfileL10n', window.userProfileL10n, '5.5.0' );
 135  
 136  /**
 137   * Removed in 5.5.0, needed for back-compatibility.
 138   *
 139   * @since 4.9.6
 140   * @deprecated 5.5.0
 141   */
 142  window.privacyToolsL10n = window.privacyToolsL10n || {
 143      noDataFound: '',
 144      foundAndRemoved: '',
 145      noneRemoved: '',
 146      someNotRemoved: '',
 147      removalError: '',
 148      emailSent: '',
 149      noExportFile: '',
 150      exportError: ''
 151  };
 152  
 153  window.privacyToolsL10n = deprecateL10nObject( 'privacyToolsL10n', window.privacyToolsL10n, '5.5.0' );
 154  
 155  /**
 156   * Removed in 5.5.0, needed for back-compatibility.
 157   *
 158   * @since 3.6.0
 159   * @deprecated 5.5.0
 160   */
 161  window.authcheckL10n = {
 162      beforeunload: ''
 163  };
 164  
 165  window.authcheckL10n = window.authcheckL10n || deprecateL10nObject( 'authcheckL10n', window.authcheckL10n, '5.5.0' );
 166  
 167  /**
 168   * Removed in 5.5.0, needed for back-compatibility.
 169   *
 170   * @since 2.8.0
 171   * @deprecated 5.5.0
 172   */
 173  window.tagsl10n = {
 174      noPerm: '',
 175      broken: ''
 176  };
 177  
 178  window.tagsl10n = window.tagsl10n || deprecateL10nObject( 'tagsl10n', window.tagsl10n, '5.5.0' );
 179  
 180  /**
 181   * Removed in 5.5.0, needed for back-compatibility.
 182   *
 183   * @since 2.5.0
 184   * @deprecated 5.5.0
 185   */
 186  window.adminCommentsL10n = window.adminCommentsL10n || {
 187      hotkeys_highlight_first: {
 188          alternative: 'window.adminCommentsSettings.hotkeys_highlight_first',
 189          func: function() { return window.adminCommentsSettings.hotkeys_highlight_first; }
 190      },
 191      hotkeys_highlight_last: {
 192          alternative: 'window.adminCommentsSettings.hotkeys_highlight_last',
 193          func: function() { return window.adminCommentsSettings.hotkeys_highlight_last; }
 194      },
 195      replyApprove: '',
 196      reply: '',
 197      warnQuickEdit: '',
 198      warnCommentChanges: '',
 199      docTitleComments: '',
 200      docTitleCommentsCount: ''
 201  };
 202  
 203  window.adminCommentsL10n = deprecateL10nObject( 'adminCommentsL10n', window.adminCommentsL10n, '5.5.0' );
 204  
 205  /**
 206   * Removed in 5.5.0, needed for back-compatibility.
 207   *
 208   * @since 2.5.0
 209   * @deprecated 5.5.0
 210   */
 211  window.tagsSuggestL10n = window.tagsSuggestL10n || {
 212      tagDelimiter: '',
 213      removeTerm: '',
 214      termSelected: '',
 215      termAdded: '',
 216      termRemoved: ''
 217  };
 218  
 219  window.tagsSuggestL10n = deprecateL10nObject( 'tagsSuggestL10n', window.tagsSuggestL10n, '5.5.0' );
 220  
 221  /**
 222   * Removed in 5.5.0, needed for back-compatibility.
 223   *
 224   * @since 3.5.0
 225   * @deprecated 5.5.0
 226   */
 227  window.wpColorPickerL10n = window.wpColorPickerL10n || {
 228      clear: '',
 229      clearAriaLabel: '',
 230      defaultString: '',
 231      defaultAriaLabel: '',
 232      pick: '',
 233      defaultLabel: ''
 234  };
 235  
 236  window.wpColorPickerL10n = deprecateL10nObject( 'wpColorPickerL10n', window.wpColorPickerL10n, '5.5.0' );
 237  
 238  /**
 239   * Removed in 5.5.0, needed for back-compatibility.
 240   *
 241   * @since 2.7.0
 242   * @deprecated 5.5.0
 243   */
 244  window.attachMediaBoxL10n = window.attachMediaBoxL10n || {
 245      error: ''
 246  };
 247  
 248  window.attachMediaBoxL10n = deprecateL10nObject( 'attachMediaBoxL10n', window.attachMediaBoxL10n, '5.5.0' );
 249  
 250  /**
 251   * Removed in 5.5.0, needed for back-compatibility.
 252   *
 253   * @since 2.5.0
 254   * @deprecated 5.5.0
 255   */
 256  window.postL10n = window.postL10n || {
 257      ok: '',
 258      cancel: '',
 259      publishOn: '',
 260      publishOnFuture: '',
 261      publishOnPast: '',
 262      dateFormat: '',
 263      showcomm: '',
 264      endcomm: '',
 265      publish: '',
 266      schedule: '',
 267      update: '',
 268      savePending: '',
 269      saveDraft: '',
 270      'private': '',
 271      'public': '',
 272      publicSticky: '',
 273      password: '',
 274      privatelyPublished: '',
 275      published: '',
 276      saveAlert: '',
 277      savingText: '',
 278      permalinkSaved: ''
 279  };
 280  
 281  window.postL10n = deprecateL10nObject( 'postL10n', window.postL10n, '5.5.0' );
 282  
 283  /**
 284   * Removed in 5.5.0, needed for back-compatibility.
 285   *
 286   * @since 2.7.0
 287   * @deprecated 5.5.0
 288   */
 289  window.inlineEditL10n = window.inlineEditL10n || {
 290      error: '',
 291      ntdeltitle: '',
 292      notitle: '',
 293      comma: '',
 294      saved: ''
 295  };
 296  
 297  window.inlineEditL10n = deprecateL10nObject( 'inlineEditL10n', window.inlineEditL10n, '5.5.0' );
 298  
 299  /**
 300   * Removed in 5.5.0, needed for back-compatibility.
 301   *
 302   * @since 2.7.0
 303   * @deprecated 5.5.0
 304   */
 305  window.plugininstallL10n = window.plugininstallL10n || {
 306      plugin_information: '',
 307      plugin_modal_label: '',
 308      ays: ''
 309  };
 310  
 311  window.plugininstallL10n = deprecateL10nObject( 'plugininstallL10n', window.plugininstallL10n, '5.5.0' );
 312  
 313  /**
 314   * Removed in 5.5.0, needed for back-compatibility.
 315   *
 316   * @since 3.0.0
 317   * @deprecated 5.5.0
 318   */
 319  window.navMenuL10n = window.navMenuL10n || {
 320      noResultsFound: '',
 321      warnDeleteMenu: '',
 322      saveAlert: '',
 323      untitled: ''
 324  };
 325  
 326  window.navMenuL10n = deprecateL10nObject( 'navMenuL10n', window.navMenuL10n, '5.5.0' );
 327  
 328  /**
 329   * Removed in 5.5.0, needed for back-compatibility.
 330   *
 331   * @since 2.5.0
 332   * @deprecated 5.5.0
 333   */
 334  window.commentL10n = window.commentL10n || {
 335      submittedOn: '',
 336      dateFormat: ''
 337  };
 338  
 339  window.commentL10n = deprecateL10nObject( 'commentL10n', window.commentL10n, '5.5.0' );
 340  
 341  /**
 342   * Removed in 5.5.0, needed for back-compatibility.
 343   *
 344   * @since 2.9.0
 345   * @deprecated 5.5.0
 346   */
 347  window.setPostThumbnailL10n = window.setPostThumbnailL10n || {
 348      setThumbnail: '',
 349      saving: '',
 350      error: '',
 351      done: ''
 352  };
 353  
 354  window.setPostThumbnailL10n = deprecateL10nObject( 'setPostThumbnailL10n', window.setPostThumbnailL10n, '5.5.0' );
 355  
 356  /**
 357   * Removed in 6.5.0, needed for back-compatibility.
 358   *
 359   * @since 4.5.0
 360   * @deprecated 6.5.0
 361   */
 362  window.uiAutocompleteL10n = window.uiAutocompleteL10n || {
 363      noResults: '',
 364      oneResult: '',
 365      manyResults: '',
 366      itemSelected: ''
 367  };
 368  
 369  window.uiAutocompleteL10n = deprecateL10nObject( 'uiAutocompleteL10n', window.uiAutocompleteL10n, '6.5.0' );
 370  
 371  /**
 372   * Removed in 3.3.0, needed for back-compatibility.
 373   *
 374   * @since 2.7.0
 375   * @deprecated 3.3.0
 376   */
 377  window.adminMenu = {
 378      init : function() {},
 379      fold : function() {},
 380      restoreMenuState : function() {},
 381      toggle : function() {},
 382      favorites : function() {}
 383  };
 384  
 385  // Show/hide/save table columns.
 386  window.columns = {
 387  
 388      /**
 389       * Initializes the column toggles in the screen options.
 390       *
 391       * Binds an onClick event to the checkboxes to show or hide the table columns
 392       * based on their toggled state. And persists the toggled state.
 393       *
 394       * @since 2.7.0
 395       *
 396       * @return {void}
 397       */
 398      init : function() {
 399          var that = this;
 400          $('.hide-column-tog', '#adv-settings').on( 'click', function() {
 401              var $t = $(this), column = $t.val();
 402              if ( $t.prop('checked') )
 403                  that.checked(column);
 404              else
 405                  that.unchecked(column);
 406  
 407              columns.saveManageColumnsState();
 408          });
 409      },
 410  
 411      /**
 412       * Saves the toggled state for the columns.
 413       *
 414       * Saves whether the columns should be shown or hidden on a page.
 415       *
 416       * @since 3.0.0
 417       *
 418       * @return {void}
 419       */
 420      saveManageColumnsState : function() {
 421          var hidden = this.hidden();
 422          $.post(ajaxurl, {
 423              action: 'hidden-columns',
 424              hidden: hidden,
 425              screenoptionnonce: $('#screenoptionnonce').val(),
 426              page: pagenow
 427          });
 428      },
 429  
 430      /**
 431       * Makes a column visible and adjusts the column span for the table.
 432       *
 433       * @since 3.0.0
 434       * @param {string} column The column name.
 435       *
 436       * @return {void}
 437       */
 438      checked : function(column) {
 439          $('.column-' + column).removeClass( 'hidden' );
 440          this.colSpanChange(+1);
 441      },
 442  
 443      /**
 444       * Hides a column and adjusts the column span for the table.
 445       *
 446       * @since 3.0.0
 447       * @param {string} column The column name.
 448       *
 449       * @return {void}
 450       */
 451      unchecked : function(column) {
 452          $('.column-' + column).addClass( 'hidden' );
 453          this.colSpanChange(-1);
 454      },
 455  
 456      /**
 457       * Gets all hidden columns.
 458       *
 459       * @since 3.0.0
 460       *
 461       * @return {string} The hidden column names separated by a comma.
 462       */
 463      hidden : function() {
 464          return $( '.manage-column[id]' ).filter( '.hidden' ).map(function() {
 465              return this.id;
 466          }).get().join( ',' );
 467      },
 468  
 469      /**
 470       * Gets the checked column toggles from the screen options.
 471       *
 472       * @since 3.0.0
 473       *
 474       * @return {string} String containing the checked column names.
 475       */
 476      useCheckboxesForHidden : function() {
 477          this.hidden = function(){
 478              return $('.hide-column-tog').not(':checked').map(function() {
 479                  var id = this.id;
 480                  return id.substring( id, id.length - 5 );
 481              }).get().join(',');
 482          };
 483      },
 484  
 485      /**
 486       * Adjusts the column span for the table.
 487       *
 488       * @since 3.1.0
 489       *
 490       * @param {number} diff The modifier for the column span.
 491       */
 492      colSpanChange : function(diff) {
 493          var $t = $('table').find('.colspanchange'), n;
 494          if ( !$t.length )
 495              return;
 496          n = parseInt( $t.attr('colspan'), 10 ) + diff;
 497          $t.attr('colspan', n.toString());
 498      }
 499  };
 500  
 501  $( function() { columns.init(); } );
 502  
 503  /**
 504   * Validates that the required form fields are not empty.
 505   *
 506   * @since 2.9.0
 507   *
 508   * @param {jQuery} form The form to validate.
 509   *
 510   * @return {boolean} Returns true if all required fields are not an empty string.
 511   */
 512  window.validateForm = function( form ) {
 513      return !$( form )
 514          .find( '.form-required' )
 515          .filter( function() { return $( ':input:visible', this ).val() === ''; } )
 516          .addClass( 'form-invalid' )
 517          .find( ':input:visible' )
 518          .on( 'change', function() { $( this ).closest( '.form-invalid' ).removeClass( 'form-invalid' ); } )
 519          .length;
 520  };
 521  
 522  // Stub for doing better warnings.
 523  /**
 524   * Shows message pop-up notice or confirmation message.
 525   *
 526   * @since 2.7.0
 527   *
 528   * @type {{warn: showNotice.warn, note: showNotice.note}}
 529   *
 530   * @return {void}
 531   */
 532  window.showNotice = {
 533  
 534      /**
 535       * Shows a delete confirmation pop-up message.
 536       *
 537       * @since 2.7.0
 538       *
 539       * @return {boolean} Returns true if the message is confirmed.
 540       */
 541      warn : function() {
 542          if ( confirm( __( 'You are about to permanently delete these items from your site.\nThis action cannot be undone.\n\'Cancel\' to stop, \'OK\' to delete.' ) ) ) {
 543              return true;
 544          }
 545  
 546          return false;
 547      },
 548  
 549      /**
 550       * Shows an alert message.
 551       *
 552       * @since 2.7.0
 553       *
 554       * @param text The text to display in the message.
 555       */
 556      note : function(text) {
 557          alert(text);
 558      }
 559  };
 560  
 561  /**
 562   * Represents the functions for the meta screen options panel.
 563   *
 564   * @since 3.2.0
 565   *
 566   * @type {{element: null, toggles: null, page: null, init: screenMeta.init,
 567   *         toggleEvent: screenMeta.toggleEvent, open: screenMeta.open,
 568   *         close: screenMeta.close}}
 569   *
 570   * @return {void}
 571   */
 572  window.screenMeta = {
 573      element: null, // #screen-meta
 574      toggles: null, // .screen-meta-toggle
 575      page:    null, // #wpcontent
 576  
 577      /**
 578       * Initializes the screen meta options panel.
 579       *
 580       * @since 3.2.0
 581       *
 582       * @return {void}
 583       */
 584      init: function() {
 585          this.element = $('#screen-meta');
 586          this.toggles = $( '#screen-meta-links' ).find( '.show-settings' );
 587          this.page    = $('#wpcontent');
 588  
 589          this.toggles.on( 'click', this.toggleEvent );
 590      },
 591  
 592      /**
 593       * Toggles the screen meta options panel.
 594       *
 595       * @since 3.2.0
 596       *
 597       * @return {void}
 598       */
 599      toggleEvent: function() {
 600          var panel = $( '#' + $( this ).attr( 'aria-controls' ) );
 601  
 602          if ( !panel.length )
 603              return;
 604  
 605          if ( panel.is(':visible') )
 606              screenMeta.close( panel, $(this) );
 607          else
 608              screenMeta.open( panel, $(this) );
 609      },
 610  
 611      /**
 612       * Opens the screen meta options panel.
 613       *
 614       * @since 3.2.0
 615       *
 616       * @param {jQuery} panel  The screen meta options panel div.
 617       * @param {jQuery} button The toggle button.
 618       *
 619       * @return {void}
 620       */
 621      open: function( panel, button ) {
 622  
 623          $( '#screen-meta-links' ).find( '.screen-meta-toggle' ).not( button.parent() ).css( 'visibility', 'hidden' );
 624  
 625          panel.parent().show();
 626  
 627          /**
 628           * Sets the focus to the meta options panel and adds the necessary CSS classes.
 629           *
 630           * @since 3.2.0
 631           *
 632           * @return {void}
 633           */
 634          panel.slideDown( 'fast', function() {
 635              panel.removeClass( 'hidden' ).trigger( 'focus' );
 636              button.addClass( 'screen-meta-active' ).attr( 'aria-expanded', true );
 637          });
 638  
 639          $document.trigger( 'screen:options:open' );
 640      },
 641  
 642      /**
 643       * Closes the screen meta options panel.
 644       *
 645       * @since 3.2.0
 646       *
 647       * @param {jQuery} panel  The screen meta options panel div.
 648       * @param {jQuery} button The toggle button.
 649       *
 650       * @return {void}
 651       */
 652      close: function( panel, button ) {
 653          /**
 654           * Hides the screen meta options panel.
 655           *
 656           * @since 3.2.0
 657           *
 658           * @return {void}
 659           */
 660          panel.slideUp( 'fast', function() {
 661              button.removeClass( 'screen-meta-active' ).attr( 'aria-expanded', false );
 662              $('.screen-meta-toggle').css('visibility', '');
 663              panel.parent().hide();
 664              panel.addClass( 'hidden' );
 665          });
 666  
 667          $document.trigger( 'screen:options:close' );
 668      }
 669  };
 670  
 671  /**
 672   * Initializes the help tabs in the help panel.
 673   *
 674   * @param {Event} e The event object.
 675   *
 676   * @return {void}
 677   */
 678  $('.contextual-help-tabs').on( 'click', 'a', function(e) {
 679      var link = $(this),
 680          panel;
 681  
 682      e.preventDefault();
 683  
 684      // Don't do anything if the click is for the tab already showing.
 685      if ( link.is('.active a') )
 686          return false;
 687  
 688      // Links.
 689      $('.contextual-help-tabs .active').removeClass('active');
 690      link.parent('li').addClass('active');
 691  
 692      panel = $( link.attr('href') );
 693  
 694      // Panels.
 695      $('.help-tab-content').not( panel ).removeClass('active').hide();
 696      panel.addClass('active').show();
 697  });
 698  
 699  /**
 700   * Update custom permalink structure via buttons.
 701   */
 702  var permalinkStructureFocused = false,
 703      $permalinkStructure       = $( '#permalink_structure' ),
 704      $permalinkStructureInputs = $( '.permalink-structure input:radio' ),
 705      $permalinkCustomSelection = $( '#custom_selection' ),
 706      $availableStructureTags   = $( '.form-table.permalink-structure .available-structure-tags button' );
 707  
 708  // Change permalink structure input when selecting one of the common structures.
 709  $permalinkStructureInputs.on( 'change', function() {
 710      if ( 'custom' === this.value ) {
 711          return;
 712      }
 713  
 714      $permalinkStructure.val( this.value );
 715  
 716      // Update button states after selection.
 717      $availableStructureTags.each( function() {
 718          changeStructureTagButtonState( $( this ) );
 719      } );
 720  } );
 721  
 722  $permalinkStructure.on( 'click input', function() {
 723      $permalinkCustomSelection.prop( 'checked', true );
 724  } );
 725  
 726  // Check if the permalink structure input field has had focus at least once.
 727  $permalinkStructure.on( 'focus', function( event ) {
 728      permalinkStructureFocused = true;
 729      $( this ).off( event );
 730  } );
 731  
 732  /**
 733   * Enables or disables a structure tag button depending on its usage.
 734   *
 735   * If the structure is already used in the custom permalink structure,
 736   * it will be disabled.
 737   *
 738   * @param {Object} button Button jQuery object.
 739   */
 740  function changeStructureTagButtonState( button ) {
 741      if ( -1 !== $permalinkStructure.val().indexOf( button.text().trim() ) ) {
 742          button.attr( 'data-label', button.attr( 'aria-label' ) );
 743          button.attr( 'aria-label', button.attr( 'data-used' ) );
 744          button.attr( 'aria-pressed', true );
 745          button.addClass( 'active' );
 746      } else if ( button.attr( 'data-label' ) ) {
 747          button.attr( 'aria-label', button.attr( 'data-label' ) );
 748          button.attr( 'aria-pressed', false );
 749          button.removeClass( 'active' );
 750      }
 751  }
 752  
 753  // Check initial button state.
 754  $availableStructureTags.each( function() {
 755      changeStructureTagButtonState( $( this ) );
 756  } );
 757  
 758  // Observe permalink structure field and disable buttons of tags that are already present.
 759  $permalinkStructure.on( 'change', function() {
 760      $availableStructureTags.each( function() {
 761          changeStructureTagButtonState( $( this ) );
 762      } );
 763  } );
 764  
 765  $availableStructureTags.on( 'click', function() {
 766      var permalinkStructureValue = $permalinkStructure.val(),
 767          selectionStart          = $permalinkStructure[ 0 ].selectionStart,
 768          selectionEnd            = $permalinkStructure[ 0 ].selectionEnd,
 769          textToAppend            = $( this ).text().trim(),
 770          textToAnnounce,
 771          newSelectionStart;
 772  
 773      if ( $( this ).hasClass( 'active' ) ) {
 774          textToAnnounce = $( this ).attr( 'data-removed' );
 775      } else {
 776          textToAnnounce = $( this ).attr( 'data-added' );
 777      }
 778  
 779      // Remove structure tag if already part of the structure.
 780      if ( -1 !== permalinkStructureValue.indexOf( textToAppend ) ) {
 781          permalinkStructureValue = permalinkStructureValue.replace( textToAppend + '/', '' );
 782  
 783          $permalinkStructure.val( '/' === permalinkStructureValue ? '' : permalinkStructureValue );
 784  
 785          // Announce change to screen readers.
 786          $( '#custom_selection_updated' ).text( textToAnnounce );
 787  
 788          // Disable button.
 789          changeStructureTagButtonState( $( this ) );
 790  
 791          return;
 792      }
 793  
 794      // Input field never had focus, move selection to end of input.
 795      if ( ! permalinkStructureFocused && 0 === selectionStart && 0 === selectionEnd ) {
 796          selectionStart = selectionEnd = permalinkStructureValue.length;
 797      }
 798  
 799      $permalinkCustomSelection.prop( 'checked', true );
 800  
 801      // Prepend and append slashes if necessary.
 802      if ( '/' !== permalinkStructureValue.substr( 0, selectionStart ).substr( -1 ) ) {
 803          textToAppend = '/' + textToAppend;
 804      }
 805  
 806      if ( '/' !== permalinkStructureValue.substr( selectionEnd, 1 ) ) {
 807          textToAppend = textToAppend + '/';
 808      }
 809  
 810      // Insert structure tag at the specified position.
 811      $permalinkStructure.val( permalinkStructureValue.substr( 0, selectionStart ) + textToAppend + permalinkStructureValue.substr( selectionEnd ) );
 812  
 813      // Announce change to screen readers.
 814      $( '#custom_selection_updated' ).text( textToAnnounce );
 815  
 816      // Disable button.
 817      changeStructureTagButtonState( $( this ) );
 818  
 819      // If input had focus give it back with cursor right after appended text.
 820      if ( permalinkStructureFocused && $permalinkStructure[0].setSelectionRange ) {
 821          newSelectionStart = ( permalinkStructureValue.substr( 0, selectionStart ) + textToAppend ).length;
 822          $permalinkStructure[0].setSelectionRange( newSelectionStart, newSelectionStart );
 823          $permalinkStructure.trigger( 'focus' );
 824      }
 825  } );
 826  
 827  $( function() {
 828      var checks, first, last, checked, sliced, mobileEvent, transitionTimeout, focusedRowActions,
 829          lastClicked = false,
 830          pageInput = $('input.current-page'),
 831          currentPage = pageInput.val(),
 832          isIOS = /iPhone|iPad|iPod/.test( navigator.userAgent ),
 833          isAndroid = navigator.userAgent.indexOf( 'Android' ) !== -1,
 834          $adminMenuWrap = $( '#adminmenuwrap' ),
 835          $wpwrap = $( '#wpwrap' ),
 836          $adminmenu = $( '#adminmenu' ),
 837          $overlay = $( '#wp-responsive-overlay' ),
 838          $toolbar = $( '#wp-toolbar' ),
 839          $toolbarPopups = $toolbar.find( 'a[aria-haspopup="true"]' ),
 840          $sortables = $('.meta-box-sortables'),
 841          wpResponsiveActive = false,
 842          $adminbar = $( '#wpadminbar' ),
 843          lastScrollPosition = 0,
 844          pinnedMenuTop = false,
 845          pinnedMenuBottom = false,
 846          menuTop = 0,
 847          menuState,
 848          menuIsPinned = false,
 849          height = {
 850              window: $window.height(),
 851              wpwrap: $wpwrap.height(),
 852              adminbar: $adminbar.height(),
 853              menu: $adminMenuWrap.height()
 854          },
 855          $headerEnd = $( '.wp-header-end' );
 856  
 857      /**
 858       * Makes the fly-out submenu header clickable, when the menu is folded.
 859       *
 860       * @param {Event} e The event object.
 861       *
 862       * @return {void}
 863       */
 864      $adminmenu.on('click.wp-submenu-head', '.wp-submenu-head', function(e){
 865          $(e.target).parent().siblings('a').get(0).click();
 866      });
 867  
 868      /**
 869       * Collapses the admin menu.
 870       *
 871       * @return {void}
 872       */
 873      $( '#collapse-button' ).on( 'click.collapse-menu', function() {
 874          var viewportWidth = getViewportWidth() || 961;
 875  
 876          // Reset any compensation for submenus near the bottom of the screen.
 877          $('#adminmenu div.wp-submenu').css('margin-top', '');
 878  
 879          if ( viewportWidth <= 960 ) {
 880              if ( $body.hasClass('auto-fold') ) {
 881                  $body.removeClass('auto-fold').removeClass('folded');
 882                  setUserSetting('unfold', 1);
 883                  setUserSetting('mfold', 'o');
 884                  menuState = 'open';
 885              } else {
 886                  $body.addClass('auto-fold');
 887                  setUserSetting('unfold', 0);
 888                  menuState = 'folded';
 889              }
 890          } else {
 891              if ( $body.hasClass('folded') ) {
 892                  $body.removeClass('folded');
 893                  setUserSetting('mfold', 'o');
 894                  menuState = 'open';
 895              } else {
 896                  $body.addClass('folded');
 897                  setUserSetting('mfold', 'f');
 898                  menuState = 'folded';
 899              }
 900          }
 901  
 902          $document.trigger( 'wp-collapse-menu', { state: menuState } );
 903      });
 904  
 905      /**
 906       * Ensures an admin submenu is within the visual viewport.
 907       *
 908       * @since 4.1.0
 909       *
 910       * @param {jQuery} $menuItem The parent menu item containing the submenu.
 911       *
 912       * @return {void}
 913       */
 914  	function adjustSubmenu( $menuItem ) {
 915          var bottomOffset, pageHeight, adjustment, theFold, menutop, wintop, maxtop,
 916              $submenu = $menuItem.find( '.wp-submenu' );
 917  
 918          menutop = $menuItem.offset().top;
 919          wintop = $window.scrollTop();
 920          maxtop = menutop - wintop - 30; // max = make the top of the sub almost touch admin bar.
 921  
 922          bottomOffset = menutop + $submenu.height() + 1; // Bottom offset of the menu.
 923          pageHeight = $wpwrap.height();                  // Height of the entire page.
 924          adjustment = 60 + bottomOffset - pageHeight;
 925          theFold = $window.height() + wintop - 50;       // The fold.
 926  
 927          if ( theFold < ( bottomOffset - adjustment ) ) {
 928              adjustment = bottomOffset - theFold;
 929          }
 930  
 931          if ( adjustment > maxtop ) {
 932              adjustment = maxtop;
 933          }
 934  
 935          if ( adjustment > 1 && $('#wp-admin-bar-menu-toggle').is(':hidden') ) {
 936              $submenu.css( 'margin-top', '-' + adjustment + 'px' );
 937          } else {
 938              $submenu.css( 'margin-top', '' );
 939          }
 940      }
 941  
 942      if ( 'ontouchstart' in window || /IEMobile\/[1-9]/.test(navigator.userAgent) ) { // Touch screen device.
 943          // iOS Safari works with touchstart, the rest work with click.
 944          mobileEvent = isIOS ? 'touchstart' : 'click';
 945  
 946          /**
 947           * Closes any open submenus when touch/click is not on the menu.
 948           *
 949           * @param {Event} e The event object.
 950           *
 951           * @return {void}
 952           */
 953          $body.on( mobileEvent+'.wp-mobile-hover', function(e) {
 954              if ( $adminmenu.data('wp-responsive') ) {
 955                  return;
 956              }
 957  
 958              if ( ! $( e.target ).closest( '#adminmenu' ).length ) {
 959                  $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' );
 960              }
 961          });
 962  
 963          /**
 964           * Handles the opening or closing the submenu based on the mobile click|touch event.
 965           *
 966           * @param {Event} event The event object.
 967           *
 968           * @return {void}
 969           */
 970          $adminmenu.find( 'a.wp-has-submenu' ).on( mobileEvent + '.wp-mobile-hover', function( event ) {
 971              var $menuItem = $(this).parent();
 972  
 973              if ( $adminmenu.data( 'wp-responsive' ) ) {
 974                  return;
 975              }
 976  
 977              /*
 978               * Show the sub instead of following the link if:
 979               *     - the submenu is not open.
 980               *     - the submenu is not shown inline or the menu is not folded.
 981               */
 982              if ( ! $menuItem.hasClass( 'opensub' ) && ( ! $menuItem.hasClass( 'wp-menu-open' ) || $menuItem.width() < 40 ) ) {
 983                  event.preventDefault();
 984                  adjustSubmenu( $menuItem );
 985                  $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' );
 986                  $menuItem.addClass('opensub');
 987              }
 988          });
 989      }
 990  
 991      if ( ! isIOS && ! isAndroid ) {
 992          $adminmenu.find( 'li.wp-has-submenu' ).hoverIntent({
 993  
 994              /**
 995               * Opens the submenu when hovered over the menu item for desktops.
 996               *
 997               * @return {void}
 998               */
 999              over: function() {
1000                  var $menuItem = $( this ),
1001                      $submenu = $menuItem.find( '.wp-submenu' ),
1002                      top = parseInt( $submenu.css( 'top' ), 10 );
1003  
1004                  if ( isNaN( top ) || top > -5 ) { // The submenu is visible.
1005                      return;
1006                  }
1007  
1008                  if ( $adminmenu.data( 'wp-responsive' ) ) {
1009                      // The menu is in responsive mode, bail.
1010                      return;
1011                  }
1012  
1013                  adjustSubmenu( $menuItem );
1014                  $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' );
1015                  $menuItem.addClass( 'opensub' );
1016              },
1017  
1018              /**
1019               * Closes the submenu when no longer hovering the menu item.
1020               *
1021               * @return {void}
1022               */
1023              out: function(){
1024                  if ( $adminmenu.data( 'wp-responsive' ) ) {
1025                      // The menu is in responsive mode, bail.
1026                      return;
1027                  }
1028  
1029                  $( this ).removeClass( 'opensub' ).find( '.wp-submenu' ).css( 'margin-top', '' );
1030              },
1031              timeout: 200,
1032              sensitivity: 7,
1033              interval: 90
1034          });
1035  
1036          /**
1037           * Opens the submenu on when focused on the menu item.
1038           *
1039           * @param {Event} event The event object.
1040           *
1041           * @return {void}
1042           */
1043          $adminmenu.on( 'focus.adminmenu', '.wp-submenu a', function( event ) {
1044              if ( $adminmenu.data( 'wp-responsive' ) ) {
1045                  // The menu is in responsive mode, bail.
1046                  return;
1047              }
1048  
1049              $( event.target ).closest( 'li.menu-top' ).addClass( 'opensub' );
1050  
1051              /**
1052               * Closes the submenu on blur from the menu item.
1053               *
1054               * @param {Event} event The event object.
1055               *
1056               * @return {void}
1057               */
1058          }).on( 'blur.adminmenu', '.wp-submenu a', function( event ) {
1059              if ( $adminmenu.data( 'wp-responsive' ) ) {
1060                  return;
1061              }
1062  
1063              $( event.target ).closest( 'li.menu-top' ).removeClass( 'opensub' );
1064  
1065              /**
1066               * Adjusts the size for the submenu.
1067               *
1068               * @return {void}
1069               */
1070          }).find( 'li.wp-has-submenu.wp-not-current-submenu' ).on( 'focusin.adminmenu', function() {
1071              adjustSubmenu( $( this ) );
1072          });
1073      }
1074  
1075      /*
1076       * The `.below-h2` class is here just for backward compatibility with plugins
1077       * that are (incorrectly) using it. Do not use. Use `.inline` instead. See #34570.
1078       * If '.wp-header-end' is found, append the notices after it otherwise
1079       * after the first h1 or h2 heading found within the main content.
1080       */
1081      if ( ! $headerEnd.length ) {
1082          $headerEnd = $( '.wrap h1, .wrap h2' ).first();
1083      }
1084      $( 'div.updated, div.error, div.notice' ).not( '.inline, .below-h2' ).insertAfter( $headerEnd );
1085  
1086      /**
1087       * Makes notices dismissible.
1088       *
1089       * @since 4.4.0
1090       *
1091       * @return {void}
1092       */
1093  	function makeNoticesDismissible() {
1094          $( '.notice.is-dismissible' ).each( function() {
1095              var $el = $( this ),
1096                  $button = $( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' );
1097  
1098              if ( $el.find( '.notice-dismiss' ).length ) {
1099                  return;
1100              }
1101  
1102              // Ensure plain text.
1103              $button.find( '.screen-reader-text' ).text( __( 'Dismiss this notice.' ) );
1104              $button.on( 'click.wp-dismiss-notice', function( event ) {
1105                  event.preventDefault();
1106                  $el.fadeTo( 100, 0, function() {
1107                      $el.slideUp( 100, function() {
1108                          $el.remove();
1109                      });
1110                  });
1111              });
1112  
1113              $el.append( $button );
1114          });
1115      }
1116  
1117      $document.on( 'wp-updates-notice-added wp-plugin-install-error wp-plugin-update-error wp-plugin-delete-error wp-theme-install-error wp-theme-delete-error wp-notice-added', makeNoticesDismissible );
1118  
1119      // Init screen meta.
1120      screenMeta.init();
1121  
1122      /**
1123       * Checks a checkbox.
1124       *
1125       * This event needs to be delegated. Ticket #37973.
1126       *
1127       * @return {boolean} Returns whether a checkbox is checked or not.
1128       */
1129      $body.on( 'click', 'tbody > tr > .check-column :checkbox', function( event ) {
1130          // Shift click to select a range of checkboxes.
1131          if ( 'undefined' == event.shiftKey ) { return true; }
1132          if ( event.shiftKey ) {
1133              if ( !lastClicked ) { return true; }
1134              checks = $( lastClicked ).closest( 'form' ).find( ':checkbox' ).filter( ':visible:enabled' );
1135              first = checks.index( lastClicked );
1136              last = checks.index( this );
1137              checked = $(this).prop('checked');
1138              if ( 0 < first && 0 < last && first != last ) {
1139                  sliced = ( last > first ) ? checks.slice( first, last ) : checks.slice( last, first );
1140                  sliced.prop( 'checked', function() {
1141                      if ( $(this).closest('tr').is(':visible') )
1142                          return checked;
1143  
1144                      return false;
1145                  });
1146              }
1147          }
1148          lastClicked = this;
1149  
1150          // Toggle the "Select all" checkboxes depending if the other ones are all checked or not.
1151          var unchecked = $(this).closest('tbody').find('tr').find(':checkbox').filter(':visible:enabled').not(':checked');
1152  
1153          /**
1154           * Determines if all checkboxes are checked.
1155           *
1156           * @return {boolean} Returns true if there are no unchecked checkboxes.
1157           */
1158          $(this).closest('table').children('thead, tfoot').find(':checkbox').prop('checked', function() {
1159              return ( 0 === unchecked.length );
1160          });
1161  
1162          return true;
1163      });
1164  
1165      /**
1166       * Controls all the toggles on bulk toggle change.
1167       *
1168       * When the bulk checkbox is changed, all the checkboxes in the tables are changed accordingly.
1169       * When the shift-button is pressed while changing the bulk checkbox the checkboxes in the table are inverted.
1170       *
1171       * This event needs to be delegated. Ticket #37973.
1172       *
1173       * @param {Event} event The event object.
1174       *
1175       * @return {boolean}
1176       */
1177      $body.on( 'click.wp-toggle-checkboxes', 'thead .check-column :checkbox, tfoot .check-column :checkbox', function( event ) {
1178          var $this = $(this),
1179              $table = $this.closest( 'table' ),
1180              controlChecked = $this.prop('checked'),
1181              toggle = event.shiftKey || $this.data('wp-toggle');
1182  
1183          $table.children( 'tbody' ).filter(':visible')
1184              .children().children('.check-column').find(':checkbox')
1185              /**
1186               * Updates the checked state on the checkbox in the table.
1187               *
1188               * @return {boolean} True checks the checkbox, False unchecks the checkbox.
1189               */
1190              .prop('checked', function() {
1191                  if ( $(this).is(':hidden,:disabled') ) {
1192                      return false;
1193                  }
1194  
1195                  if ( toggle ) {
1196                      return ! $(this).prop( 'checked' );
1197                  } else if ( controlChecked ) {
1198                      return true;
1199                  }
1200  
1201                  return false;
1202              });
1203  
1204          $table.children('thead,  tfoot').filter(':visible')
1205              .children().children('.check-column').find(':checkbox')
1206  
1207              /**
1208               * Syncs the bulk checkboxes on the top and bottom of the table.
1209               *
1210               * @return {boolean} True checks the checkbox, False unchecks the checkbox.
1211               */
1212              .prop('checked', function() {
1213                  if ( toggle ) {
1214                      return false;
1215                  } else if ( controlChecked ) {
1216                      return true;
1217                  }
1218  
1219                  return false;
1220              });
1221      });
1222  
1223      /**
1224       * Marries a secondary control to its primary control.
1225       *
1226       * @param {jQuery} topSelector    The top selector element.
1227       * @param {jQuery} topSubmit      The top submit element.
1228       * @param {jQuery} bottomSelector The bottom selector element.
1229       * @param {jQuery} bottomSubmit   The bottom submit element.
1230       * @return {void}
1231       */
1232  	function marryControls( topSelector, topSubmit, bottomSelector, bottomSubmit ) {
1233          /**
1234           * Updates the primary selector when the secondary selector is changed.
1235           *
1236           * @since 5.7.0
1237           *
1238           * @return {void}
1239           */
1240  		function updateTopSelector() {
1241              topSelector.val($(this).val());
1242          }
1243          bottomSelector.on('change', updateTopSelector);
1244  
1245          /**
1246           * Updates the secondary selector when the primary selector is changed.
1247           *
1248           * @since 5.7.0
1249           *
1250           * @return {void}
1251           */
1252  		function updateBottomSelector() {
1253              bottomSelector.val($(this).val());
1254          }
1255          topSelector.on('change', updateBottomSelector);
1256  
1257          /**
1258           * Triggers the primary submit when then secondary submit is clicked.
1259           *
1260           * @since 5.7.0
1261           *
1262           * @return {void}
1263           */
1264  		function triggerSubmitClick(e) {
1265              e.preventDefault();
1266              e.stopPropagation();
1267  
1268              topSubmit.trigger('click');
1269          }
1270          bottomSubmit.on('click', triggerSubmitClick);
1271      }
1272  
1273      // Marry the secondary "Bulk actions" controls to the primary controls:
1274      marryControls( $('#bulk-action-selector-top'), $('#doaction'), $('#bulk-action-selector-bottom'), $('#doaction2') );
1275  
1276      // Marry the secondary "Change role to" controls to the primary controls:
1277      marryControls( $('#new_role'), $('#changeit'), $('#new_role2'), $('#changeit2') );
1278  
1279      var addAdminNotice = function( data ) {
1280          var $notice = $( data.selector ),
1281              $headerEnd = $( '.wp-header-end' ),
1282              type,
1283              dismissible,
1284              $adminNotice;
1285  
1286          delete data.selector;
1287  
1288          dismissible = ( data.dismissible && data.dismissible === true ) ? ' is-dismissible' : '';
1289          type        = ( data.type ) ? data.type : 'info';
1290  
1291          $adminNotice = '<div id="' + data.id + '" class="notice notice-' + data.type + dismissible + '"><p>' + data.message + '</p></div>';
1292  
1293          // Check if this admin notice already exists.
1294          if ( ! $notice.length ) {
1295              $notice = $( '#' + data.id );
1296          }
1297  
1298          if ( $notice.length ) {
1299              $notice.replaceWith( $adminNotice );
1300          } else if ( $headerEnd.length ) {
1301              $headerEnd.after( $adminNotice );
1302          } else {
1303              if ( 'customize' === pagenow ) {
1304                  $( '.customize-themes-notifications' ).append( $adminNotice );
1305              } else {
1306                  $( '.wrap' ).find( '> h1' ).after( $adminNotice );
1307              }
1308          }
1309  
1310          $document.trigger( 'wp-notice-added' );
1311      };
1312  
1313      $( '.bulkactions' ).parents( 'form' ).on( 'submit', function( event ) {
1314          var form = this,
1315              submitterName = event.originalEvent && event.originalEvent.submitter ? event.originalEvent.submitter.name : false;
1316  
1317          // Observe submissions from posts lists for 'bulk_action' or users lists for 'new_role'.
1318          var bulkFieldRelations = {
1319              'bulk_action' : 'action',
1320              'changeit' : 'new_role'
1321          };
1322          if ( ! Object.keys( bulkFieldRelations ).includes( submitterName ) ) {
1323              return;
1324          }
1325  
1326          var values = new FormData(form);
1327          var value = values.get( bulkFieldRelations[ submitterName ] ) || '-1';
1328  
1329          // Check that the action is not the default one.
1330          if ( value !== '-1' ) {
1331              // Check that at least one item is selected.
1332              var itemsSelected = form.querySelectorAll( '.wp-list-table tbody .check-column input[type="checkbox"]:checked' );
1333  
1334              if ( itemsSelected.length > 0 ) {
1335                  return;
1336              }
1337          }
1338          event.preventDefault();
1339          event.stopPropagation();
1340          $( 'html, body' ).animate( { scrollTop: 0 } );
1341  
1342          var errorMessage = __( 'Please select at least one item to perform this action on.' );
1343          addAdminNotice( {
1344              id: 'no-items-selected',
1345              type: 'error',
1346              message: errorMessage,
1347              dismissible: true,
1348          } );
1349  
1350          wp.a11y.speak( errorMessage );
1351      });
1352  
1353      /**
1354       * Shows row actions on focus of its parent container element or any other elements contained within.
1355       *
1356       * @return {void}
1357       */
1358      $( '#wpbody-content' ).on({
1359          focusin: function() {
1360              clearTimeout( transitionTimeout );
1361              focusedRowActions = $( this ).find( '.row-actions' );
1362              // transitionTimeout is necessary for Firefox, but Chrome won't remove the CSS class without a little help.
1363              $( '.row-actions' ).not( this ).removeClass( 'visible' );
1364              focusedRowActions.addClass( 'visible' );
1365          },
1366          focusout: function() {
1367              // Tabbing between post title and .row-actions links needs a brief pause, otherwise
1368              // the .row-actions div gets hidden in transit in some browsers (ahem, Firefox).
1369              transitionTimeout = setTimeout( function() {
1370                  focusedRowActions.removeClass( 'visible' );
1371              }, 30 );
1372          }
1373      }, '.table-view-list .has-row-actions' );
1374  
1375      // Toggle list table rows on small screens.
1376      $( 'tbody' ).on( 'click', '.toggle-row', function() {
1377          $( this ).closest( 'tr' ).toggleClass( 'is-expanded' );
1378      });
1379  
1380      $('#default-password-nag-no').on( 'click', function() {
1381          setUserSetting('default_password_nag', 'hide');
1382          $('div.default-password-nag').hide();
1383          return false;
1384      });
1385  
1386      /**
1387       * Handles tab keypresses in theme and plugin file editor textareas.
1388       *
1389       * @param {Event} e The event object.
1390       *
1391       * @return {void}
1392       */
1393      $('#newcontent').on('keydown.wpevent_InsertTab', function(e) {
1394          var el = e.target, selStart, selEnd, val, scroll, sel;
1395  
1396          // After pressing escape key (keyCode: 27), the tab key should tab out of the textarea.
1397          if ( e.keyCode == 27 ) {
1398              // When pressing Escape: Opera 12 and 27 blur form fields, IE 8 clears them.
1399              e.preventDefault();
1400              $(el).data('tab-out', true);
1401              return;
1402          }
1403  
1404          // Only listen for plain tab key (keyCode: 9) without any modifiers.
1405          if ( e.keyCode != 9 || e.ctrlKey || e.altKey || e.shiftKey )
1406              return;
1407  
1408          // After tabbing out, reset it so next time the tab key can be used again.
1409          if ( $(el).data('tab-out') ) {
1410              $(el).data('tab-out', false);
1411              return;
1412          }
1413  
1414          selStart = el.selectionStart;
1415          selEnd = el.selectionEnd;
1416          val = el.value;
1417  
1418          // If any text is selected, replace the selection with a tab character.
1419          if ( document.selection ) {
1420              el.focus();
1421              sel = document.selection.createRange();
1422              sel.text = '\t';
1423          } else if ( selStart >= 0 ) {
1424              scroll = this.scrollTop;
1425              el.value = val.substring(0, selStart).concat('\t', val.substring(selEnd) );
1426              el.selectionStart = el.selectionEnd = selStart + 1;
1427              this.scrollTop = scroll;
1428          }
1429  
1430          // Cancel the regular tab functionality, to prevent losing focus of the textarea.
1431          if ( e.stopPropagation )
1432              e.stopPropagation();
1433          if ( e.preventDefault )
1434              e.preventDefault();
1435      });
1436  
1437      // Reset page number variable for new filters/searches but not for bulk actions. See #17685.
1438      if ( pageInput.length ) {
1439  
1440          /**
1441           * Handles pagination variable when filtering the list table.
1442           *
1443           * Set the pagination argument to the first page when the post-filter form is submitted.
1444           * This happens when pressing the 'filter' button on the list table page.
1445           *
1446           * The pagination argument should not be touched when the bulk action dropdowns are set to do anything.
1447           *
1448           * The form closest to the pageInput is the post-filter form.
1449           *
1450           * @return {void}
1451           */
1452          pageInput.closest('form').on( 'submit', function() {
1453              /*
1454               * action = bulk action dropdown at the top of the table
1455               */
1456              if ( $('select[name="action"]').val() == -1 && pageInput.val() == currentPage )
1457                  pageInput.val('1');
1458          });
1459      }
1460  
1461      /**
1462       * Resets the bulk actions when the search button is clicked.
1463       *
1464       * @return {void}
1465       */
1466      $('.search-box input[type="search"], .search-box input[type="submit"]').on( 'mousedown', function () {
1467          $('select[name^="action"]').val('-1');
1468      });
1469  
1470      /**
1471       * Scrolls into view when focus.scroll-into-view is triggered.
1472       *
1473       * @param {Event} e The event object.
1474       *
1475       * @return {void}
1476        */
1477      $('#contextual-help-link, #show-settings-link').on( 'focus.scroll-into-view', function(e){
1478          if ( e.target.scrollIntoViewIfNeeded )
1479              e.target.scrollIntoViewIfNeeded(false);
1480      });
1481  
1482      /**
1483       * Disables the submit upload buttons when no data is entered.
1484       *
1485       * @return {void}
1486       */
1487      (function(){
1488          var button, input, form = $('form.wp-upload-form');
1489  
1490          // Exit when no upload form is found.
1491          if ( ! form.length )
1492              return;
1493  
1494          button = form.find('input[type="submit"]');
1495          input = form.find('input[type="file"]');
1496  
1497          /**
1498           * Determines if any data is entered in any file upload input.
1499           *
1500           * @since 3.5.0
1501           *
1502           * @return {void}
1503           */
1504  		function toggleUploadButton() {
1505              // When no inputs have a value, disable the upload buttons.
1506              button.prop('disabled', '' === input.map( function() {
1507                  return $(this).val();
1508              }).get().join(''));
1509          }
1510  
1511          // Update the status initially.
1512          toggleUploadButton();
1513          // Update the status when any file input changes.
1514          input.on('change', toggleUploadButton);
1515      })();
1516  
1517      /**
1518       * Pins the menu while distraction-free writing is enabled.
1519       *
1520       * @param {Event} event Event data.
1521       *
1522       * @since 4.1.0
1523       *
1524       * @return {void}
1525       */
1526  	function pinMenu( event ) {
1527          var windowPos = $window.scrollTop(),
1528              resizing = ! event || event.type !== 'scroll';
1529  
1530          if ( isIOS || $adminmenu.data( 'wp-responsive' ) ) {
1531              return;
1532          }
1533  
1534          /*
1535           * When the menu is higher than the window and smaller than the entire page.
1536           * It should be adjusted to be able to see the entire menu.
1537           *
1538           * Otherwise it can be accessed normally.
1539           */
1540          if ( height.menu + height.adminbar < height.window ||
1541              height.menu + height.adminbar + 20 > height.wpwrap ) {
1542              unpinMenu();
1543              return;
1544          }
1545  
1546          menuIsPinned = true;
1547  
1548          // If the menu is higher than the window, compensate on scroll.
1549          if ( height.menu + height.adminbar > height.window ) {
1550              // Check for overscrolling, this happens when swiping up at the top of the document in modern browsers.
1551              if ( windowPos < 0 ) {
1552                  // Stick the menu to the top.
1553                  if ( ! pinnedMenuTop ) {
1554                      pinnedMenuTop = true;
1555                      pinnedMenuBottom = false;
1556  
1557                      $adminMenuWrap.css({
1558                          position: 'fixed',
1559                          top: '',
1560                          bottom: ''
1561                      });
1562                  }
1563  
1564                  return;
1565              } else if ( windowPos + height.window > $document.height() - 1 ) {
1566                  // When overscrolling at the bottom, stick the menu to the bottom.
1567                  if ( ! pinnedMenuBottom ) {
1568                      pinnedMenuBottom = true;
1569                      pinnedMenuTop = false;
1570  
1571                      $adminMenuWrap.css({
1572                          position: 'fixed',
1573                          top: '',
1574                          bottom: 0
1575                      });
1576                  }
1577  
1578                  return;
1579              }
1580  
1581              if ( windowPos > lastScrollPosition ) {
1582                  // When a down scroll has been detected.
1583  
1584                  // If it was pinned to the top, unpin and calculate relative scroll.
1585                  if ( pinnedMenuTop ) {
1586                      pinnedMenuTop = false;
1587                      // Calculate new offset position.
1588                      menuTop = $adminMenuWrap.offset().top - height.adminbar - ( windowPos - lastScrollPosition );
1589  
1590                      if ( menuTop + height.menu + height.adminbar < windowPos + height.window ) {
1591                          menuTop = windowPos + height.window - height.menu - height.adminbar;
1592                      }
1593  
1594                      $adminMenuWrap.css({
1595                          position: 'absolute',
1596                          top: menuTop,
1597                          bottom: ''
1598                      });
1599                  } else if ( ! pinnedMenuBottom && $adminMenuWrap.offset().top + height.menu < windowPos + height.window ) {
1600                      // Pin it to the bottom.
1601                      pinnedMenuBottom = true;
1602  
1603                      $adminMenuWrap.css({
1604                          position: 'fixed',
1605                          top: '',
1606                          bottom: 0
1607                      });
1608                  }
1609              } else if ( windowPos < lastScrollPosition ) {
1610                  // When a scroll up is detected.
1611  
1612                  // If it was pinned to the bottom, unpin and calculate relative scroll.
1613                  if ( pinnedMenuBottom ) {
1614                      pinnedMenuBottom = false;
1615  
1616                      // Calculate new offset position.
1617                      menuTop = $adminMenuWrap.offset().top - height.adminbar + ( lastScrollPosition - windowPos );
1618  
1619                      if ( menuTop + height.menu > windowPos + height.window ) {
1620                          menuTop = windowPos;
1621                      }
1622  
1623                      $adminMenuWrap.css({
1624                          position: 'absolute',
1625                          top: menuTop,
1626                          bottom: ''
1627                      });
1628                  } else if ( ! pinnedMenuTop && $adminMenuWrap.offset().top >= windowPos + height.adminbar ) {
1629  
1630                      // Pin it to the top.
1631                      pinnedMenuTop = true;
1632  
1633                      $adminMenuWrap.css({
1634                          position: 'fixed',
1635                          top: '',
1636                          bottom: ''
1637                      });
1638                  }
1639              } else if ( resizing ) {
1640                  // Window is being resized.
1641  
1642                  pinnedMenuTop = pinnedMenuBottom = false;
1643  
1644                  // Calculate the new offset.
1645                  menuTop = windowPos + height.window - height.menu - height.adminbar - 1;
1646  
1647                  if ( menuTop > 0 ) {
1648                      $adminMenuWrap.css({
1649                          position: 'absolute',
1650                          top: menuTop,
1651                          bottom: ''
1652                      });
1653                  } else {
1654                      unpinMenu();
1655                  }
1656              }
1657          }
1658  
1659          lastScrollPosition = windowPos;
1660      }
1661  
1662      /**
1663       * Determines the height of certain elements.
1664       *
1665       * @since 4.1.0
1666       *
1667       * @return {void}
1668       */
1669  	function resetHeights() {
1670          height = {
1671              window: $window.height(),
1672              wpwrap: $wpwrap.height(),
1673              adminbar: $adminbar.height(),
1674              menu: $adminMenuWrap.height()
1675          };
1676      }
1677  
1678      /**
1679       * Unpins the menu.
1680       *
1681       * @since 4.1.0
1682       *
1683       * @return {void}
1684       */
1685  	function unpinMenu() {
1686          if ( isIOS || ! menuIsPinned ) {
1687              return;
1688          }
1689  
1690          pinnedMenuTop = pinnedMenuBottom = menuIsPinned = false;
1691          $adminMenuWrap.css({
1692              position: '',
1693              top: '',
1694              bottom: ''
1695          });
1696      }
1697  
1698      /**
1699       * Pins and unpins the menu when applicable.
1700       *
1701       * @since 4.1.0
1702       *
1703       * @return {void}
1704       */
1705  	function setPinMenu() {
1706          resetHeights();
1707  
1708          if ( $adminmenu.data('wp-responsive') ) {
1709              $body.removeClass( 'sticky-menu' );
1710              unpinMenu();
1711          } else if ( height.menu + height.adminbar > height.window ) {
1712              pinMenu();
1713              $body.removeClass( 'sticky-menu' );
1714          } else {
1715              $body.addClass( 'sticky-menu' );
1716              unpinMenu();
1717          }
1718      }
1719  
1720      if ( ! isIOS ) {
1721          $window.on( 'scroll.pin-menu', pinMenu );
1722          $document.on( 'tinymce-editor-init.pin-menu', function( event, editor ) {
1723              editor.on( 'wp-autoresize', resetHeights );
1724          });
1725      }
1726  
1727      /**
1728       * Changes the sortables and responsiveness of metaboxes.
1729       *
1730       * @since 3.8.0
1731       *
1732       * @return {void}
1733       */
1734      window.wpResponsive = {
1735  
1736          /**
1737           * Initializes the wpResponsive object.
1738           *
1739           * @since 3.8.0
1740           *
1741           * @return {void}
1742           */
1743          init: function() {
1744              var self = this;
1745  
1746              this.maybeDisableSortables = this.maybeDisableSortables.bind( this );
1747  
1748              // Modify functionality based on custom activate/deactivate event.
1749              $document.on( 'wp-responsive-activate.wp-responsive', function() {
1750                  self.activate();
1751                  self.toggleAriaHasPopup( 'add' );
1752              }).on( 'wp-responsive-deactivate.wp-responsive', function() {
1753                  self.deactivate();
1754                  self.toggleAriaHasPopup( 'remove' );
1755              });
1756  
1757              $( '#wp-admin-bar-menu-toggle a' ).attr( 'aria-expanded', 'false' );
1758  
1759              // Toggle sidebar when toggle is clicked.
1760              $( '#wp-admin-bar-menu-toggle' ).on( 'click.wp-responsive', function( event ) {
1761                  event.preventDefault();
1762  
1763                  // Close any open toolbar submenus.
1764                  $adminbar.find( '.hover' ).removeClass( 'hover' );
1765  
1766                  $wpwrap.toggleClass( 'wp-responsive-open' );
1767                  if ( $wpwrap.hasClass( 'wp-responsive-open' ) ) {
1768                      $(this).find('a').attr( 'aria-expanded', 'true' );
1769                      $( '#adminmenu a:first' ).trigger( 'focus' );
1770                  } else {
1771                      $(this).find('a').attr( 'aria-expanded', 'false' );
1772                  }
1773              } );
1774  
1775              // Close sidebar when target moves outside of toggle and sidebar.
1776              $( document ).on( 'click', function( event ) {
1777                  if ( ! $wpwrap.hasClass( 'wp-responsive-open' ) || ! document.hasFocus() ) {
1778                      return;
1779                  }
1780  
1781                  var focusIsInToggle  = $.contains( $( '#wp-admin-bar-menu-toggle' )[0], event.target );
1782                  var focusIsInSidebar = $.contains( $( '#adminmenuwrap' )[0], event.target );
1783  
1784                  if ( ! focusIsInToggle && ! focusIsInSidebar ) {
1785                      $( '#wp-admin-bar-menu-toggle' ).trigger( 'click.wp-responsive' );
1786                  }
1787              } );
1788  
1789              // Close sidebar when a keypress completes outside of toggle and sidebar.
1790              $( document ).on( 'keyup', function( event ) {
1791                  var toggleButton   = $( '#wp-admin-bar-menu-toggle' )[0];
1792                  if ( ! $wpwrap.hasClass( 'wp-responsive-open' ) ) {
1793                      return;
1794                  }
1795                  if ( 27 === event.keyCode ) {
1796                      $( toggleButton ).trigger( 'click.wp-responsive' );
1797                      $( toggleButton ).find( 'a' ).trigger( 'focus' );
1798                  } else {
1799                      if ( 9 === event.keyCode ) {
1800                          var sidebar        = $( '#adminmenuwrap' )[0];
1801                          var focusedElement = event.relatedTarget || document.activeElement;
1802                          // A brief delay is required to allow focus to switch to another element.
1803                          setTimeout( function() {
1804                              var focusIsInToggle  = $.contains( toggleButton, focusedElement );
1805                              var focusIsInSidebar = $.contains( sidebar, focusedElement );
1806  
1807                              if ( ! focusIsInToggle && ! focusIsInSidebar ) {
1808                                  $( toggleButton ).trigger( 'click.wp-responsive' );
1809                              }
1810                          }, 10 );
1811                      }
1812                  }
1813              });
1814  
1815              // Add menu events.
1816              $adminmenu.on( 'click.wp-responsive', 'li.wp-has-submenu > a', function( event ) {
1817                  if ( ! $adminmenu.data('wp-responsive') ) {
1818                      return;
1819                  }
1820                  let state = ( 'false' === $( this ).attr( 'aria-expanded' ) ) ? 'true' : 'false';
1821                  $( this ).parent( 'li' ).toggleClass( 'selected' );
1822                  $( this ).attr( 'aria-expanded', state );
1823                  $( this ).trigger( 'focus' );
1824                  event.preventDefault();
1825              });
1826  
1827              self.trigger();
1828              $document.on( 'wp-window-resized.wp-responsive', this.trigger.bind( this ) );
1829  
1830              // This needs to run later as UI Sortable may be initialized when the document is ready.
1831              $window.on( 'load.wp-responsive', this.maybeDisableSortables );
1832              $document.on( 'postbox-toggled', this.maybeDisableSortables );
1833  
1834              // When the screen columns are changed, potentially disable sortables.
1835              $( '#screen-options-wrap input' ).on( 'click', this.maybeDisableSortables );
1836          },
1837  
1838          /**
1839           * Disable sortables if there is only one metabox, or the screen is in one column mode. Otherwise, enable sortables.
1840           *
1841           * @since 5.3.0
1842           *
1843           * @return {void}
1844           */
1845          maybeDisableSortables: function() {
1846              var width = navigator.userAgent.indexOf('AppleWebKit/') > -1 ? $window.width() : window.innerWidth;
1847  
1848              if (
1849                  ( width <= 782 ) ||
1850                  ( 1 >= $sortables.find( '.ui-sortable-handle:visible' ).length && jQuery( '.columns-prefs-1 input' ).prop( 'checked' ) )
1851              ) {
1852                  this.disableSortables();
1853              } else {
1854                  this.enableSortables();
1855              }
1856          },
1857  
1858          /**
1859           * Changes properties of body and admin menu.
1860           *
1861           * Pins and unpins the menu and adds the auto-fold class to the body.
1862           * Makes the admin menu responsive and disables the metabox sortables.
1863           *
1864           * @since 3.8.0
1865           *
1866           * @return {void}
1867           */
1868          activate: function() {
1869              setPinMenu();
1870  
1871              if ( ! $body.hasClass( 'auto-fold' ) ) {
1872                  $body.addClass( 'auto-fold' );
1873              }
1874  
1875              $adminmenu.data( 'wp-responsive', 1 );
1876              this.disableSortables();
1877          },
1878  
1879          /**
1880           * Changes properties of admin menu and enables metabox sortables.
1881           *
1882           * Pin and unpin the menu.
1883           * Removes the responsiveness of the admin menu and enables the metabox sortables.
1884           *
1885           * @since 3.8.0
1886           *
1887           * @return {void}
1888           */
1889          deactivate: function() {
1890              setPinMenu();
1891              $adminmenu.removeData('wp-responsive');
1892  
1893              this.maybeDisableSortables();
1894          },
1895  
1896          /**
1897           * Toggles the aria-haspopup attribute for the responsive admin menu.
1898           *
1899           * The aria-haspopup attribute is only necessary for the responsive menu.
1900           * See ticket https://core.trac.wordpress.org/ticket/43095
1901           *
1902           * @since 6.6.0
1903           *
1904           * @param {string} action Whether to add or remove the aria-haspopup attribute.
1905           *
1906           * @return {void}
1907           */
1908          toggleAriaHasPopup: function( action ) {
1909              var elements = $adminmenu.find( '[data-ariahaspopup]' );
1910  
1911              if ( action === 'add' ) {
1912                  elements.each( function() {
1913                      $( this ).attr( 'aria-haspopup', 'menu' ).attr( 'aria-expanded', 'false' );
1914                  } );
1915  
1916                  return;
1917              }
1918  
1919              elements.each( function() {
1920                  $( this ).removeAttr( 'aria-haspopup' ).removeAttr( 'aria-expanded' );
1921              } );
1922          },
1923  
1924          /**
1925           * Sets the responsiveness and enables the overlay based on the viewport width.
1926           *
1927           * @since 3.8.0
1928           *
1929           * @return {void}
1930           */
1931          trigger: function() {
1932              var viewportWidth = getViewportWidth();
1933  
1934              // Exclude IE < 9, it doesn't support @media CSS rules.
1935              if ( ! viewportWidth ) {
1936                  return;
1937              }
1938  
1939              if ( viewportWidth <= 782 ) {
1940                  if ( ! wpResponsiveActive ) {
1941                      $document.trigger( 'wp-responsive-activate' );
1942                      wpResponsiveActive = true;
1943                  }
1944              } else {
1945                  if ( wpResponsiveActive ) {
1946                      $document.trigger( 'wp-responsive-deactivate' );
1947                      wpResponsiveActive = false;
1948                  }
1949              }
1950  
1951              if ( viewportWidth <= 480 ) {
1952                  this.enableOverlay();
1953              } else {
1954                  this.disableOverlay();
1955              }
1956  
1957              this.maybeDisableSortables();
1958          },
1959  
1960          /**
1961           * Inserts a responsive overlay and toggles the window.
1962           *
1963           * @since 3.8.0
1964           *
1965           * @return {void}
1966           */
1967          enableOverlay: function() {
1968              if ( $overlay.length === 0 ) {
1969                  $overlay = $( '<div id="wp-responsive-overlay"></div>' )
1970                      .insertAfter( '#wpcontent' )
1971                      .hide()
1972                      .on( 'click.wp-responsive', function() {
1973                          $toolbar.find( '.menupop.hover' ).removeClass( 'hover' );
1974                          $( this ).hide();
1975                      });
1976              }
1977  
1978              $toolbarPopups.on( 'click.wp-responsive', function() {
1979                  $overlay.show();
1980              });
1981          },
1982  
1983          /**
1984           * Disables the responsive overlay and removes the overlay.
1985           *
1986           * @since 3.8.0
1987           *
1988           * @return {void}
1989           */
1990          disableOverlay: function() {
1991              $toolbarPopups.off( 'click.wp-responsive' );
1992              $overlay.hide();
1993          },
1994  
1995          /**
1996           * Disables sortables.
1997           *
1998           * @since 3.8.0
1999           *
2000           * @return {void}
2001           */
2002          disableSortables: function() {
2003              if ( $sortables.length ) {
2004                  try {
2005                      $sortables.sortable( 'disable' );
2006                      $sortables.find( '.ui-sortable-handle' ).addClass( 'is-non-sortable' );
2007                  } catch ( e ) {}
2008              }
2009          },
2010  
2011          /**
2012           * Enables sortables.
2013           *
2014           * @since 3.8.0
2015           *
2016           * @return {void}
2017           */
2018          enableSortables: function() {
2019              if ( $sortables.length ) {
2020                  try {
2021                      $sortables.sortable( 'enable' );
2022                      $sortables.find( '.ui-sortable-handle' ).removeClass( 'is-non-sortable' );
2023                  } catch ( e ) {}
2024              }
2025          }
2026      };
2027  
2028      /**
2029       * Add an ARIA role `button` to elements that behave like UI controls when JavaScript is on.
2030       *
2031       * @since 4.5.0
2032       *
2033       * @return {void}
2034       */
2035  	function aria_button_if_js() {
2036          $( '.aria-button-if-js' ).attr( 'role', 'button' );
2037      }
2038  
2039      $( document ).on( 'ajaxComplete', function() {
2040          aria_button_if_js();
2041      });
2042  
2043      /**
2044       * Get the viewport width.
2045       *
2046       * @since 4.7.0
2047       *
2048       * @return {number|boolean} The current viewport width or false if the
2049       *                          browser doesn't support innerWidth (IE < 9).
2050       */
2051  	function getViewportWidth() {
2052          var viewportWidth = false;
2053  
2054          if ( window.innerWidth ) {
2055              // On phones, window.innerWidth is affected by zooming.
2056              viewportWidth = Math.max( window.innerWidth, document.documentElement.clientWidth );
2057          }
2058  
2059          return viewportWidth;
2060      }
2061  
2062      /**
2063       * Sets the admin menu collapsed/expanded state.
2064       *
2065       * Sets the global variable `menuState` and triggers a custom event passing
2066       * the current menu state.
2067       *
2068       * @since 4.7.0
2069       *
2070       * @return {void}
2071       */
2072  	function setMenuState() {
2073          var viewportWidth = getViewportWidth() || 961;
2074  
2075          if ( viewportWidth <= 782  ) {
2076              menuState = 'responsive';
2077          } else if ( $body.hasClass( 'folded' ) || ( $body.hasClass( 'auto-fold' ) && viewportWidth <= 960 && viewportWidth > 782 ) ) {
2078              menuState = 'folded';
2079          } else {
2080              menuState = 'open';
2081          }
2082  
2083          $document.trigger( 'wp-menu-state-set', { state: menuState } );
2084      }
2085  
2086      // Set the menu state when the window gets resized.
2087      $document.on( 'wp-window-resized.set-menu-state', setMenuState );
2088  
2089      /**
2090       * Sets ARIA attributes on the collapse/expand menu button.
2091       *
2092       * When the admin menu is open or folded, updates the `aria-expanded` and
2093       * `aria-label` attributes of the button to give feedback to assistive
2094       * technologies. In the responsive view, the button is always hidden.
2095       *
2096       * @since 4.7.0
2097       *
2098       * @return {void}
2099       */
2100      $document.on( 'wp-menu-state-set wp-collapse-menu', function( event, eventData ) {
2101          var $collapseButton = $( '#collapse-button' ),
2102              ariaExpanded, ariaLabelText;
2103  
2104          if ( 'folded' === eventData.state ) {
2105              ariaExpanded = 'false';
2106              ariaLabelText = __( 'Expand Main menu' );
2107          } else {
2108              ariaExpanded = 'true';
2109              ariaLabelText = __( 'Collapse Main menu' );
2110          }
2111  
2112          $collapseButton.attr({
2113              'aria-expanded': ariaExpanded,
2114              'aria-label': ariaLabelText
2115          });
2116      });
2117  
2118      window.wpResponsive.init();
2119      setPinMenu();
2120      setMenuState();
2121      makeNoticesDismissible();
2122      aria_button_if_js();
2123  
2124      $document.on( 'wp-pin-menu wp-window-resized.pin-menu postboxes-columnchange.pin-menu postbox-toggled.pin-menu wp-collapse-menu.pin-menu wp-scroll-start.pin-menu', setPinMenu );
2125  
2126      // Set initial focus on a specific element.
2127      $( '.wp-initial-focus' ).trigger( 'focus' );
2128  
2129      // Toggle update details on update-core.php.
2130      $body.on( 'click', '.js-update-details-toggle', function() {
2131          var $updateNotice = $( this ).closest( '.js-update-details' ),
2132              $progressDiv = $( '#' + $updateNotice.data( 'update-details' ) );
2133  
2134          /*
2135           * When clicking on "Show details" move the progress div below the update
2136           * notice. Make sure it gets moved just the first time.
2137           */
2138          if ( ! $progressDiv.hasClass( 'update-details-moved' ) ) {
2139              $progressDiv.insertAfter( $updateNotice ).addClass( 'update-details-moved' );
2140          }
2141  
2142          // Toggle the progress div visibility.
2143          $progressDiv.toggle();
2144          // Toggle the Show Details button expanded state.
2145          $( this ).attr( 'aria-expanded', $progressDiv.is( ':visible' ) );
2146      });
2147  });
2148  
2149  /**
2150   * Hides the update button for expired plugin or theme uploads.
2151   *
2152   * On the "Update plugin/theme from uploaded zip" screen, once the upload has expired,
2153   * hides the "Replace current with uploaded" button and displays a warning.
2154   *
2155   * @since 5.5.0
2156   */
2157  $( function( $ ) {
2158      var $overwrite, $warning;
2159  
2160      if ( ! $body.hasClass( 'update-php' ) ) {
2161          return;
2162      }
2163  
2164      $overwrite = $( 'a.update-from-upload-overwrite' );
2165      $warning   = $( '.update-from-upload-expired' );
2166  
2167      if ( ! $overwrite.length || ! $warning.length ) {
2168          return;
2169      }
2170  
2171      window.setTimeout(
2172          function() {
2173              $overwrite.hide();
2174              $warning.removeClass( 'hidden' );
2175  
2176              if ( window.wp && window.wp.a11y ) {
2177                  window.wp.a11y.speak( $warning.text() );
2178              }
2179          },
2180          7140000 // 119 minutes. The uploaded file is deleted after 2 hours.
2181      );
2182  } );
2183  
2184  // Fire a custom jQuery event at the end of window resize.
2185  ( function() {
2186      var timeout;
2187  
2188      /**
2189       * Triggers the WP window-resize event.
2190       *
2191       * @since 3.8.0
2192       *
2193       * @return {void}
2194       */
2195  	function triggerEvent() {
2196          $document.trigger( 'wp-window-resized' );
2197      }
2198  
2199      /**
2200       * Fires the trigger event again after 200 ms.
2201       *
2202       * @since 3.8.0
2203       *
2204       * @return {void}
2205       */
2206  	function fireOnce() {
2207          window.clearTimeout( timeout );
2208          timeout = window.setTimeout( triggerEvent, 200 );
2209      }
2210  
2211      $window.on( 'resize.wp-fire-once', fireOnce );
2212  }());
2213  
2214  // Make Windows 8 devices play along nicely.
2215  (function(){
2216      if ( '-ms-user-select' in document.documentElement.style && navigator.userAgent.match(/IEMobile\/10\.0/) ) {
2217          var msViewportStyle = document.createElement( 'style' );
2218          msViewportStyle.appendChild(
2219              document.createTextNode( '@-ms-viewport{width:auto!important}' )
2220          );
2221          document.getElementsByTagName( 'head' )[0].appendChild( msViewportStyle );
2222      }
2223  })();
2224  
2225  }( jQuery, window ));
2226  
2227  /**
2228   * Freeze animated plugin icons when reduced motion is enabled.
2229   *
2230   * When the user has enabled the 'prefers-reduced-motion' setting, this module
2231   * stops animations for all GIFs on the page with the class 'plugin-icon' or
2232   * plugin icon images in the update plugins table.
2233   *
2234   * @since 6.4.0
2235   */
2236  (function() {
2237      // Private variables and methods.
2238      var priv = {},
2239          pub = {},
2240          mediaQuery;
2241  
2242      // Initialize pauseAll to false; it will be set to true if reduced motion is preferred.
2243      priv.pauseAll = false;
2244      if ( window.matchMedia ) {
2245          mediaQuery = window.matchMedia( '(prefers-reduced-motion: reduce)' );
2246          if ( ! mediaQuery || mediaQuery.matches ) {
2247              priv.pauseAll = true;
2248          }
2249      }
2250  
2251      // Method to replace animated GIFs with a static frame.
2252      priv.freezeAnimatedPluginIcons = function( img ) {
2253          var coverImage = function() {
2254              var width = img.width;
2255              var height = img.height;
2256              var canvas = document.createElement( 'canvas' );
2257  
2258              // Set canvas dimensions.
2259              canvas.width = width;
2260              canvas.height = height;
2261  
2262              // Copy classes from the image to the canvas.
2263              canvas.className = img.className;
2264  
2265              // Check if the image is inside a specific table.
2266              var isInsideUpdateTable = img.closest( '#update-plugins-table' );
2267  
2268              if ( isInsideUpdateTable ) {
2269                  // Transfer computed styles from image to canvas.
2270                  var computedStyles = window.getComputedStyle( img ),
2271                      i, max;
2272                  for ( i = 0, max = computedStyles.length; i < max; i++ ) {
2273                      var propName = computedStyles[ i ];
2274                      var propValue = computedStyles.getPropertyValue( propName );
2275                      canvas.style[ propName ] = propValue;
2276                  }
2277              }
2278  
2279              // Draw the image onto the canvas.
2280              canvas.getContext( '2d' ).drawImage( img, 0, 0, width, height );
2281  
2282              // Set accessibility attributes on canvas.
2283              canvas.setAttribute( 'aria-hidden', 'true' );
2284              canvas.setAttribute( 'role', 'presentation' );
2285  
2286              // Insert canvas before the image and set the image to be near-invisible.
2287              var parent = img.parentNode;
2288              parent.insertBefore( canvas, img );
2289              img.style.opacity = 0.01;
2290              img.style.width = '0px';
2291              img.style.height = '0px';
2292          };
2293  
2294          // If the image is already loaded, apply the coverImage function.
2295          if ( img.complete ) {
2296              coverImage();
2297          } else {
2298              // Otherwise, wait for the image to load.
2299              img.addEventListener( 'load', coverImage, true );
2300          }
2301      };
2302  
2303      // Public method to freeze all relevant GIFs on the page.
2304      pub.freezeAll = function() {
2305          var images = document.querySelectorAll( '.plugin-icon, #update-plugins-table img' );
2306          for ( var x = 0; x < images.length; x++ ) {
2307              if ( /\.gif(?:\?|$)/i.test( images[ x ].src ) ) {
2308                  priv.freezeAnimatedPluginIcons( images[ x ] );
2309              }
2310          }
2311      };
2312  
2313      // Only run the freezeAll method if the user prefers reduced motion.
2314      if ( true === priv.pauseAll ) {
2315          pub.freezeAll();
2316      }
2317  
2318      // Listen for jQuery AJAX events.
2319      ( function( $ ) {
2320          if ( window.pagenow === 'plugin-install' ) {
2321              // Only listen for ajaxComplete if this is the plugin-install.php page.
2322              $( document ).ajaxComplete( function( event, xhr, settings ) {
2323  
2324                  // Check if this is the 'search-install-plugins' request.
2325                  if ( settings.data && typeof settings.data === 'string' && settings.data.includes( 'action=search-install-plugins' ) ) {
2326                      // Recheck if the user prefers reduced motion.
2327                      if ( window.matchMedia ) {
2328                          var mediaQuery = window.matchMedia( '(prefers-reduced-motion: reduce)' );
2329                          if ( mediaQuery.matches ) {
2330                              pub.freezeAll();
2331                          }
2332                      } else {
2333                          // Fallback for browsers that don't support matchMedia.
2334                          if ( true === priv.pauseAll ) {
2335                              pub.freezeAll();
2336                          }
2337                      }
2338                  }
2339              } );
2340          }
2341      } )( jQuery );
2342  
2343      // Expose public methods.
2344      return pub;
2345  })();


Generated : Thu Nov 21 08:20:01 2024 Cross-referenced by PHPXref