PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 13.5.2
Jetpack – WP Security, Backup, Speed, & Growth v13.5.2
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 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / modules / likes / queuehandler.js
jetpack / modules / likes Last commit date
jetpack-likes-master-iframe.php 2 years ago jetpack-likes-settings.php 2 years ago post-count-jetpack.js 4 years ago post-count.js 4 years ago queuehandler.js 2 years ago style.css 2 years ago
queuehandler.js
533 lines
1 /* global wpcom_reblog */
2
3 var jetpackLikesWidgetBatch = [];
4 var jetpackLikesMasterReady = false;
5
6 // Due to performance problems on pages with a large number of widget iframes that need to be loaded,
7 // we are limiting the processing at any instant to unloaded widgets that are currently in viewport,
8 // plus this constant that will allow processing of widgets above and bellow the current fold.
9 // This aim of it is to improve the UX and hide the transition from unloaded to loaded state from users.
10 var jetpackLikesLookAhead = 2000; // pixels
11
12 // Keeps track of loaded comment likes widget so we can unload them when they are scrolled out of view.
13 var jetpackCommentLikesLoadedWidgets = [];
14
15 var jetpackLikesDocReadyPromise = new Promise( resolve => {
16 if ( document.readyState !== 'loading' ) {
17 resolve();
18 } else {
19 window.addEventListener( 'DOMContentLoaded', () => resolve() );
20 }
21 } );
22
23 function JetpackLikesPostMessage( message, target ) {
24 if ( typeof message === 'string' ) {
25 try {
26 message = JSON.parse( message );
27 } catch ( e ) {
28 return;
29 }
30 }
31
32 if ( target && typeof target.postMessage === 'function' ) {
33 try {
34 target.postMessage(
35 JSON.stringify( {
36 type: 'likesMessage',
37 data: message,
38 } ),
39 '*'
40 );
41 } catch ( e ) {
42 return;
43 }
44 }
45 }
46
47 function JetpackLikesBatchHandler() {
48 const requests = [];
49 document.querySelectorAll( 'div.jetpack-likes-widget-unloaded' ).forEach( widget => {
50 if ( jetpackLikesWidgetBatch.indexOf( widget.id ) > -1 ) {
51 return;
52 }
53
54 if ( ! jetpackIsScrolledIntoView( widget ) ) {
55 return;
56 }
57
58 jetpackLikesWidgetBatch.push( widget.id );
59
60 var regex = /like-(post|comment)-wrapper-(\d+)-(\d+)-(\w+)/,
61 match = regex.exec( widget.id ),
62 info;
63
64 if ( ! match || match.length !== 5 ) {
65 return;
66 }
67
68 info = {
69 blog_id: match[ 2 ],
70 width: widget.width,
71 };
72
73 if ( 'post' === match[ 1 ] ) {
74 info.post_id = match[ 3 ];
75 } else if ( 'comment' === match[ 1 ] ) {
76 info.comment_id = match[ 3 ];
77 }
78
79 info.obj_id = match[ 4 ];
80
81 requests.push( info );
82 } );
83
84 if ( requests.length > 0 ) {
85 JetpackLikesPostMessage(
86 { event: 'initialBatch', requests: requests },
87 window.frames[ 'likes-master' ]
88 );
89 }
90 }
91
92 function JetpackLikesMessageListener( event ) {
93 let message = event && event.data;
94 if ( typeof message === 'string' ) {
95 try {
96 message = JSON.parse( message );
97 } catch ( err ) {
98 return;
99 }
100 }
101
102 const type = message && message.type;
103 const data = message && message.data;
104
105 if ( type !== 'likesMessage' || typeof data.event === 'undefined' ) {
106 return;
107 }
108
109 // We only allow messages from one origin
110 const allowedOrigin = 'https://widgets.wp.com';
111 if ( allowedOrigin !== event.origin ) {
112 return;
113 }
114
115 switch ( data.event ) {
116 case 'masterReady':
117 jetpackLikesDocReadyPromise.then( () => {
118 jetpackLikesMasterReady = true;
119
120 const stylesData = {
121 event: 'injectStyles',
122 };
123 const sdTextColor = document.querySelector( '.sd-text-color' );
124 const sdLinkColor = document.querySelector( '.sd-link-color' );
125 const sdTextColorStyles = ( sdTextColor && getComputedStyle( sdTextColor ) ) || {};
126 const sdLinkColorStyles = ( sdLinkColor && getComputedStyle( sdLinkColor ) ) || {};
127
128 // enable reblogs if we're on a single post page
129 if ( document.body.classList.contains( 'single' ) ) {
130 JetpackLikesPostMessage( { event: 'reblogsEnabled' }, window.frames[ 'likes-master' ] );
131 }
132
133 stylesData.textStyles = {
134 color: sdTextColorStyles[ 'color' ],
135 fontFamily: sdTextColorStyles[ 'font-family' ],
136 fontSize: sdTextColorStyles[ 'font-size' ],
137 direction: sdTextColorStyles[ 'direction' ],
138 fontWeight: sdTextColorStyles[ 'font-weight' ],
139 fontStyle: sdTextColorStyles[ 'font-style' ],
140 textDecoration: sdTextColorStyles[ 'text-decoration' ],
141 };
142
143 stylesData.linkStyles = {
144 color: sdLinkColorStyles[ 'color' ],
145 fontFamily: sdLinkColorStyles[ 'font-family' ],
146 fontSize: sdLinkColorStyles[ 'font-size' ],
147 textDecoration: sdLinkColorStyles[ 'text-decoration' ],
148 fontWeight: sdLinkColorStyles[ 'font-weight' ],
149 fontStyle: sdLinkColorStyles[ 'font-style' ],
150 };
151
152 JetpackLikesPostMessage( stylesData, window.frames[ 'likes-master' ] );
153
154 JetpackLikesBatchHandler();
155 } );
156
157 break;
158
159 case 'showLikeWidget': {
160 const placeholder = document.querySelector( `#${ data.id } .likes-widget-placeholder` );
161 if ( placeholder ) {
162 placeholder.style.display = 'none';
163 }
164 break;
165 }
166
167 case 'showCommentLikeWidget': {
168 const placeholder = document.querySelector( `#${ data.id } .likes-widget-placeholder` );
169 if ( placeholder ) {
170 placeholder.style.display = 'none';
171 }
172 break;
173 }
174
175 case 'killCommentLikes':
176 // If kill switch for comment likes is enabled remove all widgets wrappers and `Loading...` placeholders.
177 document
178 .querySelectorAll( '.jetpack-comment-likes-widget-wrapper' )
179 .forEach( wrapper => wrapper.remove() );
180 break;
181
182 case 'clickReblogFlair':
183 if ( wpcom_reblog && typeof wpcom_reblog.toggle_reblog_box_flair === 'function' ) {
184 wpcom_reblog.toggle_reblog_box_flair( data.obj_id );
185 }
186 break;
187
188 case 'hideOtherGravatars': {
189 hideLikersPopover();
190 break;
191 }
192
193 case 'showOtherGravatars': {
194 const container = document.querySelector( '#likes-other-gravatars' );
195
196 if ( ! container ) {
197 break;
198 }
199
200 const newLayout = container.classList.contains( 'wpl-new-layout' );
201
202 const list = container.querySelector( 'ul' );
203
204 container.style.display = 'none';
205 list.innerHTML = '';
206
207 if ( newLayout ) {
208 container
209 .querySelectorAll( '.likes-text span' )
210 .forEach( item => ( item.textContent = data.totalLikesLabel ) );
211 } else {
212 container
213 .querySelectorAll( '.likes-text span' )
214 .forEach( item => ( item.textContent = data.total ) );
215 }
216
217 ( data.likers || [] ).forEach( async ( liker, index ) => {
218 if ( liker.profile_URL.substr( 0, 4 ) !== 'http' ) {
219 // We only display gravatars with http or https schema
220 return;
221 }
222
223 const element = document.createElement( 'li' );
224 list.append( element );
225
226 if ( newLayout ) {
227 element.innerHTML = `
228 <a href="${ encodeURI( liker.profile_URL ) }" rel="nofollow" target="_parent" class="wpl-liker">
229 <img src="${ encodeURI( liker.avatar_URL ) }"
230 alt=""
231 style="width: 28px; height: 28px;" />
232 <span></span>
233 </a>
234 `;
235 } else {
236 element.innerHTML = `
237 <a href="${ encodeURI( liker.profile_URL ) }" rel="nofollow" target="_parent" class="wpl-liker">
238 <img src="${ encodeURI( liker.avatar_URL ) }"
239 alt=""
240 style="width: 30px; height: 30px; padding-right: 3px;" />
241 </a>
242 `;
243 }
244
245 // Add some extra attributes through native methods, to ensure strings are sanitized.
246 element.classList.add( liker.css_class );
247 element.querySelector( 'img' ).alt = data.avatarAltTitle.replace( '%s', liker.name );
248
249 if ( newLayout ) {
250 element.querySelector( 'span' ).innerText = liker.name;
251 }
252
253 if ( index === data.likers.length - 1 ) {
254 element.addEventListener( 'keydown', e => {
255 if ( e.key === 'Tab' && ! e.shiftKey ) {
256 e.preventDefault();
257 hideLikersPopover();
258
259 JetpackLikesPostMessage(
260 { event: 'focusLikesCount', parent: data.parent },
261 window.frames[ 'likes-master' ]
262 );
263 }
264 } );
265 }
266 } );
267
268 const positionPopup = function () {
269 const containerStyle = getComputedStyle( container );
270 const isRtl = containerStyle.direction === 'rtl';
271
272 const el = document.querySelector( `*[name='${ data.parent }']` );
273 const rect = el.getBoundingClientRect();
274 const win = el.ownerDocument.defaultView;
275 const offset = {
276 top: rect.top + win.pageYOffset,
277 left: rect.left + win.pageXOffset,
278 };
279
280 let containerLeft = 0;
281 if ( newLayout ) {
282 container.style.top = offset.top + data.position.top - 1 + 'px';
283
284 if ( isRtl ) {
285 const visibleAvatarsCount = data && data.likers ? Math.min( data.likers.length, 5 ) : 0;
286 // 24px is the width of the avatar + 4px is the padding between avatars
287 containerLeft = offset.left + data.position.left + 24 * visibleAvatarsCount + 4;
288 container.style.transform = 'translateX(-100%)';
289 } else {
290 containerLeft = offset.left + data.position.left;
291 }
292 container.style.left = containerLeft + 'px';
293 } else {
294 container.style.left = offset.left + data.position.left - 10 + 'px';
295 container.style.top = offset.top + data.position.top - 33 + 'px';
296 }
297
298 // Container width - padding
299 const initContainerWidth = data.width - 20;
300 const rowLength = Math.floor( initContainerWidth / 37 );
301 // # of rows + (avatar + avatar padding) + text above + container padding
302 let height = Math.ceil( data.likers.length / rowLength ) * 37 + 17 + 22;
303 if ( height > 204 ) {
304 height = 204;
305 }
306
307 if ( ! newLayout ) {
308 // Avatars + padding
309 const containerWidth = rowLength * 37 + 13;
310 container.style.height = height + 'px';
311 container.style.width = containerWidth + 'px';
312
313 const listWidth = rowLength * 37;
314 list.style.width = listWidth + 'px';
315 }
316
317 // If the popup is overflows viewport width, we should show it on the next line
318 if ( newLayout ) {
319 // Push it offscreen to calculated rendered width
320 container.style.left = '-9999px';
321 container.style.display = 'block';
322
323 // If the popup exceeds the viewport width,
324 // flip the position of the popup.
325 const containerWidth = container.offsetWidth;
326 const containerRight = containerLeft + containerWidth;
327 if ( containerRight > win.innerWidth ) {
328 containerLeft = rect.right - containerWidth;
329 }
330
331 // Set the container left
332 container.style.left = containerLeft + 'px';
333 } else {
334 container.style.display = 'block';
335 }
336 container.setAttribute( 'aria-hidden', 'false' );
337 };
338
339 positionPopup();
340 container.focus();
341
342 const debounce = function ( func, wait ) {
343 var timeout;
344 return function () {
345 var context = this;
346 var args = arguments;
347 clearTimeout( timeout );
348 timeout = setTimeout( function () {
349 func.apply( context, args );
350 }, wait );
351 };
352 };
353
354 const debouncedPositionPopup = debounce( positionPopup, 100 );
355
356 // Keep a reference of this function in the element itself
357 // so that we can destroy it later
358 container.__resizeHandler = debouncedPositionPopup;
359
360 // When window is resized, resize the popup.
361 window.addEventListener( 'resize', debouncedPositionPopup );
362
363 container.focus();
364 }
365 }
366 }
367
368 window.addEventListener( 'message', JetpackLikesMessageListener );
369
370 function hideLikersPopover() {
371 const container = document.querySelector( '#likes-other-gravatars' );
372
373 if ( container ) {
374 container.style.display = 'none';
375 container.setAttribute( 'aria-hidden', 'true' );
376
377 // Remove the resize event listener and cleanup.
378 const resizeHandler = container.__resizeHandler;
379 if ( resizeHandler ) {
380 window.removeEventListener( 'resize', resizeHandler );
381 delete container.__resizeHandler;
382 }
383 }
384 }
385
386 document.addEventListener( 'click', hideLikersPopover );
387
388 function JetpackLikesWidgetQueueHandler() {
389 var wrapperID;
390
391 if ( ! jetpackLikesMasterReady ) {
392 setTimeout( JetpackLikesWidgetQueueHandler, 500 );
393 return;
394 }
395
396 // Restore widgets to initial unloaded state when they are scrolled out of view.
397 jetpackUnloadScrolledOutWidgets();
398
399 var unloadedWidgetsInView = jetpackGetUnloadedWidgetsInView();
400
401 if ( unloadedWidgetsInView.length > 0 ) {
402 // Grab any unloaded widgets for a batch request
403 JetpackLikesBatchHandler();
404 }
405
406 for ( var i = 0, length = unloadedWidgetsInView.length; i <= length - 1; i++ ) {
407 wrapperID = unloadedWidgetsInView[ i ].id;
408
409 if ( ! wrapperID ) {
410 continue;
411 }
412
413 jetpackLoadLikeWidgetIframe( wrapperID );
414 }
415 }
416
417 function jetpackLoadLikeWidgetIframe( wrapperID ) {
418 if ( typeof wrapperID === 'undefined' ) {
419 return;
420 }
421
422 const wrapper = document.querySelector( '#' + wrapperID );
423 wrapper.querySelectorAll( 'iframe' ).forEach( iFrame => iFrame.remove() );
424
425 const placeholder = wrapper.querySelector( '.likes-widget-placeholder' );
426
427 // Post like iframe
428 if ( placeholder && placeholder.classList.contains( 'post-likes-widget-placeholder' ) ) {
429 const postLikesFrame = document.createElement( 'iframe' );
430
431 postLikesFrame.classList.add( 'post-likes-widget', 'jetpack-likes-widget' );
432 postLikesFrame.name = wrapper.dataset.name;
433 postLikesFrame.src = wrapper.dataset.src;
434 postLikesFrame.height = '55px';
435 postLikesFrame.width = '100%';
436 postLikesFrame.frameBorder = '0';
437 postLikesFrame.scrolling = 'no';
438 postLikesFrame.title = wrapper.dataset.title;
439
440 placeholder.after( postLikesFrame );
441 }
442
443 // Comment like iframe
444 if ( placeholder.classList.contains( 'comment-likes-widget-placeholder' ) ) {
445 const commentLikesFrame = document.createElement( 'iframe' );
446
447 commentLikesFrame.class = 'comment-likes-widget-frame jetpack-likes-widget-frame';
448 commentLikesFrame.name = wrapper.dataset.name;
449 commentLikesFrame.src = wrapper.dataset.src;
450 commentLikesFrame.height = '18px';
451 commentLikesFrame.width = '100%';
452 commentLikesFrame.frameBorder = '0';
453 commentLikesFrame.scrolling = 'no';
454
455 wrapper.querySelector( '.comment-like-feedback' ).after( commentLikesFrame );
456
457 jetpackCommentLikesLoadedWidgets.push( commentLikesFrame );
458 }
459
460 wrapper.classList.remove( 'jetpack-likes-widget-unloaded' );
461 wrapper.classList.add( 'jetpack-likes-widget-loading' );
462
463 wrapper.querySelector( 'iframe' ).addEventListener( 'load', e => {
464 JetpackLikesPostMessage(
465 { event: 'loadLikeWidget', name: e.target.name, width: e.target.width },
466 window.frames[ 'likes-master' ]
467 );
468
469 wrapper.classList.remove( 'jetpack-likes-widget-loading' );
470 wrapper.classList.add( 'jetpack-likes-widget-loaded' );
471 } );
472 }
473
474 function jetpackGetUnloadedWidgetsInView() {
475 const unloadedWidgets = document.querySelectorAll( 'div.jetpack-likes-widget-unloaded' );
476
477 return [ ...unloadedWidgets ].filter( item => jetpackIsScrolledIntoView( item ) );
478 }
479
480 function jetpackIsScrolledIntoView( element ) {
481 const top = element.getBoundingClientRect().top;
482 const bottom = element.getBoundingClientRect().bottom;
483
484 // Allow some slack above and bellow the fold with jetpackLikesLookAhead,
485 // with the aim of hiding the transition from unloaded to loaded widget from users.
486 return top + jetpackLikesLookAhead >= 0 && bottom <= window.innerHeight + jetpackLikesLookAhead;
487 }
488
489 function jetpackUnloadScrolledOutWidgets() {
490 for ( let i = jetpackCommentLikesLoadedWidgets.length - 1; i >= 0; i-- ) {
491 const currentWidgetIframe = jetpackCommentLikesLoadedWidgets[ i ];
492
493 if ( ! jetpackIsScrolledIntoView( currentWidgetIframe ) ) {
494 const widgetWrapper =
495 currentWidgetIframe &&
496 currentWidgetIframe.parentElement &&
497 currentWidgetIframe.parentElement.parentElement;
498
499 // Restore parent class to 'unloaded' so this widget can be picked up by queue manager again if needed.
500 widgetWrapper.classList.remove( 'jetpack-likes-widget-loaded' );
501 widgetWrapper.classList.remove( 'jetpack-likes-widget-loading' );
502 widgetWrapper.classList.add( 'jetpack-likes-widget-unloaded' );
503
504 // Bring back the loading placeholder into view.
505 widgetWrapper
506 .querySelectorAll( '.comment-likes-widget-placeholder' )
507 .forEach( item => ( item.style.display = 'block' ) );
508
509 // Remove it from the list of loaded widgets.
510 jetpackCommentLikesLoadedWidgets.splice( i, 1 );
511
512 // Remove comment like widget iFrame.
513 currentWidgetIframe.remove();
514 }
515 }
516 }
517
518 var jetpackWidgetsDelayedExec = function ( after, fn ) {
519 var timer;
520 return function () {
521 clearTimeout( timer );
522 timer = setTimeout( fn, after );
523 };
524 };
525
526 var jetpackOnScrollStopped = jetpackWidgetsDelayedExec( 250, JetpackLikesWidgetQueueHandler );
527
528 // Load initial batch of widgets, prior to any scrolling events.
529 JetpackLikesWidgetQueueHandler();
530
531 // Add event listener to execute queue handler after scroll.
532 window.addEventListener( 'scroll', jetpackOnScrollStopped, true );
533