[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[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 6.5.0, needed for back-compatibility. 358 * 359 * @since 4.5.0 360 * @deprecated 6.5.0 361 */ 362 window.uiAutocompleteL10n = window.uiAutocompleteL10n || { 363 noResults: '', 364 oneResult: '', 365 manyResults: '', 366 itemSelected: '' 367 }; 368 369 window.uiAutocompleteL10n = deprecateL10nObject( 'uiAutocompleteL10n', window.uiAutocompleteL10n, '6.5.0' ); 370 371 /** 372 * Removed in 3.3.0, needed for back-compatibility. 373 * 374 * @since 2.7.0 375 * @deprecated 3.3.0 376 */ 377 window.adminMenu = { 378 init : function() {}, 379 fold : function() {}, 380 restoreMenuState : function() {}, 381 toggle : function() {}, 382 favorites : function() {} 383 }; 384 385 // Show/hide/save table columns. 386 window.columns = { 387 388 /** 389 * Initializes the column toggles in the screen options. 390 * 391 * Binds an onClick event to the checkboxes to show or hide the table columns 392 * based on their toggled state. And persists the toggled state. 393 * 394 * @since 2.7.0 395 * 396 * @return {void} 397 */ 398 init : function() { 399 var that = this; 400 $('.hide-column-tog', '#adv-settings').on( 'click', function() { 401 var $t = $(this), column = $t.val(); 402 if ( $t.prop('checked') ) 403 that.checked(column); 404 else 405 that.unchecked(column); 406 407 columns.saveManageColumnsState(); 408 }); 409 }, 410 411 /** 412 * Saves the toggled state for the columns. 413 * 414 * Saves whether the columns should be shown or hidden on a page. 415 * 416 * @since 3.0.0 417 * 418 * @return {void} 419 */ 420 saveManageColumnsState : function() { 421 var hidden = this.hidden(); 422 $.post(ajaxurl, { 423 action: 'hidden-columns', 424 hidden: hidden, 425 screenoptionnonce: $('#screenoptionnonce').val(), 426 page: pagenow 427 }); 428 }, 429 430 /** 431 * Makes a column visible and adjusts the column span for the table. 432 * 433 * @since 3.0.0 434 * @param {string} column The column name. 435 * 436 * @return {void} 437 */ 438 checked : function(column) { 439 $('.column-' + column).removeClass( 'hidden' ); 440 this.colSpanChange(+1); 441 }, 442 443 /** 444 * Hides a column and adjusts the column span for the table. 445 * 446 * @since 3.0.0 447 * @param {string} column The column name. 448 * 449 * @return {void} 450 */ 451 unchecked : function(column) { 452 $('.column-' + column).addClass( 'hidden' ); 453 this.colSpanChange(-1); 454 }, 455 456 /** 457 * Gets all hidden columns. 458 * 459 * @since 3.0.0 460 * 461 * @return {string} The hidden column names separated by a comma. 462 */ 463 hidden : function() { 464 return $( '.manage-column[id]' ).filter( '.hidden' ).map(function() { 465 return this.id; 466 }).get().join( ',' ); 467 }, 468 469 /** 470 * Gets the checked column toggles from the screen options. 471 * 472 * @since 3.0.0 473 * 474 * @return {string} String containing the checked column names. 475 */ 476 useCheckboxesForHidden : function() { 477 this.hidden = function(){ 478 return $('.hide-column-tog').not(':checked').map(function() { 479 var id = this.id; 480 return id.substring( id, id.length - 5 ); 481 }).get().join(','); 482 }; 483 }, 484 485 /** 486 * Adjusts the column span for the table. 487 * 488 * @since 3.1.0 489 * 490 * @param {number} diff The modifier for the column span. 491 */ 492 colSpanChange : function(diff) { 493 var $t = $('table').find('.colspanchange'), n; 494 if ( !$t.length ) 495 return; 496 n = parseInt( $t.attr('colspan'), 10 ) + diff; 497 $t.attr('colspan', n.toString()); 498 } 499 }; 500 501 $( function() { columns.init(); } ); 502 503 /** 504 * Validates that the required form fields are not empty. 505 * 506 * @since 2.9.0 507 * 508 * @param {jQuery} form The form to validate. 509 * 510 * @return {boolean} Returns true if all required fields are not an empty string. 511 */ 512 window.validateForm = function( form ) { 513 return !$( form ) 514 .find( '.form-required' ) 515 .filter( function() { return $( ':input:visible', this ).val() === ''; } ) 516 .addClass( 'form-invalid' ) 517 .find( ':input:visible' ) 518 .on( 'change', function() { $( this ).closest( '.form-invalid' ).removeClass( 'form-invalid' ); } ) 519 .length; 520 }; 521 522 // Stub for doing better warnings. 523 /** 524 * Shows message pop-up notice or confirmation message. 525 * 526 * @since 2.7.0 527 * 528 * @type {{warn: showNotice.warn, note: showNotice.note}} 529 * 530 * @return {void} 531 */ 532 window.showNotice = { 533 534 /** 535 * Shows a delete confirmation pop-up message. 536 * 537 * @since 2.7.0 538 * 539 * @return {boolean} Returns true if the message is confirmed. 540 */ 541 warn : function() { 542 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.' ) ) ) { 543 return true; 544 } 545 546 return false; 547 }, 548 549 /** 550 * Shows an alert message. 551 * 552 * @since 2.7.0 553 * 554 * @param text The text to display in the message. 555 */ 556 note : function(text) { 557 alert(text); 558 } 559 }; 560 561 /** 562 * Represents the functions for the meta screen options panel. 563 * 564 * @since 3.2.0 565 * 566 * @type {{element: null, toggles: null, page: null, init: screenMeta.init, 567 * toggleEvent: screenMeta.toggleEvent, open: screenMeta.open, 568 * close: screenMeta.close}} 569 * 570 * @return {void} 571 */ 572 window.screenMeta = { 573 element: null, // #screen-meta 574 toggles: null, // .screen-meta-toggle 575 page: null, // #wpcontent 576 577 /** 578 * Initializes the screen meta options panel. 579 * 580 * @since 3.2.0 581 * 582 * @return {void} 583 */ 584 init: function() { 585 this.element = $('#screen-meta'); 586 this.toggles = $( '#screen-meta-links' ).find( '.show-settings' ); 587 this.page = $('#wpcontent'); 588 589 this.toggles.on( 'click', this.toggleEvent ); 590 }, 591 592 /** 593 * Toggles the screen meta options panel. 594 * 595 * @since 3.2.0 596 * 597 * @return {void} 598 */ 599 toggleEvent: function() { 600 var panel = $( '#' + $( this ).attr( 'aria-controls' ) ); 601 602 if ( !panel.length ) 603 return; 604 605 if ( panel.is(':visible') ) 606 screenMeta.close( panel, $(this) ); 607 else 608 screenMeta.open( panel, $(this) ); 609 }, 610 611 /** 612 * Opens the screen meta options panel. 613 * 614 * @since 3.2.0 615 * 616 * @param {jQuery} panel The screen meta options panel div. 617 * @param {jQuery} button The toggle button. 618 * 619 * @return {void} 620 */ 621 open: function( panel, button ) { 622 623 $( '#screen-meta-links' ).find( '.screen-meta-toggle' ).not( button.parent() ).css( 'visibility', 'hidden' ); 624 625 panel.parent().show(); 626 627 /** 628 * Sets the focus to the meta options panel and adds the necessary CSS classes. 629 * 630 * @since 3.2.0 631 * 632 * @return {void} 633 */ 634 panel.slideDown( 'fast', function() { 635 panel.removeClass( 'hidden' ).trigger( 'focus' ); 636 button.addClass( 'screen-meta-active' ).attr( 'aria-expanded', true ); 637 }); 638 639 $document.trigger( 'screen:options:open' ); 640 }, 641 642 /** 643 * Closes the screen meta options panel. 644 * 645 * @since 3.2.0 646 * 647 * @param {jQuery} panel The screen meta options panel div. 648 * @param {jQuery} button The toggle button. 649 * 650 * @return {void} 651 */ 652 close: function( panel, button ) { 653 /** 654 * Hides the screen meta options panel. 655 * 656 * @since 3.2.0 657 * 658 * @return {void} 659 */ 660 panel.slideUp( 'fast', function() { 661 button.removeClass( 'screen-meta-active' ).attr( 'aria-expanded', false ); 662 $('.screen-meta-toggle').css('visibility', ''); 663 panel.parent().hide(); 664 panel.addClass( 'hidden' ); 665 }); 666 667 $document.trigger( 'screen:options:close' ); 668 } 669 }; 670 671 /** 672 * Initializes the help tabs in the help panel. 673 * 674 * @param {Event} e The event object. 675 * 676 * @return {void} 677 */ 678 $('.contextual-help-tabs').on( 'click', 'a', function(e) { 679 var link = $(this), 680 panel; 681 682 e.preventDefault(); 683 684 // Don't do anything if the click is for the tab already showing. 685 if ( link.is('.active a') ) 686 return false; 687 688 // Links. 689 $('.contextual-help-tabs .active').removeClass('active'); 690 link.parent('li').addClass('active'); 691 692 panel = $( link.attr('href') ); 693 694 // Panels. 695 $('.help-tab-content').not( panel ).removeClass('active').hide(); 696 panel.addClass('active').show(); 697 }); 698 699 /** 700 * Update custom permalink structure via buttons. 701 */ 702 var permalinkStructureFocused = false, 703 $permalinkStructure = $( '#permalink_structure' ), 704 $permalinkStructureInputs = $( '.permalink-structure input:radio' ), 705 $permalinkCustomSelection = $( '#custom_selection' ), 706 $availableStructureTags = $( '.form-table.permalink-structure .available-structure-tags button' ); 707 708 // Change permalink structure input when selecting one of the common structures. 709 $permalinkStructureInputs.on( 'change', function() { 710 if ( 'custom' === this.value ) { 711 return; 712 } 713 714 $permalinkStructure.val( this.value ); 715 716 // Update button states after selection. 717 $availableStructureTags.each( function() { 718 changeStructureTagButtonState( $( this ) ); 719 } ); 720 } ); 721 722 $permalinkStructure.on( 'click input', function() { 723 $permalinkCustomSelection.prop( 'checked', true ); 724 } ); 725 726 // Check if the permalink structure input field has had focus at least once. 727 $permalinkStructure.on( 'focus', function( event ) { 728 permalinkStructureFocused = true; 729 $( this ).off( event ); 730 } ); 731 732 /** 733 * Enables or disables a structure tag button depending on its usage. 734 * 735 * If the structure is already used in the custom permalink structure, 736 * it will be disabled. 737 * 738 * @param {Object} button Button jQuery object. 739 */ 740 function changeStructureTagButtonState( button ) { 741 if ( -1 !== $permalinkStructure.val().indexOf( button.text().trim() ) ) { 742 button.attr( 'data-label', button.attr( 'aria-label' ) ); 743 button.attr( 'aria-label', button.attr( 'data-used' ) ); 744 button.attr( 'aria-pressed', true ); 745 button.addClass( 'active' ); 746 } else if ( button.attr( 'data-label' ) ) { 747 button.attr( 'aria-label', button.attr( 'data-label' ) ); 748 button.attr( 'aria-pressed', false ); 749 button.removeClass( 'active' ); 750 } 751 } 752 753 // Check initial button state. 754 $availableStructureTags.each( function() { 755 changeStructureTagButtonState( $( this ) ); 756 } ); 757 758 // Observe permalink structure field and disable buttons of tags that are already present. 759 $permalinkStructure.on( 'change', function() { 760 $availableStructureTags.each( function() { 761 changeStructureTagButtonState( $( this ) ); 762 } ); 763 } ); 764 765 $availableStructureTags.on( 'click', function() { 766 var permalinkStructureValue = $permalinkStructure.val(), 767 selectionStart = $permalinkStructure[ 0 ].selectionStart, 768 selectionEnd = $permalinkStructure[ 0 ].selectionEnd, 769 textToAppend = $( this ).text().trim(), 770 textToAnnounce, 771 newSelectionStart; 772 773 if ( $( this ).hasClass( 'active' ) ) { 774 textToAnnounce = $( this ).attr( 'data-removed' ); 775 } else { 776 textToAnnounce = $( this ).attr( 'data-added' ); 777 } 778 779 // Remove structure tag if already part of the structure. 780 if ( -1 !== permalinkStructureValue.indexOf( textToAppend ) ) { 781 permalinkStructureValue = permalinkStructureValue.replace( textToAppend + '/', '' ); 782 783 $permalinkStructure.val( '/' === permalinkStructureValue ? '' : permalinkStructureValue ); 784 785 // Announce change to screen readers. 786 $( '#custom_selection_updated' ).text( textToAnnounce ); 787 788 // Disable button. 789 changeStructureTagButtonState( $( this ) ); 790 791 return; 792 } 793 794 // Input field never had focus, move selection to end of input. 795 if ( ! permalinkStructureFocused && 0 === selectionStart && 0 === selectionEnd ) { 796 selectionStart = selectionEnd = permalinkStructureValue.length; 797 } 798 799 $permalinkCustomSelection.prop( 'checked', true ); 800 801 // Prepend and append slashes if necessary. 802 if ( '/' !== permalinkStructureValue.substr( 0, selectionStart ).substr( -1 ) ) { 803 textToAppend = '/' + textToAppend; 804 } 805 806 if ( '/' !== permalinkStructureValue.substr( selectionEnd, 1 ) ) { 807 textToAppend = textToAppend + '/'; 808 } 809 810 // Insert structure tag at the specified position. 811 $permalinkStructure.val( permalinkStructureValue.substr( 0, selectionStart ) + textToAppend + permalinkStructureValue.substr( selectionEnd ) ); 812 813 // Announce change to screen readers. 814 $( '#custom_selection_updated' ).text( textToAnnounce ); 815 816 // Disable button. 817 changeStructureTagButtonState( $( this ) ); 818 819 // If input had focus give it back with cursor right after appended text. 820 if ( permalinkStructureFocused && $permalinkStructure[0].setSelectionRange ) { 821 newSelectionStart = ( permalinkStructureValue.substr( 0, selectionStart ) + textToAppend ).length; 822 $permalinkStructure[0].setSelectionRange( newSelectionStart, newSelectionStart ); 823 $permalinkStructure.trigger( 'focus' ); 824 } 825 } ); 826 827 $( function() { 828 var checks, first, last, checked, sliced, mobileEvent, transitionTimeout, focusedRowActions, 829 lastClicked = false, 830 pageInput = $('input.current-page'), 831 currentPage = pageInput.val(), 832 isIOS = /iPhone|iPad|iPod/.test( navigator.userAgent ), 833 isAndroid = navigator.userAgent.indexOf( 'Android' ) !== -1, 834 $adminMenuWrap = $( '#adminmenuwrap' ), 835 $wpwrap = $( '#wpwrap' ), 836 $adminmenu = $( '#adminmenu' ), 837 $overlay = $( '#wp-responsive-overlay' ), 838 $toolbar = $( '#wp-toolbar' ), 839 $toolbarPopups = $toolbar.find( 'a[aria-haspopup="true"]' ), 840 $sortables = $('.meta-box-sortables'), 841 wpResponsiveActive = false, 842 $adminbar = $( '#wpadminbar' ), 843 lastScrollPosition = 0, 844 pinnedMenuTop = false, 845 pinnedMenuBottom = false, 846 menuTop = 0, 847 menuState, 848 menuIsPinned = false, 849 height = { 850 window: $window.height(), 851 wpwrap: $wpwrap.height(), 852 adminbar: $adminbar.height(), 853 menu: $adminMenuWrap.height() 854 }, 855 $headerEnd = $( '.wp-header-end' ); 856 857 /** 858 * Makes the fly-out submenu header clickable, when the menu is folded. 859 * 860 * @param {Event} e The event object. 861 * 862 * @return {void} 863 */ 864 $adminmenu.on('click.wp-submenu-head', '.wp-submenu-head', function(e){ 865 $(e.target).parent().siblings('a').get(0).click(); 866 }); 867 868 /** 869 * Collapses the admin menu. 870 * 871 * @return {void} 872 */ 873 $( '#collapse-button' ).on( 'click.collapse-menu', function() { 874 var viewportWidth = getViewportWidth() || 961; 875 876 // Reset any compensation for submenus near the bottom of the screen. 877 $('#adminmenu div.wp-submenu').css('margin-top', ''); 878 879 if ( viewportWidth <= 960 ) { 880 if ( $body.hasClass('auto-fold') ) { 881 $body.removeClass('auto-fold').removeClass('folded'); 882 setUserSetting('unfold', 1); 883 setUserSetting('mfold', 'o'); 884 menuState = 'open'; 885 } else { 886 $body.addClass('auto-fold'); 887 setUserSetting('unfold', 0); 888 menuState = 'folded'; 889 } 890 } else { 891 if ( $body.hasClass('folded') ) { 892 $body.removeClass('folded'); 893 setUserSetting('mfold', 'o'); 894 menuState = 'open'; 895 } else { 896 $body.addClass('folded'); 897 setUserSetting('mfold', 'f'); 898 menuState = 'folded'; 899 } 900 } 901 902 $document.trigger( 'wp-collapse-menu', { state: menuState } ); 903 }); 904 905 /** 906 * Handles the `aria-haspopup` attribute on the current menu item when it has a submenu. 907 * 908 * @since 4.4.0 909 * 910 * @return {void} 911 */ 912 function currentMenuItemHasPopup() { 913 var $current = $( 'a.wp-has-current-submenu' ); 914 915 if ( 'folded' === menuState ) { 916 // When folded or auto-folded and not responsive view, the current menu item does have a fly-out sub-menu. 917 $current.attr( 'aria-haspopup', 'true' ); 918 } else { 919 // When expanded or in responsive view, reset aria-haspopup. 920 $current.attr( 'aria-haspopup', 'false' ); 921 } 922 } 923 924 $document.on( 'wp-menu-state-set wp-collapse-menu wp-responsive-activate wp-responsive-deactivate', currentMenuItemHasPopup ); 925 926 /** 927 * Ensures an admin submenu is within the visual viewport. 928 * 929 * @since 4.1.0 930 * 931 * @param {jQuery} $menuItem The parent menu item containing the submenu. 932 * 933 * @return {void} 934 */ 935 function adjustSubmenu( $menuItem ) { 936 var bottomOffset, pageHeight, adjustment, theFold, menutop, wintop, maxtop, 937 $submenu = $menuItem.find( '.wp-submenu' ); 938 939 menutop = $menuItem.offset().top; 940 wintop = $window.scrollTop(); 941 maxtop = menutop - wintop - 30; // max = make the top of the sub almost touch admin bar. 942 943 bottomOffset = menutop + $submenu.height() + 1; // Bottom offset of the menu. 944 pageHeight = $wpwrap.height(); // Height of the entire page. 945 adjustment = 60 + bottomOffset - pageHeight; 946 theFold = $window.height() + wintop - 50; // The fold. 947 948 if ( theFold < ( bottomOffset - adjustment ) ) { 949 adjustment = bottomOffset - theFold; 950 } 951 952 if ( adjustment > maxtop ) { 953 adjustment = maxtop; 954 } 955 956 if ( adjustment > 1 && $('#wp-admin-bar-menu-toggle').is(':hidden') ) { 957 $submenu.css( 'margin-top', '-' + adjustment + 'px' ); 958 } else { 959 $submenu.css( 'margin-top', '' ); 960 } 961 } 962 963 if ( 'ontouchstart' in window || /IEMobile\/[1-9]/.test(navigator.userAgent) ) { // Touch screen device. 964 // iOS Safari works with touchstart, the rest work with click. 965 mobileEvent = isIOS ? 'touchstart' : 'click'; 966 967 /** 968 * Closes any open submenus when touch/click is not on the menu. 969 * 970 * @param {Event} e The event object. 971 * 972 * @return {void} 973 */ 974 $body.on( mobileEvent+'.wp-mobile-hover', function(e) { 975 if ( $adminmenu.data('wp-responsive') ) { 976 return; 977 } 978 979 if ( ! $( e.target ).closest( '#adminmenu' ).length ) { 980 $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' ); 981 } 982 }); 983 984 /** 985 * Handles the opening or closing the submenu based on the mobile click|touch event. 986 * 987 * @param {Event} event The event object. 988 * 989 * @return {void} 990 */ 991 $adminmenu.find( 'a.wp-has-submenu' ).on( mobileEvent + '.wp-mobile-hover', function( event ) { 992 var $menuItem = $(this).parent(); 993 994 if ( $adminmenu.data( 'wp-responsive' ) ) { 995 return; 996 } 997 998 /* 999 * Show the sub instead of following the link if: 1000 * - the submenu is not open. 1001 * - the submenu is not shown inline or the menu is not folded. 1002 */ 1003 if ( ! $menuItem.hasClass( 'opensub' ) && ( ! $menuItem.hasClass( 'wp-menu-open' ) || $menuItem.width() < 40 ) ) { 1004 event.preventDefault(); 1005 adjustSubmenu( $menuItem ); 1006 $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' ); 1007 $menuItem.addClass('opensub'); 1008 } 1009 }); 1010 } 1011 1012 if ( ! isIOS && ! isAndroid ) { 1013 $adminmenu.find( 'li.wp-has-submenu' ).hoverIntent({ 1014 1015 /** 1016 * Opens the submenu when hovered over the menu item for desktops. 1017 * 1018 * @return {void} 1019 */ 1020 over: function() { 1021 var $menuItem = $( this ), 1022 $submenu = $menuItem.find( '.wp-submenu' ), 1023 top = parseInt( $submenu.css( 'top' ), 10 ); 1024 1025 if ( isNaN( top ) || top > -5 ) { // The submenu is visible. 1026 return; 1027 } 1028 1029 if ( $adminmenu.data( 'wp-responsive' ) ) { 1030 // The menu is in responsive mode, bail. 1031 return; 1032 } 1033 1034 adjustSubmenu( $menuItem ); 1035 $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' ); 1036 $menuItem.addClass( 'opensub' ); 1037 }, 1038 1039 /** 1040 * Closes the submenu when no longer hovering the menu item. 1041 * 1042 * @return {void} 1043 */ 1044 out: function(){ 1045 if ( $adminmenu.data( 'wp-responsive' ) ) { 1046 // The menu is in responsive mode, bail. 1047 return; 1048 } 1049 1050 $( this ).removeClass( 'opensub' ).find( '.wp-submenu' ).css( 'margin-top', '' ); 1051 }, 1052 timeout: 200, 1053 sensitivity: 7, 1054 interval: 90 1055 }); 1056 1057 /** 1058 * Opens the submenu on when focused on the menu item. 1059 * 1060 * @param {Event} event The event object. 1061 * 1062 * @return {void} 1063 */ 1064 $adminmenu.on( 'focus.adminmenu', '.wp-submenu a', function( event ) { 1065 if ( $adminmenu.data( 'wp-responsive' ) ) { 1066 // The menu is in responsive mode, bail. 1067 return; 1068 } 1069 1070 $( event.target ).closest( 'li.menu-top' ).addClass( 'opensub' ); 1071 1072 /** 1073 * Closes the submenu on blur from the menu item. 1074 * 1075 * @param {Event} event The event object. 1076 * 1077 * @return {void} 1078 */ 1079 }).on( 'blur.adminmenu', '.wp-submenu a', function( event ) { 1080 if ( $adminmenu.data( 'wp-responsive' ) ) { 1081 return; 1082 } 1083 1084 $( event.target ).closest( 'li.menu-top' ).removeClass( 'opensub' ); 1085 1086 /** 1087 * Adjusts the size for the submenu. 1088 * 1089 * @return {void} 1090 */ 1091 }).find( 'li.wp-has-submenu.wp-not-current-submenu' ).on( 'focusin.adminmenu', function() { 1092 adjustSubmenu( $( this ) ); 1093 }); 1094 } 1095 1096 /* 1097 * The `.below-h2` class is here just for backward compatibility with plugins 1098 * that are (incorrectly) using it. Do not use. Use `.inline` instead. See #34570. 1099 * If '.wp-header-end' is found, append the notices after it otherwise 1100 * after the first h1 or h2 heading found within the main content. 1101 */ 1102 if ( ! $headerEnd.length ) { 1103 $headerEnd = $( '.wrap h1, .wrap h2' ).first(); 1104 } 1105 $( 'div.updated, div.error, div.notice' ).not( '.inline, .below-h2' ).insertAfter( $headerEnd ); 1106 1107 /** 1108 * Makes notices dismissible. 1109 * 1110 * @since 4.4.0 1111 * 1112 * @return {void} 1113 */ 1114 function makeNoticesDismissible() { 1115 $( '.notice.is-dismissible' ).each( function() { 1116 var $el = $( this ), 1117 $button = $( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' ); 1118 1119 if ( $el.find( '.notice-dismiss' ).length ) { 1120 return; 1121 } 1122 1123 // Ensure plain text. 1124 $button.find( '.screen-reader-text' ).text( __( 'Dismiss this notice.' ) ); 1125 $button.on( 'click.wp-dismiss-notice', function( event ) { 1126 event.preventDefault(); 1127 $el.fadeTo( 100, 0, function() { 1128 $el.slideUp( 100, function() { 1129 $el.remove(); 1130 }); 1131 }); 1132 }); 1133 1134 $el.append( $button ); 1135 }); 1136 } 1137 1138 $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 ); 1139 1140 // Init screen meta. 1141 screenMeta.init(); 1142 1143 /** 1144 * Checks a checkbox. 1145 * 1146 * This event needs to be delegated. Ticket #37973. 1147 * 1148 * @return {boolean} Returns whether a checkbox is checked or not. 1149 */ 1150 $body.on( 'click', 'tbody > tr > .check-column :checkbox', function( event ) { 1151 // Shift click to select a range of checkboxes. 1152 if ( 'undefined' == event.shiftKey ) { return true; } 1153 if ( event.shiftKey ) { 1154 if ( !lastClicked ) { return true; } 1155 checks = $( lastClicked ).closest( 'form' ).find( ':checkbox' ).filter( ':visible:enabled' ); 1156 first = checks.index( lastClicked ); 1157 last = checks.index( this ); 1158 checked = $(this).prop('checked'); 1159 if ( 0 < first && 0 < last && first != last ) { 1160 sliced = ( last > first ) ? checks.slice( first, last ) : checks.slice( last, first ); 1161 sliced.prop( 'checked', function() { 1162 if ( $(this).closest('tr').is(':visible') ) 1163 return checked; 1164 1165 return false; 1166 }); 1167 } 1168 } 1169 lastClicked = this; 1170 1171 // Toggle the "Select all" checkboxes depending if the other ones are all checked or not. 1172 var unchecked = $(this).closest('tbody').find('tr.iedit').find(':checkbox').filter(':visible:enabled').not(':checked'); 1173 1174 /** 1175 * Determines if all checkboxes are checked. 1176 * 1177 * @return {boolean} Returns true if there are no unchecked checkboxes. 1178 */ 1179 $(this).closest('table').children('thead, tfoot').find(':checkbox').prop('checked', function() { 1180 return ( 0 === unchecked.length ); 1181 }); 1182 1183 return true; 1184 }); 1185 1186 /** 1187 * Controls all the toggles on bulk toggle change. 1188 * 1189 * When the bulk checkbox is changed, all the checkboxes in the tables are changed accordingly. 1190 * When the shift-button is pressed while changing the bulk checkbox the checkboxes in the table are inverted. 1191 * 1192 * This event needs to be delegated. Ticket #37973. 1193 * 1194 * @param {Event} event The event object. 1195 * 1196 * @return {boolean} 1197 */ 1198 $body.on( 'click.wp-toggle-checkboxes', 'thead .check-column :checkbox, tfoot .check-column :checkbox', function( event ) { 1199 var $this = $(this), 1200 $table = $this.closest( 'table' ), 1201 controlChecked = $this.prop('checked'), 1202 toggle = event.shiftKey || $this.data('wp-toggle'); 1203 1204 $table.children( 'tbody' ).filter(':visible') 1205 .children().children('.check-column').find(':checkbox') 1206 /** 1207 * Updates the checked state on the checkbox in the table. 1208 * 1209 * @return {boolean} True checks the checkbox, False unchecks the checkbox. 1210 */ 1211 .prop('checked', function() { 1212 if ( $(this).is(':hidden,:disabled') ) { 1213 return false; 1214 } 1215 1216 if ( toggle ) { 1217 return ! $(this).prop( 'checked' ); 1218 } else if ( controlChecked ) { 1219 return true; 1220 } 1221 1222 return false; 1223 }); 1224 1225 $table.children('thead, tfoot').filter(':visible') 1226 .children().children('.check-column').find(':checkbox') 1227 1228 /** 1229 * Syncs the bulk checkboxes on the top and bottom of the table. 1230 * 1231 * @return {boolean} True checks the checkbox, False unchecks the checkbox. 1232 */ 1233 .prop('checked', function() { 1234 if ( toggle ) { 1235 return false; 1236 } else if ( controlChecked ) { 1237 return true; 1238 } 1239 1240 return false; 1241 }); 1242 }); 1243 1244 /** 1245 * Marries a secondary control to its primary control. 1246 * 1247 * @param {jQuery} topSelector The top selector element. 1248 * @param {jQuery} topSubmit The top submit element. 1249 * @param {jQuery} bottomSelector The bottom selector element. 1250 * @param {jQuery} bottomSubmit The bottom submit element. 1251 * @return {void} 1252 */ 1253 function marryControls( topSelector, topSubmit, bottomSelector, bottomSubmit ) { 1254 /** 1255 * Updates the primary selector when the secondary selector is changed. 1256 * 1257 * @since 5.7.0 1258 * 1259 * @return {void} 1260 */ 1261 function updateTopSelector() { 1262 topSelector.val($(this).val()); 1263 } 1264 bottomSelector.on('change', updateTopSelector); 1265 1266 /** 1267 * Updates the secondary selector when the primary selector is changed. 1268 * 1269 * @since 5.7.0 1270 * 1271 * @return {void} 1272 */ 1273 function updateBottomSelector() { 1274 bottomSelector.val($(this).val()); 1275 } 1276 topSelector.on('change', updateBottomSelector); 1277 1278 /** 1279 * Triggers the primary submit when then secondary submit is clicked. 1280 * 1281 * @since 5.7.0 1282 * 1283 * @return {void} 1284 */ 1285 function triggerSubmitClick(e) { 1286 e.preventDefault(); 1287 e.stopPropagation(); 1288 1289 topSubmit.trigger('click'); 1290 } 1291 bottomSubmit.on('click', triggerSubmitClick); 1292 } 1293 1294 // Marry the secondary "Bulk actions" controls to the primary controls: 1295 marryControls( $('#bulk-action-selector-top'), $('#doaction'), $('#bulk-action-selector-bottom'), $('#doaction2') ); 1296 1297 // Marry the secondary "Change role to" controls to the primary controls: 1298 marryControls( $('#new_role'), $('#changeit'), $('#new_role2'), $('#changeit2') ); 1299 1300 /** 1301 * Shows row actions on focus of its parent container element or any other elements contained within. 1302 * 1303 * @return {void} 1304 */ 1305 $( '#wpbody-content' ).on({ 1306 focusin: function() { 1307 clearTimeout( transitionTimeout ); 1308 focusedRowActions = $( this ).find( '.row-actions' ); 1309 // transitionTimeout is necessary for Firefox, but Chrome won't remove the CSS class without a little help. 1310 $( '.row-actions' ).not( this ).removeClass( 'visible' ); 1311 focusedRowActions.addClass( 'visible' ); 1312 }, 1313 focusout: function() { 1314 // Tabbing between post title and .row-actions links needs a brief pause, otherwise 1315 // the .row-actions div gets hidden in transit in some browsers (ahem, Firefox). 1316 transitionTimeout = setTimeout( function() { 1317 focusedRowActions.removeClass( 'visible' ); 1318 }, 30 ); 1319 } 1320 }, '.table-view-list .has-row-actions' ); 1321 1322 // Toggle list table rows on small screens. 1323 $( 'tbody' ).on( 'click', '.toggle-row', function() { 1324 $( this ).closest( 'tr' ).toggleClass( 'is-expanded' ); 1325 }); 1326 1327 $('#default-password-nag-no').on( 'click', function() { 1328 setUserSetting('default_password_nag', 'hide'); 1329 $('div.default-password-nag').hide(); 1330 return false; 1331 }); 1332 1333 /** 1334 * Handles tab keypresses in theme and plugin file editor textareas. 1335 * 1336 * @param {Event} e The event object. 1337 * 1338 * @return {void} 1339 */ 1340 $('#newcontent').on('keydown.wpevent_InsertTab', function(e) { 1341 var el = e.target, selStart, selEnd, val, scroll, sel; 1342 1343 // After pressing escape key (keyCode: 27), the tab key should tab out of the textarea. 1344 if ( e.keyCode == 27 ) { 1345 // When pressing Escape: Opera 12 and 27 blur form fields, IE 8 clears them. 1346 e.preventDefault(); 1347 $(el).data('tab-out', true); 1348 return; 1349 } 1350 1351 // Only listen for plain tab key (keyCode: 9) without any modifiers. 1352 if ( e.keyCode != 9 || e.ctrlKey || e.altKey || e.shiftKey ) 1353 return; 1354 1355 // After tabbing out, reset it so next time the tab key can be used again. 1356 if ( $(el).data('tab-out') ) { 1357 $(el).data('tab-out', false); 1358 return; 1359 } 1360 1361 selStart = el.selectionStart; 1362 selEnd = el.selectionEnd; 1363 val = el.value; 1364 1365 // If any text is selected, replace the selection with a tab character. 1366 if ( document.selection ) { 1367 el.focus(); 1368 sel = document.selection.createRange(); 1369 sel.text = '\t'; 1370 } else if ( selStart >= 0 ) { 1371 scroll = this.scrollTop; 1372 el.value = val.substring(0, selStart).concat('\t', val.substring(selEnd) ); 1373 el.selectionStart = el.selectionEnd = selStart + 1; 1374 this.scrollTop = scroll; 1375 } 1376 1377 // Cancel the regular tab functionality, to prevent losing focus of the textarea. 1378 if ( e.stopPropagation ) 1379 e.stopPropagation(); 1380 if ( e.preventDefault ) 1381 e.preventDefault(); 1382 }); 1383 1384 // Reset page number variable for new filters/searches but not for bulk actions. See #17685. 1385 if ( pageInput.length ) { 1386 1387 /** 1388 * Handles pagination variable when filtering the list table. 1389 * 1390 * Set the pagination argument to the first page when the post-filter form is submitted. 1391 * This happens when pressing the 'filter' button on the list table page. 1392 * 1393 * The pagination argument should not be touched when the bulk action dropdowns are set to do anything. 1394 * 1395 * The form closest to the pageInput is the post-filter form. 1396 * 1397 * @return {void} 1398 */ 1399 pageInput.closest('form').on( 'submit', function() { 1400 /* 1401 * action = bulk action dropdown at the top of the table 1402 */ 1403 if ( $('select[name="action"]').val() == -1 && pageInput.val() == currentPage ) 1404 pageInput.val('1'); 1405 }); 1406 } 1407 1408 /** 1409 * Resets the bulk actions when the search button is clicked. 1410 * 1411 * @return {void} 1412 */ 1413 $('.search-box input[type="search"], .search-box input[type="submit"]').on( 'mousedown', function () { 1414 $('select[name^="action"]').val('-1'); 1415 }); 1416 1417 /** 1418 * Scrolls into view when focus.scroll-into-view is triggered. 1419 * 1420 * @param {Event} e The event object. 1421 * 1422 * @return {void} 1423 */ 1424 $('#contextual-help-link, #show-settings-link').on( 'focus.scroll-into-view', function(e){ 1425 if ( e.target.scrollIntoViewIfNeeded ) 1426 e.target.scrollIntoViewIfNeeded(false); 1427 }); 1428 1429 /** 1430 * Disables the submit upload buttons when no data is entered. 1431 * 1432 * @return {void} 1433 */ 1434 (function(){ 1435 var button, input, form = $('form.wp-upload-form'); 1436 1437 // Exit when no upload form is found. 1438 if ( ! form.length ) 1439 return; 1440 1441 button = form.find('input[type="submit"]'); 1442 input = form.find('input[type="file"]'); 1443 1444 /** 1445 * Determines if any data is entered in any file upload input. 1446 * 1447 * @since 3.5.0 1448 * 1449 * @return {void} 1450 */ 1451 function toggleUploadButton() { 1452 // When no inputs have a value, disable the upload buttons. 1453 button.prop('disabled', '' === input.map( function() { 1454 return $(this).val(); 1455 }).get().join('')); 1456 } 1457 1458 // Update the status initially. 1459 toggleUploadButton(); 1460 // Update the status when any file input changes. 1461 input.on('change', toggleUploadButton); 1462 })(); 1463 1464 /** 1465 * Pins the menu while distraction-free writing is enabled. 1466 * 1467 * @param {Event} event Event data. 1468 * 1469 * @since 4.1.0 1470 * 1471 * @return {void} 1472 */ 1473 function pinMenu( event ) { 1474 var windowPos = $window.scrollTop(), 1475 resizing = ! event || event.type !== 'scroll'; 1476 1477 if ( isIOS || $adminmenu.data( 'wp-responsive' ) ) { 1478 return; 1479 } 1480 1481 /* 1482 * When the menu is higher than the window and smaller than the entire page. 1483 * It should be adjusted to be able to see the entire menu. 1484 * 1485 * Otherwise it can be accessed normally. 1486 */ 1487 if ( height.menu + height.adminbar < height.window || 1488 height.menu + height.adminbar + 20 > height.wpwrap ) { 1489 unpinMenu(); 1490 return; 1491 } 1492 1493 menuIsPinned = true; 1494 1495 // If the menu is higher than the window, compensate on scroll. 1496 if ( height.menu + height.adminbar > height.window ) { 1497 // Check for overscrolling, this happens when swiping up at the top of the document in modern browsers. 1498 if ( windowPos < 0 ) { 1499 // Stick the menu to the top. 1500 if ( ! pinnedMenuTop ) { 1501 pinnedMenuTop = true; 1502 pinnedMenuBottom = false; 1503 1504 $adminMenuWrap.css({ 1505 position: 'fixed', 1506 top: '', 1507 bottom: '' 1508 }); 1509 } 1510 1511 return; 1512 } else if ( windowPos + height.window > $document.height() - 1 ) { 1513 // When overscrolling at the bottom, stick the menu to the bottom. 1514 if ( ! pinnedMenuBottom ) { 1515 pinnedMenuBottom = true; 1516 pinnedMenuTop = false; 1517 1518 $adminMenuWrap.css({ 1519 position: 'fixed', 1520 top: '', 1521 bottom: 0 1522 }); 1523 } 1524 1525 return; 1526 } 1527 1528 if ( windowPos > lastScrollPosition ) { 1529 // When a down scroll has been detected. 1530 1531 // If it was pinned to the top, unpin and calculate relative scroll. 1532 if ( pinnedMenuTop ) { 1533 pinnedMenuTop = false; 1534 // Calculate new offset position. 1535 menuTop = $adminMenuWrap.offset().top - height.adminbar - ( windowPos - lastScrollPosition ); 1536 1537 if ( menuTop + height.menu + height.adminbar < windowPos + height.window ) { 1538 menuTop = windowPos + height.window - height.menu - height.adminbar; 1539 } 1540 1541 $adminMenuWrap.css({ 1542 position: 'absolute', 1543 top: menuTop, 1544 bottom: '' 1545 }); 1546 } else if ( ! pinnedMenuBottom && $adminMenuWrap.offset().top + height.menu < windowPos + height.window ) { 1547 // Pin it to the bottom. 1548 pinnedMenuBottom = true; 1549 1550 $adminMenuWrap.css({ 1551 position: 'fixed', 1552 top: '', 1553 bottom: 0 1554 }); 1555 } 1556 } else if ( windowPos < lastScrollPosition ) { 1557 // When a scroll up is detected. 1558 1559 // If it was pinned to the bottom, unpin and calculate relative scroll. 1560 if ( pinnedMenuBottom ) { 1561 pinnedMenuBottom = false; 1562 1563 // Calculate new offset position. 1564 menuTop = $adminMenuWrap.offset().top - height.adminbar + ( lastScrollPosition - windowPos ); 1565 1566 if ( menuTop + height.menu > windowPos + height.window ) { 1567 menuTop = windowPos; 1568 } 1569 1570 $adminMenuWrap.css({ 1571 position: 'absolute', 1572 top: menuTop, 1573 bottom: '' 1574 }); 1575 } else if ( ! pinnedMenuTop && $adminMenuWrap.offset().top >= windowPos + height.adminbar ) { 1576 1577 // Pin it to the top. 1578 pinnedMenuTop = true; 1579 1580 $adminMenuWrap.css({ 1581 position: 'fixed', 1582 top: '', 1583 bottom: '' 1584 }); 1585 } 1586 } else if ( resizing ) { 1587 // Window is being resized. 1588 1589 pinnedMenuTop = pinnedMenuBottom = false; 1590 1591 // Calculate the new offset. 1592 menuTop = windowPos + height.window - height.menu - height.adminbar - 1; 1593 1594 if ( menuTop > 0 ) { 1595 $adminMenuWrap.css({ 1596 position: 'absolute', 1597 top: menuTop, 1598 bottom: '' 1599 }); 1600 } else { 1601 unpinMenu(); 1602 } 1603 } 1604 } 1605 1606 lastScrollPosition = windowPos; 1607 } 1608 1609 /** 1610 * Determines the height of certain elements. 1611 * 1612 * @since 4.1.0 1613 * 1614 * @return {void} 1615 */ 1616 function resetHeights() { 1617 height = { 1618 window: $window.height(), 1619 wpwrap: $wpwrap.height(), 1620 adminbar: $adminbar.height(), 1621 menu: $adminMenuWrap.height() 1622 }; 1623 } 1624 1625 /** 1626 * Unpins the menu. 1627 * 1628 * @since 4.1.0 1629 * 1630 * @return {void} 1631 */ 1632 function unpinMenu() { 1633 if ( isIOS || ! menuIsPinned ) { 1634 return; 1635 } 1636 1637 pinnedMenuTop = pinnedMenuBottom = menuIsPinned = false; 1638 $adminMenuWrap.css({ 1639 position: '', 1640 top: '', 1641 bottom: '' 1642 }); 1643 } 1644 1645 /** 1646 * Pins and unpins the menu when applicable. 1647 * 1648 * @since 4.1.0 1649 * 1650 * @return {void} 1651 */ 1652 function setPinMenu() { 1653 resetHeights(); 1654 1655 if ( $adminmenu.data('wp-responsive') ) { 1656 $body.removeClass( 'sticky-menu' ); 1657 unpinMenu(); 1658 } else if ( height.menu + height.adminbar > height.window ) { 1659 pinMenu(); 1660 $body.removeClass( 'sticky-menu' ); 1661 } else { 1662 $body.addClass( 'sticky-menu' ); 1663 unpinMenu(); 1664 } 1665 } 1666 1667 if ( ! isIOS ) { 1668 $window.on( 'scroll.pin-menu', pinMenu ); 1669 $document.on( 'tinymce-editor-init.pin-menu', function( event, editor ) { 1670 editor.on( 'wp-autoresize', resetHeights ); 1671 }); 1672 } 1673 1674 /** 1675 * Changes the sortables and responsiveness of metaboxes. 1676 * 1677 * @since 3.8.0 1678 * 1679 * @return {void} 1680 */ 1681 window.wpResponsive = { 1682 1683 /** 1684 * Initializes the wpResponsive object. 1685 * 1686 * @since 3.8.0 1687 * 1688 * @return {void} 1689 */ 1690 init: function() { 1691 var self = this; 1692 1693 this.maybeDisableSortables = this.maybeDisableSortables.bind( this ); 1694 1695 // Modify functionality based on custom activate/deactivate event. 1696 $document.on( 'wp-responsive-activate.wp-responsive', function() { 1697 self.activate(); 1698 }).on( 'wp-responsive-deactivate.wp-responsive', function() { 1699 self.deactivate(); 1700 }); 1701 1702 $( '#wp-admin-bar-menu-toggle a' ).attr( 'aria-expanded', 'false' ); 1703 1704 // Toggle sidebar when toggle is clicked. 1705 $( '#wp-admin-bar-menu-toggle' ).on( 'click.wp-responsive', function( event ) { 1706 event.preventDefault(); 1707 1708 // Close any open toolbar submenus. 1709 $adminbar.find( '.hover' ).removeClass( 'hover' ); 1710 1711 $wpwrap.toggleClass( 'wp-responsive-open' ); 1712 if ( $wpwrap.hasClass( 'wp-responsive-open' ) ) { 1713 $(this).find('a').attr( 'aria-expanded', 'true' ); 1714 $( '#adminmenu a:first' ).trigger( 'focus' ); 1715 } else { 1716 $(this).find('a').attr( 'aria-expanded', 'false' ); 1717 } 1718 } ); 1719 1720 // Close sidebar when target moves outside of toggle and sidebar. 1721 $( document ).on( 'click', function( event ) { 1722 if ( ! $wpwrap.hasClass( 'wp-responsive-open' ) || ! document.hasFocus() ) { 1723 return; 1724 } 1725 1726 var focusIsInToggle = $.contains( $( '#wp-admin-bar-menu-toggle' )[0], event.target ); 1727 var focusIsInSidebar = $.contains( $( '#adminmenuwrap' )[0], event.target ); 1728 1729 if ( ! focusIsInToggle && ! focusIsInSidebar ) { 1730 $( '#wp-admin-bar-menu-toggle' ).trigger( 'click.wp-responsive' ); 1731 } 1732 } ); 1733 1734 // Close sidebar when a keypress completes outside of toggle and sidebar. 1735 $( document ).on( 'keyup', function( event ) { 1736 var toggleButton = $( '#wp-admin-bar-menu-toggle' )[0]; 1737 if ( ! $wpwrap.hasClass( 'wp-responsive-open' ) ) { 1738 return; 1739 } 1740 if ( 27 === event.keyCode ) { 1741 $( toggleButton ).trigger( 'click.wp-responsive' ); 1742 $( toggleButton ).find( 'a' ).trigger( 'focus' ); 1743 } else { 1744 if ( 9 === event.keyCode ) { 1745 var sidebar = $( '#adminmenuwrap' )[0]; 1746 var focusedElement = event.relatedTarget || document.activeElement; 1747 // A brief delay is required to allow focus to switch to another element. 1748 setTimeout( function() { 1749 var focusIsInToggle = $.contains( toggleButton, focusedElement ); 1750 var focusIsInSidebar = $.contains( sidebar, focusedElement ); 1751 1752 if ( ! focusIsInToggle && ! focusIsInSidebar ) { 1753 $( toggleButton ).trigger( 'click.wp-responsive' ); 1754 } 1755 }, 10 ); 1756 } 1757 } 1758 }); 1759 1760 // Add menu events. 1761 $adminmenu.on( 'click.wp-responsive', 'li.wp-has-submenu > a', function( event ) { 1762 if ( ! $adminmenu.data('wp-responsive') ) { 1763 return; 1764 } 1765 1766 $( this ).parent( 'li' ).toggleClass( 'selected' ); 1767 $( this ).trigger( 'focus' ); 1768 event.preventDefault(); 1769 }); 1770 1771 self.trigger(); 1772 $document.on( 'wp-window-resized.wp-responsive', this.trigger.bind( this ) ); 1773 1774 // This needs to run later as UI Sortable may be initialized when the document is ready. 1775 $window.on( 'load.wp-responsive', this.maybeDisableSortables ); 1776 $document.on( 'postbox-toggled', this.maybeDisableSortables ); 1777 1778 // When the screen columns are changed, potentially disable sortables. 1779 $( '#screen-options-wrap input' ).on( 'click', this.maybeDisableSortables ); 1780 }, 1781 1782 /** 1783 * Disable sortables if there is only one metabox, or the screen is in one column mode. Otherwise, enable sortables. 1784 * 1785 * @since 5.3.0 1786 * 1787 * @return {void} 1788 */ 1789 maybeDisableSortables: function() { 1790 var width = navigator.userAgent.indexOf('AppleWebKit/') > -1 ? $window.width() : window.innerWidth; 1791 1792 if ( 1793 ( width <= 782 ) || 1794 ( 1 >= $sortables.find( '.ui-sortable-handle:visible' ).length && jQuery( '.columns-prefs-1 input' ).prop( 'checked' ) ) 1795 ) { 1796 this.disableSortables(); 1797 } else { 1798 this.enableSortables(); 1799 } 1800 }, 1801 1802 /** 1803 * Changes properties of body and admin menu. 1804 * 1805 * Pins and unpins the menu and adds the auto-fold class to the body. 1806 * Makes the admin menu responsive and disables the metabox sortables. 1807 * 1808 * @since 3.8.0 1809 * 1810 * @return {void} 1811 */ 1812 activate: function() { 1813 setPinMenu(); 1814 1815 if ( ! $body.hasClass( 'auto-fold' ) ) { 1816 $body.addClass( 'auto-fold' ); 1817 } 1818 1819 $adminmenu.data( 'wp-responsive', 1 ); 1820 this.disableSortables(); 1821 }, 1822 1823 /** 1824 * Changes properties of admin menu and enables metabox sortables. 1825 * 1826 * Pin and unpin the menu. 1827 * Removes the responsiveness of the admin menu and enables the metabox sortables. 1828 * 1829 * @since 3.8.0 1830 * 1831 * @return {void} 1832 */ 1833 deactivate: function() { 1834 setPinMenu(); 1835 $adminmenu.removeData('wp-responsive'); 1836 1837 this.maybeDisableSortables(); 1838 }, 1839 1840 /** 1841 * Sets the responsiveness and enables the overlay based on the viewport width. 1842 * 1843 * @since 3.8.0 1844 * 1845 * @return {void} 1846 */ 1847 trigger: function() { 1848 var viewportWidth = getViewportWidth(); 1849 1850 // Exclude IE < 9, it doesn't support @media CSS rules. 1851 if ( ! viewportWidth ) { 1852 return; 1853 } 1854 1855 if ( viewportWidth <= 782 ) { 1856 if ( ! wpResponsiveActive ) { 1857 $document.trigger( 'wp-responsive-activate' ); 1858 wpResponsiveActive = true; 1859 } 1860 } else { 1861 if ( wpResponsiveActive ) { 1862 $document.trigger( 'wp-responsive-deactivate' ); 1863 wpResponsiveActive = false; 1864 } 1865 } 1866 1867 if ( viewportWidth <= 480 ) { 1868 this.enableOverlay(); 1869 } else { 1870 this.disableOverlay(); 1871 } 1872 1873 this.maybeDisableSortables(); 1874 }, 1875 1876 /** 1877 * Inserts a responsive overlay and toggles the window. 1878 * 1879 * @since 3.8.0 1880 * 1881 * @return {void} 1882 */ 1883 enableOverlay: function() { 1884 if ( $overlay.length === 0 ) { 1885 $overlay = $( '<div id="wp-responsive-overlay"></div>' ) 1886 .insertAfter( '#wpcontent' ) 1887 .hide() 1888 .on( 'click.wp-responsive', function() { 1889 $toolbar.find( '.menupop.hover' ).removeClass( 'hover' ); 1890 $( this ).hide(); 1891 }); 1892 } 1893 1894 $toolbarPopups.on( 'click.wp-responsive', function() { 1895 $overlay.show(); 1896 }); 1897 }, 1898 1899 /** 1900 * Disables the responsive overlay and removes the overlay. 1901 * 1902 * @since 3.8.0 1903 * 1904 * @return {void} 1905 */ 1906 disableOverlay: function() { 1907 $toolbarPopups.off( 'click.wp-responsive' ); 1908 $overlay.hide(); 1909 }, 1910 1911 /** 1912 * Disables sortables. 1913 * 1914 * @since 3.8.0 1915 * 1916 * @return {void} 1917 */ 1918 disableSortables: function() { 1919 if ( $sortables.length ) { 1920 try { 1921 $sortables.sortable( 'disable' ); 1922 $sortables.find( '.ui-sortable-handle' ).addClass( 'is-non-sortable' ); 1923 } catch ( e ) {} 1924 } 1925 }, 1926 1927 /** 1928 * Enables sortables. 1929 * 1930 * @since 3.8.0 1931 * 1932 * @return {void} 1933 */ 1934 enableSortables: function() { 1935 if ( $sortables.length ) { 1936 try { 1937 $sortables.sortable( 'enable' ); 1938 $sortables.find( '.ui-sortable-handle' ).removeClass( 'is-non-sortable' ); 1939 } catch ( e ) {} 1940 } 1941 } 1942 }; 1943 1944 /** 1945 * Add an ARIA role `button` to elements that behave like UI controls when JavaScript is on. 1946 * 1947 * @since 4.5.0 1948 * 1949 * @return {void} 1950 */ 1951 function aria_button_if_js() { 1952 $( '.aria-button-if-js' ).attr( 'role', 'button' ); 1953 } 1954 1955 $( document ).on( 'ajaxComplete', function() { 1956 aria_button_if_js(); 1957 }); 1958 1959 /** 1960 * Get the viewport width. 1961 * 1962 * @since 4.7.0 1963 * 1964 * @return {number|boolean} The current viewport width or false if the 1965 * browser doesn't support innerWidth (IE < 9). 1966 */ 1967 function getViewportWidth() { 1968 var viewportWidth = false; 1969 1970 if ( window.innerWidth ) { 1971 // On phones, window.innerWidth is affected by zooming. 1972 viewportWidth = Math.max( window.innerWidth, document.documentElement.clientWidth ); 1973 } 1974 1975 return viewportWidth; 1976 } 1977 1978 /** 1979 * Sets the admin menu collapsed/expanded state. 1980 * 1981 * Sets the global variable `menuState` and triggers a custom event passing 1982 * the current menu state. 1983 * 1984 * @since 4.7.0 1985 * 1986 * @return {void} 1987 */ 1988 function setMenuState() { 1989 var viewportWidth = getViewportWidth() || 961; 1990 1991 if ( viewportWidth <= 782 ) { 1992 menuState = 'responsive'; 1993 } else if ( $body.hasClass( 'folded' ) || ( $body.hasClass( 'auto-fold' ) && viewportWidth <= 960 && viewportWidth > 782 ) ) { 1994 menuState = 'folded'; 1995 } else { 1996 menuState = 'open'; 1997 } 1998 1999 $document.trigger( 'wp-menu-state-set', { state: menuState } ); 2000 } 2001 2002 // Set the menu state when the window gets resized. 2003 $document.on( 'wp-window-resized.set-menu-state', setMenuState ); 2004 2005 /** 2006 * Sets ARIA attributes on the collapse/expand menu button. 2007 * 2008 * When the admin menu is open or folded, updates the `aria-expanded` and 2009 * `aria-label` attributes of the button to give feedback to assistive 2010 * technologies. In the responsive view, the button is always hidden. 2011 * 2012 * @since 4.7.0 2013 * 2014 * @return {void} 2015 */ 2016 $document.on( 'wp-menu-state-set wp-collapse-menu', function( event, eventData ) { 2017 var $collapseButton = $( '#collapse-button' ), 2018 ariaExpanded, ariaLabelText; 2019 2020 if ( 'folded' === eventData.state ) { 2021 ariaExpanded = 'false'; 2022 ariaLabelText = __( 'Expand Main menu' ); 2023 } else { 2024 ariaExpanded = 'true'; 2025 ariaLabelText = __( 'Collapse Main menu' ); 2026 } 2027 2028 $collapseButton.attr({ 2029 'aria-expanded': ariaExpanded, 2030 'aria-label': ariaLabelText 2031 }); 2032 }); 2033 2034 window.wpResponsive.init(); 2035 setPinMenu(); 2036 setMenuState(); 2037 currentMenuItemHasPopup(); 2038 makeNoticesDismissible(); 2039 aria_button_if_js(); 2040 2041 $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 ); 2042 2043 // Set initial focus on a specific element. 2044 $( '.wp-initial-focus' ).trigger( 'focus' ); 2045 2046 // Toggle update details on update-core.php. 2047 $body.on( 'click', '.js-update-details-toggle', function() { 2048 var $updateNotice = $( this ).closest( '.js-update-details' ), 2049 $progressDiv = $( '#' + $updateNotice.data( 'update-details' ) ); 2050 2051 /* 2052 * When clicking on "Show details" move the progress div below the update 2053 * notice. Make sure it gets moved just the first time. 2054 */ 2055 if ( ! $progressDiv.hasClass( 'update-details-moved' ) ) { 2056 $progressDiv.insertAfter( $updateNotice ).addClass( 'update-details-moved' ); 2057 } 2058 2059 // Toggle the progress div visibility. 2060 $progressDiv.toggle(); 2061 // Toggle the Show Details button expanded state. 2062 $( this ).attr( 'aria-expanded', $progressDiv.is( ':visible' ) ); 2063 }); 2064 }); 2065 2066 /** 2067 * Hides the update button for expired plugin or theme uploads. 2068 * 2069 * On the "Update plugin/theme from uploaded zip" screen, once the upload has expired, 2070 * hides the "Replace current with uploaded" button and displays a warning. 2071 * 2072 * @since 5.5.0 2073 */ 2074 $( function( $ ) { 2075 var $overwrite, $warning; 2076 2077 if ( ! $body.hasClass( 'update-php' ) ) { 2078 return; 2079 } 2080 2081 $overwrite = $( 'a.update-from-upload-overwrite' ); 2082 $warning = $( '.update-from-upload-expired' ); 2083 2084 if ( ! $overwrite.length || ! $warning.length ) { 2085 return; 2086 } 2087 2088 window.setTimeout( 2089 function() { 2090 $overwrite.hide(); 2091 $warning.removeClass( 'hidden' ); 2092 2093 if ( window.wp && window.wp.a11y ) { 2094 window.wp.a11y.speak( $warning.text() ); 2095 } 2096 }, 2097 7140000 // 119 minutes. The uploaded file is deleted after 2 hours. 2098 ); 2099 } ); 2100 2101 // Fire a custom jQuery event at the end of window resize. 2102 ( function() { 2103 var timeout; 2104 2105 /** 2106 * Triggers the WP window-resize event. 2107 * 2108 * @since 3.8.0 2109 * 2110 * @return {void} 2111 */ 2112 function triggerEvent() { 2113 $document.trigger( 'wp-window-resized' ); 2114 } 2115 2116 /** 2117 * Fires the trigger event again after 200 ms. 2118 * 2119 * @since 3.8.0 2120 * 2121 * @return {void} 2122 */ 2123 function fireOnce() { 2124 window.clearTimeout( timeout ); 2125 timeout = window.setTimeout( triggerEvent, 200 ); 2126 } 2127 2128 $window.on( 'resize.wp-fire-once', fireOnce ); 2129 }()); 2130 2131 // Make Windows 8 devices play along nicely. 2132 (function(){ 2133 if ( '-ms-user-select' in document.documentElement.style && navigator.userAgent.match(/IEMobile\/10\.0/) ) { 2134 var msViewportStyle = document.createElement( 'style' ); 2135 msViewportStyle.appendChild( 2136 document.createTextNode( '@-ms-viewport{width:auto!important}' ) 2137 ); 2138 document.getElementsByTagName( 'head' )[0].appendChild( msViewportStyle ); 2139 } 2140 })(); 2141 2142 }( jQuery, window )); 2143 2144 /** 2145 * Freeze animated plugin icons when reduced motion is enabled. 2146 * 2147 * When the user has enabled the 'prefers-reduced-motion' setting, this module 2148 * stops animations for all GIFs on the page with the class 'plugin-icon' or 2149 * plugin icon images in the update plugins table. 2150 * 2151 * @since 6.4.0 2152 */ 2153 (function() { 2154 // Private variables and methods. 2155 var priv = {}, 2156 pub = {}, 2157 mediaQuery; 2158 2159 // Initialize pauseAll to false; it will be set to true if reduced motion is preferred. 2160 priv.pauseAll = false; 2161 if ( window.matchMedia ) { 2162 mediaQuery = window.matchMedia( '(prefers-reduced-motion: reduce)' ); 2163 if ( ! mediaQuery || mediaQuery.matches ) { 2164 priv.pauseAll = true; 2165 } 2166 } 2167 2168 // Method to replace animated GIFs with a static frame. 2169 priv.freezeAnimatedPluginIcons = function( img ) { 2170 var coverImage = function() { 2171 var width = img.width; 2172 var height = img.height; 2173 var canvas = document.createElement( 'canvas' ); 2174 2175 // Set canvas dimensions. 2176 canvas.width = width; 2177 canvas.height = height; 2178 2179 // Copy classes from the image to the canvas. 2180 canvas.className = img.className; 2181 2182 // Check if the image is inside a specific table. 2183 var isInsideUpdateTable = img.closest( '#update-plugins-table' ); 2184 2185 if ( isInsideUpdateTable ) { 2186 // Transfer computed styles from image to canvas. 2187 var computedStyles = window.getComputedStyle( img ), 2188 i, max; 2189 for ( i = 0, max = computedStyles.length; i < max; i++ ) { 2190 var propName = computedStyles[ i ]; 2191 var propValue = computedStyles.getPropertyValue( propName ); 2192 canvas.style[ propName ] = propValue; 2193 } 2194 } 2195 2196 // Draw the image onto the canvas. 2197 canvas.getContext( '2d' ).drawImage( img, 0, 0, width, height ); 2198 2199 // Set accessibility attributes on canvas. 2200 canvas.setAttribute( 'aria-hidden', 'true' ); 2201 canvas.setAttribute( 'role', 'presentation' ); 2202 2203 // Insert canvas before the image and set the image to be near-invisible. 2204 var parent = img.parentNode; 2205 parent.insertBefore( canvas, img ); 2206 img.style.opacity = 0.01; 2207 img.style.width = '0px'; 2208 img.style.height = '0px'; 2209 }; 2210 2211 // If the image is already loaded, apply the coverImage function. 2212 if ( img.complete ) { 2213 coverImage(); 2214 } else { 2215 // Otherwise, wait for the image to load. 2216 img.addEventListener( 'load', coverImage, true ); 2217 } 2218 }; 2219 2220 // Public method to freeze all relevant GIFs on the page. 2221 pub.freezeAll = function() { 2222 var images = document.querySelectorAll( '.plugin-icon, #update-plugins-table img' ); 2223 for ( var x = 0; x < images.length; x++ ) { 2224 if ( /\.gif(?:\?|$)/i.test( images[ x ].src ) ) { 2225 priv.freezeAnimatedPluginIcons( images[ x ] ); 2226 } 2227 } 2228 }; 2229 2230 // Only run the freezeAll method if the user prefers reduced motion. 2231 if ( true === priv.pauseAll ) { 2232 pub.freezeAll(); 2233 } 2234 2235 // Listen for jQuery AJAX events. 2236 ( function( $ ) { 2237 if ( window.pagenow === 'plugin-install' ) { 2238 // Only listen for ajaxComplete if this is the plugin-install.php page. 2239 $( document ).ajaxComplete( function( event, xhr, settings ) { 2240 2241 // Check if this is the 'search-install-plugins' request. 2242 if ( settings.data && typeof settings.data === 'string' && settings.data.includes( 'action=search-install-plugins' ) ) { 2243 // Recheck if the user prefers reduced motion. 2244 if ( window.matchMedia ) { 2245 var mediaQuery = window.matchMedia( '(prefers-reduced-motion: reduce)' ); 2246 if ( mediaQuery.matches ) { 2247 pub.freezeAll(); 2248 } 2249 } else { 2250 // Fallback for browsers that don't support matchMedia. 2251 if ( true === priv.pauseAll ) { 2252 pub.freezeAll(); 2253 } 2254 } 2255 } 2256 } ); 2257 } 2258 } )( jQuery ); 2259 2260 // Expose public methods. 2261 return pub; 2262 })();
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Thu Mar 28 08:20:01 2024 | Cross-referenced by PHPXref |