diff --git a/resources/skins.citizen.preferences/skins.citizen.preferences.js b/resources/skins.citizen.preferences/skins.citizen.preferences.js index 2ab2e943..fb455703 100644 --- a/resources/skins.citizen.preferences/skins.citizen.preferences.js +++ b/resources/skins.citizen.preferences/skins.citizen.preferences.js @@ -1,5 +1,3 @@ -const CLASS = 'citizen-pref'; - /** * Clientprefs names theme differently from Citizen, we will need to translate it * TODO: Migrate to clientprefs fully on MW 1.43 @@ -13,17 +11,17 @@ const CLIENTPREFS_THEME_MAP = { const clientPrefs = require( './clientPrefs.polyfill.js' )(); /** - * Dismiss the prefernce panel when clicked outside + * Dismiss the preference panel when clicked outside * * @param {Event} event */ function dismissOnClickOutside( event ) { - const pref = document.getElementById( CLASS ); + const pref = document.getElementById( 'citizen-pref' ); if ( event.target instanceof Node && !pref.contains( event.target ) ) { - const panel = document.getElementById( CLASS + '-panel' ); + const panel = document.getElementById( 'citizen-pref-panel' ); - if ( panel.classList.contains( CLASS + '-panel--active' ) ) { + if ( panel.classList.contains( 'citizen-pref-panel--active' ) ) { togglePanel(); } } @@ -49,10 +47,10 @@ function dismissOnEscape( event ) { */ function togglePanel() { // .citizen-pref-panel--active - const CLASS_PANEL_ACTIVE = CLASS + '-panel--active'; + const CLASS_PANEL_ACTIVE = 'citizen-pref-panel--active'; const - toggle = document.getElementById( CLASS + '-toggle' ), - panel = document.getElementById( CLASS + '-panel' ); + toggle = document.getElementById( 'citizen-pref-toggle' ), + panel = document.getElementById( 'citizen-pref-panel' ); if ( !panel.classList.contains( CLASS_PANEL_ACTIVE ) ) { panel.classList.add( CLASS_PANEL_ACTIVE ); @@ -68,13 +66,13 @@ function togglePanel() { } /** - * Set up the DOM and initial input states for the panel - * It only loads when user first clicked the toggle + * Creates a panel element for user preferences. + * The panel includes a header with the text retrieved from mw.message('preferences').text() + * and a container element with the id 'citizen-client-prefs'. * - * @param {Event} event - * @return {void} + * @return {Element} The created panel element. */ -function initPanel( event ) { +function createPanel() { const panel = document.createElement( 'aside' ); panel.id = 'citizen-pref-panel'; panel.classList.add( 'citizen-pref-panel' ); @@ -89,21 +87,44 @@ function initPanel( event ) { panel.append( header, container ); - // Attach panel after button - event.currentTarget.parentNode.insertBefore( panel, event.currentTarget.nextSibling ); + return panel; +} +/** + * Attaches a panel element to a target button element. + * + * @param {Element} panel - The panel element to attach. + * @param {Element} button - The button element to attach the panel to. + * @return {void} + */ +function attachPanel( panel, button ) { + button.parentNode.insertBefore( panel, button.nextSibling ); +} + +/** + * Initializes the click event listener for a given button element. + * Toggles the panel visibility when the button is clicked. + * + * @param {Element} button - The button element to attach the click event listener to. + * @return {void} + */ +function initPanelClick( button ) { togglePanel(); - event.currentTarget.addEventListener( 'click', togglePanel ); - event.currentTarget.removeEventListener( 'click', initPanel ); + button.addEventListener( 'click', togglePanel ); + button.removeEventListener( 'click', initPanelClick ); +} - const clientPreferenceSelector = '#citizen-client-prefs'; - const clientPreferenceExists = document.querySelectorAll( clientPreferenceSelector ).length > 0; +/** + * Function to handle client preferences based on the existence of 'citizen-client-prefs' element. + * If the element exists, it loads client preferences and config, sets a callback for skin theme change, + * updates the document's classes based on the theme, and renders the client preferences. + */ +function handleClientPreferences() { + const clientPreferenceExists = document.getElementById( 'citizen-client-prefs' ) !== null; if ( clientPreferenceExists ) { - const clientPreferences = require( /** @type {string} */ ( './clientPreferences.js' ) ); + const clientPreferences = require( /** @type {string} */( './clientPreferences.js' ) ); const clientPreferenceConfig = ( require( './clientPreferences.json' ) ); - // Support legacy skin-citizen-* class - // TODO: Remove it in the future version after sufficient time clientPreferenceConfig[ 'skin-theme' ].callback = () => { const LEGACY_THEME_CLASSES = [ 'skin-citizen-auto', @@ -117,40 +138,21 @@ function initPanel( event ) { document.documentElement.classList.add( `skin-citizen-${ legacyThemeKey }` ); }; - clientPreferences.render( clientPreferenceSelector, clientPreferenceConfig ); + clientPreferences.render( '#citizen-client-prefs', clientPreferenceConfig ); } } /** - * Test if storage is avaliable - * Taken from https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API + * Initializes a panel by creating it, attaching it to a target element, + * setting up click event listeners, and handling client preferences. * - * @param {string} type - * @return {boolean|Error} + * @param {Event} event - The event triggering the initialization. */ -function storageAvailable( type ) { - let storage; - - try { - storage = window[ type ]; - const x = '__storage_test__'; - storage.setItem( x, x ); - storage.removeItem( x ); - return true; - } catch ( /** @type {Error} */ e ) { - return e instanceof DOMException && ( - // everything except Firefox - e.code === 22 || - // Firefox - e.code === 1014 || - // test name field too, because code might not be present - // everything except Firefox - e.name === 'QuotaExceededError' || - // Firefox - e.name === 'NS_ERROR_DOM_QUOTA_REACHED' ) && - // acknowledge QuotaExceededError only if there's something already stored - ( storage && storage.length !== 0 ); - } +function initPanel( event ) { + const panel = createPanel(); + attachPanel( panel, event.currentTarget ); + initPanelClick( event.currentTarget ); + handleClientPreferences(); } /** @@ -173,34 +175,32 @@ function initPref( window ) { } ); } - if ( storageAvailable( 'localStorage' ) ) { - if ( typeof window.mw !== 'undefined' ) { - const headerTools = document.querySelector( '.citizen-header__end' ), - container = document.createElement( 'div' ), - button = document.createElement( 'button' ), - icon = document.createElement( 'span' ); + if ( typeof window.localStorage !== 'undefined' && typeof window.mw !== 'undefined' ) { + const headerTools = document.querySelector( '.citizen-header__end' ); + const container = document.createElement( 'div' ); + const button = document.createElement( 'button' ); + const icon = document.createElement( 'span' ); - // citizen-pref - container.id = CLASS; + // citizen-pref + container.id = 'citizen-pref'; - container.classList.add( CLASS, 'citizen-header__item' ); - button.id = CLASS + '-toggle'; + container.classList.add( 'citizen-pref', 'citizen-header__item' ); + button.id = 'citizen-pref-toggle'; - button.classList.add( CLASS + '__button', 'citizen-header__button', 'citizen-button' ); - button.setAttribute( 'title', mw.message( 'preferences' ).text() ); - button.setAttribute( 'aria-label', mw.message( 'preferences' ).text() ); - button.setAttribute( 'aria-controls', CLASS + '-panel' ); - button.setAttribute( 'aria-expanded', false ); - icon.classList.add( 'citizen-ui-icon', 'mw-ui-icon-wikimedia-settings' ); - button.prepend( icon ); - container.prepend( button ); - headerTools.prepend( container ); + button.classList.add( 'citizen-pref__button', 'citizen-header__button', 'citizen-button' ); + button.setAttribute( 'title', mw.message( 'preferences' ).text() ); + button.setAttribute( 'aria-label', mw.message( 'preferences' ).text() ); + button.setAttribute( 'aria-controls', 'citizen-pref-panel' ); + button.setAttribute( 'aria-expanded', false ); + icon.classList.add( 'citizen-ui-icon', 'mw-ui-icon-wikimedia-settings' ); + button.prepend( icon ); + container.prepend( button ); + headerTools.prepend( container ); - button.addEventListener( 'click', initPanel, { once: true } ); - } + button.addEventListener( 'click', initPanel, { once: true } ); } else { // eslint-disable-next-line no-console - console.log( 'Preference module is disabled due to localStoarge being not avaliable.' ); + console.error( 'Preference module is disabled due to localStoarge being not avaliable.' ); } }