summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'UniversalLanguageSelector/resources/js/ext.uls.interface.js')
-rw-r--r--UniversalLanguageSelector/resources/js/ext.uls.interface.js778
1 files changed, 778 insertions, 0 deletions
diff --git a/UniversalLanguageSelector/resources/js/ext.uls.interface.js b/UniversalLanguageSelector/resources/js/ext.uls.interface.js
new file mode 100644
index 00000000..5ae9a8b4
--- /dev/null
+++ b/UniversalLanguageSelector/resources/js/ext.uls.interface.js
@@ -0,0 +1,778 @@
+/*!
+ * 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' );
+
+ /**
+ * Construct the display settings link
+ *
+ * @return {jQuery}
+ */
+ function displaySettings() {
+ return $( '<button>' )
+ .addClass( 'display-settings-block' )
+ .attr( {
+ title: $.i18n( 'ext-uls-display-settings-desc' ),
+ 'data-i18n': 'ext-uls-display-settings-title'
+ } )
+ .i18n();
+ }
+
+ /**
+ * Construct the input settings link
+ *
+ * @return {jQuery}
+ */
+ function inputSettings() {
+ return $( '<button>' )
+ .addClass( 'input-settings-block' )
+ .attr( {
+ title: $.i18n( 'ext-uls-input-settings-desc' ),
+ 'data-i18n': 'ext-uls-input-settings-title'
+ } )
+ .i18n();
+ }
+
+ /**
+ * 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' );
+ }
+
+ /**
+ * @return {jQuery}
+ */
+ function createActionsMenuTrigger() {
+ var classes = [ 'mw-ui-button', 'mw-ui-quiet', 'uls-language-actions-button' ];
+ return $( '<button>' ).addClass( classes );
+ }
+
+ /**
+ * @param {jQuery} actionsMenuTrigger
+ * @param {number} menuItemsCount
+ */
+ function setActionsMenuTriggerIconClass( actionsMenuTrigger, menuItemsCount ) {
+ var iconClass, iconClasses = {
+ single: 'uls-language-actions-button--single',
+ multiple: 'uls-language-actions-button--multiple'
+ };
+ if ( menuItemsCount > 1 ) {
+ iconClass = iconClasses.multiple;
+ } else {
+ iconClass = iconClasses.single;
+ }
+ iconClasses = Object.keys( iconClasses ).map( function ( key ) {
+ return iconClasses[ key ];
+ } );
+ // reset icon classes
+ // The following classes are being removed here (if present):
+ // * uls-language-actions-button--multiple
+ // * uls-language-actions-button--single
+ actionsMenuTrigger.removeClass( iconClasses );
+ // One of the following classes are being added here:
+ // * uls-language-actions-button--multiple
+ // OR
+ // * uls-language-actions-button--single
+ actionsMenuTrigger.addClass( iconClass );
+ }
+
+ function hideLanguageSettingsFooter( uls ) {
+ uls.$menu.find( '#uls-settings-block' ).eq( 0 ).hide();
+ }
+
+ /**
+ * @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 $actionsMenuTrigger = createActionsMenuTrigger();
+ setActionsMenuTriggerIconClass( $actionsMenuTrigger, actionItemsRegistry.size() );
+ 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 );
+ } );
+
+ }
+
+ /**
+ * Add language actions menu
+ *
+ * @param {Object} uls The ULS object
+ */
+ function addActionsMenuTrigger( uls ) {
+ var actionsMenuDialog;
+
+ function openActionsMenuEventHandler( event ) {
+ event.stopPropagation();
+
+ function onMenuClose() {
+ actionsMenuDialog.hide();
+ 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 $actionsMenuTrigger = createActionsMenuTrigger();
+ setActionsMenuTriggerIconClass( $actionsMenuTrigger, actionItemsRegistry.size() );
+
+ function registerTriggerListener() {
+ $actionsMenuTrigger.off( 'click' );
+ $actionsMenuTrigger.on( 'click', function () {
+ var menuItemsLength = actionItemsRegistry.size();
+
+ if ( menuItemsLength === 1 ) {
+ openLanguageSettings( $actionsMenuTrigger, uls.show.bind( uls ), uls );
+ $actionsMenuTrigger.off( 'click' );
+ } else if ( menuItemsLength > 1 ) {
+ actionsMenuDialog = actionsMenuDialog || new ActionsMenu( {
+ actions: actionItemsRegistry.getItems(),
+ onPosition: uls.position.bind( uls ),
+ onClose: uls.show.bind( uls )
+ } );
+ actionsMenuDialog.render();
+ uls.hide();
+ }
+ } );
+ }
+ function onActionItemAdded( item ) {
+ var itemsLength = actionItemsRegistry.size();
+ setActionsMenuTriggerIconClass( $actionsMenuTrigger, itemsLength );
+ registerTriggerListener();
+ if ( actionsMenuDialog ) {
+ actionsMenuDialog.renderAction( item );
+ }
+ }
+ actionItemsRegistry.on( 'register', onActionItemAdded );
+ uls.$menu.append( $actionsMenuTrigger );
+ // 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 () {
+ registerTriggerListener();
+ } );
+ }
+
+ /**
+ * Add display settings link to the settings bar in ULS
+ *
+ * @param {Object} uls The ULS object
+ */
+ function addDisplaySettings( uls ) {
+ var $displaySettings = displaySettings();
+
+ uls.$menu.find( '#uls-settings-block' ).append( $displaySettings );
+ // Initialize the trigger
+ $displaySettings.one( 'click', function () {
+ $displaySettings.languagesettings( {
+ defaultModule: 'display',
+ onClose: uls.show.bind( uls ),
+ onPosition: uls.position.bind( uls ),
+ onVisible: uls.hide.bind( uls )
+ } ).trigger( 'click' );
+ } );
+ }
+
+ /**
+ * Add input settings link to the settings bar in ULS
+ *
+ * @param {Object} uls The ULS object
+ */
+ function addInputSettings( uls ) {
+ var $inputSettings = inputSettings();
+
+ uls.$menu.find( '#uls-settings-block' ).append( $inputSettings );
+ // Initialize the trigger
+ $inputSettings.one( 'click', function () {
+ $inputSettings.languagesettings( {
+ defaultModule: 'input',
+ onClose: uls.show.bind( uls ),
+ onPosition: uls.position.bind( uls ),
+ onVisible: uls.hide.bind( uls )
+ } ).trigger( 'click' );
+ } );
+ }
+
+ function userCanChangeLanguage() {
+ return mw.config.get( 'wgULSAnonCanChangeLanguage' ) || !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 );
+ }
+ } );
+ }
+
+ /**
+ * Adds display and input settings to the ULS dialog after loading their code.
+ *
+ * @param {Object} uls The ULS instance
+ * @return {jQuery.Promise}
+ */
+ function loadDisplayAndInputSettings( uls ) {
+ return mw.loader.using( languageSettingsModules ).then( function () {
+ addDisplaySettings( uls );
+ addInputSettings( uls );
+ } );
+ }
+
+ 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();
+ },
+ onReady: function () {
+ loadDisplayAndInputSettings( 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' ).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' );
+ // 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
+ hideLanguageSettingsFooter( uls );
+ if ( languageNodes.length ) {
+ addActionsMenuTrigger( uls );
+ } else {
+ // 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();
+ }
+ }
+
+ function init() {
+ 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 );
+ }
+
+}() );