--- a/wp/wp-admin/js/common.js Thu Sep 29 08:06:27 2022 +0200
+++ b/wp/wp-admin/js/common.js Fri Sep 05 18:40:08 2025 +0200
@@ -354,6 +354,21 @@
window.setPostThumbnailL10n = deprecateL10nObject( 'setPostThumbnailL10n', window.setPostThumbnailL10n, '5.5.0' );
/**
+ * Removed in 6.5.0, needed for back-compatibility.
+ *
+ * @since 4.5.0
+ * @deprecated 6.5.0
+ */
+window.uiAutocompleteL10n = window.uiAutocompleteL10n || {
+ noResults: '',
+ oneResult: '',
+ manyResults: '',
+ itemSelected: ''
+};
+
+window.uiAutocompleteL10n = deprecateL10nObject( 'uiAutocompleteL10n', window.uiAutocompleteL10n, '6.5.0' );
+
+/**
* Removed in 3.3.0, needed for back-compatibility.
*
* @since 2.7.0
@@ -617,7 +632,7 @@
* @return {void}
*/
panel.slideDown( 'fast', function() {
- panel.trigger( 'focus' );
+ panel.removeClass( 'hidden' ).trigger( 'focus' );
button.addClass( 'screen-meta-active' ).attr( 'aria-expanded', true );
});
@@ -646,6 +661,7 @@
button.removeClass( 'screen-meta-active' ).attr( 'aria-expanded', false );
$('.screen-meta-toggle').css('visibility', '');
panel.parent().hide();
+ panel.addClass( 'hidden' );
});
$document.trigger( 'screen:options:close' );
@@ -751,9 +767,15 @@
selectionStart = $permalinkStructure[ 0 ].selectionStart,
selectionEnd = $permalinkStructure[ 0 ].selectionEnd,
textToAppend = $( this ).text().trim(),
- textToAnnounce = $( this ).attr( 'data-added' ),
+ textToAnnounce,
newSelectionStart;
+ if ( $( this ).hasClass( 'active' ) ) {
+ textToAnnounce = $( this ).attr( 'data-removed' );
+ } else {
+ textToAnnounce = $( this ).attr( 'data-added' );
+ }
+
// Remove structure tag if already part of the structure.
if ( -1 !== permalinkStructureValue.indexOf( textToAppend ) ) {
permalinkStructureValue = permalinkStructureValue.replace( textToAppend + '/', '' );
@@ -881,27 +903,6 @@
});
/**
- * Handles the `aria-haspopup` attribute on the current menu item when it has a submenu.
- *
- * @since 4.4.0
- *
- * @return {void}
- */
- function currentMenuItemHasPopup() {
- var $current = $( 'a.wp-has-current-submenu' );
-
- if ( 'folded' === menuState ) {
- // When folded or auto-folded and not responsive view, the current menu item does have a fly-out sub-menu.
- $current.attr( 'aria-haspopup', 'true' );
- } else {
- // When expanded or in responsive view, reset aria-haspopup.
- $current.attr( 'aria-haspopup', 'false' );
- }
- }
-
- $document.on( 'wp-menu-state-set wp-collapse-menu wp-responsive-activate wp-responsive-deactivate', currentMenuItemHasPopup );
-
- /**
* Ensures an admin submenu is within the visual viewport.
*
* @since 4.1.0
@@ -931,7 +932,7 @@
adjustment = maxtop;
}
- if ( adjustment > 1 ) {
+ if ( adjustment > 1 && $('#wp-admin-bar-menu-toggle').is(':hidden') ) {
$submenu.css( 'margin-top', '-' + adjustment + 'px' );
} else {
$submenu.css( 'margin-top', '' );
@@ -1147,7 +1148,7 @@
lastClicked = this;
// Toggle the "Select all" checkboxes depending if the other ones are all checked or not.
- var unchecked = $(this).closest('tbody').find(':checkbox').filter(':visible:enabled').not(':checked');
+ var unchecked = $(this).closest('tbody').find('tr').find(':checkbox').filter(':visible:enabled').not(':checked');
/**
* Determines if all checkboxes are checked.
@@ -1400,8 +1401,8 @@
* @return {void}
*/
$('#contextual-help-link, #show-settings-link').on( 'focus.scroll-into-view', function(e){
- if ( e.target.scrollIntoView )
- e.target.scrollIntoView(false);
+ if ( e.target.scrollIntoViewIfNeeded )
+ e.target.scrollIntoViewIfNeeded(false);
});
/**
@@ -1673,8 +1674,10 @@
// Modify functionality based on custom activate/deactivate event.
$document.on( 'wp-responsive-activate.wp-responsive', function() {
self.activate();
+ self.toggleAriaHasPopup( 'add' );
}).on( 'wp-responsive-deactivate.wp-responsive', function() {
self.deactivate();
+ self.toggleAriaHasPopup( 'remove' );
});
$( '#wp-admin-bar-menu-toggle a' ).attr( 'aria-expanded', 'false' );
@@ -1695,13 +1698,55 @@
}
} );
+ // Close sidebar when target moves outside of toggle and sidebar.
+ $( document ).on( 'click', function( event ) {
+ if ( ! $wpwrap.hasClass( 'wp-responsive-open' ) || ! document.hasFocus() ) {
+ return;
+ }
+
+ var focusIsInToggle = $.contains( $( '#wp-admin-bar-menu-toggle' )[0], event.target );
+ var focusIsInSidebar = $.contains( $( '#adminmenuwrap' )[0], event.target );
+
+ if ( ! focusIsInToggle && ! focusIsInSidebar ) {
+ $( '#wp-admin-bar-menu-toggle' ).trigger( 'click.wp-responsive' );
+ }
+ } );
+
+ // Close sidebar when a keypress completes outside of toggle and sidebar.
+ $( document ).on( 'keyup', function( event ) {
+ var toggleButton = $( '#wp-admin-bar-menu-toggle' )[0];
+ if ( ! $wpwrap.hasClass( 'wp-responsive-open' ) ) {
+ return;
+ }
+ if ( 27 === event.keyCode ) {
+ $( toggleButton ).trigger( 'click.wp-responsive' );
+ $( toggleButton ).find( 'a' ).trigger( 'focus' );
+ } else {
+ if ( 9 === event.keyCode ) {
+ var sidebar = $( '#adminmenuwrap' )[0];
+ var focusedElement = event.relatedTarget || document.activeElement;
+ // A brief delay is required to allow focus to switch to another element.
+ setTimeout( function() {
+ var focusIsInToggle = $.contains( toggleButton, focusedElement );
+ var focusIsInSidebar = $.contains( sidebar, focusedElement );
+
+ if ( ! focusIsInToggle && ! focusIsInSidebar ) {
+ $( toggleButton ).trigger( 'click.wp-responsive' );
+ }
+ }, 10 );
+ }
+ }
+ });
+
// Add menu events.
$adminmenu.on( 'click.wp-responsive', 'li.wp-has-submenu > a', function( event ) {
if ( ! $adminmenu.data('wp-responsive') ) {
return;
}
-
+ let state = ( 'false' === $( this ).attr( 'aria-expanded' ) ) ? 'true' : 'false';
$( this ).parent( 'li' ).toggleClass( 'selected' );
+ $( this ).attr( 'aria-expanded', state );
+ $( this ).trigger( 'focus' );
event.preventDefault();
});
@@ -1775,6 +1820,34 @@
},
/**
+ * Toggles the aria-haspopup attribute for the responsive admin menu.
+ *
+ * The aria-haspopup attribute is only necessary for the responsive menu.
+ * See ticket https://core.trac.wordpress.org/ticket/43095
+ *
+ * @since 6.6.0
+ *
+ * @param {string} action Whether to add or remove the aria-haspopup attribute.
+ *
+ * @return {void}
+ */
+ toggleAriaHasPopup: function( action ) {
+ var elements = $adminmenu.find( '[data-ariahaspopup]' );
+
+ if ( action === 'add' ) {
+ elements.each( function() {
+ $( this ).attr( 'aria-haspopup', 'menu' ).attr( 'aria-expanded', 'false' );
+ } );
+
+ return;
+ }
+
+ elements.each( function() {
+ $( this ).removeAttr( 'aria-haspopup' ).removeAttr( 'aria-expanded' );
+ } );
+ },
+
+ /**
* Sets the responsiveness and enables the overlay based on the viewport width.
*
* @since 3.8.0
@@ -1971,7 +2044,6 @@
window.wpResponsive.init();
setPinMenu();
setMenuState();
- currentMenuItemHasPopup();
makeNoticesDismissible();
aria_button_if_js();
@@ -2077,3 +2149,123 @@
})();
}( jQuery, window ));
+
+/**
+ * Freeze animated plugin icons when reduced motion is enabled.
+ *
+ * When the user has enabled the 'prefers-reduced-motion' setting, this module
+ * stops animations for all GIFs on the page with the class 'plugin-icon' or
+ * plugin icon images in the update plugins table.
+ *
+ * @since 6.4.0
+ */
+(function() {
+ // Private variables and methods.
+ var priv = {},
+ pub = {},
+ mediaQuery;
+
+ // Initialize pauseAll to false; it will be set to true if reduced motion is preferred.
+ priv.pauseAll = false;
+ if ( window.matchMedia ) {
+ mediaQuery = window.matchMedia( '(prefers-reduced-motion: reduce)' );
+ if ( ! mediaQuery || mediaQuery.matches ) {
+ priv.pauseAll = true;
+ }
+ }
+
+ // Method to replace animated GIFs with a static frame.
+ priv.freezeAnimatedPluginIcons = function( img ) {
+ var coverImage = function() {
+ var width = img.width;
+ var height = img.height;
+ var canvas = document.createElement( 'canvas' );
+
+ // Set canvas dimensions.
+ canvas.width = width;
+ canvas.height = height;
+
+ // Copy classes from the image to the canvas.
+ canvas.className = img.className;
+
+ // Check if the image is inside a specific table.
+ var isInsideUpdateTable = img.closest( '#update-plugins-table' );
+
+ if ( isInsideUpdateTable ) {
+ // Transfer computed styles from image to canvas.
+ var computedStyles = window.getComputedStyle( img ),
+ i, max;
+ for ( i = 0, max = computedStyles.length; i < max; i++ ) {
+ var propName = computedStyles[ i ];
+ var propValue = computedStyles.getPropertyValue( propName );
+ canvas.style[ propName ] = propValue;
+ }
+ }
+
+ // Draw the image onto the canvas.
+ canvas.getContext( '2d' ).drawImage( img, 0, 0, width, height );
+
+ // Set accessibility attributes on canvas.
+ canvas.setAttribute( 'aria-hidden', 'true' );
+ canvas.setAttribute( 'role', 'presentation' );
+
+ // Insert canvas before the image and set the image to be near-invisible.
+ var parent = img.parentNode;
+ parent.insertBefore( canvas, img );
+ img.style.opacity = 0.01;
+ img.style.width = '0px';
+ img.style.height = '0px';
+ };
+
+ // If the image is already loaded, apply the coverImage function.
+ if ( img.complete ) {
+ coverImage();
+ } else {
+ // Otherwise, wait for the image to load.
+ img.addEventListener( 'load', coverImage, true );
+ }
+ };
+
+ // Public method to freeze all relevant GIFs on the page.
+ pub.freezeAll = function() {
+ var images = document.querySelectorAll( '.plugin-icon, #update-plugins-table img' );
+ for ( var x = 0; x < images.length; x++ ) {
+ if ( /\.gif(?:\?|$)/i.test( images[ x ].src ) ) {
+ priv.freezeAnimatedPluginIcons( images[ x ] );
+ }
+ }
+ };
+
+ // Only run the freezeAll method if the user prefers reduced motion.
+ if ( true === priv.pauseAll ) {
+ pub.freezeAll();
+ }
+
+ // Listen for jQuery AJAX events.
+ ( function( $ ) {
+ if ( window.pagenow === 'plugin-install' ) {
+ // Only listen for ajaxComplete if this is the plugin-install.php page.
+ $( document ).ajaxComplete( function( event, xhr, settings ) {
+
+ // Check if this is the 'search-install-plugins' request.
+ if ( settings.data && typeof settings.data === 'string' && settings.data.includes( 'action=search-install-plugins' ) ) {
+ // Recheck if the user prefers reduced motion.
+ if ( window.matchMedia ) {
+ var mediaQuery = window.matchMedia( '(prefers-reduced-motion: reduce)' );
+ if ( mediaQuery.matches ) {
+ pub.freezeAll();
+ }
+ } else {
+ // Fallback for browsers that don't support matchMedia.
+ if ( true === priv.pauseAll ) {
+ pub.freezeAll();
+ }
+ }
+ }
+ } );
+ }
+ } )( jQuery );
+
+ // Expose public methods.
+ return pub;
+})();