@@ -17,8 +17,7 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
1717/**
1818 * The collection of all {@link module:engine/model/markercollection~Marker markers} attached to the document.
1919 * It lets you {@link module:engine/model/markercollection~MarkerCollection#get get} markers or track them using
20- * {@link module:engine/model/markercollection~MarkerCollection#event:set} and
21- * {@link module:engine/model/markercollection~MarkerCollection#event:remove} events.
20+ * {@link module:engine/model/markercollection~MarkerCollection#event:update} event.
2221 *
2322 * To create, change or remove makers use {@link module:engine/model/writer~Writer model writers'} methods:
2423 * {@link module:engine/model/writer~Writer#setMarker} or {@link module:engine/model/writer~Writer#removeMarker}. Since
@@ -79,37 +78,47 @@ export default class MarkerCollection {
7978 * Creates and adds a {@link ~Marker marker} to the `MarkerCollection` with given name on given
8079 * {@link module:engine/model/range~Range range}.
8180 *
82- * If `MarkerCollection` already had a marker with given name (or {@link ~Marker marker} was passed) and the range to
83- * set is different, the marker in collection is removed and then new marker is added. If the range was same, nothing
84- * happens and `false` is returned .
81+ * If `MarkerCollection` already had a marker with given name (or {@link ~Marker marker} was passed), the marker in
82+ * collection is updated and { @link module:engine/model/markercollection~MarkerCollection#event:update} event is fired
83+ * but only if there was a change (marker range or { @link ~Marker#managedUsingOperations} flag has changed .
8584 *
8685 * @protected
87- * @fires module:engine/model/markercollection~MarkerCollection#event:set
88- * @fires module:engine/model/markercollection~MarkerCollection#event:remove
86+ * @fires module:engine/model/markercollection~MarkerCollection#event:update
8987 * @param {String|module:engine/model/markercollection~Marker } markerOrName Name of marker to set or marker instance to update.
9088 * @param {module:engine/model/range~Range } range Marker range.
91- * @param {Boolean } managedUsingOperations Specifies whether the marker is managed using operations.
92- * @returns {module:engine/model/markercollection~Marker } `Marker` instance added to the collection .
89+ * @param {Boolean } [ managedUsingOperations=false] Specifies whether the marker is managed using operations.
90+ * @returns {module:engine/model/markercollection~Marker } `Marker` instance which was added or updated .
9391 */
94- _set ( markerOrName , range , managedUsingOperations ) {
92+ _set ( markerOrName , range , managedUsingOperations = false ) {
9593 const markerName = markerOrName instanceof Marker ? markerOrName . name : markerOrName ;
9694 const oldMarker = this . _markers . get ( markerName ) ;
9795
9896 if ( oldMarker ) {
9997 const oldRange = oldMarker . getRange ( ) ;
98+ let hasChanged = false ;
99+
100+ if ( ! oldRange . isEqual ( range ) ) {
101+ oldMarker . _attachLiveRange ( LiveRange . createFromRange ( range ) ) ;
102+ hasChanged = true ;
103+ }
100104
101- if ( oldRange . isEqual ( range ) && managedUsingOperations === oldMarker . managedUsingOperations ) {
102- return oldMarker ;
105+ if ( managedUsingOperations != oldMarker . managedUsingOperations ) {
106+ oldMarker . _managedUsingOperations = managedUsingOperations ;
107+ hasChanged = true ;
103108 }
104109
105- this . _remove ( markerName ) ;
110+ if ( hasChanged ) {
111+ this . fire ( 'update:' + markerName , oldMarker , oldRange , range ) ;
112+ }
113+
114+ return oldMarker ;
106115 }
107116
108117 const liveRange = LiveRange . createFromRange ( range ) ;
109118 const marker = new Marker ( markerName , liveRange , managedUsingOperations ) ;
110119
111120 this . _markers . set ( markerName , marker ) ;
112- this . fire ( 'set :' + markerName , marker ) ;
121+ this . fire ( 'update :' + markerName , marker , null , range ) ;
113122
114123 return marker ;
115124 }
@@ -118,6 +127,7 @@ export default class MarkerCollection {
118127 * Removes given {@link ~Marker marker} or a marker with given name from the `MarkerCollection`.
119128 *
120129 * @protected
130+ * @fires module:engine/model/markercollection~MarkerCollection#event:update
121131 * @param {String } markerOrName Marker or name of a marker to remove.
122132 * @returns {Boolean } `true` if marker was found and removed, `false` otherwise.
123133 */
@@ -127,7 +137,7 @@ export default class MarkerCollection {
127137
128138 if ( oldMarker ) {
129139 this . _markers . delete ( markerName ) ;
130- this . fire ( 'remove :' + markerName , oldMarker ) ;
140+ this . fire ( 'update :' + markerName , oldMarker , oldMarker . getRange ( ) , null ) ;
131141
132142 this . _destroyMarker ( oldMarker ) ;
133143
@@ -193,22 +203,18 @@ export default class MarkerCollection {
193203 */
194204 _destroyMarker ( marker ) {
195205 marker . stopListening ( ) ;
196- marker . _liveRange . detach ( ) ;
197- marker . _liveRange = null ;
206+ marker . _detachLiveRange ( ) ;
198207 }
199208
200209 /**
201- * Fired whenever marker is added or updated in `MarkerCollection`.
210+ * Fired whenever marker is added, updated or removed from `MarkerCollection`.
202211 *
203- * @event set
204- * @param {module:engine/model/markercollection~Marker } The set marker.
205- */
206-
207- /**
208- * Fired whenever marker is removed from `MarkerCollection`.
209- *
210- * @event remove
211- * @param {module:engine/model/markercollection~Marker } marker The removed marker.
212+ * @event update
213+ * @param {module:engine/model/markercollection~Marker } Updated Marker.
214+ * @param {module:engine/model/range~Range|null } oldRange Marker range before the update. When is not defined it
215+ * means that marker is just added.
216+ * @param {module:engine/model/range~Range|null } newRange Marker range after update. When is not defined it
217+ * means that marker is just removed.
212218 */
213219}
214220
@@ -291,6 +297,7 @@ class Marker {
291297 *
292298 * @param {String } name Marker name.
293299 * @param {module:engine/model/liverange~LiveRange } liveRange Range marked by the marker.
300+ * @param {Boolean } managedUsingOperations Specifies whether the marker is managed using operations.
294301 */
295302 constructor ( name , liveRange , managedUsingOperations ) {
296303 /**
@@ -302,25 +309,35 @@ class Marker {
302309 this . name = name ;
303310
304311 /**
305- * Flag indicates if the marker is managed using operations or not. See {@link ~Marker marker class description}
306- * to learn more about marker types. See {@link module:engine/model/writer~Writer#setMarker}.
312+ * Flag indicates if the marker is managed using operations or not.
307313 *
308- * @readonly
309- * @type {Boolean }
314+ * @protected
315+ * @member {Boolean}
310316 */
311- this . managedUsingOperations = managedUsingOperations ;
317+ this . _managedUsingOperations = managedUsingOperations ;
312318
313319 /**
314320 * Range marked by the marker.
315321 *
316- * @protected
317- * @type {module:engine/model/liverange~LiveRange }
322+ * @private
323+ * @member {module:engine/model/liverange~LiveRange} #_liveRange
318324 */
319- this . _liveRange = liveRange ;
325+ this . _liveRange = this . _attachLiveRange ( liveRange ) ;
326+ }
320327
321- // Delegating does not work with namespaces. Alternatively, we could delegate all events (using `*`).
322- this . _liveRange . delegate ( 'change:range' ) . to ( this ) ;
323- this . _liveRange . delegate ( 'change:content' ) . to ( this ) ;
328+ /**
329+ * Returns value of flag indicates if the marker is managed using operations or not.
330+ * See {@link ~Marker marker class description} to learn more about marker types.
331+ * See {@link module:engine/model/writer~Writer#setMarker}.
332+ *
333+ * @returns {Boolean }
334+ */
335+ get managedUsingOperations ( ) {
336+ if ( ! this . _liveRange ) {
337+ throw new CKEditorError ( 'marker-destroyed: Cannot use a destroyed marker instance.' ) ;
338+ }
339+
340+ return this . _managedUsingOperations ;
324341 }
325342
326343 /**
@@ -369,6 +386,39 @@ class Marker {
369386 return Range . createFromRange ( this . _liveRange ) ;
370387 }
371388
389+ /**
390+ * Binds new live range to marker and detach the old one if is attached.
391+ *
392+ * @protected
393+ * @param {module:engine/model/liverange~LiveRange } liveRange Live range to attach
394+ * @return {module:engine/model/liverange~LiveRange } Attached live range.
395+ */
396+ _attachLiveRange ( liveRange ) {
397+ if ( this . _liveRange ) {
398+ this . _detachLiveRange ( ) ;
399+ }
400+
401+ // Delegating does not work with namespaces. Alternatively, we could delegate all events (using `*`).
402+ liveRange . delegate ( 'change:range' ) . to ( this ) ;
403+ liveRange . delegate ( 'change:content' ) . to ( this ) ;
404+
405+ this . _liveRange = liveRange ;
406+
407+ return liveRange ;
408+ }
409+
410+ /**
411+ * Unbinds and destroys currently attached live range.
412+ *
413+ * @protected
414+ */
415+ _detachLiveRange ( ) {
416+ this . _liveRange . stopDelegating ( 'change:range' , this ) ;
417+ this . _liveRange . stopDelegating ( 'change:content' , this ) ;
418+ this . _liveRange . detach ( ) ;
419+ this . _liveRange = null ;
420+ }
421+
372422 /**
373423 * Fired whenever {@link ~Marker#_liveRange marker range} is changed due to changes on {@link module:engine/model/document~Document}.
374424 * This is a delegated {@link module:engine/model/liverange~LiveRange#event:change:range LiveRange change:range event}.
0 commit comments