@ckeditor/ckeditor5-engine 47.6.1 → 48.0.0-alpha.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 (258) hide show
  1. package/LICENSE.md +1 -1
  2. package/{src → dist}/engineconfig.d.ts +6 -15
  3. package/dist/index-editor.css +38 -15
  4. package/dist/index.css +37 -37
  5. package/dist/index.css.map +1 -1
  6. package/{src → dist}/index.d.ts +0 -1
  7. package/dist/index.js +588 -94
  8. package/dist/index.js.map +1 -1
  9. package/{src → dist}/model/model.d.ts +10 -4
  10. package/{src → dist}/model/selection.d.ts +1 -1
  11. package/{src → dist}/view/downcastwriter.d.ts +3 -2
  12. package/{src → dist}/view/element.d.ts +2 -2
  13. package/{src → dist}/view/matcher.d.ts +4 -2
  14. package/dist/view/styles/background.d.ts +18 -0
  15. package/{src → dist}/view/styles/border.d.ts +0 -12
  16. package/{src → dist}/view/styles/margin.d.ts +0 -13
  17. package/{src → dist}/view/styles/padding.d.ts +0 -13
  18. package/{src → dist}/view/styles/utils.d.ts +12 -0
  19. package/package.json +20 -39
  20. package/src/controller/datacontroller.js +0 -522
  21. package/src/controller/editingcontroller.js +0 -181
  22. package/src/conversion/conversion.js +0 -606
  23. package/src/conversion/conversionhelpers.js +0 -33
  24. package/src/conversion/downcastdispatcher.js +0 -563
  25. package/src/conversion/downcasthelpers.js +0 -2160
  26. package/src/conversion/mapper.js +0 -1050
  27. package/src/conversion/modelconsumable.js +0 -331
  28. package/src/conversion/upcastdispatcher.js +0 -470
  29. package/src/conversion/upcasthelpers.js +0 -952
  30. package/src/conversion/viewconsumable.js +0 -541
  31. package/src/dataprocessor/basichtmlwriter.js +0 -22
  32. package/src/dataprocessor/dataprocessor.js +0 -5
  33. package/src/dataprocessor/htmldataprocessor.js +0 -107
  34. package/src/dataprocessor/htmlwriter.js +0 -5
  35. package/src/dataprocessor/xmldataprocessor.js +0 -127
  36. package/src/dev-utils/model.js +0 -396
  37. package/src/dev-utils/operationreplayer.js +0 -116
  38. package/src/dev-utils/utils.js +0 -122
  39. package/src/dev-utils/view.js +0 -990
  40. package/src/engineconfig.js +0 -5
  41. package/src/index.js +0 -134
  42. package/src/legacyerrors.js +0 -17
  43. package/src/model/batch.js +0 -98
  44. package/src/model/differ.js +0 -1288
  45. package/src/model/document.js +0 -398
  46. package/src/model/documentfragment.js +0 -332
  47. package/src/model/documentselection.js +0 -1026
  48. package/src/model/element.js +0 -323
  49. package/src/model/history.js +0 -206
  50. package/src/model/item.js +0 -5
  51. package/src/model/liveposition.js +0 -93
  52. package/src/model/liverange.js +0 -121
  53. package/src/model/markercollection.js +0 -436
  54. package/src/model/model.js +0 -866
  55. package/src/model/node.js +0 -371
  56. package/src/model/nodelist.js +0 -244
  57. package/src/model/operation/attributeoperation.js +0 -172
  58. package/src/model/operation/detachoperation.js +0 -87
  59. package/src/model/operation/insertoperation.js +0 -153
  60. package/src/model/operation/markeroperation.js +0 -136
  61. package/src/model/operation/mergeoperation.js +0 -184
  62. package/src/model/operation/moveoperation.js +0 -179
  63. package/src/model/operation/nooperation.js +0 -48
  64. package/src/model/operation/operation.js +0 -78
  65. package/src/model/operation/operationfactory.js +0 -44
  66. package/src/model/operation/renameoperation.js +0 -128
  67. package/src/model/operation/rootattributeoperation.js +0 -173
  68. package/src/model/operation/rootoperation.js +0 -106
  69. package/src/model/operation/splitoperation.js +0 -214
  70. package/src/model/operation/transform.js +0 -2211
  71. package/src/model/operation/utils.js +0 -217
  72. package/src/model/position.js +0 -1041
  73. package/src/model/range.js +0 -880
  74. package/src/model/rootelement.js +0 -82
  75. package/src/model/schema.js +0 -1542
  76. package/src/model/selection.js +0 -814
  77. package/src/model/text.js +0 -92
  78. package/src/model/textproxy.js +0 -202
  79. package/src/model/treewalker.js +0 -313
  80. package/src/model/typecheckable.js +0 -16
  81. package/src/model/utils/autoparagraphing.js +0 -63
  82. package/src/model/utils/deletecontent.js +0 -509
  83. package/src/model/utils/getselectedcontent.js +0 -126
  84. package/src/model/utils/insertcontent.js +0 -750
  85. package/src/model/utils/insertobject.js +0 -135
  86. package/src/model/utils/modifyselection.js +0 -187
  87. package/src/model/utils/selection-post-fixer.js +0 -264
  88. package/src/model/writer.js +0 -1318
  89. package/src/view/attributeelement.js +0 -220
  90. package/src/view/containerelement.js +0 -91
  91. package/src/view/datatransfer.js +0 -106
  92. package/src/view/document.js +0 -139
  93. package/src/view/documentfragment.js +0 -251
  94. package/src/view/documentselection.js +0 -270
  95. package/src/view/domconverter.js +0 -1661
  96. package/src/view/downcastwriter.js +0 -1589
  97. package/src/view/editableelement.js +0 -74
  98. package/src/view/element.js +0 -1053
  99. package/src/view/elementdefinition.js +0 -5
  100. package/src/view/emptyelement.js +0 -83
  101. package/src/view/filler.js +0 -161
  102. package/src/view/item.js +0 -5
  103. package/src/view/matcher.js +0 -437
  104. package/src/view/node.js +0 -238
  105. package/src/view/observer/arrowkeysobserver.js +0 -40
  106. package/src/view/observer/bubblingemittermixin.js +0 -215
  107. package/src/view/observer/bubblingeventinfo.js +0 -49
  108. package/src/view/observer/clickobserver.js +0 -26
  109. package/src/view/observer/compositionobserver.js +0 -64
  110. package/src/view/observer/domeventdata.js +0 -63
  111. package/src/view/observer/domeventobserver.js +0 -81
  112. package/src/view/observer/fakeselectionobserver.js +0 -95
  113. package/src/view/observer/focusobserver.js +0 -166
  114. package/src/view/observer/inputobserver.js +0 -236
  115. package/src/view/observer/keyobserver.js +0 -36
  116. package/src/view/observer/mouseobserver.js +0 -26
  117. package/src/view/observer/mutationobserver.js +0 -219
  118. package/src/view/observer/observer.js +0 -92
  119. package/src/view/observer/pointerobserver.js +0 -26
  120. package/src/view/observer/selectionobserver.js +0 -318
  121. package/src/view/observer/tabobserver.js +0 -42
  122. package/src/view/observer/touchobserver.js +0 -26
  123. package/src/view/placeholder.js +0 -285
  124. package/src/view/position.js +0 -341
  125. package/src/view/range.js +0 -451
  126. package/src/view/rawelement.js +0 -115
  127. package/src/view/renderer.js +0 -1148
  128. package/src/view/rooteditableelement.js +0 -78
  129. package/src/view/selection.js +0 -594
  130. package/src/view/styles/background.d.ts +0 -33
  131. package/src/view/styles/background.js +0 -74
  132. package/src/view/styles/border.js +0 -316
  133. package/src/view/styles/margin.js +0 -34
  134. package/src/view/styles/padding.js +0 -34
  135. package/src/view/styles/utils.js +0 -219
  136. package/src/view/stylesmap.js +0 -941
  137. package/src/view/text.js +0 -110
  138. package/src/view/textproxy.js +0 -136
  139. package/src/view/tokenlist.js +0 -194
  140. package/src/view/treewalker.js +0 -389
  141. package/src/view/typecheckable.js +0 -19
  142. package/src/view/uielement.js +0 -194
  143. package/src/view/upcastwriter.js +0 -363
  144. package/src/view/view.js +0 -579
  145. package/theme/placeholder.css +0 -36
  146. package/theme/renderer.css +0 -9
  147. /package/{src → dist}/controller/datacontroller.d.ts +0 -0
  148. /package/{src → dist}/controller/editingcontroller.d.ts +0 -0
  149. /package/{src → dist}/conversion/conversion.d.ts +0 -0
  150. /package/{src → dist}/conversion/conversionhelpers.d.ts +0 -0
  151. /package/{src → dist}/conversion/downcastdispatcher.d.ts +0 -0
  152. /package/{src → dist}/conversion/downcasthelpers.d.ts +0 -0
  153. /package/{src → dist}/conversion/mapper.d.ts +0 -0
  154. /package/{src → dist}/conversion/modelconsumable.d.ts +0 -0
  155. /package/{src → dist}/conversion/upcastdispatcher.d.ts +0 -0
  156. /package/{src → dist}/conversion/upcasthelpers.d.ts +0 -0
  157. /package/{src → dist}/conversion/viewconsumable.d.ts +0 -0
  158. /package/{src → dist}/dataprocessor/basichtmlwriter.d.ts +0 -0
  159. /package/{src → dist}/dataprocessor/dataprocessor.d.ts +0 -0
  160. /package/{src → dist}/dataprocessor/htmldataprocessor.d.ts +0 -0
  161. /package/{src → dist}/dataprocessor/htmlwriter.d.ts +0 -0
  162. /package/{src → dist}/dataprocessor/xmldataprocessor.d.ts +0 -0
  163. /package/{src → dist}/dev-utils/model.d.ts +0 -0
  164. /package/{src → dist}/dev-utils/operationreplayer.d.ts +0 -0
  165. /package/{src → dist}/dev-utils/utils.d.ts +0 -0
  166. /package/{src → dist}/dev-utils/view.d.ts +0 -0
  167. /package/{src → dist}/legacyerrors.d.ts +0 -0
  168. /package/{src → dist}/model/batch.d.ts +0 -0
  169. /package/{src → dist}/model/differ.d.ts +0 -0
  170. /package/{src → dist}/model/document.d.ts +0 -0
  171. /package/{src → dist}/model/documentfragment.d.ts +0 -0
  172. /package/{src → dist}/model/documentselection.d.ts +0 -0
  173. /package/{src → dist}/model/element.d.ts +0 -0
  174. /package/{src → dist}/model/history.d.ts +0 -0
  175. /package/{src → dist}/model/item.d.ts +0 -0
  176. /package/{src → dist}/model/liveposition.d.ts +0 -0
  177. /package/{src → dist}/model/liverange.d.ts +0 -0
  178. /package/{src → dist}/model/markercollection.d.ts +0 -0
  179. /package/{src → dist}/model/node.d.ts +0 -0
  180. /package/{src → dist}/model/nodelist.d.ts +0 -0
  181. /package/{src → dist}/model/operation/attributeoperation.d.ts +0 -0
  182. /package/{src → dist}/model/operation/detachoperation.d.ts +0 -0
  183. /package/{src → dist}/model/operation/insertoperation.d.ts +0 -0
  184. /package/{src → dist}/model/operation/markeroperation.d.ts +0 -0
  185. /package/{src → dist}/model/operation/mergeoperation.d.ts +0 -0
  186. /package/{src → dist}/model/operation/moveoperation.d.ts +0 -0
  187. /package/{src → dist}/model/operation/nooperation.d.ts +0 -0
  188. /package/{src → dist}/model/operation/operation.d.ts +0 -0
  189. /package/{src → dist}/model/operation/operationfactory.d.ts +0 -0
  190. /package/{src → dist}/model/operation/renameoperation.d.ts +0 -0
  191. /package/{src → dist}/model/operation/rootattributeoperation.d.ts +0 -0
  192. /package/{src → dist}/model/operation/rootoperation.d.ts +0 -0
  193. /package/{src → dist}/model/operation/splitoperation.d.ts +0 -0
  194. /package/{src → dist}/model/operation/transform.d.ts +0 -0
  195. /package/{src → dist}/model/operation/utils.d.ts +0 -0
  196. /package/{src → dist}/model/position.d.ts +0 -0
  197. /package/{src → dist}/model/range.d.ts +0 -0
  198. /package/{src → dist}/model/rootelement.d.ts +0 -0
  199. /package/{src → dist}/model/schema.d.ts +0 -0
  200. /package/{src → dist}/model/text.d.ts +0 -0
  201. /package/{src → dist}/model/textproxy.d.ts +0 -0
  202. /package/{src → dist}/model/treewalker.d.ts +0 -0
  203. /package/{src → dist}/model/typecheckable.d.ts +0 -0
  204. /package/{src → dist}/model/utils/autoparagraphing.d.ts +0 -0
  205. /package/{src → dist}/model/utils/deletecontent.d.ts +0 -0
  206. /package/{src → dist}/model/utils/getselectedcontent.d.ts +0 -0
  207. /package/{src → dist}/model/utils/insertcontent.d.ts +0 -0
  208. /package/{src → dist}/model/utils/insertobject.d.ts +0 -0
  209. /package/{src → dist}/model/utils/modifyselection.d.ts +0 -0
  210. /package/{src → dist}/model/utils/selection-post-fixer.d.ts +0 -0
  211. /package/{src → dist}/model/writer.d.ts +0 -0
  212. /package/{src → dist}/view/attributeelement.d.ts +0 -0
  213. /package/{src → dist}/view/containerelement.d.ts +0 -0
  214. /package/{src → dist}/view/datatransfer.d.ts +0 -0
  215. /package/{src → dist}/view/document.d.ts +0 -0
  216. /package/{src → dist}/view/documentfragment.d.ts +0 -0
  217. /package/{src → dist}/view/documentselection.d.ts +0 -0
  218. /package/{src → dist}/view/domconverter.d.ts +0 -0
  219. /package/{src → dist}/view/editableelement.d.ts +0 -0
  220. /package/{src → dist}/view/elementdefinition.d.ts +0 -0
  221. /package/{src → dist}/view/emptyelement.d.ts +0 -0
  222. /package/{src → dist}/view/filler.d.ts +0 -0
  223. /package/{src → dist}/view/item.d.ts +0 -0
  224. /package/{src → dist}/view/node.d.ts +0 -0
  225. /package/{src → dist}/view/observer/arrowkeysobserver.d.ts +0 -0
  226. /package/{src → dist}/view/observer/bubblingemittermixin.d.ts +0 -0
  227. /package/{src → dist}/view/observer/bubblingeventinfo.d.ts +0 -0
  228. /package/{src → dist}/view/observer/clickobserver.d.ts +0 -0
  229. /package/{src → dist}/view/observer/compositionobserver.d.ts +0 -0
  230. /package/{src → dist}/view/observer/domeventdata.d.ts +0 -0
  231. /package/{src → dist}/view/observer/domeventobserver.d.ts +0 -0
  232. /package/{src → dist}/view/observer/fakeselectionobserver.d.ts +0 -0
  233. /package/{src → dist}/view/observer/focusobserver.d.ts +0 -0
  234. /package/{src → dist}/view/observer/inputobserver.d.ts +0 -0
  235. /package/{src → dist}/view/observer/keyobserver.d.ts +0 -0
  236. /package/{src → dist}/view/observer/mouseobserver.d.ts +0 -0
  237. /package/{src → dist}/view/observer/mutationobserver.d.ts +0 -0
  238. /package/{src → dist}/view/observer/observer.d.ts +0 -0
  239. /package/{src → dist}/view/observer/pointerobserver.d.ts +0 -0
  240. /package/{src → dist}/view/observer/selectionobserver.d.ts +0 -0
  241. /package/{src → dist}/view/observer/tabobserver.d.ts +0 -0
  242. /package/{src → dist}/view/observer/touchobserver.d.ts +0 -0
  243. /package/{src → dist}/view/placeholder.d.ts +0 -0
  244. /package/{src → dist}/view/position.d.ts +0 -0
  245. /package/{src → dist}/view/range.d.ts +0 -0
  246. /package/{src → dist}/view/rawelement.d.ts +0 -0
  247. /package/{src → dist}/view/renderer.d.ts +0 -0
  248. /package/{src → dist}/view/rooteditableelement.d.ts +0 -0
  249. /package/{src → dist}/view/selection.d.ts +0 -0
  250. /package/{src → dist}/view/stylesmap.d.ts +0 -0
  251. /package/{src → dist}/view/text.d.ts +0 -0
  252. /package/{src → dist}/view/textproxy.d.ts +0 -0
  253. /package/{src → dist}/view/tokenlist.d.ts +0 -0
  254. /package/{src → dist}/view/treewalker.d.ts +0 -0
  255. /package/{src → dist}/view/typecheckable.d.ts +0 -0
  256. /package/{src → dist}/view/uielement.d.ts +0 -0
  257. /package/{src → dist}/view/upcastwriter.d.ts +0 -0
  258. /package/{src → dist}/view/view.d.ts +0 -0
@@ -1,398 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
4
- */
5
- /**
6
- * @module engine/model/document
7
- */
8
- import { Differ } from './differ.js';
9
- import { ModelDocumentSelection } from './documentselection.js';
10
- import { History } from './history.js';
11
- import { ModelRootElement } from './rootelement.js';
12
- import { CKEditorError, Collection, EmitterMixin, isInsideSurrogatePair, isInsideCombinedSymbol } from '@ckeditor/ckeditor5-utils';
13
- import { clone } from 'es-toolkit/compat';
14
- // @if CK_DEBUG_ENGINE // const { logDocument } = require( '../dev-utils/utils' );
15
- const graveyardName = '$graveyard';
16
- /**
17
- * Data model's document. It contains the model's structure, its selection and the history of changes.
18
- *
19
- * Read more about working with the model in
20
- * {@glink framework/architecture/editing-engine#model introduction to the the editing engine's architecture}.
21
- *
22
- * Usually, the document contains just one {@link module:engine/model/document~ModelDocument#roots root element}, so
23
- * you can retrieve it by just calling {@link module:engine/model/document~ModelDocument#getRoot} without specifying its name:
24
- *
25
- * ```ts
26
- * model.document.getRoot(); // -> returns the main root
27
- * ```
28
- *
29
- * However, the document may contain multiple roots – e.g. when the editor has multiple editable areas
30
- * (e.g. a title and a body of a message).
31
- */
32
- export class ModelDocument extends /* #__PURE__ */ EmitterMixin() {
33
- /**
34
- * The {@link module:engine/model/model~Model model} that the document is a part of.
35
- */
36
- model;
37
- /**
38
- * The document's history.
39
- */
40
- history;
41
- /**
42
- * The selection in this document.
43
- */
44
- selection;
45
- /**
46
- * A list of roots that are owned and managed by this document. Use {@link #createRoot}, {@link #getRoot} and
47
- * {@link #getRootNames} to manipulate it.
48
- */
49
- roots;
50
- /**
51
- * The model differ object. Its role is to buffer changes done on the model document and then calculate a diff of those changes.
52
- */
53
- differ;
54
- /**
55
- * Defines whether the document is in a read-only mode.
56
- *
57
- * The user should not be able to change the data of a document that is read-only.
58
- *
59
- * @readonly
60
- */
61
- isReadOnly;
62
- /**
63
- * Post-fixer callbacks registered to the model document.
64
- */
65
- _postFixers;
66
- /**
67
- * A flag that indicates whether the selection has changed since last change block.
68
- */
69
- _hasSelectionChangedFromTheLastChangeBlock;
70
- /**
71
- * Creates an empty document instance with no {@link #roots} (other than
72
- * the {@link #graveyard graveyard root}).
73
- */
74
- constructor(model) {
75
- super();
76
- this.model = model;
77
- this.history = new History();
78
- this.selection = new ModelDocumentSelection(this);
79
- this.roots = new Collection({ idProperty: 'rootName' });
80
- this.differ = new Differ(model.markers);
81
- this.isReadOnly = false;
82
- this._postFixers = new Set();
83
- this._hasSelectionChangedFromTheLastChangeBlock = false;
84
- // Graveyard tree root. Document always have a graveyard root, which stores removed nodes.
85
- this.createRoot('$root', graveyardName);
86
- // Then, still before an operation is applied on model, buffer the change in differ.
87
- this.listenTo(model, 'applyOperation', (evt, args) => {
88
- const operation = args[0];
89
- if (operation.isDocumentOperation) {
90
- this.differ.bufferOperation(operation);
91
- }
92
- }, { priority: 'high' });
93
- // After the operation is applied, bump document's version and add the operation to the history.
94
- this.listenTo(model, 'applyOperation', (evt, args) => {
95
- const operation = args[0];
96
- if (operation.isDocumentOperation) {
97
- this.history.addOperation(operation);
98
- }
99
- }, { priority: 'low' });
100
- // Listen to selection changes. If selection changed, mark it.
101
- this.listenTo(this.selection, 'change', () => {
102
- this._hasSelectionChangedFromTheLastChangeBlock = true;
103
- });
104
- // Buffer marker changes.
105
- // This is not covered in buffering operations because markers may change outside of them (when they
106
- // are modified using `model.markers` collection, not through `MarkerOperation`).
107
- this.listenTo(model.markers, 'update', (evt, marker, oldRange, newRange, oldMarkerData) => {
108
- // Copy the `newRange` to the new marker data as during the marker removal the range is not updated.
109
- const newMarkerData = { ...marker.getData(), range: newRange };
110
- // Whenever marker is updated, buffer that change.
111
- this.differ.bufferMarkerChange(marker.name, oldMarkerData, newMarkerData);
112
- if (oldRange === null) {
113
- // If this is a new marker, add a listener that will buffer change whenever marker changes.
114
- marker.on('change', (evt, oldRange) => {
115
- const markerData = marker.getData();
116
- this.differ.bufferMarkerChange(marker.name, { ...markerData, range: oldRange }, markerData);
117
- });
118
- }
119
- });
120
- // This is a solution for a problem that may occur during real-time editing. If one client detached a root and another added
121
- // something there at the same moment, the OT does not solve this problem currently. In such situation, the added elements would
122
- // stay in the detached root.
123
- //
124
- // This is incorrect, a detached root should be empty and all elements from it should be removed. To solve this, the post-fixer will
125
- // remove any element that is left in a detached root.
126
- //
127
- // Similarly, markers that are created at the beginning or at the end of the detached root will not be removed as well.
128
- //
129
- // The drawback of this solution over the OT solution is that the elements removed by the post-fixer will never be brought back.
130
- // If the root detachment gets undone (and the root is brought back), the removed elements will not be there.
131
- this.registerPostFixer(writer => {
132
- let result = false;
133
- for (const root of this.roots) {
134
- if (!root.isAttached() && !root.isEmpty) {
135
- writer.remove(writer.createRangeIn(root));
136
- result = true;
137
- }
138
- }
139
- for (const marker of this.model.markers) {
140
- if (!marker.getRange().root.isAttached()) {
141
- writer.removeMarker(marker);
142
- result = true;
143
- }
144
- }
145
- return result;
146
- });
147
- }
148
- /**
149
- * The document version. Every applied operation increases the version number. It is used to
150
- * ensure that operations are applied on a proper document version.
151
- *
152
- * This property is equal to {@link module:engine/model/history~History#version `model.Document#history#version`}.
153
- *
154
- * If the {@link module:engine/model/operation/operation~Operation#baseVersion base version} does not match the document version,
155
- * a {@link module:utils/ckeditorerror~CKEditorError model-document-applyoperation-wrong-version} error is thrown.
156
- */
157
- get version() {
158
- return this.history.version;
159
- }
160
- set version(version) {
161
- this.history.version = version;
162
- }
163
- /**
164
- * The graveyard tree root. A document always has a graveyard root that stores removed nodes.
165
- */
166
- get graveyard() {
167
- return this.getRoot(graveyardName);
168
- }
169
- /**
170
- * Creates a new root.
171
- *
172
- * **Note:** do not use this method after the editor has been initialized! If you want to dynamically add a root, use
173
- * {@link module:engine/model/writer~ModelWriter#addRoot `model.Writer#addRoot`} instead.
174
- *
175
- * @param elementName The element name. Defaults to `'$root'` which also has some basic schema defined
176
- * (e.g. `$block` elements are allowed inside the `$root`). Make sure to define a proper schema if you use a different name.
177
- * @param rootName A unique root name.
178
- * @returns The created root.
179
- */
180
- createRoot(elementName = '$root', rootName = 'main') {
181
- if (this.roots.get(rootName)) {
182
- /**
183
- * A root with the specified name already exists.
184
- *
185
- * @error model-document-createroot-name-exists
186
- */
187
- throw new CKEditorError('model-document-createroot-name-exists', this, { name: rootName });
188
- }
189
- const root = new ModelRootElement(this, elementName, rootName);
190
- this.roots.add(root);
191
- return root;
192
- }
193
- /**
194
- * Removes all event listeners set by the document instance.
195
- */
196
- destroy() {
197
- this.selection.destroy();
198
- this.stopListening();
199
- }
200
- /**
201
- * Returns a root by its name.
202
- *
203
- * Detached roots are returned by this method. This is to be able to operate on the detached root (for example, to be able to create
204
- * a position inside such a root for undo feature purposes).
205
- *
206
- * @param name The root name of the root to return.
207
- * @returns The root registered under a given name or `null` when there is no root with the given name.
208
- */
209
- getRoot(name = 'main') {
210
- return this.roots.get(name);
211
- }
212
- /**
213
- * Returns an array with names of all roots added to the document (except the {@link #graveyard graveyard root}).
214
- *
215
- * Detached roots **are not** returned by this method by default. This is to make sure that all features or algorithms that operate
216
- * on the document data know which roots are still a part of the document and should be processed.
217
- *
218
- * @param includeDetached Specified whether detached roots should be returned as well.
219
- */
220
- getRootNames(includeDetached = false) {
221
- return this.getRoots(includeDetached).map(root => root.rootName);
222
- }
223
- /**
224
- * Returns an array with all roots added to the document (except the {@link #graveyard graveyard root}).
225
- *
226
- * Detached roots **are not** returned by this method by default. This is to make sure that all features or algorithms that operate
227
- * on the document data know which roots are still a part of the document and should be processed.
228
- *
229
- * @param includeDetached Specified whether detached roots should be returned as well.
230
- */
231
- getRoots(includeDetached = false) {
232
- return this.roots.filter(root => root != this.graveyard && (includeDetached || root.isAttached()) && root._isLoaded);
233
- }
234
- /**
235
- * Used to register a post-fixer callback. A post-fixer mechanism guarantees that the features
236
- * will operate on a correct model state.
237
- *
238
- * An execution of a feature may lead to an incorrect document tree state. The callbacks are used to fix the document tree after
239
- * it has changed. Post-fixers are fired just after all changes from the outermost change block were applied but
240
- * before the {@link module:engine/model/document~ModelDocument#event:change change event} is fired. If a post-fixer callback made
241
- * a change, it should return `true`. When this happens, all post-fixers are fired again to check if something else should
242
- * not be fixed in the new document tree state.
243
- *
244
- * As a parameter, a post-fixer callback receives a {@link module:engine/model/writer~ModelWriter writer} instance connected with the
245
- * executed changes block. Thanks to that, all changes done by the callback will be added to the same
246
- * {@link module:engine/model/batch~Batch batch} (and undo step) as the original changes. This makes post-fixer changes transparent
247
- * for the user.
248
- *
249
- * An example of a post-fixer is a callback that checks if all the data were removed from the editor. If so, the
250
- * callback should add an empty paragraph so that the editor is never empty:
251
- *
252
- * ```ts
253
- * document.registerPostFixer( writer => {
254
- * const changes = document.differ.getChanges();
255
- *
256
- * // Check if the changes lead to an empty root in the editor.
257
- * for ( const entry of changes ) {
258
- * if ( entry.type == 'remove' && entry.position.root.isEmpty ) {
259
- * writer.insertElement( 'paragraph', entry.position.root, 0 );
260
- *
261
- * // It is fine to return early, even if multiple roots would need to be fixed.
262
- * // All post-fixers will be fired again, so if there are more empty roots, those will be fixed, too.
263
- * return true;
264
- * }
265
- * }
266
- *
267
- * return false;
268
- * } );
269
- * ```
270
- */
271
- registerPostFixer(postFixer) {
272
- this._postFixers.add(postFixer);
273
- }
274
- /**
275
- * A custom `toJSON()` method to solve child-parent circular dependencies.
276
- *
277
- * @returns A clone of this object with the document property changed to a string.
278
- */
279
- toJSON() {
280
- const json = clone(this);
281
- // Due to circular references we need to remove parent reference.
282
- json.selection = '[engine.model.DocumentSelection]';
283
- json.model = '[engine.model.Model]';
284
- return json;
285
- }
286
- /**
287
- * Check if there were any changes done on document, and if so, call post-fixers,
288
- * fire `change` event for features and conversion and then reset the differ.
289
- * Fire `change:data` event when at least one operation or buffered marker changes the data.
290
- *
291
- * @internal
292
- * @fires change
293
- * @fires change:data
294
- * @param writer The writer on which post-fixers will be called.
295
- */
296
- _handleChangeBlock(writer) {
297
- if (this._hasDocumentChangedFromTheLastChangeBlock()) {
298
- this._callPostFixers(writer);
299
- // Refresh selection attributes according to the final position in the model after the change.
300
- this.selection.refresh();
301
- if (this.differ.hasDataChanges()) {
302
- this.fire('change:data', writer.batch);
303
- }
304
- else {
305
- this.fire('change', writer.batch);
306
- }
307
- // Theoretically, it is not necessary to refresh selection after change event because
308
- // post-fixers are the last who should change the model, but just in case...
309
- this.selection.refresh();
310
- this.differ.reset();
311
- }
312
- this._hasSelectionChangedFromTheLastChangeBlock = false;
313
- }
314
- /**
315
- * Returns whether there is a buffered change or if the selection has changed from the last
316
- * {@link module:engine/model/model~Model#enqueueChange `enqueueChange()` block}
317
- * or {@link module:engine/model/model~Model#change `change()` block}.
318
- *
319
- * @returns Returns `true` if document has changed from the last `change()` or `enqueueChange()` block.
320
- */
321
- _hasDocumentChangedFromTheLastChangeBlock() {
322
- return !this.differ.isEmpty || this._hasSelectionChangedFromTheLastChangeBlock;
323
- }
324
- /**
325
- * Returns the default root for this document which is either the first root that was added to the document using
326
- * {@link #createRoot} or the {@link #graveyard graveyard root} if no other roots were created.
327
- *
328
- * @returns The default root for this document.
329
- */
330
- _getDefaultRoot() {
331
- const roots = this.getRoots();
332
- return roots.length ? roots[0] : this.graveyard;
333
- }
334
- /**
335
- * Returns the default range for this selection. The default range is a collapsed range that starts and ends
336
- * at the beginning of this selection's document {@link #_getDefaultRoot default root}.
337
- *
338
- * @internal
339
- */
340
- _getDefaultRange() {
341
- const defaultRoot = this._getDefaultRoot();
342
- const model = this.model;
343
- const schema = model.schema;
344
- // Find the first position where the selection can be put.
345
- const position = model.createPositionFromPath(defaultRoot, [0]);
346
- const nearestRange = schema.getNearestSelectionRange(position);
347
- // If valid selection range is not found - return range collapsed at the beginning of the root.
348
- return nearestRange || model.createRange(position);
349
- }
350
- /**
351
- * Checks whether a given {@link module:engine/model/range~ModelRange range} is a valid range for
352
- * the {@link #selection document's selection}.
353
- *
354
- * @internal
355
- * @param range A range to check.
356
- * @returns `true` if `range` is valid, `false` otherwise.
357
- */
358
- _validateSelectionRange(range) {
359
- return range.start.isValid() && range.end.isValid() &&
360
- validateTextNodePosition(range.start) && validateTextNodePosition(range.end);
361
- }
362
- /**
363
- * Performs post-fixer loops. Executes post-fixer callbacks as long as none of them has done any changes to the model.
364
- *
365
- * @param writer The writer on which post-fixer callbacks will be called.
366
- */
367
- _callPostFixers(writer) {
368
- let wasFixed = false;
369
- do {
370
- for (const callback of this._postFixers) {
371
- // Ensure selection attributes are up to date before each post-fixer.
372
- // https://github.com/ckeditor/ckeditor5-engine/issues/1673.
373
- //
374
- // It might be good to refresh the selection after each operation but at the moment it leads
375
- // to losing attributes for composition or and spell checking
376
- // https://github.com/ckeditor/ckeditor5-typing/issues/188
377
- this.selection.refresh();
378
- wasFixed = callback(writer);
379
- if (wasFixed) {
380
- break;
381
- }
382
- }
383
- } while (wasFixed);
384
- }
385
- }
386
- /**
387
- * Checks whether given range boundary position is valid for document selection, meaning that is not between
388
- * unicode surrogate pairs or base character and combining marks.
389
- */
390
- function validateTextNodePosition(rangeBoundary) {
391
- const textNode = rangeBoundary.textNode;
392
- if (textNode) {
393
- const data = textNode.data;
394
- const offset = rangeBoundary.offset - textNode.startOffset;
395
- return !isInsideSurrogatePair(data, offset) && !isInsideCombinedSymbol(data, offset);
396
- }
397
- return true;
398
- }