wikimedia/mediawiki-extensions-MobileFrontend

View on GitHub
resources/dist/mobile.special.mobileoptions.scripts.js.map.json

Summary

Maintainability
Test Coverage
{"version":3,"file":"mobile.special.mobileoptions.scripts.js","mappings":"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","sources":["webpack://mfModules/./node_modules/@wikimedia/mediawiki.skins.clientpreferences/clientPreferences.js","webpack://mfModules/./src/mobile.special.mobileoptions.scripts.js"],"sourcesContent":["/**\n * @typedef {Object} ClientPreference\n * @property {string[]} options that are valid for this client preference\n * @property {string} preferenceKey for registered users.\n * @property {string} betaMessage whether to show a notice indicating this feature is in beta.\n * @property {string} [type] defaults to radio. Supported: radio, switch\n * @property {Function} [callback] callback executed after a client preference has been modified.\n */\n\n/**\n * @typedef {Object} UserPreferencesApi\n * @property {Function} saveOptions\n */\n/**\n * @typedef {Object} PreferenceOption\n * @property {string} label\n * @property {string} value\n */\n\n/**\n * Get the list of client preferences that are active on the page, including hidden.\n *\n * @return {string[]} of active client preferences\n */\nfunction getClientPreferences() {\n\treturn Array.from( document.documentElement.classList ).filter(\n\t\t( className ) => className.match( /-clientpref-/ )\n\t).map( ( className ) => className.split( '-clientpref-' )[ 0 ] );\n}\n\n/**\n * Check if the feature is excluded from the current page.\n *\n * @param {string} featureName\n * @return {boolean}\n */\nfunction isFeatureExcluded( featureName ) {\n\treturn document.documentElement.classList.contains( featureName + '-clientpref--excluded' );\n}\n\n/**\n * Get the list of client preferences that are active on the page and not hidden.\n *\n * @param {Record<string,ClientPreference>} config\n * @return {string[]} of user facing client preferences\n */\nfunction getVisibleClientPreferences( config ) {\n\tconst active = getClientPreferences();\n\t// Order should be based on key in config.json\n\treturn Object.keys( config ).filter( ( key ) => active.indexOf( key ) > -1 );\n}\n\n/**\n * @param {string} featureName\n * @param {string} value\n * @param {Record<string,ClientPreference>} config\n * @param {UserPreferencesApi} [userPreferences]\n */\nfunction toggleDocClassAndSave( featureName, value, config, userPreferences ) {\n\tconst pref = config[ featureName ];\n\tconst callback = pref.callback || ( () => {} );\n\tif ( mw.user.isNamed() ) {\n\t\t// FIXME: Ideally this would be done in mw.user.clientprefs API.\n\t\t// mw.user.clientPrefs.get is marked as being only stable for anonymous and temporary users.\n\t\t// So instead we have to keep track of all the different possible values and remove them\n\t\t// before adding the new class.\n\t\tconfig[ featureName ].options.forEach( ( possibleValue ) => {\n\t\t\tdocument.documentElement.classList.remove( `${ featureName }-clientpref-${ possibleValue }` );\n\t\t} );\n\t\tdocument.documentElement.classList.add( `${ featureName }-clientpref-${ value }` );\n\t\t// Ideally this should be taken care of via a single core helper function.\n\t\tmw.util.debounce( () => {\n\t\t\tuserPreferences = userPreferences || new mw.Api();\n\t\t\tuserPreferences.saveOptions( { [ pref.preferenceKey ]: value } ).then( () => {\n\t\t\t\tcallback();\n\t\t\t} );\n\t\t}, 100 )();\n\t\t// END FIXME.\n\t} else {\n\t\t// This case is much simpler, the API transparently takes care of classes as well as storage\n\t\tmw.user.clientPrefs.set( featureName, value );\n\t\tcallback();\n\t}\n}\n\n/**\n * @param {string} featureName\n * @param {string} value\n * @return {string}\n */\nconst getInputId = ( featureName, value ) => `skin-client-pref-${ featureName }-value-${ value }`;\n\n/**\n * @param {string} type\n * @param {string} featureName\n * @param {string} value\n * @return {HTMLInputElement}\n */\nfunction makeInputElement( type, featureName, value ) {\n\tconst input = document.createElement( 'input' );\n\tconst name = `skin-client-pref-${ featureName }-group`;\n\tconst id = getInputId( featureName, value );\n\tinput.name = name;\n\tinput.id = id;\n\tinput.type = type;\n\tif ( type === 'checkbox' ) {\n\t\tinput.checked = value === '1';\n\t} else {\n\t\tinput.value = value;\n\t}\n\tinput.setAttribute( 'data-event-name', id );\n\treturn input;\n}\n\n/**\n * @param {string} featureName\n * @param {string} value\n * @return {HTMLLabelElement}\n */\nfunction makeLabelElement( featureName, value ) {\n\tconst label = document.createElement( 'label' );\n\t// eslint-disable-next-line mediawiki/msg-doc\n\tlabel.textContent = mw.msg( `${ featureName }-${ value }-label` );\n\tlabel.setAttribute( 'for', getInputId( featureName, value ) );\n\treturn label;\n}\n\n/**\n * Create an element that informs users that a feature is not functional\n * on a given page. This message is hidden by default and made visible in\n * CSS if a specific exclusion class exists.\n *\n * @param {string} featureName\n * @return {HTMLElement}\n */\nfunction makeExclusionNotice( featureName ) {\n\tconst p = document.createElement( 'p' );\n\t// eslint-disable-next-line mediawiki/msg-doc\n\tconst noticeMessage = mw.message( `${ featureName }-exclusion-notice` );\n\tp.classList.add( 'exclusion-notice', `${ featureName }-exclusion-notice` );\n\tp.textContent = noticeMessage.text();\n\treturn p;\n}\n\n/**\n * @return {HTMLElement}\n */\nfunction makeBetaInfoTag() {\n\tconst infoTag = document.createElement( 'span' );\n\t// custom style to avoid moving heading bottom border.\n\tconst infoTagText = document.createElement( 'span' );\n\tinfoTagText.textContent = mw.message( 'vector-night-mode-beta-tag' ).text();\n\tinfoTag.appendChild( infoTagText );\n\treturn infoTag;\n}\n\n/**\n * @param {Element} parent\n * @param {string} featureName\n * @param {string} value\n * @param {string} currentValue\n * @param {Record<string,ClientPreference>} config\n * @param {UserPreferencesApi} userPreferences\n */\nfunction appendRadioToggle( parent, featureName, value, currentValue, config, userPreferences ) {\n\tconst input = makeInputElement( 'radio', featureName, value );\n\tinput.classList.add( 'cdx-radio__input' );\n\tif ( currentValue === value ) {\n\t\tinput.checked = true;\n\t}\n\n\tif ( isFeatureExcluded( featureName ) ) {\n\t\tinput.disabled = true;\n\t}\n\n\tconst icon = document.createElement( 'span' );\n\ticon.classList.add( 'cdx-radio__icon' );\n\tconst label = makeLabelElement( featureName, value );\n\tlabel.classList.add( 'cdx-radio__label' );\n\tconst container = document.createElement( 'div' );\n\tcontainer.classList.add( 'cdx-radio' );\n\tcontainer.appendChild( input );\n\tcontainer.appendChild( icon );\n\tcontainer.appendChild( label );\n\tparent.appendChild( container );\n\tinput.addEventListener( 'change', () => {\n\t\ttoggleDocClassAndSave( featureName, value, config, userPreferences );\n\t} );\n}\n\n/**\n * @param {Element} form\n * @param {string} featureName\n * @param {HTMLElement} labelElement\n * @param {string} currentValue\n * @param {Record<string,ClientPreference>} config\n * @param {UserPreferencesApi} userPreferences\n */\nfunction appendToggleSwitch( form, featureName, labelElement, currentValue, config, userPreferences ) {\n\tconst input = makeInputElement( 'checkbox', featureName, currentValue );\n\tinput.classList.add( 'cdx-toggle-switch__input' );\n\tconst switcher = document.createElement( 'span' );\n\tswitcher.classList.add( 'cdx-toggle-switch__switch' );\n\tconst grip = document.createElement( 'span' );\n\tgrip.classList.add( 'cdx-toggle-switch__switch__grip' );\n\tswitcher.appendChild( grip );\n\tconst label = labelElement || makeLabelElement( featureName, currentValue );\n\tlabel.classList.add( 'cdx-toggle-switch__label' );\n\tconst toggleSwitch = document.createElement( 'span' );\n\ttoggleSwitch.classList.add( 'cdx-toggle-switch' );\n\ttoggleSwitch.appendChild( input );\n\ttoggleSwitch.appendChild( switcher );\n\ttoggleSwitch.appendChild( label );\n\tinput.addEventListener( 'change', () => {\n\t\ttoggleDocClassAndSave( featureName, input.checked ? '1' : '0', config, userPreferences );\n\t} );\n\tform.appendChild( toggleSwitch );\n}\n\n/**\n * @param {string} className\n * @return {Element}\n */\nfunction createRow( className ) {\n\tconst row = document.createElement( 'div' );\n\trow.setAttribute( 'class', className );\n\treturn row;\n}\n\n/**\n * Get the label for the feature.\n *\n * @param {string} featureName\n * @return {MwMessage}\n */\n// eslint-disable-next-line mediawiki/msg-doc\nconst getFeatureLabelMsg = ( featureName ) => mw.message( `${ featureName }-name` );\n\n/**\n * adds a toggle button\n *\n * @param {string} featureName\n * @param {Record<string,ClientPreference>} config\n * @param {UserPreferencesApi} userPreferences\n * @return {Element|null}\n */\nfunction makeControl( featureName, config, userPreferences ) {\n\tconst pref = config[ featureName ];\n\tconst isExcluded = isFeatureExcluded( featureName );\n\n\tif ( !pref ) {\n\t\treturn null;\n\t}\n\tconst currentValue = mw.user.clientPrefs.get( featureName );\n\t// The client preference was invalid. This shouldn't happen unless a gadget\n\t// or script has modified the documentElement or client preference is excluded.\n\tif ( typeof currentValue === 'boolean' && !isExcluded ) {\n\t\treturn null;\n\t}\n\tconst row = createRow( '' );\n\tconst form = document.createElement( 'form' );\n\tconst type = pref.type || 'radio';\n\tswitch ( type ) {\n\t\tcase 'radio':\n\t\t\tpref.options.forEach( ( value ) => {\n\t\t\t\tappendRadioToggle( form, featureName, value, String( currentValue ), config, userPreferences );\n\t\t\t} );\n\t\t\tbreak;\n\t\tcase 'switch': {\n\t\t\tconst labelElement = document.createElement( 'label' );\n\t\t\tlabelElement.textContent = getFeatureLabelMsg( featureName ).text();\n\t\t\tappendToggleSwitch( form, featureName, labelElement, String( currentValue ), config, userPreferences );\n\t\t\tbreak;\n\t\t} default:\n\t\t\tthrow new Error( 'Unknown client preference! Only switch or radio are supported.' );\n\t}\n\trow.appendChild( form );\n\n\tif ( isExcluded ) {\n\t\tconst exclusionNotice = makeExclusionNotice( featureName );\n\t\trow.appendChild( exclusionNotice );\n\t}\n\treturn row;\n}\n\n/**\n * @param {Element} parent\n * @param {string} featureName\n * @param {Record<string,ClientPreference>} config\n * @param {UserPreferencesApi} userPreferences\n */\nfunction makeClientPreference( parent, featureName, config, userPreferences ) {\n\tconst labelMsg = getFeatureLabelMsg( featureName );\n\t// If the user is not debugging messages and no language exists,\n\t// exit as its a hidden client preference.\n\tif ( !labelMsg.exists() && mw.config.get( 'wgUserLanguage' ) !== 'qqx' ) {\n\t\treturn;\n\t} else {\n\t\tconst id = `skin-client-prefs-${ featureName }`;\n\t\t// @ts-ignore TODO: upstream patch URL\n\t\tconst portlet = mw.util.addPortlet( id, labelMsg.text() );\n\n\t\tif ( config[ featureName ].betaMessage ) {\n\t\t\tconst betaInfoTag = makeBetaInfoTag();\n\t\t\tif ( !portlet.querySelector( '.vector-menu-heading span' ) ) {\n\t\t\t\tportlet.querySelector( '.vector-menu-heading' ).textContent += ' ';\n\t\t\t\tportlet.querySelector( '.vector-menu-heading' ).appendChild( betaInfoTag );\n\t\t\t}\n\t\t}\n\n\t\tconst labelElement = portlet.querySelector( 'label' );\n\n\t\t// Add additional description for mobile\n\t\t// eslint-disable-next-line mediawiki/msg-doc\n\t\tconst descriptionMsg = mw.message( `${ featureName }-description` );\n\t\tif ( descriptionMsg.exists() ) {\n\t\t\tconst desc = document.createElement( 'span' );\n\t\t\tdesc.classList.add( 'skin-client-pref-description' );\n\t\t\tdesc.textContent = descriptionMsg.text();\n\t\t\tif ( labelElement && labelElement.parentNode ) {\n\t\t\t\tlabelElement.appendChild( desc );\n\t\t\t}\n\t\t}\n\n\t\t// Add exclusion notice for desktop\n\t\t// eslint-disable-next-line mediawiki/msg-doc\n\t\tconst exclusionNoticeMsg = mw.message( `${ featureName }-exclusion-notice` );\n\t\tif ( exclusionNoticeMsg.exists() ) {\n\t\t\tconst content = portlet.querySelector( '.vector-menu-content' );\n\t\t\tconst notice = document.createElement( 'span' );\n\t\t\tnotice.classList.add( 'skin-client-pref-exclusion-notice' );\n\t\t\tnotice.textContent = exclusionNoticeMsg.text();\n\t\t\tif ( content ) {\n\t\t\t\tcontent.appendChild( notice );\n\t\t\t}\n\t\t}\n\n\t\tparent.appendChild( portlet );\n\t\tconst row = makeControl( featureName, config, userPreferences );\n\t\tif ( row ) {\n\t\t\tconst tmp = mw.util.addPortletLink( id, '', '' );\n\t\t\t// create a dummy link\n\t\t\tif ( tmp ) {\n\t\t\t\tconst link = tmp.querySelector( 'a' );\n\t\t\t\tif ( link ) {\n\t\t\t\t\tlink.replaceWith( row );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( config[ featureName ].betaMessage && !isFeatureExcluded( featureName ) ) {\n\t\t\t\tconst betaMessageElement = document.createElement( 'span' );\n\t\t\t\tbetaMessageElement.id = `${ featureName }-beta-notice`;\n\t\t\t\tconst pageWikiLink = `[https://${ window.location.hostname + mw.util.getUrl( mw.config.get( 'wgPageName' ) ) } ${ mw.config.get( 'wgTitle' ) }]`;\n\t\t\t\tconst preloadTitle = mw.message( 'vector-night-mode-issue-reporting-preload-title', pageWikiLink ).text();\n\t\t\t\tconst link = mw.msg( 'vector-night-mode-issue-reporting-notice-url', window.location.host, preloadTitle );\n\t\t\t\tconst linkLabel = mw.message( 'vector-night-mode-issue-reporting-link-label' ).text();\n\t\t\t\tconst anchor = document.createElement( 'a' );\n\t\t\t\t// per requirements: only logged in users can report errors.\n\t\t\t\tif ( !mw.user.isAnon() ) {\n\t\t\t\t\tanchor.setAttribute( 'href', link );\n\t\t\t\t\tanchor.setAttribute( 'target', '_blank' );\n\t\t\t\t}\n\t\t\t\tanchor.textContent = linkLabel;\n\t\t\t\tconst showSuccessFeedback = function () {\n\t\t\t\t\tconst icon = document.createElement( 'span' );\n\t\t\t\t\ticon.classList.add( 'vector-icon', 'vector-icon--heart' );\n\t\t\t\t\tanchor.textContent = mw.msg( 'vector-night-mode-issue-reporting-link-notification' );\n\t\t\t\t\tanchor.classList.add( 'skin-theme-beta-notice-success' );\n\t\t\t\t\tanchor.prepend( icon );\n\t\t\t\t\tanchor.removeEventListener( 'click', showSuccessFeedback );\n\t\t\t\t};\n\t\t\t\tanchor.addEventListener( 'click', showSuccessFeedback );\n\t\t\t\tbetaMessageElement.appendChild( anchor );\n\t\t\t\trow.appendChild( betaMessageElement );\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Fills the client side preference dropdown with controls.\n *\n * @param {string} selector of element to fill with client preferences\n * @param {Record<string,ClientPreference>} config\n * @param {UserPreferencesApi} [userPreferences]\n * @return {Promise<Node>}\n */\nfunction render( selector, config, userPreferences ) {\n\tconst node = document.querySelector( selector );\n\tif ( !node ) {\n\t\treturn Promise.reject();\n\t}\n\treturn new Promise( ( resolve ) => {\n\t\tgetVisibleClientPreferences( config ).forEach( ( pref ) => {\n\t\t\tuserPreferences = userPreferences || new mw.Api();\n\t\t\tmakeClientPreference( node, pref, config, userPreferences );\n\t\t} );\n\t\tmw.requestIdleCallback( () => {\n\t\t\tresolve( node );\n\t\t} );\n\t} );\n}\n\n/**\n * @param {string} clickSelector what to click\n * @param {string} renderSelector where to render\n * @param {Record<string,ClientPreference>} config\n * @param {UserPreferencesApi} [userPreferences]\n */\nfunction bind( clickSelector, renderSelector, config, userPreferences ) {\n\tlet enhanced = false;\n\tconst chk = /** @type {HTMLInputElement} */ (\n\t\tdocument.querySelector( clickSelector )\n\t);\n\tif ( !chk ) {\n\t\treturn;\n\t}\n\tif ( !userPreferences ) {\n\t\tuserPreferences = new mw.Api();\n\t}\n\tif ( chk.checked ) {\n\t\trender( renderSelector, config, userPreferences );\n\t\tenhanced = true;\n\t} else {\n\t\tchk.addEventListener( 'input', () => {\n\t\t\tif ( enhanced ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\trender( renderSelector, config, userPreferences );\n\t\t\tenhanced = true;\n\t\t} );\n\t}\n}\nmodule.exports = {\n\tbind,\n\ttoggleDocClassAndSave,\n\trender\n};\n","/* global $ */\n/* See T354224 for information on the @wikimedia/mediawiki.skins.clientpreferences module. */\nconst clientPrefs = require( '@wikimedia/mediawiki.skins.clientpreferences' ),\n\ttoast = require( './mobile.startup/showOnPageReload' ),\n\tamcOutreach = require( './mobile.startup/amcOutreach/amcOutreach' ),\n\tEXPAND_SECTIONS_KEY = 'mf-expand-sections',\n\tmsg = mw.msg,\n\tUSER_FONT_SIZE_SMALL = 'small',\n\tUSER_FONT_SIZE_REGULAR = 'regular',\n\tUSER_FONT_SIZE_LARGE = 'large',\n\tTHEME = 'skin-theme',\n\t// FIXME: This value should be synced between back-end and front-end code,\n\t// but it's currently hard-coded because ResourceLoader virtual imports\n\t// i.e. require( './config.json') are incompatible with the\n\t// Webpack build. Requires updating to Webpack 5 and common.js magic comments.\n\t// https://webpack.js.org/configuration/module/#moduleparserjavascriptcommonjsmagiccomments\n\t// e.g: require(/* webpackIgnore: true */ './config.json');\n\tFONT_SIZE_KEY = 'mf-font-size';\n\n/**\n * Notifies the user that settings were asynchronously saved.\n *\n * @private\n * @param {boolean} [isPending] if set toast will show after page has been reloaded.\n */\nfunction notify( isPending ) {\n\tif ( isPending ) {\n\t\ttoast.showOnPageReload( msg( 'mobile-frontend-settings-save' ) );\n\t} else {\n\t\tmw.notify( msg( 'mobile-frontend-settings-save' ) );\n\t}\n}\n\nlet api;\n/**\n * @ignore\n * @param {Object<string,string|number>} options\n * @return {JQuery.Promise<Object>}\n */\nfunction saveOptions( options ) {\n\tapi = api || new mw.Api();\n\t// @ts-ignore\n\treturn api.saveOptions( options, {\n\t\tglobal: 'update'\n\t} );\n}\n\n/**\n * Adds a font changer field to the form\n *\n * @private\n * @param {jQuery.Object} $form\n * @param {Record<string,ClientPreference>} clientPreferences\n * @return {Promise<Node>}\n */\nfunction addClientPreferencesToForm( $form, clientPreferences ) {\n\tconst cp = document.createElement( 'div' );\n\tconst id = 'mf-client-preferences';\n\tcp.id = id;\n\t$form.prepend( cp );\n\treturn clientPrefs.render( `#${ id }`, clientPreferences, { saveOptions } );\n}\n\n/**\n * Helper method to infuse checkbox elements with OO magic\n * Additionally it applies all known hacks to make it mobile friendly\n *\n * @private\n * @param {Object[]} toggleObjects an array of toggle objects to infuse\n * @param {jQuery.Object} $form form to submit when there is interaction with toggle\n */\nfunction infuseToggles( toggleObjects, $form ) {\n\ttoggleObjects.forEach( function ( toggleObject ) {\n\t\tconst $toggleElement = toggleObject.$el;\n\t\tconst enableToggle = OO.ui.infuse( $toggleElement );\n\t\tconst $checkbox = enableToggle.$element;\n\t\tconst toggleSwitch = new OO.ui.ToggleSwitchWidget( {\n\t\t\tvalue: enableToggle.isSelected()\n\t\t} );\n\n\t\t// Strangely the ToggleSwitchWidget does not behave as an input so any change\n\t\t// to it is not reflected in the form. (see T182466)\n\t\t// Ideally we'd replaceWith here and not have to hide the original element.\n\t\ttoggleSwitch.$element.insertAfter( $checkbox );\n\t\t// although the checkbox is hidden already, that is done via visibility\n\t\t// as a result, it still takes up space. We don't want it to any more now that the\n\t\t// new toggle switch has been added.\n\t\t$checkbox.hide();\n\n\t\t// listening on checkbox change is required to make the clicking on label working.\n\t\t// Otherwise clicking on label changes the checkbox \"checked\" state\n\t\t// but it's not reflected in the toggle switch\n\t\t$checkbox.on( 'change', function () {\n\t\t\t// disable checkbox as submit is delayed by 0.25s\n\t\t\t$checkbox.attr( 'disabled', true );\n\t\t\ttoggleSwitch.setValue( enableToggle.isSelected() );\n\t\t} );\n\t\ttoggleSwitch.on( 'change', function ( value ) {\n\t\t\t// execute callback\n\t\t\ttoggleObject.onToggle( value );\n\n\t\t\t// ugly hack, we're delaying submit form by 0.25s\n\t\t\t// and we want to disable registering clicks\n\t\t\t// we want to disable the toggleSwitch\n\t\t\t// but we cannot use setDisabled(true) as it makes button gray\n\t\t\ttoggleSwitch.setValue = function () {};\n\n\t\t\t$checkbox.find( 'input' )\n\t\t\t\t.prop( 'checked', value );\n\t\t\tnotify( true );\n\t\t\t// On some Android devices animation gets stuck in the middle as browser\n\t\t\t// starts submitting the form.\n\t\t\t// Let's call submit on the form after toggle button transition is done\n\t\t\t// (0.25s, defined in OOUI)\n\t\t\tsetTimeout( function () {\n\t\t\t\t$form.trigger( 'submit' );\n\t\t\t}, 250 );\n\t\t} );\n\t} );\n}\n\n/**\n * Add features, that depends on localStorage, such as \"expand all sections\" or \"fontchanger\".\n * The checkbox is used for turning on/off expansion of all sections on page load.\n * @private\n */\nfunction initMobileOptions() {\n\tconst $form = $( '#mobile-options' ),\n\t\t$betaToggle = $( '#enable-beta-toggle' ),\n\t\t$amcToggle = $( '#enable-amc-toggle' ),\n\t\ttoggles = [];\n\n\tif ( $betaToggle.length ) {\n\t\ttoggles.push( {\n\t\t\t$el: $betaToggle,\n\t\t\tonToggle: function () {}\n\t\t} );\n\t}\n\tif ( $amcToggle.length ) {\n\t\ttoggles.push( {\n\t\t\t$el: $amcToggle,\n\t\t\tonToggle: function ( value ) {\n\t\t\t\tif ( !value && amcOutreach.loadCampaign().isCampaignActive() ) {\n\t\t\t\t\t// Make all amc outreach actions ineligible so the user doesn't have\n\t\t\t\t\t// to see the outreach drawer again\n\t\t\t\t\tamcOutreach.loadCampaign().makeAllActionsIneligible();\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t}\n\tinfuseToggles( toggles, $form );\n\n\tconst clientPreferences = {};\n\n\tif ( mw.config.get( 'wgMFEnableFontChanger' ) ) {\n\t\tclientPreferences[ FONT_SIZE_KEY ] = {\n\t\t\toptions: [\n\t\t\t\tUSER_FONT_SIZE_SMALL,\n\t\t\t\tUSER_FONT_SIZE_REGULAR,\n\t\t\t\tUSER_FONT_SIZE_LARGE\n\t\t\t],\n\t\t\tpreferenceKey: FONT_SIZE_KEY,\n\t\t\tcallback: notify\n\t\t};\n\t}\n\n\tconst skin = mw.config.get( 'skin' );\n\tclientPreferences[ THEME ] = {\n\t\toptions: [ 'day', 'night', 'os' ],\n\t\tpreferenceKey: `${ skin }-theme`\n\t};\n\n\tclientPreferences[ EXPAND_SECTIONS_KEY ] = {\n\t\toptions: [\n\t\t\t'0',\n\t\t\t'1'\n\t\t],\n\t\ttype: 'switch',\n\t\tpreferenceKey: EXPAND_SECTIONS_KEY,\n\t\tcallback: notify\n\t};\n\n\tif ( !mw.user.isAnon() ) {\n\t\tclientPreferences[ 'mw-mf-amc' ] = {\n\t\t\toptions: [\n\t\t\t\t'0',\n\t\t\t\t'1'\n\t\t\t],\n\t\t\ttype: 'switch',\n\t\t\tpreferenceKey: 'mf_amc_optin',\n\t\t\tcallback: () => location.reload()\n\t\t};\n\t}\n\n\t// Transport existing links to new layout.\n\n\t/**\n\t * Currently toggle switches have duplicate headings and a description that is not\n\t * part of the toggle switch layout.\n\t * This works around this to retain the classic MobileFrontend layout for these\n\t * controls.\n\t * FIXME: This should be upstreamed to @wikimedia/mediawiki.skins.clientpreferences\n\t *\n\t * @param {jQuery} $node\n\t */\n\tfunction modifyToggleSwitch( $node ) {\n\t\t// Move the description from the heading into the label.\n\t\t$node.find( '.skin-client-pref-description' )\n\t\t\t.appendTo( $node.find( '.cdx-toggle-switch__label' ) );\n\t\t// Drop the duplicate label.\n\t\t$node.find( '> label' ).remove();\n\t}\n\n\taddClientPreferencesToForm( $form, clientPreferences ).then( () => {\n\t\t// Make some modifications that are currently not supported by the Vector client preferences\n\t\t// Move the links from the server side preference into the row.\n\t\t$( '#amc-field .option-links' ).appendTo( '#skin-client-prefs-mw-mf-amc' );\n\t\tmodifyToggleSwitch( $( '#skin-client-prefs-mf-expand-sections' ) );\n\t\tmodifyToggleSwitch( $( '#skin-client-prefs-mw-mf-amc' ) );\n\t\t// Remove the server side rendered OOUI field.\n\t\t$( '#amc-field' ).remove();\n\n\t} );\n}\n\nif ( !window.QUnit ) {\n\tmw.loader.using( 'oojs-ui-widgets' ).then( initMobileOptions );\n}\n\nmodule.exports = {\n\ttest: {\n\t\taddClientPreferencesToForm\n\t}\n};\n"],"names":["isFeatureExcluded","featureName","document","documentElement","classList","contains","toggleDocClassAndSave","value","config","userPreferences","pref","callback","mw","user","isNamed","options","forEach","possibleValue","remove","add","util","debounce","Api","saveOptions","preferenceKey","then","clientPrefs","set","getInputId","makeInputElement","type","input","createElement","name","id","checked","setAttribute","makeLabelElement","label","textContent","msg","getFeatureLabelMsg","message","makeClientPreference","parent","labelMsg","exists","get","portlet","addPortlet","text","betaMessage","betaInfoTag","infoTag","infoTagText","appendChild","makeBetaInfoTag","querySelector","labelElement","descriptionMsg","desc","parentNode","exclusionNoticeMsg","content","notice","row","isExcluded","currentValue","createRow","form","disabled","icon","container","addEventListener","appendRadioToggle","String","switcher","grip","toggleSwitch","appendToggleSwitch","Error","exclusionNotice","p","noticeMessage","makeExclusionNotice","makeControl","tmp","addPortletLink","link","replaceWith","betaMessageElement","pageWikiLink","window","location","hostname","getUrl","preloadTitle","host","linkLabel","anchor","isAnon","showSuccessFeedback","prepend","removeEventListener","render","selector","node","Promise","resolve","active","Array","from","filter","className","match","map","split","Object","keys","key","indexOf","getVisibleClientPreferences","requestIdleCallback","reject","module","exports","bind","clickSelector","renderSelector","enhanced","chk","api","require","toast","amcOutreach","EXPAND_SECTIONS_KEY","FONT_SIZE_KEY","notify","isPending","showOnPageReload","global","addClientPreferencesToForm","$form","clientPreferences","cp","concat","QUnit","loader","using","$","$betaToggle","$amcToggle","toggles","length","push","$el","onToggle","loadCampaign","isCampaignActive","makeAllActionsIneligible","toggleObjects","toggleObject","$toggleElement","enableToggle","OO","ui","infuse","$checkbox","$element","ToggleSwitchWidget","isSelected","insertAfter","hide","on","attr","setValue","find","prop","setTimeout","trigger","infuseToggles","skin","modifyToggleSwitch","$node","appendTo","reload","test"],"sourceRoot":""}