@ckeditor/ckeditor5-engine 47.6.1 → 48.0.0-alpha.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 (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,750 +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/utils/insertcontent
7
- */
8
- import { ModelDocumentSelection } from '../documentselection.js';
9
- import { ModelElement } from '../element.js';
10
- import { ModelLivePosition } from '../liveposition.js';
11
- import { ModelLiveRange } from '../liverange.js';
12
- import { ModelPosition } from '../position.js';
13
- import { ModelRange } from '../range.js';
14
- import { CKEditorError } from '@ckeditor/ckeditor5-utils';
15
- /**
16
- * Inserts content into the editor (specified selection) as one would expect the paste functionality to work.
17
- *
18
- * It takes care of removing the selected content, splitting elements (if needed), inserting elements and merging elements appropriately.
19
- *
20
- * Some examples:
21
- *
22
- * ```html
23
- * <p>x^</p> + <p>y</p> => <p>x</p><p>y</p> => <p>xy[]</p>
24
- * <p>x^y</p> + <p>z</p> => <p>x</p>^<p>y</p> + <p>z</p> => <p>x</p><p>z</p><p>y</p> => <p>xz[]y</p>
25
- * <p>x^y</p> + <img /> => <p>x</p>^<p>y</p> + <img /> => <p>x</p><img /><p>y</p>
26
- * <p>x</p><p>^</p><p>z</p> + <p>y</p> => <p>x</p><p>y[]</p><p>z</p> (no merging)
27
- * <p>x</p>[<img />]<p>z</p> + <p>y</p> => <p>x</p>^<p>z</p> + <p>y</p> => <p>x</p><p>y[]</p><p>z</p>
28
- * ```
29
- *
30
- * If an instance of {@link module:engine/model/selection~ModelSelection} is passed as `selectable` it will be modified
31
- * to the insertion selection (equal to a range to be selected after insertion).
32
- *
33
- * If `selectable` is not passed, the content will be inserted using the current selection of the model document.
34
- *
35
- * **Note:** Use {@link module:engine/model/model~Model#insertContent} instead of this function.
36
- * This function is only exposed to be reusable in algorithms which change the {@link module:engine/model/model~Model#insertContent}
37
- * method's behavior.
38
- *
39
- * @param model The model in context of which the insertion should be performed.
40
- * @param content The content to insert.
41
- * @param selectable Selection into which the content should be inserted.
42
- * @returns Range which contains all the performed changes. This is a range that, if removed,
43
- * would return the model to the state before the insertion. If no changes were preformed by `insertContent`, returns a range collapsed
44
- * at the insertion position.
45
- * @internal
46
- */
47
- export function insertContent(model, content, selectable) {
48
- return model.change(writer => {
49
- const selection = selectable ? selectable : model.document.selection;
50
- if (!selection.isCollapsed) {
51
- model.deleteContent(selection, { doNotAutoparagraph: true });
52
- }
53
- const insertion = new Insertion(model, writer, selection.anchor);
54
- const fakeMarkerElements = [];
55
- let nodesToInsert;
56
- if (content.is('documentFragment')) {
57
- // If document fragment has any markers, these markers should be inserted into the model as well.
58
- if (content.markers.size) {
59
- const markersPosition = [];
60
- for (const [name, range] of content.markers) {
61
- const { start, end } = range;
62
- const isCollapsed = start.isEqual(end);
63
- markersPosition.push({ position: start, name, isCollapsed }, { position: end, name, isCollapsed });
64
- }
65
- // Markers position is sorted backwards to ensure that the insertion of fake markers will not change
66
- // the position of the next markers.
67
- markersPosition.sort(({ position: posA }, { position: posB }) => posA.isBefore(posB) ? 1 : -1);
68
- for (const { position, name, isCollapsed } of markersPosition) {
69
- let fakeElement = null;
70
- let collapsed = null;
71
- const isAtBeginning = position.parent === content && position.isAtStart;
72
- const isAtEnd = position.parent === content && position.isAtEnd;
73
- // We have two ways of handling markers. In general, we want to add temporary <$marker> model elements to
74
- // represent marker boundaries. These elements will be inserted into content together with the rest
75
- // of the document fragment. After insertion is done, positions for these elements will be read
76
- // and proper, actual markers will be created in the model and fake elements will be removed.
77
- //
78
- // However, if the <$marker> element is at the beginning or at the end of the document fragment,
79
- // it may affect how the inserted content is merged with current model, impacting the insertion
80
- // result. To avoid that, we don't add <$marker> elements at these positions. Instead, we will use
81
- // `Insertion#getAffectedRange()` to figure out new positions for these marker boundaries.
82
- if (!isAtBeginning && !isAtEnd) {
83
- fakeElement = writer.createElement('$marker');
84
- writer.insert(fakeElement, position);
85
- }
86
- else if (isCollapsed) {
87
- // Save whether the collapsed marker was at the beginning or at the end of document fragment
88
- // to know where to create it after the insertion is done.
89
- collapsed = isAtBeginning ? 'start' : 'end';
90
- }
91
- fakeMarkerElements.push({
92
- name,
93
- element: fakeElement,
94
- collapsed
95
- });
96
- }
97
- }
98
- nodesToInsert = content.getChildren();
99
- }
100
- else {
101
- nodesToInsert = [content];
102
- }
103
- insertion.handleNodes(nodesToInsert);
104
- let newRange = insertion.getSelectionRange();
105
- if (content.is('documentFragment') && fakeMarkerElements.length) {
106
- // After insertion was done, the selection was set but the model contains fake <$marker> elements.
107
- // These <$marker> elements will be now removed. Because of that, we will need to fix the selection.
108
- // We will create a live range that will automatically be update as <$marker> elements are removed.
109
- const selectionLiveRange = newRange ? ModelLiveRange.fromRange(newRange) : null;
110
- // Marker name -> [ start position, end position ].
111
- const markersData = {};
112
- // Note: `fakeMarkerElements` are sorted backwards. However, now, we want to handle the markers
113
- // from the beginning, so that existing <$marker> elements do not affect markers positions.
114
- // This is why we iterate from the end to the start.
115
- for (let i = fakeMarkerElements.length - 1; i >= 0; i--) {
116
- const { name, element, collapsed } = fakeMarkerElements[i];
117
- const isStartBoundary = !markersData[name];
118
- if (isStartBoundary) {
119
- markersData[name] = [];
120
- }
121
- if (element) {
122
- // Read fake marker element position to learn where the marker should be created.
123
- const elementPosition = writer.createPositionAt(element, 'before');
124
- markersData[name].push(elementPosition);
125
- writer.remove(element);
126
- }
127
- else {
128
- // If the fake marker element does not exist, it means that the marker boundary was at the beginning or at the end.
129
- const rangeOnInsertion = insertion.getAffectedRange();
130
- if (!rangeOnInsertion) {
131
- // If affected range is `null` it means that nothing was in the document fragment or all content was filtered out.
132
- // Some markers that were in the filtered content may be removed (partially or totally).
133
- // Let's handle only those markers that were at the beginning or at the end of the document fragment.
134
- if (collapsed) {
135
- markersData[name].push(insertion.position);
136
- }
137
- continue;
138
- }
139
- if (collapsed) {
140
- // If the marker was collapsed at the beginning or at the end of the document fragment,
141
- // put both boundaries at the beginning or at the end of inserted range (to keep the marker collapsed).
142
- markersData[name].push(rangeOnInsertion[collapsed]);
143
- }
144
- else {
145
- markersData[name].push(isStartBoundary ? rangeOnInsertion.start : rangeOnInsertion.end);
146
- }
147
- }
148
- }
149
- for (const [name, [start, end]] of Object.entries(markersData)) {
150
- // For now, we ignore markers if they are included in the filtered-out content.
151
- // In the future implementation we will improve that case to create markers that are not filtered out completely.
152
- if (start && end && start.root === end.root && start.root.document && !writer.model.markers.has(name)) {
153
- writer.addMarker(name, {
154
- usingOperation: true,
155
- affectsData: true,
156
- range: new ModelRange(start, end)
157
- });
158
- }
159
- }
160
- if (selectionLiveRange) {
161
- newRange = selectionLiveRange.toRange();
162
- selectionLiveRange.detach();
163
- }
164
- }
165
- /* istanbul ignore else -- @preserve */
166
- if (newRange) {
167
- if (selection instanceof ModelDocumentSelection) {
168
- writer.setSelection(newRange);
169
- }
170
- else {
171
- selection.setTo(newRange);
172
- }
173
- }
174
- else {
175
- // We are not testing else because it's a safe check for unpredictable edge cases:
176
- // an insertion without proper range to select.
177
- //
178
- // @if CK_DEBUG // console.warn( 'Cannot determine a proper selection range after insertion.' );
179
- }
180
- const affectedRange = insertion.getAffectedRange() || model.createRange(selection.anchor);
181
- insertion.destroy();
182
- return affectedRange;
183
- });
184
- }
185
- /**
186
- * Utility class for performing content insertion.
187
- */
188
- class Insertion {
189
- /**
190
- * The model in context of which the insertion should be performed.
191
- */
192
- model;
193
- /**
194
- * Batch to which operations will be added.
195
- */
196
- writer;
197
- /**
198
- * The position at which (or near which) the next node will be inserted.
199
- */
200
- position;
201
- /**
202
- * Elements with which the inserted elements can be merged.
203
- *
204
- * ```html
205
- * <p>x^</p><p>y</p> + <p>z</p> (can merge to <p>x</p>)
206
- * <p>x</p><p>^y</p> + <p>z</p> (can merge to <p>y</p>)
207
- * <p>x^y</p> + <p>z</p> (can merge to <p>xy</p> which will be split during the action,
208
- * so both its pieces will be added to this set)
209
- * ```
210
- */
211
- canMergeWith;
212
- /**
213
- * Schema of the model.
214
- */
215
- schema;
216
- /**
217
- * The temporary ModelDocumentFragment used for grouping multiple nodes for single insert operation.
218
- */
219
- _documentFragment;
220
- /**
221
- * The current position in the temporary ModelDocumentFragment.
222
- */
223
- _documentFragmentPosition;
224
- /**
225
- * The reference to the first inserted node.
226
- */
227
- _firstNode = null;
228
- /**
229
- * The reference to the last inserted node.
230
- */
231
- _lastNode = null;
232
- /**
233
- * The reference to the last auto paragraph node.
234
- */
235
- _lastAutoParagraph = null;
236
- /**
237
- * The array of nodes that should be cleaned of not allowed attributes and sub-nodes.
238
- */
239
- _filterAttributesAndChildrenOf = [];
240
- /**
241
- * Beginning of the affected range. See {@link module:engine/model/utils/insertcontent~Insertion#getAffectedRange}.
242
- */
243
- _affectedStart = null;
244
- /**
245
- * End of the affected range. See {@link module:engine/model/utils/insertcontent~Insertion#getAffectedRange}.
246
- */
247
- _affectedEnd = null;
248
- _nodeToSelect = null;
249
- constructor(model, writer, position) {
250
- this.model = model;
251
- this.writer = writer;
252
- this.position = position;
253
- this.canMergeWith = new Set([this.position.parent]);
254
- this.schema = model.schema;
255
- this._documentFragment = writer.createDocumentFragment();
256
- this._documentFragmentPosition = writer.createPositionAt(this._documentFragment, 0);
257
- }
258
- /**
259
- * Handles insertion of a set of nodes.
260
- *
261
- * @param nodes Nodes to insert.
262
- */
263
- handleNodes(nodes) {
264
- for (const node of Array.from(nodes)) {
265
- // Ignore empty nodes, especially empty text nodes.
266
- if (node.offsetSize > 0) {
267
- this._handleNode(node);
268
- }
269
- }
270
- // Insert nodes collected in temporary ModelDocumentFragment.
271
- this._insertPartialFragment();
272
- // If there was an auto paragraph then we might need to adjust the end of insertion.
273
- if (this._lastAutoParagraph) {
274
- this._updateLastNodeFromAutoParagraph(this._lastAutoParagraph);
275
- }
276
- // After the content was inserted we may try to merge it with its next sibling if the selection was in it initially.
277
- // Merging with the previous sibling was performed just after inserting the first node to the document.
278
- this._mergeOnRight();
279
- // TMP this will become a post-fixer.
280
- this.schema.removeDisallowedAttributes(this._filterAttributesAndChildrenOf, this.writer);
281
- if (this.model._config?.get('experimentalFlags.modelInsertContentDeepSchemaVerification')) {
282
- this._removeDisallowedChildren(this._filterAttributesAndChildrenOf);
283
- }
284
- this._filterAttributesAndChildrenOf = [];
285
- }
286
- /**
287
- * Removes disallowed children from nodes that were inserted or modified during insertion.
288
- */
289
- _removeDisallowedChildren(nodes) {
290
- // Make sure we do not modify the original iterable.
291
- const nodesArray = Array.from(nodes);
292
- // Check all sub-nodes of top level inserted nodes.
293
- // We do it at this point so node is already in the model tree and schema checks will be correct.
294
- for (const node of nodesArray) {
295
- if (!node.is('element')) {
296
- continue;
297
- }
298
- const remove = [];
299
- const unwrap = [];
300
- const walker = this.writer.createRangeIn(node).getWalker({ ignoreElementEnd: true });
301
- for (const { item } of walker) {
302
- const itemParent = item.parent;
303
- if (!this.schema.checkChild(itemParent, item)) {
304
- if (item.is('element') && !this.schema.isObject(item)) {
305
- // Unwrap non-object element.
306
- unwrap.push(item);
307
- // Store the parent for re-checking children after unwrap.
308
- nodesArray.push(itemParent);
309
- }
310
- else {
311
- // Remove object with children, or text.
312
- remove.push(item);
313
- }
314
- // Skip the whole subtree as it will be removed or processed later.
315
- walker.jumpTo(this.writer.createPositionAfter(item));
316
- }
317
- }
318
- for (const item of unwrap) {
319
- this.writer.unwrap(item);
320
- }
321
- for (const item of remove) {
322
- this.writer.remove(item);
323
- }
324
- }
325
- }
326
- /**
327
- * Updates the last node after the auto paragraphing.
328
- *
329
- * @param node The last auto paragraphing node.
330
- */
331
- _updateLastNodeFromAutoParagraph(node) {
332
- const positionAfterLastNode = this.writer.createPositionAfter(this._lastNode);
333
- const positionAfterNode = this.writer.createPositionAfter(node);
334
- // If the real end was after the last auto paragraph then update relevant properties.
335
- if (positionAfterNode.isAfter(positionAfterLastNode)) {
336
- this._lastNode = node;
337
- /* istanbul ignore if -- @preserve */
338
- if (this.position.parent != node || !this.position.isAtEnd) {
339
- // Algorithm's correctness check. We should never end up here but it's good to know that we did.
340
- // At this point the insertion position should be at the end of the last auto paragraph.
341
- // Note: This error is documented in other place in this file.
342
- throw new CKEditorError('insertcontent-invalid-insertion-position', this);
343
- }
344
- this.position = positionAfterNode;
345
- this._setAffectedBoundaries(this.position);
346
- }
347
- }
348
- /**
349
- * Returns range to be selected after insertion.
350
- * Returns `null` if there is no valid range to select after insertion.
351
- */
352
- getSelectionRange() {
353
- if (this._nodeToSelect) {
354
- return ModelRange._createOn(this._nodeToSelect);
355
- }
356
- return this.model.schema.getNearestSelectionRange(this.position);
357
- }
358
- /**
359
- * Returns a range which contains all the performed changes. This is a range that, if removed, would return the model to the state
360
- * before the insertion. Returns `null` if no changes were done.
361
- */
362
- getAffectedRange() {
363
- if (!this._affectedStart) {
364
- return null;
365
- }
366
- return new ModelRange(this._affectedStart, this._affectedEnd);
367
- }
368
- /**
369
- * Destroys `Insertion` instance.
370
- */
371
- destroy() {
372
- if (this._affectedStart) {
373
- this._affectedStart.detach();
374
- }
375
- if (this._affectedEnd) {
376
- this._affectedEnd.detach();
377
- }
378
- }
379
- /**
380
- * Handles insertion of a single node.
381
- */
382
- _handleNode(node) {
383
- // Split the position.parent's branch up to a point where the node can be inserted.
384
- // If it isn't allowed in the whole branch, then of course don't split anything.
385
- if (!this._checkAndSplitToAllowedPosition(node)) {
386
- // Handle element children if it's not an object (strip container).
387
- if (!this.schema.isObject(node)) {
388
- this._handleDisallowedNode(node);
389
- }
390
- return;
391
- }
392
- // Add node to the current temporary ModelDocumentFragment.
393
- node = this._appendToFragment(node);
394
- // Store the first and last nodes for easy access for merging with sibling nodes.
395
- if (!this._firstNode) {
396
- this._firstNode = node;
397
- }
398
- this._lastNode = node;
399
- }
400
- /**
401
- * Inserts the temporary ModelDocumentFragment into the model.
402
- */
403
- _insertPartialFragment() {
404
- if (this._documentFragment.isEmpty) {
405
- return;
406
- }
407
- const livePosition = ModelLivePosition.fromPosition(this.position, 'toNext');
408
- this._setAffectedBoundaries(this.position);
409
- // If the very first node of the whole insertion process is inserted, insert it separately for OT reasons (undo).
410
- // Note: there can be multiple calls to `_insertPartialFragment()` during one insertion process.
411
- // Note: only the very first node can be merged so we have to do separate operation only for it.
412
- if (this._documentFragment.getChild(0) == this._firstNode) {
413
- this.writer.insert(this._firstNode, this.position);
414
- // We must merge the first node just after inserting it to avoid problems with OT.
415
- // (See: https://github.com/ckeditor/ckeditor5/pull/8773#issuecomment-760945652).
416
- this._mergeOnLeft();
417
- this.position = livePosition.toPosition();
418
- }
419
- // Insert the remaining nodes from document fragment.
420
- if (!this._documentFragment.isEmpty) {
421
- this.writer.insert(this._documentFragment, this.position);
422
- }
423
- this._documentFragmentPosition = this.writer.createPositionAt(this._documentFragment, 0);
424
- this.position = livePosition.toPosition();
425
- livePosition.detach();
426
- }
427
- /**
428
- * @param node The disallowed node which needs to be handled.
429
- */
430
- _handleDisallowedNode(node) {
431
- // If the node is an element, try inserting its children (strip the parent).
432
- if (node.is('element')) {
433
- this.handleNodes(node.getChildren());
434
- }
435
- }
436
- /**
437
- * Append a node to the temporary ModelDocumentFragment.
438
- *
439
- * @param node The node to insert.
440
- */
441
- _appendToFragment(node) {
442
- /* istanbul ignore if -- @preserve */
443
- if (!this.schema.checkChild(this.position, node)) {
444
- // Algorithm's correctness check. We should never end up here but it's good to know that we did.
445
- // Note that it would often be a silent issue if we insert node in a place where it's not allowed.
446
- /**
447
- * Given node cannot be inserted on the given position.
448
- *
449
- * @error insertcontent-wrong-position
450
- * @param {module:engine/model/node~ModelNode} node Node to insert.
451
- * @param {module:engine/model/position~ModelPosition} position Position to insert the node at.
452
- */
453
- throw new CKEditorError('insertcontent-wrong-position', this, { node, position: this.position });
454
- }
455
- this.writer.insert(node, this._documentFragmentPosition);
456
- this._documentFragmentPosition = this._documentFragmentPosition.getShiftedBy(node.offsetSize);
457
- // In case text node was merged with already inserted text node, we need to get the actual node that is in the document.
458
- // This happens when there is a non-allowed object between text nodes.
459
- if (!node.parent) {
460
- node = this._documentFragmentPosition.nodeBefore;
461
- }
462
- // The last inserted object should be selected because we can't put a collapsed selection after it.
463
- if (this.schema.isObject(node) && !this.schema.checkChild(this.position, '$text')) {
464
- this._nodeToSelect = node;
465
- }
466
- else {
467
- this._nodeToSelect = null;
468
- }
469
- this._filterAttributesAndChildrenOf.push(node);
470
- return node;
471
- }
472
- /**
473
- * Sets `_affectedStart` and `_affectedEnd` to the given `position`. Should be used before a change is done during insertion process to
474
- * mark the affected range.
475
- *
476
- * This method is used before inserting a node or splitting a parent node. `_affectedStart` and `_affectedEnd` are also changed
477
- * during merging, but the logic there is more complicated so it is left out of this function.
478
- */
479
- _setAffectedBoundaries(position) {
480
- // Set affected boundaries stickiness so that those position will "expand" when something is inserted in between them:
481
- // <paragraph>Foo][bar</paragraph> -> <paragraph>Foo]xx[bar</paragraph>
482
- // This is why it cannot be a range but two separate positions.
483
- if (!this._affectedStart) {
484
- this._affectedStart = ModelLivePosition.fromPosition(position, 'toPrevious');
485
- }
486
- // If `_affectedEnd` is before the new boundary position, expand `_affectedEnd`. This can happen if first inserted node was
487
- // inserted into the parent but the next node is moved-out of that parent:
488
- // (1) <paragraph>Foo][</paragraph> -> <paragraph>Foo]xx[</paragraph>
489
- // (2) <paragraph>Foo]xx[</paragraph> -> <paragraph>Foo]xx</paragraph><widget></widget>[
490
- if (!this._affectedEnd || this._affectedEnd.isBefore(position)) {
491
- if (this._affectedEnd) {
492
- this._affectedEnd.detach();
493
- }
494
- this._affectedEnd = ModelLivePosition.fromPosition(position, 'toNext');
495
- }
496
- }
497
- /**
498
- * Merges the previous sibling of the first node if it should be merged.
499
- *
500
- * After the content was inserted we may try to merge it with its siblings.
501
- * This should happen only if the selection was in those elements initially.
502
- */
503
- _mergeOnLeft() {
504
- const node = this._firstNode;
505
- if (!(node instanceof ModelElement)) {
506
- return;
507
- }
508
- if (!this._canMergeLeft(node)) {
509
- return;
510
- }
511
- const mergePosLeft = ModelLivePosition._createBefore(node);
512
- mergePosLeft.stickiness = 'toNext';
513
- const livePosition = ModelLivePosition.fromPosition(this.position, 'toNext');
514
- // If `_affectedStart` is sames as merge position, it means that the element "marked" by `_affectedStart` is going to be
515
- // removed and its contents will be moved. This won't transform `ModelLivePosition` so `_affectedStart` needs to be moved
516
- // by hand to properly reflect affected range. (Due to `_affectedStart` and `_affectedEnd` stickiness, the "range" is
517
- // shown as `][`).
518
- //
519
- // Example - insert `<paragraph>Abc</paragraph><paragraph>Xyz</paragraph>` at the end of `<paragraph>Foo^</paragraph>`:
520
- //
521
- // <paragraph>Foo</paragraph><paragraph>Bar</paragraph> -->
522
- // <paragraph>Foo</paragraph>]<paragraph>Abc</paragraph><paragraph>Xyz</paragraph>[<paragraph>Bar</paragraph> -->
523
- // <paragraph>Foo]Abc</paragraph><paragraph>Xyz</paragraph>[<paragraph>Bar</paragraph>
524
- //
525
- // Note, that if we are here then something must have been inserted, so `_affectedStart` and `_affectedEnd` have to be set.
526
- if (this._affectedStart.isEqual(mergePosLeft)) {
527
- this._affectedStart.detach();
528
- this._affectedStart = ModelLivePosition._createAt(mergePosLeft.nodeBefore, 'end', 'toPrevious');
529
- }
530
- // We need to update the references to the first and last nodes if they will be merged into the previous sibling node
531
- // because the reference would point to the removed node.
532
- //
533
- // <p>A^A</p> + <p>X</p>
534
- //
535
- // <p>A</p>^<p>A</p>
536
- // <p>A</p><p>X</p><p>A</p>
537
- // <p>AX</p><p>A</p>
538
- // <p>AXA</p>
539
- if (this._firstNode === this._lastNode) {
540
- this._firstNode = mergePosLeft.nodeBefore;
541
- this._lastNode = mergePosLeft.nodeBefore;
542
- }
543
- this.writer.merge(mergePosLeft);
544
- // If only one element (the merged one) is in the "affected range", also move the affected range end appropriately.
545
- //
546
- // Example - insert `<paragraph>Abc</paragraph>` at the of `<paragraph>Foo^</paragraph>`:
547
- //
548
- // <paragraph>Foo</paragraph><paragraph>Bar</paragraph> -->
549
- // <paragraph>Foo</paragraph>]<paragraph>Abc</paragraph>[<paragraph>Bar</paragraph> -->
550
- // <paragraph>Foo]Abc</paragraph>[<paragraph>Bar</paragraph> -->
551
- // <paragraph>Foo]Abc[</paragraph><paragraph>Bar</paragraph>
552
- if (mergePosLeft.isEqual(this._affectedEnd) && this._firstNode === this._lastNode) {
553
- this._affectedEnd.detach();
554
- this._affectedEnd = ModelLivePosition._createAt(mergePosLeft.nodeBefore, 'end', 'toNext');
555
- }
556
- this.position = livePosition.toPosition();
557
- livePosition.detach();
558
- // After merge elements that were marked by _insert() to be filtered might be gone so
559
- // we need to mark the new container.
560
- this._filterAttributesAndChildrenOf.push(this.position.parent);
561
- mergePosLeft.detach();
562
- }
563
- /**
564
- * Merges the next sibling of the last node if it should be merged.
565
- *
566
- * After the content was inserted we may try to merge it with its siblings.
567
- * This should happen only if the selection was in those elements initially.
568
- */
569
- _mergeOnRight() {
570
- const node = this._lastNode;
571
- if (!(node instanceof ModelElement)) {
572
- return;
573
- }
574
- if (!this._canMergeRight(node)) {
575
- return;
576
- }
577
- const mergePosRight = ModelLivePosition._createAfter(node);
578
- mergePosRight.stickiness = 'toNext';
579
- /* istanbul ignore if -- @preserve */
580
- if (!this.position.isEqual(mergePosRight)) {
581
- // Algorithm's correctness check. We should never end up here but it's good to know that we did.
582
- // At this point the insertion position should be after the node we'll merge. If it isn't,
583
- // it should need to be secured as in the left merge case.
584
- /**
585
- * An internal error occurred when merging inserted content with its siblings.
586
- * The insertion position should equal the merge position.
587
- *
588
- * If you encountered this error, report it back to the CKEditor 5 team
589
- * with as many details as possible regarding the content being inserted and the insertion position.
590
- *
591
- * @error insertcontent-invalid-insertion-position
592
- */
593
- throw new CKEditorError('insertcontent-invalid-insertion-position', this);
594
- }
595
- // Move the position to the previous node, so it isn't moved to the graveyard on merge.
596
- // <p>x</p>[]<p>y</p> => <p>x[]</p><p>y</p>
597
- this.position = ModelPosition._createAt(mergePosRight.nodeBefore, 'end');
598
- // Explanation of setting position stickiness to `'toPrevious'`:
599
- // OK: <p>xx[]</p> + <p>yy</p> => <p>xx[]yy</p> (when sticks to previous)
600
- // NOK: <p>xx[]</p> + <p>yy</p> => <p>xxyy[]</p> (when sticks to next)
601
- const livePosition = ModelLivePosition.fromPosition(this.position, 'toPrevious');
602
- // See comment in `_mergeOnLeft()` on moving `_affectedStart`.
603
- if (this._affectedEnd.isEqual(mergePosRight)) {
604
- this._affectedEnd.detach();
605
- this._affectedEnd = ModelLivePosition._createAt(mergePosRight.nodeBefore, 'end', 'toNext');
606
- }
607
- // We need to update the references to the first and last nodes if they will be merged into the previous sibling node
608
- // because the reference would point to the removed node.
609
- //
610
- // <p>A^A</p> + <p>X</p>
611
- //
612
- // <p>A</p>^<p>A</p>
613
- // <p>A</p><p>X</p><p>A</p>
614
- // <p>AX</p><p>A</p>
615
- // <p>AXA</p>
616
- if (this._firstNode === this._lastNode) {
617
- this._firstNode = mergePosRight.nodeBefore;
618
- this._lastNode = mergePosRight.nodeBefore;
619
- }
620
- this.writer.merge(mergePosRight);
621
- // See comment in `_mergeOnLeft()` on moving `_affectedStart`.
622
- if (mergePosRight.getShiftedBy(-1).isEqual(this._affectedStart) && this._firstNode === this._lastNode) {
623
- this._affectedStart.detach();
624
- this._affectedStart = ModelLivePosition._createAt(mergePosRight.nodeBefore, 0, 'toPrevious');
625
- }
626
- this.position = livePosition.toPosition();
627
- livePosition.detach();
628
- // After merge elements that were marked by _insert() to be filtered might be gone so
629
- // we need to mark the new container.
630
- this._filterAttributesAndChildrenOf.push(this.position.parent);
631
- mergePosRight.detach();
632
- }
633
- /**
634
- * Checks whether specified node can be merged with previous sibling element.
635
- *
636
- * @param node The node which could potentially be merged.
637
- */
638
- _canMergeLeft(node) {
639
- const previousSibling = node.previousSibling;
640
- return (previousSibling instanceof ModelElement) &&
641
- this.canMergeWith.has(previousSibling) &&
642
- this.model.schema.checkMerge(previousSibling, node);
643
- }
644
- /**
645
- * Checks whether specified node can be merged with next sibling element.
646
- *
647
- * @param node The node which could potentially be merged.
648
- */
649
- _canMergeRight(node) {
650
- const nextSibling = node.nextSibling;
651
- return (nextSibling instanceof ModelElement) &&
652
- this.canMergeWith.has(nextSibling) &&
653
- this.model.schema.checkMerge(node, nextSibling);
654
- }
655
- /**
656
- * Inserts a paragraph and moves the insertion position into it.
657
- */
658
- _insertAutoParagraph() {
659
- // Insert nodes collected in temporary ModelDocumentFragment if the position parent needs change to process further nodes.
660
- this._insertPartialFragment();
661
- // Insert a paragraph and move insertion position to it.
662
- const paragraph = this.writer.createElement('paragraph');
663
- this.writer.insert(paragraph, this.position);
664
- this._setAffectedBoundaries(this.position);
665
- this._lastAutoParagraph = paragraph;
666
- this.position = this.writer.createPositionAt(paragraph, 0);
667
- }
668
- /**
669
- * @returns Whether an allowed position was found.
670
- * `false` is returned if the node isn't allowed at any position up in the tree, `true` if was.
671
- */
672
- _checkAndSplitToAllowedPosition(node) {
673
- const allowedIn = this._getAllowedIn(this.position.parent, node);
674
- if (!allowedIn) {
675
- return false;
676
- }
677
- // Insert nodes collected in temporary ModelDocumentFragment if the position parent needs change to process further nodes.
678
- if (allowedIn != this.position.parent) {
679
- this._insertPartialFragment();
680
- }
681
- while (allowedIn != this.position.parent) {
682
- if (this.position.isAtStart) {
683
- // If insertion position is at the beginning of the parent, move it out instead of splitting.
684
- // <p>^Foo</p> -> ^<p>Foo</p>
685
- const parent = this.position.parent;
686
- this.position = this.writer.createPositionBefore(parent);
687
- // Special case – parent is empty (<p>^</p>).
688
- //
689
- // 1. parent.isEmpty
690
- // We can remove the element after moving insertion position out of it.
691
- //
692
- // 2. parent.parent === allowedIn
693
- // However parent should remain in place when allowed element is above limit element in document tree.
694
- // For example there shouldn't be allowed to remove empty paragraph from tableCell, when is pasted
695
- // content allowed in $root.
696
- if (parent.isEmpty && parent.parent === allowedIn) {
697
- this.writer.remove(parent);
698
- }
699
- }
700
- else if (this.position.isAtEnd) {
701
- // If insertion position is at the end of the parent, move it out instead of splitting.
702
- // <p>Foo^</p> -> <p>Foo</p>^
703
- this.position = this.writer.createPositionAfter(this.position.parent);
704
- }
705
- else {
706
- const tempPos = this.writer.createPositionAfter(this.position.parent);
707
- this._setAffectedBoundaries(this.position);
708
- this.writer.split(this.position);
709
- this.position = tempPos;
710
- this.canMergeWith.add(this.position.nodeAfter);
711
- }
712
- }
713
- // At this point, we split elements up to the parent in which `node` is allowed.
714
- // Note that `_getAllowedIn()` checks if the `node` is allowed either directly, or when auto-paragraphed.
715
- // So, let's check if the `node` is allowed directly. If not, we need to auto-paragraph it.
716
- if (!this.schema.checkChild(this.position.parent, node)) {
717
- this._insertAutoParagraph();
718
- }
719
- return true;
720
- }
721
- /**
722
- * Gets the element in which the given node is allowed. It checks the passed element and all its ancestors.
723
- *
724
- * It also verifies if auto-paragraphing could help.
725
- *
726
- * @param contextElement The element in which context the node should be checked.
727
- * @param childNode The node to check.
728
- */
729
- _getAllowedIn(contextElement, childNode) {
730
- const context = this.schema.createContext(contextElement);
731
- // Check if a node can be inserted in the given context...
732
- if (this.schema.checkChild(context, childNode)) {
733
- return contextElement;
734
- }
735
- // ...or it would be accepted if a paragraph would be inserted.
736
- if (this.schema.checkChild(context, 'paragraph') &&
737
- this.schema.checkChild(context.push('paragraph'), childNode)) {
738
- return contextElement;
739
- }
740
- // If the child wasn't allowed in the context element and the element is a limit there's no point in
741
- // checking any further towards the root. This is it: the limit is unsplittable and there's nothing
742
- // we can do about it. Without this check, the algorithm will analyze parent of the limit and may create
743
- // an illusion of the child being allowed. There's no way to insert it down there, though. It results in
744
- // infinite loops.
745
- if (this.schema.isLimit(contextElement)) {
746
- return null;
747
- }
748
- return this._getAllowedIn(contextElement.parent, childNode);
749
- }
750
- }