const languageButton = require( './languageButton.js' ), pinnableElement = require( './pinnableElement.js' ), searchToggle = require( './searchToggle.js' ), echo = require( './echo.js' ), initExperiment = require( './AB.js' ), ABTestConfig = require( /** @type {string} */ ( './activeABTest.json' ) ), initSearchLoader = require( './searchLoader.js' ).initSearchLoader, portletsManager = require( './portlets.js' ), dropdownMenus = require( './dropdownMenus.js' ).dropdownMenus, tables = require( './tables.js' ).init, watchstar = require( './watchstar.js' ).init, setupIntersectionObservers = require( './setupIntersectionObservers.js' ), menuTabs = require( './menuTabs.js' ), userPreferences = require( './userPreferences.js' ), { isNightModeGadgetEnabled, disableNightModeForGadget, alterExclusionMessage, removeBetaNotice } = require( './disableNightModeIfGadget.js' ), teleportTarget = /** @type {HTMLElement} */require( /** @type {string} */ ( 'mediawiki.page.ready' ) ).teleportTarget; /** * Wait for first paint before calling this function. That's its whole purpose. * * Some CSS animations and transitions are "disabled" by default as a workaround to this old Chrome * bug, https://bugs.chromium.org/p/chromium/issues/detail?id=332189, which otherwise causes them to * render in their terminal state on page load. By adding the `vector-animations-ready` class to the * `html` root element **after** first paint, the animation selectors suddenly match causing the * animations to become "enabled" when they will work properly. A similar pattern is used in Minerva * (see T234570#5779890, T246419). * * Example usage in Less: * * ```less * .foo { * color: #f00; * transform: translateX( -100% ); * } * * // This transition will be disabled initially for JavaScript users. It will never be enabled for * // non-JavaScript users. * .vector-animations-ready .foo { * transition: transform 100ms ease-out; * } * ``` * * @param {Document} document * @return {void} */ function enableCssAnimations( document ) { document.documentElement.classList.add( 'vector-animations-ready' ); } /** * @param {Window} window * @return {void} */ function main( window ) { enableCssAnimations( window.document ); initSearchLoader( document ); languageButton(); echo(); portletsManager.main(); watchstar(); // Initialize the search toggle for the main header only. The sticky header // toggle is initialized after Codex search loads. const searchToggleElement = document.querySelector( '.mw-header .search-toggle' ); if ( searchToggleElement ) { searchToggle( searchToggleElement ); } pinnableElement.initPinnableElement(); // Initializes the TOC and sticky header, behaviour of which depend on scroll behaviour. setupIntersectionObservers.main(); // Apply body styles to teleported elements teleportTarget.classList.add( 'vector-body' ); // Load client preferences const appearanceMenuSelector = '#vector-appearance'; const appearanceMenuExists = document.querySelectorAll( appearanceMenuSelector ).length > 0; if ( appearanceMenuExists ) { mw.loader.using( [ 'skins.vector.clientPreferences', 'skins.vector.search.codex.styles' ] ).then( () => { const clientPreferences = require( /** @type {string} */ ( 'skins.vector.clientPreferences' ) ); const clientPreferenceConfig = ( require( './clientPreferences.json' ) ); // Can be removed once wgVectorNightMode is removed. if ( document.documentElement.classList.contains( 'vector-feature-night-mode-disabled' ) ) { // @ts-ignore issues relating to delete operator are not relevant here. delete clientPreferenceConfig[ 'skin-theme' ]; } // while we're in beta, temporarily check if the night mode gadget is installed and // disable our night mode if so if ( isNightModeGadgetEnabled() ) { disableNightModeForGadget(); clientPreferences.render( appearanceMenuSelector, clientPreferenceConfig, userPreferences ); alterExclusionMessage(); removeBetaNotice(); } else { clientPreferences.render( appearanceMenuSelector, clientPreferenceConfig, userPreferences ); } } ); } dropdownMenus(); // menuTabs should follow `dropdownMenus` as that can move menu items from a // tab menu to a dropdown. menuTabs(); tables(); } /** * @param {Window} window * @return {void} */ function init( window ) { const now = mw.now(); // This is the earliest time we can run JS for users (and bucket anonymous // users for A/B tests). // Where the browser supports it, for a 10% sample of users // we record a value to give us a sense of the expected delay in running A/B tests or // disabling JS features. This will inform us on various things including what to expect // with regards to delay while running A/B tests to anonymous users. // When EventLogging is not available this will reject. // This code can be removed by the end of the Desktop improvements project. // https://www.mediawiki.org/wiki/Desktop_improvements mw.loader.using( 'ext.eventLogging' ).then( () => { if ( mw.eventLog && mw.eventLog.eventInSample( 100 /* 1 in 100 */ ) && window.performance && window.performance.timing && window.performance.timing.navigationStart ) { mw.track( 'timing.Vector.ready', now - window.performance.timing.navigationStart ); // milliseconds } } ); } init( window ); if ( ABTestConfig.enabled && !mw.user.isAnon() ) { initExperiment( ABTestConfig, String( mw.user.getId() ) ); } if ( document.readyState === 'interactive' || document.readyState === 'complete' ) { main( window ); } else { // This is needed when document.readyState === 'loading'. document.addEventListener( 'DOMContentLoaded', () => { main( window ); } ); } // Provider of skins.vector.js module: /** * skins.vector.js * * @stable for use inside WikimediaEvents ONLY. */ module.exports = { pinnableElement };