diff options
Diffstat (limited to 'www/wiki/extensions/UploadWizard/resources/details/uw.MultipleLanguageInputWidget.js')
-rw-r--r-- | www/wiki/extensions/UploadWizard/resources/details/uw.MultipleLanguageInputWidget.js | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/www/wiki/extensions/UploadWizard/resources/details/uw.MultipleLanguageInputWidget.js b/www/wiki/extensions/UploadWizard/resources/details/uw.MultipleLanguageInputWidget.js new file mode 100644 index 00000000..6e8e7633 --- /dev/null +++ b/www/wiki/extensions/UploadWizard/resources/details/uw.MultipleLanguageInputWidget.js @@ -0,0 +1,299 @@ +( function ( mw, uw, $, OO ) { + + /** + * A multi-language input field in UploadWizard's "Details" step form. + * + * @class uw.MultipleLanguageInputWidget + * @extends uw.DetailsWidget + * @mixins OO.ui.mixin.GroupElement + * @constructor + * @param {Object} [config] + * @cfg {boolean} [required=true] + * @cfg {mw.Message} [label] Text for label + * @cfg {mw.Message} [placeholder] Placeholder text for input field + * @cfg {mw.Message} [remove] Title text for remove icon + * @cfg {mw.Message} [error] Error message + * @cfg {number} [minLength=0] Minimum input length + * @cfg {number} [maxLength=99999] Maximum input length + * @cfg {Object} [languages] { langcode: text } map of languages + */ + uw.MultipleLanguageInputWidget = function UWMultipleLanguageInputWidget( config ) { + this.config = $.extend( { + required: true, + label: mw.message( '' ), + languages: this.getLanguageOptions() + }, config ); + uw.MultipleLanguageInputWidget.parent.call( this ); + OO.ui.mixin.GroupElement.call( this ); + + this.required = !!this.config.required; + this.addButton = new OO.ui.ButtonWidget( { + framed: false, + flags: [ 'progressive' ], + label: this.getLabelText() + } ); + this.addButton.connect( this, { click: [ 'addLanguageInput', this.config ] } ); + + // if a language becomes available because the input gets removed, + // or unavailable because it gets added, we'll need to update other + // language dropdowns to reflect the change + this.connect( this, { add: 'onChangeLanguages' } ); + this.connect( this, { remove: 'onChangeLanguages' } ); + + // update the 'add language' button accordingly + this.connect( this, { add: 'recount' } ); + this.connect( this, { remove: 'recount' } ); + + // Aggregate 'change' event + this.aggregate( { change: 'change' } ); + + this.$element.addClass( 'mwe-upwiz-multipleLanguageInputsWidget' ); + this.$element.append( + this.$group, + this.addButton.$element + ); + + // Add empty input (non-removable if this field is required) + this.addLanguageInput( $.extend( {}, this.config, { canBeRemoved: !this.required } ) ); + }; + OO.inheritClass( uw.MultipleLanguageInputWidget, uw.DetailsWidget ); + OO.mixinClass( uw.MultipleLanguageInputWidget, OO.ui.mixin.GroupElement ); + + /** + * @param {object} config + * @param {string} [text] + */ + uw.MultipleLanguageInputWidget.prototype.addLanguageInput = function ( config, text ) { + var allLanguages = this.config.languages, + unusedLanguages = this.getUnusedLanguages(), + languages = {}, + item; + + if ( unusedLanguages.length === 0 ) { + return; + } + + // only add given language + unused/remaining languages - we don't want + // languages that have already been selected to show up in the next dropdown... + if ( config.defaultLanguage ) { + languages[ config.defaultLanguage ] = allLanguages[ config.defaultLanguage ]; + languages = $.extend( {}, languages, unusedLanguages ); + } else { + languages = unusedLanguages; + } + + config = $.extend( {}, config, { languages: languages } ); + item = new uw.SingleLanguageInputWidget( config ); + item.setText( text || '' ); + + // if a language is changed, we'll need to update other language dropdowns + // to reflect the change + item.connect( this, { select: 'onChangeLanguages' } ); + + this.addItems( [ item ] ); + }; + + /** + * When a language changes (or an input is removed), the old language + * becomes available again in other language dropdowns, and the new + * language should no longer be selected. + * This will iterate all inputs, destroy then, and construct new ones + * with the updated language selections. + */ + uw.MultipleLanguageInputWidget.prototype.onChangeLanguages = function () { + var allLanguages = this.config.languages, + unusedLanguages = this.getUnusedLanguages(), + items = this.getItems(), + languages, + item, + i; + + for ( i = 0; i < items.length; i++ ) { + item = items[ i ]; + + // only add existing language + unused/remaining languages - we don't want + // languages that have already been selected to show up in the next dropdown... + languages = {}; + languages[ item.getLanguage() ] = allLanguages[ item.getLanguage() ]; + languages = $.extend( {}, languages, unusedLanguages ); + item.updateLanguages( languages ); + } + }; + + /** + * Returns an object of `langcode: text` pairs with the languages + * already used in dropdowns. + * + * @return {Object} + */ + uw.MultipleLanguageInputWidget.prototype.getUsedLanguages = function () { + var allLanguages = this.config.languages, + items = this.getItems(); + + return items.reduce( function ( obj, item ) { + var languageCode = item.getLanguage(); + obj[ languageCode ] = allLanguages[ languageCode ]; + return obj; + }, {} ); + }; + + /** + * Returns an object of `langcode: text` pairs with remaining languages + * not yet used in dropdowns. + * + * @return {Object} + */ + uw.MultipleLanguageInputWidget.prototype.getUnusedLanguages = function () { + var allLanguages = this.config.languages, + usedLanguageCodes = Object.keys( this.getUsedLanguages() ); + + return Object.keys( allLanguages ).reduce( function ( remaining, language ) { + if ( usedLanguageCodes.indexOf( language ) < 0 ) { + remaining[ language ] = allLanguages[ language ]; + } + return remaining; + }, {} ); + }; + + /** + * Update the button label after adding or removing inputs. + */ + uw.MultipleLanguageInputWidget.prototype.recount = function () { + var text = this.getLabelText(), + unusedLanguages = this.getUnusedLanguages(); + + this.addButton.setLabel( text ); + // hide the button if there are no remaining languages... + this.addButton.toggle( Object.keys( unusedLanguages ).length > 0 ); + }; + + /** + * @return {string} + */ + uw.MultipleLanguageInputWidget.prototype.getLabelText = function () { + var text = '', msg; + if ( this.config.label.exists() ) { + // clone the original object: `.params` doesn't replace existing + // params so follow-up calls here would otherwise just keep adding + // to the params instead of setting a new value for the first param + msg = mw.message( this.config.label.key ).params( this.config.label.parameters ); + text = msg.params( [ this.items.length ] ).text(); + } + + return text; + }; + + /** + * @return {Object} + */ + uw.MultipleLanguageInputWidget.prototype.getLanguageOptions = function () { + var languages, code; + + languages = {}; + for ( code in mw.UploadWizard.config.uwLanguages ) { + if ( mw.UploadWizard.config.uwLanguages.hasOwnProperty( code ) ) { + languages[ code ] = mw.UploadWizard.config.uwLanguages[ code ]; + } + } + return languages; + }; + + /** + * @inheritdoc + */ + uw.MultipleLanguageInputWidget.prototype.getErrors = function () { + var self = this, + // Gather errors from each item + errorPromises = this.getItems().map( function ( item ) { + return item.getErrors(); + } ); + + return $.when.apply( $, errorPromises ).then( function () { + var i, errors; + errors = []; + // Fold all errors into a single one (they are displayed in the UI for each item, but we still + // need to return an error here to prevent form submission). + for ( i = 0; i < arguments.length; i++ ) { + if ( arguments[ i ].length ) { + // One of the items has errors + errors.push( self.config.error ); + break; + } + } + // And add some more: + if ( this.required && this.getWikiText() === '' ) { + errors.push( mw.message( 'mwe-upwiz-error-blank' ) ); + } + // TODO Check for duplicate languages + return errors; + }.bind( this ) ); + }; + + /** + * @return {object} Object where the properties are language codes & values are input + */ + uw.MultipleLanguageInputWidget.prototype.getValues = function () { + var values = {}, + widgets = this.getItems(), + language, + text, + i; + + for ( i = 0; i < widgets.length; i++ ) { + language = widgets[ i ].getLanguage(); + text = widgets[ i ].getText(); + + if ( text !== '' ) { + values[ language ] = text; + } + } + + return values; + }; + + /** + * @inheritdoc + */ + uw.MultipleLanguageInputWidget.prototype.getWikiText = function () { + // Some code here and in mw.UploadWizardDetails relies on this function returning an empty + // string when there are some inputs, but all are empty. + return this.getItems().map( function ( widget ) { + return widget.getWikiText(); + } ).filter( function ( wikiText ) { + return !!wikiText; + } ).join( '\n' ); + }; + + /** + * @inheritdoc + * @return {Object} See #setSerialized + */ + uw.MultipleLanguageInputWidget.prototype.getSerialized = function () { + var inputs = this.getItems().map( function ( widget ) { + return widget.getSerialized(); + } ); + return { + inputs: inputs + }; + }; + + /** + * @inheritdoc + * @param {Object} serialized + * @param {Object[]} serialized.inputs Array of serialized inputs, + * see uw.SingleLanguageInputWidget#setSerialized + */ + uw.MultipleLanguageInputWidget.prototype.setSerialized = function ( serialized ) { + var config = this.config, + i; + + // remove all existing + this.removeItems( this.getItems() ); + + for ( i = 0; i < serialized.inputs.length; i++ ) { + config = $.extend( {}, config, { defaultLanguage: serialized.inputs[ i ].language } ); + this.addLanguageInput( config, serialized.inputs[ i ].text ); + } + }; + +}( mediaWiki, mediaWiki.uploadWizard, jQuery, OO ) ); |