| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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 ( ! postboxes.metaBoxReorderingEnabled ) { 108 return; 109 } 110 111 if ( 'dashboard_browser_nag' === postboxId ) { 112 return; 113 } 114 115 // If on the first or last position, do nothing and send an audible message to screen reader users. 116 if ( 'true' === button.attr( 'aria-disabled' ) ) { 117 firstOrLastPositionMessage = button.hasClass( 'handle-order-higher' ) ? 118 __( 'The box is on the first position' ) : 119 __( 'The box is on the last position' ); 120 121 wp.a11y.speak( firstOrLastPositionMessage ); 122 return; 123 } 124 125 // Move a postbox up. 126 if ( button.hasClass( 'handle-order-higher' ) ) { 127 // If the box is first within a sortable area, move it to the previous sortable area. 128 if ( 0 === postboxWithinSortablesIndex ) { 129 postboxes.handleOrderBetweenSortables( 'previous', button, postbox ); 130 return; 131 } 132 133 postbox.prevAll( '.postbox:visible' ).eq( 0 ).before( postbox ); 134 button.trigger( 'focus' ); 135 postboxes.updateOrderButtonsProperties(); 136 postboxes.save_order( postboxes.page ); 137 } 138 139 // Move a postbox down. 140 if ( button.hasClass( 'handle-order-lower' ) ) { 141 // If the box is last within a sortable area, move it to the next sortable area. 142 if ( postboxWithinSortablesIndex + 1 === postboxesWithinSortablesCount ) { 143 postboxes.handleOrderBetweenSortables( 'next', button, postbox ); 144 return; 145 } 146 147 postbox.nextAll( '.postbox:visible' ).eq( 0 ).after( postbox ); 148 button.trigger( 'focus' ); 149 postboxes.updateOrderButtonsProperties(); 150 postboxes.save_order( postboxes.page ); 151 } 152 153 }, 154 155 /** 156 * Moves postboxes between the sortables areas. 157 * 158 * @since 5.5.0 159 * 160 * @param {string} position The "previous" or "next" sortables area. 161 * @param {Object} button The jQuery object representing the button that was clicked. 162 * @param {Object} postbox The jQuery object representing the postbox to be moved. 163 * 164 * @return {void} 165 */ 166 handleOrderBetweenSortables: function( position, button, postbox ) { 167 var closestSortablesId = button.closest( '.meta-box-sortables' ).attr( 'id' ), 168 sortablesIds = [], 169 sortablesIndex, 170 detachedPostbox; 171 172 // Get the list of sortables within the page. 173 $( '.meta-box-sortables:visible' ).each( function() { 174 sortablesIds.push( $( this ).attr( 'id' ) ); 175 }); 176 177 // Return if there's only one visible sortables area, e.g. in the block editor page. 178 if ( 1 === sortablesIds.length ) { 179 return; 180 } 181 182 // Find the index of the current sortables area within all the sortable areas. 183 sortablesIndex = $.inArray( closestSortablesId, sortablesIds ); 184 // Detach the postbox to be moved. 185 detachedPostbox = postbox.detach(); 186 187 // Move the detached postbox to its new position. 188 if ( 'previous' === position ) { 189 $( detachedPostbox ).appendTo( '#' + sortablesIds[ sortablesIndex - 1 ] ); 190 } 191 192 if ( 'next' === position ) { 193 $( detachedPostbox ).prependTo( '#' + sortablesIds[ sortablesIndex + 1 ] ); 194 } 195 196 postboxes._mark_area(); 197 button.focus(); 198 postboxes.updateOrderButtonsProperties(); 199 postboxes.save_order( postboxes.page ); 200 }, 201 202 /** 203 * Update the move buttons properties depending on the postbox position. 204 * 205 * @since 5.5.0 206 * 207 * @return {void} 208 */ 209 updateOrderButtonsProperties: function() { 210 var firstSortablesId = $( '.meta-box-sortables:visible:first' ).attr( 'id' ), 211 lastSortablesId = $( '.meta-box-sortables:visible:last' ).attr( 'id' ), 212 firstPostbox = $( '.postbox:visible:first' ), 213 lastPostbox = $( '.postbox:visible:last' ), 214 firstPostboxId = firstPostbox.attr( 'id' ), 215 lastPostboxId = lastPostbox.attr( 'id' ), 216 firstPostboxSortablesId = firstPostbox.closest( '.meta-box-sortables' ).attr( 'id' ), 217 lastPostboxSortablesId = lastPostbox.closest( '.meta-box-sortables' ).attr( 'id' ), 218 moveUpButtons = $( '.handle-order-higher' ), 219 moveDownButtons = $( '.handle-order-lower' ); 220 221 // Enable all buttons as a reset first. 222 moveUpButtons 223 .attr( 'aria-disabled', 'false' ) 224 .removeClass( 'hidden' ); 225 moveDownButtons 226 .attr( 'aria-disabled', 'false' ) 227 .removeClass( 'hidden' ); 228 229 // When there's only one "sortables" area (e.g. in the block editor) and only one visible postbox, hide the buttons. 230 if ( firstSortablesId === lastSortablesId && firstPostboxId === lastPostboxId ) { 231 moveUpButtons.addClass( 'hidden' ); 232 moveDownButtons.addClass( 'hidden' ); 233 } 234 235 // Set an aria-disabled=true attribute on the first visible "move" buttons. 236 if ( firstSortablesId === firstPostboxSortablesId ) { 237 $( firstPostbox ).find( '.handle-order-higher' ).attr( 'aria-disabled', 'true' ); 238 } 239 240 // Set an aria-disabled=true attribute on the last visible "move" buttons. 241 if ( lastSortablesId === lastPostboxSortablesId ) { 242 $( '.postbox:visible .handle-order-lower' ).last().attr( 'aria-disabled', 'true' ); 243 } 244 }, 245 246 /** 247 * Adds event handlers to all postboxes and screen option on the current page. 248 * 249 * @since 2.7.0 250 * 251 * @memberof postboxes 252 * 253 * @param {string} page The page we are currently on. 254 * @param {Object} [args] Optional arguments for the postbox toggles. 255 * @param {Function} [args.pbshow] A callback that is called when a postbox opens. 256 * @param {Function} [args.pbhide] A callback that is called when a postbox closes. 257 * @return {void} 258 */ 259 add_postbox_toggles : function (page, args) { 260 var $handles = $( '.postbox .hndle, .postbox .handlediv' ), 261 $orderButtons = $( '.postbox .handle-order-higher, .postbox .handle-order-lower' ); 262 263 this.page = page; 264 this.init( page, args ); 265 266 $handles.on( 'click.postboxes', this.handle_click ); 267 268 // Handle the order of the postboxes. 269 $orderButtons.on( 'click.postboxes', this.handleOrder ); 270 271 /** 272 * @since 2.7.0 273 */ 274 $('.postbox .hndle a').on( 'click', function(e) { 275 e.stopPropagation(); 276 }); 277 278 /** 279 * Hides a postbox. 280 * 281 * Event handler for the postbox dismiss button. After clicking the button 282 * the postbox will be hidden. 283 * 284 * As of WordPress 5.5, this is only used for the browser update nag. 285 * 286 * @since 3.2.0 287 * 288 * @return {void} 289 */ 290 $( '.postbox a.dismiss' ).on( 'click.postboxes', function( e ) { 291 var hide_id = $(this).parents('.postbox').attr('id') + '-hide'; 292 e.preventDefault(); 293 $( '#' + hide_id ).prop('checked', false).triggerHandler('click'); 294 }); 295 296 /** 297 * Hides the postbox element 298 * 299 * Event handler for the screen options checkboxes. When a checkbox is 300 * clicked this function will hide or show the relevant postboxes. 301 * 302 * @since 2.7.0 303 * @ignore 304 * 305 * @fires postboxes#postbox-toggled 306 * 307 * @return {void} 308 */ 309 $('.hide-postbox-tog').on('click.postboxes', function() { 310 var $el = $(this), 311 boxId = $el.val(), 312 $postbox = $( '#' + boxId ); 313 314 if ( $el.prop( 'checked' ) ) { 315 $postbox.show(); 316 if ( typeof postboxes.pbshow === 'function' ) { 317 postboxes.pbshow( boxId ); 318 } 319 } else { 320 $postbox.hide(); 321 if ( typeof postboxes.pbhide === 'function' ) { 322 postboxes.pbhide( boxId ); 323 } 324 } 325 326 postboxes.save_state( page ); 327 postboxes._mark_area(); 328 329 /** 330 * @since 4.0.0 331 * @see postboxes.handle_click 332 */ 333 $document.trigger( 'postbox-toggled', $postbox ); 334 }); 335 336 /** 337 * Changes the amount of columns based on the layout preferences. 338 * 339 * @since 2.8.0 340 * 341 * @return {void} 342 */ 343 $('.columns-prefs input[type="radio"]').on('click.postboxes', function(){ 344 var n = parseInt($(this).val(), 10); 345 346 if ( n ) { 347 postboxes._pb_edit(n); 348 postboxes.save_order( page ); 349 } 350 }); 351 352 $( '.meta-box-reordering-toggle' ).on( 'click.postboxes', function() { 353 var enabled = $( this ).prop( 'checked' ); 354 355 postboxes.setMetaBoxReordering( enabled ); 356 postboxes.save_meta_box_reordering_state( enabled ); 357 } ); 358 }, 359 360 /** 361 * Initializes all the postboxes, mainly their sortable behavior. 362 * 363 * @since 2.7.0 364 * 365 * @memberof postboxes 366 * 367 * @param {string} page The page we are currently on. 368 * @param {Object} [args={}] The arguments for the postbox initializer. 369 * @param {Function} args.pbshow A callback that is called when a postbox opens. 370 * @param {Function} args.pbhide A callback that is called when a postbox 371 * closes. 372 * 373 * @return {void} 374 */ 375 init : function(page, args) { 376 var isMobile = $( document.body ).hasClass( 'mobile' ), 377 $handleButtons = $( '.postbox .handlediv' ); 378 379 $.extend( this, args || {} ); 380 $('.meta-box-sortables').sortable({ 381 placeholder: 'sortable-placeholder', 382 connectWith: '.meta-box-sortables', 383 items: '.postbox', 384 handle: '.hndle', 385 cursor: 'move', 386 delay: ( isMobile ? 200 : 0 ), 387 distance: 2, 388 tolerance: 'pointer', 389 forcePlaceholderSize: true, 390 helper: function( event, element ) { 391 /* `helper: 'clone'` is equivalent to `return element.clone();` 392 * Cloning a checked radio and then inserting that clone next to the original 393 * radio unchecks the original radio (since only one of the two can be checked). 394 * We get around this by renaming the helper's inputs' name attributes so that, 395 * when the helper is inserted into the DOM for the sortable, no radios are 396 * duplicated, and no original radio gets unchecked. 397 */ 398 return element.clone() 399 .find( ':input' ) 400 .attr( 'name', function( i, currentName ) { 401 return 'sort_' + parseInt( Math.random() * 100000, 10 ).toString() + '_' + currentName; 402 } ) 403 .end(); 404 }, 405 opacity: 0.65, 406 start: function() { 407 $( 'body' ).addClass( 'is-dragging-metaboxes' ); 408 // Refresh the cached positions of all the sortable items so that the min-height set while dragging works. 409 $( '.meta-box-sortables' ).sortable( 'refreshPositions' ); 410 }, 411 stop: function() { 412 var $el = $( this ); 413 414 $( 'body' ).removeClass( 'is-dragging-metaboxes' ); 415 416 if ( $el.find( '#dashboard_browser_nag' ).is( ':visible' ) && 'dashboard_browser_nag' != this.firstChild.id ) { 417 $el.sortable('cancel'); 418 return; 419 } 420 421 postboxes.updateOrderButtonsProperties(); 422 postboxes.save_order(page); 423 }, 424 receive: function(e,ui) { 425 if ( 'dashboard_browser_nag' == ui.item[0].id ) 426 $(ui.sender).sortable('cancel'); 427 428 postboxes._mark_area(); 429 $document.trigger( 'postbox-moved', ui.item ); 430 } 431 }); 432 433 this.setMetaBoxReordering( this.isMetaBoxReorderingEnabled() ); 434 435 if ( isMobile ) { 436 $(document.body).on('orientationchange.postboxes', function(){ postboxes._pb_change(); }); 437 this._pb_change(); 438 } 439 440 this._mark_area(); 441 442 // Update the "move" buttons properties. 443 this.updateOrderButtonsProperties(); 444 $document.on( 'postbox-toggled', this.updateOrderButtonsProperties ); 445 446 // Set the handle buttons `aria-expanded` attribute initial value on page load. 447 $handleButtons.each( function () { 448 var $el = $( this ); 449 $el.attr( 'aria-expanded', ! $el.closest( '.postbox' ).hasClass( 'closed' ) ); 450 }); 451 }, 452 453 /** 454 * Checks whether meta box reordering is enabled. 455 * 456 * @since 7.2.0 457 * 458 * @return {boolean} Whether meta box reordering is enabled. 459 */ 460 isMetaBoxReorderingEnabled: function() { 461 var $toggle = $( '#meta-box-reordering' ); 462 463 if ( $toggle.length ) { 464 return $toggle.prop( 'checked' ); 465 } 466 467 return ! $( document.body ).hasClass( 'meta-box-reordering-disabled' ); 468 }, 469 470 /** 471 * Enables or disables the meta box reordering UI. 472 * 473 * @since 7.2.0 474 * 475 * @param {boolean} enabled Whether reordering should be enabled. 476 * @return {void} 477 */ 478 setMetaBoxReordering: function( enabled ) { 479 var $sortables = $( '.meta-box-sortables' ); 480 481 this.metaBoxReorderingEnabled = enabled; 482 483 $( document.body ).toggleClass( 'meta-box-reordering-disabled', ! this.metaBoxReorderingEnabled ); 484 485 if ( window.wpResponsive && window.wpResponsive.maybeDisableSortables ) { 486 window.wpResponsive.maybeDisableSortables(); 487 return; 488 } 489 490 if ( $sortables.hasClass( 'ui-sortable' ) ) { 491 $sortables 492 .sortable( this.metaBoxReorderingEnabled ? 'enable' : 'disable' ) 493 .find( '.ui-sortable-handle' ) 494 .toggleClass( 'is-non-sortable', ! this.metaBoxReorderingEnabled ); 495 } 496 }, 497 498 /** 499 * Saves the state of the postboxes to the server. 500 * 501 * It sends two lists, one with all the closed postboxes, one with all the 502 * hidden postboxes. 503 * 504 * @since 2.7.0 505 * 506 * @memberof postboxes 507 * 508 * @param {string} page The page we are currently on. 509 * @return {void} 510 */ 511 save_state : function(page) { 512 var closed, hidden; 513 514 // Return on the nav-menus.php screen, see #35112. 515 if ( 'nav-menus' === page ) { 516 return; 517 } 518 519 closed = $( '.postbox' ).filter( '.closed' ).map( function() { return this.id; } ).get().join( ',' ); 520 hidden = $( '.postbox' ).filter( ':hidden' ).map( function() { return this.id; } ).get().join( ',' ); 521 522 $.post( 523 ajaxurl, 524 { 525 action: 'closed-postboxes', 526 closed: closed, 527 hidden: hidden, 528 closedpostboxesnonce: jQuery('#closedpostboxesnonce').val(), 529 page: page 530 }, 531 function() { 532 wp.a11y.speak( __( 'Screen Options updated.' ) ); 533 } 534 ); 535 }, 536 537 /** 538 * Saves the meta box reordering setting to the server. 539 * 540 * @since 7.2.0 541 * 542 * @param {boolean} enabled Whether meta box reordering is enabled. 543 * @return {void} 544 */ 545 save_meta_box_reordering_state: function( enabled ) { 546 $.post( 547 ajaxurl, 548 { 549 action: 'meta-box-reordering', 550 enabled: enabled ? 1 : 0, 551 screenoptionnonce: $( '#screenoptionnonce' ).val() 552 }, 553 function() { 554 wp.a11y.speak( 555 enabled ? 556 __( 'Rearranging screen elements enabled.' ) : 557 __( 'Rearranging screen elements disabled.' ) 558 ); 559 } 560 ); 561 }, 562 563 /** 564 * Saves the order of the postboxes to the server. 565 * 566 * Sends a list of all postboxes inside a sortable area to the server. 567 * 568 * @since 2.8.0 569 * 570 * @memberof postboxes 571 * 572 * @param {string} page The page we are currently on. 573 * @return {void} 574 */ 575 save_order : function(page) { 576 var postVars, page_columns = $('.columns-prefs input:checked').val() || 0; 577 578 postVars = { 579 action: 'meta-box-order', 580 _ajax_nonce: $('#meta-box-order-nonce').val(), 581 page_columns: page_columns, 582 page: page 583 }; 584 585 $('.meta-box-sortables').each( function() { 586 postVars[ 'order[' + this.id.split( '-' )[0] + ']' ] = $( this ).sortable( 'toArray' ).join( ',' ); 587 } ); 588 589 $.post( 590 ajaxurl, 591 postVars, 592 function( response ) { 593 if ( response.success ) { 594 wp.a11y.speak( __( 'The boxes order has been saved.' ) ); 595 } 596 } 597 ); 598 }, 599 600 /** 601 * Marks empty postbox areas. 602 * 603 * Adds a message to empty sortable areas on the dashboard page. Also adds a 604 * border around the side area on the post edit screen if there are no postboxes 605 * present. 606 * 607 * @since 3.3.0 608 * @access private 609 * 610 * @memberof postboxes 611 * 612 * @return {void} 613 */ 614 _mark_area : function() { 615 var visible = $( 'div.postbox:visible' ).length, 616 visibleSortables = $( '#dashboard-widgets .meta-box-sortables:visible, #post-body .meta-box-sortables:visible' ), 617 areAllVisibleSortablesEmpty = true; 618 619 visibleSortables.each( function() { 620 var t = $(this); 621 622 if ( visible == 1 || t.children( '.postbox:visible' ).length ) { 623 t.removeClass('empty-container'); 624 areAllVisibleSortablesEmpty = false; 625 } 626 else { 627 t.addClass('empty-container'); 628 } 629 }); 630 631 postboxes.updateEmptySortablesText( visibleSortables, areAllVisibleSortablesEmpty ); 632 }, 633 634 /** 635 * Updates the text for the empty sortable areas on the Dashboard. 636 * 637 * @since 5.5.0 638 * 639 * @param {Object} visibleSortables The jQuery object representing the visible sortable areas. 640 * @param {boolean} areAllVisibleSortablesEmpty Whether all the visible sortable areas are "empty". 641 * 642 * @return {void} 643 */ 644 updateEmptySortablesText: function( visibleSortables, areAllVisibleSortablesEmpty ) { 645 var isDashboard = $( '#dashboard-widgets' ).length, 646 emptySortableText = areAllVisibleSortablesEmpty ? __( 'Add boxes from the Screen Options menu' ) : __( 'Drag boxes here' ); 647 648 if ( ! isDashboard ) { 649 return; 650 } 651 652 visibleSortables.each( function() { 653 if ( $( this ).hasClass( 'empty-container' ) ) { 654 $( this ).attr( 'data-emptyString', emptySortableText ); 655 } 656 } ); 657 }, 658 659 /** 660 * Changes the amount of columns on the post edit page. 661 * 662 * @since 3.3.0 663 * @access private 664 * 665 * @memberof postboxes 666 * 667 * @fires postboxes#postboxes-columnchange 668 * 669 * @param {number} n The amount of columns to divide the post edit page in. 670 * @return {void} 671 */ 672 _pb_edit : function(n) { 673 var el = $('.metabox-holder').get(0); 674 675 if ( el ) { 676 el.className = el.className.replace(/columns-\d+/, 'columns-' + n); 677 } 678 679 /** 680 * Fires when the amount of columns on the post edit page has been changed. 681 * 682 * @since 4.0.0 683 * @ignore 684 * 685 * @event postboxes#postboxes-columnchange 686 */ 687 $( document ).trigger( 'postboxes-columnchange' ); 688 }, 689 690 /** 691 * Changes the amount of columns the postboxes are in based on the current 692 * orientation of the browser. 693 * 694 * @since 3.3.0 695 * @access private 696 * 697 * @memberof postboxes 698 * 699 * @return {void} 700 */ 701 _pb_change : function() { 702 var check = $( 'label.columns-prefs-1 input[type="radio"]' ); 703 704 switch ( window.orientation ) { 705 case 90: 706 case -90: 707 if ( !check.length || !check.is(':checked') ) 708 this._pb_edit(2); 709 break; 710 case 0: 711 case 180: 712 if ( $( '#poststuff' ).length ) { 713 this._pb_edit(1); 714 } else { 715 if ( !check.length || !check.is(':checked') ) 716 this._pb_edit(2); 717 } 718 break; 719 } 720 }, 721 722 /* Callbacks */ 723 724 /** 725 * @since 2.7.0 726 * @access public 727 * 728 * @property {Function|boolean} pbshow A callback that is called when a postbox 729 * is opened. 730 * @memberof postboxes 731 */ 732 pbshow : false, 733 734 /** 735 * @since 2.7.0 736 * @access public 737 * @property {Function|boolean} pbhide A callback that is called when a postbox 738 * is closed. 739 * @memberof postboxes 740 */ 741 pbhide : false 742 }; 743 744 }(jQuery));
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Wed Oct 14 08:20:33 2026 | Cross-referenced by PHPXref |