@ckeditor/ckeditor5-engine 34.2.0 → 35.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/CHANGELOG.md +823 -0
  2. package/LICENSE.md +4 -0
  3. package/package.json +32 -25
  4. package/src/controller/datacontroller.js +467 -561
  5. package/src/controller/editingcontroller.js +168 -204
  6. package/src/conversion/conversion.js +541 -565
  7. package/src/conversion/conversionhelpers.js +24 -28
  8. package/src/conversion/downcastdispatcher.js +457 -686
  9. package/src/conversion/downcasthelpers.js +1583 -1965
  10. package/src/conversion/mapper.js +518 -707
  11. package/src/conversion/modelconsumable.js +240 -283
  12. package/src/conversion/upcastdispatcher.js +372 -718
  13. package/src/conversion/upcasthelpers.js +707 -818
  14. package/src/conversion/viewconsumable.js +524 -581
  15. package/src/dataprocessor/basichtmlwriter.js +12 -16
  16. package/src/dataprocessor/dataprocessor.js +5 -0
  17. package/src/dataprocessor/htmldataprocessor.js +101 -117
  18. package/src/dataprocessor/htmlwriter.js +1 -18
  19. package/src/dataprocessor/xmldataprocessor.js +117 -138
  20. package/src/dev-utils/model.js +260 -352
  21. package/src/dev-utils/operationreplayer.js +106 -126
  22. package/src/dev-utils/utils.js +34 -51
  23. package/src/dev-utils/view.js +632 -753
  24. package/src/index.js +0 -11
  25. package/src/model/batch.js +111 -127
  26. package/src/model/differ.js +988 -1233
  27. package/src/model/document.js +340 -449
  28. package/src/model/documentfragment.js +327 -364
  29. package/src/model/documentselection.js +996 -1189
  30. package/src/model/element.js +306 -410
  31. package/src/model/history.js +224 -262
  32. package/src/model/item.js +5 -0
  33. package/src/model/liveposition.js +84 -145
  34. package/src/model/liverange.js +108 -185
  35. package/src/model/markercollection.js +379 -480
  36. package/src/model/model.js +883 -1034
  37. package/src/model/node.js +419 -463
  38. package/src/model/nodelist.js +175 -201
  39. package/src/model/operation/attributeoperation.js +153 -182
  40. package/src/model/operation/detachoperation.js +64 -83
  41. package/src/model/operation/insertoperation.js +135 -166
  42. package/src/model/operation/markeroperation.js +114 -140
  43. package/src/model/operation/mergeoperation.js +163 -191
  44. package/src/model/operation/moveoperation.js +157 -187
  45. package/src/model/operation/nooperation.js +28 -38
  46. package/src/model/operation/operation.js +106 -125
  47. package/src/model/operation/operationfactory.js +30 -34
  48. package/src/model/operation/renameoperation.js +109 -135
  49. package/src/model/operation/rootattributeoperation.js +155 -188
  50. package/src/model/operation/splitoperation.js +196 -232
  51. package/src/model/operation/transform.js +1833 -2204
  52. package/src/model/operation/utils.js +140 -204
  53. package/src/model/position.js +899 -1053
  54. package/src/model/range.js +910 -1028
  55. package/src/model/rootelement.js +77 -97
  56. package/src/model/schema.js +1189 -1835
  57. package/src/model/selection.js +745 -862
  58. package/src/model/text.js +90 -114
  59. package/src/model/textproxy.js +204 -240
  60. package/src/model/treewalker.js +316 -397
  61. package/src/model/typecheckable.js +16 -0
  62. package/src/model/utils/autoparagraphing.js +32 -44
  63. package/src/model/utils/deletecontent.js +334 -418
  64. package/src/model/utils/findoptimalinsertionrange.js +25 -36
  65. package/src/model/utils/getselectedcontent.js +96 -118
  66. package/src/model/utils/insertcontent.js +654 -773
  67. package/src/model/utils/insertobject.js +96 -119
  68. package/src/model/utils/modifyselection.js +120 -158
  69. package/src/model/utils/selection-post-fixer.js +153 -201
  70. package/src/model/writer.js +1305 -1474
  71. package/src/view/attributeelement.js +189 -225
  72. package/src/view/containerelement.js +75 -85
  73. package/src/view/document.js +172 -215
  74. package/src/view/documentfragment.js +200 -249
  75. package/src/view/documentselection.js +338 -367
  76. package/src/view/domconverter.js +1371 -1613
  77. package/src/view/downcastwriter.js +1747 -2076
  78. package/src/view/editableelement.js +81 -97
  79. package/src/view/element.js +739 -890
  80. package/src/view/elementdefinition.js +5 -0
  81. package/src/view/emptyelement.js +82 -92
  82. package/src/view/filler.js +35 -50
  83. package/src/view/item.js +5 -0
  84. package/src/view/matcher.js +260 -559
  85. package/src/view/node.js +274 -360
  86. package/src/view/observer/arrowkeysobserver.js +19 -28
  87. package/src/view/observer/bubblingemittermixin.js +120 -263
  88. package/src/view/observer/bubblingeventinfo.js +47 -55
  89. package/src/view/observer/clickobserver.js +7 -13
  90. package/src/view/observer/compositionobserver.js +14 -24
  91. package/src/view/observer/domeventdata.js +57 -67
  92. package/src/view/observer/domeventobserver.js +40 -64
  93. package/src/view/observer/fakeselectionobserver.js +81 -96
  94. package/src/view/observer/focusobserver.js +45 -61
  95. package/src/view/observer/inputobserver.js +7 -13
  96. package/src/view/observer/keyobserver.js +17 -27
  97. package/src/view/observer/mouseobserver.js +7 -14
  98. package/src/view/observer/mutationobserver.js +220 -315
  99. package/src/view/observer/observer.js +81 -102
  100. package/src/view/observer/selectionobserver.js +191 -246
  101. package/src/view/observer/tabobserver.js +23 -36
  102. package/src/view/placeholder.js +128 -173
  103. package/src/view/position.js +350 -401
  104. package/src/view/range.js +453 -513
  105. package/src/view/rawelement.js +85 -112
  106. package/src/view/renderer.js +874 -1014
  107. package/src/view/rooteditableelement.js +80 -90
  108. package/src/view/selection.js +608 -689
  109. package/src/view/styles/background.js +43 -44
  110. package/src/view/styles/border.js +220 -276
  111. package/src/view/styles/margin.js +8 -17
  112. package/src/view/styles/padding.js +8 -16
  113. package/src/view/styles/utils.js +127 -160
  114. package/src/view/stylesmap.js +728 -905
  115. package/src/view/text.js +102 -126
  116. package/src/view/textproxy.js +144 -170
  117. package/src/view/treewalker.js +383 -479
  118. package/src/view/typecheckable.js +19 -0
  119. package/src/view/uielement.js +166 -187
  120. package/src/view/upcastwriter.js +395 -449
  121. package/src/view/view.js +569 -664
  122. package/src/dataprocessor/dataprocessor.jsdoc +0 -64
  123. package/src/model/item.jsdoc +0 -14
  124. package/src/view/elementdefinition.jsdoc +0 -59
  125. package/src/view/item.jsdoc +0 -14
@@ -2,16 +2,14 @@
2
2
  * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
-
5
+ /* eslint-disable new-cap */
6
6
  /**
7
7
  * @module engine/model/markercollection
8
8
  */
9
-
9
+ import TypeCheckable from './typecheckable';
10
10
  import LiveRange from './liverange';
11
- import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
11
+ import EmitterMixin, { Emitter } from '@ckeditor/ckeditor5-utils/src/emittermixin';
12
12
  import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
13
- import mix from '@ckeditor/ckeditor5-utils/src/mix';
14
-
15
13
  /**
16
14
  * The collection of all {@link module:engine/model/markercollection~Marker markers} attached to the document.
17
15
  * It lets you {@link module:engine/model/markercollection~MarkerCollection#get get} markers or track them using
@@ -26,269 +24,218 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
26
24
  *
27
25
  * @see module:engine/model/markercollection~Marker
28
26
  */
29
- export default class MarkerCollection {
30
- /**
31
- * Creates a markers collection.
32
- */
33
- constructor() {
34
- /**
35
- * Stores {@link ~Marker markers} added to the collection.
36
- *
37
- * @private
38
- * @member {Map} #_markers
39
- */
40
- this._markers = new Map();
41
- }
42
-
43
- /**
44
- * Iterable interface.
45
- *
46
- * Iterates over all {@link ~Marker markers} added to the collection.
47
- *
48
- * @returns {Iterable}
49
- */
50
- [ Symbol.iterator ]() {
51
- return this._markers.values();
52
- }
53
-
54
- /**
55
- * Checks if given {@link ~Marker marker} or marker name is in the collection.
56
- *
57
- * @param {String|module:engine/model/markercollection~Marker} markerOrName Name of marker or marker instance to check.
58
- * @returns {Boolean} `true` if marker is in the collection, `false` otherwise.
59
- */
60
- has( markerOrName ) {
61
- const markerName = markerOrName instanceof Marker ? markerOrName.name : markerOrName;
62
- return this._markers.has( markerName );
63
- }
64
-
65
- /**
66
- * Returns {@link ~Marker marker} with given `markerName`.
67
- *
68
- * @param {String} markerName Name of marker to get.
69
- * @returns {module:engine/model/markercollection~Marker|null} Marker with given name or `null` if such marker was
70
- * not added to the collection.
71
- */
72
- get( markerName ) {
73
- return this._markers.get( markerName ) || null;
74
- }
75
-
76
- /**
77
- * Creates and adds a {@link ~Marker marker} to the `MarkerCollection` with given name on given
78
- * {@link module:engine/model/range~Range range}.
79
- *
80
- * If `MarkerCollection` already had a marker with given name (or {@link ~Marker marker} was passed), the marker in
81
- * collection is updated and {@link module:engine/model/markercollection~MarkerCollection#event:update} event is fired
82
- * but only if there was a change (marker range or {@link module:engine/model/markercollection~Marker#managedUsingOperations}
83
- * flag has changed.
84
- *
85
- * @protected
86
- * @fires module:engine/model/markercollection~MarkerCollection#event:update
87
- * @param {String|module:engine/model/markercollection~Marker} markerOrName Name of marker to set or marker instance to update.
88
- * @param {module:engine/model/range~Range} range Marker range.
89
- * @param {Boolean} [managedUsingOperations=false] Specifies whether the marker is managed using operations.
90
- * @param {Boolean} [affectsData=false] Specifies whether the marker affects the data produced by the data pipeline
91
- * (is persisted in the editor's data).
92
- * @returns {module:engine/model/markercollection~Marker} `Marker` instance which was added or updated.
93
- */
94
- _set( markerOrName, range, managedUsingOperations = false, affectsData = false ) {
95
- const markerName = markerOrName instanceof Marker ? markerOrName.name : markerOrName;
96
-
97
- if ( markerName.includes( ',' ) ) {
98
- /**
99
- * Marker name cannot contain the "," character.
100
- *
101
- * @error markercollection-incorrect-marker-name
102
- */
103
- throw new CKEditorError( 'markercollection-incorrect-marker-name', this );
104
- }
105
-
106
- const oldMarker = this._markers.get( markerName );
107
-
108
- if ( oldMarker ) {
109
- const oldMarkerData = oldMarker.getData();
110
-
111
- const oldRange = oldMarker.getRange();
112
- let hasChanged = false;
113
-
114
- if ( !oldRange.isEqual( range ) ) {
115
- oldMarker._attachLiveRange( LiveRange.fromRange( range ) );
116
- hasChanged = true;
117
- }
118
-
119
- if ( managedUsingOperations != oldMarker.managedUsingOperations ) {
120
- oldMarker._managedUsingOperations = managedUsingOperations;
121
- hasChanged = true;
122
- }
123
-
124
- if ( typeof affectsData === 'boolean' && affectsData != oldMarker.affectsData ) {
125
- oldMarker._affectsData = affectsData;
126
- hasChanged = true;
127
- }
128
-
129
- if ( hasChanged ) {
130
- this.fire( 'update:' + markerName, oldMarker, oldRange, range, oldMarkerData );
131
- }
132
-
133
- return oldMarker;
134
- }
135
-
136
- const liveRange = LiveRange.fromRange( range );
137
- const marker = new Marker( markerName, liveRange, managedUsingOperations, affectsData );
138
-
139
- this._markers.set( markerName, marker );
140
- this.fire( 'update:' + markerName, marker, null, range, { ...marker.getData(), range: null } );
141
-
142
- return marker;
143
- }
144
-
145
- /**
146
- * Removes given {@link ~Marker marker} or a marker with given name from the `MarkerCollection`.
147
- *
148
- * @protected
149
- * @fires module:engine/model/markercollection~MarkerCollection#event:update
150
- * @param {String} markerOrName Marker or name of a marker to remove.
151
- * @returns {Boolean} `true` if marker was found and removed, `false` otherwise.
152
- */
153
- _remove( markerOrName ) {
154
- const markerName = markerOrName instanceof Marker ? markerOrName.name : markerOrName;
155
- const oldMarker = this._markers.get( markerName );
156
-
157
- if ( oldMarker ) {
158
- this._markers.delete( markerName );
159
- this.fire( 'update:' + markerName, oldMarker, oldMarker.getRange(), null, oldMarker.getData() );
160
-
161
- this._destroyMarker( oldMarker );
162
-
163
- return true;
164
- }
165
-
166
- return false;
167
- }
168
-
169
- /**
170
- * Fires an {@link module:engine/model/markercollection~MarkerCollection#event:update} event for the given {@link ~Marker marker}
171
- * but does not change the marker. Useful to force {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher downcast
172
- * conversion} for the marker.
173
- *
174
- * @protected
175
- * @fires module:engine/model/markercollection~MarkerCollection#event:update
176
- * @param {String} markerOrName Marker or name of a marker to refresh.
177
- */
178
- _refresh( markerOrName ) {
179
- const markerName = markerOrName instanceof Marker ? markerOrName.name : markerOrName;
180
- const marker = this._markers.get( markerName );
181
-
182
- if ( !marker ) {
183
- /**
184
- * Marker with provided name does not exists.
185
- *
186
- * @error markercollection-refresh-marker-not-exists
187
- */
188
- throw new CKEditorError( 'markercollection-refresh-marker-not-exists', this );
189
- }
190
-
191
- const range = marker.getRange();
192
-
193
- this.fire( 'update:' + markerName, marker, range, range, marker.getData() );
194
- }
195
-
196
- /**
197
- * Returns iterator that iterates over all markers, which ranges contain given {@link module:engine/model/position~Position position}.
198
- *
199
- * @param {module:engine/model/position~Position} position
200
- * @returns {Iterable.<module:engine/model/markercollection~Marker>}
201
- */
202
- * getMarkersAtPosition( position ) {
203
- for ( const marker of this ) {
204
- if ( marker.getRange().containsPosition( position ) ) {
205
- yield marker;
206
- }
207
- }
208
- }
209
-
210
- /**
211
- * Returns iterator that iterates over all markers, which intersects with given {@link module:engine/model/range~Range range}.
212
- *
213
- * @param {module:engine/model/range~Range} range
214
- * @returns {Iterable.<module:engine/model/markercollection~Marker>}
215
- */
216
- * getMarkersIntersectingRange( range ) {
217
- for ( const marker of this ) {
218
- if ( marker.getRange().getIntersection( range ) !== null ) {
219
- yield marker;
220
- }
221
- }
222
- }
223
-
224
- /**
225
- * Destroys marker collection and all markers inside it.
226
- */
227
- destroy() {
228
- for ( const marker of this._markers.values() ) {
229
- this._destroyMarker( marker );
230
- }
231
-
232
- this._markers = null;
233
-
234
- this.stopListening();
235
- }
236
-
237
- /**
238
- * Iterates over all markers that starts with given `prefix`.
239
- *
240
- * const markerFooA = markersCollection.set( 'foo:a', rangeFooA );
241
- * const markerFooB = markersCollection.set( 'foo:b', rangeFooB );
242
- * const markerBarA = markersCollection.set( 'bar:a', rangeBarA );
243
- * const markerFooBarA = markersCollection.set( 'foobar:a', rangeFooBarA );
244
- * Array.from( markersCollection.getMarkersGroup( 'foo' ) ); // [ markerFooA, markerFooB ]
245
- * Array.from( markersCollection.getMarkersGroup( 'a' ) ); // []
246
- *
247
- * @param prefix
248
- * @returns {Iterable.<module:engine/model/markercollection~Marker>}
249
- */
250
- * getMarkersGroup( prefix ) {
251
- for ( const marker of this._markers.values() ) {
252
- if ( marker.name.startsWith( prefix + ':' ) ) {
253
- yield marker;
254
- }
255
- }
256
- }
257
-
258
- /**
259
- * Destroys the marker.
260
- *
261
- * @private
262
- * @param {module:engine/model/markercollection~Marker} marker Marker to destroy.
263
- */
264
- _destroyMarker( marker ) {
265
- marker.stopListening();
266
- marker._detachLiveRange();
267
- }
268
-
269
- /**
270
- * Fired whenever marker is added, updated or removed from `MarkerCollection`.
271
- *
272
- * @event update
273
- * @param {module:engine/model/markercollection~Marker} marker Updated Marker.
274
- * @param {module:engine/model/range~Range|null} oldRange Marker range before the update. When is not defined it
275
- * means that marker is just added.
276
- * @param {module:engine/model/range~Range|null} newRange Marker range after update. When is not defined it
277
- * means that marker is just removed.
278
- * @param {module:engine/model/markercollection~MarkerData} oldMarkerData Data of the marker before the change.
279
- */
27
+ export default class MarkerCollection extends Emitter {
28
+ /**
29
+ * Creates a markers collection.
30
+ */
31
+ constructor() {
32
+ super();
33
+ /**
34
+ * Stores {@link ~Marker markers} added to the collection.
35
+ *
36
+ * @private
37
+ * @member {Map} #_markers
38
+ */
39
+ this._markers = new Map();
40
+ }
41
+ /**
42
+ * Iterable interface.
43
+ *
44
+ * Iterates over all {@link ~Marker markers} added to the collection.
45
+ *
46
+ * @returns {Iterator}
47
+ */
48
+ [Symbol.iterator]() {
49
+ return this._markers.values();
50
+ }
51
+ /**
52
+ * Checks if given {@link ~Marker marker} or marker name is in the collection.
53
+ *
54
+ * @param {String|module:engine/model/markercollection~Marker} markerOrName Name of marker or marker instance to check.
55
+ * @returns {Boolean} `true` if marker is in the collection, `false` otherwise.
56
+ */
57
+ has(markerOrName) {
58
+ const markerName = markerOrName instanceof Marker ? markerOrName.name : markerOrName;
59
+ return this._markers.has(markerName);
60
+ }
61
+ /**
62
+ * Returns {@link ~Marker marker} with given `markerName`.
63
+ *
64
+ * @param {String} markerName Name of marker to get.
65
+ * @returns {module:engine/model/markercollection~Marker|null} Marker with given name or `null` if such marker was
66
+ * not added to the collection.
67
+ */
68
+ get(markerName) {
69
+ return this._markers.get(markerName) || null;
70
+ }
71
+ /**
72
+ * Creates and adds a {@link ~Marker marker} to the `MarkerCollection` with given name on given
73
+ * {@link module:engine/model/range~Range range}.
74
+ *
75
+ * If `MarkerCollection` already had a marker with given name (or {@link ~Marker marker} was passed), the marker in
76
+ * collection is updated and {@link module:engine/model/markercollection~MarkerCollection#event:update} event is fired
77
+ * but only if there was a change (marker range or {@link module:engine/model/markercollection~Marker#managedUsingOperations}
78
+ * flag has changed.
79
+ *
80
+ * @internal
81
+ * @protected
82
+ * @fires module:engine/model/markercollection~MarkerCollection#event:update
83
+ * @param {String|module:engine/model/markercollection~Marker} markerOrName Name of marker to set or marker instance to update.
84
+ * @param {module:engine/model/range~Range} range Marker range.
85
+ * @param {Boolean} [managedUsingOperations=false] Specifies whether the marker is managed using operations.
86
+ * @param {Boolean} [affectsData=false] Specifies whether the marker affects the data produced by the data pipeline
87
+ * (is persisted in the editor's data).
88
+ * @returns {module:engine/model/markercollection~Marker} `Marker` instance which was added or updated.
89
+ */
90
+ _set(markerOrName, range, managedUsingOperations = false, affectsData = false) {
91
+ const markerName = markerOrName instanceof Marker ? markerOrName.name : markerOrName;
92
+ if (markerName.includes(',')) {
93
+ /**
94
+ * Marker name cannot contain the "," character.
95
+ *
96
+ * @error markercollection-incorrect-marker-name
97
+ */
98
+ throw new CKEditorError('markercollection-incorrect-marker-name', this);
99
+ }
100
+ const oldMarker = this._markers.get(markerName);
101
+ if (oldMarker) {
102
+ const oldMarkerData = oldMarker.getData();
103
+ const oldRange = oldMarker.getRange();
104
+ let hasChanged = false;
105
+ if (!oldRange.isEqual(range)) {
106
+ oldMarker._attachLiveRange(LiveRange.fromRange(range));
107
+ hasChanged = true;
108
+ }
109
+ if (managedUsingOperations != oldMarker.managedUsingOperations) {
110
+ oldMarker._managedUsingOperations = managedUsingOperations;
111
+ hasChanged = true;
112
+ }
113
+ if (typeof affectsData === 'boolean' && affectsData != oldMarker.affectsData) {
114
+ oldMarker._affectsData = affectsData;
115
+ hasChanged = true;
116
+ }
117
+ if (hasChanged) {
118
+ this.fire(`update:${markerName}`, oldMarker, oldRange, range, oldMarkerData);
119
+ }
120
+ return oldMarker;
121
+ }
122
+ const liveRange = LiveRange.fromRange(range);
123
+ const marker = new Marker(markerName, liveRange, managedUsingOperations, affectsData);
124
+ this._markers.set(markerName, marker);
125
+ this.fire(`update:${markerName}`, marker, null, range, { ...marker.getData(), range: null });
126
+ return marker;
127
+ }
128
+ /**
129
+ * Removes given {@link ~Marker marker} or a marker with given name from the `MarkerCollection`.
130
+ *
131
+ * @internal
132
+ * @protected
133
+ * @fires module:engine/model/markercollection~MarkerCollection#event:update
134
+ * @param {String|module:engine/model/markercollection~Marker} markerOrName Marker or name of a marker to remove.
135
+ * @returns {Boolean} `true` if marker was found and removed, `false` otherwise.
136
+ */
137
+ _remove(markerOrName) {
138
+ const markerName = markerOrName instanceof Marker ? markerOrName.name : markerOrName;
139
+ const oldMarker = this._markers.get(markerName);
140
+ if (oldMarker) {
141
+ this._markers.delete(markerName);
142
+ this.fire(`update:${markerName}`, oldMarker, oldMarker.getRange(), null, oldMarker.getData());
143
+ this._destroyMarker(oldMarker);
144
+ return true;
145
+ }
146
+ return false;
147
+ }
148
+ /**
149
+ * Fires an {@link module:engine/model/markercollection~MarkerCollection#event:update} event for the given {@link ~Marker marker}
150
+ * but does not change the marker. Useful to force {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher downcast
151
+ * conversion} for the marker.
152
+ *
153
+ * @internal
154
+ * @protected
155
+ * @fires module:engine/model/markercollection~MarkerCollection#event:update
156
+ * @param {String|module:engine/model/markercollection~Marker} markerOrName Marker or name of a marker to refresh.
157
+ */
158
+ _refresh(markerOrName) {
159
+ const markerName = markerOrName instanceof Marker ? markerOrName.name : markerOrName;
160
+ const marker = this._markers.get(markerName);
161
+ if (!marker) {
162
+ /**
163
+ * Marker with provided name does not exists.
164
+ *
165
+ * @error markercollection-refresh-marker-not-exists
166
+ */
167
+ throw new CKEditorError('markercollection-refresh-marker-not-exists', this);
168
+ }
169
+ const range = marker.getRange();
170
+ this.fire(`update:${markerName}`, marker, range, range, marker.getData());
171
+ }
172
+ /**
173
+ * Returns iterator that iterates over all markers, which ranges contain given {@link module:engine/model/position~Position position}.
174
+ *
175
+ * @param {module:engine/model/position~Position} position
176
+ * @returns {Iterable.<module:engine/model/markercollection~Marker>}
177
+ */
178
+ *getMarkersAtPosition(position) {
179
+ for (const marker of this) {
180
+ if (marker.getRange().containsPosition(position)) {
181
+ yield marker;
182
+ }
183
+ }
184
+ }
185
+ /**
186
+ * Returns iterator that iterates over all markers, which intersects with given {@link module:engine/model/range~Range range}.
187
+ *
188
+ * @param {module:engine/model/range~Range} range
189
+ * @returns {Iterable.<module:engine/model/markercollection~Marker>}
190
+ */
191
+ *getMarkersIntersectingRange(range) {
192
+ for (const marker of this) {
193
+ if (marker.getRange().getIntersection(range) !== null) {
194
+ yield marker;
195
+ }
196
+ }
197
+ }
198
+ /**
199
+ * Destroys marker collection and all markers inside it.
200
+ */
201
+ destroy() {
202
+ for (const marker of this._markers.values()) {
203
+ this._destroyMarker(marker);
204
+ }
205
+ this._markers = null;
206
+ this.stopListening();
207
+ }
208
+ /**
209
+ * Iterates over all markers that starts with given `prefix`.
210
+ *
211
+ * const markerFooA = markersCollection.set( 'foo:a', rangeFooA );
212
+ * const markerFooB = markersCollection.set( 'foo:b', rangeFooB );
213
+ * const markerBarA = markersCollection.set( 'bar:a', rangeBarA );
214
+ * const markerFooBarA = markersCollection.set( 'foobar:a', rangeFooBarA );
215
+ * Array.from( markersCollection.getMarkersGroup( 'foo' ) ); // [ markerFooA, markerFooB ]
216
+ * Array.from( markersCollection.getMarkersGroup( 'a' ) ); // []
217
+ *
218
+ * @param prefix
219
+ * @returns {Iterable.<module:engine/model/markercollection~Marker>}
220
+ */
221
+ *getMarkersGroup(prefix) {
222
+ for (const marker of this._markers.values()) {
223
+ if (marker.name.startsWith(prefix + ':')) {
224
+ yield marker;
225
+ }
226
+ }
227
+ }
228
+ /**
229
+ * Destroys the marker.
230
+ *
231
+ * @private
232
+ * @param {module:engine/model/markercollection~Marker} marker Marker to destroy.
233
+ */
234
+ _destroyMarker(marker) {
235
+ marker.stopListening();
236
+ marker._detachLiveRange();
237
+ }
280
238
  }
281
-
282
- mix( MarkerCollection, EmitterMixin );
283
-
284
- /**
285
- * @typedef {Object} module:engine/model/markercollection~MarkerData
286
- *
287
- * @property {module:engine/model/range~Range|null} range Marker range. `null` if the marker was removed.
288
- * @property {Boolean} affectsData A property defining if the marker affects data.
289
- * @property {Boolean} managedUsingOperations A property defining if the marker is managed using operations.
290
- */
291
-
292
239
  /**
293
240
  * `Marker` is a continuous parts of model (like a range), is named and represent some kind of information about marked
294
241
  * part of model document. In contrary to {@link module:engine/model/node~Node nodes}, which are building blocks of
@@ -356,220 +303,172 @@ mix( MarkerCollection, EmitterMixin );
356
303
  *
357
304
  * `Marker` instances are created and destroyed only by {@link ~MarkerCollection MarkerCollection}.
358
305
  */
359
- class Marker {
360
- /**
361
- * Creates a marker instance.
362
- *
363
- * @param {String} name Marker name.
364
- * @param {module:engine/model/liverange~LiveRange} liveRange Range marked by the marker.
365
- * @param {Boolean} managedUsingOperations Specifies whether the marker is managed using operations.
366
- * @param {Boolean} affectsData Specifies whether the marker affects the data produced by the data pipeline
367
- * (is persisted in the editor's data).
368
- */
369
- constructor( name, liveRange, managedUsingOperations, affectsData ) {
370
- /**
371
- * Marker's name.
372
- *
373
- * @readonly
374
- * @type {String}
375
- */
376
- this.name = name;
377
-
378
- /**
379
- * Range marked by the marker.
380
- *
381
- * @protected
382
- * @member {module:engine/model/liverange~LiveRange}
383
- */
384
- this._liveRange = this._attachLiveRange( liveRange );
385
-
386
- /**
387
- * Flag indicates if the marker is managed using operations or not.
388
- *
389
- * @private
390
- * @member {Boolean}
391
- */
392
- this._managedUsingOperations = managedUsingOperations;
393
-
394
- /**
395
- * Specifies whether the marker affects the data produced by the data pipeline
396
- * (is persisted in the editor's data).
397
- *
398
- * @private
399
- * @member {Boolean}
400
- */
401
- this._affectsData = affectsData;
402
- }
403
-
404
- /**
405
- * A value indicating if the marker is managed using operations.
406
- * See {@link ~Marker marker class description} to learn more about marker types.
407
- * See {@link module:engine/model/writer~Writer#addMarker}.
408
- *
409
- * @returns {Boolean}
410
- */
411
- get managedUsingOperations() {
412
- if ( !this._liveRange ) {
413
- throw new CKEditorError( 'marker-destroyed', this );
414
- }
415
-
416
- return this._managedUsingOperations;
417
- }
418
-
419
- /**
420
- * A value indicating if the marker changes the data.
421
- *
422
- * @returns {Boolean}
423
- */
424
- get affectsData() {
425
- if ( !this._liveRange ) {
426
- throw new CKEditorError( 'marker-destroyed', this );
427
- }
428
-
429
- return this._affectsData;
430
- }
431
-
432
- /**
433
- * Returns the marker data (properties defining the marker).
434
- *
435
- * @returns {module:engine/model/markercollection~MarkerData}
436
- */
437
- getData() {
438
- return {
439
- range: this.getRange(),
440
- affectsData: this.affectsData,
441
- managedUsingOperations: this.managedUsingOperations
442
- };
443
- }
444
-
445
- /**
446
- * Returns current marker start position.
447
- *
448
- * @returns {module:engine/model/position~Position}
449
- */
450
- getStart() {
451
- if ( !this._liveRange ) {
452
- throw new CKEditorError( 'marker-destroyed', this );
453
- }
454
-
455
- return this._liveRange.start.clone();
456
- }
457
-
458
- /**
459
- * Returns current marker end position.
460
- *
461
- * @returns {module:engine/model/position~Position}
462
- */
463
- getEnd() {
464
- if ( !this._liveRange ) {
465
- throw new CKEditorError( 'marker-destroyed', this );
466
- }
467
-
468
- return this._liveRange.end.clone();
469
- }
470
-
471
- /**
472
- * Returns a range that represents the current state of the marker.
473
- *
474
- * Keep in mind that returned value is a {@link module:engine/model/range~Range Range}, not a
475
- * {@link module:engine/model/liverange~LiveRange LiveRange}. This means that it is up-to-date and relevant only
476
- * until next model document change. Do not store values returned by this method. Instead, store {@link ~Marker#name}
477
- * and get `Marker` instance from {@link module:engine/model/markercollection~MarkerCollection MarkerCollection} every
478
- * time there is a need to read marker properties. This will guarantee that the marker has not been removed and
479
- * that it's data is up-to-date.
480
- *
481
- * @returns {module:engine/model/range~Range}
482
- */
483
- getRange() {
484
- if ( !this._liveRange ) {
485
- throw new CKEditorError( 'marker-destroyed', this );
486
- }
487
-
488
- return this._liveRange.toRange();
489
- }
490
-
491
- /**
492
- * Checks whether this object is of the given.
493
- *
494
- * marker.is( 'marker' ); // -> true
495
- * marker.is( 'model:marker' ); // -> true
496
- *
497
- * marker.is( 'view:element' ); // -> false
498
- * marker.is( 'documentSelection' ); // -> false
499
- *
500
- * {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.
501
- *
502
- * @param {String} type
503
- * @returns {Boolean}
504
- */
505
- is( type ) {
506
- return type === 'marker' || type === 'model:marker';
507
- }
508
-
509
- /**
510
- * Binds new live range to the marker and detach the old one if is attached.
511
- *
512
- * @protected
513
- * @param {module:engine/model/liverange~LiveRange} liveRange Live range to attach
514
- * @returns {module:engine/model/liverange~LiveRange} Attached live range.
515
- */
516
- _attachLiveRange( liveRange ) {
517
- if ( this._liveRange ) {
518
- this._detachLiveRange();
519
- }
520
-
521
- // Delegating does not work with namespaces. Alternatively, we could delegate all events (using `*`).
522
- liveRange.delegate( 'change:range' ).to( this );
523
- liveRange.delegate( 'change:content' ).to( this );
524
-
525
- this._liveRange = liveRange;
526
-
527
- return liveRange;
528
- }
529
-
530
- /**
531
- * Unbinds and destroys currently attached live range.
532
- *
533
- * @protected
534
- */
535
- _detachLiveRange() {
536
- this._liveRange.stopDelegating( 'change:range', this );
537
- this._liveRange.stopDelegating( 'change:content', this );
538
- this._liveRange.detach();
539
- this._liveRange = null;
540
- }
541
-
542
- /**
543
- * Fired whenever {@link ~Marker#_liveRange marker range} is changed due to changes on {@link module:engine/model/document~Document}.
544
- * This is a delegated {@link module:engine/model/liverange~LiveRange#event:change:range LiveRange change:range event}.
545
- *
546
- * When marker is removed from {@link module:engine/model/markercollection~MarkerCollection MarkerCollection},
547
- * all event listeners listening to it should be removed. It is best to do it on
548
- * {@link module:engine/model/markercollection~MarkerCollection#event:update MarkerCollection update event}.
549
- *
550
- * @see module:engine/model/liverange~LiveRange#event:change:range
551
- * @event change:range
552
- * @param {module:engine/model/range~Range} oldRange
553
- * @param {Object} data
554
- */
555
-
556
- /**
557
- * Fired whenever change on {@link module:engine/model/document~Document} is done inside {@link ~Marker#_liveRange marker range}.
558
- * This is a delegated {@link module:engine/model/liverange~LiveRange#event:change:content LiveRange change:content event}.
559
- *
560
- * When marker is removed from {@link module:engine/model/markercollection~MarkerCollection MarkerCollection},
561
- * all event listeners listening to it should be removed. It is best to do it on
562
- * {@link module:engine/model/markercollection~MarkerCollection#event:update MarkerCollection update event}.
563
- *
564
- * @see module:engine/model/liverange~LiveRange#event:change:content
565
- * @event change:content
566
- * @param {module:engine/model/range~Range} oldRange
567
- * @param {Object} data
568
- */
306
+ class Marker extends EmitterMixin(TypeCheckable) {
307
+ /**
308
+ * Creates a marker instance.
309
+ *
310
+ * @param {String} name Marker name.
311
+ * @param {module:engine/model/liverange~LiveRange} liveRange Range marked by the marker.
312
+ * @param {Boolean} managedUsingOperations Specifies whether the marker is managed using operations.
313
+ * @param {Boolean} affectsData Specifies whether the marker affects the data produced by the data pipeline
314
+ * (is persisted in the editor's data).
315
+ */
316
+ constructor(name, liveRange, managedUsingOperations, affectsData) {
317
+ super();
318
+ /**
319
+ * Marker's name.
320
+ *
321
+ * @readonly
322
+ * @type {String}
323
+ */
324
+ this.name = name;
325
+ /**
326
+ * Range marked by the marker.
327
+ *
328
+ * @protected
329
+ * @member {module:engine/model/liverange~LiveRange}
330
+ */
331
+ this._liveRange = this._attachLiveRange(liveRange);
332
+ /**
333
+ * Flag indicates if the marker is managed using operations or not.
334
+ *
335
+ * @private
336
+ * @member {Boolean}
337
+ */
338
+ this._managedUsingOperations = managedUsingOperations;
339
+ /**
340
+ * Specifies whether the marker affects the data produced by the data pipeline
341
+ * (is persisted in the editor's data).
342
+ *
343
+ * @private
344
+ * @member {Boolean}
345
+ */
346
+ this._affectsData = affectsData;
347
+ }
348
+ /**
349
+ * A value indicating if the marker is managed using operations.
350
+ * See {@link ~Marker marker class description} to learn more about marker types.
351
+ * See {@link module:engine/model/writer~Writer#addMarker}.
352
+ *
353
+ * @returns {Boolean}
354
+ */
355
+ get managedUsingOperations() {
356
+ if (!this._liveRange) {
357
+ throw new CKEditorError('marker-destroyed', this);
358
+ }
359
+ return this._managedUsingOperations;
360
+ }
361
+ /**
362
+ * A value indicating if the marker changes the data.
363
+ *
364
+ * @returns {Boolean}
365
+ */
366
+ get affectsData() {
367
+ if (!this._liveRange) {
368
+ throw new CKEditorError('marker-destroyed', this);
369
+ }
370
+ return this._affectsData;
371
+ }
372
+ /**
373
+ * Returns the marker data (properties defining the marker).
374
+ *
375
+ * @returns {module:engine/model/markercollection~MarkerData}
376
+ */
377
+ getData() {
378
+ return {
379
+ range: this.getRange(),
380
+ affectsData: this.affectsData,
381
+ managedUsingOperations: this.managedUsingOperations
382
+ };
383
+ }
384
+ /**
385
+ * Returns current marker start position.
386
+ *
387
+ * @returns {module:engine/model/position~Position}
388
+ */
389
+ getStart() {
390
+ if (!this._liveRange) {
391
+ throw new CKEditorError('marker-destroyed', this);
392
+ }
393
+ return this._liveRange.start.clone();
394
+ }
395
+ /**
396
+ * Returns current marker end position.
397
+ *
398
+ * @returns {module:engine/model/position~Position}
399
+ */
400
+ getEnd() {
401
+ if (!this._liveRange) {
402
+ throw new CKEditorError('marker-destroyed', this);
403
+ }
404
+ return this._liveRange.end.clone();
405
+ }
406
+ /**
407
+ * Returns a range that represents the current state of the marker.
408
+ *
409
+ * Keep in mind that returned value is a {@link module:engine/model/range~Range Range}, not a
410
+ * {@link module:engine/model/liverange~LiveRange LiveRange}. This means that it is up-to-date and relevant only
411
+ * until next model document change. Do not store values returned by this method. Instead, store {@link ~Marker#name}
412
+ * and get `Marker` instance from {@link module:engine/model/markercollection~MarkerCollection MarkerCollection} every
413
+ * time there is a need to read marker properties. This will guarantee that the marker has not been removed and
414
+ * that it's data is up-to-date.
415
+ *
416
+ * @returns {module:engine/model/range~Range}
417
+ */
418
+ getRange() {
419
+ if (!this._liveRange) {
420
+ throw new CKEditorError('marker-destroyed', this);
421
+ }
422
+ return this._liveRange.toRange();
423
+ }
424
+ /**
425
+ * Binds new live range to the marker and detach the old one if is attached.
426
+ *
427
+ * @internal
428
+ * @protected
429
+ * @param {module:engine/model/liverange~LiveRange} liveRange Live range to attach
430
+ * @returns {module:engine/model/liverange~LiveRange} Attached live range.
431
+ */
432
+ _attachLiveRange(liveRange) {
433
+ if (this._liveRange) {
434
+ this._detachLiveRange();
435
+ }
436
+ // Delegating does not work with namespaces. Alternatively, we could delegate all events (using `*`).
437
+ liveRange.delegate('change:range').to(this);
438
+ liveRange.delegate('change:content').to(this);
439
+ this._liveRange = liveRange;
440
+ return liveRange;
441
+ }
442
+ /**
443
+ * Unbinds and destroys currently attached live range.
444
+ *
445
+ * @internal
446
+ * @protected
447
+ */
448
+ _detachLiveRange() {
449
+ this._liveRange.stopDelegating('change:range', this);
450
+ this._liveRange.stopDelegating('change:content', this);
451
+ this._liveRange.detach();
452
+ this._liveRange = null;
453
+ }
569
454
  }
570
-
571
- mix( Marker, EmitterMixin );
572
-
455
+ /**
456
+ * Checks whether this object is of the given.
457
+ *
458
+ * marker.is( 'marker' ); // -> true
459
+ * marker.is( 'model:marker' ); // -> true
460
+ *
461
+ * marker.is( 'view:element' ); // -> false
462
+ * marker.is( 'documentSelection' ); // -> false
463
+ *
464
+ * {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.
465
+ *
466
+ * @param {String} type
467
+ * @returns {Boolean}
468
+ */
469
+ Marker.prototype.is = function (type) {
470
+ return type === 'marker' || type === 'model:marker';
471
+ };
573
472
  /**
574
473
  * Cannot use a {@link module:engine/model/markercollection~MarkerCollection#destroy destroyed marker} instance.
575
474
  *