@ckeditor/ckeditor5-engine 35.4.0 → 36.0.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/LICENSE.md +1 -1
- package/package.json +22 -22
- package/src/controller/datacontroller.js +5 -1
- package/src/controller/editingcontroller.js +1 -1
- package/src/conversion/conversion.js +1 -1
- package/src/conversion/conversionhelpers.js +1 -1
- package/src/conversion/downcastdispatcher.js +1 -1
- package/src/conversion/downcasthelpers.js +1 -1
- package/src/conversion/mapper.js +1 -1
- package/src/conversion/modelconsumable.js +1 -1
- package/src/conversion/upcastdispatcher.js +1 -1
- package/src/conversion/upcasthelpers.js +8 -1
- package/src/conversion/viewconsumable.js +1 -1
- package/src/dataprocessor/basichtmlwriter.js +1 -1
- package/src/dataprocessor/dataprocessor.js +1 -1
- package/src/dataprocessor/htmldataprocessor.js +1 -2
- package/src/dataprocessor/htmlwriter.js +1 -1
- package/src/dataprocessor/xmldataprocessor.js +1 -1
- package/src/dev-utils/model.js +1 -1
- package/src/dev-utils/operationreplayer.js +1 -1
- package/src/dev-utils/utils.js +1 -1
- package/src/dev-utils/view.js +1 -1
- package/src/index.js +3 -2
- package/src/model/batch.js +9 -46
- package/src/model/differ.js +81 -174
- package/src/model/document.js +36 -92
- package/src/model/documentfragment.js +43 -96
- package/src/model/documentselection.js +151 -245
- package/src/model/element.js +47 -100
- package/src/model/history.js +15 -46
- package/src/model/item.js +1 -1
- package/src/model/liveposition.js +10 -36
- package/src/model/liverange.js +13 -36
- package/src/model/markercollection.js +40 -111
- package/src/model/model.js +212 -289
- package/src/model/node.js +35 -125
- package/src/model/nodelist.js +11 -39
- package/src/model/operation/attributeoperation.js +13 -44
- package/src/model/operation/detachoperation.js +3 -16
- package/src/model/operation/insertoperation.js +6 -34
- package/src/model/operation/markeroperation.js +9 -48
- package/src/model/operation/mergeoperation.js +8 -41
- package/src/model/operation/moveoperation.js +14 -37
- package/src/model/operation/nooperation.js +1 -7
- package/src/model/operation/operation.js +5 -63
- package/src/model/operation/operationfactory.js +3 -6
- package/src/model/operation/renameoperation.js +8 -28
- package/src/model/operation/rootattributeoperation.js +18 -47
- package/src/model/operation/splitoperation.js +9 -47
- package/src/model/operation/transform.js +109 -150
- package/src/model/operation/utils.js +36 -50
- package/src/model/position.js +117 -228
- package/src/model/range.js +145 -200
- package/src/model/rootelement.js +8 -47
- package/src/model/schema.js +236 -272
- package/src/model/selection.js +134 -192
- package/src/model/text.js +10 -37
- package/src/model/textproxy.js +15 -69
- package/src/model/treewalker.js +10 -101
- package/src/model/typecheckable.js +1 -1
- package/src/model/utils/autoparagraphing.js +11 -12
- package/src/model/utils/deletecontent.js +93 -62
- package/src/model/utils/findoptimalinsertionrange.js +24 -24
- package/src/model/utils/getselectedcontent.js +3 -6
- package/src/model/utils/insertcontent.js +36 -129
- package/src/model/utils/insertobject.js +19 -21
- package/src/model/utils/modifyselection.js +23 -33
- package/src/model/utils/selection-post-fixer.js +53 -59
- package/src/model/writer.js +208 -314
- package/src/view/attributeelement.js +1 -1
- package/src/view/containerelement.js +1 -1
- package/src/view/datatransfer.js +1 -1
- package/src/view/document.js +1 -17
- package/src/view/documentfragment.js +49 -1
- package/src/view/documentselection.js +1 -1
- package/src/view/domconverter.js +4 -3
- package/src/view/downcastwriter.js +1 -1
- package/src/view/editableelement.js +1 -1
- package/src/view/element.js +5 -5
- package/src/view/elementdefinition.js +1 -1
- package/src/view/emptyelement.js +1 -1
- package/src/view/filler.js +1 -1
- package/src/view/item.js +1 -1
- package/src/view/matcher.js +1 -1
- package/src/view/node.js +1 -1
- package/src/view/observer/arrowkeysobserver.js +1 -1
- package/src/view/observer/bubblingemittermixin.js +1 -1
- package/src/view/observer/bubblingeventinfo.js +1 -1
- package/src/view/observer/clickobserver.js +1 -1
- package/src/view/observer/compositionobserver.js +1 -1
- package/src/view/observer/domeventdata.js +1 -1
- package/src/view/observer/domeventobserver.js +1 -1
- package/src/view/observer/fakeselectionobserver.js +1 -1
- package/src/view/observer/focusobserver.js +22 -4
- package/src/view/observer/inputobserver.js +1 -1
- package/src/view/observer/keyobserver.js +1 -1
- package/src/view/observer/mouseobserver.js +1 -1
- package/src/view/observer/mutationobserver.js +1 -1
- package/src/view/observer/observer.js +1 -1
- package/src/view/observer/selectionobserver.js +13 -2
- package/src/view/observer/tabobserver.js +1 -1
- package/src/view/placeholder.js +1 -1
- package/src/view/position.js +1 -1
- package/src/view/range.js +1 -1
- package/src/view/rawelement.js +1 -1
- package/src/view/renderer.js +1 -14
- package/src/view/rooteditableelement.js +1 -1
- package/src/view/selection.js +1 -1
- package/src/view/styles/background.js +1 -1
- package/src/view/styles/border.js +1 -1
- package/src/view/styles/margin.js +1 -1
- package/src/view/styles/padding.js +1 -1
- package/src/view/styles/utils.js +1 -1
- package/src/view/stylesmap.js +1 -1
- package/src/view/text.js +1 -1
- package/src/view/textproxy.js +1 -1
- package/src/view/treewalker.js +1 -1
- package/src/view/typecheckable.js +1 -1
- package/src/view/uielement.js +1 -1
- package/src/view/upcastwriter.js +1 -1
- package/src/view/view.js +5 -5
- package/theme/placeholder.css +1 -1
- package/theme/renderer.css +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Copyright (c) 2003-
|
|
2
|
+
* @license Copyright (c) 2003-2023, 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
|
import InsertOperation from './insertoperation';
|
|
@@ -31,10 +31,7 @@ const transformations = new Map();
|
|
|
31
31
|
* The `transformationFunction` should return transformation result, which is an array with one or multiple
|
|
32
32
|
* {@link module:engine/model/operation/operation~Operation operation} instances.
|
|
33
33
|
*
|
|
34
|
-
* @
|
|
35
|
-
* @param {Function} OperationA
|
|
36
|
-
* @param {Function} OperationB
|
|
37
|
-
* @param {Function} transformationFunction Function to use for transforming.
|
|
34
|
+
* @param transformationFunction Function to use for transforming.
|
|
38
35
|
*/
|
|
39
36
|
function setTransformation(OperationA, OperationB, transformationFunction) {
|
|
40
37
|
let aGroup = transformations.get(OperationA);
|
|
@@ -51,10 +48,7 @@ function setTransformation(OperationA, OperationB, transformationFunction) {
|
|
|
51
48
|
* is returned. This means that if no transformation was set, the `OperationA` instance will not change when transformed
|
|
52
49
|
* by the `OperationB` instance.
|
|
53
50
|
*
|
|
54
|
-
* @
|
|
55
|
-
* @param {Function} OperationA
|
|
56
|
-
* @param {Function} OperationB
|
|
57
|
-
* @returns {Function} Function set to transform an instance of `OperationA` by an instance of `OperationB`.
|
|
51
|
+
* @returns Function set to transform an instance of `OperationA` by an instance of `OperationB`.
|
|
58
52
|
*/
|
|
59
53
|
function getTransformation(OperationA, OperationB) {
|
|
60
54
|
const aGroup = transformations.get(OperationA);
|
|
@@ -65,10 +59,6 @@ function getTransformation(OperationA, OperationB) {
|
|
|
65
59
|
}
|
|
66
60
|
/**
|
|
67
61
|
* A transformation function that only clones operation to transform, without changing it.
|
|
68
|
-
*
|
|
69
|
-
* @private
|
|
70
|
-
* @param {module:engine/model/operation/operation~Operation} a Operation to transform.
|
|
71
|
-
* @returns {Array.<module:engine/model/operation/operation~Operation>}
|
|
72
62
|
*/
|
|
73
63
|
function noUpdateTransformation(a) {
|
|
74
64
|
return [a];
|
|
@@ -76,10 +66,10 @@ function noUpdateTransformation(a) {
|
|
|
76
66
|
/**
|
|
77
67
|
* Transforms operation `a` by operation `b`.
|
|
78
68
|
*
|
|
79
|
-
* @param
|
|
80
|
-
* @param
|
|
81
|
-
* @param
|
|
82
|
-
* @returns
|
|
69
|
+
* @param a Operation to be transformed.
|
|
70
|
+
* @param b Operation to transform by.
|
|
71
|
+
* @param context Transformation context for this transformation.
|
|
72
|
+
* @returns Transformation result.
|
|
83
73
|
*/
|
|
84
74
|
export function transform(a, b, context = {}) {
|
|
85
75
|
const transformationFunction = getTransformation(a.constructor, b.constructor);
|
|
@@ -123,22 +113,17 @@ export function transform(a, b, context = {}) {
|
|
|
123
113
|
* * transformed `operationsA` start from `9` and there are `3` of them, so the last will have `baseVersion` equal to `11`,
|
|
124
114
|
* * transformed `operationsB` start from `7` and there are `5` of them, so the last will have `baseVersion` equal to `11`.
|
|
125
115
|
*
|
|
126
|
-
* @param
|
|
127
|
-
* @param
|
|
128
|
-
* @param
|
|
129
|
-
* @param
|
|
130
|
-
* @param
|
|
131
|
-
*
|
|
132
|
-
* @param {Boolean} [options.padWithNoOps=false] Whether additional {@link module:engine/model/operation/nooperation~NoOperation}s
|
|
116
|
+
* @param operationsA
|
|
117
|
+
* @param operationsB
|
|
118
|
+
* @param options Additional transformation options.
|
|
119
|
+
* @param options.document Document which the operations change.
|
|
120
|
+
* @param options.useRelations Whether during transformation relations should be used (used during undo for better conflict resolution).
|
|
121
|
+
* @param options.padWithNoOps Whether additional {@link module:engine/model/operation/nooperation~NoOperation}s
|
|
133
122
|
* should be added to the transformation results to force the same last base version for both transformed sets (in case
|
|
134
123
|
* if some operations got broken into multiple operations during transformation).
|
|
135
|
-
* @param
|
|
124
|
+
* @param options.forceWeakRemove If set to `false`, remove operation will be always stronger than move operation,
|
|
136
125
|
* so the removed nodes won't end up back in the document root. When set to `true`, context data will be used.
|
|
137
|
-
* @returns
|
|
138
|
-
* @returns {Array.<module:engine/model/operation/operation~Operation>} return.operationsA Transformed `operationsA`.
|
|
139
|
-
* @returns {Array.<module:engine/model/operation/operation~Operation>} return.operationsB Transformed `operationsB`.
|
|
140
|
-
* @returns {Map} return.originalOperations A map that links transformed operations to original operations. The keys are the transformed
|
|
141
|
-
* operations and the values are the original operations from the input (`operationsA` and `operationsB`).
|
|
126
|
+
* @returns Transformation result.
|
|
142
127
|
*/
|
|
143
128
|
export function transformSets(operationsA, operationsB, options) {
|
|
144
129
|
// Create new arrays so the originally passed arguments are not changed.
|
|
@@ -349,16 +334,20 @@ export function transformSets(operationsA, operationsB, options) {
|
|
|
349
334
|
updateBaseVersions(operationsB, data.nextBaseVersionA);
|
|
350
335
|
return { operationsA, operationsB, originalOperations };
|
|
351
336
|
}
|
|
352
|
-
|
|
353
|
-
|
|
337
|
+
/**
|
|
338
|
+
* Gathers additional data about operations processed during transformation. Can be used to obtain contextual information
|
|
339
|
+
* about two operations that are about to be transformed. This contextual information can be used for better conflict resolution.
|
|
340
|
+
*/
|
|
354
341
|
class ContextFactory {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
342
|
+
/**
|
|
343
|
+
* Creates `ContextFactory` instance.
|
|
344
|
+
*
|
|
345
|
+
* @param document Document which the operations change.
|
|
346
|
+
* @param useRelations Whether during transformation relations should be used (used during undo for
|
|
347
|
+
* better conflict resolution).
|
|
348
|
+
* @param forceWeakRemove If set to `false`, remove operation will be always stronger than move operation,
|
|
349
|
+
* so the removed nodes won't end up back in the document root. When set to `true`, context data will be used.
|
|
350
|
+
*/
|
|
362
351
|
constructor(document, useRelations, forceWeakRemove = false) {
|
|
363
352
|
// For each operation that is created during transformation process, we keep a reference to the original operation
|
|
364
353
|
// which it comes from. The original operation works as a kind of "identifier". Every contextual information
|
|
@@ -376,36 +365,34 @@ class ContextFactory {
|
|
|
376
365
|
// we keep relations between them.
|
|
377
366
|
this._relations = new Map();
|
|
378
367
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
// @param {module:engine/model/operation/operation~Operation|null} [takeFrom=null]
|
|
368
|
+
/**
|
|
369
|
+
* Sets "original operation" for given operations.
|
|
370
|
+
*
|
|
371
|
+
* During transformation process, operations are cloned, then changed, then processed again, sometimes broken into two
|
|
372
|
+
* or multiple operations. When gathering additional data it is important that all operations can be somehow linked
|
|
373
|
+
* so a cloned and transformed "version" still kept track of the data assigned earlier to it.
|
|
374
|
+
*
|
|
375
|
+
* The original operation object will be used as such an universal linking id. Throughout the transformation process
|
|
376
|
+
* all cloned operations will refer to "the original operation" when storing and reading additional data.
|
|
377
|
+
*
|
|
378
|
+
* If `takeFrom` is not set, each operation from `operations` array will be assigned itself as "the original operation".
|
|
379
|
+
* This should be used as an initialization step.
|
|
380
|
+
*
|
|
381
|
+
* If `takeFrom` is set, each operation from `operations` will be assigned the same original operation as assigned
|
|
382
|
+
* for `takeFrom` operation. This should be used to update original operations. It should be used in a way that
|
|
383
|
+
* `operations` are the result of `takeFrom` transformation to ensure proper "original operation propagation".
|
|
384
|
+
*/
|
|
397
385
|
setOriginalOperations(operations, takeFrom = null) {
|
|
398
386
|
const originalOperation = takeFrom ? this.originalOperations.get(takeFrom) : null;
|
|
399
387
|
for (const operation of operations) {
|
|
400
388
|
this.originalOperations.set(operation, originalOperation || operation);
|
|
401
389
|
}
|
|
402
390
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
// @param {module:engine/model/operation/operation~Operation} opB
|
|
391
|
+
/**
|
|
392
|
+
* Saves a relation between operations `opA` and `opB`.
|
|
393
|
+
*
|
|
394
|
+
* Relations are then later used to help solve conflicts when operations are transformed.
|
|
395
|
+
*/
|
|
409
396
|
updateRelation(opA, opB) {
|
|
410
397
|
// The use of relations is described in a bigger detail in transformation functions.
|
|
411
398
|
//
|
|
@@ -507,11 +494,9 @@ class ContextFactory {
|
|
|
507
494
|
}
|
|
508
495
|
}
|
|
509
496
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
// @param {module:engine/model/operation/operation~Operation} opB
|
|
514
|
-
// @returns {module:engine/model/operation/transform~TransformationContext}
|
|
497
|
+
/**
|
|
498
|
+
* Evaluates and returns contextual information about two given operations `opA` and `opB` which are about to be transformed.
|
|
499
|
+
*/
|
|
515
500
|
getContext(opA, opB, aIsStrong) {
|
|
516
501
|
return {
|
|
517
502
|
aIsStrong,
|
|
@@ -522,12 +507,11 @@ class ContextFactory {
|
|
|
522
507
|
forceWeakRemove: this._forceWeakRemove
|
|
523
508
|
};
|
|
524
509
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
// @returns {Boolean}
|
|
510
|
+
/**
|
|
511
|
+
* Returns whether given operation `op` has already been undone.
|
|
512
|
+
*
|
|
513
|
+
* Information whether an operation was undone gives more context when making a decision when two operations are in conflict.
|
|
514
|
+
*/
|
|
531
515
|
_wasUndone(op) {
|
|
532
516
|
// For `op`, get its original operation. After all, if `op` is a clone (or even transformed clone) of another
|
|
533
517
|
// operation, literally `op` couldn't be undone. It was just generated. If anything, it was the operation it origins
|
|
@@ -536,30 +520,28 @@ class ContextFactory {
|
|
|
536
520
|
// And check with the document if the original operation was undone.
|
|
537
521
|
return originalOp.wasUndone || this._history.isUndoneOperation(originalOp);
|
|
538
522
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
// @param {module:engine/model/operation/operation~Operation} opB
|
|
562
|
-
// @returns {String|null}
|
|
523
|
+
/**
|
|
524
|
+
* Returns a relation between `opA` and an operation which is undone by `opB`. This can be `String` value if a relation
|
|
525
|
+
* was set earlier or `null` if there was no relation between those operations.
|
|
526
|
+
*
|
|
527
|
+
* This is a little tricky to understand, so let's compare it to `ContextFactory#_wasUndone`.
|
|
528
|
+
*
|
|
529
|
+
* When `wasUndone( opB )` is used, we check if the `opB` has already been undone. It is obvious, that the
|
|
530
|
+
* undoing operation must happen after the undone operation. So, essentially, we have `opB`, we take document history,
|
|
531
|
+
* we look forward in the future and ask if in that future `opB` was undone.
|
|
532
|
+
*
|
|
533
|
+
* Relations is a backward process to `wasUndone()`.
|
|
534
|
+
*
|
|
535
|
+
* Long story short - using relations is asking what happened in the past. Looking back. This time we have an undoing
|
|
536
|
+
* operation `opB` which has undone some other operation. When there is a transformation `opA` x `opB` and there is
|
|
537
|
+
* a conflict to solve and `opB` is an undoing operation, we can look back in the history and see what was a relation
|
|
538
|
+
* between `opA` and the operation which `opB` undone. Basing on that relation from the past, we can now make
|
|
539
|
+
* a better decision when resolving a conflict between two operations, because we know more about the context of
|
|
540
|
+
* those two operations.
|
|
541
|
+
*
|
|
542
|
+
* This is why this function does not return a relation directly between `opA` and `opB` because we need to look
|
|
543
|
+
* back to search for a meaningful contextual information.
|
|
544
|
+
*/
|
|
563
545
|
_getRelation(opA, opB) {
|
|
564
546
|
// Get the original operation. Similarly as in `wasUndone()` it is used as an universal identifier for stored data.
|
|
565
547
|
const origB = this.originalOperations.get(opB);
|
|
@@ -576,12 +558,9 @@ class ContextFactory {
|
|
|
576
558
|
}
|
|
577
559
|
return null;
|
|
578
560
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
// @param {module:engine/model/operation/operation~Operation} opA
|
|
583
|
-
// @param {module:engine/model/operation/operation~Operation} opB
|
|
584
|
-
// @param {String} relation
|
|
561
|
+
/**
|
|
562
|
+
* Helper function for `ContextFactory#updateRelations`.
|
|
563
|
+
*/
|
|
585
564
|
_setRelation(opA, opB, relation) {
|
|
586
565
|
// As always, setting is for original operations, not the clones/transformed operations.
|
|
587
566
|
const origA = this.originalOperations.get(opA);
|
|
@@ -601,9 +580,8 @@ class ContextFactory {
|
|
|
601
580
|
* The function simply sets `baseVersion` as a base version of the first passed operation and then increments it for
|
|
602
581
|
* each following operation in `operations`.
|
|
603
582
|
*
|
|
604
|
-
* @
|
|
605
|
-
* @param
|
|
606
|
-
* @param {Number} baseVersion Base version to set for the first operation in `operations`.
|
|
583
|
+
* @param operations Operations to update.
|
|
584
|
+
* @param baseVersion Base version to set for the first operation in `operations`.
|
|
607
585
|
*/
|
|
608
586
|
function updateBaseVersions(operations, baseVersion) {
|
|
609
587
|
for (const operation of operations) {
|
|
@@ -612,10 +590,6 @@ function updateBaseVersions(operations, baseVersion) {
|
|
|
612
590
|
}
|
|
613
591
|
/**
|
|
614
592
|
* Adds `howMany` instances of {@link module:engine/model/operation/nooperation~NoOperation} to `operations` set.
|
|
615
|
-
*
|
|
616
|
-
* @private
|
|
617
|
-
* @param {Array.<module:engine/model/operation/operation~Operation>} operations
|
|
618
|
-
* @param {Number} howMany
|
|
619
593
|
*/
|
|
620
594
|
function padWithNoOps(operations, howMany) {
|
|
621
595
|
for (let i = 0; i < howMany; i++) {
|
|
@@ -730,12 +704,6 @@ setTransformation(AttributeOperation, InsertOperation, (a, b) => {
|
|
|
730
704
|
*
|
|
731
705
|
* For given `insertOperation` it checks the inserted node if it has an attribute `key` set to a value different
|
|
732
706
|
* than `newValue`. If so, it generates an `AttributeOperation` which changes the value of `key` attribute to `newValue`.
|
|
733
|
-
*
|
|
734
|
-
* @private
|
|
735
|
-
* @param {module:engine/model/operation/insertoperation~InsertOperation} insertOperation
|
|
736
|
-
* @param {String} key
|
|
737
|
-
* @param {*} newValue
|
|
738
|
-
* @returns {module:engine/model/operation/attributeoperation~AttributeOperation|null}
|
|
739
707
|
*/
|
|
740
708
|
function _getComplementaryAttributeOperations(insertOperation, key, newValue) {
|
|
741
709
|
const nodes = insertOperation.nodes;
|
|
@@ -774,20 +742,17 @@ setTransformation(AttributeOperation, MoveOperation, (a, b) => {
|
|
|
774
742
|
// Create `AttributeOperation`s out of the ranges.
|
|
775
743
|
return ranges.map(range => new AttributeOperation(range, a.key, a.oldValue, a.newValue, a.baseVersion));
|
|
776
744
|
});
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
// @param {module:engine/model/range~Range} range
|
|
789
|
-
// @param {module:engine/model/operation/moveoperation~MoveOperation} moveOp
|
|
790
|
-
// @returns {Array.<module:engine/model/range~Range>}
|
|
745
|
+
/**
|
|
746
|
+
* Helper function for `AttributeOperation` x `MoveOperation` transformation.
|
|
747
|
+
*
|
|
748
|
+
* Takes the passed `range` and transforms it by move operation `moveOp` in a specific way. Only top-level nodes of `range`
|
|
749
|
+
* are considered to be in the range. If move operation moves nodes deep from inside of the range, those nodes won't
|
|
750
|
+
* be included in the result. In other words, top-level nodes of the ranges from the result are exactly the same as
|
|
751
|
+
* top-level nodes of the original `range`.
|
|
752
|
+
*
|
|
753
|
+
* This is important for `AttributeOperation` because, for its range, it changes only the top-level nodes. So we need to
|
|
754
|
+
* track only how those nodes have been affected by `MoveOperation`.
|
|
755
|
+
*/
|
|
791
756
|
function _breakRangeByMoveOperation(range, moveOp) {
|
|
792
757
|
const moveRange = Range._createFromPositionAndShift(moveOp.sourcePosition, moveOp.howMany);
|
|
793
758
|
// We are transforming `range` (original range) by `moveRange` (range moved by move operation). As usual when it comes to
|
|
@@ -1963,28 +1928,22 @@ setTransformation(SplitOperation, SplitOperation, (a, b, context) => {
|
|
|
1963
1928
|
a.insertionPosition = SplitOperation.getInsertionPosition(a.splitPosition);
|
|
1964
1929
|
return [a];
|
|
1965
1930
|
});
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
// @param {module:engine/model/operation/moveoperation~MoveOperation} a
|
|
1970
|
-
// @param {module:engine/model/operation/moveoperation~MoveOperation} b
|
|
1971
|
-
// @returns {Boolean}
|
|
1931
|
+
/**
|
|
1932
|
+
* Checks whether `MoveOperation` `targetPosition` is inside a node from the moved range of the other `MoveOperation`.
|
|
1933
|
+
*/
|
|
1972
1934
|
function _moveTargetIntoMovedRange(a, b) {
|
|
1973
1935
|
return a.targetPosition._getTransformedByDeletion(b.sourcePosition, b.howMany) === null;
|
|
1974
1936
|
}
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
// @param {Array.<module:engine/model/range~Range>} ranges
|
|
1986
|
-
// @param {module:engine/model/position~Position} targetPosition
|
|
1987
|
-
// @returns {Array.<module:engine/model/operation/moveoperation~MoveOperation>}
|
|
1937
|
+
/**
|
|
1938
|
+
* Helper function for `MoveOperation` x `MoveOperation` transformation. Converts given ranges and target position to
|
|
1939
|
+
* move operations and returns them.
|
|
1940
|
+
*
|
|
1941
|
+
* Ranges and target position will be transformed on-the-fly when generating operations.
|
|
1942
|
+
*
|
|
1943
|
+
* Given `ranges` should be in the order of how they were in the original transformed operation.
|
|
1944
|
+
*
|
|
1945
|
+
* Given `targetPosition` is the target position of the first range from `ranges`.
|
|
1946
|
+
*/
|
|
1988
1947
|
function _makeMoveOperationsFromRanges(ranges, targetPosition) {
|
|
1989
1948
|
// At this moment we have some ranges and a target position, to which those ranges should be moved.
|
|
1990
1949
|
// Order in `ranges` array is the go-to order of after transformation.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Copyright (c) 2003-
|
|
2
|
+
* @license Copyright (c) 2003-2023, 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
|
/**
|
|
@@ -10,22 +10,13 @@ import Range from '../range';
|
|
|
10
10
|
import Text from '../text';
|
|
11
11
|
import TextProxy from '../textproxy';
|
|
12
12
|
import { CKEditorError, isIterable } from '@ckeditor/ckeditor5-utils';
|
|
13
|
-
/**
|
|
14
|
-
* Contains functions used for composing model tree by {@link module:engine/model/operation/operation~Operation operations}.
|
|
15
|
-
* Those functions are built on top of {@link module:engine/model/node~Node node}, and it's child classes', APIs.
|
|
16
|
-
*
|
|
17
|
-
* @protected
|
|
18
|
-
* @namespace utils
|
|
19
|
-
*/
|
|
20
13
|
/**
|
|
21
14
|
* Inserts given nodes at given position.
|
|
22
15
|
*
|
|
23
16
|
* @internal
|
|
24
|
-
* @
|
|
25
|
-
* @
|
|
26
|
-
* @
|
|
27
|
-
* @param {module:engine/model/node~NodeSet} normalizedNodes Nodes to insert.
|
|
28
|
-
* @returns {module:engine/model/range~Range} Range spanning over inserted elements.
|
|
17
|
+
* @param position Position at which nodes should be inserted.
|
|
18
|
+
* @param normalizedNodes Nodes to insert.
|
|
19
|
+
* @returns Range spanning over inserted elements.
|
|
29
20
|
*/
|
|
30
21
|
export function _insert(position, nodes) {
|
|
31
22
|
const normalizedNodes = _normalizeNodes(nodes);
|
|
@@ -47,10 +38,7 @@ export function _insert(position, nodes) {
|
|
|
47
38
|
* Removed nodes in given range. Only {@link module:engine/model/range~Range#isFlat flat} ranges are accepted.
|
|
48
39
|
*
|
|
49
40
|
* @internal
|
|
50
|
-
* @
|
|
51
|
-
* @function module:engine/model/operation/utils~utils._remove
|
|
52
|
-
* @param {module:engine/model/range~Range} range Range containing nodes to remove.
|
|
53
|
-
* @returns {Array.<module:engine/model/node~Node>}
|
|
41
|
+
* @param range Range containing nodes to remove.
|
|
54
42
|
*/
|
|
55
43
|
export function _remove(range) {
|
|
56
44
|
if (!range.isFlat) {
|
|
@@ -76,11 +64,9 @@ export function _remove(range) {
|
|
|
76
64
|
* Moves nodes in given range to given target position. Only {@link module:engine/model/range~Range#isFlat flat} ranges are accepted.
|
|
77
65
|
*
|
|
78
66
|
* @internal
|
|
79
|
-
* @
|
|
80
|
-
* @
|
|
81
|
-
* @
|
|
82
|
-
* @param {module:engine/model/position~Position} targetPosition Position to which nodes should be moved.
|
|
83
|
-
* @returns {module:engine/model/range~Range} Range containing moved nodes.
|
|
67
|
+
* @param sourceRange Range containing nodes to move.
|
|
68
|
+
* @param targetPosition Position to which nodes should be moved.
|
|
69
|
+
* @returns Range containing moved nodes.
|
|
84
70
|
*/
|
|
85
71
|
export function _move(sourceRange, targetPosition) {
|
|
86
72
|
if (!sourceRange.isFlat) {
|
|
@@ -101,11 +87,9 @@ export function _move(sourceRange, targetPosition) {
|
|
|
101
87
|
* Sets given attribute on nodes in given range. The attributes are only set on top-level nodes of the range, not on its children.
|
|
102
88
|
*
|
|
103
89
|
* @internal
|
|
104
|
-
* @
|
|
105
|
-
* @
|
|
106
|
-
* @param
|
|
107
|
-
* @param {String} key Key of attribute to set.
|
|
108
|
-
* @param {*} value Attribute value.
|
|
90
|
+
* @param range Range containing nodes that should have the attribute set. Must be a flat range.
|
|
91
|
+
* @param key Key of attribute to set.
|
|
92
|
+
* @param value Attribute value.
|
|
109
93
|
*/
|
|
110
94
|
export function _setAttribute(range, key, value) {
|
|
111
95
|
// Range might start or end in text nodes, so we have to split them.
|
|
@@ -133,10 +117,9 @@ export function _setAttribute(range, key, value) {
|
|
|
133
117
|
* Normalizes given object or an array of objects to an array of {@link module:engine/model/node~Node nodes}. See
|
|
134
118
|
* {@link module:engine/model/node~NodeSet NodeSet} for details on how normalization is performed.
|
|
135
119
|
*
|
|
136
|
-
* @
|
|
137
|
-
* @
|
|
138
|
-
* @
|
|
139
|
-
* @returns {Array.<module:engine/model/node~Node>} Normalized nodes.
|
|
120
|
+
* @internal
|
|
121
|
+
* @param nodes Objects to normalize.
|
|
122
|
+
* @returns Normalized nodes.
|
|
140
123
|
*/
|
|
141
124
|
export function _normalizeNodes(nodes) {
|
|
142
125
|
const normalized = [];
|
|
@@ -170,14 +153,15 @@ export function _normalizeNodes(nodes) {
|
|
|
170
153
|
}
|
|
171
154
|
return normalized;
|
|
172
155
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
156
|
+
/**
|
|
157
|
+
* Checks if nodes before and after given index in given element are {@link module:engine/model/text~Text text nodes} and
|
|
158
|
+
* merges them into one node if they have same attributes.
|
|
159
|
+
*
|
|
160
|
+
* Merging is done by removing two text nodes and inserting a new text node containing data from both merged text nodes.
|
|
161
|
+
*
|
|
162
|
+
* @param element Parent element of nodes to merge.
|
|
163
|
+
* @param index Index between nodes to merge.
|
|
164
|
+
*/
|
|
181
165
|
function _mergeNodesAtIndex(element, index) {
|
|
182
166
|
const nodeBefore = element.getChild(index - 1);
|
|
183
167
|
const nodeAfter = element.getChild(index);
|
|
@@ -191,11 +175,12 @@ function _mergeNodesAtIndex(element, index) {
|
|
|
191
175
|
element._insertChild(index - 1, mergedNode);
|
|
192
176
|
}
|
|
193
177
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
178
|
+
/**
|
|
179
|
+
* Checks if given position is in a text node, and if so, splits the text node in two text nodes, each of them
|
|
180
|
+
* containing a part of original text node.
|
|
181
|
+
*
|
|
182
|
+
* @param position Position at which node should be split.
|
|
183
|
+
*/
|
|
199
184
|
function _splitNodeAtPosition(position) {
|
|
200
185
|
const textNode = position.textNode;
|
|
201
186
|
const element = position.parent;
|
|
@@ -208,12 +193,13 @@ function _splitNodeAtPosition(position) {
|
|
|
208
193
|
element._insertChild(index, [firstPart, secondPart]);
|
|
209
194
|
}
|
|
210
195
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
196
|
+
/**
|
|
197
|
+
* Checks whether two given nodes have same attributes.
|
|
198
|
+
*
|
|
199
|
+
* @param nodeA Node to check.
|
|
200
|
+
* @param nodeB Node to check.
|
|
201
|
+
* @returns `true` if nodes have same attributes, `false` otherwise.
|
|
202
|
+
*/
|
|
217
203
|
function _haveSameAttributes(nodeA, nodeB) {
|
|
218
204
|
const iteratorA = nodeA.getAttributes();
|
|
219
205
|
const iteratorB = nodeB.getAttributes();
|