@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,1026 +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/documentselection
7
- */
8
- import { ModelTypeCheckable } from './typecheckable.js';
9
- import { ModelLiveRange } from './liverange.js';
10
- import { ModelSelection } from './selection.js';
11
- import { ModelText } from './text.js';
12
- import { ModelTextProxy } from './textproxy.js';
13
- import { CKEditorError, Collection, EmitterMixin, toMap, uid } from '@ckeditor/ckeditor5-utils';
14
- const storePrefix = 'selection:';
15
- /**
16
- * `ModelDocumentSelection` is a special selection which is used as the
17
- * {@link module:engine/model/document~ModelDocument#selection document's selection}.
18
- * There can be only one instance of `ModelDocumentSelection` per document.
19
- *
20
- * Document selection can only be changed by using the {@link module:engine/model/writer~ModelWriter} instance
21
- * inside the {@link module:engine/model/model~Model#change `change()`} block, as it provides a secure way to modify model.
22
- *
23
- * `ModelDocumentSelection` is automatically updated upon changes in the {@link module:engine/model/document~ModelDocument document}
24
- * to always contain valid ranges. Its attributes are inherited from the text unless set explicitly.
25
- *
26
- * Differences between {@link module:engine/model/selection~ModelSelection} and `ModelDocumentSelection` are:
27
- * * there is always a range in `ModelDocumentSelection` - even if no ranges were added there is a "default range"
28
- * present in the selection,
29
- * * ranges added to this selection updates automatically when the document changes,
30
- * * attributes of `ModelDocumentSelection` are updated automatically according to selection ranges.
31
- *
32
- * Since `ModelDocumentSelection` uses {@link module:engine/model/liverange~ModelLiveRange live ranges}
33
- * and is updated when {@link module:engine/model/document~ModelDocument document}
34
- * changes, it cannot be set on {@link module:engine/model/node~ModelNode nodes}
35
- * that are inside {@link module:engine/model/documentfragment~ModelDocumentFragment document fragment}.
36
- * If you need to represent a selection in document fragment,
37
- * use {@link module:engine/model/selection~ModelSelection Selection class} instead.
38
- */
39
- export class ModelDocumentSelection extends /* #__PURE__ */ EmitterMixin(ModelTypeCheckable) {
40
- /**
41
- * Selection used internally by that class (`ModelDocumentSelection` is a proxy to that selection).
42
- */
43
- _selection;
44
- /**
45
- * Creates an empty live selection for given {@link module:engine/model/document~ModelDocument}.
46
- *
47
- * @param doc Document which owns this selection.
48
- */
49
- constructor(doc) {
50
- super();
51
- this._selection = new LiveSelection(doc);
52
- this._selection.delegate('change:range').to(this);
53
- this._selection.delegate('change:attribute').to(this);
54
- this._selection.delegate('change:marker').to(this);
55
- }
56
- /**
57
- * Describes whether the selection is collapsed. Selection is collapsed when there is exactly one range which is
58
- * collapsed.
59
- */
60
- get isCollapsed() {
61
- return this._selection.isCollapsed;
62
- }
63
- /**
64
- * Selection anchor. Anchor may be described as a position where the most recent part of the selection starts.
65
- * Together with {@link #focus} they define the direction of selection, which is important
66
- * when expanding/shrinking selection. Anchor is always {@link module:engine/model/range~ModelRange#start start} or
67
- * {@link module:engine/model/range~ModelRange#end end} position of the most recently added range.
68
- *
69
- * Is set to `null` if there are no ranges in selection.
70
- *
71
- * @see #focus
72
- */
73
- get anchor() {
74
- return this._selection.anchor;
75
- }
76
- /**
77
- * Selection focus. Focus is a position where the selection ends.
78
- *
79
- * Is set to `null` if there are no ranges in selection.
80
- *
81
- * @see #anchor
82
- */
83
- get focus() {
84
- return this._selection.focus;
85
- }
86
- /**
87
- * Number of ranges in selection.
88
- */
89
- get rangeCount() {
90
- return this._selection.rangeCount;
91
- }
92
- /**
93
- * Describes whether `Documentselection` has own range(s) set, or if it is defaulted to
94
- * {@link module:engine/model/document~ModelDocument#_getDefaultRange document's default range}.
95
- */
96
- get hasOwnRange() {
97
- return this._selection.hasOwnRange;
98
- }
99
- /**
100
- * Specifies whether the {@link #focus}
101
- * precedes {@link #anchor}.
102
- *
103
- * @readonly
104
- * @type {Boolean}
105
- */
106
- get isBackward() {
107
- return this._selection.isBackward;
108
- }
109
- /**
110
- * Describes whether the gravity is overridden (using {@link module:engine/model/writer~ModelWriter#overrideSelectionGravity}) or not.
111
- *
112
- * Note that the gravity remains overridden as long as will not be restored the same number of times as it was overridden.
113
- */
114
- get isGravityOverridden() {
115
- return this._selection.isGravityOverridden;
116
- }
117
- /**
118
- * A collection of selection {@link module:engine/model/markercollection~Marker markers}.
119
- * Marker is a selection marker when selection range is inside the marker range.
120
- *
121
- * **Note**: Only markers from {@link ~ModelDocumentSelection#observeMarkers observed markers groups} are collected.
122
- */
123
- get markers() {
124
- return this._selection.markers;
125
- }
126
- /**
127
- * Used for the compatibility with the {@link module:engine/model/selection~ModelSelection#isEqual} method.
128
- *
129
- * @internal
130
- */
131
- get _ranges() {
132
- return this._selection._ranges;
133
- }
134
- /**
135
- * Returns an iterable that iterates over copies of selection ranges.
136
- */
137
- getRanges() {
138
- return this._selection.getRanges();
139
- }
140
- /**
141
- * Returns the first position in the selection.
142
- * First position is the position that {@link module:engine/model/position~ModelPosition#isBefore is before}
143
- * any other position in the selection.
144
- *
145
- * Returns `null` if there are no ranges in selection.
146
- */
147
- getFirstPosition() {
148
- return this._selection.getFirstPosition();
149
- }
150
- /**
151
- * Returns the last position in the selection.
152
- * Last position is the position that {@link module:engine/model/position~ModelPosition#isAfter is after}
153
- * any other position in the selection.
154
- *
155
- * Returns `null` if there are no ranges in selection.
156
- */
157
- getLastPosition() {
158
- return this._selection.getLastPosition();
159
- }
160
- /**
161
- * Returns a copy of the first range in the selection.
162
- * First range is the one which {@link module:engine/model/range~ModelRange#start start} position
163
- * {@link module:engine/model/position~ModelPosition#isBefore is before} start position of all other ranges
164
- * (not to confuse with the first range added to the selection).
165
- *
166
- * Returns `null` if there are no ranges in selection.
167
- */
168
- getFirstRange() {
169
- return this._selection.getFirstRange();
170
- }
171
- /**
172
- * Returns a copy of the last range in the selection.
173
- * Last range is the one which {@link module:engine/model/range~ModelRange#end end} position
174
- * {@link module:engine/model/position~ModelPosition#isAfter is after} end position of all
175
- * other ranges (not to confuse with the range most recently added to the selection).
176
- *
177
- * Returns `null` if there are no ranges in selection.
178
- */
179
- getLastRange() {
180
- return this._selection.getLastRange();
181
- }
182
- /**
183
- * Gets elements of type {@link module:engine/model/schema~ModelSchema#isBlock "block"} touched by the selection.
184
- *
185
- * This method's result can be used for example to apply block styling to all blocks covered by this selection.
186
- *
187
- * **Note:** `getSelectedBlocks()` returns blocks that are nested in other non-block elements
188
- * but will not return blocks nested in other blocks.
189
- *
190
- * In this case the function will return exactly all 3 paragraphs (note: `<blockQuote>` is not a block itself):
191
- *
192
- * ```
193
- * <paragraph>[a</paragraph>
194
- * <blockQuote>
195
- * <paragraph>b</paragraph>
196
- * </blockQuote>
197
- * <paragraph>c]d</paragraph>
198
- * ```
199
- *
200
- * In this case the paragraph will also be returned, despite the collapsed selection:
201
- *
202
- * ```
203
- * <paragraph>[]a</paragraph>
204
- * ```
205
- *
206
- * In such a scenario, however, only blocks A, B & E will be returned as blocks C & D are nested in block B:
207
- *
208
- * ```
209
- * [<blockA></blockA>
210
- * <blockB>
211
- * <blockC></blockC>
212
- * <blockD></blockD>
213
- * </blockB>
214
- * <blockE></blockE>]
215
- * ```
216
- *
217
- * If the selection is inside a block all the inner blocks (A & B) are returned:
218
- *
219
- * ```
220
- * <block>
221
- * <blockA>[a</blockA>
222
- * <blockB>b]</blockB>
223
- * </block>
224
- * ```
225
- *
226
- * **Special case**: If a selection ends at the beginning of a block, that block is not returned as from user perspective
227
- * this block wasn't selected. See [#984](https://github.com/ckeditor/ckeditor5-engine/issues/984) for more details.
228
- *
229
- * ```
230
- * <paragraph>[a</paragraph>
231
- * <paragraph>b</paragraph>
232
- * <paragraph>]c</paragraph> // this block will not be returned
233
- * ```
234
- */
235
- getSelectedBlocks() {
236
- return this._selection.getSelectedBlocks();
237
- }
238
- /**
239
- * Returns the selected element. {@link module:engine/model/element~ModelElement Element} is considered as selected if there is only
240
- * one range in the selection, and that range contains exactly one element.
241
- * Returns `null` if there is no selected element.
242
- */
243
- getSelectedElement() {
244
- return this._selection.getSelectedElement();
245
- }
246
- /**
247
- * Checks whether the selection contains the entire content of the given element. This means that selection must start
248
- * at a position {@link module:engine/model/position~ModelPosition#isTouching touching} the element's start and ends at position
249
- * touching the element's end.
250
- *
251
- * By default, this method will check whether the entire content of the selection's current root is selected.
252
- * Useful to check if e.g. the user has just pressed <kbd>Ctrl</kbd> + <kbd>A</kbd>.
253
- */
254
- containsEntireContent(element) {
255
- return this._selection.containsEntireContent(element);
256
- }
257
- /**
258
- * Unbinds all events previously bound by document selection.
259
- */
260
- destroy() {
261
- this._selection.destroy();
262
- }
263
- /**
264
- * Returns iterable that iterates over this selection's attribute keys.
265
- */
266
- getAttributeKeys() {
267
- return this._selection.getAttributeKeys();
268
- }
269
- /**
270
- * Returns iterable that iterates over this selection's attributes.
271
- *
272
- * Attributes are returned as arrays containing two items. First one is attribute key and second is attribute value.
273
- * This format is accepted by native `Map` object and also can be passed in `Node` constructor.
274
- */
275
- getAttributes() {
276
- return this._selection.getAttributes();
277
- }
278
- /**
279
- * Gets an attribute value for given key or `undefined` if that attribute is not set on the selection.
280
- *
281
- * @param key Key of attribute to look for.
282
- * @returns Attribute value or `undefined`.
283
- */
284
- getAttribute(key) {
285
- return this._selection.getAttribute(key);
286
- }
287
- /**
288
- * Checks if the selection has an attribute for given key.
289
- *
290
- * @param key Key of attribute to check.
291
- * @returns `true` if attribute with given key is set on selection, `false` otherwise.
292
- */
293
- hasAttribute(key) {
294
- return this._selection.hasAttribute(key);
295
- }
296
- /**
297
- * Refreshes selection attributes and markers according to the current position in the model.
298
- */
299
- refresh() {
300
- this._selection.updateMarkers();
301
- this._selection._updateAttributes(false);
302
- }
303
- /**
304
- * Registers a marker group prefix or a marker name to be collected in the
305
- * {@link ~ModelDocumentSelection#markers selection markers collection}.
306
- *
307
- * See also {@link module:engine/model/markercollection~MarkerCollection#getMarkersGroup `MarkerCollection#getMarkersGroup()`}.
308
- *
309
- * @param prefixOrName The marker group prefix or marker name.
310
- */
311
- observeMarkers(prefixOrName) {
312
- this._selection.observeMarkers(prefixOrName);
313
- }
314
- /**
315
- * Converts `DocumentSelection` to plain object and returns it.
316
- *
317
- * @returns `DocumentSelection` converted to plain object.
318
- */
319
- toJSON() {
320
- return this._selection.toJSON();
321
- }
322
- /**
323
- * Moves {@link module:engine/model/documentselection~ModelDocumentSelection#focus} to the specified location.
324
- * Should be used only within the {@link module:engine/model/writer~ModelWriter#setSelectionFocus} method.
325
- *
326
- * The location can be specified in the same form as
327
- * {@link module:engine/model/writer~ModelWriter#createPositionAt writer.createPositionAt()} parameters.
328
- *
329
- * @see module:engine/model/writer~ModelWriter#setSelectionFocus
330
- * @internal
331
- * @param offset Offset or one of the flags. Used only when
332
- * first parameter is a {@link module:engine/model/item~ModelItem model item}.
333
- */
334
- _setFocus(itemOrPosition, offset) {
335
- this._selection.setFocus(itemOrPosition, offset);
336
- }
337
- /**
338
- * Sets this selection's ranges and direction to the specified location based on the given
339
- * {@link module:engine/model/selection~ModelSelectable selectable}.
340
- * Should be used only within the {@link module:engine/model/writer~ModelWriter#setSelection} method.
341
- *
342
- * @see module:engine/model/writer~ModelWriter#setSelection
343
- * @internal
344
- */
345
- _setTo(...args) {
346
- this._selection.setTo(...args);
347
- }
348
- /**
349
- * Sets attribute on the selection. If attribute with the same key already is set, it's value is overwritten.
350
- * Should be used only within the {@link module:engine/model/writer~ModelWriter#setSelectionAttribute} method.
351
- *
352
- * @see module:engine/model/writer~ModelWriter#setSelectionAttribute
353
- * @internal
354
- * @param key Key of the attribute to set.
355
- * @param value Attribute value.
356
- */
357
- _setAttribute(key, value) {
358
- this._selection.setAttribute(key, value);
359
- }
360
- /**
361
- * Removes an attribute with given key from the selection.
362
- * If the given attribute was set on the selection, fires the {@link module:engine/model/selection~ModelSelection#event:change:range}
363
- * event with removed attribute key.
364
- * Should be used only within the {@link module:engine/model/writer~ModelWriter#removeSelectionAttribute} method.
365
- *
366
- * @see module:engine/model/writer~ModelWriter#removeSelectionAttribute
367
- * @internal
368
- * @param key Key of the attribute to remove.
369
- */
370
- _removeAttribute(key) {
371
- this._selection.removeAttribute(key);
372
- }
373
- /**
374
- * Returns an iterable that iterates through all selection attributes stored in current selection's parent.
375
- *
376
- * @internal
377
- */
378
- _getStoredAttributes() {
379
- return this._selection.getStoredAttributes();
380
- }
381
- /**
382
- * Temporarily changes the gravity of the selection from the left to the right.
383
- *
384
- * The gravity defines from which direction the selection inherits its attributes. If it's the default left
385
- * gravity, the selection (after being moved by the the user) inherits attributes from its left hand side.
386
- * This method allows to temporarily override this behavior by forcing the gravity to the right.
387
- *
388
- * It returns an unique identifier which is required to restore the gravity. It guarantees the symmetry
389
- * of the process.
390
- *
391
- * @see module:engine/model/writer~ModelWriter#overrideSelectionGravity
392
- * @internal
393
- * @returns The unique id which allows restoring the gravity.
394
- */
395
- _overrideGravity() {
396
- return this._selection.overrideGravity();
397
- }
398
- /**
399
- * Restores the {@link ~ModelDocumentSelection#_overrideGravity overridden gravity}.
400
- *
401
- * Restoring the gravity is only possible using the unique identifier returned by
402
- * {@link ~ModelDocumentSelection#_overrideGravity}. Note that the gravity remains overridden as long as won't be restored
403
- * the same number of times it was overridden.
404
- *
405
- * @see module:engine/model/writer~ModelWriter#restoreSelectionGravity
406
- * @internal
407
- * @param uid The unique id returned by {@link #_overrideGravity}.
408
- */
409
- _restoreGravity(uid) {
410
- this._selection.restoreGravity(uid);
411
- }
412
- /**
413
- * Generates and returns an attribute key for selection attributes store, basing on original attribute key.
414
- *
415
- * @internal
416
- * @param key Attribute key to convert.
417
- * @returns Converted attribute key, applicable for selection store.
418
- */
419
- static _getStoreAttributeKey(key) {
420
- return storePrefix + key;
421
- }
422
- /**
423
- * Checks whether the given attribute key is an attribute stored on an element.
424
- *
425
- * @internal
426
- */
427
- static _isStoreAttributeKey(key) {
428
- return key.startsWith(storePrefix);
429
- }
430
- }
431
- // The magic of type inference using `is` method is centralized in `TypeCheckable` class.
432
- // Proper overload would interfere with that.
433
- ModelDocumentSelection.prototype.is = function (type) {
434
- return type === 'selection' ||
435
- type == 'model:selection' ||
436
- type == 'documentSelection' ||
437
- type == 'model:documentSelection';
438
- };
439
- /**
440
- * `LiveSelection` is used internally by {@link module:engine/model/documentselection~ModelDocumentSelection}
441
- * and shouldn't be used directly.
442
- *
443
- * `LiveSelection` is automatically updated upon changes in the {@link module:engine/model/document~ModelDocument document}
444
- * to always contain valid ranges. Its attributes are inherited from the text unless set explicitly.
445
- *
446
- * Differences between {@link module:engine/model/selection~ModelSelection} and `LiveSelection` are:
447
- * * there is always a range in `LiveSelection` - even if no ranges were added there is a "default range"
448
- * present in the selection,
449
- * * ranges added to this selection updates automatically when the document changes,
450
- * * attributes of `LiveSelection` are updated automatically according to selection ranges.
451
- */
452
- class LiveSelection extends ModelSelection {
453
- /**
454
- * List of selection markers.
455
- * Marker is a selection marker when selection range is inside the marker range.
456
- */
457
- markers = new Collection({ idProperty: 'name' });
458
- /**
459
- * Document which owns this selection.
460
- */
461
- _model;
462
- /**
463
- * Document which owns this selection.
464
- */
465
- _document;
466
- /**
467
- * Keeps mapping of attribute name to priority with which the attribute got modified (added/changed/removed)
468
- * last time. Possible values of priority are: `'low'` and `'normal'`.
469
- *
470
- * Priorities are used by internal `LiveSelection` mechanisms. All attributes set using `LiveSelection`
471
- * attributes API are set with `'normal'` priority.
472
- */
473
- _attributePriority = new Map();
474
- /**
475
- * Position to which the selection should be set if the last selection range was moved to the graveyard.
476
- */
477
- _selectionRestorePosition = null;
478
- /**
479
- * Flag that informs whether the selection ranges have changed. It is changed on true when `LiveRange#change:range` event is fired.
480
- */
481
- _hasChangedRange = false;
482
- /**
483
- * Each overriding gravity adds an UID to the set and each removal removes it.
484
- * Gravity is overridden when there's at least one UID in the set.
485
- * Gravity is restored when the set is empty.
486
- * This is to prevent conflicts when gravity is overridden by more than one feature at the same time.
487
- */
488
- _overriddenGravityRegister = new Set();
489
- /**
490
- * Prefixes of marker names that should affect `LiveSelection#markers` collection.
491
- */
492
- _observedMarkers = new Set();
493
- /**
494
- * Creates an empty live selection for given {@link module:engine/model/document~ModelDocument}.
495
- *
496
- * @param doc Document which owns this selection.
497
- */
498
- constructor(doc) {
499
- super();
500
- this._model = doc.model;
501
- this._document = doc;
502
- // Ensure selection is correct after each operation.
503
- this.listenTo(this._model, 'applyOperation', (evt, args) => {
504
- const operation = args[0];
505
- if (!operation.isDocumentOperation || operation.type == 'marker' || operation.type == 'rename' || operation.type == 'noop') {
506
- return;
507
- }
508
- // Fix selection if the last range was removed from it and we have a position to which we can restore the selection.
509
- if (this._ranges.length == 0 && this._selectionRestorePosition) {
510
- this._fixGraveyardSelection(this._selectionRestorePosition);
511
- }
512
- // "Forget" the restore position even if it was not "used".
513
- this._selectionRestorePosition = null;
514
- if (this._hasChangedRange) {
515
- this._hasChangedRange = false;
516
- this.fire('change:range', { directChange: false });
517
- }
518
- }, { priority: 'lowest' });
519
- // Ensure selection is correct and up to date after each range change.
520
- this.on('change:range', () => {
521
- this._validateSelectionRanges(this.getRanges());
522
- });
523
- // Update markers data stored by the selection after each marker change.
524
- // This handles only marker changes done through marker operations (not model tree changes).
525
- this.listenTo(this._model.markers, 'update', (evt, marker, oldRange, newRange) => {
526
- this._updateMarker(marker, newRange);
527
- });
528
- // Ensure selection is up to date after each change block.
529
- this.listenTo(this._document, 'change', (evt, batch) => {
530
- clearAttributesStoredInElement(this._model, batch);
531
- });
532
- }
533
- get isCollapsed() {
534
- const length = this._ranges.length;
535
- return length === 0 ? this._document._getDefaultRange().isCollapsed : super.isCollapsed;
536
- }
537
- get anchor() {
538
- return super.anchor || this._document._getDefaultRange().start;
539
- }
540
- get focus() {
541
- return super.focus || this._document._getDefaultRange().end;
542
- }
543
- get rangeCount() {
544
- return this._ranges.length ? this._ranges.length : 1;
545
- }
546
- /**
547
- * Describes whether `LiveSelection` has own range(s) set, or if it is defaulted to
548
- * {@link module:engine/model/document~ModelDocument#_getDefaultRange document's default range}.
549
- */
550
- get hasOwnRange() {
551
- return this._ranges.length > 0;
552
- }
553
- /**
554
- * When set to `true` then selection attributes on node before the caret won't be taken
555
- * into consideration while updating selection attributes.
556
- */
557
- get isGravityOverridden() {
558
- return !!this._overriddenGravityRegister.size;
559
- }
560
- /**
561
- * Unbinds all events previously bound by live selection.
562
- */
563
- destroy() {
564
- for (let i = 0; i < this._ranges.length; i++) {
565
- this._ranges[i].detach();
566
- }
567
- this.stopListening();
568
- }
569
- *getRanges() {
570
- if (this._ranges.length) {
571
- yield* super.getRanges();
572
- }
573
- else {
574
- yield this._document._getDefaultRange();
575
- }
576
- }
577
- getFirstRange() {
578
- return super.getFirstRange() || this._document._getDefaultRange();
579
- }
580
- getLastRange() {
581
- return super.getLastRange() || this._document._getDefaultRange();
582
- }
583
- setTo(...args) {
584
- super.setTo(...args);
585
- this._updateAttributes(true);
586
- this.updateMarkers();
587
- }
588
- setFocus(itemOrPosition, offset) {
589
- super.setFocus(itemOrPosition, offset);
590
- this._updateAttributes(true);
591
- this.updateMarkers();
592
- }
593
- setAttribute(key, value) {
594
- if (this._setAttribute(key, value)) {
595
- // Fire event with exact data.
596
- const attributeKeys = [key];
597
- this.fire('change:attribute', { attributeKeys, directChange: true });
598
- }
599
- }
600
- removeAttribute(key) {
601
- if (this._removeAttribute(key)) {
602
- // Fire event with exact data.
603
- const attributeKeys = [key];
604
- this.fire('change:attribute', { attributeKeys, directChange: true });
605
- }
606
- }
607
- overrideGravity() {
608
- const overrideUid = uid();
609
- // Remember that another overriding has been requested. It will need to be removed
610
- // before the gravity is to be restored.
611
- this._overriddenGravityRegister.add(overrideUid);
612
- if (this._overriddenGravityRegister.size === 1) {
613
- this._updateAttributes(true);
614
- }
615
- return overrideUid;
616
- }
617
- restoreGravity(uid) {
618
- if (!this._overriddenGravityRegister.has(uid)) {
619
- /**
620
- * Restoring gravity for an unknown UID is not possible. Make sure you are using a correct
621
- * UID obtained from the {@link module:engine/model/writer~ModelWriter#overrideSelectionGravity} to restore.
622
- *
623
- * @error document-selection-gravity-wrong-restore
624
- * @param {string} uid The unique identifier returned by
625
- * {@link module:engine/model/documentselection~ModelDocumentSelection#_overrideGravity}.
626
- */
627
- throw new CKEditorError('document-selection-gravity-wrong-restore', this, { uid });
628
- }
629
- this._overriddenGravityRegister.delete(uid);
630
- // Restore gravity only when all overriding have been restored.
631
- if (!this.isGravityOverridden) {
632
- this._updateAttributes(true);
633
- }
634
- }
635
- observeMarkers(prefixOrName) {
636
- this._observedMarkers.add(prefixOrName);
637
- this.updateMarkers();
638
- }
639
- _replaceAllRanges(ranges) {
640
- this._validateSelectionRanges(ranges);
641
- super._replaceAllRanges(ranges);
642
- }
643
- _popRange() {
644
- this._ranges.pop().detach();
645
- }
646
- _pushRange(range) {
647
- const liveRange = this._prepareRange(range);
648
- // `undefined` is returned when given `range` is in graveyard root.
649
- if (liveRange) {
650
- this._ranges.push(liveRange);
651
- }
652
- }
653
- /**
654
- * Converts `LiveSelection` to plain object and returns it.
655
- *
656
- * @returns `LiveSelection` converted to plain object.
657
- */
658
- toJSON() {
659
- const json = super.toJSON();
660
- if (this.markers.length) {
661
- json.markers = this.markers.map(marker => marker.toJSON());
662
- }
663
- return json;
664
- }
665
- _validateSelectionRanges(ranges) {
666
- for (const range of ranges) {
667
- if (!this._document._validateSelectionRange(range)) {
668
- /**
669
- * Range from {@link module:engine/model/documentselection~ModelDocumentSelection document selection}
670
- * starts or ends at incorrect position.
671
- *
672
- * @error document-selection-wrong-position
673
- * @param {module:engine/model/range~ModelRange} range The invalid range.
674
- */
675
- throw new CKEditorError('document-selection-wrong-position', this, { range });
676
- }
677
- }
678
- }
679
- /**
680
- * Prepares given range to be added to selection. Checks if it is correct,
681
- * converts it to {@link module:engine/model/liverange~ModelLiveRange ModelLiveRange}
682
- * and sets listeners listening to the range's change event.
683
- */
684
- _prepareRange(range) {
685
- this._checkRange(range);
686
- if (range.root == this._document.graveyard) {
687
- // @if CK_DEBUG // console.warn( 'Trying to add a Range that is in the graveyard root. Range rejected.' );
688
- return;
689
- }
690
- const liveRange = ModelLiveRange.fromRange(range);
691
- // If selection range is moved to the graveyard remove it from the selection object.
692
- // Also, save some data that can be used to restore selection later, on `Model#applyOperation` event.
693
- liveRange.on('change:range', (evt, oldRange, data) => {
694
- this._hasChangedRange = true;
695
- if (liveRange.root == this._document.graveyard) {
696
- this._selectionRestorePosition = data.deletionPosition;
697
- const index = this._ranges.indexOf(liveRange);
698
- this._ranges.splice(index, 1);
699
- liveRange.detach();
700
- }
701
- });
702
- return liveRange;
703
- }
704
- updateMarkers() {
705
- if (!this._observedMarkers.size) {
706
- return;
707
- }
708
- const markers = [];
709
- let changed = false;
710
- for (const marker of this._model.markers) {
711
- const markerGroup = marker.name.split(':', 1)[0];
712
- if (!this._observedMarkers.has(markerGroup)) {
713
- continue;
714
- }
715
- const markerRange = marker.getRange();
716
- for (const selectionRange of this.getRanges()) {
717
- if (markerRange.containsRange(selectionRange, !selectionRange.isCollapsed)) {
718
- markers.push(marker);
719
- }
720
- }
721
- }
722
- const oldMarkers = Array.from(this.markers);
723
- for (const marker of markers) {
724
- if (!this.markers.has(marker)) {
725
- this.markers.add(marker);
726
- changed = true;
727
- }
728
- }
729
- for (const marker of Array.from(this.markers)) {
730
- if (!markers.includes(marker)) {
731
- this.markers.remove(marker);
732
- changed = true;
733
- }
734
- }
735
- if (changed) {
736
- this.fire('change:marker', { oldMarkers, directChange: false });
737
- }
738
- }
739
- _updateMarker(marker, markerRange) {
740
- const markerGroup = marker.name.split(':', 1)[0];
741
- if (!this._observedMarkers.has(markerGroup)) {
742
- return;
743
- }
744
- let changed = false;
745
- const oldMarkers = Array.from(this.markers);
746
- const hasMarker = this.markers.has(marker);
747
- if (!markerRange) {
748
- if (hasMarker) {
749
- this.markers.remove(marker);
750
- changed = true;
751
- }
752
- }
753
- else {
754
- let contained = false;
755
- for (const selectionRange of this.getRanges()) {
756
- if (markerRange.containsRange(selectionRange, !selectionRange.isCollapsed)) {
757
- contained = true;
758
- break;
759
- }
760
- }
761
- if (contained && !hasMarker) {
762
- this.markers.add(marker);
763
- changed = true;
764
- }
765
- else if (!contained && hasMarker) {
766
- this.markers.remove(marker);
767
- changed = true;
768
- }
769
- }
770
- if (changed) {
771
- this.fire('change:marker', { oldMarkers, directChange: false });
772
- }
773
- }
774
- /**
775
- * Updates this selection attributes according to its ranges and the {@link module:engine/model/document~ModelDocument model document}.
776
- */
777
- _updateAttributes(clearAll) {
778
- const newAttributes = toMap(this._getSurroundingAttributes());
779
- const oldAttributes = toMap(this.getAttributes());
780
- if (clearAll) {
781
- // If `clearAll` remove all attributes and reset priorities.
782
- this._attributePriority = new Map();
783
- this._attrs = new Map();
784
- }
785
- else {
786
- // If not, remove only attributes added with `low` priority.
787
- for (const [key, priority] of this._attributePriority) {
788
- if (priority == 'low') {
789
- this._attrs.delete(key);
790
- this._attributePriority.delete(key);
791
- }
792
- }
793
- }
794
- this._setAttributesTo(newAttributes);
795
- // Let's evaluate which attributes really changed.
796
- const changed = [];
797
- // First, loop through all attributes that are set on selection right now.
798
- // Check which of them are different than old attributes.
799
- for (const [newKey, newValue] of this.getAttributes()) {
800
- if (!oldAttributes.has(newKey) || oldAttributes.get(newKey) !== newValue) {
801
- changed.push(newKey);
802
- }
803
- }
804
- // Then, check which of old attributes got removed.
805
- for (const [oldKey] of oldAttributes) {
806
- if (!this.hasAttribute(oldKey)) {
807
- changed.push(oldKey);
808
- }
809
- }
810
- // Fire event with exact data (fire only if anything changed).
811
- if (changed.length > 0) {
812
- this.fire('change:attribute', { attributeKeys: changed, directChange: false });
813
- }
814
- }
815
- /**
816
- * Internal method for setting `LiveSelection` attribute. Supports attribute priorities (through `directChange`
817
- * parameter).
818
- */
819
- _setAttribute(key, value, directChange = true) {
820
- const priority = directChange ? 'normal' : 'low';
821
- if (priority == 'low' && this._attributePriority.get(key) == 'normal') {
822
- // Priority too low.
823
- return false;
824
- }
825
- const oldValue = super.getAttribute(key);
826
- // Don't do anything if value has not changed.
827
- if (oldValue === value) {
828
- return false;
829
- }
830
- this._attrs.set(key, value);
831
- // Update priorities map.
832
- this._attributePriority.set(key, priority);
833
- return true;
834
- }
835
- /**
836
- * Internal method for removing `LiveSelection` attribute. Supports attribute priorities (through `directChange`
837
- * parameter).
838
- *
839
- * NOTE: Even if attribute is not present in the selection but is provided to this method, it's priority will
840
- * be changed according to `directChange` parameter.
841
- */
842
- _removeAttribute(key, directChange = true) {
843
- const priority = directChange ? 'normal' : 'low';
844
- if (priority == 'low' && this._attributePriority.get(key) == 'normal') {
845
- // Priority too low.
846
- return false;
847
- }
848
- // Update priorities map.
849
- this._attributePriority.set(key, priority);
850
- // Don't do anything if value has not changed.
851
- if (!super.hasAttribute(key)) {
852
- return false;
853
- }
854
- this._attrs.delete(key);
855
- return true;
856
- }
857
- /**
858
- * Internal method for setting multiple `LiveSelection` attributes. Supports attribute priorities (through
859
- * `directChange` parameter).
860
- */
861
- _setAttributesTo(attrs) {
862
- const changed = new Set();
863
- for (const [oldKey, oldValue] of this.getAttributes()) {
864
- // Do not remove attribute if attribute with same key and value is about to be set.
865
- if (attrs.get(oldKey) === oldValue) {
866
- continue;
867
- }
868
- // All rest attributes will be removed so changed attributes won't change .
869
- this._removeAttribute(oldKey, false);
870
- }
871
- for (const [key, value] of attrs) {
872
- // Attribute may not be set because of attributes or because same key/value is already added.
873
- const gotAdded = this._setAttribute(key, value, false);
874
- if (gotAdded) {
875
- changed.add(key);
876
- }
877
- }
878
- return changed;
879
- }
880
- /**
881
- * Returns an iterable that iterates through all selection attributes stored in current selection's parent.
882
- */
883
- *getStoredAttributes() {
884
- const selectionParent = this.getFirstPosition().parent;
885
- if (this.isCollapsed && selectionParent.isEmpty) {
886
- for (const key of selectionParent.getAttributeKeys()) {
887
- if (key.startsWith(storePrefix)) {
888
- const realKey = key.substr(storePrefix.length);
889
- yield [realKey, selectionParent.getAttribute(key)];
890
- }
891
- }
892
- }
893
- }
894
- /**
895
- * Checks model text nodes that are closest to the selection's first position and returns attributes of first
896
- * found element. If there are no text nodes in selection's first position parent, it returns selection
897
- * attributes stored in that parent.
898
- */
899
- _getSurroundingAttributes() {
900
- const position = this.getFirstPosition();
901
- const schema = this._model.schema;
902
- if (position.root.rootName == '$graveyard') {
903
- return null;
904
- }
905
- let attrs = null;
906
- if (!this.isCollapsed) {
907
- // 1. If selection is a range...
908
- const range = this.getFirstRange();
909
- // ...look for a first character node in that range and take attributes from it.
910
- for (const value of range) {
911
- // If the item is an object, we don't want to get attributes from its children...
912
- if (value.item.is('element') && schema.isObject(value.item)) {
913
- // ...but collect attributes from inline object.
914
- attrs = getTextAttributes(value.item, schema);
915
- break;
916
- }
917
- if (value.type == 'text') {
918
- attrs = value.item.getAttributes();
919
- break;
920
- }
921
- }
922
- }
923
- else {
924
- // 2. If the selection is a caret or the range does not contain a character node...
925
- const nodeBefore = position.textNode ? position.textNode : position.nodeBefore;
926
- const nodeAfter = position.textNode ? position.textNode : position.nodeAfter;
927
- // When gravity is overridden then don't take node before into consideration.
928
- if (!this.isGravityOverridden) {
929
- // ...look at the node before caret and take attributes from it if it is a character node.
930
- attrs = getTextAttributes(nodeBefore, schema);
931
- }
932
- // 3. If not, look at the node after caret...
933
- if (!attrs) {
934
- attrs = getTextAttributes(nodeAfter, schema);
935
- }
936
- // 4. If not, try to find the first character on the left, that is in the same node.
937
- // When gravity is overridden then don't take node before into consideration.
938
- if (!this.isGravityOverridden && !attrs) {
939
- let node = nodeBefore;
940
- while (node && !attrs) {
941
- node = node.previousSibling;
942
- attrs = getTextAttributes(node, schema);
943
- }
944
- }
945
- // 5. If not found, try to find the first character on the right, that is in the same node.
946
- if (!attrs) {
947
- let node = nodeAfter;
948
- while (node && !attrs) {
949
- node = node.nextSibling;
950
- attrs = getTextAttributes(node, schema);
951
- }
952
- }
953
- // 6. If not found, selection should retrieve attributes from parent.
954
- if (!attrs) {
955
- attrs = this.getStoredAttributes();
956
- }
957
- }
958
- return attrs;
959
- }
960
- /**
961
- * Fixes the selection after all its ranges got removed.
962
- * @param deletionPosition Position where the deletion happened.
963
- */
964
- _fixGraveyardSelection(deletionPosition) {
965
- // Find a range that is a correct selection range and is closest to the position where the deletion happened.
966
- const selectionRange = this._model.schema.getNearestSelectionRange(deletionPosition);
967
- // If nearest valid selection range has been found - add it in the place of old range.
968
- if (selectionRange) {
969
- // Check the range, convert it to live range, bind events, etc.
970
- this._pushRange(selectionRange);
971
- }
972
- // If nearest valid selection range cannot be found don't add any range. Selection will be set to the default range.
973
- }
974
- }
975
- /**
976
- * Helper function for {@link module:engine/model/liveselection~LiveSelection#_updateAttributes}.
977
- *
978
- * It checks if the passed model item is a text node (or text proxy) and, if so, returns it's attributes.
979
- * If not, it checks if item is an inline object and does the same. Otherwise it returns `null`.
980
- */
981
- function getTextAttributes(node, schema) {
982
- if (!node) {
983
- return null;
984
- }
985
- if (node instanceof ModelTextProxy || node instanceof ModelText) {
986
- return node.getAttributes();
987
- }
988
- if (!schema.isInline(node)) {
989
- return null;
990
- }
991
- // Stop on inline elements (such as `<softBreak>`) that are not objects (such as `<imageInline>` or `<mathml>`).
992
- if (!schema.isObject(node)) {
993
- return [];
994
- }
995
- const attributes = [];
996
- // Collect all attributes that can be applied to the text node.
997
- for (const [key, value] of node.getAttributes()) {
998
- if (schema.checkAttribute('$text', key) &&
999
- schema.getAttributeProperties(key).copyFromObject !== false) {
1000
- attributes.push([key, value]);
1001
- }
1002
- }
1003
- return attributes;
1004
- }
1005
- /**
1006
- * Removes selection attributes from element which is not empty anymore.
1007
- */
1008
- function clearAttributesStoredInElement(model, batch) {
1009
- const differ = model.document.differ;
1010
- for (const entry of differ.getChanges()) {
1011
- if (entry.type != 'insert') {
1012
- continue;
1013
- }
1014
- const changeParent = entry.position.parent;
1015
- const isNoLongerEmpty = entry.length === changeParent.maxOffset;
1016
- if (isNoLongerEmpty) {
1017
- model.enqueueChange(batch, writer => {
1018
- const storedAttributes = Array.from(changeParent.getAttributeKeys())
1019
- .filter(key => key.startsWith(storePrefix));
1020
- for (const key of storedAttributes) {
1021
- writer.removeAttribute(key, changeParent);
1022
- }
1023
- });
1024
- }
1025
- }
1026
- }