mediawiki-extensions-Discus.../modules/dt.ui.UsernameCompletion.js
David Lynch b7ad053105 @Username completion in VE (source or visual) mode
Bug: T232601
Depends-On: I075cac9aa195574c3d416a40bbdc5ec2d64424e2
Change-Id: Ie605e783b333be0f81099d8d953725c38eb4cee1
2020-05-05 09:44:08 -05:00

126 lines
4.3 KiB
JavaScript

/*!
* VisualEditor UserInterface MWUsernameCompletionAction class.
*
* @copyright 2011-2019 VisualEditor Team and others; see http://ve.mit-license.org
*/
/**
* MWUsernameCompletionAction action.
*
* Controls autocompletion of usernames
*
* @class
* @extends ve.ui.CompletionAction
* @constructor
* @param {ve.ui.Surface} surface Surface to act on
*/
function MWUsernameCompletionAction( surface ) {
// Parent constructor
MWUsernameCompletionAction.super.call( this, surface );
this.api = new mw.Api();
this.searchedPrefixes = {};
this.remoteUsers = [];
}
/* Inheritance */
OO.inheritClass( MWUsernameCompletionAction, ve.ui.CompletionAction );
/* Static Properties */
MWUsernameCompletionAction.static.name = 'mwUsernameCompletion';
/* Methods */
MWUsernameCompletionAction.prototype.open = function () {
var surfaceModel = this.surface.getModel(),
data = surfaceModel.getDocument().data,
offset = surfaceModel.getSelection().getRange(),
// The character before the @:
precedingCharacterOffset = new ve.Range(
offset.from - MWUsernameCompletionAction.static.triggerLength - 1,
offset.from - MWUsernameCompletionAction.static.triggerLength
),
precedingCharacter = data.getText( false, precedingCharacterOffset );
// This is fundamentally "don't trigger on email addresses"
if ( precedingCharacter && !precedingCharacter.match( /\s/ ) ) {
return false;
}
return MWUsernameCompletionAction.super.prototype.open.apply( this, arguments );
};
MWUsernameCompletionAction.prototype.getSuggestions = function ( input ) {
var capitalizedInput = input.length > 0 && input[ 0 ].toUpperCase() + input.slice( 1 ),
action = this;
this.api.abort(); // Abort all unfinished API requests
if ( capitalizedInput && !this.searchedPrefixes[ capitalizedInput ] ) {
this.api.get( {
action: 'query',
list: 'allusers',
// Prefix of list=allusers is case sensitive, and users are stored in the DB capitalized, so:
auprefix: capitalizedInput,
aulimit: this.limit
} ).done( function ( response ) {
var suggestions = response.query.allusers.map( function ( user ) {
return user.name;
} ).filter( function ( username ) {
return action.surface.authors.indexOf( username ) === -1 &&
action.remoteUsers.indexOf( username ) === -1;
} );
action.remoteUsers.push.apply( action.remoteUsers, suggestions );
action.remoteUsers.sort();
action.searchedPrefixes[ capitalizedInput ] = true;
} );
}
return ve.createDeferred().resolve(
// By concatenating on-thread authors and remote-fetched authors, both
// sorted alphabetically, we'll get our suggestion popup sorted so all
// on-thread matches come first.
this.filterSuggestionsForInput( this.surface.authors.concat( this.remoteUsers ), input )
).promise();
};
MWUsernameCompletionAction.prototype.insertCompletion = function ( word, range ) {
var title = mw.Title.newFromText( word, mw.config.get( 'wgNamespaceIds' ).user ),
annotation = ve.dm.MWInternalLinkAnnotation.static.newFromTitle( title );
if ( this.surface.getMode() === 'source' ) {
// TODO: this should be configurable per-wiki so that e.g. custom templates can be used
word = '[[' + title.getPrefixedText() + '|' + word + ']]';
return MWUsernameCompletionAction.super.prototype.insertCompletion.call( this, word, range );
}
return MWUsernameCompletionAction.super.prototype.insertCompletion.apply( this, arguments )
.annotateContent( 'clear', 'link' ).annotateContent( 'clear', 'link/mwInternal' )
.annotateContent( 'set', annotation );
};
MWUsernameCompletionAction.prototype.shouldAbandon = function ( input ) {
// TODO: need to consider whether pending loads from server are happening here
return MWUsernameCompletionAction.super.prototype.shouldAbandon.apply( this, arguments ) && input.split( /\s+/ ).length > 2;
};
/* Registration */
ve.ui.actionFactory.register( MWUsernameCompletionAction );
ve.ui.commandRegistry.register(
new ve.ui.Command(
'showMWUsernameCompletions', MWUsernameCompletionAction.static.name, 'open',
{ supportedSelections: [ 'linear' ] }
)
);
ve.ui.sequenceRegistry.register(
new ve.ui.Sequence( 'autocompleteMWUsernames', 'showMWUsernameCompletions', '@', 0, false, false, true )
);
ve.ui.wikitextSequenceRegistry.register(
new ve.ui.Sequence( 'autocompleteMWUsernamesWikitext', 'showMWUsernameCompletions', '@', 0, false, false, true )
);
module.exports = MWUsernameCompletionAction;