/*!
* VisualEditor ContentEditable SurfaceSynchronizer class
*
* @copyright See AUTHORS.txt
*/
/**
* ContentEditable surface synchronizer
*
* Handles displaying remote author selections from the surface synchronizer model.
*
* @param {ve.ce.Surface} surface
* @param {ve.dm.SurfaceSynchronizer} model
*/
ve.ce.SurfaceSynchronizer = function VeCeSurfaceSynchronizer( surface, model ) {
this.surface = surface;
this.model = model;
this.userSelectionDeactivateTimers = new Map();
// Events
this.model.connect( this, {
authorSelect: 'onSynchronizerAuthorUpdate',
authorChange: 'onSynchronizerAuthorUpdate',
authorDisconnect: 'onSynchronizerAuthorDisconnect',
wrongDoc: 'onSynchronizerWrongDoc',
pause: 'onSynchronizerPause'
} );
this.surface.connect( this, { position: 'onSurfacePosition' } );
};
/* Initialization */
OO.initClass( ve.ce.SurfaceSynchronizer );
/* Methods */
/**
* Get the model synchronizer
*
* @return {ve.dm.SurfaceSynchronizer}
*/
ve.ce.SurfaceSynchronizer.prototype.getModel = function () {
return this.model;
};
/**
* Get the surface
*
* @return {ve.ce.Surface}
*/
ve.ce.SurfaceSynchronizer.prototype.getSurface = function () {
return this.surface;
};
/**
* Paint a remote author's current selection, as stored in the synchronizer
*
* @param {number} authorId The author ID
*/
ve.ce.SurfaceSynchronizer.prototype.paintAuthor = function ( authorId ) {
const authorData = this.getModel().getAuthorData( authorId ),
selection = this.getModel().authorSelections[ authorId ],
selectionManager = this.getSurface().getSelectionManager();
if ( !authorData || !selection || authorId === this.getModel().getAuthorId() ) {
return;
}
const color = '#' + authorData.color;
const selectionOptions = {
wrapperClass: 've-ce-surface-selections-otherUserSelection',
color,
showCursor: true,
label: authorData.name
};
const selectionOptionsInactive = Object.assign( {}, selectionOptions, {
wrapperClass: 've-ce-surface-selections-otherUserSelection ve-ce-surface-selections-otherUserSelection-inactive'
} );
if ( !this.userSelectionDeactivateTimers.has( authorId ) ) {
this.userSelectionDeactivateTimers.set( authorId, ve.debounce( () => {
selectionManager.setOptions( 'otherUserSelection-' + authorId, selectionOptionsInactive );
}, 5000 ) );
}
this.userSelectionDeactivateTimers.get( authorId )();
if ( !selection || selection.isNull() ) {
selectionManager.drawSelections( 'otherUserSelection-' + authorId, [] );
return;
}
selectionManager.drawSelections(
'otherUserSelection-' + authorId,
[ ve.ce.Selection.static.newFromModel( selection, this.getSurface() ) ],
selectionOptions
);
};
/**
* Called when the synchronizer receives a remote author selection or name change
*
* @param {number} authorId The author ID
*/
ve.ce.SurfaceSynchronizer.prototype.onSynchronizerAuthorUpdate = function ( authorId ) {
this.paintAuthor( authorId );
};
/**
* Called when the synchronizer receives a remote author disconnect
*
* @param {number} authorId The author ID
*/
ve.ce.SurfaceSynchronizer.prototype.onSynchronizerAuthorDisconnect = function ( authorId ) {
this.getSurface().getSelectionManager().drawSelections( 'otherUserSelection-' + authorId, [] );
};
/**
* Called when the synchronizer reconnects and their is a server doc ID mismatch
*/
ve.ce.SurfaceSynchronizer.prototype.onSynchronizerWrongDoc = function () {
OO.ui.alert(
ve.msg( 'visualeditor-rebase-missing-document-error' ),
{ title: ve.msg( 'visualeditor-rebase-missing-document-title' ) }
);
};
/**
* Handle pause events from the synchronizer
*
* Drops the opacity of the surface to indicate that no updates are
* being received from other users.
*/
ve.ce.SurfaceSynchronizer.prototype.onSynchronizerPause = function () {
this.getSurface().$element.toggleClass( 've-ce-surface-paused', !!this.getModel().paused );
};
/**
* Handle position events from the surface
*/
ve.ce.SurfaceSynchronizer.prototype.onSurfacePosition = function () {
// Defer to allow synchronizer to adjust for transactions
setTimeout( () => {
const authorSelections = this.getModel().authorSelections;
for ( const authorId in authorSelections ) {
this.onSynchronizerAuthorUpdate( +authorId );
}
} );
};