{"version":3,"sources":["webpack://mfModules.[name]/./src/constants.js","webpack://mfModules.[name]/./src/mobile.init/fakeToolbar.js","webpack://mfModules.[name]/./src/mobile.startup/Anchor.js","webpack://mfModules.[name]/./src/mobile.startup/Browser.js","webpack://mfModules.[name]/./src/mobile.startup/Button.js","webpack://mfModules.[name]/./src/mobile.startup/CtaDrawer.js","webpack://mfModules.[name]/./src/mobile.startup/Drawer.js","webpack://mfModules.[name]/./src/mobile.startup/Icon.js","webpack://mfModules.[name]/./src/mobile.startup/Overlay.js","webpack://mfModules.[name]/./src/mobile.startup/OverlayManager.js","webpack://mfModules.[name]/./src/mobile.startup/Page.js","webpack://mfModules.[name]/./src/mobile.startup/PageGateway.js","webpack://mfModules.[name]/./src/mobile.startup/PageHTMLParser.js","webpack://mfModules.[name]/./src/mobile.startup/PageList.js","webpack://mfModules.[name]/./src/mobile.startup/Section.js","webpack://mfModules.[name]/./src/mobile.startup/Skin.js","webpack://mfModules.[name]/./src/mobile.startup/Thumbnail.js","webpack://mfModules.[name]/./src/mobile.startup/Toggler.js","webpack://mfModules.[name]/./src/mobile.startup/View.js","webpack://mfModules.[name]/./src/mobile.startup/actionParams.js","webpack://mfModules.[name]/./src/mobile.startup/amcOutreach/AmcEnableForm.js","webpack://mfModules.[name]/./src/mobile.startup/amcOutreach/amcOutreach.js","webpack://mfModules.[name]/./src/mobile.startup/amcOutreach/amcOutreachDrawer.js","webpack://mfModules.[name]/./src/mobile.startup/currentPage.js","webpack://mfModules.[name]/./src/mobile.startup/currentPageHTMLParser.js","webpack://mfModules.[name]/./src/mobile.startup/eventBusSingleton.js","webpack://mfModules.[name]/./src/mobile.startup/extendSearchParams.js","webpack://mfModules.[name]/./src/mobile.startup/headers.js","webpack://mfModules.[name]/./src/mobile.startup/icons.js","webpack://mfModules.[name]/./src/mobile.startup/lazyImages/lazyImageLoader.js","webpack://mfModules.[name]/./src/mobile.startup/mfExtend.js","webpack://mfModules.[name]/./src/mobile.startup/moduleLoader.js","webpack://mfModules.[name]/./src/mobile.startup/moduleLoaderSingleton.js","webpack://mfModules.[name]/./src/mobile.startup/page/pageJSONParser.js","webpack://mfModules.[name]/./src/mobile.startup/promoCampaign/promoCampaign.js","webpack://mfModules.[name]/./src/mobile.startup/showOnPageReload.js","webpack://mfModules.[name]/./src/mobile.startup/util.js","webpack://mfModules.[name]/./src/mobile.startup/watchstar/WatchstarGateway.js","webpack://mfModules.[name]/./src/mobile.startup/watchstar/WatchstarPageList.js","webpack://mfModules.[name]/./src/mobile.startup/watchstar/watchstar.js"],"names":["USER_FONT_SIZES","module","exports","USER_FONT_SIZE_REGULAR","icons","require","$goBack","$loadingMessage","cancel","tagName","$el","attr","spinner","type","additionalClassNames","label","mw","msg","$","addClass","append","View","util","Anchor","progressive","undefined","destructive","href","template","browser","memoize","method","memoized","cache","this","cacheId","key","join","call","arguments","Object","prototype","hasOwnProperty","apply","Date","now","toString","Math","random","Browser","ua","$container","userAgent","isIos","version","ios","test","isWideScreen","val","parseInt","config","get","window","innerWidth","supportsTouchEvents","getSingleton","$html","getDocument","navigator","mfExtend","Button","options","isTemplateMode","defaults","disabled","block","quiet","Drawer","CtaDrawer","params","redirectParams","queryParams","returnTo","extend","children","parseHTML","text","content","getUrl","progressiveButton","signUpParams","signupQueryParams","actionAnchor","redirectURL","returnto","push","Icon","props","drawerClassName","className","collapseIcon","name","onBeforeHide","showCollapseIcon","events","hide","bind","ev","preventDefault","click","stopPropagation","minHideDelay","show","d","Deferred","find","hasClass","resolve","setTimeout","onShow","promise","removeClass","postRender","$mask","$drawer","trim","prepend","isTypeButton","preRender","_rotationClass","getRotationClass","_iconClasses","getIconClasses","rotationClass","rotation","Error","base","modifiers","getGlyphClassName","glyphPrefix","isSmall","title","getClassName","header","Overlay","headerChrome","hideTimeout","showSpinner","hideSpinner","footerAnchor","$overlayContent","headers","heading","headerActions","onExitClick","exit","onBeforeExit","scrollTop","pageYOffset","scrollTo","clearTimeout","pageXOffset","detach","emit","showHidden","make","view","overlay","overlayManager","MANAGED_STATE","OverlayManager","router","container","on","_checkRoute","entries","stack","hideCurrent","attachHideEvent","_onHideOverlayOutsideOverlayManager","length","currentRoute","route","routeIsString","currentPath","getPath","routeIsSame","match","back","_attachOverlay","parents","appendChild","_show","history","replaceState","location","once","_hideOverlay","onBeforeExitCancel","result","off","_processMatch","factoryResult","current","keys","reduce","m","id","_matchRoute","path","entry","next","didMatch","captures","previous","self","slice","shift","factory","unshift","add","docReady","replaceCurrent","stackOverlay","loader","document","createElement","hash","state","body","pushState","__clearCache","HTML","html","Page","relevantTitle","titleObj","displayTitle","escape","namespaceNumber","protection","url","wikidataDescription","_isMainPage","isMainPage","isMissing","lastModified","anchor","revId","_isWatched","isWatched","thumbnail","width","isLandscape","height","namespace","args","split","toLowerCase","replace","actionParams","PageGateway","api","invalidatePage","_getLanguageVariantsFromApiResponse","pageLang","data","variantsData","query","languageinfo","variantnames","variantPath","contLang","variants","forEach","code","variant","autonym","lang","getPageLanguages","language","viewLang","meta","liprop","licode","prop","lllimit","titles","llprop","llinlanguagecode","then","resp","languages","pages","langlinks","reject","Thumbnail","HEADING_SELECTOR","EXCLUDE_THUMBNAIL_CLASS_SELECTORS","PageHTMLParser","$headings","sectionIndex","filter","eq","selector","$heading","$nextHeading","$lead","headingSelector","withNestedChildren","$matchingNodes","addBack","getLeadSectionElement","findSectionHeadingByIndex","prevAll","nextAll","nextUntil","$leadSection","notSelector","thumbs","not","each","$a","$lazyImage","valid","legacyMatch","RegExp","el","filename","percentDecodeFragment","PageList","renderPageImages","style","templatePartials","item","Section","tag","level","line","hasReferences","subsections","section","skin","currentPage","eventBus","Skin","page","isBorderBox","getLicenseLinks","mobileLicense","message","mobileMsgExists","exists","parseDom","clone","getLicenseMsg","licenseMsg","$licenseLinks","licensePlural","convertNumber","$termsLink","parse","description","siblings","getDescription","getFileName","escapeSelector","arrowOptions","Toggler","_enable","prefix","isClosed","getExpandedSections","expandedSections","storage","session","getObject","storeSectionToggleState","headline","setObject","saveExpandedSections","expandStoredSections","toggler","$sectionHeading","$headline","toggle","wasExpanded","is","toggleClass","indicator","$indicatorElement","$content","setAttribute","removeAttr","requestIdleCallback","expanded","reveal","$target","e","prev","offset","top","collapseSectionsByDefault","internalRedirect","internalRedirectHash","expandSections","checkHash","indexOf","decodedHash","i","$headingLabel","$indicator","target","tabindex","role","replaceWith","prependTo","which","enableKeyboardActions","classList","contains","$link","getWindow","_getExpandedSections","_expandStoredSections","delegateEventSplitter","idCounter","initialize","OO","mixinClass","EventEmitter","cid","compile","_postInitialize","render","undelegateEvents","skipTemplateRender","delegateEvents","delegate","eventName","listener","undelegate","$child","defaultParams","action","formatversion","otherParams","scriptPath","origin","AmcEnableForm","buttonLabel","campaign","toast","createPromoCampaign","amcOutreachDrawer","ACTIONS","onDesktopLink","onHistoryLink","onTalkLink","loadCampaign","returnToTitle","returnToQuery","user","tokens","promoCampaign","mwMessage","mwUtil","csrfToken","postUrl","returntoquery","fields","value","submit","makeActionIneligible","showOnPageReload","permissions","concat","Title","newFromText","getPrefixedText","edit","pageHTMLParser","feature","displayWikibaseDescriptions","search","watchlist","tagline","Array","makeHeader","headingOrView","headerCancel","templateData","hasActions","isHidden","insertAfter","map","component","savingHeader","saveHeader","saveButtonMessage","formHeader","formHTMLOrView","CANCEL_GLYPH","glyph","error","watchIcon","watchedIcon","placeholderClass","queryPlaceholders","root","getElementsByClassName","loadImages","placeholders","Promise","all","placeholder","loadImage","deferred","dataset","image","Image","class","alt","useMap","usemap","cssText","addEventListener","parentNode","replaceChild","src","srcset","Child","ParentOrPrototype","inheritClass","initClass","ModuleLoader","_register","registry","localRequire","define","obj","deprecate","deprecatedId","replacement","log","revision","thumb","pageprops","displaytitle","terms","entityterms","revisions","timestamp","pageid","missing","actions","campaignName","campaignActive","userEligible","mwStorage","ACTIONS_TO_STORAGE_KEYS","a","isCampaignActive","validateAction","isActionEligible","showIfEligible","set","makeAllActionsIneligible","storageKey","JSON","notify","remove","warn","stringify","promises","when","fn","documentElement","ctx","source","getSource","partials","partialSource","Mustache","WatchstarGateway","getStatuses","ids","getStatusesByID","getStatusesByTitle","inprop","pageids","rsp","_unmarshalGetResponse","postStatusesByTitle","watched","unwatch","postWithToken","statuses","watchstar","WatchstarPageList","wsGateway","$items","queryUnitializedItems","parsePagesFromItems","getPages","renderItems","isAnon","_","$item","sections","getTitle","_appendWatchstar","appendTo","iconProps","WATCH_CLASS","WATCHED_CLASS","activeIcon"],"mappings":"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","file":"mobile.common.js","sourcesContent":["// Be careful when changing these.\n// These are used to construct CSS classes\n// e.g.  mf-font-size-small and  mf-font-size-regular\nconst USER_FONT_SIZE_SMALL = 'small';\nconst USER_FONT_SIZE_REGULAR = 'regular';\nconst USER_FONT_SIZE_LARGE = 'large';\nconst USER_FONT_SIZE_XLARGE = 'x-large';\n\nconst USER_FONT_SIZES = [\n\tUSER_FONT_SIZE_SMALL,\n\tUSER_FONT_SIZE_REGULAR,\n\tUSER_FONT_SIZE_LARGE,\n\tUSER_FONT_SIZE_XLARGE\n];\n\nmodule.exports = {\n\tUSER_FONT_SIZES,\n\tUSER_FONT_SIZE_REGULAR\n};\n","const icons = require( '../mobile.startup/icons' );\n\n/* global $ */\nmodule.exports = function fakeToolbar() {\n\tvar $fakeToolbar, $goBack, $loadingMessage;\n\n\t$goBack = icons.cancel( null, {\n\t\ttagName: 'a'\n\t} ).$el.attr( 'tabindex', '0' )\n\t\t.attr( 'role', 'button' );\n\n\t$loadingMessage = icons.spinner( {\n\t\ttagName: 'span',\n\t\ttype: 'before',\n\t\tadditionalClassNames: '',\n\t\tlabel: mw.msg( 'mobile-frontend-editor-loading' )\n\t} ).$el;\n\n\t// Wrappers similar to .overlay-header-container, .overlay-header and .oo-ui-toolbar\n\t$fakeToolbar = $( '<div>' )\n\t\t.addClass( 've-mobile-fakeToolbar-container' )\n\t\t.append( $( '<div>' )\n\t\t\t.addClass( 've-mobile-fakeToolbar-header' )\n\t\t\t// Minerva has some complicated styling for this class, so we have to include it\n\t\t\t.addClass( 'overlay-header' )\n\t\t\t.append( $( '<div>' )\n\t\t\t\t.addClass( 've-mobile-fakeToolbar' )\n\t\t\t\t.append( $goBack, $loadingMessage )\n\t\t\t)\n\t\t);\n\n\treturn $fakeToolbar;\n};\n","var\n\tView = require( './View' ),\n\tutil = require( './util' );\n\n/**\n * A wrapper for creating an anchor.\n *\n * @extends View\n */\nclass Anchor extends View {\n\t/**\n\t * @inheritdoc\n\t */\n\tget isTemplateMode() {\n\t\treturn true;\n\t}\n\t/**\n\t * @memberof Anchor\n\t * @instance\n\t * @mixes View#defaults\n\t * @property {Object} defaults Default options hash.\n\t * @property {boolean} defaults.progressive is progressive action\n\t * @property {boolean} defaults.destructive is destructive action\n\t * @property {string} defaults.additionalClassNames Additional class name(s).\n\t * @property {string} defaults.href url\n\t * @property {string} defaults.label of anchor\n\t */\n\tget defaults() {\n\t\treturn {\n\t\t\tprogressive: undefined,\n\t\t\tdestructive: undefined,\n\t\t\tadditionalClassNames: '',\n\t\t\thref: undefined,\n\t\t\tlabel: undefined\n\t\t};\n\t}\n\t/**\n\t * @inheritdoc\n\t */\n\tget template() {\n\t\treturn util.template( `\n<a {{#href}}href=\"{{href}}\"{{/href}} class=\"mw-ui-anchor\n\t{{#progressive}} mw-ui-progressive{{/progressive}}\n\t{{#destructive}} mw-ui-destructive{{/destructive}}\n\t {{additionalClassNames}}\">{{label}}</a>\n\t` );\n\t}\n}\n\nmodule.exports = Anchor;\n","var\n\tutil = require( './util' ),\n\tbrowser;\n\n/**\n * Memoize a class method. Caches the result of the method based on the\n * arguments. Instances do not share a cache.\n *\n * @param {Function} method Method to be memoized\n * @return {Function}\n */\nfunction memoize( method ) {\n\t/**\n\t * Memoized version of the method\n\t *\n\t * @return {Function}\n\t */\n\tvar memoized = function () {\n\t\tvar cache = this[ '__cache' + memoized.cacheId ] ||\n\t\t\t( this[ '__cache' + memoized.cacheId ] = {} ),\n\t\t\tkey = [].join.call( arguments, '|' );\n\t\tif ( Object.prototype.hasOwnProperty.call( cache, key ) ) {\n\t\t\treturn cache[ key ];\n\t\t}\n\t\treturn ( cache[ key ] = method.apply( this, arguments ) );\n\t};\n\tmemoized.cacheId = Date.now().toString() + Math.random().toString();\n\treturn memoized;\n}\n\n/**\n * Representation of user's current browser\n *\n * @class Browser\n * @param {string} ua the user agent of the current browser\n * @param {jQuery.Object} $container an element to associate with the Browser object\n */\nfunction Browser( ua, $container ) {\n\tthis.userAgent = ua;\n\tthis.$el = $container;\n}\n\nBrowser.prototype = {\n\t/**\n\t * Returns whether the current browser is an ios device.\n\t * FIXME: jquery.client does not support iPad detection so we cannot use it.\n\t *\n\t * @memberof Browser\n\t * @instance\n\t * @param {number} [version] integer describing a specific version you want to test against.\n\t * @return {boolean}\n\t */\n\tisIos: memoize( function ( version ) {\n\t\tvar ua = this.userAgent,\n\t\t\tios = /ipad|iphone|ipod/i.test( ua );\n\n\t\tif ( ios && version ) {\n\t\t\tswitch ( version ) {\n\t\t\t\tcase 8:\n\t\t\t\t\t// Test UA for iOS8. Or for simulator look for Version 8\n\t\t\t\t\t// In the iOS simulator the OS is the host machine OS version\n\t\t\t\t\t// This makes testing in iOS8 simulator work as expected\n\t\t\t\t\treturn /OS 8_/.test( ua ) || /Version\\/8/.test( ua );\n\t\t\t\tcase 4:\n\t\t\t\t\treturn /OS 4_/.test( ua );\n\t\t\t\tcase 5:\n\t\t\t\t\treturn /OS 5_/.test( ua );\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t} else {\n\t\t\treturn ios;\n\t\t}\n\t} ),\n\t/**\n\t * Determine if a device has a widescreen.\n\t *\n\t * @memberof Browser\n\t * @instance\n\t * @return {boolean}\n\t */\n\tisWideScreen: memoize( function () {\n\t\tvar val = parseInt( mw.config.get( 'wgMFDeviceWidthTablet' ), 10 );\n\t\t// Check viewport width to determine mobile vs tablet.\n\t\t// Note: Mobile devices held in landscape mode might receive tablet treatment.\n\t\treturn window.innerWidth >= val;\n\t} ),\n\t/**\n\t * Whether touchstart and other touch events are supported by the current browser.\n\t *\n\t * @memberof Browser\n\t * @instance\n\t * @return {boolean}\n\t */\n\tsupportsTouchEvents: memoize( function () {\n\t\treturn 'ontouchstart' in window;\n\t} )\n};\n\n/**\n * @memberof Browser\n * @return {Browser}\n */\nBrowser.getSingleton = function () {\n\tvar $html;\n\tif ( !browser ) {\n\t\t$html = util.getDocument();\n\t\tbrowser = new Browser( window.navigator.userAgent, $html );\n\t}\n\treturn browser;\n};\n\nmodule.exports = Browser;\n","var\n\tmfExtend = require( './mfExtend' ),\n\tutil = require( './util' ),\n\tView = require( './View' );\n\n/**\n * A wrapper for creating a button.\n *\n * @class Button\n * @extends View\n *\n * @param {Object} options Configuration options\n */\nfunction Button( options ) {\n\tif ( options.href ) {\n\t\toptions.tagName = 'a';\n\t}\n\tView.call( this, options );\n}\n\nmfExtend( Button, View, {\n\t/**\n\t * @inheritdoc\n\t * @memberof Button\n\t * @instance\n\t */\n\tisTemplateMode: true,\n\t/**\n\t * @memberof Button\n\t * @instance\n\t * @mixes View#defaults\n\t * @property {Object} defaults Default options hash.\n\t * @property {string} defaults.tagName The name of the tag in which the button is wrapped.\n\t * @property {boolean} defaults.block is stacked button\n\t * @property {boolean} defaults.progressive is progressive action\n\t * @property {boolean} defaults.quiet is quiet button\n\t * @property {boolean} defaults.destructive is destructive action\n\t * @property {string} defaults.additionalClassNames Additional class name(s).\n\t * @property {string} defaults.href url\n\t * @property {string} defaults.label of button\n\t * @property {boolean} defaults.disabled should only be used with tagName button\n\t */\n\tdefaults: {\n\t\ttagName: 'a',\n\t\tdisabled: false,\n\t\tblock: undefined,\n\t\tprogressive: undefined,\n\t\tdestructive: undefined,\n\t\tquiet: undefined,\n\t\tadditionalClassNames: '',\n\t\thref: undefined,\n\t\tlabel: undefined\n\t},\n\t/**\n\t * @memberof Button\n\t * @instance\n\t */\n\ttemplate: util.template( `<{{tagName}} {{#disabled}}disabled{{/disabled}}\n{{#href}}href=\"{{href}}\"{{/href}}\nclass=\"mw-ui-button {{#progressive}}mw-ui-progressive{{/progressive}}\n\t{{#block}}mw-ui-block{{/block}}\n\t{{#quiet}}mw-ui-quiet{{/quiet}}\n\t{{#destructive}}mw-ui-destructive{{/destructive}} {{additionalClassNames}}\n\">{{label}}</{{tagName}}>` )\n} );\n\nmodule.exports = Button;\n","var\n\tDrawer = require( './Drawer' ),\n\tutil = require( './util' ),\n\tButton = require( './Button' ),\n\tAnchor = require( './Anchor' );\n\n/**\n * @typedef {string|number|boolean|undefined} QueryVal\n * @typedef {Object.<string, QueryVal|QueryVal[]>} QueryParams\n *\n * @typedef {Object} Options\n * @prop {string} [returnTo]\n * @prop {QueryParams} [queryParams]\n * @prop {QueryParams} [signupQueryParams]\n * @prop {Object} [progressiveButton] button options for Button element for signing in.\n *  If omitted will create a login URL.\n * @prop {Object} [actionAnchor] anchor options for Anchor element for signing up. If omitted\n *   will create a sign up URL\n * @prop {string} content text - what is the call to action?\n */\n\n/**\n * This creates the drawer at the bottom of the screen that appears when an anonymous\n * user tries to perform an action that requires being logged in. It presents the user\n * with options to log in or sign up for a new account.\n *\n * @uses Button\n * @uses Icon\n * @uses Anchor\n * @param {Options} options\n * @return {Drawer}\n */\nfunction CtaDrawer( options = {} ) {\n\tvar params = redirectParams( options.queryParams, options.returnTo );\n\treturn new Drawer(\n\t\tutil.extend( {\n\t\t\tchildren: [\n\t\t\t\tutil.parseHTML( '<p>' ).text( options.content ),\n\t\t\t\tnew Button( util.extend( {\n\t\t\t\t\tprogressive: true,\n\t\t\t\t\thref: mw.util.getUrl( 'Special:UserLogin', params ),\n\t\t\t\t\tlabel: mw.msg( 'mobile-frontend-watchlist-cta-button-login' )\n\t\t\t\t}, options.progressiveButton ) ).$el,\n\t\t\t\tutil.parseHTML( '<div>' ).addClass( 'cta-drawer__anchors' ).append(\n\t\t\t\t\t// Update Minerva first to avoid needing to keep this closeAnchor\n\t\t\t\t\tnew Anchor( util.extend( {\n\t\t\t\t\t\thref: mw.util.getUrl(\n\t\t\t\t\t\t\t'Special:UserLogin', signUpParams( params, options.signupQueryParams )\n\t\t\t\t\t\t),\n\t\t\t\t\t\tprogressive: true,\n\t\t\t\t\t\tlabel: mw.msg( 'mobile-frontend-watchlist-cta-button-signup' )\n\t\t\t\t\t}, options.actionAnchor ) ).$el\n\t\t\t\t)\n\t\t\t]\n\t\t}, options )\n\t);\n}\n\n/**\n * Special:UserLogin post-request redirect query parameters.\n *\n * @param {QueryParams} params\n * @param {string} [redirectURL]\n * @return {QueryParams}\n */\nfunction redirectParams( params, redirectURL ) {\n\treturn util.extend( {\n\t\t// use wgPageName as this includes the namespace if outside Main\n\t\treturnto: redirectURL || mw.config.get( 'wgPageName' )\n\t}, params );\n}\n\n/**\n * Special:UserLogin account creation query parameters.\n *\n * @param {...QueryParams} params\n * @return {QueryParams}\n */\nfunction signUpParams() {\n\t[].push.call( arguments, { type: 'signup' } );\n\treturn util.extend.apply( util, arguments );\n}\n\nCtaDrawer.prototype.test = {\n\tredirectParams: redirectParams,\n\tsignUpParams: signUpParams\n};\n\nmodule.exports = CtaDrawer;\n","var\n\tmfExtend = require( './mfExtend' ),\n\tView = require( './View' ),\n\tutil = require( './util' ),\n\tIcon = require( './Icon' );\n\n/**\n * A {@link View} that pops up from the bottom of the screen.\n *\n * @class Drawer\n * @extends View\n * @final\n * @param {Object} props\n * @param {string} [props.className] Additional CSS classes to add\n * @param {jQuery.Element[]} [props.children] An array of elements to append to\n * @param {Function} [props.onShow] Callback called before showing the drawer.\n *  It receives a promise given the show process is asynchronous.\n * @param {Function} [props.onBeforeHide] Callback called before hiding the drawer\n */\nfunction Drawer( props ) {\n\tthis.drawerClassName = props.className || '';\n\tthis.collapseIcon = new Icon( {\n\t\tname: 'expand',\n\t\tadditionalClassNames: 'cancel'\n\t} );\n\tView.call( this,\n\t\tutil.extend(\n\t\t\t{\n\t\t\t\tonBeforeHide: () => {},\n\t\t\t\tshowCollapseIcon: true\n\t\t\t},\n\t\t\tprops,\n\t\t\t{\n\t\t\t\tclassName: 'drawer-container'\n\t\t\t},\n\t\t\t{ events: util.extend( {\n\t\t\t\t'click .drawer-container__mask': function () {\n\t\t\t\t\tthis.hide();\n\t\t\t\t}.bind( this ),\n\t\t\t\t'click .cancel': function ( ev ) {\n\t\t\t\t\tev.preventDefault();\n\t\t\t\t\tthis.hide();\n\t\t\t\t}.bind( this ),\n\t\t\t\tclick: function ( ev ) {\n\t\t\t\t\tev.stopPropagation();\n\t\t\t\t}\n\t\t\t}, props.events ) }\n\t\t)\n\t);\n}\n\nmfExtend( Drawer, View, {\n\t// in milliseconds\n\tminHideDelay: 100,\n\n\t/**\n\t * Shows panel after a slight delay\n\t *\n\t * @memberof View\n\t * @instance\n\t * @method\n\t * @return {jQuery.Promise}\n\t */\n\tshow: function () {\n\t\tconst d = util.Deferred();\n\t\tthis.$el.find( '.drawer-container__mask' )\n\t\t\t.addClass( 'drawer-container__mask--visible' );\n\t\tif ( !this.$el.find( '.drawer' ).hasClass( 'visible' ) ) {\n\t\t\t// use setTimeout to allow the browser to redraw if render() was called\n\t\t\t// just before show(); this is important for animations to work\n\t\t\t// (0ms doesn't work on Firefox, 10ms is enough)\n\t\t\t//\n\t\t\t// FIXME: setTimeout should be reconsidered in T209129\n\t\t\tsetTimeout( function () {\n\t\t\t\tthis.$el.find( '.drawer' ).addClass( 'visible' );\n\t\t\t\tif ( this.options.onShow ) {\n\t\t\t\t\tthis.options.onShow( d );\n\t\t\t\t}\n\t\t\t\tsetTimeout( function () {\n\t\t\t\t\td.resolve();\n\t\t\t\t}, this.minHideDelay );\n\t\t\t}.bind( this ), this.minHideDelay );\n\t\t} else {\n\t\t\td.resolve();\n\t\t}\n\t\treturn d.promise();\n\t},\n\n\t/**\n\t * Hides panel\n\t *\n\t * @memberof View\n\t * @instance\n\t */\n\thide: function () {\n\t\tthis.$el.find( '.drawer-container__mask' )\n\t\t\t.removeClass( 'drawer-container__mask--visible' );\n\t\tthis.$el.find( '.drawer' ).removeClass( 'visible' );\n\t\t// see comment in show()\n\t\tsetTimeout( function () {\n\t\t\tthis.$el.find( '.drawer' ).removeClass( 'visible' );\n\t\t\tthis.options.onBeforeHide( this );\n\t\t}.bind( this ), this.minHideDelay );\n\t},\n\n\t/**\n\t * @inheritdoc\n\t * @memberof Drawer\n\t * @instance\n\t */\n\tpostRender: function () {\n\t\tconst props = this.options,\n\t\t\t$mask = util.parseHTML( '<div>' )\n\t\t\t\t.addClass( 'drawer-container__mask' ),\n\t\t\t// eslint-disable-next-line mediawiki/class-doc\n\t\t\t$drawer = util.parseHTML( '<div>' )\n\t\t\t\t.addClass( `drawer drawer-container__drawer position-fixed ${this.drawerClassName}`.trim() );\n\n\t\tif ( props.showCollapseIcon ) {\n\t\t\t// append the collapse icon at the top of the drawer\n\t\t\t$drawer.prepend( this.collapseIcon.$el );\n\t\t}\n\n\t\tif ( props.children ) {\n\t\t\t// append children\n\t\t\t$drawer.append( props.children );\n\t\t}\n\t\tthis.$el.append( $mask );\n\t\tthis.$el.append( $drawer );\n\t}\n} );\n\nmodule.exports = Drawer;\n","var\n\tmfExtend = require( './mfExtend' ),\n\tutil = require( './util' ),\n\tView = require( './View' );\n\n/**\n * A wrapper for creating an icon.\n *\n * @class Icon\n * @extends View\n *\n * @param {Object} options Configuration options\n */\nfunction Icon( options ) {\n\tif ( options.href ) {\n\t\toptions.tagName = 'a';\n\t}\n\tif ( options.tagName === 'button' ) {\n\t\toptions.isTypeButton = true;\n\t}\n\tView.call( this, options );\n}\n\nmfExtend( Icon, View, {\n\t/**\n\t * @inheritdoc\n\t * @memberof Icon\n\t * @instance\n\t */\n\tpreRender: function () {\n\t\tthis.options._rotationClass = this.getRotationClass();\n\t\tthis.options._iconClasses = this.getIconClasses();\n\t},\n\t/**\n\t * Internal method that sets the correct rotation class for the icon\n\t * based on the value of rotation\n\t *\n\t * @memberof Icon\n\t * @instance\n\t * @private\n\t */\n\tgetRotationClass: function () {\n\t\tvar rotationClass = '';\n\t\tif ( this.options.rotation ) {\n\t\t\tswitch ( this.options.rotation ) {\n\t\t\t\tcase -180:\n\t\t\t\tcase 180:\n\t\t\t\t\trotationClass = 'mf-mw-ui-icon-rotate-flip';\n\t\t\t\t\tbreak;\n\t\t\t\tcase -90:\n\t\t\t\t\trotationClass = 'mf-mw-ui-icon-rotate-anti-clockwise';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 90:\n\t\t\t\t\trotationClass = 'mf-mw-ui-icon-rotate-clockwise';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 0:\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new Error( 'Bad value for rotation given. Must be ±90, 0 or ±180.' );\n\t\t\t}\n\t\t}\n\t\treturn rotationClass;\n\t},\n\t/**\n\t * Set icon glyph class and icon type class\n\t *\n\t * @memberof Icon\n\t * @instance\n\t * @private\n\t */\n\tgetIconClasses: function () {\n\t\tvar base = this.options.base;\n\t\tvar name = this.options.name;\n\t\tvar type = this.options.type;\n\t\tvar additionalClassNames = this.options.additionalClassNames;\n\n\t\tvar modifiers = '';\n\t\tif ( type ) {\n\t\t\tmodifiers += base + '-' + type + ' ';\n\t\t}\n\t\tif ( name ) {\n\t\t\tmodifiers += this.getGlyphClassName();\n\t\t}\n\t\tif ( type === 'element' ) {\n\t\t\tadditionalClassNames += ' mw-ui-button mw-ui-quiet';\n\t\t}\n\n\t\treturn base + ' ' + modifiers + ' ' + additionalClassNames;\n\t},\n\t/**\n\t * @inheritdoc\n\t * @memberof Icon\n\t * @instance\n\t */\n\tisTemplateMode: true,\n\t/**\n\t * @memberof Icon\n\t * @instance\n\t * @mixes View#defaults\n\t * @property {Object} defaults Default options hash.\n\t * @property {boolean} defaults.isSmall Whether the icon should be small.\n\t * @property {string} [defaults.href] value of href attribute,\n\t *  when set tagName will default to anchor tag\n\t * @property {string} defaults.tagName The name of the tag in which the icon is wrapped.\n\t *  Defaults to 'a' when href option present.\n\t * @property {string} defaults.base String used as a base for generating class names.\n\t * Defaults to 'mw-ui-icon'.\n\t * @property {string} defaults.name Name of the icon.\n\t * @property {string} defaults.type Icon type\n\t * Defaults to 'element'.\n\t * @property {string} defaults.title Tooltip text.\n\t * @property {string} defaults.additionalClassNames Additional classes to be added to the icon.\n\t * @property {boolean} defaults.rotation will rotate the icon by a certain number\n\t *  of degrees.\n\t *  Must be ±90, 0 or ±180 or will throw exception.\n\t * @property {boolean} defaults.disabled should only be used with tagName button\n\t */\n\tdefaults: {\n\t\trotation: 0,\n\t\thref: undefined,\n\t\tglyphPrefix: 'mf',\n\t\ttagName: 'div',\n\t\tdisabled: false,\n\t\tisSmall: false,\n\t\tbase: 'mw-ui-icon',\n\t\tname: '',\n\t\ttype: 'element',\n\t\ttitle: '',\n\t\tadditionalClassNames: ''\n\t},\n\t/**\n\t * Return the full class name that is required for the icon to render\n\t *\n\t * @memberof Icon\n\t * @instance\n\t * @return {string}\n\t */\n\tgetClassName: function () {\n\t\treturn this.$el.attr( 'class' );\n\t},\n\t/**\n\t * Return the class that relates to the icon glyph\n\t *\n\t * @memberof Icon\n\t * @instance\n\t * @return {string}\n\t */\n\tgetGlyphClassName: function () {\n\t\tif ( this.options.glyphPrefix ) {\n\t\t\treturn this.options.base + '-' + this.options.glyphPrefix + '-' + this.options.name;\n\t\t}\n\t\treturn this.options.base + '-' + this.options.name;\n\t},\n\ttemplate: util.template(\n\t\t'<{{tagName}} ' +\n\t\t\t'{{#isTypeButton}}type=\"button\" {{#disabled}}disabled{{/disabled}}{{/isTypeButton}} ' +\n\t\t\t'class=\"{{_iconClasses}} ' +\n\t\t\t\t'{{#isSmall}}mw-ui-icon-small{{/isSmall}} ' +\n\t\t\t\t'{{#_rotationClass}}{{_rotationClass}}{{/_rotationClass}}\" ' +\n\t\t\t'{{#id}}id=\"{{id}}\"{{/id}} ' +\n\t\t\t'{{#href}}href=\"{{href}}\"{{/href}} ' +\n\t\t\t'{{#title}}title=\"{{title}}\"{{/title}}>' +\n\t\t\t\t'{{label}}' +\n\t\t'</{{tagName}}>'\n\t)\n} );\n\nmodule.exports = Icon;\n","var\n\tView = require( './View' ),\n\theader = require( './headers' ).header,\n\tAnchor = require( './Anchor' ),\n\tutil = require( './util' ),\n\tbrowser = require( './Browser' ).getSingleton(),\n\tmfExtend = require( './mfExtend' );\n\n/**\n * Mobile modal window\n *\n * @class Overlay\n * @extends View\n * @uses Icon\n * @uses Button\n * @fires Overlay#hide\n * @param {Object} props\n * @param {Object} props.events - custom events to be bound to the overlay.\n * @param {boolean} [props.headerChrome] Whether the header has chrome.\n * @param {View[]} [props.headerActions] children (usually buttons or icons)\n *   that should be placed in the header actions. Ignored when `headers` used.\n * @param {string} [props.heading] heading for the overlay header. Use `headers` where\n *  overlays require more than one header. Ignored when `headers` used.\n * @param {boolean} props.noHeader renders an overlay without a header\n * @param {Element[]} [props.headers] allows overlays to have more than one\n *  header. When used it is an array of jQuery Objects representing\n *  headers created via the header util function. It is expected that only one of these\n *  should be visible. If undefined, headerActions and heading is used.\n * @param {Object} [props.footerAnchor] options for an optional Anchor\n *  that can appear in the footer\n * @param {Function} props.onBeforeExit allows a consumer to prevent exits in certain\n *  situations. This callback gets the following parameters:\n *  - 1) the exit function which should be run after the consumer has made their changes.\n *  - 2) the cancel function which should be run if the consumer explicitly changes their mind\n */\nfunction Overlay( props ) {\n\tthis.isIos = browser.isIos();\n\n\tView.call(\n\t\tthis,\n\t\tutil.extend(\n\t\t\ttrue,\n\t\t\t{\n\t\t\t\theaderChrome: false,\n\t\t\t\tclassName: 'overlay'\n\t\t\t},\n\t\t\tprops,\n\t\t\t{\n\t\t\t\tevents: util.extend(\n\t\t\t\t\t{\n\t\t\t\t\t\t// FIXME: Remove .initial-header selector\n\t\t\t\t\t\t'click .cancel, .confirm, .initial-header .back': 'onExitClick',\n\t\t\t\t\t\tclick: ( ev ) => {\n\t\t\t\t\t\t\tev.stopPropagation();\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tprops.events\n\t\t\t\t)\n\t\t\t}\n\t\t)\n\t);\n}\n\nmfExtend( Overlay, View, {\n\ttemplate: util.template( `\n{{^noHeader}}\n<div class=\"overlay-header-container header-container{{#headerChrome}}\n\theader-chrome{{/headerChrome}} position-fixed\">\n</div>\n{{/noHeader}}\n<div class=\"overlay-content\">\n\t{{>content}}\n</div>\n<div class=\"overlay-footer-container position-fixed\"></div>\n\t` ),\n\n\thideTimeout: null,\n\n\t/**\n\t * Shows the spinner right to the input field.\n\t *\n\t * @memberof Overlay\n\t * @instance\n\t * @method\n\t */\n\tshowSpinner: function () {\n\t\tthis.$el.find( '.spinner' ).removeClass( 'hidden' );\n\t},\n\n\t/**\n\t * Hide the spinner near to the input field.\n\t *\n\t * @memberof Overlay\n\t * @instance\n\t * @method\n\t */\n\thideSpinner: function () {\n\t\tthis.$el.find( '.spinner' ).addClass( 'hidden' );\n\t},\n\n\t/**\n\t * @inheritdoc\n\t * @memberof Overlay\n\t * @instance\n\t */\n\tpostRender: function () {\n\t\tconst footerAnchor = this.options.footerAnchor;\n\t\tthis.$overlayContent = this.$el.find( '.overlay-content' );\n\t\tif ( this.isIos ) {\n\t\t\tthis.$el.addClass( 'overlay-ios' );\n\t\t}\n\t\tif ( footerAnchor ) {\n\t\t\tthis.$el.find( '.overlay-footer-container' ).append( new Anchor( footerAnchor ).$el );\n\t\t}\n\t\tconst headers = this.options.headers || [\n\t\t\theader(\n\t\t\t\tthis.options.heading,\n\t\t\t\tthis.options.headerActions\n\t\t\t)\n\t\t];\n\t\tthis.$el.find( '.overlay-header-container' ).append( headers );\n\t},\n\n\t/**\n\t * ClickBack event handler\n\t *\n\t * @memberof Overlay\n\t * @instance\n\t * @param {Object} ev event object\n\t */\n\tonExitClick: function ( ev ) {\n\t\tconst exit = function () {\n\t\t\tthis.hide();\n\t\t}.bind( this );\n\t\tev.preventDefault();\n\t\tev.stopPropagation();\n\t\tif ( this.options.onBeforeExit ) {\n\t\t\tthis.options.onBeforeExit( exit, function () {} );\n\t\t} else {\n\t\t\texit();\n\t\t}\n\n\t},\n\t/**\n\t * Attach overlay to current view and show it.\n\t *\n\t * @memberof Overlay\n\t * @instance\n\t */\n\tshow: function () {\n\t\tvar $html = util.getDocument();\n\n\t\tthis.scrollTop = window.pageYOffset;\n\n\t\t$html.addClass( 'overlay-enabled' );\n\t\t// skip the URL bar if possible\n\t\twindow.scrollTo( 0, 1 );\n\n\t\tthis.$el.addClass( 'visible' );\n\n\t\t// If .hide() was called earlier, and it scheduled an asynchronous detach\n\t\t// but it hasn't happened yet, cancel it\n\t\tif ( this.hideTimeout !== null ) {\n\t\t\tclearTimeout( this.hideTimeout );\n\t\t\tthis.hideTimeout = null;\n\t\t}\n\t},\n\t/**\n\t * Detach the overlay from the current view\n\t * Should not be overriden as soon to be deprecated.\n\t *\n\t * @memberof Overlay\n\t * @instance\n\t * @final\n\t * @return {boolean} Whether the overlay was successfully hidden or not\n\t */\n\thide: function () {\n\t\tutil.getDocument().removeClass( 'overlay-enabled' );\n\t\t// return to last known scroll position\n\t\twindow.scrollTo( window.pageXOffset, this.scrollTop );\n\n\t\t// Since the hash change event caused by emitting hide will be detected later\n\t\t// and to avoid the article being shown during a transition from one overlay to\n\t\t// another, we regretfully detach the element asynchronously.\n\t\tthis.hideTimeout = setTimeout( function () {\n\t\t\tthis.$el.detach();\n\t\t\tthis.hideTimeout = null;\n\t\t}.bind( this ), 0 );\n\n\t\t/**\n\t\t * Fired when the overlay is closed.\n\t\t *\n\t\t * @event Overlay#hide\n\t\t */\n\t\tthis.emit( 'hide' );\n\n\t\treturn true;\n\t},\n\n\t/**\n\t * Show elements that are selected by the className.\n\t * Also hide .hideable elements\n\t * Can't use jQuery's hide() and show() because show() sets display: block.\n\t * And we want display: table for headers.\n\t *\n\t * @memberof Overlay\n\t * @instance\n\t * @protected\n\t * @param {string} className CSS selector to show\n\t */\n\tshowHidden: function ( className ) {\n\t\tthis.$el.find( '.hideable' ).addClass( 'hidden' );\n\t\tthis.$el.find( className ).removeClass( 'hidden' );\n\t}\n} );\n\n/**\n * Factory method for an overlay with a single child\n *\n * @memberof Overlay\n * @instance\n * @protected\n * @param {Object} options\n * @param {View} view\n * @return {Overlay}\n */\nOverlay.make = function ( options, view ) {\n\tvar overlay = new Overlay( options );\n\toverlay.$el.find( '.overlay-content' ).append( view.$el );\n\treturn overlay;\n};\n\nmodule.exports = Overlay;\n","var\n\tutil = require( './util' ),\n\toverlayManager = null;\n\n// We pass this to history.pushState/replaceState to indicate that we're controlling the page URL.\n// Then we look for this marker on page load so that if the page is refreshed, we don't generate an\n// extra history entry (see #getSingleton below and T201852).\nconst MANAGED_STATE = 'MobileFrontend OverlayManager was here!';\n\n/**\n * Manages opening and closing overlays when the URL hash changes to one\n * of the registered values (see OverlayManager.add()).\n *\n * This allows overlays to function like real pages, with similar browser back/forward\n * and refresh behavior.\n *\n * @class OverlayManager\n * @param {Router} router\n * @param {Element} container where overlays should be managed\n */\nfunction OverlayManager( router, container ) {\n\trouter.on( 'route', this._checkRoute.bind( this ) );\n\tthis.router = router;\n\t// use an object instead of an array for entries so that we don't\n\t// duplicate entries that already exist\n\tthis.entries = {};\n\t// stack of all the open overlays, stack[0] is the latest one\n\tthis.stack = [];\n\tthis.hideCurrent = true;\n\t// Set the element that overlays will be appended to\n\tthis.container = container;\n}\n\n/**\n * Attach an event to the overlays hide event\n *\n * @param {Overlay} overlay\n */\nfunction attachHideEvent( overlay ) {\n\toverlay.on( 'hide', function () {\n\t\toverlay.emit( '_om_hide' );\n\t} );\n}\n\nOverlayManager.prototype = {\n\t/**\n\t * Don't try to hide the active overlay on a route change event triggered\n\t * by hiding another overlay.\n\t * Called when something other than OverlayManager calls Overlay.hide\n\t * on an overlay that it itself managed by the OverlayManager.\n\t * MUST be called when the stack is not empty.\n\t *\n\t * @memberof OverlayManager\n\t * @instance\n\t * @private\n\t */\n\t_onHideOverlayOutsideOverlayManager: function () {\n\t\tif ( !this.stack.length ) {\n\t\t\treturn;\n\t\t}\n\t\tconst currentRoute = this.stack[0].route,\n\t\t\trouteIsString = typeof currentRoute === 'string',\n\t\t\tcurrentPath = this.router.getPath(),\n\t\t\t// Since routes can be strings or regexes, it's important to do an equality\n\t\t\t// check BEFORE a match check.\n\t\t\trouteIsSame = ( routeIsString && currentPath === currentRoute ) ||\n\t\t\t\tcurrentPath.match( currentRoute );\n\n\t\tthis.hideCurrent = false;\n\n\t\t// If the path hasn't changed then the user didn't close the overlay by\n\t\t// calling history.back() or triggering a route change. We must go back\n\t\t// to get out of the overlay. See T237677.\n\t\tif ( routeIsSame ) {\n\t\t\t// does the route need to change?\n\t\t\tthis.router.back();\n\t\t}\n\t},\n\n\t/**\n\t * Attach overlay to DOM\n\t *\n\t * @memberof OverlayManager\n\t * @instance\n\t * @private\n\t * @param {Overlay} overlay to attach\n\t */\n\t_attachOverlay: function ( overlay ) {\n\t\tif ( !overlay.$el.parents().length ) {\n\t\t\tthis.container.appendChild( overlay.$el[0] );\n\t\t}\n\t},\n\t/**\n\t * Show the overlay and bind the '_om_hide' event to _onHideOverlay.\n\t *\n\t * @memberof OverlayManager\n\t * @instance\n\t * @private\n\t * @param {Overlay} overlay to show\n\t */\n\t_show: function ( overlay ) {\n\t\t// Mark the state so that if the page is refreshed, we don't generate an extra history entry\n\t\t// (see #getSingleton below and T201852).\n\t\t// eslint-disable-next-line no-restricted-properties\n\t\twindow.history.replaceState( MANAGED_STATE, null, window.location.href );\n\n\t\t// the _om_hide event is added to an overlay that is displayed.\n\t\t// It will fire if an Overlay emits a hide event (See attachHideEvent)\n\t\t// in the case where a route change has not occurred (this event is disabled\n\t\t// inside _hideOverlay which is called inside _checkRoute)\n\t\toverlay.once( '_om_hide', this._onHideOverlayOutsideOverlayManager.bind( this ) );\n\n\t\tthis._attachOverlay( overlay );\n\t\toverlay.show();\n\t},\n\n\t/**\n\t * Hide overlay\n\t *\n\t * @memberof OverlayManager\n\t * @instance\n\t * @private\n\t * @param {Overlay} overlay to hide\n\t * @param {Function} onBeforeExitCancel to pass to onBeforeExit\n\t * @return {boolean} Whether the overlay has been hidden\n\t */\n\t_hideOverlay: function ( overlay, onBeforeExitCancel ) {\n\t\tlet result;\n\n\t\tfunction exit() {\n\t\t\tresult = true;\n\t\t\toverlay.hide();\n\t\t}\n\t\t// remove the callback for updating state when overlay closed using\n\t\t// overlay close button\n\t\toverlay.off( '_om_hide' );\n\n\t\tif ( overlay.options && overlay.options.onBeforeExit ) {\n\t\t\toverlay.options.onBeforeExit( exit, onBeforeExitCancel );\n\t\t} else {\n\t\t\texit();\n\t\t}\n\n\t\t// if closing prevented, reattach the callback\n\t\tif ( !result ) {\n\t\t\toverlay.once( '_om_hide', this._onHideOverlayOutsideOverlayManager.bind( this ) );\n\t\t}\n\n\t\treturn result;\n\t},\n\n\t/**\n\t * Show match's overlay if match is not null.\n\t *\n\t * @memberof OverlayManager\n\t * @instance\n\t * @private\n\t * @param {Object|null} match Object with factory function's result. null if no match.\n\t */\n\t_processMatch: function ( match ) {\n\t\tvar factoryResult,\n\t\t\tself = this;\n\n\t\tif ( match ) {\n\t\t\tif ( match.overlay ) {\n\t\t\t\t// if the match is an overlay that was previously opened, reuse it\n\t\t\t\tself._show( match.overlay );\n\t\t\t} else {\n\t\t\t\t// else create an overlay using the factory function result\n\t\t\t\tfactoryResult = match.factoryResult;\n\t\t\t\t// We were getting errors relating to no factoryResult.\n\t\t\t\t// This should never happen.\n\t\t\t\t// If it does an error should not be thrown.\n\t\t\t\tif ( factoryResult ) {\n\t\t\t\t\tmatch.overlay = factoryResult;\n\t\t\t\t\tattachHideEvent( match.overlay );\n\t\t\t\t\tself._show( factoryResult );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\t/**\n\t * A callback for Router's `route` event.\n\t *\n\t * @memberof OverlayManager\n\t * @instance\n\t * @private\n\t * @param {jQuery.Event} ev Event object.\n\t */\n\t_checkRoute: function ( ev ) {\n\t\tconst current = this.stack[0];\n\n\t\t// When entering an overlay for the first time,\n\t\t// the manager should remember the user's scroll position\n\t\t// overlays always open at top of page\n\t\t// and we'll want to restore it later.\n\t\t// This should happen before the call to _matchRoute which will \"show\" the overlay.\n\t\t// The Overlay has similar logic for overlays that are not managed via the overlay.\n\t\tif ( !current ) {\n\t\t\tthis.scrollTop = window.pageYOffset;\n\t\t}\n\n\t\t// if there is an overlay in the stack and it's opened, try to close it\n\t\tif (\n\t\t\tcurrent &&\n\t\t\tcurrent.overlay !== undefined &&\n\t\t\tthis.hideCurrent &&\n\t\t\t!this._hideOverlay( current.overlay, () => {\n\t\t\t\t// if hide prevented, prevent route change event\n\t\t\t\tev.preventDefault();\n\t\t\t} )\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst match = Object.keys( this.entries ).reduce( function ( m, id ) {\n\t\t\treturn m || this._matchRoute( ev.path, this.entries[ id ] );\n\t\t}.bind( this ), null );\n\n\t\tif ( !match ) {\n\t\t\t// if hidden and no new matches, reset the stack\n\t\t\tthis.stack = [];\n\t\t\t// restore the scroll position.\n\t\t\twindow.scrollTo( window.pageXOffset, this.scrollTop );\n\t\t}\n\n\t\tthis.hideCurrent = true;\n\t\tthis._processMatch( match );\n\t},\n\n\t/**\n\t * Check if a given path matches one of the existing entries and\n\t * remove it from the stack.\n\t *\n\t * @memberof OverlayManager\n\t * @instance\n\t * @private\n\t * @param {string} path Path (hash) to check.\n\t * @param {Object} entry Entry object created in OverlayManager#add.\n\t * @return {Object|null} Match object with factory function's result.\n\t *  Returns null if no match.\n\t */\n\t_matchRoute: function ( path, entry ) {\n\t\tvar\n\t\t\tnext,\n\t\t\tdidMatch,\n\t\t\tcaptures,\n\t\t\tmatch,\n\t\t\tprevious = this.stack[1],\n\t\t\tself = this;\n\n\t\tif ( typeof entry.route === 'string' ) {\n\t\t\tdidMatch = entry.route === path;\n\t\t\tcaptures = [];\n\t\t} else {\n\t\t\tmatch = path.match( entry.route );\n\t\t\tdidMatch = !!match;\n\t\t\tcaptures = didMatch ? match.slice( 1 ) : [];\n\t\t}\n\n\t\t/**\n\t\t * Returns object to add to stack\n\t\t *\n\t\t * @method\n\t\t * @ignore\n\t\t * @return {Object}\n\t\t */\n\t\tfunction getNext() {\n\t\t\treturn {\n\t\t\t\tpath: path,\n\t\t\t\t// Important for managing states of things such as the image overlay which change\n\t\t\t\t// overlay routing parameters during usage.\n\t\t\t\troute: entry.route,\n\t\t\t\tfactoryResult: entry.factory.apply( self, captures )\n\t\t\t};\n\t\t}\n\n\t\tif ( didMatch ) {\n\t\t\t// if previous stacked overlay's path matches, assume we're going back\n\t\t\t// and reuse a previously opened overlay\n\t\t\tif ( previous && previous.path === path ) {\n\t\t\t\tself.stack.shift();\n\t\t\t\treturn previous;\n\t\t\t} else {\n\t\t\t\tnext = getNext();\n\t\t\t\tif ( this.stack[0] && next.path === this.stack[0].path ) {\n\t\t\t\t\t// current overlay path is same as path to check which means overlay\n\t\t\t\t\t// is attempting to refresh so just replace current overlay with new\n\t\t\t\t\t// overlay\n\t\t\t\t\tself.stack[0] = next;\n\t\t\t\t} else {\n\t\t\t\t\tself.stack.unshift( next );\n\t\t\t\t}\n\t\t\t\treturn next;\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t},\n\n\t/**\n\t * Add an overlay that should be shown for a specific fragment identifier.\n\t *\n\t * The following code will display an overlay whenever a user visits a URL that\n\t * ends with '#/hi/name'. The value of `name` will be passed to the overlay.\n\t * Note the factory must return an Overlay.\n\t * If the overlay needs to load code asynchronously that should be done inside\n\t * the overlay.\n\t *\n\t *     @example\n\t *     overlayManager.add( /\\/hi\\/(.*)/, function ( name ) {\n\t *         var HiOverlay = M.require( 'HiOverlay' );\n\t *         return new HiOverlay( { name: name } ) );\n\t *     } );\n\t *\n\t * @memberof OverlayManager\n\t * @instance\n\t * @param {RegExp|string} route definition that can be a regular\n\t * expression (optionally with parameters) or a string literal.\n\t *\n\t * T238364: Routes should only contain characters allowed by RFC3986 to ensure\n\t * compatibility across browsers. Encode the route with `encodeURIComponent()`\n\t * prior to registering it with OverlayManager if necessary (should probably\n\t * be done with all routes containing user generated characters) to avoid\n\t * inconsistencies with how different browsers encode illegal URI characters:\n\t *\n\t * ```\n\t *   var encodedRoute = encodeURIComponent('ugc < \" ` >');\n\t *\n\t *   overlayManager.add(\n\t *     encodedRoute,\n\t *     function () { return new Overlay(); }\n\t *   );\n\t *\n\t *   window.location.hash = '#' + encodedRoute;\n\t * ```\n\t * The above example shows how to register a string literal route with illegal\n\t * URI characters. Routes registered as a regex will likely NOT have to\n\t * perform any encoding (unless they explicitly contain illegal URI\n\t * characters) as their user generated content portion will likely just be a\n\t * capturing group (e.g. `/\\/hi\\/(.*)/`).\n\t * @param {Function} factory a function returning an overlay\n\t */\n\tadd: function ( route, factory ) {\n\t\tvar self = this,\n\t\t\tentry = {\n\t\t\t\troute: route,\n\t\t\t\tfactory: factory\n\t\t\t};\n\n\t\tthis.entries[route] = entry;\n\t\t// Check if overlay should be shown for the current path.\n\t\t// The DOM must fully load before we can show the overlay because Overlay relies on it.\n\t\tutil.docReady( function () {\n\t\t\tself._processMatch( self._matchRoute( self.router.getPath(), entry ) );\n\t\t} );\n\t},\n\n\t/**\n\t * Replace the currently displayed overlay with a new overlay without changing the\n\t * URL. This is useful for when you want to switch overlays, but don't want to\n\t * change the back button or close box behavior.\n\t *\n\t * @memberof OverlayManager\n\t * @instance\n\t * @param {Object} overlay The overlay to display\n\t */\n\treplaceCurrent: function ( overlay ) {\n\t\tif ( this.stack.length === 0 ) {\n\t\t\tthrow new Error( 'Trying to replace OverlayManager\\'s current overlay, but stack is empty' );\n\t\t}\n\t\tconst stackOverlay = this.stack[0].overlay;\n\t\tif ( stackOverlay ) {\n\t\t\tthis._hideOverlay( stackOverlay );\n\t\t}\n\t\tthis.stack[0].overlay = overlay;\n\t\tattachHideEvent( overlay );\n\t\tthis._show( overlay );\n\t}\n};\n\n/**\n * Retrieve a singleton instance using 'mediawiki.router'.\n *\n * @memberof OverlayManager\n * @return {OverlayManager}\n */\nOverlayManager.getSingleton = function () {\n\tif ( !overlayManager ) {\n\t\tconst\n\t\t\trouter = mw.loader.require( 'mediawiki.router' ),\n\t\t\tcontainer = document.createElement( 'div' ),\n\t\t\t// Note getPath returns hash minus the '#' character:\n\t\t\thash = router.getPath(),\n\t\t\t// eslint-disable-next-line no-restricted-properties\n\t\t\tstate = window.history.state;\n\t\tcontainer.className = 'mw-overlays-container';\n\t\tdocument.body.appendChild( container );\n\t\t// If an overlay was loaded by directly navigating to an URL with a hash (e.g. linked from\n\t\t// another page or browser bookmark), generate an extra history entry to allow closing the\n\t\t// overlay without leaving the page (see T201852). Put our marker into the entry state so\n\t\t// that we can detect it if the page is refreshed and do not generate another entry.\n\t\tif ( hash && state !== MANAGED_STATE ) {\n\t\t\t// eslint-disable-next-line no-restricted-properties\n\t\t\twindow.history.replaceState( null, null, '#' );\n\t\t\t// eslint-disable-next-line no-restricted-properties\n\t\t\twindow.history.pushState( MANAGED_STATE, null, `#${hash}` );\n\t\t}\n\t\toverlayManager = new OverlayManager( router, container );\n\t}\n\treturn overlayManager;\n};\n\nOverlayManager.test = {\n\tMANAGED_STATE,\n\t__clearCache: () => {\n\t\toverlayManager = null;\n\t}\n};\nmodule.exports = OverlayManager;\n","var\n\tHTML = mw.html,\n\tutil = require( './util' );\n\n/**\n * Mobile page view object\n */\nclass Page {\n\t/**\n\t * @param {Object} options Configuration options\n\t * @param {number} options.id Page ID. The default value of 0 represents a\n\t * new or missing page. Be sure to override it to avoid side effects.\n\t * @param {string} options.title Title of the page. It includes prefix where needed and\n\t * is human readable, e.g. Talk:The man who lived.\n\t * @param {Object} options.titleObj\n\t * @param {string} options.displayTitle HTML title of the page for display. Falls back\n\t * to defaults.title (escaped) if no value is provided. Must be safe HTML!\n\t * @param {number} options.namespaceNumber the number of the\n\t *  namespace the page belongs to\n\t * @param {Object} options.protection List of permissions as returned by API,\n\t * e.g. [{ edit: ['*'] }]\n\t * @param {string} options.url\n\t * @param {string} options.wikidataDescription\n\t * @param {boolean} options.isMainPage Whether the page is the Main Page.\n\t * @param {boolean} options.isMissing Whether the page exists in the wiki.\n\t * @param {Date} [options.lastModified]\n\t * @param {string} options.anchor\n\t * @param {string} [options.relevantTitle] associated with page.\n\t *  For example Special:WhatLinksHere/Foo would be associated with the page `Foo`.\n\t * @param {number} options.revId  Revision ID. See `wgRevisionId`.\n\t * @param {boolean} options.isWatched Whether the page is being watched\n\t * @param {Object} options.thumbnail thumbnail definition corresponding to page image\n\t * @param {boolean} options.thumbnail.isLandscape whether the image is in\n\t *  landscape format\n\t * @param {number} options.thumbnail.width of image in pixels.\n\t * @param {number} options.thumbnail.height of image in pixels.\n\t * @param {string} options.thumbnail.source url for image\n\t */\n\tconstructor( options ) {\n\t\tconst title = options.title || '';\n\t\tutil.extend( this, {\n\t\t\tid: options.id || 0,\n\t\t\t// FIXME: Deprecate title property as it can be derived from titleObj\n\t\t\t// using getPrefixedText\n\t\t\ttitle,\n\t\t\trelevantTitle: options.relevantTitle || title,\n\t\t\ttitleObj: options.titleObj,\n\t\t\tdisplayTitle: options.displayTitle || HTML.escape( title ),\n\t\t\tnamespaceNumber: options.namespaceNumber || 0,\n\t\t\tprotection: options.protection,\n\t\t\turl: options.url || mw.util.getUrl( title ),\n\t\t\twikidataDescription: options.wikidataDescription,\n\t\t\t_isMainPage: options.isMainPage || false,\n\t\t\tisMissing: ( options.isMissing !== undefined ) ?\n\t\t\t\toptions.isMissing : options.id === 0,\n\t\t\tlastModified: options.lastModified,\n\t\t\tanchor: options.anchor,\n\t\t\trevId: options.revId,\n\t\t\t_isWatched: options.isWatched,\n\t\t\tthumbnail: ( Object.prototype.hasOwnProperty.call( options, 'thumbnail' ) ) ?\n\t\t\t\toptions.thumbnail : false\n\t\t} );\n\n\t\tif ( this.thumbnail && this.thumbnail.width ) {\n\t\t\tthis.thumbnail.isLandscape = this.thumbnail.width > this.thumbnail.height;\n\t\t}\n\t}\n\n\t/**\n\t * Retrieve the title that should be displayed to the user\n\t *\n\t * @return {string} HTML\n\t */\n\tgetDisplayTitle() {\n\t\treturn this.displayTitle;\n\t}\n\t/**\n\t * Determine if current page is in a specified namespace\n\t *\n\t * @param {string} namespace Name of namespace\n\t * @return {boolean}\n\t */\n\tinNamespace( namespace ) {\n\t\treturn this.namespaceNumber === mw.config.get( 'wgNamespaceIds' )[namespace];\n\t}\n\n\t/**\n\t * Determines if content model is wikitext\n\t *\n\t * @return {boolean}\n\t */\n\tisWikiText() {\n\t\treturn mw.config.get( 'wgPageContentModel' ) === 'wikitext';\n\t}\n\n\t/**\n\t * Checks whether the current page is the main page\n\t *\n\t * @return {boolean}\n\t */\n\tisMainPage() {\n\t\treturn this._isMainPage;\n\t}\n\t/**\n\t * Checks whether the current page is watched\n\t *\n\t * @return {boolean}\n\t */\n\tisWatched() {\n\t\treturn this._isWatched;\n\t}\n\n\t/**\n\t * Return the latest revision id for this page\n\t *\n\t * @return {number}\n\t */\n\tgetRevisionId() {\n\t\treturn this.revId;\n\t}\n\n\t/**\n\t * Return prefixed page title\n\t *\n\t * @return {string}\n\t */\n\tgetTitle() {\n\t\treturn this.title;\n\t}\n\n\t/**\n\t * return namespace id\n\t *\n\t * @return {number} namespace Number\n\t */\n\tgetNamespaceId() {\n\t\tvar nsId,\n\t\t\targs = this.title.split( ':' );\n\n\t\tif ( args[1] ) {\n\t\t\tnsId = mw.config.get( 'wgNamespaceIds' )[ args[0].toLowerCase().replace( ' ', '_' ) ] || 0;\n\t\t} else {\n\t\t\tnsId = 0;\n\t\t}\n\t\treturn nsId;\n\t}\n}\n\nmodule.exports = Page;\n","var util = require( './util.js' ),\n\tactionParams = require( './actionParams' ),\n\tcache = {};\n\n/**\n * API for providing Page data\n *\n * @class PageGateway\n * @param {mw.Api} api\n */\nfunction PageGateway( api ) {\n\tthis.api = api;\n}\n\nPageGateway.prototype = {\n\t/**\n\t * Invalidate the internal cache for a given page\n\t *\n\t * @memberof PageGateway\n\t * @instance\n\t * @param {string} title the title of the page who's cache you want to invalidate\n\t */\n\tinvalidatePage: function ( title ) {\n\t\tdelete cache[title];\n\t},\n\n\t/**\n\t * Gets language variant list for a page; helper function for getPageLanguages()\n\t *\n\t * @memberof PageGateway\n\t * @instance\n\t * @private\n\t * @param  {string} title Name of the page to obtain variants for\n\t * @param  {string} pageLang Content language of the page\n\t * @param  {Object} data Data from API\n\t * @return {Array|boolean} List of language variant objects or false if no variants exist\n\t */\n\t_getLanguageVariantsFromApiResponse: function ( title, pageLang, data ) {\n\t\tvar variantsData = data.query.languageinfo[ pageLang ].variantnames,\n\t\t\tvariantPath = mw.config.get( 'wgVariantArticlePath' ),\n\t\t\tcontLang = mw.config.get( 'wgContentLanguage' ),\n\t\t\tvariants = [];\n\n\t\t// Variants always contain itself.\n\t\tif ( Object.keys( variantsData ).length < 2 ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Create the data object for each variant and store it\n\t\tObject.keys( variantsData ).forEach( function ( code ) {\n\t\t\tvar variant = {\n\t\t\t\tautonym: variantsData[ code ],\n\t\t\t\tlang: code\n\t\t\t};\n\n\t\t\tif ( variantPath && pageLang === contLang ) {\n\t\t\t\tvariant.url = variantPath\n\t\t\t\t\t.replace( '$1', title )\n\t\t\t\t\t.replace( '$2', code );\n\t\t\t} else {\n\t\t\t\tvariant.url = mw.util.getUrl( title, {\n\t\t\t\t\tvariant: code\n\t\t\t\t} );\n\t\t\t}\n\t\t\tvariants.push( variant );\n\t\t} );\n\n\t\treturn variants;\n\t},\n\n\t/**\n\t * Retrieve available languages for a given title\n\t *\n\t * @memberof PageGateway\n\t * @instance\n\t * @param {string} title the title of the page languages should be retrieved for\n\t * @param {string} [language] when provided the names of the languages returned\n\t *  will be translated additionally into this language.\n\t * @return {jQuery.Deferred} which is called with an object containing langlinks\n\t * and variant links as defined @ https://en.m.wikipedia.org/w/api.php?action=help&modules=query%2Blanglinks\n\t */\n\tgetPageLanguages: function ( title, language ) {\n\t\t// FIXME: Get rid of special handlings after T326997 is implemented.\n\t\tconst PAGELANG_MAP = {\n\t\t\t'ike-cans': 'iu',\n\t\t\t'ike-latn': 'iu'\n\t\t};\n\t\tvar self = this,\n\t\t\tviewLang = mw.config.get( 'wgPageContentLanguage' ),\n\t\t\tpageLang = PAGELANG_MAP[ viewLang ] || viewLang.split( '-' )[ 0 ],\n\t\t\targs = actionParams( {\n\t\t\t\tmeta: 'languageinfo',\n\t\t\t\tliprop: 'variantnames',\n\t\t\t\tlicode: pageLang,\n\t\t\t\tprop: 'langlinks',\n\t\t\t\tlllimit: 'max',\n\t\t\t\ttitles: title\n\t\t\t} );\n\n\t\tif ( language ) {\n\t\t\targs.llprop = 'url|autonym|langname';\n\t\t\targs.llinlanguagecode = language;\n\t\t} else {\n\t\t\targs.llprop = 'url|autonym';\n\t\t}\n\t\treturn this.api.get( args ).then( function ( resp ) {\n\t\t\treturn {\n\t\t\t\tlanguages: resp.query.pages[0].langlinks || [],\n\t\t\t\tvariants: self._getLanguageVariantsFromApiResponse( title, pageLang, resp )\n\t\t\t};\n\t\t}, function () {\n\t\t\treturn util.Deferred().reject();\n\t\t} );\n\t}\n};\n\nmodule.exports = PageGateway;\n","const\n\tThumbnail = require( './Thumbnail' ),\n\tHEADING_SELECTOR = mw.config.get( 'wgMFMobileFormatterHeadings', [ 'h1', 'h2', 'h3', 'h4', 'h5' ] ).join( ',' ),\n\tEXCLUDE_THUMBNAIL_CLASS_SELECTORS = [ 'noviewer', 'metadata' ];\n\nclass PageHTMLParser {\n\t/**\n\t * @param {jQuery.Object} $container Used when parsing to find children within\n\t * this container\n\t */\n\tconstructor( $container ) {\n\t\tthis.$el = $container;\n\n\t\t// T220751: Cache headings as $el.find is a very expensive call.\n\t\t/** @private */\n\t\tthis.$headings = this.$el.find( HEADING_SELECTOR );\n\t}\n\n\t/**\n\t * Find the heading in the page.\n\t * This has the benefit of excluding any additional h2s and h3s that may\n\t * have been added programatically.\n\t *\n\t * @param {number} sectionIndex as defined by the PHP parser.\n\t *  It should correspond to the section id\n\t *  used in the edit link for the section.\n\t *  Note, confusingly, this is different from section \"ID\" which is\n\t * used in methods\n\t * @return {jQuery.Object}\n\t */\n\tfindSectionHeadingByIndex( sectionIndex ) {\n\t\tif ( sectionIndex < 1 ) {\n\t\t\t// negative indexes will search from the end, which is behaviour we do not want.\n\t\t\t// return an empty set when this happens.\n\t\t\t// eslint-disable-next-line no-undef\n\t\t\treturn $( [] );\n\t\t} else {\n\t\t\treturn this.$headings\n\t\t\t\t// Headings must strictly be a child element of a section element\n\t\t\t\t// or the parser-output.\n\t\t\t\t// Not an ancestor!\n\t\t\t\t.filter( '.mw-parser-output > *, [class^=\"mf-section-\"] > *' ).eq( sectionIndex - 1 );\n\t\t}\n\t}\n\n\t/**\n\t * Finds all child elements that match the selector in a given section or subsection.\n\t * Returns any direct child elements that match the selector,\n\t * (i.e. searches only one level deep)\n\t * as well as any elements that match the selector within those children.\n\t * If the Page has no headings (e.g. a stub),\n\t * then the search will target all nodes within the page.\n\t *\n\t * This code should work on desktop (PHP parser HTML)\n\t * as well as mobile formatted HTML (PHP parser + MobileFormatter)\n\t *\n\t * @param {number} sectionIndex as defined by the PHP parser. It should correspond to\n\t *  the section id used in the edit link for the section.\n\t *  Note, confusingly, this is different from section \"ID\" which is\n\t *  used in methods\n\t * @param {string} selector to match\n\t * @return {jQuery.Object}\n\t */\n\tfindChildInSectionLead( sectionIndex, selector ) {\n\t\tvar $heading, $nextHeading, $el, $lead,\n\t\t\theadingSelector = HEADING_SELECTOR;\n\n\t\tfunction withNestedChildren( $matchingNodes ) {\n\t\t\treturn $matchingNodes.find( selector ).addBack();\n\t\t}\n\n\t\tif ( sectionIndex === 0 ) {\n\t\t\t// lead is easy\n\t\t\t$lead = this.getLeadSectionElement();\n\t\t\tif ( $lead && $lead.length ) {\n\t\t\t\treturn withNestedChildren( $lead.children( selector ) );\n\t\t\t} else {\n\t\t\t\t$heading = this.findSectionHeadingByIndex( 1 );\n\t\t\t\treturn $heading.length ? withNestedChildren( $heading.prevAll( selector ) ) :\n\t\t\t\t\t// this page is a stub so search entire page\n\t\t\t\t\tthis.$el.find( selector );\n\t\t\t}\n\t\t}\n\n\t\t// find heading associated with the section by looking at its\n\t\t// index position in the article\n\t\t// section ids relate to the element position in the page and the first heading\n\t\t// lead has been dealt with above, so first heading corresponds to section 1,\n\t\t// the first heading in the article.\n\t\t$heading = this.findSectionHeadingByIndex( sectionIndex );\n\n\t\t// If section-heading is present on the heading,\n\t\t// then we know the page has been MobileFormatted\n\t\t// and that this is a wrapped section\n\t\tif ( $heading.hasClass( 'section-heading' ) ) {\n\t\t\t// get content of section\n\t\t\t$el = $heading.next();\n\t\t\t// inside section find the first heading\n\t\t\t$nextHeading = $el.find( headingSelector ).eq( 0 );\n\t\t\treturn $nextHeading.length ?\n\t\t\t\t// find all amboxes before the next heading\n\t\t\t\twithNestedChildren( $nextHeading.prevAll( selector ) ) :\n\t\t\t\t// There is no subheadings inside\n\t\t\t\t// Grab all issues in section\n\t\t\t\twithNestedChildren( $el.children( selector ) );\n\t\t} else {\n\t\t\t// the heading relates to a subsection (or unwrapped desktop section),\n\t\t\t// so grab elements between this and the next one\n\t\t\t$nextHeading = $heading.eq( 0 ).nextAll( headingSelector ).eq( 0 );\n\t\t\treturn $heading.nextUntil( $nextHeading, selector );\n\t\t}\n\t}\n\n\t/**\n\t * Get the lead section of the page view.\n\t *\n\t * @return {jQuery.Object|null}\n\t */\n\tgetLeadSectionElement() {\n\t\t/*\n\t\t * The page is formatted as follows:\n\t\t * <div id=\"bodyContent\">\n\t\t *   <!-- content of the page.. -->\n\t\t *   <div id=\"mw-content-text\">\n\t\t *     <div class=\"mf-section-0\">lead section</div>\n\t\t *     <h2></h2>\n\t\t *     <div class=\"mf-section-1\">second section</div>\n\t\t *   </div>\n\t\t * </div>\n\t\t */\n\t\tconst $leadSection = this.$el.find( '.mf-section-0' );\n\n\t\tif ( $leadSection.length ) {\n\t\t\treturn $leadSection;\n\t\t}\n\t\t// no lead section found\n\t\treturn null;\n\t}\n\n\t/**\n\t * Return all the thumbnails in the article.\n\t * Images which have a class or link container (.image|.thumbimage)\n\t * that matches one of the items of the constant EXCLUDE_THUMBNAIL_CLASS_SELECTORS\n\t * will be excluded.\n\t * A thumbnail nested inside one of these classes will still be returned.\n\t * e.g. `<div class=\"noviewer\"><a class=\"image\"><img></a></div>` is not a valid thumbnail\n\t * `<a class=\"image noviewer\"><img></a>` is not a valid thumbnail\n\t * `<a class=\"image\"><img class=\"noviewer\"></a>` is not a valid thumbnail\n\t *\n\t * @param {jQuery} [$el] Container to search, defaults to this.$el.\n\t * @return {Thumbnail[]}\n\t */\n\tgetThumbnails( $el ) {\n\t\tvar $thumbs,\n\t\t\tnotSelector = '.' + EXCLUDE_THUMBNAIL_CLASS_SELECTORS.join( ',.' ),\n\t\t\tthumbs = [];\n\n\t\t$el = $el || this.$el;\n\n\t\t$thumbs = $el.find( 'a.image, a.thumbimage' )\n\t\t\t.not( notSelector );\n\n\t\t$thumbs.each( function () {\n\t\t\tvar $a = $el.find( this ),\n\t\t\t\t$lazyImage = $a.find( '.lazy-image-placeholder' ),\n\t\t\t\t// Parents need to be checked as well.\n\t\t\t\tvalid = $a.parents( notSelector ).length === 0 &&\n\t\t\t\t\t$a.find( notSelector ).length === 0,\n\t\t\t\thref = $a.attr( 'href' ),\n\t\t\t\tlegacyMatch = href && href.match( /title=([^/&]+)/ ),\n\t\t\t\tmatch = href && href.match( /[^/]+$/ );\n\n\t\t\t// filter out invalid lazy loaded images if so far image is valid\n\t\t\tif ( $lazyImage.length && valid ) {\n\t\t\t\t// if the regex matches it means the image has one of the classes\n\t\t\t\t// thus we must invert the result\n\t\t\t\tvalid = !new RegExp( '\\\\b(' + EXCLUDE_THUMBNAIL_CLASS_SELECTORS.join( '|' ) + ')\\\\b' )\n\t\t\t\t\t.test( $lazyImage.data( 'class' ) );\n\t\t\t}\n\n\t\t\tif ( valid && ( legacyMatch || match ) ) {\n\t\t\t\tthumbs.push(\n\t\t\t\t\tnew Thumbnail( {\n\t\t\t\t\t\tel: $a,\n\t\t\t\t\t\tfilename: mw.util.percentDecodeFragment(\n\t\t\t\t\t\t\tlegacyMatch ? legacyMatch[1] : match[0]\n\t\t\t\t\t\t)\n\t\t\t\t\t} )\n\t\t\t\t);\n\t\t\t}\n\t\t} );\n\t\treturn thumbs;\n\t}\n\n\t/**\n\t * Returns a jQuery object representing all redlinks on the page.\n\t *\n\t * @return {jQuery.Object}\n\t */\n\tgetRedLinks() {\n\t\treturn this.$el.find( '.new' );\n\t}\n}\n\n/**\n * Selector for matching headings\n *\n * @memberof PageHTMLParser\n */\nPageHTMLParser.HEADING_SELECTOR = HEADING_SELECTOR;\n\nmodule.exports = PageHTMLParser;\n","var util = require( './util.js' ),\n\tmfExtend = require( './mfExtend' ),\n\tView = require( './View' ),\n\tbrowser = require( './Browser' ).getSingleton();\n\n/**\n * List of items page view\n *\n * @class PageList\n * @extends View\n */\nfunction PageList() {\n\tView.apply( this, arguments );\n}\n\nmfExtend( PageList, View, {\n\t/**\n\t * @memberof PageList\n\t * @instance\n\t * @mixes View#defaults\n\t * @property {Object} defaults Default options hash.\n\t * @property {Page[]} defaults.pages Array of Page objects. These should match\n\t *                              the Page model and not necessarily the\n\t *                              underlying API format used.\n\t * E.g. [\n\t *   {\n\t *     heading: \"<strong>C</strong>laude Monet\",\n\t *     id: undefined,\n\t *     title: \"Claude Monet\",\n\t *     displayTitle: \"<i>Claude Monet</i>\",\n\t *     url: \"/wiki/Claude_Monet\",\n\t *     thumbnail: {\n\t *       height: 62,\n\t *       source: \"http://127.0.0.1:8080/images/thumb/thumb.jpg\",\n\t *       width: 80,\n\t *       isLandscape: true\n\t *     }\n\t *   }\n\t * ]\n\t */\n\tdefaults: {\n\t\tpages: []\n\t},\n\t/**\n\t * Render page images for the existing page list. Assumes no page images have been loaded.\n\t *\n\t * @memberof PageList\n\t * @instance\n\t */\n\trenderPageImages: function () {\n\t\tvar self = this;\n\n\t\tsetTimeout( function () {\n\t\t\tself.$el.find( '.list-thumb' ).each( function () {\n\t\t\t\tvar style = self.$el.find( this ).data( 'style' );\n\t\t\t\tself.$el.find( this ).attr( 'style', style );\n\t\t\t} );\n\t\t\t// Delay an unnecessary load of images on mobile (slower?) connections\n\t\t\t// In particular on search results which can be regenerated quickly.\n\t\t}, browser.isWideScreen() ? 0 : 1000 );\n\t},\n\t/**\n\t * @inheritdoc\n\t * @memberof PageList\n\t * @instance\n\t */\n\tpostRender: function () {\n\t\tthis.renderPageImages();\n\t},\n\ttemplate: util.template( `\n<ul class=\"page-list thumbs actionable\">\n\t{{#pages}}\n\t\t{{>item}}\n\t{{/pages}}\n</ul>\n\t` ),\n\t/**\n\t * @memberof PageList\n\t * @instance\n\t */\n\ttemplatePartials: {\n\t\t// The server uses a very different structure in\n\t\t// SpecialMobileEditWatchlist.getLineHtml(). Be aware of these differences\n\t\t// when updating server rendered items.\n\t\titem: util.template( `\n<li title=\"{{title}}\" data-id=\"{{id}}\" class=\"page-summary\">\n  <a href=\"{{url}}\" class=\"title {{#isMissing}}new{{/isMissing}}\"\n    {{#anchor}}name=\"{{anchor}}\"{{/anchor}}\n    {{#latitude}}data-latlng=\"{{latitude}},{{longitude}}\"{{/latitude}}\n    data-title=\"{{title}}\">\n    <div class=\"list-thumb\n      {{^thumbnail}}list-thumb-none list-thumb-x{{/thumbnail}}\n      {{#thumbnail.isLandscape}}list-thumb-y{{/thumbnail.isLandscape}}\n      {{^thumbnail.isLandscape}}list-thumb-x{{/thumbnail.isLandscape}}\"\n      {{#thumbnail}}data-style=\"background-image: url( {{thumbnail.source}} )\"{{/thumbnail}}></div>\n    <h3>{{{displayTitle}}}</h3>\n    {{#wikidataDescription}}\n    <div class=\"wikidata-description\">{{wikidataDescription}}</div>\n    {{/wikidataDescription}}\n    {{#lastModified}}\n    <div class=\"info\">{{lastModified}}</div>\n    {{/lastModified}}\n    {{#proximity}}\n    <div class=\"info proximity\">{{proximity}}</div>\n    {{/proximity}}\n  </a>\n</li>\n\t` )\n\t}\n} );\n\nmodule.exports = PageList;\n","var util = require( './util.js' ),\n\tmfExtend = require( './mfExtend' ),\n\tView = require( './View' );\n\n/**\n * Builds a section of a page\n *\n * @class Section\n * @extends View\n *\n * @param {Object} options Configuration options\n */\nfunction Section( options ) {\n\tvar self = this;\n\toptions.tag = 'h' + options.level;\n\tthis.line = options.line;\n\tthis.text = options.text;\n\tthis.hasReferences = options.hasReferences || false;\n\tthis.id = options.id || null;\n\tthis.anchor = options.anchor;\n\tthis.subsections = [];\n\t( options.subsections || [] ).forEach( function ( section ) {\n\t\tself.subsections.push( new Section( section ) );\n\t} );\n\tView.call( this, options );\n}\n\nmfExtend( Section, View, {\n\ttemplate: util.template( `\n<h{{level}} id=\"{{anchor}}\">{{{line}}}</h{{level}}>\n{{{text}}}\n\t` ),\n\t/**\n\t * @memberof Section\n\t * @instance\n\t * @mixes View#defaults\n\t * @property {Object} defaults Default options hash.\n\t * @property {string} defaults.text Section text.\n\t */\n\tdefaults: {\n\t\tline: undefined,\n\t\ttext: ''\n\t}\n} );\n\nmodule.exports = Section;\n","var skin,\n\tbrowser = require( './Browser' ).getSingleton(),\n\tView = require( './View' ),\n\tutil = require( './util' ),\n\tcurrentPage = require( './currentPage' ),\n\teventBus = require( './eventBusSingleton' ),\n\tmfExtend = require( './mfExtend' );\n\n/**\n * Representation of the current skin being rendered.\n *\n * @class Skin\n * @extends View\n * @uses Browser\n * @uses Page\n * @fires Skin#click\n * @param {Object} params Configuration options\n * @param {OO.EventEmitter} params.eventBus Object used to listen for\n * @param {Page} params.page\n * scroll:throttled, resize:throttled, and section-toggled events\n */\nfunction Skin( params ) {\n\tvar options = util.extend( {}, params );\n\n\tthis.page = options.page;\n\tthis.name = options.name;\n\tthis.eventBus = options.eventBus;\n\toptions.isBorderBox = false;\n\tView.call( this, options );\n}\n\nmfExtend( Skin, View, {\n\t/**\n\t * @memberof Skin\n\t * @instance\n\t * @mixes View#defaults\n\t * @property {Object} defaults Default options hash.\n\t * @property {Page} defaults.page page the skin is currently rendering\n\t */\n\tdefaults: {\n\t\tpage: undefined\n\t},\n\n\t/**\n\t * @inheritdoc\n\t * @memberof Skin\n\t * @instance\n\t */\n\tpostRender: function () {\n\t\tvar $el = this.$el;\n\n\t\tif ( browser.supportsTouchEvents() ) {\n\t\t\t$el.addClass( 'touch-events' );\n\t\t}\n\n\t\t/**\n\t\t * Fired when the skin is clicked.\n\t\t *\n\t\t * @event Skin#click\n\t\t */\n\t\tthis.$el.find( '#mw-mf-page-center' ).on( 'click', ( ev ) => {\n\t\t\tthis.emit( 'click', ev );\n\t\t} );\n\t},\n\n\t/**\n\t * @throws {Error} if mediawiki message is in unexpected format.\n\t * @return {jQuery.Object} a list of links\n\t */\n\tgetLicenseLinks: function () {\n\t\tconst mobileLicense = mw.message( 'mobile-frontend-license-links' );\n\t\tconst mobileMsgExists = mobileLicense.exists() && mobileLicense.text();\n\t\tconst userLanguage = mw.config.get( 'wgUserLanguage' );\n\t\tif ( userLanguage === 'qqx' ) {\n\t\t\t// Special handling for qqx code so we can easily debug what's going on here.\n\t\t\treturn mobileLicense.parseDom();\n\t\t} else {\n\t\t\treturn mobileMsgExists ? mobileLicense.parseDom() : this.$el.find( '#footer-info-copyright a' ).clone();\n\t\t}\n\t},\n\t/**\n\t * Returns the appropriate license message including links/name to\n\t * terms of use (if any) and license page\n\t *\n\t * @memberof Skin\n\t * @instance\n\t * @return {string|undefined}\n\t */\n\tgetLicenseMsg: function () {\n\t\tvar licenseMsg,\n\t\t\t$licenseLinks = this.getLicenseLinks();\n\n\t\tif ( $licenseLinks.length ) {\n\t\t\tconst licensePlural = mw.language.convertNumber(\n\t\t\t\t$licenseLinks.filter( 'a' ).length\n\t\t\t);\n\n\t\t\tif ( this.$el.find( '#footer-places-terms-use' ).length > 0 ) {\n\n\t\t\t\tvar $termsLink = mw.message(\n\t\t\t\t\t'mobile-frontend-editor-terms-link',\n\t\t\t\t\tthis.$el.find( '#footer-places-terms-use a' ).attr( 'href' )\n\t\t\t\t).parseDom();\n\t\t\t\tlicenseMsg = mw.message(\n\t\t\t\t\t'mobile-frontend-editor-licensing-with-terms',\n\t\t\t\t\t$termsLink,\n\t\t\t\t\t$licenseLinks,\n\t\t\t\t\tlicensePlural\n\t\t\t\t).parse();\n\t\t\t} else {\n\t\t\t\tlicenseMsg = mw.message(\n\t\t\t\t\t'mobile-frontend-editor-licensing',\n\t\t\t\t\t$licenseLinks,\n\t\t\t\t\tlicensePlural\n\t\t\t\t).parse();\n\t\t\t}\n\t\t}\n\n\t\treturn licenseMsg;\n\t}\n} );\n\n/**\n * Get a skin singleton\n *\n * @return {Skin}\n */\nSkin.getSingleton = function () {\n\tif ( !skin ) {\n\t\tskin = new Skin( {\n\t\t\tel: 'body',\n\t\t\tpage: currentPage(),\n\t\t\teventBus\n\t\t} );\n\t}\n\treturn skin;\n};\nmodule.exports = Skin;\n","var\n\tmfExtend = require( './mfExtend' ),\n\tutil = require( './util' ),\n\tView = require( './View' );\n\n/**\n * Representation of a thumbnail\n *\n * @class Thumbnail\n * @extends View\n * @param {Object} options\n */\nfunction Thumbnail( options ) {\n\tView.call( this,\n\t\tutil.extend( { isBorderBox: false }, options )\n\t);\n}\n\nmfExtend( Thumbnail, View, {\n\t/**\n\t * @memberof Thumbnail\n\t * @instance\n\t * @mixes View#defaults\n\t * @property {Object} defaults Default options hash.\n\t * @property {string} defaults.filename uri decoded filename including File: prefix\n\t *  associated with thumbnail\n\t */\n\tdefaults: {\n\t\tfilename: undefined\n\t},\n\t/**\n\t * @inheritdoc\n\t * @memberof Thumbnail\n\t * @instance\n\t */\n\tpostRender: function () {\n\t\tthis.options.description = this.$el.siblings( '.thumbcaption' ).text();\n\t},\n\t/**\n\t * Obtain description for thumbnail\n\t *\n\t * @memberof Thumbnail\n\t * @instance\n\t * @return {string}\n\t */\n\tgetDescription: function () {\n\t\treturn this.options.description;\n\t},\n\t/**\n\t * Return the page title for the thumbnail\n\t *\n\t * @memberof Thumbnail\n\t * @instance\n\t * @return {string}\n\t */\n\tgetFileName: function () {\n\t\treturn this.options.filename;\n\t}\n} );\n\nmodule.exports = Thumbnail;\n","var browser = require( './Browser' ).getSingleton(),\n\tutil = require( './util' ),\n\tescapeSelector = util.escapeSelector,\n\tarrowOptions = {\n\t\tname: 'expand',\n\t\ttype: '',\n\t\tisSmall: true,\n\t\tadditionalClassNames: 'indicator mw-ui-icon-flush-left'\n\t},\n\tIcon = require( './Icon' );\n\n/**\n *\n * @typedef {Object} ToggledEvent\n * @prop {boolean} expanded True if section is opened, false if closed.\n * @prop {Page} page\n * @prop {jQuery.Object} $heading\n */\n\n/**\n * A class for enabling toggling\n *\n * Toggling can be disabled on a sepcific heading by adding the\n * collapsible-heading-disabled class.\n *\n * @class Toggler\n * @param {Object} options\n * @param {OO.EventEmitter} options.eventBus Object used to emit section-toggled events.\n * @param {jQuery.Object} options.$container to apply toggling to\n * @param {string} options.prefix a prefix to use for the id.\n * @param {Page} options.page to allow storage of session for future visits\n * @param {boolean} [options.isClosed]\n */\nfunction Toggler( options ) {\n\tthis.eventBus = options.eventBus;\n\tthis._enable( options.$container, options.prefix, options.page, options.isClosed );\n}\n\n/**\n * Using the settings module looks at what sections were previously expanded on\n * existing page.\n *\n * @param {Page} page\n * @return {Object} representing open sections\n */\nfunction getExpandedSections( page ) {\n\tvar expandedSections = mw.storage.session.getObject( 'expandedSections' ) || {};\n\texpandedSections[page.title] = expandedSections[page.title] || {};\n\treturn expandedSections;\n}\n\n/**\n * Save expandedSections to sessionStorage\n *\n * @param {Object} expandedSections\n */\nfunction saveExpandedSections( expandedSections ) {\n\tmw.storage.session.setObject(\n\t\t'expandedSections', expandedSections\n\t);\n}\n\n/**\n * Given an expanded heading, store it to sessionStorage.\n * If the heading is collapsed, remove it from sessionStorage.\n *\n * @param {jQuery.Object} $heading - A heading belonging to a section\n * @param {Page} page\n */\nfunction storeSectionToggleState( $heading, page ) {\n\tvar headline = $heading.find( '.mw-headline' ).attr( 'id' ),\n\t\texpandedSections = getExpandedSections( page );\n\n\tif ( headline && expandedSections[page.title] ) {\n\t\tvar isSectionOpen = $heading.hasClass( 'open-block' );\n\t\tif ( isSectionOpen ) {\n\t\t\texpandedSections[page.title][headline] = true;\n\t\t} else {\n\t\t\tdelete expandedSections[page.title][headline];\n\t\t}\n\n\t\tsaveExpandedSections( expandedSections );\n\t}\n}\n\n/**\n * Expand sections that were previously expanded before leaving this page.\n *\n * @param {Toggler} toggler\n * @param {jQuery.Object} $container\n * @param {Page} page\n */\nfunction expandStoredSections( toggler, $container, page ) {\n\tvar $sectionHeading, $headline,\n\t\texpandedSections = getExpandedSections( page ),\n\t\t$headlines = $container.find( '.section-heading span' );\n\n\t$headlines.each( function () {\n\t\t$headline = $container.find( this );\n\t\t$sectionHeading = $headline.parents( '.section-heading' );\n\t\t// toggle only if the section is not already expanded\n\t\tif (\n\t\t\texpandedSections[page.title][$headline.attr( 'id' )] &&\n\t\t!$sectionHeading.hasClass( 'open-block' )\n\t\t) {\n\t\t\ttoggler.toggle( $sectionHeading, page );\n\t\t}\n\t} );\n}\n\n/**\n * Given a heading, toggle it and any of its children\n *\n * @memberof Toggler\n * @instance\n * @param {jQuery.Object} $heading A heading belonging to a section\n * @param {Page} page\n * @return {boolean}\n */\nToggler.prototype.toggle = function ( $heading, page ) {\n\tif ( $heading.hasClass( 'collapsible-heading-disabled' ) ) {\n\t\treturn false;\n\t}\n\n\tvar self = this,\n\t\twasExpanded = $heading.is( '.open-block' );\n\n\t$heading.toggleClass( 'open-block' );\n\n\tarrowOptions.rotation = wasExpanded ? 0 : 180;\n\tvar indicator = new Icon( arrowOptions );\n\tvar $indicatorElement = $heading.data( 'indicator' );\n\tif ( $indicatorElement ) {\n\t\t$indicatorElement.attr( 'class', indicator.getClassName() );\n\t}\n\n\tvar $headingLabel = $heading.find( '.mw-headline' );\n\t$headingLabel.attr( 'aria-expanded', !wasExpanded );\n\n\tvar $content = $heading.next();\n\tif ( $content.hasClass( 'open-block' ) ) {\n\t\t$content.removeClass( 'open-block' );\n\t\t// jquery doesn't allow custom values for the hidden attribute it seems.\n\t\t$content.get( 0 ).setAttribute( 'hidden', 'until-found' );\n\t} else {\n\t\t$content.addClass( 'open-block' );\n\t\t$content.removeAttr( 'hidden' );\n\t}\n\n\t/* T239418 We consider this event as a low-priority one and emit it asynchronously.\n\tThis ensures that any logic associated with section toggling is async and not contributing\n\tdirectly to a slow click/press event handler.\n\n\tCurrently costly reflow-inducing viewport size computation is being done for lazy-loaded\n\timages by the main listener to this event. */\n\tmw.requestIdleCallback( function () {\n\t\t/**\n\t\t * Global event emitted after a section has been toggled\n\t\t *\n\t\t * @event section-toggled\n\t\t * @type {ToggledEvent}\n\t\t */\n\n\t\tself.eventBus.emit( 'section-toggled', {\n\t\t\texpanded: wasExpanded,\n\t\t\t$heading: $heading\n\t\t} );\n\t} );\n\n\tif ( !browser.isWideScreen() ) {\n\t\tstoreSectionToggleState( $heading, page );\n\t}\n\treturn true;\n};\n\n/**\n * Enables toggling via enter and space keys\n *\n * @param {Toggler} toggler instance.\n * @param {jQuery.Object} $heading\n * @param {Page} page\n */\nfunction enableKeyboardActions( toggler, $heading, page ) {\n\t$heading.on( 'keypress', function ( ev ) {\n\t\tif ( ev.which === 13 || ev.which === 32 ) {\n\t\t\t// Only handle keypresses on the \"Enter\" or \"Space\" keys\n\t\t\ttoggler.toggle( $heading, page );\n\t\t}\n\t} ).find( 'a' ).on( 'keypress mouseup', function ( ev ) {\n\t\tev.stopPropagation();\n\t} );\n}\n\n/**\n * Reveals an element and its parent section as identified by it's id\n *\n * @memberof Toggler\n * @instance\n * @param {string} id An element ID within the $container\n * @param {Object} $container jQuery element to search in\n * @param {Page} page\n * @return {boolean} Target ID was found\n */\nToggler.prototype.reveal = function ( id, $container, page ) {\n\tvar $target;\n\t// jQuery will throw for hashes containing certain characters which can break toggling\n\ttry {\n\t\t$target = $container.find( '#' + escapeSelector( id ) );\n\t} catch ( e ) {}\n\tif ( !$target || !$target.length ) {\n\t\treturn false;\n\t}\n\n\tvar $heading = $target.parents( '.collapsible-heading' );\n\t// The heading is not a section heading, check if in a content block!\n\tif ( !$heading.length ) {\n\t\t$heading = $target.parents( '.collapsible-block' ).prev( '.collapsible-heading' );\n\t}\n\tif ( $heading.length && !$heading.hasClass( 'open-block' ) ) {\n\t\tthis.toggle( $heading, page );\n\t}\n\tif ( $heading.length ) {\n\t\t// scroll again after opening section (opening section makes the page longer)\n\t\twindow.scrollTo( 0, $target.offset().top );\n\t}\n\treturn true;\n};\n\n/**\n * Enables section toggling in a given container when wgMFCollapseSectionsByDefault\n * is enabled.\n *\n * @memberof Toggler\n * @instance\n * @param {jQuery.Object} $container to apply toggling to\n * @param {string} prefix a prefix to use for the id.\n * @param {Page} page to allow storage of session for future visits\n * @param {boolean} [isClosed] whether the element should begin closed\n * @private\n */\nToggler.prototype._enable = function ( $container, prefix, page, isClosed ) {\n\tvar self = this,\n\t\tcollapseSectionsByDefault = mw.config.get( 'wgMFCollapseSectionsByDefault' );\n\n\tif ( collapseSectionsByDefault === undefined ) {\n\t\t// Old default behavior if on cached output\n\t\tcollapseSectionsByDefault = true;\n\t}\n\t// NB: 'expandSections' uses localStorage, unlike 'expandedSections' which uses sessionStorage\n\tvar expandSections = !collapseSectionsByDefault || mw.storage.get( 'expandSections' ) === 'true';\n\n\t// FIXME This should use .find() instead of .children(), some extensions like Wikibase\n\t// want to toggle other headlines than direct descendants of $container. (T95889)\n\t$container.children( '.section-heading' ).each( function ( i ) {\n\t\tvar $heading = $container.find( this ),\n\t\t\t$headingLabel = $heading.find( '.mw-headline' ),\n\t\t\t$indicator = $heading.find( '.indicator' ),\n\t\t\tid = prefix + 'collapsible-block-' + i;\n\t\t// Be sure there is a `section` wrapping the section content.\n\t\t// Otherwise, collapsible sections for this page is not enabled.\n\t\tif ( $heading.next().is( 'section' ) ) {\n\t\t\tvar $content = $heading.next( 'section' );\n\t\t\t$heading\n\t\t\t\t.addClass( 'collapsible-heading ' )\n\t\t\t\t.data( 'section-number', i )\n\t\t\t\t.on( 'click', function ( ev ) {\n\t\t\t\t\t// don't toggle, if the click target was a link\n\t\t\t\t\t// (a link in a section heading)\n\t\t\t\t\t// See T117880\n\t\t\t\t\tif ( !ev.target.href ) {\n\t\t\t\t\t\t// prevent taps/clicks on edit button after toggling (T58209)\n\t\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t\tself.toggle( $heading, page );\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t$headingLabel\n\t\t\t\t.attr( {\n\t\t\t\t\ttabindex: 0,\n\t\t\t\t\trole: 'button',\n\t\t\t\t\t'aria-controls': id,\n\t\t\t\t\t'aria-expanded': 'false'\n\t\t\t\t} );\n\n\t\t\tarrowOptions.rotation = expandSections ? 180 : 0;\n\t\t\tvar indicator = new Icon( arrowOptions );\n\n\t\t\tif ( $indicator.length ) {\n\t\t\t\t// replace the existing indicator\n\t\t\t\t$indicator.replaceWith( indicator.$el );\n\t\t\t} else {\n\t\t\t\tindicator.prependTo( $heading );\n\t\t\t}\n\t\t\t$heading.data( 'indicator', indicator.$el );\n\t\t\t$content\n\t\t\t\t.addClass( 'collapsible-block' )\n\t\t\t\t.eq( 0 )\n\t\t\t\t.attr( {\n\t\t\t\t\t// We need to give each content block a unique id as that's\n\t\t\t\t\t// the only way we can tell screen readers what element we're\n\t\t\t\t\t// referring to via `aria-controls`.\n\t\t\t\t\tid: id\n\t\t\t\t} )\n\t\t\t\t.on( 'beforematch', function () {\n\t\t\t\t\tself.toggle( $heading, page );\n\t\t\t\t} )\n\t\t\t\t.addClass( 'collapsible-block-js' )\n\t\t\t\t.get( 0 ).setAttribute( 'hidden', 'until-found' );\n\n\t\t\tenableKeyboardActions( self, $heading, page );\n\n\t\t\t// When the collapsible-headings-collapsed class is present on the body,\n\t\t\t// never expand **all** sections, regardless of device size or preferences.\n\t\t\t// (T321618, T322628)\n\t\t\tvar alwaysCollapsed = document.body.classList.contains( 'collapsible-headings-collapsed' );\n\t\t\tif (\n\t\t\t\t!alwaysCollapsed && (\n\t\t\t\t\t!isClosed && browser.isWideScreen() || expandSections\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\t// Expand sections by default on wide screen devices\n\t\t\t\t// or if the expand sections setting is set.\n\t\t\t\t// The wide screen logic for determining whether to collapse sections initially\n\t\t\t\t// should be kept in sync with mobileoptions#initLocalStorageElements().\n\t\t\t\tself.toggle( $heading, page );\n\t\t\t}\n\t\t}\n\t} );\n\n\t/**\n\t * Checks the existing hash and toggles open any section that contains the fragment.\n\t *\n\t * @method\n\t */\n\tfunction checkHash() {\n\t\t// eslint-disable-next-line no-restricted-properties\n\t\tvar hash = window.location.hash;\n\t\tif ( hash.indexOf( '#' ) === 0 ) {\n\t\t\thash = hash.slice( 1 );\n\t\t\t// Per https://html.spec.whatwg.org/multipage/browsing-the-web.html#target-element\n\t\t\t// we try the raw fragment first, then the percent-decoded fragment.\n\t\t\tif ( !self.reveal( hash, $container, page ) ) {\n\t\t\t\tvar decodedHash = mw.util.percentDecodeFragment( hash );\n\t\t\t\tif ( decodedHash ) {\n\t\t\t\t\tself.reveal( decodedHash, $container, page );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Checks the value of wgInternalRedirectTargetUrl and sets the hash if present.\n\t * checkHash() will reveal the collapsed section that contains it afterwards.\n\t *\n\t * @method\n\t */\n\tfunction checkInternalRedirectAndHash() {\n\t\tvar internalRedirect = mw.config.get( 'wgInternalRedirectTargetUrl' ),\n\t\t\tinternalRedirectHash = internalRedirect ? internalRedirect.split( '#' )[1] : false;\n\n\t\tif ( internalRedirectHash ) {\n\t\t\t// eslint-disable-next-line no-restricted-properties\n\t\t\twindow.location.hash = internalRedirectHash;\n\t\t}\n\t}\n\n\tcheckInternalRedirectAndHash();\n\tcheckHash();\n\t// Restricted to links created by editors and thus outside our control\n\t// T166544 - don't do this for reference links - they will be handled elsewhere\n\tvar $link = $container.find( 'a:not(.reference a)' );\n\t$link.on( 'click', function () {\n\t\t// the link might be an internal link with a hash.\n\t\t// if it is check if we need to reveal any sections.\n\t\tif ( $link.attr( 'href' ) !== undefined &&\n\t\t$link.attr( 'href' ).indexOf( '#' ) > -1\n\t\t) {\n\t\t\tcheckHash();\n\t\t}\n\t} );\n\tutil.getWindow().on( 'hashchange', function () {\n\t\tcheckHash();\n\t} );\n\n\tif ( !browser.isWideScreen() && page ) {\n\t\texpandStoredSections( this, $container, page );\n\t}\n};\n\nToggler._getExpandedSections = getExpandedSections;\nToggler._expandStoredSections = expandStoredSections;\n\nmodule.exports = Toggler;\n","/* global $ */\nvar util = require( './util' ),\n\tmfExtend = require( './mfExtend' ),\n\t// Cached regex to split keys for `delegate`.\n\tdelegateEventSplitter = /^(\\S+)\\s*(.*)$/,\n\tidCounter = 0;\n\n/**\n * Generate a unique integer id (unique within the entire client session).\n * Useful for temporary DOM ids.\n *\n * @param {string} prefix Prefix to be used when generating the id.\n * @return {string}\n */\nfunction uniqueId( prefix ) {\n\tvar id = ( ++idCounter ).toString();\n\treturn prefix ? prefix + id : id;\n}\n\n/**\n * Should be extended using extend().\n *\n * When options contains el property, this.$el in the constructed object\n * will be set to the corresponding jQuery object. Otherwise, this.$el\n * will be an empty div.\n *\n * When extended using extend(), if the extended prototype contains\n * template property, this.$el will be filled with rendered template (with\n * options parameter used as template data).\n *\n * template property can be a string which will be passed to mw.template.compile()\n * or an object that has a render() function which accepts an object with\n * template data as its argument (similarly to an object created by\n * mw.template.compile()).\n *\n * You can also define a defaults property which should be an object\n * containing default values for the template (if they're not present in\n * the options parameter).\n *\n * If this.$el is not a jQuery object bound to existing DOM element, the\n * view can be attached to an element using appendTo(), prependTo(),\n * insertBefore(), insertAfter() proxy functions.\n *\n * append(), prepend(), before(), after() can be used to modify $el. on()\n * can be used to bind events.\n *\n * You can also use declarative DOM events binding by specifying an `events`\n * map on the class. The keys will be 'event selector' and the value can be\n * either the name of a method to call, or a function. All methods and\n * functions will be executed on the context of the View.\n *\n * Inspired from Backbone.js\n * https://github.com/jashkenas/backbone/blob/master/backbone.js#L1128\n *\n * Example:\n * ```js\n *     var\n *       MyComponent = View.extend( {\n *         edit: function ( ev ) {\n *           //...\n *         },\n *         save: function ( ev ) {\n *           //...\n *         }\n *       } ),\n *       instance = new MyComponent({\n *         events: {\n *           'mousedown .title': 'edit',\n *           'click .button': 'save',\n *           'click .open': function(e) { ... }\n *         }\n *       });\n * ```\n *\n * Example:\n * ```js\n *     var View, section;\n *     function Section( options ) {\n *       var defaultOptions = {\n *         events: {\n *           // ...\n *         }\n *       }\n *       View.call( this, util.extends( {}, defaultOptions, options ) );\n *     }\n *     View = require( './View' );\n *     require( './mfExtend' )( Section, View, {\n *       template: mw.template.compile( \"&lt;h2&gt;{{title}}&lt;/h2&gt;\" ),\n *     } );\n *     section = new Section( { title: 'Test', text: 'Test section body' } );\n *     section.appendTo( 'body' );\n * ```\n *\n * @class View\n * @mixins OO.EventEmitter\n */\n\nfunction View() {\n\tthis.initialize.apply( this, arguments );\n}\nOO.mixinClass( View, OO.EventEmitter );\nmfExtend( View, {\n\t/**\n\t * Name of tag that contains the rendered template\n\t *\n\t * @memberof View\n\t * @instance\n\t * @property {string} tagName\n\t */\n\ttagName: 'div',\n\t/**\n\t * Tells the View to ignore tagName and className when constructing the element\n\t * and to rely solely on the template\n\t *\n\t * @memberof View\n\t * @instance\n\t * @property {boolean} isTemplateMode\n\t */\n\tisTemplateMode: false,\n\t/**\n\t * @memberof View\n\t * @instance\n\t * @property {Mixed}\n\t * Specifies the template used in render(). Object|string\n\t */\n\ttemplate: undefined,\n\n\t/**\n\t * Specifies partials (sub-templates) for the main template. Example:\n\t *\n\t *     @example\n\t *     // example content for the \"some\" template (sub-template will be\n\t *     // inserted where {{>content}} is):\n\t *     // <h1>Heading</h1>\n\t *     // {{>content}}\n\t *\n\t *     oo.mfExtend( SomeView, View, {\n\t *       template: util.template( '<source-code>' ),\n\t *       templatePartials: { content: util.template( '<source-code>' ) }\n\t *     }\n\t *\n\t * @memberof View\n\t * @instance\n\t * @property {Object}\n\t */\n\ttemplatePartials: {},\n\n\t/**\n\t * A set of default options that are merged with options passed into the initialize\n\t * function.\n\t *\n\t * @memberof View\n\t * @instance\n\t * @property {Object} defaults Default options hash.\n\t * @property {jQuery.Object|string} [defaults.el] jQuery selector to use for rendering.\n\t * @property {boolean} [defaults.skipTemplateRender] Whether to enhance views already in\n\t * DOM. When enabled, the template is disabled so that it is not rendered in the DOM.\n\t * Use in conjunction with View::defaults.$el to associate the View with an existing\n\t * already rendered element in the DOM.\n\t */\n\tdefaults: {},\n\n\t/**\n\t * Run once during construction to set up the View\n\t *\n\t * @memberof View\n\t * @instance\n\t * @param {Object} options Object passed to the constructor.\n\t * @param {Object.<string, string>} [options.events]\n\t */\n\tinitialize: function ( options ) {\n\t\tvar self = this;\n\n\t\tOO.EventEmitter.call( this );\n\t\toptions = util.extend( {}, this.defaults, options );\n\t\tthis.options = options;\n\t\t// Assign a unique id for dom events binding/unbinding\n\t\tthis.cid = uniqueId( 'view' );\n\n\t\t// TODO: if template compilation is too slow, don't compile them on a\n\t\t// per object basis, but don't worry about it now (maybe add cache to\n\t\t// M.template.compile())\n\t\tif ( typeof this.template === 'string' ) {\n\t\t\tthis.template = mw.template.compile( this.template );\n\t\t}\n\n\t\tif ( options.el ) {\n\t\t\t// Note the element may not be in the document so must use global jQuery here\n\t\t\tthis.$el = $( options.el );\n\t\t} else {\n\t\t\tthis.$el = this.parseHTML( '<' + this.tagName + '>' );\n\t\t}\n\n\t\t// Make sure the element is ready to be manipulated\n\t\tif ( this.$el.length ) {\n\t\t\tthis._postInitialize( options );\n\t\t} else {\n\t\t\tutil.docReady( function () {\n\t\t\t\t// Note the element may not be in the document so must use global jQuery here\n\t\t\t\tself.$el = $( options.el );\n\t\t\t\tself._postInitialize( options );\n\t\t\t} );\n\t\t}\n\t},\n\n\t/**\n\t * Called when this.$el is ready.\n\t *\n\t * @memberof View\n\t * @instance\n\t * @private\n\t * @param {Object} props\n\t */\n\t_postInitialize: function ( props ) {\n\t\t// eslint-disable-next-line mediawiki/class-doc\n\t\tthis.$el.addClass( props.className );\n\t\t// border-box will be added provided this flag is not set\n\t\tif ( props.isBorderBox !== false ) {\n\t\t\tthis.$el.addClass( 'view-border-box' );\n\t\t}\n\n\t\tthis.render( {} );\n\t},\n\n\t/**\n\t * Function called before the view is rendered. Can be redefined in\n\t * objects that extend View.\n\t *\n\t * @memberof View\n\t * @instance\n\t */\n\tpreRender: function () {},\n\n\t/**\n\t * Function called after the view is rendered. Can be redefined in\n\t * objects that extend View.\n\t *\n\t * @memberof View\n\t * @instance\n\t */\n\tpostRender: function () {},\n\n\t/**\n\t * Fill this.$el with template rendered using data if template is set.\n\t *\n\t * @memberof View\n\t * @instance\n\t * @param {Object} data Template data. Will be merged into the view's\n\t * options\n\t * @chainable\n\t */\n\trender: function ( data ) {\n\t\tvar $el, html;\n\t\tutil.extend( this.options, data );\n\t\tthis.preRender();\n\t\tthis.undelegateEvents();\n\t\tif ( this.template && !this.options.skipTemplateRender ) {\n\t\t\thtml = this.template.render( this.options, this.templatePartials );\n\t\t\tif ( this.isTemplateMode ) {\n\t\t\t\t$el = $( html );\n\t\t\t\tthis.$el.replaceWith( $el );\n\t\t\t\tthis.$el = $el;\n\t\t\t} else {\n\t\t\t\tthis.$el.html( html );\n\t\t\t}\n\t\t}\n\t\tthis.postRender();\n\t\tthis.delegateEvents();\n\t\treturn this;\n\t},\n\n\t/**\n\t * Set callbacks, where `this.options.events` is a hash of\n\t *\n\t * { 'event selector': 'callback' }\n\t *\n\t * {\n\t *   'mousedown .title': 'edit',\n\t *   'click .button': 'save',\n\t *   'click .open': function(e) { ... }\n\t * }\n\t *\n\t * pairs. Callbacks will be bound to the view, with `this` set properly.\n\t * Uses event delegation for efficiency.\n\t * Omitting the selector binds the event to `this.el`.\n\t *\n\t * @memberof View\n\t * @instance\n\t * @param {Object} events Optionally set this events instead of the ones on this.\n\t */\n\tdelegateEvents: function ( events ) {\n\t\tvar match, key, method;\n\t\tevents = events || this.options.events;\n\t\tif ( events ) {\n\t\t\t// Remove current events before re-binding them\n\t\t\tthis.undelegateEvents();\n\t\t\tfor ( key in events ) {\n\t\t\t\tmethod = events[ key ];\n\t\t\t\t// If the method is a string name of this.method, get it\n\t\t\t\tif ( typeof method !== 'function' ) {\n\t\t\t\t\tmethod = this[ events[ key ] ];\n\t\t\t\t}\n\t\t\t\tif ( method ) {\n\t\t\t\t\t// Extract event and selector from the key\n\t\t\t\t\tmatch = key.match( delegateEventSplitter );\n\t\t\t\t\tthis.delegate( match[ 1 ], match[ 2 ], method.bind( this ) );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\t/**\n\t * Add a single event listener to the view's element (or a child element\n\t * using `selector`). This only works for delegate-able events: not `focus`\n\t * or `blur`.\n\t *\n\t * @memberof View\n\t * @instance\n\t * @param {string} eventName\n\t * @param {string} selector\n\t * @param {Function} listener\n\t */\n\tdelegate: function ( eventName, selector, listener ) {\n\t\tthis.$el.on( eventName + '.delegateEvents' + this.cid, selector,\n\t\t\tlistener );\n\t},\n\n\t/**\n\t * Clears all callbacks previously bound to the view by `delegateEvents`.\n\t * You usually don't need to use this, but may wish to if you have multiple\n\t * views attached to the same DOM element.\n\t *\n\t * @memberof View\n\t * @instance\n\t */\n\tundelegateEvents: function () {\n\t\tif ( this.$el ) {\n\t\t\tthis.$el.off( '.delegateEvents' + this.cid );\n\t\t}\n\t},\n\n\t/**\n\t * A finer-grained `undelegateEvents` for removing a single delegated event.\n\t * `selector` and `listener` are both optional.\n\t *\n\t * @memberof View\n\t * @instance\n\t * @param {string} eventName\n\t * @param {string} selector\n\t * @param {Function} listener\n\t */\n\tundelegate: function ( eventName, selector, listener ) {\n\t\tthis.$el.off( eventName + '.delegateEvents' + this.cid, selector,\n\t\t\tlistener );\n\t},\n\n\t/**\n\t * See parseHTML method of util singleton\n\t *\n\t * @memberof View\n\t * @instance\n\t * @param {string} html to turn into a jQuery object.\n\t * @return {jQuery.Object}\n\t */\n\tparseHTML: function ( html ) {\n\t\t// document is explicitly passed due to a bug we found in Safari 11.1.2 where failure\n\t\t// to use document resulted in an element without access to the documentElement\n\t\t// this should be redundant, but no problem in being explicit (T214451).\n\t\treturn util.parseHTML( html, document );\n\t}\n} );\n\n/**\n * @memberof View\n * @instance\n * @func append\n * @param {...(string|Node|Node[]|jQuery)} contents\n * @return {this}\n */\n\n/**\n * @memberof View\n * @instance\n * @func append\n * @param {function(number, string): string|Node|Node[]|jQuery} contents\n * @return {this}\n */\n\n/**\n * @memberof View\n * @instance\n * @func prepend\n * @param {...(string|Node|Node[]|jQuery)} contents\n * @return {this}\n */\n\n/**\n * @memberof View\n * @instance\n * @func prepend\n * @param {function(number, string): string|Node|Node[]|jQuery} contents\n * @return {this}\n */\n\n/**\n * @memberof View\n * @instance\n * @func appendTo\n * @param {string|Node|Node[]|jQuery} target\n * @return {this}\n */\n\n/**\n * @memberof View\n * @instance\n * @func prependTo\n * @param {string|Node|Node[]|jQuery} target\n * @return {this}\n */\n\n/**\n * @memberof View\n * @instance\n * @func after\n * @param {...(string|Node|Node[]|jQuery)} contents\n * @return {this}\n */\n\n/**\n * @memberof View\n * @instance\n * @func after\n * @param {function(number, string): string|Node|Node[]|jQuery} contents\n * @return {this}\n */\n\n/**\n * @memberof View\n * @instance\n * @func before\n * @param {...(string|Node|Node[]|jQuery)} contents\n * @return {this}\n */\n\n/**\n * @memberof View\n * @instance\n * @func before\n * @param {function(number, string): string|Node|Node[]|jQuery} contents\n * @return {this}\n */\n\n/**\n * @memberof View\n * @instance\n * @property {jQuery.Object} $el\n */\n\n/**\n * @memberof View\n * @instance\n * @func insertAfter\n * @param {string|Node|Node[]|jQuery} target\n * @return {this}\n */\n\n/**\n * @memberof View\n * @instance\n * @func insertBefore\n * @param {string|Node|Node[]|jQuery} target\n * @return {this}\n */\n\n/**\n * @memberof View\n * @instance\n * @func remove\n * @param {string} [selector]\n * @return {this}\n */\n\n/**\n * @memberof View\n * @instance\n * @func detach\n * @param {string} [selector]\n * @return {this}\n */\n\n[\n\t'append',\n\t'prepend',\n\t'appendTo',\n\t'prependTo',\n\t'after',\n\t'before',\n\t'insertAfter',\n\t'insertBefore',\n\t'remove',\n\t'detach'\n].forEach( function ( prop ) {\n\tView.prototype[prop] = function () {\n\t\tthis.$el[prop].apply( this.$el, arguments );\n\t\treturn this;\n\t};\n} );\n\n/**\n * Generates a view with children\n *\n * @param {Object} options\n * @param {jQuery.Element[]} children\n * @return {View}\n */\nView.make = function ( options = {}, children = [] ) {\n\tvar view = new View( options );\n\tchildren.forEach( function ( $child ) {\n\t\tview.append( $child );\n\t} );\n\treturn view;\n};\n\nmodule.exports = View;\n","var util = require( './util' ),\n\tdefaultParams = {\n\t\taction: 'query',\n\t\tformatversion: 2\n\t};\n\n/**\n * Extends the default params for an action query with otherParams\n *\n * @param {Object} otherParams\n * @return {Object}\n */\nfunction actionParams( otherParams ) {\n\tvar scriptPath = mw.config.get( 'wgMFScriptPath' );\n\treturn util.extend( {}, defaultParams, {\n\t\torigin: scriptPath ? '*' : undefined\n\t}, otherParams );\n}\n\nmodule.exports = actionParams;\n","const\n\tButton = require( '../Button' ),\n\tutil = require( '../util' ),\n\tView = require( '../View' );\n\n/**\n * @typedef {Object} FormField\n * @property {string} name\n * @property {string} value\n */\n\n/**\n * @param {Object} options\n * @param {string} options.postUrl Form will POST to this endpoint\n * @param {FormField[]} options.fields An array of hidden form fields\n * @param {string} options.buttonLabel Label for submit button\n * submitted\n * @extends View\n */\nclass AmcEnableForm extends View {\n\t/** @inheritdoc */\n\tget isTemplateMode() {\n\t\treturn true;\n\t}\n\n\t/** @inheritdoc */\n\tget template() {\n\t\treturn util.template( `\n<form class=\"amc-enable-form\" action=\"{{postUrl}}\" method=\"POST\">\n\t{{#fields}}\n\t\t<input type=\"hidden\" name=\"{{name}}\" value=\"{{value}}\">\n\t{{/fields}}\n</form>\n\t\t` );\n\t}\n\n\t/** @inheritdoc */\n\tpostRender() {\n\t\tthis.$el.append(\n\t\t\tnew Button( {\n\t\t\t\ttagName: 'button',\n\t\t\t\tprogressive: true,\n\t\t\t\tlabel: this.options.buttonLabel\n\t\t\t} ).$el\n\t\t);\n\t}\n}\n\nmodule.exports = AmcEnableForm;\n","const\n\ttoast = require( '../showOnPageReload' ),\n\tcreatePromoCampaign = require( '../promoCampaign/promoCampaign' ),\n\tamcOutreachDrawer = require( './amcOutreachDrawer' ),\n\t// MW constants should be kept in sync with onMakeGlobalVariableScript() from\n\t// MobileFrontendHooks.php\n\tMW_CONFIG_CAMPAIGN_ACTIVE_NAME = 'wgMFAmcOutreachActive',\n\tMW_CONFIG_USER_ELIGIBLE_NAME = 'wgMFAmcOutreachUserEligible',\n\t// This object contains arbitrary actions that are meant to only be shown one\n\t// time at most. Each action is either 'eligible' or 'ineligible' at any given\n\t// time.\n\t//\n\t// When a new action is desired, it should be added to this object.\n\t//\n\t// In other languages, this would be an enum. However, because JS doesn't\n\t// support enums, the keys/value names are identical in an attempt to mimic\n\t// some of their functionality. The names in this object are used by\n\t// promoCampaign to mark when an action has become 'ineligible'.\n\tACTIONS = {\n\t\tonDesktopLink: 'onDesktopLink',\n\t\tonHistoryLink: 'onHistoryLink',\n\t\tonTalkLink: 'onTalkLink'\n\t},\n\tCAMPAIGN_NAME = 'amc-outreach';\n\n// singleton;\nlet campaign;\n\nmodule.exports = {\n\t/**\n\t * @return {PromoCampaign}\n\t */\n\tloadCampaign: () => {\n\t\tif ( campaign ) {\n\t\t\treturn campaign;\n\t\t}\n\n\t\tcampaign = createPromoCampaign(\n\t\t\t/**\n\t\t\t * This callback is executed by promoCampaign's `showIfEligible` method.\n\t\t\t * promoCampaign will only execute it when an action is 'eligible'.\n\t\t\t *\n\t\t\t * @param {string} action Name of one of the actions in the ACTIONS\n\t\t\t * object. This is used by the drawer to notify promoCampaign when the\n\t\t\t * action has become 'ineligible' (e.g. after enabling or dismissing the\n\t\t\t * drawer).\n\t\t\t * @param {onBeforeHide} onBeforeHide Callback exected after user\n\t\t\t * dismisses drawer.\n\t\t\t * @param {string} returnToTitle Title of page to redirect to after user enables\n\t\t\t * AMC\n\t\t\t * @param {string} [returnToQuery] Optional query params to add to redirected\n\t\t\t * URL after user enables AMC. Can also include anchor (e.g.\n\t\t\t * `foo=bar#/Talk`\n\t\t\t * @return {Drawer|null}\n\t\t\t */\n\t\t\t( action, onBeforeHide, returnToTitle, returnToQuery ) => {\n\t\t\t\treturn amcOutreachDrawer(\n\t\t\t\t\taction,\n\t\t\t\t\tcampaign,\n\t\t\t\t\tmw.message,\n\t\t\t\t\tmw.util,\n\t\t\t\t\ttoast,\n\t\t\t\t\tmw.user.tokens.get( 'csrfToken' ),\n\t\t\t\t\tonBeforeHide,\n\t\t\t\t\treturnToTitle,\n\t\t\t\t\treturnToQuery\n\t\t\t\t);\n\t\t\t},\n\t\t\tACTIONS,\n\t\t\tCAMPAIGN_NAME,\n\t\t\t// in minerva desktop, this config will not be set\n\t\t\t!!mw.config.get( MW_CONFIG_CAMPAIGN_ACTIVE_NAME ),\n\t\t\t!!mw.config.get( MW_CONFIG_USER_ELIGIBLE_NAME ),\n\t\t\tmw.storage\n\t\t);\n\n\t\treturn campaign;\n\t},\n\tACTIONS: ACTIONS\n};\n","const\n\tDrawer = require( '../Drawer' ),\n\tAnchor = require( '../Anchor' ),\n\tutil = require( '../util' ),\n\tAmcEnableForm = require( './AmcEnableForm' ),\n\t// These constants should be kept in sync with SpecialMobileOptions.php\n\tAMC_ENABLE_FIELD_NAME = 'enableAMC',\n\tAMC_ENABLE_FIELD_VALUE = '1';\n\n/**\n * Callback intended to allow the client run extra logic (e.g. show a modal)\n * after the drawer is dismissed.\n *\n * @callback onBeforeHide\n */\n\n/**\n * @param {string} action Used by the drawer to notify promoCampaign when the\n * action has become 'ineligible' (e.g. after enabling or dismissing the drawer).\n * @param {PromoCampaign} promoCampaign\n * @param {mw.message} mwMessage Used for i18n\n * @param {mw.util} mwUtil Used to determine POST url for the enable form\n * @param {Toast} toast Displays success message after user submits enable form\n * @param {string} csrfToken\n * @param {onBeforeHide} onBeforeHide\n * @param {string} returnToTitle Title to redirect to after user enables\n * AMC\n * @param {string} [returnToQuery] Optional query params to add to redirected\n * URL after user enables AMC\n * @return {Drawer} Returns the drawer that is shown\n */\nfunction amcOutreachDrawer(\n\taction,\n\tpromoCampaign,\n\tmwMessage,\n\tmwUtil,\n\ttoast,\n\tcsrfToken,\n\tonBeforeHide,\n\treturnToTitle,\n\treturnToQuery\n) {\n\treturn new Drawer( {\n\t\tclassName: 'amc-outreach-drawer',\n\t\tchildren: [\n\t\t\tutil.parseHTML( '<div>' ).addClass( 'amc-outreach-image' ),\n\t\t\tutil.parseHTML( '<p>' ).append(\n\t\t\t\tutil.parseHTML( '<strong>' ).text(\n\t\t\t\t\tmwMessage( 'mobile-frontend-amc-outreach-intro' ).text()\n\t\t\t\t)\n\t\t\t),\n\t\t\tutil.parseHTML( '<p>' ).text(\n\t\t\t\tmwMessage( 'mobile-frontend-amc-outreach-description' ).text()\n\t\t\t),\n\t\t\tnew AmcEnableForm( {\n\t\t\t\tpostUrl: mwUtil.getUrl( 'Special:MobileOptions', {\n\t\t\t\t\treturnto: returnToTitle,\n\t\t\t\t\treturntoquery: returnToQuery || ''\n\t\t\t\t} ),\n\t\t\t\tfields: [\n\t\t\t\t\t{\n\t\t\t\t\t\tname: 'updateSingleOption',\n\t\t\t\t\t\tvalue: AMC_ENABLE_FIELD_NAME\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tname: 'enableAMC',\n\t\t\t\t\t\tvalue: AMC_ENABLE_FIELD_VALUE\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tname: 'token',\n\t\t\t\t\t\tvalue: csrfToken\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\tbuttonLabel: mwMessage( 'mobile-frontend-amc-outreach-enable' ).text(),\n\t\t\t\tevents: {\n\t\t\t\t\tsubmit: () => {\n\t\t\t\t\t\tpromoCampaign.makeActionIneligible( action );\n\t\t\t\t\t\ttoast.showOnPageReload( mwMessage( 'mobile-frontend-amc-outreach-enabled-message' ).text() );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} ).$el,\n\t\t\tnew Anchor( {\n\t\t\t\thref: '#',\n\t\t\t\tadditionalClassNames: 'cancel',\n\t\t\t\tprogressive: true,\n\t\t\t\tlabel: mwMessage( 'mobile-frontend-amc-outreach-no-thanks' ).text()\n\t\t\t} ).$el\n\t\t],\n\t\tonBeforeHide: () => {\n\t\t\tpromoCampaign.makeActionIneligible( action );\n\t\t\tonBeforeHide();\n\t\t}\n\t} );\n}\n\nmodule.exports = amcOutreachDrawer;\n","/* global $ */\n\nconst\n\tPage = require( './Page' );\n\nlet page;\n\n/**\n * Constructs an incomplete Page model singleton representing the currently loaded page.\n *\n * Because this depends on the presence of certain DOM elements, it\n * should only be called after the DOMContentLoaded event.\n *\n * @return {Page}\n */\nfunction loadCurrentPage() {\n\tif ( page ) {\n\t\treturn page;\n\t}\n\n\tconst permissions = [].concat( mw.config.get( 'wgRestrictionEdit', [] ) ),\n\t\trelevantTitle = mw.Title.newFromText( mw.config.get( 'wgRelevantPageName' ) ),\n\t\ttitle = mw.Title.newFromText( mw.config.get( 'wgPageName' ) );\n\n\tif ( permissions.length === 0 ) {\n\t\tpermissions.push( '*' );\n\t}\n\n\tpage = new Page( {\n\t\ttitle: title.getPrefixedText(),\n\t\ttitleObj: title,\n\t\trelevantTitle: relevantTitle.getPrefixedText(),\n\t\tprotection: {\n\t\t\tedit: permissions\n\t\t},\n\t\trevId: mw.config.get( 'wgRevisionId' ),\n\t\tisMainPage: mw.config.get( 'wgIsMainPage' ),\n\t\tisWatched: $( '#ca-watch' ).hasClass( 'watched' ),\n\t\tisMissing: mw.config.get( 'wgArticleId' ) === 0,\n\t\tid: mw.config.get( 'wgArticleId' ),\n\t\tnamespaceNumber: mw.config.get( 'wgNamespaceNumber' )\n\t} );\n\n\treturn page;\n}\n\nmodule.exports = loadCurrentPage;\n","/* global $ */\n\nconst\n\tPageHTMLParser = require( './PageHTMLParser' );\n\nlet pageHTMLParser;\n\n/**\n * Constructs a page parser singleton specific to the current page to find common child elements\n * more easily.\n *\n * Because this depends on the presence of certain DOM elements, it\n * should only be called after the DOMContentLoaded event.\n *\n * @return {PageHTMLParser}\n */\nfunction loadCurrentPageHTMLParser() {\n\tif ( pageHTMLParser ) {\n\t\treturn pageHTMLParser;\n\t}\n\n\tpageHTMLParser = new PageHTMLParser( $( '#content #bodyContent' ) );\n\n\treturn pageHTMLParser;\n}\n\nmodule.exports = loadCurrentPageHTMLParser;\n","/**\n * T156186: Please make judicious use of this singleton whose purpose is to\n * allow disparate components the ability to subscribe to a set of events and\n * react to those events.\n *\n * Prefer to use a more localized event bus when possible.\n *\n * Only import this in files at the edges. For example:\n *\n * Good: initialization scripts responsible for initializing classes such as\n * mobile.init/init.js\n * Bad: ImageOverlay.js or in any other component.\n *\n * By doing this, and using dependency injection of the event bus in the\n * components themselves, it will make it easier to switch this event bus out\n * for something more localized later on in our refactoring efforts.\n */\nmodule.exports = new OO.EventEmitter();\n","var util = require( './util' ),\n\tactionParams = require( './actionParams.js' );\n\n/**\n * Extends the API query parameters to include those parameters required to also fetch Wikibase\n * descriptions and appropriately sized thumbnail images as well as those required to make a query.\n *\n * This function wraps `util.extend` with some Wikibase-specific configuration\n * variable management\n * but, like `util.extend`, is variadic and so can be used as a replacement for it in search\n * gateways, e.g.\n *\n * ```\n * var params = extendSearchParams(\n *   'search',\n *   baseParams,\n *   specializedParams,\n *   moreSpecializedParams\n * );\n * ```\n *\n * @param {string} feature The name of the feature\n * @throws {Error} If `feature` isn't one that shows Wikidata descriptions. See the\n *  `wgMFDisplayWikibaseDescriptions` configuration variable for detail\n * @return {Object}\n */\nfunction extendSearchParams( feature ) {\n\tvar displayWikibaseDescriptions = mw.config.get( 'wgMFDisplayWikibaseDescriptions' ) || {\n\t\t\t// Fail safe for when config is not available e.g. storybook\n\t\t\t// These must be defined, as these are all the features that this can be used on.\n\t\t\t// If not defined, all these features will see their API calls broken\n\t\t\tsearch: true,\n\t\t\twatchlist: true,\n\t\t\ttagline: false\n\t\t},\n\t\tscriptPath = mw.config.get( 'wgMFScriptPath' ),\n\t\targs,\n\t\tresult;\n\n\tif ( !Object.prototype.hasOwnProperty.call( displayWikibaseDescriptions, feature ) ) {\n\t\tthrow new Error( '\"' + feature + '\" isn\\'t a feature that shows Wikibase descriptions.' );\n\t}\n\n\t// Construct the arguments for a call to `util.extend`\n\t// such that if it were hand-written, then it\n\t// would look like the following:\n\t//\n\t// ```\n\t// var result = util.extend( {\n\t//   prop: []\n\t// }, params, /* ..., */ mw.config.get( 'wgMFSearchAPIParams' ) );\n\t// ```\n\targs = Array.prototype.slice.call( arguments, 1 );\n\targs.unshift( {\n\t\tprop: []\n\t} );\n\targs.push( mw.config.get( 'wgMFSearchAPIParams' ) );\n\n\tresult = util.extend.apply( {}, args );\n\tresult.prop = result.prop.concat( mw.config.get( 'wgMFQueryPropModules' ) );\n\n\tif ( displayWikibaseDescriptions[feature] ) {\n\t\tif ( result.prop.indexOf( 'description' ) === -1 ) {\n\t\t\tresult.prop.push( 'description' );\n\t\t}\n\t}\n\n\tif ( scriptPath ) {\n\t\t// A foreign api is being accessed! Enable anonymous CORS queries!\n\t\tresult.origin = '*';\n\t}\n\treturn actionParams( result );\n}\nmodule.exports = extendSearchParams;\n","var util = require( './util' ),\n\tButton = require( './Button' ),\n\ticons = require( './icons' );\n\n/**\n * Creates a header\n *\n * @param {string|View} headingOrView (HTML allowed)\n * @param {View[]} headerActions\n * @param {View} [headerCancel] defaults to cancel button\n * @param {string} [additionalClassNames] (should be escaped)\n * @return {Element}\n */\nfunction makeHeader( headingOrView, headerActions, headerCancel, additionalClassNames ) {\n\tconst heading = typeof headingOrView === 'string' ? headingOrView : undefined,\n\t\ttemplateData = {\n\t\t\thasActions: headerActions && headerActions.length,\n\t\t\tisHidden: false,\n\t\t\theading\n\t\t},\n\t\thtml = util.template( `\n<div class=\"overlay-header header ${additionalClassNames || ''} hideable\">\n\t<ul class=\"header-cancel\">\n\t\t<li></li>\n\t</ul>\n\t{{{heading}}}\n\t{{#hasActions}}\n\t<div class=\"header-action\"></div>\n\t{{/hasActions}}\n</div>\n\t\t` ).render( templateData );\n\theaderCancel = headerCancel || icons.cancel();\n\tconst $el = util.parseHTML( html );\n\t// Truncate any text inside in the overlay header.\n\t$el.find( 'h2 span' ).addClass( 'truncated-text' );\n\t$el.find( '.header-cancel li' ).append(\n\t\theaderCancel.$el\n\t);\n\tif ( heading === undefined ) {\n\t\theadingOrView.$el.insertAfter( $el.find( '.header-cancel' ) );\n\t}\n\tif ( headerActions && headerActions.length ) {\n\t\t$el.find( '.header-action' ).append(\n\t\t\theaderActions.map( function ( component ) {\n\t\t\t\treturn component.$el;\n\t\t\t} )\n\t\t);\n\t}\n\treturn $el[0];\n}\n\n/**\n * Creates a header with a h2 heading\n *\n * @param {string} heading (HTML allowed)\n * @param {View[]} headerActions\n * @param {View} [headerCancel] defaults to cancel button\n * @param {string} [additionalClassNames] (should be escaped)\n * @return {Element}\n */\nfunction header( heading, headerActions, headerCancel, additionalClassNames ) {\n\theading = `<div class=\"overlay-title\"><h2>${heading}</h2></div>`;\n\treturn makeHeader( heading, headerActions, headerCancel, additionalClassNames );\n}\n\n/**\n * Creates a header with a form\n *\n * @param {string|View} formHTMLOrView of the header\n * @param {View[]} headerActions\n * @param {View} [headerCancel] defaults to cancel button\n * @param {string} [additionalClassNames] (should be escaped)\n * @return {Element}\n */\nfunction formHeader( formHTMLOrView, headerActions, headerCancel, additionalClassNames ) {\n\treturn makeHeader( formHTMLOrView, headerActions, headerCancel, additionalClassNames );\n}\n\n/**\n * Creates a header with a form\n *\n * @param {string} heading of the header\n * @param {string} additionalClassNames of the header\n * @return {Element}\n */\nfunction saveHeader( heading, additionalClassNames ) {\n\treturn header(\n\t\theading,\n\t\t[\n\t\t\tnew Button( {\n\t\t\t\ttagName: 'button',\n\t\t\t\tadditionalClassNames: 'save submit',\n\t\t\t\tdisabled: true,\n\t\t\t\tlabel: util.saveButtonMessage()\n\t\t\t} )\n\t\t],\n\t\ticons.back(),\n\t\tadditionalClassNames\n\t);\n}\n/**\n * Creates a header with a form\n *\n * @param {string} heading of the header\n * @param {string} additionalClassNames of the header\n * @return {Element}\n */\nfunction savingHeader( heading ) {\n\treturn header(\n\t\theading,\n\t\t[\n\t\t\ticons.spinner( {\n\t\t\t\tadditionalClassNames: 'savespinner loading'\n\t\t\t} )\n\t\t],\n\t\ticons.cancel(),\n\t\t'saving-header hidden'\n\t);\n}\n\nmodule.exports = {\n\tsavingHeader: savingHeader,\n\tsaveHeader: saveHeader,\n\tformHeader: formHeader,\n\theader: header\n};\n","var\n\tCANCEL_GLYPH = 'close',\n\tIcon = require( './Icon' ),\n\tutil = require( './util' );\n\n/**\n * A set of shared icons.\n *\n * Factory methods are used to keep separate features that use the same icons\n * from accidentally manipulating one another's DOM when calling methods like\n * `remove`.\n *\n * @class icons\n * @singleton\n * @uses Icon\n */\nmodule.exports = {\n\tCANCEL_GLYPH: CANCEL_GLYPH,\n\t// Exported to support testing and stubbing\n\tIcon: Icon,\n\t/**\n\t * Gets a back icon\n\t *\n\t * The icon should be used to inform the user that the front-end is\n\t * communicating with the back-end.\n\t *\n\t * @memberof icons\n\t * @instance\n\t * @return {Icon}\n\t */\n\tback: function () {\n\t\treturn new Icon( {\n\t\t\ttagName: 'button',\n\t\t\tname: 'previous-base20',\n\t\t\tadditionalClassNames: 'back',\n\t\t\tlabel: mw.msg( 'mobile-frontend-overlay-close' )\n\t\t} );\n\t},\n\t/**\n\t * Gets a cancel icon\n\t *\n\t * The icon should be used to inform the user that the front-end is\n\t * communicating with the back-end.\n\t *\n\t * @memberof icons\n\t * @instance\n\t * @param {string} [variant] defaults to `base20`.\n\t * @param {Object} [props] to extend\n\t * @return {Icon}\n\t */\n\tcancel: function ( variant, props = {} ) {\n\t\tvar glyph = variant ? `${CANCEL_GLYPH}-${variant}` : `${CANCEL_GLYPH}-base20`;\n\t\tprops.additionalClassNames = props.additionalClassNames || '';\n\t\tprops.additionalClassNames += ' cancel';\n\n\t\treturn new this.Icon( util.extend( {\n\t\t\ttagName: 'button',\n\t\t\tname: glyph,\n\t\t\tlabel: mw.msg( 'mobile-frontend-overlay-close' )\n\t\t}, props ) );\n\t},\n\t/**\n\t * Gets a spinner icon.\n\t *\n\t * The icon should be used to inform the user that the front-end is\n\t * communicating with the back-end.\n\t *\n\t * @memberof icons\n\t * @instance\n\t * @param {Object} [props] See `Icon` for more details\n\t * @return {Icon}\n\t */\n\tspinner: function ( props = {} ) {\n\t\tif ( props.additionalClassNames === undefined ) {\n\t\t\tprops.additionalClassNames = 'spinner loading';\n\t\t}\n\n\t\treturn new this.Icon( util.extend( {\n\t\t\tname: 'spinner',\n\t\t\tlabel: mw.msg( 'mobile-frontend-loading-message' )\n\t\t}, props ) );\n\t},\n\t/**\n\t * Gets a failure (error) icon\n\t *\n\t * @memberof icons\n\t * @instance\n\t * @return {Icon}\n\t */\n\terror: function () {\n\t\treturn new Icon( {\n\t\t\tname: 'alert-invert',\n\t\t\tadditionalClassNames: 'load-fail-msg-icon'\n\t\t} );\n\t},\n\t/**\n\t * Gets a non-filled watch star icon.\n\t *\n\t * @memberof icons\n\t * @instance\n\t * @param {Object} props\n\t * @return {Icon}\n\t */\n\twatchIcon: function ( props = {} ) {\n\t\tprops.additionalClassNames = props.additionalClassNames || '';\n\t\tprops.additionalClassNames += ' watch-this-article';\n\n\t\treturn new this.Icon( util.extend( {\n\t\t\tname: 'star-base20',\n\t\t\tglyphPrefix: 'wikimedia'\n\t\t}, props ) );\n\t},\n\t/**\n\t * Gets a filled watch star icon.\n\t *\n\t * @memberof icons\n\t * @instance\n\t * @param {Object} props\n\t * @return {Icon}\n\t */\n\twatchedIcon: function ( props = {} ) {\n\t\tprops.additionalClassNames = props.additionalClassNames || '';\n\t\tprops.additionalClassNames += ' watch-this-article watched';\n\n\t\treturn new this.Icon( util.extend( {\n\t\t\tname: 'unStar-progressive',\n\t\t\tglyphPrefix: 'wikimedia'\n\t\t}, props ) );\n\t}\n};\n","var\n\tutil = require( '../util' ),\n\tplaceholderClass = 'lazy-image-placeholder';\n\n/**\n * @param {HTMLElement} root\n * @return {HTMLElement[]}\n */\nfunction queryPlaceholders( root ) {\n\treturn Array.prototype.slice.call(\n\t\troot.getElementsByClassName( placeholderClass )\n\t);\n}\n\n/**\n * Load an image on demand\n *\n * @param {HTMLElement[]} placeholders a list of images that have not been loaded.\n * @return {jQuery.Deferred}\n */\nfunction loadImages( placeholders ) {\n\treturn util.Promise.all(\n\t\tplaceholders.map( function ( placeholder ) {\n\t\t\treturn module.exports.loadImage( placeholder ).promise;\n\t\t} )\n\t);\n}\n\n/**\n * Load an image on demand\n *\n * @param {HTMLElement} placeholder\n * @return {{promise: jQuery.Deferred<'load'|'error'>, image: HTMLImageElement}}\n */\nfunction loadImage( placeholder ) {\n\tvar\n\t\tdeferred = util.Deferred(),\n\t\t// data-width and height are attributes and do not specify dimension.\n\t\twidth = placeholder.dataset.width || '0',\n\t\theight = placeholder.dataset.height || '0',\n\t\timage = new Image( parseInt( width, 10 ), parseInt( height, 10 ) );\n\n\t// eslint-disable-next-line mediawiki/class-doc\n\timage.className = placeholder.dataset.class || '';\n\timage.alt = placeholder.dataset.alt || '';\n\timage.useMap = placeholder.dataset.usemap;\n\timage.style.cssText = placeholder.style.cssText || '';\n\n\t// When the image has loaded\n\timage.addEventListener( 'load', function () {\n\t\t// Swap the HTML inside the placeholder (to keep the layout and\n\t\t// dimensions the same and not trigger layouts\n\t\timage.classList.add( 'image-lazy-loaded' );\n\t\tif ( placeholder.parentNode ) {\n\t\t\tplaceholder.parentNode.replaceChild( image, placeholder );\n\t\t}\n\t\tdeferred.resolve( 'load' );\n\t}, { once: true } );\n\timage.addEventListener( 'error', function () {\n\t\t// Swap the HTML and let the browser decide what to do with the broken image.\n\t\tif ( placeholder.parentNode ) {\n\t\t\tplaceholder.parentNode.replaceChild( image, placeholder );\n\t\t}\n\t\t// Never reject. Quietly resolve so that Promise.all() awaits for all Deferreds to complete.\n\t\t// Reevaluate using Deferred.reject in T136693.\n\t\tdeferred.resolve( 'error' );\n\t}, { once: true } );\n\n\t// Trigger image download after binding the load handler\n\timage.src = placeholder.dataset.src || '';\n\timage.srcset = placeholder.dataset.srcset || '';\n\n\treturn {\n\t\tpromise: deferred,\n\t\timage: image\n\t};\n}\n\nmodule.exports = {\n\tplaceholderClass,\n\tqueryPlaceholders: queryPlaceholders,\n\tloadImages: loadImages,\n\tloadImage: loadImage,\n\ttest: {\n\t\tplaceholderClass: placeholderClass\n\t}\n};\n","/**\n * Extends a class with new methods and member properties.\n *\n * @param {Function} Child function\n * @param {Object|Function} ParentOrPrototype class to inherit from\n *  OR if no inheriting class a prototype to extend the class with\n * @param {Object} [prototype]\n */\nfunction mfExtend( Child, ParentOrPrototype, prototype ) {\n\tvar key;\n\tif ( prototype ) {\n\t\tOO.inheritClass( Child, ParentOrPrototype );\n\t} else {\n\t\tOO.initClass( Child );\n\t\tprototype = ParentOrPrototype;\n\t}\n\tfor ( key in prototype ) {\n\t\tChild.prototype[key] = prototype[key];\n\t}\n}\n\nmodule.exports = mfExtend;\n","/**\n * Class for managing modules\n *\n * A module in this context is essentially a Javascript class (not to be confused with\n * ResourceLoader modules).\n *\n * @class ModuleLoader\n */\nfunction ModuleLoader() {\n\t/**\n\t * @property {Object} register of defined modules\n\t * @private\n\t */\n\tthis._register = {};\n}\n\nModuleLoader.prototype = {\n\t/**\n\t * Require (import) a module previously defined using define().\n\t * Searches core module registry using mw.loader.require before consulting\n\t * its own local registry. This method is deprecated, please do not use.\n\t *\n\t * @memberof ModuleLoader\n\t * @instance\n\t * @param {string} id Required module id.\n\t * @return {Object} Required module, can be any JavaScript object.\n\t */\n\trequire: function ( id ) {\n\t\tvar module, args,\n\t\t\tregistry = this._register;\n\n\t\t/**\n\t\t * @return {Object} Module\n\t\t */\n\t\tfunction localRequire() {\n\t\t\tif ( !Object.hasOwnProperty.call( registry, id ) ) {\n\t\t\t\tthrow new Error( 'MobileFrontend Module not found: ' + id );\n\t\t\t}\n\t\t\treturn registry[ id ];\n\t\t}\n\t\targs = id.split( '/' );\n\t\ttry {\n\t\t\tmodule = mw.loader.require( args[0] );\n\t\t\tif ( module[ args[1] ] ) {\n\t\t\t\treturn module[ args[1] ];\n\t\t\t} else {\n\t\t\t\treturn localRequire();\n\t\t\t}\n\t\t} catch ( e ) {\n\t\t\treturn localRequire();\n\t\t}\n\t},\n\n\t/**\n\t * Define a module which can be later required (imported) using require().\n\t *\n\t * @memberof ModuleLoader\n\t * @instance\n\t * @param {string} id Defined module id.\n\t * @param {Object} obj Defined module body, can be any JavaScript object.\n\t * @return {Object}\n\t */\n\tdefine: function ( id, obj ) {\n\t\tvar self = this;\n\n\t\tif ( Object.hasOwnProperty.call( this._register, id ) ) {\n\t\t\tthrow new Error( 'Module already exists: ' + id );\n\t\t}\n\t\tthis._register[ id ] = obj;\n\t\t// return an object of additionally functions to do with the registered module\n\t\treturn {\n\t\t\t/**\n\t\t\t * @see ModuleLoader#deprecate\n\t\t\t * @param {string} deprecatedId Defined module id, which is deprecated.\n\t\t\t * @ignore\n\t\t\t */\n\t\t\tdeprecate: function ( deprecatedId ) {\n\t\t\t\tself.deprecate( deprecatedId, obj, id );\n\t\t\t}\n\t\t};\n\t},\n\n\t/**\n\t * Deprecate a module and give an replacement (if there is any).\n\t *\n\t * @memberof ModuleLoader\n\t * @instance\n\t * @param {string} id Defined module id, which is deprecated.\n\t * @param {Object} obj Defined module body, can be any JavaScript object.\n\t * @param {string} [replacement] Give an optional replacement for this module (which\n\t * needs to be already defined!)\n\t */\n\tdeprecate: function ( id, obj, replacement ) {\n\t\tvar msg;\n\t\tif ( replacement ) {\n\t\t\t// add an alternative for this module, if any given\n\t\t\tmsg = 'Use ' + replacement + ' instead.';\n\t\t}\n\t\t// register it as a deprecated one\n\t\tmw.log.deprecate( this._register, id, obj, msg );\n\t}\n};\n\nmodule.exports = ModuleLoader;\n","var ModuleLoader = require( './moduleLoader' );\n\nmodule.exports = new ModuleLoader();\n","const Page = require( '../Page' );\nconst util = require( '../util' );\n\n/**\n * Create a Page object from an API response.\n *\n * @memberof Page\n * @param {Object} resp as representing a page in the API\n * @return {Page}\n */\nfunction parse( resp ) {\n\tvar revision, displayTitle,\n\t\tthumb = resp.thumbnail,\n\t\tpageprops = resp.pageprops || {\n\t\t\tdisplaytitle: mw.html.escape( resp.title )\n\t\t},\n\t\tterms = resp.terms || resp.entityterms;\n\n\tif ( pageprops || terms ) {\n\t\t// The label is either the display title or the label pageprop\n\t\t// (the latter used by Wikidata)\n\t\t// Long term we want to consolidate these.\n\t\t// Note that pageprops.displaytitle is HTML, while\n\t\t// terms.label[0] is plain text.\n\t\tdisplayTitle = terms && terms.label ?\n\t\t\tmw.html.escape( terms.label[0] ) : pageprops.displaytitle;\n\t}\n\t// Add Wikidata descriptions if available (T101719)\n\tresp.wikidataDescription = resp.description || undefined;\n\n\tif ( thumb ) {\n\t\tresp.thumbnail.isLandscape = thumb.width > thumb.height;\n\t}\n\n\t// page may or may not exist.\n\tif ( resp.revisions && resp.revisions[0] ) {\n\t\trevision = resp.revisions[0];\n\t\tresp.lastModified = new Date( revision.timestamp );\n\t}\n\n\treturn new Page(\n\t\tutil.extend( resp, {\n\t\t\tid: resp.pageid,\n\t\t\tisMissing: !!resp.missing,\n\t\t\turl: mw.util.getUrl( resp.title ),\n\t\t\tdisplayTitle: displayTitle // this is HTML!\n\t\t} )\n\t);\n}\n\nmodule.exports = { parse };\n","/**\n * @typedef {Object} PromoCampaign\n * @property {Function} showIfEligible\n * @property {Function} makeActionIneligible\n * @property {Function} makeAllActionsIneligible\n * @property {Function} isCampaignActive\n */\n\n/**\n * Creates a campaign that makes showing promo drawers, modals, etc that should\n * only be shown once (using localStorage) per action (when page loads, when\n * user clicks on a link, etc) easier. The campaign executes a given callback\n * (e.g. showing a drawer or modal) for a specific action only when it is\n * eligible. A campaign can have multiple arbitrary actions via the supplied\n * `actions` object. An action is either 'eligible' or 'ineligible' at any given\n * time. If `ineligible`, the `showIfEligible` will not execute the `onShow`\n * callback.\n *\n * @param {Function} onShow A callback intended to show something related to the\n * campaign (drawer, modal, etc) when executed. The callback will only execute\n * after the client calls `showIfEligible` and only if the passed in action is\n * 'eligible'.\n * @param {Object} actions Object of arbitrary actions that are intended to be\n * either \"eligible\" or \"ineligible\" at any given time (onPageLoad,\n * onHistoryLinkClick). The `onShow` callback will only be executed when an\n * action is 'eligible'. For each action, the key and value should be the same\n * (e.g. \"onLoad\":\"onLoad\") to mimic an enum. The client is responsible for\n * notifying when each action becomes \"ineligible\" by calling the\n * `makeActionIneligible` method. All actions can be marked as ineligible by\n * calling the `makeAllActionsIneligible` method.\n * @param {string} campaignName Name of campaign. This is only used to form part\n * of the localStorage key for each action.\n * @param {boolean} campaignActive Is campaign active\n * @param {boolean} userEligible Is current user eligible\n * @param {mw.storage} mwStorage Used to mark actions as ineligible\n * into localStorage\n * @return {PromoCampaign}\n */\nfunction createPromoCampaign(\n\tonShow,\n\tactions,\n\tcampaignName,\n\tcampaignActive,\n\tuserEligible,\n\tmwStorage\n) {\n\t// This object maps actions to localStorage keys\n\tconst ACTIONS_TO_STORAGE_KEYS = {};\n\tfor ( const key in actions ) {\n\t\tconst a = actions[ key ];\n\t\tACTIONS_TO_STORAGE_KEYS[a] = `mobile-frontend-${campaignName}-ineligible-${a}`;\n\t}\n\n\t/**\n\t * @return {boolean}\n\t */\n\tfunction isCampaignActive() {\n\t\treturn campaignActive;\n\t}\n\n\t/**\n\t * @param {string} action\n\t * @throws {Error} Throws an error if action is not valid.\n\t */\n\tfunction validateAction( action ) {\n\t\tif ( !( action in actions ) ) {\n\t\t\tthrow new Error(\n\t\t\t\t`Action '${action}' not found in 'actions' object. Please add this to\n\t\t\t\tthe object when creating a campaign with promoCampaign.js if you believe\n\t\t\t\tthis is a valid action.`\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * @param {string} action Will check the eligibility of this action. This\n\t * should be a value in the actions object.\n\t * @throws {Error} Throws an error if action is not valid.\n\t * @return {boolean}\n\t */\n\tfunction isActionEligible( action ) {\n\t\tvalidateAction( action );\n\n\t\treturn isCampaignActive() &&\n\t\t\tuserEligible &&\n\t\t\tmwStorage.get( ACTIONS_TO_STORAGE_KEYS[action] ) === null;\n\t}\n\n\treturn {\n\t\t/**\n\t\t * @param {string} action Should be one of the values in the\n\t\t * actions param\n\t\t * @param {...*} [args] Args to pass to the onShow callback\n\t\t * @throws {Error} Throws an error if action is not valid.\n\t\t * @return {Drawer|null} Returns Drawer if drawer is eligible to be shown and\n\t\t * null if not.\n\t\t */\n\t\tshowIfEligible: function ( action, ...args ) {\n\t\t\tif ( !isActionEligible( action ) ) {\n\t\t\t\t// If not eligible, there is no sense in continuing.\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn onShow( action, ...args );\n\t\t},\n\t\t/**\n\t\t * @param {string} action\n\t\t * @throws {Error} Throws an error if action is not valid.\n\t\t * @return {boolean} Whether the save operation was successful\n\t\t */\n\t\tmakeActionIneligible: function ( action ) {\n\t\t\tvalidateAction( action );\n\n\t\t\t// The value here actually doesn't matter. The only thing that matters is\n\t\t\t// if this key exists in localStorage.\n\t\t\treturn mwStorage.set( ACTIONS_TO_STORAGE_KEYS[action], '~' );\n\t\t},\n\t\tmakeAllActionsIneligible: function () {\n\t\t\tvar key, action;\n\t\t\tfor ( key in actions ) {\n\t\t\t\taction = actions[key];\n\t\t\t\tthis.makeActionIneligible( action );\n\t\t\t}\n\t\t},\n\t\tisCampaignActive: isCampaignActive\n\t};\n}\n\nmodule.exports = createPromoCampaign;\n","var\n\tstorageKey = 'mobileFrontend/toast';\n\n/**\n * Show the previously saved toast data and delete it from storage\n *\n * @memberof Toast\n * @instance\n * @private\n */\nfunction showPending() {\n\tvar data = mw.storage.get( storageKey );\n\tif ( data ) {\n\t\tdata = JSON.parse( data );\n\t\tmw.notify( data.content, data.options );\n\t\tmw.storage.remove( storageKey );\n\t}\n}\n\nmw.requestIdleCallback( showPending );\n\n/**\n * Save the toast data in storage so that we can show it on page reload.\n * Also check whether there is a pending message that's not shown yet.\n * If yes, output a warning message and discard this message.\n * This is to ensure that the page needs to be reloaded before adding\n * a new message for showing later.\n *\n * @memberof Toast\n * @instance\n * @param {string} content Content to be placed in element\n * @param {Object|string} [options]\n *  If a string (deprecated) CSS class to add to the element\n *  If an object, more options for the notification see mw.notification.show.\n *  For backwards compatibility reasons if a string is given it will be\n *  treated as options.type\n */\nfunction showOnPageReload( content, options ) {\n\tif ( mw.storage.get( storageKey ) ) {\n\t\tmw.log.warn(\n\t\t\t'A pending toast message already exits. ' +\n\t\t\t\t'The page should have been reloaded by now.'\n\t\t);\n\t\treturn;\n\t}\n\tmw.storage.set( storageKey, JSON.stringify( {\n\t\tcontent: content,\n\t\toptions: options\n\t} ) );\n}\n\nmodule.exports = { showOnPageReload: showOnPageReload };\n","/* global $ */\n\n/**\n * Utility library\n *\n * @class util\n * @singleton\n */\nmodule.exports = {\n\t/**\n\t * Obtains the correct label for the save button which is project specific. It's either\n\t * \"save\" or \"publish\"\n\t *\n\t * @return {string}\n\t */\n\tsaveButtonMessage: function () {\n\t\treturn mw.config.get( 'wgEditSubmitButtonLabelPublish' ) ?\n\t\t\tmw.msg( 'mobile-frontend-editor-publish' ) : mw.msg( 'mobile-frontend-editor-save' );\n\t},\n\t/**\n\t * Wrapper class for Promises\n\t *\n\t * @memberof util\n\t * @instance\n\t */\n\tPromise: {\n\t\t/**\n\t\t * Wrapper class for the $.when that is compatible with Promise.all\n\t\t *\n\t\t * @memberof util\n\t\t * @param {jQuery.Promise[]} promises\n\t\t * @instance\n\t\t * @return {jQuery.Promise}\n\t\t */\n\t\tall: function ( promises ) {\n\t\t\treturn $.when.apply( $, promises );\n\t\t}\n\t},\n\t/**\n\t * Escape a string for use as a css selector\n\t *\n\t * @memberof util\n\t * @instance\n\t * @param {string} selector\n\t * @return {string}\n\t */\n\tescapeSelector: function ( selector ) {\n\t\treturn $.escapeSelector( selector );\n\t},\n\t/**\n\t * Run method when document is ready.\n\t *\n\t * @memberof util\n\t * @instance\n\t * @param {Function} fn\n\t * @return {jQuery.Object}\n\t */\n\tdocReady: function ( fn ) {\n\t\treturn $( fn );\n\t},\n\t/**\n\t * Wrapper class for the Deferred method\n\t *\n\t * @memberof util\n\t * @instance\n\t * @return {jQuery.Deferred}\n\t */\n\tDeferred: function () {\n\t\treturn $.Deferred();\n\t},\n\t/**\n\t * Adds a class to the document\n\t *\n\t * @memberof util\n\t * @instance\n\t * @return {jQuery.Object} element representing the documentElement\n\t */\n\tgetDocument: function () {\n\t\treturn $( document.documentElement );\n\t},\n\t/**\n\t * Get the window object\n\t *\n\t * @memberof util\n\t * @instance\n\t * @return {jQuery.Object}\n\t */\n\tgetWindow: function () {\n\t\treturn $( window );\n\t},\n\t/**\n\t * Given some html, create new element(s).\n\t * Unlike jQuery.parseHTML this will return a jQuery object\n\t * not an array.\n\t *\n\t * @memberof util\n\t * @instance\n\t * @param {string} html\n\t * @param {Element} [ctx] Document element to serve as the context\n\t *  in which the HTML fragment will be created\n\t * @return {jQuery.Object}\n\t */\n\tparseHTML: function ( html, ctx ) {\n\t\tctx = ctx || document;\n\t\treturn $( $.parseHTML( html, ctx ) );\n\t},\n\t/**\n\t * Wrapper for jQuery.extend method. In future this can be bound to Object.assign\n\t * when support allows.\n\t *\n\t * Warning: if only one argument is supplied to util.extend(), this means the target argument\n\t * was omitted. In this case, the jQuery object itself is assumed to be the target.\n\t *\n\t * @memberof util\n\t * @instance\n\t * @return {Object}\n\t */\n\textend: function () {\n\t\treturn $.extend.apply( $, arguments );\n\t},\n\n\t/**\n\t * @typedef {Object} Template\n\t * @property {Function} render returning string of rendered HTML\n\t *\n\t * Centralised place for template rendering.\n\t *\n\t * T223927: We depend on the global Mustache brought in by the\n\t * mediawiki.template.mustache module but do not delegate to\n\t * mediawiki.template.mustache.js because its render method returns a jQuery\n\t * object, but our MobileFrontend code depends on .render returning a string.\n\t *\n\t * @param {string} source code of template that is Mustache compatible.\n\t * @return {Template}\n\t */\n\ttemplate: function ( source ) {\n\t\treturn {\n\t\t\t/**\n\t\t\t * @ignore\n\t\t\t * @return {string} The raw source code of the template\n\t\t\t */\n\t\t\tgetSource: function () {\n\t\t\t\treturn source;\n\t\t\t},\n\t\t\t/**\n\t\t\t * @ignore\n\t\t\t * @param {Object} data Data to render\n\t\t\t * @param {Object} partials Map partial names to Mustache template objects\n\t\t\t * @return {string} Rendered HTML\n\t\t\t */\n\t\t\trender: function ( data, partials ) {\n\t\t\t\tconst partialSource = {};\n\t\t\t\t// Map MobileFrontend templates to partial strings\n\t\t\t\tObject.keys( partials || {} ).forEach( ( key ) => {\n\t\t\t\t\tpartialSource[ key ] = partials[ key ].getSource();\n\t\t\t\t} );\n\n\t\t\t\t// Use global Mustache which is loaded by mediawiki.template.mustache (a\n\t\t\t\t// dependency of the mobile.startup module)\n\t\t\t\t// eslint-disable-next-line no-undef\n\t\t\t\treturn Mustache.render(\n\t\t\t\t\tsource.trim(),\n\t\t\t\t\tdata,\n\t\t\t\t\tpartialSource\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\t}\n};\n","var util = require( '../util' ),\n\tactionParams = require( '../actionParams' );\n\n/**\n * @typedef {string|number} PageID Page ID. 0 / \"0\" is a special no-ID value.\n * {@link https://www.mediawiki.org/wiki/Manual:Page_table#page_id Page ID}\n *\n * @typedef {string} PageTitle Canonical page title.\n * {@link https://www.mediawiki.org/wiki/Manual:Title.php#Canonical_forms Canonical forms}\n *\n * @typedef {boolean} WatchStatus Page watch status; true if watched, false if\n *                                unwatched.\n * {@link https://www.mediawiki.org/wiki/API:Info API:Info} (see inprop.watched)\n * {@link https://www.mediawiki.org/wiki/API:Watch API:Watch} (see unwatch)\n *\n * @typedef {Object.<PageTitle, WatchStatus>} WatchStatusMap\n */\n\n/**\n * API for retrieving and modifying page watch statuses. This module interacts\n * with two endpoints, API:Info for GETs and API:Watch and for POSTs.\n *\n * @class WatchstarGateway\n * @param {mw.Api} api\n */\nfunction WatchstarGateway( api ) {\n\tthis.api = api;\n}\n\nWatchstarGateway.prototype = {\n\t/**\n\t * Issues zero to two asynchronous HTTP requests for the watch status of\n\t * each page ID and title passed.\n\t *\n\t * Every watch entry has a title but not necessarily a page ID. Entries\n\t * without IDs are missing pages, i.e., pages that do not exist. These\n\t * entries are used to observe when a page with a given title is created.\n\t * Although it is convenient to use titles because they're always present,\n\t * IDs are preferred since they're far less likely to exceed the URL length\n\t * limit.\n\t *\n\t * No request is issued when no IDs and no titles are passed. Given that the\n\t * server state does not change between the two requests, overlapping title\n\t * and ID members will behave as expected but there is no reason to issue\n\t * such a request.\n\t *\n\t * @memberof WatchstarGateway\n\t * @instance\n\t * @param {PageID[]} ids\n\t * @param {PageTitle[]} titles\n\t * @return {jQuery.Deferred<WatchStatusMap>}\n\t */\n\tgetStatuses: function ( ids, titles ) {\n\t\t// Issue two requests and coalesce the results.\n\t\treturn util.Promise.all( [\n\t\t\tthis.getStatusesByID( ids ),\n\t\t\tthis.getStatusesByTitle( titles )\n\t\t] ).then( function () { return util.extend.apply( util, arguments ); } );\n\t},\n\n\t/**\n\t * @memberof WatchstarGateway\n\t * @instance\n\t * @param {PageID[]} ids\n\t * @return {jQuery.Deferred<WatchStatusMap>}\n\t */\n\tgetStatusesByID: function ( ids ) {\n\t\tvar self = this;\n\t\tif ( !ids.length ) {\n\t\t\treturn util.Deferred().resolve( {} );\n\t\t}\n\n\t\treturn this.api.get( {\n\t\t\tformatversion: 2,\n\t\t\taction: 'query',\n\t\t\tprop: 'info',\n\t\t\tinprop: 'watched',\n\t\t\tpageids: ids\n\t\t} ).then( function ( rsp ) {\n\t\t\treturn self._unmarshalGetResponse( rsp );\n\t\t} );\n\t},\n\n\t/**\n\t * @memberof WatchstarGateway\n\t * @instance\n\t * @param {PageTitle[]} titles\n\t * @return {jQuery.Deferred<WatchStatusMap>}\n\t */\n\tgetStatusesByTitle: function ( titles ) {\n\t\tvar self = this;\n\t\tif ( !titles.length ) {\n\t\t\treturn util.Deferred().resolve( {} );\n\t\t}\n\n\t\treturn this.api.get( actionParams( {\n\t\t\tprop: 'info',\n\t\t\tinprop: 'watched',\n\t\t\ttitles: titles\n\t\t} ) ).then( function ( rsp ) {\n\t\t\treturn self._unmarshalGetResponse( rsp );\n\t\t} );\n\t},\n\n\t/**\n\t * @memberof WatchstarGateway\n\t * @instance\n\t * @param {PageTitle[]} titles\n\t * @param {WatchStatus} watched\n\t * @return {jQuery.Deferred}\n\t */\n\tpostStatusesByTitle: function ( titles, watched ) {\n\t\tvar params = {\n\t\t\taction: 'watch',\n\t\t\ttitles: titles\n\t\t};\n\t\tif ( !watched ) {\n\t\t\tparams.unwatch = !watched;\n\t\t}\n\t\treturn this.api.postWithToken( 'watch', params );\n\t},\n\n\t/**\n\t * @memberof WatchstarGateway\n\t * @instance\n\t * @param {Object} rsp The API:Info response.\n\t * @return {jQuery.Deferred<WatchStatusMap>}\n\t * @see getStatusesByID\n\t * @see getStatusesByTitle\n\t */\n\t_unmarshalGetResponse: function ( rsp ) {\n\t\tvar pages = rsp && rsp.query && rsp.query.pages || [];\n\t\treturn pages.reduce( function ( statuses, page ) {\n\t\t\tstatuses[page.title] = page.watched;\n\t\t\treturn statuses;\n\t\t}, {} );\n\t}\n};\n\nmodule.exports = WatchstarGateway;\n","var PageList = require( '../PageList' ),\n\twatchstar = require( './watchstar' ),\n\tuser = mw.user,\n\tutil = require( '../util' ),\n\tPage = require( '../Page' ),\n\tmfExtend = require( '../mfExtend' ),\n\tWatchstarGateway = require( './WatchstarGateway' );\n\n/**\n * @typedef {Object.<PageTitle, PageID>} PageTitleToPageIDMap\n */\n\n/**\n * List of items page view\n *\n * @class WatchstarPageList\n * @uses Page\n * @uses WatchstarGateway\n * @uses Watchstar\n * @extends PageList\n *\n * @fires WatchstarPageList#unwatch\n * @fires WatchstarPageList#watch\n * @param {Object} options Configuration options\n */\nfunction WatchstarPageList( options ) {\n\tthis.wsGateway = new WatchstarGateway( options.api );\n\tPageList.apply( this, arguments );\n}\n\nmfExtend( WatchstarPageList, PageList, {\n\t/**\n\t * @memberof WatchstarPageList\n\t * @instance\n\t * @mixes PageList#defaults\n\t * @property {Object} defaults Default options hash.\n\t * @property {mw.Api} defaults.api\n\t */\n\n\tpostRender: function () {\n\t\tvar\n\t\t\tself = this,\n\t\t\t$items,\n\t\t\tpages,\n\t\t\tids = [],\n\t\t\ttitles = [];\n\n\t\tPageList.prototype.postRender.apply( this );\n\n\t\t$items = this.queryUnitializedItems();\n\t\tpages = this.parsePagesFromItems( $items );\n\n\t\tObject.keys( pages ).forEach( function ( title ) {\n\t\t\tvar id = pages[title];\n\t\t\t// Favor IDs since they're short and unlikely to exceed URL length\n\t\t\t// limits when batched.\n\t\t\tif ( id && id !== '0' ) {\n\t\t\t\t// ID is present and valid.\n\t\t\t\tids.push( id );\n\t\t\t} else {\n\t\t\t\t// Only titles are available for missing pages.\n\t\t\t\ttitles.push( title );\n\t\t\t}\n\t\t} );\n\n\t\treturn this.getPages( ids, titles ).then( function ( statuses ) {\n\t\t\tself.renderItems( $items, statuses );\n\t\t} );\n\t},\n\n\t/**\n\t * @param {jQuery.Element} $items\n\t * @param {WatchStatusMap} statuses\n\t * @return {void}\n\t */\n\tqueryUnitializedItems: function () {\n\t\treturn this.$el.find( 'li:not(.with-watchstar)' );\n\t},\n\n\t/**\n\t * Retrieve pages\n\t *\n\t * @memberof WatchstarPageList\n\t * @instance\n\t * @param {PageID[]} ids\n\t * @param {PageTitle[]} titles\n\t * @return {jQuery.Deferred<WatchStatusMap>}\n\t */\n\tgetPages: function ( ids, titles ) {\n\t\t// Rendering Watchstars for anonymous users is not useful. Short-circuit\n\t\t// the request.\n\t\tif ( user.isAnon() ) {\n\t\t\treturn util.Deferred().resolve( {} );\n\t\t}\n\n\t\treturn this.wsGateway.getStatuses( ids, titles );\n\t},\n\n\t/**\n\t * @param {jQuery.Element} $items\n\t * @return {PageTitleToPageIDMap}\n\t * @memberof WatchstarPageList\n\t * @instance\n\t */\n\tparsePagesFromItems: function ( $items ) {\n\t\tvar\n\t\t\tself = this,\n\t\t\tpages = {};\n\t\t$items.each( function ( _, item ) {\n\t\t\tvar $item = self.$el.find( item );\n\t\t\tpages[ $item.attr( 'title' ) ] = $item.data( 'id' );\n\t\t} );\n\t\treturn pages;\n\t},\n\n\t/**\n\t * @param {jQuery.Element} $items\n\t * @param {WatchStatusMap} statuses\n\t * @return {void}\n\t */\n\trenderItems: function ( $items, statuses ) {\n\t\tvar self = this;\n\n\t\t// Rendering Watchstars for anonymous users is not useful. Nothing to do.\n\t\tif ( user.isAnon() ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Create watch stars for each entry in list\n\t\t$items.each( function ( _, item ) {\n\t\t\tvar\n\t\t\t\t$item = self.$el.find( item ),\n\t\t\t\tpage = new Page( {\n\t\t\t\t\t// FIXME: Set sections so we don't hit the api (hacky)\n\t\t\t\t\tsections: [],\n\t\t\t\t\ttitle: $item.attr( 'title' ),\n\t\t\t\t\tid: $item.data( 'id' )\n\t\t\t\t} ),\n\t\t\t\twatched = statuses[ page.getTitle() ];\n\n\t\t\tself._appendWatchstar( $item, page, watched );\n\t\t\t$item.addClass( 'with-watchstar' );\n\t\t} );\n\t},\n\n\t/**\n\t * @param {jQuery.Object} $item\n\t * @param {Page} page\n\t * @param {WatchStatus} watched\n\t */\n\t_appendWatchstar: function ( $item, page, watched ) {\n\t\twatchstar( {\n\t\t\t// WatchstarPageList.getPages() already retrieved the status of\n\t\t\t// each page. Explicitly set the watch state so another request\n\t\t\t// will not be issued by the Watchstar.\n\t\t\tisWatched: watched,\n\t\t\tpage: page\n\t\t} ).appendTo( $item );\n\t}\n} );\n\nmodule.exports = WatchstarPageList;\n","var icons = require( '../icons' );\n\n/**\n * A clickable watchstar for logged in users.\n * Should not be used for anonymous users.\n *\n * @param {Object} options Configuration options\n * @param {Page} options.page\n * @param {boolean} options.isWatched is the article watched?\n * @return {Icon}\n */\nmodule.exports = function ( options ) {\n\tconst isWatched = options.isWatched,\n\t\t// FIXME: mw.loader.require is not a public function (T235198)\n\t\twatchstar = mw.loader.require( 'mediawiki.page.watch.ajax' ).watchstar,\n\t\taction = isWatched ? 'unwatch' : 'watch',\n\t\ticonProps = {\n\t\t\thref: mw.Title.newFromText( options.page.title ).getUrl( { action } )\n\t\t},\n\t\twatchIcon = icons.watchIcon( iconProps ),\n\t\twatchedIcon = icons.watchedIcon( iconProps ),\n\t\tWATCH_CLASS = watchIcon.$el.attr( 'class' ),\n\t\tWATCHED_CLASS = watchedIcon.$el.attr( 'class' ),\n\t\tactiveIcon = isWatched ? watchedIcon : watchIcon,\n\t\tcallback = ( $link, watched ) => {\n\t\t\t$link.attr( 'class', watched ?\n\t\t\t\tWATCHED_CLASS : WATCH_CLASS );\n\t\t};\n\n\twatchstar( activeIcon.$el, options.page.title, callback );\n\treturn activeIcon;\n};\n"],"sourceRoot":""}