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

Commit

Permalink
Merge edd36c4 into 1c3749e
Browse files Browse the repository at this point in the history
  • Loading branch information
scofalik committed Feb 5, 2020
2 parents 1c3749e + edd36c4 commit 033286e
Show file tree
Hide file tree
Showing 2 changed files with 243 additions and 0 deletions.
25 changes: 25 additions & 0 deletions src/model/documentselection.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export default class DocumentSelection {

this._selection.delegate( 'change:range' ).to( this );
this._selection.delegate( 'change:attribute' ).to( this );
this._selection.delegate( 'change:marker' ).to( this );
}

/**
Expand Down Expand Up @@ -542,6 +543,17 @@ mix( DocumentSelection, EmitterMixin );
* @param {Array.<String>} attributeKeys Array containing keys of attributes that changed.
*/

/**
* Fired when selection marker(s) changed.
*
* @event change:marker
* @param {Boolean} directChange This is always set to `false` in case of `change:marker` event as there is no possibility
* to change markers directly through {@link module:engine/model/documentselection~DocumentSelection} API.
* See also {@link module:engine/model/documentselection~DocumentSelection#event:change:range} and
* {@link module:engine/model/documentselection~DocumentSelection#event:change:attribute}.
* @param {Array.<module:engine/model/markercollection~Marker>} oldMarkers Markers in which the selection was before the change.
*/

// `LiveSelection` is used internally by {@link module:engine/model/documentselection~DocumentSelection} and shouldn't be used directly.
//
// LiveSelection` is automatically updated upon changes in the {@link module:engine/model/document~Document document}
Expand Down Expand Up @@ -721,11 +733,13 @@ class LiveSelection extends Selection {
setTo( selectable, optionsOrPlaceOrOffset, options ) {
super.setTo( selectable, optionsOrPlaceOrOffset, options );
this._updateAttributes( true );
this._updateMarkers();
}

setFocus( itemOrPosition, offset ) {
super.setFocus( itemOrPosition, offset );
this._updateAttributes( true );
this._updateMarkers();
}

setAttribute( key, value ) {
Expand Down Expand Up @@ -830,6 +844,7 @@ class LiveSelection extends Selection {

_updateMarkers() {
const markers = [];
let changed = false;

for ( const marker of this._model.markers ) {
const markerRange = marker.getRange();
Expand All @@ -841,17 +856,27 @@ class LiveSelection extends Selection {
}
}

const oldMarkers = Array.from( this.markers );

for ( const marker of markers ) {
if ( !this.markers.has( marker ) ) {
this.markers.add( marker );

changed = true;
}
}

for ( const marker of Array.from( this.markers ) ) {
if ( !markers.includes( marker ) ) {
this.markers.remove( marker );

changed = true;
}
}

if ( changed ) {
this.fire( 'change:marker', { oldMarkers, directChange: false } );
}
}

// Updates this selection attributes according to its ranges and the {@link module:engine/model/document~Document model document}.
Expand Down
218 changes: 218 additions & 0 deletions tests/model/documentselection.js
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,224 @@ describe( 'DocumentSelection', () => {

expect( selection.markers.map( marker => marker.name ) ).to.have.members( [ 'marker' ] );
} );

describe( 'should fire change:marker event when', () => {
// Set marker to range 0-4.
beforeEach( () => {
model.change( writer => {
writer.addMarker( 'marker-1', {
range: writer.createRange(
writer.createPositionFromPath( root, [ 2, 0 ] ),
writer.createPositionFromPath( root, [ 2, 4 ] )
),
usingOperation: false
} );
} );
} );

it( 'selection ranges change (marker added to the selection)', () => {
const spy = sinon.spy();

model.change( writer => {
// The selection has no markers before the change.
model.document.selection.on( 'change:marker', ( evt, data ) => {
expect( data.oldMarkers ).to.deep.equal( [] );
spy();
} );

// Move selection to 1-2, that is inside 0-4 marker.
writer.setSelection( writer.createRange(
writer.createPositionFromPath( root, [ 2, 1 ] ),
writer.createPositionFromPath( root, [ 2, 2 ] )
) );
} );

expect( spy.calledOnce ).to.be.true;
} );

it( 'selection ranges change (marker removed from the selection)', () => {
const spy = sinon.spy();

model.change( writer => {
writer.setSelection( writer.createRange(
writer.createPositionFromPath( root, [ 2, 1 ] ),
writer.createPositionFromPath( root, [ 2, 2 ] )
) );

// The selection is in a marker before the change.
model.document.selection.on( 'change:marker', ( evt, data ) => {
expect( data.oldMarkers.map( marker => marker.name ) ).to.deep.equal( [ 'marker-1' ] );
spy();
} );

// Move the selection out of the marker.
writer.setSelection( writer.createPositionFromPath( root, [ 2, 5 ] ) );
} );

expect( spy.calledOnce ).to.be.true;
} );

it( 'selection focus changes (marker removed from the selection)', () => {
const spy = sinon.spy();

model.change( writer => {
writer.setSelection( writer.createPositionFromPath( root, [ 2, 2 ] ) );

// The selection is in a marker before the change.
model.document.selection.on( 'change:marker', ( evt, data ) => {
expect( data.oldMarkers.map( marker => marker.name ) ).to.deep.equal( [ 'marker-1' ] );
spy();
} );

// Move the selection focus out of the marker.
writer.setSelectionFocus( writer.createPositionFromPath( root, [ 2, 5 ] ) );
} );

expect( spy.calledOnce ).to.be.true;
} );

it( 'a new marker contains the selection', () => {
const spy = sinon.spy();

model.change( writer => {
writer.setSelection( writer.createPositionFromPath( root, [ 2, 5 ] ) );

// The selection is not in a marker before the change.
model.document.selection.on( 'change:marker', ( evt, data ) => {
expect( data.oldMarkers ).to.deep.equal( [] );
spy();
} );

writer.updateMarker( 'marker-1', {
range: writer.createRange(
writer.createPositionFromPath( root, [ 2, 0 ] ),
writer.createPositionFromPath( root, [ 2, 6 ] )
)
} );
} );

expect( spy.calledOnce ).to.be.true;
} );

it( 'a marker stops contains the selection', () => {
const spy = sinon.spy();

model.change( writer => {
writer.setSelection( writer.createPositionFromPath( root, [ 2, 3 ] ) );

// The selection is in a marker before the change.
model.document.selection.on( 'change:marker', ( evt, data ) => {
expect( data.oldMarkers.map( marker => marker.name ) ).to.deep.equal( [ 'marker-1' ] );
spy();
} );

writer.updateMarker( 'marker-1', {
range: writer.createRange(
writer.createPositionFromPath( root, [ 2, 0 ] ),
writer.createPositionFromPath( root, [ 2, 1 ] )
)
} );
} );

expect( spy.calledOnce ).to.be.true;
} );
} );

describe( 'should not fire change:marker event when', () => {
// Set marker to range 0-4.
beforeEach( () => {
model.change( writer => {
writer.addMarker( 'marker-1', {
range: writer.createRange(
writer.createPositionFromPath( root, [ 2, 0 ] ),
writer.createPositionFromPath( root, [ 2, 4 ] )
),
usingOperation: false
} );
} );
} );

it( 'selection ranges change does not change selection markers - no markers', () => {
const spy = sinon.spy();

model.document.selection.on( 'change:marker', spy );

model.change( writer => {
writer.setSelection( writer.createPositionFromPath( root, [ 2, 5 ] ) );
} );

expect( spy.called ).to.be.false;
} );

it( 'selection ranges change does not change selection markers - same markers', () => {
model.change( writer => {
writer.setSelection( writer.createPositionFromPath( root, [ 2, 2 ] ) );
} );

const spy = sinon.spy();

model.document.selection.on( 'change:marker', spy );

model.change( writer => {
writer.setSelection( writer.createPositionFromPath( root, [ 2, 3 ] ) );
} );

expect( spy.called ).to.be.false;
} );

it( 'selection focus change does not change selection markers', () => {
model.change( writer => {
writer.setSelection( writer.createPositionFromPath( root, [ 2, 2 ] ) );
} );

const spy = sinon.spy();

model.document.selection.on( 'change:marker', spy );

model.change( writer => {
writer.setSelectionFocus( writer.createPositionFromPath( root, [ 2, 3 ] ) );
} );

expect( spy.called ).to.be.false;
} );

it( 'changed marker still contains the selection', () => {
model.change( writer => {
writer.setSelection( writer.createPositionFromPath( root, [ 2, 2 ] ) );
} );

const spy = sinon.spy();

model.document.selection.on( 'change:marker', spy );

model.change( writer => {
writer.updateMarker( 'marker-1', {
range: writer.createRange(
writer.createPositionFromPath( root, [ 2, 0 ] ),
writer.createPositionFromPath( root, [ 2, 5 ] )
)
} );
} );

expect( spy.called ).to.be.false;
} );

it( 'removed marker did not contain the selection', () => {
model.change( writer => {
writer.setSelection( writer.createPositionFromPath( root, [ 2, 5 ] ) );
} );

const spy = sinon.spy();

model.document.selection.on( 'change:marker', spy );

model.change( writer => {
writer.removeMarker( 'marker-1' );
} );

expect( spy.called ).to.be.false;
} );
} );
} );

describe( 'destroy()', () => {
Expand Down

0 comments on commit 033286e

Please sign in to comment.