PageSourceSearch

https://ndigi.tech/_static/??wp-content/plugins/easy-fancybox/vend…/modules/likes/queuehandler.js?m=1790019728

js ndigi.tech collected 2026-10-03 21:09:57 UTC 19,247 bytes, 529 lines download raw bytes

1!function(n){"function"==typeof define&&define.amd?define(["jquery"],function(e){return n(e)}):"object"==typeof module&&"object"==typeof module.exports?exports=n(require("jquery")):n(jQuery)}(function(n){function e(n){var e=7.5625,t=2.75;return n<1/t?e*n*n:n<2/t?e*(n-=1.5/t)*n+.75:n<2.5/t?e*(n-=2.25/t)*n+.9375:e*(n-=2.625/t)*n+.984375}void 0!==n.easing&&(n.easing.jswing=n.easing.swing);var t=Math.pow,u=Math.sqrt,r=Math.sin,i=Math.cos,a=Math.PI,c=1.70158,o=1.525*c,s=2*a/3,f=2*a/4.5;n.extend(n.easing,{def:"easeOutQuad",swing:function(e){return n.easing[n.easing.def](e)},easeInQuad:function(n){return n*n},easeOutQuad:function(n){return 1-(1-n)*(1-n)},easeInOutQuad:function(n){return n<.5?2*n*n:1-t(-2*n+2,2)/2},easeInCubic:function(n){return n*n*n},easeOutCubic:function(n){return 1-t(1-n,3)},easeInOutCubic:function(n){return n<.5?4*n*n*n:1-t(-2*n+2,3)/2},easeInQuart:function(n){return n*n*n*n},easeOutQuart:function(n){return 1-t(1-n,4)},easeInOutQuart:function(n){return n<.5?8*n*n*n*n:1-t(-2*n+2,4)/2},easeInQuint:function(n){return n*n*n*n*n},easeOutQuint:function(n){return 1-t(1-n,5)},easeInOutQuint:function(n){return n<.5?16*n*n*n*n*n:1-t(-2*n+2,5)/2},easeInSine:function(n){return 1-i(n*a/2)},easeOutSine:function(n){return r(n*a/2)},easeInOutSine:function(n){return-(i(a*n)-1)/2},easeInExpo:function(n){return 0===n?0:t(2,10*n-10)},easeOutExpo:function(n){return 1===n?1:1-t(2,-10*n)},easeInOutExpo:function(n){return 0===n?0:1===n?1:n<.5?t(2,20*n-10)/2:(2-t(2,-20*n+10))/2},easeInCirc:function(n){return 1-u(1-t(n,2))},easeOutCirc:function(n){return u(1-t(n-1,2))},easeInOutCirc:function(n){return n<.5?(1-u(1-t(2*n,2)))/2:(u(1-t(-2*n+2,2))+1)/2}
1,easeInElastic:function(n){return 0===n?0:1===n?1:-t(2,10*n-10)*r((10*n-10.75)*s)},easeOutElastic:function(n){return 0===n?0:1===n?1:t(2,-10*n)*r((10*n-.75)*s)+1},easeInOutElastic:function(n){return 0===n?0:1===n?1:n<.5?-(t(2,20*n-10)*r((20*n-11.125)*f))/2:t(2,-20*n+10)*r((20*n-11.125)*f)/2+1},easeInBack:function(n){return(c+1)*n*n*n-c*n*n},easeOutBack:function(n){return 1+(c+1)*t(n-1,3)+c*t(n-1,2)},easeInOutBack:function(n){return n<.5?t(2*n,2)*(7.189819*n-o)/2:(t(2*n-2,2)*((o+1)*(2*n-2)+o)+2)/2},easeInBounce:function(n){return 1-e(1-n)},easeOutBounce:e,easeInOutBounce:function(n){return n<.5?(1-e(1-2*n))/2:(1+e(2*n-1))/2}})});
2;
3/* global wpcom_reblog */
4
5var jetpackLikesWidgetBatch = [];
6var jetpackLikesMasterReady = false;
7
8// Due to performance problems on pages with a large number of widget iframes that need to be loaded,
9// we are limiting the processing at any instant to unloaded widgets that are currently in viewport,
10// plus this constant that will allow processing of widgets above and bellow the current fold.
11// This aim of it is to improve the UX and hide the transition from unloaded to loaded state from users.
12var jetpackLikesLookAhead = 2000; // pixels
13
14// Keeps track of loaded comment likes widget so we can unload them when they are scrolled out of view.
15var jetpackCommentLikesLoadedWidgets = [];
16
17var jetpackLikesDocReadyPromise = new Promise( resolve => {
18	if ( document.readyState !== 'loading' ) {
19		resolve();
20	} else {
21		window.addEventListener( 'DOMContentLoaded', () => resolve() );
22	}
23} );
24
25function JetpackLikesPostMessage( message, target ) {
26	if ( typeof message === 'string' ) {
27		try {
28			message = JSON.parse( message );
29		} catch {
30			return;
31		}
32	}
33
34	if ( target && typeof target.postMessage === 'function' ) {
35		try {
36			target.postMessage(
37				JSON.stringify( {
38					type: 'likesMessage',
39					data: message,
40				} ),
41				'*'
42			);
43		} catch {
44			// Ignore error
45		}
46	}
47}
48
49function JetpackLikesBatchHandler() {
50	const requests = [];
51	document.querySelectorAll( 'div.jetpack-likes-widget-unloaded' ).forEach( widget => {
52		if ( jetpackLikesWidgetBatch.indexOf( widget.id ) > -1 ) {
53			return;
54		}
55
56		if ( ! jetpackIsScrolledIntoView( widget ) ) {
57			return;
58		}
59
60		jetpackLikesWidgetBatch.push( widget.id );
61
62		var regex = /like-(post|comment)-wrapper-(\d+)-(\d+)-(\w+)/,
63			match = regex.exec( widget.id ),
64			info;
65
66		if ( ! match || match.length !== 5 ) {
67			return;
68		}
69
70		info = {
71			blog_id: match[ 2 ],
72			width: widget.width,
73		};
74
75		if ( 'post' === match[ 1 ] ) {
76			info.post_id = match[ 3 ];
77		} else if ( 'comment' === match[ 1 ] ) {
78			info.comment_id = match[ 3 ];
79		}
80
81		info.obj_id = match[ 4 ];
82
83		requests.push( info );
84	} );
85
86	if ( requests.length > 0 ) {
87		JetpackLikesPostMessage(
88			{ event: 'initialBatch', requests: requests },
89			window.frames[ 'likes-master' ]
90		);
91	}
92}
93
94function JetpackLikesMessageListener( event ) {
95	let message = event && event.data;
96	if ( typeof message === 'string' ) {
97		try {
98			message = JSON.parse( message );
99		} catch {
100			return;
101		}
102	}
103
104	const type = message && message.type;
105	const data = message && message.data;
106
107	if ( type !== 'likesMessage' || typeof data.event === 'undefined' ) {
108		return;
109	}
110
111	// We only allow messages from one origin
112	const allowedOrigin = 'https://widgets.wp.com';
113	if ( allowedOrigin !== event.origin ) {
114		return;
115	}
116
117	switch ( data.event ) {
118		case 'masterReady':
119			jetpackLikesDocReadyPromise.then( () => {
120				jetpackLikesMasterReady = true;
121
122				const stylesData = {
123					event: 'injectStyles',
124				};
125				const sdTextColor = document.querySelector( '.sd-text-color' );
126				const sdLinkColor = document.querySelector( '.sd-link-color' );
127				const sdTextColorStyles = ( sdTextColor && getComputedStyle( sdTextColor ) ) || {};
128				const sdLinkColorStyles = ( sdLinkColor && getComputedStyle( sdLinkColor ) ) || {};
129
130				// enable reblogs if we're on a single post page
131				if ( document.body.classList.contains( 'single' ) ) {
132					JetpackLikesPostMessage( { event: 'reblogsEnabled' }, window.frames[ 'likes-master' ] );
133				}
134
135				stylesData.textStyles = {
136					color: sdTextColorStyles.color,
137					fontFamily: sdTextColorStyles[ 'font-family' ],
138					fontSize: sdTextColorStyles[ 'font-size' ],
139					direction: sdTextColorStyles.direction,
140					fontWeight: sdTextColorStyles[ 'font-weight' ],
141					fontStyle: sdTextColorStyles[ 'font-style' ],
142					textDecoration: sdTextColorStyles[ 'text-decoration' ],
143				};
144
145				stylesData.linkStyles = {
146					color: sdLinkColorStyles.color,
147					fontFamily: sdLinkColorStyles[ 'font-family' ],
148					fontSize: sdLinkColorStyles[ 'font-size' ],
149					textDecoration: sdLinkColorStyles[ 'text-decoration' ],
150					fontWeight: sdLinkColorStyles[ 'font-weight' ],
151					fontStyle: sdLinkColorStyles[ 'font-style' ],
152				};
153
154				JetpackLikesPostMessage( stylesData, window.frames[ 'likes-master' ] );
155
156				JetpackLikesBatchHandler();
157			} );
158
159			break;
160
161		// We're keeping this for planned future follow ups.
162		// @see: https://github.com/Automattic/jetpack/pull/42361#discussion_r1995338815
163		case 'showLikeWidget':
164			break;
165
166		// We're keeping this for planned future follow ups.
167		// @see: https://github.com/Automattic/jetpack/pull/42361#discussion_r1995338815
168		case 'showCommentLikeWidget':
169			break;
170
171		case 'killCommentLikes':
172			// If kill switch for comment likes is enabled remove all widgets wrappers and `Loading...` placeholders.
173			document
174				.querySelectorAll( '.jetpack-comment-likes-widget-wrapper' )
175				.forEach( wrapper => wrapper.remove() );
176			break;
177
178		case 'clickReblogFlair':
179			if ( wpcom_reblog && typeof wpcom_reblog.toggle_reblog_box_flair === 'function' ) {
180				wpcom_reblog.toggle_reblog_box_flair( data.obj_id );
181			}
182			break;
183
184		case 'hideOtherGravatars': {
185			hideLikersPopover();
186			break;
187		}
188
189		case 'showOtherGravatars': {
190			const container = document.querySelector( '#likes-other-gravatars' );
191
192			if ( ! container ) {
193				break;
194			}
195
196			const list = container.querySelector( 'ul' );
197
198			container.style.display = 'none';
199			list.innerHTML = '';
200
201			container
202				.querySelectorAll( '.likes-text span' )
203				.forEach( item => ( item.textContent = data.totalLikesLabel ) );
204
205			( data.likers || [] ).forEach( async ( liker, index ) => {
206				if ( liker.profile_URL.substr( 0, 4 ) !== 'http' ) {
207					// We only display gravatars with http or https schema
208					return;
209				}
210
211				const element = document.createElement( 'li' );
212				list.append( element );
213
214				const profileLink = encodeURI( liker.profile_URL );
215				const avatarLink = encodeURI( liker.avatar_URL );
216				element.innerHTML = `<a href="${ profileLink }" rel="nofollow" target="_parent" class="wpl-liker">
217						<img src="${ avatarLink }"
218							alt=""
219							style="width: 28px; height: 28px;" />
220						<span></span>
221					</a>`;
222
223				// Add some extra attributes through native methods, to ensure strings are sanitized.
224				element.classList.add( liker.css_class );
225				element.querySelector( 'img' ).alt = data.avatarAltTitle.replace( '%s', liker.name );
226				element.querySelector( 'span' ).innerText = liker.name;
227
228				if ( index === data.likers.length - 1 ) {
229					element.addEventListener( 'keydown', e => {
230						if ( e.key === 'Tab' && ! e.shiftKey ) {
231							e.preventDefault();
232							hideLikersPopover();
233
234							JetpackLikesPostMessage(
235								{ event: 'focusLikesCount', parent: data.parent },
236								window.frames[ 'likes-master' ]
237							);
238						}
239					} );
240				}
241			} );
242
243			const positionPopup = function () {
244				const containerStyle = getComputedStyle( container );
245				const isRtl = containerStyle.direction === 'rtl';
246
247				const el = document.querySelector( `*[name='${ data.parent }']` );
248				const rect = el.getBoundingClientRect();
249				const win = el.ownerDocument.defaultView;
250				const offset = {
251					top: rect.top + win.pageYOffset,
252					left: rect.left + win.pageXOffset,
253				};
254
255				let containerLeft;
256				container.style.top = offset.top + data.position.top - 1 + 'px';
257
258				if ( isRtl ) {
259					const visibleAvatarsCount = data && data.likers ? Math.min( data.likers.length, 5 ) : 0;
260					// 24px is the width of the avatar + 4px is the padding between avatars
261					containerLeft = offset.left + data.position.left + 24 * visibleAvatarsCount + 4;
262					container.style.transform = 'translateX(-100%)';
263				} else {
264					containerLeft = offset.left + data.position.left;
265				}
266				container.style.left = containerLeft + 'px';
267
268				// If the popup overflows viewport width, we should show it on the next line.
269				// Push it offscreen to calculated rendered width.
270				container.style.left = '-9999px';
271				container.style.display = 'block';
272
273				// If the popup exceeds the viewport width,
274				// flip the position of the popup.
275				const containerWidth = container.offsetWidth;
276				const containerRight = containerLeft + containerWidth;
277				if ( containerRight > win.innerWidth ) {
278					containerLeft = rect.right - containerWidth;
279				}
280
281				// Set the container left
282				container.style.left = containerLeft + 'px';
283				container.setAttribute( 'aria-hidden', 'false' );
284			};
285
286			positionPopup();
287			container.focus();
288
289			const debounce = function ( func, wait ) {
290				var timeout;
291				return function () {
292					var context = this;
293					var args = arguments;
294					clearTimeout( timeout );
295					timeout = setTimeout( function () {
296						func.apply( context, args );
297					}, wait );
298				};
299			};
300
301			const debouncedPositionPopup = debounce( positionPopup, 100 );
302
303			// Keep a reference of this function in the element itself
304			// so that we can destroy it later
305			container.__resizeHandler = debouncedPositionPopup;
306
307			// When window is resized, resize the popup.
308			window.addEventListener( 'resize', debouncedPositionPopup );
309
310			container.focus();
311		}
312	}
313}
314
315window.addEventListener( 'message', JetpackLikesMessageListener );
316
317function hideLikersPopover() {
318	const container = document.querySelector( '#likes-other-gravatars' );
319
320	if ( container ) {
321		container.style.display = 'none';
322		container.setAttribute( 'aria-hidden', 'true' );
323
324		// Remove the resize event listener and cleanup.
325		const resizeHandler = container.__resizeHandler;
326		if ( resizeHandler ) {
327			window.removeEventListener( 'resize', resizeHandler );
328			delete container.__resizeHandler;
329		}
330	}
331}
332
333document.addEventListener( 'click', hideLikersPopover );
334
335function JetpackLikesWidgetQueueHandler() {
336	var wrapperID;
337
338	if ( ! jetpackLikesMasterReady ) {
339		// The master iframe emits `masterReady` a single time, when it finishes loading. On
340		// script-heavy pages it can finish loading — and emit — before this script attaches its
341		// message listener above, so that one event is missed and the queue never starts. Ping the
342		// master iframe while we wait so it re-emits `masterReady` once both it and our listener
343		// are ready.
344		JetpackLikesPostMessage( { event: 'queryMasterReady' }, window.frames[ 'likes-master' ] );
345		setTimeout( JetpackLikesWidgetQueueHandler, 500 );
346		return;
347	}
348
349	// Restore widgets to initial unloaded state when they are scrolled out of view.
350	jetpackUnloadScrolledOutWidgets();
351
352	jetpackObserveUnloadedWidgets();
353
354	var unloadedWidgetsInView = jetpackGetUnloadedWidgetsInView();
355
356	if ( unloadedWidgetsInView.length > 0 ) {
357		// Grab any unloaded widgets for a batch request
358		JetpackLikesBatchHandler();
359	}
360
361	for ( var i = 0, length = unloadedWidgetsInView.length; i <= length - 1; i++ ) {
362		wrapperID = unloadedWidgetsInView[ i ].id;
363
364		if ( ! wrapperID ) {
365			continue;
366		}
367
368		jetpackLoadLikeWidgetIframe( wrapperID );
369	}
370}
371
372function jetpackLoadLikeWidgetIframe( wrapperID ) {
373	if ( typeof wrapperID === 'undefined' ) {
374		return;
375	}
376
377	const wrapper = document.querySelector( '#' + wrapperID );
378	wrapper.querySelectorAll( 'iframe' ).forEach( iFrame => iFrame.remove() );
379
380	const placeholder = wrapper.querySelector( '.likes-widget-placeholder' );
381
382	// Post like iframe
383	if ( placeholder && placeholder.classList.contains( 'post-likes-widget-placeholder' ) ) {
384		const postLikesFrame = document.createElement( 'iframe' );
385
386		postLikesFrame.classList.add( 'post-likes-widget', 'jetpack-likes-widget' );
387		postLikesFrame.name = wrapper.dataset.name;
388		postLikesFrame.src = wrapper.dataset.src;
389		postLikesFrame.height = '55px';
390		postLikesFrame.width = '100%';
391		postLikesFrame.frameBorder = '0';
392		postLikesFrame.scrolling = 'no';
393		postLikesFrame.title = wrapper.dataset.title;
394
395		placeholder.after( postLikesFrame );
396	}
397
398	// Comment like iframe
399	if ( placeholder.classList.contains( 'comment-likes-widget-placeholder' ) ) {
400		const commentLikesFrame = document.createElement( 'iframe' );
401
402		commentLikesFrame.class = 'comment-likes-widget-frame jetpack-likes-widget-frame';
403		commentLikesFrame.name = wrapper.dataset.name;
404		commentLikesFrame.src = wrapper.dataset.src;
405		commentLikesFrame.height = '18px';
406		commentLikesFrame.width = '100%';
407		commentLikesFrame.frameBorder = '0';
408		commentLikesFrame.scrolling = 'no';
409
410		wrapper.querySelector( '.comment-like-feedback' ).after( commentLikesFrame );
411
412		jetpackCommentLikesLoadedWidgets.push( commentLikesFrame );
413	}
414
415	wrapper.classList.remove( 'jetpack-likes-widget-unloaded' );
416	wrapper.classList.add( 'jetpack-likes-widget-loading' );
417
418	wrapper.querySelector( 'iframe' ).addEventListener( 'load', e => {
419		// A widget scrolled out of view mid-load has its iframe dropped, and a reloaded one
420		// replaces it, either of which leaves this closure holding a detached iframe. Acting on
421		// it would hide a placeholder that is now the only thing the widget has left to show.
422		if ( ! wrapper.contains( e.target ) ) {
423			return;
424		}
425
426		JetpackLikesPostMessage(
427			{ event: 'loadLikeWidget', name: e.target.name, width: e.target.width },
428			window.frames[ 'likes-master' ]
429		);
430
431		// The stylesheet is otherwise the only thing that hides the placeholder, so one that never
432		// applies leaves "Loading…" sitting over a widget that loaded fine.
433		if ( placeholder ) {
434			placeholder.style.display = 'none';
435		}
436
437		wrapper.classList.remove( 'jetpack-likes-widget-loading' );
438		wrapper.classList.add( 'jetpack-likes-widget-loaded' );
439	} );
440}
441
442function jetpackGetUnloadedWidgetsInView() {
443	const unloadedWidgets = document.querySelectorAll( 'div.jetpack-likes-widget-unloaded' );
444
445	return [ ...unloadedWidgets ].filter( item => jetpackIsScrolledIntoView( item ) );
446}
447
448function jetpackIsScrolledIntoView( element ) {
449	const top = element.getBoundingClientRect().top;
450	const bottom = element.getBoundingClientRect().bottom;
451
452	// Allow some slack above and bellow the fold with jetpackLikesLookAhead,
453	// with the aim of hiding the transition from unloaded to loaded widget from users.
454	return top + jetpackLikesLookAhead >= 0 && bottom <= window.innerHeight + jetpackLikesLookAhead;
455}
456
457function jetpackUnloadScrolledOutWidgets() {
458	for ( let i = jetpackCommentLikesLoadedWidgets.length - 1; i >= 0; i-- ) {
459		const currentWidgetIframe = jetpackCommentLikesLoadedWidgets[ i ];
460
461		if ( ! jetpackIsScrolledIntoView( currentWidgetIframe ) ) {
462			const widgetWrapper =
463				currentWidgetIframe &&
464				currentWidgetIframe.parentElement &&
465				currentWidgetIframe.parentElement.parentElement;
466
467			// Restore parent class to 'unloaded' so this widget can be picked up by queue manager again if needed.
468			widgetWrapper.classList.remove( 'jetpack-likes-widget-loaded' );
469			widgetWrapper.classList.remove( 'jetpack-likes-widget-loading' );
470			widgetWrapper.classList.add( 'jetpack-likes-widget-unloaded' );
471
472			// An empty string removes the inline declaration rather than setting one, handing the
473			// placeholder back to the stylesheet. The `display: none` set on load would otherwise
474			// outrank it, leaving an unloaded widget with neither an iframe nor a placeholder.
475			const placeholder = widgetWrapper.querySelector( '.likes-widget-placeholder' );
476			if ( placeholder ) {
477				placeholder.style.display = '';
478			}
479
480			// Remove it from the list of loaded widgets.
481			jetpackCommentLikesLoadedWidgets.splice( i, 1 );
482
483			// Remove comment like widget iFrame.
484			currentWidgetIframe.remove();
485		}
486	}
487}
488
489var jetpackWidgetsDelayedExec = function ( after, fn ) {
490	var timer;
491	return function () {
492		clearTimeout( timer );
493		timer = setTimeout( fn, after );
494	};
495};
496
497var jetpackOnScrollStopped = jetpackWidgetsDelayedExec( 250, JetpackLikesWidgetQueueHandler );
498
499// Scrolling is not the only thing that brings a widget into range. A stylesheet that lands late
500// reflows the page without firing a scroll event, and the queue would never look at that widget
501// again, leaving it on "Loading…" until the reader happens to scroll.
502var jetpackLikesWidgetObserver =
503	typeof IntersectionObserver === 'function'
504		? new IntersectionObserver( jetpackOnScrollStopped, {
505				rootMargin: `${ jetpackLikesLookAhead }px`,
506				// jetpackIsScrolledIntoView() wants the widget fully inside the band, so ask to be
507				// told when it gets there - crossing into partial overlap alone would not load it.
508				threshold: [ 0, 1 ],
509			} )
510		: null;
511
512// Observing an element twice is a no-op, so every queue pass can call this to pick up widgets
513// added after load.
514function jetpackObserveUnloadedWidgets() {
515	if ( ! jetpackLikesWidgetObserver ) {
516		return;
517	}
518
519	document
520		.querySelectorAll( 'div.jetpack-likes-widget-unloaded' )
521		.forEach( widget => jetpackLikesWidgetObserver.observe( widget ) );
522}
523
524// Load initial batch of widgets, prior to any scrolling events.
525JetpackLikesWidgetQueueHandler();
526
527// Add event listener to execute queue handler after scroll.
528window.addEventListener( 'scroll', jetpackOnScrollStopped, true );
529;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.