@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.
- package/CHANGELOG.md +823 -0
- package/LICENSE.md +4 -0
- package/package.json +32 -25
- package/src/controller/datacontroller.js +467 -561
- package/src/controller/editingcontroller.js +168 -204
- package/src/conversion/conversion.js +541 -565
- package/src/conversion/conversionhelpers.js +24 -28
- package/src/conversion/downcastdispatcher.js +457 -686
- package/src/conversion/downcasthelpers.js +1583 -1965
- package/src/conversion/mapper.js +518 -707
- package/src/conversion/modelconsumable.js +240 -283
- package/src/conversion/upcastdispatcher.js +372 -718
- package/src/conversion/upcasthelpers.js +707 -818
- package/src/conversion/viewconsumable.js +524 -581
- package/src/dataprocessor/basichtmlwriter.js +12 -16
- package/src/dataprocessor/dataprocessor.js +5 -0
- package/src/dataprocessor/htmldataprocessor.js +101 -117
- package/src/dataprocessor/htmlwriter.js +1 -18
- package/src/dataprocessor/xmldataprocessor.js +117 -138
- package/src/dev-utils/model.js +260 -352
- package/src/dev-utils/operationreplayer.js +106 -126
- package/src/dev-utils/utils.js +34 -51
- package/src/dev-utils/view.js +632 -753
- package/src/index.js +0 -11
- package/src/model/batch.js +111 -127
- package/src/model/differ.js +988 -1233
- package/src/model/document.js +340 -449
- package/src/model/documentfragment.js +327 -364
- package/src/model/documentselection.js +996 -1189
- package/src/model/element.js +306 -410
- package/src/model/history.js +224 -262
- package/src/model/item.js +5 -0
- package/src/model/liveposition.js +84 -145
- package/src/model/liverange.js +108 -185
- package/src/model/markercollection.js +379 -480
- package/src/model/model.js +883 -1034
- package/src/model/node.js +419 -463
- package/src/model/nodelist.js +175 -201
- package/src/model/operation/attributeoperation.js +153 -182
- package/src/model/operation/detachoperation.js +64 -83
- package/src/model/operation/insertoperation.js +135 -166
- package/src/model/operation/markeroperation.js +114 -140
- package/src/model/operation/mergeoperation.js +163 -191
- package/src/model/operation/moveoperation.js +157 -187
- package/src/model/operation/nooperation.js +28 -38
- package/src/model/operation/operation.js +106 -125
- package/src/model/operation/operationfactory.js +30 -34
- package/src/model/operation/renameoperation.js +109 -135
- package/src/model/operation/rootattributeoperation.js +155 -188
- package/src/model/operation/splitoperation.js +196 -232
- package/src/model/operation/transform.js +1833 -2204
- package/src/model/operation/utils.js +140 -204
- package/src/model/position.js +899 -1053
- package/src/model/range.js +910 -1028
- package/src/model/rootelement.js +77 -97
- package/src/model/schema.js +1189 -1835
- package/src/model/selection.js +745 -862
- package/src/model/text.js +90 -114
- package/src/model/textproxy.js +204 -240
- package/src/model/treewalker.js +316 -397
- package/src/model/typecheckable.js +16 -0
- package/src/model/utils/autoparagraphing.js +32 -44
- package/src/model/utils/deletecontent.js +334 -418
- package/src/model/utils/findoptimalinsertionrange.js +25 -36
- package/src/model/utils/getselectedcontent.js +96 -118
- package/src/model/utils/insertcontent.js +654 -773
- package/src/model/utils/insertobject.js +96 -119
- package/src/model/utils/modifyselection.js +120 -158
- package/src/model/utils/selection-post-fixer.js +153 -201
- package/src/model/writer.js +1305 -1474
- package/src/view/attributeelement.js +189 -225
- package/src/view/containerelement.js +75 -85
- package/src/view/document.js +172 -215
- package/src/view/documentfragment.js +200 -249
- package/src/view/documentselection.js +338 -367
- package/src/view/domconverter.js +1371 -1613
- package/src/view/downcastwriter.js +1747 -2076
- package/src/view/editableelement.js +81 -97
- package/src/view/element.js +739 -890
- package/src/view/elementdefinition.js +5 -0
- package/src/view/emptyelement.js +82 -92
- package/src/view/filler.js +35 -50
- package/src/view/item.js +5 -0
- package/src/view/matcher.js +260 -559
- package/src/view/node.js +274 -360
- package/src/view/observer/arrowkeysobserver.js +19 -28
- package/src/view/observer/bubblingemittermixin.js +120 -263
- package/src/view/observer/bubblingeventinfo.js +47 -55
- package/src/view/observer/clickobserver.js +7 -13
- package/src/view/observer/compositionobserver.js +14 -24
- package/src/view/observer/domeventdata.js +57 -67
- package/src/view/observer/domeventobserver.js +40 -64
- package/src/view/observer/fakeselectionobserver.js +81 -96
- package/src/view/observer/focusobserver.js +45 -61
- package/src/view/observer/inputobserver.js +7 -13
- package/src/view/observer/keyobserver.js +17 -27
- package/src/view/observer/mouseobserver.js +7 -14
- package/src/view/observer/mutationobserver.js +220 -315
- package/src/view/observer/observer.js +81 -102
- package/src/view/observer/selectionobserver.js +191 -246
- package/src/view/observer/tabobserver.js +23 -36
- package/src/view/placeholder.js +128 -173
- package/src/view/position.js +350 -401
- package/src/view/range.js +453 -513
- package/src/view/rawelement.js +85 -112
- package/src/view/renderer.js +874 -1014
- package/src/view/rooteditableelement.js +80 -90
- package/src/view/selection.js +608 -689
- package/src/view/styles/background.js +43 -44
- package/src/view/styles/border.js +220 -276
- package/src/view/styles/margin.js +8 -17
- package/src/view/styles/padding.js +8 -16
- package/src/view/styles/utils.js +127 -160
- package/src/view/stylesmap.js +728 -905
- package/src/view/text.js +102 -126
- package/src/view/textproxy.js +144 -170
- package/src/view/treewalker.js +383 -479
- package/src/view/typecheckable.js +19 -0
- package/src/view/uielement.js +166 -187
- package/src/view/upcastwriter.js +395 -449
- package/src/view/view.js +569 -664
- package/src/dataprocessor/dataprocessor.jsdoc +0 -64
- package/src/model/item.jsdoc +0 -14
- package/src/view/elementdefinition.jsdoc +0 -59
- package/src/view/item.jsdoc +0 -14
|
@@ -2,17 +2,12 @@
|
|
|
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
|
-
|
|
6
5
|
/**
|
|
7
6
|
* @module engine/conversion/downcastdispatcher
|
|
8
7
|
*/
|
|
9
|
-
|
|
10
8
|
import Consumable from './modelconsumable';
|
|
11
9
|
import Range from '../model/range';
|
|
12
|
-
|
|
13
|
-
import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
|
|
14
|
-
import mix from '@ckeditor/ckeditor5-utils/src/mix';
|
|
15
|
-
|
|
10
|
+
import { Emitter } from '@ckeditor/ckeditor5-utils/src/emittermixin';
|
|
16
11
|
/**
|
|
17
12
|
* The downcast dispatcher is a central point of downcasting (conversion from the model to the view), which is a process of reacting
|
|
18
13
|
* to changes in the model and firing a set of events. The callbacks listening to these events are called converters. The
|
|
@@ -102,641 +97,441 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
|
|
|
102
97
|
* conversionApi.writer.insert( viewPosition, viewElement );
|
|
103
98
|
* } );
|
|
104
99
|
*/
|
|
105
|
-
export default class DowncastDispatcher {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
const eventsFiredForItem = eventsFiredForConversion.get( itemKey );
|
|
540
|
-
|
|
541
|
-
if ( !eventsFiredForItem ) {
|
|
542
|
-
eventsFiredForConversion.set( itemKey, new Set( [ eventName ] ) );
|
|
543
|
-
} else if ( !eventsFiredForItem.has( eventName ) ) {
|
|
544
|
-
eventsFiredForItem.add( eventName );
|
|
545
|
-
} else {
|
|
546
|
-
return;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
this.fire( eventName, data, conversionApi );
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
/**
|
|
553
|
-
* Fires not already fired events for setting attributes on just inserted item.
|
|
554
|
-
*
|
|
555
|
-
* @private
|
|
556
|
-
* @param {module:engine/model/item~Item} item The model item to convert attributes for.
|
|
557
|
-
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi The conversion API object.
|
|
558
|
-
*/
|
|
559
|
-
_testAndFireAddAttributes( item, conversionApi ) {
|
|
560
|
-
const data = {
|
|
561
|
-
item,
|
|
562
|
-
range: Range._createOn( item )
|
|
563
|
-
};
|
|
564
|
-
|
|
565
|
-
for ( const key of data.item.getAttributeKeys() ) {
|
|
566
|
-
data.attributeKey = key;
|
|
567
|
-
data.attributeOldValue = null;
|
|
568
|
-
data.attributeNewValue = data.item.getAttribute( key );
|
|
569
|
-
|
|
570
|
-
this._testAndFire( `attribute:${ key }`, data, conversionApi );
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
/**
|
|
575
|
-
* Builds an instance of the {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi} from a template and a given
|
|
576
|
-
* {@link module:engine/view/downcastwriter~DowncastWriter `DowncastWriter`} and options object.
|
|
577
|
-
*
|
|
578
|
-
* @private
|
|
579
|
-
* @param {module:engine/view/downcastwriter~DowncastWriter} writer View writer that should be used to modify the view document.
|
|
580
|
-
* @param {Set.<module:engine/model/element~Element>} [refreshedItems] A set of model elements that should not reuse their
|
|
581
|
-
* previous view representations.
|
|
582
|
-
* @param {Object} [options] Optional options passed to `convertionApi.options`.
|
|
583
|
-
* @return {module:engine/conversion/downcastdispatcher~DowncastConversionApi} The conversion API object.
|
|
584
|
-
*/
|
|
585
|
-
_createConversionApi( writer, refreshedItems = new Set(), options = {} ) {
|
|
586
|
-
const conversionApi = {
|
|
587
|
-
...this._conversionApi,
|
|
588
|
-
consumable: new Consumable(),
|
|
589
|
-
writer,
|
|
590
|
-
options,
|
|
591
|
-
convertItem: item => this._convertInsert( Range._createOn( item ), conversionApi ),
|
|
592
|
-
convertChildren: element => this._convertInsert( Range._createIn( element ), conversionApi, { doNotAddConsumables: true } ),
|
|
593
|
-
convertAttributes: item => this._testAndFireAddAttributes( item, conversionApi ),
|
|
594
|
-
canReuseView: viewElement => !refreshedItems.has( conversionApi.mapper.toModelElement( viewElement ) )
|
|
595
|
-
};
|
|
596
|
-
|
|
597
|
-
this._firedEventsMap.set( conversionApi, new Map() );
|
|
598
|
-
|
|
599
|
-
return conversionApi;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
/**
|
|
603
|
-
* Fired to enable reducing (transforming) changes buffered in the {@link module:engine/model/differ~Differ `Differ`} before
|
|
604
|
-
* {@link #convertChanges `convertChanges()`} will fire any conversion events.
|
|
605
|
-
*
|
|
606
|
-
* For instance, a feature can replace selected {@link module:engine/model/differ~DiffItem `DiffItem`}s with a `reinsert` entry
|
|
607
|
-
* to trigger reconversion of an element when e.g. its attribute has changes.
|
|
608
|
-
* The {@link module:engine/conversion/downcasthelpers~DowncastHelpers#elementToStructure
|
|
609
|
-
* `DowncastHelpers.elementToStructure()`} helper is using this event to trigger reconversion of an element when the element,
|
|
610
|
-
* its attributes or direct children changed.
|
|
611
|
-
*
|
|
612
|
-
* @param {Object} data
|
|
613
|
-
* @param {Iterable.<module:engine/model/differ~DiffItem>} data.changes A buffered changes to get reduced.
|
|
614
|
-
* @event reduceChanges
|
|
615
|
-
*/
|
|
616
|
-
|
|
617
|
-
/**
|
|
618
|
-
* Fired for inserted nodes.
|
|
619
|
-
*
|
|
620
|
-
* `insert` is a namespace for a class of events. Names of actually called events follow this pattern:
|
|
621
|
-
* `insert:name`. `name` is either `'$text'`, when {@link module:engine/model/text~Text a text node} has been inserted,
|
|
622
|
-
* or {@link module:engine/model/element~Element#name name} of inserted element.
|
|
623
|
-
*
|
|
624
|
-
* This way, the listeners can either listen to a general `insert` event or specific event (for example `insert:paragraph`).
|
|
625
|
-
*
|
|
626
|
-
* @event insert
|
|
627
|
-
* @param {Object} data Additional information about the change.
|
|
628
|
-
* @param {module:engine/model/item~Item} data.item The inserted item.
|
|
629
|
-
* @param {module:engine/model/range~Range} data.range Range spanning over inserted item.
|
|
630
|
-
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi Conversion interface
|
|
631
|
-
* to be used by callback, passed in the `DowncastDispatcher` constructor.
|
|
632
|
-
*/
|
|
633
|
-
|
|
634
|
-
/**
|
|
635
|
-
* Fired for removed nodes.
|
|
636
|
-
*
|
|
637
|
-
* `remove` is a namespace for a class of events. Names of actually called events follow this pattern:
|
|
638
|
-
* `remove:name`. `name` is either `'$text'`, when a {@link module:engine/model/text~Text a text node} has been removed,
|
|
639
|
-
* or the {@link module:engine/model/element~Element#name name} of removed element.
|
|
640
|
-
*
|
|
641
|
-
* This way, listeners can either listen to a general `remove` event or specific event (for example `remove:paragraph`).
|
|
642
|
-
*
|
|
643
|
-
* @event remove
|
|
644
|
-
* @param {Object} data Additional information about the change.
|
|
645
|
-
* @param {module:engine/model/position~Position} data.position Position from which the node has been removed.
|
|
646
|
-
* @param {Number} data.length Offset size of the removed node.
|
|
647
|
-
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi Conversion interface
|
|
648
|
-
* to be used by callback, passed in `DowncastDispatcher` constructor.
|
|
649
|
-
*/
|
|
650
|
-
|
|
651
|
-
/**
|
|
652
|
-
* Fired in the following cases:
|
|
653
|
-
*
|
|
654
|
-
* * when an attribute has been added, changed, or removed from a node,
|
|
655
|
-
* * when a node with an attribute is inserted,
|
|
656
|
-
* * when a collapsed model selection attribute is converted.
|
|
657
|
-
*
|
|
658
|
-
* `attribute` is a namespace for a class of events. Names of actually called events follow this pattern:
|
|
659
|
-
* `attribute:attributeKey:name`. `attributeKey` is the key of added/changed/removed attribute.
|
|
660
|
-
* `name` is either `'$text'` if change was on {@link module:engine/model/text~Text a text node},
|
|
661
|
-
* or the {@link module:engine/model/element~Element#name name} of element which attribute has changed.
|
|
662
|
-
*
|
|
663
|
-
* This way listeners can either listen to a general `attribute:bold` event or specific event (for example `attribute:src:imageBlock`).
|
|
664
|
-
*
|
|
665
|
-
* @event attribute
|
|
666
|
-
* @param {Object} data Additional information about the change.
|
|
667
|
-
* @param {module:engine/model/item~Item|module:engine/model/documentselection~DocumentSelection} data.item Changed item
|
|
668
|
-
* or converted selection.
|
|
669
|
-
* @param {module:engine/model/range~Range} data.range Range spanning over changed item or selection range.
|
|
670
|
-
* @param {String} data.attributeKey Attribute key.
|
|
671
|
-
* @param {*} data.attributeOldValue Attribute value before the change. This is `null` when selection attribute is converted.
|
|
672
|
-
* @param {*} data.attributeNewValue New attribute value.
|
|
673
|
-
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi Conversion interface
|
|
674
|
-
* to be used by callback, passed in `DowncastDispatcher` constructor.
|
|
675
|
-
*/
|
|
676
|
-
|
|
677
|
-
/**
|
|
678
|
-
* Fired for {@link module:engine/model/selection~Selection selection} changes.
|
|
679
|
-
*
|
|
680
|
-
* @event selection
|
|
681
|
-
* @param {module:engine/model/selection~Selection} selection Selection that is converted.
|
|
682
|
-
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi Conversion interface
|
|
683
|
-
* to be used by callback, passed in `DowncastDispatcher` constructor.
|
|
684
|
-
*/
|
|
685
|
-
|
|
686
|
-
/**
|
|
687
|
-
* Fired when a new marker is added to the model. Also fired when a collapsed model selection that is inside a marker is converted.
|
|
688
|
-
*
|
|
689
|
-
* `addMarker` is a namespace for a class of events. Names of actually called events follow this pattern:
|
|
690
|
-
* `addMarker:markerName`. By specifying certain marker names, you can make the events even more gradual. For example,
|
|
691
|
-
* if markers are named `foo:abc`, `foo:bar`, then it is possible to listen to `addMarker:foo` or `addMarker:foo:abc` and
|
|
692
|
-
* `addMarker:foo:bar` events.
|
|
693
|
-
*
|
|
694
|
-
* If the marker range is not collapsed:
|
|
695
|
-
*
|
|
696
|
-
* * the event is fired for each item in the marker range one by one,
|
|
697
|
-
* * `conversionApi.consumable` includes each item of the marker range and the consumable value is same as the event name.
|
|
698
|
-
*
|
|
699
|
-
* If the marker range is collapsed:
|
|
700
|
-
*
|
|
701
|
-
* * there is only one event,
|
|
702
|
-
* * `conversionApi.consumable` includes marker range with the event name.
|
|
703
|
-
*
|
|
704
|
-
* If the selection inside a marker is converted:
|
|
705
|
-
*
|
|
706
|
-
* * there is only one event,
|
|
707
|
-
* * `conversionApi.consumable` includes the selection instance with the event name.
|
|
708
|
-
*
|
|
709
|
-
* @event addMarker
|
|
710
|
-
* @param {Object} data Additional information about the change.
|
|
711
|
-
* @param {module:engine/model/item~Item|module:engine/model/selection~Selection} data.item Item inside the new marker or
|
|
712
|
-
* the selection that is being converted.
|
|
713
|
-
* @param {module:engine/model/range~Range} [data.range] Range spanning over converted item. Available only in marker conversion, if
|
|
714
|
-
* the marker range was not collapsed.
|
|
715
|
-
* @param {module:engine/model/range~Range} data.markerRange Marker range.
|
|
716
|
-
* @param {String} data.markerName Marker name.
|
|
717
|
-
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi Conversion interface
|
|
718
|
-
* to be used by callback, passed in `DowncastDispatcher` constructor.
|
|
719
|
-
*/
|
|
720
|
-
|
|
721
|
-
/**
|
|
722
|
-
* Fired when a marker is removed from the model.
|
|
723
|
-
*
|
|
724
|
-
* `removeMarker` is a namespace for a class of events. Names of actually called events follow this pattern:
|
|
725
|
-
* `removeMarker:markerName`. By specifying certain marker names, you can make the events even more gradual. For example,
|
|
726
|
-
* if markers are named `foo:abc`, `foo:bar`, then it is possible to listen to `removeMarker:foo` or `removeMarker:foo:abc` and
|
|
727
|
-
* `removeMarker:foo:bar` events.
|
|
728
|
-
*
|
|
729
|
-
* @event removeMarker
|
|
730
|
-
* @param {Object} data Additional information about the change.
|
|
731
|
-
* @param {module:engine/model/range~Range} data.markerRange Marker range.
|
|
732
|
-
* @param {String} data.markerName Marker name.
|
|
733
|
-
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi Conversion interface
|
|
734
|
-
* to be used by callback, passed in `DowncastDispatcher` constructor.
|
|
735
|
-
*/
|
|
100
|
+
export default class DowncastDispatcher extends Emitter {
|
|
101
|
+
/**
|
|
102
|
+
* Creates a downcast dispatcher instance.
|
|
103
|
+
*
|
|
104
|
+
* @see module:engine/conversion/downcastdispatcher~DowncastConversionApi
|
|
105
|
+
* @param {Object} conversionApi Additional properties for an interface that will be passed to events fired
|
|
106
|
+
* by the downcast dispatcher.
|
|
107
|
+
*/
|
|
108
|
+
constructor(conversionApi) {
|
|
109
|
+
super();
|
|
110
|
+
/**
|
|
111
|
+
* A template for an interface passed by the dispatcher to the event callbacks.
|
|
112
|
+
*
|
|
113
|
+
* @protected
|
|
114
|
+
* @member {module:engine/conversion/downcastdispatcher~DowncastConversionApi}
|
|
115
|
+
*/
|
|
116
|
+
this._conversionApi = { dispatcher: this, ...conversionApi };
|
|
117
|
+
/**
|
|
118
|
+
* A map of already fired events for a given `ModelConsumable`.
|
|
119
|
+
*
|
|
120
|
+
* @private
|
|
121
|
+
* @member {WeakMap.<module:engine/conversion/downcastdispatcher~DowncastConversionApi,Map>}
|
|
122
|
+
*/
|
|
123
|
+
this._firedEventsMap = new WeakMap();
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Converts changes buffered in the given {@link module:engine/model/differ~Differ model differ}
|
|
127
|
+
* and fires conversion events based on it.
|
|
128
|
+
*
|
|
129
|
+
* @fires insert
|
|
130
|
+
* @fires remove
|
|
131
|
+
* @fires attribute
|
|
132
|
+
* @fires addMarker
|
|
133
|
+
* @fires removeMarker
|
|
134
|
+
* @fires reduceChanges
|
|
135
|
+
* @param {module:engine/model/differ~Differ} differ The differ object with buffered changes.
|
|
136
|
+
* @param {module:engine/model/markercollection~MarkerCollection} markers Markers related to the model fragment to convert.
|
|
137
|
+
* @param {module:engine/view/downcastwriter~DowncastWriter} writer The view writer that should be used to modify the view document.
|
|
138
|
+
*/
|
|
139
|
+
convertChanges(differ, markers, writer) {
|
|
140
|
+
const conversionApi = this._createConversionApi(writer, differ.getRefreshedItems());
|
|
141
|
+
// Before the view is updated, remove markers which have changed.
|
|
142
|
+
for (const change of differ.getMarkersToRemove()) {
|
|
143
|
+
this._convertMarkerRemove(change.name, change.range, conversionApi);
|
|
144
|
+
}
|
|
145
|
+
// Let features modify the change list (for example to allow reconversion).
|
|
146
|
+
const changes = this._reduceChanges(differ.getChanges());
|
|
147
|
+
// Convert changes that happened on model tree.
|
|
148
|
+
for (const entry of changes) {
|
|
149
|
+
if (entry.type === 'insert') {
|
|
150
|
+
this._convertInsert(Range._createFromPositionAndShift(entry.position, entry.length), conversionApi);
|
|
151
|
+
}
|
|
152
|
+
else if (entry.type === 'reinsert') {
|
|
153
|
+
this._convertReinsert(Range._createFromPositionAndShift(entry.position, entry.length), conversionApi);
|
|
154
|
+
}
|
|
155
|
+
else if (entry.type === 'remove') {
|
|
156
|
+
this._convertRemove(entry.position, entry.length, entry.name, conversionApi);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
// Defaults to 'attribute' change.
|
|
160
|
+
this._convertAttribute(entry.range, entry.attributeKey, entry.attributeOldValue, entry.attributeNewValue, conversionApi);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
for (const markerName of conversionApi.mapper.flushUnboundMarkerNames()) {
|
|
164
|
+
const markerRange = markers.get(markerName).getRange();
|
|
165
|
+
this._convertMarkerRemove(markerName, markerRange, conversionApi);
|
|
166
|
+
this._convertMarkerAdd(markerName, markerRange, conversionApi);
|
|
167
|
+
}
|
|
168
|
+
// After the view is updated, convert markers which have changed.
|
|
169
|
+
for (const change of differ.getMarkersToAdd()) {
|
|
170
|
+
this._convertMarkerAdd(change.name, change.range, conversionApi);
|
|
171
|
+
}
|
|
172
|
+
// Remove mappings for all removed view elements.
|
|
173
|
+
conversionApi.mapper.flushDeferredBindings();
|
|
174
|
+
// Verify if all insert consumables were consumed.
|
|
175
|
+
conversionApi.consumable.verifyAllConsumed('insert');
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Starts a conversion of a model range and the provided markers.
|
|
179
|
+
*
|
|
180
|
+
* @fires insert
|
|
181
|
+
* @fires attribute
|
|
182
|
+
* @fires addMarker
|
|
183
|
+
* @param {module:engine/model/range~Range} range The inserted range.
|
|
184
|
+
* @param {Map<String,module:engine/model/range~Range>} markers The map of markers that should be down-casted.
|
|
185
|
+
* @param {module:engine/view/downcastwriter~DowncastWriter} writer The view writer that should be used to modify the view document.
|
|
186
|
+
* @param {Object} [options] Optional options object passed to `convertionApi.options`.
|
|
187
|
+
*/
|
|
188
|
+
convert(range, markers, writer, options = {}) {
|
|
189
|
+
const conversionApi = this._createConversionApi(writer, undefined, options);
|
|
190
|
+
this._convertInsert(range, conversionApi);
|
|
191
|
+
for (const [name, range] of markers) {
|
|
192
|
+
this._convertMarkerAdd(name, range, conversionApi);
|
|
193
|
+
}
|
|
194
|
+
// Verify if all insert consumables were consumed.
|
|
195
|
+
conversionApi.consumable.verifyAllConsumed('insert');
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Starts the model selection conversion.
|
|
199
|
+
*
|
|
200
|
+
* Fires events for a given {@link module:engine/model/selection~Selection selection} to start the selection conversion.
|
|
201
|
+
*
|
|
202
|
+
* @fires selection
|
|
203
|
+
* @fires addMarker
|
|
204
|
+
* @fires attribute
|
|
205
|
+
* @param {module:engine/model/selection~Selection} selection The selection to convert.
|
|
206
|
+
* @param {module:engine/model/markercollection~MarkerCollection} markers Markers connected with the converted model.
|
|
207
|
+
* @param {module:engine/view/downcastwriter~DowncastWriter} writer View writer that should be used to modify the view document.
|
|
208
|
+
*/
|
|
209
|
+
convertSelection(selection, markers, writer) {
|
|
210
|
+
const markersAtSelection = Array.from(markers.getMarkersAtPosition(selection.getFirstPosition()));
|
|
211
|
+
const conversionApi = this._createConversionApi(writer);
|
|
212
|
+
this._addConsumablesForSelection(conversionApi.consumable, selection, markersAtSelection);
|
|
213
|
+
this.fire('selection', { selection }, conversionApi);
|
|
214
|
+
if (!selection.isCollapsed) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
for (const marker of markersAtSelection) {
|
|
218
|
+
const markerRange = marker.getRange();
|
|
219
|
+
if (!shouldMarkerChangeBeConverted(selection.getFirstPosition(), marker, conversionApi.mapper)) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
const data = {
|
|
223
|
+
item: selection,
|
|
224
|
+
markerName: marker.name,
|
|
225
|
+
markerRange
|
|
226
|
+
};
|
|
227
|
+
if (conversionApi.consumable.test(selection, 'addMarker:' + marker.name)) {
|
|
228
|
+
this.fire(`addMarker:${marker.name}`, data, conversionApi);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
for (const key of selection.getAttributeKeys()) {
|
|
232
|
+
const data = {
|
|
233
|
+
item: selection,
|
|
234
|
+
range: selection.getFirstRange(),
|
|
235
|
+
attributeKey: key,
|
|
236
|
+
attributeOldValue: null,
|
|
237
|
+
attributeNewValue: selection.getAttribute(key)
|
|
238
|
+
};
|
|
239
|
+
// Do not fire event if the attribute has been consumed.
|
|
240
|
+
if (conversionApi.consumable.test(selection, 'attribute:' + data.attributeKey)) {
|
|
241
|
+
this.fire(`attribute:${data.attributeKey}:$text`, data, conversionApi);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Fires insertion conversion of a range of nodes.
|
|
247
|
+
*
|
|
248
|
+
* For each node in the range, {@link #event:insert `insert` event is fired}. For each attribute on each node,
|
|
249
|
+
* {@link #event:attribute `attribute` event is fired}.
|
|
250
|
+
*
|
|
251
|
+
* @protected
|
|
252
|
+
* @fires insert
|
|
253
|
+
* @fires attribute
|
|
254
|
+
* @param {module:engine/model/range~Range} range The inserted range.
|
|
255
|
+
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi The conversion API object.
|
|
256
|
+
* @param {Object} [options]
|
|
257
|
+
* @param {Boolean} [options.doNotAddConsumables=false] Whether the ModelConsumable should not get populated
|
|
258
|
+
* for items in the provided range.
|
|
259
|
+
*/
|
|
260
|
+
_convertInsert(range, conversionApi, options = {}) {
|
|
261
|
+
if (!options.doNotAddConsumables) {
|
|
262
|
+
// Collect a list of things that can be consumed, consisting of nodes and their attributes.
|
|
263
|
+
this._addConsumablesForInsert(conversionApi.consumable, Array.from(range));
|
|
264
|
+
}
|
|
265
|
+
// Fire a separate insert event for each node and text fragment contained in the range.
|
|
266
|
+
for (const data of Array.from(range.getWalker({ shallow: true })).map(walkerValueToEventData)) {
|
|
267
|
+
this._testAndFire('insert', data, conversionApi);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Fires conversion of a single node removal. Fires {@link #event:remove remove event} with provided data.
|
|
272
|
+
*
|
|
273
|
+
* @protected
|
|
274
|
+
* @param {module:engine/model/position~Position} position Position from which node was removed.
|
|
275
|
+
* @param {Number} length Offset size of removed node.
|
|
276
|
+
* @param {String} name Name of removed node.
|
|
277
|
+
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi The conversion API object.
|
|
278
|
+
*/
|
|
279
|
+
_convertRemove(position, length, name, conversionApi) {
|
|
280
|
+
this.fire(`remove:${name}`, { position, length }, conversionApi);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Starts a conversion of an attribute change on a given `range`.
|
|
284
|
+
*
|
|
285
|
+
* For each node in the given `range`, {@link #event:attribute attribute event} is fired with the passed data.
|
|
286
|
+
*
|
|
287
|
+
* @protected
|
|
288
|
+
* @fires attribute
|
|
289
|
+
* @param {module:engine/model/range~Range} range Changed range.
|
|
290
|
+
* @param {String} key Key of the attribute that has changed.
|
|
291
|
+
* @param {*} oldValue Attribute value before the change or `null` if the attribute has not been set before.
|
|
292
|
+
* @param {*} newValue New attribute value or `null` if the attribute has been removed.
|
|
293
|
+
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi The conversion API object.
|
|
294
|
+
*/
|
|
295
|
+
_convertAttribute(range, key, oldValue, newValue, conversionApi) {
|
|
296
|
+
// Create a list with attributes to consume.
|
|
297
|
+
this._addConsumablesForRange(conversionApi.consumable, range, `attribute:${key}`);
|
|
298
|
+
// Create a separate attribute event for each node in the range.
|
|
299
|
+
for (const value of range) {
|
|
300
|
+
const data = {
|
|
301
|
+
item: value.item,
|
|
302
|
+
range: Range._createFromPositionAndShift(value.previousPosition, value.length),
|
|
303
|
+
attributeKey: key,
|
|
304
|
+
attributeOldValue: oldValue,
|
|
305
|
+
attributeNewValue: newValue
|
|
306
|
+
};
|
|
307
|
+
this._testAndFire(`attribute:${key}`, data, conversionApi);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Fires re-insertion conversion (with a `reconversion` flag passed to `insert` events)
|
|
312
|
+
* of a range of elements (only elements on the range depth, without children).
|
|
313
|
+
*
|
|
314
|
+
* For each node in the range on its depth (without children), {@link #event:insert `insert` event} is fired.
|
|
315
|
+
* For each attribute on each node, {@link #event:attribute `attribute` event} is fired.
|
|
316
|
+
*
|
|
317
|
+
* @protected
|
|
318
|
+
* @fires insert
|
|
319
|
+
* @fires attribute
|
|
320
|
+
* @param {module:engine/model/range~Range} range The range to reinsert.
|
|
321
|
+
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi The conversion API object.
|
|
322
|
+
*/
|
|
323
|
+
_convertReinsert(range, conversionApi) {
|
|
324
|
+
// Convert the elements - without converting children.
|
|
325
|
+
const walkerValues = Array.from(range.getWalker({ shallow: true }));
|
|
326
|
+
// Collect a list of things that can be consumed, consisting of nodes and their attributes.
|
|
327
|
+
this._addConsumablesForInsert(conversionApi.consumable, walkerValues);
|
|
328
|
+
// Fire a separate insert event for each node and text fragment contained shallowly in the range.
|
|
329
|
+
for (const data of walkerValues.map(walkerValueToEventData)) {
|
|
330
|
+
this._testAndFire('insert', { ...data, reconversion: true }, conversionApi);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Converts the added marker. Fires the {@link #event:addMarker `addMarker`} event for each item
|
|
335
|
+
* in the marker's range. If the range is collapsed, a single event is dispatched. See the event description for more details.
|
|
336
|
+
*
|
|
337
|
+
* @protected
|
|
338
|
+
* @fires addMarker
|
|
339
|
+
* @param {String} markerName Marker name.
|
|
340
|
+
* @param {module:engine/model/range~Range} markerRange The marker range.
|
|
341
|
+
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi The conversion API object.
|
|
342
|
+
*/
|
|
343
|
+
_convertMarkerAdd(markerName, markerRange, conversionApi) {
|
|
344
|
+
// Do not convert if range is in graveyard.
|
|
345
|
+
if (markerRange.root.rootName == '$graveyard') {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
// In markers' case, event name == consumable name.
|
|
349
|
+
const eventName = `addMarker:${markerName}`;
|
|
350
|
+
//
|
|
351
|
+
// First, fire an event for the whole marker.
|
|
352
|
+
//
|
|
353
|
+
conversionApi.consumable.add(markerRange, eventName);
|
|
354
|
+
this.fire(eventName, { markerName, markerRange }, conversionApi);
|
|
355
|
+
//
|
|
356
|
+
// Do not fire events for each item inside the range if the range got consumed.
|
|
357
|
+
// Also consume the whole marker consumable if it wasn't consumed.
|
|
358
|
+
//
|
|
359
|
+
if (!conversionApi.consumable.consume(markerRange, eventName)) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
//
|
|
363
|
+
// Then, fire an event for each item inside the marker range.
|
|
364
|
+
//
|
|
365
|
+
this._addConsumablesForRange(conversionApi.consumable, markerRange, eventName);
|
|
366
|
+
for (const item of markerRange.getItems()) {
|
|
367
|
+
// Do not fire event for already consumed items.
|
|
368
|
+
if (!conversionApi.consumable.test(item, eventName)) {
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
const data = { item, range: Range._createOn(item), markerName, markerRange };
|
|
372
|
+
this.fire(eventName, data, conversionApi);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Fires the conversion of the marker removal. Fires the {@link #event:removeMarker `removeMarker`} event with the provided data.
|
|
377
|
+
*
|
|
378
|
+
* @protected
|
|
379
|
+
* @fires removeMarker
|
|
380
|
+
* @param {String} markerName Marker name.
|
|
381
|
+
* @param {module:engine/model/range~Range} markerRange The marker range.
|
|
382
|
+
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi The conversion API object.
|
|
383
|
+
*/
|
|
384
|
+
_convertMarkerRemove(markerName, markerRange, conversionApi) {
|
|
385
|
+
// Do not convert if range is in graveyard.
|
|
386
|
+
if (markerRange.root.rootName == '$graveyard') {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
this.fire(`removeMarker:${markerName}`, { markerName, markerRange }, conversionApi);
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Fires the reduction of changes buffered in the {@link module:engine/model/differ~Differ `Differ`}.
|
|
393
|
+
*
|
|
394
|
+
* Features can replace selected {@link module:engine/model/differ~DiffItem `DiffItem`}s with `reinsert` entries to trigger
|
|
395
|
+
* reconversion. The {@link module:engine/conversion/downcasthelpers~DowncastHelpers#elementToStructure
|
|
396
|
+
* `DowncastHelpers.elementToStructure()`} is using this event to trigger reconversion.
|
|
397
|
+
*
|
|
398
|
+
* @private
|
|
399
|
+
* @fires reduceChanges
|
|
400
|
+
* @param {Iterable.<module:engine/model/differ~DiffItem>} changes
|
|
401
|
+
* @returns {Iterable.<module:engine/model/differ~DiffItem>}
|
|
402
|
+
*/
|
|
403
|
+
_reduceChanges(changes) {
|
|
404
|
+
const data = { changes };
|
|
405
|
+
this.fire('reduceChanges', data);
|
|
406
|
+
return data.changes;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Populates provided {@link module:engine/conversion/modelconsumable~ModelConsumable} with values to consume from a given range,
|
|
410
|
+
* assuming that the range has just been inserted to the model.
|
|
411
|
+
*
|
|
412
|
+
* @private
|
|
413
|
+
* @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable The consumable.
|
|
414
|
+
* @param {Iterable.<module:engine/model/treewalker~TreeWalkerValue>} walkerValues The walker values for the inserted range.
|
|
415
|
+
* @returns {module:engine/conversion/modelconsumable~ModelConsumable} The values to consume.
|
|
416
|
+
*/
|
|
417
|
+
_addConsumablesForInsert(consumable, walkerValues) {
|
|
418
|
+
for (const value of walkerValues) {
|
|
419
|
+
const item = value.item;
|
|
420
|
+
// Add consumable if it wasn't there yet.
|
|
421
|
+
if (consumable.test(item, 'insert') === null) {
|
|
422
|
+
consumable.add(item, 'insert');
|
|
423
|
+
for (const key of item.getAttributeKeys()) {
|
|
424
|
+
consumable.add(item, 'attribute:' + key);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return consumable;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Populates provided {@link module:engine/conversion/modelconsumable~ModelConsumable} with values to consume for a given range.
|
|
432
|
+
*
|
|
433
|
+
* @private
|
|
434
|
+
* @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable The consumable.
|
|
435
|
+
* @param {module:engine/model/range~Range} range The affected range.
|
|
436
|
+
* @param {String} type Consumable type.
|
|
437
|
+
* @returns {module:engine/conversion/modelconsumable~ModelConsumable} The values to consume.
|
|
438
|
+
*/
|
|
439
|
+
_addConsumablesForRange(consumable, range, type) {
|
|
440
|
+
for (const item of range.getItems()) {
|
|
441
|
+
consumable.add(item, type);
|
|
442
|
+
}
|
|
443
|
+
return consumable;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Populates provided {@link module:engine/conversion/modelconsumable~ModelConsumable} with selection consumable values.
|
|
447
|
+
*
|
|
448
|
+
* @private
|
|
449
|
+
* @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable The consumable.
|
|
450
|
+
* @param {module:engine/model/selection~Selection} selection The selection to create the consumable from.
|
|
451
|
+
* @param {Iterable.<module:engine/model/markercollection~Marker>} markers Markers that contain the selection.
|
|
452
|
+
* @returns {module:engine/conversion/modelconsumable~ModelConsumable} The values to consume.
|
|
453
|
+
*/
|
|
454
|
+
_addConsumablesForSelection(consumable, selection, markers) {
|
|
455
|
+
consumable.add(selection, 'selection');
|
|
456
|
+
for (const marker of markers) {
|
|
457
|
+
consumable.add(selection, 'addMarker:' + marker.name);
|
|
458
|
+
}
|
|
459
|
+
for (const key of selection.getAttributeKeys()) {
|
|
460
|
+
consumable.add(selection, 'attribute:' + key);
|
|
461
|
+
}
|
|
462
|
+
return consumable;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Tests whether given event wasn't already fired and if so, fires it.
|
|
466
|
+
*
|
|
467
|
+
* @private
|
|
468
|
+
* @fires insert
|
|
469
|
+
* @fires attribute
|
|
470
|
+
* @param {String} type Event type.
|
|
471
|
+
* @param {Object} data Event data.
|
|
472
|
+
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi The conversion API object.
|
|
473
|
+
*/
|
|
474
|
+
_testAndFire(type, data, conversionApi) {
|
|
475
|
+
const eventName = getEventName(type, data);
|
|
476
|
+
const itemKey = data.item.is('$textProxy') ? conversionApi.consumable._getSymbolForTextProxy(data.item) : data.item;
|
|
477
|
+
const eventsFiredForConversion = this._firedEventsMap.get(conversionApi);
|
|
478
|
+
const eventsFiredForItem = eventsFiredForConversion.get(itemKey);
|
|
479
|
+
if (!eventsFiredForItem) {
|
|
480
|
+
eventsFiredForConversion.set(itemKey, new Set([eventName]));
|
|
481
|
+
}
|
|
482
|
+
else if (!eventsFiredForItem.has(eventName)) {
|
|
483
|
+
eventsFiredForItem.add(eventName);
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
this.fire(eventName, data, conversionApi);
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Fires not already fired events for setting attributes on just inserted item.
|
|
492
|
+
*
|
|
493
|
+
* @private
|
|
494
|
+
* @param {module:engine/model/item~Item} item The model item to convert attributes for.
|
|
495
|
+
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi The conversion API object.
|
|
496
|
+
*/
|
|
497
|
+
_testAndFireAddAttributes(item, conversionApi) {
|
|
498
|
+
const data = {
|
|
499
|
+
item,
|
|
500
|
+
range: Range._createOn(item)
|
|
501
|
+
};
|
|
502
|
+
for (const key of data.item.getAttributeKeys()) {
|
|
503
|
+
data.attributeKey = key;
|
|
504
|
+
data.attributeOldValue = null;
|
|
505
|
+
data.attributeNewValue = data.item.getAttribute(key);
|
|
506
|
+
this._testAndFire(`attribute:${key}`, data, conversionApi);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Builds an instance of the {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi} from a template and a given
|
|
511
|
+
* {@link module:engine/view/downcastwriter~DowncastWriter `DowncastWriter`} and options object.
|
|
512
|
+
*
|
|
513
|
+
* @private
|
|
514
|
+
* @param {module:engine/view/downcastwriter~DowncastWriter} writer View writer that should be used to modify the view document.
|
|
515
|
+
* @param {Set.<module:engine/model/element~Element>} [refreshedItems] A set of model elements that should not reuse their
|
|
516
|
+
* previous view representations.
|
|
517
|
+
* @param {Object} [options] Optional options passed to `convertionApi.options`.
|
|
518
|
+
* @return {module:engine/conversion/downcastdispatcher~DowncastConversionApi} The conversion API object.
|
|
519
|
+
*/
|
|
520
|
+
_createConversionApi(writer, refreshedItems = new Set(), options = {}) {
|
|
521
|
+
const conversionApi = {
|
|
522
|
+
...this._conversionApi,
|
|
523
|
+
consumable: new Consumable(),
|
|
524
|
+
writer,
|
|
525
|
+
options,
|
|
526
|
+
convertItem: item => this._convertInsert(Range._createOn(item), conversionApi),
|
|
527
|
+
convertChildren: element => this._convertInsert(Range._createIn(element), conversionApi, { doNotAddConsumables: true }),
|
|
528
|
+
convertAttributes: item => this._testAndFireAddAttributes(item, conversionApi),
|
|
529
|
+
canReuseView: viewElement => !refreshedItems.has(conversionApi.mapper.toModelElement(viewElement))
|
|
530
|
+
};
|
|
531
|
+
this._firedEventsMap.set(conversionApi, new Map());
|
|
532
|
+
return conversionApi;
|
|
533
|
+
}
|
|
736
534
|
}
|
|
737
|
-
|
|
738
|
-
mix( DowncastDispatcher, EmitterMixin );
|
|
739
|
-
|
|
740
535
|
// Helper function, checks whether change of `marker` at `modelPosition` should be converted. Marker changes are not
|
|
741
536
|
// converted if they happen inside an element with custom conversion method.
|
|
742
537
|
//
|
|
@@ -744,78 +539,57 @@ mix( DowncastDispatcher, EmitterMixin );
|
|
|
744
539
|
// @param {module:engine/model/markercollection~Marker} marker
|
|
745
540
|
// @param {module:engine/conversion/mapper~Mapper} mapper
|
|
746
541
|
// @returns {Boolean}
|
|
747
|
-
function shouldMarkerChangeBeConverted(
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
} );
|
|
760
|
-
|
|
761
|
-
return !hasCustomHandling;
|
|
542
|
+
function shouldMarkerChangeBeConverted(modelPosition, marker, mapper) {
|
|
543
|
+
const range = marker.getRange();
|
|
544
|
+
const ancestors = Array.from(modelPosition.getAncestors());
|
|
545
|
+
ancestors.shift(); // Remove root element. It cannot be passed to `model.Range#containsItem`.
|
|
546
|
+
ancestors.reverse();
|
|
547
|
+
const hasCustomHandling = ancestors.some(element => {
|
|
548
|
+
if (range.containsItem(element)) {
|
|
549
|
+
const viewElement = mapper.toViewElement(element);
|
|
550
|
+
return !!viewElement.getCustomProperty('addHighlight');
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
return !hasCustomHandling;
|
|
762
554
|
}
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
return `${ type }:${ name }`;
|
|
555
|
+
function getEventName(type, data) {
|
|
556
|
+
const name = data.item.is('element') ? data.item.name : '$text';
|
|
557
|
+
return `${type}:${name}`;
|
|
768
558
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
range: itemRange
|
|
777
|
-
};
|
|
559
|
+
function walkerValueToEventData(value) {
|
|
560
|
+
const item = value.item;
|
|
561
|
+
const itemRange = Range._createFromPositionAndShift(value.previousPosition, value.length);
|
|
562
|
+
return {
|
|
563
|
+
item,
|
|
564
|
+
range: itemRange
|
|
565
|
+
};
|
|
778
566
|
}
|
|
779
|
-
|
|
780
|
-
/**
|
|
781
|
-
* Conversion interface that is registered for given {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}
|
|
782
|
-
* and is passed as one of parameters when {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher dispatcher}
|
|
783
|
-
* fires its events.
|
|
784
|
-
*
|
|
785
|
-
* @interface module:engine/conversion/downcastdispatcher~DowncastConversionApi
|
|
786
|
-
*/
|
|
787
|
-
|
|
788
567
|
/**
|
|
789
568
|
* The {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher} instance.
|
|
790
569
|
*
|
|
791
570
|
* @member {module:engine/conversion/downcastdispatcher~DowncastDispatcher} #dispatcher
|
|
792
571
|
*/
|
|
793
|
-
|
|
794
572
|
/**
|
|
795
573
|
* Stores the information about what parts of a processed model item are still waiting to be handled. After a piece of a model item was
|
|
796
574
|
* converted, an appropriate consumable value should be {@link module:engine/conversion/modelconsumable~ModelConsumable#consume consumed}.
|
|
797
575
|
*
|
|
798
576
|
* @member {module:engine/conversion/modelconsumable~ModelConsumable} #consumable
|
|
799
577
|
*/
|
|
800
|
-
|
|
801
578
|
/**
|
|
802
579
|
* The {@link module:engine/conversion/mapper~Mapper} instance.
|
|
803
580
|
*
|
|
804
581
|
* @member {module:engine/conversion/mapper~Mapper} #mapper
|
|
805
582
|
*/
|
|
806
|
-
|
|
807
583
|
/**
|
|
808
584
|
* The {@link module:engine/model/schema~Schema} instance set for the model that is downcast.
|
|
809
585
|
*
|
|
810
586
|
* @member {module:engine/model/schema~Schema} #schema
|
|
811
587
|
*/
|
|
812
|
-
|
|
813
588
|
/**
|
|
814
589
|
* The {@link module:engine/view/downcastwriter~DowncastWriter} instance used to manipulate the data during conversion.
|
|
815
590
|
*
|
|
816
591
|
* @member {module:engine/view/downcastwriter~DowncastWriter} #writer
|
|
817
592
|
*/
|
|
818
|
-
|
|
819
593
|
/**
|
|
820
594
|
* Triggers conversion of a specified item.
|
|
821
595
|
* This conversion is triggered within (as a separate process of) the parent conversion.
|
|
@@ -823,21 +597,18 @@ function walkerValueToEventData( value ) {
|
|
|
823
597
|
* @method #convertItem
|
|
824
598
|
* @param {module:engine/model/item~Item} item The model item to trigger nested insert conversion on.
|
|
825
599
|
*/
|
|
826
|
-
|
|
827
600
|
/**
|
|
828
601
|
* Triggers conversion of children of a specified element.
|
|
829
602
|
*
|
|
830
603
|
* @method #convertChildren
|
|
831
604
|
* @param {module:engine/model/element~Element} element The model element to trigger children insert conversion on.
|
|
832
605
|
*/
|
|
833
|
-
|
|
834
606
|
/**
|
|
835
607
|
* Triggers conversion of attributes of a specified item.
|
|
836
608
|
*
|
|
837
609
|
* @method #convertAttributes
|
|
838
610
|
* @param {module:engine/model/item~Item} item The model item to trigger attribute conversion on.
|
|
839
611
|
*/
|
|
840
|
-
|
|
841
612
|
/**
|
|
842
613
|
* An object with an additional configuration which can be used during the conversion process. Available only for data downcast conversion.
|
|
843
614
|
*
|