resources/js/ext.translate.messagerenamedialog.js
/**
* 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 = $( '<p>' )
.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 = $( '<div>' ).append(
$( '<a>' ).text( message.title ).addClass( 'smg-rename-msg-key' )
.prop( 'href', message.link )
.data( 'msg-key', message.key ),
$( '<span>' ).text(
mw.msg( 'percent', mw.language.convertNumber( ( message.similarity * 100 ).toFixed() ) )
).addClass( 'smg-rename-similarity' )
);
var $content = $( '<div>' ).text( message.content ).addClass( 'smg-rename-msg-content' );
var $container = $( '<div>' ).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().indexOf( normalizedSearchVal ) !== -1 &&
message.content.toLowerCase().indexOf( normalizedSearchVal ) !== -1;
} );
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 );
};