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