[ Index ]

PHP Cross Reference of WordPress

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 3.3.0, needed for back-compatibility.
 358   *
 359   * @since 2.7.0
 360   * @deprecated 3.3.0
 361   */
 362  window.adminMenu = {
 363      init : function() {},
 364      fold : function() {},
 365      restoreMenuState : function() {},
 366      toggle : function() {},
 367      favorites : function() {}
 368  };
 369  
 370  // Show/hide/save table columns.
 371  window.columns = {
 372  
 373      /**
 374       * Initializes the column toggles in the screen options.
 375       *
 376       * Binds an onClick event to the checkboxes to show or hide the table columns
 377       * based on their toggled state. And persists the toggled state.
 378       *
 379       * @since 2.7.0
 380       *
 381       * @return {void}
 382       */
 383      init : function() {
 384          var that = this;
 385          $('.hide-column-tog', '#adv-settings').on( 'click', function() {
 386              var $t = $(this), column = $t.val();
 387              if ( $t.prop('checked') )
 388                  that.checked(column);
 389              else
 390                  that.unchecked(column);
 391  
 392              columns.saveManageColumnsState();
 393          });
 394      },
 395  
 396      /**
 397       * Saves the toggled state for the columns.
 398       *
 399       * Saves whether the columns should be shown or hidden on a page.
 400       *
 401       * @since 3.0.0
 402       *
 403       * @return {void}
 404       */
 405      saveManageColumnsState : function() {
 406          var hidden = this.hidden();
 407          $.post(ajaxurl, {
 408              action: 'hidden-columns',
 409              hidden: hidden,
 410              screenoptionnonce: $('#screenoptionnonce').val(),
 411              page: pagenow
 412          });
 413      },
 414  
 415      /**
 416       * Makes a column visible and adjusts the column span for the table.
 417       *
 418       * @since 3.0.0
 419       * @param {string} column The column name.
 420       *
 421       * @return {void}
 422       */
 423      checked : function(column) {
 424          $('.column-' + column).removeClass( 'hidden' );
 425          this.colSpanChange(+1);
 426      },
 427  
 428      /**
 429       * Hides a column and adjusts the column span for the table.
 430       *
 431       * @since 3.0.0
 432       * @param {string} column The column name.
 433       *
 434       * @return {void}
 435       */
 436      unchecked : function(column) {
 437          $('.column-' + column).addClass( 'hidden' );
 438          this.colSpanChange(-1);
 439      },
 440  
 441      /**
 442       * Gets all hidden columns.
 443       *
 444       * @since 3.0.0
 445       *
 446       * @return {string} The hidden column names separated by a comma.
 447       */
 448      hidden : function() {
 449          return $( '.manage-column[id]' ).filter( '.hidden' ).map(function() {
 450              return this.id;
 451          }).get().join( ',' );
 452      },
 453  
 454      /**
 455       * Gets the checked column toggles from the screen options.
 456       *
 457       * @since 3.0.0
 458       *
 459       * @return {string} String containing the checked column names.
 460       */
 461      useCheckboxesForHidden : function() {
 462          this.hidden = function(){
 463              return $('.hide-column-tog').not(':checked').map(function() {
 464                  var id = this.id;
 465                  return id.substring( id, id.length - 5 );
 466              }).get().join(',');
 467          };
 468      },
 469  
 470      /**
 471       * Adjusts the column span for the table.
 472       *
 473       * @since 3.1.0
 474       *
 475       * @param {number} diff The modifier for the column span.
 476       */
 477      colSpanChange : function(diff) {
 478          var $t = $('table').find('.colspanchange'), n;
 479          if ( !$t.length )
 480              return;
 481          n = parseInt( $t.attr('colspan'), 10 ) + diff;
 482          $t.attr('colspan', n.toString());
 483      }
 484  };
 485  
 486  $( function() { columns.init(); } );
 487  
 488  /**
 489   * Validates that the required form fields are not empty.
 490   *
 491   * @since 2.9.0
 492   *
 493   * @param {jQuery} form The form to validate.
 494   *
 495   * @return {boolean} Returns true if all required fields are not an empty string.
 496   */
 497  window.validateForm = function( form ) {
 498      return !$( form )
 499          .find( '.form-required' )
 500          .filter( function() { return $( ':input:visible', this ).val() === ''; } )
 501          .addClass( 'form-invalid' )
 502          .find( ':input:visible' )
 503          .on( 'change', function() { $( this ).closest( '.form-invalid' ).removeClass( 'form-invalid' ); } )
 504          .length;
 505  };
 506  
 507  // Stub for doing better warnings.
 508  /**
 509   * Shows message pop-up notice or confirmation message.
 510   *
 511   * @since 2.7.0
 512   *
 513   * @type {{warn: showNotice.warn, note: showNotice.note}}
 514   *
 515   * @return {void}
 516   */
 517  window.showNotice = {
 518  
 519      /**
 520       * Shows a delete confirmation pop-up message.
 521       *
 522       * @since 2.7.0
 523       *
 524       * @return {boolean} Returns true if the message is confirmed.
 525       */
 526      warn : function() {
 527          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.' ) ) ) {
 528              return true;
 529          }
 530  
 531          return false;
 532      },
 533  
 534      /**
 535       * Shows an alert message.
 536       *
 537       * @since 2.7.0
 538       *
 539       * @param text The text to display in the message.
 540       */
 541      note : function(text) {
 542          alert(text);
 543      }
 544  };
 545  
 546  /**
 547   * Represents the functions for the meta screen options panel.
 548   *
 549   * @since 3.2.0
 550   *
 551   * @type {{element: null, toggles: null, page: null, init: screenMeta.init,
 552   *         toggleEvent: screenMeta.toggleEvent, open: screenMeta.open,
 553   *         close: screenMeta.close}}
 554   *
 555   * @return {void}
 556   */
 557  window.screenMeta = {
 558      element: null, // #screen-meta
 559      toggles: null, // .screen-meta-toggle
 560      page:    null, // #wpcontent
 561  
 562      /**
 563       * Initializes the screen meta options panel.
 564       *
 565       * @since 3.2.0
 566       *
 567       * @return {void}
 568       */
 569      init: function() {
 570          this.element = $('#screen-meta');
 571          this.toggles = $( '#screen-meta-links' ).find( '.show-settings' );
 572          this.page    = $('#wpcontent');
 573  
 574          this.toggles.on( 'click', this.toggleEvent );
 575      },
 576  
 577      /**
 578       * Toggles the screen meta options panel.
 579       *
 580       * @since 3.2.0
 581       *
 582       * @return {void}
 583       */
 584      toggleEvent: function() {
 585          var panel = $( '#' + $( this ).attr( 'aria-controls' ) );
 586  
 587          if ( !panel.length )
 588              return;
 589  
 590          if ( panel.is(':visible') )
 591              screenMeta.close( panel, $(this) );
 592          else
 593              screenMeta.open( panel, $(this) );
 594      },
 595  
 596      /**
 597       * Opens the screen meta options panel.
 598       *
 599       * @since 3.2.0
 600       *
 601       * @param {jQuery} panel  The screen meta options panel div.
 602       * @param {jQuery} button The toggle button.
 603       *
 604       * @return {void}
 605       */
 606      open: function( panel, button ) {
 607  
 608          $( '#screen-meta-links' ).find( '.screen-meta-toggle' ).not( button.parent() ).css( 'visibility', 'hidden' );
 609  
 610          panel.parent().show();
 611  
 612          /**
 613           * Sets the focus to the meta options panel and adds the necessary CSS classes.
 614           *
 615           * @since 3.2.0
 616           *
 617           * @return {void}
 618           */
 619          panel.slideDown( 'fast', function() {
 620              panel.trigger( 'focus' );
 621              button.addClass( 'screen-meta-active' ).attr( 'aria-expanded', true );
 622          });
 623  
 624          $document.trigger( 'screen:options:open' );
 625      },
 626  
 627      /**
 628       * Closes the screen meta options panel.
 629       *
 630       * @since 3.2.0
 631       *
 632       * @param {jQuery} panel  The screen meta options panel div.
 633       * @param {jQuery} button The toggle button.
 634       *
 635       * @return {void}
 636       */
 637      close: function( panel, button ) {
 638          /**
 639           * Hides the screen meta options panel.
 640           *
 641           * @since 3.2.0
 642           *
 643           * @return {void}
 644           */
 645          panel.slideUp( 'fast', function() {
 646              button.removeClass( 'screen-meta-active' ).attr( 'aria-expanded', false );
 647              $('.screen-meta-toggle').css('visibility', '');
 648              panel.parent().hide();
 649          });
 650  
 651          $document.trigger( 'screen:options:close' );
 652      }
 653  };
 654  
 655  /**
 656   * Initializes the help tabs in the help panel.
 657   *
 658   * @param {Event} e The event object.
 659   *
 660   * @return {void}
 661   */
 662  $('.contextual-help-tabs').on( 'click', 'a', function(e) {
 663      var link = $(this),
 664          panel;
 665  
 666      e.preventDefault();
 667  
 668      // Don't do anything if the click is for the tab already showing.
 669      if ( link.is('.active a') )
 670          return false;
 671  
 672      // Links.
 673      $('.contextual-help-tabs .active').removeClass('active');
 674      link.parent('li').addClass('active');
 675  
 676      panel = $( link.attr('href') );
 677  
 678      // Panels.
 679      $('.help-tab-content').not( panel ).removeClass('active').hide();
 680      panel.addClass('active').show();
 681  });
 682  
 683  /**
 684   * Update custom permalink structure via buttons.
 685   */
 686  var permalinkStructureFocused = false,
 687      $permalinkStructure       = $( '#permalink_structure' ),
 688      $permalinkStructureInputs = $( '.permalink-structure input:radio' ),
 689      $permalinkCustomSelection = $( '#custom_selection' ),
 690      $availableStructureTags   = $( '.form-table.permalink-structure .available-structure-tags button' );
 691  
 692  // Change permalink structure input when selecting one of the common structures.
 693  $permalinkStructureInputs.on( 'change', function() {
 694      if ( 'custom' === this.value ) {
 695          return;
 696      }
 697  
 698      $permalinkStructure.val( this.value );
 699  
 700      // Update button states after selection.
 701      $availableStructureTags.each( function() {
 702          changeStructureTagButtonState( $( this ) );
 703      } );
 704  } );
 705  
 706  $permalinkStructure.on( 'click input', function() {
 707      $permalinkCustomSelection.prop( 'checked', true );
 708  } );
 709  
 710  // Check if the permalink structure input field has had focus at least once.
 711  $permalinkStructure.on( 'focus', function( event ) {
 712      permalinkStructureFocused = true;
 713      $( this ).off( event );
 714  } );
 715  
 716  /**
 717   * Enables or disables a structure tag button depending on its usage.
 718   *
 719   * If the structure is already used in the custom permalink structure,
 720   * it will be disabled.
 721   *
 722   * @param {Object} button Button jQuery object.
 723   */
 724  function changeStructureTagButtonState( button ) {
 725      if ( -1 !== $permalinkStructure.val().indexOf( button.text().trim() ) ) {
 726          button.attr( 'data-label', button.attr( 'aria-label' ) );
 727          button.attr( 'aria-label', button.attr( 'data-used' ) );
 728          button.attr( 'aria-pressed', true );
 729          button.addClass( 'active' );
 730      } else if ( button.attr( 'data-label' ) ) {
 731          button.attr( 'aria-label', button.attr( 'data-label' ) );
 732          button.attr( 'aria-pressed', false );
 733          button.removeClass( 'active' );
 734      }
 735  }
 736  
 737  // Check initial button state.
 738  $availableStructureTags.each( function() {
 739      changeStructureTagButtonState( $( this ) );
 740  } );
 741  
 742  // Observe permalink structure field and disable buttons of tags that are already present.
 743  $permalinkStructure.on( 'change', function() {
 744      $availableStructureTags.each( function() {
 745          changeStructureTagButtonState( $( this ) );
 746      } );
 747  } );
 748  
 749  $availableStructureTags.on( 'click', function() {
 750      var permalinkStructureValue = $permalinkStructure.val(),
 751          selectionStart          = $permalinkStructure[ 0 ].selectionStart,
 752          selectionEnd            = $permalinkStructure[ 0 ].selectionEnd,
 753          textToAppend            = $( this ).text().trim(),
 754          textToAnnounce          = $( this ).attr( 'data-added' ),
 755          newSelectionStart;
 756  
 757      // Remove structure tag if already part of the structure.
 758      if ( -1 !== permalinkStructureValue.indexOf( textToAppend ) ) {
 759          permalinkStructureValue = permalinkStructureValue.replace( textToAppend + '/', '' );
 760  
 761          $permalinkStructure.val( '/' === permalinkStructureValue ? '' : permalinkStructureValue );
 762  
 763          // Announce change to screen readers.
 764          $( '#custom_selection_updated' ).text( textToAnnounce );
 765  
 766          // Disable button.
 767          changeStructureTagButtonState( $( this ) );
 768  
 769          return;
 770      }
 771  
 772      // Input field never had focus, move selection to end of input.
 773      if ( ! permalinkStructureFocused && 0 === selectionStart && 0 === selectionEnd ) {
 774          selectionStart = selectionEnd = permalinkStructureValue.length;
 775      }
 776  
 777      $permalinkCustomSelection.prop( 'checked', true );
 778  
 779      // Prepend and append slashes if necessary.
 780      if ( '/' !== permalinkStructureValue.substr( 0, selectionStart ).substr( -1 ) ) {
 781          textToAppend = '/' + textToAppend;
 782      }
 783  
 784      if ( '/' !== permalinkStructureValue.substr( selectionEnd, 1 ) ) {
 785          textToAppend = textToAppend + '/';
 786      }
 787  
 788      // Insert structure tag at the specified position.
 789      $permalinkStructure.val( permalinkStructureValue.substr( 0, selectionStart ) + textToAppend + permalinkStructureValue.substr( selectionEnd ) );
 790  
 791      // Announce change to screen readers.
 792      $( '#custom_selection_updated' ).text( textToAnnounce );
 793  
 794      // Disable button.
 795      changeStructureTagButtonState( $( this ) );
 796  
 797      // If input had focus give it back with cursor right after appended text.
 798      if ( permalinkStructureFocused && $permalinkStructure[0].setSelectionRange ) {
 799          newSelectionStart = ( permalinkStructureValue.substr( 0, selectionStart ) + textToAppend ).length;
 800          $permalinkStructure[0].setSelectionRange( newSelectionStart, newSelectionStart );
 801          $permalinkStructure.trigger( 'focus' );
 802      }
 803  } );
 804  
 805  $( function() {
 806      var checks, first, last, checked, sliced, mobileEvent, transitionTimeout, focusedRowActions,
 807          lastClicked = false,
 808          pageInput = $('input.current-page'),
 809          currentPage = pageInput.val(),
 810          isIOS = /iPhone|iPad|iPod/.test( navigator.userAgent ),
 811          isAndroid = navigator.userAgent.indexOf( 'Android' ) !== -1,
 812          $adminMenuWrap = $( '#adminmenuwrap' ),
 813          $wpwrap = $( '#wpwrap' ),
 814          $adminmenu = $( '#adminmenu' ),
 815          $overlay = $( '#wp-responsive-overlay' ),
 816          $toolbar = $( '#wp-toolbar' ),
 817          $toolbarPopups = $toolbar.find( 'a[aria-haspopup="true"]' ),
 818          $sortables = $('.meta-box-sortables'),
 819          wpResponsiveActive = false,
 820          $adminbar = $( '#wpadminbar' ),
 821          lastScrollPosition = 0,
 822          pinnedMenuTop = false,
 823          pinnedMenuBottom = false,
 824          menuTop = 0,
 825          menuState,
 826          menuIsPinned = false,
 827          height = {
 828              window: $window.height(),
 829              wpwrap: $wpwrap.height(),
 830              adminbar: $adminbar.height(),
 831              menu: $adminMenuWrap.height()
 832          },
 833          $headerEnd = $( '.wp-header-end' );
 834  
 835      /**
 836       * Makes the fly-out submenu header clickable, when the menu is folded.
 837       *
 838       * @param {Event} e The event object.
 839       *
 840       * @return {void}
 841       */
 842      $adminmenu.on('click.wp-submenu-head', '.wp-submenu-head', function(e){
 843          $(e.target).parent().siblings('a').get(0).click();
 844      });
 845  
 846      /**
 847       * Collapses the admin menu.
 848       *
 849       * @return {void}
 850       */
 851      $( '#collapse-button' ).on( 'click.collapse-menu', function() {
 852          var viewportWidth = getViewportWidth() || 961;
 853  
 854          // Reset any compensation for submenus near the bottom of the screen.
 855          $('#adminmenu div.wp-submenu').css('margin-top', '');
 856  
 857          if ( viewportWidth <= 960 ) {
 858              if ( $body.hasClass('auto-fold') ) {
 859                  $body.removeClass('auto-fold').removeClass('folded');
 860                  setUserSetting('unfold', 1);
 861                  setUserSetting('mfold', 'o');
 862                  menuState = 'open';
 863              } else {
 864                  $body.addClass('auto-fold');
 865                  setUserSetting('unfold', 0);
 866                  menuState = 'folded';
 867              }
 868          } else {
 869              if ( $body.hasClass('folded') ) {
 870                  $body.removeClass('folded');
 871                  setUserSetting('mfold', 'o');
 872                  menuState = 'open';
 873              } else {
 874                  $body.addClass('folded');
 875                  setUserSetting('mfold', 'f');
 876                  menuState = 'folded';
 877              }
 878          }
 879  
 880          $document.trigger( 'wp-collapse-menu', { state: menuState } );
 881      });
 882  
 883      /**
 884       * Handles the `aria-haspopup` attribute on the current menu item when it has a submenu.
 885       *
 886       * @since 4.4.0
 887       *
 888       * @return {void}
 889       */
 890  	function currentMenuItemHasPopup() {
 891          var $current = $( 'a.wp-has-current-submenu' );
 892  
 893          if ( 'folded' === menuState ) {
 894              // When folded or auto-folded and not responsive view, the current menu item does have a fly-out sub-menu.
 895              $current.attr( 'aria-haspopup', 'true' );
 896          } else {
 897              // When expanded or in responsive view, reset aria-haspopup.
 898              $current.attr( 'aria-haspopup', 'false' );
 899          }
 900      }
 901  
 902      $document.on( 'wp-menu-state-set wp-collapse-menu wp-responsive-activate wp-responsive-deactivate', currentMenuItemHasPopup );
 903  
 904      /**
 905       * Ensures an admin submenu is within the visual viewport.
 906       *
 907       * @since 4.1.0
 908       *
 909       * @param {jQuery} $menuItem The parent menu item containing the submenu.
 910       *
 911       * @return {void}
 912       */
 913  	function adjustSubmenu( $menuItem ) {
 914          var bottomOffset, pageHeight, adjustment, theFold, menutop, wintop, maxtop,
 915              $submenu = $menuItem.find( '.wp-submenu' );
 916  
 917          menutop = $menuItem.offset().top;
 918          wintop = $window.scrollTop();
 919          maxtop = menutop - wintop - 30; // max = make the top of the sub almost touch admin bar.
 920  
 921          bottomOffset = menutop + $submenu.height() + 1; // Bottom offset of the menu.
 922          pageHeight = $wpwrap.height();                  // Height of the entire page.
 923          adjustment = 60 + bottomOffset - pageHeight;
 924          theFold = $window.height() + wintop - 50;       // The fold.
 925  
 926          if ( theFold < ( bottomOffset - adjustment ) ) {
 927              adjustment = bottomOffset - theFold;
 928          }
 929  
 930          if ( adjustment > maxtop ) {
 931              adjustment = maxtop;
 932          }
 933  
 934          if ( adjustment > 1 ) {
 935              $submenu.css( 'margin-top', '-' + adjustment + 'px' );
 936          } else {
 937              $submenu.css( 'margin-top', '' );
 938          }
 939      }
 940  
 941      if ( 'ontouchstart' in window || /IEMobile\/[1-9]/.test(navigator.userAgent) ) { // Touch screen device.
 942          // iOS Safari works with touchstart, the rest work with click.
 943          mobileEvent = isIOS ? 'touchstart' : 'click';
 944  
 945          /**
 946           * Closes any open submenus when touch/click is not on the menu.
 947           *
 948           * @param {Event} e The event object.
 949           *
 950           * @return {void}
 951           */
 952          $body.on( mobileEvent+'.wp-mobile-hover', function(e) {
 953              if ( $adminmenu.data('wp-responsive') ) {
 954                  return;
 955              }
 956  
 957              if ( ! $( e.target ).closest( '#adminmenu' ).length ) {
 958                  $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' );
 959              }
 960          });
 961  
 962          /**
 963           * Handles the opening or closing the submenu based on the mobile click|touch event.
 964           *
 965           * @param {Event} event The event object.
 966           *
 967           * @return {void}
 968           */
 969          $adminmenu.find( 'a.wp-has-submenu' ).on( mobileEvent + '.wp-mobile-hover', function( event ) {
 970              var $menuItem = $(this).parent();
 971  
 972              if ( $adminmenu.data( 'wp-responsive' ) ) {
 973                  return;
 974              }
 975  
 976              /*
 977               * Show the sub instead of following the link if:
 978               *     - the submenu is not open.
 979               *     - the submenu is not shown inline or the menu is not folded.
 980               */
 981              if ( ! $menuItem.hasClass( 'opensub' ) && ( ! $menuItem.hasClass( 'wp-menu-open' ) || $menuItem.width() < 40 ) ) {
 982                  event.preventDefault();
 983                  adjustSubmenu( $menuItem );
 984                  $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' );
 985                  $menuItem.addClass('opensub');
 986              }
 987          });
 988      }
 989  
 990      if ( ! isIOS && ! isAndroid ) {
 991          $adminmenu.find( 'li.wp-has-submenu' ).hoverIntent({
 992  
 993              /**
 994               * Opens the submenu when hovered over the menu item for desktops.
 995               *
 996               * @return {void}
 997               */
 998              over: function() {
 999                  var $menuItem = $( this ),
1000                      $submenu = $menuItem.find( '.wp-submenu' ),
1001                      top = parseInt( $submenu.css( 'top' ), 10 );
1002  
1003                  if ( isNaN( top ) || top > -5 ) { // The submenu is visible.
1004                      return;
1005                  }
1006  
1007                  if ( $adminmenu.data( 'wp-responsive' ) ) {
1008                      // The menu is in responsive mode, bail.
1009                      return;
1010                  }
1011  
1012                  adjustSubmenu( $menuItem );
1013                  $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' );
1014                  $menuItem.addClass( 'opensub' );
1015              },
1016  
1017              /**
1018               * Closes the submenu when no longer hovering the menu item.
1019               *
1020               * @return {void}
1021               */
1022              out: function(){
1023                  if ( $adminmenu.data( 'wp-responsive' ) ) {
1024                      // The menu is in responsive mode, bail.
1025                      return;
1026                  }
1027  
1028                  $( this ).removeClass( 'opensub' ).find( '.wp-submenu' ).css( 'margin-top', '' );
1029              },
1030              timeout: 200,
1031              sensitivity: 7,
1032              interval: 90
1033          });
1034  
1035          /**
1036           * Opens the submenu on when focused on the menu item.
1037           *
1038           * @param {Event} event The event object.
1039           *
1040           * @return {void}
1041           */
1042          $adminmenu.on( 'focus.adminmenu', '.wp-submenu a', function( event ) {
1043              if ( $adminmenu.data( 'wp-responsive' ) ) {
1044                  // The menu is in responsive mode, bail.
1045                  return;
1046              }
1047  
1048              $( event.target ).closest( 'li.menu-top' ).addClass( 'opensub' );
1049  
1050              /**
1051               * Closes the submenu on blur from the menu item.
1052               *
1053               * @param {Event} event The event object.
1054               *
1055               * @return {void}
1056               */
1057          }).on( 'blur.adminmenu', '.wp-submenu a', function( event ) {
1058              if ( $adminmenu.data( 'wp-responsive' ) ) {
1059                  return;
1060              }
1061  
1062              $( event.target ).closest( 'li.menu-top' ).removeClass( 'opensub' );
1063  
1064              /**
1065               * Adjusts the size for the submenu.
1066               *
1067               * @return {void}
1068               */
1069          }).find( 'li.wp-has-submenu.wp-not-current-submenu' ).on( 'focusin.adminmenu', function() {
1070              adjustSubmenu( $( this ) );
1071          });
1072      }
1073  
1074      /*
1075       * The `.below-h2` class is here just for backward compatibility with plugins
1076       * that are (incorrectly) using it. Do not use. Use `.inline` instead. See #34570.
1077       * If '.wp-header-end' is found, append the notices after it otherwise
1078       * after the first h1 or h2 heading found within the main content.
1079       */
1080      if ( ! $headerEnd.length ) {
1081          $headerEnd = $( '.wrap h1, .wrap h2' ).first();
1082      }
1083      $( 'div.updated, div.error, div.notice' ).not( '.inline, .below-h2' ).insertAfter( $headerEnd );
1084  
1085      /**
1086       * Makes notices dismissible.
1087       *
1088       * @since 4.4.0
1089       *
1090       * @return {void}
1091       */
1092  	function makeNoticesDismissible() {
1093          $( '.notice.is-dismissible' ).each( function() {
1094              var $el = $( this ),
1095                  $button = $( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' );
1096  
1097              if ( $el.find( '.notice-dismiss' ).length ) {
1098                  return;
1099              }
1100  
1101              // Ensure plain text.
1102              $button.find( '.screen-reader-text' ).text( __( 'Dismiss this notice.' ) );
1103              $button.on( 'click.wp-dismiss-notice', function( event ) {
1104                  event.preventDefault();
1105                  $el.fadeTo( 100, 0, function() {
1106                      $el.slideUp( 100, function() {
1107                          $el.remove();
1108                      });
1109                  });
1110              });
1111  
1112              $el.append( $button );
1113          });
1114      }
1115  
1116      $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', makeNoticesDismissible );
1117  
1118      // Init screen meta.
1119      screenMeta.init();
1120  
1121      /**
1122       * Checks a checkbox.
1123       *
1124       * This event needs to be delegated. Ticket #37973.
1125       *
1126       * @return {boolean} Returns whether a checkbox is checked or not.
1127       */
1128      $body.on( 'click', 'tbody > tr > .check-column :checkbox', function( event ) {
1129          // Shift click to select a range of checkboxes.
1130          if ( 'undefined' == event.shiftKey ) { return true; }
1131          if ( event.shiftKey ) {
1132              if ( !lastClicked ) { return true; }
1133              checks = $( lastClicked ).closest( 'form' ).find( ':checkbox' ).filter( ':visible:enabled' );
1134              first = checks.index( lastClicked );
1135              last = checks.index( this );
1136              checked = $(this).prop('checked');
1137              if ( 0 < first && 0 < last && first != last ) {
1138                  sliced = ( last > first ) ? checks.slice( first, last ) : checks.slice( last, first );
1139                  sliced.prop( 'checked', function() {
1140                      if ( $(this).closest('tr').is(':visible') )
1141                          return checked;
1142  
1143                      return false;
1144                  });
1145              }
1146          }
1147          lastClicked = this;
1148  
1149          // Toggle the "Select all" checkboxes depending if the other ones are all checked or not.
1150          var unchecked = $(this).closest('tbody').find(':checkbox').filter(':visible:enabled').not(':checked');
1151  
1152          /**
1153           * Determines if all checkboxes are checked.
1154           *
1155           * @return {boolean} Returns true if there are no unchecked checkboxes.
1156           */
1157          $(this).closest('table').children('thead, tfoot').find(':checkbox').prop('checked', function() {
1158              return ( 0 === unchecked.length );
1159          });
1160  
1161          return true;
1162      });
1163  
1164      /**
1165       * Controls all the toggles on bulk toggle change.
1166       *
1167       * When the bulk checkbox is changed, all the checkboxes in the tables are changed accordingly.
1168       * When the shift-button is pressed while changing the bulk checkbox the checkboxes in the table are inverted.
1169       *
1170       * This event needs to be delegated. Ticket #37973.
1171       *
1172       * @param {Event} event The event object.
1173       *
1174       * @return {boolean}
1175       */
1176      $body.on( 'click.wp-toggle-checkboxes', 'thead .check-column :checkbox, tfoot .check-column :checkbox', function( event ) {
1177          var $this = $(this),
1178              $table = $this.closest( 'table' ),
1179              controlChecked = $this.prop('checked'),
1180              toggle = event.shiftKey || $this.data('wp-toggle');
1181  
1182          $table.children( 'tbody' ).filter(':visible')
1183              .children().children('.check-column').find(':checkbox')
1184              /**
1185               * Updates the checked state on the checkbox in the table.
1186               *
1187               * @return {boolean} True checks the checkbox, False unchecks the checkbox.
1188               */
1189              .prop('checked', function() {
1190                  if ( $(this).is(':hidden,:disabled') ) {
1191                      return false;
1192                  }
1193  
1194                  if ( toggle ) {
1195                      return ! $(this).prop( 'checked' );
1196                  } else if ( controlChecked ) {
1197                      return true;
1198                  }
1199  
1200                  return false;
1201              });
1202  
1203          $table.children('thead,  tfoot').filter(':visible')
1204              .children().children('.check-column').find(':checkbox')
1205  
1206              /**
1207               * Syncs the bulk checkboxes on the top and bottom of the table.
1208               *
1209               * @return {boolean} True checks the checkbox, False unchecks the checkbox.
1210               */
1211              .prop('checked', function() {
1212                  if ( toggle ) {
1213                      return false;
1214                  } else if ( controlChecked ) {
1215                      return true;
1216                  }
1217  
1218                  return false;
1219              });
1220      });
1221  
1222      /**
1223       * Marries a secondary control to its primary control.
1224       *
1225       * @param {jQuery} topSelector    The top selector element.
1226       * @param {jQuery} topSubmit      The top submit element.
1227       * @param {jQuery} bottomSelector The bottom selector element.
1228       * @param {jQuery} bottomSubmit   The bottom submit element.
1229       * @return {void}
1230       */
1231  	function marryControls( topSelector, topSubmit, bottomSelector, bottomSubmit ) {
1232          /**
1233           * Updates the primary selector when the secondary selector is changed.
1234           *
1235           * @since 5.7.0
1236           *
1237           * @return {void}
1238           */
1239  		function updateTopSelector() {
1240              topSelector.val($(this).val());
1241          }
1242          bottomSelector.on('change', updateTopSelector);
1243  
1244          /**
1245           * Updates the secondary selector when the primary selector is changed.
1246           *
1247           * @since 5.7.0
1248           *
1249           * @return {void}
1250           */
1251  		function updateBottomSelector() {
1252              bottomSelector.val($(this).val());
1253          }
1254          topSelector.on('change', updateBottomSelector);
1255  
1256          /**
1257           * Triggers the primary submit when then secondary submit is clicked.
1258           *
1259           * @since 5.7.0
1260           *
1261           * @return {void}
1262           */
1263  		function triggerSubmitClick(e) {
1264              e.preventDefault();
1265              e.stopPropagation();
1266  
1267              topSubmit.trigger('click');
1268          }
1269          bottomSubmit.on('click', triggerSubmitClick);
1270      }
1271  
1272      // Marry the secondary "Bulk actions" controls to the primary controls:
1273      marryControls( $('#bulk-action-selector-top'), $('#doaction'), $('#bulk-action-selector-bottom'), $('#doaction2') );
1274  
1275      // Marry the secondary "Change role to" controls to the primary controls:
1276      marryControls( $('#new_role'), $('#changeit'), $('#new_role2'), $('#changeit2') );
1277  
1278      /**
1279       * Shows row actions on focus of its parent container element or any other elements contained within.
1280       *
1281       * @return {void}
1282       */
1283      $( '#wpbody-content' ).on({
1284          focusin: function() {
1285              clearTimeout( transitionTimeout );
1286              focusedRowActions = $( this ).find( '.row-actions' );
1287              // transitionTimeout is necessary for Firefox, but Chrome won't remove the CSS class without a little help.
1288              $( '.row-actions' ).not( this ).removeClass( 'visible' );
1289              focusedRowActions.addClass( 'visible' );
1290          },
1291          focusout: function() {
1292              // Tabbing between post title and .row-actions links needs a brief pause, otherwise
1293              // the .row-actions div gets hidden in transit in some browsers (ahem, Firefox).
1294              transitionTimeout = setTimeout( function() {
1295                  focusedRowActions.removeClass( 'visible' );
1296              }, 30 );
1297          }
1298      }, '.table-view-list .has-row-actions' );
1299  
1300      // Toggle list table rows on small screens.
1301      $( 'tbody' ).on( 'click', '.toggle-row', function() {
1302          $( this ).closest( 'tr' ).toggleClass( 'is-expanded' );
1303      });
1304  
1305      $('#default-password-nag-no').on( 'click', function() {
1306          setUserSetting('default_password_nag', 'hide');
1307          $('div.default-password-nag').hide();
1308          return false;
1309      });
1310  
1311      /**
1312       * Handles tab keypresses in theme and plugin file editor textareas.
1313       *
1314       * @param {Event} e The event object.
1315       *
1316       * @return {void}
1317       */
1318      $('#newcontent').on('keydown.wpevent_InsertTab', function(e) {
1319          var el = e.target, selStart, selEnd, val, scroll, sel;
1320  
1321          // After pressing escape key (keyCode: 27), the tab key should tab out of the textarea.
1322          if ( e.keyCode == 27 ) {
1323              // When pressing Escape: Opera 12 and 27 blur form fields, IE 8 clears them.
1324              e.preventDefault();
1325              $(el).data('tab-out', true);
1326              return;
1327          }
1328  
1329          // Only listen for plain tab key (keyCode: 9) without any modifiers.
1330          if ( e.keyCode != 9 || e.ctrlKey || e.altKey || e.shiftKey )
1331              return;
1332  
1333          // After tabbing out, reset it so next time the tab key can be used again.
1334          if ( $(el).data('tab-out') ) {
1335              $(el).data('tab-out', false);
1336              return;
1337          }
1338  
1339          selStart = el.selectionStart;
1340          selEnd = el.selectionEnd;
1341          val = el.value;
1342  
1343          // If any text is selected, replace the selection with a tab character.
1344          if ( document.selection ) {
1345              el.focus();
1346              sel = document.selection.createRange();
1347              sel.text = '\t';
1348          } else if ( selStart >= 0 ) {
1349              scroll = this.scrollTop;
1350              el.value = val.substring(0, selStart).concat('\t', val.substring(selEnd) );
1351              el.selectionStart = el.selectionEnd = selStart + 1;
1352              this.scrollTop = scroll;
1353          }
1354  
1355          // Cancel the regular tab functionality, to prevent losing focus of the textarea.
1356          if ( e.stopPropagation )
1357              e.stopPropagation();
1358          if ( e.preventDefault )
1359              e.preventDefault();
1360      });
1361  
1362      // Reset page number variable for new filters/searches but not for bulk actions. See #17685.
1363      if ( pageInput.length ) {
1364  
1365          /**
1366           * Handles pagination variable when filtering the list table.
1367           *
1368           * Set the pagination argument to the first page when the post-filter form is submitted.
1369           * This happens when pressing the 'filter' button on the list table page.
1370           *
1371           * The pagination argument should not be touched when the bulk action dropdowns are set to do anything.
1372           *
1373           * The form closest to the pageInput is the post-filter form.
1374           *
1375           * @return {void}
1376           */
1377          pageInput.closest('form').on( 'submit', function() {
1378              /*
1379               * action = bulk action dropdown at the top of the table
1380               */
1381              if ( $('select[name="action"]').val() == -1 && pageInput.val() == currentPage )
1382                  pageInput.val('1');
1383          });
1384      }
1385  
1386      /**
1387       * Resets the bulk actions when the search button is clicked.
1388       *
1389       * @return {void}
1390       */
1391      $('.search-box input[type="search"], .search-box input[type="submit"]').on( 'mousedown', function () {
1392          $('select[name^="action"]').val('-1');
1393      });
1394  
1395      /**
1396       * Scrolls into view when focus.scroll-into-view is triggered.
1397       *
1398       * @param {Event} e The event object.
1399       *
1400       * @return {void}
1401        */
1402      $('#contextual-help-link, #show-settings-link').on( 'focus.scroll-into-view', function(e){
1403          if ( e.target.scrollIntoView )
1404              e.target.scrollIntoView(false);
1405      });
1406  
1407      /**
1408       * Disables the submit upload buttons when no data is entered.
1409       *
1410       * @return {void}
1411       */
1412      (function(){
1413          var button, input, form = $('form.wp-upload-form');
1414  
1415          // Exit when no upload form is found.
1416          if ( ! form.length )
1417              return;
1418  
1419          button = form.find('input[type="submit"]');
1420          input = form.find('input[type="file"]');
1421  
1422          /**
1423           * Determines if any data is entered in any file upload input.
1424           *
1425           * @since 3.5.0
1426           *
1427           * @return {void}
1428           */
1429  		function toggleUploadButton() {
1430              // When no inputs have a value, disable the upload buttons.
1431              button.prop('disabled', '' === input.map( function() {
1432                  return $(this).val();
1433              }).get().join(''));
1434          }
1435  
1436          // Update the status initially.
1437          toggleUploadButton();
1438          // Update the status when any file input changes.
1439          input.on('change', toggleUploadButton);
1440      })();
1441  
1442      /**
1443       * Pins the menu while distraction-free writing is enabled.
1444       *
1445       * @param {Event} event Event data.
1446       *
1447       * @since 4.1.0
1448       *
1449       * @return {void}
1450       */
1451  	function pinMenu( event ) {
1452          var windowPos = $window.scrollTop(),
1453              resizing = ! event || event.type !== 'scroll';
1454  
1455          if ( isIOS || $adminmenu.data( 'wp-responsive' ) ) {
1456              return;
1457          }
1458  
1459          /*
1460           * When the menu is higher than the window and smaller than the entire page.
1461           * It should be adjusted to be able to see the entire menu.
1462           *
1463           * Otherwise it can be accessed normally.
1464           */
1465          if ( height.menu + height.adminbar < height.window ||
1466              height.menu + height.adminbar + 20 > height.wpwrap ) {
1467              unpinMenu();
1468              return;
1469          }
1470  
1471          menuIsPinned = true;
1472  
1473          // If the menu is higher than the window, compensate on scroll.
1474          if ( height.menu + height.adminbar > height.window ) {
1475              // Check for overscrolling, this happens when swiping up at the top of the document in modern browsers.
1476              if ( windowPos < 0 ) {
1477                  // Stick the menu to the top.
1478                  if ( ! pinnedMenuTop ) {
1479                      pinnedMenuTop = true;
1480                      pinnedMenuBottom = false;
1481  
1482                      $adminMenuWrap.css({
1483                          position: 'fixed',
1484                          top: '',
1485                          bottom: ''
1486                      });
1487                  }
1488  
1489                  return;
1490              } else if ( windowPos + height.window > $document.height() - 1 ) {
1491                  // When overscrolling at the bottom, stick the menu to the bottom.
1492                  if ( ! pinnedMenuBottom ) {
1493                      pinnedMenuBottom = true;
1494                      pinnedMenuTop = false;
1495  
1496                      $adminMenuWrap.css({
1497                          position: 'fixed',
1498                          top: '',
1499                          bottom: 0
1500                      });
1501                  }
1502  
1503                  return;
1504              }
1505  
1506              if ( windowPos > lastScrollPosition ) {
1507                  // When a down scroll has been detected.
1508  
1509                  // If it was pinned to the top, unpin and calculate relative scroll.
1510                  if ( pinnedMenuTop ) {
1511                      pinnedMenuTop = false;
1512                      // Calculate new offset position.
1513                      menuTop = $adminMenuWrap.offset().top - height.adminbar - ( windowPos - lastScrollPosition );
1514  
1515                      if ( menuTop + height.menu + height.adminbar < windowPos + height.window ) {
1516                          menuTop = windowPos + height.window - height.menu - height.adminbar;
1517                      }
1518  
1519                      $adminMenuWrap.css({
1520                          position: 'absolute',
1521                          top: menuTop,
1522                          bottom: ''
1523                      });
1524                  } else if ( ! pinnedMenuBottom && $adminMenuWrap.offset().top + height.menu < windowPos + height.window ) {
1525                      // Pin it to the bottom.
1526                      pinnedMenuBottom = true;
1527  
1528                      $adminMenuWrap.css({
1529                          position: 'fixed',
1530                          top: '',
1531                          bottom: 0
1532                      });
1533                  }
1534              } else if ( windowPos < lastScrollPosition ) {
1535                  // When a scroll up is detected.
1536  
1537                  // If it was pinned to the bottom, unpin and calculate relative scroll.
1538                  if ( pinnedMenuBottom ) {
1539                      pinnedMenuBottom = false;
1540  
1541                      // Calculate new offset position.
1542                      menuTop = $adminMenuWrap.offset().top - height.adminbar + ( lastScrollPosition - windowPos );
1543  
1544                      if ( menuTop + height.menu > windowPos + height.window ) {
1545                          menuTop = windowPos;
1546                      }
1547  
1548                      $adminMenuWrap.css({
1549                          position: 'absolute',
1550                          top: menuTop,
1551                          bottom: ''
1552                      });
1553                  } else if ( ! pinnedMenuTop && $adminMenuWrap.offset().top >= windowPos + height.adminbar ) {
1554  
1555                      // Pin it to the top.
1556                      pinnedMenuTop = true;
1557  
1558                      $adminMenuWrap.css({
1559                          position: 'fixed',
1560                          top: '',
1561                          bottom: ''
1562                      });
1563                  }
1564              } else if ( resizing ) {
1565                  // Window is being resized.
1566  
1567                  pinnedMenuTop = pinnedMenuBottom = false;
1568  
1569                  // Calculate the new offset.
1570                  menuTop = windowPos + height.window - height.menu - height.adminbar - 1;
1571  
1572                  if ( menuTop > 0 ) {
1573                      $adminMenuWrap.css({
1574                          position: 'absolute',
1575                          top: menuTop,
1576                          bottom: ''
1577                      });
1578                  } else {
1579                      unpinMenu();
1580                  }
1581              }
1582          }
1583  
1584          lastScrollPosition = windowPos;
1585      }
1586  
1587      /**
1588       * Determines the height of certain elements.
1589       *
1590       * @since 4.1.0
1591       *
1592       * @return {void}
1593       */
1594  	function resetHeights() {
1595          height = {
1596              window: $window.height(),
1597              wpwrap: $wpwrap.height(),
1598              adminbar: $adminbar.height(),
1599              menu: $adminMenuWrap.height()
1600          };
1601      }
1602  
1603      /**
1604       * Unpins the menu.
1605       *
1606       * @since 4.1.0
1607       *
1608       * @return {void}
1609       */
1610  	function unpinMenu() {
1611          if ( isIOS || ! menuIsPinned ) {
1612              return;
1613          }
1614  
1615          pinnedMenuTop = pinnedMenuBottom = menuIsPinned = false;
1616          $adminMenuWrap.css({
1617              position: '',
1618              top: '',
1619              bottom: ''
1620          });
1621      }
1622  
1623      /**
1624       * Pins and unpins the menu when applicable.
1625       *
1626       * @since 4.1.0
1627       *
1628       * @return {void}
1629       */
1630  	function setPinMenu() {
1631          resetHeights();
1632  
1633          if ( $adminmenu.data('wp-responsive') ) {
1634              $body.removeClass( 'sticky-menu' );
1635              unpinMenu();
1636          } else if ( height.menu + height.adminbar > height.window ) {
1637              pinMenu();
1638              $body.removeClass( 'sticky-menu' );
1639          } else {
1640              $body.addClass( 'sticky-menu' );
1641              unpinMenu();
1642          }
1643      }
1644  
1645      if ( ! isIOS ) {
1646          $window.on( 'scroll.pin-menu', pinMenu );
1647          $document.on( 'tinymce-editor-init.pin-menu', function( event, editor ) {
1648              editor.on( 'wp-autoresize', resetHeights );
1649          });
1650      }
1651  
1652      /**
1653       * Changes the sortables and responsiveness of metaboxes.
1654       *
1655       * @since 3.8.0
1656       *
1657       * @return {void}
1658       */
1659      window.wpResponsive = {
1660  
1661          /**
1662           * Initializes the wpResponsive object.
1663           *
1664           * @since 3.8.0
1665           *
1666           * @return {void}
1667           */
1668          init: function() {
1669              var self = this;
1670  
1671              this.maybeDisableSortables = this.maybeDisableSortables.bind( this );
1672  
1673              // Modify functionality based on custom activate/deactivate event.
1674              $document.on( 'wp-responsive-activate.wp-responsive', function() {
1675                  self.activate();
1676              }).on( 'wp-responsive-deactivate.wp-responsive', function() {
1677                  self.deactivate();
1678              });
1679  
1680              $( '#wp-admin-bar-menu-toggle a' ).attr( 'aria-expanded', 'false' );
1681  
1682              // Toggle sidebar when toggle is clicked.
1683              $( '#wp-admin-bar-menu-toggle' ).on( 'click.wp-responsive', function( event ) {
1684                  event.preventDefault();
1685  
1686                  // Close any open toolbar submenus.
1687                  $adminbar.find( '.hover' ).removeClass( 'hover' );
1688  
1689                  $wpwrap.toggleClass( 'wp-responsive-open' );
1690                  if ( $wpwrap.hasClass( 'wp-responsive-open' ) ) {
1691                      $(this).find('a').attr( 'aria-expanded', 'true' );
1692                      $( '#adminmenu a:first' ).trigger( 'focus' );
1693                  } else {
1694                      $(this).find('a').attr( 'aria-expanded', 'false' );
1695                  }
1696              } );
1697  
1698              // Add menu events.
1699              $adminmenu.on( 'click.wp-responsive', 'li.wp-has-submenu > a', function( event ) {
1700                  if ( ! $adminmenu.data('wp-responsive') ) {
1701                      return;
1702                  }
1703  
1704                  $( this ).parent( 'li' ).toggleClass( 'selected' );
1705                  event.preventDefault();
1706              });
1707  
1708              self.trigger();
1709              $document.on( 'wp-window-resized.wp-responsive', this.trigger.bind( this ) );
1710  
1711              // This needs to run later as UI Sortable may be initialized when the document is ready.
1712              $window.on( 'load.wp-responsive', this.maybeDisableSortables );
1713              $document.on( 'postbox-toggled', this.maybeDisableSortables );
1714  
1715              // When the screen columns are changed, potentially disable sortables.
1716              $( '#screen-options-wrap input' ).on( 'click', this.maybeDisableSortables );
1717          },
1718  
1719          /**
1720           * Disable sortables if there is only one metabox, or the screen is in one column mode. Otherwise, enable sortables.
1721           *
1722           * @since 5.3.0
1723           *
1724           * @return {void}
1725           */
1726          maybeDisableSortables: function() {
1727              var width = navigator.userAgent.indexOf('AppleWebKit/') > -1 ? $window.width() : window.innerWidth;
1728  
1729              if (
1730                  ( width <= 782 ) ||
1731                  ( 1 >= $sortables.find( '.ui-sortable-handle:visible' ).length && jQuery( '.columns-prefs-1 input' ).prop( 'checked' ) )
1732              ) {
1733                  this.disableSortables();
1734              } else {
1735                  this.enableSortables();
1736              }
1737          },
1738  
1739          /**
1740           * Changes properties of body and admin menu.
1741           *
1742           * Pins and unpins the menu and adds the auto-fold class to the body.
1743           * Makes the admin menu responsive and disables the metabox sortables.
1744           *
1745           * @since 3.8.0
1746           *
1747           * @return {void}
1748           */
1749          activate: function() {
1750              setPinMenu();
1751  
1752              if ( ! $body.hasClass( 'auto-fold' ) ) {
1753                  $body.addClass( 'auto-fold' );
1754              }
1755  
1756              $adminmenu.data( 'wp-responsive', 1 );
1757              this.disableSortables();
1758          },
1759  
1760          /**
1761           * Changes properties of admin menu and enables metabox sortables.
1762           *
1763           * Pin and unpin the menu.
1764           * Removes the responsiveness of the admin menu and enables the metabox sortables.
1765           *
1766           * @since 3.8.0
1767           *
1768           * @return {void}
1769           */
1770          deactivate: function() {
1771              setPinMenu();
1772              $adminmenu.removeData('wp-responsive');
1773  
1774              this.maybeDisableSortables();
1775          },
1776  
1777          /**
1778           * Sets the responsiveness and enables the overlay based on the viewport width.
1779           *
1780           * @since 3.8.0
1781           *
1782           * @return {void}
1783           */
1784          trigger: function() {
1785              var viewportWidth = getViewportWidth();
1786  
1787              // Exclude IE < 9, it doesn't support @media CSS rules.
1788              if ( ! viewportWidth ) {
1789                  return;
1790              }
1791  
1792              if ( viewportWidth <= 782 ) {
1793                  if ( ! wpResponsiveActive ) {
1794                      $document.trigger( 'wp-responsive-activate' );
1795                      wpResponsiveActive = true;
1796                  }
1797              } else {
1798                  if ( wpResponsiveActive ) {
1799                      $document.trigger( 'wp-responsive-deactivate' );
1800                      wpResponsiveActive = false;
1801                  }
1802              }
1803  
1804              if ( viewportWidth <= 480 ) {
1805                  this.enableOverlay();
1806              } else {
1807                  this.disableOverlay();
1808              }
1809  
1810              this.maybeDisableSortables();
1811          },
1812  
1813          /**
1814           * Inserts a responsive overlay and toggles the window.
1815           *
1816           * @since 3.8.0
1817           *
1818           * @return {void}
1819           */
1820          enableOverlay: function() {
1821              if ( $overlay.length === 0 ) {
1822                  $overlay = $( '<div id="wp-responsive-overlay"></div>' )
1823                      .insertAfter( '#wpcontent' )
1824                      .hide()
1825                      .on( 'click.wp-responsive', function() {
1826                          $toolbar.find( '.menupop.hover' ).removeClass( 'hover' );
1827                          $( this ).hide();
1828                      });
1829              }
1830  
1831              $toolbarPopups.on( 'click.wp-responsive', function() {
1832                  $overlay.show();
1833              });
1834          },
1835  
1836          /**
1837           * Disables the responsive overlay and removes the overlay.
1838           *
1839           * @since 3.8.0
1840           *
1841           * @return {void}
1842           */
1843          disableOverlay: function() {
1844              $toolbarPopups.off( 'click.wp-responsive' );
1845              $overlay.hide();
1846          },
1847  
1848          /**
1849           * Disables sortables.
1850           *
1851           * @since 3.8.0
1852           *
1853           * @return {void}
1854           */
1855          disableSortables: function() {
1856              if ( $sortables.length ) {
1857                  try {
1858                      $sortables.sortable( 'disable' );
1859                      $sortables.find( '.ui-sortable-handle' ).addClass( 'is-non-sortable' );
1860                  } catch ( e ) {}
1861              }
1862          },
1863  
1864          /**
1865           * Enables sortables.
1866           *
1867           * @since 3.8.0
1868           *
1869           * @return {void}
1870           */
1871          enableSortables: function() {
1872              if ( $sortables.length ) {
1873                  try {
1874                      $sortables.sortable( 'enable' );
1875                      $sortables.find( '.ui-sortable-handle' ).removeClass( 'is-non-sortable' );
1876                  } catch ( e ) {}
1877              }
1878          }
1879      };
1880  
1881      /**
1882       * Add an ARIA role `button` to elements that behave like UI controls when JavaScript is on.
1883       *
1884       * @since 4.5.0
1885       *
1886       * @return {void}
1887       */
1888  	function aria_button_if_js() {
1889          $( '.aria-button-if-js' ).attr( 'role', 'button' );
1890      }
1891  
1892      $( document ).on( 'ajaxComplete', function() {
1893          aria_button_if_js();
1894      });
1895  
1896      /**
1897       * Get the viewport width.
1898       *
1899       * @since 4.7.0
1900       *
1901       * @return {number|boolean} The current viewport width or false if the
1902       *                          browser doesn't support innerWidth (IE < 9).
1903       */
1904  	function getViewportWidth() {
1905          var viewportWidth = false;
1906  
1907          if ( window.innerWidth ) {
1908              // On phones, window.innerWidth is affected by zooming.
1909              viewportWidth = Math.max( window.innerWidth, document.documentElement.clientWidth );
1910          }
1911  
1912          return viewportWidth;
1913      }
1914  
1915      /**
1916       * Sets the admin menu collapsed/expanded state.
1917       *
1918       * Sets the global variable `menuState` and triggers a custom event passing
1919       * the current menu state.
1920       *
1921       * @since 4.7.0
1922       *
1923       * @return {void}
1924       */
1925  	function setMenuState() {
1926          var viewportWidth = getViewportWidth() || 961;
1927  
1928          if ( viewportWidth <= 782  ) {
1929              menuState = 'responsive';
1930          } else if ( $body.hasClass( 'folded' ) || ( $body.hasClass( 'auto-fold' ) && viewportWidth <= 960 && viewportWidth > 782 ) ) {
1931              menuState = 'folded';
1932          } else {
1933              menuState = 'open';
1934          }
1935  
1936          $document.trigger( 'wp-menu-state-set', { state: menuState } );
1937      }
1938  
1939      // Set the menu state when the window gets resized.
1940      $document.on( 'wp-window-resized.set-menu-state', setMenuState );
1941  
1942      /**
1943       * Sets ARIA attributes on the collapse/expand menu button.
1944       *
1945       * When the admin menu is open or folded, updates the `aria-expanded` and
1946       * `aria-label` attributes of the button to give feedback to assistive
1947       * technologies. In the responsive view, the button is always hidden.
1948       *
1949       * @since 4.7.0
1950       *
1951       * @return {void}
1952       */
1953      $document.on( 'wp-menu-state-set wp-collapse-menu', function( event, eventData ) {
1954          var $collapseButton = $( '#collapse-button' ),
1955              ariaExpanded, ariaLabelText;
1956  
1957          if ( 'folded' === eventData.state ) {
1958              ariaExpanded = 'false';
1959              ariaLabelText = __( 'Expand Main menu' );
1960          } else {
1961              ariaExpanded = 'true';
1962              ariaLabelText = __( 'Collapse Main menu' );
1963          }
1964  
1965          $collapseButton.attr({
1966              'aria-expanded': ariaExpanded,
1967              'aria-label': ariaLabelText
1968          });
1969      });
1970  
1971      window.wpResponsive.init();
1972      setPinMenu();
1973      setMenuState();
1974      currentMenuItemHasPopup();
1975      makeNoticesDismissible();
1976      aria_button_if_js();
1977  
1978      $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 );
1979  
1980      // Set initial focus on a specific element.
1981      $( '.wp-initial-focus' ).trigger( 'focus' );
1982  
1983      // Toggle update details on update-core.php.
1984      $body.on( 'click', '.js-update-details-toggle', function() {
1985          var $updateNotice = $( this ).closest( '.js-update-details' ),
1986              $progressDiv = $( '#' + $updateNotice.data( 'update-details' ) );
1987  
1988          /*
1989           * When clicking on "Show details" move the progress div below the update
1990           * notice. Make sure it gets moved just the first time.
1991           */
1992          if ( ! $progressDiv.hasClass( 'update-details-moved' ) ) {
1993              $progressDiv.insertAfter( $updateNotice ).addClass( 'update-details-moved' );
1994          }
1995  
1996          // Toggle the progress div visibility.
1997          $progressDiv.toggle();
1998          // Toggle the Show Details button expanded state.
1999          $( this ).attr( 'aria-expanded', $progressDiv.is( ':visible' ) );
2000      });
2001  });
2002  
2003  /**
2004   * Hides the update button for expired plugin or theme uploads.
2005   *
2006   * On the "Update plugin/theme from uploaded zip" screen, once the upload has expired,
2007   * hides the "Replace current with uploaded" button and displays a warning.
2008   *
2009   * @since 5.5.0
2010   */
2011  $( function( $ ) {
2012      var $overwrite, $warning;
2013  
2014      if ( ! $body.hasClass( 'update-php' ) ) {
2015          return;
2016      }
2017  
2018      $overwrite = $( 'a.update-from-upload-overwrite' );
2019      $warning   = $( '.update-from-upload-expired' );
2020  
2021      if ( ! $overwrite.length || ! $warning.length ) {
2022          return;
2023      }
2024  
2025      window.setTimeout(
2026          function() {
2027              $overwrite.hide();
2028              $warning.removeClass( 'hidden' );
2029  
2030              if ( window.wp && window.wp.a11y ) {
2031                  window.wp.a11y.speak( $warning.text() );
2032              }
2033          },
2034          7140000 // 119 minutes. The uploaded file is deleted after 2 hours.
2035      );
2036  } );
2037  
2038  // Fire a custom jQuery event at the end of window resize.
2039  ( function() {
2040      var timeout;
2041  
2042      /**
2043       * Triggers the WP window-resize event.
2044       *
2045       * @since 3.8.0
2046       *
2047       * @return {void}
2048       */
2049  	function triggerEvent() {
2050          $document.trigger( 'wp-window-resized' );
2051      }
2052  
2053      /**
2054       * Fires the trigger event again after 200 ms.
2055       *
2056       * @since 3.8.0
2057       *
2058       * @return {void}
2059       */
2060  	function fireOnce() {
2061          window.clearTimeout( timeout );
2062          timeout = window.setTimeout( triggerEvent, 200 );
2063      }
2064  
2065      $window.on( 'resize.wp-fire-once', fireOnce );
2066  }());
2067  
2068  // Make Windows 8 devices play along nicely.
2069  (function(){
2070      if ( '-ms-user-select' in document.documentElement.style && navigator.userAgent.match(/IEMobile\/10\.0/) ) {
2071          var msViewportStyle = document.createElement( 'style' );
2072          msViewportStyle.appendChild(
2073              document.createTextNode( '@-ms-viewport{width:auto!important}' )
2074          );
2075          document.getElementsByTagName( 'head' )[0].appendChild( msViewportStyle );
2076      }
2077  })();
2078  
2079  }( jQuery, window ));


Generated: Sun Apr 28 01:00:03 2024 Cross-referenced by PHPXref 0.7.1