@ckeditor/ckeditor5-engine 37.1.0 → 38.0.0-rc.1

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 (38) hide show
  1. package/package.json +22 -22
  2. package/src/conversion/conversion.d.ts +5 -3
  3. package/src/index.d.ts +3 -1
  4. package/src/index.js +2 -0
  5. package/src/model/document.d.ts +8 -0
  6. package/src/model/document.js +1 -0
  7. package/src/model/markercollection.d.ts +3 -3
  8. package/src/model/markercollection.js +3 -3
  9. package/src/model/model.d.ts +38 -3
  10. package/src/model/model.js +35 -1
  11. package/src/model/operation/attributeoperation.d.ts +5 -0
  12. package/src/model/operation/attributeoperation.js +6 -0
  13. package/src/model/operation/detachoperation.d.ts +5 -0
  14. package/src/model/operation/detachoperation.js +6 -0
  15. package/src/model/operation/insertoperation.d.ts +5 -0
  16. package/src/model/operation/insertoperation.js +6 -0
  17. package/src/model/operation/markeroperation.d.ts +5 -0
  18. package/src/model/operation/markeroperation.js +18 -0
  19. package/src/model/operation/mergeoperation.d.ts +5 -0
  20. package/src/model/operation/mergeoperation.js +12 -0
  21. package/src/model/operation/moveoperation.d.ts +5 -0
  22. package/src/model/operation/moveoperation.js +9 -0
  23. package/src/model/operation/nooperation.d.ts +5 -0
  24. package/src/model/operation/nooperation.js +6 -0
  25. package/src/model/operation/operation.d.ts +7 -0
  26. package/src/model/operation/renameoperation.d.ts +5 -0
  27. package/src/model/operation/renameoperation.js +6 -0
  28. package/src/model/operation/rootattributeoperation.d.ts +5 -0
  29. package/src/model/operation/rootattributeoperation.js +6 -0
  30. package/src/model/operation/rootoperation.d.ts +5 -0
  31. package/src/model/operation/rootoperation.js +6 -0
  32. package/src/model/operation/splitoperation.d.ts +5 -0
  33. package/src/model/operation/splitoperation.js +14 -0
  34. package/src/model/selection.js +2 -2
  35. package/src/model/writer.d.ts +1 -1
  36. package/src/view/datatransfer.d.ts +4 -0
  37. package/src/view/datatransfer.js +6 -0
  38. package/src/view/domconverter.js +10 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-engine",
3
- "version": "37.1.0",
3
+ "version": "38.0.0-rc.1",
4
4
  "description": "The editing engine of CKEditor 5 – the best browser-based rich text editor.",
5
5
  "keywords": [
6
6
  "wysiwyg",
@@ -23,30 +23,30 @@
23
23
  ],
24
24
  "main": "src/index.js",
25
25
  "dependencies": {
26
- "@ckeditor/ckeditor5-utils": "^37.1.0",
26
+ "@ckeditor/ckeditor5-utils": "^38.0.0-rc.1",
27
27
  "lodash-es": "^4.17.15"
28
28
  },
29
29
  "devDependencies": {
30
- "@ckeditor/ckeditor5-basic-styles": "^37.1.0",
31
- "@ckeditor/ckeditor5-block-quote": "^37.1.0",
32
- "@ckeditor/ckeditor5-clipboard": "^37.1.0",
33
- "@ckeditor/ckeditor5-cloud-services": "^37.1.0",
34
- "@ckeditor/ckeditor5-core": "^37.1.0",
35
- "@ckeditor/ckeditor5-editor-classic": "^37.1.0",
36
- "@ckeditor/ckeditor5-enter": "^37.1.0",
37
- "@ckeditor/ckeditor5-essentials": "^37.1.0",
38
- "@ckeditor/ckeditor5-heading": "^37.1.0",
39
- "@ckeditor/ckeditor5-image": "^37.1.0",
40
- "@ckeditor/ckeditor5-link": "^37.1.0",
41
- "@ckeditor/ckeditor5-list": "^37.1.0",
42
- "@ckeditor/ckeditor5-mention": "^37.1.0",
43
- "@ckeditor/ckeditor5-paragraph": "^37.1.0",
44
- "@ckeditor/ckeditor5-table": "^37.1.0",
45
- "@ckeditor/ckeditor5-theme-lark": "^37.1.0",
46
- "@ckeditor/ckeditor5-typing": "^37.1.0",
47
- "@ckeditor/ckeditor5-ui": "^37.1.0",
48
- "@ckeditor/ckeditor5-undo": "^37.1.0",
49
- "@ckeditor/ckeditor5-widget": "^37.1.0",
30
+ "@ckeditor/ckeditor5-basic-styles": "^38.0.0-rc.1",
31
+ "@ckeditor/ckeditor5-block-quote": "^38.0.0-rc.1",
32
+ "@ckeditor/ckeditor5-clipboard": "^38.0.0-rc.1",
33
+ "@ckeditor/ckeditor5-cloud-services": "^38.0.0-rc.1",
34
+ "@ckeditor/ckeditor5-core": "^38.0.0-rc.1",
35
+ "@ckeditor/ckeditor5-editor-classic": "^38.0.0-rc.1",
36
+ "@ckeditor/ckeditor5-enter": "^38.0.0-rc.1",
37
+ "@ckeditor/ckeditor5-essentials": "^38.0.0-rc.1",
38
+ "@ckeditor/ckeditor5-heading": "^38.0.0-rc.1",
39
+ "@ckeditor/ckeditor5-image": "^38.0.0-rc.1",
40
+ "@ckeditor/ckeditor5-link": "^38.0.0-rc.1",
41
+ "@ckeditor/ckeditor5-list": "^38.0.0-rc.1",
42
+ "@ckeditor/ckeditor5-mention": "^38.0.0-rc.1",
43
+ "@ckeditor/ckeditor5-paragraph": "^38.0.0-rc.1",
44
+ "@ckeditor/ckeditor5-table": "^38.0.0-rc.1",
45
+ "@ckeditor/ckeditor5-theme-lark": "^38.0.0-rc.1",
46
+ "@ckeditor/ckeditor5-typing": "^38.0.0-rc.1",
47
+ "@ckeditor/ckeditor5-ui": "^38.0.0-rc.1",
48
+ "@ckeditor/ckeditor5-undo": "^38.0.0-rc.1",
49
+ "@ckeditor/ckeditor5-widget": "^38.0.0-rc.1",
50
50
  "typescript": "^4.8.4",
51
51
  "webpack": "^5.58.1",
52
52
  "webpack-cli": "^4.9.0"
@@ -72,9 +72,9 @@ export default class Conversion {
72
72
  addAlias(alias: `${string}Downcast`, dispatcher: DowncastDispatcher): void;
73
73
  addAlias(alias: `${string}Upcast`, dispatcher: UpcastDispatcher): void;
74
74
  addAlias(alias: string, dispatcher: DowncastDispatcher | UpcastDispatcher): void;
75
- for(groupName: 'downcast' | `${string}Downcast`): DowncastHelpers;
76
- for(groupName: 'upcast' | `${string}Upcast`): UpcastHelpers;
77
- for(groupName: string): DowncastHelpers | UpcastHelpers;
75
+ for(groupName: 'downcast' | 'dataDowncast' | 'editingDowncast'): DowncastHelpers;
76
+ for(groupName: 'upcast'): UpcastHelpers;
77
+ for<T extends string>(groupName: T): ConversionType<T>;
78
78
  /**
79
79
  * Sets up converters between the model and the view that convert a model element to a view element (and vice versa).
80
80
  * For example, the model `<paragraph>Foo</paragraph>` is turned into `<p>Foo</p>` in the view.
@@ -474,3 +474,5 @@ export default class Conversion {
474
474
  */
475
475
  private _createConversionHelpers;
476
476
  }
477
+ type ConversionType<T extends string> = T extends `${string}Downcast` ? DowncastHelpers : T extends `${string}Upcast` ? UpcastHelpers : DowncastHelpers | UpcastHelpers;
478
+ export {};
package/src/index.d.ts CHANGED
@@ -28,6 +28,7 @@ export { default as AttributeOperation } from './model/operation/attributeoperat
28
28
  export { default as RenameOperation } from './model/operation/renameoperation';
29
29
  export { default as RootAttributeOperation } from './model/operation/rootattributeoperation';
30
30
  export { default as RootOperation } from './model/operation/rootoperation';
31
+ export { default as NoOperation } from './model/operation/nooperation';
31
32
  export { transformSets } from './model/operation/transform';
32
33
  export { default as DocumentSelection, type DocumentSelectionChangeRangeEvent } from './model/documentselection';
33
34
  export { default as Range } from './model/range';
@@ -55,7 +56,7 @@ export type { default as Writer } from './model/writer';
55
56
  export { findOptimalInsertionRange } from './model/utils/findoptimalinsertionrange';
56
57
  export type { DocumentChangeEvent } from './model/document';
57
58
  export type { DocumentSelectionChangeEvent } from './model/documentselection';
58
- export type { ModelApplyOperationEvent, ModelDeleteContentEvent, ModelGetSelectedContentEvent, ModelInsertContentEvent, ModelInsertObjectEvent, ModelModifySelectionEvent } from './model/model';
59
+ export type { ModelApplyOperationEvent, ModelDeleteContentEvent, ModelGetSelectedContentEvent, ModelInsertContentEvent, ModelInsertObjectEvent, ModelModifySelectionEvent, ModelCanEditAtEvent } from './model/model';
59
60
  export type { SelectionChangeRangeEvent } from './model/selection';
60
61
  export { default as DataTransfer } from './view/datatransfer';
61
62
  export { default as DomConverter } from './view/domconverter';
@@ -66,6 +67,7 @@ export { default as ViewText } from './view/text';
66
67
  export { default as ViewElement, type ElementAttributes as ViewElementAttributes } from './view/element';
67
68
  export { default as ViewContainerElement } from './view/containerelement';
68
69
  export { default as ViewEditableElement } from './view/editableelement';
70
+ export { default as ViewRootEditableElement } from './view/rooteditableelement';
69
71
  export { default as ViewAttributeElement } from './view/attributeelement';
70
72
  export { default as ViewEmptyElement } from './view/emptyelement';
71
73
  export { default as ViewRawElement } from './view/rawelement';
package/src/index.js CHANGED
@@ -22,6 +22,7 @@ export { default as AttributeOperation } from './model/operation/attributeoperat
22
22
  export { default as RenameOperation } from './model/operation/renameoperation';
23
23
  export { default as RootAttributeOperation } from './model/operation/rootattributeoperation';
24
24
  export { default as RootOperation } from './model/operation/rootoperation';
25
+ export { default as NoOperation } from './model/operation/nooperation';
25
26
  export { transformSets } from './model/operation/transform';
26
27
  // Model.
27
28
  export { default as DocumentSelection } from './model/documentselection';
@@ -47,6 +48,7 @@ export { default as ViewText } from './view/text';
47
48
  export { default as ViewElement } from './view/element';
48
49
  export { default as ViewContainerElement } from './view/containerelement';
49
50
  export { default as ViewEditableElement } from './view/editableelement';
51
+ export { default as ViewRootEditableElement } from './view/rooteditableelement';
50
52
  export { default as ViewAttributeElement } from './view/attributeelement';
51
53
  export { default as ViewEmptyElement } from './view/emptyelement';
52
54
  export { default as ViewRawElement } from './view/rawelement';
@@ -56,6 +56,14 @@ export default class Document extends Document_base {
56
56
  * The model differ object. Its role is to buffer changes done on the model document and then calculate a diff of those changes.
57
57
  */
58
58
  readonly differ: Differ;
59
+ /**
60
+ * Defines whether the document is in a read-only mode.
61
+ *
62
+ * The user should not be able to change the data of a document that is read-only.
63
+ *
64
+ * @readonly
65
+ */
66
+ isReadOnly: boolean;
59
67
  /**
60
68
  * Post-fixer callbacks registered to the model document.
61
69
  */
@@ -41,6 +41,7 @@ export default class Document extends EmitterMixin() {
41
41
  this.selection = new DocumentSelection(this);
42
42
  this.roots = new Collection({ idProperty: 'rootName' });
43
43
  this.differ = new Differ(model.markers);
44
+ this.isReadOnly = false;
44
45
  this._postFixers = new Set();
45
46
  this._hasSelectionChangedFromTheLastChangeBlock = false;
46
47
  // Graveyard tree root. Document always have a graveyard root, which stores removed nodes.
@@ -137,9 +137,9 @@ export interface MarkerData {
137
137
  }
138
138
  declare const Marker_base: import("@ckeditor/ckeditor5-utils").Mixed<typeof TypeCheckable, import("@ckeditor/ckeditor5-utils").Emitter>;
139
139
  /**
140
- * `Marker` is a continuous part of model (like a range), is named and represent some kind of information about marked
141
- * part of model document. In contrary to {@link module:engine/model/node~Node nodes}, which are building blocks of
142
- * model document tree, markers are not stored directly in document tree but in
140
+ * `Marker` is a continuous part of the model (like a range), is named and represents some kind of information about the
141
+ * marked part of the model document. In contrary to {@link module:engine/model/node~Node nodes}, which are building blocks of
142
+ * the model document tree, markers are not stored directly in the document tree but in the
143
143
  * {@link module:engine/model/model~Model#markers model markers' collection}. Still, they are document data, by giving
144
144
  * additional meaning to the part of a model document between marker start and marker end.
145
145
  *
@@ -214,9 +214,9 @@ export default class MarkerCollection extends EmitterMixin() {
214
214
  }
215
215
  }
216
216
  /**
217
- * `Marker` is a continuous part of model (like a range), is named and represent some kind of information about marked
218
- * part of model document. In contrary to {@link module:engine/model/node~Node nodes}, which are building blocks of
219
- * model document tree, markers are not stored directly in document tree but in
217
+ * `Marker` is a continuous part of the model (like a range), is named and represents some kind of information about the
218
+ * marked part of the model document. In contrary to {@link module:engine/model/node~Node nodes}, which are building blocks of
219
+ * the model document tree, markers are not stored directly in the document tree but in the
220
220
  * {@link module:engine/model/model~Model#markers model markers' collection}. Still, they are document data, by giving
221
221
  * additional meaning to the part of a model document between marker start and marker end.
222
222
  *
@@ -553,6 +553,20 @@ export default class Model extends Model_base {
553
553
  ignoreWhitespaces?: boolean;
554
554
  ignoreMarkers?: boolean;
555
555
  }): boolean;
556
+ /**
557
+ * Check whether given selectable is at a place in the model where it can be edited (returns `true`) or not (returns `false`).
558
+ *
559
+ * Should be used instead of {@link module:core/editor/editor~Editor#isReadOnly} to check whether a user action can happen at
560
+ * given selectable. It may be decorated and used differently in different environment (e.g. multi-root editor can disable
561
+ * a particular root).
562
+ *
563
+ * This method is decorated. Although this method accepts any parameter of `Selectable` type, the
564
+ * {@link ~Model#event:canEditAt `canEditAt` event} is fired with `selectable` normalized to an instance of
565
+ * {@link module:engine/model/selection~Selection} or {@link module:engine/model/documentselection~DocumentSelection}
566
+ *
567
+ * @fires canEditAt
568
+ */
569
+ canEditAt(selectable: Selectable): boolean;
556
570
  /**
557
571
  * Creates a position from the given root and path in that root.
558
572
  *
@@ -856,7 +870,7 @@ export type ModelInsertObjectEvent = {
856
870
  * Event fired when {@link ~Model#deleteContent} method is called.
857
871
  *
858
872
  * The {@link ~Model#deleteContent default action of that method} is implemented as a
859
- * listener to this event so it can be fully customized by the features.
873
+ * listener to this event, so it can be fully customized by the features.
860
874
  *
861
875
  * @eventName ~Model#deleteContent
862
876
  * @param args The arguments passed to the original method.
@@ -866,7 +880,7 @@ export type ModelDeleteContentEvent = DecoratedMethodEvent<Model, 'deleteContent
866
880
  * Event fired when {@link ~Model#modifySelection} method is called.
867
881
  *
868
882
  * The {@link ~Model#modifySelection default action of that method} is implemented as a
869
- * listener to this event so it can be fully customized by the features.
883
+ * listener to this event, so it can be fully customized by the features.
870
884
  *
871
885
  * @eventName ~Model#modifySelection
872
886
  * @param args The arguments passed to the original method.
@@ -876,10 +890,31 @@ export type ModelModifySelectionEvent = DecoratedMethodEvent<Model, 'modifySelec
876
890
  * Event fired when {@link ~Model#getSelectedContent} method is called.
877
891
  *
878
892
  * The {@link ~Model#getSelectedContent default action of that method} is implemented as a
879
- * listener to this event so it can be fully customized by the features.
893
+ * listener to this event, so it can be fully customized by the features.
880
894
  *
881
895
  * @eventName ~Model#getSelectedContent
882
896
  * @param args The arguments passed to the original method.
883
897
  */
884
898
  export type ModelGetSelectedContentEvent = DecoratedMethodEvent<Model, 'getSelectedContent'>;
899
+ /**
900
+ * Event fired when {@link ~Model#canEditAt} method is called.
901
+ *
902
+ * The {@link ~Model#canEditAt default action of that method} is implemented as a
903
+ * listener to this event, so it can be fully customized by the features.
904
+ *
905
+ * Although the original method accepts any parameter of `Selectable` type, this event is fired with `selectable` normalized
906
+ * to an instance of {@link module:engine/model/selection~Selection} or {@link module:engine/model/documentselection~DocumentSelection}.
907
+ *
908
+ * @eventName ~Model#canEditAt
909
+ * @param args The arguments passed to the original method.
910
+ */
911
+ export type ModelCanEditAtEvent = {
912
+ name: 'canEditAt';
913
+ args: [
914
+ [
915
+ selectable?: ModelSelection | DocumentSelection
916
+ ]
917
+ ];
918
+ return: boolean;
919
+ };
885
920
  export {};
@@ -106,6 +106,15 @@ export default class Model extends ObservableMixin() {
106
106
  this.on('insertObject', (evt, [element, selection, options]) => {
107
107
  evt.return = insertObject(this, element, selection, options);
108
108
  });
109
+ // The base implementation for "decorated" method with remapped arguments.
110
+ this.on('canEditAt', evt => {
111
+ const canEditAt = !this.document.isReadOnly;
112
+ evt.return = canEditAt;
113
+ if (!canEditAt) {
114
+ // Prevent further processing if the selection is at non-editable place.
115
+ evt.stop();
116
+ }
117
+ });
109
118
  // @if CK_DEBUG_ENGINE // initDocumentDumping( this.document );
110
119
  // @if CK_DEBUG_ENGINE // this.on( 'applyOperation', () => {
111
120
  // @if CK_DEBUG_ENGINE // dumpTrees( this.document, this.document.version );
@@ -619,6 +628,23 @@ export default class Model extends ObservableMixin() {
619
628
  }
620
629
  return false;
621
630
  }
631
+ /**
632
+ * Check whether given selectable is at a place in the model where it can be edited (returns `true`) or not (returns `false`).
633
+ *
634
+ * Should be used instead of {@link module:core/editor/editor~Editor#isReadOnly} to check whether a user action can happen at
635
+ * given selectable. It may be decorated and used differently in different environment (e.g. multi-root editor can disable
636
+ * a particular root).
637
+ *
638
+ * This method is decorated. Although this method accepts any parameter of `Selectable` type, the
639
+ * {@link ~Model#event:canEditAt `canEditAt` event} is fired with `selectable` normalized to an instance of
640
+ * {@link module:engine/model/selection~Selection} or {@link module:engine/model/documentselection~DocumentSelection}
641
+ *
642
+ * @fires canEditAt
643
+ */
644
+ canEditAt(selectable) {
645
+ const selection = normalizeSelectable(selectable);
646
+ return this.fire('canEditAt', [selection]);
647
+ }
622
648
  /**
623
649
  * Creates a position from the given root and path in that root.
624
650
  *
@@ -803,7 +829,15 @@ function normalizeSelectable(selectable, placeOrOffset) {
803
829
  return selectable;
804
830
  }
805
831
  if (selectable instanceof Node) {
806
- return new ModelSelection(selectable, placeOrOffset);
832
+ if (placeOrOffset || placeOrOffset === 0) {
833
+ return new ModelSelection(selectable, placeOrOffset);
834
+ }
835
+ else if (selectable.is('rootElement')) {
836
+ return new ModelSelection(selectable, 'in');
837
+ }
838
+ else {
839
+ return new ModelSelection(selectable, 'on');
840
+ }
807
841
  }
808
842
  return new ModelSelection(selectable);
809
843
  }
@@ -8,6 +8,7 @@
8
8
  import Operation from './operation';
9
9
  import Range from '../range';
10
10
  import type Document from '../document';
11
+ import type { Selectable } from '../selection';
11
12
  /**
12
13
  * Operation to change nodes' attribute.
13
14
  *
@@ -62,6 +63,10 @@ export default class AttributeOperation extends Operation {
62
63
  * @inheritDoc
63
64
  */
64
65
  get type(): 'addAttribute' | 'removeAttribute' | 'changeAttribute';
66
+ /**
67
+ * @inheritDoc
68
+ */
69
+ get affectedSelectable(): Selectable;
65
70
  /**
66
71
  * Creates and returns an operation that has the same parameters as this operation.
67
72
  */
@@ -56,6 +56,12 @@ export default class AttributeOperation extends Operation {
56
56
  return 'changeAttribute';
57
57
  }
58
58
  }
59
+ /**
60
+ * @inheritDoc
61
+ */
62
+ get affectedSelectable() {
63
+ return this.range.clone();
64
+ }
59
65
  /**
60
66
  * Creates and returns an operation that has the same parameters as this operation.
61
67
  */
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import Operation from './operation';
9
9
  import type Position from '../position';
10
+ import type { Selectable } from '../selection';
10
11
  /**
11
12
  * Operation to permanently remove node from detached root.
12
13
  * Note this operation is only a local operation and won't be send to the other clients.
@@ -34,6 +35,10 @@ export default class DetachOperation extends Operation {
34
35
  * @inheritDoc
35
36
  */
36
37
  get type(): 'detach';
38
+ /**
39
+ * @inheritDoc
40
+ */
41
+ get affectedSelectable(): Selectable;
37
42
  /**
38
43
  * @inheritDoc
39
44
  */
@@ -33,6 +33,12 @@ export default class DetachOperation extends Operation {
33
33
  get type() {
34
34
  return 'detach';
35
35
  }
36
+ /**
37
+ * @inheritDoc
38
+ */
39
+ get affectedSelectable() {
40
+ return null;
41
+ }
36
42
  /**
37
43
  * @inheritDoc
38
44
  */
@@ -9,6 +9,7 @@ import Operation from './operation';
9
9
  import Position from '../position';
10
10
  import NodeList from '../nodelist';
11
11
  import { type NodeSet } from './utils';
12
+ import type { Selectable } from '../selection';
12
13
  import type Document from '../document';
13
14
  /**
14
15
  * Operation to insert one or more nodes at given position in the model.
@@ -49,6 +50,10 @@ export default class InsertOperation extends Operation {
49
50
  * Total offset size of inserted nodes.
50
51
  */
51
52
  get howMany(): number;
53
+ /**
54
+ * @inheritDoc
55
+ */
56
+ get affectedSelectable(): Selectable;
52
57
  /**
53
58
  * Creates and returns an operation that has the same parameters as this operation.
54
59
  */
@@ -44,6 +44,12 @@ export default class InsertOperation extends Operation {
44
44
  get howMany() {
45
45
  return this.nodes.maxOffset;
46
46
  }
47
+ /**
48
+ * @inheritDoc
49
+ */
50
+ get affectedSelectable() {
51
+ return this.position.clone();
52
+ }
47
53
  /**
48
54
  * Creates and returns an operation that has the same parameters as this operation.
49
55
  */
@@ -9,6 +9,7 @@ import Operation from './operation';
9
9
  import Range from '../range';
10
10
  import type Document from '../document';
11
11
  import type MarkerCollection from '../markercollection';
12
+ import type { Selectable } from '../selection';
12
13
  export default class MarkerOperation extends Operation {
13
14
  /**
14
15
  * Marker name.
@@ -54,6 +55,10 @@ export default class MarkerOperation extends Operation {
54
55
  * @inheritDoc
55
56
  */
56
57
  get type(): 'marker';
58
+ /**
59
+ * @inheritDoc
60
+ */
61
+ get affectedSelectable(): Selectable;
57
62
  /**
58
63
  * Creates and returns an operation that has the same parameters as this operation.
59
64
  */
@@ -32,6 +32,24 @@ export default class MarkerOperation extends Operation {
32
32
  get type() {
33
33
  return 'marker';
34
34
  }
35
+ /**
36
+ * @inheritDoc
37
+ */
38
+ get affectedSelectable() {
39
+ const ranges = [];
40
+ if (this.oldRange) {
41
+ ranges.push(this.oldRange.clone());
42
+ }
43
+ if (this.newRange) {
44
+ if (this.oldRange) {
45
+ ranges.push(...this.newRange.getDifference(this.oldRange));
46
+ }
47
+ else {
48
+ ranges.push(this.newRange.clone());
49
+ }
50
+ }
51
+ return ranges;
52
+ }
35
53
  /**
36
54
  * Creates and returns an operation that has the same parameters as this operation.
37
55
  */
@@ -9,6 +9,7 @@ import Operation from './operation';
9
9
  import Position from '../position';
10
10
  import Range from '../range';
11
11
  import type Document from '../document';
12
+ import type { Selectable } from '../selection';
12
13
  /**
13
14
  * Operation to merge two {@link module:engine/model/element~Element elements}.
14
15
  *
@@ -59,6 +60,10 @@ export default class MergeOperation extends Operation {
59
60
  * The range starts at {@link ~MergeOperation#sourcePosition} and ends in the same parent, at `POSITIVE_INFINITY` offset.
60
61
  */
61
62
  get movedRange(): Range;
63
+ /**
64
+ * @inheritDoc
65
+ */
66
+ get affectedSelectable(): Selectable;
62
67
  /**
63
68
  * Creates and returns an operation that has the same parameters as this operation.
64
69
  */
@@ -63,6 +63,18 @@ export default class MergeOperation extends Operation {
63
63
  const end = this.sourcePosition.getShiftedBy(Number.POSITIVE_INFINITY);
64
64
  return new Range(this.sourcePosition, end);
65
65
  }
66
+ /**
67
+ * @inheritDoc
68
+ */
69
+ get affectedSelectable() {
70
+ const mergedElement = this.sourcePosition.parent;
71
+ return [
72
+ Range._createOn(mergedElement),
73
+ // These could be positions but `Selectable` type only supports `Iterable<Range>`.
74
+ Range._createFromPositionAndShift(this.targetPosition, 0),
75
+ Range._createFromPositionAndShift(this.graveyardPosition, 0)
76
+ ];
77
+ }
66
78
  /**
67
79
  * Creates and returns an operation that has the same parameters as this operation.
68
80
  */
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import Operation from './operation';
9
9
  import Position from '../position';
10
+ import type { Selectable } from '../selection';
10
11
  import type Document from '../document';
11
12
  /**
12
13
  * Operation to move a range of {@link module:engine/model/item~Item model items}
@@ -40,6 +41,10 @@ export default class MoveOperation extends Operation {
40
41
  * @inheritDoc
41
42
  */
42
43
  get type(): 'move' | 'remove' | 'reinsert';
44
+ /**
45
+ * @inheritDoc
46
+ */
47
+ get affectedSelectable(): Selectable;
43
48
  /**
44
49
  * Creates and returns an operation that has the same parameters as this operation.
45
50
  */
@@ -47,6 +47,15 @@ export default class MoveOperation extends Operation {
47
47
  }
48
48
  return 'move';
49
49
  }
50
+ /**
51
+ * @inheritDoc
52
+ */
53
+ get affectedSelectable() {
54
+ return [
55
+ Range._createFromPositionAndShift(this.sourcePosition, this.howMany),
56
+ Range._createFromPositionAndShift(this.targetPosition, 0)
57
+ ];
58
+ }
50
59
  /**
51
60
  * Creates and returns an operation that has the same parameters as this operation.
52
61
  */
@@ -6,6 +6,7 @@
6
6
  * @module engine/model/operation/nooperation
7
7
  */
8
8
  import Operation from './operation';
9
+ import type { Selectable } from '../selection';
9
10
  /**
10
11
  * Operation which is doing nothing ("empty operation", "do-nothing operation", "noop"). This is an operation,
11
12
  * which when executed does not change the tree model. It still has some parameters defined for transformation purposes.
@@ -16,6 +17,10 @@ import Operation from './operation';
16
17
  */
17
18
  export default class NoOperation extends Operation {
18
19
  get type(): 'noop';
20
+ /**
21
+ * @inheritDoc
22
+ */
23
+ get affectedSelectable(): Selectable;
19
24
  /**
20
25
  * Creates and returns an operation that has the same parameters as this operation.
21
26
  */
@@ -18,6 +18,12 @@ export default class NoOperation extends Operation {
18
18
  get type() {
19
19
  return 'noop';
20
20
  }
21
+ /**
22
+ * @inheritDoc
23
+ */
24
+ get affectedSelectable() {
25
+ return null;
26
+ }
21
27
  /**
22
28
  * Creates and returns an operation that has the same parameters as this operation.
23
29
  */
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import type Batch from '../batch';
6
6
  import type Document from '../document';
7
+ import type { Selectable } from '../selection';
7
8
  /**
8
9
  * @module engine/model/operation/operation
9
10
  */
@@ -38,6 +39,12 @@ export default abstract class Operation {
38
39
  * can be applied or `null` if the operation operates on detached (non-document) tree.
39
40
  */
40
41
  constructor(baseVersion: number | null);
42
+ /**
43
+ * A selectable that will be affected by the operation after it is executed.
44
+ *
45
+ * The exact returned parameter differs between operation types.
46
+ */
47
+ abstract get affectedSelectable(): Selectable;
41
48
  /**
42
49
  * Creates and returns an operation that has the same parameters as this operation.
43
50
  *
@@ -8,6 +8,7 @@
8
8
  import Operation from './operation';
9
9
  import Position from '../position';
10
10
  import type Document from '../document';
11
+ import type { Selectable } from '../selection';
11
12
  /**
12
13
  * Operation to change element's name.
13
14
  *
@@ -40,6 +41,10 @@ export default class RenameOperation extends Operation {
40
41
  * @inheritDoc
41
42
  */
42
43
  get type(): 'rename';
44
+ /**
45
+ * @inheritDoc
46
+ */
47
+ get affectedSelectable(): Selectable;
43
48
  /**
44
49
  * Creates and returns an operation that has the same parameters as this operation.
45
50
  *
@@ -38,6 +38,12 @@ export default class RenameOperation extends Operation {
38
38
  get type() {
39
39
  return 'rename';
40
40
  }
41
+ /**
42
+ * @inheritDoc
43
+ */
44
+ get affectedSelectable() {
45
+ return this.position.nodeAfter;
46
+ }
41
47
  /**
42
48
  * Creates and returns an operation that has the same parameters as this operation.
43
49
  *
@@ -8,6 +8,7 @@
8
8
  import Operation from './operation';
9
9
  import type Document from '../document';
10
10
  import type RootElement from '../rootelement';
11
+ import type { Selectable } from '../selection';
11
12
  /**
12
13
  * Operation to change root element's attribute. Using this class you can add, remove or change value of the attribute.
13
14
  *
@@ -55,6 +56,10 @@ export default class RootAttributeOperation extends Operation {
55
56
  * @inheritDoc
56
57
  */
57
58
  get type(): 'addRootAttribute' | 'removeRootAttribute' | 'changeRootAttribute';
59
+ /**
60
+ * @inheritDoc
61
+ */
62
+ get affectedSelectable(): Selectable;
58
63
  /**
59
64
  * Creates and returns an operation that has the same parameters as this operation.
60
65
  *
@@ -50,6 +50,12 @@ export default class RootAttributeOperation extends Operation {
50
50
  return 'changeRootAttribute';
51
51
  }
52
52
  }
53
+ /**
54
+ * @inheritDoc
55
+ */
56
+ get affectedSelectable() {
57
+ return this.root;
58
+ }
53
59
  /**
54
60
  * Creates and returns an operation that has the same parameters as this operation.
55
61
  *
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import Operation from './operation';
9
9
  import type Document from '../document';
10
+ import type { Selectable } from '../selection';
10
11
  /**
11
12
  * Operation that creates (or attaches) or detaches a root element.
12
13
  */
@@ -41,6 +42,10 @@ export default class RootOperation extends Operation {
41
42
  * @inheritDoc
42
43
  */
43
44
  get type(): 'addRoot' | 'detachRoot';
45
+ /**
46
+ * @inheritDoc
47
+ */
48
+ get affectedSelectable(): Selectable;
44
49
  /**
45
50
  * @inheritDoc
46
51
  */
@@ -41,6 +41,12 @@ export default class RootOperation extends Operation {
41
41
  get type() {
42
42
  return this.isAdd ? 'addRoot' : 'detachRoot';
43
43
  }
44
+ /**
45
+ * @inheritDoc
46
+ */
47
+ get affectedSelectable() {
48
+ return this._document.getRoot(this.rootName);
49
+ }
44
50
  /**
45
51
  * @inheritDoc
46
52
  */
@@ -9,6 +9,7 @@ import Operation from './operation';
9
9
  import Position from '../position';
10
10
  import Range from '../range';
11
11
  import type Document from '../document';
12
+ import type { Selectable } from '../selection';
12
13
  /**
13
14
  * Operation to split {@link module:engine/model/element~Element an element} at given
14
15
  * {@link module:engine/model/operation/splitoperation~SplitOperation#splitPosition split position} into two elements,
@@ -61,6 +62,10 @@ export default class SplitOperation extends Operation {
61
62
  * The range starts at {@link #splitPosition} and ends in the same parent, at `POSITIVE_INFINITY` offset.
62
63
  */
63
64
  get movedRange(): Range;
65
+ /**
66
+ * @inheritDoc
67
+ */
68
+ get affectedSelectable(): Selectable;
64
69
  /**
65
70
  * Creates and returns an operation that has the same parameters as this operation.
66
71
  *
@@ -65,6 +65,20 @@ export default class SplitOperation extends Operation {
65
65
  const end = this.splitPosition.getShiftedBy(Number.POSITIVE_INFINITY);
66
66
  return new Range(this.splitPosition, end);
67
67
  }
68
+ /**
69
+ * @inheritDoc
70
+ */
71
+ get affectedSelectable() {
72
+ // These could be positions but `Selectable` type only supports `Iterable<Range>`.
73
+ const ranges = [
74
+ Range._createFromPositionAndShift(this.splitPosition, 0),
75
+ Range._createFromPositionAndShift(this.insertionPosition, 0)
76
+ ];
77
+ if (this.graveyardPosition) {
78
+ ranges.push(Range._createFromPositionAndShift(this.graveyardPosition, 0));
79
+ }
80
+ return ranges;
81
+ }
68
82
  /**
69
83
  * Creates and returns an operation that has the same parameters as this operation.
70
84
  *
@@ -719,7 +719,7 @@ function isTopBlockInRange(block, range) {
719
719
  return !isParentInRange;
720
720
  }
721
721
  /**
722
- * If a selection starts at the end of a block, that block is not returned as from user perspective this block wasn't selected.
722
+ * If a selection starts at the end of a block, that block is not returned as from the user's perspective this block wasn't selected.
723
723
  * See [#11585](https://github.com/ckeditor/ckeditor5/issues/11585) for more details.
724
724
  *
725
725
  * ```xml
@@ -747,7 +747,7 @@ function isStartBlockSelected(startBlock, range) {
747
747
  return isTopBlockInRange(startBlock, range);
748
748
  }
749
749
  /**
750
- * If a selection ends at the beginning of a block, that block is not returned as from user perspective this block wasn't selected.
750
+ * If a selection ends at the beginning of a block, that block is not returned as from the user's perspective this block wasn't selected.
751
751
  * See [#984](https://github.com/ckeditor/ckeditor5-engine/issues/984) for more details.
752
752
  *
753
753
  * ```xml
@@ -472,7 +472,7 @@ export default class Writer {
472
472
  * @param element The element to rename.
473
473
  * @param newName New element name.
474
474
  */
475
- rename(element: Element, newName: string): void;
475
+ rename(element: Element | DocumentFragment, newName: string): void;
476
476
  /**
477
477
  * Splits elements starting from the given position and going to the top of the model tree as long as given
478
478
  * `limitElement` is reached. When `limitElement` is not defined then only the parent of the given position will be split.
@@ -59,6 +59,10 @@ export default class DataTransfer {
59
59
  */
60
60
  set dropEffect(value: DropEffect);
61
61
  get dropEffect(): DropEffect;
62
+ /**
63
+ * Set a preview image of the dragged content.
64
+ */
65
+ setDragImage(image: Element, x: number, y: number): void;
62
66
  /**
63
67
  * Whether the dragging operation was canceled.
64
68
  */
@@ -71,6 +71,12 @@ export default class DataTransfer {
71
71
  get dropEffect() {
72
72
  return this._native.dropEffect;
73
73
  }
74
+ /**
75
+ * Set a preview image of the dragged content.
76
+ */
77
+ setDragImage(image, x, y) {
78
+ this._native.setDragImage(image, x, y);
79
+ }
74
80
  /**
75
81
  * Whether the dragging operation was canceled.
76
82
  */
@@ -16,7 +16,7 @@ import ViewDocumentFragment from './documentfragment';
16
16
  import ViewTreeWalker from './treewalker';
17
17
  import { default as Matcher } from './matcher';
18
18
  import { BR_FILLER, INLINE_FILLER_LENGTH, NBSP_FILLER, MARKED_NBSP_FILLER, getDataWithoutFiller, isInlineFiller, startsWithFiller } from './filler';
19
- import { global, logWarning, indexOf, getAncestors, isText, isComment, first } from '@ckeditor/ckeditor5-utils';
19
+ import { global, logWarning, indexOf, getAncestors, isText, isComment, isValidAttributeName, first } from '@ckeditor/ckeditor5-utils';
20
20
  const BR_FILLER_REF = BR_FILLER(global.document); // eslint-disable-line new-cap
21
21
  const NBSP_FILLER_REF = NBSP_FILLER(global.document); // eslint-disable-line new-cap
22
22
  const MARKED_NBSP_FILLER_REF = MARKED_NBSP_FILLER(global.document); // eslint-disable-line new-cap
@@ -305,6 +305,15 @@ export default class DomConverter {
305
305
  if (!shouldRenderAttribute) {
306
306
  logWarning('domconverter-unsafe-attribute-detected', { domElement, key, value });
307
307
  }
308
+ if (!isValidAttributeName(key)) {
309
+ /**
310
+ * Invalid attribute name was ignored during rendering.
311
+ *
312
+ * @error domconverter-invalid-attribute-detected
313
+ */
314
+ logWarning('domconverter-invalid-attribute-detected', { domElement, key, value });
315
+ return;
316
+ }
308
317
  // The old value was safe but the new value is unsafe.
309
318
  if (domElement.hasAttribute(key) && !shouldRenderAttribute) {
310
319
  domElement.removeAttribute(key);