| 1 |
/** |
| 2 |
* @file Functionality for the plugin install screens. |
| 3 |
* |
| 4 |
* @output wp-admin/js/plugin-install.js |
| 5 |
*/ |
| 6 |
|
| 7 |
/* global tb_click, tb_remove, tb_position */ |
| 8 |
|
| 9 |
jQuery( function( $ ) { |
| 10 |
|
| 11 |
var tbWindow, |
| 12 |
$iframeBody, |
| 13 |
$tabbables, |
| 14 |
$firstTabbable, |
| 15 |
$lastTabbable, |
| 16 |
$focusedBefore = $(), |
| 17 |
$uploadViewToggle = $( '.upload-view-toggle' ), |
| 18 |
$wrap = $ ( '.wrap' ), |
| 19 |
$body = $( document.body ); |
| 20 |
|
| 21 |
window.tb_position = function() { |
| 22 |
var width = $( window ).width(), |
| 23 |
H = $( window ).height() - ( ( 792 < width ) ? 60 : 20 ), |
| 24 |
W = ( 792 < width ) ? 772 : width - 20; |
| 25 |
|
| 26 |
tbWindow = $( '#TB_window' ); |
| 27 |
|
| 28 |
if ( tbWindow.length ) { |
| 29 |
tbWindow.width( W ).height( H ); |
| 30 |
$( '#TB_iframeContent' ).width( W ).height( H ); |
| 31 |
tbWindow.css({ |
| 32 |
'margin-left': '-' + parseInt( ( W / 2 ), 10 ) + 'px' |
| 33 |
}); |
| 34 |
if ( typeof document.body.style.maxWidth !== 'undefined' ) { |
| 35 |
tbWindow.css({ |
| 36 |
'top': '30px', |
| 37 |
'margin-top': '0' |
| 38 |
}); |
| 39 |
} |
| 40 |
} |
| 41 |
|
| 42 |
return $( 'a.thickbox' ).each( function() { |
| 43 |
var href = $( this ).attr( 'href' ); |
| 44 |
if ( ! href ) { |
| 45 |
return; |
| 46 |
} |
| 47 |
href = href.replace( /&width=[0-9]+/g, '' ); |
| 48 |
href = href.replace( /&height=[0-9]+/g, '' ); |
| 49 |
$(this).attr( 'href', href + '&width=' + W + '&height=' + ( H ) ); |
| 50 |
}); |
| 51 |
}; |
| 52 |
|
| 53 |
$( window ).on( 'resize', function() { |
| 54 |
tb_position(); |
| 55 |
}); |
| 56 |
|
| 57 |
/* |
| 58 |
* Custom events: when a Thickbox iframe has loaded and when the Thickbox |
| 59 |
* modal gets removed from the DOM. |
| 60 |
*/ |
| 61 |
$body |
| 62 |
.on( 'thickbox:iframe:loaded', tbWindow, function() { |
| 63 |
/* |
| 64 |
* Return if it's not the modal with the plugin details iframe. Other |
| 65 |
* thickbox instances might want to load an iframe with content from |
| 66 |
* an external domain. Avoid to access the iframe contents when we're |
| 67 |
* not sure the iframe loads from the same domain. |
| 68 |
*/ |
| 69 |
if ( ! tbWindow.hasClass( 'plugin-details-modal' ) ) { |
| 70 |
return; |
| 71 |
} |
| 72 |
|
| 73 |
iframeLoaded(); |
| 74 |
}) |
| 75 |
.on( 'thickbox:removed', function() { |
| 76 |
// Set focus back to the element that opened the modal dialog. |
| 77 |
// Note: IE 8 would need this wrapped in a fake setTimeout `0`. |
| 78 |
$focusedBefore.trigger( 'focus' ); |
| 79 |
}); |
| 80 |
|
| 81 |
function iframeLoaded() { |
| 82 |
var $iframe = tbWindow.find( '#TB_iframeContent' ); |
| 83 |
|
| 84 |
// Get the iframe body. |
| 85 |
$iframeBody = $iframe.contents().find( 'body' ); |
| 86 |
|
| 87 |
// Get the tabbable elements and handle the keydown event on first load. |
| 88 |
handleTabbables(); |
| 89 |
|
| 90 |
// Set initial focus on the "Close" button. |
| 91 |
$firstTabbable.trigger( 'focus' ); |
| 92 |
|
| 93 |
/* |
| 94 |
* When the "Install" button is disabled (e.g. the Plugin is already installed) |
| 95 |
* then we can't predict where the last focusable element is. We need to get |
| 96 |
* the tabbable elements and handle the keydown event again and again, |
| 97 |
* each time the active tab panel changes. |
| 98 |
*/ |
| 99 |
$( '#plugin-information-tabs a', $iframeBody ).on( 'click', function() { |
| 100 |
handleTabbables(); |
| 101 |
}); |
| 102 |
|
| 103 |
// Close the modal when pressing Escape. |
| 104 |
$iframeBody.on( 'keydown', function( event ) { |
| 105 |
if ( 27 !== event.which ) { |
| 106 |
return; |
| 107 |
} |
| 108 |
tb_remove(); |
| 109 |
}); |
| 110 |
} |
| 111 |
|
| 112 |
/* |
| 113 |
* Get the tabbable elements and detach/attach the keydown event. |
| 114 |
* Called after the iframe has fully loaded so we have all the elements we need. |
| 115 |
* Called again each time a Tab gets clicked. |
| 116 |
* @todo Consider to implement a WordPress general utility for this and don't use jQuery UI. |
| 117 |
*/ |
| 118 |
function handleTabbables() { |
| 119 |
var $firstAndLast; |
| 120 |
// Get all the tabbable elements. |
| 121 |
$tabbables = $( ':tabbable', $iframeBody ); |
| 122 |
// Our first tabbable element is always the "Close" button. |
| 123 |
$firstTabbable = tbWindow.find( '#TB_closeWindowButton' ); |
| 124 |
// Get the last tabbable element. |
| 125 |
$lastTabbable = $tabbables.last(); |
| 126 |
// Make a jQuery collection. |
| 127 |
$firstAndLast = $firstTabbable.add( $lastTabbable ); |
| 128 |
// Detach any previously attached keydown event. |
| 129 |
$firstAndLast.off( 'keydown.wp-plugin-details' ); |
| 130 |
// Attach again the keydown event on the first and last focusable elements. |
| 131 |
$firstAndLast.on( 'keydown.wp-plugin-details', function( event ) { |
| 132 |
constrainTabbing( event ); |
| 133 |
}); |
| 134 |
} |
| 135 |
|
| 136 |
// Constrain tabbing within the plugin modal dialog. |
| 137 |
function constrainTabbing( event ) { |
| 138 |
if ( 9 !== event.which ) { |
| 139 |
return; |
| 140 |
} |
| 141 |
|
| 142 |
if ( $lastTabbable[0] === event.target && ! event.shiftKey ) { |
| 143 |
event.preventDefault(); |
| 144 |
$firstTabbable.trigger( 'focus' ); |
| 145 |
} else if ( $firstTabbable[0] === event.target && event.shiftKey ) { |
| 146 |
event.preventDefault(); |
| 147 |
$lastTabbable.trigger( 'focus' ); |
| 148 |
} |
| 149 |
} |
| 150 |
|
| 151 |
/* |
| 152 |
* Open the Plugin details modal. The event is delegated to get also the links |
| 153 |
* in the plugins search tab, after the Ajax search rebuilds the HTML. It's |
| 154 |
* delegated on the closest ancestor and not on the body to avoid conflicts |
| 155 |
* with other handlers, see Trac ticket #43082. |
| 156 |
*/ |
| 157 |
$( '.yaymail-layout.yaymail-layout-has-sider' ).on( 'click', '.thickbox.open-plugin-details-modal', function( e ) { |
| 158 |
// The `data-title` attribute is used only in the Plugin screens. |
| 159 |
var title = $( this ).data( 'title' ) ? |
| 160 |
wp.i18n.sprintf( |
| 161 |
// translators: %s: Plugin name. |
| 162 |
wp.i18n.__( 'Plugin: %s' ), |
| 163 |
$( this ).data( 'title' ) |
| 164 |
) : |
| 165 |
wp.i18n.__( 'Plugin details' ); |
| 166 |
|
| 167 |
e.preventDefault(); |
| 168 |
e.stopPropagation(); |
| 169 |
|
| 170 |
// Store the element that has focus before opening the modal dialog, i.e. the control which opens it. |
| 171 |
$focusedBefore = $( this ); |
| 172 |
|
| 173 |
tb_click.call(this); |
| 174 |
|
| 175 |
// Set ARIA role, ARIA label, and add a CSS class. |
| 176 |
tbWindow |
| 177 |
.attr({ |
| 178 |
'role': 'dialog', |
| 179 |
'aria-label': wp.i18n.__( 'Plugin details' ) |
| 180 |
}) |
| 181 |
.addClass( 'plugin-details-modal' ); |
| 182 |
|
| 183 |
// Set title attribute on the iframe. |
| 184 |
tbWindow.find( '#TB_iframeContent' ).attr( 'title', title ); |
| 185 |
}); |
| 186 |
|
| 187 |
/* Plugin install related JS */ |
| 188 |
$( '#plugin-information-tabs a' ).on( 'click', function( event ) { |
| 189 |
var tab = $( this ).attr( 'name' ); |
| 190 |
event.preventDefault(); |
| 191 |
|
| 192 |
// Flip the tab. |
| 193 |
$( '#plugin-information-tabs a.current' ).removeClass( 'current' ); |
| 194 |
$( this ).addClass( 'current' ); |
| 195 |
|
| 196 |
// Only show the fyi box in the description section, on smaller screen, |
| 197 |
// where it's otherwise always displayed at the top. |
| 198 |
if ( 'description' !== tab && $( window ).width() < 772 ) { |
| 199 |
$( '#plugin-information-content' ).find( '.fyi' ).hide(); |
| 200 |
} else { |
| 201 |
$( '#plugin-information-content' ).find( '.fyi' ).show(); |
| 202 |
} |
| 203 |
|
| 204 |
// Flip the content. |
| 205 |
$( '#section-holder div.section' ).hide(); // Hide 'em all. |
| 206 |
$( '#section-' + tab ).show(); |
| 207 |
}); |
| 208 |
|
| 209 |
/* |
| 210 |
* When a user presses the "Upload Plugin" button, show the upload form in place |
| 211 |
* rather than sending them to the devoted upload plugin page. |
| 212 |
* The `?tab=upload` page still exists for no-js support and for plugins that |
| 213 |
* might access it directly. When we're in this page, let the link behave |
| 214 |
* like a link. Otherwise we're in the normal plugin installer pages and the |
| 215 |
* link should behave like a toggle button. |
| 216 |
*/ |
| 217 |
if ( ! $wrap.hasClass( 'plugin-install-tab-upload' ) ) { |
| 218 |
$uploadViewToggle |
| 219 |
.attr({ |
| 220 |
role: 'button', |
| 221 |
'aria-expanded': 'false' |
| 222 |
}) |
| 223 |
.on( 'click', function( event ) { |
| 224 |
event.preventDefault(); |
| 225 |
$body.toggleClass( 'show-upload-view' ); |
| 226 |
$uploadViewToggle.attr( 'aria-expanded', $body.hasClass( 'show-upload-view' ) ); |
| 227 |
}); |
| 228 |
} |
| 229 |
}); |
| 230 |
|