/** * Dialog for displaying possible renamed messages. * Note that methods are not safe to call before the dialog has initialized. * * @copyright See AUTHORS.txt * @license GPL-2.0-or-later */ 'use strict'; mw.translate = mw.translate || {}; /** * @class * @extends OO.ui.ProcessDialog * * @private * @constructor * @param {Object} [config] Similar configuration as the OO.ui.ProcessDialog * @param {Function} [onRenameSelect] Function to call when the rename button is pressed */ mw.translate.MessageRenameDialog = function ( config, onRenameSelect ) { // HTML Elements this.messageSearch = null; this.searchButton = null; this.panel = null; this.form = null; this.$notice = null; // Data properties this.possibleRenames = null; this.currentGroupId = null; this.targetKey = null; this.selectedMessage = null; this.resetProperties(); if ( !onRenameSelect ) { var errMsg = 'Must provide the "onRenameSelect" callback function.'; mw.log.error( errMsg ); throw new Error( errMsg ); } this.onRenameSelect = onRenameSelect; // Parent constructor mw.translate.MessageRenameDialog.super.call( this, config ); }; /* Inheritance */ OO.inheritClass( mw.translate.MessageRenameDialog, OO.ui.ProcessDialog ); /* Static Properties */ mw.translate.MessageRenameDialog.static.name = 'MessageRenameDialog'; mw.translate.MessageRenameDialog.static.actions = [ { flags: [ 'primary', 'progressive' ], label: mw.msg( 'translate-smg-rename-select' ), action: 'select', active: true }, { flags: 'safe', label: mw.msg( 'translate-smg-rename-cancel' ), action: 'cancel' } ]; /** * @inheritdoc */ mw.translate.MessageRenameDialog.prototype.initialize = function () { mw.translate.MessageRenameDialog.super.prototype.initialize.call( this ); this.messageSearch = new OO.ui.TextInputWidget( { placeholder: mw.msg( 'translate-smg-rename-search' ) } ); this.searchButton = new OO.ui.ButtonWidget( { icon: 'search', invisibleLabel: true } ); this.panel = new OO.ui.PanelLayout( { padded: true, expanded: false } ); this.form = new OO.ui.FormLayout( { padded: true, expanded: false, items: [ new OO.ui.ActionFieldLayout( this.messageSearch, this.searchButton, { classes: [ 'smg-rename-msg-search' ] } ) ], method: 'post' } ); this.$notice = $( '

' ) .addClass( 'smg-rename-notice hide' ); this.form.$element.append( this.$notice ); this.panel.$element.append( this.form.$element ); this.$body.append( this.panel.$element ); this.addEvents(); }; mw.translate.MessageRenameDialog.prototype.addEvents = function () { this.form.$element.on( 'click', '.smg-rename-list', this.selectMessage.bind( this ) ); this.messageSearch.on( 'change', OO.ui.debounce( this.filterMessages.bind( this ), 300 ) ); }; /** * @inheritdoc */ mw.translate.MessageRenameDialog.prototype.getSetupProcess = function ( renameDialogData ) { var dialogData = renameDialogData || {}; return mw.translate.MessageRenameDialog.super.prototype.getSetupProcess.call( this, dialogData ) .next( function () { // Set up contents based on data this.possibleRenames = dialogData.messages; this.currentGroupId = dialogData.groupId; this.targetKey = dialogData.targetKey; this.selectedMessage = null; this.displayMessages( this.possibleRenames ); }, this ); }; /** * @inheritdoc */ mw.translate.MessageRenameDialog.prototype.getActionProcess = function ( action ) { if ( action === 'cancel' ) { return new OO.ui.Process( function () { this.close(); this.emit( action ); }, this ); } else if ( action === 'select' ) { if ( !this.selectedMessage ) { return new OO.ui.Process( function () { this.displayNotice( mw.msg( 'translate-smg-rename-select-err' ), 'error' ); }, this ); } return mw.translate.MessageRenameDialog.super.prototype.getActionProcess.call( this, action ) .next( this.rename.bind( this ) ) .next( function () { return this.close().closed; }.bind( this ) ); } return mw.translate.MessageRenameDialog.super.prototype.getActionProcess.call( this, action ); }; /** * @inheritdoc */ mw.translate.MessageRenameDialog.prototype.getTeardownProcess = function ( data ) { return mw.translate.MessageRenameDialog.super.prototype.getTeardownProcess.call( this, data ) .first( function () { // Perform any cleanup as needed this.clearMessages(); this.messageSearch.setValue( '' ); this.resetProperties(); }, this ); }; /** * Displays the given messages on the dialog box. * * @param {Array} messages */ mw.translate.MessageRenameDialog.prototype.displayMessages = function ( messages ) { if ( !messages.length ) { this.displayNotice( mw.msg( 'translate-smg-rename-no-msg' ), 'info' ); return; } for ( var i = 0; i < messages.length; i++ ) { this.displayMessage( messages[ i ] ); } }; /** * Generates the HTML to display a single message * * @param {Object} message */ mw.translate.MessageRenameDialog.prototype.displayMessage = function ( message ) { var $title = $( '

' ).append( $( '' ).text( message.title ).addClass( 'smg-rename-msg-key' ) .prop( 'href', message.link ) .data( 'msg-key', message.key ), $( '' ).text( mw.msg( 'percent', mw.language.convertNumber( ( message.similarity * 100 ).toFixed() ) ) ).addClass( 'smg-rename-similarity' ) ); var $content = $( '
' ).text( message.content ).addClass( 'smg-rename-msg-content' ); var $container = $( '
' ).addClass( 'smg-rename-list' ); $container.append( $title, $content ); this.form.$element.append( $container ); }; /** * Callback triggered when a message is selected. * * @param {Object} event */ mw.translate.MessageRenameDialog.prototype.selectMessage = function ( event ) { var $target = $( event.currentTarget ); this.selectedMessage = $target.find( '.smg-rename-msg-key' ).data( 'msgKey' ); this.form.$element.find( '.smg-rename-list' ).removeClass( 'smg-rename-selected' ); $target.addClass( 'smg-rename-selected' ); this.hideNotice(); }; /** * Used to reset the state properties for the dialog box. */ mw.translate.MessageRenameDialog.prototype.resetProperties = function () { this.possibleRenames = []; this.currentGroupId = null; this.targetKey = null; this.selectedMessage = null; }; /** * Perform the actual rename * * @return {jQuery.Promise} Resolves after making call to the onRenameSelect function. */ mw.translate.MessageRenameDialog.prototype.rename = function () { var deferred = $.Deferred(); var promise = deferred.promise(); var renameData = { groupId: this.currentGroupId, targetKey: this.targetKey, selectedKey: this.selectedMessage }; this.onRenameSelect( renameData ).done( function () { return deferred.resolve(); } ).fail( function ( code, result ) { if ( result.error ) { if ( result.error.code === 'permissiondenied' ) { return deferred.reject( new OO.ui.Error( result.error.info, { recoverable: false } ) ); } return deferred.reject( new OO.ui.Error( result.error.info ) ); } } ); return promise; }; /** * Callback function triggered to handle the search. * * @param {Object} searchValue */ mw.translate.MessageRenameDialog.prototype.filterMessages = function ( searchValue ) { var normalizedSearchVal = searchValue.toLowerCase(), filteredMessages = []; // if the dialog is closing, let's not do anything. if ( this.isClosing() || !this.isVisible() ) { return; } filteredMessages = this.possibleRenames.filter( function ( message ) { return message.key.toLowerCase().includes( normalizedSearchVal ) && message.content.toLowerCase().includes( normalizedSearchVal ); } ); this.clearMessages(); this.displayMessages( filteredMessages ); this.updateSize(); }; /** * Method use to display a notice on the dialog box * * @param {string} msg * @param {string} type Type of notice to display. */ mw.translate.MessageRenameDialog.prototype.displayNotice = function ( msg, type ) { var possibleTypes = [ 'info', 'error', 'warning' ]; // `type` classes documented above. Will be one of "possibleTypes". // eslint-disable-next-line mediawiki/class-doc this.$notice.removeClass( possibleTypes ); // eslint-disable-next-line mediawiki/class-doc this.$notice.text( msg ).addClass( type ).removeClass( 'hide' ); this.updateSize(); }; /** * Hide displayed notice. */ mw.translate.MessageRenameDialog.prototype.hideNotice = function () { this.$notice.addClass( 'hide' ); this.updateSize(); }; /** * Clears all messages from the DOM. */ mw.translate.MessageRenameDialog.prototype.clearMessages = function () { this.form.$element.find( '.smg-rename-list' ).remove(); this.hideNotice(); }; /** * @inheritDoc */ mw.translate.MessageRenameDialog.prototype.getReadyProcess = function ( data ) { return mw.translate.MessageRenameDialog.super.prototype.getReadyProcess.call( this, data ) .next( function () { this.messageSearch.$element.find( 'input' ).trigger( 'focus' ); }, this ); };