This reverts commit d29050d080.
Reason for revert: I had mistakenly assumed the @wikimedia/codex
module was loaded on clicking the language button. It appears to
be added on page load.
Given T248718 it's still unclear what our policy on loading
@wikimedia/codex on page load is and given several people are out
I think it would be better to attempt this again next week, possibly
delaying further if needed.
Bug: T353071
Bug: T248718
Change-Id: I09334a1faec06a7400e61013ddda374156bf5d72
744 lines
25 KiB
JavaScript
744 lines
25 KiB
JavaScript
/*!
|
|
* ULS interface integration logic
|
|
*
|
|
* Copyright (C) 2012-2013 Alolita Sharma, Amir Aharoni, Arun Ganesh, Brandon Harris,
|
|
* Niklas Laxström, Pau Giner, Santhosh Thottingal, Siebrand Mazeland and other
|
|
* contributors. See CREDITS for a list.
|
|
*
|
|
* UniversalLanguageSelector is dual licensed GPLv2 or later and MIT. You don't
|
|
* have to do anything special to choose one license or the other and you don't
|
|
* have to notify anyone which license you are using. You are free to use
|
|
* UniversalLanguageSelector in commercial projects as long as the copyright
|
|
* header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
|
|
*
|
|
* @file
|
|
* @ingroup Extensions
|
|
* @licence GNU General Public Licence 2.0 or later
|
|
* @licence MIT License
|
|
*/
|
|
|
|
( function () {
|
|
'use strict';
|
|
var languageSettingsModules = [ 'ext.uls.displaysettings' ],
|
|
launchULS = require( './ext.uls.launch.js' ),
|
|
ActionsMenu = require( './ext.uls.actions.menu.js' ),
|
|
ActionsMenuItem = require( './ext.uls.actions.menu.item.js' );
|
|
require( './ext.uls.actions.menu.items.registry.js' );
|
|
|
|
/**
|
|
* For Vector, check if the language button id exists.
|
|
* For other skins, check wgULSDisplaySettingsInInterlanguage for the current skin.
|
|
*
|
|
* @return {boolean}
|
|
*/
|
|
function isUsingStandaloneLanguageButton() {
|
|
// Checking for the ULS language button id returns true for Vector, false for other skins.
|
|
return $( '#p-lang-btn' ).length > 0 || mw.config.get( 'wgULSDisplaySettingsInInterlanguage' );
|
|
}
|
|
|
|
/**
|
|
* @param {jQuery} $element
|
|
* @param {Function} onCloseHandler
|
|
* @param {Object} uls
|
|
*/
|
|
function openLanguageSettings( $element, onCloseHandler, uls ) {
|
|
mw.loader.using( languageSettingsModules ).then( function () {
|
|
$element.languagesettings( {
|
|
defaultModule: 'display',
|
|
onClose: onCloseHandler,
|
|
onPosition: uls.position.bind( uls ),
|
|
onVisible: uls.hide.bind( uls )
|
|
} ).trigger( 'click' );
|
|
} );
|
|
}
|
|
|
|
/**
|
|
* Provide entry points to create article in other languages. T290436
|
|
*
|
|
* @param {Object} uls The ULS object
|
|
*/
|
|
function addEmptyState( uls ) {
|
|
var $emptyStateContainer = $( '<section>' ).addClass( 'uls-empty-state' );
|
|
|
|
function openActionsMenuEventHandler( event ) {
|
|
event.stopPropagation();
|
|
function onMenuClose() {
|
|
uls.show();
|
|
}
|
|
openLanguageSettings( $( event.target ), onMenuClose, uls );
|
|
}
|
|
|
|
var languageSettingsMenuItem = {
|
|
name: 'languageSettings',
|
|
icon: 'settings',
|
|
text: $.i18n( 'ext-uls-actions-menu-language-settings-item-label' ),
|
|
handler: openActionsMenuEventHandler
|
|
};
|
|
|
|
var actionItemsRegistry = mw.uls.ActionsMenuItemsRegistry;
|
|
actionItemsRegistry.register( languageSettingsMenuItem );
|
|
|
|
var $header = $( '<h3>' )
|
|
.addClass( 'uls-empty-state__header' )
|
|
.text( $.i18n( 'ext-uls-empty-state-header' ) );
|
|
var $desc = $( '<p>' )
|
|
.addClass( 'uls-empty-state__desc' )
|
|
.text( $.i18n( 'ext-uls-empty-state-desc' ) );
|
|
$emptyStateContainer.append( $header, $desc );
|
|
uls.$resultsView.append( $emptyStateContainer );
|
|
|
|
var actionItems = actionItemsRegistry.getItems();
|
|
|
|
if ( actionItems.length > 1 ) {
|
|
// languageSettingsMenuItem will be always there.
|
|
// If other actions available, change text
|
|
$header.text( $.i18n( 'ext-uls-empty-state-header-actions-available' ) );
|
|
$desc.text( $.i18n( 'ext-uls-empty-state-desc-actions-available' ) );
|
|
}
|
|
|
|
// Action menu items need OOUI widgets. Load them and register trigger event handler.
|
|
mw.loader.using( [ 'oojs-ui-widgets', 'oojs-ui.styles.icons-interactions' ] ).done( function () {
|
|
var $actionsList = $( '<ul>' ).addClass( 'uls-language-action-items' );
|
|
actionItems.forEach( function ( actionItem ) {
|
|
var actionButton = new ActionsMenuItem(
|
|
actionItem.icon,
|
|
actionItem.text,
|
|
actionItem.handler,
|
|
actionItem.href
|
|
).render();
|
|
$actionsList.append( $( '<li>' ).append( actionButton.$element ) );
|
|
} );
|
|
|
|
$emptyStateContainer.append( $actionsList );
|
|
} );
|
|
|
|
}
|
|
|
|
/**
|
|
* Helper method for creating jQuery buttons, used in "addActionsMenuTriggers" method below
|
|
*
|
|
* @param {string} buttonClass a class to be added to the created button class list
|
|
* @return {jQuery}
|
|
*/
|
|
function createMenuButton( buttonClass ) {
|
|
var classes = [ 'mw-ui-button', 'mw-ui-quiet', buttonClass ];
|
|
// eslint-disable-next-line mediawiki/class-doc
|
|
return $( '<button>' ).addClass( classes );
|
|
}
|
|
|
|
/**
|
|
* @param {Object} uls The ULS object
|
|
* @return {jQuery}
|
|
*/
|
|
function addLanguageSettingsTrigger( uls ) {
|
|
var $ulsSettingsBlock = uls.$menu.find( '#uls-settings-block' ).eq( 0 );
|
|
$ulsSettingsBlock.addClass( 'uls-settings-block--vector-2022' );
|
|
|
|
var $languageSettingsMenuButton = createMenuButton( 'uls-language-settings-button' );
|
|
$languageSettingsMenuButton.one( 'click', function () {
|
|
openLanguageSettings( $languageSettingsMenuButton, uls.show.bind( uls ), uls );
|
|
} );
|
|
$ulsSettingsBlock.append( $languageSettingsMenuButton );
|
|
|
|
return $ulsSettingsBlock;
|
|
}
|
|
|
|
/**
|
|
* Add the button that opens the "Add languages" menu (that contain options
|
|
* like "Translate this page" and "Edit language links") and the button that
|
|
* opens the "Language settings" menu.
|
|
*
|
|
* @param {Object} uls The ULS object
|
|
*/
|
|
function addActionsMenuTriggers( uls ) {
|
|
var $ulsSettingsBlock = addLanguageSettingsTrigger( uls );
|
|
|
|
var actionItemsRegistry = mw.uls.ActionsMenuItemsRegistry;
|
|
actionItemsRegistry.on( 'register', onActionItemAdded );
|
|
|
|
var addLanguagesMenuDialog;
|
|
var $addLanguagesMenuButton;
|
|
|
|
var prependAddLanguagesMenuButton = function () {
|
|
$addLanguagesMenuButton = createMenuButton( 'uls-add-languages-button' ).attr( {
|
|
'data-i18n': 'ext-uls-add-languages-button-label'
|
|
} ).i18n();
|
|
|
|
$ulsSettingsBlock.addClass( 'uls-settings-block--with-add-languages' );
|
|
$ulsSettingsBlock.prepend( $addLanguagesMenuButton );
|
|
|
|
// Action menu items need OOUI widgets. Load them and register trigger event handler.
|
|
mw.loader.using( [ 'oojs-ui-widgets', 'oojs-ui.styles.icons-interactions' ] ).done( function () {
|
|
$addLanguagesMenuButton.on( 'click', function () {
|
|
addLanguagesMenuDialog = addLanguagesMenuDialog || new ActionsMenu( {
|
|
actions: actionItemsRegistry.getItems(),
|
|
onPosition: uls.position.bind( uls ),
|
|
onClose: uls.show.bind( uls )
|
|
} );
|
|
addLanguagesMenuDialog.render();
|
|
uls.hide();
|
|
} );
|
|
} );
|
|
};
|
|
|
|
if ( actionItemsRegistry.getItems().length ) {
|
|
prependAddLanguagesMenuButton();
|
|
}
|
|
function onActionItemAdded( itemName, item ) {
|
|
if ( !$addLanguagesMenuButton ) {
|
|
prependAddLanguagesMenuButton();
|
|
} else if ( addLanguagesMenuDialog ) {
|
|
addLanguagesMenuDialog.renderAction( item );
|
|
}
|
|
}
|
|
}
|
|
|
|
function userCanChangeLanguage() {
|
|
return mw.config.get( 'wgULSAnonCanChangeLanguage' ) ||
|
|
// mw.user.isNamed() added in MW 1.40. Remove after MLEB drop support for MW < 1.40
|
|
( typeof mw.user.isNamed === 'function' ? mw.user.isNamed() : !mw.user.isAnon() );
|
|
}
|
|
|
|
/**
|
|
* The tooltip to be shown when language changed using ULS.
|
|
* It also allows to undo the language selection.
|
|
*
|
|
* @param {string} previousLang
|
|
* @param {string} previousAutonym
|
|
*/
|
|
function showUndoTooltip( previousLang, previousAutonym ) {
|
|
var trigger, popup, popupPosition,
|
|
configPosition = mw.config.get( 'wgULSPosition' ),
|
|
triggerSelector = ( configPosition === 'interlanguage' ) ?
|
|
'.uls-settings-trigger, .mw-interlanguage-selector' :
|
|
'.uls-trigger';
|
|
|
|
// Fallback if no entry point is present
|
|
trigger = document.querySelector( triggerSelector ) || document.querySelector( '#pt-preferences' );
|
|
|
|
// Skip tooltip if there is no element to attach the tooltip to.
|
|
// It will cause errors otherwise.
|
|
if ( !trigger ) {
|
|
return;
|
|
}
|
|
|
|
function hideTipsy() {
|
|
popup.toggle( false );
|
|
}
|
|
|
|
function showTipsy( timeout ) {
|
|
var tipsyTimer = 0;
|
|
|
|
popup.toggle( true );
|
|
popup.toggleClipping( false );
|
|
|
|
// if the mouse is over the tooltip, do not hide
|
|
$( '.uls-tipsy' ).on( 'mouseover', function () {
|
|
clearTimeout( tipsyTimer );
|
|
} ).on( 'mouseout', function () {
|
|
tipsyTimer = setTimeout( hideTipsy, timeout );
|
|
} ).on( 'click', hideTipsy );
|
|
|
|
tipsyTimer = setTimeout( hideTipsy, timeout );
|
|
}
|
|
|
|
if ( configPosition === 'interlanguage' ) {
|
|
popupPosition = 'after';
|
|
} else {
|
|
popupPosition = 'below';
|
|
}
|
|
popup = new OO.ui.PopupWidget( {
|
|
padded: true,
|
|
width: 300,
|
|
classes: [ 'uls-tipsy' ],
|
|
// Automatically positioned relative to the trigger
|
|
$floatableContainer: $( trigger ),
|
|
position: popupPosition,
|
|
$content: ( function () {
|
|
var messageKey, $link;
|
|
|
|
$link = $( '<a>' )
|
|
.text( previousAutonym )
|
|
.prop( {
|
|
href: '',
|
|
class: 'uls-prevlang-link',
|
|
lang: previousLang,
|
|
// We could get dir from uls.data,
|
|
// but we are trying to avoid loading it
|
|
// and 'auto' is safe enough in this context.
|
|
// T130390: must use attr
|
|
dir: 'auto'
|
|
} )
|
|
.on( 'click', function ( event ) {
|
|
event.preventDefault();
|
|
|
|
// Track if event logging is enabled
|
|
mw.hook( 'mw.uls.language.revert' ).fire();
|
|
|
|
mw.loader.using( [ 'ext.uls.common' ] ).then( function () {
|
|
mw.uls.changeLanguage( event.target.lang );
|
|
} );
|
|
} );
|
|
|
|
if ( mw.storage.get( 'uls-gp' ) === '1' ) {
|
|
messageKey = 'ext-uls-undo-language-tooltip-text-local';
|
|
} else {
|
|
messageKey = 'ext-uls-undo-language-tooltip-text';
|
|
}
|
|
|
|
// Message keys listed above
|
|
// eslint-disable-next-line mediawiki/msg-doc
|
|
return $( '<p>' ).append( mw.message( messageKey, $link ).parseDom() );
|
|
}() )
|
|
} );
|
|
|
|
popup.$element.appendTo( document.body );
|
|
|
|
// The interlanguage position needs some time to settle down
|
|
setTimeout( function () {
|
|
// Show the tipsy tooltip on page load.
|
|
showTipsy( 6000 );
|
|
}, 700 );
|
|
|
|
// manually show the tooltip
|
|
$( trigger ).on( 'mouseover', function () {
|
|
// show only if the ULS panel is not shown
|
|
// eslint-disable-next-line no-jquery/no-sizzle
|
|
if ( !$( '.uls-menu:visible' ).length ) {
|
|
showTipsy( 3000 );
|
|
}
|
|
} );
|
|
}
|
|
|
|
function initSecondaryEntryPoints() {
|
|
$( '.uls-settings-trigger' ).one( 'click', function ( e ) {
|
|
e.preventDefault();
|
|
mw.loader.using( languageSettingsModules, function () {
|
|
$( e.target ).languagesettings();
|
|
$( e.target ).trigger( 'click' );
|
|
} );
|
|
} );
|
|
}
|
|
|
|
function initInterlanguageEntryPoint() {
|
|
var $pLang = $( '#p-lang' );
|
|
|
|
var $trigger = $( '<button>' )
|
|
.addClass( 'uls-settings-trigger' )
|
|
.prop( 'title', mw.msg( 'ext-uls-select-language-settings-icon-tooltip' ) );
|
|
|
|
// Append ULS cog to interlanguage section header in the sidebar
|
|
$pLang.prepend( $trigger );
|
|
|
|
// Replace the title of the interlanguage links area from "In other languages" to
|
|
// "Languages" if there are no language links. TODO: Remove this feature?
|
|
if ( !$pLang.find( 'div ul' ).children().length && isUsingStandaloneLanguageButton ) {
|
|
$pLang.find( 'h3' ).text( mw.msg( 'uls-plang-title-languages' ) );
|
|
}
|
|
|
|
var clickHandler = function ( e ) {
|
|
var languagesettings = $trigger.data( 'languagesettings' ),
|
|
languageSettingsOptions;
|
|
|
|
if ( languagesettings ) {
|
|
if ( !languagesettings.shown ) {
|
|
mw.hook( 'mw.uls.settings.open' ).fire( 'interlanguage' );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Initialize the Language settings window
|
|
languageSettingsOptions = {
|
|
defaultModule: 'display',
|
|
onPosition: function () {
|
|
var caretRadius, top, left,
|
|
ulsTriggerHeight = this.$element.height(),
|
|
ulsTriggerWidth = this.$element[ 0 ].offsetWidth,
|
|
ulsTriggerOffset = this.$element.offset();
|
|
|
|
// Same as border width in mixins.less, or near enough
|
|
caretRadius = 12;
|
|
|
|
if ( ulsTriggerOffset.left > $( window ).width() / 2 ) {
|
|
left = ulsTriggerOffset.left - this.$window.width() - caretRadius;
|
|
this.$window.removeClass( 'selector-left' ).addClass( 'selector-right' );
|
|
} else {
|
|
left = ulsTriggerOffset.left + ulsTriggerWidth + caretRadius;
|
|
this.$window.removeClass( 'selector-right' ).addClass( 'selector-left' );
|
|
}
|
|
|
|
// The top of the dialog is aligned in relation to
|
|
// the middle of the trigger, so that middle of the
|
|
// caret aligns with it. 16 is trigger icon height in pixels
|
|
top = ulsTriggerOffset.top +
|
|
( ulsTriggerHeight / 2 ) -
|
|
( caretRadius + 16 );
|
|
|
|
return { top: top, left: left };
|
|
},
|
|
onVisible: function () {
|
|
this.$window.addClass( 'callout' );
|
|
}
|
|
};
|
|
|
|
mw.loader.using( languageSettingsModules, function () {
|
|
$trigger.languagesettings( languageSettingsOptions ).trigger( 'click' );
|
|
} );
|
|
|
|
e.stopPropagation();
|
|
};
|
|
|
|
$trigger.on( 'click', clickHandler );
|
|
}
|
|
|
|
function initPersonalEntryPoint() {
|
|
var $trigger = $( '.uls-trigger' );
|
|
var clickHandler;
|
|
|
|
if ( !userCanChangeLanguage() ) {
|
|
clickHandler = function ( e ) {
|
|
var languagesettings = $trigger.data( 'languagesettings' );
|
|
|
|
e.preventDefault();
|
|
|
|
if ( languagesettings ) {
|
|
if ( !languagesettings.shown ) {
|
|
mw.hook( 'mw.uls.settings.open' ).fire( 'personal' );
|
|
}
|
|
} else {
|
|
mw.loader.using( languageSettingsModules, function () {
|
|
$trigger.languagesettings( { autoOpen: true } );
|
|
mw.hook( 'mw.uls.settings.open' ).fire( 'personal' );
|
|
} );
|
|
// Stop propagating the event to avoid closing the languagesettings dialog
|
|
// when the event propagates to the document click handler inside
|
|
// languagesettings
|
|
e.stopPropagation();
|
|
}
|
|
};
|
|
} else {
|
|
clickHandler = function ( e, eventParams ) {
|
|
var uls = $trigger.data( 'uls' );
|
|
|
|
e.preventDefault();
|
|
|
|
if ( uls ) {
|
|
if ( !uls.shown ) {
|
|
mw.hook( 'mw.uls.settings.open' ).fire( 'personal' );
|
|
}
|
|
} else {
|
|
mw.loader.using( 'ext.uls.mediawiki', function () {
|
|
$trigger.uls( {
|
|
quickList: function () {
|
|
return mw.uls.getFrequentLanguageList();
|
|
},
|
|
// partially copied from ext.uls.lauch
|
|
onPosition: function () {
|
|
// Default positioning of jquery.uls is middle of the screen under
|
|
// the trigger. This code aligns it under the trigger and to the
|
|
// trigger edge depending on which side of the page the trigger is
|
|
// It should work automatically both LTR and RTL.
|
|
var offset, height, width, positionCSS;
|
|
offset = $trigger.offset();
|
|
width = $trigger.outerWidth();
|
|
height = $trigger.outerHeight();
|
|
|
|
if ( offset.left + ( width / 2 ) > $( window ).width() / 2 ) {
|
|
// Midpoint of the trigger is on the right side of the viewport.
|
|
positionCSS = {
|
|
// Right dialog edge aligns with right edge of the trigger.
|
|
right: $( window ).width() - ( offset.left + width ),
|
|
top: offset.top + height
|
|
};
|
|
} else {
|
|
// Midpoint of the trigger is on the left side of the viewport.
|
|
positionCSS = {
|
|
// Left dialog edge aligns with left edge of the trigger.
|
|
left: offset.left,
|
|
top: offset.top + height
|
|
};
|
|
}
|
|
|
|
return positionCSS;
|
|
},
|
|
onReady: function () {
|
|
addLanguageSettingsTrigger( this );
|
|
},
|
|
onSelect: function ( language ) {
|
|
mw.uls.changeLanguage( language );
|
|
},
|
|
// Not actually used on sites with the gear icon
|
|
// in the interlanguage area, because this ULS
|
|
// will be a container for other ULS panels.
|
|
// However, this is used on sites with ULS
|
|
// in the personal bar, and in that case it has the same
|
|
// purpose as the selector in Display settings,
|
|
// so it has the same identifier.
|
|
ulsPurpose: 'interface-language'
|
|
} );
|
|
|
|
// Allow styles to apply first and position to work by
|
|
// delaying the activation after them.
|
|
setTimeout( function () {
|
|
$trigger.trigger( 'click', eventParams );
|
|
}, 0 );
|
|
} );
|
|
}
|
|
};
|
|
}
|
|
|
|
$trigger.on( 'click', clickHandler );
|
|
// Optimization: Prefetch the Resource loader modules for ULS on mouseover
|
|
$trigger.one( 'mouseover', function () {
|
|
mw.loader.load( languageSettingsModules );
|
|
} );
|
|
}
|
|
|
|
function initLanguageChangeUndoTooltip() {
|
|
var previousLanguage, currentLanguage, previousAutonym, currentAutonym;
|
|
|
|
if ( !userCanChangeLanguage() ) {
|
|
return;
|
|
}
|
|
|
|
previousLanguage = mw.storage.get( 'uls-previous-language-code' );
|
|
currentLanguage = mw.config.get( 'wgUserLanguage' );
|
|
previousAutonym = mw.storage.get( 'uls-previous-language-autonym' );
|
|
currentAutonym = mw.config.get( 'wgULSCurrentAutonym' );
|
|
|
|
// If storage is empty, i.e. first visit, then store the current language
|
|
// immediately so that we know when it changes.
|
|
if ( !previousLanguage || !previousAutonym ) {
|
|
mw.storage.set( 'uls-previous-language-code', currentLanguage );
|
|
mw.storage.set( 'uls-previous-language-autonym', currentAutonym );
|
|
return;
|
|
}
|
|
|
|
if ( previousLanguage !== currentLanguage ) {
|
|
mw.loader.using( 'oojs-ui-core' ).done( function () {
|
|
showUndoTooltip( previousLanguage, previousAutonym );
|
|
} );
|
|
mw.storage.set( 'uls-previous-language-code', currentLanguage );
|
|
mw.storage.set( 'uls-previous-language-autonym', currentAutonym );
|
|
// Store this language in a list of frequently used languages
|
|
mw.loader.using( [ 'ext.uls.common' ] ).then( function () {
|
|
mw.uls.addPreviousLanguage( currentLanguage );
|
|
} );
|
|
}
|
|
}
|
|
|
|
function initIme() {
|
|
var imeSelector = mw.config.get( 'wgULSImeSelectors' ).join( ', ' );
|
|
|
|
$( document.body ).on( 'focus.imeinit', imeSelector, function () {
|
|
var $input = $( this );
|
|
$( document.body ).off( '.imeinit' );
|
|
mw.loader.using( 'ext.uls.ime', function () {
|
|
mw.ime.setup();
|
|
mw.ime.handleFocus( $input );
|
|
} );
|
|
} );
|
|
}
|
|
|
|
/**
|
|
* Special handling for checkbox hack.
|
|
* Disable default checkbox behavior and bind click to "Enter" keyboard events
|
|
*/
|
|
function handleCheckboxSelector() {
|
|
// If the ULS button is also a checkbox, we can
|
|
// conclude that it's using the checkbox hack.
|
|
$( document ).on( 'input', 'input.mw-interlanguage-selector[type="checkbox"]', function ( ev ) {
|
|
var elem = ev.currentTarget;
|
|
elem.checked = false;
|
|
} );
|
|
|
|
$( document ).on( 'keydown', 'input.mw-interlanguage-selector[type="checkbox"]', function ( ev ) {
|
|
var elem = ev.currentTarget;
|
|
if ( ev.key !== 'Enter' ) {
|
|
return;
|
|
}
|
|
elem.click();
|
|
} );
|
|
}
|
|
|
|
/**
|
|
* Load and open ULS for content language selection.
|
|
*
|
|
* This dialog is primarily for selecting the language of the content, but may also provide
|
|
* access to display and input settings if isUsingStandaloneLanguageButton() returns true.
|
|
*
|
|
* @param {jQuery.Event} ev
|
|
*/
|
|
function loadContentLanguageSelector( ev ) {
|
|
var $target = $( ev.currentTarget );
|
|
|
|
// Avoid reinitializing ULS multiple times for an element
|
|
if ( $target.attr( 'data-uls-loaded' ) ) {
|
|
return;
|
|
}
|
|
|
|
ev.preventDefault();
|
|
|
|
mw.loader.using( [ 'ext.uls.mediawiki', 'mediawiki.ui.button' ] ).then( function () {
|
|
var parent, languageNodes, standalone, uls;
|
|
|
|
parent = document.querySelectorAll( '.mw-portlet-lang, #p-lang' )[ 0 ];
|
|
languageNodes = parent ? parent.querySelectorAll( '.interlanguage-link-target' ) : [];
|
|
standalone = isUsingStandaloneLanguageButton();
|
|
|
|
$target.attr( 'data-uls-loaded', true );
|
|
|
|
// Setup click handler for ULS
|
|
launchULS(
|
|
$target,
|
|
mw.uls.getInterlanguageListFromNodes( languageNodes ),
|
|
// Using this as heuristic for now. May need to reconsider later. Enables
|
|
// behavior specific to compact language links.
|
|
!standalone
|
|
);
|
|
|
|
// Trigger the click handler to open ULS once ready
|
|
if ( standalone ) {
|
|
// Provide access to display and input settings if this entry point is the single
|
|
// point of access to all language settings.
|
|
uls = $target.data( 'uls' );
|
|
if ( languageNodes.length ) {
|
|
addActionsMenuTriggers( uls );
|
|
} else {
|
|
// first hide #uls-settings-block div since it's unused, and it causes
|
|
// an unwanted extra border to show up at the bottom of the menu
|
|
uls.$menu.find( '#uls-settings-block' ).eq( 0 ).hide();
|
|
// There are no languages - The article exist only the current language wiki
|
|
// Provide entry points to create article in other languages. T290436
|
|
addEmptyState( uls );
|
|
}
|
|
$target.trigger( 'click' );
|
|
} else {
|
|
$target.trigger( 'click' );
|
|
}
|
|
} );
|
|
}
|
|
|
|
/** Setup lazy-loading for content language selector */
|
|
function initContentLanguageSelectorClickHandler() {
|
|
// FIXME: In Timeless ULS is embedded in a menu which stops event propagation
|
|
if ( $( '.sidebar-inner' ).length ) {
|
|
$( '.sidebar-inner #p-lang' )
|
|
.one( 'click', '.mw-interlanguage-selector', loadContentLanguageSelector );
|
|
} else {
|
|
// This button may be created by the new Vector skin, or ext.uls.compactlinks module
|
|
// if there are many languages. Warning: Both this module and ext.uls.compactlinks
|
|
// module may run simultaneously. Using event delegation to avoid race conditions where
|
|
// the trigger may be created after this code.
|
|
$( document ).on( 'click', '.mw-interlanguage-selector', loadContentLanguageSelector );
|
|
// Special handling for checkbox hack.
|
|
handleCheckboxSelector();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The new Vector 2022 skin uses a less prominent language button for non-content pages.
|
|
* For these pages, the ULS should not be displayed, but a dropdown with an appropriate message
|
|
* should appear. The UniversalLanguageSelector extension should add a button to open the
|
|
* language settings, inside this dropdown.
|
|
* This method adds this button inside the dropdown.
|
|
*/
|
|
function addLanguageSettingsToNonContentPages() {
|
|
var $languageBtn = $( '#p-lang-btn' );
|
|
var clickHandler = function ( event ) {
|
|
event.stopPropagation();
|
|
mw.loader.using( languageSettingsModules ).then( function () {
|
|
$( event.target ).languagesettings( {
|
|
autoOpen: true,
|
|
onPosition: function () {
|
|
var offset = $languageBtn.offset();
|
|
var top = offset.top + $languageBtn.outerHeight();
|
|
var right = $( window ).width() - offset.left - $languageBtn.outerWidth();
|
|
return { top: top, right: right };
|
|
}
|
|
} );
|
|
} );
|
|
};
|
|
// the first time the language button is clicked inside a non-content page,
|
|
// we should add the "Open language settings" button inside the dropdown
|
|
$languageBtn.one( 'mouseover', function () {
|
|
mw.loader.using( [ 'oojs-ui-widgets', 'oojs-ui.styles.icons-interactions', 'ext.uls.messages' ] )
|
|
.done( function () {
|
|
var actionButton = new ActionsMenuItem(
|
|
'settings',
|
|
$.i18n( 'ext-uls-actions-menu-language-settings-item-label' ),
|
|
clickHandler,
|
|
null
|
|
).render();
|
|
actionButton.$element.addClass( 'empty-language-selector__language-settings-button' );
|
|
var $emptyLanguageSelectorBody = $( '.mw-portlet-empty-language-selector-body' );
|
|
$emptyLanguageSelectorBody.after( actionButton.$element );
|
|
} );
|
|
} );
|
|
}
|
|
function init() {
|
|
// if it's not Vector skin, nothing to be done here
|
|
if ( mw.config.get( 'skin' ) === 'vector-2022' && mw.config.get( 'wgULSisLanguageSelectorEmpty' ) ) {
|
|
// if this is a non-content page, we should add the "Open language settings" button
|
|
// inside the language dropdown
|
|
addLanguageSettingsToNonContentPages();
|
|
}
|
|
|
|
initLanguageChangeUndoTooltip();
|
|
initIme();
|
|
|
|
// There are three basic components of ULS interface:
|
|
// - language selection for interface
|
|
// - language selection for content
|
|
// - settings view (access to language selection for interface, fonts, input methods)
|
|
//
|
|
// These can be combined in different ways:
|
|
// - Vector skin (recently) has an omni selector that has content language selection as
|
|
// primary action with access to the settings view. It is on top right corner (LTR) of
|
|
// the page content area. It may not be present on all pages.
|
|
// - Compact language links provides access to content language selection only and it is in
|
|
// the interlanguage section of the sidebar. This is in addition to one of the main entry
|
|
// points below.
|
|
// - Personal entry point appears at the top of the page. It provides quick access to the
|
|
// interface language selection with access to the settings view, except if user is not
|
|
// logged in and not allowed to change a language. In this case it defaults to settings
|
|
// view without language selection.
|
|
// - Interlanguage entry point (a cog) appears in the interlanguage section in the sidebar.
|
|
// It defaults to the settings view.
|
|
//
|
|
// The three main entry points (omni selector, personal, interlanguage) are mutually
|
|
// exclusive. There may be secondary entry points anywhere on the page using the
|
|
// uls-settings-trigger class.
|
|
|
|
// First init secondary to avoid initing the interlanguage entry point multiple times
|
|
initSecondaryEntryPoints();
|
|
var position = mw.config.get( 'wgULSPosition' );
|
|
if ( position === 'interlanguage' ) {
|
|
initInterlanguageEntryPoint();
|
|
} else {
|
|
initPersonalEntryPoint();
|
|
}
|
|
|
|
var compact = mw.config.get( 'wgULSisCompactLinksEnabled' );
|
|
// The scope of the compact language links user preference has been expanded to also
|
|
// determine whether to show the omni box or not. Compact language links is already not
|
|
// loaded server side, so this is only relevant for the omnibox.
|
|
if ( compact ) {
|
|
// Init compact languages OR omni selector using the mw-interlanguage-selector class
|
|
initContentLanguageSelectorClickHandler();
|
|
} else {
|
|
$( '.mw-interlanguage-selector' ).removeClass( 'mw-interlanguage-selector' );
|
|
document.body.classList.add( 'mw-interlanguage-selector-disabled' );
|
|
}
|
|
}
|
|
|
|
// Early execute of init
|
|
if ( document.readyState === 'interactive' ) {
|
|
init();
|
|
} else {
|
|
$( init );
|
|
}
|
|
|
|
}() );
|