[ 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 ( '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));
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Wed Dec 25 08:20:01 2024 | Cross-referenced by PHPXref |