PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 12.9.5
Jetpack – WP Security, Backup, Speed, & Growth v12.9.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
472 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 'showOtherGravatars': {
204 const container = document.querySelector( '#likes-other-gravatars' );
205 if ( ! container ) {
206 break;
207 }
208
209 const newLayout = container.classList.contains( 'wpl-new-layout' );
210
211 const list = container.querySelector( 'ul' );
212
213 container.style.display = 'none';
214 list.innerHTML = '';
215
216 if ( newLayout ) {
217 container
218 .querySelectorAll( '.likes-text span' )
219 .forEach( item => ( item.textContent = data.totalLikesLabel ) );
220 } else {
221 container
222 .querySelectorAll( '.likes-text span' )
223 .forEach( item => ( item.textContent = data.total ) );
224 }
225
226 ( data.likers || [] ).forEach( liker => {
227 if ( liker.profile_URL.substr( 0, 4 ) !== 'http' ) {
228 // We only display gravatars with http or https schema
229 return;
230 }
231
232 const element = document.createElement( 'li' );
233 if ( newLayout ) {
234 element.innerHTML = `
235 <a href="${ encodeURI( liker.profile_URL ) }" rel="nofollow" target="_parent" class="wpl-liker">
236 <img src="${ encodeURI( liker.avatar_URL ) }"
237 alt=""
238 style="width: 28px; height: 28px;" />
239 <span></span>
240 </a>
241 `;
242 } else {
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: 30px; height: 30px; padding-right: 3px;" />
248 </a>
249 `;
250 }
251
252 list.append( element );
253
254 // Add some extra attributes through native methods, to ensure strings are sanitized.
255 element.classList.add( liker.css_class );
256 element.querySelector( 'img' ).alt = liker.name;
257 if ( newLayout ) {
258 element.querySelector( 'span' ).innerText = liker.name;
259 }
260 } );
261
262 const containerStyle = getComputedStyle( container );
263 const isRtl = containerStyle.direction === 'rtl';
264
265 const el = document.querySelector( `*[name='${ data.parent }']` );
266 const rect = el.getBoundingClientRect();
267 const win = el.ownerDocument.defaultView;
268 const offset = {
269 top: rect.top + win.pageYOffset,
270 left: rect.left + win.pageXOffset,
271 };
272
273 if ( newLayout ) {
274 container.style.top = offset.top + data.position.top - 1 + 'px';
275
276 if ( isRtl ) {
277 const visibleAvatarsCount = data && data.likers ? Math.min( data.likers.length, 5 ) : 0;
278 // 24px is the width of the avatar + 4px is the padding between avatars
279 container.style.left =
280 offset.left + data.position.left + 24 * visibleAvatarsCount + 4 + 'px';
281 container.style.transform = 'translateX(-100%)';
282 } else {
283 container.style.left = offset.left + data.position.left + 'px';
284 }
285 } else {
286 container.style.left = offset.left + data.position.left - 10 + 'px';
287 container.style.top = offset.top + data.position.top - 33 + 'px';
288 }
289
290 const rowLength = Math.floor( data.width / 37 );
291 let height = Math.ceil( data.likers.length / rowLength ) * 37 + 13;
292 if ( height > 204 ) {
293 height = 204;
294 }
295
296 if ( ! newLayout ) {
297 // Avatars + padding
298 const containerWidth = rowLength * 37 - 7;
299 container.style.height = height + 'px';
300 container.style.width = containerWidth + 'px';
301
302 const listWidth = rowLength * 37;
303 list.style.width = listWidth + 'px';
304
305 const scrollbarWidth = list.offsetWidth - list.clientWidth;
306 if ( scrollbarWidth > 0 ) {
307 container.style.width = containerWidth + scrollbarWidth + 'px';
308 list.style.width = listWidth + scrollbarWidth + 'px';
309 }
310 }
311
312 container.style.display = 'block';
313 }
314 }
315 }
316
317 window.addEventListener( 'message', JetpackLikesMessageListener );
318
319 document.addEventListener( 'click', e => {
320 const container = document.querySelector( '#likes-other-gravatars' );
321
322 if ( container && ! container.contains( e.target ) ) {
323 container.style.display = 'none';
324 }
325 } );
326
327 function JetpackLikesWidgetQueueHandler() {
328 var wrapperID;
329
330 if ( ! jetpackLikesMasterReady ) {
331 setTimeout( JetpackLikesWidgetQueueHandler, 500 );
332 return;
333 }
334
335 // Restore widgets to initial unloaded state when they are scrolled out of view.
336 jetpackUnloadScrolledOutWidgets();
337
338 var unloadedWidgetsInView = jetpackGetUnloadedWidgetsInView();
339
340 if ( unloadedWidgetsInView.length > 0 ) {
341 // Grab any unloaded widgets for a batch request
342 JetpackLikesBatchHandler();
343 }
344
345 for ( var i = 0, length = unloadedWidgetsInView.length; i <= length - 1; i++ ) {
346 wrapperID = unloadedWidgetsInView[ i ].id;
347
348 if ( ! wrapperID ) {
349 continue;
350 }
351
352 jetpackLoadLikeWidgetIframe( wrapperID );
353 }
354 }
355
356 function jetpackLoadLikeWidgetIframe( wrapperID ) {
357 if ( typeof wrapperID === 'undefined' ) {
358 return;
359 }
360
361 const wrapper = document.querySelector( '#' + wrapperID );
362 wrapper.querySelectorAll( 'iframe' ).forEach( iFrame => iFrame.remove() );
363
364 const placeholder = wrapper.querySelector( '.likes-widget-placeholder' );
365
366 // Post like iframe
367 if ( placeholder && placeholder.classList.contains( 'post-likes-widget-placeholder' ) ) {
368 const postLikesFrame = document.createElement( 'iframe' );
369
370 postLikesFrame.classList.add( 'post-likes-widget', 'jetpack-likes-widget' );
371 postLikesFrame.name = wrapper.dataset.name;
372 postLikesFrame.src = wrapper.dataset.src;
373 postLikesFrame.height = '55px';
374 postLikesFrame.width = '100%';
375 postLikesFrame.frameBorder = '0';
376 postLikesFrame.scrolling = 'no';
377 postLikesFrame.title = wrapper.dataset.title;
378
379 placeholder.after( postLikesFrame );
380 }
381
382 // Comment like iframe
383 if ( placeholder.classList.contains( 'comment-likes-widget-placeholder' ) ) {
384 const commentLikesFrame = document.createElement( 'iframe' );
385
386 commentLikesFrame.class = 'comment-likes-widget-frame jetpack-likes-widget-frame';
387 commentLikesFrame.name = wrapper.dataset.name;
388 commentLikesFrame.src = wrapper.dataset.src;
389 commentLikesFrame.height = '18px';
390 commentLikesFrame.width = '100%';
391 commentLikesFrame.frameBorder = '0';
392 commentLikesFrame.scrolling = 'no';
393
394 wrapper.querySelector( '.comment-like-feedback' ).after( commentLikesFrame );
395
396 jetpackCommentLikesLoadedWidgets.push( commentLikesFrame );
397 }
398
399 wrapper.classList.remove( 'jetpack-likes-widget-unloaded' );
400 wrapper.classList.add( 'jetpack-likes-widget-loading' );
401
402 wrapper.querySelector( 'iframe' ).addEventListener( 'load', e => {
403 JetpackLikesPostMessage(
404 { event: 'loadLikeWidget', name: e.target.name, width: e.target.width },
405 window.frames[ 'likes-master' ]
406 );
407
408 wrapper.classList.remove( 'jetpack-likes-widget-loading' );
409 wrapper.classList.add( 'jetpack-likes-widget-loaded' );
410 } );
411 }
412
413 function jetpackGetUnloadedWidgetsInView() {
414 const unloadedWidgets = document.querySelectorAll( 'div.jetpack-likes-widget-unloaded' );
415
416 return [ ...unloadedWidgets ].filter( item => jetpackIsScrolledIntoView( item ) );
417 }
418
419 function jetpackIsScrolledIntoView( element ) {
420 const top = element.getBoundingClientRect().top;
421 const bottom = element.getBoundingClientRect().bottom;
422
423 // Allow some slack above and bellow the fold with jetpackLikesLookAhead,
424 // with the aim of hiding the transition from unloaded to loaded widget from users.
425 return top + jetpackLikesLookAhead >= 0 && bottom <= window.innerHeight + jetpackLikesLookAhead;
426 }
427
428 function jetpackUnloadScrolledOutWidgets() {
429 for ( let i = jetpackCommentLikesLoadedWidgets.length - 1; i >= 0; i-- ) {
430 const currentWidgetIframe = jetpackCommentLikesLoadedWidgets[ i ];
431
432 if ( ! jetpackIsScrolledIntoView( currentWidgetIframe ) ) {
433 const widgetWrapper =
434 currentWidgetIframe &&
435 currentWidgetIframe.parentElement &&
436 currentWidgetIframe.parentElement.parentElement;
437
438 // Restore parent class to 'unloaded' so this widget can be picked up by queue manager again if needed.
439 widgetWrapper.classList.remove( 'jetpack-likes-widget-loaded' );
440 widgetWrapper.classList.remove( 'jetpack-likes-widget-loading' );
441 widgetWrapper.classList.add( 'jetpack-likes-widget-unloaded' );
442
443 // Bring back the loading placeholder into view.
444 widgetWrapper
445 .querySelectorAll( '.comment-likes-widget-placeholder' )
446 .forEach( item => ( item.style.display = 'block' ) );
447
448 // Remove it from the list of loaded widgets.
449 jetpackCommentLikesLoadedWidgets.splice( i, 1 );
450
451 // Remove comment like widget iFrame.
452 currentWidgetIframe.remove();
453 }
454 }
455 }
456
457 var jetpackWidgetsDelayedExec = function ( after, fn ) {
458 var timer;
459 return function () {
460 clearTimeout( timer );
461 timer = setTimeout( fn, after );
462 };
463 };
464
465 var jetpackOnScrollStopped = jetpackWidgetsDelayedExec( 250, JetpackLikesWidgetQueueHandler );
466
467 // Load initial batch of widgets, prior to any scrolling events.
468 JetpackLikesWidgetQueueHandler();
469
470 // Add event listener to execute queue handler after scroll.
471 window.addEventListener( 'scroll', jetpackOnScrollStopped, true );
472