/** * JavaScript enhancement to persist the sidebar state for logged-in users. * */ /** @interface MwApi */ var /** @type {MwApi} */api, SIDEBAR_BUTTON_ID = 'mw-sidebar-button', debounce = require( /** @type {string} */ ( 'mediawiki.util' ) ).debounce, SIDEBAR_CHECKBOX_ID = 'mw-sidebar-checkbox', SIDEBAR_PREFERENCE_NAME = 'VectorSidebarVisible'; /** * Checks if persistent is enabled at current time. * When a user is using a browser with a screen resolution of < 1000 it is assumed * that it is preferred that the sidebar remains closed across page views, as otherwise * it gets in the way of reading. More context at T316191. * * @return {boolean} */ function isPersistentEnabled() { return window.innerWidth >= 1000; } /** * Execute a debounced API request to save the sidebar user preference. * The request is meant to fire 1000 milliseconds after the last click on * the sidebar button. * * @param {HTMLInputElement} checkbox * @param {number} timeout duration * @param {boolean} shouldTriggerResize whether a resize event is needed. * @return {any} */ function saveSidebarState( checkbox, timeout, shouldTriggerResize ) { return debounce( function () { api = api || new mw.Api(); api.saveOption( SIDEBAR_PREFERENCE_NAME, checkbox.checked ? 1 : 0 ); if ( !shouldTriggerResize ) { return; } // Trigger a resize event so other parts of the page can adapt: var event; if ( typeof Event === 'function' ) { event = new Event( 'resize' ); } else { // IE11 event = window.document.createEvent( 'UIEvents' ); event.initUIEvent( 'resize', true, false, window, 0 ); } window.dispatchEvent( event ); }, timeout ); } /** * Bind the event handler that saves the sidebar state to the click event * on the sidebar button. * * @param {HTMLElement|null} checkbox * @param {HTMLElement|null} button */ function bindSidebarClickEvent( checkbox, button ) { if ( checkbox instanceof HTMLInputElement && button ) { var handler = saveSidebarState( checkbox, 1000, true ); checkbox.addEventListener( 'input', function () { if ( isPersistentEnabled() ) { handler(); } } ); } } var /** @type {boolean} */ wasCollapsedDuringResize = false; /** * Collapses the sidebar if screen resolution too small. * * @param {HTMLInputElement} checkbox */ function collapseSidebar( checkbox ) { if ( checkbox.checked ) { wasCollapsedDuringResize = true; checkbox.checked = false; saveSidebarState( checkbox, 0, false )(); } } /** * Expands the sidebar when the window is resized if it was previously collapsed. * * @param {HTMLInputElement} checkbox */ function expandSidebar( checkbox ) { if ( wasCollapsedDuringResize && !checkbox.checked ) { wasCollapsedDuringResize = false; checkbox.checked = true; saveSidebarState( checkbox, 0, false )(); } } function init() { var checkbox = /** @type {HTMLInputElement|null} */ ( window.document.getElementById( SIDEBAR_CHECKBOX_ID ) ), button = window.document.getElementById( SIDEBAR_BUTTON_ID ); if ( mw.config.get( 'wgUserName' ) && !mw.config.get( 'wgVectorDisableSidebarPersistence' ) ) { bindSidebarClickEvent( checkbox, button ); } // If the user has resized their window, an open sidebar will be taking up lots of space // so we should disable it. // When this happens the user must expand it again manually, to avoid conflicts with multiple // open windows (for example when an editor is viewing 2 articles side by side). if ( checkbox ) { var mediaQuery = window.matchMedia( '(max-width: 999px)' ); var onMediaQueryChangeCollapse = function ( /** @type {MediaQueryListEvent} */ event ) { if ( event.matches ) { // @ts-ignore we checked it already. collapseSidebar( checkbox ); } else { // @ts-ignore we checked it already. expandSidebar( checkbox ); } }; if ( mediaQuery.matches ) { collapseSidebar( checkbox ); } if ( mediaQuery.addEventListener ) { mediaQuery.addEventListener( 'change', onMediaQueryChangeCollapse ); } else { // Before Safari 14, MediaQueryList is based on EventTarget, // so you must use addListener() and removeListener() to observe media query lists. mediaQuery.addListener( onMediaQueryChangeCollapse ); } } } module.exports = { init: init };