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