mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Popups
synced 2024-12-11 15:27:04 +00:00
553e76e2bc
Excluding tests for the renderer which keeps failing. This will be readded in a later patch. Bug: T213415 Bug: T213908 Change-Id: If79fa3d0a7a20f121b1ceda6e0e33ad691b1ad30
1 line
186 KiB
JSON
1 line
186 KiB
JSON
{"version":3,"sources":["/w/extensions/Popups/webpack/bootstrap","/w/extensions/Popups/./node_modules/redux-thunk/dist/redux-thunk.min.js","/w/extensions/Popups/./node_modules/redux/dist/redux.min.js","/w/extensions/Popups/(webpack)/buildin/global.js","/w/extensions/Popups/(webpack)/buildin/module.js","/w/extensions/Popups/./src/counts.js","/w/extensions/Popups/./src/constants.js","/w/extensions/Popups/./src/bracketedPixelRatio.js","/w/extensions/Popups/./src/preview/model.js","/w/extensions/Popups/./src/formatter.js","/w/extensions/Popups/./src/gateway/mediawiki.js","/w/extensions/Popups/./src/gateway/rest.js","/w/extensions/Popups/./src/gateway/restFormatters.js","/w/extensions/Popups/./src/gateway/index.js","/w/extensions/Popups/./src/gateway/reference.js","/w/extensions/Popups/./src/userSettings.js","/w/extensions/Popups/./src/previewBehavior.js","/w/extensions/Popups/./src/ui/templates/templateUtil.js","/w/extensions/Popups/./src/ui/templates/settingsDialog/settingsDialog.js","/w/extensions/Popups/./src/ui/settingsDialog.js","/w/extensions/Popups/./src/ui/settingsDialogRenderer.js","/w/extensions/Popups/./src/changeListener.js","/w/extensions/Popups/./src/title.js","/w/extensions/Popups/./src/wait.js","/w/extensions/Popups/./src/ui/thumbnail.js","/w/extensions/Popups/./src/ui/templates/popup/popup.js","/w/extensions/Popups/./src/ui/templates/preview/preview.js","/w/extensions/Popups/./src/ui/templates/referencePreview/referencePreview.js","/w/extensions/Popups/./src/ui/renderer.js","/w/extensions/Popups/./src/ui/templates/pagePreview/pagePreview.js","/w/extensions/Popups/./src/changeListeners/footerLink.js","/w/extensions/Popups/./src/changeListeners/eventLogging.js","/w/extensions/Popups/./src/changeListeners/linkTitle.js","/w/extensions/Popups/./src/changeListeners/syncUserSettings.js","/w/extensions/Popups/./src/changeListeners/index.js","/w/extensions/Popups/./src/changeListeners/pageviews.js","/w/extensions/Popups/./src/changeListeners/render.js","/w/extensions/Popups/./src/changeListeners/settings.js","/w/extensions/Popups/./src/changeListeners/statsv.js","/w/extensions/Popups/./src/actionTypes.js","/w/extensions/Popups/./src/actions.js","/w/extensions/Popups/./src/reducers/nextState.js","/w/extensions/Popups/./src/reducers/eventLogging.js","/w/extensions/Popups/./src/reducers/index.js","/w/extensions/Popups/./src/reducers/pageviews.js","/w/extensions/Popups/./src/reducers/preview.js","/w/extensions/Popups/./src/reducers/settings.js","/w/extensions/Popups/./src/reducers/statsv.js","/w/extensions/Popups/./src/getPageviewTracker.js","/w/extensions/Popups/./src/index.js","/w/extensions/Popups/./src/experiments.js","/w/extensions/Popups/./src/instrumentation/statsv.js","/w/extensions/Popups/./src/instrumentation/eventLogging.js","/w/extensions/Popups/./src/isEnabled.js","/w/extensions/Popups/./src/integrations/mwpopups.js","/w/extensions/Popups/./src/ui/pointer-mask.svg"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","e","id","loaded","dispatch","getState","withExtraArgument","default","global","observable","self","window","Math","random","toString","substring","split","join","INIT","REPLACE","PROBE_UNKNOWN_ACTION","type","apply","this","arguments","u","configurable","writable","a","length","Array","reduce","createStore","Error","f","slice","h","push","indexOf","splice","y","getPrototypeOf","subscribe","replaceReducer","TypeError","next","unsubscribe","combineReducers","keys","forEach","bindActionCreators","applyMiddleware","map","getOwnPropertySymbols","concat","filter","getOwnPropertyDescriptor","compose","__DO_NOT_USE__ActionTypes","g","Function","webpackPolyfill","deprecate","paths","children","getEditCountBucket","count","bucket","getPreviewCountBucket","undefined","bpr","dpr","devicePixelRatio","bracketedPixelRatio","constants","BRACKETED_DEVICE_PIXEL_RATIO","THUMBNAIL_SIZE","EXTRACT_LENGTH","previewTypes","TYPE_GENERIC","TYPE_PAGE","TYPE_DISAMBIGUATION","TYPE_REFERENCE","createModel","title","url","languageCode","languageDirection","extract","thumbnail","pageId","processedExtract","processExtract","getPreviewType","createNullModel","$","jQuery","mw","mediaWiki","formatPlainTextExtract","plainTextExtract","elements","boldIdentifier","snip","replace","trim","escapedTitle","RegExp","escape","regExp","part","text","document","createTextNode","makeTitleInExtractBold","CACHE_LIFETIME","extractPageFromResponse","data","query","pages","result","extend","formatter","convertPageToModel","page","canonicalurl","pagelanguagehtmlcode","pagelanguagedir","pageid","RESTBASE_PROFILE","createRESTBaseGateway","ajax","config","extractParser","fetch","endpoint","encodeURIComponent","headers","Accept","Accept-Language","acceptLanguage","fetchPreviewForTitle","titleText","getPrefixedDb","xhr","then","catch","jqXHR","textStatus","errorThrown","Deferred","reject","exception","promise","abort","generateThumbnailData","original","thumbSize","parts","source","lastPart","originalIsSafe","filename","test","isSafeImgFormat","filenamePxIndex","width","height","substr","floor","Title","getUrl","lang","dir","originalimage","parseHTMLResponse","extract_html","parseHTML","parsePlainTextResponse","createPagePreviewGateway","gatewayConfig","restConfig","api","action","prop","formatversion","redirects","exintro","exchars","explaintext","piprop","pithumbsize","pilicense","rvprop","inprop","titles","smaxage","maxage","uselang","X-Analytics","createMediaWikiApiGateway","Api","formatters","IS_ENABLED_KEY","PREVIEW_COUNT_KEY","escapeHTML","str","html","renderSettingsDialog","model","heading","saveLabel","closeLabel","helpText","okLabel","choices","_ref","description","isChecked","escapeChoices","_ref2","createSettingsDialogRenderer","$dialog","$overlay","boundActions","navPopupsEnabled","isNavPopupsEnabled","msg","addClass","find","click","enabled","val","saveSettings","hideSettings","appendTo","el","show","hide","toggleHelp","visible","$el","setEnabled","pg","fn","disablePopups","registerChangeListener","store","callback","state","prevState","fromElement","contentNamespaces","mwTitle","newFromText","namespace","isValid","href","linkHref","titleRegex","Uri","host","location","hostname","queryLength","matches","exec","path","decodeURIComponent","getTitle","wait","delay","deferred","timer","setTimeout","resolve","clearTimeout","SIZES","portraitImage","w","landscapeImage","createThumbnailElement","className","x","thumbnailWidth","thumbnailHeight","nsSvg","line","createElementNS","points","setAttribute","$thumbnailSVGImage","setAttributeNS","attr","$thumbnail","xmlns","append","renderPopup","renderPreview","showTitle","extractMsg","linkMsg","renderReferencePreview","defaultExtractWidth","$window","landscapePopupWidth","portraitPopupWidth","pointerSize","init","container","body","pointerMaskSVG","render","preview","rawThumbnail","tall","thumbWidth","thumbHeight","isNarrow","isTall","offset","createThumbnail","hasThumbnail","renderPagePreview","extractWidth","getExtractWidth","css","createPagePreview","createDisambiguationPreview","createReferencePreview","createEmptyPreview","createPreviewWithType","event","token","$link","behavior","layout","isPreviewTall","eventData","linkData","windowData","flippedX","flippedY","offsetTop","pageY","getClosestYPosition","scrollTop","clientRects","top","offsetLeft","pageX","left","clientTop","clientY","createLayout","getClientRects","classes","predefinedLandscapeImageHeight","popup","outerHeight","maskID","getThumbnailClipPathID","entries","tx","mask","getElementById","setThumbnailClipPath","layoutPreview","getClasses","on","previewDwell","previewAbandon","settingsUrl","stopPropagation","showSettings","bindBehavior","previewShow","target","documentElement","getAttribute","fadeInClass","hasClass","fadeOutClass","removeClass","remove","rects","isTop","minY","rect","deltaY","abs","bottom","ceil","reducer","syncIfChanged","sync","current","changeListeners","footerLink","$footerLink","message","$footer","parent","createFooterLink","preventDefault","settings","shouldShowFooterLink","eventLogging","eventLoggingTracker","getCurrentTimestamp","_","baseData","timestamp","eventLogged","linkTitle","hasPrevActiveLink","activeLink","destroyTitleAttr","pageviews","pageviewTracker","pageview","source_page_id","source_namespace","namespaceId","source_title","source_url","pageviewLogged","previewBehavior","shouldShow","renderer","fetchResponse","activeEvent","activeToken","showHelp","statsv","track","statsvLogged","syncUserSettings","userSettings","setPreviewCount","setIsEnabled","actionTypes","BOOT","LINK_DWELL","ABANDON_START","ABANDON_END","LINK_CLICK","FETCH_START","FETCH_END","FETCH_COMPLETE","FETCH_FAILED","FETCH_ABORTED","PAGEVIEW_LOGGED","PREVIEW_DWELL","PREVIEW_SHOW","PREVIEW_CLICK","PREVIEW_SEEN","SETTINGS_SHOW","SETTINGS_HIDE","SETTINGS_CHANGE","EVENT_LOGGED","STATSV_LOGGED","FETCH_START_DELAY","PREVIEW_SEEN_DURATION","FETCH_COMPLETE_TARGET_DELAY","ABANDON_END_DELAY","timedAction","baseAction","now","boot","isEnabled","user","editCount","previewCount","getPreviewCount","types","sessionToken","sessionId","pageToken","getPageviewToken","isAnon","gateway","chain","err","when","ex","showNullPreview","readyState","linkDwell","generateToken","isNewInteraction","abandon","_getState$preview","linkClick","currentToken","validType","wasEnabled","nextState","updates","createEvent","interaction","actionData","linkInteractionToken","pageTitleHover","namespaceIdHover","timeToPreviewShow","previewType","perceivedWait","createClosingEvent","totalInteractionTime","round","finished","started","finalized","reducers","nextCount","newState","bootAction","pageTitleSource","namespaceIdSource","pageIdSource","popupEnabled","previewCountBucket","counts","hovercardsSuppressedByGadget","editCountBucket","link","isUserDwelling","page_title","page_id","page_namespace","fetchStartedAt","linkDwellStartedAt","prepareEventData","sourceUrl","maxLength","truncatedUrl","every","char","getPageviewTracker","loader","trackerGetter","sendBeacon","topic","word","schema","toUpperCase","dependencies","evLog","payload","prepare","makeBeaconUrl","BLACKLISTED_LINKS","performance","navigatorObj","mwExperiments","storage","Redux","generateRandomSessionId","pagePreviewGateway","referenceGateway","getFragment","getIsEnabled","set","hasIsEnabled","Boolean","parseInt","isNaN","settingsDialog","experiments","weightedBoolean","trueWeight","getBucket","buckets","true","false","statsvTracker","bucketingRate","isStatsvEnabled","getStatsvTracker","using","eventLog","navigator","createElement","src","isEventLoggingEnabled","getEventLoggingTracker","createIsEnabled","__REDUX_DEVTOOLS_EXTENSION_COMPOSE__","ReduxThunk","actions","createPreviewBehavior","registerChangeListeners","popups","createMediaWikiPopupsObject","invalidLinksSelector","validLinkSelector","rendererInit","titleFromElement","fragment"],"mappings":"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","file":"index.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./src/index.js\");\n","!function(t,e){\"object\"==typeof exports&&\"object\"==typeof module?module.exports=e():\"function\"==typeof define&&define.amd?define([],e):\"object\"==typeof exports?exports.ReduxThunk=e():t.ReduxThunk=e()}(this,function(){return function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={exports:{},id:o,loaded:!1};return t[o].call(r.exports,r,r.exports,e),r.loaded=!0,r.exports}var n={};return e.m=t,e.c=n,e.p=\"\",e(0)}([function(t,e,n){t.exports=n(1)},function(t,e){\"use strict\";function n(t){return function(e){var n=e.dispatch,o=e.getState;return function(e){return function(r){return\"function\"==typeof r?r(n,o,t):e(r)}}}}e.__esModule=!0;var o=n();o.withExtraArgument=n,e.default=o}])});","!function(e,t){\"object\"==typeof exports&&\"undefined\"!=typeof module?t(exports):\"function\"==typeof define&&define.amd?define([\"exports\"],t):t(e.Redux={})}(this,function(e){\"use strict\";var t=function(e){var t,r=e.Symbol;return\"function\"==typeof r?r.observable?t=r.observable:(t=r(\"observable\"),r.observable=t):t=\"@@observable\",t}(\"undefined\"!=typeof self?self:\"undefined\"!=typeof window?window:\"undefined\"!=typeof global?global:\"undefined\"!=typeof module?module:Function(\"return this\")()),r=function(){return Math.random().toString(36).substring(7).split(\"\").join(\".\")},n={INIT:\"@@redux/INIT\"+r(),REPLACE:\"@@redux/REPLACE\"+r(),PROBE_UNKNOWN_ACTION:function(){return\"@@redux/PROBE_UNKNOWN_ACTION\"+r()}};function o(e,t){var r=t&&t.type;return\"Given \"+(r&&'action \"'+r+'\"'||\"an action\")+', reducer \"'+e+'\" returned undefined. To ignore an action, you must explicitly return the previous state. If you want this reducer to hold no value, you can return null instead of undefined.'}function i(e,t){return function(){return t(e.apply(this,arguments))}}function u(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(){for(var e=arguments.length,t=Array(e),r=0;e>r;r++)t[r]=arguments[r];return 0===t.length?function(e){return e}:1===t.length?t[0]:t.reduce(function(e,t){return function(){return e(t.apply(void 0,arguments))}})}e.createStore=function e(r,o,i){var u;if(\"function\"==typeof o&&\"function\"==typeof i||\"function\"==typeof i&&\"function\"==typeof arguments[3])throw Error(\"It looks like you are passing several store enhancers to createStore(). This is not supported. Instead, compose them together to a single function\");if(\"function\"==typeof o&&void 0===i&&(i=o,o=void 0),void 0!==i){if(\"function\"!=typeof i)throw Error(\"Expected the enhancer to be a function.\");return i(e)(r,o)}if(\"function\"!=typeof r)throw Error(\"Expected the reducer to be a function.\");var a=r,c=o,f=[],s=f,d=!1;function l(){s===f&&(s=f.slice())}function p(){if(d)throw Error(\"You may not call store.getState() while the reducer is executing. The reducer has already received the state as an argument. Pass it down from the top reducer instead of reading it from the store.\");return c}function h(e){if(\"function\"!=typeof e)throw Error(\"Expected the listener to be a function.\");if(d)throw Error(\"You may not call store.subscribe() while the reducer is executing. If you would like to be notified after the store has been updated, subscribe from a component and invoke store.getState() in the callback to access the latest state. See https://redux.js.org/api-reference/store#subscribe(listener) for more details.\");var t=!0;return l(),s.push(e),function(){if(t){if(d)throw Error(\"You may not unsubscribe from a store listener while the reducer is executing. See https://redux.js.org/api-reference/store#subscribe(listener) for more details.\");t=!1,l();var r=s.indexOf(e);s.splice(r,1)}}}function y(e){if(!function(e){if(\"object\"!=typeof e||null===e)return!1;for(var t=e;null!==Object.getPrototypeOf(t);)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}(e))throw Error(\"Actions must be plain objects. Use custom middleware for async actions.\");if(void 0===e.type)throw Error('Actions may not have an undefined \"type\" property. Have you misspelled a constant?');if(d)throw Error(\"Reducers may not dispatch actions.\");try{d=!0,c=a(c,e)}finally{d=!1}for(var t=f=s,r=0;t.length>r;r++)(0,t[r])();return e}return y({type:n.INIT}),(u={dispatch:y,subscribe:h,getState:p,replaceReducer:function(e){if(\"function\"!=typeof e)throw Error(\"Expected the nextReducer to be a function.\");a=e,y({type:n.REPLACE})}})[t]=function(){var e,r=h;return(e={subscribe:function(e){if(\"object\"!=typeof e||null===e)throw new TypeError(\"Expected the observer to be an object.\");function t(){e.next&&e.next(p())}return t(),{unsubscribe:r(t)}}})[t]=function(){return this},e},u},e.combineReducers=function(e){for(var t=Object.keys(e),r={},i=0;t.length>i;i++){var u=t[i];\"function\"==typeof e[u]&&(r[u]=e[u])}var a,c=Object.keys(r);try{!function(e){Object.keys(e).forEach(function(t){var r=e[t];if(void 0===r(void 0,{type:n.INIT}))throw Error('Reducer \"'+t+\"\\\" returned undefined during initialization. If the state passed to the reducer is undefined, you must explicitly return the initial state. The initial state may not be undefined. If you don't want to set a value for this reducer, you can use null instead of undefined.\");if(void 0===r(void 0,{type:n.PROBE_UNKNOWN_ACTION()}))throw Error('Reducer \"'+t+\"\\\" returned undefined when probed with a random type. Don't try to handle \"+n.INIT+' or other actions in \"redux/*\" namespace. They are considered private. Instead, you must return the current state for any unknown actions, unless it is undefined, in which case you must return the initial state, regardless of the action type. The initial state may not be undefined, but can be null.')})}(r)}catch(e){a=e}return function(e,t){if(void 0===e&&(e={}),a)throw a;for(var n=!1,i={},u=0;c.length>u;u++){var f=c[u],s=e[f],d=(0,r[f])(s,t);if(void 0===d){var l=o(f,t);throw Error(l)}i[f]=d,n=n||d!==s}return n?i:e}},e.bindActionCreators=function(e,t){if(\"function\"==typeof e)return i(e,t);if(\"object\"!=typeof e||null===e)throw Error(\"bindActionCreators expected an object or a function, instead received \"+(null===e?\"null\":typeof e)+'. Did you write \"import ActionCreators from\" instead of \"import * as ActionCreators from\"?');for(var r=Object.keys(e),n={},o=0;r.length>o;o++){var u=r[o],a=e[u];\"function\"==typeof a&&(n[u]=i(a,t))}return n},e.applyMiddleware=function(){for(var e=arguments.length,t=Array(e),r=0;e>r;r++)t[r]=arguments[r];return function(e){return function(){var r=e.apply(void 0,arguments),n=function(){throw Error(\"Dispatching while constructing your middleware is not allowed. Other middleware would not be applied to this dispatch.\")},o={getState:r.getState,dispatch:function(){return n.apply(void 0,arguments)}},i=t.map(function(e){return e(o)});return function(e){for(var t=1;arguments.length>t;t++){var r=null!=arguments[t]?arguments[t]:{},n=Object.keys(r);\"function\"==typeof Object.getOwnPropertySymbols&&(n=n.concat(Object.getOwnPropertySymbols(r).filter(function(e){return Object.getOwnPropertyDescriptor(r,e).enumerable}))),n.forEach(function(t){u(e,t,r[t])})}return e}({},r,{dispatch:n=a.apply(void 0,i)(r.dispatch)})}}},e.compose=a,e.__DO_NOT_USE__ActionTypes=n,Object.defineProperty(e,\"__esModule\",{value:!0})});\n","var g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn this;\n})();\n\ntry {\n\t// This works if eval is allowed (see CSP)\n\tg = g || new Function(\"return this\")();\n} catch (e) {\n\t// This works if the window reference is available\n\tif (typeof window === \"object\") g = window;\n}\n\n// g can still be undefined, but nothing to do about it...\n// We return undefined, instead of nothing here, so it's\n// easier to handle this case. if(!global) { ...}\n\nmodule.exports = g;\n","module.exports = function(module) {\n\tif (!module.webpackPolyfill) {\n\t\tmodule.deprecate = function() {};\n\t\tmodule.paths = [];\n\t\t// module.parent = undefined by default\n\t\tif (!module.children) module.children = [];\n\t\tObject.defineProperty(module, \"loaded\", {\n\t\t\tenumerable: true,\n\t\t\tget: function() {\n\t\t\t\treturn module.l;\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(module, \"id\", {\n\t\t\tenumerable: true,\n\t\t\tget: function() {\n\t\t\t\treturn module.i;\n\t\t\t}\n\t\t});\n\t\tmodule.webpackPolyfill = 1;\n\t}\n\treturn module;\n};\n","/**\n * @module counts\n */\n\n/**\n * Gets the count bucket for the number of edits a user has made.\n *\n * The buckets are defined as part of\n * [the Popups schema](https://meta.wikimedia.org/wiki/Schema:Popups).\n *\n * Extracted from `mw.popups.schemaPopups.getEditCountBucket`.\n *\n * @param {number} count\n * @return {string}\n */\nexports.getEditCountBucket = function getEditCountBucket( count ) {\n\tlet bucket;\n\n\tif ( count === 0 ) {\n\t\tbucket = '0';\n\t} else if ( count >= 1 && count <= 4 ) {\n\t\tbucket = '1-4';\n\t} else if ( count >= 5 && count <= 99 ) {\n\t\tbucket = '5-99';\n\t} else if ( count >= 100 && count <= 999 ) {\n\t\tbucket = '100-999';\n\t} else if ( count >= 1000 ) {\n\t\tbucket = '1000+';\n\t}\n\n\treturn `${ bucket } edits`;\n};\n\n/**\n * Gets the count bucket for the number of previews a user has seen.\n *\n * If local storage isn't available - because the user has disabled it\n * or the browser doesn't support it - then \"unknown\" is returned.\n *\n * The buckets are defined as part of\n * [the Popups schema](https://meta.wikimedia.org/wiki/Schema:Popups).\n *\n * Extracted from `mw.popups.getPreviewCountBucket`.\n *\n * @param {number|null|string|boolean} [count]\n * @return {string}\n */\nexports.getPreviewCountBucket = function getPreviewCountBucket( count ) {\n\tlet bucket;\n\n\tif ( count === 0 ) {\n\t\tbucket = '0';\n\t} else if ( count >= 1 && count <= 4 ) {\n\t\tbucket = '1-4';\n\t} else if ( count >= 5 && count <= 20 ) {\n\t\tbucket = '5-20';\n\t} else if ( count >= 21 ) {\n\t\tbucket = '21+';\n\t}\n\n\treturn bucket !== undefined ? ( `${ bucket } previews` ) : 'unknown';\n};\n","import bracketedPixelRatio from './bracketedPixelRatio';\n\nconst bpr = bracketedPixelRatio();\n\nexport default {\n\tBRACKETED_DEVICE_PIXEL_RATIO: bpr,\n\tTHUMBNAIL_SIZE: 320 * bpr,\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 preview/model\n */\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 */\n\tTYPE_GENERIC: 'generic',\n\t/** standard preview */\n\tTYPE_PAGE: 'page',\n\t/** disambiguation preview */\n\tTYPE_DISAMBIGUATION: 'disambiguation',\n\t/** reference preview **/\n\tTYPE_REFERENCE: 'reference'\n};\n\nexport { previewTypes };\n\n/**\n * Preview Model\n *\n * @typedef {Object} PreviewModel\n * @property {string} title\n * @property {string} url The canonical URL of the page being previewed\n * @property {string} languageCode\n * @property {string} languageDirection Either \"ltr\" or \"rtl\"\n * @property {?Array} 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} type One of the previewTypes.TYPE_… constants.\n * @property {?Object|undefined} thumbnail\n * @property {?number|undefined} pageId\n *\n * @global\n */\n\n/**\n * Creates a 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} extract\n * @param {string} type\n * @param {?Object} [thumbnail]\n * @param {?number} [pageId]\n * @return {PreviewModel}\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 = getPreviewType( 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 preview model.\n *\n * @param {!string} title\n * @param {!string} url\n * @return {!PreviewModel}\n */\nexport function createNullModel( title, url ) {\n\treturn createModel( title, url, '', '', [], '' );\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 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 {string} [processedExtract]\n * @return {string} One of the previewTypes.TYPE_… constants.\n */\n\nfunction getPreviewType( type, processedExtract ) {\n\n\tif ( processedExtract === undefined ) {\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\tcase previewTypes.TYPE_REFERENCE:\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","const $ = jQuery,\n\tmw = mediaWiki;\n\n/**\n * Improves the plain text extracts\n * @param {string} plainTextExtract\n * @param {string} title\n * @return {Array}\n */\nexport function formatPlainTextExtract( plainTextExtract, title ) {\n\tlet extract = plainTextExtract;\n\tif ( plainTextExtract === undefined ) {\n\t\treturn [];\n\t}\n\n\t// After cleaning the extract it may have been blanked\n\tif ( extract.length === 0 ) {\n\t\treturn [];\n\t}\n\n\textract = makeTitleInExtractBold( extract, title );\n\treturn extract;\n}\n\n/**\n * Converts the extract into a list of elements, which correspond to fragments\n * of the extract. Fragments that match the title verbatim are wrapped in a\n * `<b>` element.\n *\n * Using the bolded elements of the extract of the page directly is covered by\n * [T141651](https://phabricator.wikimedia.org/T141651).\n *\n * Extracted from `mw.popups.renderer.article.getProcessedElements`.\n *\n * @param {string} extract\n * @param {string} title\n * @return {Array} A set of HTML Elements\n */\nfunction makeTitleInExtractBold( extract, title ) {\n\tconst elements = [],\n\t\tboldIdentifier = `<bi-${ Math.random() }>`,\n\t\tsnip = `<snip-${ Math.random() }>`;\n\n\ttitle = title.replace( /\\s+/g, ' ' ).trim(); // Remove extra white spaces\n\tconst escapedTitle = mw.RegExp.escape( title ); // Escape RegExp elements\n\tconst regExp = new RegExp( `(^|\\\\s)(${ escapedTitle })(|$)`, 'i' );\n\n\t// Remove text in parentheses along with the parentheses\n\textract = extract.replace( /\\s+/, ' ' ); // Remove extra white spaces\n\n\t// Make title bold in the extract text\n\t// As the extract is html escaped there can be no such string in it\n\t// Also, the title is escaped of RegExp elements thus can't have \"*\"\n\textract = extract.replace(\n\t\tregExp,\n\t\t`$1${ snip }${ boldIdentifier }$2${ snip }$3`\n\t);\n\textract = extract.split( snip );\n\n\textract.forEach( ( part ) => {\n\t\tif ( part.indexOf( boldIdentifier ) === 0 ) {\n\t\t\telements.push( $( '<b>' )\n\t\t\t\t.text( part.substring( boldIdentifier.length ) ) );\n\t\t} else {\n\t\t\telements.push( document.createTextNode( part ) );\n\t\t}\n\t} );\n\n\treturn elements;\n}\n","/**\n * @module gateway/mediawiki\n */\n\nimport { createModel } from '../preview/model';\nimport * as formatter from '../formatter';\n\n// Public and private cache lifetime (5 minutes)\n//\n// FIXME: Move this to src/constants.js.\nconst CACHE_LIFETIME = 300,\n\t$ = jQuery;\n\n/**\n * @typedef {Gateway} MediaWikiGateway\n * @prop {Function(object): object} extractPageFromResponse\n * @prop {Function(object): object} formatPlainTextExtract\n */\n\n/**\n * Creates an instance of the MediaWiki API gateway.\n *\n * @param {mw.Api} api\n * @param {Object} config Configuration that affects the major behavior of the\n * gateway.\n * @param {number} config.THUMBNAIL_SIZE The length of the major dimension of\n * the thumbnail.\n * @param {number} config.EXTRACT_LENGTH The maximum length, in characters,\n * of the extract.\n * @param {string} config.acceptLanguage The accepted language sent in the\n * header\n * @return {MediaWikiGateway}\n */\nexport default function createMediaWikiApiGateway( api, config ) {\n\tfunction fetch( title ) {\n\t\treturn api.get( {\n\t\t\taction: 'query',\n\t\t\tprop: 'info|extracts|pageimages|revisions|info',\n\t\t\tformatversion: 2,\n\t\t\tredirects: true,\n\t\t\texintro: true,\n\t\t\texchars: config.EXTRACT_LENGTH,\n\n\t\t\t// There is an added geometric limit on .mwe-popups-extract\n\t\t\t// so that text does not overflow from the card.\n\t\t\texplaintext: true,\n\n\t\t\tpiprop: 'thumbnail',\n\t\t\tpithumbsize: config.THUMBNAIL_SIZE,\n\t\t\tpilicense: 'any',\n\t\t\trvprop: 'timestamp',\n\t\t\tinprop: 'url',\n\t\t\ttitles: title,\n\t\t\tsmaxage: CACHE_LIFETIME,\n\t\t\tmaxage: CACHE_LIFETIME,\n\t\t\tuselang: 'content'\n\t\t}, {\n\t\t\theaders: {\n\t\t\t\t'X-Analytics': 'preview=1',\n\t\t\t\t'Accept-Language': config.acceptLanguage\n\t\t\t}\n\t\t} );\n\t}\n\n\t/**\n\t * @param {mw.Title} title\n\t * @returns {AbortPromise<PreviewModel>}\n\t */\n\tfunction fetchPreviewForTitle( title ) {\n\t\tconst xhr = fetch( title.getPrefixedDb() );\n\t\treturn xhr.then( ( data ) => {\n\t\t\tconst page = extractPageFromResponse( data );\n\t\t\tconst plainTextExtract = formatPlainTextExtract( page );\n\t\t\treturn convertPageToModel( plainTextExtract );\n\t\t} ).promise( {\n\t\t\tabort() {\n\t\t\t\txhr.abort();\n\t\t\t}\n\t\t} );\n\t}\n\n\treturn {\n\t\tfetch,\n\t\textractPageFromResponse,\n\t\tconvertPageToModel,\n\t\tfetchPreviewForTitle,\n\t\tformatPlainTextExtract\n\t};\n}\n\n/**\n * Extracts page data from the API response.\n *\n * @function\n * @name MediaWikiGateway#extractPageFromResponse\n * @param {Object} data The response\n * @throws {Error} If the response is empty or doesn't contain data about the\n * page\n * @return {Object}\n */\nfunction extractPageFromResponse( data ) {\n\tif (\n\t\tdata.query &&\n\t\tdata.query.pages &&\n\t\tdata.query.pages.length\n\t) {\n\t\treturn data.query.pages[ 0 ];\n\t}\n\n\tthrow new Error( 'API response `query.pages` is empty.' );\n}\n\n/**\n * Make plain text nicer by applying formatter.\n *\n * @function\n * @name MediaWikiGateway#formatPlainTextExtract\n * @param {Object} data The response\n * @return {Object}\n */\nfunction formatPlainTextExtract( data ) {\n\tconst result = $.extend( {}, data );\n\tresult.extract = formatter.formatPlainTextExtract( data.extract, data.title );\n\treturn result;\n}\n\n/**\n * Converts the API response to a preview model.\n *\n * @function\n * @name MediaWikiGateway#convertPageToModel\n * @param {Object} page\n * @return {PreviewModel}\n */\nfunction convertPageToModel( page ) {\n\treturn createModel(\n\t\tpage.title,\n\t\tpage.canonicalurl,\n\t\tpage.pagelanguagehtmlcode,\n\t\tpage.pagelanguagedir,\n\t\tpage.extract,\n\t\tpage.type,\n\t\tpage.thumbnail,\n\t\tpage.pageid\n\t);\n}\n","/**\n * @module gateway/rest\n */\n\nimport { createModel } from '../preview/model';\n\nconst RESTBASE_PROFILE = 'https://www.mediawiki.org/wiki/Specs/Summary/1.2.0',\n\tmw = mediaWiki,\n\t$ = jQuery;\n\n/** @typedef {Function(JQuery.AjaxSettings=): JQuery.jqXHR} Ajax */\n\n/**\n * Creates an instance of the RESTBase gateway.\n *\n * This gateway differs from the {@link MediaWikiGateway MediaWiki gateway} in\n * that it fetches page data from [the RESTBase page summary endpoint][0].\n *\n * [0]: https://en.wikipedia.org/api/rest_v1/#!/Page_content/get_page_summary_title\n *\n * @param {Ajax} ajax A function with the same signature as `jQuery.ajax`\n * @param {Object} config Configuration that affects the major behavior of the\n * gateway.\n * @param {Function} extractParser A function that takes response and returns\n * parsed extract\n * @return {Gateway}\n */\nexport default function createRESTBaseGateway( ajax, config, extractParser ) {\n\t/**\n\t * Fetches page data from [the RESTBase page summary endpoint][0].\n\t *\n\t * [0]: https://en.wikipedia.org/api/rest_v1/#!/Page_content/get_page_summary_title\n\t *\n\t * @function\n\t * @name RESTBaseGateway#fetch\n\t * @param {string} title\n\t * @return {JQuery.jqXHR}\n\t */\n\tfunction fetch( title ) {\n\t\tconst endpoint = config.endpoint;\n\n\t\treturn ajax( {\n\t\t\turl: endpoint + encodeURIComponent( title ),\n\t\t\theaders: {\n\t\t\t\tAccept: `application/json; charset=utf-8; profile=\"${ RESTBASE_PROFILE }\"`,\n\t\t\t\t'Accept-Language': config.acceptLanguage\n\t\t\t}\n\t\t} );\n\t}\n\n\t/**\n\t * @param {mw.Title} title\n\t * @returns {AbortPromise<PreviewModel>}\n\t */\n\tfunction fetchPreviewForTitle( title ) {\n\t\tconst titleText = title.getPrefixedDb(),\n\t\t\txhr = fetch( titleText );\n\t\treturn xhr.then( ( page ) => {\n\t\t\t// Endpoint response may be empty or simply missing a title.\n\t\t\tif ( !page || !page.title ) {\n\t\t\t\tpage = $.extend( true, page || {}, { title: titleText } );\n\t\t\t}\n\t\t\t// And extract may be omitted if empty string\n\t\t\tif ( page.extract === undefined ) {\n\t\t\t\tpage.extract = '';\n\t\t\t}\n\t\t\treturn convertPageToModel(\n\t\t\t\tpage, config.THUMBNAIL_SIZE, extractParser\n\t\t\t);\n\t\t} ).catch( ( jqXHR, textStatus, errorThrown ) => {\n\t\t\t// The client will choose how to handle these errors which may include\n\t\t\t// those due to HTTP 4xx and 5xx status. The rejection typing matches\n\t\t\t// fetch failures.\n\t\t\treturn $.Deferred().reject( 'http', {\n\t\t\t\txhr: jqXHR,\n\t\t\t\ttextStatus,\n\t\t\t\texception: errorThrown\n\t\t\t} );\n\t\t} ).promise( { abort() { xhr.abort(); } } );\n\t}\n\n\treturn {\n\t\tfetch,\n\t\tconvertPageToModel,\n\t\tfetchPreviewForTitle\n\t};\n}\n\n/**\n * Checks whether the `originalImage` property contains an image\n * format that's safe to render.\n * https://www.mediawiki.org/wiki/Help:Images#Supported_media_types_for_images\n *\n * @param {string} filename\n *\n * @return {boolean}\n */\nfunction isSafeImgFormat( filename ) {\n\tconst safeImage = new RegExp( /\\.(jpg|jpeg|png|gif)$/i );\n\treturn safeImage.test( filename );\n}\n\n/**\n * Resizes the thumbnail to the requested width, preserving its aspect ratio.\n *\n * The requested width is limited to that of the original image unless the image\n * is an SVG, which can be scaled infinitely.\n *\n * This function is only intended to mangle the pretty thumbnail URLs used on\n * Wikimedia Commons. Once [an official thumb API](https://phabricator.wikimedia.org/T66214)\n * is fully specified and implemented, this function can be made more general.\n *\n * @param {Object} thumbnail The thumbnail image\n * @param {Object} original The original image\n * @param {number} thumbSize The requested size\n * @return {Object|undefined}\n */\nfunction generateThumbnailData( thumbnail, original, thumbSize ) {\n\tconst parts = thumbnail.source.split( '/' ),\n\t\tlastPart = parts[ parts.length - 1 ],\n\t\toriginalIsSafe = isSafeImgFormat( original.source ) || undefined;\n\n\t// The last part, the thumbnail's full filename, is in the following form:\n\t// ${width}px-${filename}.${extension}. Splitting the thumbnail's filename\n\t// makes this function resilient to the thumbnail not having the same\n\t// extension as the original image, which is definitely the case for SVG's\n\t// where the thumbnail's extension is .svg.png.\n\tconst filenamePxIndex = lastPart.indexOf( 'px-' );\n\tif ( filenamePxIndex === -1 ) {\n\t\t// The thumbnail size is not customizable. Presumably, RESTBase requested a\n\t\t// width greater than the original and so MediaWiki returned the original's\n\t\t// URL instead of a thumbnail compatible URL. An original URL does not have\n\t\t// a \"thumb\" path, e.g.:\n\t\t//\n\t\t// https://upload.wikimedia.org/wikipedia/commons/a/aa/Red_Giant_Earth_warm.jpg\n\t\t//\n\t\t// Instead of:\n\t\t//\n\t\t// https://upload.wikimedia.org/wikipedia/commons/thumb/a/aa/Red_Giant_Earth_warm.jpg/512px-Red_Giant_Earth_warm.jpg\n\t\t//\n\t\t// Use the original if it's a supported image format.\n\t\treturn originalIsSafe && original;\n\t}\n\tconst filename = lastPart.substr( filenamePxIndex + 3 );\n\n\t// Scale the thumbnail's largest dimension.\n\tlet width, height;\n\tif ( thumbnail.width > thumbnail.height ) {\n\t\twidth = thumbSize;\n\t\theight = Math.floor( ( thumbSize / thumbnail.width ) * thumbnail.height );\n\t} else {\n\t\twidth = Math.floor( ( thumbSize / thumbnail.height ) * thumbnail.width );\n\t\theight = thumbSize;\n\t}\n\n\t// If the image isn't an SVG, then it shouldn't be scaled past its original\n\t// dimensions.\n\tif ( width >= original.width && filename.indexOf( '.svg' ) === -1 ) {\n\t\t// if the image format is not supported, it shouldn't be rendered.\n\t\treturn originalIsSafe && original;\n\t}\n\n\tparts[ parts.length - 1 ] = `${ width }px-${ filename }`;\n\n\treturn {\n\t\tsource: parts.join( '/' ),\n\t\twidth,\n\t\theight\n\t};\n}\n\n/**\n * Converts the API response to a preview model.\n *\n * @function\n * @name RESTBaseGateway#convertPageToModel\n * @param {Object} page\n * @param {number} thumbSize\n * @param {Function} extractParser\n * @return {PreviewModel}\n */\nexport function convertPageToModel( page, thumbSize, extractParser ) {\n\treturn createModel(\n\t\tpage.title,\n\t\tnew mw.Title( page.title ).getUrl(),\n\t\tpage.lang,\n\t\tpage.dir,\n\t\textractParser( page ),\n\t\tpage.type,\n\t\tpage.thumbnail ?\n\t\t\tgenerateThumbnailData(\n\t\t\t\tpage.thumbnail, page.originalimage, thumbSize\n\t\t\t) : undefined,\n\t\tpage.pageid\n\t);\n}\n","import * as formatter from '../formatter';\n\n/**\n * Prepare extract\n * @param {Object} page Rest response\n * @return {Array} An array of DOM Elements\n */\nexport function parseHTMLResponse( page ) {\n\tconst extract = page.extract_html;\n\n\treturn extract.length === 0 ? [] : $.parseHTML( extract );\n}\n\n/**\n * Prepare extract\n * @param {Object} page Rest response\n * @return {Array} An array of DOM Elements\n */\nexport function parsePlainTextResponse( page ) {\n\treturn formatter.formatPlainTextExtract( page.extract, page.title );\n}\n","import constants from '../constants';\nimport createMediaWikiApiGateway from './mediawiki';\nimport createRESTBaseGateway from './rest';\nimport * as formatters from './restFormatters';\n\nconst mw = mediaWiki,\n\t$ = jQuery;\n\n/**\n * The interface implemented by all preview gateways.\n * @typedef Gateway\n * @prop {Function(string): JQuery.jqXHR} fetch\n * @prop {FetchPreviewForTitle} fetchPreviewForTitle\n * @prop {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 * @template T\n * @typedef {JQuery.Promise<T>} AbortPromise\n * @prop {Function(): void} abort\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): AbortPromise<PreviewModel>} FetchPreviewForTitle\n */\n\n/**\n * Converts the API response to a preview model. Exposed for testing only.\n *\n * @typedef {Function(object, ...any): PreviewModel} ConvertPageToModel\n */\n\n/**\n * Creates a gateway with sensible values for the dependencies.\n *\n * @param {mw.Map} config\n * @return {Gateway}\n */\nexport default function createPagePreviewGateway( config ) {\n\tconst gatewayConfig = $.extend( {}, constants, {\n\t\tacceptLanguage: config.get( 'wgPageContentLanguage' )\n\t} );\n\tconst restConfig = $.extend( {}, gatewayConfig, {\n\t\tendpoint: config.get( 'wgPopupsRestGatewayEndpoint' )\n\t} );\n\tswitch ( config.get( 'wgPopupsGateway' ) ) {\n\t\tcase 'mwApiPlain':\n\t\t\treturn createMediaWikiApiGateway( new mw.Api(), gatewayConfig );\n\t\tcase 'restbasePlain':\n\t\t\treturn createRESTBaseGateway(\n\t\t\t\t$.ajax, restConfig, formatters.parsePlainTextResponse );\n\t\tcase 'restbaseHTML':\n\t\t\treturn createRESTBaseGateway(\n\t\t\t\t$.ajax, restConfig, formatters.parseHTMLResponse );\n\t\tdefault:\n\t\t\tthrow new Error( 'Unknown gateway' );\n\t}\n}\n","/**\n * @module gateway/reference\n */\n\nimport { previewTypes } from '../preview/model';\n\nconst mw = mediaWiki,\n\t$ = jQuery;\n\n/**\n * @return {Gateway}\n */\nexport default function createReferenceGateway() {\n\t/**\n\t * @param {mw.Title} title\n\t * @returns {AbortPromise<PreviewModel>}\n\t */\n\tfunction fetchPreviewForTitle( title ) {\n\t\tconst id = title.getFragment();\n\n\t\treturn $.Deferred().resolve( {\n\t\t\ttitle: mw.msg( 'popups-refpreview-footnote' ),\n\t\t\turl: `#${id}`,\n\t\t\t// TODO: Can probably be removed\n\t\t\t// languageCode: 'en',\n\t\t\t// languageDirection: 'ltr',\n\t\t\textract: $( `#${id} .reference-text` ).html(),\n\t\t\ttype: previewTypes.TYPE_REFERENCE\n\t\t\t// TODO: Can probably be removed\n\t\t\t// thumbnail: '',\n\t\t\t// pageId: '0'\n\t\t} ).promise( { abort() {} } );\n\t}\n\n\treturn {\n\t\tfetchPreviewForTitle\n\t};\n}\n","/**\n * @module userSettings\n */\n\n/**\n * @interface UserSettings\n *\n * @global\n */\n\nconst IS_ENABLED_KEY = 'mwe-popups-enabled',\n\tPREVIEW_COUNT_KEY = 'ext.popups.core.previewCount';\n\n/**\n * Creates an object whose methods encapsulate all interactions with the UA's\n * storage.\n *\n * @param {Object} storage The `mw.storage` singleton instance\n *\n * @return {UserSettings}\n */\nexport default function createUserSettings( storage ) {\n\treturn {\n\n\t\t/**\n\t\t * Gets whether the user has previously enabled Page Previews.\n\t\t *\n\t\t * N.B. that if the user hasn't previously enabled or disabled Page\n\t\t * Previews, i.e. userSettings.setIsEnabled(true), then they are treated as\n\t\t * if they have enabled them.\n\t\t *\n\t\t * @function\n\t\t * @name UserSettings#getIsEnabled\n\t\t * @return {boolean}\n\t\t */\n\t\tgetIsEnabled() {\n\t\t\treturn storage.get( IS_ENABLED_KEY ) !== '0';\n\t\t},\n\n\t\t/**\n\t\t * Sets whether the user has enabled Page Previews.\n\t\t *\n\t\t * @function\n\t\t * @name UserSettings#setIsEnabled\n\t\t * @param {boolean} isEnabled\n\t\t * @return {void}\n\t\t */\n\t\tsetIsEnabled( isEnabled ) {\n\t\t\tstorage.set( IS_ENABLED_KEY, isEnabled ? '1' : '0' );\n\t\t},\n\n\t\t/**\n\t\t * Gets whether the user has previously enabled **or disabled** Page\n\t\t * Previews.\n\t\t *\n\t\t * @function\n\t\t * @name UserSettings#hasIsEnabled\n\t\t * @return {boolean}\n\t\t */\n\t\thasIsEnabled() {\n\t\t\tconst value = storage.get( IS_ENABLED_KEY );\n\n\t\t\treturn Boolean( value ) !== false;\n\t\t},\n\n\t\t/**\n\t\t * Gets the number of previews that the user has seen.\n\t\t *\n\t\t * - If the storage isn't available, then -1 is returned.\n\t\t * - If the value in storage is not a number it will override stored value\n\t\t * to 0\n\t\t *\n\t\t * @function\n\t\t * @name UserSettings#getPreviewCount\n\t\t * @return {number}\n\t\t */\n\t\tgetPreviewCount() {\n\t\t\tconst result = storage.get( PREVIEW_COUNT_KEY );\n\n\t\t\tif ( result === false ) {\n\t\t\t\treturn -1;\n\t\t\t} else if ( result === null ) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tlet count = parseInt( result, 10 );\n\n\t\t\t// stored number is not a zero, override it to zero and store new value\n\t\t\tif ( isNaN( count ) ) {\n\t\t\t\tcount = 0;\n\t\t\t\tthis.setPreviewCount( count );\n\t\t\t}\n\t\t\treturn count;\n\t\t},\n\n\t\t/**\n\t\t * Sets the number of previews that the user has seen.\n\t\t *\n\t\t * @function\n\t\t * @name UserSettings#setPreviewCount\n\t\t * @param {number} count\n\t\t * @return {void}\n\t\t */\n\t\tsetPreviewCount( count ) {\n\t\t\tstorage.set( PREVIEW_COUNT_KEY, count.toString() );\n\t\t}\n\t};\n}\n","/**\n * @module previewBehaviour\n */\n\nconst mw = mediaWiki;\n\n/**\n * A collection of event handlers specific to how the user interacts with all\n * previews. The event handlers are are agnostic to how/when they are bound\n * //but not to what they are bound//, i.e. the showSettings event handler is\n * written to be bound to either an `<a>` or `<button>` element.\n *\n * @typedef {Object} ext.popups.PreviewBehavior\n * @property {string} settingsUrl\n * @property {Function} showSettings\n * @property {Function} previewDwell\n * @property {Function} previewAbandon\n * @property {Function} previewShow\n * @property {Function} click handler for the entire preview\n */\n\n/**\n * Creates an instance of `ext.popups.PreviewBehavior`.\n *\n * If the user is logged out, then clicking the cog should show the settings\n * modal.\n *\n * If the user is logged in, then clicking the cog should send them to the\n * the \"Appearance\" tab otherwise.\n *\n * @param {mw.User} user\n * @param {Object} actions The action creators bound to the Redux store\n * @return {ext.popups.PreviewBehavior}\n */\nexport default function createPreviewBehavior( user, actions ) {\n\tlet settingsUrl, showSettings = () => {};\n\n\tif ( user.isAnon() ) {\n\t\tshowSettings = ( event ) => {\n\t\t\tevent.preventDefault();\n\n\t\t\tactions.showSettings();\n\t\t};\n\t} else {\n\t\tconst rawTitle = 'Special:Preferences#mw-prefsection-rendering';\n\n\t\tsettingsUrl = mw.Title.newFromText( rawTitle )\n\t\t\t.getUrl();\n\t}\n\n\treturn {\n\t\tsettingsUrl,\n\t\tshowSettings,\n\t\tpreviewDwell: actions.previewDwell,\n\t\tpreviewAbandon: actions.abandon,\n\t\tpreviewShow: actions.previewShow,\n\t\tclick: actions.linkClick\n\t};\n}\n","/**\n * @module templateUtil\n */\n\nconst mw = mediaWiki;\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","/**\n * @module settingsDialog\n */\n\nimport { escapeHTML } from '../templateUtil';\n\n/**\n * @typedef {Object} SettingsModel\n * @property {string} heading\n * @property {string} closeLabel\n * @property {string} saveLabel\n * @property {string} helpText\n * @property {string} okLabel\n * @property {SettingsChoiceModel[]} [choices]\n *\n * @global\n */\n\n/**\n * @typedef {Object} SettingsChoiceModel\n * @property {string} id Portion of the elements' IDs and value of the input.\n * @property {string} name\n * @property {string} [description]\n * @property {boolean} [isChecked] Whether the setting is checked.\n *\n * @global\n */\n\n/**\n * @param {SettingsChoiceModel[]} [choices]\n * @return {SettingsChoiceModel}\n */\nfunction escapeChoices( choices = [] ) {\n\treturn choices.map(\n\t\t( { id, name, description, isChecked } ) =>\n\t\t\t( {\n\t\t\t\tid: escapeHTML( id ),\n\t\t\t\tname: escapeHTML( name ),\n\t\t\t\tdescription: description ? escapeHTML( description ) : '',\n\t\t\t\tisChecked\n\t\t\t} )\n\t);\n}\n\n/**\n * @param {SettingsModel} model\n * @return {string} HTML string.\n */\nexport function renderSettingsDialog( model ) {\n\tconst heading = escapeHTML( model.heading ),\n\t\tsaveLabel = escapeHTML( model.saveLabel ),\n\t\tcloseLabel = escapeHTML( model.closeLabel ),\n\t\thelpText = escapeHTML( model.helpText ),\n\t\tokLabel = escapeHTML( model.okLabel ),\n\t\tchoices = escapeChoices( model.choices );\n\treturn `\n\t\t<section id='mwe-popups-settings'>\n\t\t\t<header>\n\t\t\t\t<div>\n\t\t\t\t\t<div class='mw-ui-icon mw-ui-icon-element mw-ui-icon-popups-close close'>${ closeLabel }</div>\n\t\t\t\t</div>\n\t\t\t\t<h1>${ heading }</h1>\n\t\t\t\t<div>\n\t\t\t\t\t<button class='save mw-ui-button mw-ui-progressive'>${ saveLabel }</button>\n\t\t\t\t\t<button class='okay mw-ui-button mw-ui-progressive' style='display:none;'>${ okLabel }</button>\n\t\t\t\t</div>\n\t\t\t</header>\n\t\t\t<main id='mwe-popups-settings-form'>\n\t\t\t\t<form>\n\t\t\t\t\t${ choices.map( ( { id, name, description, isChecked } ) => `\n\t\t\t\t\t<p>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tname='mwe-popups-setting'\n\t\t\t\t\t\t\t${ isChecked ? 'checked' : '' }\n\t\t\t\t\t\t\tvalue='${ id }'\n\t\t\t\t\t\t\ttype='radio'\n\t\t\t\t\t\t\tid='mwe-popups-settings-${ id }'>\n\t\t\t\t\t\t<label for='mwe-popups-settings-${ id }'>\n\t\t\t\t\t\t\t<span>${ name }</span>\n\t\t\t\t\t\t\t${ description }\n\t\t\t\t\t\t</label>\n\t\t\t\t\t</p>` ).join( '' ) }\n\t\t\t\t</form>\n\t\t\t</main>\n\t\t\t<div class='mwe-popups-settings-help' style='display:none;'>\n\t\t\t\t<div class=\"mw-ui-icon mw-ui-icon-element mw-ui-icon-footer\"></div>\n\t\t\t\t<p>${ helpText }</p>\n\t\t\t</div>\n\t\t</section>\n\t`.trim();\n}\n","/**\n * @module settingsDialog\n */\n\nimport { renderSettingsDialog } from './templates/settingsDialog/settingsDialog';\n\nconst mw = mediaWiki;\n\n/**\n * Create the settings dialog shown to anonymous users.\n *\n * @param {boolean} navPopupsEnabled\n * @return {JQuery} settings dialog\n */\nexport function createSettingsDialog( navPopupsEnabled ) {\n\tconst choices = [\n\t\t{\n\t\t\tid: 'simple',\n\t\t\tname: mw.msg( 'popups-settings-option-simple' ),\n\t\t\tdescription: mw.msg( 'popups-settings-option-simple-description' ),\n\t\t\tisChecked: true\n\t\t},\n\t\t{\n\t\t\tid: 'advanced',\n\t\t\tname: mw.msg( 'popups-settings-option-advanced' ),\n\t\t\tdescription: mw.msg( 'popups-settings-option-advanced-description' )\n\t\t},\n\t\t{\n\t\t\tid: 'off',\n\t\t\tname: mw.msg( 'popups-settings-option-off' )\n\t\t}\n\t];\n\n\tif ( !navPopupsEnabled ) {\n\t\t// remove the advanced option\n\t\tchoices.splice( 1, 1 );\n\t}\n\n\t// render the template\n\tconst $el = $( $.parseHTML( renderSettingsDialog( {\n\t\theading: mw.msg( 'popups-settings-title' ),\n\t\tcloseLabel: mw.msg( 'popups-settings-cancel' ),\n\t\tsaveLabel: mw.msg( 'popups-settings-save' ),\n\t\thelpText: mw.msg( 'popups-settings-help' ),\n\t\tokLabel: mw.msg( 'popups-settings-help-ok' ),\n\t\tchoices\n\t} ) ) );\n\n\treturn $el;\n}\n","/**\n * @module settingsDialogRenderer\n */\n\nimport { createSettingsDialog } from './settingsDialog';\n\nconst $ = jQuery;\n\n/**\n * Creates a render function that will create the settings dialog and return\n * a set of methods to operate on it\n * @return {Function} render function\n */\nexport default function createSettingsDialogRenderer() {\n\n\t/**\n\t * Cached settings dialog\n\t *\n\t * @type {JQuery}\n\t */\n\tlet $dialog,\n\t\t/**\n\t\t * Cached settings overlay\n\t\t *\n\t\t * @type {JQuery}\n\t\t */\n\t\t$overlay;\n\n\t/**\n\t * Renders the relevant form and labels in the settings dialog\n\t * @param {Object} boundActions\n\t * @return {Object} object with methods to affect the rendered UI\n\t */\n\treturn ( boundActions ) => {\n\n\t\tif ( !$dialog ) {\n\t\t\t$dialog = createSettingsDialog( isNavPopupsEnabled() );\n\t\t\t$overlay = $( '<div>' ).addClass( 'mwe-popups-overlay' );\n\n\t\t\t// Setup event bindings\n\n\t\t\t$dialog.find( '.save' ).click( () => {\n\t\t\t\t// Find the selected value (simple|advanced|off)\n\t\t\t\tconst selected = getSelectedSetting( $dialog ),\n\t\t\t\t\t// Only simple means enabled, advanced is disabled in favor of\n\t\t\t\t\t// NavPops and off means disabled.\n\t\t\t\t\tenabled = selected === 'simple';\n\n\t\t\t\tboundActions.saveSettings( enabled );\n\t\t\t} );\n\t\t\t$dialog.find( '.close, .okay' ).click( boundActions.hideSettings );\n\t\t}\n\n\t\treturn {\n\t\t\t/**\n\t\t\t * Append the dialog and overlay to a DOM element\n\t\t\t * @param {HTMLElement} el\n\t\t\t * @return {void}\n\t\t\t */\n\t\t\tappendTo( el ) {\n\t\t\t\t$overlay.appendTo( el );\n\t\t\t\t$dialog.appendTo( $overlay );\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * Show the settings element and position it correctly\n\t\t\t * @return {void}\n\t\t\t */\n\t\t\tshow() {\n\t\t\t\t$overlay.show();\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * Hide the settings dialog.\n\t\t\t * @return {void}\n\t\t\t */\n\t\t\thide() {\n\t\t\t\t$overlay.hide();\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * Toggle the help dialog on or off\n\t\t\t * @param {boolean} visible if you want to show or hide the help dialog\n\t\t\t * @return {void}\n\t\t\t */\n\t\t\ttoggleHelp( visible ) {\n\t\t\t\ttoggleHelp( $dialog, visible );\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * Update the form depending on the enabled flag\n\t\t\t *\n\t\t\t * If false and no navpops, then checks 'off'\n\t\t\t * If true, then checks 'on'\n\t\t\t * If false, and there are navpops, then checks 'advanced'\n\t\t\t *\n\t\t\t * @param {boolean} enabled if page previews are enabled\n\t\t\t * @return {void}\n\t\t\t */\n\t\t\tsetEnabled( enabled ) {\n\t\t\t\tlet name = 'off';\n\t\t\t\tif ( enabled ) {\n\t\t\t\t\tname = 'simple';\n\t\t\t\t} else if ( isNavPopupsEnabled() ) {\n\t\t\t\t\tname = 'advanced';\n\t\t\t\t}\n\n\t\t\t\t// Check the appropriate radio button\n\t\t\t\t$dialog.find( `#mwe-popups-settings-${ name }` )\n\t\t\t\t\t.prop( 'checked', true );\n\t\t\t}\n\t\t};\n\t};\n}\n\n/**\n * Get the selected value on the radio button\n *\n * @param {JQuery.Object} $el the element to extract the setting from\n * @return {string} Which should be (simple|advanced|off)\n */\nfunction getSelectedSetting( $el ) {\n\treturn $el.find(\n\t\t'input[name=mwe-popups-setting]:checked, #mwe-popups-settings'\n\t).val();\n}\n\n/**\n * Toggles the visibility between a form and the help\n * @param {JQuery.Object} $el element that contains form and help\n * @param {boolean} visible if the help should be visible, or the form\n * @return {void}\n */\nfunction toggleHelp( $el, visible ) {\n\tconst $dialog = $( '#mwe-popups-settings' ),\n\t\tformSelectors = 'main, .save, .close',\n\t\thelpSelectors = '.mwe-popups-settings-help, .okay';\n\n\tif ( visible ) {\n\t\t$dialog.find( formSelectors ).hide();\n\t\t$dialog.find( helpSelectors ).show();\n\t} else {\n\t\t$dialog.find( formSelectors ).show();\n\t\t$dialog.find( helpSelectors ).hide();\n\t}\n}\n\n/**\n * Checks if the NavigationPopups gadget is enabled by looking at the global\n * variables\n * @return {boolean} if navpops was found to be enabled\n */\nfunction isNavPopupsEnabled() {\n\t/* global pg: false*/\n\treturn typeof pg !== 'undefined' && pg.fn.disablePopups !== undefined;\n}\n","/**\n * @module changeListener\n */\n\n/**\n * @typedef {Function} ext.popups.ChangeListener\n * @param {Object} prevState The previous state\n * @param {Object} state The current state\n */\n\n/**\n * Registers a change listener, which is bound to the\n * [store](http://redux.js.org/docs/api/Store.html).\n *\n * A change listener is a function that is only invoked when the state in the\n * [store](http://redux.js.org/docs/api/Store.html) changes. N.B. that there\n * may not be a 1:1 correspondence with actions being dispatched to the store\n * and the state in the store changing.\n *\n * See [Store#subscribe](http://redux.js.org/docs/api/Store.html#subscribe)\n * for more information about what change listeners may and may not do.\n *\n * @param {Redux.Store} store\n * @param {ext.popups.ChangeListener} callback\n * @return {void}\n */\nexport default function registerChangeListener( store, callback ) {\n\t// This function is based on the example in [the documentation for\n\t// Store#subscribe](http://redux.js.org/docs/api/Store.html#subscribe),\n\t// which was written by Dan Abramov.\n\n\tlet state;\n\n\tstore.subscribe( () => {\n\t\tconst prevState = state;\n\n\t\tstate = store.getState();\n\n\t\tif ( prevState !== state ) {\n\t\t\tcallback( prevState, state );\n\t\t}\n\t} );\n}\n","/**\n * @module title\n */\n\nconst mw = mediaWiki;\n\n/**\n * Gets the title of a local page from an href given some configuration.\n *\n * @param {string} href\n * @param {mw.Map} config\n * @return {string|undefined}\n */\nexport function getTitle( href, config ) {\n\tconst titleRegex = new RegExp( mw.RegExp.escape( config.get( 'wgArticlePath' ) )\n\t\t.replace( '\\\\$1', '(.+)' ) );\n\n\t// Skip every URI that mw.Uri cannot parse\n\tlet linkHref;\n\ttry {\n\t\tlinkHref = new mw.Uri( href );\n\t} catch ( e ) {\n\t\treturn undefined;\n\t}\n\n\t// External links\n\tif ( linkHref.host !== location.hostname ) {\n\t\treturn undefined;\n\t}\n\n\tconst queryLength = Object.keys( linkHref.query ).length;\n\n\t// No query params (pretty URL)\n\tif ( !queryLength ) {\n\t\tconst matches = titleRegex.exec( linkHref.path );\n\t\treturn matches ? decodeURIComponent( matches[ 1 ] ) : undefined;\n\t} else if ( queryLength === 1 && linkHref.query.hasOwnProperty( 'title' ) ) {\n\t\t// URL is not pretty, but only has a `title` parameter\n\t\treturn linkHref.query.title;\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Given a page title it will return the mediawiki.Title if it is an eligible\n * link for showing page previews, null otherwise\n *\n * @param {string} title page title to check if it should show preview\n * @param {number[]} contentNamespaces contentNamespaces as specified in\n * wgContentNamespaces\n * @return {mw.Title|null}\n */\nexport function isValid( title, contentNamespaces ) {\n\tif ( !title ) {\n\t\treturn null;\n\t}\n\n\t// Is title in a content namespace?\n\tconst mwTitle = mw.Title.newFromText( title );\n\tif ( mwTitle && contentNamespaces.indexOf( mwTitle.namespace ) >= 0 ) {\n\t\treturn mwTitle;\n\t}\n\n\treturn null;\n}\n\n/**\n * Return a mw.Title from a HTMLElement if valid for page previews. Convenience\n * method\n *\n * @param {Element} el\n * @param {mw.Map} config\n * @return {mw.Title|null}\n */\nexport function fromElement( el, config ) {\n\treturn isValid(\n\t\tgetTitle( el.href, config ),\n\t\tconfig.get( 'wgContentNamespaces' )\n\t);\n}\n","/**\n * @module wait\n */\n\nconst $ = jQuery;\n\n/**\n * A Promise usually for a long running or costly request that is abortable.\n * @template T\n * @typedef {JQuery.Promise<T>} AbortPromise\n * @prop {Function(): void} abort\n */\n\n/**\n * Sugar around `window.setTimeout`.\n *\n * @example\n * import wait from './wait';\n *\n * wait( 150 )\n * .then( () => {\n * // Continue processing...\n * } );\n *\n * @param {number} delay The number of milliseconds to wait\n * @return {AbortPromise<void>}\n */\nexport default function wait( delay ) {\n\tconst deferred = $.Deferred();\n\n\tconst timer = setTimeout( () => {\n\t\tdeferred.resolve();\n\t}, delay );\n\tdeferred.catch( () => clearTimeout( timer ) );\n\n\treturn deferred.promise( {\n\t\tabort() {\n\t\t\tdeferred.reject();\n\t\t}\n\t} );\n}\n","/**\n * @module thumbnail\n */\n\nimport constants from '../constants';\n\nexport const SIZES = {\n\tportraitImage: {\n\t\th: 250, // Exact height\n\t\tw: 203 // Max width\n\t},\n\tlandscapeImage: {\n\t\th: 200, // Max height\n\t\tw: 320 // Exact Width\n\t}\n};\nconst $ = jQuery;\n\n/**\n * @typedef {Object} ext.popups.Thumbnail\n * @property {JQuery} el\n * @property {boolean} isTall Whether or not the thumbnail is portrait\n * @property {number} width\n * @property {number} height\n * @property {boolean} isNarrow whether the thumbnail is portrait and also\n * thinner than the default portrait thumbnail width\n * (as defined in SIZES.portraitImage.w)\n * @property {number} offset in pixels between the thumbnail width and the\n * standard portrait thumbnail width (as defined in SIZES.portraitImage.w)\n */\n\n/**\n * Creates a thumbnail from the representation of a thumbnail returned by the\n * PageImages MediaWiki API query module.\n *\n * If there's no thumbnail, the thumbnail is too small, or the thumbnail's URL\n * contains characters that could be used to perform an\n * [XSS attack via CSS](https://www.owasp.org/index.php/Testing_for_CSS_Injection_(OTG-CLIENT-005)),\n * then `null` is returned.\n *\n * Extracted from `mw.popups.renderer.article.createThumbnail`.\n *\n * @param {Object} rawThumbnail\n * @return {ext.popups.Thumbnail|null}\n */\nexport function createThumbnail( rawThumbnail ) {\n\tconst devicePixelRatio = constants.BRACKETED_DEVICE_PIXEL_RATIO;\n\n\tif ( !rawThumbnail ) {\n\t\treturn null;\n\t}\n\n\tconst tall = rawThumbnail.width < rawThumbnail.height;\n\tconst thumbWidth = rawThumbnail.width / devicePixelRatio;\n\tconst thumbHeight = rawThumbnail.height / devicePixelRatio;\n\n\tif (\n\t\t// Image too small for landscape display\n\t\t( !tall && thumbWidth < SIZES.landscapeImage.w ) ||\n\t\t// Image too small for portrait display\n\t\t( tall && thumbHeight < SIZES.portraitImage.h ) ||\n\t\t// These characters in URL that could inject CSS and thus JS\n\t\t(\n\t\t\trawThumbnail.source.indexOf( '\\\\' ) > -1 ||\n\t\t\trawThumbnail.source.indexOf( '\\'' ) > -1 ||\n\t\t\trawThumbnail.source.indexOf( '\"' ) > -1\n\t\t)\n\t) {\n\t\treturn null;\n\t}\n\n\tlet x, y, width, height;\n\tif ( tall ) {\n\t\tx = ( thumbWidth > SIZES.portraitImage.w ) ?\n\t\t\t( ( thumbWidth - SIZES.portraitImage.w ) / -2 ) :\n\t\t\t( SIZES.portraitImage.w - thumbWidth );\n\t\ty = ( thumbHeight > SIZES.portraitImage.h ) ?\n\t\t\t( ( thumbHeight - SIZES.portraitImage.h ) / -2 ) : 0;\n\t\twidth = SIZES.portraitImage.w;\n\t\theight = SIZES.portraitImage.h;\n\n\t\t// Special handling for thin tall images\n\t\t// https://phabricator.wikimedia.org/T192928#4312088\n\t\tif ( thumbWidth < width ) {\n\t\t\tx = 0;\n\t\t\twidth = thumbWidth;\n\t\t}\n\t} else {\n\t\tx = 0;\n\t\ty = ( thumbHeight > SIZES.landscapeImage.h ) ?\n\t\t\t( ( thumbHeight - SIZES.landscapeImage.h ) / -2 ) : 0;\n\t\twidth = SIZES.landscapeImage.w;\n\t\theight = ( thumbHeight > SIZES.landscapeImage.h ) ?\n\t\t\tSIZES.landscapeImage.h : thumbHeight;\n\t}\n\n\tconst isNarrow = tall && thumbWidth < SIZES.portraitImage.w;\n\n\treturn {\n\t\tel: createThumbnailElement(\n\t\t\ttall ? 'mwe-popups-is-tall' : 'mwe-popups-is-not-tall',\n\t\t\trawThumbnail.source,\n\t\t\tx,\n\t\t\ty,\n\t\t\tthumbWidth,\n\t\t\tthumbHeight,\n\t\t\twidth,\n\t\t\theight\n\t\t),\n\t\tisTall: tall,\n\t\tisNarrow,\n\t\toffset: isNarrow ? SIZES.portraitImage.w - thumbWidth : 0,\n\t\twidth: thumbWidth,\n\t\theight: thumbHeight\n\t};\n}\n\n/**\n * Creates the SVG image element that represents the thumbnail.\n *\n * This function is distinct from `createThumbnail` as it abstracts away some\n * browser issues that are uncovered when manipulating elements across\n * namespaces.\n *\n * @param {string} className\n * @param {string} url\n * @param {number} x\n * @param {number} y\n * @param {number} thumbnailWidth\n * @param {number} thumbnailHeight\n * @param {number} width\n * @param {number} height\n * @return {JQuery}\n */\nexport function createThumbnailElement(\n\tclassName, url, x, y, thumbnailWidth, thumbnailHeight, width, height\n) {\n\tconst nsSvg = 'http://www.w3.org/2000/svg',\n\t\tnsXlink = 'http://www.w3.org/1999/xlink';\n\n\t// We want to visually separate the image from the summary\n\t// Given we use an SVG mask, we cannot rely on border to do this\n\t// and instead must insert a polyline element to visually separate\n\tconst line = document.createElementNS( nsSvg, 'polyline' );\n\tconst isTall = className.indexOf( 'not-tall' ) === -1;\n\tconst points = isTall ? [ 0, 0, 0, height ] :\n\t\t[ 0, height - 1, width, height - 1 ];\n\n\tline.setAttribute( 'stroke', 'rgba(0,0,0,0.1)' );\n\tline.setAttribute( 'points', points.join( ' ' ) );\n\tline.setAttribute( 'stroke-width', 1 );\n\n\tconst $thumbnailSVGImage = $( document.createElementNS( nsSvg, 'image' ) );\n\t$thumbnailSVGImage[ 0 ].setAttributeNS( nsXlink, 'href', url );\n\t$thumbnailSVGImage\n\t\t.addClass( className )\n\t\t.attr( {\n\t\t\tx,\n\t\t\ty,\n\t\t\twidth: thumbnailWidth,\n\t\t\theight: thumbnailHeight\n\t\t} );\n\n\tconst $thumbnail = $( document.createElementNS( nsSvg, 'svg' ) )\n\t\t.attr( {\n\t\t\txmlns: nsSvg,\n\t\t\twidth,\n\t\t\theight\n\t\t} )\n\t\t.append( $thumbnailSVGImage );\n\n\t$thumbnail.append( line );\n\treturn $thumbnail;\n}\n","/**\n * @module popup\n */\n\n/**\n * @param {ext.popups.previewTypes} type\n * @param {string} html HTML string.\n * @return {string} HTML string.\n */\nexport function renderPopup( type, html ) {\n\treturn `\n\t<div class='mwe-popups mwe-popups-type-${ type }' role='tooltip' aria-hidden>\n\t\t<div class='mwe-popups-container'>${ html }</div>\n\t</div>\n\t`.trim();\n}\n","/**\n * @module preview\n */\n\nimport { renderPopup } from '../popup/popup';\nimport { escapeHTML } from '../templateUtil';\n\n/**\n * @param {ext.popups.PreviewModel} model\n * @param {boolean} showTitle\n * @param {string} extractMsg\n * @param {string} linkMsg\n * @return {string} HTML string.\n */\nexport function renderPreview(\n\t{ title, url, type }, showTitle, extractMsg, linkMsg\n) {\n\ttitle = escapeHTML( title );\n\textractMsg = escapeHTML( extractMsg );\n\tlinkMsg = escapeHTML( linkMsg );\n\treturn renderPopup( type,\n\t\t`\n\t\t\t<div class='mw-ui-icon mw-ui-icon-element mw-ui-icon-preview-${ type }'></div>\n\t\t\t${ showTitle ? `<strong class='mwe-popups-title'>${ title }</strong>` : '' }\n\t\t\t<a href='${ url }' class='mwe-popups-extract'>\n\t\t\t\t<span class='mwe-popups-message'>${ extractMsg }</span>\n\t\t\t</a>\n\t\t\t<footer>\n\t\t\t\t<a href='${ url }' class='mwe-popups-read-link'>${ linkMsg }</a>\n\t\t\t</footer>\n\t\t`\n\t);\n}\n","/**\n * @module referencePreview\n */\n\nimport { renderPopup } from '../popup/popup';\nimport { escapeHTML } from '../templateUtil';\n\nconst mw = mediaWiki;\n\n/**\n * @param {ext.popups.PreviewModel} model\n * @return {string} HTML string.\n */\nexport function renderReferencePreview(\n\tmodel\n) {\n\t// TODO: Possibly remove this unused boolean flag.\n\tconst showTitle = true,\n\t\t// TODO: Implement a fallback to the default localized title \"Footnote\".\n\t\ttitle = escapeHTML( model.title ),\n\t\tlinkMsg = escapeHTML( mw.msg( 'popups-refpreview-jump-to-reference' ) );\n\n\treturn renderPopup( model.type,\n\t\t`\n\t\t\t${ showTitle ? `<strong class='mwe-popups-title'>${ title }</strong>` : '' }\n\t\t\t<div class='mwe-popups-extract'>\n\t\t\t\t<span class='mwe-popups-message'>${ model.extract }</span>\n\t\t\t</div>\n\t\t\t<footer>\n\t\t\t\t<a href='${ model.url }' class='mwe-popups-read-link'>${ linkMsg }</a>\n\t\t\t</footer>\n\t\t`\n\t);\n}\n","/**\n * @module renderer\n */\n\nimport wait from '../wait';\nimport pointerMaskSVG from './pointer-mask.svg';\nimport { SIZES, createThumbnail } from './thumbnail';\nimport { previewTypes } from '../preview/model';\nimport { renderPreview } from './templates/preview/preview';\nimport { renderReferencePreview } from './templates/referencePreview/referencePreview';\nimport { renderPagePreview } from './templates/pagePreview/pagePreview';\n\nconst mw = mediaWiki,\n\t$ = jQuery,\n\tdefaultExtractWidth = 215,\n\t$window = $( window ),\n\tlandscapePopupWidth = 450,\n\tportraitPopupWidth = 320,\n\tpointerSize = 8; // Height of the pointer.\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 * @return {void}\n */\nexport function createPointerMasks( container ) {\n\t$( '<div>' )\n\t\t.attr( 'id', 'mwe-popups-svg' )\n\t\t.html( pointerMaskSVG )\n\t\t.appendTo( container );\n}\n\n/**\n * Initializes the renderer.\n * @return {void}\n */\nexport function init() {\n\tcreatePointerMasks( document.body );\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\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<void>}\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<void>}\n\t\t */\n\t\thide() {\n\t\t\treturn hide( preview );\n\t\t}\n\t};\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\tswitch ( model.type ) {\n\t\tcase previewTypes.TYPE_PAGE:\n\t\t\treturn createPagePreview( model );\n\t\tcase previewTypes.TYPE_DISAMBIGUATION:\n\t\t\treturn createDisambiguationPreview( model );\n\t\tcase previewTypes.TYPE_REFERENCE:\n\t\t\treturn createReferencePreview( model );\n\t\tdefault:\n\t\t\treturn createEmptyPreview( model );\n\t}\n}\n\nexport { defaultExtractWidth }; // for testing\n\n/**\n * Calculates width of extract based on the associated thumbnail\n *\n * @param {ext.popups.Thumbnail} [thumbnail] model\n * @return {string} representing the css width attribute to be\n * used for the extract\n */\nexport function getExtractWidth( thumbnail ) {\n\treturn thumbnail && thumbnail.isNarrow ? `${defaultExtractWidth + thumbnail.offset}px` : '';\n}\n\n/**\n * Creates an instance of the DTO backing a preview.\n *\n * @param {ext.popups.PreviewModel} model\n * @return {ext.popups.Preview}\n */\nfunction createPagePreview( model ) {\n\tconst thumbnail = createThumbnail( model.thumbnail ),\n\t\thasThumbnail = thumbnail !== null,\n\t\textract = model.extract;\n\n\tconst $el = $( $.parseHTML( renderPagePreview( model, hasThumbnail ) ) );\n\n\tif ( hasThumbnail ) {\n\t\t$el.find( '.mwe-popups-discreet' ).append( thumbnail.el );\n\t}\n\tif ( extract ) {\n\t\tconst extractWidth = getExtractWidth( thumbnail );\n\t\t$el.find( '.mwe-popups-extract' )\n\t\t\t.css( 'width', extractWidth )\n\t\t\t.append( extract );\n\t\t$el.find( 'footer' ).css( 'width', extractWidth );\n\t}\n\n\treturn {\n\t\tel: $el,\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.PreviewModel} model\n * @return {ext.popups.Preview}\n */\nfunction createEmptyPreview( model ) {\n\tconst showTitle = false,\n\t\textractMsg = mw.msg( 'popups-preview-no-preview' ),\n\t\tlinkMsg = mw.msg( 'popups-preview-footer-read' );\n\n\tconst $el = $(\n\t\t$.parseHTML( renderPreview( model, showTitle, extractMsg, linkMsg ) )\n\t);\n\n\treturn {\n\t\tel: $el,\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.PreviewModel} model\n * @return {ext.popups.Preview}\n */\nfunction createDisambiguationPreview( model ) {\n\tconst showTitle = true,\n\t\textractMsg = mw.msg( 'popups-preview-disambiguation' ),\n\t\tlinkMsg = mw.msg( 'popups-preview-disambiguation-link' );\n\n\tconst $el = $(\n\t\t$.parseHTML( renderPreview( model, showTitle, extractMsg, linkMsg ) )\n\t);\n\n\treturn {\n\t\tel: $el,\n\t\thasThumbnail: false,\n\t\tisTall: false\n\t};\n}\n\n/**\n * @param {ext.popups.PreviewModel} model\n * @return {ext.popups.Preview}\n */\nfunction createReferencePreview( model ) {\n\tconst $el = $(\n\t\t$.parseHTML( renderReferencePreview( model ) )\n\t);\n\n\treturn {\n\t\tel: $el,\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 {Event} event\n * @param {JQuery} $link event target\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<void>} A promise that resolves when the promise has\n * faded in.\n */\nexport function show(\n\tpreview, event, $link, behavior, token, container, dir\n) {\n\tconst layout = createLayout(\n\t\tpreview.isTall,\n\t\t{\n\t\t\tpageX: event.pageX,\n\t\t\tpageY: event.pageY,\n\t\t\tclientY: event.clientY\n\t\t},\n\t\t{\n\t\t\tclientRects: $link.get( 0 ).getClientRects(),\n\t\t\toffset: $link.offset(),\n\t\t\twidth: $link.width(),\n\t\t\theight: $link.height()\n\t\t},\n\t\t{\n\t\t\tscrollTop: $window.scrollTop(),\n\t\t\twidth: $window.width(),\n\t\t\theight: $window.height()\n\t\t},\n\t\tpointerSize,\n\t\tdir\n\t);\n\n\tpreview.el.appendTo( container );\n\n\tlayoutPreview(\n\t\tpreview, layout, getClasses( preview, layout ),\n\t\tSIZES.landscapeImage.h, pointerSize\n\t);\n\n\tpreview.el.show();\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 * @return {void}\n */\nexport function bindBehavior( preview, behavior ) {\n\tpreview.el.on( 'mouseenter', behavior.previewDwell )\n\t\t.on( 'mouseleave', behavior.previewAbandon );\n\n\tpreview.el.click( behavior.click );\n\n\tpreview.el.find( '.mwe-popups-settings-icon' )\n\t\t.attr( 'href', behavior.settingsUrl )\n\t\t.click( ( event ) => {\n\t\t\tevent.stopPropagation();\n\n\t\t\tbehavior.showSettings( event );\n\t\t} );\n}\n\n/**\n * Extracted from `mw.popups.render.closePopup`.\n *\n * @param {ext.popups.Preview} preview\n * @return {JQuery.Promise<void>} 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.hasClass( '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\tpreview.el\n\t\t.removeClass( fadeInClass )\n\t\t.addClass( 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 {Object} eventData Data related to the event that triggered showing\n * a popup\n * @param {number} eventData.pageX\n * @param {number} eventData.pageY\n * @param {number} eventData.clientY\n * @param {Object} linkData Data related to the link that’s used for showing\n * a popup\n * @param {ClientRectList} linkData.clientRects list of rectangles defined by\n * four edges\n * @param {Object} linkData.offset\n * @param {number} linkData.width\n * @param {number} linkData.height\n * @param {Object} windowData Data related to the window\n * @param {number} windowData.scrollTop\n * @param {number} windowData.width\n * @param {number} windowData.height\n * @param {number} pointerSize 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, eventData, linkData, windowData, pointerSize, dir\n) {\n\tlet flippedX = false,\n\t\tflippedY = false,\n\t\toffsetTop = eventData.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\teventData.pageY - windowData.scrollTop,\n\t\t\t\tlinkData.clientRects,\n\t\t\t\tfalse\n\t\t\t) + windowData.scrollTop + pointerSize :\n\t\t\t// Position according to link position or size\n\t\t\tlinkData.offset.top + linkData.height + pointerSize,\n\t\toffsetLeft = eventData.pageX ? eventData.pageX : linkData.offset.left;\n\tconst clientTop = eventData.clientY ? eventData.clientY : offsetTop;\n\n\t// X Flip\n\tif ( offsetLeft > ( windowData.width / 2 ) ) {\n\t\toffsetLeft += ( !eventData.pageX ) ? linkData.width : 0;\n\t\toffsetLeft -= !isPreviewTall ?\n\t\t\tportraitPopupWidth :\n\t\t\tlandscapePopupWidth;\n\t\tflippedX = true;\n\t}\n\n\tif ( eventData.pageX ) {\n\t\toffsetLeft += ( flippedX ) ? 20 : -20;\n\t}\n\n\t// Y Flip\n\tif ( clientTop > ( windowData.height / 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 = linkData.offset.top;\n\n\t\t// Change the Y position to the top of the link\n\t\tif ( eventData.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\teventData.pageY - windowData.scrollTop,\n\t\t\t\tlinkData.clientRects,\n\t\t\t\ttrue\n\t\t\t) + windowData.scrollTop;\n\t\t}\n\n\t\toffsetTop -= pointerSize;\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 * 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\tif ( ( !preview.hasThumbnail || preview.isTall && !layout.flippedX ) &&\n\t\t!layout.flippedY ) {\n\t\tclasses.push( 'mwe-popups-no-image-pointer' );\n\t}\n\n\tif ( preview.hasThumbnail && !preview.isTall && !layout.flippedY ) {\n\t\tclasses.push( 'mwe-popups-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} pointerSize\n * @return {void}\n */\nexport function layoutPreview(\n\tpreview, layout, classes, predefinedLandscapeImageHeight, pointerSize\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\tlet offsetTop = layout.offset.top;\n\n\tif (\n\t\t!flippedY && !isTall && hasThumbnail &&\n\t\t\tthumbnail.height < predefinedLandscapeImageHeight\n\t) {\n\t\tpopup.find( '.mwe-popups-extract' ).css(\n\t\t\t'margin-top',\n\t\t\tthumbnail.height - pointerSize\n\t\t);\n\t}\n\n\tpopup.addClass( classes.join( ' ' ) );\n\n\tif ( flippedY ) {\n\t\toffsetTop -= popup.outerHeight();\n\t}\n\n\tpopup.css( {\n\t\ttop: offsetTop,\n\t\tleft: `${ layout.offset.left }px`\n\t} );\n\n\tif ( hasThumbnail ) {\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 * @return {void}\n */\nexport function setThumbnailClipPath(\n\t{ el, isTall }, { flippedY, flippedX, dir }\n) {\n\tconst maskID = getThumbnailClipPathID( isTall, flippedY, flippedX );\n\tif ( maskID ) {\n\t\tlet entries; // = ⎡ a c tx ⎤\n\t\t// ⎣ b d ty ⎦\n\t\tif ( dir === 'rtl' ) {\n\t\t\t// Flip and translate.\n\t\t\tconst tx = isTall ? SIZES.portraitImage.w : SIZES.landscapeImage.w;\n\t\t\tentries = `-1 0 0 1 ${tx} 0`;\n\t\t} else {\n\t\t\t// Identity.\n\t\t\tentries = '1 0 0 1 0 0';\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( 'transform', `matrix(${entries})` );\n\n\t\tel.find( 'image' )[ 0 ]\n\t\t\t.setAttribute( 'clip-path', `url(#${maskID})` );\n\t}\n}\n\n/**\n * Gets the thumbnail SVG clip-path element ID.\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\tif ( flippedX && !flippedY ) {\n\t\treturn isTall ? 'mwe-popups-landscape-mask' : 'mwe-popups-mask-flip';\n\t} else if ( flippedY && flippedX && isTall ) {\n\t\treturn 'mwe-popups-landscape-mask-flip';\n\t} else if ( !flippedY && !isTall ) {\n\t\treturn 'mwe-popups-mask';\n\t}\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","/**\n * @module pagePreview\n */\n\nimport { renderPopup } from '../popup/popup';\n\n/**\n * @param {ext.popups.PreviewModel} model\n * @param {boolean} hasThumbnail\n * @return {string} HTML string.\n */\nexport function renderPagePreview(\n\t{ url, type, languageCode, languageDirection }, hasThumbnail\n) {\n\treturn renderPopup( type,\n\t\t`\n\t\t\t${ hasThumbnail ? `<a href='${ url }' class='mwe-popups-discreet'></a>` : '' }\n\t\t\t<a dir='${ languageDirection }' lang='${ languageCode }' class='mwe-popups-extract' href='${ url }'></a>\n\t\t\t<footer>\n\t\t\t\t<a class='mwe-popups-settings-icon'>\n\t\t\t\t\t<span class=\"mw-ui-icon mw-ui-icon-element mw-ui-icon-popups-settings\"></span>\n\t\t\t\t</a>\n\t\t\t</footer>\n\t\t`\n\t);\n}\n","/**\n * @module changeListeners/footerLink\n */\n\nconst mw = mediaWiki,\n\t$ = jQuery;\n\n/**\n * Creates the link element and appends it to the footer element.\n *\n * The following elements are considered to be the footer element (highest\n * priority to lowest):\n *\n * # `#footer-places`\n * # `#f-list`\n * # The parent element of `#footer li`, which is either an `ol` or `ul`.\n *\n * @return {JQuery} The link element\n */\nfunction createFooterLink() {\n\tconst $link = $( '<li>' ).append(\n\t\t$( '<a>' )\n\t\t\t.attr( 'href', '#' )\n\t\t\t.text( mw.message( 'popups-settings-enable' ).text() )\n\t);\n\n\t// As yet, we don't know whether the link should be visible.\n\t$link.hide();\n\n\t// From https://en.wikipedia.org/wiki/MediaWiki:Gadget-ReferenceTooltips.js,\n\t// which was written by Yair rand <https://en.wikipedia.org/wiki/User:Yair_rand>.\n\tlet $footer = $( '#footer-places, #f-list' );\n\n\tif ( $footer.length === 0 ) {\n\t\t$footer = $( '#footer li' ).parent();\n\t}\n\n\t$footer.append( $link );\n\n\treturn $link;\n}\n\n/**\n * Creates an instance of the footer link change listener.\n *\n * The change listener covers the following behaviour:\n *\n * * The \"Enable previews\" link (the \"link\") is appended to the footer menu\n * (see `createFooterLink` above).\n * * When Page Previews are disabled, then the link is shown; otherwise, the\n * link is hidden.\n * * When the user clicks the link, then the `showSettings` bound action\n * creator is called.\n *\n * @param {Object} boundActions\n * @return {ext.popups.ChangeListener}\n */\nexport default function footerLink( boundActions ) {\n\tlet $footerLink;\n\n\treturn ( prevState, state ) => {\n\t\tif ( $footerLink === undefined ) {\n\t\t\t$footerLink = createFooterLink();\n\t\t\t$footerLink.click( ( e ) => {\n\t\t\t\te.preventDefault();\n\t\t\t\tboundActions.showSettings();\n\t\t\t} );\n\t\t}\n\n\t\tif ( state.settings.shouldShowFooterLink ) {\n\t\t\t$footerLink.show();\n\t\t} else {\n\t\t\t$footerLink.hide();\n\t\t}\n\t};\n}\n","/**\n * @module changeListeners/eventLogging\n */\n\nconst $ = jQuery;\n\n/**\n * Creates an instance of the event logging change listener.\n *\n * When an event is enqueued it'll be logged using the schema. Since it's the\n * responsibility of Event Logging (and the UA) to deliver logged events,\n * `EVENT_LOGGED` is immediately dispatched rather than waiting for some\n * indicator of completion.\n *\n * @param {Object} boundActions\n * @param {EventTracker} eventLoggingTracker\n * @param {Function} getCurrentTimestamp\n * @return {ext.popups.ChangeListener}\n */\nexport default function eventLogging(\n\tboundActions, eventLoggingTracker, getCurrentTimestamp\n) {\n\treturn ( _, state ) => {\n\t\tconst eventLogging = state.eventLogging;\n\t\tlet event = eventLogging.event;\n\n\t\tif ( !event ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Per https://meta.wikimedia.org/wiki/Schema:Popups, the timestamp\n\t\t// property should be the time at which the event is logged and not the\n\t\t// time at which the interaction started.\n\t\t//\n\t\t// Rightly or wrongly, it's left as an exercise for the analyst to\n\t\t// calculate the time at which the interaction started as part of their\n\t\t// analyses, e.g. https://phabricator.wikimedia.org/T186016#4002923.\n\t\tevent = $.extend( true, {}, eventLogging.baseData, event, {\n\t\t\ttimestamp: getCurrentTimestamp()\n\t\t} );\n\n\t\teventLoggingTracker( 'event.Popups', event );\n\t\t// Dispatch the eventLogged action so that the state tree can be\n\t\t// cleared/updated.\n\t\tboundActions.eventLogged( event );\n\t};\n}\n","const $ = jQuery;\n\n/**\n * Creates an instance of the link title change listener.\n *\n * While the user dwells on a link, then it becomes the active link. The\n * change listener will remove a link's `title` attribute while it's the\n * active link.\n *\n * @return {ext.popups.ChangeListener}\n */\nexport default function linkTitle() {\n\tlet title;\n\n\t/**\n\t * Destroys the title attribute of the element, storing its value in local\n\t * state so that it can be restored later (see `restoreTitleAttr`).\n\t *\n\t * @param {Element} el\n\t * @return {void}\n\t */\n\tfunction destroyTitleAttr( el ) {\n\t\tconst $el = $( el );\n\n\t\t// Has the user dwelled on a link? If we've already removed its title\n\t\t// attribute, then NOOP.\n\t\tif ( title ) {\n\t\t\treturn;\n\t\t}\n\n\t\ttitle = $el.attr( 'title' );\n\n\t\t$el.attr( 'title', '' );\n\t}\n\n\t/**\n\t * Restores the title attribute of the element.\n\t *\n\t * @param {Element} el\n\t * @return {void}\n\t */\n\tfunction restoreTitleAttr( el ) {\n\t\t$( el ).attr( 'title', title );\n\n\t\ttitle = undefined;\n\t}\n\n\treturn ( prevState, state ) => {\n\t\tconst hasPrevActiveLink = prevState && prevState.preview.activeLink;\n\n\t\tif ( !state.preview.enabled ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( hasPrevActiveLink ) {\n\n\t\t\t// Has the user dwelled on a link immediately after abandoning another\n\t\t\t// (remembering that the ABANDON_END action is delayed by\n\t\t\t// ~100 ms).\n\t\t\tif ( prevState.preview.activeLink !== state.preview.activeLink ) {\n\t\t\t\trestoreTitleAttr( prevState.preview.activeLink );\n\t\t\t}\n\t\t}\n\n\t\tif ( state.preview.activeLink ) {\n\t\t\tdestroyTitleAttr( state.preview.activeLink );\n\t\t}\n\t};\n}\n","/**\n * @module changeListeners/syncUserSettings\n */\n\n/**\n * Creates an instance of the user settings sync change listener.\n *\n * This change listener syncs certain parts of the state tree to user\n * settings when they change.\n *\n * Used for:\n *\n * * Enabled state: If the previews are enabled or disabled.\n * * Preview count: When the user dwells on a link for long enough that\n * a preview is shown, then their preview count will be incremented (see\n * `reducers/eventLogging.js`, and is persisted to local storage.\n *\n * @param {ext.popups.UserSettings} userSettings\n * @return {ext.popups.ChangeListener}\n */\nexport default function syncUserSettings( userSettings ) {\n\n\treturn ( prevState, state ) => {\n\n\t\tsyncIfChanged(\n\t\t\tprevState, state, 'eventLogging', 'previewCount',\n\t\t\tuserSettings.setPreviewCount\n\t\t);\n\t\tsyncIfChanged(\n\t\t\tprevState, state, 'preview', 'enabled',\n\t\t\tuserSettings.setIsEnabled\n\t\t);\n\n\t};\n}\n\n/**\n * Given a state tree, reducer and property, safely return the value of the\n * property if the reducer and property exist\n * @param {Object} state tree\n * @param {string} reducer key to access on the state tree\n * @param {string} prop key to access on the reducer key of the state tree\n * @return {*}\n */\nfunction get( state, reducer, prop ) {\n\treturn state[ reducer ] && state[ reducer ][ prop ];\n}\n\n/**\n * Calls a sync function if the property prop on the property reducer on\n * the state trees has changed value.\n * @param {Object} prevState\n * @param {Object} state\n * @param {string} reducer key to access on the state tree\n * @param {string} prop key to access on the reducer key of the state tree\n * @param {Function} sync function to be called with the newest value if\n * changed\n * @return {void}\n */\nfunction syncIfChanged( prevState, state, reducer, prop, sync ) {\n\tconst current = get( state, reducer, prop );\n\tif ( prevState && ( get( prevState, reducer, prop ) !== current ) ) {\n\t\tsync( current );\n\t}\n}\n","import footerLink from './footerLink';\nimport eventLogging from './eventLogging';\nimport linkTitle from './linkTitle';\nimport pageviews from './pageviews';\nimport render from './render';\nimport settings from './settings';\nimport statsv from './statsv';\nimport syncUserSettings from './syncUserSettings';\n\nexport default {\n\tfooterLink,\n\teventLogging,\n\tlinkTitle,\n\tpageviews,\n\trender,\n\tsettings,\n\tstatsv,\n\tsyncUserSettings\n};\n","/**\n * @module changeListeners/pageviews\n */\n\n/**\n * Creates an instance of the pageviews change listener.\n *\n * When a pageview enqueued it'll be logged using the VirtualPageView schema.\n * Note, it's the responsibility of Event Logging (and the UA) to\n * deliver logged events.\n *\n * @param {Object} boundActions\n * @param {EventTracker} pageviewTracker\n * @return {ext.popups.ChangeListener}\n */\nexport default function pageviews(\n\tboundActions, pageviewTracker\n) {\n\treturn ( _, state ) => {\n\t\tlet page;\n\t\tif ( state.pageviews && state.pageviews.pageview && state.pageviews.page ) {\n\t\t\tpage = state.pageviews.page;\n\t\t\tpageviewTracker( 'event.VirtualPageView', $.extend( {},\n\t\t\t\t{\n\t\t\t\t\t/* eslint-disable camelcase */\n\t\t\t\t\tsource_page_id: page.id,\n\t\t\t\t\tsource_namespace: page.namespaceId,\n\t\t\t\t\tsource_title: page.title,\n\t\t\t\t\tsource_url: page.url\n\t\t\t\t\t/* eslint-enable camelcase */\n\t\t\t\t},\n\t\t\t\tstate.pageviews.pageview )\n\t\t\t);\n\t\t\t// Clear the pageview now its been logged.\n\t\t\tboundActions.pageviewLogged();\n\t\t}\n\t};\n}\n","import * as renderer from '../ui/renderer';\n\n/**\n * Creates an instance of the render change listener.\n *\n * FIXME: Remove hard coupling with renderer, inject it as a parameter\n * * Wire it up in index.js\n * * Fix tests to remove require mocking\n *\n * @param {ext.popups.PreviewBehavior} previewBehavior\n * @return {ext.popups.ChangeListener}\n */\nexport default function render( previewBehavior ) {\n\tlet preview;\n\n\treturn ( prevState, state ) => {\n\t\tif ( state.preview.shouldShow && !preview ) {\n\t\t\tpreview = renderer.render( state.preview.fetchResponse );\n\t\t\tpreview.show(\n\t\t\t\tstate.preview.activeEvent,\n\t\t\t\tpreviewBehavior,\n\t\t\t\tstate.preview.activeToken\n\t\t\t);\n\t\t} else if ( !state.preview.shouldShow && preview ) {\n\t\t\tpreview.hide();\n\t\t\tpreview = undefined;\n\t\t}\n\t};\n}\n","/**\n * Creates an instance of the settings change listener.\n *\n * @param {Object} boundActions\n * @param {Object} render function that renders a jQuery el with the settings\n * @return {ext.popups.ChangeListener}\n */\nexport default function settings( boundActions, render ) {\n\tlet settings;\n\n\treturn ( prevState, state ) => {\n\t\tif ( !prevState ) {\n\t\t\t// Nothing to do on initialization\n\t\t\treturn;\n\t\t}\n\n\t\t// Update global modal visibility\n\t\tif (\n\t\t\tprevState.settings.shouldShow === false &&\n\t\t\tstate.settings.shouldShow === true\n\t\t) {\n\t\t\t// Lazily instantiate the settings UI\n\t\t\tif ( !settings ) {\n\t\t\t\tsettings = render( boundActions );\n\t\t\t\tsettings.appendTo( document.body );\n\t\t\t}\n\n\t\t\t// Update the UI settings with the current settings\n\t\t\tsettings.setEnabled( state.preview.enabled );\n\n\t\t\tsettings.show();\n\t\t} else if (\n\t\t\tprevState.settings.shouldShow === true &&\n\t\t\tstate.settings.shouldShow === false\n\t\t) {\n\t\t\tsettings.hide();\n\t\t}\n\n\t\t// Update help visibility\n\t\tif ( prevState.settings.showHelp !== state.settings.showHelp ) {\n\t\t\tsettings.toggleHelp( state.settings.showHelp );\n\t\t}\n\t};\n}\n","/**\n * Creates an instance of the statsv change listener.\n *\n * The listener will log events to StatsD via the [the \"StatsD timers and\n * counters\" analytics event protocol][0].\n *\n * [0]: https://github.com/wikimedia/mediawiki-extensions-WikimediaEvents/blob/29c864a0/modules/ext.wikimediaEvents.statsd.js\n *\n * @param {Object} boundActions\n * @param {EventTracker} track\n * @return {ext.popups.ChangeListener}\n */\nexport default function statsv( boundActions, track ) {\n\treturn ( _, state ) => {\n\t\tconst statsv = state.statsv;\n\n\t\tif ( statsv.action ) {\n\t\t\ttrack( statsv.action, statsv.data );\n\n\t\t\tboundActions.statsvLogged();\n\t\t}\n\t};\n}\n","/**\n * @module actionTypes\n */\n\nexport default {\n\tBOOT: 'BOOT',\n\tLINK_DWELL: 'LINK_DWELL',\n\tABANDON_START: 'ABANDON_START',\n\tABANDON_END: 'ABANDON_END',\n\tLINK_CLICK: 'LINK_CLICK',\n\t/** Precedes a fetch. */\n\tFETCH_START: 'FETCH_START',\n\t/** Follows a successful fetch. */\n\tFETCH_END: 'FETCH_END',\n\t/** Follows a fetch regardless of whether it was successful. */\n\tFETCH_COMPLETE: 'FETCH_COMPLETE',\n\t/** Follows an unsuccessful fetch. */\n\tFETCH_FAILED: 'FETCH_FAILED',\n\t/** Follows an aborted fetch */\n\tFETCH_ABORTED: 'FETCH_ABORTED',\n\tPAGEVIEW_LOGGED: 'PAGEVIEW_LOGGED',\n\tPREVIEW_DWELL: 'PREVIEW_DWELL',\n\tPREVIEW_SHOW: 'PREVIEW_SHOW',\n\tPREVIEW_CLICK: 'PREVIEW_CLICK',\n\t/**\n\t\tOccurs when a preview has been opened for a significant amount of time and\n\t\tis assumed to have been viewed.\n\t*/\n\tPREVIEW_SEEN: 'PREVIEW_SEEN',\n\tSETTINGS_SHOW: 'SETTINGS_SHOW',\n\tSETTINGS_HIDE: 'SETTINGS_HIDE',\n\tSETTINGS_CHANGE: 'SETTINGS_CHANGE',\n\tEVENT_LOGGED: 'EVENT_LOGGED',\n\tSTATSV_LOGGED: 'STATSV_LOGGED'\n};\n","/**\n * @module actions\n */\n\nimport types from './actionTypes';\nimport wait from './wait';\nimport { createNullModel, previewTypes } from './preview/model';\n\nconst $ = jQuery,\n\tmw = mediaWiki,\n\n\t// See the following for context around this value.\n\t//\n\t// * https://phabricator.wikimedia.org/T161284\n\t// * https://phabricator.wikimedia.org/T70861#3129780\n\tFETCH_START_DELAY = 150, // ms.\n\n\t// The minimum time a preview must be open before we judge it\n\t// has been seen.\n\t// See https://phabricator.wikimedia.org/T184793\n\tPREVIEW_SEEN_DURATION = 1000, // ms\n\n\t// The delay after which a FETCH_COMPLETE action should be dispatched.\n\t//\n\t// If the API endpoint responds faster than 350 ms (or, say, the API\n\t// response is served from the UA's cache), then we introduce a delay of\n\t// 350 ms - t to make the preview delay consistent to the user. The total\n\t// delay from start to finish is 500 ms.\n\tFETCH_COMPLETE_TARGET_DELAY = 350 + FETCH_START_DELAY, // ms.\n\n\tABANDON_END_DELAY = 300; // ms.\n\n/**\n * Mixes in timing information to an action.\n *\n * Warning: the `baseAction` parameter is modified and returned.\n *\n * @param {Object} baseAction\n * @return {Object}\n */\nfunction timedAction( baseAction ) {\n\tbaseAction.timestamp = mw.now();\n\n\treturn baseAction;\n}\n\n/**\n * Represents Page Previews booting.\n *\n * When a Redux store is created, the `@@INIT` action is immediately\n * dispatched to it. To avoid overriding the term, we refer to booting rather\n * than initializing.\n *\n * Page Previews persists critical pieces of information to local storage.\n * Since reading from and writing to local storage are synchronous, Page\n * Previews is booted when the browser is idle (using\n * [`mw.requestIdleCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback))\n * so as not to impact latency-critical events.\n *\n * @param {boolean} isEnabled See `isEnabled.js`\n * @param {mw.user} user\n * @param {ext.popups.UserSettings} userSettings\n * @param {mw.Map} config The config of the MediaWiki client-side application,\n * i.e. `mw.config`\n * @param {string} url url\n * @return {Object}\n */\nexport function boot(\n\tisEnabled,\n\tuser,\n\tuserSettings,\n\tconfig,\n\turl\n) {\n\tconst editCount = config.get( 'wgUserEditCount' ),\n\t\tpreviewCount = userSettings.getPreviewCount();\n\n\treturn {\n\t\ttype: types.BOOT,\n\t\tisEnabled,\n\t\tisNavPopupsEnabled: config.get( 'wgPopupsConflictsWithNavPopupGadget' ),\n\t\tsessionToken: user.sessionId(),\n\t\tpageToken: user.getPageviewToken(),\n\t\tpage: {\n\t\t\turl,\n\t\t\ttitle: config.get( 'wgTitle' ),\n\t\t\tnamespaceId: config.get( 'wgNamespaceNumber' ),\n\t\t\tid: config.get( 'wgArticleId' )\n\t\t},\n\t\tuser: {\n\t\t\tisAnon: user.isAnon(),\n\t\t\teditCount,\n\t\t\tpreviewCount\n\t\t}\n\t};\n}\n\n/**\n * Represents Page Previews fetching data via the gateway.\n *\n * @param {Gateway} gateway\n * @param {mw.Title} title\n * @param {Element} el\n * @param {string} token The unique token representing the link interaction that\n * triggered the fetch\n * @return {Redux.Thunk}\n */\nexport function fetch( gateway, title, el, token ) {\n\tconst titleText = title.getPrefixedDb(),\n\t\tnamespaceId = title.namespace;\n\n\treturn ( dispatch ) => {\n\t\tconst xhr = gateway.fetchPreviewForTitle( title );\n\n\t\tdispatch( timedAction( {\n\t\t\ttype: types.FETCH_START,\n\t\t\tel,\n\t\t\ttitle: titleText,\n\t\t\tnamespaceId,\n\t\t\tpromise: xhr\n\t\t} ) );\n\n\t\tconst chain = xhr\n\t\t\t.then( ( result ) => {\n\t\t\t\tdispatch( timedAction( {\n\t\t\t\t\ttype: types.FETCH_END,\n\t\t\t\t\tel\n\t\t\t\t} ) );\n\n\t\t\t\treturn result;\n\t\t\t} )\n\t\t\t.catch( ( err, data ) => {\n\t\t\t\tconst exception = new Error( err );\n\t\t\t\tconst type = data && data.textStatus && data.textStatus === 'abort' ?\n\t\t\t\t\ttypes.FETCH_ABORTED : types.FETCH_FAILED;\n\n\t\t\t\texception.data = data;\n\t\t\t\tdispatch( {\n\t\t\t\t\ttype,\n\t\t\t\t\tel\n\t\t\t\t} );\n\t\t\t\t// Keep the request promise in a rejected status since it failed.\n\t\t\t\tthrow exception;\n\t\t\t} );\n\n\t\treturn $.when(\n\t\t\tchain,\n\t\t\twait( FETCH_COMPLETE_TARGET_DELAY - FETCH_START_DELAY )\n\t\t)\n\t\t\t.then( ( result ) => {\n\t\t\t\tdispatch( {\n\t\t\t\t\ttype: types.FETCH_COMPLETE,\n\t\t\t\t\tel,\n\t\t\t\t\tresult,\n\t\t\t\t\ttoken\n\t\t\t\t} );\n\t\t\t} )\n\t\t\t.catch( ( ex ) => {\n\t\t\t\tconst result = ex.data;\n\t\t\t\tlet showNullPreview = true;\n\t\t\t\t// All failures, except those due to being offline or network error,\n\t\t\t\t// should present \"There was an issue displaying this preview\".\n\t\t\t\t// e.g.:\n\t\t\t\t// - Show (timeout): data=\"http\" {xhr: {…}, textStatus: \"timeout\",\n\t\t\t\t// exception: \"timeout\"}\n\t\t\t\t// - Show (bad MW request): data=\"unknown_action\" {error: {…}}\n\t\t\t\t// - Show (RB 4xx): data=\"http\" {xhr: {…}, textStatus: \"error\",\n\t\t\t\t// exception: \"Bad Request\"}\n\t\t\t\t// - Show (RB 5xx): data=\"http\" {xhr: {…}, textStatus: \"error\",\n\t\t\t\t// exception: \"Service Unavailable\"}\n\t\t\t\t// - Suppress (offline or network error): data=\"http\"\n\t\t\t\t// result={xhr: {…}, textStatus: \"error\", exception: \"\"}\n\t\t\t\t// - Abort: data=\"http\"\n\t\t\t\t// result={xhr: {…}, textStatus: \"abort\", exception: \"abort\"}\n\n\t\t\t\tif ( result && result.xhr && result.xhr.readyState === 0 ) {\n\t\t\t\t\tconst isNetworkError = result.textStatus === 'error' && result.exception === '';\n\t\t\t\t\tshowNullPreview = !( isNetworkError || result.textStatus === 'abort' );\n\t\t\t\t}\n\n\t\t\t\tif ( showNullPreview ) {\n\t\t\t\t\tdispatch( {\n\t\t\t\t\t\ttype: types.FETCH_COMPLETE,\n\t\t\t\t\t\tel,\n\t\t\t\t\t\tresult: createNullModel( titleText, title.getUrl() ),\n\t\t\t\t\t\ttoken\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t} );\n\t};\n}\n\n/**\n * Represents the user dwelling on a link, either by hovering over it with\n * their mouse or by focussing it using their keyboard or an assistive device.\n *\n * @param {mw.Title} title\n * @param {Element} el\n * @param {Event} event\n * @param {Gateway} gateway\n * @param {Function} generateToken\n * @return {Redux.Thunk}\n */\nexport function linkDwell( title, el, event, gateway, generateToken ) {\n\tconst token = generateToken(),\n\t\ttitleText = title.getPrefixedDb(),\n\t\tnamespaceId = title.namespace;\n\n\treturn ( dispatch, getState ) => {\n\t\tconst promise = wait( FETCH_START_DELAY );\n\t\tconst action = timedAction( {\n\t\t\ttype: types.LINK_DWELL,\n\t\t\tel,\n\t\t\tevent,\n\t\t\ttoken,\n\t\t\ttitle: titleText,\n\t\t\tnamespaceId,\n\t\t\tpromise\n\t\t} );\n\n\t\tdispatch( action );\n\n\t\t// Has the new generated token been accepted?\n\t\tfunction isNewInteraction() {\n\t\t\treturn getState().preview.activeToken === token;\n\t\t}\n\n\t\tif ( !isNewInteraction() ) {\n\t\t\treturn $.Deferred().resolve().promise();\n\t\t}\n\n\t\treturn promise.then( () => {\n\t\t\tconst previewState = getState().preview;\n\n\t\t\tif ( previewState.enabled && isNewInteraction() ) {\n\t\t\t\treturn dispatch( fetch( gateway, title, el, token ) );\n\t\t\t}\n\t\t} );\n\t};\n}\n\n/**\n * Represents the user abandoning a link, either by moving their mouse away\n * from it or by shifting focus to another UI element using their keyboard or\n * an assistive device, or abandoning a preview by moving their mouse away\n * from it.\n *\n * @return {Redux.Thunk}\n */\nexport function abandon() {\n\treturn ( dispatch, getState ) => {\n\t\tconst { activeToken: token, promise } = getState().preview;\n\n\t\tif ( !token ) {\n\t\t\treturn $.Deferred().resolve().promise();\n\t\t}\n\n\t\t// Immediately abandon any outstanding fetch request. Do not wait.\n\t\tpromise.abort();\n\n\t\tdispatch( timedAction( {\n\t\t\ttype: types.ABANDON_START,\n\t\t\ttoken\n\t\t} ) );\n\n\t\treturn wait( ABANDON_END_DELAY )\n\t\t\t.then( () => {\n\t\t\t\tdispatch( {\n\t\t\t\t\ttype: types.ABANDON_END,\n\t\t\t\t\ttoken\n\t\t\t\t} );\n\t\t\t} );\n\t};\n}\n\n/**\n * Represents the user clicking on a link with their mouse, keyboard, or an\n * assistive device.\n *\n * @param {Element} el\n * @return {Object}\n */\nexport function linkClick( el ) {\n\treturn timedAction( {\n\t\ttype: types.LINK_CLICK,\n\t\tel\n\t} );\n}\n\n/**\n * Represents the user dwelling on a preview with their mouse.\n *\n * @return {Object}\n */\nexport function previewDwell() {\n\treturn {\n\t\ttype: types.PREVIEW_DWELL\n\t};\n}\n\n/**\n * Represents a preview being shown to the user.\n *\n * This action is dispatched by the `./changeListeners/render.js` change\n * listener.\n *\n * @param {string} token\n * @return {Object}\n */\nexport function previewShow( token ) {\n\treturn ( dispatch, getState ) => {\n\t\tdispatch(\n\t\t\ttimedAction( {\n\t\t\t\ttype: types.PREVIEW_SHOW,\n\t\t\t\ttoken\n\t\t\t} )\n\t\t);\n\n\t\treturn wait( PREVIEW_SEEN_DURATION )\n\t\t\t.then( () => {\n\t\t\t\tconst state = getState(),\n\t\t\t\t\tpreview = state.preview,\n\t\t\t\t\tfetchResponse = preview && preview.fetchResponse,\n\t\t\t\t\tcurrentToken = preview && preview.activeToken,\n\t\t\t\t\tvalidType = fetchResponse && [\n\t\t\t\t\t\tpreviewTypes.TYPE_PAGE,\n\t\t\t\t\t\tpreviewTypes.TYPE_DISAMBIGUATION\n\t\t\t\t\t].indexOf( fetchResponse.type ) > -1;\n\n\t\t\t\tif (\n\t\t\t\t\t// Check the pageview can still be associated with original event\n\t\t\t\t\tcurrentToken && currentToken === token &&\n\t\t\t\t\t// and the preview is still active and of type `page`\n\t\t\t\t\tfetchResponse && validType\n\t\t\t\t) {\n\t\t\t\t\tdispatch( {\n\t\t\t\t\t\ttype: types.PREVIEW_SEEN,\n\t\t\t\t\t\ttitle: fetchResponse.title,\n\t\t\t\t\t\tpageId: fetchResponse.pageId,\n\t\t\t\t\t\t// The existing version of summary endpoint does not\n\t\t\t\t\t\t// provide namespace information, but new version\n\t\t\t\t\t\t// will. Given we only show pageviews for main namespace\n\t\t\t\t\t\t// this is hardcoded until the newer version is available.\n\t\t\t\t\t\tnamespace: 0\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t} );\n\t};\n}\n\n/**\n * Represents the situation when a pageview has been logged\n * (see previewShow and PREVIEW_SEEN action type)\n *\n * @return {Object}\n */\nexport function pageviewLogged() {\n\treturn {\n\t\ttype: types.PAGEVIEW_LOGGED\n\t};\n}\n\n/**\n * Represents the user clicking either the \"Enable previews\" footer menu link,\n * or the \"cog\" icon that's present on each preview.\n *\n * @return {Object}\n */\nexport function showSettings() {\n\treturn {\n\t\ttype: types.SETTINGS_SHOW\n\t};\n}\n\n/**\n * Represents the user closing the settings dialog and saving their settings.\n *\n * @return {Object}\n */\nexport function hideSettings() {\n\treturn {\n\t\ttype: types.SETTINGS_HIDE\n\t};\n}\n\n/**\n * Represents the user saving their settings.\n *\n * N.B. This action returns a Redux.Thunk not because it needs to perform\n * asynchronous work, but because it needs to query the global state for the\n * current enabled state. In order to keep the enabled state in a single\n * place (the preview reducer), we query it and dispatch it as `wasEnabled`\n * so that other reducers (like settings) can act on it without having to\n * duplicate the `enabled` state locally.\n * See docs/adr/0003-keep-enabled-state-only-in-preview-reducer.md for more\n * details.\n *\n * @param {boolean} enabled if previews are enabled or not\n * @return {Redux.Thunk}\n */\nexport function saveSettings( enabled ) {\n\treturn ( dispatch, getState ) => {\n\t\tdispatch( {\n\t\t\ttype: types.SETTINGS_CHANGE,\n\t\t\twasEnabled: getState().preview.enabled,\n\t\t\tenabled\n\t\t} );\n\t};\n}\n\n/**\n * Represents the queued event being logged `changeListeners/eventLogging.js`\n * change listener.\n *\n * @param {Object} event\n * @return {Object}\n */\nexport function eventLogged( event ) {\n\treturn {\n\t\ttype: types.EVENT_LOGGED,\n\t\tevent\n\t};\n}\n\n/**\n * Represents the queued statsv event being logged.\n * See `mw.popups.changeListeners.statsv` change listener.\n *\n * @return {Object}\n */\nexport function statsvLogged() {\n\treturn {\n\t\ttype: types.STATSV_LOGGED\n\t};\n}\n","/**\n * Creates the next state tree from the current state tree and some updates.\n *\n * N.B. OO.copy doesn't copy Element instances, whereas $.extend does.\n * However, OO.copy does copy properties whose values are undefined or null,\n * whereas $.extend doesn't. Since the state tree contains an Element instance\n * - the preview.activeLink property - and we want to copy undefined/null into\n * the state we need to manually iterate over updates and check with\n * hasOwnProperty to copy over to the new state.\n *\n * In [change listeners](/docs/change_listener.md), for example, we talk about\n * the previous state and the current state (the `prevState` and `state`\n * parameters, respectively). Since\n * [reducers](http://redux.js.org/docs/basics/Reducers.html) take the current\n * state and an action and make updates, \"next state\" seems appropriate.\n *\n * @param {Object} state\n * @param {Object} updates\n * @return {Object}\n */\nexport default function nextState( state, updates ) {\n\tconst result = {};\n\n\tfor ( const key in state ) {\n\t\tif ( state.hasOwnProperty( key ) && !updates.hasOwnProperty( key ) ) {\n\t\t\tresult[ key ] = state[ key ];\n\t\t}\n\t}\n\n\tfor ( const key in updates ) {\n\t\tif ( updates.hasOwnProperty( key ) ) {\n\t\t\tresult[ key ] = updates[ key ];\n\t\t}\n\t}\n\n\treturn result;\n}\n","/**\n * @module reducers/eventLogging\n */\n\nimport actionTypes from '../actionTypes';\nimport nextState from './nextState';\nimport * as counts from '../counts';\n\n/**\n * Initialize the data that's shared between all events.\n *\n * @param {Object} bootAction\n * @return {Object}\n */\nfunction getBaseData( bootAction ) {\n\tconst result = {\n\t\tpageTitleSource: bootAction.page.title,\n\t\tnamespaceIdSource: bootAction.page.namespaceId,\n\t\tpageIdSource: bootAction.page.id,\n\t\tisAnon: bootAction.user.isAnon,\n\t\tpopupEnabled: bootAction.isEnabled,\n\t\tpageToken: bootAction.pageToken,\n\t\tsessionToken: bootAction.sessionToken,\n\t\tpreviewCountBucket: counts.getPreviewCountBucket(\n\t\t\tbootAction.user.previewCount\n\t\t),\n\t\thovercardsSuppressedByGadget: bootAction.isNavPopupsEnabled\n\t};\n\n\tif ( !bootAction.user.isAnon ) {\n\t\tresult.editCountBucket =\n\t\t\tcounts.getEditCountBucket( bootAction.user.editCount );\n\t}\n\n\treturn result;\n}\n\n/**\n * Takes data specific to the action and adds the following properties:\n *\n * * `linkInteractionToken`;\n * * `pageTitleHover` and `namespaceIdHover`; and\n * * `previewType` and `perceivedWait`, if a preview has been shown.\n *\n * The linkInteractionToken is renewed on each new preview dwelling unlike the pageToken which has a\n * lifespan tied to the pageview. It is erroneous to use the same linkInteractionToken across\n * multiple previews even if the previews are for the same link.\n *\n * @param {Object} interaction\n * @param {Object} actionData Data specific to the action, e.g. see\n * {@link module:reducers/eventLogging~createClosingEvent `createClosingEvent`}\n * @return {Object}\n */\nfunction createEvent( interaction, actionData ) {\n\tactionData.linkInteractionToken = interaction.token;\n\tactionData.pageTitleHover = interaction.title;\n\tactionData.namespaceIdHover = interaction.namespaceId;\n\n\t// Has the preview been shown?\n\tif ( interaction.timeToPreviewShow !== undefined ) {\n\t\tactionData.previewType = interaction.previewType;\n\t\tactionData.perceivedWait = interaction.timeToPreviewShow;\n\t}\n\n\treturn actionData;\n}\n\n/**\n * Creates an event that, when mixed into the base data (see\n * {@link module:reducers/eventLogging~getBaseData `getBaseData`}), represents\n * the user abandoning a link or preview.\n *\n * Since the event should be logged when the user has either abandoned a link or\n * dwelled on a different link, we refer to these events as \"closing\" events as\n * the link interaction has finished and a new one will be created later.\n *\n * If the link interaction is finalized, then no closing event is created.\n *\n * @param {Object} interaction\n * @return {Object|undefined}\n */\nfunction createClosingEvent( interaction ) {\n\tconst actionData = {\n\t\ttotalInteractionTime:\n\t\t\tMath.round( interaction.finished - interaction.started )\n\t};\n\n\tif ( interaction.finalized ) {\n\t\treturn undefined;\n\t}\n\n\t// Has the preview been shown? If so, then, in the context of the\n\t// instrumentation, then the preview has been dismissed by the user\n\t// rather than the user has abandoned the link.\n\tactionData.action =\n\t\tinteraction.timeToPreviewShow ? 'dismissed' : 'dwelledButAbandoned';\n\n\treturn createEvent( interaction, actionData );\n}\n\n/**\n * Reducer for actions that may result in an event being logged with [the\n * Popups schema][0] via EventLogging.\n *\n * The complexity of this reducer reflects the complexity of [the schema][0],\n * which is compounded by the introduction of two delays introduced by the\n * system to provide reasonable performance and a consistent UX.\n *\n * The reducer must:\n *\n * * Accumulate the state required to log events. This state is\n * referred to as \"the interaction state\" or \"the interaction\";\n * * Enforce the invariant that one event is logged per interaction;\n * * Defend against delayed actions being dispatched; and, as a direct\n * consequence\n * * Handle transitioning from one interaction to another at the same time.\n *\n * Furthermore, we distinguish between \"finalizing\" and \"closing\" the current\n * interaction state. Since only one event should be logged per link\n * interaction, we say that the interaction state is *finalized* when an event\n * has been logged and is *closed* when a new interaction should be created.\n * In practice, the interaction state is only finalized when the user clicks a\n * link or a preview.\n *\n * [0]: https://meta.wikimedia.org/wiki/Schema:Popups\n *\n * @param {Object} state\n * @param {Object} action\n * @return {Object} The state resulting from reducing the action with the\n * current state\n */\nexport default function eventLogging( state, action ) {\n\tlet nextCount, newState;\n\tconst actionTypesWithTokens = [\n\t\tactionTypes.FETCH_COMPLETE,\n\t\tactionTypes.ABANDON_END,\n\t\tactionTypes.PREVIEW_SHOW\n\t];\n\n\tif ( state === undefined ) {\n\t\tstate = {\n\t\t\tpreviewCount: undefined,\n\t\t\tbaseData: {},\n\t\t\tinteraction: undefined,\n\t\t\tevent: undefined\n\t\t};\n\t}\n\n\t// Was the action delayed? Then it requires a token to be reduced. Enforce\n\t// this here to avoid repetition and reduce nesting below.\n\tif (\n\t\tactionTypesWithTokens.indexOf( action.type ) !== -1 &&\n\t\t( !state.interaction || action.token !== state.interaction.token )\n\t) {\n\t\treturn state;\n\t}\n\n\t// If there is no interaction ongoing, ignore all actions except for:\n\t// * Application initialization\n\t// * New link dwells (which start a new interaction)\n\t// * Clearing queued events\n\t//\n\t// For example, after ctrl+clicking a link or preview, any other actions\n\t// until the new interaction should be ignored.\n\tif (\n\t\t!state.interaction &&\n\t\taction.type !== actionTypes.BOOT &&\n\t\taction.type !== actionTypes.LINK_DWELL &&\n\t\taction.type !== actionTypes.EVENT_LOGGED &&\n\t\taction.type !== actionTypes.SETTINGS_CHANGE\n\t) {\n\t\treturn state;\n\t}\n\tswitch ( action.type ) {\n\t\tcase actionTypes.BOOT:\n\t\t\treturn nextState( state, {\n\t\t\t\tpreviewCount: action.user.previewCount,\n\t\t\t\tbaseData: getBaseData( action ),\n\t\t\t\tevent: {\n\t\t\t\t\taction: 'pageLoaded'\n\t\t\t\t}\n\t\t\t} );\n\n\t\tcase actionTypes.EVENT_LOGGED:\n\t\t\tnewState = nextState( state, {\n\t\t\t\tevent: undefined\n\t\t\t} );\n\n\t\t\t// If an event was logged with an interaction token, and it is still\n\t\t\t// the current interaction, finish the interaction since logging is\n\t\t\t// the exit point of the state machine and an interaction should never\n\t\t\t// be logged twice.\n\t\t\tif (\n\t\t\t\taction.event.linkInteractionToken &&\n\t\t\t\tstate.interaction &&\n\t\t\t\t( action.event.linkInteractionToken === state.interaction.token )\n\t\t\t) {\n\t\t\t\tnewState.interaction = undefined;\n\t\t\t}\n\t\t\treturn newState;\n\n\t\tcase actionTypes.FETCH_COMPLETE:\n\t\t\treturn nextState( state, {\n\t\t\t\tinteraction: nextState( state.interaction, {\n\t\t\t\t\tpreviewType: action.result.type\n\t\t\t\t} )\n\t\t\t} );\n\n\t\tcase actionTypes.PREVIEW_SHOW:\n\t\t\tnextCount = state.previewCount + 1;\n\n\t\t\treturn nextState( state, {\n\t\t\t\tpreviewCount: nextCount,\n\t\t\t\tbaseData: nextState( state.baseData, {\n\t\t\t\t\tpreviewCountBucket: counts.getPreviewCountBucket( nextCount )\n\t\t\t\t} ),\n\t\t\t\tinteraction: nextState( state.interaction, {\n\t\t\t\t\ttimeToPreviewShow:\n\t\t\t\t\t\tMath.round( action.timestamp - state.interaction.started )\n\t\t\t\t} )\n\t\t\t} );\n\n\t\tcase actionTypes.LINK_DWELL:\n\n\t\t\t// Not a new interaction?\n\t\t\tif ( state.interaction && action.el === state.interaction.link ) {\n\t\t\t\treturn nextState( state, {\n\t\t\t\t\tinteraction: nextState( state.interaction, {\n\t\t\t\t\t\tisUserDwelling: true\n\t\t\t\t\t} )\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\treturn nextState( state, {\n\n\t\t\t\t// TODO: Extract this object into a module that can be shared between\n\t\t\t\t// this and the preview reducer.\n\t\t\t\tinteraction: {\n\t\t\t\t\tlink: action.el,\n\t\t\t\t\ttitle: action.title,\n\t\t\t\t\tnamespaceId: action.namespaceId,\n\t\t\t\t\ttoken: action.token,\n\t\t\t\t\tstarted: action.timestamp,\n\n\t\t\t\t\tisUserDwelling: true\n\t\t\t\t},\n\n\t\t\t\t// Was the user interacting with another link? If so, then log the\n\t\t\t\t// abandoned event.\n\t\t\t\tevent: state.interaction ?\n\t\t\t\t\tcreateClosingEvent( state.interaction ) : undefined\n\t\t\t} );\n\n\t\tcase actionTypes.PREVIEW_DWELL:\n\t\t\treturn nextState( state, {\n\t\t\t\tinteraction: nextState( state.interaction, {\n\t\t\t\t\tisUserDwelling: true\n\t\t\t\t} )\n\t\t\t} );\n\n\t\tcase actionTypes.LINK_CLICK:\n\t\t\treturn nextState( state, {\n\t\t\t\tinteraction: nextState( state.interaction, {\n\t\t\t\t\tfinalized: true\n\t\t\t\t} ),\n\t\t\t\tevent: createEvent( state.interaction, {\n\t\t\t\t\taction: 'opened',\n\t\t\t\t\ttotalInteractionTime:\n\t\t\t\t\t\tMath.round( action.timestamp - state.interaction.started )\n\t\t\t\t} )\n\t\t\t} );\n\n\t\tcase actionTypes.ABANDON_START:\n\t\t\treturn nextState( state, {\n\t\t\t\tinteraction: nextState( state.interaction, {\n\t\t\t\t\tfinished: action.timestamp,\n\n\t\t\t\t\tisUserDwelling: false\n\t\t\t\t} )\n\t\t\t} );\n\n\t\tcase actionTypes.ABANDON_END:\n\t\t\tif ( !state.interaction.isUserDwelling ) {\n\t\t\t\treturn nextState( state, {\n\t\t\t\t\tinteraction: undefined,\n\t\t\t\t\tevent: createClosingEvent( state.interaction )\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\treturn state;\n\n\t\tcase actionTypes.SETTINGS_SHOW:\n\t\t\treturn nextState( state, {\n\t\t\t\tevent: createEvent( state.interaction, {\n\t\t\t\t\taction: 'tapped settings cog'\n\t\t\t\t} )\n\t\t\t} );\n\n\t\tcase actionTypes.SETTINGS_CHANGE:\n\t\t\tif ( action.wasEnabled && !action.enabled ) {\n\t\t\t\treturn nextState( state, {\n\t\t\t\t\tevent: {\n\t\t\t\t\t\taction: 'disabled',\n\t\t\t\t\t\tpopupEnabled: false\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t} else {\n\t\t\t\treturn state;\n\t\t\t}\n\t\tdefault:\n\t\t\treturn state;\n\t}\n}\n","import eventLogging from './eventLogging';\nimport pageviews from './pageviews';\nimport preview from './preview';\nimport settings from './settings';\nimport statsv from './statsv';\n\nexport default {\n\teventLogging,\n\tpageviews,\n\tpreview,\n\tsettings,\n\tstatsv\n};\n","/**\n * @module reducers/pageviews\n */\n\nimport actionTypes from '../actionTypes';\nimport nextState from './nextState';\n\n/**\n * Reducer for actions that queues and clears events for\n * being logged as virtual pageviews [0]\n *\n * [0]: https://meta.wikimedia.org/wiki/Schema:VirtualPageViews\n *\n * @param {Object} state\n * @param {Object} action\n * @return {Object} The state resulting from reducing the action with the\n * current state\n */\nexport default function pageviews( state, action ) {\n\tif ( state === undefined ) {\n\t\tstate = {\n\t\t\tpageview: undefined\n\t\t};\n\t}\n\n\tswitch ( action.type ) {\n\t\tcase actionTypes.BOOT:\n\t\t\treturn nextState( state, {\n\t\t\t\tpage: action.page\n\t\t\t} );\n\t\tcase actionTypes.PAGEVIEW_LOGGED:\n\t\t\treturn nextState( state, {\n\t\t\t\tpageview: undefined\n\t\t\t} );\n\t\tcase actionTypes.PREVIEW_SEEN:\n\t\t\treturn nextState( state, {\n\t\t\t\tpageview: {\n\t\t\t\t\t/* eslint-disable camelcase */\n\t\t\t\t\tpage_title: action.title,\n\t\t\t\t\tpage_id: action.pageId,\n\t\t\t\t\tpage_namespace: action.namespace\n\t\t\t\t\t/* eslint-enable camelcase */\n\t\t\t\t}\n\t\t\t} );\n\t\tdefault:\n\t\t\treturn state;\n\t}\n}\n","import actionTypes from '../actionTypes';\nimport nextState from './nextState';\n\n/**\n * Reducer for actions that modify the state of the preview model\n *\n * @param {?Object} state before action\n * @param {!Object} action Redux action that modified state.\n * Must have `type` property.\n * @return {!Object} state after action\n */\nexport default function preview( state, action ) {\n\tif ( state === undefined ) {\n\t\tstate = {\n\t\t\tenabled: undefined,\n\t\t\tactiveLink: undefined,\n\t\t\tactiveEvent: undefined,\n\t\t\tactiveToken: '',\n\t\t\tshouldShow: false,\n\t\t\tisUserDwelling: false\n\t\t};\n\t}\n\n\tswitch ( action.type ) {\n\t\tcase actionTypes.BOOT:\n\t\t\treturn nextState( state, {\n\t\t\t\tenabled: action.isEnabled\n\t\t\t} );\n\n\t\tcase actionTypes.SETTINGS_CHANGE:\n\t\t\treturn nextState( state, {\n\t\t\t\tenabled: action.enabled\n\t\t\t} );\n\n\t\tcase actionTypes.LINK_DWELL:\n\t\t\t// New interaction\n\t\t\tif ( action.el !== state.activeLink ) {\n\t\t\t\treturn nextState( state, {\n\t\t\t\t\tactiveLink: action.el,\n\t\t\t\t\tactiveEvent: action.event,\n\t\t\t\t\tactiveToken: action.token,\n\n\t\t\t\t\t// When the user dwells on a link with their keyboard, a preview is\n\t\t\t\t\t// renderered, and then dwells on another link, the ABANDON_END\n\t\t\t\t\t// action will be ignored.\n\t\t\t\t\t//\n\t\t\t\t\t// Ensure that all the preview is hidden.\n\t\t\t\t\tshouldShow: false,\n\n\t\t\t\t\tisUserDwelling: true,\n\t\t\t\t\tpromise: action.promise\n\t\t\t\t} );\n\t\t\t}\n\t\t\t// Dwelling back into the same link\n\t\t\treturn nextState( state, {\n\t\t\t\tisUserDwelling: true\n\t\t\t} );\n\n\t\tcase actionTypes.ABANDON_END:\n\t\t\tif ( action.token === state.activeToken && !state.isUserDwelling ) {\n\t\t\t\treturn nextState( state, {\n\t\t\t\t\tactiveLink: undefined,\n\t\t\t\t\tactiveToken: undefined,\n\t\t\t\t\tactiveEvent: undefined,\n\t\t\t\t\tfetchResponse: undefined,\n\t\t\t\t\tshouldShow: false\n\t\t\t\t} );\n\t\t\t}\n\t\t\treturn state;\n\n\t\tcase actionTypes.PREVIEW_DWELL:\n\t\t\treturn nextState( state, {\n\t\t\t\tisUserDwelling: true\n\t\t\t} );\n\n\t\tcase actionTypes.ABANDON_START:\n\t\t\treturn nextState( state, {\n\t\t\t\tisUserDwelling: false\n\t\t\t} );\n\n\t\tcase actionTypes.FETCH_START:\n\t\t\treturn nextState( state, {\n\t\t\t\tfetchResponse: undefined,\n\t\t\t\tpromise: action.promise\n\t\t\t} );\n\n\t\tcase actionTypes.FETCH_COMPLETE:\n\t\t\tif ( action.token === state.activeToken ) {\n\t\t\t\treturn nextState( state, {\n\t\t\t\t\tfetchResponse: action.result,\n\t\t\t\t\tshouldShow: state.isUserDwelling\n\t\t\t\t} );\n\t\t\t} // else fall through\n\t\tdefault:\n\t\t\treturn state;\n\t}\n}\n","import actionTypes from '../actionTypes';\nimport nextState from './nextState';\n\n/**\n * Reducer for actions that modify the state of the settings\n *\n * @param {Object} state\n * @param {Object} action\n * @return {Object} state after action\n */\nexport default function settings( state, action ) {\n\tif ( state === undefined ) {\n\t\tstate = {\n\t\t\tshouldShow: false,\n\t\t\tshowHelp: false,\n\t\t\tshouldShowFooterLink: false\n\t\t};\n\t}\n\n\tswitch ( action.type ) {\n\t\tcase actionTypes.SETTINGS_SHOW:\n\t\t\treturn nextState( state, {\n\t\t\t\tshouldShow: true,\n\t\t\t\tshowHelp: false\n\t\t\t} );\n\t\tcase actionTypes.SETTINGS_HIDE:\n\t\t\treturn nextState( state, {\n\t\t\t\tshouldShow: false,\n\t\t\t\tshowHelp: false\n\t\t\t} );\n\t\tcase actionTypes.SETTINGS_CHANGE:\n\t\t\treturn action.wasEnabled === action.enabled ?\n\t\t\t\t// If the setting is the same, just hide the dialogs\n\t\t\t\tnextState( state, {\n\t\t\t\t\tshouldShow: false\n\t\t\t\t} ) :\n\t\t\t\t// If the settings have changed...\n\t\t\t\tnextState( state, {\n\t\t\t\t\t// If we enabled, we just hide directly, no help\n\t\t\t\t\t// If we disabled, keep it showing and let the ui show the help.\n\t\t\t\t\tshouldShow: !action.enabled,\n\t\t\t\t\tshowHelp: !action.enabled,\n\n\t\t\t\t\t// Since the footer link is only ever shown to anonymous users (see\n\t\t\t\t\t// the BOOT case below), state.userIsAnon is always truthy here.\n\t\t\t\t\tshouldShowFooterLink: !action.enabled\n\t\t\t\t} );\n\n\t\tcase actionTypes.BOOT:\n\t\t\treturn nextState( state, {\n\t\t\t\tshouldShowFooterLink: action.user.isAnon && !action.isEnabled\n\t\t\t} );\n\t\tdefault:\n\t\t\treturn state;\n\t}\n}\n","import actionTypes from './../actionTypes';\nimport nextState from './nextState';\n\n/**\n * Reducer for actions that may result in an event being logged via statsv.\n *\n * @param {Object} state\n * @param {Object} action\n * @return {Object} state after action\n */\nexport default function statsv( state, action ) {\n\tstate = state || {};\n\n\tswitch ( action.type ) {\n\t\tcase actionTypes.FETCH_START:\n\t\t\treturn nextState( state, {\n\t\t\t\tfetchStartedAt: action.timestamp\n\t\t\t} );\n\n\t\tcase actionTypes.FETCH_END:\n\t\t\treturn nextState( state, {\n\t\t\t\taction: 'timing.PagePreviewsApiResponse',\n\t\t\t\tdata: action.timestamp - state.fetchStartedAt\n\t\t\t} );\n\n\t\tcase actionTypes.FETCH_FAILED:\n\t\t\treturn nextState( state, {\n\t\t\t\taction: 'counter.PagePreviewsApiFailure',\n\t\t\t\tdata: 1\n\t\t\t} );\n\n\t\tcase actionTypes.LINK_DWELL:\n\t\t\treturn nextState( state, {\n\t\t\t\tlinkDwellStartedAt: action.timestamp\n\t\t\t} );\n\n\t\tcase actionTypes.PREVIEW_SHOW:\n\t\t\treturn nextState( state, {\n\t\t\t\taction: 'timing.PagePreviewsPreviewShow',\n\t\t\t\tdata: action.timestamp - state.linkDwellStartedAt\n\t\t\t} );\n\n\t\tcase actionTypes.STATSV_LOGGED:\n\t\t\treturn nextState( state, {\n\t\t\t\taction: null,\n\t\t\t\tdata: null\n\t\t\t} );\n\n\t\tdefault:\n\t\t\treturn state;\n\t}\n}\n","/**\n * @module getPageviewTracker\n */\nconst mw = mediaWiki;\n/**\n * @typedef {Object} MwCodeLoader\n *\n * Loads code from the server to the client on demand.\n *\n * @param {array} dependencies to load\n * @return {JQuery.Deferred} resolving when the code is loaded and\n * can be used by the client.\n *\n * @global\n */\n\n/**\n * Convert the first letter of a string to uppercase.\n *\n * @param {string} word\n * @return {string}\n */\nfunction titleCase( word ) {\n\treturn word[ 0 ].toUpperCase() + word.slice( 1 );\n}\n\n/**\n * Truncates a string to a maximum length based on its URI encoded value.\n *\n * @param {string} sourceUrl source string\n * @param {number} maxLength maximum length\n * @return {string} string is returned in the same encoding as the input\n */\nfunction limitByEncodedURILength( sourceUrl, maxLength ) {\n\tlet truncatedUrl = '';\n\n\tsourceUrl.split( '' ).every( ( char ) => {\n\t\treturn ( encodeURIComponent( truncatedUrl + char ).length < maxLength ) ?\n\t\t\t( truncatedUrl += char ) :\n\t\t\tfalse;\n\t} );\n\treturn truncatedUrl;\n}\n\n/**\n * Convert Title properties into mediawiki canonical form\n * and limit the length of source_url.\n *\n * @param {Object} eventData\n * @return {Object}\n */\nfunction prepareEventData( eventData ) {\n\tconst data = eventData;\n\t/* eslint-disable camelcase */\n\tdata.source_title = mw.Title.newFromText( eventData.source_title )\n\t\t.getPrefixedDb();\n\tdata.page_title = mw.Title.newFromText( eventData.page_title )\n\t\t.getPrefixedDb();\n\t// prevent source_url from exceeding varnish max-url size - T196904\n\tdata.source_url = limitByEncodedURILength( eventData.source_url, 1000 );\n\n\t/* eslint-enable camelcase */\n\treturn data;\n}\n\n/**\n * Gets the appropriate analytics event tracker for logging virtual pageviews.\n * Note this bypasses EventLogging in order to track virtual pageviews\n * for pages where the DNT header (do not track) has been added.\n * This is explained in https://phabricator.wikimedia.org/T187277.\n *\n * @param {Object} config\n * @param {MwCodeLoader} loader that can source code that obeys the\n * EventLogging api specification.\n * @param {Function} trackerGetter when called returns an instance\n * of MediaWiki's EventLogging client\n * @param {Function} sendBeacon see\n * https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon\n * @return {EventTracker}\n */\nfunction getPageviewTracker( config, loader, trackerGetter, sendBeacon ) {\n\tconst pageviewTracker = function ( topic, eventData ) {\n\t\tconst schema = titleCase( topic.slice( topic.indexOf( '.' ) + 1 ) );\n\t\tconst dependencies = [ 'ext.eventLogging', `schema.${schema}` ];\n\t\treturn loader( dependencies ).then( function () {\n\t\t\tconst evLog = trackerGetter();\n\t\t\tconst payload = evLog.prepare( schema, prepareEventData( eventData ) );\n\t\t\tconst url = evLog.makeBeaconUrl( payload );\n\t\t\tsendBeacon( url );\n\t\t} );\n\t};\n\treturn config.get( 'wgPopupsVirtualPageViews' ) ? pageviewTracker : () => {};\n}\n\n/**\n * Gets a function that can asynchronously transfer a small amount of data\n * over HTTP to a web server.\n *\n * @param {Window.Navigator} navigatorObj\n * @return {Function}\n */\nfunction getSendBeacon( navigatorObj ) {\n\treturn navigatorObj.sendBeacon ?\n\t\tnavigatorObj.sendBeacon.bind( navigatorObj ) :\n\t\t( url ) => {\n\t\t\tdocument.createElement( 'img' ).src = url;\n\t\t};\n}\n\nexport { getSendBeacon, limitByEncodedURILength };\nexport default getPageviewTracker;\n","/**\n * @module popups\n */\n\nimport * as Redux from 'redux';\nimport * as ReduxThunk from 'redux-thunk';\n\nimport createPagePreviewGateway from './gateway';\nimport createReferenceGateway from './gateway/reference';\nimport createUserSettings from './userSettings';\nimport createPreviewBehavior from './previewBehavior';\nimport createSettingsDialogRenderer from './ui/settingsDialogRenderer';\nimport registerChangeListener from './changeListener';\nimport createIsEnabled from './isEnabled';\nimport { fromElement as titleFromElement } from './title';\nimport { init as rendererInit } from './ui/renderer';\nimport createExperiments from './experiments';\nimport { isEnabled as isStatsvEnabled } from './instrumentation/statsv';\nimport { isEnabled as isEventLoggingEnabled }\n\tfrom './instrumentation/eventLogging';\nimport changeListeners from './changeListeners';\nimport * as actions from './actions';\nimport reducers from './reducers';\nimport createMediaWikiPopupsObject from './integrations/mwpopups';\nimport getPageviewTracker, { getSendBeacon } from './getPageviewTracker';\n\nconst mw = mediaWiki,\n\t$ = jQuery,\n\n\tBLACKLISTED_LINKS = [\n\t\t'.extiw',\n\t\t'.image',\n\t\t'.new',\n\t\t'.internal',\n\t\t'.external',\n\t\t'.mw-cite-backlink a',\n\t\t'.oo-ui-buttonedElement-button',\n\t\t'.cancelLink a'\n\t];\n\n/**\n * @typedef {Function} EventTracker\n *\n * An analytics event tracker, i.e. `mw.track`.\n *\n * @param {string} topic\n * @param {Object} data\n *\n * @global\n */\n\n/**\n * Gets the appropriate analytics event tracker for logging metrics to StatsD\n * via [the \"StatsD timers and counters\" analytics event protocol][0].\n *\n * If logging metrics to StatsD is enabled for the duration of the user's\n * session, then the appriopriate function is `mw.track`; otherwise it's\n * `() => {}`.\n *\n * [0]: https://github.com/wikimedia/mediawiki-extensions-WikimediaEvents/blob/29c864a0/modules/ext.wikimediaEvents.statsd.js\n *\n * @param {Object} user\n * @param {Object} config\n * @param {Experiments} experiments\n * @return {EventTracker}\n */\nfunction getStatsvTracker( user, config, experiments ) {\n\treturn isStatsvEnabled( user, config, experiments ) ? mw.track : () => {};\n}\n\n/**\n * Gets the appropriate analytics event tracker for logging EventLogging events\n * via [the \"EventLogging subscriber\" analytics event protocol][0].\n *\n * If logging EventLogging events is enabled for the duration of the user's\n * session, then the appriopriate function is `mw.track`; otherwise it's\n * `() => {}`.\n *\n * [0]: https://github.com/wikimedia/mediawiki-extensions-EventLogging/blob/d1409759/modules/ext.eventLogging.subscriber.js\n *\n * @param {Object} user\n * @param {Object} config\n * @param {Window} window\n * @return {EventTracker}\n */\nfunction getEventLoggingTracker( user, config, window ) {\n\treturn isEventLoggingEnabled(\n\t\tuser,\n\t\tconfig,\n\t\twindow\n\t) ? mw.track : () => {};\n}\n\n/**\n * Returns timestamp since the beginning of the current document's origin\n * as reported by `window.performance.now()`. See\n * https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp#The_time_origin\n * for a detailed explanation of the time origin.\n *\n * The value returned by this function is used for [the `timestamp` property\n * of the Schema:Popups events sent by the EventLogging\n * instrumentation](./src/changeListeners/eventLogging.js).\n *\n * @return {number|null}\n */\nfunction getCurrentTimestamp() {\n\tif ( window.performance && window.performance.now ) {\n\t\t// return an integer; see T182000\n\t\treturn Math.round( window.performance.now() );\n\t}\n\treturn null;\n}\n\n/**\n * Subscribes the registered change listeners to the\n * [store](http://redux.js.org/docs/api/Store.html#store).\n *\n * @param {Redux.Store} store\n * @param {Object} actions\n * @param {UserSettings} userSettings\n * @param {Function} settingsDialog\n * @param {PreviewBehavior} previewBehavior\n * @param {EventTracker} statsvTracker\n * @param {EventTracker} eventLoggingTracker\n * @param {EventTracker} pageviewTracker\n * @param {Function} getCurrentTimestamp\n * @return {void}\n */\nfunction registerChangeListeners(\n\tstore, actions, userSettings, settingsDialog, previewBehavior,\n\tstatsvTracker, eventLoggingTracker, pageviewTracker, getCurrentTimestamp\n) {\n\tregisterChangeListener( store, changeListeners.footerLink( actions ) );\n\tregisterChangeListener( store, changeListeners.linkTitle() );\n\tregisterChangeListener( store, changeListeners.render( previewBehavior ) );\n\tregisterChangeListener(\n\t\tstore, changeListeners.statsv( actions, statsvTracker ) );\n\tregisterChangeListener(\n\t\tstore, changeListeners.syncUserSettings( userSettings ) );\n\tregisterChangeListener(\n\t\tstore, changeListeners.settings( actions, settingsDialog ) );\n\tregisterChangeListener(\n\t\tstore,\n\t\tchangeListeners.eventLogging(\n\t\t\tactions, eventLoggingTracker, getCurrentTimestamp\n\t\t) );\n\tregisterChangeListener( store,\n\t\tchangeListeners.pageviews( actions, pageviewTracker )\n\t);\n}\n\n/*\n * Initialize the application by:\n * 1. Initializing side-effects and \"services\"\n * 2. Creating the state store\n * 3. Binding the actions to such store\n * 4. Registering change listeners\n * 5. Triggering the boot action to bootstrap the system\n * 6. When the page content is ready:\n * - Initializing the renderer\n * - Binding hover and click events to the eligible links to trigger actions\n */\n( function init() {\n\tlet compose = Redux.compose;\n\tconst\n\t\t// So-called \"services\".\n\t\tgenerateToken = mw.user.generateRandomSessionId,\n\t\tpagePreviewGateway = createPagePreviewGateway( mw.config ),\n\t\treferenceGateway = createReferenceGateway(),\n\t\tuserSettings = createUserSettings( mw.storage ),\n\t\tsettingsDialog = createSettingsDialogRenderer(),\n\t\texperiments = createExperiments( mw.experiments ),\n\t\tstatsvTracker = getStatsvTracker( mw.user, mw.config, experiments ),\n\t\t// Virtual pageviews are always tracked.\n\t\tpageviewTracker = getPageviewTracker( mw.config,\n\t\t\tmw.loader.using,\n\t\t\t() => mw.eventLog,\n\t\t\tgetSendBeacon( window.navigator )\n\t\t),\n\t\teventLoggingTracker = getEventLoggingTracker(\n\t\t\tmw.user,\n\t\t\tmw.config,\n\t\t\twindow\n\t\t),\n\t\tisEnabled = createIsEnabled( mw.user, userSettings, mw.config );\n\n\t// If debug mode is enabled, then enable Redux DevTools.\n\tif ( mw.config.get( 'debug' ) === true ||\n\t\tprocess.env.NODE_ENV !== 'production' ) { // eslint-disable-line no-undef\n\t\t// eslint-disable-next-line no-underscore-dangle\n\t\tcompose = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;\n\t}\n\n\tconst store = Redux.createStore(\n\t\tRedux.combineReducers( reducers ),\n\t\tcompose( Redux.applyMiddleware(\n\t\t\tReduxThunk.default\n\t\t) )\n\t);\n\tconst boundActions = Redux.bindActionCreators( actions, store.dispatch );\n\tconst previewBehavior = createPreviewBehavior( mw.user, boundActions );\n\n\tregisterChangeListeners(\n\t\tstore, boundActions, userSettings, settingsDialog,\n\t\tpreviewBehavior, statsvTracker, eventLoggingTracker,\n\t\tpageviewTracker,\n\t\tgetCurrentTimestamp\n\t);\n\n\tboundActions.boot(\n\t\tisEnabled,\n\t\tmw.user,\n\t\tuserSettings,\n\t\tmw.config,\n\t\twindow.location.href\n\t);\n\n\t/*\n\t * Register external interface exposing popups internals so that other\n\t * extensions can query it (T171287)\n\t */\n\tmw.popups = createMediaWikiPopupsObject( store );\n\n\tconst invalidLinksSelector = BLACKLISTED_LINKS.join( ', ' );\n\tlet validLinkSelector = `#mw-content-text a[href][title]:not(${ invalidLinksSelector })`;\n\tif ( mw.config.get( 'wgPopupsReferencePreviews' ) ) {\n\t\tvalidLinkSelector += ', .reference > a[href]';\n\t}\n\n\trendererInit();\n\n\t/*\n\t * Binding hover and click events to the eligible links to trigger actions\n\t */\n\t$( document )\n\t\t.on( 'mouseover keyup', validLinkSelector, function ( event ) {\n\t\t\tconst mwTitle = titleFromElement( this, mw.config );\n\t\t\tlet gateway = pagePreviewGateway;\n\n\t\t\tif ( mw.config.get( 'wgPopupsReferencePreviews' ) ) {\n\t\t\t\t// TODO: Can this condition be true for non-reference links?\n\t\t\t\tif ( $( event.target ).parent().hasClass( 'reference' ) ) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// TODO: Possibly refactor titleFromElement() to avoid duplicate Uri() call\n\t\t\t\t\t\tmwTitle.fragment = new mw.Uri( event.target.href ).fragment;\n\t\t\t\t\t} catch ( e ) {\n\t\t\t\t\t\t// Skip links with broken encoding\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tgateway = referenceGateway;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( mwTitle ) {\n\t\t\t\tboundActions.linkDwell(\n\t\t\t\t\tmwTitle, this, event, gateway, generateToken\n\t\t\t\t);\n\t\t\t}\n\t\t} )\n\t\t.on( 'mouseout blur', validLinkSelector, function () {\n\t\t\tconst mwTitle = titleFromElement( this, mw.config );\n\n\t\t\tif ( mwTitle ) {\n\t\t\t\tboundActions.abandon( this );\n\t\t\t}\n\t\t} )\n\t\t.on( 'click', validLinkSelector, function () {\n\t\t\tconst mwTitle = titleFromElement( this, mw.config );\n\n\t\t\tif ( mwTitle ) {\n\t\t\t\tboundActions.linkClick( this );\n\t\t\t}\n\t\t} );\n}() );\n\nwindow.Redux = Redux;\nwindow.ReduxThunk = ReduxThunk;\n","/**\n * @module experiments\n */\n\n/**\n * @interface Experiments\n *\n * @global\n */\n\n/**\n * Creates a helper wrapper for the MediaWiki-provided\n * `mw.experiments#getBucket` bucketing function.\n *\n * @param {mw.experiments} mwExperiments The `mw.experiments` singleton instance\n * @return {Experiments}\n */\nexport default function createExperiments( mwExperiments ) {\n\treturn {\n\n\t\t/**\n\t\t * Gets whether something is true given a name and a token.\n\t\t *\n\t\t * @example\n\t\t * import createExperiments from './src/experiments';\n\t\t * const experiments = createExperiments( mw.experiments );\n\t\t * const isFooEnabled = experiments.weightedBoolean(\n\t\t * 'foo',\n\t\t * 10 / 100, // 10% of all unique tokens should have foo enabled.\n\t\t * token\n\t\t * );\n\t\t *\n\t\t * @function\n\t\t * @name Experiments#weightedBoolean\n\t\t * @param {string} name The name of the thing. Since this is used as the\n\t\t * name of the underlying experiment it should be unique to reduce the\n\t\t * likelihood of collisions with other enabled experiments\n\t\t * @param {number} trueWeight A number between 0 and 1, representing the\n\t\t * probability of the thing being true\n\t\t * @param {string} token A token associated with the user for the duration\n\t\t * of the experiment\n\t\t * @return {boolean}\n\t\t */\n\t\tweightedBoolean( name, trueWeight, token ) {\n\t\t\treturn mwExperiments.getBucket( {\n\t\t\t\tenabled: true,\n\n\t\t\t\tname,\n\t\t\t\tbuckets: {\n\t\t\t\t\t'true': trueWeight,\n\t\t\t\t\t'false': 1 - trueWeight\n\t\t\t\t}\n\t\t\t}, token ) === 'true';\n\t\t}\n\t};\n}\n","/**\n * @module instrumentation/statsv\n */\n\n/**\n * Gets whether Graphite logging (via [the statsv HTTP endpoint][0]) is enabled\n * for the duration of the user's session. The bucketing rate is controlled by\n * `wgPopupsStatsvSamplingRate`.\n *\n * [0]: https://wikitech.wikimedia.org/wiki/Graphite#statsv\n *\n * @param {mw.user} user The `mw.user` singleton instance\n * @param {mw.Map} config The `mw.config` singleton instance\n * @param {Experiments} experiments\n * @return {boolean}\n */\nexport function isEnabled( user, config, experiments ) {\n\tconst bucketingRate = config.get( 'wgPopupsStatsvSamplingRate', 0 );\n\n\treturn experiments.weightedBoolean(\n\t\t'ext.Popups.statsv',\n\t\tbucketingRate,\n\t\tuser.sessionId()\n\t);\n}\n","/**\n * @module instrumentation/eventLogging\n */\n\n/**\n * Gets whether EventLogging logging is enabled for the duration of the user's\n * session.\n * If wgPopupsEventLogging is false this will return false unless debug=true has\n * been enabled.\n * However, if the UA doesn't support [the Beacon API][1], then bucketing is\n * disabled.\n *\n * [1]: https://w3c.github.io/beacon/\n *\n * @param {mw.user} user The `mw.user` singleton instance\n * @param {mw.Map} config The `mw.config` singleton instance\n * @param {Window} window\n * @return {boolean}\n */\nexport function isEnabled( user, config, window ) {\n\t// if debug mode is on, always enable event logging. @see T168847\n\tif ( config.get( 'debug' ) === true ) {\n\t\treturn true;\n\t}\n\n\tif ( !config.get( 'wgPopupsEventLogging' ) ) {\n\t\treturn false;\n\t}\n\n\tif (\n\t\t!window.navigator ||\n\t\t!window.navigator.sendBeacon\n\t) {\n\t\treturn false;\n\t}\n\n\treturn user.isAnon();\n}\n","/**\n * @module isEnabled\n */\n\n/**\n * Given the global state of the application, creates a function that gets\n * whether or not the user should have Page Previews enabled.\n *\n * Page Previews is disabled when the Navigation Popups gadget is enabled.\n *\n * If Page Previews is configured as a user preference, then the user must\n * either be logged in and have enabled the preference or be logged out and have\n * not disabled previews via the settings modal.\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}\n */\nexport default function isEnabled( user, userSettings, config ) {\n\tif ( config.get( 'wgPopupsConflictsWithNavPopupGadget' ) ) {\n\t\treturn false;\n\t}\n\n\tif ( !user.isAnon() ) {\n\t\treturn config.get( 'wgPopupsShouldSendModuleToUser' );\n\t}\n\n\tif ( !userSettings.hasIsEnabled() ) {\n\t\treturn true;\n\t}\n\n\treturn userSettings.getIsEnabled();\n}\n","/**\n * @module MediaWiki-Popups Integration\n */\n\n/**\n * This function provides a mw.popups object which can be used by 3rd party\n * to interact with Popups. Currently it allows only to read isEnabled flag.\n *\n * @param {Redux.Store} store Popups store\n * @return {Object} external Popups interface\n */\nexport default function createMwPopups( store ) {\n\treturn {\n\t\tisEnabled: function isEnabled() {\n\t\t\treturn store.getState().preview.enabled;\n\t\t}\n\t};\n\n}\n","module.exports = \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"0\\\" height=\\\"0\\\"><defs><clippath id=\\\"mwe-popups-mask\\\"><polygon points=\\\"0 8, 10 8, 18 0, 26 8, 1000 8, 1000 1000, 0 1000\\\"></polygon></clippath><clippath id=\\\"mwe-popups-mask-flip\\\"><polygon points=\\\"0 8, 274 8, 282 0, 290 8, 1000 8, 1000 1000, 0 1000\\\"></polygon></clippath><clippath id=\\\"mwe-popups-landscape-mask\\\"><polygon points=\\\"0 8, 174 8, 182 0, 190 8, 1000 8, 1000 1000, 0 1000\\\"></polygon></clippath><clippath id=\\\"mwe-popups-landscape-mask-flip\\\"><polygon points=\\\"0 0, 1000 0, 1000 242, 190 242, 182 250, 174 242, 0 242\\\"></polygon></clippath></defs></svg>\""],"sourceRoot":""} |