@ckeditor/ckeditor5-engine 30.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.
Files changed (117) hide show
  1. package/LICENSE.md +17 -0
  2. package/README.md +30 -0
  3. package/package.json +70 -0
  4. package/src/controller/datacontroller.js +563 -0
  5. package/src/controller/editingcontroller.js +149 -0
  6. package/src/conversion/conversion.js +644 -0
  7. package/src/conversion/conversionhelpers.js +40 -0
  8. package/src/conversion/downcastdispatcher.js +914 -0
  9. package/src/conversion/downcasthelpers.js +1706 -0
  10. package/src/conversion/mapper.js +696 -0
  11. package/src/conversion/modelconsumable.js +329 -0
  12. package/src/conversion/upcastdispatcher.js +807 -0
  13. package/src/conversion/upcasthelpers.js +997 -0
  14. package/src/conversion/viewconsumable.js +623 -0
  15. package/src/dataprocessor/basichtmlwriter.js +32 -0
  16. package/src/dataprocessor/dataprocessor.jsdoc +64 -0
  17. package/src/dataprocessor/htmldataprocessor.js +159 -0
  18. package/src/dataprocessor/htmlwriter.js +22 -0
  19. package/src/dataprocessor/xmldataprocessor.js +161 -0
  20. package/src/dev-utils/model.js +482 -0
  21. package/src/dev-utils/operationreplayer.js +140 -0
  22. package/src/dev-utils/utils.js +103 -0
  23. package/src/dev-utils/view.js +1091 -0
  24. package/src/index.js +52 -0
  25. package/src/model/batch.js +82 -0
  26. package/src/model/differ.js +1282 -0
  27. package/src/model/document.js +483 -0
  28. package/src/model/documentfragment.js +390 -0
  29. package/src/model/documentselection.js +1261 -0
  30. package/src/model/element.js +438 -0
  31. package/src/model/history.js +138 -0
  32. package/src/model/item.jsdoc +14 -0
  33. package/src/model/liveposition.js +182 -0
  34. package/src/model/liverange.js +221 -0
  35. package/src/model/markercollection.js +553 -0
  36. package/src/model/model.js +934 -0
  37. package/src/model/node.js +507 -0
  38. package/src/model/nodelist.js +217 -0
  39. package/src/model/operation/attributeoperation.js +202 -0
  40. package/src/model/operation/detachoperation.js +103 -0
  41. package/src/model/operation/insertoperation.js +188 -0
  42. package/src/model/operation/markeroperation.js +154 -0
  43. package/src/model/operation/mergeoperation.js +216 -0
  44. package/src/model/operation/moveoperation.js +209 -0
  45. package/src/model/operation/nooperation.js +58 -0
  46. package/src/model/operation/operation.js +139 -0
  47. package/src/model/operation/operationfactory.js +49 -0
  48. package/src/model/operation/renameoperation.js +155 -0
  49. package/src/model/operation/rootattributeoperation.js +211 -0
  50. package/src/model/operation/splitoperation.js +254 -0
  51. package/src/model/operation/transform.js +2389 -0
  52. package/src/model/operation/utils.js +292 -0
  53. package/src/model/position.js +1164 -0
  54. package/src/model/range.js +1049 -0
  55. package/src/model/rootelement.js +111 -0
  56. package/src/model/schema.js +1851 -0
  57. package/src/model/selection.js +902 -0
  58. package/src/model/text.js +138 -0
  59. package/src/model/textproxy.js +279 -0
  60. package/src/model/treewalker.js +414 -0
  61. package/src/model/utils/autoparagraphing.js +77 -0
  62. package/src/model/utils/deletecontent.js +528 -0
  63. package/src/model/utils/getselectedcontent.js +150 -0
  64. package/src/model/utils/insertcontent.js +824 -0
  65. package/src/model/utils/modifyselection.js +229 -0
  66. package/src/model/utils/selection-post-fixer.js +297 -0
  67. package/src/model/writer.js +1574 -0
  68. package/src/view/attributeelement.js +274 -0
  69. package/src/view/containerelement.js +123 -0
  70. package/src/view/document.js +221 -0
  71. package/src/view/documentfragment.js +273 -0
  72. package/src/view/documentselection.js +387 -0
  73. package/src/view/domconverter.js +1437 -0
  74. package/src/view/downcastwriter.js +2121 -0
  75. package/src/view/editableelement.js +118 -0
  76. package/src/view/element.js +945 -0
  77. package/src/view/elementdefinition.jsdoc +59 -0
  78. package/src/view/emptyelement.js +119 -0
  79. package/src/view/filler.js +161 -0
  80. package/src/view/item.jsdoc +14 -0
  81. package/src/view/matcher.js +776 -0
  82. package/src/view/node.js +391 -0
  83. package/src/view/observer/arrowkeysobserver.js +58 -0
  84. package/src/view/observer/bubblingemittermixin.js +307 -0
  85. package/src/view/observer/bubblingeventinfo.js +71 -0
  86. package/src/view/observer/clickobserver.js +46 -0
  87. package/src/view/observer/compositionobserver.js +79 -0
  88. package/src/view/observer/domeventdata.js +82 -0
  89. package/src/view/observer/domeventobserver.js +99 -0
  90. package/src/view/observer/fakeselectionobserver.js +118 -0
  91. package/src/view/observer/focusobserver.js +106 -0
  92. package/src/view/observer/inputobserver.js +44 -0
  93. package/src/view/observer/keyobserver.js +83 -0
  94. package/src/view/observer/mouseobserver.js +56 -0
  95. package/src/view/observer/mutationobserver.js +345 -0
  96. package/src/view/observer/observer.js +118 -0
  97. package/src/view/observer/selectionobserver.js +242 -0
  98. package/src/view/placeholder.js +285 -0
  99. package/src/view/position.js +426 -0
  100. package/src/view/range.js +533 -0
  101. package/src/view/rawelement.js +148 -0
  102. package/src/view/renderer.js +1037 -0
  103. package/src/view/rooteditableelement.js +107 -0
  104. package/src/view/selection.js +718 -0
  105. package/src/view/styles/background.js +73 -0
  106. package/src/view/styles/border.js +362 -0
  107. package/src/view/styles/margin.js +41 -0
  108. package/src/view/styles/padding.js +40 -0
  109. package/src/view/styles/utils.js +277 -0
  110. package/src/view/stylesmap.js +938 -0
  111. package/src/view/text.js +147 -0
  112. package/src/view/textproxy.js +199 -0
  113. package/src/view/treewalker.js +496 -0
  114. package/src/view/uielement.js +238 -0
  115. package/src/view/upcastwriter.js +484 -0
  116. package/src/view/view.js +721 -0
  117. package/theme/placeholder.css +27 -0
@@ -0,0 +1,139 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+
6
+ /**
7
+ * @module engine/model/operation/operation
8
+ */
9
+
10
+ /**
11
+ * Abstract base operation class.
12
+ *
13
+ * @abstract
14
+ */
15
+ export default class Operation {
16
+ /**
17
+ * Base operation constructor.
18
+ *
19
+ * @param {Number|null} baseVersion Document {@link module:engine/model/document~Document#version} on which operation
20
+ * can be applied or `null` if the operation operates on detached (non-document) tree.
21
+ */
22
+ constructor( baseVersion ) {
23
+ /**
24
+ * {@link module:engine/model/document~Document#version} on which operation can be applied. If you try to
25
+ * {@link module:engine/model/model~Model#applyOperation apply} operation with different base version than the
26
+ * {@link module:engine/model/document~Document#version document version} the
27
+ * {@link module:utils/ckeditorerror~CKEditorError model-document-applyOperation-wrong-version} error is thrown.
28
+ *
29
+ * @member {Number}
30
+ */
31
+ this.baseVersion = baseVersion;
32
+
33
+ /**
34
+ * Defines whether operation is executed on attached or detached {@link module:engine/model/item~Item items}.
35
+ *
36
+ * @readonly
37
+ * @member {Boolean} #isDocumentOperation
38
+ */
39
+ this.isDocumentOperation = this.baseVersion !== null;
40
+
41
+ /**
42
+ * {@link module:engine/model/batch~Batch Batch} to which the operation is added or `null` if the operation is not
43
+ * added to any batch yet.
44
+ *
45
+ * @member {module:engine/model/batch~Batch|null} #batch
46
+ */
47
+ this.batch = null;
48
+
49
+ /**
50
+ * Operation type.
51
+ *
52
+ * @readonly
53
+ * @member {String} #type
54
+ */
55
+
56
+ /**
57
+ * Creates and returns an operation that has the same parameters as this operation.
58
+ *
59
+ * @method #clone
60
+ * @returns {module:engine/model/operation/operation~Operation} Clone of this operation.
61
+ */
62
+
63
+ /**
64
+ * Creates and returns a reverse operation. Reverse operation when executed right after
65
+ * the original operation will bring back tree model state to the point before the original
66
+ * operation execution. In other words, it reverses changes done by the original operation.
67
+ *
68
+ * Keep in mind that tree model state may change since executing the original operation,
69
+ * so reverse operation will be "outdated". In that case you will need to transform it by
70
+ * all operations that were executed after the original operation.
71
+ *
72
+ * @method #getReversed
73
+ * @returns {module:engine/model/operation/operation~Operation} Reversed operation.
74
+ */
75
+
76
+ /**
77
+ * Executes the operation - modifications described by the operation properties will be applied to the model tree.
78
+ *
79
+ * @protected
80
+ * @method #_execute
81
+ */
82
+ }
83
+
84
+ /**
85
+ * Checks whether the operation's parameters are correct and the operation can be correctly executed. Throws
86
+ * an error if operation is not valid.
87
+ *
88
+ * @protected
89
+ * @method #_validate
90
+ */
91
+ _validate() {
92
+ }
93
+
94
+ /**
95
+ * Custom toJSON method to solve child-parent circular dependencies.
96
+ *
97
+ * @method #toJSON
98
+ * @returns {Object} Clone of this object with the operation property replaced with string.
99
+ */
100
+ toJSON() {
101
+ // This method creates only a shallow copy, all nested objects should be defined separately.
102
+ // See https://github.com/ckeditor/ckeditor5-engine/issues/1477.
103
+ const json = Object.assign( {}, this );
104
+
105
+ json.__className = this.constructor.className;
106
+
107
+ // Remove reference to the parent `Batch` to avoid circular dependencies.
108
+ delete json.batch;
109
+
110
+ // Only document operations are shared with other clients so it is not necessary to keep this information.
111
+ delete json.isDocumentOperation;
112
+
113
+ return json;
114
+ }
115
+
116
+ /**
117
+ * Name of the operation class used for serialization.
118
+ *
119
+ * @type {String}
120
+ */
121
+ static get className() {
122
+ return 'Operation';
123
+ }
124
+
125
+ /**
126
+ * Creates Operation object from deserilized object, i.e. from parsed JSON string.
127
+ *
128
+ * @param {Object} json Deserialized JSON object.
129
+ * @param {module:engine/model/document~Document} doc Document on which this operation will be applied.
130
+ * @returns {module:engine/model/operation/operation~Operation}
131
+ */
132
+ static fromJSON( json ) {
133
+ return new this( json.baseVersion );
134
+ }
135
+
136
+ // @if CK_DEBUG_ENGINE // log() {
137
+ // @if CK_DEBUG_ENGINE // console.log( this.toString() );
138
+ // @if CK_DEBUG_ENGINE // }
139
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+
6
+ /**
7
+ * @module engine/model/operation/operationfactory
8
+ */
9
+
10
+ import AttributeOperation from '../operation/attributeoperation';
11
+ import InsertOperation from '../operation/insertoperation';
12
+ import MarkerOperation from '../operation/markeroperation';
13
+ import MoveOperation from '../operation/moveoperation';
14
+ import NoOperation from '../operation/nooperation';
15
+ import Operation from '../operation/operation';
16
+ import RenameOperation from '../operation/renameoperation';
17
+ import RootAttributeOperation from '../operation/rootattributeoperation';
18
+ import SplitOperation from '../operation/splitoperation';
19
+ import MergeOperation from '../operation/mergeoperation';
20
+
21
+ const operations = {};
22
+ operations[ AttributeOperation.className ] = AttributeOperation;
23
+ operations[ InsertOperation.className ] = InsertOperation;
24
+ operations[ MarkerOperation.className ] = MarkerOperation;
25
+ operations[ MoveOperation.className ] = MoveOperation;
26
+ operations[ NoOperation.className ] = NoOperation;
27
+ operations[ Operation.className ] = Operation;
28
+ operations[ RenameOperation.className ] = RenameOperation;
29
+ operations[ RootAttributeOperation.className ] = RootAttributeOperation;
30
+ operations[ SplitOperation.className ] = SplitOperation;
31
+ operations[ MergeOperation.className ] = MergeOperation;
32
+
33
+ /**
34
+ * A factory class for creating operations.
35
+ *
36
+ * @abstract
37
+ */
38
+ export default class OperationFactory {
39
+ /**
40
+ * Creates an operation instance from a JSON object (parsed JSON string).
41
+ *
42
+ * @param {Object} json Deserialized JSON object.
43
+ * @param {module:engine/model/document~Document} document Document on which this operation will be applied.
44
+ * @returns {module:engine/model/operation/operation~Operation}
45
+ */
46
+ static fromJSON( json, document ) {
47
+ return operations[ json.__className ].fromJSON( json, document );
48
+ }
49
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+
6
+ /**
7
+ * @module engine/model/operation/renameoperation
8
+ */
9
+
10
+ import Operation from './operation';
11
+ import Element from '../element';
12
+ import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
13
+ import Position from '../position';
14
+
15
+ /**
16
+ * Operation to change element's name.
17
+ *
18
+ * Using this class you can change element's name.
19
+ *
20
+ * @extends module:engine/model/operation/operation~Operation
21
+ */
22
+ export default class RenameOperation extends Operation {
23
+ /**
24
+ * Creates an operation that changes element's name.
25
+ *
26
+ * @param {module:engine/model/position~Position} position Position before an element to change.
27
+ * @param {String} oldName Current name of the element.
28
+ * @param {String} newName New name for the element.
29
+ * @param {Number|null} baseVersion Document {@link module:engine/model/document~Document#version} on which operation
30
+ * can be applied or `null` if the operation operates on detached (non-document) tree.
31
+ */
32
+ constructor( position, oldName, newName, baseVersion ) {
33
+ super( baseVersion );
34
+
35
+ /**
36
+ * Position before an element to change.
37
+ *
38
+ * @member {module:engine/model/position~Position} module:engine/model/operation/renameoperation~RenameOperation#position
39
+ */
40
+ this.position = position;
41
+ // This position sticks to the next node because it is a position before the node that we want to change.
42
+ this.position.stickiness = 'toNext';
43
+
44
+ /**
45
+ * Current name of the element.
46
+ *
47
+ * @member {String} module:engine/model/operation/renameoperation~RenameOperation#oldName
48
+ */
49
+ this.oldName = oldName;
50
+
51
+ /**
52
+ * New name for the element.
53
+ *
54
+ * @member {String} module:engine/model/operation/renameoperation~RenameOperation#newName
55
+ */
56
+ this.newName = newName;
57
+ }
58
+
59
+ /**
60
+ * @inheritDoc
61
+ */
62
+ get type() {
63
+ return 'rename';
64
+ }
65
+
66
+ /**
67
+ * Creates and returns an operation that has the same parameters as this operation.
68
+ *
69
+ * @returns {module:engine/model/operation/renameoperation~RenameOperation} Clone of this operation.
70
+ */
71
+ clone() {
72
+ return new RenameOperation( this.position.clone(), this.oldName, this.newName, this.baseVersion );
73
+ }
74
+
75
+ /**
76
+ * See {@link module:engine/model/operation/operation~Operation#getReversed `Operation#getReversed()`}.
77
+ *
78
+ * @returns {module:engine/model/operation/renameoperation~RenameOperation}
79
+ */
80
+ getReversed() {
81
+ return new RenameOperation( this.position.clone(), this.newName, this.oldName, this.baseVersion + 1 );
82
+ }
83
+
84
+ /**
85
+ * @inheritDoc
86
+ */
87
+ _validate() {
88
+ const element = this.position.nodeAfter;
89
+
90
+ if ( !( element instanceof Element ) ) {
91
+ /**
92
+ * Given position is invalid or node after it is not instance of Element.
93
+ *
94
+ * @error rename-operation-wrong-position
95
+ */
96
+ throw new CKEditorError(
97
+ 'rename-operation-wrong-position',
98
+ this
99
+ );
100
+ } else if ( element.name !== this.oldName ) {
101
+ /**
102
+ * Element to change has different name than operation's old name.
103
+ *
104
+ * @error rename-operation-wrong-name
105
+ */
106
+ throw new CKEditorError(
107
+ 'rename-operation-wrong-name',
108
+ this
109
+ );
110
+ }
111
+ }
112
+
113
+ /**
114
+ * @inheritDoc
115
+ */
116
+ _execute() {
117
+ const element = this.position.nodeAfter;
118
+
119
+ element.name = this.newName;
120
+ }
121
+
122
+ /**
123
+ * @inheritDoc
124
+ */
125
+ toJSON() {
126
+ const json = super.toJSON();
127
+
128
+ json.position = this.position.toJSON();
129
+
130
+ return json;
131
+ }
132
+
133
+ /**
134
+ * @inheritDoc
135
+ */
136
+ static get className() {
137
+ return 'RenameOperation';
138
+ }
139
+
140
+ /**
141
+ * Creates `RenameOperation` object from deserialized object, i.e. from parsed JSON string.
142
+ *
143
+ * @param {Object} json Deserialized JSON object.
144
+ * @param {module:engine/model/document~Document} document Document on which this operation will be applied.
145
+ * @returns {module:engine/model/operation/attributeoperation~AttributeOperation}
146
+ */
147
+ static fromJSON( json, document ) {
148
+ return new RenameOperation( Position.fromJSON( json.position, document ), json.oldName, json.newName, json.baseVersion );
149
+ }
150
+
151
+ // @if CK_DEBUG_ENGINE // toString() {
152
+ // @if CK_DEBUG_ENGINE // return `RenameOperation( ${ this.baseVersion } ): ` +
153
+ // @if CK_DEBUG_ENGINE // `${ this.position }: "${ this.oldName }" -> "${ this.newName }"`;
154
+ // @if CK_DEBUG_ENGINE // }
155
+ }
@@ -0,0 +1,211 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+
6
+ /**
7
+ * @module engine/model/operation/rootattributeoperation
8
+ */
9
+
10
+ import Operation from './operation';
11
+ import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
12
+
13
+ /**
14
+ * Operation to change root element's attribute. Using this class you can add, remove or change value of the attribute.
15
+ *
16
+ * This operation is needed, because root elements can't be changed through
17
+ * @link module:engine/model/operation/attributeoperation~AttributeOperation}.
18
+ * It is because {@link module:engine/model/operation/attributeoperation~AttributeOperation}
19
+ * requires a range to change and root element can't
20
+ * be a part of range because every {@link module:engine/model/position~Position} has to be inside a root.
21
+ * {@link module:engine/model/position~Position} can't be created before a root element.
22
+ *
23
+ * @extends module:engine/model/operation/operation~Operation
24
+ */
25
+ export default class RootAttributeOperation extends Operation {
26
+ /**
27
+ * Creates an operation that changes, removes or adds attributes on root element.
28
+ *
29
+ * @see module:engine/model/operation/attributeoperation~AttributeOperation
30
+ * @param {module:engine/model/rootelement~RootElement} root Root element to change.
31
+ * @param {String} key Key of an attribute to change or remove.
32
+ * @param {*} oldValue Old value of the attribute with given key or `null` if adding a new attribute.
33
+ * @param {*} newValue New value to set for the attribute. If `null`, then the operation just removes the attribute.
34
+ * @param {Number|null} baseVersion Document {@link module:engine/model/document~Document#version} on which operation
35
+ * can be applied or `null` if the operation operates on detached (non-document) tree.
36
+ */
37
+ constructor( root, key, oldValue, newValue, baseVersion ) {
38
+ super( baseVersion );
39
+
40
+ /**
41
+ * Root element to change.
42
+ *
43
+ * @readonly
44
+ * @member {module:engine/model/rootelement~RootElement}
45
+ */
46
+ this.root = root;
47
+
48
+ /**
49
+ * Key of an attribute to change or remove.
50
+ *
51
+ * @readonly
52
+ * @member {String}
53
+ */
54
+ this.key = key;
55
+
56
+ /**
57
+ * Old value of the attribute with given key or `null` if adding a new attribute.
58
+ *
59
+ * @readonly
60
+ * @member {*}
61
+ */
62
+ this.oldValue = oldValue;
63
+
64
+ /**
65
+ * New value to set for the attribute. If `null`, then the operation just removes the attribute.
66
+ *
67
+ * @readonly
68
+ * @member {*}
69
+ */
70
+ this.newValue = newValue;
71
+ }
72
+
73
+ /**
74
+ * @inheritDoc
75
+ */
76
+ get type() {
77
+ if ( this.oldValue === null ) {
78
+ return 'addRootAttribute';
79
+ } else if ( this.newValue === null ) {
80
+ return 'removeRootAttribute';
81
+ } else {
82
+ return 'changeRootAttribute';
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Creates and returns an operation that has the same parameters as this operation.
88
+ *
89
+ * @returns {module:engine/model/operation/rootattributeoperation~RootAttributeOperation} Clone of this operation.
90
+ */
91
+ clone() {
92
+ return new RootAttributeOperation( this.root, this.key, this.oldValue, this.newValue, this.baseVersion );
93
+ }
94
+
95
+ /**
96
+ * See {@link module:engine/model/operation/operation~Operation#getReversed `Operation#getReversed()`}.
97
+ *
98
+ * @returns {module:engine/model/operation/rootattributeoperation~RootAttributeOperation}
99
+ */
100
+ getReversed() {
101
+ return new RootAttributeOperation( this.root, this.key, this.newValue, this.oldValue, this.baseVersion + 1 );
102
+ }
103
+
104
+ /**
105
+ * @inheritDoc
106
+ */
107
+ _validate() {
108
+ if ( this.root != this.root.root || this.root.is( 'documentFragment' ) ) {
109
+ /**
110
+ * The element to change is not a root element.
111
+ *
112
+ * @error rootattribute-operation-not-a-root
113
+ * @param {module:engine/model/rootelement~RootElement} root
114
+ * @param {String} key
115
+ * @param {*} value
116
+ */
117
+ throw new CKEditorError(
118
+ 'rootattribute-operation-not-a-root',
119
+ this,
120
+ { root: this.root, key: this.key }
121
+ );
122
+ }
123
+
124
+ if ( this.oldValue !== null && this.root.getAttribute( this.key ) !== this.oldValue ) {
125
+ /**
126
+ * The attribute which should be removed does not exists for the given node.
127
+ *
128
+ * @error rootattribute-operation-wrong-old-value
129
+ * @param {module:engine/model/rootelement~RootElement} root
130
+ * @param {String} key
131
+ * @param {*} value
132
+ */
133
+ throw new CKEditorError(
134
+ 'rootattribute-operation-wrong-old-value',
135
+ this,
136
+ { root: this.root, key: this.key }
137
+ );
138
+ }
139
+
140
+ if ( this.oldValue === null && this.newValue !== null && this.root.hasAttribute( this.key ) ) {
141
+ /**
142
+ * The attribute with given key already exists for the given node.
143
+ *
144
+ * @error rootattribute-operation-attribute-exists
145
+ * @param {module:engine/model/rootelement~RootElement} root
146
+ * @param {String} key
147
+ */
148
+ throw new CKEditorError(
149
+ 'rootattribute-operation-attribute-exists',
150
+ this,
151
+ { root: this.root, key: this.key }
152
+ );
153
+ }
154
+ }
155
+
156
+ /**
157
+ * @inheritDoc
158
+ */
159
+ _execute() {
160
+ if ( this.newValue !== null ) {
161
+ this.root._setAttribute( this.key, this.newValue );
162
+ } else {
163
+ this.root._removeAttribute( this.key );
164
+ }
165
+ }
166
+
167
+ /**
168
+ * @inheritDoc
169
+ */
170
+ toJSON() {
171
+ const json = super.toJSON();
172
+
173
+ json.root = this.root.toJSON();
174
+
175
+ return json;
176
+ }
177
+
178
+ /**
179
+ * @inheritDoc
180
+ */
181
+ static get className() {
182
+ return 'RootAttributeOperation';
183
+ }
184
+
185
+ /**
186
+ * Creates RootAttributeOperation object from deserilized object, i.e. from parsed JSON string.
187
+ *
188
+ * @param {Object} json Deserialized JSON object.
189
+ * @param {module:engine/model/document~Document} document Document on which this operation will be applied.
190
+ * @returns {module:engine/model/operation/rootattributeoperation~RootAttributeOperation}
191
+ */
192
+ static fromJSON( json, document ) {
193
+ if ( !document.getRoot( json.root ) ) {
194
+ /**
195
+ * Cannot create RootAttributeOperation for document. Root with specified name does not exist.
196
+ *
197
+ * @error rootattribute-operation-fromjson-no-root
198
+ * @param {String} rootName
199
+ */
200
+ throw new CKEditorError( 'rootattribute-operation-fromjson-no-root', this, { rootName: json.root } );
201
+ }
202
+
203
+ return new RootAttributeOperation( document.getRoot( json.root ), json.key, json.oldValue, json.newValue, json.baseVersion );
204
+ }
205
+
206
+ // @if CK_DEBUG_ENGINE // toString() {
207
+ // @if CK_DEBUG_ENGINE // return `RootAttributeOperation( ${ this.baseVersion } ): ` +
208
+ // @if CK_DEBUG_ENGINE // `"${ this.key }": ${ JSON.stringify( this.oldValue ) }` +
209
+ // @if CK_DEBUG_ENGINE // ` -> ${ JSON.stringify( this.newValue ) }, ${ this.root.rootName }`;
210
+ // @if CK_DEBUG_ENGINE // }
211
+ }