{"version":3,"sources":["webpack://mfModules.[name]/./src/mobile.editor.overlay/parseSaveError.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/MessageBox.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/PageList.js","webpack://mfModules.[name]/./src/mobile.startup/Panel.js","webpack://mfModules.[name]/./src/mobile.startup/ScrollEndEventEmitter.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/View.js","webpack://mfModules.[name]/./src/mobile.startup/actionParams.js","webpack://mfModules.[name]/./src/mobile.startup/cache.js","webpack://mfModules.[name]/./src/mobile.startup/context.js","webpack://mfModules.[name]/./src/mobile.startup/eventBusSingleton.js","webpack://mfModules.[name]/./src/mobile.startup/extendSearchParams.js","webpack://mfModules.[name]/./src/mobile.startup/icons.js","webpack://mfModules.[name]/./src/mobile.startup/languageOverlay/getDeviceLanguage.js","webpack://mfModules.[name]/./src/mobile.startup/languageOverlay/languageOverlay.js","webpack://mfModules.[name]/./src/mobile.startup/lazyImages/lazyImageLoader.js","webpack://mfModules.[name]/./src/mobile.startup/lazyImages/lazyImageTransformer.js","webpack://mfModules.[name]/./src/mobile.startup/lazyReferencesLoader.js","webpack://mfModules.[name]/./src/mobile.startup/loadingOverlay.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/notifications/overlay.js","webpack://mfModules.[name]/./src/mobile.startup/promisedView.js","webpack://mfModules.[name]/./src/mobile.startup/references/ReferencesGateway.js","webpack://mfModules.[name]/./src/mobile.startup/references/ReferencesHtmlScraperGateway.js","webpack://mfModules.[name]/./src/mobile.startup/references/ReferencesMobileViewGateway.js","webpack://mfModules.[name]/./src/mobile.startup/rlModuleLoader.js","webpack://mfModules.[name]/./src/mobile.startup/search/SearchGateway.js","webpack://mfModules.[name]/./src/mobile.startup/talk/overlay.js","webpack://mfModules.[name]/./src/mobile.startup/time.js","webpack://mfModules.[name]/./src/mobile.startup/toast.js","webpack://mfModules.[name]/./src/mobile.startup/util.js","webpack://mfModules.[name]/./src/mobile.startup/watchstar/Watchstar.js","webpack://mfModules.[name]/./src/mobile.startup/watchstar/WatchstarGateway.js","webpack://mfModules.[name]/./src/mobile.startup/watchstar/WatchstarPageList.js"],"names":["module","exports","data","code","warning","type","details","error","edit","captcha","test","message","View","require","Anchor","apply","this","arguments","mfExtend","isTemplateMode","defaults","progressive","undefined","destructive","additionalClassNames","href","label","template","mw","get","browser","util","memoize","method","memoized","cache","cacheId","key","join","call","Object","prototype","hasOwnProperty","Date","now","toString","Math","random","Browser","ua","$container","userAgent","$el","isIos","version","ios","isWideScreen","val","parseInt","config","window","innerWidth","innerHeight","supportsAnimations","elemStyle","document","createElement","style","supportsProperty","property","toUpperCase","slice","supportsTouchEvents","supportsGeoLocation","navigator","getSingleton","$html","getDocument","Button","options","tagName","block","quiet","Drawer","CtaDrawer","extend","progressiveButton","msg","actionAnchor","redirectParams","params","redirectURL","returnto","signUpParams","push","templatePartials","button","anchor","preRender","queryParams","returnTo","getUrl","signupQueryParams","Panel","Icon","props","className","events","click","collapseIcon","name","icon","appendToElement","closeOnScroll","postRender","self","docReady","appendTo","parent","addClass","on","onShowDrawer","bind","onHideDrawer","stopPropagation","ev","setTimeout","$window","getWindow","one","hide","minHideDelay","removeClass","off","hasText","modifier","setRotationClass","rotation","_rotationClass","Error","glyphPrefix","isSmall","base","title","getClassName","attr","getGlyphClassName","toHtmlString","parseHTML","append","html","MessageBox","icons","Overlay","hasLoadError","click .cancel, .confirm, .initial-header .back","fullScreen","hideOnExitClick","header","headerActions","saveMsg","cancelButton","cancel","backButton","headerChrome","spinner","closeOnContentTap","showSpinner","$spinner","hideSpinner","hasActions","headerButtons","length","$overlayContent","find","map","component","onExitClick","exit","emit","EVENT_EXIT","preventDefault","onBeforeExit","show","scrollTop","pageYOffset","scrollTo","pageXOffset","detach","showHidden","make","view","overlay","overlayManager","OverlayManager","router","container","_checkRoute","entries","stack","hideCurrent","attachHideEvent","_onHideOverlay","back","_attachOverlay","parents","appendChild","_show","once","_hideOverlay","result","_processMatch","match","factoryResult","promise","then","current","keys","reduce","m","id","_matchRoute","path","entry","next","route","previous","getNext","factory","shift","unshift","add","getPath","replaceCurrent","body","loader","HTML","time","Section","Thumbnail","HEADING_SELECTOR","BLACKLISTED_THUMBNAIL_CLASS_SELECTORS","Page","titleObj","displayTitle","url","wikidataDescription","thumbnail","isMissing","isBorderBox","languageUrl","getDisplayTitle","width","isLandscape","height","namespaceNumber","protection","sections","isMainPage","source","escape","inNamespace","namespace","findSectionHeadingByIndex","sectionIndex","filter","eq","findChildInSectionLead","selector","$heading","$nextHeading","$lead","headingSelector","withNestedChildren","$matchingNodes","addBack","getLeadSectionElement","children","prevAll","hasClass","nextAll","nextUntil","isWikiText","isWatched","getRevisionId","revId","getTitle","getId","getNamespaceId","args","split","toLowerCase","replace","isTalkPage","ns","_sectionLookup","forEach","sectionData","section","getThumbnails","blacklistSelector","thumbs","_thumbs","not","each","$a","$lazyImage","valid","legacyMatch","RegExp","el","filename","decodeURIComponent","getSection","getSections","getRedLinks","newFromJSON","resp","revision","thumb","pageprops","displaytitle","terms","description","revisions","lastModified","getLastModifiedMessage","timestamp","getTime","user","pageid","missing","sectionTemplate","actionParams","transformSections","lastSection","existingSectionLevels","s","level","collapseLevel","min","line","subsections","assignToParent","listOfSections","child","text","render","PageGateway","api","Deferred","getPage","pageData","page","resolve","reject","endpoint","leadOnly","d","dataType","action","variant","redirect","prop","noheadings","sectionprop","resolveObj","mv","mobileview","lastmodified","lastmodifiedby","Array","isArray","lead","mainpage","historyUrl","lastModifiedTimestamp","languageCount","languagecount","hasVariants","hasvariants","lastModifiedUserName","lastModifiedUserGender","gender","invalidatePage","_getLanguageVariantsFromApiResponse","generalData","query","general","variantPath","variantarticlepath","variants","index","item","autonym","lang","getPageLanguages","language","meta","siprop","lllimit","titles","llprop","llinlanguagecode","languages","pages","langlinks","_getAPIResponseFromHTML","substr","$span","getSectionsFromHTML","PageList","renderPageImages","click .cancel","onCancel","isVisible","toggle","ScrollEndEventEmitter","eventBus","threshold","enable","OO","EventEmitter","mixinClass","EVENT_SCROLL_END","_bindScroll","_scrollHandler","_onScroll","_unbindScroll","enabled","scrollNearEnd","disable","scrollBottom","endPosition","offset","top","outerHeight","setElement","tag","hasReferences","lazyImageLoader","lazyImageTransformer","lazyReferencesLoader","Skin","mainMenu","log","warn","referencesGateway","getElementById","images","queryPlaceholders","newLazyImageTransformer","loadImages","loadReferences","getLicenseMsg","licenseMsg","mfLicense","licensePlural","convertNumber","plural","link","parse","siblings","getDescription","getFileName","delegateEventSplitter","idCounter","initialize","cid","prefix","uniqueId","compile","$","_postInitialize","undelegateEvents","skipTemplateRender","replaceWith","delegateEvents","delegate","eventName","listener","undelegate","$child","defaultParams","formatversion","otherParams","scriptPath","origin","MemoryCache","_cache","NoCache","set","value","getMode","feature","displayWikibaseDescriptions","concat","indexOf","CANCEL_GLYPH","glyph","watchIcon","watchedIcon","userLanguage","browserLanguage","systemLanguage","getDeviceLanguage","promisedView","loadLanguageSearcher","gateway","using","deviceLanguage","languageOverlay","pageGateway","heading","placeholderClass","root","getElementsByClassName","placeholders","Promise","all","placeholder","loadImage","deferred","dataset","image","Image","class","alt","cssText","addEventListener","classList","parentNode","replaceChild","src","srcset","imagePlaceholders","grep","$placeholder","viewport","isElementCloseToViewport","is","shouldLoadImage","getSectionId","hSelector","$parent","$content","expanded","isReferenceSection","prependTo","getReferencesLists","lastId","refListIndex","getReferencesList","refListElements","remove","loadImagesAndSetData","noHeader","Child","ParentOrPrototype","inheritClass","initClass","ModuleLoader","_register","registry","localRequire","e","define","obj","deprecate","deprecatedId","replacement","onCountChange","onNotificationListRendered","markAllReadButton","oouiPromise","ui","ButtonWidget","$element","markAllReadButtonView","escaped","footerAnchor","list","echo","newView","errorView","ReferencesGateway","getReference","ERROR_NOT_EXIST","ERROR_OTHER","ReferencesHtmlScraperGateway","getReferenceFromContainer","escapeSelector","referencesMobileViewGateway","ReferencesMobileViewGateway","cachedReferencesSections","$section","headingId","sectionId","Api","loadingOverlay","newLoadingOverlay","loadModule","delegateHide","showLoadingOverlay","hideOverlayIfNeeded","extendSearchParams","SearchGateway","searchCache","generator","searchNamespace","getApiData","redirects","pilimit","pithumbsize","tiny","_createSearchRegEx","str","_highlightSearchTerm","term","_getPage","pageInfo","displaytext","_processData","results","sort","a","b","search","xhr","request","isCached","abort","Boolean","isAnon","talkBoard","units","limits","timeAgo","timestampDelta","i","round","unit","getTimeAgoDelta","isNow","delta","ts","username","seconds","minutes","hours","days","months","years","getRegistrationMessage","isRecent","storageKey","Toast","requestIdleCallback","_showPending","notification","notify","notif","close","showOnPageReload","content","storage","JSON","stringify","promises","when","fn","documentElement","ctx","isNumeric","escapeHash","hash","isModifiedEvent","altKey","ctrlKey","metaKey","shiftKey","repeatEvent","proxy","event","WatchstarGateway","toast","Watchstar","funnel","ctaDrawerOptions","campaign","returntoquery","_super","_watched","tooltip","unwatchedClass","watchedClass","onStatusToggleAnon","drawer","onStatusToggleUser","checker","postWatched","stopInterval","clearInterval","setInterval","postStatusesByTitle","onStatusToggle","getStatuses","ids","getStatusesByID","getStatusesByTitle","inprop","pageids","rsp","_unmarshalGetResponse","watched","unwatch","postWithToken","statuses","WatchstarPageList","wsGateway","$items","queryUnitializedItems","parsePagesFromItems","getPages","renderItems","_","$item","_appendWatchstar","watchstar"],"mappings":"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","file":"mobile.common.js","sourcesContent":["/**\n * Turn an API response on save failure into a details object\n * @param {Object|null} data API response data\n * @param {string} [code] Text status message from API\n * @return {Object} An object with `type` and `details` properties.\n * `type` is a string describing the type of error, `details` can be any\n * object (usually error message).\n*/\nfunction parseSaveError( data, code ) {\n\tvar warning;\n\tif ( code ) {\n\t\tswitch ( code ) {\n\t\t\tcase 'readonly':\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'readonly',\n\t\t\t\t\tdetails: data.error\n\t\t\t\t};\n\t\t\tcase 'editconflict':\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'editconflict',\n\t\t\t\t\tdetails: data.error\n\t\t\t\t};\n\t\t\tdefault:\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'error',\n\t\t\t\t\tdetails: 'http'\n\t\t\t\t};\n\t\t}\n\t}\n\tif ( data ) {\n\t\tif ( data.error ) {\n\t\t\t// Edit API error\n\t\t\treturn {\n\t\t\t\ttype: 'error',\n\t\t\t\tdetails: data.error.code\n\t\t\t};\n\t\t} else if ( data.edit && data.edit.captcha ) {\n\t\t\t// CAPTCHAs\n\t\t\treturn {\n\t\t\t\ttype: 'captcha',\n\t\t\t\tdetails: data.edit.captcha\n\t\t\t};\n\t\t} else if ( data.edit && data.edit.code ) {\n\t\t\tcode = data.edit.code;\n\t\t\twarning = data.edit.warning;\n\n\t\t\t// FIXME: AbuseFilter should have more consistent API responses\n\t\t\tif ( /^abusefilter-warning/.test( code ) ) {\n\t\t\t\t// AbuseFilter warning\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'abusefilter',\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\ttype: 'warning',\n\t\t\t\t\t\tmessage: warning\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t} else if ( /^abusefilter-disallow/.test( code ) ) {\n\t\t\t\t// AbuseFilter disallow\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'abusefilter',\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\ttype: 'disallow',\n\t\t\t\t\t\tmessage: warning\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t} else if ( /^abusefilter/.test( code ) ) {\n\t\t\t\t// AbuseFilter other\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'abusefilter',\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\ttype: 'other',\n\t\t\t\t\t\tmessage: warning\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t\t// other errors\n\t\t\treturn {\n\t\t\t\ttype: 'error',\n\t\t\t\tdetails: code\n\t\t\t};\n\t\t}\n\t}\n\treturn {\n\t\ttype: 'error',\n\t\tdetails: 'unknown'\n\t};\n}\nmodule.exports = parseSaveError;\n","var\n\tView = require( './View' ),\n\tmfExtend = require( './mfExtend' );\n\n/**\n * A wrapper for creating an anchor.\n * @class Anchor\n * @extends View\n */\nfunction Anchor() {\n\tView.apply( this, arguments );\n}\n\nmfExtend( Anchor, View, {\n\t/**\n\t * @inheritdoc\n\t * @memberof Anchor\n\t * @instance\n\t */\n\tisTemplateMode: true,\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\tdefaults: {\n\t\tprogressive: undefined,\n\t\tdestructive: undefined,\n\t\tadditionalClassNames: '',\n\t\thref: undefined,\n\t\tlabel: undefined\n\t},\n\t/**\n\t * @inheritdoc\n\t * @memberof Anchor\n\t * @instance\n\t */\n\ttemplate: mw.template.get( 'mobile.startup', 'anchor.hogan' )\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 * @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 * @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 * @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 * @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 * @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 portrait and landscape mode to be consistent\n\t\treturn window.innerWidth >= val || window.innerHeight >= val;\n\t} ),\n\t/**\n\t * Checks browser support for CSS transforms, transitions\n\t * and CSS animation.\n\t * Currently assumes support for the latter 2 in the case of the\n\t * former.\n\t * See http://stackoverflow.com/a/12621264/365238\n\t * @memberof Browser\n\t * @instance\n\t * @return {boolean}\n\t */\n\tsupportsAnimations: memoize( function () {\n\t\tvar elemStyle = document.createElement( 'foo' ).style;\n\t\tfunction supportsProperty( property ) {\n\t\t\t// We only test \"webkit-\", because that's the only prefix needed for the relevant\n\t\t\t// properties (in supportsAnimations) and supported browsers. If usage is expanded,\n\t\t\t// other prefixes may need to be checked as well.\n\t\t\treturn property in elemStyle ||\n\t\t\t\t( 'webkit' + property[ 0 ].toUpperCase() + property.slice( 1 ) ) in elemStyle;\n\t\t}\n\t\treturn supportsProperty( 'animationName' ) &&\n\t\t\tsupportsProperty( 'transform' ) &&\n\t\t\tsupportsProperty( 'transition' );\n\t} ),\n\t/**\n\t * Whether touchstart and other touch events are supported by the current browser.\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\t/**\n\t * Detect if browser supports geolocation\n\t * @memberof Browser\n\t * @instance\n\t * @return {boolean}\n\t */\n\tsupportsGeoLocation: memoize( function () {\n\t\treturn 'geolocation' in window.navigator;\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\tView = require( './View' );\n\n/**\n * A wrapper for creating a button.\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 *   This option is deprecated. Please use `progressive`.\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 */\n\tdefaults: {\n\t\ttagName: 'a',\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: mw.template.get( 'mobile.startup', 'button.hogan' )\n} );\n\nmodule.exports = Button;\n","var\n\tmfExtend = require( './mfExtend' ),\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} [options.progressiveButton] template options for Button element for signing in\n * @prop {Object} [options.actionAnchor] template options for Anchor element for signing up\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 * @class CtaDrawer\n * @extends Drawer\n * @uses Button\n * @uses Icon\n * @uses Anchor\n * @param {Options} options\n */\nfunction CtaDrawer( options ) {\n\tDrawer.call( this,\n\t\tutil.extend( {}, Drawer.prototype.defaults, {\n\t\t\tprogressiveButton: new Button( {\n\t\t\t\tprogressive: true,\n\t\t\t\tlabel: mw.msg( 'mobile-frontend-watchlist-cta-button-login' )\n\t\t\t} ).options,\n\t\t\tactionAnchor: new Anchor( {\n\t\t\t\tprogressive: true,\n\t\t\t\tlabel: mw.msg( 'mobile-frontend-watchlist-cta-button-signup' )\n\t\t\t} ).options\n\t\t}, options )\n\t);\n}\n\nmfExtend( CtaDrawer, Drawer, {\n\t/**\n\t * @memberof CtaDrawer\n\t * @instance\n\t */\n\ttemplatePartials: util.extend( {}, Drawer.prototype.templatePartials, {\n\t\tbutton: Button.prototype.template,\n\t\tanchor: Anchor.prototype.template\n\t} ),\n\t/**\n\t\t * @memberof CtaDrawer\n\t\t * @instance\n\t\t */\n\ttemplate: mw.template.get( 'mobile.startup', 'CtaDrawer.hogan' ),\n\t/**\n\t\t * @inheritdoc\n\t\t * @memberof CtaDrawer\n\t\t * @instance\n\t\t */\n\tpreRender: function () {\n\t\tvar params = redirectParams( this.options.queryParams, this.options.returnTo );\n\n\t\t// Give the button and the anchor a default target, if it isn't set already. Buttons are\n\t\t// customized by Minerva's red link drawer, skins.minerva.scripts/init.js.\n\t\tif ( !this.options.progressiveButton.href ) {\n\t\t\tthis.options.progressiveButton.href = mw.util.getUrl( 'Special:UserLogin', params );\n\t\t}\n\t\tif ( !this.options.actionAnchor.href ) {\n\t\t\tthis.options.actionAnchor.href = mw.util.getUrl(\n\t\t\t\t'Special:UserLogin', signUpParams( params, this.options.signupQueryParams )\n\t\t\t);\n\t\t}\n\t}\n} );\n\n/**\n * Special:UserLogin post-request redirect query parameters.\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 * @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\tPanel = require( './Panel' ),\n\tutil = require( './util' ),\n\tIcon = require( './Icon' );\n\n/**\n * A {@link View} that pops up from the bottom of the screen.\n * @class Drawer\n * @extends Panel\n * @param {Object} [props]\n */\nfunction Drawer( props ) {\n\tPanel.call( this,\n\t\tutil.extend(\n\t\t\t{ className: 'drawer position-fixed' },\n\t\t\tprops,\n\t\t\t{ events: util.extend( { click: 'stopPropagation' }, ( props || {} ).events ) }\n\t\t)\n\t);\n}\n\nmfExtend( Drawer, Panel, {\n\t/**\n\t * @memberof Drawer\n\t * @instance\n\t * @mixes Panel#defaults\n\t * @property {Object} defaults Default options hash.\n\t * @property {string} defaults.cancelButton HTML of the button that closes the drawer.\n\t */\n\tdefaults: util.extend( {}, Panel.prototype.defaults, {\n\t\t// Used by CtaDrawer, BlockMessage.\n\t\tcollapseIcon: new Icon( {\n\t\t\tname: 'arrow',\n\t\t\tadditionalClassNames: 'cancel'\n\t\t} ).options\n\t} ),\n\t/**\n\t * @memberof Drawer\n\t * @instance\n\t */\n\ttemplatePartials: util.extend( {}, Panel.prototype.templatePartials, {\n\t\ticon: Icon.prototype.template\n\t} ),\n\t/**\n\t * Defines an element that the Drawer should automatically be appended to.\n\t * @memberof Drawer\n\t * @instance\n\t * @property {string}\n\t */\n\tappendToElement: 'body',\n\t/**\n\t * Whether the drawer should disappear on a scroll event\n\t * @memberof Drawer\n\t * @instance\n\t * @property {boolean}\n\t */\n\tcloseOnScroll: true,\n\n\t/**\n\t * @inheritdoc\n\t * @memberof Drawer\n\t * @instance\n\t */\n\tpostRender: function () {\n\t\tvar self = this;\n\t\t// This module might be loaded at the top of the page e.g. Special:Uploads\n\t\t// Thus ensure we wait for the DOM to be loaded\n\t\tutil.docReady( function () {\n\t\t\tself.appendTo( self.appendToElement );\n\t\t\tself.$el.parent().addClass( 'has-drawer' );\n\t\t} );\n\t\tthis.on( 'show', this.onShowDrawer.bind( this ) );\n\t\tthis.on( 'hide', this.onHideDrawer.bind( this ) );\n\t},\n\t/**\n\t * Stop Propagation event handler\n\t * @memberof Drawer\n\t * @instance\n\t * @param {Object} ev event object\n\t * Allow the drawer itself to be clickable (e.g. for copying and pasting references\n\t * clicking links in reference)\n\t */\n\tstopPropagation: function ( ev ) {\n\t\tev.stopPropagation();\n\t},\n\n\t/**\n\t * ShowDrawer event handler\n\t * @memberof Drawer\n\t * @instance\n\t */\n\tonShowDrawer: function () {\n\t\tvar self = this;\n\n\t\tthis.$el.parent().addClass( 'drawer-visible' );\n\n\t\tsetTimeout( function () {\n\t\t\tvar $window = util.getWindow();\n\t\t\t$window.one( 'click.drawer', self.hide.bind( self ) );\n\t\t\tif ( self.closeOnScroll ) {\n\t\t\t\t$window.one( 'scroll.drawer', self.hide.bind( self ) );\n\t\t\t}\n\t\t}, self.minHideDelay );\n\t},\n\n\t/**\n\t * HideDrawer event handler\n\t * @memberof Drawer\n\t * @instance\n\t */\n\tonHideDrawer: function () {\n\t\tthis.$el.parent().removeClass( 'drawer-visible' );\n\t\t// .one() registers one callback for scroll and click independently\n\t\t// if one fired, get rid of the other one\n\t\tutil.getWindow().off( '.drawer' );\n\t}\n} );\n\nmodule.exports = Drawer;\n","var\n\tmfExtend = require( './mfExtend' ),\n\tView = require( './View' );\n\n/**\n * A wrapper for creating an icon.\n * @class Icon\n * @extends View\n *\n * @param {Object} options Configuration options\n */\nfunction Icon( options ) {\n\tif ( options.hasText ) {\n\t\toptions.modifier = 'mw-ui-icon-before';\n\t}\n\tif ( options.href ) {\n\t\toptions.tagName = 'a';\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.setRotationClass();\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 * @memberof Icon\n\t * @instance\n\t * @private\n\t */\n\tsetRotationClass: function () {\n\t\tvar options = this.options;\n\t\tif ( options.rotation ) {\n\t\t\tswitch ( options.rotation ) {\n\t\t\t\tcase -180:\n\t\t\t\tcase 180:\n\t\t\t\t\toptions._rotationClass = 'mf-mw-ui-icon-rotate-flip';\n\t\t\t\t\tbreak;\n\t\t\t\tcase -90:\n\t\t\t\t\toptions._rotationClass = 'mf-mw-ui-icon-rotate-anti-clockwise';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 90:\n\t\t\t\t\toptions._rotationClass = '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},\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.hasText Whether the icon has text.\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.modifier Additional class name.\n\t * Defaults to 'mw-ui-icon-element'.\n\t * @property {string} defaults.title Tooltip text.\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 */\n\tdefaults: {\n\t\trotation: 0,\n\t\thasText: false,\n\t\thref: undefined,\n\t\tglyphPrefix: 'mf',\n\t\ttagName: 'div',\n\t\tisSmall: false,\n\t\tbase: 'mw-ui-icon',\n\t\tname: '',\n\t\tmodifier: 'mw-ui-icon-element',\n\t\ttitle: ''\n\t},\n\t/**\n\t * Return the full class name that is required for the icon to render\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 * @memberof Icon\n\t * @instance\n\t * @return {string}\n\t */\n\tgetGlyphClassName: function () {\n\t\treturn this.options.base + '-' + this.options.glyphPrefix + '-' + this.options.name;\n\t},\n\t/**\n\t * Return the HTML representation of this view\n\t * @memberof Icon\n\t * @instance\n\t * @return {string}\n\t */\n\ttoHtmlString: function () {\n\t\treturn this.parseHTML( '<div>' ).append( this.$el ).html();\n\t},\n\ttemplate: mw.template.get( 'mobile.startup', 'icon.hogan' )\n} );\n\nmodule.exports = Icon;\n","var\n\tView = require( './View' ),\n\tmfExtend = require( './mfExtend' );\n\n/**\n * @class MessageBox\n * @extends View\n */\nfunction MessageBox() {\n\tView.apply( this, arguments );\n}\n\nmfExtend( MessageBox, View, {\n\t/**\n\t * @inheritdoc\n\t * @memberof MessageBox\n\t * @instance\n\t */\n\tisTemplateMode: true,\n\t/**\n\t * @memberof MessageBox\n\t * @instance\n\t * @mixes View#defaults\n\t * @property {Object} defaults Default options hash.\n\t * @property {string} [defaults.heading] heading to show along with message (text)\n\t * @property {string} defaults.msg message to show (html)\n\t * @property {string} defaults.className either errorbox, warningbox or successbox\n\t */\n\tdefaults: {},\n\t/**\n\t * @memberof MessageBox\n\t * @instance\n\t */\n\ttemplate: mw.template.get( 'mobile.startup', 'MessageBox.hogan' )\n} );\n\nmodule.exports = MessageBox;\n","var\n\tView = require( './View' ),\n\tIcon = require( './Icon' ),\n\tButton = require( './Button' ),\n\tAnchor = require( './Anchor' ),\n\ticons = require( './icons' ),\n\tutil = require( './util' ),\n\tbrowser = require( './Browser' ).getSingleton(),\n\tmfExtend = require( './mfExtend' );\n\n/**\n * Mobile modal window\n * @typedef {Object} HeaderButtonDefinition\n * @property {string} href of button\n * @property {string} className of button\n * @property {boolean} disabled whether button is disabled initially\n * @property {string} msg button label\n * @class Overlay\n * @extends View\n * @uses Icon\n * @uses Button\n * @fires Overlay#Overlay-exit\n * @fires Overlay#hide\n * @param {Object} props\n * @param {Object} props.events - custom events to be bound to the overlay.\n * @param {View[]} props.headerActions children (usually buttons or icons)\n *   that should be placed in the header actions\n * @param {boolean} props.noHeader renders an overlay without a header\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 * @param {HeaderButtonDefinition[]} [props.headerButtons] define buttons to go in header\n */\nfunction Overlay( props ) {\n\tthis.isIos = browser.isIos();\n\t// Set to true when overlay has failed to load\n\tthis.hasLoadError = false;\n\n\tView.call(\n\t\tthis,\n\t\tutil.extend(\n\t\t\ttrue,\n\t\t\t{ className: 'overlay' },\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: 'stopPropagation'\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\t/**\n\t * Is overlay fullscreen\n\t * @memberof Overlay\n\t * @instance\n\t * @property {boolean}\n\t */\n\tfullScreen: true,\n\n\t/**\n\t * True if this.hide() should be invoked before firing the Overlay-exit\n\t * event\n\t * @memberof Overlay\n\t * @instance\n\t * @property {boolean}\n\t */\n\thideOnExitClick: true,\n\n\ttemplatePartials: {\n\t\theader: mw.template.get( 'mobile.startup', 'header.hogan' ),\n\t\tanchor: Anchor.prototype.template,\n\t\tbutton: Button.prototype.template\n\t},\n\ttemplate: mw.template.get( 'mobile.startup', 'Overlay.hogan' ),\n\t/**\n\t * @memberof Overlay\n\t * @instance\n\t * @mixes View#defaults\n\t * @property {Object} defaults Default options hash.\n\t * @property {string} defaults.saveMessage Caption for save button on edit form.\n\t * @property {string} defaults.cancelButton HTML of the cancel button.\n\t * @property {string} defaults.backButton HTML of the back button.\n\t * @property {View[]} defaults.headerActions children (usually buttons or icons)\n\t *  that should be placed in the header actions\n\t * @property {HeaderButtonDefinition[]} defaults.headerButtons definitions\n\t * @property {boolean} defaults.headerChrome Whether the header has chrome.\n\t * @property {string} defaults.spinner HTML of the spinner icon.\n\t * @property {Object} [defaults.footerAnchor] options for an optional Anchor\n\t *  that can appear in the footer\n\t */\n\tdefaults: {\n\t\theaderActions: [],\n\t\tsaveMsg: mw.config.get( 'wgEditSubmitButtonLabelPublish' ) ?\n\t\t\tmw.msg( 'mobile-frontend-editor-publish' ) : mw.msg( 'mobile-frontend-editor-save' ),\n\t\tcancelButton: icons.cancel().toHtmlString(),\n\t\tbackButton: new Icon( {\n\t\t\ttagName: 'button',\n\t\t\tname: 'back',\n\t\t\tadditionalClassNames: 'back',\n\t\t\tlabel: mw.msg( 'mobile-frontend-overlay-close' )\n\t\t} ).toHtmlString(),\n\t\theaderChrome: false,\n\t\tspinner: icons.spinner().toHtmlString()\n\t},\n\t/**\n\t * Flag overlay to close on content tap\n\t * @memberof Overlay\n\t * @instance\n\t * @property {boolean}\n\t */\n\tcloseOnContentTap: false,\n\n\t/**\n\t * Shows the spinner right to the input field.\n\t * @memberof Overlay\n\t * @instance\n\t * @method\n\t */\n\tshowSpinner: function () {\n\t\tthis.$spinner.removeClass( 'hidden' );\n\t},\n\n\t/**\n\t * Hide the spinner near to the input field.\n\t * @memberof Overlay\n\t * @instance\n\t * @method\n\t */\n\thideSpinner: function () {\n\t\tthis.$spinner.addClass( 'hidden' );\n\t},\n\n\t/**\n\t * @inheritdoc\n\t * @memberof Overlay\n\t * @instance\n\t */\n\tpreRender: function () {\n\t\tvar props = this.options;\n\t\tthis.options.hasActions = props.headerButtons || props.headerActions.length;\n\t},\n\n\t/**\n\t * @inheritdoc\n\t * @memberof Overlay\n\t * @instance\n\t */\n\tpostRender: function () {\n\t\tthis.$overlayContent = this.$el.find( '.overlay-content' );\n\t\tthis.$spinner = this.$el.find( '.spinner' );\n\t\tif ( this.isIos ) {\n\t\t\tthis.$el.addClass( 'overlay-ios' );\n\t\t}\n\t\t// Truncate any text inside in the overlay header.\n\t\tthis.$el.find( '.overlay-header h2 span' ).addClass( 'truncated-text' );\n\t\tthis.$el.find( '.header-action' ).append(\n\t\t\tthis.options.headerActions.map( function ( component ) {\n\t\t\t\treturn component.$el;\n\t\t\t} )\n\t\t);\n\t},\n\n\t/**\n\t * ClickBack event handler\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\t// FIXME: This check will be removed once ImageOverlay\n\t\t\t// is using onBeforeExit.\n\t\t\tif ( this.hideOnExitClick ) {\n\t\t\t\tthis.hide();\n\t\t\t}\n\t\t\tthis.emit( Overlay.EVENT_EXIT );\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 );\n\t\t} else {\n\t\t\texit();\n\t\t}\n\n\t},\n\t/**\n\t * Stop clicks in the overlay from propagating to the page\n\t * (prevents non-fullscreen overlays from being closed when they're tapped)\n\t * @memberof Overlay\n\t * @instance\n\t * @param {Object} ev Event Object\n\t */\n\tstopPropagation: function ( ev ) {\n\t\tev.stopPropagation();\n\t},\n\t/**\n\t * Attach overlay to current view and show it.\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\tif ( this.fullScreen ) {\n\t\t\t$html.addClass( 'overlay-enabled' );\n\t\t\t// skip the URL bar if possible\n\t\t\twindow.scrollTo( 0, 1 );\n\t\t}\n\n\t\tif ( this.closeOnContentTap ) {\n\t\t\t$html.find( '#mw-mf-page-center' ).one( 'click', this.hide.bind( this ) );\n\t\t}\n\n\t\tthis.$el.addClass( 'visible' );\n\t},\n\t/**\n\t * Detach the overlay from the current view\n\t * @memberof Overlay\n\t * @instance\n\t * @return {boolean} Whether the overlay was successfully hidden or not\n\t */\n\thide: function () {\n\t\tvar $html = util.getDocument();\n\n\t\tif ( this.fullScreen ) {\n\t\t\t$html.removeClass( 'overlay-enabled' );\n\t\t\t// return to last known scroll position\n\t\t\twindow.scrollTo( window.pageXOffset, this.scrollTop );\n\t\t}\n\n\t\tthis.$el.detach();\n\n\t\t/**\n\t\t * Fired when the overlay is closed.\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 * @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 * Fires when close button is clicked. Not to be confused with hide event.\n * @memberof Overlay\n * @event Overlay#Overlay-exit\n */\nOverlay.EVENT_EXIT = 'Overlay-exit';\n\n/**\n * Factory method for an overlay with a single child\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\tmfExtend = require( './mfExtend' ),\n\toverlayManager = null;\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 * @param {Overlay} overlay\n */\nfunction attachHideEvent( overlay ) {\n\toverlay.on( 'hide', function () {\n\t\toverlay.emit( '_om_hide' );\n\t} );\n}\n\nmfExtend( OverlayManager, {\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 hiding an overlay.\n\t * @memberof OverlayManager\n\t * @instance\n\t * @private\n\t */\n\t_onHideOverlay: function () {\n\t\tthis.hideCurrent = false;\n\n\t\tthis.router.back();\n\t},\n\n\t/** Attach overlay to DOM\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 * @memberof OverlayManager\n\t * @instance\n\t * @private\n\t * @param {Overlay} overlay to show\n\t */\n\t_show: function ( overlay ) {\n\n\t\t// if hidden using overlay (not hardware) button, update the state\n\t\toverlay.once( '_om_hide', this._onHideOverlay.bind( this ) );\n\n\t\tthis._attachOverlay( overlay );\n\t\toverlay.show();\n\t},\n\n\t/**\n\t * Hide overlay\n\t * @memberof OverlayManager\n\t * @instance\n\t * @private\n\t * @param {Overlay} overlay to hide\n\t * @return {boolean} Whether the overlay has been hidden\n\t */\n\t_hideOverlay: function ( overlay ) {\n\t\tlet result;\n\n\t\tfunction exit() {\n\t\t\tresult = true;\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 );\n\t\t} else {\n\t\t\tresult = overlay.hide();\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._onHideOverlay.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 * @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 (either\n\t\t\t\t// a promise or an overlay)\n\t\t\t\tfactoryResult = match.factoryResult;\n\t\t\t\t// http://stackoverflow.com/a/13075985/365238\n\t\t\t\tif ( typeof factoryResult.promise === 'function' ) {\n\t\t\t\t\tfactoryResult.then( function ( overlay ) {\n\t\t\t\t\t\tmatch.overlay = overlay;\n\t\t\t\t\t\tattachHideEvent( overlay );\n\t\t\t\t\t\tself._show( overlay );\n\t\t\t\t\t} );\n\t\t\t\t} else {\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 * @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\tlet current = this.stack[0],\n\t\t\tmatch;\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) {\n\t\t\t// if hide prevented, prevent route change event\n\t\t\tev.preventDefault();\n\t\t\treturn;\n\t\t}\n\n\t\tmatch = 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 * @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\tmatch = path.match( entry.route ),\n\t\t\tprevious = this.stack[1],\n\t\t\tself = this;\n\n\t\t/**\n\t\t * Returns object to add to stack\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\tfactoryResult: entry.factory.apply( self, match.slice( 1 ) )\n\t\t\t};\n\t\t}\n\n\t\tif ( match ) {\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\tif ( previous.overlay && previous.overlay.hasLoadError ) {\n\t\t\t\t\tself.stack.shift();\n\t\t\t\t\t// Loading of overlay failed so we want to replace it with a new\n\t\t\t\t\t// overlay (which will try to load successfully)\n\t\t\t\t\tself.stack[0] = getNext();\n\t\t\t\t\treturn self.stack[0];\n\t\t\t\t}\n\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 * end with '#/hi/name'. The value of `name` will be passed to the overlay.\n\t *\n\t *     @example\n\t *     overlayManager.add( /\\/hi\\/(.*)/, function ( name ) {\n\t *       var factoryResult = $.Deferred();\n\t *\n\t *       mw.using( 'mobile.HiOverlay', function () {\n\t *         var HiOverlay = M.require( 'HiOverlay' );\n\t *         factoryResult.resolve( new HiOverlay( { name: name } ) );\n\t *       } );\n\t *\n\t *       return factoryResult;\n\t *     } );\n\t *\n\t * @memberof OverlayManager\n\t * @instance\n\t * @param {RegExp} route route regular expression, optionally with parameters.\n\t * @param {Function} factory a function returning an overlay or a $.Deferred\n\t * which resolves to 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\tthis._hideOverlay( this.stack[0].overlay );\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 * @memberof OverlayManager\n * @return {OverlayManager}\n */\nOverlayManager.getSingleton = function () {\n\tif ( !overlayManager ) {\n\t\tconst container = document.createElement( 'div' );\n\t\tcontainer.className = 'mw-overlays-container';\n\t\tdocument.body.appendChild( container );\n\t\toverlayManager = new OverlayManager( mw.loader.require( 'mediawiki.router' ), container );\n\t}\n\treturn overlayManager;\n};\n\nmodule.exports = OverlayManager;\n","var\n\tHTML = mw.html,\n\tmfExtend = require( './mfExtend' ),\n\ttime = require( './time' ),\n\tutil = require( './util' ),\n\tSection = require( './Section' ),\n\tThumbnail = require( './Thumbnail' ),\n\tView = require( './View' ),\n\tHEADING_SELECTOR = mw.config.get( 'wgMFMobileFormatterHeadings', [ 'h1', 'h2', 'h3', 'h4', 'h5' ] ).join( ',' ),\n\tBLACKLISTED_THUMBNAIL_CLASS_SELECTORS = [ 'noviewer', 'metadata' ];\n\n/**\n * Mobile page view object\n *\n * @class Page\n * @uses Section\n * @extends View\n *\n * @param {Object} options Configuration options\n */\nfunction Page( options ) {\n\n\tutil.extend( this, {\n\t\toptions: options,\n\t\t// FIXME: Deprecate title property as it can be derived from titleObj using getPrefixedText\n\t\ttitle: options.title,\n\t\ttitleObj: options.titleObj,\n\t\tdisplayTitle: options.displayTitle,\n\t\turl: options.url || mw.util.getUrl( options.title ),\n\t\tid: options.id,\n\t\twikidataDescription: options.wikidataDescription,\n\t\tthumbnail: ( Object.prototype.hasOwnProperty.call( options, 'thumbnail' ) ) ?\n\t\t\toptions.thumbnail : false,\n\t\tisMissing: ( options.isMissing !== undefined ) ?\n\t\t\toptions.isMissing : options.id === 0\n\t} );\n\n\tthis.options.isBorderBox = false;\n\tthis.options.languageUrl = mw.util.getUrl( 'Special:MobileLanguages/' + this.title );\n\n\tView.call( this, this.options );\n\n\t// Fallback if no displayTitle provided\n\tif ( !this.displayTitle ) {\n\t\tthis.displayTitle = this.getDisplayTitle();\n\t}\n\n\tif ( this.thumbnail && this.thumbnail.width ) {\n\t\tthis.thumbnail.isLandscape = this.thumbnail.width > this.thumbnail.height;\n\t}\n}\n\nmfExtend( Page, View, {\n\t/**\n\t * @memberof Page\n\t * @instance\n\t * @mixes View#defaults\n\t * @property {Object} defaults Default options hash.\n\t * @property {number} defaults.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 * @property {string} defaults.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 * @property {string} defaults.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 * @property {number} defaults.namespaceNumber the number of the\n\t *  namespace the page belongs to\n\t * @property {Object} defaults.protection List of permissions as returned by API,\n\t * e.g. [{ edit: ['*'] }]\n\t * @property {Array} defaults.sections Array of {Section} objects.\n\t * @property {boolean} defaults.isMainPage Whether the page is the Main Page.\n\t * @property {boolean} defaults.isMissing Whether the page exists in the wiki.\n\t * @property {Object} defaults.thumbnail thumbnail definition corresponding to page image\n\t * @property {boolean} defaults.thumbnail.isLandscape whether the image is in\n\t *  landscape format\n\t * @property {number} defaults.thumbnail.width of image in pixels.\n\t * @property {number} defaults.thumbnail.height of image in pixels.\n\t * @property {string} defaults.thumbnail.source url for image\n\t */\n\tdefaults: {\n\t\tid: 0,\n\t\ttitle: '',\n\t\tdisplayTitle: '',\n\t\tnamespaceNumber: 0,\n\t\tprotection: {\n\t\t\tedit: [ '*' ]\n\t\t},\n\t\tsections: [],\n\t\tisMissing: false,\n\t\tisMainPage: false,\n\t\turl: undefined,\n\t\tthumbnail: {\n\t\t\tisLandscape: undefined,\n\t\t\tsource: undefined,\n\t\t\twidth: undefined,\n\t\t\theight: undefined\n\t\t}\n\t},\n\t/**\n\t * Retrieve the title that should be displayed to the user\n\t * @memberof Page\n\t * @instance\n\t * @return {string} HTML\n\t */\n\tgetDisplayTitle: function () {\n\t\treturn this.options.displayTitle || HTML.escape( this.options.title );\n\t},\n\t/**\n\t * Determine if current page is in a specified namespace\n\t * @memberof Page\n\t * @instance\n\t * @param {string} namespace Name of namespace\n\t * @return {boolean}\n\t */\n\tinNamespace: function ( namespace ) {\n\t\treturn this.options.namespaceNumber === mw.config.get( 'wgNamespaceIds' )[namespace];\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 * @method\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: function ( 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\treturn this.$el.find();\n\t\t} else {\n\t\t\treturn this.$el.find( HEADING_SELECTOR )\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\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 * @method\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: function ( sectionIndex, selector ) {\n\t\tvar $heading, $nextHeading, $container, $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$container = $heading.next();\n\t\t\t// inside section find the first heading\n\t\t\t$nextHeading = $container.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( $container.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 * @memberof Page\n\t * @instance\n\t * @return {jQuery.Object|null}\n\t */\n\tgetLeadSectionElement: function () {\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\tif ( this.$el.find( '.mf-section-0' ).length ) {\n\t\t\treturn this.$el.find( '.mf-section-0' );\n\t\t}\n\t\t// no lead section found\n\t\treturn null;\n\t},\n\n\t/**\n\t * Determines if content model is wikitext\n\t * @memberof Page\n\t * @instance\n\t * @return {boolean}\n\t */\n\tisWikiText: function () {\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 * @memberof Page\n\t * @instance\n\t * @return {boolean}\n\t */\n\tisMainPage: function () {\n\t\treturn this.options.isMainPage;\n\t},\n\t/**\n\t * Checks whether the current page is watched\n\t * @memberof Page\n\t * @instance\n\t * @return {boolean}\n\t */\n\tisWatched: function () {\n\t\treturn this.options.isWatched;\n\t},\n\n\t/**\n\t * Return the latest revision id for this page\n\t * @memberof Page\n\t * @instance\n\t * @return {number}\n\t */\n\tgetRevisionId: function () {\n\t\treturn this.options.revId;\n\t},\n\n\t/**\n\t * Return prefixed page title\n\t * @memberof Page\n\t * @instance\n\t * @return {string}\n\t */\n\tgetTitle: function () {\n\t\treturn this.options.title;\n\t},\n\n\t/**\n\t * Return page id\n\t * @memberof Page\n\t * @instance\n\t * @return {number}\n\t */\n\tgetId: function () {\n\t\treturn this.options.id;\n\t},\n\n\t/**\n\t * return namespace id\n\t * @memberof Page\n\t * @instance\n\t * @return {number} namespace Number\n\t */\n\tgetNamespaceId: function () {\n\t\tvar nsId,\n\t\t\targs = this.options.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\t/**\n\t * Determines if current page is a talk page\n\t * @memberof Page\n\t * @instance\n\t * @return {boolean} Whether the page is a talk page or not\n\t */\n\tisTalkPage: function () {\n\t\tvar ns = this.getNamespaceId();\n\t\t// all talk pages are odd Numbers (except the case of special pages)\n\t\treturn ns > 0 && ns % 2 === 1;\n\t},\n\n\t/**\n\t * @inheritdoc\n\t * @memberof Page\n\t * @instance\n\t */\n\tpreRender: function () {\n\t\tthis.sections = [];\n\t\tthis._sectionLookup = {};\n\t\tthis.title = this.options.title;\n\n\t\tthis.options.sections.forEach( function ( sectionData ) {\n\t\t\tvar section = new Section( sectionData );\n\t\t\tthis.sections.push( section );\n\t\t\tthis._sectionLookup[section.id] = section;\n\t\t}.bind( this ) );\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 BLACKLISTED_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 * @memberof Page\n\t * @instance\n\t * @return {Thumbnail[]}\n\t */\n\tgetThumbnails: function () {\n\t\tvar $thumbs,\n\t\t\t$el = this.$el,\n\t\t\tblacklistSelector = '.' + BLACKLISTED_THUMBNAIL_CLASS_SELECTORS.join( ',.' ),\n\t\t\tthumbs = [];\n\n\t\tif ( !this._thumbs ) {\n\t\t\t$thumbs = $el.find( 'a.image, a.thumbimage' )\n\t\t\t\t.not( blacklistSelector );\n\n\t\t\t$thumbs.each( function () {\n\t\t\t\tvar $a = $el.find( this ),\n\t\t\t\t\t$lazyImage = $a.find( '.lazy-image-placeholder' ),\n\t\t\t\t\t// Parents need to be checked as well.\n\t\t\t\t\tvalid = $a.parents( blacklistSelector ).length === 0 &&\n\t\t\t\t\t\t$a.find( blacklistSelector ).length === 0,\n\t\t\t\t\tlegacyMatch = $a.attr( 'href' ).match( /title=([^/&]+)/ ),\n\t\t\t\t\tmatch = $a.attr( 'href' ).match( /[^/]+$/ );\n\n\t\t\t\t// filter out invalid lazy loaded images if so far image is valid\n\t\t\t\tif ( $lazyImage.length && valid ) {\n\t\t\t\t\t// if the regex matches it means the image has one of the classes\n\t\t\t\t\t// thus we must invert the result\n\t\t\t\t\tvalid = !new RegExp( '\\\\b(' + BLACKLISTED_THUMBNAIL_CLASS_SELECTORS.join( '|' ) + ')\\\\b' )\n\t\t\t\t\t\t.test( $lazyImage.data( 'class' ) );\n\t\t\t\t}\n\n\t\t\t\tif ( valid && ( legacyMatch || match ) ) {\n\t\t\t\t\tthumbs.push(\n\t\t\t\t\t\tnew Thumbnail( {\n\t\t\t\t\t\t\tel: $a,\n\t\t\t\t\t\t\tfilename: decodeURIComponent(\n\t\t\t\t\t\t\t\tlegacyMatch ? legacyMatch[1] : match[0]\n\t\t\t\t\t\t\t)\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\tthis._thumbs = thumbs;\n\t\t}\n\t\treturn this._thumbs;\n\t},\n\n\t/**\n\t * FIXME: Change function signature to take the anchor of the heading\n\t * @memberof Page\n\t * @instance\n\t * @param {string} id of the section as defined by MobileFormatter.\n\t * Note, that currently, this is different from\n\t * the PHP parser in that it relates to top-level sections.\n\t * For example, mf-section-1 would relate to section 1. See FIXME.\n\t * @return {Section}\n\t */\n\tgetSection: function ( id ) {\n\t\treturn this._sectionLookup[ id ];\n\t},\n\n\t/**\n\t * Obtain the list of high level (and grouped) sections.\n\t * Note that this list will not include subsections.\n\t * @memberof Page\n\t * @instance\n\t * @return {Array} of Section instances\n\t */\n\tgetSections: function () {\n\t\treturn this.sections;\n\t},\n\n\t/**\n\t * Returns a jQuery object representing all redlinks on the page.\n\t * @memberof Page\n\t * @instance\n\t * @return {jQuery.Object}\n\t */\n\tgetRedLinks: function () {\n\t\treturn this.$el.find( '.new' );\n\t}\n} );\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 */\nPage.newFromJSON = function ( resp ) {\n\tvar revision, displayTitle,\n\t\tthumb = resp.thumbnail,\n\t\tpageprops = resp.pageprops || {\n\t\t\tdisplaytitle: HTML.escape( resp.title )\n\t\t},\n\t\tterms = resp.terms;\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\tHTML.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 = time.getLastModifiedMessage(\n\t\t\tnew Date( revision.timestamp ).getTime() / 1000,\n\t\t\trevision.user\n\t\t);\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\n/**\n * Selector for matching headings\n *\n * @memberof Page\n */\nPage.HEADING_SELECTOR = HEADING_SELECTOR;\n\nmodule.exports = Page;\n","var sectionTemplate = mw.template.get( 'mobile.startup', 'Section.hogan' ),\n\tutil = require( './util.js' ),\n\tPage = require( './Page' ),\n\tactionParams = require( './actionParams' ),\n\tcache = {};\n\n/**\n * Add child to listOfSections if the level of child is the same as the last\n * child of listOfSections, otherwise add it to the children of the last\n * section of listOfSections. If listOfSections is empty, just add child to it.\n * @private\n * @param {Array} listOfSections - Array of section ids\n * @param {Object} child - Section to be added to listOfSections\n */\nfunction assignToParent( listOfSections, child ) {\n\tvar section;\n\tif ( listOfSections.length === 0 ) {\n\t\tlistOfSections.push( child );\n\t} else {\n\t\t// take a look at the last child\n\t\tsection = listOfSections[listOfSections.length - 1];\n\t\t// If the level is the same as another section in this list it is a sibling\n\t\tif ( parseInt( section.level, 10 ) === parseInt( child.level, 10 ) ) {\n\t\t\tlistOfSections.push( child );\n\t\t} else {\n\t\t\t// Otherwise take a look at that sections children recursively\n\t\t\tassignToParent( section.subsections, child );\n\t\t}\n\t}\n}\n\n/**\n * Order sections hierarchically\n * @private\n * @param {Array} sections Array of section objects created from response HTML\n * @return {Array} Ordered array of sections\n */\nfunction transformSections( sections ) {\n\tvar\n\t\tsectionLevels = sections.map( function ( s ) {\n\t\t\treturn s.level;\n\t\t} ),\n\t\texistingSectionLevels = sectionLevels.filter( function ( level ) {\n\t\t\treturn !!level;\n\t\t} ),\n\t\tcollapseLevel = Math.min.apply( this, existingSectionLevels ).toString(),\n\t\tlastSection,\n\t\tresult = [];\n\n\t// if the first section level is not equal to collapseLevel, this first\n\t// section will not have a parent and will be appended to the result.\n\tsections.forEach( function ( section ) {\n\t\tif ( section.line !== undefined ) {\n\t\t\tsection.line = section.line.replace( /<\\/?a\\b[^>]*>/g, '' );\n\t\t}\n\t\tsection.subsections = [];\n\n\t\tif (\n\t\t\t!lastSection ||\n\t\t\t(\n\t\t\t\t!section.level ||\n\t\t\t\tsection.level === collapseLevel\n\t\t\t) ||\n\t\t\t// make sure lastSections first child's level is bigger than section.level\n\t\t\t(\n\t\t\t\tlastSection.subsections.length &&\n\t\t\t\tlastSection.subsections[0].level > section.level\n\t\t\t) ||\n\t\t\t// also make sure section.level is not bigger than the lastSection.level\n\t\t\t(\n\t\t\t\tlastSection.level &&\n\t\t\t\tlastSection.level >= section.level\n\t\t\t)\n\t\t) {\n\t\t\tresult.push( section );\n\t\t\tlastSection = section;\n\t\t} else {\n\t\t\tassignToParent( lastSection.subsections, section );\n\t\t\tlastSection.text += sectionTemplate.render( section );\n\t\t}\n\t} );\n\n\treturn result;\n}\n\n/**\n * API for providing Page data\n * @class PageGateway\n * @param {mw.Api} api\n */\nfunction PageGateway( api ) {\n\tthis.api = api;\n}\n\nPageGateway.prototype = {\n\t/**\n\t * Retrieve the sections of a page\n\t * @memberof PageGateway\n\t * @instance\n\t * @param {string} title the title of the page to retrieve sections for\n\t * @return {JQuery.Promise}\n\t */\n\tgetSections: function ( title ) {\n\t\tvar promise = util.Deferred();\n\t\tthis.getPage( title ).then( function ( pageData ) {\n\t\t\tvar page = new Page( pageData );\n\t\t\tpromise.resolve( page.getSections() );\n\t\t}, function ( resp ) {\n\t\t\t// If the API returns the error code 'missingtitle', that means the\n\t\t\t// talk page doesn't exist yet.\n\t\t\tif ( resp === 'missingtitle' ) {\n\t\t\t\tpromise.resolve( [] );\n\t\t\t} else {\n\t\t\t\tpromise.reject();\n\t\t\t}\n\t\t} );\n\t\treturn promise.promise();\n\t},\n\t/**\n\t * Retrieve a page from the api\n\t * @memberof PageGateway\n\t * @instance\n\t * @param {string} title the title of the page to be retrieved\n\t * @param {string} [endpoint] an alternative api url to retrieve the page from\n\t * @param {boolean} [leadOnly] When set only the lead section content is returned\n\t * @return {jQuery.Deferred} with parameter page data that can be passed to a Page view\n\t */\n\tgetPage: function ( title, endpoint, leadOnly ) {\n\t\tvar timestamp,\n\t\t\td = util.Deferred(),\n\t\t\tparams = endpoint ? {\n\t\t\t\turl: endpoint,\n\t\t\t\tdataType: 'jsonp'\n\t\t\t} : {},\n\t\t\tprotection = {\n\t\t\t\tedit: [ '*' ]\n\t\t\t};\n\n\t\tutil.extend( params,\n\t\t\tactionParams( {\n\t\t\t\taction: 'mobileview',\n\t\t\t\tpage: title,\n\t\t\t\tvariant: mw.config.get( 'wgPageContentLanguage' ),\n\t\t\t\tredirect: 'yes',\n\t\t\t\tprop: 'id|sections|text|lastmodified|lastmodifiedby|languagecount|hasvariants|protection|displaytitle|revision',\n\t\t\t\tnoheadings: 'yes',\n\t\t\t\tsectionprop: 'level|line|anchor',\n\t\t\t\tsections: leadOnly ? 0 : 'all'\n\t\t\t} )\n\t\t);\n\n\t\tif ( !cache[title] ) {\n\t\t\tcache[title] = this.api.get( params ).then( function ( resp ) {\n\t\t\t\tvar sections, lastModified, resolveObj, mv;\n\n\t\t\t\tif ( resp.error ) {\n\t\t\t\t\treturn d.reject( resp.error );\n\t\t\t\t} else if ( !resp.mobileview.sections ) {\n\t\t\t\t\treturn d.reject( 'No sections' );\n\t\t\t\t} else {\n\t\t\t\t\tmv = resp.mobileview;\n\t\t\t\t\tsections = transformSections( mv.sections );\n\t\t\t\t\t// Assume the timestamp is in the form TS_ISO_8601\n\t\t\t\t\t// and we don't care about old browsers\n\t\t\t\t\t// change to seconds to be consistent with PHP\n\t\t\t\t\ttimestamp = new Date( mv.lastmodified ).getTime() / 1000;\n\t\t\t\t\tlastModified = mv.lastmodifiedby;\n\n\t\t\t\t\t// FIXME: [API] the API sometimes returns an object and sometimes an array\n\t\t\t\t\t// There are various quirks with the format of protection level\n\t\t\t\t\t// as returned by api.\n\t\t\t\t\t// Also it is usually incomplete - if something is missing this means\n\t\t\t\t\t// that it has no protection level.\n\t\t\t\t\t// When an array this means there is no protection level set.\n\t\t\t\t\t// To keep the data type consistent either use the predefined\n\t\t\t\t\t// protection level,\n\t\t\t\t\t// or extend it with what is returned by API.\n\t\t\t\t\tprotection = Array.isArray( mv.protection ) ?\n\t\t\t\t\t\tprotection :\n\t\t\t\t\t\tutil.extend( protection, mv.protection );\n\n\t\t\t\t\tresolveObj = {\n\t\t\t\t\t\ttitle: title,\n\t\t\t\t\t\tid: mv.id,\n\t\t\t\t\t\trevId: mv.revId,\n\t\t\t\t\t\tprotection: protection,\n\t\t\t\t\t\tlead: sections[0].text,\n\t\t\t\t\t\tsections: sections.slice( 1 ),\n\t\t\t\t\t\tisMainPage: mv.mainpage !== undefined,\n\t\t\t\t\t\thistoryUrl: mw.util.getUrl( title, {\n\t\t\t\t\t\t\taction: 'history'\n\t\t\t\t\t\t} ),\n\t\t\t\t\t\tlastModifiedTimestamp: timestamp,\n\t\t\t\t\t\tlanguageCount: mv.languagecount,\n\t\t\t\t\t\thasVariants: mv.hasvariants !== undefined,\n\t\t\t\t\t\tdisplayTitle: mv.displaytitle\n\t\t\t\t\t};\n\t\t\t\t\t// Add non-anonymous user information\n\t\t\t\t\tif ( lastModified ) {\n\t\t\t\t\t\tutil.extend( resolveObj, {\n\t\t\t\t\t\t\tlastModifiedUserName: lastModified.name,\n\t\t\t\t\t\t\tlastModifiedUserGender: lastModified.gender\n\t\t\t\t\t\t} );\n\t\t\t\t\t}\n\t\t\t\t\t// FIXME: Return a Page class here\n\t\t\t\t\treturn resolveObj;\n\t\t\t\t}\n\t\t\t}, function ( msg ) {\n\t\t\t\treturn d.reject( msg );\n\t\t\t} );\n\t\t}\n\n\t\treturn cache[title];\n\t},\n\n\t/**\n\t * Invalidate the internal cache for a given page\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 * @memberof PageGateway\n\t * @instance\n\t * @private\n\t * @param  {string} title Name of the page to obtain variants for\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, data ) {\n\t\tvar generalData = data.query.general,\n\t\t\tvariantPath = generalData.variantarticlepath,\n\t\t\tvariants = [];\n\n\t\tif ( !generalData.variants ) {\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( generalData.variants ).forEach( function ( index ) {\n\t\t\tvar item = generalData.variants[ index ],\n\t\t\t\tvariant = {\n\t\t\t\t\tautonym: item.name,\n\t\t\t\t\tlang: item.code\n\t\t\t\t};\n\n\t\t\tif ( variantPath ) {\n\t\t\t\tvariant.url = variantPath\n\t\t\t\t\t.replace( '$1', title )\n\t\t\t\t\t.replace( '$2', item.code );\n\t\t\t} else {\n\t\t\t\tvariant.url = mw.util.getUrl( title, {\n\t\t\t\t\tvariant: item.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 * @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\tvar self = this,\n\t\t\targs = actionParams( {\n\t\t\t\tmeta: 'siteinfo',\n\t\t\t\tsiprop: 'general',\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, resp )\n\t\t\t};\n\t\t}, function () {\n\t\t\treturn util.Deferred().reject();\n\t\t} );\n\t},\n\n\t/**\n\t * Extract sections from headings in $el\n\t * @memberof PageGateway\n\t * @instance\n\t * @private\n\t * @param {jQuery.Object} $el object from which sections are extracted\n\t * @return {Array} Array of section objects created from headings in $el\n\t * FIXME: Where's a better place for getSectionsFromHTML and this function to live?\n\t * Answer: Possibly Page.js\n\t */\n\t_getAPIResponseFromHTML: function ( $el ) {\n\t\t// FIXME: use Page.HEADING_SELECTOR\n\t\tvar $headings = $el.find( 'h1,h2,h3,h4,h5,h6' ),\n\t\t\tsections = [];\n\n\t\t$headings.each( function () {\n\t\t\tvar level = this.tagName.substr( 1 ),\n\t\t\t\t$span = $el.find( this ).find( '.mw-headline' );\n\n\t\t\tif ( $span.length ) {\n\t\t\t\tsections.push( {\n\t\t\t\t\tlevel: level,\n\t\t\t\t\tline: $span.html(),\n\t\t\t\t\tanchor: $span.attr( 'id' ) || '',\n\t\t\t\t\ttext: ''\n\t\t\t\t} );\n\t\t\t}\n\t\t} );\n\t\treturn sections;\n\t},\n\n\t/**\n\t * Order sections hierarchically\n\t * @memberof PageGateway\n\t * @instance\n\t * @param {jQuery.Object} $el object from which sections are extracted\n\t * @return {Array} Ordered array of sections\n\t */\n\tgetSectionsFromHTML: function ( $el ) {\n\t\treturn transformSections( this._getAPIResponseFromHTML( $el ) );\n\t}\n};\n\nmodule.exports = PageGateway;\n","var\n\tmfExtend = require( './mfExtend' ),\n\tView = require( './View' ),\n\tbrowser = require( './Browser' ).getSingleton();\n\n/**\n * List of items page view\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 * @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: mw.template.get( 'mobile.startup', 'PageList.hogan' ),\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: mw.template.get( 'mobile.startup', 'PageListItem.hogan' )\n\t}\n} );\n\nmodule.exports = PageList;\n","var\n\tmfExtend = require( './mfExtend' ),\n\tutil = require( './util' ),\n\tView = require( './View' );\n\n/**\n * An abstract class for a {@link View} that comprises a simple panel.\n * @class Panel\n * @extends View\n *\n * @param {Object} [params] Configuration options\n */\nfunction Panel( params ) {\n\tView.call( this, util.extend(\n\t\t{ className: 'panel' },\n\t\tparams,\n\t\t{ events: util.extend( { 'click .cancel': 'onCancel' }, ( params || {} ).events ) } )\n\t);\n}\n\nmfExtend( Panel, View, {\n\t// in milliseconds\n\tminHideDelay: 10,\n\n\t/**\n\t * Cancel event handler\n\t * @memberof Panel\n\t * @instance\n\t * @param {Object} ev Event Object\n\t */\n\tonCancel: function ( ev ) {\n\t\tev.preventDefault();\n\t\tthis.hide();\n\t},\n\n\t/**\n\t * Shows panel after a slight delay\n\t * @memberof Panel\n\t * @instance\n\t * @method\n\t */\n\tshow: function () {\n\t\tvar self = this;\n\n\t\tif ( !self.isVisible() ) {\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\tself.$el.addClass( 'visible animated' );\n\t\t\t\tself.emit( 'show' );\n\t\t\t}, self.minHideDelay );\n\t\t}\n\t},\n\n\t/**\n\t * Hides panel\n\t * @memberof Panel\n\t * @instance\n\t */\n\thide: function () {\n\t\tvar self = this;\n\n\t\t// see comment in show()\n\t\tsetTimeout( function () {\n\t\t\tself.$el.removeClass( 'visible' );\n\t\t\tself.emit( 'hide' );\n\t\t}, self.minHideDelay );\n\t},\n\n\t/**\n\t * Determines if panel is visible\n\t * @memberof Panel\n\t * @instance\n\t * @return {boolean} Panel is visible\n\t */\n\tisVisible: function () {\n\t\treturn this.$el.hasClass( 'visible' );\n\t},\n\n\t/**\n\t * Shows or hides panel\n\t * @memberof Panel\n\t * @instance\n\t */\n\ttoggle: function () {\n\t\tif ( this.isVisible() ) {\n\t\t\tthis.hide();\n\t\t} else {\n\t\t\tthis.show();\n\t\t}\n\t}\n} );\n\nmodule.exports = Panel;\n","var util = require( './util' ),\n\tmfExtend = require( './mfExtend' );\n\n/**\n * Class to assist a view in implementing infinite scrolling on some DOM\n * element. This module itself is only responsible for emitting an Event when\n * the bottom of an Element is scrolled to.\n *\n * @class ScrollEndEventEmitter\n * @mixins OO.EventEmitter\n *\n * Use this class in a view to help it do infinite scrolling.\n *\n * 1. Initialize it in the constructor `initialize` and listen to the\n *   EVENT_SCROLL_END event it emits (and call your loading function then)\n * 2. On preRender (once we have the DOM element) set it into the infinite\n *   scrolling object and disable it until we've loaded.\n * 3. Once you have loaded the list and put it in the DOM, enable the\n *   infinite scrolling detection.\n *   * Everytime the scroller detection triggers a load, it auto disables\n *     to not trigger multiple times. After you have loaded, manually\n *     re-enable it.\n *\n * Example:\n *     @example\n *     <code>\n *       var\n *         mfExtend = require( './mfExtend' ),\n *         ScrollEndEventEmitter = require( './ScrollEndEventEmitter' ),\n *         eventBus = require( './eventBusSingleton' );\n *       mfExtend( PhotoList, View, {\n *         //...\n *         initialize: function ( options ) {\n *           this.gateway = new PhotoListGateway( {\n *             username: options.username\n *           } );\n *           // 1. Set up infinite scroll helper and listen to events\n *           this.scrollEndEventEmitter = new ScrollEndEventEmitter( eventBus, 1000 );\n *           this.scrollEndEventEmitter.on( ScrollEndEventEmitter.EVENT_SCROLL_END,\n *             this._loadPhotos.bind( this ) );\n *           View.prototype.initialize.apply( this, arguments );\n *         },\n *         preRender: function () {\n *           // 2. Disable until we've got the list rendered and set DOM el\n *           this.scrollEndEventEmitter.setElement( this.$el );\n *           this.scrollEndEventEmitter.disable();\n *         },\n *         _loadPhotos: function () {\n *           var self = this;\n *           this.gateway.getPhotos().then( function ( photos ) {\n *             // load photos into the DOM ...\n *             // 3. and (re-)enable infinite scrolling\n *             self.scrollEndEventEmitter.enable();\n *           } );\n *         }\n *       } );\n *     </code>\n *\n * @fires ScrollEndEventEmitter#ScrollEndEventEmitter-scrollEnd\n * @param {Object} eventBus object to listen for scroll:throttled events\n * @param {number} [threshold=100] distance in pixels used to calculate if scroll\n * position is near the end of the $el\n */\nfunction ScrollEndEventEmitter( eventBus, threshold ) {\n\tthis.threshold = threshold || 100;\n\tthis.eventBus = eventBus;\n\tthis.enable();\n\tOO.EventEmitter.call( this );\n}\nOO.mixinClass( ScrollEndEventEmitter, OO.EventEmitter );\n\n/**\n * Fired when scroll bottom has been reached.\n * @event ScrollEndEventEmitter#ScrollEndEventEmitter-scrollEnd\n */\nScrollEndEventEmitter.EVENT_SCROLL_END = 'ScrollEndEventEmitter-scrollEnd';\n\nmfExtend( ScrollEndEventEmitter, {\n\t/**\n\t * Listen to scroll on window and notify this._onScroll\n\t * @memberof ScrollEndEventEmitter\n\t * @instance\n\t * @private\n\t */\n\t_bindScroll: function () {\n\t\tif ( !this._scrollHandler ) {\n\t\t\tthis._scrollHandler = this._onScroll.bind( this );\n\t\t\tthis.eventBus.on( 'scroll:throttled', this._scrollHandler );\n\t\t}\n\t},\n\t/**\n\t * Unbind scroll handler\n\t * @memberof ScrollEndEventEmitter\n\t * @instance\n\t * @private\n\t */\n\t_unbindScroll: function () {\n\t\tif ( this._scrollHandler ) {\n\t\t\tthis.eventBus.off( 'scroll:throttled', this._scrollHandler );\n\t\t\tthis._scrollHandler = null;\n\t\t}\n\t},\n\t/**\n\t * Scroll handler. Triggers load event when near the end of the container.\n\t * @memberof ScrollEndEventEmitter\n\t * @instance\n\t * @private\n\t */\n\t_onScroll: function () {\n\t\tif ( this.$el && this.enabled && this.scrollNearEnd() ) {\n\t\t\t// Disable when triggering an event. Won't trigger again until\n\t\t\t// re-enabled.\n\t\t\tthis.disable();\n\t\t\tthis.emit( ScrollEndEventEmitter.EVENT_SCROLL_END );\n\t\t}\n\t},\n\t/**\n\t * Is the scroll position near the end of the container element?\n\t * @memberof ScrollEndEventEmitter\n\t * @instance\n\t * @private\n\t * @return {boolean}\n\t */\n\tscrollNearEnd: function () {\n\t\tvar $window = util.getWindow(),\n\t\t\tscrollBottom = $window.scrollTop() + $window.height(),\n\t\t\tendPosition = this.$el.offset().top + this.$el.outerHeight();\n\t\treturn scrollBottom + this.threshold > endPosition;\n\t},\n\t/**\n\t * Enable the ScrollEndEventEmitter so that it triggers events.\n\t * @memberof ScrollEndEventEmitter\n\t * @instance\n\t */\n\tenable: function () {\n\t\tthis.enabled = true;\n\t\tthis._bindScroll();\n\t},\n\t/**\n\t * Disable the ScrollEndEventEmitter so that it doesn't trigger events.\n\t * @memberof ScrollEndEventEmitter\n\t * @instance\n\t */\n\tdisable: function () {\n\t\tthis.enabled = false;\n\t\tthis._unbindScroll();\n\t},\n\t/**\n\t * Set the element to compare to scroll position to\n\t * @memberof ScrollEndEventEmitter\n\t * @instance\n\t * @param {jQuery.Object} $el jQuery element where we want to listen for\n\t * scroll end.\n\t */\n\tsetElement: function ( $el ) {\n\t\tthis.$el = $el;\n\t}\n} );\n\nmodule.exports = ScrollEndEventEmitter;\n","var\n\tmfExtend = require( './mfExtend' ),\n\ticons = require( './icons' ),\n\tView = require( './View' );\n\n/**\n * Builds a section of a page\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: mw.template.get( 'mobile.startup', 'Section.hogan' ),\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 * @property {string} defaults.spinner HTML of the spinner icon.\n\t */\n\tdefaults: {\n\t\tline: undefined,\n\t\ttext: '',\n\t\tspinner: icons.spinner().toHtmlString()\n\t}\n} );\n\nmodule.exports = Section;\n","var\n\tbrowser = require( './Browser' ).getSingleton(),\n\tlazyImageLoader = require( './lazyImages/lazyImageLoader' ),\n\tlazyImageTransformer = require( './lazyImages/lazyImageTransformer' ),\n\tlazyReferencesLoader = require( './lazyReferencesLoader' ),\n\tView = require( './View' ),\n\tutil = require( './util' ),\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 * @fires Skin#references-loaded\n * @fires Skin#changed\n * @param {Object} params Configuration options\n * @param {OO.EventEmitter} params.eventBus Object used to listen for\n * scroll:throttled, resize:throttled, and section-toggled events\n */\nfunction Skin( params ) {\n\tvar self = this,\n\t\toptions = util.extend( {}, params );\n\n\tthis.page = options.page;\n\tthis.name = options.name;\n\tif ( options.mainMenu ) {\n\t\tthis.mainMenu = options.mainMenu;\n\t\tmw.log.warn( 'Skin: Use of mainMenu is deprecated.' );\n\t}\n\tthis.eventBus = options.eventBus;\n\toptions.isBorderBox = false;\n\tView.call( this, options );\n\tthis.referencesGateway = options.referencesGateway;\n\n\tif (\n\t\tmw.config.get( 'wgMFLazyLoadImages' )\n\t) {\n\t\tutil.docReady( function () {\n\t\t\tvar\n\t\t\t\tcontainer = document.getElementById( 'content' ),\n\t\t\t\t// todo: remove when tests are only headless. There is no #content in\n\t\t\t\t//       Special:JavaScriptTest.\n\t\t\t\timages = ( container && lazyImageLoader.queryPlaceholders( container ) ) || [];\n\t\t\tself.lazyImageTransformer = lazyImageTransformer.newLazyImageTransformer(\n\t\t\t\tself.eventBus, self.$el.find.bind( self.$el ),\n\t\t\t\tutil.getWindow().height() * 1.5, images\n\t\t\t);\n\t\t\tself.lazyImageTransformer.loadImages();\n\t\t} );\n\t}\n\n\tif ( mw.config.get( 'wgMFLazyLoadReferences' ) ) {\n\t\tthis.eventBus.on( 'section-toggled', function ( data ) {\n\t\t\tlazyReferencesLoader.loadReferences(\n\t\t\t\tself.eventBus, data, self.referencesGateway, self.page\n\t\t\t);\n\t\t} );\n\t}\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 * @property {ReferencesGateway} defaults.referencesGateway instance of references gateway\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\t\tif ( browser.supportsAnimations() ) {\n\t\t\t$el.addClass( 'animations' );\n\t\t}\n\t\tif ( browser.supportsTouchEvents() ) {\n\t\t\t$el.addClass( 'touch-events' );\n\t\t}\n\t\tutil.parseHTML( '<div class=\"transparent-shield cloaked-element\">' )\n\t\t\t.appendTo( $el.find( '#mw-mf-page-center' ) );\n\t\tif ( this.lazyImageTransformer ) {\n\t\t\tthis.lazyImageTransformer.loadImages();\n\t\t}\n\n\t\t/**\n\t\t * Fired when the skin is clicked.\n\t\t * @event Skin#click\n\t\t */\n\t\tthis.$el.find( '#mw-mf-page-center' ).on( 'click', this.emit.bind( this, 'click' ) );\n\t},\n\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 * @memberof Skin\n\t * @instance\n\t * @return {string}\n\t */\n\tgetLicenseMsg: function () {\n\t\tvar licenseMsg,\n\t\t\tmfLicense = mw.config.get( 'wgMFLicense' ),\n\t\t\tlicensePlural = mw.language.convertNumber( mfLicense.plural );\n\n\t\tif ( mfLicense.link ) {\n\t\t\tif ( this.$el.find( '#footer-places-terms-use' ).length > 0 ) {\n\t\t\t\tlicenseMsg = mw.msg(\n\t\t\t\t\t'mobile-frontend-editor-licensing-with-terms',\n\t\t\t\t\tmw.message(\n\t\t\t\t\t\t'mobile-frontend-editor-terms-link',\n\t\t\t\t\t\tthis.$el.find( '#footer-places-terms-use a' ).attr( 'href' )\n\t\t\t\t\t).parse(),\n\t\t\t\t\tmfLicense.link,\n\t\t\t\t\tlicensePlural\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tlicenseMsg = mw.msg(\n\t\t\t\t\t'mobile-frontend-editor-licensing',\n\t\t\t\t\tmfLicense.link,\n\t\t\t\t\tlicensePlural\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\treturn licenseMsg;\n\t}\n} );\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 * @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 * @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","/* global $, document */\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 * @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 * @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 * @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|HoganTemplate\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: M.template.get( 'some.hogan' ),\n\t *       templatePartials: { content: M.template.get( 'sub.hogan' ) }\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 * @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 * @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 * @memberof View\n\t * @instance\n\t * @private\n\t * @param {Object} props\n\t */\n\t_postInitialize: function ( props ) {\n\t\tvar\n\t\t\tBORDER_BOX_CLASS = 'view-border-box',\n\t\t\tclassName = props.className;\n\n\t\tthis.$el.addClass( 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( BORDER_BOX_CLASS );\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 * @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// eslint-disable-next-line valid-jsdoc\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 * Wraps this.$el.find, so that you can search for elements in the view's\n\t * ($el's) scope.\n\t *\n\t * @memberof View\n\t * @instance\n\t * @param {string} query A jQuery CSS selector.\n\t * @return {JQuery.Object} jQuery object containing results of the search.\n\t */\n\t$: function ( query ) {\n\t\tmw.log.warn( 'Use of View.$ is deprecated. Use View.$el.find instead.' );\n\t\treturn this.$el.find( query );\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 * `blur`, and not `change`, `submit`, and `reset` in Internet Explorer.\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 * @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 * @param {Object} otherParams\n * @return {Object}\n */\nfunction actionParams( otherParams ) {\n\tvar scriptPath = mw.config.get( 'wgMFContentProviderScriptPath' );\n\treturn util.extend( {}, defaultParams, {\n\t\torigin: scriptPath ? '*' : undefined\n\t}, otherParams );\n}\n\nmodule.exports = actionParams;\n","/* This module defines several types of cache classes to use in other\n * modules.\n * The interface, that all types use, is kept synchronous driven by current\n * usage patterns, but will need to be revisited in case usage patterns\n * suggest we need asynchronous caches.\n */\n\n/**\n * In memory cache implementation\n *\n * @class MemoryCache\n */\nfunction MemoryCache() {\n\tthis._cache = {};\n}\n\n/**\n * Retrieve a cached value from a key\n * @memberof MemoryCache\n * @instance\n * @param {string} key\n * @return {Mixed}\n */\nMemoryCache.prototype.get = function ( key ) {\n\treturn this._cache[ key ];\n};\n\n/**\n * Cache a value by key\n * @memberof MemoryCache\n * @instance\n * @param {string} key\n * @param {Mixed} value\n */\nMemoryCache.prototype.set = function ( key, value ) {\n\tthis._cache[ key ] = value;\n};\n\n/**\n * Null object cache implementation\n *\n * @class NoCache\n */\nfunction NoCache() { }\n\n/**\n * NoOp\n * @memberof NoCache\n * @instance\n */\nNoCache.prototype.get = function () { };\n\n/**\n * NoOp\n * @memberof NoCache\n * @instance\n */\nNoCache.prototype.set = function () { };\n\nmodule.exports = {\n\tMemoryCache: MemoryCache,\n\tNoCache: NoCache\n};\n","/**\n * Mobile mode helper class\n *\n * @class context\n * @singleton\n */\nmodule.exports = {\n\t/**\n\t * Gets current mobile mode\n\t * @memberof context\n\t * @instance\n\t * @return {string|null} Name of mode - either `stable` or `beta`. It is `null` if desktop.\n\t */\n\tgetMode: function () {\n\t\treturn mw.config.get( 'wgMFMode' );\n\t}\n};\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. (e.g.\n * mobile.special.nearby.scripts/nearby.js in Change-Id:\n * I632124515d4c26ae5ce77dd503d00a62e5a65dda)\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 *   'nearby',\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\tscriptPath = mw.config.get( 'wgMFContentProviderScriptPath' ),\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\n\tCANCEL_GLYPH = 'overlay-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 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 * @memberof icons\n\t * @instance\n\t * @param {string} variant\n\t * @return {Icon}\n\t */\n\tcancel: function ( variant ) {\n\t\tvar glyph = variant ? CANCEL_GLYPH + '-' + variant : CANCEL_GLYPH;\n\t\treturn new this.Icon( {\n\t\t\ttagName: 'button',\n\t\t\tname: glyph,\n\t\t\tadditionalClassNames: 'cancel',\n\t\t\tlabel: mw.msg( 'mobile-frontend-overlay-close' )\n\t\t} );\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 * @memberof icons\n\t * @instance\n\t * @param {Object} [options] See `Icon` for more details\n\t * @return {Icon}\n\t */\n\tspinner: function ( options ) {\n\t\toptions = options || {};\n\n\t\treturn new this.Icon( util.extend( options, {\n\t\t\tname: 'spinner',\n\t\t\tlabel: mw.msg( 'mobile-frontend-loading-message' ),\n\t\t\tadditionalClassNames: 'spinner loading'\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 * @return {Icon}\n\t */\n\twatchIcon: function () {\n\t\treturn new this.Icon( {\n\t\t\tname: 'watch',\n\t\t\tadditionalClassNames: 'watch-this-article'\n\t\t} );\n\t},\n\t/**\n\t * Gets a filled watch star icon.\n\t *\n\t * @memberof icons\n\t * @instance\n\t * @return {Icon}\n\t */\n\twatchedIcon: function () {\n\t\treturn new this.Icon( {\n\t\t\tname: 'watched',\n\t\t\tadditionalClassNames: 'watch-this-article watched'\n\t\t} );\n\t}\n};\n","/**\n * Return the language code of the device in lowercase\n *\n * @ignore\n * @param {window.navigator} navigator\n * @return {string|undefined}\n */\nfunction getDeviceLanguage( navigator ) {\n\tconst lang = navigator.languages ?\n\t\tnavigator.languages[ 0 ] :\n\t\tnavigator.language || navigator.userLanguage ||\n\t\t\t\tnavigator.browserLanguage || navigator.systemLanguage;\n\n\treturn lang ? lang.toLowerCase() : undefined;\n}\n\nmodule.exports = getDeviceLanguage;\n","const\n\tm = require( '../moduleLoaderSingleton' ),\n\tgetDeviceLanguage = require( './getDeviceLanguage' ),\n\tOverlay = require( '../Overlay' ),\n\tpromisedView = require( '../promisedView' );\n\n/**\n * @ignore\n * @param {PageGateway} gateway\n * @return {jQuery.Promise} Resolves to LanguageSearcher\n */\nfunction loadLanguageSearcher( gateway ) {\n\treturn mw.loader.using( 'mobile.languages.structured' ).then( function () {\n\t\treturn gateway.getPageLanguages( mw.config.get( 'wgPageName' ), mw.config.get( 'wgUserLanguage' ) );\n\t} ).then( function ( data ) {\n\t\tconst LanguageSearcher = m.require( 'mobile.languages.structured/LanguageSearcher' );\n\n\t\treturn new LanguageSearcher( {\n\t\t\tlanguages: data.languages,\n\t\t\tvariants: data.variants,\n\t\t\tdeviceLanguage: getDeviceLanguage( navigator )\n\t\t} );\n\n\t} );\n}\n\n/**\n * Factory function that returns a language featured instance of an Overlay\n *\n * @param {PageGateway} pageGateway\n * @return {Overlay}\n */\nfunction languageOverlay( pageGateway ) {\n\treturn Overlay.make(\n\t\t{\n\t\t\theading: mw.msg( 'mobile-frontend-language-heading' ),\n\t\t\tclassName: 'overlay language-overlay'\n\t\t}, promisedView( loadLanguageSearcher( pageGateway ) )\n\t);\n}\n\n// To make knowing when async logic has resolved easier in tests\nlanguageOverlay.test = {\n\tloadLanguageSearcher: loadLanguageSearcher\n};\n\nmodule.exports = languageOverlay;\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 * @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 * @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\timage.className = placeholder.dataset.class || '';\n\timage.alt = placeholder.dataset.alt || '';\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// Never reject. Quietly resolve so that jQuery.when() 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\tqueryPlaceholders: queryPlaceholders,\n\tloadImages: loadImages,\n\tloadImage: loadImage,\n\ttest: {\n\t\tplaceholderClass: placeholderClass\n\t}\n};\n","var\n\tlazyImageLoader = require( './lazyImageLoader' ),\n\tutil = require( '../util' );\n\n/**\n * Setup listeners to watch unloaded images and load them into the page\n * as and when they are needed.\n * @param {OO.EventEmitter} eventBus\n * @param {JQuery.find} find\n * @param {number} offset\n * @param {HTMLElement[]} imagePlaceholders\n * @return {{loadImages: function(): void}}.\n */\nfunction newLazyImageTransformer( eventBus, find, offset, imagePlaceholders ) {\n\t/**\n\t * Load remaining images in viewport\n\t * @return {jQuery.Deferred}\n\t */\n\tfunction loadImages() {\n\t\tvar placeholders = [];\n\t\t// Filter unloaded images to only the images that still need to be loaded\n\t\timagePlaceholders = util.grep( imagePlaceholders, function ( placeholder ) {\n\t\t\tvar $placeholder = find( placeholder );\n\t\t\t// Check length to ensure the image is still in the DOM.\n\t\t\tif ( $placeholder.length && shouldLoadImage( $placeholder, offset ) ) {\n\t\t\t\tplaceholders.push( placeholder );\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t} );\n\n\t\t// When no images are left unbind all events\n\t\tif ( !imagePlaceholders.length ) {\n\t\t\teventBus.off( 'scroll:throttled', loadImages );\n\t\t\teventBus.off( 'resize:throttled', loadImages );\n\t\t\teventBus.off( 'section-toggled', loadImages );\n\t\t}\n\n\t\t// load any remaining images.\n\t\treturn lazyImageLoader.loadImages( placeholders );\n\t}\n\n\teventBus.on( 'scroll:throttled', loadImages );\n\teventBus.on( 'resize:throttled', loadImages );\n\teventBus.on( 'section-toggled', loadImages );\n\n\treturn {\n\t\tloadImages: loadImages\n\t};\n}\n\n/**\n * Check whether an image should be loaded based on its proximity to the\n * viewport; and whether it is displayed to the user.\n * @param {jQuery.Object} $placeholder\n * @param {number} offset\n * @return {boolean}\n */\nfunction shouldLoadImage( $placeholder, offset ) {\n\treturn mw.viewport.isElementCloseToViewport( $placeholder[0], offset ) &&\n\t\t// If a placeholder is an inline element without a height attribute set\n\t\t// it will record as hidden\n\t\t// to circumvent this we also need to test the height (see T143768).\n\t\t( $placeholder.is( ':visible' ) || $placeholder.height() === 0 );\n}\n\nmodule.exports = {\n\tnewLazyImageTransformer: newLazyImageTransformer\n};\n","var\n\tlazyImageLoader = require( './lazyImages/lazyImageLoader' ),\n\tutil = require( './util' ),\n\tPage = require( './Page' ),\n\tDeferred = util.Deferred,\n\ticons = require( './icons' ),\n\tspinner = icons.spinner();\n\n/**\n * Load the references section content from API if it's not already loaded.\n *\n * All references tags content will be loaded per section.\n * @param {OO.EventEmitter} eventBus\n * @param {ToggledEvent} data Information about the section.\n * @param {ReferencesGateway} gateway\n * @param {Page} page\n * @return {jQuery.Promise|void} rejected when not a reference section.\n */\nfunction loadReferences( eventBus, data, gateway, page ) {\n\tvar $content, $spinner;\n\n\t// If the section was expanded before toggling, do not load anything as\n\t// section is being collapsed now.\n\t// Also return early if lazy loading is not required or the section is\n\t// not a reference section\n\tif (\n\t\tdata.expanded ||\n\t\t!data.isReferenceSection\n\t) {\n\t\treturn;\n\t}\n\n\t$content = data.$heading.next();\n\n\tfunction loadImagesAndSetData() {\n\t\t// lazy load images if any\n\t\tlazyImageLoader.loadImages( lazyImageLoader.queryPlaceholders( $content[0] ) );\n\t\t// Do not attempt further loading even if we're unable to load this time.\n\t\t$content.data( 'are-references-loaded', 1 );\n\t}\n\n\tif ( !$content.data( 'are-references-loaded' ) ) {\n\t\t$content.children().addClass( 'hidden' );\n\t\t$spinner = spinner.$el.prependTo( $content );\n\n\t\t// First ensure we retrieve all of the possible lists\n\t\treturn gateway.getReferencesLists( data.page )\n\t\t\t.then( function () {\n\t\t\t\tvar lastId;\n\n\t\t\t\t$content.find( '.mf-lazy-references-placeholder' ).each( function () {\n\t\t\t\t\tvar refListIndex = 0,\n\t\t\t\t\t\t$placeholder = $content.find( this ),\n\t\t\t\t\t\t// search for id of the collapsible heading\n\t\t\t\t\t\tid = getSectionId( $placeholder );\n\n\t\t\t\t\tif ( lastId !== id ) {\n\t\t\t\t\t\t// If the placeholder belongs to a new section reset index\n\t\t\t\t\t\trefListIndex = 0;\n\t\t\t\t\t\tlastId = id;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// otherwise increment it\n\t\t\t\t\t\trefListIndex++;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( id ) {\n\t\t\t\t\t\tgateway.getReferencesList( data.page, id )\n\t\t\t\t\t\t\t.then( function ( refListElements ) {\n\t\t\t\t\t\t\t\t// Note if no section html is provided\n\t\t\t\t\t\t\t\t// no substitution will happen\n\t\t\t\t\t\t\t\t// so user is forced to rely on placeholder link.\n\t\t\t\t\t\t\t\tif ( refListElements && refListElements[refListIndex] ) {\n\t\t\t\t\t\t\t\t\t$placeholder.replaceWith(\n\t\t\t\t\t\t\t\t\t\trefListElements[refListIndex]\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t\t// Show the section now the references lists have been placed.\n\t\t\t\t$spinner.remove();\n\t\t\t\t$content.children().removeClass( 'hidden' );\n\t\t\t\t/**\n\t\t\t\t * Fired when references list is loaded into the HTML\n\t\t\t\t * @event references-loaded\n\t\t\t\t */\n\t\t\t\teventBus.emit( 'references-loaded', page );\n\n\t\t\t\tloadImagesAndSetData();\n\t\t\t}, function () {\n\t\t\t\t$spinner.remove();\n\t\t\t\t// unhide on a failure\n\t\t\t\t$content.children().removeClass( 'hidden' );\n\n\t\t\t\tloadImagesAndSetData();\n\t\t\t} );\n\t} else {\n\t\treturn Deferred().reject().promise();\n\t}\n}\n\n/**\n * Get the id of the section $el belongs to.\n * @param {jQuery.Object} $el\n * @return {string|null} either the anchor (id attribute of the section heading\n *  or null if none found)\n */\nfunction getSectionId( $el ) {\n\tvar id,\n\t\thSelector = Page.HEADING_SELECTOR,\n\t\t$parent = $el.parent(),\n\t\t// e.g. matches Subheading in\n\t\t// <h2>H</h2><div><h3 id=\"subheading\">Subh</h3><a class=\"element\"></a></div>\n\t\t$heading = $el.prevAll( hSelector ).eq( 0 );\n\n\tif ( $heading.length ) {\n\t\tid = $heading.find( '.mw-headline' ).attr( 'id' );\n\t\tif ( id ) {\n\t\t\treturn id;\n\t\t}\n\t}\n\tif ( $parent.length ) {\n\t\t// if we couldnt find a sibling heading, check the sibling of the parents\n\t\t// consider <div><h2 /><div><$el/></div></div>\n\t\treturn getSectionId( $parent );\n\t} else {\n\t\treturn null;\n\t}\n}\n\nmodule.exports = {\n\tloadReferences: loadReferences,\n\ttest: {\n\t\tgetSectionId: getSectionId\n\t}\n};\n","var\n\ticons = require( './icons' ),\n\tOverlay = require( './Overlay' );\n\n/**\n * Overlay that initially shows loading animation until\n * caller hides it with .hide()\n *\n * @return {Overlay}\n */\nfunction loadingOverlay() {\n\tvar overlay = new Overlay( {\n\t\tclassName: 'overlay overlay-loading',\n\t\tnoHeader: true\n\t} );\n\ticons.spinner().$el.appendTo( overlay.$el.find( '.overlay-content' ) );\n\treturn overlay;\n}\n\nmodule.exports = loadingOverlay;\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 * @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 * @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 * @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","var Overlay = require( '../Overlay' ),\n\tm = require( '../moduleLoaderSingleton' ),\n\tpromisedView = require( '../promisedView' ),\n\tView = require( '../View' ),\n\tAnchor = require( '../Anchor' );\n\n/**\n * This callback is displayed as a global member.\n * @callback countChangeCallback\n * @param {number} count a capped (0-99 or 99+) count\n */\n\n/**\n * Make a notification overlay\n *\n * @param {countChangeCallback} onCountChange receives one parameter - a capped (0-99 or 99+) count.\n * @param {Function} onNotificationListRendered a function that is called when the\n *   notifications list has fully rendered (taking no arguments)\n * @return {Overlay}\n */\nfunction notificationsOverlay( onCountChange, onNotificationListRendered ) {\n\tvar markAllReadButton,\n\t\toouiPromise = mw.loader.using( 'mobile.notifications.overlay' ).then( function () {\n\t\t\tmarkAllReadButton = new OO.ui.ButtonWidget( {\n\t\t\t\ticon: 'checkAll',\n\t\t\t\ttitle: mw.msg( 'echo-mark-all-as-read' )\n\t\t\t} );\n\t\t\treturn View.make(\n\t\t\t\t{ class: 'notifications-overlay-header-markAllRead' },\n\t\t\t\t[ markAllReadButton.$element ]\n\t\t\t);\n\t\t} ),\n\t\tmarkAllReadButtonView = promisedView( oouiPromise );\n\t// hide the button spinner as it is confusing to see in the top right corner\n\tmarkAllReadButtonView.$el.hide();\n\n\treturn Overlay.make(\n\t\t{\n\t\t\theading: '<strong>' + mw.message( 'notifications' ).escaped() + '</strong>',\n\t\t\tfooterAnchor: new Anchor( {\n\t\t\t\thref: mw.util.getUrl( 'Special:Notifications' ),\n\t\t\t\tprogressive: true,\n\t\t\t\tadditionalClassNames: 'footer-link notifications-archive-link',\n\t\t\t\tlabel: mw.msg( 'echo-overlay-link' )\n\t\t\t} ).options,\n\t\t\theaderActions: [ markAllReadButtonView ],\n\t\t\tisBorderBox: false,\n\t\t\tclassName: 'overlay notifications-overlay navigation-drawer'\n\t\t},\n\t\tpromisedView(\n\t\t\toouiPromise.then( function () {\n\t\t\t\tvar list = m.require( 'mobile.notifications.overlay' ).list;\n\t\t\t\treturn list( mw.echo, markAllReadButton, onCountChange,\n\t\t\t\t\tonNotificationListRendered );\n\t\t\t} )\n\t\t)\n\t);\n}\n\nmodule.exports = notificationsOverlay;\n","var\n\ticons = require( './icons' ),\n\tView = require( './View' );\n\n/**\n * It's a view that spins until the promise resolves!\n * If the promise successfully resolves, the newView will be shown. if the\n * promise rejects and rejects to a view, the errorView will be shown.\n *\n * @param {jQuery.Promise} promise\n * @return {View}\n */\nfunction promisedView( promise ) {\n\tvar view = new View( {\n\t\tclassName: 'promised-view'\n\t} );\n\tview.$el.append( icons.spinner().$el );\n\tpromise.then( function ( newView ) {\n\t\tview.$el.replaceWith( newView.$el );\n\t\t// update the internal reference.\n\t\tview.$el = newView.$el;\n\t}, function ( errorView ) {\n\t\tif ( !errorView || !errorView.$el ) {\n\t\t\t// return early to keep backwards compatibility with clients of\n\t\t\t// promisedView that do not reject to an error view\n\t\t\treturn;\n\t\t}\n\n\t\tview.$el.replaceWith( errorView.$el );\n\t\t// update the internal reference.\n\t\tview.$el = errorView.$el;\n\t} );\n\n\treturn view;\n}\n\nmodule.exports = promisedView;\n","/**\n * Abstract base class\n * Gateway for retrieving references\n *\n * @class ReferencesGateway\n * @abstract\n *\n * @param {mw.Api} api\n */\nfunction ReferencesGateway( api ) {\n\tthis.api = api;\n}\n\n/**\n * Return the matched reference via API or DOM query\n *\n * @memberof ReferencesGateway\n * @instance\n * @param {string} id CSS selector\n * @param {Page} page to find reference for\n * @return {jQuery.Promise} resolves with an Object representing reference\n *  with a `text` property\n *  The promise should be rejected with ReferenceGateway.ERROR_NOT_EXIST:\n *  if the reference is not found.\n *  If for some reason locating the reference fails return ReferenceGateway.ERROR_OTHER.\n */\nReferencesGateway.prototype.getReference = null;\n\n/**\n * @memberof ReferencesGateway\n * @property ERROR_NOT_EXIST error code to be returned by getReference\n *  when a reference does not exist.\n */\nReferencesGateway.ERROR_NOT_EXIST = 'NOT_EXIST_ERROR';\n/**\n * @memberof ReferencesGateway\n * @property ERROR_OTHER error code to be returned by getReference\n *  under any other circumstance not covered\n *  by ERROR_NOT_EXIST. It should be used if it is unclear whether a reference exists or not.\n */\nReferencesGateway.ERROR_OTHER = 'OTHER_ERROR';\n\nmodule.exports = ReferencesGateway;\n","var ReferencesGateway = require( './ReferencesGateway' ),\n\tmfExtend = require( './../mfExtend' ),\n\tutil = require( './../util' );\n\n/**\n * Gateway for retrieving references via the content of the Page\n *\n * @class ReferencesHtmlScraperGateway\n * @extends ReferencesGateway\n * @inheritdoc\n */\nfunction ReferencesHtmlScraperGateway() {\n\tReferencesGateway.apply( this, arguments );\n}\n\nmfExtend( ReferencesHtmlScraperGateway, ReferencesGateway, {\n\t/**\n\t * @memberof ReferencesHtmlScraperGateway\n\t * @instance\n\t * @param {string} id of a DOM element in the page with '#' prefix.\n\t *  can be encoded or decoded.\n\t * @param {jQuery.Object} $container to scan for an element\n\t * @return {jQuery.Promise} that can be used by getReference\n\t */\n\tgetReferenceFromContainer: function ( id, $container ) {\n\t\tvar $el,\n\t\t\tresult = util.Deferred();\n\n\t\t$el = $container.find( '#' + util.escapeSelector( id.substr( 1 ) ) );\n\t\tif ( $el.length ) {\n\t\t\tresult.resolve( { text: $el.html() } );\n\t\t} else {\n\t\t\tresult.reject( ReferencesGateway.ERROR_NOT_EXIST );\n\t\t}\n\t\treturn result.promise();\n\t},\n\t/**\n\t * @inheritdoc\n\t * @memberof ReferencesHtmlScraperGateway\n\t * @instance\n\t */\n\tgetReference: function ( id, page ) {\n\t\t// If an id is not found it's possible the id passed needs decoding (per T188547).\n\t\treturn this.getReferenceFromContainer( decodeURIComponent( id ), page.$el.find( 'ol.references' ) );\n\t}\n} );\n\nmodule.exports = ReferencesHtmlScraperGateway;\n","var ReferencesHtmlScraperGateway =\n\trequire( './ReferencesHtmlScraperGateway' ),\n\tcache = require( './../cache' ),\n\tReferencesGateway = require( './ReferencesGateway' ),\n\tMemoryCache = cache.MemoryCache,\n\tutil = require( './../util' ),\n\tmfExtend = require( './../mfExtend' ),\n\tNoCache = cache.NoCache,\n\treferencesMobileViewGateway = null;\n\n/**\n * Gateway for retrieving references via the MobileView API\n *\n * By default not cached, if it receives a cache instance then it will be\n * used to store and get references sections.\n *\n * @class ReferencesMobileViewGateway\n * @extends ReferencesHtmlScraperGateway\n * @inheritdoc\n *\n * @param {mw.Api} api class to use for making requests\n * @param {NoCache|MemoryCache} [cache] class to use for caching request\n * results. By default it uses the NoCache implementation, which doesn't\n * cache anything. The singleton instance exposed by this module uses\n * a MemoryCache which caches requests in-memory. Any other Cache class\n * compatible with mobile.startup/cache's interface will actually work.\n */\nfunction ReferencesMobileViewGateway( api, cache ) {\n\tReferencesHtmlScraperGateway.call( this, api );\n\tthis.cache = cache || new NoCache();\n}\n\nmfExtend( ReferencesMobileViewGateway, ReferencesHtmlScraperGateway, {\n\t/**\n\t * Retrieve references list for a given page.\n\t * Also cache the result for a later use.\n\t *\n\t * @memberof ReferencesMobileViewGateway\n\t * @instance\n\t * @param {Page} page\n\t * @return {jQuery.Deferred} promise that resolves with the list of\n\t *  sections on the page\n\t */\n\tgetReferencesLists: function ( page ) {\n\t\tvar self = this,\n\t\t\tresult = util.Deferred(),\n\t\t\tcachedReferencesSections = this.cache.get( page.id );\n\n\t\tif ( cachedReferencesSections ) {\n\t\t\treturn result.resolve( cachedReferencesSections ).promise();\n\t\t}\n\n\t\tthis.api.get( {\n\t\t\taction: 'mobileview',\n\t\t\tpage: page.getTitle(),\n\t\t\tsections: 'references',\n\t\t\tprop: 'text',\n\t\t\trevision: page.getRevisionId()\n\t\t} ).then( function ( data ) {\n\t\t\tvar sections = {};\n\n\t\t\tdata.mobileview.sections.forEach( function ( section ) {\n\t\t\t\tvar $section = util.parseHTML( '<div>' ).html( section.text );\n\n\t\t\t\tsections[ $section.find( '.mw-headline' ).attr( 'id' ) ] = $section.find( '.references' );\n\t\t\t} );\n\n\t\t\tself.cache.set( page.id, sections );\n\t\t\tresult.resolve( sections );\n\t\t}, function () {\n\t\t\tresult.reject( ReferencesGateway.ERROR_OTHER );\n\t\t} );\n\n\t\treturn result.promise();\n\t},\n\t/**\n\t * Retrieve all the references lists for a given page and section ID.\n\t *\n\t * @memberof ReferencesMobileViewGateway\n\t * @instance\n\t * @param {Page} page\n\t * @param {string} headingId\n\t * @return {jQuery.Promise} promise that resolves with the section\n\t *  HTML or `false` if no such section exists\n\t */\n\tgetReferencesList: function ( page, headingId ) {\n\t\treturn this.getReferencesLists( page ).then( function ( data ) {\n\t\t\treturn Object.prototype.hasOwnProperty.call( data, headingId ) ?\n\t\t\t\tdata[ headingId ] : false;\n\t\t} );\n\t},\n\t/**\n\t * @inheritdoc\n\t * @memberof ReferencesMobileViewGateway\n\t * @instance\n\t */\n\tgetReference: function ( id, page ) {\n\t\tvar self = this;\n\n\t\treturn this.getReferencesLists( page ).then( function ( sections ) {\n\t\t\tvar $container = util.parseHTML( '<div>' );\n\n\t\t\tObject.keys( sections ).forEach( function ( sectionId ) {\n\t\t\t\t$container.append( sections[ sectionId ] );\n\t\t\t} );\n\n\t\t\treturn self.getReferenceFromContainer( id, $container );\n\t\t} );\n\t}\n} );\n\n/**\n * Retrieve a singleton instance w/ cache that uses mw.Api\n * @memberof ReferencesMobileViewGateway\n * @return {ReferencesMobileViewGateway}\n */\nReferencesMobileViewGateway.getSingleton = function () {\n\tif ( !referencesMobileViewGateway ) {\n\t\treferencesMobileViewGateway = new ReferencesMobileViewGateway(\n\t\t\tnew mw.Api(),\n\t\t\tnew MemoryCache()\n\t\t);\n\t}\n\treturn referencesMobileViewGateway;\n};\n\nmodule.exports = ReferencesMobileViewGateway;\n","var\n\tloadingOverlay = require( './loadingOverlay' ),\n\tutil = require( './util' );\n\n/**\n * Utility library for looking up details on the current user\n * @class loader\n * @singleton\n */\nmodule.exports = {\n\t// Exported to support testing and stubbing\n\tnewLoadingOverlay: function () {\n\t\treturn loadingOverlay();\n\t},\n\t/**\n\t * Loads a module via ResourceLoader\n\t * and displays a full screen LoadingOverlay during load time.\n\t * @memberof loader\n\t * @instance\n\t * @param {string} name ResourceLoader module name to load asynchronously.\n\t * @param {boolean} delegateHide if true the caller is responsible for hiding\n\t *  the intermediate loader.\n\t * @param {boolean} [showLoadingOverlay] if false a loading overlay will be hidden while\n\t *  loading the module. Defaults to true.\n\t * @return {jQuery.Promise}\n\t */\n\tloadModule: function ( name, delegateHide, showLoadingOverlay ) {\n\t\tvar loadingOverlay = this.newLoadingOverlay();\n\n\t\tshowLoadingOverlay = ( showLoadingOverlay !== undefined ) ? showLoadingOverlay : true;\n\t\tif ( showLoadingOverlay ) {\n\t\t\tloadingOverlay.appendTo( document.body );\n\t\t\tloadingOverlay.show();\n\t\t}\n\n\t\tfunction hideOverlayIfNeeded() {\n\t\t\tif ( !delegateHide && showLoadingOverlay ) {\n\t\t\t\tloadingOverlay.hide();\n\t\t\t}\n\t\t}\n\t\treturn mw.loader.using( name ).then( function () {\n\t\t\thideOverlayIfNeeded();\n\n\t\t\treturn loadingOverlay;\n\t\t}, function () {\n\t\t\thideOverlayIfNeeded();\n\n\t\t\treturn util.Deferred().reject().promise();\n\t\t} );\n\t}\n};\n","var\n\tPage = require( '../Page' ),\n\tutil = require( '../util' ),\n\textendSearchParams = require( '../extendSearchParams' );\n\n/**\n * @class SearchGateway\n * @uses mw.Api\n * @param {mw.Api} api\n */\nfunction SearchGateway( api ) {\n\tthis.api = api;\n\tthis.searchCache = {};\n\tthis.generator = mw.config.get( 'wgMFSearchGenerator' );\n}\n\nSearchGateway.prototype = {\n\t/**\n\t * The namespace to search in.\n\t * @memberof SearchGateway\n\t * @instance\n\t * @type {number}\n\t */\n\tsearchNamespace: 0,\n\n\t/**\n\t * Get the data used to do the search query api call.\n\t * @memberof SearchGateway\n\t * @instance\n\t * @param {string} query to search for\n\t * @return {Object}\n\t */\n\tgetApiData: function ( query ) {\n\t\tvar prefix = this.generator.prefix,\n\t\t\tdata = extendSearchParams( 'search', {\n\t\t\t\tgenerator: this.generator.name\n\t\t\t} );\n\n\t\tdata.redirects = '';\n\n\t\tdata['g' + prefix + 'search'] = query;\n\t\tdata['g' + prefix + 'namespace'] = this.searchNamespace;\n\t\tdata['g' + prefix + 'limit'] = 15;\n\n\t\t// If PageImages is being used configure further.\n\t\tif ( data.pilimit ) {\n\t\t\tdata.pilimit = 15;\n\t\t\tdata.pithumbsize = mw.config.get( 'wgMFThumbnailSizes' ).tiny;\n\t\t}\n\t\treturn data;\n\t},\n\n\t/**\n\t * Escapes regular expression wildcards (metacharacters) by adding a \\\\ prefix\n\t * @memberof SearchGateway\n\t * @instance\n\t * @param {string} str a string\n\t * @return {Object} a regular expression that can be used to search for that str\n\t * @private\n\t */\n\t_createSearchRegEx: function ( str ) {\n\t\t// '\\[' can be unescaped, but leave it balanced with '`]'\n\t\t// eslint-disable-next-line no-useless-escape\n\t\tstr = str.replace( /[-\\[\\]{}()*+?.,\\\\^$|#\\s]/g, '\\\\$&' );\n\t\treturn new RegExp( '^(' + str + ')', 'ig' );\n\t},\n\n\t/**\n\t * Takes a label potentially beginning with term\n\t * and highlights term if it is present with strong\n\t * @memberof SearchGateway\n\t * @instance\n\t * @param {string} label a piece of text\n\t * @param {string} term a string to search for from the start\n\t * @return {string} safe html string with matched terms encapsulated in strong tags\n\t * @private\n\t */\n\t_highlightSearchTerm: function ( label, term ) {\n\t\tlabel = util.parseHTML( '<span>' ).text( label ).html();\n\t\tterm = util.parseHTML( '<span>' ).text( term ).html();\n\n\t\treturn label.replace( this._createSearchRegEx( term ), '<strong>$1</strong>' );\n\t},\n\n\t/**\n\t * Return data used for creating {Page} objects\n\t * @memberof SearchGateway\n\t * @instance\n\t * @param {string} query to search for\n\t * @param {Object} pageInfo from the API\n\t * @return {Object} data needed to create a {Page}\n\t * @private\n\t */\n\t_getPage: function ( query, pageInfo ) {\n\t\tvar page = Page.newFromJSON( pageInfo );\n\n\t\t// If displaytext is set in the generator result (eg. by Wikibase),\n\t\t// use that as display title.\n\t\t// Otherwise default to the page's title.\n\t\t// FIXME: Given that displayTitle could have html in it be safe and just highlight text.\n\t\t// Note that highlightSearchTerm does full HTML escaping before highlighting.\n\t\tpage.displayTitle = this._highlightSearchTerm(\n\t\t\tpageInfo.displaytext ? pageInfo.displaytext : page.title,\n\t\t\tquery\n\t\t);\n\t\tpage.index = pageInfo.index;\n\n\t\treturn page;\n\t},\n\n\t/**\n\t * Process the data returned by the api call.\n\t * @memberof SearchGateway\n\t * @instance\n\t * @param {string} query to search for\n\t * @param {Object} data from api\n\t * @return {Array}\n\t * @private\n\t */\n\t_processData: function ( query, data ) {\n\t\tvar self = this,\n\t\t\tresults = [];\n\n\t\tif ( data.query ) {\n\n\t\t\tresults = data.query.pages || {};\n\t\t\tresults = Object.keys( results ).map( function ( id ) {\n\t\t\t\treturn self._getPage( query, results[ id ] );\n\t\t\t} );\n\t\t\t// sort in order of index\n\t\t\tresults.sort( function ( a, b ) {\n\t\t\t\treturn a.index < b.index ? -1 : 1;\n\t\t\t} );\n\t\t}\n\n\t\treturn results;\n\t},\n\n\t/**\n\t * Perform a search for the given query.\n\t * @memberof SearchGateway\n\t * @instance\n\t * @param {string} query to search for\n\t * @return {jQuery.Deferred}\n\t */\n\tsearch: function ( query ) {\n\t\tvar xhr, request,\n\t\t\tself = this;\n\n\t\tif ( !this.isCached( query ) ) {\n\t\t\txhr = this.api.get( this.getApiData( query ) );\n\t\t\trequest = xhr\n\t\t\t\t.then( function ( data ) {\n\t\t\t\t\t// resolve the Deferred object\n\t\t\t\t\treturn {\n\t\t\t\t\t\tquery: query,\n\t\t\t\t\t\tresults: self._processData( query, data )\n\t\t\t\t\t};\n\t\t\t\t}, function () {\n\t\t\t\t\t// reset cached result, it maybe contains no value\n\t\t\t\t\tself.searchCache[query] = undefined;\n\t\t\t\t} );\n\n\t\t\t// cache the result to prevent the execution of one search query twice\n\t\t\t// in one session\n\t\t\tthis.searchCache[query] = request.promise( {\n\t\t\t\tabort: function () { xhr.abort(); }\n\t\t\t} );\n\t\t}\n\n\t\treturn this.searchCache[query];\n\t},\n\n\t/**\n\t * Check if the search has already been performed in given session.\n\t * @memberof SearchGateway\n\t * @instance\n\t * @param {string} query\n\t * @return {boolean}\n\t */\n\tisCached: function ( query ) {\n\t\treturn Boolean( this.searchCache[ query ] );\n\t}\n};\n\nmodule.exports = SearchGateway;\n","var m = require( '../moduleLoaderSingleton' ),\n\tpromisedView = require( '../promisedView' ),\n\tAnchor = require( '../Anchor' ),\n\tutil = require( '../util' ),\n\tOverlay = require( '../Overlay' );\n\n/**\n * Produce an overlay for talk page\n * @param {string} title of the page to get talk topics for\n * @param {PageGateway} gateway for interacting with API.\n * @return {Overlay}\n */\nfunction talkOverlay( title, gateway ) {\n\tvar user = mw.user;\n\n\treturn Overlay.make(\n\t\t{\n\t\t\theading: '<strong>' + mw.msg( 'mobile-frontend-talk-overlay-header' ) + '</strong>',\n\t\t\theaderButtons: [ user.isAnon() ? {} : {\n\t\t\t\thref: '#/talk/new',\n\t\t\t\tclassName: 'continue',\n\t\t\t\tmsg: mw.msg( 'mobile-frontend-talk-add-overlay-submit' )\n\t\t\t} ],\n\t\t\tfooterAnchor: new Anchor( {\n\t\t\t\tprogressive: true,\n\t\t\t\thref: mw.util.getUrl( title ),\n\t\t\t\tadditionalClassNames: 'footer-link talk-fullpage',\n\t\t\t\tlabel: mw.msg( 'mobile-frontend-talk-fullpage' )\n\t\t\t} ).options,\n\t\t\tclassName: 'talk-overlay overlay'\n\t\t},\n\t\tpromisedView(\n\t\t\tutil.Promise.all( [\n\t\t\t\tgateway.getSections( title ),\n\t\t\t\tmw.loader.using( 'mobile.talk.overlays' )\n\t\t\t] ).then( function ( sections ) {\n\t\t\t\tvar talkBoard = m.require( 'mobile.talk.overlays/talkBoard' );\n\t\t\t\treturn talkBoard( sections );\n\t\t\t} )\n\t\t)\n\t);\n}\n\nmodule.exports = talkOverlay;\n","var units = [ 'seconds', 'minutes', 'hours', 'days', 'months', 'years' ],\n\tutil = require( './util' ),\n\tlimits = [ 1, 60, 3600, 86400, 2592000, 31536000 ];\n\n/** @class time */\n\n/**\n * Calculate the correct unit of timestamp\n * @memberof time\n * @instance\n * @param {number} timestampDelta\n * @return {{value: number, unit: string}}\n */\nfunction timeAgo( timestampDelta ) {\n\tvar i = 0;\n\twhile ( i < limits.length && timestampDelta > limits[i + 1] ) {\n\t\t++i;\n\t}\n\treturn {\n\t\tvalue: Math.round( timestampDelta / limits[i] ),\n\t\tunit: units[i]\n\t};\n}\n\n/**\n * Calculate the correct unit of timestamp delta\n * @memberof time\n * @instance\n * @param {number} timestamp\n * @return {{value: number, unit: string}}\n */\nfunction getTimeAgoDelta( timestamp ) {\n\tvar currentTimestamp = Math.round( new Date().getTime() / 1000 );\n\n\treturn timeAgo( currentTimestamp - timestamp );\n}\n\n/**\n * Whether timestamp delta is less than a day old\n * @memberof time\n * @instance\n * @param {{value: number, unit: string}} delta Object of timestamp and its label\n * @return {boolean}\n */\nfunction isRecent( delta ) {\n\treturn [ 'seconds', 'minutes', 'hours' ].indexOf( delta.unit ) > -1;\n}\n\n/**\n * Is delta less than 10 seconds?\n * @memberof time\n * @instance\n * @param {{value: number, unit: string}} delta Object of timestamp and its label\n * @return {boolean}\n */\nfunction isNow( delta ) {\n\treturn delta.unit === 'seconds' && delta.value < 10;\n}\n\n/**\n * Return a message relating to the last modified relative time.\n * @memberof time\n * @instance\n * @param {number} ts timestamp\n * @param {string} username of the last user to modify the page\n * @param {string} [gender] of the last user to modify the page\n * @param {string} [historyUrl] url to the history page for the message, if omitted\n *  returns plain text string rather than html\n * @return {string}\n */\nfunction getLastModifiedMessage( ts, username, gender, historyUrl ) {\n\tvar delta, html,\n\t\tkeys = {\n\t\t\tseconds: 'mobile-frontend-last-modified-with-user-seconds',\n\t\t\tminutes: 'mobile-frontend-last-modified-with-user-minutes',\n\t\t\thours: 'mobile-frontend-last-modified-with-user-hours',\n\t\t\tdays: 'mobile-frontend-last-modified-with-user-days',\n\t\t\tmonths: 'mobile-frontend-last-modified-with-user-months',\n\t\t\tyears: 'mobile-frontend-last-modified-with-user-years'\n\t\t},\n\t\targs = [];\n\n\tgender = gender || 'unknown';\n\n\tdelta = getTimeAgoDelta( ts );\n\tif ( isNow( delta ) ) {\n\t\targs.push( 'mobile-frontend-last-modified-with-user-just-now', gender, username );\n\t} else {\n\t\targs.push( keys[ delta.unit ], gender, username,\n\t\t\tmw.language.convertNumber( delta.value )\n\t\t);\n\t}\n\n\targs.push(\n\t\thistoryUrl || '#',\n\t\t// Abuse PLURAL support to determine if the user is anonymous or not\n\t\tmw.language.convertNumber( username ? 1 : 0 ),\n\t\t// Our abuse of PLURAL support means we have to pass the relative URL\n\t\t// rather than construct it from a wikilink\n\t\tusername ? mw.util.getUrl( 'User:' + username ) : ''\n\t);\n\thtml = mw.message.apply( this, args ).parse();\n\tif ( historyUrl ) {\n\t\treturn html;\n\t} else {\n\t\treturn util.parseHTML( '<div>' ).html( html ).text();\n\t}\n}\n\n/**\n * Return a message relating to the registration date of the user\n * @memberof time\n * @instance\n * @param {string} ts timestamp\n * @param {string} [gender] of the last user editing this page\n * @return {string}\n */\nfunction getRegistrationMessage( ts, gender ) {\n\tvar delta, html,\n\t\tkeys = {\n\t\t\tseconds: 'mobile-frontend-joined-seconds',\n\t\t\tminutes: 'mobile-frontend-joined-minutes',\n\t\t\thours: 'mobile-frontend-joined-hours',\n\t\t\tdays: 'mobile-frontend-joined-days',\n\t\t\tmonths: 'mobile-frontend-joined-months',\n\t\t\tyears: 'mobile-frontend-joined-years'\n\t\t},\n\t\targs = [];\n\n\tgender = gender || 'unknown';\n\n\tdelta = getTimeAgoDelta( parseInt( ts, 10 ) );\n\tif ( isNow( delta ) ) {\n\t\targs.push( 'mobile-frontend-joined-just-now', gender );\n\t} else {\n\t\targs.push( keys[ delta.unit ], gender, mw.language.convertNumber( delta.value ) );\n\t}\n\thtml = mw.message.apply( this, args ).parse();\n\treturn html;\n}\n\nmodule.exports = {\n\tgetLastModifiedMessage: getLastModifiedMessage,\n\tgetRegistrationMessage: getRegistrationMessage,\n\ttimeAgo: timeAgo,\n\tgetTimeAgoDelta: getTimeAgoDelta,\n\tisNow: isNow,\n\tisRecent: isRecent\n};\n","var\n\tutil = require( './util' ),\n\tstorageKey = 'mobileFrontend/toast';\n\n/**\n * Wrapper for one global Toast\n * @class Toast\n */\nfunction Toast() {\n\tmw.requestIdleCallback( this._showPending.bind( this ) );\n}\n\n/**\n * Show a message with the given class in a toast.\n * @memberof Toast\n * @instance\n * @param {string} msg Message to show in the toast\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 */\nToast.prototype.show = function ( msg, options ) {\n\tif ( typeof options === 'string' ) {\n\t\tmw.log.warn( 'The use of the cssClass parameter of Toast.show is deprecated, please convert it to an ' +\n\t\t\t\t'options object.' );\n\t\toptions = {\n\t\t\ttype: options\n\t\t};\n\t}\n\n\toptions = util.extend( {\n\t\ttag: 'toast'\n\t}, options );\n\n\tthis.notification = mw.notify( msg, options );\n};\n\n/**\n * Hide the Toast if it's visible.\n * @memberof Toast\n * @instance\n */\nToast.prototype.hide = function () {\n\tif ( this.notification !== undefined ) {\n\t\tthis.notification.then( function ( notif ) {\n\t\t\tnotif.close();\n\t\t} );\n\t}\n};\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 * @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 */\nToast.prototype.showOnPageReload = function ( 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\n/**\n * Show the previously saved toast data and delete it from storage\n * @memberof Toast\n * @instance\n * @private\n */\nToast.prototype._showPending = function () {\n\tvar data = mw.storage.get( storageKey );\n\tif ( data ) {\n\t\tdata = JSON.parse( data );\n\t\tthis.show( data.content, data.options );\n\t\tmw.storage.remove( storageKey );\n\t}\n};\n\nmodule.exports = new Toast();\n","/* global $ */\n\n/**\n * Utility library\n * @class util\n * @singleton\n */\nmodule.exports = {\n\t/**\n\t * Wrapper class for Promises\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 * @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 * @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 * Wrapper class for the $.grep\n\t * @memberof util\n\t * @instance\n\t * @return {jQuery.Deferred}\n\t */\n\tgrep: function () {\n\t\treturn $.grep.apply( $, arguments );\n\t},\n\t/**\n\t * Run method when document is ready.\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 * @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 * @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 * @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 * @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 util function to check if something is numeric\n\t * @memberof util\n\t * @instance\n\t * @return {boolean}\n\t */\n\tisNumeric: function () {\n\t\treturn $.isNumeric.apply( $, arguments );\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\t/**\n\t * Escape dots and colons in a hash, jQuery doesn't like them because they\n\t * look like CSS classes and pseudoclasses. See\n\t * http://bugs.jquery.com/ticket/5241\n\t * http://stackoverflow.com/questions/350292/how-do-i-get-jquery-to-select-elements-with-a-period-in-their-id\n\t * @memberof util\n\t * @instance\n\t * @param {string} hash A hash to escape\n\t * @return {string}\n\t */\n\tescapeHash: function ( hash ) {\n\t\treturn hash.replace( /(:|\\.)/g, '\\\\$1' );\n\t},\n\n\t/**\n\t * Heuristic for determining whether an Event should be handled by\n\t * MobileFrontend or allowed to bubble to the browser.\n\t * @memberof util\n\t * @instance\n\t * @param {Event} ev\n\t * @return {boolean} True if event is modified with control, alt, meta, or\n\t *                   shift keys and should probably be handled by the\n\t *                   browser.\n\t *\n\t * todo: move this function to a ClickUtil file once bundling and code\n\t * splitting is supported.\n\t */\n\tisModifiedEvent: function ( ev ) {\n\t\treturn ev.altKey || ev.ctrlKey || ev.metaKey || ev.shiftKey;\n\t},\n\n\t/**\n\t * Pipe event emitted by source through proxy. Subscribers to proxy will receive the event as\n\t * though proxy was the originator.\n\t *\n\t * @param {OO.EventEmitter} src\n\t * @param {OO.EventEmitter} proxy\n\t * @param {string} event Event type to listen for.\n\t * @param {any[]} [args] Arguments to pass to\n\t *  subscribers, will be prepended to emitted arguments.\n\t * @return {OO.EventEmitter} The source.\n\t */\n\trepeatEvent: function ( src, proxy, event, args ) {\n\t\treturn src.on( event, function ( args ) {\n\t\t\treturn proxy.emit( event, args );\n\t\t}, args );\n\t}\n};\n","var View = require( '../View' ),\n\tWatchstarGateway = require( './WatchstarGateway' ),\n\ticons = require( '../icons' ),\n\tutil = require( '../util' ),\n\tmfExtend = require( '../mfExtend' ),\n\ttoast = require( '../toast' ),\n\tCtaDrawer = require( '../CtaDrawer' );\n\n/**\n * A clickable watchstar\n * @class Watchstar\n * @extends View\n * @uses CtaDrawer\n * @uses Icon\n * @uses WatchstarGateway\n * @uses Toast\n * @fires Watchstar#unwatch\n * @fires Watchstar#watch\n *\n * @param {Object} options Configuration options\n */\nfunction Watchstar( options ) {\n\tView.call(\n\t\tthis,\n\t\tutil.extend(\n\t\t\ttrue,\n\t\t\t{\n\t\t\t\tclassName: icons.watchIcon().getClassName(),\n\t\t\t\tevents: {\n\t\t\t\t\tclick: 'onStatusToggle'\n\t\t\t\t}\n\t\t\t},\n\t\t\toptions\n\t\t)\n\t);\n}\n\nmfExtend( Watchstar, View, {\n\t/**\n\t * @memberof Watchstar\n\t * @instance\n\t * @mixes View#defaults\n\t * @property {Object} defaults Default options hash.\n\t * @property {mw.Api} defaults.api\n\t * @property {Page} defaults.page Current page.\n\t * @property {string} defaults.funnel to log events with\n\t */\n\tdefaults: {\n\t\tpage: undefined,\n\t\tfunnel: 'unknown'\n\t},\n\t/**\n\t * @memberof Watchstar\n\t * @instance\n\t * @property {Object} ctaDrawerOptions Default options hash for the anonymous CtaDrawer.\n\t */\n\tctaDrawerOptions: {\n\t\tcontent: mw.msg( 'mobile-frontend-watchlist-cta' ),\n\t\tqueryParams: {\n\t\t\twarning: 'mobile-frontend-watchlist-purpose',\n\t\t\tcampaign: 'mobile_watchPageActionCta',\n\t\t\treturntoquery: 'article_action=watch'\n\t\t},\n\t\tsignupQueryParams: {\n\t\t\twarning: 'mobile-frontend-watchlist-signup-action'\n\t\t}\n\t},\n\t/**\n\t * @inheritdoc\n\t * @memberof Watchstar\n\t * @instance\n\t */\n\tinitialize: function ( options ) {\n\t\tvar self = this,\n\t\t\t_super = View.prototype.initialize;\n\n\t\tthis._watched = options.isWatched;\n\t\tthis.gateway = new WatchstarGateway( options.api );\n\n\t\t_super.call( self, options );\n\t},\n\t/**\n\t * @inheritdoc\n\t * @memberof Watchstar\n\t * @instance\n\t */\n\tpreRender: function () {\n\t\tthis.options.tooltip = this._watched ? mw.msg( 'unwatchthispage' ) : mw.msg( 'watchthispage' );\n\t},\n\t/**\n\t * @inheritdoc\n\t * @memberof Watchstar\n\t * @instance\n\t */\n\tpostRender: function () {\n\t\tvar unwatchedClass = icons.watchIcon().getGlyphClassName(),\n\t\t\twatchedClass = icons.watchedIcon().getGlyphClassName() + ' watched';\n\n\t\tthis.$el.text( this.options.tooltip );\n\n\t\t// Add watched class if necessary\n\t\tif ( !mw.user.isAnon() && this._watched ) {\n\t\t\tthis.$el.addClass( watchedClass ).removeClass( unwatchedClass );\n\t\t} else {\n\t\t\tthis.$el.addClass( unwatchedClass ).removeClass( watchedClass );\n\t\t}\n\t\tthis.$el.removeClass( 'hidden' );\n\t},\n\t/**\n\t * Triggered when a user anonymously clicks on the watchstar.\n\t * @memberof Watchstar\n\t * @instance\n\t */\n\tonStatusToggleAnon: function () {\n\t\tif ( !this.drawer ) {\n\t\t\tthis.drawer = new CtaDrawer( this.ctaDrawerOptions );\n\n\t\t}\n\t\tthis.drawer.show();\n\t},\n\n\t/**\n\t * Triggered when a logged in user clicks on the watchstar.\n\t * @memberof Watchstar\n\t * @instance\n\t */\n\tonStatusToggleUser: function () {\n\t\tvar\n\t\t\tself = this,\n\t\t\tgateway = this.gateway,\n\t\t\tpage = this.options.page,\n\t\t\tchecker,\n\t\t\tpostWatched = !this._watched;\n\n\t\tchecker = setInterval( function () {\n\t\t\ttoast.show( mw.msg( 'mobile-frontend-watchlist-please-wait' ) );\n\t\t}, 1000 );\n\t\tfunction stopInterval() {\n\t\t\tclearInterval( checker );\n\t\t}\n\t\tgateway.postStatusesByTitle( [ page.getTitle() ], postWatched ).then( function () {\n\t\t\tstopInterval();\n\n\t\t\tself._watched = postWatched;\n\t\t\tif ( postWatched ) {\n\t\t\t\tself.render();\n\t\t\t\t/**\n\t\t\t\t * Fired when the watch star is changed to watched status\n\t\t\t\t * @event Watchstar#watch\n\t\t\t\t */\n\t\t\t\tself.emit( 'watch' );\n\t\t\t\ttoast.show( mw.msg( 'mobile-frontend-watchlist-add', page.title ) );\n\t\t\t} else {\n\t\t\t\t/**\n\t\t\t\t * Fired when the watch star is changed to unwatched status\n\t\t\t\t * @event Watchstar#unwatch\n\t\t\t\t */\n\t\t\t\tself.emit( 'unwatch' );\n\t\t\t\tself.render();\n\t\t\t\ttoast.show( mw.msg( 'mobile-frontend-watchlist-removed', page.title ) );\n\t\t\t}\n\t\t}, function () {\n\t\t\tstopInterval();\n\n\t\t\ttoast.show( mw.msg( 'mobile-frontend-watchlist-error' ), { type: 'error' } );\n\t\t} );\n\t},\n\n\t/**\n\t * Event handler for clicking on watch star.\n\t * Make an API request if user is not anonymous.\n\t * @memberof Watchstar\n\t * @instance\n\t * @param {jQuery.Event} ev jQuery event object\n\t */\n\tonStatusToggle: function ( ev ) {\n\t\tev.preventDefault();\n\t\tif ( mw.user.isAnon() ) {\n\t\t\tthis.onStatusToggleAnon.apply( this, arguments );\n\t\t} else {\n\t\t\tthis.onStatusToggleUser.apply( this, arguments );\n\t\t}\n\t}\n\n} );\n\nmodule.exports = Watchstar;\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 * @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\tel = self.parseHTML( '<div>' ).appendTo( $item ),\n\t\t\t\twatched = statuses[ page.getTitle() ];\n\n\t\t\tself._appendWatchstar( el, page, watched );\n\t\t\t$item.addClass( 'with-watchstar' );\n\t\t} );\n\t},\n\n\t/**\n\t * @param {HTMLElement} el\n\t * @param {Page} page\n\t * @param {WatchStatus} watched\n\t * @return {Watchstar}\n\t */\n\t_appendWatchstar: function ( el, page, watched ) {\n\t\tvar watchstar = new Watchstar( {\n\t\t\tapi: this.options.api,\n\t\t\tfunnel: this.options.funnel,\n\t\t\tisAnon: user.isAnon(),\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\tel: el\n\t\t} );\n\n\t\t/**\n\t\t * @event watch\n\t\t * Fired when an article in the PageList is watched.\n\t\t */\n\t\tutil.repeatEvent( watchstar, this, 'watch' );\n\n\t\t/**\n\t\t * @event unwatch\n\t\t * Fired when an article in the PageList is watched.\n\t\t */\n\t\tutil.repeatEvent( watchstar, this, 'unwatch' );\n\n\t\treturn watchstar;\n\t}\n} );\n\nmodule.exports = WatchstarPageList;\n"],"sourceRoot":""}