mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Popups
synced 2024-11-23 23:24:39 +00:00
Parse template HTML only once, as HTML parsing is expensive
When creating a popup, clone the previously created DOM element and populate the attributes and content. Ideally this would be done with a template element, but since IE11 is still supported this is not possible. Change-Id: I347615cf1f613d97d767d60627b13b6b3ff9c762 Bug: T269338
This commit is contained in:
parent
ff2ba9ebf5
commit
497eb631d1
|
@ -67,7 +67,7 @@
|
|||
"bundlesize": [
|
||||
{
|
||||
"path": "resources/dist/index.js",
|
||||
"maxSize": "13.61kB"
|
||||
"maxSize": "13.8kB"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
BIN
resources/dist/index.js
vendored
BIN
resources/dist/index.js
vendored
Binary file not shown.
BIN
resources/dist/index.js.map.json
vendored
BIN
resources/dist/index.js.map.json
vendored
Binary file not shown.
|
@ -3,9 +3,20 @@
|
|||
*/
|
||||
|
||||
import { renderPopup } from '../popup/popup';
|
||||
import { escapeHTML } from '../templateUtil';
|
||||
import { createNodeFromTemplate } from '../templateUtil';
|
||||
|
||||
const defaultExtractWidth = 215;
|
||||
const templateHTML = `
|
||||
<div>
|
||||
<a class="mwe-popups-discreet"></a>
|
||||
<a class="mwe-popups-extract"></a>
|
||||
<footer>
|
||||
<a class="mwe-popups-settings-icon">
|
||||
<span class="mw-ui-icon mw-ui-icon-element mw-ui-icon-small mw-ui-icon-settings"></span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
`;
|
||||
|
||||
/**
|
||||
* @param {ext.popups.PagePreviewModel} model
|
||||
|
@ -15,24 +26,19 @@ const defaultExtractWidth = 215;
|
|||
export function renderPagePreview(
|
||||
model, thumbnail
|
||||
) {
|
||||
const url = escapeHTML( model.url ),
|
||||
languageCode = escapeHTML( model.languageCode ),
|
||||
languageDirection = escapeHTML( model.languageDirection );
|
||||
const $el = renderPopup( model.type, createNodeFromTemplate( templateHTML ) );
|
||||
|
||||
const $el = renderPopup( model.type,
|
||||
`
|
||||
${thumbnail ? `<a href='${url}' class='mwe-popups-discreet'></a>` : ''}
|
||||
<a dir='${languageDirection}' lang='${languageCode}' class='mwe-popups-extract' href='${url}'></a>
|
||||
<footer>
|
||||
<a class='mwe-popups-settings-icon'>
|
||||
<span class="mw-ui-icon mw-ui-icon-element mw-ui-icon-small mw-ui-icon-settings"></span>
|
||||
</a>
|
||||
</footer>
|
||||
`
|
||||
);
|
||||
$el.find( '.mwe-popups-discreet, .mwe-popups-extract' )
|
||||
.attr( 'href', model.url );
|
||||
|
||||
$el.find( '.mwe-popups-extract' )
|
||||
.attr( 'dir', model.languageDirection )
|
||||
.attr( 'lang', model.languageCode );
|
||||
|
||||
if ( thumbnail ) {
|
||||
$el.find( '.mwe-popups-discreet' ).append( thumbnail.el );
|
||||
} else {
|
||||
$el.find( '.mwe-popups-discreet' ).remove();
|
||||
}
|
||||
|
||||
if ( model.extract ) {
|
||||
|
|
|
@ -2,19 +2,26 @@
|
|||
* @module popup
|
||||
*/
|
||||
|
||||
import { escapeHTML } from '../templateUtil';
|
||||
import { createNodeFromTemplate } from '../templateUtil';
|
||||
|
||||
const templateHTML = `
|
||||
<div class="mwe-popups" aria-hidden></div>
|
||||
`;
|
||||
/**
|
||||
* @param {ext.popups.previewTypes} type
|
||||
* @param {string} html HTML string.
|
||||
* @param {Element} element The contents of the popup.
|
||||
* @return {JQuery}
|
||||
*/
|
||||
export function renderPopup( type, html ) {
|
||||
type = escapeHTML( type );
|
||||
|
||||
return $( $.parseHTML( `
|
||||
<div class='mwe-popups mwe-popups-type-${type}' aria-hidden>
|
||||
<div class='mwe-popups-container'>${html}</div>
|
||||
</div>
|
||||
`.trim() ) );
|
||||
export function renderPopup( type, container ) {
|
||||
const element = createNodeFromTemplate( templateHTML );
|
||||
// The following classes are used here:
|
||||
// * mwe-popups-type-reference
|
||||
// * mwe-popups-type-unknown
|
||||
// * mwe-popups-type-generic
|
||||
// * mwe-popups-type-disambiguation
|
||||
element.className = `mwe-popups mwe-popups-type-${type}`;
|
||||
container.className = 'mwe-popups-container';
|
||||
element.appendChild( container );
|
||||
return $( element );
|
||||
}
|
||||
|
|
|
@ -3,7 +3,20 @@
|
|||
*/
|
||||
|
||||
import { renderPopup } from '../popup/popup';
|
||||
import { escapeHTML } from '../templateUtil';
|
||||
import { createNodeFromTemplate, escapeHTML } from '../templateUtil';
|
||||
|
||||
const templateHTML = `
|
||||
<div class="mwe-popups-container">
|
||||
<div class="mw-ui-icon mw-ui-icon-element"></div>
|
||||
<strong class="mwe-popups-title"></strong>
|
||||
<a class="mwe-popups-extract">
|
||||
<span class="mwe-popups-message"></span>
|
||||
</a>
|
||||
<footer>
|
||||
<a class="mwe-popups-read-link"></a>
|
||||
</footer>
|
||||
</div>
|
||||
`;
|
||||
|
||||
/**
|
||||
* @param {ext.popups.PagePreviewModel} model
|
||||
|
@ -15,22 +28,24 @@ import { escapeHTML } from '../templateUtil';
|
|||
export function renderPreview(
|
||||
model, showTitle, extractMsg, linkMsg
|
||||
) {
|
||||
const title = escapeHTML( model.title ),
|
||||
url = escapeHTML( model.url ),
|
||||
type = escapeHTML( model.type );
|
||||
extractMsg = escapeHTML( extractMsg );
|
||||
linkMsg = escapeHTML( linkMsg );
|
||||
const $popup = renderPopup( model.type, createNodeFromTemplate( templateHTML ) );
|
||||
|
||||
return renderPopup( model.type,
|
||||
`
|
||||
<div class='mw-ui-icon mw-ui-icon-element mw-ui-icon-preview-${type}'></div>
|
||||
${showTitle ? `<strong class='mwe-popups-title'>${title}</strong>` : ''}
|
||||
<a href='${url}' class='mwe-popups-extract'>
|
||||
<span class='mwe-popups-message'>${extractMsg}</span>
|
||||
</a>
|
||||
<footer>
|
||||
<a href='${url}' class='mwe-popups-read-link'>${linkMsg}</a>
|
||||
</footer>
|
||||
`
|
||||
);
|
||||
// The following classes are used here:
|
||||
// * mw-icon-preview-reference
|
||||
// * mw-icon-preview-unknown
|
||||
// * mw-icon-preview-generic
|
||||
// * mw-icon-preview-disambiguation
|
||||
$popup.find( '.mw-ui-icon ' ).addClass( `mw-ui-icon-preview-${model.type}` );
|
||||
$popup.find( '.mwe-popups-extract' ).attr( 'href', model.url );
|
||||
$popup.find( '.mwe-popups-message' ).html( escapeHTML( extractMsg ) );
|
||||
$popup.find( '.mwe-popups-read-link' )
|
||||
.html( escapeHTML( linkMsg ) )
|
||||
.attr( 'href', model.url );
|
||||
if ( showTitle ) {
|
||||
$popup.find( '.mwe-popups-title' ).html( escapeHTML( model.title ) );
|
||||
} else {
|
||||
$popup.find( '.mwe-popups-title' ).remove();
|
||||
}
|
||||
|
||||
return $popup;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,21 @@
|
|||
|
||||
import { renderPopup } from '../popup/popup';
|
||||
import { escapeHTML } from '../templateUtil';
|
||||
import { createNodeFromTemplate } from '../templateUtil';
|
||||
|
||||
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>
|
||||
<div class="mwe-popups-fade" />
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// Known citation type strings currently supported with icons and messages.
|
||||
const KNOWN_TYPES = [ 'book', 'journal', 'news', 'web' ];
|
||||
|
@ -37,21 +52,18 @@ export function renderReferencePreview(
|
|||
// * popups-refpreview-reference
|
||||
// * popups-refpreview-web
|
||||
title = escapeHTML( mw.msg( titleMsg ) );
|
||||
|
||||
const $el = renderPopup( model.type,
|
||||
`
|
||||
<div class='mwe-popups-extract'>
|
||||
<div class='mwe-popups-scroll'>
|
||||
<strong class='mwe-popups-title'>
|
||||
<span class='mw-ui-icon mw-ui-icon-element mw-ui-icon-reference-${type}'></span>
|
||||
${title}
|
||||
</strong>
|
||||
<div class='mw-parser-output'>${model.extract}</div>
|
||||
</div>
|
||||
<div class='mwe-popups-fade' />
|
||||
</div>
|
||||
`
|
||||
);
|
||||
const $el = renderPopup( model.type, createNodeFromTemplate( templateHTML ) );
|
||||
$el.find( '.mwe-popups-title-placeholder' )
|
||||
.replaceWith( title );
|
||||
// The following classes are used here:
|
||||
// * mw-icon-reference-reference
|
||||
// * mw-icon-reference-unknown
|
||||
// * mw-icon-reference-generic
|
||||
// * mw-icon-reference-disambiguation
|
||||
$el.find( '.mw-ui-icon' )
|
||||
.addClass( `mw-ui-icon-reference-${type}` );
|
||||
$el.find( '.mw-parser-output' )
|
||||
.html( model.extract );
|
||||
|
||||
// Make sure to not destroy existing targets, if any
|
||||
$el.find( '.mwe-popups-extract a[href][class~="external"]:not([target])' ).each( ( i, a ) => {
|
||||
|
|
|
@ -9,3 +9,19 @@
|
|||
export function escapeHTML( str ) {
|
||||
return mw.html.escape( str );
|
||||
}
|
||||
|
||||
const templates = {};
|
||||
/**
|
||||
* @param {string} html markup of the template
|
||||
* @return {Element} a cloned root element of the template
|
||||
*/
|
||||
export function createNodeFromTemplate( html ) {
|
||||
if ( !templates[ html ] ) {
|
||||
// TODO: use <template> element when IE11 dies
|
||||
const div = document.createElement( 'div' );
|
||||
div.innerHTML = html;
|
||||
templates[ html ] = div.firstElementChild;
|
||||
}
|
||||
|
||||
return templates[ html ].cloneNode( true );
|
||||
}
|
||||
|
|
|
@ -112,8 +112,8 @@ module.exports = ( env, argv ) => ( {
|
|||
// Minified uncompressed size limits for chunks / assets and entrypoints. Keep these numbers
|
||||
// up-to-date and rounded to the nearest 10th of a kibibyte so that code sizing costs are
|
||||
// well understood. Related to bundlesize minified, gzipped compressed file size tests.
|
||||
maxAssetSize: 41.5 * 1024,
|
||||
maxEntrypointSize: 41.5 * 1024,
|
||||
maxAssetSize: 41.8 * 1024,
|
||||
maxEntrypointSize: 41.8 * 1024,
|
||||
|
||||
// The default filter excludes map files but we rename ours.
|
||||
assetFilter: ( filename ) => !filename.endsWith( srcMapExt )
|
||||
|
|
Loading…
Reference in a new issue