';
diff --git a/src/wp-admin/includes/update.php b/src/wp-admin/includes/update.php
index 8939c89901..e412073ebe 100644
--- a/src/wp-admin/includes/update.php
+++ b/src/wp-admin/includes/update.php
@@ -339,7 +339,7 @@ function wp_plugin_update_row( $file, $plugin_data ) {
if ( ! current_user_can( 'update_plugins' ) ) {
/* translators: 1: plugin name, 2: details URL, 3: accessibility text, 4: version number */
- printf( __( 'There is a new version of %1$s available. View version %4$s details.' ),
+ printf( __( 'There is a new version of %1$s available. View version %4$s details.' ),
$plugin_name,
esc_url( $details_url ),
/* translators: 1: plugin name, 2: version number */
@@ -348,7 +348,7 @@ function wp_plugin_update_row( $file, $plugin_data ) {
);
} elseif ( empty( $r->package ) ) {
/* translators: 1: plugin name, 2: details URL, 3: accessibility text, 4: version number */
- printf( __( 'There is a new version of %1$s available. View version %4$s details. Automatic update is unavailable for this plugin.' ),
+ printf( __( 'There is a new version of %1$s available. View version %4$s details. Automatic update is unavailable for this plugin.' ),
$plugin_name,
esc_url( $details_url ),
/* translators: 1: plugin name, 2: version number */
@@ -357,7 +357,7 @@ function wp_plugin_update_row( $file, $plugin_data ) {
);
} else {
/* translators: 1: plugin name, 2: details URL, 3: accessibility text, 4: version number, 5: update URL, 6: accessibility text */
- printf( __( 'There is a new version of %1$s available. View version %4$s details or update now.' ),
+ printf( __( 'There is a new version of %1$s available. View version %4$s details or update now.' ),
$plugin_name,
esc_url( $details_url ),
/* translators: 1: plugin name, 2: version number */
@@ -469,7 +469,7 @@ function wp_theme_update_row( $theme_key, $theme ) {
echo '
';
if ( ! current_user_can('update_themes') ) {
/* translators: 1: theme name, 2: details URL, 3: accessibility text, 4: version number */
- printf( __( 'There is a new version of %1$s available. View version %4$s details.'),
+ printf( __( 'There is a new version of %1$s available. View version %4$s details.'),
$theme_name,
esc_url( $details_url ),
/* translators: 1: theme name, 2: version number */
@@ -478,7 +478,7 @@ function wp_theme_update_row( $theme_key, $theme ) {
);
} elseif ( empty( $r['package'] ) ) {
/* translators: 1: theme name, 2: details URL, 3: accessibility text, 4: version number */
- printf( __( 'There is a new version of %1$s available. View version %4$s details. Automatic update is unavailable for this theme.' ),
+ printf( __( 'There is a new version of %1$s available. View version %4$s details. Automatic update is unavailable for this theme.' ),
$theme_name,
esc_url( $details_url ),
/* translators: 1: theme name, 2: version number */
@@ -487,7 +487,7 @@ function wp_theme_update_row( $theme_key, $theme ) {
);
} else {
/* translators: 1: theme name, 2: details URL, 3: accessibility text, 4: version number, 5: update URL, 6: accessibility text */
- printf( __( 'There is a new version of %1$s available. View version %4$s details or update now.' ),
+ printf( __( 'There is a new version of %1$s available. View version %4$s details or update now.' ),
$theme_name,
esc_url( $details_url ),
/* translators: 1: theme name, 2: version number */
diff --git a/src/wp-admin/js/plugin-install.js b/src/wp-admin/js/plugin-install.js
index 6693598e17..dc0f5a58d8 100644
--- a/src/wp-admin/js/plugin-install.js
+++ b/src/wp-admin/js/plugin-install.js
@@ -1,14 +1,23 @@
-/* global plugininstallL10n, tb_click */
+/* global plugininstallL10n, tb_click, tb_remove */
/* Plugin Browser Thickbox related JS*/
var tb_position;
jQuery( document ).ready( function( $ ) {
+
+ var tbWindow,
+ $focusedBefore,
+ $iframeBody,
+ $tabbables,
+ $firstTabbable,
+ $lastTabbable;
+
tb_position = function() {
- var tbWindow = $( '#TB_window' ),
- width = $( window ).width(),
+ var width = $( window ).width(),
H = $( window ).height() - ( ( 792 < width ) ? 60 : 20 ),
W = ( 792 < width ) ? 772 : width - 20;
+ tbWindow = $( '#TB_window' );
+
if ( tbWindow.length ) {
tbWindow.width( W ).height( H );
$( '#TB_iframeContent' ).width( W ).height( H );
@@ -38,16 +47,111 @@ jQuery( document ).ready( function( $ ) {
tb_position();
});
- $( '.plugin-card, .plugins .plugin-version-author-uri' ).on( 'click', 'a.thickbox', function( e ) {
+ /*
+ * Custom events: when a Thickbox iframe has loaded and when the Thickbox
+ * modal gets removed from the DOM.
+ */
+ $( 'body' )
+ .on( 'thickbox:iframe:loaded', tbWindow, function() {
+ iframeLoaded();
+ })
+ .on( 'thickbox:removed', function() {
+ // Set focus back to the element that opened the modal dialog.
+ // Note: IE 8 would need this wrapped in a fake setTimeout `0`.
+ $focusedBefore.focus();
+ });
+
+ function iframeLoaded() {
+ var $iframe = tbWindow.find( '#TB_iframeContent' );
+
+ // Get the iframe body.
+ $iframeBody = $iframe.contents().find( 'body' );
+
+ // Get the tabbable elements and handle the keydown event on first load.
+ handleTabbables();
+
+ // Set initial focus on the "Close" button.
+ $firstTabbable.focus();
+
+ /*
+ * When the "Install" button is disabled (e.g. the Plugin is already installed)
+ * then we can't predict where the last focusable element is. We need to get
+ * the tabbable elements and handle the keydown event again and again,
+ * each time the active tab panel changes.
+ */
+ $( '#plugin-information-tabs a', $iframeBody ).on( 'click', function() {
+ handleTabbables();
+ });
+
+ // Close the modal when pressing Escape.
+ $iframeBody.on( 'keydown', function( event ) {
+ if ( 27 !== event.which ) {
+ return;
+ }
+ tb_remove();
+ });
+ }
+
+ /*
+ * Get the tabbable elements and detach/attach the keydown event.
+ * Called after the iframe has fully loaded so we have all the elements we need.
+ * Called again each time a Tab gets clicked.
+ * @todo Consider to implement a WordPress general utility for this and don't use jQuery UI.
+ */
+ function handleTabbables() {
+ var $firstAndLast;
+ // Get all the tabbable elements.
+ $tabbables = $( ':tabbable', $iframeBody );
+ // Our first tabbable element is always the "Close" button.
+ $firstTabbable = tbWindow.find( '#TB_closeWindowButton' );
+ // Get the last tabbable element.
+ $lastTabbable = $tabbables.last();
+ // Make a jQuery collection.
+ $firstAndLast = $firstTabbable.add( $lastTabbable );
+ // Detach any previously attached keydown event.
+ $firstAndLast.off( 'keydown.wp-plugin-details' );
+ // Attach again the keydown event on the first and last focusable elements.
+ $firstAndLast.on( 'keydown.wp-plugin-details', function( event ) {
+ constrainTabbing( event );
+ });
+ }
+
+ // Constrain tabbing within the plugin modal dialog.
+ function constrainTabbing( event ) {
+ if ( 9 !== event.which ) {
+ return;
+ }
+
+ if ( $lastTabbable[0] === event.target && ! event.shiftKey ) {
+ event.preventDefault();
+ $firstTabbable.focus();
+ } else if ( $firstTabbable[0] === event.target && event.shiftKey ) {
+ event.preventDefault();
+ $lastTabbable.focus();
+ }
+ }
+
+ // Open the Plugin details modal.
+ $( '.thickbox.open-plugin-details-modal' ).on( 'click', function( e ) {
+ // The `data-title` attribute is used only in the Plugin screens.
+ var title = $( this ).data( 'title' ) ? plugininstallL10n.plugin_information + ' ' + $( this ).data( 'title' ) : plugininstallL10n.plugin_modal_label;
+
e.preventDefault();
e.stopPropagation();
+ // Store the element that has focus before opening the modal dialog, i.e. the control which opens it.
+ $focusedBefore = $( this );
+
tb_click.call(this);
- $('#TB_title').css({'background-color':'#23282d','color':'#cfcfcf'});
- $('#TB_ajaxWindowTitle').html( '' + plugininstallL10n.plugin_information + ' ' + $(this).data( 'title' ) );
- $('#TB_iframeContent').attr( 'title', plugininstallL10n.plugin_information + ' ' + $(this).data( 'title' ) );
- $('#TB_closeWindowButton').focus();
+ // Set ARIA role and ARIA label.
+ tbWindow.attr({
+ 'role': 'dialog',
+ 'aria-label': plugininstallL10n.plugin_modal_label
+ });
+
+ // Set title attribute on the iframe.
+ tbWindow.find( '#TB_iframeContent' ).attr( 'title', title );
});
/* Plugin install related JS */
diff --git a/src/wp-admin/update-core.php b/src/wp-admin/update-core.php
index a38b5bbba1..bbed380326 100644
--- a/src/wp-admin/update-core.php
+++ b/src/wp-admin/update-core.php
@@ -284,7 +284,7 @@ function list_plugin_updates() {
$details_name = sprintf( '%1$s', esc_attr( $plugin_data->Name ) );
/* translators: 1: Plugin name 2: Plugin version */
$details_text = sprintf( __( 'View %1$s version %2$s details.' ), $details_name, $plugin_data->update->new_version );
- $details = sprintf( '%2$s', esc_url( $details_url ), $details_text );
+ $details = sprintf( '%2$s', esc_url( $details_url ), $details_text );
$checkbox_id = "checkbox_" . md5( $plugin_data->Name );
?>
diff --git a/src/wp-includes/js/thickbox/thickbox.css b/src/wp-includes/js/thickbox/thickbox.css
index 28c99adf4e..6bfac61ee4 100644
--- a/src/wp-includes/js/thickbox/thickbox.css
+++ b/src/wp-includes/js/thickbox/thickbox.css
@@ -43,12 +43,19 @@
float: right;
}
-#TB_closeAjaxWindow {
- float: right;
-}
-
-#TB_closeAjaxWindow a {
- text-decoration: none;
+#TB_closeWindowButton {
+ position: absolute;
+ left: auto;
+ right: 0;
+ width: 29px;
+ height: 29px;
+ border: 0;
+ padding: 0;
+ background: none;
+ cursor: pointer;
+ outline: none;
+ -webkit-transition: color .1s ease-in-out, background .1s ease-in-out;
+ transition: color .1s ease-in-out, background .1s ease-in-out;
}
#TB_ajaxWindowTitle {
@@ -115,6 +122,7 @@
}
.tb-close-icon {
+ display: block;
color: #666;
text-align: center;
line-height: 29px;
@@ -133,6 +141,7 @@
-moz-osx-font-smoothing: grayscale;
}
-.tb-close-icon:hover {
+#TB_closeWindowButton:hover .tb-close-icon,
+#TB_closeWindowButton:focus .tb-close-icon {
color: #00a0d2;
}
diff --git a/src/wp-includes/js/thickbox/thickbox.js b/src/wp-includes/js/thickbox/thickbox.js
index 581838496b..164f72e616 100644
--- a/src/wp-includes/js/thickbox/thickbox.js
+++ b/src/wp-includes/js/thickbox/thickbox.js
@@ -41,6 +41,8 @@ function tb_click(){
function tb_show(caption, url, imageGroup) {//function called when the user clicks on a thickbox link
+ var $closeBtn;
+
try {
if (typeof document.body.style.maxHeight === "undefined") {//if IE 6
jQuery("body","html").css({height: "100%", width: "100%"});
@@ -137,7 +139,7 @@ function tb_show(caption, url, imageGroup) {//function called when the user clic
TB_WIDTH = imageWidth + 30;
TB_HEIGHT = imageHeight + 60;
- jQuery("#TB_window").append(""+thickboxL10n.close+"" + "
");
jQuery("#TB_closeWindowButton").click(tb_remove);
@@ -202,10 +204,10 @@ function tb_show(caption, url, imageGroup) {//function called when the user clic
urlNoQuery = url.split('TB_');
jQuery("#TB_iframeContent").remove();
if(params['modal'] != "true"){//iframe no modal
- jQuery("#TB_window").append("