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