{"version":3,"file":"referencePreviews.js","mappings":"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","sources":["/w/extensions/Popups/./src/canSaveToUserPreferences.js","/w/extensions/Popups/./src/ui/pointer-mask.svg","/w/extensions/Popups/webpack/bootstrap","/w/extensions/Popups/./src/ext.popups.referencePreviews/constants.js","/w/extensions/Popups/./src/ext.popups.referencePreviews/isReferencePreviewsEnabled.js","/w/extensions/Popups/./src/ext.popups.referencePreviews/referencePreviews.js","/w/extensions/Popups/./src/ui/templates/templateUtil.js","/w/extensions/Popups/./src/ui/templates/popup/popup.js","/w/extensions/Popups/./src/ext.popups.referencePreviews/createReferencePreview.js","/w/extensions/Popups/./src/constants.js","/w/extensions/Popups/./src/bracketedPixelRatio.js","/w/extensions/Popups/./src/ui/renderer.js","/w/extensions/Popups/./src/ext.popups.referencePreviews/setUserConfigFlags.js","/w/extensions/Popups/./src/preview/model.js","/w/extensions/Popups/./src/userSettings.js","/w/extensions/Popups/./src/ext.popups.referencePreviews/index.js","/w/extensions/Popups/./src/ext.popups.referencePreviews/createReferenceGateway.js","/w/extensions/Popups/./src/gateway/index.js"],"sourcesContent":["/**\n * Can the current user save to user preferences?\n *\n * @param {User} user\n * @return {boolean}\n */\nconst canSaveToUserPreferences = ( user ) => {\n\treturn !user.isAnon() && user.isNamed();\n};\n\nmodule.exports = canSaveToUserPreferences;\n","module.exports = \"\"","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","export const TYPE_REFERENCE = 'reference';\nexport const FETCH_DELAY_REFERENCE_TYPE = 150; // ms.\n","import { TYPE_REFERENCE } from './constants.js';\n\n/**\n * @module isReferencePreviewsEnabled\n */\nconst canSaveToUserPreferences = require( '../canSaveToUserPreferences.js' );\n\n/**\n * Given the global state of the application, creates a function that gets\n * whether or not the user should have Reference Previews enabled.\n *\n * @param {mw.User} user The `mw.user` singleton instance\n * @param {Object} userSettings An object returned by `userSettings.js`\n * @param {mw.Map} config\n *\n * @return {boolean|null} Null when there is no way the popup type can be enabled at run-time.\n */\nexport default function isReferencePreviewsEnabled( user, userSettings, config ) {\n\t// TODO: This and the final `mw.user.options` check are currently redundant. Only this here\n\t// should be removed when the wgPopupsReferencePreviews feature flag is not needed any more.\n\tif ( !config.get( 'wgPopupsReferencePreviews' ) ) {\n\t\treturn null;\n\t}\n\n\t// T265872: Unavailable when in conflict with (one of the) reference tooltips gadgets.\n\tif ( config.get( 'wgPopupsConflictsWithRefTooltipsGadget' ) ||\n\t\tconfig.get( 'wgPopupsConflictsWithNavPopupGadget' ) ||\n\t\t// T243822: Temporarily disabled in the mobile skin\n\t\tconfig.get( 'skin' ) === 'minerva'\n\t) {\n\t\treturn null;\n\t}\n\n\t// For anonymous users, the code loads always, but the feature can be toggled at run-time via\n\t// local storage.\n\tif ( !canSaveToUserPreferences( user ) ) {\n\t\treturn userSettings.isPreviewTypeEnabled( TYPE_REFERENCE );\n\t}\n\n\t// Registered users never can enable popup types at run-time.\n\treturn user.options.get( 'popups-reference-previews' ) === '1' ? true : null;\n}\n","let isTracking = false;\n\nexport const LOGGING_SCHEMA = 'event.ReferencePreviewsPopups';\n\n/**\n * Run once the preview is initialized.\n */\nexport function initReferencePreviewsInstrumentation() {\n\tif ( mw.config.get( 'wgPopupsReferencePreviews' ) &&\n\t\tnavigator.sendBeacon &&\n\t\tmw.config.get( 'wgIsArticle' ) &&\n\t\t!isTracking\n\t) {\n\t\tisTracking = true;\n\t\tmw.track( LOGGING_SCHEMA, { action: 'pageview' } );\n\t}\n}\n\nexport function isTrackingEnabled() {\n\treturn isTracking;\n}\n","/**\n * @module templateUtil\n */\n\n/**\n * @param {string} str\n * @return {string} The string with any HTML entities escaped.\n */\nexport function escapeHTML( str ) {\n\treturn mw.html.escape( str );\n}\n\nconst templates = {};\n/**\n * @param {string} html markup of the template\n * @return {HTMLElement} a cloned root element of the template\n */\nexport function createNodeFromTemplate( html ) {\n\tif ( !templates[ html ] ) {\n\t\t// TODO: use element when IE11 dies\n\t\tconst div = document.createElement( 'div' );\n\t\tdiv.innerHTML = html;\n\t\ttemplates[ html ] = div.firstElementChild;\n\t}\n\n\treturn templates[ html ].cloneNode( true );\n}\n","/**\n * @module popup\n */\n\nimport { createNodeFromTemplate } from '../templateUtil';\n\nconst templateHTML = `\n\t\n`;\n/**\n * @param {ext.popups.previewTypes} type\n * @param {HTMLElement} element The contents of the popup.\n * @return {HTMLElement}\n */\n\nexport function renderPopup( type, container ) {\n\tconst element = createNodeFromTemplate( templateHTML );\n\t// The following classes are used here:\n\t// * mwe-popups-type-reference\n\t// * mwe-popups-type-unknown\n\t// * mwe-popups-type-generic\n\t// * mwe-popups-type-disambiguation\n\telement.className = `mwe-popups mwe-popups-type-${ type }`;\n\tcontainer.className = 'mwe-popups-container';\n\telement.appendChild( container );\n\treturn element;\n}\n","/**\n * @module referencePreview\n */\nimport { isTrackingEnabled, LOGGING_SCHEMA } from './referencePreviews';\nimport { renderPopup } from '../ui/templates/popup/popup';\nimport { createNodeFromTemplate, escapeHTML } from '../ui/templates/templateUtil';\n\nconst templateHTML = `\n
\n
\n
\n \n \n \n \n \n
\n \n
\n\t\n
`;\n\n/**\n * @param {HTMLElement} node\n * @param {HTMLElement|string} htmlOrOtherNode\n */\nconst replaceWith = ( node, htmlOrOtherNode ) => {\n\tif ( typeof htmlOrOtherNode === 'string' ) {\n\t\tnode.insertAdjacentHTML( 'afterend', htmlOrOtherNode );\n\t} else {\n\t\tnode.parentNode.appendChild( htmlOrOtherNode );\n\t}\n\tnode.remove();\n};\n\n/**\n * @param {ext.popups.ReferencePreviewModel} model\n * @return {jQuery}\n */\nfunction renderReferencePreview(\n\tmodel\n) {\n\tconst type = model.referenceType || 'generic';\n\t// The following messages are used here:\n\t// * popups-refpreview-book\n\t// * popups-refpreview-journal\n\t// * popups-refpreview-news\n\t// * popups-refpreview-note\n\t// * popups-refpreview-web\n\tlet titleMsg = mw.message( `popups-refpreview-${ type }` );\n\tif ( !titleMsg.exists() ) {\n\t\ttitleMsg = mw.message( 'popups-refpreview-reference' );\n\t}\n\n\tconst el = renderPopup( model.type, createNodeFromTemplate( templateHTML ) );\n\treplaceWith( el.querySelector( '.mwe-popups-title-placeholder' ), escapeHTML( titleMsg.text() ) );\n\t// The following classes are used here:\n\t// * popups-icon--reference-generic\n\t// * popups-icon--reference-book\n\t// * popups-icon--reference-journal\n\t// * popups-icon--reference-news\n\t// * popups-icon--reference-note\n\t// * popups-icon--reference-web\n\tel.querySelector( '.mwe-popups-title .popups-icon' )\n\t\t.classList.add( `popups-icon--reference-${ type }` );\n\tel.querySelector( '.mw-parser-output' )\n\t\t.innerHTML = model.extract;\n\n\t// Make sure to not destroy existing targets, if any\n\tArray.prototype.forEach.call(\n\t\tel.querySelectorAll( '.mwe-popups-extract a[href][class~=\"external\"]:not([target])' ),\n\t\t( a ) => {\n\t\t\ta.target = '_blank';\n\t\t\t// Don't let the external site access and possibly manipulate window.opener.location\n\t\t\ta.rel = `${ a.rel ? `${ a.rel } ` : '' }noopener`;\n\t\t}\n\t);\n\n\t// We assume elements that benefit from being collapsible are to large for the popup\n\tArray.prototype.forEach.call( el.querySelectorAll( '.mw-collapsible' ), ( node ) => {\n\t\tconst otherNode = document.createElement( 'div' );\n\t\totherNode.classList.add( 'mwe-collapsible-placeholder' );\n\t\tconst icon = document.createElement( 'span' );\n\t\ticon.classList.add( 'popups-icon', 'popups-icon--infoFilled' );\n\t\tconst label = document.createElement( 'span' );\n\t\tlabel.classList.add( 'mwe-collapsible-placeholder-label' );\n\t\tlabel.textContent = mw.msg( 'popups-refpreview-collapsible-placeholder' );\n\t\totherNode.appendChild( icon );\n\t\totherNode.appendChild( label );\n\t\treplaceWith( node, otherNode );\n\t} );\n\n\t// Undo remaining effects from the jquery.tablesorter.js plugin\n\tconst undoHeaderSort = ( headerSort ) => {\n\t\theaderSort.classList.remove( 'headerSort' );\n\t\theaderSort.removeAttribute( 'tabindex' );\n\t\theaderSort.removeAttribute( 'title' );\n\t};\n\tArray.prototype.forEach.call( el.querySelectorAll( 'table.sortable' ), ( node ) => {\n\t\tnode.classList.remove( 'sortable', 'jquery-tablesorter' );\n\t\tArray.prototype.forEach.call( node.querySelectorAll( '.headerSort' ), undoHeaderSort );\n\t} );\n\n\t// TODO: Do not remove this but move it up into the templateHTML constant!\n\tconst settingsButton = document.createElement( 'a' );\n\tsettingsButton.classList.add( 'cdx-button', 'cdx-button--fake-button', 'cdx-button--fake-button--enabled', 'cdx-button--weight-quiet', 'cdx-button--icon-only', 'mwe-popups-settings-button' );\n\tconst settingsIcon = document.createElement( 'span' );\n\tsettingsIcon.classList.add( 'popups-icon', 'popups-icon--size-small', 'popups-icon--settings' );\n\tconst settingsButtonLabel = document.createElement( 'span' );\n\tsettingsButtonLabel.textContent = mw.msg( 'popups-settings-icon-gear-title' );\n\tsettingsButton.append( settingsIcon );\n\tsettingsButton.append( settingsButtonLabel );\n\tel.querySelector( '.mwe-popups-settings' ).appendChild( settingsButton );\n\n\tif ( isTrackingEnabled() ) {\n\t\tel.querySelector( '.mw-parser-output' ).addEventListener( 'click', ( ev ) => {\n\t\t\tif ( !ev.target.matches( 'a' ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmw.track( LOGGING_SCHEMA, {\n\t\t\t\taction: 'clickedReferencePreviewsContentLink'\n\t\t\t} );\n\t\t} );\n\t}\n\n\tel.querySelector( '.mwe-popups-scroll' ).addEventListener( 'scroll', function ( e ) {\n\t\tconst element = e.target,\n\t\t\t// We are dealing with floating point numbers here when the page is zoomed!\n\t\t\tscrolledToBottom = element.scrollTop >= element.scrollHeight - element.clientHeight - 1;\n\n\t\tif ( isTrackingEnabled() ) {\n\t\t\tif ( !element.isOpenRecorded ) {\n\t\t\t\tmw.track( LOGGING_SCHEMA, {\n\t\t\t\t\taction: 'poppedOpen',\n\t\t\t\t\tscrollbarsPresent: element.scrollHeight > element.clientHeight\n\t\t\t\t} );\n\t\t\t\telement.isOpenRecorded = true;\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\telement.scrollTop > 0 &&\n\t\t\t\t!element.isScrollRecorded\n\t\t\t) {\n\t\t\t\tmw.track( LOGGING_SCHEMA, {\n\t\t\t\t\taction: 'scrolled'\n\t\t\t\t} );\n\t\t\t\telement.isScrollRecorded = true;\n\t\t\t}\n\t\t}\n\n\t\tif ( !scrolledToBottom && element.isScrolling ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst extract = element.parentNode,\n\t\t\thasHorizontalScroll = element.scrollWidth > element.clientWidth,\n\t\t\tscrollbarHeight = element.offsetHeight - element.clientHeight,\n\t\t\thasVerticalScroll = element.scrollHeight > element.clientHeight,\n\t\t\tscrollbarWidth = element.offsetWidth - element.clientWidth;\n\t\tconst fade = extract.querySelector( '.mwe-popups-fade' );\n\t\tfade.style.bottom = hasHorizontalScroll ? `${ scrollbarHeight }px` : 0;\n\t\tfade.style.right = hasVerticalScroll ? `${ scrollbarWidth }px` : 0;\n\n\t\telement.isScrolling = !scrolledToBottom;\n\t\textract.classList.toggle( 'mwe-popups-fade-out', element.isScrolling );\n\t\textract.setAttribute( 'lang', mw.config.get( 'wgPageContentLanguage' ) );\n\t} );\n\n\treturn el;\n}\n\n/**\n * @param {ext.popups.ReferencePreviewModel} model\n * @return {ext.popups.Preview}\n */\nexport default function createReferencePreview( model ) {\n\treturn {\n\t\tel: renderReferencePreview( model ),\n\t\thasThumbnail: false,\n\t\tisTall: false\n\t};\n}\n","import bracketedPixelRatio from './bracketedPixelRatio';\n\nconst bpr = bracketedPixelRatio();\n\n// See the following for context around this value.\n//\n// * https://phabricator.wikimedia.org/T161284\n// * https://phabricator.wikimedia.org/T70861#3129780\nexport const FETCH_START_DELAY = 150; // ms.\n// The delay after which a FETCH_COMPLETE action should be dispatched.\n//\n// If the API endpoint responds faster than 350 ms (or, say, the API\n// response is served from the UA's cache), then we introduce a delay of\n// 350 ms - t to make the preview delay consistent to the user. The total\n// delay from start to finish is 500 ms.\nexport const FETCH_COMPLETE_TARGET_DELAY = 350 + FETCH_START_DELAY; // ms.\n// The minimum time a preview must be open before we judge it\n// has been seen.\n// See https://phabricator.wikimedia.org/T184793\nexport const PREVIEW_SEEN_DURATION = 1000; // ms\nexport const ABANDON_END_DELAY = 300;\n\nexport default {\n\tBRACKETED_DEVICE_PIXEL_RATIO: bpr,\n\t// See https://phabricator.wikimedia.org/T272169: requesting a larger thumbnail to avoid bluriness\n\tTHUMBNAIL_SIZE: 320 * Math.max( bpr, 1.5 ),\n\tEXTRACT_LENGTH: 525\n};\n","/**\n * @module bracketedPixelRatio\n */\n\n/**\n * Normalizes a user's device pixel ratio to either 1, 1.5, or 2.\n *\n * This is important when the server resizes images on the fly in order to\n * reduce the work it has to do for device pixel ratios that deviate from a\n * set of common ratios.\n *\n * Adapted from mediawiki/core /resources/src/jquery/jquery.hidpi.js\n *\n * @param {number} [dpr=window.devicePixelRatio]\n * @return {number} The bracketed device pixel ratio\n */\nexport default function ( dpr = window.devicePixelRatio ) {\n\tif ( !dpr ) {\n\t\t// Probably legacy browser so assume 1\n\t\treturn 1;\n\t}\n\n\tif ( dpr > 1.5 ) {\n\t\treturn 2;\n\t}\n\n\tif ( dpr > 1 ) {\n\t\treturn 1.5;\n\t}\n\n\treturn 1;\n}\n","/**\n * @module renderer\n */\n\nimport wait from '../wait';\nimport pointerMaskSVG from './pointer-mask.svg';\nimport { SIZES, createThumbnail } from './thumbnail';\nimport { renderPreview } from './templates/preview/preview';\nimport { renderPagePreview } from './templates/pagePreview/pagePreview';\n\nconst landscapePopupWidth = 450,\n\tportraitPopupWidth = 320,\n\tpointerSize = 8, // Height of the pointer.\n\tmaxLinkWidthForCenteredPointer = 28; // Link with roughly < 4 chars.\n\nexport { pointerSize, landscapePopupWidth, portraitPopupWidth }; // for use in storybook\n\n/**\n * @typedef {Object} ext.popups.Measures\n * @property {number} pageX\n * @property {number} pageY\n * @property {number} clientY\n * @property {ClientRectList} clientRects list of rectangles defined by\n * four edges\n * @property {Object} offset\n * @property {number} width\n * @property {number} height\n * @property {number} scrollTop\n * @property {number} windowWidth\n * @property {number} windowHeight\n */\n\n/**\n * Extracted from `mw.popups.createSVGMasks`. This is just an SVG mask to point\n * or \"point\" at the link that's hovered over. The \"pointer\" appears to be cut\n * out of the image itself:\n * _______ link\n * | | _/\\_____ _/\\____ <-- Pointer pointing at link\n * | :-] | + |xxxxxxx = | :-] |\n * |_______| |xxxxxxx |_______|\n * :\n * Thumbnail Pointer Page preview\n * image clip-path bubble w/ pointer\n *\n * SVG masks are used in place of CSS masks for browser support issues (see\n * https://caniuse.com/#feat=css-masks).\n *\n * @private\n * @param {Object} container DOM object to which pointer masks are appended\n */\nexport function createPointerMasks( container ) {\n\tconst node = document.createElement( 'div' );\n\tnode.setAttribute( 'id', 'mwe-popups-svg' );\n\tnode.innerHTML = pointerMaskSVG;\n\tcontainer.appendChild( node );\n}\n\n/**\n * Initializes the renderer.\n *\n */\nexport function init() {\n\tif ( !supportsCSSClipPath() ) {\n\t\tcreatePointerMasks( document.body );\n\t}\n}\n\n/**\n * The model of how a view is rendered, which is constructed from a response\n * from the gateway.\n *\n * TODO: Rename `isTall` to `isPortrait`.\n *\n * @typedef {Object} ext.popups.Preview\n * @property {jQuery} el\n * @property {boolean} hasThumbnail\n * @property {Object} thumbnail\n * @property {boolean} isTall Sugar around\n * `preview.hasThumbnail && thumbnail.isTall`\n */\n\n/**\n * Renders a preview given data from the {@link gateway Gateway}.\n * The preview is rendered and added to the DOM but will remain hidden until\n * the `show` method is called.\n *\n * Previews are rendered at:\n *\n * # The position of the mouse when the user dwells on the link with their\n * mouse.\n * # The centermost point of the link when the user dwells on the link with\n * their keyboard or other assistive device.\n *\n * Since the content of the preview doesn't change but its position might, we\n * distinguish between \"rendering\" - generating HTML from a MediaWiki API\n * response - and \"showing/hiding\" - positioning the layout and changing its\n * orientation, if necessary.\n *\n * @param {ext.popups.PreviewModel} model\n * @return {ext.popups.Preview}\n */\nexport function render( model ) {\n\tconst preview = createPreviewWithType( model );\n\n\treturn {\n\t\t/**\n\t\t * Shows the preview given an event representing the user's interaction\n\t\t * with the active link, e.g. an instance of\n\t\t * [MouseEvent](https://developer.mozilla.org/en/docs/Web/API/MouseEvent).\n\t\t *\n\t\t * See `show` for more detail.\n\t\t *\n\t\t * @param {Event} event\n\t\t * @param {Object} boundActions The\n\t\t * [bound action creators](http://redux.js.org/docs/api/bindActionCreators.html)\n\t\t * that were (likely) created in [boot.js](./boot.js).\n\t\t * @param {string} token The unique token representing the link interaction\n\t\t * that resulted in showing the preview\n\t\t * @return {jQuery.Promise}\n\t\t */\n\t\tshow( event, boundActions, token ) {\n\t\t\treturn show(\n\t\t\t\tpreview, event, event.target, boundActions, token,\n\t\t\t\tdocument.body, document.documentElement.getAttribute( 'dir' )\n\t\t\t);\n\t\t},\n\n\t\t/**\n\t\t * Hides the preview.\n\t\t *\n\t\t * See `hide` for more detail.\n\t\t *\n\t\t * @return {jQuery.Promise}\n\t\t */\n\t\thide() {\n\t\t\treturn hide( preview );\n\t\t}\n\t};\n}\n\nlet renderers = {};\nconst renderersMeta = {};\n\n/**\n * @param {string} type\n * @param {function( ext.popups.PreviewModel ): ext.popups.Preview} [previewFn]\n * @param {boolean} [doNotRequireSummary] does this type require a summary to be renderable?\n *\n */\nexport function registerPreviewUI( type, previewFn, doNotRequireSummary ) {\n\trenderers[ type ] = previewFn || createPagePreview;\n\trenderersMeta[ type ] = {\n\t\trequireSummary: !doNotRequireSummary\n\t};\n}\n\n/**\n * Check whether this render type requires a summary to be rendered.\n *\n * @param {string} type of preview\n * @return {boolean}\n */\nexport function requiresSummary( type ) {\n\tconst meta = renderersMeta[ type ] || { requireSummary: true };\n\treturn meta.requireSummary;\n}\n\n/**\n * Creates an instance of a Preview based on\n * the type property of the PreviewModel\n *\n * @param {ext.popups.PreviewModel} model\n * @return {ext.popups.Preview}\n */\nexport function createPreviewWithType( model ) {\n\tconst fn = renderers[ model.type ] || createEmptyPreview;\n\treturn fn( model );\n}\n\nfunction supportsCSSClipPath() {\n\treturn window.CSS &&\n\t\ttypeof CSS.supports === 'function' &&\n\t\tCSS.supports( 'clip-path', 'polygon(1px 1px)' );\n\n}\n\n/**\n * Creates an instance of the DTO backing a preview.\n *\n * @param {ext.popups.PagePreviewModel} model\n * @return {ext.popups.Preview}\n */\nexport function createPagePreview( model ) {\n\tconst thumbnail = createThumbnail( model.thumbnail, supportsCSSClipPath() ),\n\t\thasThumbnail = thumbnail !== null,\n\t\twithCSSClipPath = supportsCSSClipPath(),\n\t\tlinkTitle = mw.msg( 'popups-settings-icon-gear-title' );\n\n\treturn {\n\t\tel: renderPagePreview( model, thumbnail, withCSSClipPath, linkTitle ),\n\t\thasThumbnail,\n\t\tthumbnail,\n\t\tisTall: hasThumbnail && thumbnail.isTall\n\t};\n}\n\n/**\n * Creates an instance of the DTO backing a preview. In this case the DTO\n * represents a generic preview, which covers the following scenarios:\n *\n * * The page doesn't exist, i.e. the user hovered over a redlink or a\n * redirect to a page that doesn't exist.\n * * The page doesn't have a viable extract.\n *\n * @param {ext.popups.PagePreviewModel} model\n * @return {ext.popups.Preview}\n */\nexport function createEmptyPreview( model ) {\n\tmodel.title = mw.msg( 'popups-preview-no-preview' );\n\tconst linkMsg = mw.msg( 'popups-preview-footer-read' );\n\n\treturn {\n\t\tel: renderPreview( model, null, linkMsg ),\n\t\thasThumbnail: false,\n\t\tisTall: false\n\t};\n}\n\n/**\n * Creates an instance of the disambiguation preview.\n *\n * @param {ext.popups.PagePreviewModel} model\n * @return {ext.popups.Preview}\n */\nexport function createDisambiguationPreview( model ) {\n\tconst extractMsg = mw.msg( 'popups-preview-disambiguation' );\n\tconst linkMsg = mw.msg( 'popups-preview-disambiguation-link' );\n\n\treturn {\n\t\tel: renderPreview( model, extractMsg, linkMsg ),\n\t\thasThumbnail: false,\n\t\tisTall: false\n\t};\n}\n\n/**\n * Shows the preview.\n *\n * Extracted from `mw.popups.render.openPopup`.\n *\n * TODO: From the perspective of the client, there's no need to distinguish\n * between rendering and showing a preview. Merge #render and Preview#show.\n *\n * @param {ext.popups.Preview} preview\n * @param {ext.popups.Measures} measures\n * @param {HTMLElement} _link event target (unused)\n * @param {ext.popups.PreviewBehavior} behavior\n * @param {string} token\n * @param {Object} container DOM object to which pointer masks are appended\n * @param {string} dir 'ltr' if left-to-right, 'rtl' if right-to-left.\n * @return {jQuery.Promise} A promise that resolves when the promise has\n * faded in.\n */\nexport function show(\n\tpreview, measures, _link, behavior, token, container, dir\n) {\n\tconst layout = createLayout(\n\t\tpreview.isTall,\n\t\tmeasures,\n\t\tpointerSize,\n\t\tdir\n\t);\n\n\tcontainer.appendChild( preview.el );\n\n\tlayoutPreview(\n\t\tpreview, layout, getClasses( preview, layout ),\n\t\tSIZES.landscapeImage.h, pointerSize, measures.windowHeight\n\t);\n\n\tpreview.el.style.display = 'block';\n\n\t// Trigger fading effect for reference previews after the popup has been rendered\n\tif ( preview.el.classList.contains( 'mwe-popups-type-reference' ) ) {\n\t\tpreview.el.querySelector( '.mwe-popups-scroll' ).dispatchEvent( new Event( 'scroll' ) );\n\t}\n\n\treturn wait( 200 )\n\t\t.then( () => {\n\t\t\tbindBehavior( preview, behavior );\n\t\t\tbehavior.previewShow( token );\n\t\t} );\n}\n\n/**\n * Binds the behavior to the interactive elements of the preview.\n *\n * @param {ext.popups.Preview} preview\n * @param {ext.popups.PreviewBehavior} behavior\n */\nexport function bindBehavior( preview, behavior ) {\n\tpreview.el.addEventListener( 'mouseenter', behavior.previewDwell );\n\tpreview.el.addEventListener( 'mouseleave', behavior.previewAbandon );\n\n\tpreview.el.addEventListener( 'click', behavior.click );\n\n\tconst button = preview.el.querySelector( 'a.mwe-popups-settings-button' );\n\tif ( button ) {\n\t\tbutton.href = behavior.settingsUrl;\n\t\tbutton.addEventListener( 'click', ( event ) => {\n\t\t\tevent.stopPropagation();\n\n\t\t\tbehavior.showSettings( event );\n\t\t} );\n\t}\n}\n\n/**\n * Extracted from `mw.popups.render.closePopup`.\n *\n * @param {ext.popups.Preview} preview\n * @return {jQuery.Promise} A promise that resolves when the preview has\n * faded out.\n */\nexport function hide( preview ) {\n\t// FIXME: This method clearly needs access to the layout of the preview.\n\tconst fadeInClass = ( preview.el.classList.contains( 'mwe-popups-fade-in-up' ) ) ?\n\t\t'mwe-popups-fade-in-up' :\n\t\t'mwe-popups-fade-in-down';\n\n\tconst fadeOutClass = ( fadeInClass === 'mwe-popups-fade-in-up' ) ?\n\t\t'mwe-popups-fade-out-down' :\n\t\t'mwe-popups-fade-out-up';\n\n\t// Classes documented above\n\t// eslint-disable-next-line mediawiki/class-doc\n\tpreview.el.classList.remove( fadeInClass );\n\t// eslint-disable-next-line mediawiki/class-doc\n\tpreview.el.classList.add( fadeOutClass );\n\n\treturn wait( 150 ).then( () => {\n\t\tpreview.el.remove();\n\t} );\n}\n\n/**\n * Represents the layout of a preview, which consists of a position (`offset`)\n * and whether or not the preview should be flipped horizontally or\n * vertically (`flippedX` and `flippedY` respectively).\n *\n * @typedef {Object} ext.popups.PreviewLayout\n * @property {Object} offset\n * @property {number} offset.top\n * @property {number} offset.left\n * @property {boolean} flippedX\n * @property {boolean} flippedY\n * @property {string} dir 'ltr' if left-to-right, 'rtl' if right-to-left.\n */\n\n/**\n * @param {boolean} isPreviewTall\n * @param {ext.popups.Measures} measures\n * @param {number} pointerSpaceSize Space reserved for the pointer\n * @param {string} dir 'ltr' if left-to-right, 'rtl' if right-to-left.\n * @return {ext.popups.PreviewLayout}\n */\nexport function createLayout(\n\tisPreviewTall, measures, pointerSpaceSize, dir\n) {\n\tlet flippedX = false,\n\t\tflippedY = false,\n\t\toffsetTop = measures.pageY ?\n\t\t\t// If it was a mouse event, position according to mouse\n\t\t\t// Since client rectangles are relative to the viewport,\n\t\t\t// take scroll position into account.\n\t\t\tgetClosestYPosition(\n\t\t\t\tmeasures.pageY - measures.scrollTop,\n\t\t\t\tmeasures.clientRects,\n\t\t\t\tfalse\n\t\t\t) + measures.scrollTop + pointerSpaceSize :\n\t\t\t// Position according to link position or size\n\t\t\tmeasures.offset.top + measures.height + pointerSize,\n\t\toffsetLeft;\n\tconst clientTop = measures.clientY ? measures.clientY : offsetTop;\n\n\tif ( measures.pageX ) {\n\t\tif ( measures.width > maxLinkWidthForCenteredPointer ) {\n\t\t\t// For wider links, position the popup's pointer at the\n\t\t\t// mouse pointer's location. (x-axis)\n\t\t\toffsetLeft = measures.pageX;\n\t\t} else {\n\t\t\t// For smaller links, position the popup's pointer at\n\t\t\t// the link's center. (x-axis)\n\t\t\toffsetLeft = measures.offset.left + measures.width / 2;\n\t\t}\n\t} else {\n\t\toffsetLeft = measures.offset.left;\n\t}\n\n\t// X Flip\n\tif ( offsetLeft > ( measures.windowWidth / 2 ) ) {\n\t\toffsetLeft += ( !measures.pageX ) ? measures.width : 0;\n\t\toffsetLeft -= !isPreviewTall ?\n\t\t\tportraitPopupWidth :\n\t\t\tlandscapePopupWidth;\n\t\tflippedX = true;\n\t}\n\n\tif ( measures.pageX ) {\n\t\toffsetLeft += ( flippedX ) ? 18 : -18;\n\t}\n\n\t// Y Flip\n\tif ( clientTop > ( measures.windowHeight / 2 ) ) {\n\t\tflippedY = true;\n\n\t\t// Mirror the positioning of the preview when there's no \"Y flip\": rest\n\t\t// the pointer on the edge of the link's bounding rectangle. In this case\n\t\t// the edge is the top-most.\n\t\toffsetTop = measures.offset.top;\n\n\t\t// Change the Y position to the top of the link\n\t\tif ( measures.pageY ) {\n\t\t\t// Since client rectangles are relative to the viewport,\n\t\t\t// take scroll position into account.\n\t\t\toffsetTop = getClosestYPosition(\n\t\t\t\tmeasures.pageY - measures.scrollTop,\n\t\t\t\tmeasures.clientRects,\n\t\t\t\ttrue\n\t\t\t) + measures.scrollTop;\n\t\t}\n\n\t\toffsetTop -= pointerSpaceSize;\n\t}\n\n\treturn {\n\t\toffset: {\n\t\t\ttop: offsetTop,\n\t\t\tleft: offsetLeft\n\t\t},\n\t\tflippedX: dir === 'rtl' ? !flippedX : flippedX,\n\t\tflippedY,\n\t\tdir\n\t};\n}\n\n/**\n * is there a pointer on the image of the preview?\n *\n * @param {ext.popups.Preview} preview\n * @param {ext.popups.PreviewLayout} layout\n * @return {boolean}\n */\nexport function hasPointerOnImage( preview, layout ) {\n\tif ( ( !preview.hasThumbnail || preview.isTall && !layout.flippedX ) &&\n\t\t!layout.flippedY ) {\n\t\treturn false;\n\t}\n\tif ( preview.hasThumbnail ) {\n\t\tif (\n\t\t\t( !preview.isTall && !layout.flippedY ) ||\n\t\t\t( preview.isTall && layout.flippedX )\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * Generates a list of declarative CSS classes that represent the layout of\n * the preview.\n *\n * @param {ext.popups.Preview} preview\n * @param {ext.popups.PreviewLayout} layout\n * @return {string[]}\n */\nexport function getClasses( preview, layout ) {\n\tconst classes = [];\n\n\tif ( layout.flippedY ) {\n\t\tclasses.push( 'mwe-popups-fade-in-down' );\n\t} else {\n\t\tclasses.push( 'mwe-popups-fade-in-up' );\n\t}\n\n\tif ( layout.flippedY && layout.flippedX ) {\n\t\tclasses.push( 'flipped-x-y' );\n\t} else if ( layout.flippedY ) {\n\t\tclasses.push( 'flipped-y' );\n\t} else if ( layout.flippedX ) {\n\t\tclasses.push( 'flipped-x' );\n\t}\n\n\tclasses.push(\n\t\thasPointerOnImage( preview, layout ) ?\n\t\t\t'mwe-popups-image-pointer' : 'mwe-popups-no-image-pointer'\n\t);\n\n\tif ( preview.isTall ) {\n\t\tclasses.push( 'mwe-popups-is-tall' );\n\t} else {\n\t\tclasses.push( 'mwe-popups-is-not-tall' );\n\t}\n\n\treturn classes;\n}\n\n/**\n * Lays out the preview given the layout.\n *\n * If the thumbnail is landscape and isn't the full height of the thumbnail\n * container, then pull the extract up to keep whitespace consistent across\n * previews.\n *\n * @param {ext.popups.Preview} preview\n * @param {ext.popups.PreviewLayout} layout\n * @param {string[]} classes class names used for layout out the preview\n * @param {number} predefinedLandscapeImageHeight landscape image height\n * @param {number} pointerSpaceSize\n * @param {number} windowHeight\n */\nexport function layoutPreview(\n\tpreview, layout, classes, predefinedLandscapeImageHeight, pointerSpaceSize, windowHeight\n) {\n\tconst popup = preview.el,\n\t\tisTall = preview.isTall,\n\t\thasThumbnail = preview.hasThumbnail,\n\t\tthumbnail = preview.thumbnail,\n\t\tflippedY = layout.flippedY;\n\n\tif (\n\t\t!flippedY && !isTall && hasThumbnail &&\n\t\t\tthumbnail.height < predefinedLandscapeImageHeight && !supportsCSSClipPath()\n\t) {\n\t\tconst popupExtract = popup.querySelector( '.mwe-popups-extract' );\n\t\tpopupExtract.style.marginTop = `${ ( thumbnail.height - pointerSpaceSize ) }px`;\n\t}\n\n\t// The following classes are used here:\n\t// * flipped-x\n\t// * flipped-x-y\n\t// * flipped-y\n\t// * mwe-popups-fade-in-down\n\t// * mwe-popups-fade-in-up\n\t// * mwe-popups-image-pointer\n\t// * mwe-popups-is-not-tall\n\t// * mwe-popups-is-tall\n\t// * mwe-popups-no-image-pointer\n\tpopup.classList.add.apply( popup.classList, classes );\n\n\tpopup.style.left = `${ layout.offset.left }px`;\n\tpopup.style.top = flippedY ? 'auto' : `${ layout.offset.top }px`;\n\tpopup.style.bottom = flippedY ? `${ windowHeight - layout.offset.top }px` : 'auto';\n\n\tif ( hasThumbnail && !supportsCSSClipPath() ) {\n\t\tsetThumbnailClipPath( preview, layout );\n\t}\n}\n\n/**\n * Sets the thumbnail SVG clip-path.\n *\n * If the preview should be oriented differently, then the pointer is updated,\n * e.g. if the preview should be flipped vertically, then the pointer is\n * removed.\n *\n * Note: SVG clip-paths are supported everywhere but clip-paths as CSS\n * properties are not (https://caniuse.com/#feat=css-clip-path). For this\n * reason, RTL flipping is handled in JavaScript instead of CSS.\n *\n * @param {ext.popups.Preview} preview\n * @param {ext.popups.PreviewLayout} layout\n */\nexport function setThumbnailClipPath(\n\t{ el, isTall, thumbnail }, { flippedY, flippedX, dir }\n) {\n\tconst maskID = getThumbnailClipPathID( isTall, flippedY, flippedX );\n\tif ( maskID ) {\n\t\t// CSS matrix transform entries:\n\t\t// ⎡ sx c tx ⎤\n\t\t// ⎣ sy d ty ⎦\n\t\tconst matrix = {\n\t\t\tscaleX: 1,\n\t\t\t// moving the mask horizontally if the image is less than the maximum width\n\t\t\ttranslateX: isTall ? Math.min( thumbnail.width - SIZES.portraitImage.w, 0 ) : 0\n\t\t};\n\n\t\tif ( dir === 'rtl' ) {\n\t\t\t// flipping the mask horizontally\n\t\t\tmatrix.scaleX = -1;\n\t\t\t// moving the mask horizontally to the max width of the thumbnail\n\t\t\tmatrix.translateX = isTall ? SIZES.portraitImage.w : SIZES.landscapeImage.w;\n\t\t}\n\n\t\t// Transform the clip-path not the image it is applied to.\n\t\tconst mask = document.getElementById( maskID );\n\t\tmask.setAttribute(\n\t\t\t'transform',\n\t\t\t`matrix(${ matrix.scaleX } 0 0 1 ${ matrix.translateX } 0)`\n\t\t);\n\n\t\tel.querySelector( 'image' )\n\t\t\t.setAttribute( 'clip-path', `url(#${ maskID })` );\n\t}\n}\n\n/**\n * Gets the thumbnail SVG clip-path element ID as specified in pointer-mask.svg.\n *\n * @param {boolean} isTall Sugar around\n * `preview.hasThumbnail && thumbnail.isTall`\n * @param {boolean} flippedY\n * @param {boolean} flippedX\n * @return {string|undefined}\n */\nexport function getThumbnailClipPathID( isTall, flippedY, flippedX ) {\n\t// Clip-paths are only needed when the pointer is in a corner that is covered by the thumbnail.\n\t// This is only the case in 4 of 8 situations:\n\tif ( !isTall && !flippedY ) {\n\t\t// 1. Landscape thumbnails cover the upper half of the popup. This is only the case when the\n\t\t// pointer is not flipped to the bottom.\n\t\treturn flippedX ? 'mwe-popups-mask-flip' : 'mwe-popups-mask';\n\t} else if ( isTall && flippedX ) {\n\t\t// 2. Tall thumbnails cover the right half of the popup. This is only the case when the\n\t\t// pointer is flipped to the right.\n\t\treturn flippedY ? 'mwe-popups-landscape-mask-flip' : 'mwe-popups-landscape-mask';\n\t}\n\n\t// The 4 combinations not covered above don't need a clip-path.\n\treturn undefined;\n}\n\n/**\n * Given the rectangular box(es) find the 'y' boundary of the closest\n * rectangle to the point 'y'. The point 'y' is the location of the mouse\n * on the 'y' axis and the rectangular box(es) are the borders of the\n * element over which the mouse is located. There will be more than one\n * rectangle in case the element spans multiple lines.\n *\n * In the majority of cases the mouse pointer will be inside a rectangle.\n * However, some browsers (i.e. Chrome) trigger a hover action even when\n * the mouse pointer is just outside a bounding rectangle. That's why\n * we need to look at all rectangles and not just the rectangle that\n * encloses the point.\n *\n * @private\n * @param {number} y the point for which the closest location is being\n * looked for\n * @param {ClientRectList} rects list of rectangles defined by four edges\n * @param {boolean} [isTop] should the resulting rectangle's top 'y'\n * boundary be returned. By default the bottom 'y' value is returned.\n * @return {number}\n */\nexport function getClosestYPosition( y, rects, isTop ) {\n\tlet minY = null, result;\n\n\tArray.prototype.slice.call( rects ).forEach( ( rect ) => {\n\t\tconst deltaY = Math.abs( y - rect.top + y - rect.bottom );\n\n\t\tif ( minY === null || minY > deltaY ) {\n\t\t\tminY = deltaY;\n\t\t\t// Make sure the resulting point is at or outside the rectangle\n\t\t\t// boundaries.\n\t\t\tresult = ( isTop ) ? Math.floor( rect.top ) : Math.ceil( rect.bottom );\n\t\t}\n\t} );\n\n\treturn result;\n}\n\nexport const test = {\n\t/** For testing only */\n\treset: () => {\n\t\trenderers = {};\n\t}\n};\n","/**\n * @module setUserConfigFlags\n */\n\n/**\n * Same as in includes/PopupsContext.php\n */\nconst REF_TOOLTIPS_ENABLED = 2,\n\tREFERENCE_PREVIEWS_ENABLED = 4;\n\n/**\n * Decodes the bitmask that represents preferences to the related config options.\n *\n * @param {mw.Map} config\n */\nexport default function setUserConfigFlags( config ) {\n\tconst popupsFlags = parseInt( config.get( 'wgPopupsFlags' ), 10 );\n\n\t/* eslint-disable no-bitwise */\n\tconfig.set(\n\t\t'wgPopupsConflictsWithRefTooltipsGadget',\n\t\t!!( popupsFlags & REF_TOOLTIPS_ENABLED )\n\t);\n\tconfig.set(\n\t\t'wgPopupsReferencePreviews',\n\t\t!!( popupsFlags & REFERENCE_PREVIEWS_ENABLED )\n\t);\n\t/* eslint-enable no-bitwise */\n}\n","/**\n * @module preview/model\n */\n\nimport { requiresSummary } from '../ui/renderer';\n\nconst selectors = [];\n\n/**\n * Page Preview types as defined in Schema:Popups\n * https://meta.wikimedia.org/wiki/Schema:Popups\n *\n * @constant {Object}\n */\nconst previewTypes = {\n\t/** Empty preview used in error situations */\n\tTYPE_GENERIC: 'generic',\n\t/** Standard page preview with or without thumbnail */\n\tTYPE_PAGE: 'page',\n\t/** Disambiguation page preview */\n\tTYPE_DISAMBIGUATION: 'disambiguation'\n};\n\nexport { previewTypes };\n\n/**\n * Preview Model\n *\n * @typedef {Object} PreviewModel\n * @property {string} url The canonical URL of the page being previewed\n * @property {string} type One of the previewTypes.TYPE_… constants.\n *\n * @global\n */\n\n/**\n * @typedef {Object} PagePreviewModel\n * @extends PreviewModel\n * @property {string} title\n * @property {Array|undefined} extract `undefined` if the extract isn't\n * viable, e.g. if it's empty after having ellipsis and parentheticals\n * removed; this can be used to present default or error states\n * @property {string} languageCode\n * @property {string} languageDirection Either \"ltr\" or \"rtl\", or an empty string if undefined.\n * @property {{source: string, width: number, height: number}|undefined} thumbnail\n * @property {number} pageId Currently not used by any known popup type.\n *\n * @global\n */\n\n/**\n * @typedef {Object} ReferencePreviewModel\n * @extends PreviewModel\n * @property {string} extract An HTML snippet, not necessarily with a single top-level node\n * @property {string} referenceType A type identifier, e.g. \"web\"\n * @property {string} sourceElementId ID of the parent element that triggered the preview\n *\n * @global\n */\n\n/**\n * Creates a page preview model.\n *\n * @param {string} title\n * @param {string} url The canonical URL of the page being previewed\n * @param {string} languageCode\n * @param {string} languageDirection Either \"ltr\" or \"rtl\"\n * @param {Array|undefined|null} extract\n * @param {string} type\n * @param {{source: string, width: number, height: number}|undefined} [thumbnail]\n * @param {number} [pageId]\n * @return {PagePreviewModel}\n */\nexport function createModel(\n\ttitle,\n\turl,\n\tlanguageCode,\n\tlanguageDirection,\n\textract,\n\ttype,\n\tthumbnail,\n\tpageId\n) {\n\tconst processedExtract = processExtract( extract ),\n\t\tpreviewType = getPagePreviewType( type, processedExtract );\n\n\treturn {\n\t\ttitle,\n\t\turl,\n\t\tlanguageCode,\n\t\tlanguageDirection,\n\t\textract: processedExtract,\n\t\ttype: previewType,\n\t\tthumbnail,\n\t\tpageId\n\t};\n}\n\n/**\n * Creates an empty page preview model.\n *\n * @param {string} title\n * @param {string} url\n * @return {PagePreviewModel}\n */\nexport function createNullModel( title, url ) {\n\treturn createModel( title, url, '', '', [], '' );\n}\n\n/**\n * @param {HTMLElement} element\n * @param {string} selector\n * @return {boolean}\n */\nconst elementMatchesSelector = ( element, selector ) => {\n\treturn element.matches( selector );\n};\n\n/**\n * Recursively checks the element and its parents.\n *\n * @param {HTMLElement} element\n * @return {HTMLElement|null}\n */\nexport function findNearestEligibleTarget( element ) {\n\tconst selector = selectors.join( ', ' );\n\treturn element.closest( selector );\n}\n\n/**\n * @typedef {Object} PreviewType\n * @property {string} name identifier for preview type\n * @property {string} selector a CSS selector\n * @type {PreviewType[]}\n */\nconst registeredPreviewTypes = [];\n\n/**\n * Determines the applicable popup type based on title and link element.\n *\n * @param {HTMLAnchorElement} el\n * @return {string|null} One of the previewTypes.TYPE_… constants\n */\nexport function getPreviewType( el ) {\n\tconst candidates = registeredPreviewTypes.filter(\n\t\t( type ) => elementMatchesSelector( el, type.selector )\n\t);\n\n\t// If the filter returned some possibilities, use the last registered one.\n\tif ( candidates.length > 0 ) {\n\t\treturn candidates[ candidates.length - 1 ].name;\n\t}\n\n\treturn null;\n}\n\n/**\n * Processes the extract returned by the TextExtracts MediaWiki API query\n * module.\n *\n * If the extract is `undefined`, `null`, or empty, then `undefined` is\n * returned.\n *\n * @param {Array|undefined|null} extract\n * @return {Array|undefined} Array when extract is an not empty array, undefined\n * otherwise\n */\nfunction processExtract( extract ) {\n\tif ( extract === undefined || extract === null || extract.length === 0 ) {\n\t\treturn undefined;\n\t}\n\treturn extract;\n}\n\n/**\n * Determines the page preview type based on whether or not:\n * a. Is the preview empty.\n * b. The preview type matches one of previewTypes.\n * c. Assume standard page preview if both above are false\n *\n * @param {string} type\n * @param {Array|undefined} [processedExtract]\n * @return {string} One of the previewTypes.TYPE_… constants.\n */\n\nfunction getPagePreviewType( type, processedExtract ) {\n\t// If the preview type requires a summary to display and no extract was\n\t// found show the generic (error) preview.\n\tif ( processedExtract === undefined && requiresSummary( type ) ) {\n\t\treturn previewTypes.TYPE_GENERIC;\n\t}\n\n\tswitch ( type ) {\n\t\tcase previewTypes.TYPE_GENERIC:\n\t\tcase previewTypes.TYPE_DISAMBIGUATION:\n\t\tcase previewTypes.TYPE_PAGE:\n\t\t\treturn type;\n\t\tdefault:\n\t\t\t/**\n\t\t\t * Assume type=\"page\" if extract exists & not one of previewTypes.\n\t\t\t * Note:\n\t\t\t * - Restbase response includes \"type\" prop but other gateways don't.\n\t\t\t * - event-logging Schema:Popups requires type=\"page\" but restbase\n\t\t\t * provides type=\"standard\". Model must conform to event-logging schema.\n\t\t\t */\n\t\t\treturn previewTypes.TYPE_PAGE;\n\t}\n}\n\nconst dwellDelay = {};\n\n/**\n * Determines the delay before showing the preview when dwelling a link.\n *\n * @param {string} type\n * @return {number}\n */\nexport function getDwellDelay( type ) {\n\treturn dwellDelay[ type ] || 0;\n}\n\n/***\n * Set the delay before showing the preview when dwelling a link.\n *\n * @param {string} type\n * @param {number} delay\n */\nexport function setDwellTime( type, delay ) {\n\tdwellDelay[ type ] = delay;\n}\n\n/**\n * Allows extensions to register their own page previews.\n *\n * @stable\n * @param {string} type\n * @param {string} selector A valid CSS selector to associate preview with\n * @param {number} [delay] optional delay between hovering and displaying preview.\n * If not defined, delay will be zero.\n */\nexport function registerModel( type, selector, delay ) {\n\tselectors.push( selector );\n\tregisteredPreviewTypes.push( {\n\t\tname: type,\n\t\tselector\n\t} );\n\tif ( delay ) {\n\t\tsetDwellTime( type, delay );\n\t}\n}\n\n/**\n * Check whether any kind of preview is enabled.\n *\n * @return {boolean}\n */\nexport function isAnythingEligible() {\n\treturn !!selectors.length;\n}\n\nexport const test = {\n\t/** For testing only */\n\treset: () => {\n\t\twhile ( registeredPreviewTypes.length ) {\n\t\t\tregisteredPreviewTypes.pop();\n\t\t}\n\t}\n};\n","import { previewTypes } from './preview/model';\n\n/**\n * @module userSettings\n */\n\n/**\n * @interface UserSettings\n *\n * @global\n */\n\nconst PAGE_PREVIEWS_ENABLED_KEY = 'mwe-popups-enabled',\n\tREFERENCE_PREVIEWS_ENABLED_KEY = 'mwe-popups-referencePreviews-enabled',\n\tREFERENCE_PREVIEWS_LOGGING_SCHEMA = 'event.ReferencePreviewsPopups';\n\n/**\n * Creates an object whose methods encapsulate all interactions with the UA's\n * storage.\n *\n * @param {mw.storage} storage The `mw.storage` singleton instance\n *\n * @return {UserSettings}\n */\nexport default function createUserSettings( storage ) {\n\treturn {\n\t\tmigrateOldPreferences() {\n\t\t\tconst isDisabled = !!storage.get( PAGE_PREVIEWS_ENABLED_KEY );\n\t\t\tif ( isDisabled ) {\n\t\t\t\tstorage.remove( PAGE_PREVIEWS_ENABLED_KEY );\n\t\t\t\tthis.storePreviewTypeEnabled( previewTypes.TYPE_PAGE, false );\n\t\t\t}\n\t\t\tconst isRefsDisabled = !!storage.get( REFERENCE_PREVIEWS_ENABLED_KEY );\n\t\t\tif ( isRefsDisabled ) {\n\t\t\t\tstorage.remove( REFERENCE_PREVIEWS_ENABLED_KEY );\n\t\t\t\tthis.storePreviewTypeEnabled( previewTypes.TYPE_REFERENCE, false );\n\t\t\t}\n\t\t},\n\t\t/**\n\t\t * Check whether the preview type is enabled.\n\t\t *\n\t\t * @method\n\t\t * @param {string} previewType\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisPreviewTypeEnabled( previewType ) {\n\t\t\tconst storageKey = `mwe-popups-${ previewType }-enabled`;\n\t\t\tconst value = storage.get( storageKey );\n\t\t\treturn value === null;\n\t\t},\n\n\t\t/**\n\t\t * Permanently persists (typically in localStorage) whether the user has enabled\n\t\t * the preview type.\n\t\t *\n\t\t * @method\n\t\t * @name UserSettings#storePreviewTypeEnabled\n\t\t * @param {string} previewType\n\t\t * @param {boolean} enabled\n\t\t */\n\t\tstorePreviewTypeEnabled( previewType, enabled ) {\n\t\t\tif ( previewType === previewTypes.TYPE_REFERENCE ) {\n\t\t\t\tmw.track( REFERENCE_PREVIEWS_LOGGING_SCHEMA, {\n\t\t\t\t\taction: enabled ? 'anonymousEnabled' : 'anonymousDisabled'\n\t\t\t\t} );\n\t\t\t}\n\t\t\tconst storageKey = `mwe-popups-${ previewType }-enabled`;\n\t\t\tif ( enabled ) {\n\t\t\t\tstorage.remove( storageKey );\n\t\t\t} else {\n\t\t\t\tstorage.set( storageKey, '0' );\n\t\t\t}\n\t\t}\n\t};\n}\n","import isReferencePreviewsEnabled from './isReferencePreviewsEnabled';\nimport { initReferencePreviewsInstrumentation } from './referencePreviews';\nimport createReferenceGateway from './createReferenceGateway';\nimport renderFn from './createReferencePreview';\nimport { TYPE_REFERENCE, FETCH_DELAY_REFERENCE_TYPE } from './constants';\nimport createUserSettings from '../userSettings';\nimport setUserConfigFlags from './setUserConfigFlags';\n\nsetUserConfigFlags( mw.config );\nconst userSettings = createUserSettings( mw.storage );\nconst referencePreviewsState = isReferencePreviewsEnabled( mw.user, userSettings, mw.config );\nconst gateway = createReferenceGateway();\n\nwindow.refPreviews = referencePreviewsState !== null ? {\n\ttype: TYPE_REFERENCE,\n\tselector: '#mw-content-text .reference a[ href*=\"#\" ]',\n\tdelay: FETCH_DELAY_REFERENCE_TYPE,\n\tgateway,\n\trenderFn,\n\tinit: () => {\n\t\tinitReferencePreviewsInstrumentation();\n\t}\n} : null;\n","/**\n * @module gateway/reference\n */\n\nimport { TYPE_REFERENCE } from './constants.js';\nimport { abortablePromise } from '../gateway/index.js';\n\n/**\n * @return {Gateway}\n */\nexport default function createReferenceGateway() {\n\n\t/**\n\t * @param {string} id\n\t * @return {HTMLElement}\n\t */\n\tfunction scrapeReferenceText( id ) {\n\t\tconst idSelector = `#${ CSS.escape( id ) }`;\n\n\t\t/**\n\t\t * Same alternative selectors with and without mw-… as in the RESTbased endpoint.\n\t\t *\n\t\t * @see https://phabricator.wikimedia.org/diffusion/GMOA/browse/master/lib/transformations/references/structureReferenceListContent.js$138\n\t\t */\n\t\treturn document.querySelector( `${ idSelector } .mw-reference-text, ${ idSelector } .reference-text` );\n\t}\n\n\t/**\n\t * Attempts to find a single reference type identifier, limited to a list of known types.\n\t * - When a `class=\"…\"` attribute mentions multiple known types, the last one is used, following\n\t * CSS semantics.\n\t * - When there are multiple tags, the first with a known type is used.\n\t *\n\t * @param {HTMLElement} referenceText\n\t * @return {string|null}\n\t */\n\tfunction scrapeReferenceType( referenceText ) {\n\t\tconst KNOWN_TYPES = [ 'book', 'journal', 'news', 'note', 'web' ];\n\t\tlet type = null;\n\t\tconst citeTags = referenceText.querySelectorAll( 'cite[class]' );\n\t\tArray.prototype.forEach.call( citeTags, ( element ) => {\n\t\t\t// don't need to keep scanning if one is found.\n\t\t\tif ( type ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst classNames = element.className.split( /\\s+/ );\n\t\t\tfor ( let i = classNames.length; i--; ) {\n\t\t\t\tif ( KNOWN_TYPES.indexOf( classNames[ i ] ) !== -1 ) {\n\t\t\t\t\ttype = classNames[ i ];\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t\treturn type;\n\t}\n\n\t/**\n\t * @param {mw.Title} title\n\t * @param {HTMLAnchorElement} el\n\t * @return {AbortPromise}\n\t */\n\tfunction fetchPreviewForTitle( title, el ) {\n\t\t// Need to encode the fragment again as mw.Title returns it as decoded text\n\t\tconst id = title.getFragment().replace( / /g, '_' ),\n\t\t\treferenceNode = scrapeReferenceText( id );\n\n\t\tif ( !referenceNode ||\n\t\t\t// Skip references that don't contain anything but whitespace, e.g. a single \n\t\t\t( !referenceNode.textContent.trim() && !referenceNode.children.length )\n\t\t) {\n\t\t\treturn Promise.reject(\n\t\t\t\t// Required to set `showNullPreview` to false and not open an error popup\n\t\t\t\t{ textStatus: 'abort', textContext: 'Footnote not found or empty', xhr: { readyState: 0 } }\n\t\t\t);\n\t\t}\n\n\t\tconst model = {\n\t\t\turl: `#${ id }`,\n\t\t\textract: referenceNode.innerHTML,\n\t\t\ttype: TYPE_REFERENCE,\n\t\t\treferenceType: scrapeReferenceType( referenceNode ),\n\t\t\t// Note: Even the top-most HTMLHtmlElement is guaranteed to have a parent.\n\t\t\tsourceElementId: el.parentNode.id\n\t\t};\n\n\t\treturn abortablePromise( Promise.resolve( model ) );\n\t}\n\n\treturn {\n\t\tfetchPreviewForTitle\n\t};\n}\n","/**\n * @module gateway\n */\n\n/**\n * The interface implemented by all preview gateways.\n *\n * @typedef Gateway\n * @property {function(string): JQuery.jqXHR} fetch\n * @property {FetchPreviewForTitle} fetchPreviewForTitle\n * @property {ConvertPageToModel} convertPageToModel\n */\n\n/**\n * A Promise, usually for a long running or costly task such as an HTTP request,\n * that is abortable.\n *\n * @template T\n * @typedef {Promise} AbortPromise\n * @property {function(): void} abort\n */\n\n/**\n * @param {Promise|jQuery.Promise} promise\n * @param {function(): void} [abort]\n * @return {AbortPromise}\n */\nexport function abortablePromise( promise, abort = () => {} ) {\n\t// JQuery provided.\n\tif ( promise.promise ) {\n\t\treturn promise.promise( {\n\t\t\tabort\n\t\t} );\n\t}\n\tpromise.abort = abort;\n\treturn promise;\n}\n\n/**\n * Fetches a preview for a page or reference.\n *\n * If the underlying request is successful and contains data for the requested title,\n * then the resulting promise will resolve. If not, then it will reject.\n *\n * @typedef {function(mw.Title, HTMLAnchorElement): AbortPromise} FetchPreviewForTitle\n */\n\n/**\n * Converts the API response to a preview model. Exposed for testing only.\n *\n * @typedef {function(Object, ...any): PagePreviewModel} ConvertPageToModel\n */\n\nconst gatewayMap = {};\n\n/**\n * @param {string} type Type of preview we are handling\n * @return {Gateway|undefined}\n */\nexport function getGatewayForPreviewType( type ) {\n\treturn gatewayMap[ type ];\n}\n\n/**\n * Register a gateway for a given preview type.\n *\n * @param {string} type preview type\n * @param {Gateway} gateway\n */\nexport function registerGatewayForPreviewType( type, gateway ) {\n\tgatewayMap[ type ] = gateway;\n}\n"],"names":["module","exports","user","isAnon","isNamed","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","__webpack_modules__","TYPE_REFERENCE","canSaveToUserPreferences","require","isTracking","LOGGING_SCHEMA","isTrackingEnabled","templates","createNodeFromTemplate","html","div","document","createElement","innerHTML","firstElementChild","cloneNode","replaceWith","node","htmlOrOtherNode","insertAdjacentHTML","parentNode","appendChild","remove","renderReferencePreview","model","type","referenceType","titleMsg","mw","message","concat","exists","str","el","container","element","className","renderPopup","querySelector","text","escape","classList","add","extract","Array","prototype","forEach","call","querySelectorAll","a","target","rel","otherNode","icon","label","textContent","msg","undoHeaderSort","headerSort","removeAttribute","settingsButton","settingsIcon","settingsButtonLabel","append","addEventListener","ev","matches","track","action","e","scrolledToBottom","scrollTop","scrollHeight","clientHeight","isOpenRecorded","scrollbarsPresent","isScrollRecorded","isScrolling","hasHorizontalScroll","scrollWidth","clientWidth","scrollbarHeight","offsetHeight","hasVerticalScroll","scrollbarWidth","offsetWidth","fade","style","bottom","right","toggle","setAttribute","config","get","bpr","dpr","arguments","length","window","devicePixelRatio","bracketedPixelRatio","Math","max","popupsFlags","previewTypes","TYPE_GENERIC","TYPE_PAGE","TYPE_DISAMBIGUATION","PAGE_PREVIEWS_ENABLED_KEY","REFERENCE_PREVIEWS_ENABLED_KEY","parseInt","set","storage","userSettings","migrateOldPreferences","this","storePreviewTypeEnabled","isPreviewTypeEnabled","previewType","storageKey","enabled","referencePreviewsState","options","isReferencePreviewsEnabled","gateway","fetchPreviewForTitle","title","id","getFragment","replace","referenceNode","idSelector","CSS","scrapeReferenceText","trim","children","Promise","reject","textStatus","textContext","xhr","readyState","referenceText","KNOWN_TYPES","citeTags","url","classNames","split","i","indexOf","sourceElementId","promise","abort","abortablePromise","resolve","refPreviews","selector","delay","renderFn","hasThumbnail","isTall","init","navigator","sendBeacon"],"sourceRoot":""}