"const util = require( './util.js' ),\n\tactionParams = require( './actionParams' );\n\n/**\n * API for providing language data.\n *\n * @class module:mobile.startup/languages~LanguageInfo\n * @param {mw.Api} api\n */\nfunction LanguageInfo( api ) {\n\tthis.api = api;\n}\n\nLanguageInfo.prototype = {\n\n\t/**\n\t * Get languageinfo API data from the local wiki, and transform it into a\n\t * format usable by LanguageSearcher.\n\t *\n\t * @memberof module:mobile.startup/languages~LanguageInfo\n\t * @instance\n\t * @returns {jQuery.Deferred}\n\t */\n\tgetLanguages() {\n\t\treturn this.api.get( actionParams( {\n\t\t\tmeta: 'languageinfo',\n\t\t\tliprop: 'code|autonym|name|bcp47'\n\t\t} ) ).then( ( resp ) => {\n\t\t\tconst filteredLanguages = [];\n\t\t\t// Filter out legacy languages and require an autonym.\n\t\t\t// If the bcp47 (https://w.wiki/Y7A) does not match the language\n\t\t\t// code, that is in an indication that the language code is outdated\n\t\t\t// and should not be used.\n\t\t\tObject.keys( resp.query.languageinfo ).forEach( ( key ) => {\n\t\t\t\tconst language = resp.query.languageinfo[key];\n\t\t\t\tif ( ( language.code.toLowerCase() === language.bcp47.toLowerCase() ) &&\n\t\t\t\t\tlanguage.autonym ) {\n\t\t\t\t\tfilteredLanguages.push( language );\n\t\t\t\t}\n\t\t\t} );\n\t\t\treturn filteredLanguages;\n\t\t}, () => util.Deferred().reject() ).then( ( filteredLanguages ) => ( {\n\t\t\tlanguages: filteredLanguages.map( ( data ) => {\n\t\t\t\tdata.url = '#';\n\t\t\t\tdata.lang = data.code;\n\t\t\t\tdata.langname = data.name;\n\t\t\t\t// FIXME: This isn't a \"title\" in the sense of a MediaWiki\n\t\t\t\t// Title, and it is rendered as a subheader in the list\n\t\t\t\t// item, so a different name would be wiser, both here and\n\t\t\t\t// in LanguageSearcher's template. Also it would arguably\n\t\t\t\t// be more intuitive for the language name (localized) to\n\t\t\t\t// appear as the main emphasized element of each language\n\t\t\t\t// list element; but instead the autonym has that role.\n\t\t\t\t// A more thorough refactoring of LanguageSearcher to allow\n\t\t\t\t// for generic header/subheader elements is left for a\n\t\t\t\t// follow-up.\n\t\t\t\tdata.title = data.name;\n\t\t\t\treturn data;\n\t\t\t} )\n\t\t} ), () => util.Deferred().reject() );\n\t}\n};\n\nmodule.exports = LanguageInfo;\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 {LanguageInfo} languageInfo\n * @param {boolean} showSuggestedLanguages If the suggested languages section\n * should be rendered.\n * @return {jQuery.Promise} Resolves to LanguageSearcher\n */\nfunction loadLanguageInfoSearcher( languageInfo, showSuggestedLanguages ) {\n\treturn mw.loader.using( 'mobile.languages.structured' ).then( () => languageInfo.getLanguages() ).then( ( data ) => {\n\t\tconst LanguageSearcher = m.require( 'mobile.languages.structured/LanguageSearcher' );\n\t\treturn new LanguageSearcher( {\n\t\t\tlanguages: data.languages,\n\t\t\tvariants: data.variants,\n\t\t\tshowSuggestedLanguages,\n\t\t\tdeviceLanguage: getDeviceLanguage( navigator )\n\t\t} );\n\n\t} );\n}\n\n/**\n * Factory function that returns a language info featured instance of an Overlay\n *\n * @ignore\n * @function\n * @memberof module:mobile.startup/languages\n * @param {module:mobile.startup/languages~LanguageInfo} languageInfo\n * @param {boolean} showSuggestedLanguages If the suggested languages section\n * should be rendered.\n * @return {module:mobile.startup/Overlay}\n */\nfunction languageInfoOverlay( languageInfo, showSuggestedLanguages ) {\n\treturn Overlay.make(\n\t\t{\n\t\t\theading: mw.msg( 'mobile-frontend-language-heading' ),\n\t\t\tclassName: 'overlay language-info-overlay'\n\t\t}, promisedView( loadLanguageInfoSearcher( languageInfo, showSuggestedLanguages ) )\n\t);\n}\n\n// To make knowing when async logic has resolved easier in tests\nlanguageInfoOverlay.test = {\n\tloadLanguageInfoSearcher\n};\n\nmodule.exports = languageInfoOverlay;\n",
"const\n\tm = require( '../moduleLoaderSingleton' ),\n\tgetDeviceLanguage = require( './getDeviceLanguage' ),\n\tOverlay = require( '../Overlay' ),\n\tMessageBox = require( '../MessageBox' ),\n\tcurrentPageHTMLParser = require( '../currentPageHTMLParser' )(),\n\tpromisedView = require( '../promisedView' );\n\n/**\n * @ignore\n * @return {jQuery.Promise} Resolves to LanguageSearcher\n */\nfunction loadLanguageSearcher() {\n\treturn mw.loader.using( 'mobile.languages.structured' ).then( () =>\n\t\tcurrentPageHTMLParser.getLanguages(\n\t\t\tmw.config.get( 'wgTitle' )\n\t\t) ).then( ( 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\tshowSuggestedLanguages: true,\n\t\t\tdeviceLanguage: getDeviceLanguage( navigator ),\n\t\t\t/**\n\t\t\t * Stable for use inside ContentTranslation.\n\t\t\t * @event ~'mobileFrontend.languageSearcher.onOpen'\n\t\t\t * @memberof Hooks\n\t\t\t * @param {Hooks~LanguageSearcher} searcher\n\t\t\t */\n\t\t\tonOpen: ( searcher ) => mw.hook( 'mobileFrontend.languageSearcher.onOpen' ).fire( searcher )\n\t\t} );\n\t}, () =>\n\t\tnew MessageBox( {\n\t\t\ttype: 'error',\n\t\t\tclassName: 'content',\n\t\t\tmsg: mw.msg( 'mobile-frontend-languages-structured-overlay-error' )\n\t\t} ) );\n}\n\n/**\n * Factory function that returns a language featured instance of an Overlay.\n *\n * @function\n * @memberof module:mobile.startup/languages\n * @return {module:mobile.startup/Overlay}\n */\nfunction languageOverlay() {\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() )\n\t);\n}\n\n// To make knowing when async logic has resolved easier in tests\nlanguageOverlay.test = {\n\tloadLanguageSearcher\n};\n\nmodule.exports = languageOverlay;\n",
"const currentPageHTMLParser = require( './currentPageHTMLParser' );\nconst time = require( './time' );\nconst LanguageInfo = require( './LanguageInfo' );\nconst currentPage = require( './currentPage' );\nconst Drawer = require( './Drawer' );\nconst CtaDrawer = require( './CtaDrawer' );\nconst lazyImageLoader = require( './lazyImages/lazyImageLoader' );\nconst icons = require( './icons' );\nconst PageHTMLParser = require( './PageHTMLParser' );\nconst showOnPageReload = require( './showOnPageReload' );\nconst OverlayManager = require( './OverlayManager' );\nconst View = require( './View' );\nconst Overlay = require( './Overlay' );\nconst references = require( './references/references' );\nconst search = {\n\tSearchOverlay: require( './search/SearchOverlay' ),\n\tSearchGateway: require( './search/SearchGateway' )\n};\nconst promisedView = require( './promisedView' );\nconst headers = require( './headers' );\nconst Skin = require( './Skin' );\nconst mediaViewer = {\n\toverlay: require( './mediaViewer/overlay' )\n};\nconst languageInfoOverlay = require( './languageOverlay/languageInfoOverlay' );\nconst languageOverlay = require( './languageOverlay/languageOverlay' );\nconst amcOutreach = require( './amcOutreach/amcOutreach' );\n\n// Expose chunk to temporary variable which will be deleted and exported via ResourceLoader\n// package inside mobile.startup.exports.\n\n/**\n * The main library for accessing MobileFrontend's stable APIs.\n *\n * @module mobile.startup\n */\nmw._mobileFrontend = {\n\t/**\n\t * Internal for use inside Minerva only\n\t *\n\t * @memberof module:mobile.startup\n\t * @type module:mobile.startup/AmcOutreach\n\t */\n\n\tamcOutreach,\n\t// Internal for use inside GrowthExperiments only.\n\toverlayHeader: headers.header,\n\t/**\n\t * Internal for use inside Minerva, GrowthExperiments only.\n\t * @type module:mobile.startup/Drawer\n\t */\n\tDrawer,\n\t// Internal for use inside Minerva only.\n\tCtaDrawer,\n\t/**\n\t * Internal for use inside Minerva, ExternalGuidance and Echo only.\n\t *\n\t * @memberof module:mobile.startup\n\t * @type module:mobile.startup/View\n\t */\n\tView,\n\t/**\n\t * Internal for use inside Minerva, ExternalGuidance,\n\t * GrowthExperiments and Echo only.\n\t *\n\t * @memberof module:mobile.startup\n\t * @type module:mobile.startup/Overlay\n\t */\n\tOverlay,\n\t/**\n\t * Internal for use inside Minerva only.\n\t *\n\t * @memberof module:mobile.startup\n\t * @type module:mobile.startup/PageHTMLParser\n\t */\n\tcurrentPageHTMLParser,\n\t/**\n\t * Internal for use inside Minerva, ExternalGuidance and Echo only.\n\t *\n\t * @memberof module:mobile.startup\n\t * @return {module:mobile.startup/OverlayManager}\n\t */\n\tgetOverlayManager: () => {\n\t\treturn OverlayManager.getSingleton();\n\t},\n\t/**\n\t * Internal for use inside Minerva only.\n\t * @type module:mobile.startup/Page\n\t * @memberof module:mobile.startup\n\t */\n\tcurrentPage,\n\t/**\n\t * Internal for use inside Minerva only.\n\t * @type module:mobile.startup/PageHTMLParser\n\t * @memberof module:mobile.startup\n\t */\n\tPageHTMLParser,\n\t/**\n\t * Internal for use inside Minerva only.\n\t * @type module:mobile.startup/Icon\n\t * @memberof module:mobile.startup\n\t */\n\tspinner: icons.spinner,\n\t/**\n\t * Internal for use inside Minerva only.\n\t *\n\t * @memberof module:mobile.startup\n\t * @type module:mobile.startup/mediaViewer\n\t */\n\tmediaViewer,\n\t/**\n\t * Internal for use inside Minerva only.\n\t *\n\t * @memberof module:mobile.startup\n\t * @type module:mobile.startup/references\n\t */\n\treferences,\n\t/**\n\t * Internal for use inside Minerva only.\n\t *\n\t * @memberof module:mobile.startup\n\t * @type module:mobile.startup/search\n\t */\n\tsearch,\n\t/**\n\t * Internal for use inside Minerva only.\n\t *\n\t * @memberof module:mobile.startup\n\t * @type module:mobile.startup/time\n\t */\n\ttime,\n\t// Internal for use inside Echo, GrowthExperiments only.\n\tpromisedView,\n\t/**\n\t * Loads all images on the page, stable to call.\n\t *\n\t * @memberof module:mobile.startup\n\t * @return {jQuery.Deferred}\n\t */\n\tloadAllImagesInPage: () => {\n\t\treturn lazyImageLoader.loadImages(\n\t\t\tlazyImageLoader.queryPlaceholders( document.getElementById( 'content' ) )\n\t\t);\n\t},\n\t/**\n\t * Show a notification on page reload, internal for Minerva\n\t *\n\t * @memberof module:mobile.startup\n\t * @param {string} msg\n\t * @return {jQuery.Deferred}\n\t */\n\tnotifyOnPageReload: ( msg ) => showOnPageReload( msg ),\n\t/**\n\t * Internal for use inside VisualEditor\n\t *\n\t * @memberof module:mobile.startup\n\t * @return {string|undefined}\n\t */\n\tlicense() {\n\t\tconst skin = Skin.getSingleton();\n\t\treturn skin.getLicenseMsg();\n\t},\n\t/**\n\t * Internal for use inside Minerva. See {@link module:mobile.startup} for access.\n\t *\n\t * @module mobile.startup/languages\n\t */\n\t/**\n\t * Access to language overlays for usage inside Minerva only.\n\t *\n\t * @memberof module:mobile.startup\n\t * @type module:mobile.startup/languages\n\t */\n\tlanguages: {\n\t\tlanguageOverlay,\n\t\t/**\n\t\t * Shows information about suggested languages.\n\t\t *\n\t\t * @memberof module:mobile.startup/languages\n\t\t * @param {mw.Api} api\n\t\t * @param {boolean} showSuggestedLanguage If the suggested languages section\n\t\t * should be rendered.\n\t\t */\n\t\tlanguageInfoOverlay( api, showSuggestedLanguage ) {\n\t\t\treturn languageInfoOverlay( new LanguageInfo( api ), showSuggestedLanguage );\n\t\t}\n\t}\n};\n",
"const\n\ticons = require( './icons' ),\n\tView = require( './View' );\n\n/**\n * Internal for use inside Echo, GrowthExperiments only.\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 * @function promisedView\n * @memberof module:mobile.startup\n * @param {jQuery.Promise} promise\n * @return {module:mobile.startup/View}\n */\nmodule.exports = function promisedView( promise ) {\n\tconst view = new View( {\n\t\tclassName: 'promised-view'\n\t} );\n\tview.$el.append( icons.spinner().$el );\n\tpromise.then( ( newView ) => {\n\t\tview.$el.replaceWith( newView.$el );\n\t\t// update the internal reference.\n\t\tview.$el = newView.$el;\n\t}, ( 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",
"/**\n * Abstract base class\n * Gateway for retrieving references\n *\n * @class module:mobile.startup/references~Gateway\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 module:mobile.startup/references~Gateway\n * @instance\n * @param {string} id CSS selector\n * @param {Page} page to find reference for\n * @param {module:mobile.startup/PageHTMLParser} pageHTMLParser\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 * ERROR_NOT_EXIST error code to be returned by getReference\n * when a reference does not exist.\n *\n * @memberof module:mobile.startup/references~Gateway\n * @type string\n */\nReferencesGateway.ERROR_NOT_EXIST = 'NOT_EXIST_ERROR';\n/**\n * 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 *\n * @memberof module:mobile.startup/references~Gateway\n * @type string\n */\nReferencesGateway.ERROR_OTHER = 'OTHER_ERROR';\n\nmodule.exports = ReferencesGateway;\n",
"const 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 * @memberof module:mobile.startup/references\n * @extends module:mobile.startup/references~Gateway\n * @inheritdoc\n */\nfunction ReferencesHtmlScraperGateway() {\n\tReferencesGateway.apply( this, arguments );\n}\n\nmfExtend( ReferencesHtmlScraperGateway, ReferencesGateway, {\n\t/**\n\t * @memberof ReferencesHtmlScraperGateway\n\t * @property EXTERNAL_LINK_CLASS a CSS class to place on external links\n\t * in addition to the default 'external' class.\n\t */\n\tEXTERNAL_LINK_CLASS: 'external--reference',\n\t/**\n\t * @memberof ReferencesHtmlScraperGateway\n\t * @instance\n\t * @param {string} id ID of a DOM element in the page.\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( id, $container ) {\n\t\tconst result = util.Deferred();\n\n\t\tconst $el = $container.find( '#' + util.escapeSelector( id ) );\n\t\tif ( $el.length ) {\n\t\t\tlet $parent;\n\n\t\t\t// This finds either the inner <ol class=\"mw-extended-references\">, or the outer\n\t\t\t// <ol class=\"references\">\n\t\t\tconst $ol = $el.closest( 'ol' );\n\t\t\tconst isSubref = $ol.hasClass( 'mw-extended-references' );\n\t\t\tif ( isSubref ) {\n\t\t\t\t$parent = $ol.parent();\n\t\t\t}\n\t\t\t// The following classes are used here:\n\t\t\t// * external--reference\n\t\t\t// * other values of EXTERNAL_LINK_CLASS in sub-classes\n\t\t\t( $parent || $el ).find( '.external' ).addClass( this.EXTERNAL_LINK_CLASS );\n\t\t\tresult.resolve( {\n\t\t\t\ttext: this.getReferenceHtml( $el ),\n\t\t\t\tparentText: this.getReferenceHtml( $parent ),\n\t\t\t\tisSubref\n\t\t\t} );\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 * @memberof ReferencesHtmlScraperGateway\n\t * @param {jQuery.Object|undefined} $reference\n\t * @return {string|undefined}\n\t */\n\tgetReferenceHtml( $reference ) {\n\t\treturn $reference ?\n\t\t\t$reference.children( '.mw-reference-text, .reference-text' ).first().html() :\n\t\t\t'';\n\t},\n\t/**\n\t * @inheritdoc\n\t * @memberof ReferencesHtmlScraperGateway\n\t * @instance\n\t * @param {string} hash Hash fragment with leading '#'\n\t * @param {Page} page (unused)\n\t * @param {module:mobile.startup/PageHTMLParser} pageHTMLParser\n\t */\n\tgetReference( hash, page, pageHTMLParser ) {\n\t\tconst id = mw.util.percentDecodeFragment( hash.slice( 1 ) );\n\t\t// If an id is not found it's possible the id passed needs decoding (per T188547).\n\t\treturn this.getReferenceFromContainer( id, pageHTMLParser.$el.find( 'ol.references' ) );\n\t}\n} );\n\nmodule.exports = ReferencesHtmlScraperGateway;\n",
"const Drawer = require( '../Drawer' ),\n\tutil = require( '../util' ),\n\ticons = require( '../icons' ),\n\tReferencesGateway = require( './ReferencesGateway' ),\n\tIcon = require( '../Icon' ),\n\tReferencesHtmlScraperGateway = require( './ReferencesHtmlScraperGateway' ),\n\tIconButton = require( '../IconButton' );\n\n/**\n * Create a callback for clicking references\n *\n * @ignore\n * @param {Function} onNestedReferenceClick\n * @return {Function}\n */\nfunction makeOnNestedReferenceClickHandler( onNestedReferenceClick ) {\n\treturn ( ev ) => {\n\t\tconst target = ev.currentTarget.querySelector( 'a' );\n\t\tif ( target ) {\n\t\t\tonNestedReferenceClick(\n\t\t\t\ttarget.getAttribute( 'href' ),\n\t\t\t\ttarget.textContent\n\t\t\t);\n\t\t\t// Don't hide the already shown drawer via propagation\n\t\t\treturn false;\n\t\t}\n\t};\n}\n\n/**\n * Drawer for references\n *\n * @memberof module:mobile.startup/references\n * @uses IconButton\n * @param {Object} props\n * @param {boolean} [props.error] whether an error has occurred\n * @param {string} props.title of reference e.g [1]\n * @param {string} props.text is the HTML of the reference\n * @param {string} [props.parentText] is the HTML of the parent reference if there is one\n * @param {boolean} props.isSubref true when this reference has a parent\n * @param {Function} [props.onNestedReferenceClick] callback for when a reference\n * inside the reference is clicked.\n * @return {module:mobile.startup/Drawer}\n */\nfunction referenceDrawer( props ) {\n\tconst errorIcon = props.error ? new IconButton( {\n\t\tname: 'error',\n\t\tisSmall: true\n\t} ).$el : null;\n\n\tconst mainRef = props.isSubref ? props.parentText : props.text;\n\tconst mainRefHtml = util.parseHTML( '<div>' )\n\t\t.addClass( 'main-reference-content' )\n\t\t.html( mainRef );\n\tif ( !mainRef ) {\n\t\tmainRefHtml.append( icons.spinner().$el );\n\t}\n\tconst subRefHtml = props.isSubref ?\n\t\tutil.parseHTML( '<div>' ).html( props.text ) : '';\n\n\treturn new Drawer(\n\t\tutil.extend(\n\t\t\t{\n\t\t\t\tshowCollapseIcon: false,\n\t\t\t\tclassName: 'drawer position-fixed text references-drawer',\n\t\t\t\tevents: {\n\t\t\t\t\t'click sup a': function ( ev ) {\n\t\t\t\t\t\t// Stop default scroll to hash fragment behaviour.\n\t\t\t\t\t\tev.preventDefault();\n\t\t\t\t\t},\n\t\t\t\t\t'click sup': props.onNestedReferenceClick &&\n\t\t\t\t\t\tmakeOnNestedReferenceClickHandler( props.onNestedReferenceClick )\n\t\t\t\t},\n\t\t\t\tchildren: [\n\t\t\t\t\tutil.parseHTML( '<div>' )\n\t\t\t\t\t\t.addClass( 'references-drawer__header' )\n\t\t\t\t\t\t.append( [\n\t\t\t\t\t\t\tnew Icon( {\n\t\t\t\t\t\t\t\ticon: 'reference',\n\t\t\t\t\t\t\t\tisSmall: true\n\t\t\t\t\t\t\t} ).$el,\n\t\t\t\t\t\t\tutil.parseHTML( '<span>' ).addClass( 'references-drawer__title' ).text( mw.msg( 'mobile-frontend-references-citation' ) ),\n\t\t\t\t\t\t\ticons.cancel( 'gray', {\n\t\t\t\t\t\t\t\tisSmall: true,\n\t\t\t\t\t\t\t\tadditionalClassNames: 'mf-button-flush-right'\n\t\t\t\t\t\t\t} ).$el\n\t\t\t\t\t\t] ),\n\n\t\t\t\t\t// Add .mw-parser-output so that TemplateStyles styles apply (T244510)\n\t\t\t\t\tutil.parseHTML( '<div>' ).addClass( 'mw-parser-output' ).append( [\n\t\t\t\t\t\terrorIcon,\n\t\t\t\t\t\tutil.parseHTML( '<sup>' ).text( props.title ),\n\t\t\t\t\t\tmainRefHtml,\n\t\t\t\t\t\tsubRefHtml\n\t\t\t\t\t] )\n\t\t\t\t]\n\t\t\t},\n\t\t\tprops\n\t\t)\n\t);\n}\n\n/**\n * Internal for use inside Minerva only. See {@link module:mobile.startup} for access.\n *\n * @exports module:mobile.startup/references\n */\nconst references = {\n\ttest: {\n\t\tmakeOnNestedReferenceClickHandler\n\t},\n\tReferencesHtmlScraperGateway,\n\treferenceDrawer,\n\t/**\n\t * Fetch and render nested reference upon click\n\t *\n\t * @param {string} id of the reference to be retrieved\n\t * @param {Page} page to locate reference for\n\t * @param {string} refNumber the number it identifies as in the page\n\t * @param {module:mobile.startup/PageHTMLParser} pageHTMLParser\n\t * @param {module:mobile.startup/references~Gateway} gateway\n\t * @param {Object} props for referenceDrawer\n\t * @param {Function} onShowNestedReference function call when a nested reference is triggered.\n\t * @return {jQuery.Deferred}\n\t */\n\tshowReference( id, page, refNumber, pageHTMLParser, gateway, props,\n\t\tonShowNestedReference\n\t) {\n\t\treturn gateway.getReference( id, page, pageHTMLParser ).then( ( reference ) => {\n\t\t\tconst drawer = referenceDrawer( util.extend( {\n\t\t\t\ttitle: refNumber,\n\t\t\t\ttext: reference.text,\n\t\t\t\tparentText: reference.parentText,\n\t\t\t\tisSubref: reference.isSubref,\n\t\t\t\tonNestedReferenceClick( href, text ) {\n\t\t\t\t\treferences.showReference(\n\t\t\t\t\t\thref,\n\t\t\t\t\t\tpage,\n\t\t\t\t\t\ttext,\n\t\t\t\t\t\tpageHTMLParser,\n\t\t\t\t\t\tgateway\n\t\t\t\t\t).then( ( nestedDrawer ) => {\n\t\t\t\t\t\tif ( props.onShowNestedReference ) {\n\t\t\t\t\t\t\tonShowNestedReference( drawer, nestedDrawer );\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tmw.log.warn( 'Please provide onShowNestedReferences parameter.' );\n\t\t\t\t\t\t\tdocument.body.appendChild( nestedDrawer.$el[0] );\n\t\t\t\t\t\t\tdrawer.hide();\n\t\t\t\t\t\t\tnestedDrawer.show();\n\t\t\t\t\t\t}\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t}, props ) );\n\t\t\treturn drawer;\n\t\t}, ( err ) => {\n\t\t\t// If non-existent reference nothing to do.\n\t\t\tif ( err === ReferencesGateway.ERROR_NOT_EXIST ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treturn referenceDrawer( {\n\t\t\t\terror: true,\n\t\t\t\ttitle: refNumber,\n\t\t\t\ttext: mw.msg( 'mobile-frontend-references-citation-error' )\n\t\t\t} );\n\t\t} );\n\t}\n};\n\nmodule.exports = references;\n",
"/**\n * Internal for use inside Minerva only. See {@link module:mobile.startup} for access.\n *\n * @module module:mobile.startup/search\n */\nconst\n\tpageJSONParser = require( '../page/pageJSONParser' ),\n\tutil = require( '../util' ),\n\textendSearchParams = require( '../extendSearchParams' );\n\n/**\n * Interact with MediaWiki search API.\n *\n * @memberof module:mobile.startup/search\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 *\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 *\n\t * @memberof SearchGateway\n\t * @instance\n\t * @param {string} query to search for\n\t * @return {Object}\n\t */\n\tgetApiData( query ) {\n\t\tconst 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 *\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( 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 *\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( label, term ) {\n\t\tlabel = util.parseHTML( '<span>' ).text( label ).html();\n\t\tterm = term.trim();\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 *\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( query, pageInfo ) {\n\t\tconst page = pageJSONParser.parse( 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 *\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( query, data ) {\n\t\tconst self = this;\n\n\t\tlet results = [];\n\n\t\tif ( data.query ) {\n\n\t\t\tresults = data.query.pages || {};\n\t\t\tresults = Object.keys( results ).map( ( id ) => self._getPage( query, results[id] ) );\n\t\t\t// sort in order of index\n\t\t\tresults.sort( ( a, b ) => a.index - b.index );\n\t\t}\n\n\t\treturn results;\n\t},\n\n\t/**\n\t * Perform a search for the given query.\n\t *\n\t * @memberof SearchGateway\n\t * @instance\n\t * @param {string} query to search for\n\t * @return {jQuery.Deferred}\n\t */\n\tsearch( query ) {\n\t\tconst scriptPath = mw.config.get( 'wgMFScriptPath' ),\n\t\t\tself = this;\n\n\t\tif ( !this.isCached( query ) ) {\n\t\t\tconst xhr = this.api.get( this.getApiData( query ), scriptPath ? {\n\t\t\t\turl: scriptPath\n\t\t\t} : undefined );\n\t\t\tconst request = xhr\n\t\t\t\t.then( ( data, jqXHR ) => {\n\t\t\t\t\t// resolve the Deferred object\n\t\t\t\t\treturn {\n\t\t\t\t\t\tquery,\n\t\t\t\t\t\tresults: self._processData( query, data ),\n\t\t\t\t\t\tsearchId: jqXHR && jqXHR.getResponseHeader( 'x-search-id' )\n\t\t\t\t\t};\n\t\t\t\t}, () => {\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() {\n\t\t\t\t\txhr.abort();\n\t\t\t\t}\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 *\n\t * @memberof SearchGateway\n\t * @instance\n\t * @param {string} query\n\t * @return {boolean}\n\t */\n\tisCached( query ) {\n\t\treturn Boolean( this.searchCache[query] );\n\t}\n};\n\nmodule.exports = SearchGateway;\n",
"const util = require( '../util' ),\n\tView = require( '../View' ),\n\tIconButton = require( '../IconButton' );\n\n/**\n * @extends module:mobile.startup/View\n */\nclass SearchHeaderView extends View {\n\t/**\n\t * constructor\n\t *\n\t * @inheritdoc\n\t * @param {Object} props\n\t * @param {Function} props.onInput executed every time input changes\n\t * @param {string} props.placeholderMsg\n\t * @param {string} props.action\n\t * @parm {string} [props.autocapitalize] none or sentences\n\t * @param {string} [props.searchTerm] default\n\t */\n\tconstructor( props ) {\n\t\tsuper(\n\t\t\tutil.extend( {\n\t\t\t\tautocapitalize: 'sentences'\n\t\t\t}, props, {\n\t\t\t\tevents: {\n\t\t\t\t\t'input input': 'onInput'\n\t\t\t\t}\n\t\t\t} )\n\t\t);\n\t}\n\n\t/** @inheritdoc */\n\tonInput( ev ) {\n\t\tconst query = ev.target.value;\n\t\tthis.options.onInput( query );\n\t\tif ( query ) {\n\t\t\tthis.clearIcon.$el.show();\n\t\t} else {\n\t\t\tthis.clearIcon.$el.hide();\n\t\t}\n\t}\n\n\t/** @inheritdoc */\n\tget isTemplateMode() {\n\t\treturn true;\n\t}\n\n\t/** @inheritdoc */\n\tget template() {\n\t\treturn util.template( `<div class=\"overlay-title search-header-view\">\n\t\t<form method=\"get\" action=\"{{action}}\" class=\"search-box\">\n\t\t<input class=\"search mf-icon-search\" type=\"search\" name=\"search\"\n\t\t\tautocapitalize=\"{{autocapitalize}}\"\n\t\t\tautocomplete=\"off\" placeholder=\"{{placeholderMsg}}\" aria-label=\"{{placeholderMsg}}\" value=\"{{searchTerm}}\">\n\t\t<input type=\"hidden\" name=\"title\" value=\"{{defaultSearchPage}}\">\n\t\t</form>\n</div>` );\n\t}\n\n\t/** @inheritdoc */\n\tpostRender() {\n\t\tconst clearIcon = new IconButton( {\n\t\t\ttagName: 'button',\n\t\t\ticon: 'clear',\n\t\t\tsize: 'medium',\n\t\t\tisSmall: true,\n\t\t\tlabel: mw.msg( 'mobile-frontend-clear-search' ),\n\t\t\tadditionalClassNames: 'clear',\n\t\t\tevents: {\n\t\t\t\tclick: function () {\n\t\t\t\t\tthis.$el.find( 'input' ).val( '' ).trigger( 'focus' );\n\t\t\t\t\tthis.options.onInput( '' );\n\t\t\t\t\tclearIcon.$el.hide();\n\t\t\t\t\t// In beta the clear button is on top of the search input.\n\t\t\t\t\t// Stop propagation so that the input doesn't receive the click.\n\t\t\t\t\treturn false;\n\t\t\t\t}.bind( this )\n\t\t\t}\n\t\t} );\n\t\tthis.clearIcon = clearIcon;\n\t\tclearIcon.$el.hide();\n\t\tclearIcon.$el.attr( 'aria-hidden', 'true' );\n\t\tthis.$el.find( 'form' ).append( clearIcon.$el );\n\t}\n}\n\nmodule.exports = SearchHeaderView;\n",
"const\n\tmfExtend = require( '../mfExtend' ),\n\tOverlay = require( '../Overlay' ),\n\tutil = require( '../util' ),\n\tsearchHeader = require( './searchHeader' ),\n\tSearchResultsView = require( './SearchResultsView' ),\n\tWatchstarPageList = require( '../watchstar/WatchstarPageList' ),\n\tSEARCH_DELAY = 300,\n\tSEARCH_SPINNER_DELAY = 2000;\n\n/**\n * Overlay displaying search results\n *\n * @class SearchOverlay\n * @memberof module:mobile.startup/search\n * @extends Overlay\n * @uses SearchGateway\n * @uses IconButton\n *\n * @param {Object} params Configuration options\n * @param {string} params.placeholderMsg Search input placeholder text.\n * @param {string} [params.action] of form defaults to the value of wgScript\n * @param {string} [params.defaultSearchPage] The default search page e.g. Special:Search\n * @param {SearchGateway} [params.gateway]\n */\nfunction SearchOverlay( params ) {\n\tconst header = searchHeader(\n\t\t\tparams.placeholderMsg,\n\t\t\tparams.action || mw.config.get( 'wgScript' ),\n\t\t\t( query ) => this.performSearch( query ),\n\t\t\tparams.defaultSearchPage || '',\n\t\t\tparams.autocapitalize\n\t\t),\n\t\toptions = util.extend( true, {\n\t\t\theaderChrome: true,\n\t\t\tisBorderBox: false,\n\t\t\tclassName: 'overlay search-overlay',\n\t\t\theaders: [ header ],\n\t\t\tevents: {\n\t\t\t\t'click .search-content': 'onClickSearchContent',\n\t\t\t\t'click .overlay-content': 'onClickOverlayContent',\n\t\t\t\t'click .overlay-content > div': function ( ev ) {\n\t\t\t\t\tev.stopPropagation();\n\t\t\t\t},\n\t\t\t\t'touchstart .results': 'hideKeyboardOnScroll',\n\t\t\t\t'mousedown .results': 'hideKeyboardOnScroll',\n\t\t\t\t'click .results a': 'onClickResult'\n\t\t\t}\n\t\t},\n\t\tparams );\n\n\tthis.header = header;\n\tOverlay.call( this, options );\n\n\tthis.api = options.api;\n\t// eslint-disable-next-line new-cap\n\tthis.gateway = options.gateway || new options.gatewayClass( this.api );\n\n\tthis.router = options.router;\n\n\tthis.currentSearchId = null;\n}\n\nmfExtend( SearchOverlay, Overlay, {\n\n\t/**\n\t * Initialize 'search within pages' functionality\n\t *\n\t * @memberof SearchOverlay\n\t * @instance\n\t */\n\tonClickSearchContent() {\n\t\tconst\n\t\t\t$form = this.$el.find( 'form' ),\n\t\t\t$el = $form[0].parentNode;\n\n\t\t// Add fulltext input to force fulltext search\n\t\tthis.parseHTML( '<input>' )\n\t\t\t.attr( {\n\t\t\t\ttype: 'hidden',\n\t\t\t\tname: 'fulltext',\n\t\t\t\tvalue: 'search'\n\t\t\t} )\n\t\t\t.appendTo( $form );\n\t\t// history.back queues a task so might run after this call. Thus we use setTimeout\n\t\t// http://www.w3.org/TR/2011/WD-html5-20110113/webappapis.html#queue-a-task\n\t\tsetTimeout( () => {\n\t\t\t// Firefox doesn't allow submission of a form not in the DOM\n\t\t\t// so temporarily re-add it if it's gone.\n\t\t\tif ( !$form[0].parentNode ) {\n\t\t\t\t$form.appendTo( $el );\n\t\t\t}\n\t\t\t$form.trigger( 'submit' );\n\t\t}, 0 );\n\t},\n\n\t/**\n\t * Tapping on background only should hide the overlay\n\t *\n\t * @memberof SearchOverlay\n\t * @instance\n\t */\n\tonClickOverlayContent() {\n\t\tthis.$el.find( '.cancel' ).trigger( 'click' );\n\t},\n\n\t/**\n\t * Hide the keyboard when scrolling starts (avoid weird situation when\n\t * user taps on an item, the keyboard hides and wrong item is clicked).\n\t *\n\t * @memberof SearchOverlay\n\t * @instance\n\t */\n\thideKeyboardOnScroll() {\n\t\tthis.$input.trigger( 'blur' );\n\t},\n\n\t/**\n\t * Handle the user clicking a result.\n\t *\n\t * @memberof SearchOverlay\n\t * @instance\n\t * @param {jQuery.Event} ev\n\t */\n\tonClickResult( ev ) {\n\t\tconst\n\t\t\tself = this,\n\t\t\t$link = this.$el.find( ev.currentTarget );\n\t\t/**\n\t\t * Fired when the user clicks a search result\n\t\t *\n\t\t * @type {Object}\n\t\t * @property {jQuery.Object} result The jQuery-wrapped DOM element that\n\t\t * the user clicked\n\t\t * @property {number} resultIndex The zero-based index of the\n\t\t * result in the set of results\n\t\t * @property {jQuery.Event} originalEvent The original event\n\t\t */\n\t\t// FIXME: ugly hack that removes search from browser history\n\t\t// when navigating to search results\n\t\tev.preventDefault();\n\t\tthis.router.back().then( function () {\n\t\t\t// T308288: Appends the current search id as a url param on clickthroughs\n\t\t\tif ( this.currentSearchId ) {\n\t\t\t\tconst clickUrl = new URL( location.href );\n\t\t\t\tclickUrl.searchParams.set( 'searchToken', this.currentSearchId );\n\t\t\t\tself.router.navigateTo( document.title, {\n\t\t\t\t\tpath: clickUrl.toString(),\n\t\t\t\t\tuseReplaceState: true\n\t\t\t\t} );\n\t\t\t\tthis.currentSearchId = null;\n\t\t\t}\n\t\t\t// Router.navigate does not support changing href.\n\t\t\t// FIXME: Needs upstream change T189173\n\t\t\t// eslint-disable-next-line no-restricted-properties\n\t\t\twindow.location.href = $link.attr( 'href' );\n\t\t} );\n\t},\n\n\t/**\n\t * @inheritdoc\n\t * @memberof SearchOverlay\n\t * @instance\n\t */\n\tpostRender() {\n\t\tconst self = this,\n\t\t\tsearchResults = new SearchResultsView( {\n\t\t\t\tsearchContentLabel: mw.msg( 'mobile-frontend-search-content' ),\n\t\t\t\tnoResultsMsg: mw.msg( 'mobile-frontend-search-no-results' ),\n\t\t\t\tsearchContentNoResultsMsg: mw.message( 'mobile-frontend-search-content-no-results' ).parse()\n\t\t\t} );\n\t\tlet timer;\n\n\t\tthis.$el.find( '.overlay-content' ).append( searchResults.$el );\n\t\tOverlay.prototype.postRender.call( this );\n\n\t\t// FIXME: `this.$input` should not be set. Isolate to searchHeader function\n\t\tthis.$input = this.$el.find( this.header ).find( 'input' );\n\t\t// FIXME: `this.$searchContent` should not be set. Isolate to SearchResultsView class.\n\t\tthis.$searchContent = searchResults.$el.hide();\n\t\t// FIXME: `this.$resultContainer` should not be set. Isolate to SearchResultsView class.\n\t\tthis.$resultContainer = searchResults.$el.find( '.results-list-container' );\n\n\t\t// On iOS a touchstart event while the keyboard is open will result in a scroll\n\t\t// leading to an accidental click (T299846)\n\t\t// Stopping propagation when the input is focused will prevent scrolling while\n\t\t// the keyboard is collapsed.\n\t\tthis.$resultContainer[0].addEventListener( 'touchstart', ( ev ) => {\n\t\t\tif ( document.activeElement === this.$input[0] ) {\n\t\t\t\tev.stopPropagation();\n\t\t\t}\n\t\t} );\n\n\t\t/**\n\t\t * Hide the spinner and abort timed spinner shows.\n\t\t * FIXME: Given this manipulates SearchResultsView this should be moved into that class\n\t\t */\n\t\tfunction clearSearch() {\n\t\t\tself.$spinner.hide();\n\t\t\tclearTimeout( timer );\n\t\t}\n\n\t\t// Show a spinner on top of search results\n\t\t// FIXME: Given this manipulates SearchResultsView this should be moved into that class\n\t\tthis.$spinner = searchResults.$el.find( '.spinner-container' );\n\t\tthis.on( 'search-start', ( searchData ) => {\n\t\t\tif ( timer ) {\n\t\t\t\tclearSearch();\n\t\t\t}\n\t\t\ttimer = setTimeout( () => self.$spinner.show(),\n\t\t\t\tSEARCH_SPINNER_DELAY - searchData.delay );\n\t\t} );\n\t\tthis.on( 'search-results', clearSearch );\n\t},\n\n\t/**\n\t * Trigger a focus() event on search input in order to\n\t * bring up the virtual keyboard.\n\t *\n\t * @memberof SearchOverlay\n\t * @instance\n\t */\n\tshowKeyboard() {\n\t\tconst len = this.$input.val().length;\n\t\tthis.$input.trigger( 'focus' );\n\t\t// Cursor to the end of the input\n\t\tif ( this.$input[0].setSelectionRange ) {\n\t\t\tthis.$input[0].setSelectionRange( len, len );\n\t\t}\n\t},\n\n\t/**\n\t * @inheritdoc\n\t * @memberof SearchOverlay\n\t * @instance\n\t */\n\tshow() {\n\t\t// Overlay#show defines the actual overlay visibility.\n\t\tOverlay.prototype.show.apply( this, arguments );\n\n\t\tthis.showKeyboard();\n\t},\n\n\t/**\n\t * Perform search and render results inside current view.\n\t * FIXME: Much of the logic for caching and pending queries inside this function should\n\t * actually live in SearchGateway, please move out.\n\t *\n\t * @memberof SearchOverlay\n\t * @instance\n\t * @param {string} query\n\t */\n\tperformSearch( query ) {\n\t\tconst\n\t\t\tself = this,\n\t\t\tapi = this.api,\n\t\t\tdelay = this.gateway.isCached( query ) ? 0 : SEARCH_DELAY;\n\n\t\t// it seems the input event can be fired when virtual keyboard is closed\n\t\t// (Chrome for Android)\n\t\tif ( query !== this.lastQuery ) {\n\t\t\tif ( self._pendingQuery ) {\n\t\t\t\tself._pendingQuery.abort();\n\t\t\t}\n\t\t\tclearTimeout( this.timer );\n\n\t\t\tif ( query.length ) {\n\t\t\t\tthis.timer = setTimeout( () => {\n\t\t\t\t\tconst xhr = self.gateway.search( query );\n\t\t\t\t\tself._pendingQuery = xhr.then( function ( data ) {\n\t\t\t\t\t\tthis.currentSearchId = data.searchId;\n\t\t\t\t\t\t// FIXME: Given this manipulates SearchResultsView\n\t\t\t\t\t\t// this should be moved into that class\n\t\t\t\t\t\t// check if we're getting the rights response in case of out of\n\t\t\t\t\t\t// order responses (need to get the current value of the input)\n\t\t\t\t\t\tif ( data && data.query === self.$input.val() ) {\n\t\t\t\t\t\t\tself.$el.toggleClass( 'no-results', data.results.length === 0 );\n\t\t\t\t\t\t\tself.$searchContent\n\t\t\t\t\t\t\t\t.show()\n\t\t\t\t\t\t\t\t.find( 'p' )\n\t\t\t\t\t\t\t\t.hide()\n\t\t\t\t\t\t\t\t.filter( data.results.length ? '.with-results' : '.without-results' )\n\t\t\t\t\t\t\t\t.show();\n\n\t\t\t\t\t\t\t// eslint-disable-next-line no-new\n\t\t\t\t\t\t\tnew WatchstarPageList( {\n\t\t\t\t\t\t\t\tapi,\n\t\t\t\t\t\t\t\tfunnel: 'search',\n\t\t\t\t\t\t\t\tpages: data.results,\n\t\t\t\t\t\t\t\tel: self.$resultContainer\n\t\t\t\t\t\t\t} );\n\n\t\t\t\t\t\t\tself.$results = self.$resultContainer.find( 'li' );\n\t\t\t\t\t\t}\n\t\t\t\t\t} ).promise( {\n\t\t\t\t\t\tabort() {\n\t\t\t\t\t\t\txhr.abort();\n\t\t\t\t\t\t}\n\t\t\t\t\t} );\n\t\t\t\t}, delay );\n\t\t\t} else {\n\t\t\t\tself.resetSearch();\n\t\t\t}\n\n\t\t\tthis.lastQuery = query;\n\t\t}\n\t},\n\t/**\n\t * Clear results\n\t *\n\t * @private\n\t */\n\tresetSearch() {\n\t\tthis.$el.find( '.overlay-content' ).children().hide();\n\t}\n} );\n\nmodule.exports = SearchOverlay;\n",
"const View = require( '../View' ),\n\tIcon = require( '../Icon' ),\n\tAnchor = require( '../Anchor' ),\n\ticons = require( '../icons' ),\n\t$spinner = icons.spinner().$el,\n\tutil = require( '../util' );\n\n/**\n * Render search results.\n *\n * @extends module:mobile.startup/View\n * @param {Object} options\n * @param {string} options.searchContentLabel actionable label to tell the user they can \"search\n * within pages rather than doing a full text search\n * @param {string} options.noResultsMsg shows when no results displayed\n * @param {string} options.searchContentNoResultsMsg alternative to options.searchContentLabel that\n * shows when no search results have been displayed.\n */\nclass SearchResultsView extends View {\n\t/** @inheritdoc */\n\tget isTemplateMode() {\n\t\treturn true;\n\t}\n\n\t/** @inheritdoc */\n\tget template() {\n\t\treturn util.template( `\n<div class=\"search-results-view\">\n\t<div class=\"search-content\">\n\t\t<div class=\"caption\">\n\t\t\t<p class=\"with-results\">{{searchContentLabel}}</p>\n\t\t\t<p class=\"without-results\">{{noResultsMsg}}</p>\n\t\t\t<p class=\"without-results\">{{{searchContentNoResultsMsg}}}</p>\n\t\t</div>\n\t</div>\n\t<div class=\"spinner-container position-fixed\"></div>\n\t<div class=\"results\">\n\t\t<div class=\"results-list-container\"></div>\n\t\t{{#feedback}}\n\t\t\t<div class=\"search-feedback\">\n\t\t\t\t{{prompt}}\n\t\t\t</div>\n\t\t{{/feedback}}\n\t</div>\n</div>`\n\t\t);\n\t}\n\n\t/** @inheritdoc */\n\tpreRender() {\n\t\tconst feedbackLink = mw.config.get( 'wgCirrusSearchFeedbackLink' );\n\t\tif ( feedbackLink ) {\n\t\t\tthis.options.feedback = {\n\t\t\t\tprompt: mw.msg( 'mobile-frontend-search-feedback-prompt' ) };\n\t\t}\n\t}\n\n\t/** @inheritdoc */\n\tpostRender( options ) {\n\t\tconst feedbackLink = mw.config.get( 'wgCirrusSearchFeedbackLink' );\n\t\tsuper.postRender( options );\n\t\tthis.$el.find( '.search-content' ).prepend(\n\t\t\tnew Icon( { icon: 'articlesSearch' } ).$el\n\t\t);\n\t\tthis.$el.find( '.spinner-container' ).append( $spinner );\n\t\tif ( feedbackLink ) {\n\t\t\tthis.$el.find( '.search-feedback' ).append(\n\t\t\t\tnew Anchor( {\n\t\t\t\t\tlabel: mw.msg( 'mobile-frontend-search-feedback-link-text' ),\n\t\t\t\t\thref: feedbackLink\n\t\t\t\t} ).$el\n\t\t\t);\n\t\t}\n\t}\n}\n\nmodule.exports = SearchResultsView;\n",