/*! * JavaScript for Special:Preferences: Tab navigation. */ ( function () { const nav = require( './nav.js' ); $( () => { const $tabNavigationHint = nav.insertHints( mw.msg( 'prefs-tabs-navigation-hint' ) ); const tabs = OO.ui.infuse( $( '.mw-prefs-tabs' ) ); // Support: Chrome // https://bugs.chromium.org/p/chromium/issues/detail?id=1252507 // // Infusing the tabs above involves detaching all the tabs' content from the DOM momentarily, // which causes the :target selector (used in mediawiki.special.preferences.styles.ooui.less) // not to match anything inside the tabs in Chrome. Twiddling location.href makes it work. // Only do it when a fragment is present, otherwise the page would be reloaded. if ( location.href.indexOf( '#' ) !== -1 ) { // eslint-disable-next-line no-self-assign location.href = location.href; } tabs.$element.addClass( 'mw-prefs-tabs-infused' ); function enhancePanel( panel ) { if ( !panel.$element.data( 'mw-section-infused' ) ) { panel.$element.removeClass( 'mw-htmlform-autoinfuse-lazy' ); mw.hook( 'htmlform.enhance' ).fire( panel.$element ); panel.$element.data( 'mw-section-infused', true ); } } function onTabPanelSet( panel ) { if ( nav.switchingNoHash ) { return; } // Handle hash manually to prevent jumping, // therefore save and restore scrollTop to prevent jumping. const scrollTop = $( window ).scrollTop(); // Changing the hash apparently causes keyboard focus to be lost? // Save and restore it. This makes no sense though. const active = document.activeElement; location.hash = '#' + panel.getName(); if ( active ) { active.focus(); } $( window ).scrollTop( scrollTop ); } tabs.on( 'set', onTabPanelSet ); // Hash navigation callback const setSection = function ( sectionName, fieldset ) { tabs.setTabPanel( sectionName ); enhancePanel( tabs.getCurrentTabPanel() ); // Scroll to a fieldset if provided. if ( fieldset ) { fieldset.scrollIntoView(); } }; // onSubmit callback const onSubmit = function () { const value = tabs.getCurrentTabPanelName(); mw.storage.session.set( 'mwpreferences-prevTab', value ); }; nav.onLoad( setSection, 'mw-prefsection-personal' ); nav.restorePrevSection( setSection, onSubmit ); // Search index let index, texts; function buildIndex() { index = {}; const $fields = tabs.contentPanel.$element.find( '[class^=mw-htmlform-field-]:not( .mw-prefs-search-noindex )' ); const $descFields = $fields.filter( '.oo-ui-fieldsetLayout-group > .oo-ui-widget > .mw-htmlform-field-HTMLInfoField' ); $fields.not( $descFields ).each( function () { let $field = $( this ); const $wrapper = $field.parents( '.mw-prefs-fieldset-wrapper' ); const $tabPanel = $field.closest( '.oo-ui-tabPanelLayout' ); const $labels = $field.find( '.oo-ui-labelElement-label, .oo-ui-textInputWidget .oo-ui-inputWidget-input, p' ).add( $wrapper.find( '> .oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-header .oo-ui-labelElement-label' ) ); $field = $field.add( $tabPanel.find( $descFields ) ); function addToIndex( $label, $highlight ) { const text = $label.val() || $label[ 0 ].textContent.toLowerCase().trim().replace( /\s+/, ' ' ); if ( text ) { index[ text ] = index[ text ] || []; index[ text ].push( { $highlight: $highlight || $label, $field: $field, $wrapper: $wrapper, $tabPanel: $tabPanel } ); } } $labels.each( function () { addToIndex( $( this ) ); // Check if there we are in an infusable dropdown and collect other options const $dropdown = $( this ).closest( '.oo-ui-dropdownInputWidget[data-ooui],.mw-widget-selectWithInputWidget[data-ooui]' ); if ( $dropdown.length ) { const dropdown = OO.ui.infuse( $dropdown[ 0 ] ); const dropdownWidget = ( dropdown.dropdowninput || dropdown ).dropdownWidget; if ( dropdownWidget ) { dropdownWidget.getMenu().getItems().forEach( ( option ) => { // Highlight the dropdown handle and the matched label, for when the dropdown is opened addToIndex( option.$label, dropdownWidget.$handle ); addToIndex( option.$label, option.$label ); } ); } } } ); } ); mw.hook( 'prefs.search.buildIndex' ).fire( index ); texts = Object.keys( index ); } function infuseAllPanels() { tabs.stackLayout.items.forEach( ( tabPanel ) => { const wasVisible = tabPanel.isVisible(); // Force panel to be visible while infusing tabPanel.toggle( true ); enhancePanel( tabPanel ); // Restore visibility tabPanel.toggle( wasVisible ); } ); } const searchWrapper = OO.ui.infuse( $( '.mw-prefs-search' ) ); const search = searchWrapper.fieldWidget; search.$input.on( 'focus', () => { if ( !index ) { // Lazy-build index on first focus // Infuse all widgets as we may end up showing a large subset of them infuseAllPanels(); buildIndex(); } } ); const $noResults = $( '