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

Commit b2c1d72

Browse files
author
Piotr Jasiun
authored
Merge pull request #1623 from ckeditor/t/1615
Feature: Introduced `DocumentSelection#markers` collection. Closes #1615.
2 parents b7b5b3b + b7f92d7 commit b2c1d72

File tree

3 files changed

+295
-1
lines changed

3 files changed

+295
-1
lines changed

src/controller/datacontroller.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export default class DataController {
5858
* Data processor used during the conversion.
5959
*
6060
* @readonly
61-
* @member {module:engine/dataProcessor~DataProcessor}
61+
* @member {module:engine/dataprocessor/dataprocessor~DataProcessor}
6262
*/
6363
this.processor = dataProcessor;
6464

src/model/documentselection.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import LiveRange from './liverange';
1515
import Text from './text';
1616
import TextProxy from './textproxy';
1717
import toMap from '@ckeditor/ckeditor5-utils/src/tomap';
18+
import Collection from '@ckeditor/ckeditor5-utils/src/collection';
1819
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
1920
import log from '@ckeditor/ckeditor5-utils/src/log';
2021
import uid from '@ckeditor/ckeditor5-utils/src/uid';
@@ -149,6 +150,17 @@ export default class DocumentSelection {
149150
return this._selection.isGravityOverridden;
150151
}
151152

153+
/**
154+
* A collection of selection markers.
155+
* Marker is a selection marker when selection range is inside the marker range.
156+
*
157+
* @readonly
158+
* @type {module:utils/collection~Collection.<module:engine/model/markercollection~Marker>}
159+
*/
160+
get markers() {
161+
return this._selection.markers;
162+
}
163+
152164
/**
153165
* Used for the compatibility with the {@link module:engine/model/selection~Selection#isEqual} method.
154166
*
@@ -526,6 +538,12 @@ class LiveSelection extends Selection {
526538
constructor( doc ) {
527539
super();
528540

541+
// List of selection markers.
542+
// Marker is a selection marker when selection range is inside the marker range.
543+
//
544+
// @type {module:utils/collection~Collection}
545+
this.markers = new Collection( { idProperty: 'name' } );
546+
529547
// Document which owns this selection.
530548
//
531549
// @protected
@@ -586,6 +604,9 @@ class LiveSelection extends Selection {
586604
} );
587605

588606
this.listenTo( this._document, 'change', ( evt, batch ) => {
607+
// Update selection's markers.
608+
this._updateMarkers();
609+
589610
// Update selection's attributes.
590611
this._updateAttributes( false );
591612

@@ -788,6 +809,32 @@ class LiveSelection extends Selection {
788809
return liveRange;
789810
}
790811

812+
_updateMarkers() {
813+
const markers = [];
814+
815+
for ( const marker of this._model.markers ) {
816+
const markerRange = marker.getRange();
817+
818+
for ( const selectionRange of this.getRanges() ) {
819+
if ( markerRange.containsRange( selectionRange, !selectionRange.isCollapsed ) ) {
820+
markers.push( marker );
821+
}
822+
}
823+
}
824+
825+
for ( const marker of markers ) {
826+
if ( !this.markers.has( marker ) ) {
827+
this.markers.add( marker );
828+
}
829+
}
830+
831+
for ( const marker of Array.from( this.markers ) ) {
832+
if ( !markers.includes( marker ) ) {
833+
this.markers.remove( marker );
834+
}
835+
}
836+
}
837+
791838
// Updates this selection attributes according to its ranges and the {@link module:engine/model/document~Document model document}.
792839
//
793840
// @protected

tests/model/documentselection.js

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import MoveOperation from '../../src/model/operation/moveoperation';
1616
import AttributeOperation from '../../src/model/operation/attributeoperation';
1717
import SplitOperation from '../../src/model/operation/splitoperation';
1818
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
19+
import Collection from '@ckeditor/ckeditor5-utils/src/collection';
1920
import count from '@ckeditor/ckeditor5-utils/src/count';
2021
import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
2122
import { setData, getData } from '../../src/dev-utils/model';
@@ -197,6 +198,252 @@ describe( 'DocumentSelection', () => {
197198
} );
198199
} );
199200

201+
describe( 'markers', () => {
202+
it( 'should implement #markers collection', () => {
203+
expect( selection.markers ).to.instanceof( Collection );
204+
expect( selection.markers ).to.length( 0 );
205+
} );
206+
207+
it( 'should add markers to the collection when selection is inside the marker range', () => {
208+
model.change( writer => {
209+
writer.setSelection( writer.createRange(
210+
writer.createPositionFromPath( root, [ 2, 2 ] ),
211+
writer.createPositionFromPath( root, [ 2, 4 ] )
212+
) );
213+
214+
writer.addMarker( 'marker-1', {
215+
range: writer.createRange(
216+
writer.createPositionFromPath( root, [ 0, 0 ] ),
217+
writer.createPositionFromPath( root, [ 2, 2 ] )
218+
),
219+
usingOperation: false
220+
} );
221+
222+
writer.addMarker( 'marker-2', {
223+
range: writer.createRange(
224+
writer.createPositionFromPath( root, [ 2, 2 ] ),
225+
writer.createPositionFromPath( root, [ 2, 4 ] )
226+
),
227+
usingOperation: false
228+
} );
229+
230+
writer.addMarker( 'marker-3', {
231+
range: writer.createRange(
232+
writer.createPositionFromPath( root, [ 2, 1 ] ),
233+
writer.createPositionFromPath( root, [ 2, 5 ] )
234+
),
235+
usingOperation: false
236+
} );
237+
238+
writer.addMarker( 'marker-4', {
239+
range: writer.createRange(
240+
writer.createPositionFromPath( root, [ 2, 4 ] ),
241+
writer.createPositionFromPath( root, [ 3, 0 ] )
242+
),
243+
usingOperation: false
244+
} );
245+
} );
246+
247+
expect( selection.markers.map( marker => marker.name ) ).to.have.members( [ 'marker-2', 'marker-3' ] );
248+
} );
249+
250+
it( 'should update markers after selection change', () => {
251+
model.change( writer => {
252+
writer.setSelection( writer.createRange(
253+
writer.createPositionFromPath( root, [ 2, 1 ] ),
254+
writer.createPositionFromPath( root, [ 2, 2 ] )
255+
) );
256+
257+
writer.addMarker( 'marker-1', {
258+
range: writer.createRange(
259+
writer.createPositionFromPath( root, [ 2, 0 ] ),
260+
writer.createPositionFromPath( root, [ 2, 6 ] )
261+
),
262+
usingOperation: false
263+
} );
264+
265+
writer.addMarker( 'marker-2', {
266+
range: writer.createRange(
267+
writer.createPositionFromPath( root, [ 2, 0 ] ),
268+
writer.createPositionFromPath( root, [ 2, 3 ] )
269+
),
270+
usingOperation: false
271+
} );
272+
273+
writer.addMarker( 'marker-3', {
274+
range: writer.createRange(
275+
writer.createPositionFromPath( root, [ 2, 3 ] ),
276+
writer.createPositionFromPath( root, [ 2, 6 ] )
277+
),
278+
usingOperation: false
279+
} );
280+
} );
281+
282+
expect( selection.markers.map( marker => marker.name ) ).to.have.members( [ 'marker-1', 'marker-2' ] );
283+
284+
model.change( writer => {
285+
writer.setSelection( writer.createRange(
286+
writer.createPositionFromPath( root, [ 2, 4 ] ),
287+
writer.createPositionFromPath( root, [ 2, 5 ] )
288+
) );
289+
} );
290+
291+
expect( selection.markers.map( marker => marker.name ) ).to.have.members( [ 'marker-1', 'marker-3' ] );
292+
} );
293+
294+
it( 'should update markers after markers change', () => {
295+
model.change( writer => {
296+
writer.setSelection( writer.createRange(
297+
writer.createPositionFromPath( root, [ 2, 1 ] ),
298+
writer.createPositionFromPath( root, [ 2, 2 ] )
299+
) );
300+
301+
writer.addMarker( 'marker-1', {
302+
range: writer.createRange(
303+
writer.createPositionFromPath( root, [ 2, 0 ] ),
304+
writer.createPositionFromPath( root, [ 2, 6 ] )
305+
),
306+
usingOperation: false
307+
} );
308+
309+
writer.addMarker( 'marker-2', {
310+
range: writer.createRange(
311+
writer.createPositionFromPath( root, [ 2, 0 ] ),
312+
writer.createPositionFromPath( root, [ 2, 3 ] )
313+
),
314+
usingOperation: false
315+
} );
316+
317+
writer.addMarker( 'marker-3', {
318+
range: writer.createRange(
319+
writer.createPositionFromPath( root, [ 2, 3 ] ),
320+
writer.createPositionFromPath( root, [ 2, 6 ] )
321+
),
322+
usingOperation: false
323+
} );
324+
} );
325+
326+
expect( selection.markers.map( marker => marker.name ), 1 ).to.have.members( [ 'marker-1', 'marker-2' ] );
327+
328+
model.change( writer => {
329+
writer.removeMarker( 'marker-1' );
330+
331+
writer.updateMarker( 'marker-2', {
332+
range: writer.createRange(
333+
writer.createPositionFromPath( root, [ 2, 3 ] ),
334+
writer.createPositionFromPath( root, [ 2, 6 ] )
335+
),
336+
usingOperation: false
337+
} );
338+
339+
writer.updateMarker( 'marker-3', {
340+
range: writer.createRange(
341+
writer.createPositionFromPath( root, [ 2, 0 ] ),
342+
writer.createPositionFromPath( root, [ 2, 3 ] )
343+
),
344+
usingOperation: false
345+
} );
346+
} );
347+
348+
expect( selection.markers.map( marker => marker.name ), 2 ).to.have.members( [ 'marker-3' ] );
349+
} );
350+
351+
it( 'should not add marker when collapsed selection is on the marker left bound', () => {
352+
model.change( writer => {
353+
writer.setSelection( writer.createRange(
354+
writer.createPositionFromPath( root, [ 2, 2 ] ),
355+
writer.createPositionFromPath( root, [ 2, 4 ] )
356+
) );
357+
358+
writer.addMarker( 'marker', {
359+
range: writer.createRange(
360+
writer.createPositionFromPath( root, [ 2, 2 ] )
361+
),
362+
usingOperation: false
363+
} );
364+
} );
365+
366+
expect( selection.markers ).to.length( 0 );
367+
} );
368+
369+
it( 'should not add marker when collapsed selection is on the marker right bound', () => {
370+
model.change( writer => {
371+
writer.setSelection( writer.createRange(
372+
writer.createPositionFromPath( root, [ 2, 4 ] )
373+
) );
374+
375+
writer.addMarker( 'marker', {
376+
range: writer.createRange(
377+
writer.createPositionFromPath( root, [ 2, 2 ] ),
378+
writer.createPositionFromPath( root, [ 2, 4 ] )
379+
),
380+
usingOperation: false
381+
} );
382+
} );
383+
384+
expect( selection.markers ).to.length( 0 );
385+
} );
386+
387+
it( 'should add marker when non-collapsed selection is inside a marker and touches the left bound', () => {
388+
model.change( writer => {
389+
writer.setSelection( writer.createRange(
390+
writer.createPositionFromPath( root, [ 2, 1 ] ),
391+
writer.createPositionFromPath( root, [ 2, 3 ] )
392+
) );
393+
394+
writer.addMarker( 'marker', {
395+
range: writer.createRange(
396+
writer.createPositionFromPath( root, [ 2, 1 ] ),
397+
writer.createPositionFromPath( root, [ 2, 5 ] )
398+
),
399+
usingOperation: false
400+
} );
401+
} );
402+
403+
expect( selection.markers.map( marker => marker.name ) ).to.have.members( [ 'marker' ] );
404+
} );
405+
406+
it( 'should add marker when non-collapsed selection is inside a marker and touches the right bound', () => {
407+
model.change( writer => {
408+
writer.setSelection( writer.createRange(
409+
writer.createPositionFromPath( root, [ 2, 2 ] ),
410+
writer.createPositionFromPath( root, [ 2, 5 ] )
411+
) );
412+
413+
writer.addMarker( 'marker', {
414+
range: writer.createRange(
415+
writer.createPositionFromPath( root, [ 2, 1 ] ),
416+
writer.createPositionFromPath( root, [ 2, 5 ] )
417+
),
418+
usingOperation: false
419+
} );
420+
} );
421+
422+
expect( selection.markers.map( marker => marker.name ) ).to.have.members( [ 'marker' ] );
423+
} );
424+
425+
it( 'should add marker of selected widget', () => {
426+
root._insertChild( 0, new Element( 'widget' ) );
427+
428+
model.change( writer => {
429+
writer.setSelection( writer.createRange(
430+
writer.createPositionFromPath( root, [ 0 ] ),
431+
writer.createPositionFromPath( root, [ 1 ] )
432+
) );
433+
434+
writer.addMarker( 'marker', {
435+
range: writer.createRange(
436+
writer.createPositionFromPath( root, [ 0 ] ),
437+
writer.createPositionFromPath( root, [ 1 ] )
438+
),
439+
usingOperation: false
440+
} );
441+
} );
442+
443+
expect( selection.markers.map( marker => marker.name ) ).to.have.members( [ 'marker' ] );
444+
} );
445+
} );
446+
200447
describe( 'destroy()', () => {
201448
it( 'should unbind all events', () => {
202449
selection._setTo( [ range, liveRange ] );

0 commit comments

Comments
 (0)