PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 13.0.2
Jetpack – WP Security, Backup, Speed, & Growth v13.0.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 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
492 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 if ( newLayout ) {
240 element.innerHTML = `
241 <a href="${ encodeURI( liker.profile_URL ) }" rel="nofollow" target="_parent" class="wpl-liker">
242 <img src="${ encodeURI( liker.avatar_URL ) }"
243 alt=""
244 style="width: 28px; height: 28px;" />
245 <span></span>
246 </a>
247 `;
248 } else {
249 element.innerHTML = `
250 <a href="${ encodeURI( liker.profile_URL ) }" rel="nofollow" target="_parent" class="wpl-liker">
251 <img src="${ encodeURI( liker.avatar_URL ) }"
252 alt=""
253 style="width: 30px; height: 30px; padding-right: 3px;" />
254 </a>
255 `;
256 }
257
258 list.append( element );
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 containerStyle = getComputedStyle( container );
284 const isRtl = containerStyle.direction === 'rtl';
285
286 const el = document.querySelector( `*[name='${ data.parent }']` );
287 const rect = el.getBoundingClientRect();
288 const win = el.ownerDocument.defaultView;
289 const offset = {
290 top: rect.top + win.pageYOffset,
291 left: rect.left + win.pageXOffset,
292 };
293
294 if ( newLayout ) {
295 container.style.top = offset.top + data.position.top - 1 + 'px';
296
297 if ( isRtl ) {
298 const visibleAvatarsCount = data && data.likers ? Math.min( data.likers.length, 5 ) : 0;
299 // 24px is the width of the avatar + 4px is the padding between avatars
300 container.style.left =
301 offset.left + data.position.left + 24 * visibleAvatarsCount + 4 + 'px';
302 container.style.transform = 'translateX(-100%)';
303 } else {
304 container.style.left = offset.left + data.position.left + 'px';
305 }
306 } else {
307 container.style.left = offset.left + data.position.left - 10 + 'px';
308 container.style.top = offset.top + data.position.top - 33 + 'px';
309 }
310
311 const rowLength = Math.floor( data.width / 37 );
312 let height = Math.ceil( data.likers.length / rowLength ) * 37 + 13;
313 if ( height > 204 ) {
314 height = 204;
315 }
316
317 if ( ! newLayout ) {
318 // Avatars + padding
319 const containerWidth = rowLength * 37 - 7;
320 container.style.height = height + 'px';
321 container.style.width = containerWidth + 'px';
322
323 const listWidth = rowLength * 37;
324 list.style.width = listWidth + 'px';
325 }
326
327 container.style.display = 'block';
328 container.setAttribute( 'aria-hidden', 'false' );
329 container.focus();
330 }
331 }
332 }
333
334 window.addEventListener( 'message', JetpackLikesMessageListener );
335
336 function hideLikersPopover() {
337 const container = document.querySelector( '#likes-other-gravatars' );
338
339 if ( container ) {
340 container.style.display = 'none';
341 container.setAttribute( 'aria-hidden', 'true' );
342 }
343 }
344
345 document.addEventListener( 'click', hideLikersPopover );
346
347 function JetpackLikesWidgetQueueHandler() {
348 var wrapperID;
349
350 if ( ! jetpackLikesMasterReady ) {
351 setTimeout( JetpackLikesWidgetQueueHandler, 500 );
352 return;
353 }
354
355 // Restore widgets to initial unloaded state when they are scrolled out of view.
356 jetpackUnloadScrolledOutWidgets();
357
358 var unloadedWidgetsInView = jetpackGetUnloadedWidgetsInView();
359
360 if ( unloadedWidgetsInView.length > 0 ) {
361 // Grab any unloaded widgets for a batch request
362 JetpackLikesBatchHandler();
363 }
364
365 for ( var i = 0, length = unloadedWidgetsInView.length; i <= length - 1; i++ ) {
366 wrapperID = unloadedWidgetsInView[ i ].id;
367
368 if ( ! wrapperID ) {
369 continue;
370 }
371
372 jetpackLoadLikeWidgetIframe( wrapperID );
373 }
374 }
375
376 function jetpackLoadLikeWidgetIframe( wrapperID ) {
377 if ( typeof wrapperID === 'undefined' ) {
378 return;
379 }
380
381 const wrapper = document.querySelector( '#' + wrapperID );
382 wrapper.querySelectorAll( 'iframe' ).forEach( iFrame => iFrame.remove() );
383
384 const placeholder = wrapper.querySelector( '.likes-widget-placeholder' );
385
386 // Post like iframe
387 if ( placeholder && placeholder.classList.contains( 'post-likes-widget-placeholder' ) ) {
388 const postLikesFrame = document.createElement( 'iframe' );
389
390 postLikesFrame.classList.add( 'post-likes-widget', 'jetpack-likes-widget' );
391 postLikesFrame.name = wrapper.dataset.name;
392 postLikesFrame.src = wrapper.dataset.src;
393 postLikesFrame.height = '55px';
394 postLikesFrame.width = '100%';
395 postLikesFrame.frameBorder = '0';
396 postLikesFrame.scrolling = 'no';
397 postLikesFrame.title = wrapper.dataset.title;
398
399 placeholder.after( postLikesFrame );
400 }
401
402 // Comment like iframe
403 if ( placeholder.classList.contains( 'comment-likes-widget-placeholder' ) ) {
404 const commentLikesFrame = document.createElement( 'iframe' );
405
406 commentLikesFrame.class = 'comment-likes-widget-frame jetpack-likes-widget-frame';
407 commentLikesFrame.name = wrapper.dataset.name;
408 commentLikesFrame.src = wrapper.dataset.src;
409 commentLikesFrame.height = '18px';
410 commentLikesFrame.width = '100%';
411 commentLikesFrame.frameBorder = '0';
412 commentLikesFrame.scrolling = 'no';
413
414 wrapper.querySelector( '.comment-like-feedback' ).after( commentLikesFrame );
415
416 jetpackCommentLikesLoadedWidgets.push( commentLikesFrame );
417 }
418
419 wrapper.classList.remove( 'jetpack-likes-widget-unloaded' );
420 wrapper.classList.add( 'jetpack-likes-widget-loading' );
421
422 wrapper.querySelector( 'iframe' ).addEventListener( 'load', e => {
423 JetpackLikesPostMessage(
424 { event: 'loadLikeWidget', name: e.target.name, width: e.target.width },
425 window.frames[ 'likes-master' ]
426 );
427
428 wrapper.classList.remove( 'jetpack-likes-widget-loading' );
429 wrapper.classList.add( 'jetpack-likes-widget-loaded' );
430 } );
431 }
432
433 function jetpackGetUnloadedWidgetsInView() {
434 const unloadedWidgets = document.querySelectorAll( 'div.jetpack-likes-widget-unloaded' );
435
436 return [ ...unloadedWidgets ].filter( item => jetpackIsScrolledIntoView( item ) );
437 }
438
439 function jetpackIsScrolledIntoView( element ) {
440 const top = element.getBoundingClientRect().top;
441 const bottom = element.getBoundingClientRect().bottom;
442
443 // Allow some slack above and bellow the fold with jetpackLikesLookAhead,
444 // with the aim of hiding the transition from unloaded to loaded widget from users.
445 return top + jetpackLikesLookAhead >= 0 && bottom <= window.innerHeight + jetpackLikesLookAhead;
446 }
447
448 function jetpackUnloadScrolledOutWidgets() {
449 for ( let i = jetpackCommentLikesLoadedWidgets.length - 1; i >= 0; i-- ) {
450 const currentWidgetIframe = jetpackCommentLikesLoadedWidgets[ i ];
451
452 if ( ! jetpackIsScrolledIntoView( currentWidgetIframe ) ) {
453 const widgetWrapper =
454 currentWidgetIframe &&
455 currentWidgetIframe.parentElement &&
456 currentWidgetIframe.parentElement.parentElement;
457
458 // Restore parent class to 'unloaded' so this widget can be picked up by queue manager again if needed.
459 widgetWrapper.classList.remove( 'jetpack-likes-widget-loaded' );
460 widgetWrapper.classList.remove( 'jetpack-likes-widget-loading' );
461 widgetWrapper.classList.add( 'jetpack-likes-widget-unloaded' );
462
463 // Bring back the loading placeholder into view.
464 widgetWrapper
465 .querySelectorAll( '.comment-likes-widget-placeholder' )
466 .forEach( item => ( item.style.display = 'block' ) );
467
468 // Remove it from the list of loaded widgets.
469 jetpackCommentLikesLoadedWidgets.splice( i, 1 );
470
471 // Remove comment like widget iFrame.
472 currentWidgetIframe.remove();
473 }
474 }
475 }
476
477 var jetpackWidgetsDelayedExec = function ( after, fn ) {
478 var timer;
479 return function () {
480 clearTimeout( timer );
481 timer = setTimeout( fn, after );
482 };
483 };
484
485 var jetpackOnScrollStopped = jetpackWidgetsDelayedExec( 250, JetpackLikesWidgetQueueHandler );
486
487 // Load initial batch of widgets, prior to any scrolling events.
488 JetpackLikesWidgetQueueHandler();
489
490 // Add event listener to execute queue handler after scroll.
491 window.addEventListener( 'scroll', jetpackOnScrollStopped, true );
492