kollapsminoriteten/wp-content/plugins/jetpack/modules/likes/queuehandler.js

472 lines
14 KiB
JavaScript
Raw Normal View History

2022-04-02 10:26:41 +02:00
/* global wpcom_reblog */
2019-11-15 23:26:29 +01:00
var jetpackLikesWidgetBatch = [];
var jetpackLikesMasterReady = false;
// Due to performance problems on pages with a large number of widget iframes that need to be loaded,
// we are limiting the processing at any instant to unloaded widgets that are currently in viewport,
// plus this constant that will allow processing of widgets above and bellow the current fold.
// This aim of it is to improve the UX and hide the transition from unloaded to loaded state from users.
var jetpackLikesLookAhead = 2000; // pixels
// Keeps track of loaded comment likes widget so we can unload them when they are scrolled out of view.
var jetpackCommentLikesLoadedWidgets = [];
2022-04-02 10:26:41 +02:00
var jetpackLikesDocReadyPromise = new Promise( resolve => {
if ( document.readyState !== 'loading' ) {
resolve();
} else {
window.addEventListener( 'DOMContentLoaded', () => resolve() );
}
} );
2019-11-15 23:26:29 +01:00
function JetpackLikesPostMessage( message, target ) {
2022-04-02 10:26:41 +02:00
if ( typeof message === 'string' ) {
2019-11-15 23:26:29 +01:00
try {
message = JSON.parse( message );
} catch ( e ) {
return;
}
}
2022-04-02 10:26:41 +02:00
if ( target && typeof target.postMessage === 'function' ) {
try {
target.postMessage(
JSON.stringify( {
type: 'likesMessage',
data: message,
} ),
'*'
);
} catch ( e ) {
return;
}
}
2019-11-15 23:26:29 +01:00
}
function JetpackLikesBatchHandler() {
2022-04-02 10:26:41 +02:00
const requests = [];
document.querySelectorAll( 'div.jetpack-likes-widget-unloaded' ).forEach( widget => {
if ( jetpackLikesWidgetBatch.indexOf( widget.id ) > -1 ) {
2019-11-15 23:26:29 +01:00
return;
}
2022-04-02 10:26:41 +02:00
if ( ! jetpackIsScrolledIntoView( widget ) ) {
2019-11-15 23:26:29 +01:00
return;
}
2022-04-02 10:26:41 +02:00
jetpackLikesWidgetBatch.push( widget.id );
2019-11-15 23:26:29 +01:00
var regex = /like-(post|comment)-wrapper-(\d+)-(\d+)-(\w+)/,
2022-04-02 10:26:41 +02:00
match = regex.exec( widget.id ),
2019-11-15 23:26:29 +01:00
info;
if ( ! match || match.length !== 5 ) {
return;
}
info = {
blog_id: match[ 2 ],
2022-04-02 10:26:41 +02:00
width: widget.width,
2019-11-15 23:26:29 +01:00
};
if ( 'post' === match[ 1 ] ) {
info.post_id = match[ 3 ];
} else if ( 'comment' === match[ 1 ] ) {
info.comment_id = match[ 3 ];
}
info.obj_id = match[ 4 ];
requests.push( info );
} );
if ( requests.length > 0 ) {
JetpackLikesPostMessage(
{ event: 'initialBatch', requests: requests },
window.frames[ 'likes-master' ]
);
}
}
2022-04-02 10:26:41 +02:00
function JetpackLikesMessageListener( event ) {
let message = event && event.data;
if ( typeof message === 'string' ) {
try {
message = JSON.parse( message );
} catch ( err ) {
return;
}
}
const type = message && message.type;
const data = message && message.data;
2019-11-15 23:26:29 +01:00
2022-04-02 10:26:41 +02:00
if ( type !== 'likesMessage' || typeof data.event === 'undefined' ) {
2019-11-15 23:26:29 +01:00
return;
}
// We only allow messages from one origin
2022-04-02 10:26:41 +02:00
const allowedOrigin = 'https://widgets.wp.com';
if ( allowedOrigin !== event.origin ) {
2019-11-15 23:26:29 +01:00
return;
}
2022-04-02 10:26:41 +02:00
switch ( data.event ) {
2019-11-15 23:26:29 +01:00
case 'masterReady':
2022-04-02 10:26:41 +02:00
jetpackLikesDocReadyPromise.then( () => {
2019-11-15 23:26:29 +01:00
jetpackLikesMasterReady = true;
2022-04-02 10:26:41 +02:00
const stylesData = {
event: 'injectStyles',
};
const sdTextColor = document.querySelector( '.sd-text-color' );
const sdLinkColor = document.querySelector( '.sd-link-color' );
const sdTextColorStyles = ( sdTextColor && getComputedStyle( sdTextColor ) ) || {};
const sdLinkColorStyles = ( sdLinkColor && getComputedStyle( sdLinkColor ) ) || {};
2019-11-15 23:26:29 +01:00
2022-04-02 10:26:41 +02:00
if ( document.querySelectorAll( 'iframe.admin-bar-likes-widget' ).length > 0 ) {
2019-11-15 23:26:29 +01:00
JetpackLikesPostMessage( { event: 'adminBarEnabled' }, window.frames[ 'likes-master' ] );
2022-04-02 10:26:41 +02:00
const bgSource = document.querySelector(
'#wpadminbar .quicklinks li#wp-admin-bar-wpl-like > a'
);
const wpAdminBar = document.querySelector( '#wpadminbar' );
2019-11-15 23:26:29 +01:00
stylesData.adminBarStyles = {
2022-04-02 10:26:41 +02:00
background: bgSource && getComputedStyle( bgSource ).background,
isRtl: wpAdminBar && getComputedStyle( wpAdminBar ).direction === 'rtl',
2019-11-15 23:26:29 +01:00
};
}
2022-04-02 10:26:41 +02:00
// enable reblogs if we're on a single post page
if ( document.body.classList.contains( 'single' ) ) {
JetpackLikesPostMessage( { event: 'reblogsEnabled' }, window.frames[ 'likes-master' ] );
2019-11-15 23:26:29 +01:00
}
stylesData.textStyles = {
2022-04-02 10:26:41 +02:00
color: sdTextColorStyles[ 'color' ],
fontFamily: sdTextColorStyles[ 'font-family' ],
fontSize: sdTextColorStyles[ 'font-size' ],
direction: sdTextColorStyles[ 'direction' ],
fontWeight: sdTextColorStyles[ 'font-weight' ],
fontStyle: sdTextColorStyles[ 'font-style' ],
textDecoration: sdTextColorStyles[ 'text-decoration' ],
2019-11-15 23:26:29 +01:00
};
stylesData.linkStyles = {
2022-04-02 10:26:41 +02:00
color: sdLinkColorStyles[ 'color' ],
fontFamily: sdLinkColorStyles[ 'font-family' ],
fontSize: sdLinkColorStyles[ 'font-size' ],
textDecoration: sdLinkColorStyles[ 'text-decoration' ],
fontWeight: sdLinkColorStyles[ 'font-weight' ],
fontStyle: sdLinkColorStyles[ 'font-style' ],
2019-11-15 23:26:29 +01:00
};
JetpackLikesPostMessage( stylesData, window.frames[ 'likes-master' ] );
JetpackLikesBatchHandler();
} );
break;
2022-04-02 10:26:41 +02:00
case 'showLikeWidget': {
const placeholder = document.querySelector( `#${ data.id } .likes-widget-placeholder` );
if ( placeholder ) {
placeholder.style.display = 'none';
}
2019-11-15 23:26:29 +01:00
break;
2022-04-02 10:26:41 +02:00
}
2019-11-15 23:26:29 +01:00
2022-04-02 10:26:41 +02:00
case 'showCommentLikeWidget': {
const placeholder = document.querySelector( `#${ data.id } .likes-widget-placeholder` );
if ( placeholder ) {
placeholder.style.display = 'none';
}
2019-11-15 23:26:29 +01:00
break;
2022-04-02 10:26:41 +02:00
}
2019-11-15 23:26:29 +01:00
case 'killCommentLikes':
// If kill switch for comment likes is enabled remove all widgets wrappers and `Loading...` placeholders.
2022-04-02 10:26:41 +02:00
document
.querySelectorAll( '.jetpack-comment-likes-widget-wrapper' )
.forEach( wrapper => wrapper.remove() );
2019-11-15 23:26:29 +01:00
break;
case 'clickReblogFlair':
2022-04-02 10:26:41 +02:00
if ( wpcom_reblog && typeof wpcom_reblog.toggle_reblog_box_flair === 'function' ) {
wpcom_reblog.toggle_reblog_box_flair( data.obj_id );
}
2019-11-15 23:26:29 +01:00
break;
2022-04-02 10:26:41 +02:00
case 'showOtherGravatars': {
const container = document.querySelector( '#likes-other-gravatars' );
if ( ! container ) {
break;
}
2019-11-15 23:26:29 +01:00
2023-12-07 09:44:11 +01:00
const newLayout = container.classList.contains( 'wpl-new-layout' );
2022-04-02 10:26:41 +02:00
const list = container.querySelector( 'ul' );
2019-11-15 23:26:29 +01:00
2022-04-02 10:26:41 +02:00
container.style.display = 'none';
list.innerHTML = '';
2019-11-15 23:26:29 +01:00
2023-12-07 09:44:11 +01:00
if ( newLayout ) {
container
.querySelectorAll( '.likes-text span' )
.forEach( item => ( item.textContent = data.totalLikesLabel ) );
} else {
container
.querySelectorAll( '.likes-text span' )
.forEach( item => ( item.textContent = data.total ) );
}
2019-11-15 23:26:29 +01:00
2022-04-02 10:26:41 +02:00
( data.likers || [] ).forEach( liker => {
if ( liker.profile_URL.substr( 0, 4 ) !== 'http' ) {
2019-11-15 23:26:29 +01:00
// We only display gravatars with http or https schema
return;
}
2022-04-02 10:26:41 +02:00
const element = document.createElement( 'li' );
2023-12-07 09:44:11 +01:00
if ( newLayout ) {
element.innerHTML = `
<a href="${ encodeURI( liker.profile_URL ) }" rel="nofollow" target="_parent" class="wpl-liker">
<img src="${ encodeURI( liker.avatar_URL ) }"
alt=""
style="width: 28px; height: 28px;" />
<span></span>
</a>
`;
} else {
element.innerHTML = `
<a href="${ encodeURI( liker.profile_URL ) }" rel="nofollow" target="_parent" class="wpl-liker">
<img src="${ encodeURI( liker.avatar_URL ) }"
alt=""
style="width: 30px; height: 30px; padding-right: 3px;" />
</a>
`;
}
2022-04-02 10:26:41 +02:00
list.append( element );
// Add some extra attributes through native methods, to ensure strings are sanitized.
element.classList.add( liker.css_class );
element.querySelector( 'img' ).alt = liker.name;
2023-12-07 09:44:11 +01:00
if ( newLayout ) {
element.querySelector( 'span' ).innerText = liker.name;
}
2019-11-15 23:26:29 +01:00
} );
2023-12-07 09:44:11 +01:00
const containerStyle = getComputedStyle( container );
const isRtl = containerStyle.direction === 'rtl';
2022-04-02 10:26:41 +02:00
const el = document.querySelector( `*[name='${ data.parent }']` );
const rect = el.getBoundingClientRect();
const win = el.ownerDocument.defaultView;
const offset = {
top: rect.top + win.pageYOffset,
left: rect.left + win.pageXOffset,
};
2019-11-15 23:26:29 +01:00
2023-12-07 09:44:11 +01:00
if ( newLayout ) {
container.style.top = offset.top + data.position.top - 1 + 'px';
if ( isRtl ) {
const visibleAvatarsCount = data && data.likers ? Math.min( data.likers.length, 5 ) : 0;
// 24px is the width of the avatar + 4px is the padding between avatars
container.style.left =
offset.left + data.position.left + 24 * visibleAvatarsCount + 4 + 'px';
container.style.transform = 'translateX(-100%)';
} else {
container.style.left = offset.left + data.position.left + 'px';
}
} else {
container.style.left = offset.left + data.position.left - 10 + 'px';
container.style.top = offset.top + data.position.top - 33 + 'px';
}
2019-11-15 23:26:29 +01:00
2022-04-02 10:26:41 +02:00
const rowLength = Math.floor( data.width / 37 );
let height = Math.ceil( data.likers.length / rowLength ) * 37 + 13;
2019-11-15 23:26:29 +01:00
if ( height > 204 ) {
height = 204;
}
2023-12-07 09:44:11 +01:00
if ( ! newLayout ) {
// Avatars + padding
const containerWidth = rowLength * 37 - 7;
container.style.height = height + 'px';
container.style.width = containerWidth + 'px';
2019-11-15 23:26:29 +01:00
2023-12-07 09:44:11 +01:00
const listWidth = rowLength * 37;
list.style.width = listWidth + 'px';
2019-11-15 23:26:29 +01:00
2023-12-07 09:44:11 +01:00
const scrollbarWidth = list.offsetWidth - list.clientWidth;
if ( scrollbarWidth > 0 ) {
container.style.width = containerWidth + scrollbarWidth + 'px';
list.style.width = listWidth + scrollbarWidth + 'px';
}
2019-11-15 23:26:29 +01:00
}
2023-12-07 09:44:11 +01:00
container.style.display = 'block';
2022-04-02 10:26:41 +02:00
}
2019-11-15 23:26:29 +01:00
}
}
2022-04-02 10:26:41 +02:00
window.addEventListener( 'message', JetpackLikesMessageListener );
2019-11-15 23:26:29 +01:00
2022-04-02 10:26:41 +02:00
document.addEventListener( 'click', e => {
const container = document.querySelector( '#likes-other-gravatars' );
2019-11-15 23:26:29 +01:00
2022-04-02 10:26:41 +02:00
if ( container && ! container.contains( e.target ) ) {
container.style.display = 'none';
2019-11-15 23:26:29 +01:00
}
} );
function JetpackLikesWidgetQueueHandler() {
var wrapperID;
if ( ! jetpackLikesMasterReady ) {
setTimeout( JetpackLikesWidgetQueueHandler, 500 );
return;
}
// Restore widgets to initial unloaded state when they are scrolled out of view.
jetpackUnloadScrolledOutWidgets();
var unloadedWidgetsInView = jetpackGetUnloadedWidgetsInView();
if ( unloadedWidgetsInView.length > 0 ) {
// Grab any unloaded widgets for a batch request
JetpackLikesBatchHandler();
}
for ( var i = 0, length = unloadedWidgetsInView.length; i <= length - 1; i++ ) {
wrapperID = unloadedWidgetsInView[ i ].id;
if ( ! wrapperID ) {
continue;
}
jetpackLoadLikeWidgetIframe( wrapperID );
}
}
function jetpackLoadLikeWidgetIframe( wrapperID ) {
2022-04-02 10:26:41 +02:00
if ( typeof wrapperID === 'undefined' ) {
2019-11-15 23:26:29 +01:00
return;
}
2022-04-02 10:26:41 +02:00
const wrapper = document.querySelector( '#' + wrapperID );
wrapper.querySelectorAll( 'iframe' ).forEach( iFrame => iFrame.remove() );
2019-11-15 23:26:29 +01:00
2022-04-02 10:26:41 +02:00
const placeholder = wrapper.querySelector( '.likes-widget-placeholder' );
2019-11-15 23:26:29 +01:00
// Post like iframe
2022-04-02 10:26:41 +02:00
if ( placeholder && placeholder.classList.contains( 'post-likes-widget-placeholder' ) ) {
const postLikesFrame = document.createElement( 'iframe' );
2019-11-15 23:26:29 +01:00
2021-08-17 08:33:07 +02:00
postLikesFrame.classList.add( 'post-likes-widget', 'jetpack-likes-widget' );
2022-04-02 10:26:41 +02:00
postLikesFrame.name = wrapper.dataset.name;
postLikesFrame.src = wrapper.dataset.src;
2021-08-17 08:33:07 +02:00
postLikesFrame.height = '55px';
postLikesFrame.width = '100%';
2019-11-15 23:26:29 +01:00
postLikesFrame.frameBorder = '0';
postLikesFrame.scrolling = 'no';
2022-04-02 10:26:41 +02:00
postLikesFrame.title = wrapper.dataset.title;
2019-11-15 23:26:29 +01:00
placeholder.after( postLikesFrame );
}
// Comment like iframe
2022-04-02 10:26:41 +02:00
if ( placeholder.classList.contains( 'comment-likes-widget-placeholder' ) ) {
const commentLikesFrame = document.createElement( 'iframe' );
2019-11-15 23:26:29 +01:00
2022-04-02 10:26:41 +02:00
commentLikesFrame.class = 'comment-likes-widget-frame jetpack-likes-widget-frame';
commentLikesFrame.name = wrapper.dataset.name;
commentLikesFrame.src = wrapper.dataset.src;
2019-11-15 23:26:29 +01:00
commentLikesFrame.height = '18px';
commentLikesFrame.width = '100%';
commentLikesFrame.frameBorder = '0';
commentLikesFrame.scrolling = 'no';
2022-04-02 10:26:41 +02:00
wrapper.querySelector( '.comment-like-feedback' ).after( commentLikesFrame );
2019-11-15 23:26:29 +01:00
jetpackCommentLikesLoadedWidgets.push( commentLikesFrame );
}
2022-04-02 10:26:41 +02:00
wrapper.classList.remove( 'jetpack-likes-widget-unloaded' );
wrapper.classList.add( 'jetpack-likes-widget-loading' );
2019-11-15 23:26:29 +01:00
2022-04-02 10:26:41 +02:00
wrapper.querySelector( 'iframe' ).addEventListener( 'load', e => {
2019-11-15 23:26:29 +01:00
JetpackLikesPostMessage(
2022-04-02 10:26:41 +02:00
{ event: 'loadLikeWidget', name: e.target.name, width: e.target.width },
2019-11-15 23:26:29 +01:00
window.frames[ 'likes-master' ]
);
2022-04-02 10:26:41 +02:00
wrapper.classList.remove( 'jetpack-likes-widget-loading' );
wrapper.classList.add( 'jetpack-likes-widget-loaded' );
2019-11-15 23:26:29 +01:00
} );
}
function jetpackGetUnloadedWidgetsInView() {
2022-04-02 10:26:41 +02:00
const unloadedWidgets = document.querySelectorAll( 'div.jetpack-likes-widget-unloaded' );
2019-11-15 23:26:29 +01:00
2022-04-02 10:26:41 +02:00
return [ ...unloadedWidgets ].filter( item => jetpackIsScrolledIntoView( item ) );
2019-11-15 23:26:29 +01:00
}
function jetpackIsScrolledIntoView( element ) {
2022-04-02 10:26:41 +02:00
const top = element.getBoundingClientRect().top;
const bottom = element.getBoundingClientRect().bottom;
2019-11-15 23:26:29 +01:00
// Allow some slack above and bellow the fold with jetpackLikesLookAhead,
// with the aim of hiding the transition from unloaded to loaded widget from users.
return top + jetpackLikesLookAhead >= 0 && bottom <= window.innerHeight + jetpackLikesLookAhead;
}
function jetpackUnloadScrolledOutWidgets() {
2022-04-02 10:26:41 +02:00
for ( let i = jetpackCommentLikesLoadedWidgets.length - 1; i >= 0; i-- ) {
const currentWidgetIframe = jetpackCommentLikesLoadedWidgets[ i ];
2019-11-15 23:26:29 +01:00
if ( ! jetpackIsScrolledIntoView( currentWidgetIframe ) ) {
2022-04-02 10:26:41 +02:00
const widgetWrapper =
currentWidgetIframe &&
currentWidgetIframe.parentElement &&
currentWidgetIframe.parentElement.parentElement;
2019-11-15 23:26:29 +01:00
// Restore parent class to 'unloaded' so this widget can be picked up by queue manager again if needed.
2022-04-02 10:26:41 +02:00
widgetWrapper.classList.remove( 'jetpack-likes-widget-loaded' );
widgetWrapper.classList.remove( 'jetpack-likes-widget-loading' );
widgetWrapper.classList.add( 'jetpack-likes-widget-unloaded' );
2019-11-15 23:26:29 +01:00
// Bring back the loading placeholder into view.
2022-04-02 10:26:41 +02:00
widgetWrapper
.querySelectorAll( '.comment-likes-widget-placeholder' )
.forEach( item => ( item.style.display = 'block' ) );
2019-11-15 23:26:29 +01:00
// Remove it from the list of loaded widgets.
jetpackCommentLikesLoadedWidgets.splice( i, 1 );
// Remove comment like widget iFrame.
2022-04-02 10:26:41 +02:00
currentWidgetIframe.remove();
2019-11-15 23:26:29 +01:00
}
}
}
2020-09-15 14:30:05 +02:00
var jetpackWidgetsDelayedExec = function ( after, fn ) {
2019-11-15 23:26:29 +01:00
var timer;
2020-09-15 14:30:05 +02:00
return function () {
2022-04-02 10:26:41 +02:00
clearTimeout( timer );
2019-11-15 23:26:29 +01:00
timer = setTimeout( fn, after );
};
};
var jetpackOnScrollStopped = jetpackWidgetsDelayedExec( 250, JetpackLikesWidgetQueueHandler );
// Load initial batch of widgets, prior to any scrolling events.
JetpackLikesWidgetQueueHandler();
// Add event listener to execute queue handler after scroll.
window.addEventListener( 'scroll', jetpackOnScrollStopped, true );