PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.2
Jetpack – WP Security, Backup, Speed, & Growth v16.2
16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 All 503 releases
← All changes | modules/likes/queuehandler.js +119 -112 12.9.516.2 View file →
@@ -23,9 +23,9 @@
23 23 function JetpackLikesPostMessage( message, target ) {
24 24 if ( typeof message === 'string' ) {
25 25 try {
26 26 message = JSON.parse( message );
27 - } catch ( e ) {
27 + } catch {
28 28 return;
29 29 }
30 30 }
31 31
@@ -37,10 +37,10 @@
37 37 data: message,
38 38 } ),
39 39 '*'
40 40 );
41 - } catch ( e ) {
42 - return;
41 + } catch {
42 + // Ignore error
43 43 }
44 44 }
45 45 }
46 46
@@ -93,9 +93,9 @@
93 93 let message = event && event.data;
94 94 if ( typeof message === 'string' ) {
95 95 try {
96 96 message = JSON.parse( message );
97 - } catch ( err ) {
97 + } catch {
98 98 return;
99 99 }
100 100 }
101 101
@@ -124,23 +124,8 @@
124 124 const sdLinkColor = document.querySelector( '.sd-link-color' );
125 125 const sdTextColorStyles = ( sdTextColor && getComputedStyle( sdTextColor ) ) || {};
126 126 const sdLinkColorStyles = ( sdLinkColor && getComputedStyle( sdLinkColor ) ) || {};
127 127
128 - if ( document.querySelectorAll( 'iframe.admin-bar-likes-widget' ).length > 0 ) {
129 - JetpackLikesPostMessage( { event: 'adminBarEnabled' }, window.frames[ 'likes-master' ] );
130 -
131 - const bgSource = document.querySelector(
132 - '#wpadminbar .quicklinks li#wp-admin-bar-wpl-like > a'
133 - );
134 -
135 - const wpAdminBar = document.querySelector( '#wpadminbar' );
136 -
137 - stylesData.adminBarStyles = {
138 - background: bgSource && getComputedStyle( bgSource ).background,
139 - isRtl: wpAdminBar && getComputedStyle( wpAdminBar ).direction === 'rtl',
140 - };
141 - }
142 -
143 128 // enable reblogs if we're on a single post page
144 129 if ( document.body.classList.contains( 'single' ) ) {
145 130 JetpackLikesPostMessage( { event: 'reblogsEnabled' }, window.frames[ 'likes-master' ] );
146 131 }
@@ -145,12 +130,12 @@
145 130 JetpackLikesPostMessage( { event: 'reblogsEnabled' }, window.frames[ 'likes-master' ] );
146 131 }
147 132
148 133 stylesData.textStyles = {
149 - color: sdTextColorStyles[ 'color' ],
134 + color: sdTextColorStyles.color,
150 135 fontFamily: sdTextColorStyles[ 'font-family' ],
151 136 fontSize: sdTextColorStyles[ 'font-size' ],
152 - direction: sdTextColorStyles[ 'direction' ],
137 + direction: sdTextColorStyles.direction,
153 138 fontWeight: sdTextColorStyles[ 'font-weight' ],
154 139 fontStyle: sdTextColorStyles[ 'font-style' ],
155 140 textDecoration: sdTextColorStyles[ 'text-decoration' ],
156 141 };
@@ -155,9 +140,9 @@
155 140 textDecoration: sdTextColorStyles[ 'text-decoration' ],
156 141 };
157 142
158 143 stylesData.linkStyles = {
159 - color: sdLinkColorStyles[ 'color' ],
144 + color: sdLinkColorStyles.color,
160 145 fontFamily: sdLinkColorStyles[ 'font-family' ],
161 146 fontSize: sdLinkColorStyles[ 'font-size' ],
162 147 textDecoration: sdLinkColorStyles[ 'text-decoration' ],
163 148 fontWeight: sdLinkColorStyles[ 'font-weight' ],
@@ -170,23 +155,17 @@
170 155 } );
171 156
172 157 break;
173 158
174 - case 'showLikeWidget': {
175 - const placeholder = document.querySelector( `#${ data.id } .likes-widget-placeholder` );
176 - if ( placeholder ) {
177 - placeholder.style.display = 'none';
178 - }
159 + // We're keeping this for planned future follow ups.
160 + // @see: https://github.com/Automattic/jetpack/pull/42361#discussion_r1995338815
161 + case 'showLikeWidget':
179 162 break;
180 - }
181 163
182 - case 'showCommentLikeWidget': {
183 - const placeholder = document.querySelector( `#${ data.id } .likes-widget-placeholder` );
184 - if ( placeholder ) {
185 - placeholder.style.display = 'none';
186 - }
164 + // We're keeping this for planned future follow ups.
165 + // @see: https://github.com/Automattic/jetpack/pull/42361#discussion_r1995338815
166 + case 'showCommentLikeWidget':
187 167 break;
188 - }
189 168
190 169 case 'killCommentLikes':
191 170 // If kill switch for comment likes is enabled remove all widgets wrappers and `Loading...` placeholders.
192 171 document
@@ -199,32 +178,30 @@
199 178 wpcom_reblog.toggle_reblog_box_flair( data.obj_id );
200 179 }
201 180 break;
202 181
182 + case 'hideOtherGravatars': {
183 + hideLikersPopover();
184 + break;
185 + }
186 +
203 187 case 'showOtherGravatars': {
204 188 const container = document.querySelector( '#likes-other-gravatars' );
189 +
205 190 if ( ! container ) {
206 191 break;
207 192 }
208 193
209 - const newLayout = container.classList.contains( 'wpl-new-layout' );
210 -
211 194 const list = container.querySelector( 'ul' );
212 195
213 196 container.style.display = 'none';
214 197 list.innerHTML = '';
215 198
216 - if ( newLayout ) {
217 - container
218 - .querySelectorAll( '.likes-text span' )
219 - .forEach( item => ( item.textContent = data.totalLikesLabel ) );
220 - } else {
221 - container
222 - .querySelectorAll( '.likes-text span' )
223 - .forEach( item => ( item.textContent = data.total ) );
224 - }
199 + container
200 + .querySelectorAll( '.likes-text span' )
201 + .forEach( item => ( item.textContent = data.totalLikesLabel ) );
225 202
226 - ( data.likers || [] ).forEach( liker => {
203 + ( data.likers || [] ).forEach( async ( liker, index ) => {
227 204 if ( liker.profile_URL.substr( 0, 4 ) !== 'http' ) {
228 205 // We only display gravatars with http or https schema
229 206 return;
230 207 }
@@ -229,88 +206,107 @@
229 206 return;
230 207 }
231 208
232 209 const element = document.createElement( 'li' );
233 - if ( newLayout ) {
234 - element.innerHTML = `
235 - <a href="${ encodeURI( liker.profile_URL ) }" rel="nofollow" target="_parent" class="wpl-liker">
236 - <img src="${ encodeURI( liker.avatar_URL ) }"
237 - alt=""
238 - style="width: 28px; height: 28px;" />
239 - <span></span>
240 - </a>
241 - `;
242 - } else {
243 - element.innerHTML = `
244 - <a href="${ encodeURI( liker.profile_URL ) }" rel="nofollow" target="_parent" class="wpl-liker">
245 - <img src="${ encodeURI( liker.avatar_URL ) }"
246 - alt=""
247 - style="width: 30px; height: 30px; padding-right: 3px;" />
248 - </a>
249 - `;
250 - }
210 + list.append( element );
251 211
252 - list.append( element );
212 + const profileLink = encodeURI( liker.profile_URL );
213 + const avatarLink = encodeURI( liker.avatar_URL );
214 + element.innerHTML = `<a href="${ profileLink }" rel="nofollow" target="_parent" class="wpl-liker">
215 + <img src="${ avatarLink }"
216 + alt=""
217 + style="width: 28px; height: 28px;" />
218 + <span></span>
219 + </a>`;
253 220
254 221 // Add some extra attributes through native methods, to ensure strings are sanitized.
255 222 element.classList.add( liker.css_class );
256 - element.querySelector( 'img' ).alt = liker.name;
257 - if ( newLayout ) {
258 - element.querySelector( 'span' ).innerText = liker.name;
223 + element.querySelector( 'img' ).alt = data.avatarAltTitle.replace( '%s', liker.name );
224 + element.querySelector( 'span' ).innerText = liker.name;
225 +
226 + if ( index === data.likers.length - 1 ) {
227 + element.addEventListener( 'keydown', e => {
228 + if ( e.key === 'Tab' && ! e.shiftKey ) {
229 + e.preventDefault();
230 + hideLikersPopover();
231 +
232 + JetpackLikesPostMessage(
233 + { event: 'focusLikesCount', parent: data.parent },
234 + window.frames[ 'likes-master' ]
235 + );
236 + }
237 + } );
259 238 }
260 239 } );
261 240
262 - const containerStyle = getComputedStyle( container );
263 - const isRtl = containerStyle.direction === 'rtl';
241 + const positionPopup = function () {
242 + const containerStyle = getComputedStyle( container );
243 + const isRtl = containerStyle.direction === 'rtl';
264 244
265 - const el = document.querySelector( `*[name='${ data.parent }']` );
266 - const rect = el.getBoundingClientRect();
267 - const win = el.ownerDocument.defaultView;
268 - const offset = {
269 - top: rect.top + win.pageYOffset,
270 - left: rect.left + win.pageXOffset,
271 - };
245 + const el = document.querySelector( `*[name='${ data.parent }']` );
246 + const rect = el.getBoundingClientRect();
247 + const win = el.ownerDocument.defaultView;
248 + const offset = {
249 + top: rect.top + win.pageYOffset,
250 + left: rect.left + win.pageXOffset,
251 + };
272 252
273 - if ( newLayout ) {
253 + let containerLeft;
274 254 container.style.top = offset.top + data.position.top - 1 + 'px';
275 255
276 256 if ( isRtl ) {
277 257 const visibleAvatarsCount = data && data.likers ? Math.min( data.likers.length, 5 ) : 0;
278 258 // 24px is the width of the avatar + 4px is the padding between avatars
279 - container.style.left =
280 - offset.left + data.position.left + 24 * visibleAvatarsCount + 4 + 'px';
259 + containerLeft = offset.left + data.position.left + 24 * visibleAvatarsCount + 4;
281 260 container.style.transform = 'translateX(-100%)';
282 261 } else {
283 - container.style.left = offset.left + data.position.left + 'px';
262 + containerLeft = offset.left + data.position.left;
284 263 }
285 - } else {
286 - container.style.left = offset.left + data.position.left - 10 + 'px';
287 - container.style.top = offset.top + data.position.top - 33 + 'px';
288 - }
264 + container.style.left = containerLeft + 'px';
289 265
290 - const rowLength = Math.floor( data.width / 37 );
291 - let height = Math.ceil( data.likers.length / rowLength ) * 37 + 13;
292 - if ( height > 204 ) {
293 - height = 204;
294 - }
266 + // If the popup overflows viewport width, we should show it on the next line.
267 + // Push it offscreen to calculated rendered width.
268 + container.style.left = '-9999px';
269 + container.style.display = 'block';
295 270
296 - if ( ! newLayout ) {
297 - // Avatars + padding
298 - const containerWidth = rowLength * 37 - 7;
299 - container.style.height = height + 'px';
300 - container.style.width = containerWidth + 'px';
271 + // If the popup exceeds the viewport width,
272 + // flip the position of the popup.
273 + const containerWidth = container.offsetWidth;
274 + const containerRight = containerLeft + containerWidth;
275 + if ( containerRight > win.innerWidth ) {
276 + containerLeft = rect.right - containerWidth;
277 + }
301 278
302 - const listWidth = rowLength * 37;
303 - list.style.width = listWidth + 'px';
279 + // Set the container left
280 + container.style.left = containerLeft + 'px';
281 + container.setAttribute( 'aria-hidden', 'false' );
282 + };
304 283
305 - const scrollbarWidth = list.offsetWidth - list.clientWidth;
306 - if ( scrollbarWidth > 0 ) {
307 - container.style.width = containerWidth + scrollbarWidth + 'px';
308 - list.style.width = listWidth + scrollbarWidth + 'px';
309 - }
310 - }
284 + positionPopup();
285 + container.focus();
311 286
312 - container.style.display = 'block';
287 + const debounce = function ( func, wait ) {
288 + var timeout;
289 + return function () {
290 + var context = this;
291 + var args = arguments;
292 + clearTimeout( timeout );
293 + timeout = setTimeout( function () {
294 + func.apply( context, args );
295 + }, wait );
296 + };
297 + };
298 +
299 + const debouncedPositionPopup = debounce( positionPopup, 100 );
300 +
301 + // Keep a reference of this function in the element itself
302 + // so that we can destroy it later
303 + container.__resizeHandler = debouncedPositionPopup;
304 +
305 + // When window is resized, resize the popup.
306 + window.addEventListener( 'resize', debouncedPositionPopup );
307 +
308 + container.focus();
313 309 }
314 310 }
315 311 }
316 312
@@ -315,20 +311,36 @@
315 311 }
316 312
317 313 window.addEventListener( 'message', JetpackLikesMessageListener );
318 314
319 -document.addEventListener( 'click', e => {
315 +function hideLikersPopover() {
320 316 const container = document.querySelector( '#likes-other-gravatars' );
321 317
322 - if ( container && ! container.contains( e.target ) ) {
318 + if ( container ) {
323 319 container.style.display = 'none';
320 + container.setAttribute( 'aria-hidden', 'true' );
321 +
322 + // Remove the resize event listener and cleanup.
323 + const resizeHandler = container.__resizeHandler;
324 + if ( resizeHandler ) {
325 + window.removeEventListener( 'resize', resizeHandler );
326 + delete container.__resizeHandler;
327 + }
324 328 }
325 -} );
329 +}
326 330
331 +document.addEventListener( 'click', hideLikersPopover );
332 +
327 333 function JetpackLikesWidgetQueueHandler() {
328 334 var wrapperID;
329 335
330 336 if ( ! jetpackLikesMasterReady ) {
337 + // The master iframe emits `masterReady` a single time, when it finishes loading. On
338 + // script-heavy pages it can finish loading — and emit — before this script attaches its
339 + // message listener above, so that one event is missed and the queue never starts. Ping the
340 + // master iframe while we wait so it re-emits `masterReady` once both it and our listener
341 + // are ready.
342 + JetpackLikesPostMessage( { event: 'queryMasterReady' }, window.frames[ 'likes-master' ] );
331 343 setTimeout( JetpackLikesWidgetQueueHandler, 500 );
332 344 return;
333 345 }
334 346
@@ -438,13 +450,8 @@
438 450 // Restore parent class to 'unloaded' so this widget can be picked up by queue manager again if needed.
439 451 widgetWrapper.classList.remove( 'jetpack-likes-widget-loaded' );
440 452 widgetWrapper.classList.remove( 'jetpack-likes-widget-loading' );
441 453 widgetWrapper.classList.add( 'jetpack-likes-widget-unloaded' );
442 -
443 - // Bring back the loading placeholder into view.
444 - widgetWrapper
445 - .querySelectorAll( '.comment-likes-widget-placeholder' )
446 - .forEach( item => ( item.style.display = 'block' ) );
447 454
448 455 // Remove it from the list of loaded widgets.
449 456 jetpackCommentLikesLoadedWidgets.splice( i, 1 );
450 457