[ 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').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  $document.ready(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          .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.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.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').delegate('a', 'click', 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.focus();
 802      }
 803  } );
 804  
 805  $document.ready( 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       * Shows row actions on focus of its parent container element or any other elements contained within.
1224       *
1225       * @return {void}
1226       */
1227      $( '#wpbody-content' ).on({
1228          focusin: function() {
1229              clearTimeout( transitionTimeout );
1230              focusedRowActions = $( this ).find( '.row-actions' );
1231              // transitionTimeout is necessary for Firefox, but Chrome won't remove the CSS class without a little help.
1232              $( '.row-actions' ).not( this ).removeClass( 'visible' );
1233              focusedRowActions.addClass( 'visible' );
1234          },
1235          focusout: function() {
1236              // Tabbing between post title and .row-actions links needs a brief pause, otherwise
1237              // the .row-actions div gets hidden in transit in some browsers (ahem, Firefox).
1238              transitionTimeout = setTimeout( function() {
1239                  focusedRowActions.removeClass( 'visible' );
1240              }, 30 );
1241          }
1242      }, '.table-view-list .has-row-actions' );
1243  
1244      // Toggle list table rows on small screens.
1245      $( 'tbody' ).on( 'click', '.toggle-row', function() {
1246          $( this ).closest( 'tr' ).toggleClass( 'is-expanded' );
1247      });
1248  
1249      $('#default-password-nag-no').click( function() {
1250          setUserSetting('default_password_nag', 'hide');
1251          $('div.default-password-nag').hide();
1252          return false;
1253      });
1254  
1255      /**
1256       * Handles tab keypresses in theme and plugin editor textareas.
1257       *
1258       * @param {Event} e The event object.
1259       *
1260       * @return {void}
1261       */
1262      $('#newcontent').bind('keydown.wpevent_InsertTab', function(e) {
1263          var el = e.target, selStart, selEnd, val, scroll, sel;
1264  
1265          // After pressing escape key (keyCode: 27), the tab key should tab out of the textarea.
1266          if ( e.keyCode == 27 ) {
1267              // When pressing Escape: Opera 12 and 27 blur form fields, IE 8 clears them.
1268              e.preventDefault();
1269              $(el).data('tab-out', true);
1270              return;
1271          }
1272  
1273          // Only listen for plain tab key (keyCode: 9) without any modifiers.
1274          if ( e.keyCode != 9 || e.ctrlKey || e.altKey || e.shiftKey )
1275              return;
1276  
1277          // After tabbing out, reset it so next time the tab key can be used again.
1278          if ( $(el).data('tab-out') ) {
1279              $(el).data('tab-out', false);
1280              return;
1281          }
1282  
1283          selStart = el.selectionStart;
1284          selEnd = el.selectionEnd;
1285          val = el.value;
1286  
1287          // If any text is selected, replace the selection with a tab character.
1288          if ( document.selection ) {
1289              el.focus();
1290              sel = document.selection.createRange();
1291              sel.text = '\t';
1292          } else if ( selStart >= 0 ) {
1293              scroll = this.scrollTop;
1294              el.value = val.substring(0, selStart).concat('\t', val.substring(selEnd) );
1295              el.selectionStart = el.selectionEnd = selStart + 1;
1296              this.scrollTop = scroll;
1297          }
1298  
1299          // Cancel the regular tab functionality, to prevent losing focus of the textarea.
1300          if ( e.stopPropagation )
1301              e.stopPropagation();
1302          if ( e.preventDefault )
1303              e.preventDefault();
1304      });
1305  
1306      // Reset page number variable for new filters/searches but not for bulk actions. See #17685.
1307      if ( pageInput.length ) {
1308  
1309          /**
1310           * Handles pagination variable when filtering the list table.
1311           *
1312           * Set the pagination argument to the first page when the post-filter form is submitted.
1313           * This happens when pressing the 'filter' button on the list table page.
1314           *
1315           * The pagination argument should not be touched when the bulk action dropdowns are set to do anything.
1316           *
1317           * The form closest to the pageInput is the post-filter form.
1318           *
1319           * @return {void}
1320           */
1321          pageInput.closest('form').submit( function() {
1322              /*
1323               * action = bulk action dropdown at the top of the table
1324               * action2 = bulk action dropdow at the bottom of the table
1325               */
1326              if ( $('select[name="action"]').val() == -1 && $('select[name="action2"]').val() == -1 && pageInput.val() == currentPage )
1327                  pageInput.val('1');
1328          });
1329      }
1330  
1331      /**
1332       * Resets the bulk actions when the search button is clicked.
1333       *
1334       * @return {void}
1335       */
1336      $('.search-box input[type="search"], .search-box input[type="submit"]').mousedown(function () {
1337          $('select[name^="action"]').val('-1');
1338      });
1339  
1340      /**
1341       * Scrolls into view when focus.scroll-into-view is triggered.
1342       *
1343       * @param {Event} e The event object.
1344       *
1345       * @return {void}
1346        */
1347      $('#contextual-help-link, #show-settings-link').on( 'focus.scroll-into-view', function(e){
1348          if ( e.target.scrollIntoView )
1349              e.target.scrollIntoView(false);
1350      });
1351  
1352      /**
1353       * Disables the submit upload buttons when no data is entered.
1354       *
1355       * @return {void}
1356       */
1357      (function(){
1358          var button, input, form = $('form.wp-upload-form');
1359  
1360          // Exit when no upload form is found.
1361          if ( ! form.length )
1362              return;
1363  
1364          button = form.find('input[type="submit"]');
1365          input = form.find('input[type="file"]');
1366  
1367          /**
1368           * Determines if any data is entered in any file upload input.
1369           *
1370           * @since 3.5.0
1371           *
1372           * @return {void}
1373           */
1374  		function toggleUploadButton() {
1375              // When no inputs have a value, disable the upload buttons.
1376              button.prop('disabled', '' === input.map( function() {
1377                  return $(this).val();
1378              }).get().join(''));
1379          }
1380  
1381          // Update the status initially.
1382          toggleUploadButton();
1383          // Update the status when any file input changes.
1384          input.on('change', toggleUploadButton);
1385      })();
1386  
1387      /**
1388       * Pins the menu while distraction-free writing is enabled.
1389       *
1390       * @param {Event} event Event data.
1391       *
1392       * @since 4.1.0
1393       *
1394       * @return {void}
1395       */
1396  	function pinMenu( event ) {
1397          var windowPos = $window.scrollTop(),
1398              resizing = ! event || event.type !== 'scroll';
1399  
1400          if ( isIOS || $adminmenu.data( 'wp-responsive' ) ) {
1401              return;
1402          }
1403  
1404          /*
1405           * When the menu is higher than the window and smaller than the entire page.
1406           * It should be adjusted to be able to see the entire menu.
1407           *
1408           * Otherwise it can be accessed normally.
1409           */
1410          if ( height.menu + height.adminbar < height.window ||
1411              height.menu + height.adminbar + 20 > height.wpwrap ) {
1412              unpinMenu();
1413              return;
1414          }
1415  
1416          menuIsPinned = true;
1417  
1418          // If the menu is higher than the window, compensate on scroll.
1419          if ( height.menu + height.adminbar > height.window ) {
1420              // Check for overscrolling, this happens when swiping up at the top of the document in modern browsers.
1421              if ( windowPos < 0 ) {
1422                  // Stick the menu to the top.
1423                  if ( ! pinnedMenuTop ) {
1424                      pinnedMenuTop = true;
1425                      pinnedMenuBottom = false;
1426  
1427                      $adminMenuWrap.css({
1428                          position: 'fixed',
1429                          top: '',
1430                          bottom: ''
1431                      });
1432                  }
1433  
1434                  return;
1435              } else if ( windowPos + height.window > $document.height() - 1 ) {
1436                  // When overscrolling at the bottom, stick the menu to the bottom.
1437                  if ( ! pinnedMenuBottom ) {
1438                      pinnedMenuBottom = true;
1439                      pinnedMenuTop = false;
1440  
1441                      $adminMenuWrap.css({
1442                          position: 'fixed',
1443                          top: '',
1444                          bottom: 0
1445                      });
1446                  }
1447  
1448                  return;
1449              }
1450  
1451              if ( windowPos > lastScrollPosition ) {
1452                  // When a down scroll has been detected.
1453  
1454                  // If it was pinned to the top, unpin and calculate relative scroll.
1455                  if ( pinnedMenuTop ) {
1456                      pinnedMenuTop = false;
1457                      // Calculate new offset position.
1458                      menuTop = $adminMenuWrap.offset().top - height.adminbar - ( windowPos - lastScrollPosition );
1459  
1460                      if ( menuTop + height.menu + height.adminbar < windowPos + height.window ) {
1461                          menuTop = windowPos + height.window - height.menu - height.adminbar;
1462                      }
1463  
1464                      $adminMenuWrap.css({
1465                          position: 'absolute',
1466                          top: menuTop,
1467                          bottom: ''
1468                      });
1469                  } else if ( ! pinnedMenuBottom && $adminMenuWrap.offset().top + height.menu < windowPos + height.window ) {
1470                      // Pin it to the bottom.
1471                      pinnedMenuBottom = true;
1472  
1473                      $adminMenuWrap.css({
1474                          position: 'fixed',
1475                          top: '',
1476                          bottom: 0
1477                      });
1478                  }
1479              } else if ( windowPos < lastScrollPosition ) {
1480                  // When a scroll up is detected.
1481  
1482                  // If it was pinned to the bottom, unpin and calculate relative scroll.
1483                  if ( pinnedMenuBottom ) {
1484                      pinnedMenuBottom = false;
1485  
1486                      // Calculate new offset position.
1487                      menuTop = $adminMenuWrap.offset().top - height.adminbar + ( lastScrollPosition - windowPos );
1488  
1489                      if ( menuTop + height.menu > windowPos + height.window ) {
1490                          menuTop = windowPos;
1491                      }
1492  
1493                      $adminMenuWrap.css({
1494                          position: 'absolute',
1495                          top: menuTop,
1496                          bottom: ''
1497                      });
1498                  } else if ( ! pinnedMenuTop && $adminMenuWrap.offset().top >= windowPos + height.adminbar ) {
1499  
1500                      // Pin it to the top.
1501                      pinnedMenuTop = true;
1502  
1503                      $adminMenuWrap.css({
1504                          position: 'fixed',
1505                          top: '',
1506                          bottom: ''
1507                      });
1508                  }
1509              } else if ( resizing ) {
1510                  // Window is being resized.
1511  
1512                  pinnedMenuTop = pinnedMenuBottom = false;
1513  
1514                  // Calculate the new offset.
1515                  menuTop = windowPos + height.window - height.menu - height.adminbar - 1;
1516  
1517                  if ( menuTop > 0 ) {
1518                      $adminMenuWrap.css({
1519                          position: 'absolute',
1520                          top: menuTop,
1521                          bottom: ''
1522                      });
1523                  } else {
1524                      unpinMenu();
1525                  }
1526              }
1527          }
1528  
1529          lastScrollPosition = windowPos;
1530      }
1531  
1532      /**
1533       * Determines the height of certain elements.
1534       *
1535       * @since 4.1.0
1536       *
1537       * @return {void}
1538       */
1539  	function resetHeights() {
1540          height = {
1541              window: $window.height(),
1542              wpwrap: $wpwrap.height(),
1543              adminbar: $adminbar.height(),
1544              menu: $adminMenuWrap.height()
1545          };
1546      }
1547  
1548      /**
1549       * Unpins the menu.
1550       *
1551       * @since 4.1.0
1552       *
1553       * @return {void}
1554       */
1555  	function unpinMenu() {
1556          if ( isIOS || ! menuIsPinned ) {
1557              return;
1558          }
1559  
1560          pinnedMenuTop = pinnedMenuBottom = menuIsPinned = false;
1561          $adminMenuWrap.css({
1562              position: '',
1563              top: '',
1564              bottom: ''
1565          });
1566      }
1567  
1568      /**
1569       * Pins and unpins the menu when applicable.
1570       *
1571       * @since 4.1.0
1572       *
1573       * @return {void}
1574       */
1575  	function setPinMenu() {
1576          resetHeights();
1577  
1578          if ( $adminmenu.data('wp-responsive') ) {
1579              $body.removeClass( 'sticky-menu' );
1580              unpinMenu();
1581          } else if ( height.menu + height.adminbar > height.window ) {
1582              pinMenu();
1583              $body.removeClass( 'sticky-menu' );
1584          } else {
1585              $body.addClass( 'sticky-menu' );
1586              unpinMenu();
1587          }
1588      }
1589  
1590      if ( ! isIOS ) {
1591          $window.on( 'scroll.pin-menu', pinMenu );
1592          $document.on( 'tinymce-editor-init.pin-menu', function( event, editor ) {
1593              editor.on( 'wp-autoresize', resetHeights );
1594          });
1595      }
1596  
1597      /**
1598       * Changes the sortables and responsiveness of metaboxes.
1599       *
1600       * @since 3.8.0
1601       *
1602       * @return {void}
1603       */
1604      window.wpResponsive = {
1605  
1606          /**
1607           * Initializes the wpResponsive object.
1608           *
1609           * @since 3.8.0
1610           *
1611           * @return {void}
1612           */
1613          init: function() {
1614              var self = this;
1615  
1616              this.maybeDisableSortables = this.maybeDisableSortables.bind( this );
1617  
1618              // Modify functionality based on custom activate/deactivate event.
1619              $document.on( 'wp-responsive-activate.wp-responsive', function() {
1620                  self.activate();
1621              }).on( 'wp-responsive-deactivate.wp-responsive', function() {
1622                  self.deactivate();
1623              });
1624  
1625              $( '#wp-admin-bar-menu-toggle a' ).attr( 'aria-expanded', 'false' );
1626  
1627              // Toggle sidebar when toggle is clicked.
1628              $( '#wp-admin-bar-menu-toggle' ).on( 'click.wp-responsive', function( event ) {
1629                  event.preventDefault();
1630  
1631                  // Close any open toolbar submenus.
1632                  $adminbar.find( '.hover' ).removeClass( 'hover' );
1633  
1634                  $wpwrap.toggleClass( 'wp-responsive-open' );
1635                  if ( $wpwrap.hasClass( 'wp-responsive-open' ) ) {
1636                      $(this).find('a').attr( 'aria-expanded', 'true' );
1637                      $( '#adminmenu a:first' ).focus();
1638                  } else {
1639                      $(this).find('a').attr( 'aria-expanded', 'false' );
1640                  }
1641              } );
1642  
1643              // Add menu events.
1644              $adminmenu.on( 'click.wp-responsive', 'li.wp-has-submenu > a', function( event ) {
1645                  if ( ! $adminmenu.data('wp-responsive') ) {
1646                      return;
1647                  }
1648  
1649                  $( this ).parent( 'li' ).toggleClass( 'selected' );
1650                  event.preventDefault();
1651              });
1652  
1653              self.trigger();
1654              $document.on( 'wp-window-resized.wp-responsive', $.proxy( this.trigger, this ) );
1655  
1656              // This needs to run later as UI Sortable may be initialized later on $(document).ready().
1657              $window.on( 'load.wp-responsive', this.maybeDisableSortables );
1658              $document.on( 'postbox-toggled', this.maybeDisableSortables );
1659  
1660              // When the screen columns are changed, potentially disable sortables.
1661              $( '#screen-options-wrap input' ).on( 'click', this.maybeDisableSortables );
1662          },
1663  
1664          /**
1665           * Disable sortables if there is only one metabox, or the screen is in one column mode. Otherwise, enable sortables.
1666           *
1667           * @since 5.3.0
1668           *
1669           * @return {void}
1670           */
1671          maybeDisableSortables: function() {
1672              var width = navigator.userAgent.indexOf('AppleWebKit/') > -1 ? $window.width() : window.innerWidth;
1673  
1674              if (
1675                  ( width <= 782 ) ||
1676                  ( 1 >= $sortables.find( '.ui-sortable-handle:visible' ).length && jQuery( '.columns-prefs-1 input' ).prop( 'checked' ) )
1677              ) {
1678                  this.disableSortables();
1679              } else {
1680                  this.enableSortables();
1681              }
1682          },
1683  
1684          /**
1685           * Changes properties of body and admin menu.
1686           *
1687           * Pins and unpins the menu and adds the auto-fold class to the body.
1688           * Makes the admin menu responsive and disables the metabox sortables.
1689           *
1690           * @since 3.8.0
1691           *
1692           * @return {void}
1693           */
1694          activate: function() {
1695              setPinMenu();
1696  
1697              if ( ! $body.hasClass( 'auto-fold' ) ) {
1698                  $body.addClass( 'auto-fold' );
1699              }
1700  
1701              $adminmenu.data( 'wp-responsive', 1 );
1702              this.disableSortables();
1703          },
1704  
1705          /**
1706           * Changes properties of admin menu and enables metabox sortables.
1707           *
1708           * Pin and unpin the menu.
1709           * Removes the responsiveness of the admin menu and enables the metabox sortables.
1710           *
1711           * @since 3.8.0
1712           *
1713           * @return {void}
1714           */
1715          deactivate: function() {
1716              setPinMenu();
1717              $adminmenu.removeData('wp-responsive');
1718  
1719              this.maybeDisableSortables();
1720          },
1721  
1722          /**
1723           * Sets the responsiveness and enables the overlay based on the viewport width.
1724           *
1725           * @since 3.8.0
1726           *
1727           * @return {void}
1728           */
1729          trigger: function() {
1730              var viewportWidth = getViewportWidth();
1731  
1732              // Exclude IE < 9, it doesn't support @media CSS rules.
1733              if ( ! viewportWidth ) {
1734                  return;
1735              }
1736  
1737              if ( viewportWidth <= 782 ) {
1738                  if ( ! wpResponsiveActive ) {
1739                      $document.trigger( 'wp-responsive-activate' );
1740                      wpResponsiveActive = true;
1741                  }
1742              } else {
1743                  if ( wpResponsiveActive ) {
1744                      $document.trigger( 'wp-responsive-deactivate' );
1745                      wpResponsiveActive = false;
1746                  }
1747              }
1748  
1749              if ( viewportWidth <= 480 ) {
1750                  this.enableOverlay();
1751              } else {
1752                  this.disableOverlay();
1753              }
1754  
1755              this.maybeDisableSortables();
1756          },
1757  
1758          /**
1759           * Inserts a responsive overlay and toggles the window.
1760           *
1761           * @since 3.8.0
1762           *
1763           * @return {void}
1764           */
1765          enableOverlay: function() {
1766              if ( $overlay.length === 0 ) {
1767                  $overlay = $( '<div id="wp-responsive-overlay"></div>' )
1768                      .insertAfter( '#wpcontent' )
1769                      .hide()
1770                      .on( 'click.wp-responsive', function() {
1771                          $toolbar.find( '.menupop.hover' ).removeClass( 'hover' );
1772                          $( this ).hide();
1773                      });
1774              }
1775  
1776              $toolbarPopups.on( 'click.wp-responsive', function() {
1777                  $overlay.show();
1778              });
1779          },
1780  
1781          /**
1782           * Disables the responsive overlay and removes the overlay.
1783           *
1784           * @since 3.8.0
1785           *
1786           * @return {void}
1787           */
1788          disableOverlay: function() {
1789              $toolbarPopups.off( 'click.wp-responsive' );
1790              $overlay.hide();
1791          },
1792  
1793          /**
1794           * Disables sortables.
1795           *
1796           * @since 3.8.0
1797           *
1798           * @return {void}
1799           */
1800          disableSortables: function() {
1801              if ( $sortables.length ) {
1802                  try {
1803                      $sortables.sortable( 'disable' );
1804                      $sortables.find( '.ui-sortable-handle' ).addClass( 'is-non-sortable' );
1805                  } catch ( e ) {}
1806              }
1807          },
1808  
1809          /**
1810           * Enables sortables.
1811           *
1812           * @since 3.8.0
1813           *
1814           * @return {void}
1815           */
1816          enableSortables: function() {
1817              if ( $sortables.length ) {
1818                  try {
1819                      $sortables.sortable( 'enable' );
1820                      $sortables.find( '.ui-sortable-handle' ).removeClass( 'is-non-sortable' );
1821                  } catch ( e ) {}
1822              }
1823          }
1824      };
1825  
1826      /**
1827       * Add an ARIA role `button` to elements that behave like UI controls when JavaScript is on.
1828       *
1829       * @since 4.5.0
1830       *
1831       * @return {void}
1832       */
1833  	function aria_button_if_js() {
1834          $( '.aria-button-if-js' ).attr( 'role', 'button' );
1835      }
1836  
1837      $( document ).ajaxComplete( function() {
1838          aria_button_if_js();
1839      });
1840  
1841      /**
1842       * Get the viewport width.
1843       *
1844       * @since 4.7.0
1845       *
1846       * @return {number|boolean} The current viewport width or false if the
1847       *                          browser doesn't support innerWidth (IE < 9).
1848       */
1849  	function getViewportWidth() {
1850          var viewportWidth = false;
1851  
1852          if ( window.innerWidth ) {
1853              // On phones, window.innerWidth is affected by zooming.
1854              viewportWidth = Math.max( window.innerWidth, document.documentElement.clientWidth );
1855          }
1856  
1857          return viewportWidth;
1858      }
1859  
1860      /**
1861       * Sets the admin menu collapsed/expanded state.
1862       *
1863       * Sets the global variable `menuState` and triggers a custom event passing
1864       * the current menu state.
1865       *
1866       * @since 4.7.0
1867       *
1868       * @return {void}
1869       */
1870  	function setMenuState() {
1871          var viewportWidth = getViewportWidth() || 961;
1872  
1873          if ( viewportWidth <= 782  ) {
1874              menuState = 'responsive';
1875          } else if ( $body.hasClass( 'folded' ) || ( $body.hasClass( 'auto-fold' ) && viewportWidth <= 960 && viewportWidth > 782 ) ) {
1876              menuState = 'folded';
1877          } else {
1878              menuState = 'open';
1879          }
1880  
1881          $document.trigger( 'wp-menu-state-set', { state: menuState } );
1882      }
1883  
1884      // Set the menu state when the window gets resized.
1885      $document.on( 'wp-window-resized.set-menu-state', setMenuState );
1886  
1887      /**
1888       * Sets ARIA attributes on the collapse/expand menu button.
1889       *
1890       * When the admin menu is open or folded, updates the `aria-expanded` and
1891       * `aria-label` attributes of the button to give feedback to assistive
1892       * technologies. In the responsive view, the button is always hidden.
1893       *
1894       * @since 4.7.0
1895       *
1896       * @return {void}
1897       */
1898      $document.on( 'wp-menu-state-set wp-collapse-menu', function( event, eventData ) {
1899          var $collapseButton = $( '#collapse-button' ),
1900              ariaExpanded, ariaLabelText;
1901  
1902          if ( 'folded' === eventData.state ) {
1903              ariaExpanded = 'false';
1904              ariaLabelText = __( 'Expand Main menu' );
1905          } else {
1906              ariaExpanded = 'true';
1907              ariaLabelText = __( 'Collapse Main menu' );
1908          }
1909  
1910          $collapseButton.attr({
1911              'aria-expanded': ariaExpanded,
1912              'aria-label': ariaLabelText
1913          });
1914      });
1915  
1916      window.wpResponsive.init();
1917      setPinMenu();
1918      setMenuState();
1919      currentMenuItemHasPopup();
1920      makeNoticesDismissible();
1921      aria_button_if_js();
1922  
1923      $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 );
1924  
1925      // Set initial focus on a specific element.
1926      $( '.wp-initial-focus' ).focus();
1927  
1928      // Toggle update details on update-core.php.
1929      $body.on( 'click', '.js-update-details-toggle', function() {
1930          var $updateNotice = $( this ).closest( '.js-update-details' ),
1931              $progressDiv = $( '#' + $updateNotice.data( 'update-details' ) );
1932  
1933          /*
1934           * When clicking on "Show details" move the progress div below the update
1935           * notice. Make sure it gets moved just the first time.
1936           */
1937          if ( ! $progressDiv.hasClass( 'update-details-moved' ) ) {
1938              $progressDiv.insertAfter( $updateNotice ).addClass( 'update-details-moved' );
1939          }
1940  
1941          // Toggle the progress div visibility.
1942          $progressDiv.toggle();
1943          // Toggle the Show Details button expanded state.
1944          $( this ).attr( 'aria-expanded', $progressDiv.is( ':visible' ) );
1945      });
1946  });
1947  
1948  /**
1949   * Hides the update button for expired plugin or theme uploads.
1950   *
1951   * On the "Update plugin/theme from uploaded zip" screen, once the upload has expired,
1952   * hides the "Replace current with uploaded" button and displays a warning.
1953   *
1954   * @since 5.5.0
1955   */
1956  $document.ready( function( $ ) {
1957      var $overwrite, $warning;
1958  
1959      if ( ! $body.hasClass( 'update-php' ) ) {
1960          return;
1961      }
1962  
1963      $overwrite = $( 'a.update-from-upload-overwrite' );
1964      $warning   = $( '.update-from-upload-expired' );
1965  
1966      if ( ! $overwrite.length || ! $warning.length ) {
1967          return;
1968      }
1969  
1970      window.setTimeout(
1971          function() {
1972              $overwrite.hide();
1973              $warning.removeClass( 'hidden' );
1974  
1975              if ( window.wp && window.wp.a11y ) {
1976                  window.wp.a11y.speak( $warning.text() );
1977              }
1978          },
1979          7140000 // 119 minutes. The uploaded file is deleted after 2 hours.
1980      );
1981  } );
1982  
1983  // Fire a custom jQuery event at the end of window resize.
1984  ( function() {
1985      var timeout;
1986  
1987      /**
1988       * Triggers the WP window-resize event.
1989       *
1990       * @since 3.8.0
1991       *
1992       * @return {void}
1993       */
1994  	function triggerEvent() {
1995          $document.trigger( 'wp-window-resized' );
1996      }
1997  
1998      /**
1999       * Fires the trigger event again after 200 ms.
2000       *
2001       * @since 3.8.0
2002       *
2003       * @return {void}
2004       */
2005  	function fireOnce() {
2006          window.clearTimeout( timeout );
2007          timeout = window.setTimeout( triggerEvent, 200 );
2008      }
2009  
2010      $window.on( 'resize.wp-fire-once', fireOnce );
2011  }());
2012  
2013  // Make Windows 8 devices play along nicely.
2014  (function(){
2015      if ( '-ms-user-select' in document.documentElement.style && navigator.userAgent.match(/IEMobile\/10\.0/) ) {
2016          var msViewportStyle = document.createElement( 'style' );
2017          msViewportStyle.appendChild(
2018              document.createTextNode( '@-ms-viewport{width:auto!important}' )
2019          );
2020          document.getElementsByTagName( 'head' )[0].appendChild( msViewportStyle );
2021      }
2022  })();
2023  
2024  }( jQuery, window ));


Generated: Sun Nov 29 01:00:04 2020 Cross-referenced by PHPXref 0.7.1