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