PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 13.1.5
Jetpack – WP Security, Backup, Speed, & Growth v13.1.5
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 3 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
548 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 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 // enable reblogs if we're on a single post page
144 if ( document.body.classList.contains( 'single' ) ) {
145 JetpackLikesPostMessage( { event: 'reblogsEnabled' }, window.frames[ 'likes-master' ] );
146 }
147
148 stylesData.textStyles = {
149 color: sdTextColorStyles[ 'color' ],
150 fontFamily: sdTextColorStyles[ 'font-family' ],
151 fontSize: sdTextColorStyles[ 'font-size' ],
152 direction: sdTextColorStyles[ 'direction' ],
153 fontWeight: sdTextColorStyles[ 'font-weight' ],
154 fontStyle: sdTextColorStyles[ 'font-style' ],
155 textDecoration: sdTextColorStyles[ 'text-decoration' ],
156 };
157
158 stylesData.linkStyles = {
159 color: sdLinkColorStyles[ 'color' ],
160 fontFamily: sdLinkColorStyles[ 'font-family' ],
161 fontSize: sdLinkColorStyles[ 'font-size' ],
162 textDecoration: sdLinkColorStyles[ 'text-decoration' ],
163 fontWeight: sdLinkColorStyles[ 'font-weight' ],
164 fontStyle: sdLinkColorStyles[ 'font-style' ],
165 };
166
167 JetpackLikesPostMessage( stylesData, window.frames[ 'likes-master' ] );
168
169 JetpackLikesBatchHandler();
170 } );
171
172 break;
173
174 case 'showLikeWidget': {
175 const placeholder = document.querySelector( `#${ data.id } .likes-widget-placeholder` );
176 if ( placeholder ) {
177 placeholder.style.display = 'none';
178 }
179 break;
180 }
181
182 case 'showCommentLikeWidget': {
183 const placeholder = document.querySelector( `#${ data.id } .likes-widget-placeholder` );
184 if ( placeholder ) {
185 placeholder.style.display = 'none';
186 }
187 break;
188 }
189
190 case 'killCommentLikes':
191 // If kill switch for comment likes is enabled remove all widgets wrappers and `Loading...` placeholders.
192 document
193 .querySelectorAll( '.jetpack-comment-likes-widget-wrapper' )
194 .forEach( wrapper => wrapper.remove() );
195 break;
196
197 case 'clickReblogFlair':
198 if ( wpcom_reblog && typeof wpcom_reblog.toggle_reblog_box_flair === 'function' ) {
199 wpcom_reblog.toggle_reblog_box_flair( data.obj_id );
200 }
201 break;
202
203 case 'hideOtherGravatars': {
204 hideLikersPopover();
205 break;
206 }
207
208 case 'showOtherGravatars': {
209 const container = document.querySelector( '#likes-other-gravatars' );
210
211 if ( ! container ) {
212 break;
213 }
214
215 const newLayout = container.classList.contains( 'wpl-new-layout' );
216
217 const list = container.querySelector( 'ul' );
218
219 container.style.display = 'none';
220 list.innerHTML = '';
221
222 if ( newLayout ) {
223 container
224 .querySelectorAll( '.likes-text span' )
225 .forEach( item => ( item.textContent = data.totalLikesLabel ) );
226 } else {
227 container
228 .querySelectorAll( '.likes-text span' )
229 .forEach( item => ( item.textContent = data.total ) );
230 }
231
232 ( data.likers || [] ).forEach( async ( liker, index ) => {
233 if ( liker.profile_URL.substr( 0, 4 ) !== 'http' ) {
234 // We only display gravatars with http or https schema
235 return;
236 }
237
238 const element = document.createElement( 'li' );
239 list.append( element );
240
241 if ( newLayout ) {
242 element.innerHTML = `
243 <a href="${ encodeURI( liker.profile_URL ) }" rel="nofollow" target="_parent" class="wpl-liker">
244 <img src="${ encodeURI( liker.avatar_URL ) }"
245 alt=""
246 style="width: 28px; height: 28px;" />
247 <span></span>
248 </a>
249 `;
250 } else {
251 element.innerHTML = `
252 <a href="${ encodeURI( liker.profile_URL ) }" rel="nofollow" target="_parent" class="wpl-liker">
253 <img src="${ encodeURI( liker.avatar_URL ) }"
254 alt=""
255 style="width: 30px; height: 30px; padding-right: 3px;" />
256 </a>
257 `;
258 }
259
260 // Add some extra attributes through native methods, to ensure strings are sanitized.
261 element.classList.add( liker.css_class );
262 element.querySelector( 'img' ).alt = data.avatarAltTitle.replace( '%s', liker.name );
263
264 if ( newLayout ) {
265 element.querySelector( 'span' ).innerText = liker.name;
266 }
267
268 if ( index === data.likers.length - 1 ) {
269 element.addEventListener( 'keydown', e => {
270 if ( e.key === 'Tab' && ! e.shiftKey ) {
271 e.preventDefault();
272 hideLikersPopover();
273
274 JetpackLikesPostMessage(
275 { event: 'focusLikesCount', parent: data.parent },
276 window.frames[ 'likes-master' ]
277 );
278 }
279 } );
280 }
281 } );
282
283 const positionPopup = function () {
284 const containerStyle = getComputedStyle( container );
285 const isRtl = containerStyle.direction === 'rtl';
286
287 const el = document.querySelector( `*[name='${ data.parent }']` );
288 const rect = el.getBoundingClientRect();
289 const win = el.ownerDocument.defaultView;
290 const offset = {
291 top: rect.top + win.pageYOffset,
292 left: rect.left + win.pageXOffset,
293 };
294
295 let containerLeft = 0;
296 if ( newLayout ) {
297 container.style.top = offset.top + data.position.top - 1 + 'px';
298
299 if ( isRtl ) {
300 const visibleAvatarsCount = data && data.likers ? Math.min( data.likers.length, 5 ) : 0;
301 // 24px is the width of the avatar + 4px is the padding between avatars
302 containerLeft = offset.left + data.position.left + 24 * visibleAvatarsCount + 4;
303 container.style.transform = 'translateX(-100%)';
304 } else {
305 containerLeft = offset.left + data.position.left;
306 }
307 container.style.left = containerLeft + 'px';
308 } else {
309 container.style.left = offset.left + data.position.left - 10 + 'px';
310 container.style.top = offset.top + data.position.top - 33 + 'px';
311 }
312
313 // Container width - padding
314 const initContainerWidth = data.width - 20;
315 const rowLength = Math.floor( initContainerWidth / 37 );
316 // # of rows + (avatar + avatar padding) + text above + container padding
317 let height = Math.ceil( data.likers.length / rowLength ) * 37 + 17 + 22;
318 if ( height > 204 ) {
319 height = 204;
320 }
321
322 if ( ! newLayout ) {
323 // Avatars + padding
324 const containerWidth = rowLength * 37 + 13;
325 container.style.height = height + 'px';
326 container.style.width = containerWidth + 'px';
327
328 const listWidth = rowLength * 37;
329 list.style.width = listWidth + 'px';
330 }
331
332 // If the popup is overflows viewport width, we should show it on the next line
333 if ( newLayout ) {
334 // Push it offscreen to calculated rendered width
335 container.style.left = '-9999px';
336 container.style.display = 'block';
337
338 // If the popup exceeds the viewport width,
339 // flip the position of the popup.
340 const containerWidth = container.offsetWidth;
341 const containerRight = containerLeft + containerWidth;
342 if ( containerRight > win.innerWidth ) {
343 containerLeft = rect.right - containerWidth;
344 }
345
346 // Set the container left
347 container.style.left = containerLeft + 'px';
348 } else {
349 container.style.display = 'block';
350 }
351 container.setAttribute( 'aria-hidden', 'false' );
352 };
353
354 positionPopup();
355 container.focus();
356
357 const debounce = function ( func, wait ) {
358 var timeout;
359 return function () {
360 var context = this;
361 var args = arguments;
362 clearTimeout( timeout );
363 timeout = setTimeout( function () {
364 func.apply( context, args );
365 }, wait );
366 };
367 };
368
369 const debouncedPositionPopup = debounce( positionPopup, 100 );
370
371 // Keep a reference of this function in the element itself
372 // so that we can destroy it later
373 container.__resizeHandler = debouncedPositionPopup;
374
375 // When window is resized, resize the popup.
376 window.addEventListener( 'resize', debouncedPositionPopup );
377
378 container.focus();
379 }
380 }
381 }
382
383 window.addEventListener( 'message', JetpackLikesMessageListener );
384
385 function hideLikersPopover() {
386 const container = document.querySelector( '#likes-other-gravatars' );
387
388 if ( container ) {
389 container.style.display = 'none';
390 container.setAttribute( 'aria-hidden', 'true' );
391
392 // Remove the resize event listener and cleanup.
393 const resizeHandler = container.__resizeHandler;
394 if ( resizeHandler ) {
395 window.removeEventListener( 'resize', resizeHandler );
396 delete container.__resizeHandler;
397 }
398 }
399 }
400
401 document.addEventListener( 'click', hideLikersPopover );
402
403 function JetpackLikesWidgetQueueHandler() {
404 var wrapperID;
405
406 if ( ! jetpackLikesMasterReady ) {
407 setTimeout( JetpackLikesWidgetQueueHandler, 500 );
408 return;
409 }
410
411 // Restore widgets to initial unloaded state when they are scrolled out of view.
412 jetpackUnloadScrolledOutWidgets();
413
414 var unloadedWidgetsInView = jetpackGetUnloadedWidgetsInView();
415
416 if ( unloadedWidgetsInView.length > 0 ) {
417 // Grab any unloaded widgets for a batch request
418 JetpackLikesBatchHandler();
419 }
420
421 for ( var i = 0, length = unloadedWidgetsInView.length; i <= length - 1; i++ ) {
422 wrapperID = unloadedWidgetsInView[ i ].id;
423
424 if ( ! wrapperID ) {
425 continue;
426 }
427
428 jetpackLoadLikeWidgetIframe( wrapperID );
429 }
430 }
431
432 function jetpackLoadLikeWidgetIframe( wrapperID ) {
433 if ( typeof wrapperID === 'undefined' ) {
434 return;
435 }
436
437 const wrapper = document.querySelector( '#' + wrapperID );
438 wrapper.querySelectorAll( 'iframe' ).forEach( iFrame => iFrame.remove() );
439
440 const placeholder = wrapper.querySelector( '.likes-widget-placeholder' );
441
442 // Post like iframe
443 if ( placeholder && placeholder.classList.contains( 'post-likes-widget-placeholder' ) ) {
444 const postLikesFrame = document.createElement( 'iframe' );
445
446 postLikesFrame.classList.add( 'post-likes-widget', 'jetpack-likes-widget' );
447 postLikesFrame.name = wrapper.dataset.name;
448 postLikesFrame.src = wrapper.dataset.src;
449 postLikesFrame.height = '55px';
450 postLikesFrame.width = '100%';
451 postLikesFrame.frameBorder = '0';
452 postLikesFrame.scrolling = 'no';
453 postLikesFrame.title = wrapper.dataset.title;
454
455 placeholder.after( postLikesFrame );
456 }
457
458 // Comment like iframe
459 if ( placeholder.classList.contains( 'comment-likes-widget-placeholder' ) ) {
460 const commentLikesFrame = document.createElement( 'iframe' );
461
462 commentLikesFrame.class = 'comment-likes-widget-frame jetpack-likes-widget-frame';
463 commentLikesFrame.name = wrapper.dataset.name;
464 commentLikesFrame.src = wrapper.dataset.src;
465 commentLikesFrame.height = '18px';
466 commentLikesFrame.width = '100%';
467 commentLikesFrame.frameBorder = '0';
468 commentLikesFrame.scrolling = 'no';
469
470 wrapper.querySelector( '.comment-like-feedback' ).after( commentLikesFrame );
471
472 jetpackCommentLikesLoadedWidgets.push( commentLikesFrame );
473 }
474
475 wrapper.classList.remove( 'jetpack-likes-widget-unloaded' );
476 wrapper.classList.add( 'jetpack-likes-widget-loading' );
477
478 wrapper.querySelector( 'iframe' ).addEventListener( 'load', e => {
479 JetpackLikesPostMessage(
480 { event: 'loadLikeWidget', name: e.target.name, width: e.target.width },
481 window.frames[ 'likes-master' ]
482 );
483
484 wrapper.classList.remove( 'jetpack-likes-widget-loading' );
485 wrapper.classList.add( 'jetpack-likes-widget-loaded' );
486 } );
487 }
488
489 function jetpackGetUnloadedWidgetsInView() {
490 const unloadedWidgets = document.querySelectorAll( 'div.jetpack-likes-widget-unloaded' );
491
492 return [ ...unloadedWidgets ].filter( item => jetpackIsScrolledIntoView( item ) );
493 }
494
495 function jetpackIsScrolledIntoView( element ) {
496 const top = element.getBoundingClientRect().top;
497 const bottom = element.getBoundingClientRect().bottom;
498
499 // Allow some slack above and bellow the fold with jetpackLikesLookAhead,
500 // with the aim of hiding the transition from unloaded to loaded widget from users.
501 return top + jetpackLikesLookAhead >= 0 && bottom <= window.innerHeight + jetpackLikesLookAhead;
502 }
503
504 function jetpackUnloadScrolledOutWidgets() {
505 for ( let i = jetpackCommentLikesLoadedWidgets.length - 1; i >= 0; i-- ) {
506 const currentWidgetIframe = jetpackCommentLikesLoadedWidgets[ i ];
507
508 if ( ! jetpackIsScrolledIntoView( currentWidgetIframe ) ) {
509 const widgetWrapper =
510 currentWidgetIframe &&
511 currentWidgetIframe.parentElement &&
512 currentWidgetIframe.parentElement.parentElement;
513
514 // Restore parent class to 'unloaded' so this widget can be picked up by queue manager again if needed.
515 widgetWrapper.classList.remove( 'jetpack-likes-widget-loaded' );
516 widgetWrapper.classList.remove( 'jetpack-likes-widget-loading' );
517 widgetWrapper.classList.add( 'jetpack-likes-widget-unloaded' );
518
519 // Bring back the loading placeholder into view.
520 widgetWrapper
521 .querySelectorAll( '.comment-likes-widget-placeholder' )
522 .forEach( item => ( item.style.display = 'block' ) );
523
524 // Remove it from the list of loaded widgets.
525 jetpackCommentLikesLoadedWidgets.splice( i, 1 );
526
527 // Remove comment like widget iFrame.
528 currentWidgetIframe.remove();
529 }
530 }
531 }
532
533 var jetpackWidgetsDelayedExec = function ( after, fn ) {
534 var timer;
535 return function () {
536 clearTimeout( timer );
537 timer = setTimeout( fn, after );
538 };
539 };
540
541 var jetpackOnScrollStopped = jetpackWidgetsDelayedExec( 250, JetpackLikesWidgetQueueHandler );
542
543 // Load initial batch of widgets, prior to any scrolling events.
544 JetpackLikesWidgetQueueHandler();
545
546 // Add event listener to execute queue handler after scroll.
547 window.addEventListener( 'scroll', jetpackOnScrollStopped, true );
548