/*!
* VisualEditor UserInterface LanguageInputWidget class.
*
* @copyright See AUTHORS.txt
*/
/**
* Creates an ve.ui.LanguageInputWidget object.
*
* @class
* @extends OO.ui.Widget
*
* @constructor
* @param {Object} [config] Configuration options
* @param {string} [config.dirInput='auto'] How to display the directionality input. Options are:
* - none: Directionality input is hidden.
* - no-auto: Directionality input is visible and options are LTR or RTL.
* - auto: Directionality input is visible and options include "auto" in
* addition to LTR and RTL.
* @param {boolean} [config.readOnly=false] Prevent changes to the value of the input.
* @param {boolean} [config.hideCodeInput] Prevent user from entering a language code as free text
* @param {ve.ui.WindowManager} [config.dialogManager] Window manager to launch the language search dialog in
* @param {string[]} [config.availableLanguages] Available language codes to show in search dialog
*/
ve.ui.LanguageInputWidget = function VeUiLanguageInputWidget( config ) {
// Configuration initialization
config = config || {};
// Parent constructor
ve.ui.LanguageInputWidget.super.call( this, config );
// Properties
this.lang = null;
this.dir = null;
this.setReadOnly( !!config.readOnly );
this.dialogs = config.dialogManager || new ve.ui.WindowManager( { factory: ve.ui.windowFactory } );
this.availableLanguages = config.availableLanguages;
this.findLanguageButton = new OO.ui.ButtonWidget( {
classes: [ 've-ui-languageInputWidget-findLanguageButton' ],
icon: 'ellipsis'
} );
this.findLanguageButton.$button.attr( 'aria-label', ve.msg( 'visualeditor-dialog-language-search-title' ) );
this.selectedLanguageLabel = new OO.ui.LabelWidget( {
classes: [ 've-ui-languageInputWidget-selectedLanguageLabel' ],
label: ve.msg( 'visualeditor-languageinspector-widget-changelang' )
} );
this.languageCodeTextInput = new OO.ui.TextInputWidget( {
classes: [ 've-ui-languageInputWidget-languageCodeTextInput' ]
} );
this.languageCodeTextInput.$input.attr( 'aria-label', ve.msg( 'visualeditor-languageinspector-widget-label-langcode' ) );
this.directionSelect = new OO.ui.ButtonSelectWidget( {
classes: [ 've-ui-languageInputWidget-directionSelect' ]
} );
this.directionLabel = new OO.ui.LabelWidget( {
classes: [ 've-ui-languageInputWidget-directionLabel' ],
label: ve.msg( 'visualeditor-languageinspector-widget-label-direction' )
} );
const $language = $( '<div>' ).addClass( 've-ui-languageInputWidget-languageInput' );
$language.append(
this.findLanguageButton.$element
);
if ( !config.hideCodeInput ) {
$language.prepend( this.languageCodeTextInput.$element );
}
this.findLanguageButton.$element.before( this.selectedLanguageLabel.$element );
// Events
this.findLanguageButton.connect( this, { click: 'onFindLanguageButtonClick' } );
this.languageCodeTextInput.connect( this, { change: 'onChange' } );
this.directionSelect.connect( this, { select: 'onChange' } );
// Initialization
const dirItems = [
new OO.ui.ButtonOptionWidget( {
data: 'rtl',
icon: 'textDirRTL'
} ),
new OO.ui.ButtonOptionWidget( {
data: 'ltr',
icon: 'textDirLTR'
} )
];
const dirInput = ( config.dirInput === undefined ) ? 'auto' : config.dirInput;
if ( dirInput === 'auto' ) {
dirItems.splice(
1, 0, new OO.ui.ButtonOptionWidget( {
data: null,
label: ve.msg( 'visualeditor-dialog-language-auto-direction' )
} )
);
}
this.directionSelect.addItems( dirItems );
$( OO.ui.getTeleportTarget() ).append( this.dialogs.$element );
this.$element
.addClass( 've-ui-languageInputWidget' )
.append( $language );
if ( dirInput !== 'none' ) {
this.$element.append( this.directionLabel.$element, this.directionSelect.$element );
}
};
/* Inheritance */
OO.inheritClass( ve.ui.LanguageInputWidget, OO.ui.Widget );
/* Events */
/**
* @event ve.ui.LanguageInputWidget#change
* @param {string} lang Language code
* @param {string} dir Directionality
*/
/* Methods */
/**
* Handle find language button click events.
*/
ve.ui.LanguageInputWidget.prototype.onFindLanguageButtonClick = function () {
this.dialogs.openWindow( 'languageSearch', {
availableLanguages: this.availableLanguages,
$returnFocusTo: null
} ).closing.then( ( data ) => {
data = data || {};
if ( data.action === 'done' ) {
this.setLangAndDir( data.lang, data.dir );
}
} );
};
/**
* Handle input widget change events.
*/
ve.ui.LanguageInputWidget.prototype.onChange = function () {
if ( this.updating ) {
return;
}
const selectedItem = this.directionSelect.findSelectedItem();
this.setLangAndDir(
this.languageCodeTextInput.getValue(),
selectedItem ? selectedItem.getData() : null
);
};
/**
* Set language and directionality
*
* The inputs value will automatically be updated.
*
* @param {string|null} lang Language code
* @param {string|null} dir Directionality
* @fires ve.ui.LanguageInputWidget#change
* @return {ve.ui.LanguageInputWidget}
* @chainable
*/
ve.ui.LanguageInputWidget.prototype.setLangAndDir = function ( lang, dir ) {
if ( lang === this.lang && dir === this.dir ) {
// No change
return this;
}
// Set state flag while programmatically changing input widget values
this.updating = true;
if ( lang || dir ) {
lang = lang || '';
this.languageCodeTextInput.setValue( lang );
this.selectedLanguageLabel.setLabel(
ve.init.platform.hasLanguageCode( lang.toLowerCase() ) ?
ve.init.platform.getLanguageName( lang.toLowerCase() ) :
ve.msg( 'visualeditor-languageinspector-widget-changelang' )
);
this.directionSelect.selectItemByData( dir );
} else {
this.languageCodeTextInput.setValue( '' );
this.selectedLanguageLabel.setLabel(
ve.msg( 'visualeditor-languageinspector-widget-changelang' )
);
this.directionSelect.selectItem( null );
}
// Set title as long language may be truncated
this.selectedLanguageLabel.setTitle( this.selectedLanguageLabel.$label.text() );
this.updating = false;
this.lang = lang;
this.dir = dir;
this.emit( 'change', lang, dir );
return this;
};
/**
* Get the language
*
* @return {string|null} Language code
*/
ve.ui.LanguageInputWidget.prototype.getLang = function () {
return this.lang;
};
/**
* Get the directionality
*
* @return {string|null} Directionality (ltr/rtl)
*/
ve.ui.LanguageInputWidget.prototype.getDir = function () {
return this.dir;
};
/**
* Update the disabled state of the controls
*
* @chainable
* @protected
* @return {OO.ui.NumberInputWidget} The widget, for chaining
*/
ve.ui.LanguageInputWidget.prototype.updateControlsDisabled = function () {
const disabled = this.isDisabled() || this.isReadOnly();
if ( this.findLanguageButton ) {
this.findLanguageButton.setDisabled( disabled );
}
if ( this.directionSelect ) {
this.directionSelect.setDisabled( disabled );
}
return this;
};
/**
* Disable or enable the inputs
*
* @param {boolean} disabled Set disabled or enabled
* @return {ve.ui.LanguageInputWidget}
* @chainable
*/
ve.ui.LanguageInputWidget.prototype.setDisabled = function ( disabled ) {
// Parent method
ve.ui.LanguageInputWidget.super.prototype.setDisabled.call( this, disabled );
// The 'setDisabled' method runs in the constructor before the
// inputs are initialized
if ( this.languageCodeTextInput ) {
this.languageCodeTextInput.setDisabled( disabled );
}
this.updateControlsDisabled();
return this;
};
/**
* Check if the widget is read-only
*
* @return {boolean}
*/
ve.ui.LanguageInputWidget.prototype.isReadOnly = function () {
return this.readOnly;
};
/**
* Set the read-only state of the widget
*
* @param {boolean} readOnly Make widget read-only
* @return {ve.ui.LanguageInputWidget}
* @chainable
*/
ve.ui.LanguageInputWidget.prototype.setReadOnly = function ( readOnly ) {
this.readOnly = readOnly;
if ( this.languageCodeTextInput ) {
this.languageCodeTextInput.setReadOnly( readOnly );
}
this.updateControlsDisabled();
return this;
};