Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Merge pull request #1694 from ckeditor/t/1649
Browse files Browse the repository at this point in the history
Feature: Added possibility to refresh the marker with no changes through `Writer#updateMarker()` method. Closes #1649.
  • Loading branch information
scofalik committed Mar 12, 2019
2 parents f4e4644 + 01d6585 commit cf56d90
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 4 deletions.
22 changes: 22 additions & 0 deletions src/model/markercollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,28 @@ export default class MarkerCollection {
return false;
}

/**
* Fires an {@link module:engine/model/markercollection~MarkerCollection#event:update} event for the given {@link ~Marker marker}
* but does not change the marker. Useful to force {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher downcast
* conversion} for the marker.
*
* @protected
* @fires module:engine/model/markercollection~MarkerCollection#event:update
* @param {String} markerOrName Marker or name of a marker to refresh.
*/
_refresh( markerOrName ) {
const markerName = markerOrName instanceof Marker ? markerOrName.name : markerOrName;
const marker = this._markers.get( markerName );

if ( !marker ) {
throw new CKEditorError( 'markercollection-refresh-marker-not-exists: Marker with provided name does not exists.' );
}

const range = marker.getRange();

this.fire( 'update:' + markerName, marker, range, range, marker.managedUsingOperations, marker.affectsData );
}

/**
* Returns iterator that iterates over all markers, which ranges contain given {@link module:engine/model/position~Position position}.
*
Expand Down
38 changes: 35 additions & 3 deletions src/model/writer.js
Original file line number Diff line number Diff line change
Expand Up @@ -939,13 +939,38 @@ export default class Writer {
}

/**
* Adds or updates a {@link module:engine/model/markercollection~Marker marker}. Marker is a named range, which tracks
* Adds, updates or refreshes a {@link module:engine/model/markercollection~Marker marker}. Marker is a named range, which tracks
* changes in the document and updates its range automatically, when model tree changes. Still, it is possible to change the
* marker's range directly using this method.
*
* As the first parameter you can set marker name or instance. If none of them is provided, new marker, with a unique
* name is created and returned.
*
* As the second parameter you can set the new marker data or leave this parameter as empty which will just refresh
* the marker by triggering downcast conversion for it. Refreshing the marker is useful when you want to change
* the marker {@link module:engine/view/element~Element view element} without changing any marker data.
*
* let isCommentActive = false;
*
* model.conversion.markerToHighlight( {
* model: 'comment',
* view: data => {
* const classes = [ 'comment-marker' ];
*
* if ( isCommentActive ) {
* classes.push( 'comment-marker--active' );
* }
*
* return { classes };
* }
* } );
*
* // Change the property that indicates if marker is displayed as active or not.
* isCommentActive = true;
*
* // And refresh the marker to convert it with additional class.
* model.change( writer => writer.updateMarker( 'comment' ) );
*
* The `options.usingOperation` parameter lets you change if the marker should be managed by operations or not. See
* {@link module:engine/model/markercollection~Marker marker class description} to learn about the difference between
* markers managed by operations and not-managed by operations. It is possible to change this option for an existing marker.
Expand Down Expand Up @@ -975,13 +1000,14 @@ export default class Writer {
*
* @see module:engine/model/markercollection~Marker
* @param {String} markerOrName Name of a marker to update, or a marker instance.
* @param {Object} options
* @param {Object} [options] If options object is not defined then marker will be refreshed by triggering
* downcast conversion for this marker with the same data.
* @param {module:engine/model/range~Range} [options.range] Marker range to update.
* @param {Boolean} [options.usingOperation] Flag indicated whether the marker should be added by MarkerOperation.
* See {@link module:engine/model/markercollection~Marker#managedUsingOperations}.
* @param {Boolean} [options.affectsData] Flag indicating that the marker changes the editor data.
*/
updateMarker( markerOrName, options = {} ) {
updateMarker( markerOrName, options ) {
this._assertWriterUsedCorrectly();

const markerName = typeof markerOrName == 'string' ? markerOrName : markerOrName.name;
Expand All @@ -996,6 +1022,12 @@ export default class Writer {
throw new CKEditorError( 'writer-updateMarker-marker-not-exists: Marker with provided name does not exists.' );
}

if ( !options ) {
this.model.markers._refresh( currentMarker );

return;
}

const hasUsingOperationDefined = typeof options.usingOperation == 'boolean';
const affectsDataDefined = typeof options.affectsData == 'boolean';

Expand Down
18 changes: 18 additions & 0 deletions tests/model/markercollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,24 @@ describe( 'MarkerCollection', () => {
} );
} );

describe( '_refresh()', () => {
it( 'should fire update:<markerName> event', () => {
const marker = markers._set( 'name', range );

sinon.spy( markers, 'fire' );

markers._refresh( 'name' );

sinon.assert.calledWithExactly( markers.fire, 'update:name', marker, range, range, false, false );
} );

it( 'should throw if marker does not exist', () => {
expect( () => {
markers._refresh( 'name' );
} ).to.throw( CKEditorError, 'markercollection-refresh-marker-not-exists: Marker with provided name does not exists.' );
} );
} );

describe( 'getMarkersGroup', () => {
it( 'returns all markers which names start on given prefix', () => {
const markerFooA = markers._set( 'foo:a', range );
Expand Down
17 changes: 16 additions & 1 deletion tests/model/writer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2402,7 +2402,7 @@ describe( 'Writer', () => {
it( 'should throw when range and usingOperations were not provided', () => {
expect( () => {
addMarker( 'name', { range, usingOperation: false } );
updateMarker( 'name' );
updateMarker( 'name', {} );
} ).to.throw( CKEditorError, /^writer-updateMarker-wrong-options/ );
} );

Expand All @@ -2412,6 +2412,21 @@ describe( 'Writer', () => {
} ).to.throw( CKEditorError, /^writer-updateMarker-marker-not-exists/ );
} );

it( 'should only refresh the marker when there is no provided options to update', () => {
const marker = addMarker( 'name', { range, usingOperation: true } );
const spy = sinon.spy( model.markers, '_refresh' );

updateMarker( marker );

sinon.assert.calledOnce( spy );
sinon.assert.calledWithExactly( spy, marker );

updateMarker( 'name' );

sinon.assert.calledTwice( spy );
sinon.assert.calledWithExactly( spy.secondCall, marker );
} );

it( 'should throw when trying to use detached writer', () => {
const marker = addMarker( 'name', { range, usingOperation: false } );
const writer = new Writer( model, batch );
Expand Down

0 comments on commit cf56d90

Please sign in to comment.