[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

   1  /**
   2   * Contains the postboxes logic, opening and closing postboxes, reordering and saving
   3   * the state and ordering to the database.
   4   *
   5   * @since 2.5.0
   6   * @requires jQuery
   7   * @output wp-admin/js/postbox.js
   8   */
   9  
  10  /* global ajaxurl, postboxes */
  11  
  12  (function($) {
  13      var $document = $( document ),
  14          __ = wp.i18n.__;
  15  
  16      /**
  17       * This object contains all function to handle the behavior of the post boxes. The post boxes are the boxes you see
  18       * around the content on the edit page.
  19       *
  20       * @since 2.7.0
  21       *
  22       * @namespace postboxes
  23       *
  24       * @type {Object}
  25       */
  26      window.postboxes = {
  27  
  28          /**
  29           * Handles a click on either the postbox heading or the postbox open/close icon.
  30           *
  31           * Opens or closes the postbox. Expects `this` to equal the clicked element.
  32           * Calls postboxes.pbshow if the postbox has been opened, calls postboxes.pbhide
  33           * if the postbox has been closed.
  34           *
  35           * @since 4.4.0
  36           *
  37           * @memberof postboxes
  38           *
  39           * @fires postboxes#postbox-toggled
  40           *
  41           * @return {void}
  42           */
  43          handle_click : function () {
  44              var $el = $( this ),
  45                  p = $el.closest( '.postbox' ),
  46                  id = p.attr( 'id' ),
  47                  ariaExpandedValue;
  48  
  49              if ( 'dashboard_browser_nag' === id ) {
  50                  return;
  51              }
  52  
  53              p.toggleClass( 'closed' );
  54              ariaExpandedValue = ! p.hasClass( 'closed' );
  55  
  56              if ( $el.hasClass( 'handlediv' ) ) {
  57                  // The handle button was clicked.
  58                  $el.attr( 'aria-expanded', ariaExpandedValue );
  59              } else {
  60                  // The handle heading was clicked.
  61                  $el.closest( '.postbox' ).find( 'button.handlediv' )
  62                      .attr( 'aria-expanded', ariaExpandedValue );
  63              }
  64  
  65              if ( postboxes.page !== 'press-this' ) {
  66                  postboxes.save_state( postboxes.page );
  67              }
  68  
  69              if ( id ) {
  70                  if ( !p.hasClass('closed') && typeof postboxes.pbshow === 'function' ) {
  71                      postboxes.pbshow( id );
  72                  } else if ( p.hasClass('closed') && typeof postboxes.pbhide === 'function' ) {
  73                      postboxes.pbhide( id );
  74                  }
  75              }
  76  
  77              /**
  78               * Fires when a postbox has been opened or closed.
  79               *
  80               * Contains a jQuery object with the relevant postbox element.
  81               *
  82               * @since 4.0.0
  83               * @ignore
  84               *
  85               * @event postboxes#postbox-toggled
  86               * @type {Object}
  87               */
  88              $document.trigger( 'postbox-toggled', p );
  89          },
  90  
  91          /**
  92           * Handles clicks on the move up/down buttons.
  93           *
  94           * @since 5.5.0
  95           *
  96           * @return {void}
  97           */
  98          handleOrder: function() {
  99              var button = $( this ),
 100                  postbox = button.closest( '.postbox' ),
 101                  postboxId = postbox.attr( 'id' ),
 102                  postboxesWithinSortables = postbox.closest( '.meta-box-sortables' ).find( '.postbox:visible' ),
 103                  postboxesWithinSortablesCount = postboxesWithinSortables.length,
 104                  postboxWithinSortablesIndex = postboxesWithinSortables.index( postbox ),
 105                  firstOrLastPositionMessage;
 106  
 107              if ( 'dashboard_browser_nag' === postboxId ) {
 108                  return;
 109              }
 110  
 111              // If on the first or last position, do nothing and send an audible message to screen reader users.
 112              if ( 'true' === button.attr( 'aria-disabled' ) ) {
 113                  firstOrLastPositionMessage = button.hasClass( 'handle-order-higher' ) ?
 114                      __( 'The box is on the first position' ) :
 115                      __( 'The box is on the last position' );
 116  
 117                  wp.a11y.speak( firstOrLastPositionMessage );
 118                  return;
 119              }
 120  
 121              // Move a postbox up.
 122              if ( button.hasClass( 'handle-order-higher' ) ) {
 123                  // If the box is first within a sortable area, move it to the previous sortable area.
 124                  if ( 0 === postboxWithinSortablesIndex ) {
 125                      postboxes.handleOrderBetweenSortables( 'previous', button, postbox );
 126                      return;
 127                  }
 128  
 129                  postbox.prevAll( '.postbox:visible' ).eq( 0 ).before( postbox );
 130                  button.trigger( 'focus' );
 131                  postboxes.updateOrderButtonsProperties();
 132                  postboxes.save_order( postboxes.page );
 133              }
 134  
 135              // Move a postbox down.
 136              if ( button.hasClass( 'handle-order-lower' ) ) {
 137                  // If the box is last within a sortable area, move it to the next sortable area.
 138                  if ( postboxWithinSortablesIndex + 1 === postboxesWithinSortablesCount ) {
 139                      postboxes.handleOrderBetweenSortables( 'next', button, postbox );
 140                      return;
 141                  }
 142  
 143                  postbox.nextAll( '.postbox:visible' ).eq( 0 ).after( postbox );
 144                  button.trigger( 'focus' );
 145                  postboxes.updateOrderButtonsProperties();
 146                  postboxes.save_order( postboxes.page );
 147              }
 148  
 149          },
 150  
 151          /**
 152           * Moves postboxes between the sortables areas.
 153           *
 154           * @since 5.5.0
 155           *
 156           * @param {string} position The "previous" or "next" sortables area.
 157           * @param {Object} button   The jQuery object representing the button that was clicked.
 158           * @param {Object} postbox  The jQuery object representing the postbox to be moved.
 159           *
 160           * @return {void}
 161           */
 162          handleOrderBetweenSortables: function( position, button, postbox ) {
 163              var closestSortablesId = button.closest( '.meta-box-sortables' ).attr( 'id' ),
 164                  sortablesIds = [],
 165                  sortablesIndex,
 166                  detachedPostbox;
 167  
 168              // Get the list of sortables within the page.
 169              $( '.meta-box-sortables:visible' ).each( function() {
 170                  sortablesIds.push( $( this ).attr( 'id' ) );
 171              });
 172  
 173              // Return if there's only one visible sortables area, e.g. in the block editor page.
 174              if ( 1 === sortablesIds.length ) {
 175                  return;
 176              }
 177  
 178              // Find the index of the current sortables area within all the sortable areas.
 179              sortablesIndex = $.inArray( closestSortablesId, sortablesIds );
 180              // Detach the postbox to be moved.
 181              detachedPostbox = postbox.detach();
 182  
 183              // Move the detached postbox to its new position.
 184              if ( 'previous' === position ) {
 185                  $( detachedPostbox ).appendTo( '#' + sortablesIds[ sortablesIndex - 1 ] );
 186              }
 187  
 188              if ( 'next' === position ) {
 189                  $( detachedPostbox ).prependTo( '#' + sortablesIds[ sortablesIndex + 1 ] );
 190              }
 191  
 192              postboxes._mark_area();
 193              button.focus();
 194              postboxes.updateOrderButtonsProperties();
 195              postboxes.save_order( postboxes.page );
 196          },
 197  
 198          /**
 199           * Update the move buttons properties depending on the postbox position.
 200           *
 201           * @since 5.5.0
 202           *
 203           * @return {void}
 204           */
 205          updateOrderButtonsProperties: function() {
 206              var firstSortablesId = $( '.meta-box-sortables:visible:first' ).attr( 'id' ),
 207                  lastSortablesId = $( '.meta-box-sortables:visible:last' ).attr( 'id' ),
 208                  firstPostbox = $( '.postbox:visible:first' ),
 209                  lastPostbox = $( '.postbox:visible:last' ),
 210                  firstPostboxId = firstPostbox.attr( 'id' ),
 211                  lastPostboxId = lastPostbox.attr( 'id' ),
 212                  firstPostboxSortablesId = firstPostbox.closest( '.meta-box-sortables' ).attr( 'id' ),
 213                  lastPostboxSortablesId = lastPostbox.closest( '.meta-box-sortables' ).attr( 'id' ),
 214                  moveUpButtons = $( '.handle-order-higher' ),
 215                  moveDownButtons = $( '.handle-order-lower' );
 216  
 217              // Enable all buttons as a reset first.
 218              moveUpButtons
 219                  .attr( 'aria-disabled', 'false' )
 220                  .removeClass( 'hidden' );
 221              moveDownButtons
 222                  .attr( 'aria-disabled', 'false' )
 223                  .removeClass( 'hidden' );
 224  
 225              // When there's only one "sortables" area (e.g. in the block editor) and only one visible postbox, hide the buttons.
 226              if ( firstSortablesId === lastSortablesId && firstPostboxId === lastPostboxId ) {
 227                  moveUpButtons.addClass( 'hidden' );
 228                  moveDownButtons.addClass( 'hidden' );
 229              }
 230  
 231              // Set an aria-disabled=true attribute on the first visible "move" buttons.
 232              if ( firstSortablesId === firstPostboxSortablesId ) {
 233                  $( firstPostbox ).find( '.handle-order-higher' ).attr( 'aria-disabled', 'true' );
 234              }
 235  
 236              // Set an aria-disabled=true attribute on the last visible "move" buttons.
 237              if ( lastSortablesId === lastPostboxSortablesId ) {
 238                  $( '.postbox:visible .handle-order-lower' ).last().attr( 'aria-disabled', 'true' );
 239              }
 240          },
 241  
 242          /**
 243           * Adds event handlers to all postboxes and screen option on the current page.
 244           *
 245           * @since 2.7.0
 246           *
 247           * @memberof postboxes
 248           *
 249           * @param {string} page The page we are currently on.
 250           * @param {Object} [args]
 251           * @param {Function} args.pbshow A callback that is called when a postbox opens.
 252           * @param {Function} args.pbhide A callback that is called when a postbox closes.
 253           * @return {void}
 254           */
 255          add_postbox_toggles : function (page, args) {
 256              var $handles = $( '.postbox .hndle, .postbox .handlediv' ),
 257                  $orderButtons = $( '.postbox .handle-order-higher, .postbox .handle-order-lower' );
 258  
 259              this.page = page;
 260              this.init( page, args );
 261  
 262              $handles.on( 'click.postboxes', this.handle_click );
 263  
 264              // Handle the order of the postboxes.
 265              $orderButtons.on( 'click.postboxes', this.handleOrder );
 266  
 267              /**
 268               * @since 2.7.0
 269               */
 270              $('.postbox .hndle a').on( 'click', function(e) {
 271                  e.stopPropagation();
 272              });
 273  
 274              /**
 275               * Hides a postbox.
 276               *
 277               * Event handler for the postbox dismiss button. After clicking the button
 278               * the postbox will be hidden.
 279               *
 280               * As of WordPress 5.5, this is only used for the browser update nag.
 281               *
 282               * @since 3.2.0
 283               *
 284               * @return {void}
 285               */
 286              $( '.postbox a.dismiss' ).on( 'click.postboxes', function( e ) {
 287                  var hide_id = $(this).parents('.postbox').attr('id') + '-hide';
 288                  e.preventDefault();
 289                  $( '#' + hide_id ).prop('checked', false).triggerHandler('click');
 290              });
 291  
 292              /**
 293               * Hides the postbox element
 294               *
 295               * Event handler for the screen options checkboxes. When a checkbox is
 296               * clicked this function will hide or show the relevant postboxes.
 297               *
 298               * @since 2.7.0
 299               * @ignore
 300               *
 301               * @fires postboxes#postbox-toggled
 302               *
 303               * @return {void}
 304               */
 305              $('.hide-postbox-tog').on('click.postboxes', function() {
 306                  var $el = $(this),
 307                      boxId = $el.val(),
 308                      $postbox = $( '#' + boxId );
 309  
 310                  if ( $el.prop( 'checked' ) ) {
 311                      $postbox.show();
 312                      if ( typeof postboxes.pbshow === 'function' ) {
 313                          postboxes.pbshow( boxId );
 314                      }
 315                  } else {
 316                      $postbox.hide();
 317                      if ( typeof postboxes.pbhide === 'function' ) {
 318                          postboxes.pbhide( boxId );
 319                      }
 320                  }
 321  
 322                  postboxes.save_state( page );
 323                  postboxes._mark_area();
 324  
 325                  /**
 326                   * @since 4.0.0
 327                   * @see postboxes.handle_click
 328                   */
 329                  $document.trigger( 'postbox-toggled', $postbox );
 330              });
 331  
 332              /**
 333               * Changes the amount of columns based on the layout preferences.
 334               *
 335               * @since 2.8.0
 336               *
 337               * @return {void}
 338               */
 339              $('.columns-prefs input[type="radio"]').on('click.postboxes', function(){
 340                  var n = parseInt($(this).val(), 10);
 341  
 342                  if ( n ) {
 343                      postboxes._pb_edit(n);
 344                      postboxes.save_order( page );
 345                  }
 346              });
 347          },
 348  
 349          /**
 350           * Initializes all the postboxes, mainly their sortable behavior.
 351           *
 352           * @since 2.7.0
 353           *
 354           * @memberof postboxes
 355           *
 356           * @param {string} page The page we are currently on.
 357           * @param {Object} [args={}] The arguments for the postbox initializer.
 358           * @param {Function} args.pbshow A callback that is called when a postbox opens.
 359           * @param {Function} args.pbhide A callback that is called when a postbox
 360           *                               closes.
 361           *
 362           * @return {void}
 363           */
 364          init : function(page, args) {
 365              var isMobile = $( document.body ).hasClass( 'mobile' ),
 366                  $handleButtons = $( '.postbox .handlediv' );
 367  
 368              $.extend( this, args || {} );
 369              $('.meta-box-sortables').sortable({
 370                  placeholder: 'sortable-placeholder',
 371                  connectWith: '.meta-box-sortables',
 372                  items: '.postbox',
 373                  handle: '.hndle',
 374                  cursor: 'move',
 375                  delay: ( isMobile ? 200 : 0 ),
 376                  distance: 2,
 377                  tolerance: 'pointer',
 378                  forcePlaceholderSize: true,
 379                  helper: function( event, element ) {
 380                      /* `helper: 'clone'` is equivalent to `return element.clone();`
 381                       * Cloning a checked radio and then inserting that clone next to the original
 382                       * radio unchecks the original radio (since only one of the two can be checked).
 383                       * We get around this by renaming the helper's inputs' name attributes so that,
 384                       * when the helper is inserted into the DOM for the sortable, no radios are
 385                       * duplicated, and no original radio gets unchecked.
 386                       */
 387                      return element.clone()
 388                          .find( ':input' )
 389                              .attr( 'name', function( i, currentName ) {
 390                                  return 'sort_' + parseInt( Math.random() * 100000, 10 ).toString() + '_' + currentName;
 391                              } )
 392                          .end();
 393                  },
 394                  opacity: 0.65,
 395                  start: function() {
 396                      $( 'body' ).addClass( 'is-dragging-metaboxes' );
 397                      // Refresh the cached positions of all the sortable items so that the min-height set while dragging works.
 398                      $( '.meta-box-sortables' ).sortable( 'refreshPositions' );
 399                  },
 400                  stop: function() {
 401                      var $el = $( this );
 402  
 403                      $( 'body' ).removeClass( 'is-dragging-metaboxes' );
 404  
 405                      if ( $el.find( '#dashboard_browser_nag' ).is( ':visible' ) && 'dashboard_browser_nag' != this.firstChild.id ) {
 406                          $el.sortable('cancel');
 407                          return;
 408                      }
 409  
 410                      postboxes.updateOrderButtonsProperties();
 411                      postboxes.save_order(page);
 412                  },
 413                  receive: function(e,ui) {
 414                      if ( 'dashboard_browser_nag' == ui.item[0].id )
 415                          $(ui.sender).sortable('cancel');
 416  
 417                      postboxes._mark_area();
 418                      $document.trigger( 'postbox-moved', ui.item );
 419                  }
 420              });
 421  
 422              if ( isMobile ) {
 423                  $(document.body).on('orientationchange.postboxes', function(){ postboxes._pb_change(); });
 424                  this._pb_change();
 425              }
 426  
 427              this._mark_area();
 428  
 429              // Update the "move" buttons properties.
 430              this.updateOrderButtonsProperties();
 431              $document.on( 'postbox-toggled', this.updateOrderButtonsProperties );
 432  
 433              // Set the handle buttons `aria-expanded` attribute initial value on page load.
 434              $handleButtons.each( function () {
 435                  var $el = $( this );
 436                  $el.attr( 'aria-expanded', ! $el.closest( '.postbox' ).hasClass( 'closed' ) );
 437              });
 438          },
 439  
 440          /**
 441           * Saves the state of the postboxes to the server.
 442           *
 443           * It sends two lists, one with all the closed postboxes, one with all the
 444           * hidden postboxes.
 445           *
 446           * @since 2.7.0
 447           *
 448           * @memberof postboxes
 449           *
 450           * @param {string} page The page we are currently on.
 451           * @return {void}
 452           */
 453          save_state : function(page) {
 454              var closed, hidden;
 455  
 456              // Return on the nav-menus.php screen, see #35112.
 457              if ( 'nav-menus' === page ) {
 458                  return;
 459              }
 460  
 461              closed = $( '.postbox' ).filter( '.closed' ).map( function() { return this.id; } ).get().join( ',' );
 462              hidden = $( '.postbox' ).filter( ':hidden' ).map( function() { return this.id; } ).get().join( ',' );
 463  
 464              $.post(ajaxurl, {
 465                  action: 'closed-postboxes',
 466                  closed: closed,
 467                  hidden: hidden,
 468                  closedpostboxesnonce: jQuery('#closedpostboxesnonce').val(),
 469                  page: page
 470              });
 471          },
 472  
 473          /**
 474           * Saves the order of the postboxes to the server.
 475           *
 476           * Sends a list of all postboxes inside a sortable area to the server.
 477           *
 478           * @since 2.8.0
 479           *
 480           * @memberof postboxes
 481           *
 482           * @param {string} page The page we are currently on.
 483           * @return {void}
 484           */
 485          save_order : function(page) {
 486              var postVars, page_columns = $('.columns-prefs input:checked').val() || 0;
 487  
 488              postVars = {
 489                  action: 'meta-box-order',
 490                  _ajax_nonce: $('#meta-box-order-nonce').val(),
 491                  page_columns: page_columns,
 492                  page: page
 493              };
 494  
 495              $('.meta-box-sortables').each( function() {
 496                  postVars[ 'order[' + this.id.split( '-' )[0] + ']' ] = $( this ).sortable( 'toArray' ).join( ',' );
 497              } );
 498  
 499              $.post(
 500                  ajaxurl,
 501                  postVars,
 502                  function( response ) {
 503                      if ( response.success ) {
 504                          wp.a11y.speak( __( 'The boxes order has been saved.' ) );
 505                      }
 506                  }
 507              );
 508          },
 509  
 510          /**
 511           * Marks empty postbox areas.
 512           *
 513           * Adds a message to empty sortable areas on the dashboard page. Also adds a
 514           * border around the side area on the post edit screen if there are no postboxes
 515           * present.
 516           *
 517           * @since 3.3.0
 518           * @access private
 519           *
 520           * @memberof postboxes
 521           *
 522           * @return {void}
 523           */
 524          _mark_area : function() {
 525              var visible = $( 'div.postbox:visible' ).length,
 526                  visibleSortables = $( '#dashboard-widgets .meta-box-sortables:visible, #post-body .meta-box-sortables:visible' ),
 527                  areAllVisibleSortablesEmpty = true;
 528  
 529              visibleSortables.each( function() {
 530                  var t = $(this);
 531  
 532                  if ( visible == 1 || t.children( '.postbox:visible' ).length ) {
 533                      t.removeClass('empty-container');
 534                      areAllVisibleSortablesEmpty = false;
 535                  }
 536                  else {
 537                      t.addClass('empty-container');
 538                  }
 539              });
 540  
 541              postboxes.updateEmptySortablesText( visibleSortables, areAllVisibleSortablesEmpty );
 542          },
 543  
 544          /**
 545           * Updates the text for the empty sortable areas on the Dashboard.
 546           *
 547           * @since 5.5.0
 548           *
 549           * @param {Object}  visibleSortables            The jQuery object representing the visible sortable areas.
 550           * @param {boolean} areAllVisibleSortablesEmpty Whether all the visible sortable areas are "empty".
 551           *
 552           * @return {void}
 553           */
 554          updateEmptySortablesText: function( visibleSortables, areAllVisibleSortablesEmpty ) {
 555              var isDashboard = $( '#dashboard-widgets' ).length,
 556                  emptySortableText = areAllVisibleSortablesEmpty ?  __( 'Add boxes from the Screen Options menu' ) : __( 'Drag boxes here' );
 557  
 558              if ( ! isDashboard ) {
 559                  return;
 560              }
 561  
 562              visibleSortables.each( function() {
 563                  if ( $( this ).hasClass( 'empty-container' ) ) {
 564                      $( this ).attr( 'data-emptyString', emptySortableText );
 565                  }
 566              } );
 567          },
 568  
 569          /**
 570           * Changes the amount of columns on the post edit page.
 571           *
 572           * @since 3.3.0
 573           * @access private
 574           *
 575           * @memberof postboxes
 576           *
 577           * @fires postboxes#postboxes-columnchange
 578           *
 579           * @param {number} n The amount of columns to divide the post edit page in.
 580           * @return {void}
 581           */
 582          _pb_edit : function(n) {
 583              var el = $('.metabox-holder').get(0);
 584  
 585              if ( el ) {
 586                  el.className = el.className.replace(/columns-\d+/, 'columns-' + n);
 587              }
 588  
 589              /**
 590               * Fires when the amount of columns on the post edit page has been changed.
 591               *
 592               * @since 4.0.0
 593               * @ignore
 594               *
 595               * @event postboxes#postboxes-columnchange
 596               */
 597              $( document ).trigger( 'postboxes-columnchange' );
 598          },
 599  
 600          /**
 601           * Changes the amount of columns the postboxes are in based on the current
 602           * orientation of the browser.
 603           *
 604           * @since 3.3.0
 605           * @access private
 606           *
 607           * @memberof postboxes
 608           *
 609           * @return {void}
 610           */
 611          _pb_change : function() {
 612              var check = $( 'label.columns-prefs-1 input[type="radio"]' );
 613  
 614              switch ( window.orientation ) {
 615                  case 90:
 616                  case -90:
 617                      if ( !check.length || !check.is(':checked') )
 618                          this._pb_edit(2);
 619                      break;
 620                  case 0:
 621                  case 180:
 622                      if ( $( '#poststuff' ).length ) {
 623                          this._pb_edit(1);
 624                      } else {
 625                          if ( !check.length || !check.is(':checked') )
 626                              this._pb_edit(2);
 627                      }
 628                      break;
 629              }
 630          },
 631  
 632          /* Callbacks */
 633  
 634          /**
 635           * @since 2.7.0
 636           * @access public
 637           *
 638           * @property {Function|boolean} pbshow A callback that is called when a postbox
 639           *                                     is opened.
 640           * @memberof postboxes
 641           */
 642          pbshow : false,
 643  
 644          /**
 645           * @since 2.7.0
 646           * @access public
 647           * @property {Function|boolean} pbhide A callback that is called when a postbox
 648           *                                     is closed.
 649           * @memberof postboxes
 650           */
 651          pbhide : false
 652      };
 653  
 654  }(jQuery));


Generated : Fri Apr 19 08:20:01 2024 Cross-referenced by PHPXref