2018-12-12 18:37:19 +00:00
|
|
|
/**
|
|
|
|
* @module referencePreview
|
|
|
|
*/
|
|
|
|
|
|
|
|
import { renderPopup } from '../popup/popup';
|
2021-02-18 17:45:37 +00:00
|
|
|
import { createNodeFromTemplate, escapeHTML } from '../templateUtil';
|
2020-12-01 08:47:11 +00:00
|
|
|
|
|
|
|
const templateHTML = `
|
|
|
|
<div class="mwe-popups-container">
|
|
|
|
<div class="mwe-popups-extract">
|
|
|
|
<div class="mwe-popups-scroll">
|
|
|
|
<strong class="mwe-popups-title">
|
|
|
|
<span class="mw-ui-icon mw-ui-icon-element"></span>
|
|
|
|
<span class="mwe-popups-title-placeholder"></span>
|
|
|
|
</strong>
|
|
|
|
<div class="mw-parser-output"></div>
|
|
|
|
</div>
|
2021-02-05 15:47:46 +00:00
|
|
|
<div class="mwe-popups-fade"></div>
|
2020-12-01 08:47:11 +00:00
|
|
|
</div>
|
2021-02-05 15:47:46 +00:00
|
|
|
<footer>
|
|
|
|
<div class="mwe-popups-settings"></div>
|
|
|
|
</footer>
|
2020-12-01 08:47:11 +00:00
|
|
|
</div>`;
|
2018-12-12 18:37:19 +00:00
|
|
|
|
2019-10-24 20:47:43 +00:00
|
|
|
const LOGGING_SCHEMA = 'event.ReferencePreviewsPopups';
|
2019-11-12 15:18:12 +00:00
|
|
|
let isTracking = false;
|
|
|
|
$( () => {
|
|
|
|
if ( mw.config.get( 'wgPopupsReferencePreviews' ) &&
|
|
|
|
navigator.sendBeacon &&
|
|
|
|
mw.config.get( 'wgIsArticle' ) &&
|
|
|
|
!isTracking
|
|
|
|
) {
|
|
|
|
isTracking = true;
|
2019-10-24 20:47:15 +00:00
|
|
|
mw.track( LOGGING_SCHEMA, { action: 'pageview' } );
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
|
2018-12-12 18:37:19 +00:00
|
|
|
/**
|
2019-03-18 12:18:25 +00:00
|
|
|
* @param {ext.popups.ReferencePreviewModel} model
|
2019-02-21 11:40:03 +00:00
|
|
|
* @return {JQuery}
|
2018-12-12 18:37:19 +00:00
|
|
|
*/
|
|
|
|
export function renderReferencePreview(
|
|
|
|
model
|
|
|
|
) {
|
2021-02-18 17:45:37 +00:00
|
|
|
const type = model.referenceType || 'generic';
|
|
|
|
// The following messages are used here:
|
|
|
|
// * popups-refpreview-book
|
|
|
|
// * popups-refpreview-journal
|
|
|
|
// * popups-refpreview-news
|
|
|
|
// * popups-refpreview-note
|
|
|
|
// * popups-refpreview-web
|
|
|
|
let titleMsg = mw.message( `popups-refpreview-${type}` );
|
|
|
|
if ( !titleMsg.exists() ) {
|
|
|
|
titleMsg = mw.message( 'popups-refpreview-reference' );
|
|
|
|
}
|
|
|
|
|
2020-12-01 08:47:11 +00:00
|
|
|
const $el = renderPopup( model.type, createNodeFromTemplate( templateHTML ) );
|
|
|
|
$el.find( '.mwe-popups-title-placeholder' )
|
2021-02-18 17:45:37 +00:00
|
|
|
.replaceWith( escapeHTML( titleMsg.text() ) );
|
2020-12-01 08:47:11 +00:00
|
|
|
// The following classes are used here:
|
2021-02-11 09:15:36 +00:00
|
|
|
// * mw-ui-icon-reference-generic
|
|
|
|
// * mw-ui-icon-reference-book
|
|
|
|
// * mw-ui-icon-reference-journal
|
|
|
|
// * mw-ui-icon-reference-news
|
|
|
|
// * mw-ui-icon-reference-note
|
|
|
|
// * mw-ui-icon-reference-web
|
2021-02-05 15:47:46 +00:00
|
|
|
$el.find( '.mwe-popups-title .mw-ui-icon' )
|
2020-12-01 08:47:11 +00:00
|
|
|
.addClass( `mw-ui-icon-reference-${type}` );
|
|
|
|
$el.find( '.mw-parser-output' )
|
|
|
|
.html( model.extract );
|
2019-01-26 09:56:08 +00:00
|
|
|
|
|
|
|
// Make sure to not destroy existing targets, if any
|
2019-11-26 13:19:35 +00:00
|
|
|
$el.find( '.mwe-popups-extract a[href][class~="external"]:not([target])' ).each( ( i, a ) => {
|
2019-01-26 09:56:08 +00:00
|
|
|
a.target = '_blank';
|
|
|
|
// Don't let the external site access and possibly manipulate window.opener.location
|
2019-08-05 12:42:21 +00:00
|
|
|
a.rel = `${a.rel ? `${a.rel} ` : ''}noopener`;
|
2019-01-26 09:56:08 +00:00
|
|
|
} );
|
|
|
|
|
2020-10-25 18:22:09 +00:00
|
|
|
// We assume elements that benefit from being collapsible are to large for the popup
|
2020-10-29 14:33:53 +00:00
|
|
|
$el.find( '.mw-collapsible' ).replaceWith( $( '<div>' )
|
2020-10-28 16:57:43 +00:00
|
|
|
.addClass( 'mwe-collapsible-placeholder' )
|
|
|
|
.append(
|
|
|
|
$( '<span>' )
|
|
|
|
.addClass( 'mw-ui-icon mw-ui-icon-element mw-ui-icon-infoFilled' ),
|
|
|
|
$( '<div>' )
|
|
|
|
.addClass( 'mwe-collapsible-placeholder-label' )
|
|
|
|
.text( mw.msg( 'popups-refpreview-collapsible-placeholder' ) )
|
|
|
|
)
|
|
|
|
);
|
2020-10-25 18:22:09 +00:00
|
|
|
|
|
|
|
// Undo remaining effects from the jquery.tablesorter.js plugin
|
|
|
|
$el.find( 'table.sortable' ).removeClass( 'sortable jquery-tablesorter' )
|
|
|
|
.find( '.headerSort' ).removeClass( 'headerSort' ).attr( { tabindex: null, title: null } );
|
|
|
|
|
2021-02-05 15:47:46 +00:00
|
|
|
// TODO: Remove when not in Beta any more
|
|
|
|
if ( mw.config.get( 'wgPopupsReferencePreviewsBetaFeature' ) !== true ) {
|
|
|
|
// TODO: Do not remove this but move it up into the templateHTML constant!
|
|
|
|
$el.find( '.mwe-popups-settings' ).append(
|
|
|
|
$( '<a>' )
|
|
|
|
.addClass( 'mwe-popups-settings-icon' )
|
|
|
|
.append(
|
|
|
|
$( '<span>' )
|
|
|
|
.addClass( 'mw-ui-icon mw-ui-icon-element mw-ui-icon-small mw-ui-icon-settings' )
|
|
|
|
)
|
|
|
|
);
|
2021-03-04 15:55:41 +00:00
|
|
|
} else {
|
|
|
|
// Change the styling when there is no content in the footer (to prevent empty space)
|
2021-03-05 12:52:13 +00:00
|
|
|
$el.find( '.mwe-popups-container' ).addClass( 'footer-empty' );
|
2021-02-05 15:47:46 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 15:26:58 +00:00
|
|
|
if ( isTracking ) {
|
|
|
|
$el.find( '.mw-parser-output' ).on( 'click', 'a', () => {
|
2019-10-24 20:47:43 +00:00
|
|
|
mw.track( LOGGING_SCHEMA, {
|
2019-09-23 15:26:58 +00:00
|
|
|
action: 'clickedReferencePreviewsContentLink'
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
2020-10-27 13:24:40 +00:00
|
|
|
$el.find( '.mwe-popups-scroll' ).on( 'scroll', function ( e ) {
|
2019-02-28 17:50:36 +00:00
|
|
|
const element = e.target,
|
2019-05-20 07:35:20 +00:00
|
|
|
// We are dealing with floating point numbers here when the page is zoomed!
|
|
|
|
scrolledToBottom = element.scrollTop >= element.scrollHeight - element.clientHeight - 1;
|
2019-02-28 17:50:36 +00:00
|
|
|
|
2019-09-23 15:26:58 +00:00
|
|
|
if ( isTracking ) {
|
|
|
|
if ( !element.isOpenRecorded ) {
|
2019-10-24 20:47:43 +00:00
|
|
|
mw.track( LOGGING_SCHEMA, {
|
2019-09-23 15:26:58 +00:00
|
|
|
action: 'poppedOpen',
|
|
|
|
scrollbarsPresent: element.scrollHeight > element.clientHeight
|
|
|
|
} );
|
|
|
|
element.isOpenRecorded = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
element.scrollTop > 0 &&
|
|
|
|
!element.isScrollRecorded
|
|
|
|
) {
|
2019-10-24 20:47:43 +00:00
|
|
|
mw.track( LOGGING_SCHEMA, {
|
2019-09-23 15:26:58 +00:00
|
|
|
action: 'scrolled'
|
|
|
|
} );
|
|
|
|
element.isScrollRecorded = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-28 17:50:36 +00:00
|
|
|
if ( !scrolledToBottom && element.isScrolling ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-04-26 14:00:39 +00:00
|
|
|
const $extract = $( element ).parent(),
|
2019-10-04 08:22:12 +00:00
|
|
|
hasHorizontalScroll = element.scrollWidth > element.clientWidth,
|
|
|
|
scrollbarHeight = element.offsetHeight - element.clientHeight,
|
|
|
|
hasVerticalScroll = element.scrollHeight > element.clientHeight,
|
|
|
|
scrollbarWidth = element.offsetWidth - element.clientWidth;
|
|
|
|
$extract.find( '.mwe-popups-fade' ).css( {
|
|
|
|
bottom: hasHorizontalScroll ? `${scrollbarHeight}px` : 0,
|
|
|
|
right: hasVerticalScroll ? `${scrollbarWidth}px` : 0
|
|
|
|
} );
|
2019-04-26 11:35:27 +00:00
|
|
|
|
2019-02-28 17:50:36 +00:00
|
|
|
element.isScrolling = !scrolledToBottom;
|
2019-04-26 11:35:27 +00:00
|
|
|
$extract.toggleClass( 'mwe-popups-fade-out', element.isScrolling );
|
2019-02-28 17:50:36 +00:00
|
|
|
} );
|
|
|
|
|
2019-01-26 09:56:08 +00:00
|
|
|
return $el;
|
2018-12-12 18:37:19 +00:00
|
|
|
}
|