@ckeditor/ckeditor5-link 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 (203) hide show
  1. package/LICENSE.md +1 -1
  2. package/ckeditor5-metadata.json +6 -6
  3. package/{src → dist}/autolink.d.ts +2 -2
  4. package/dist/index-editor.css +181 -105
  5. package/dist/index.css +169 -151
  6. package/dist/index.css.map +1 -1
  7. package/{src → dist}/index.d.ts +1 -1
  8. package/dist/index.js +8 -8
  9. package/dist/index.js.map +1 -1
  10. package/{src → dist}/link.d.ts +1 -1
  11. package/{src → dist}/linkcommand.d.ts +2 -2
  12. package/{src → dist}/linkconfig.d.ts +1 -1
  13. package/{src → dist}/linkediting.d.ts +3 -3
  14. package/{src → dist}/linkimage.d.ts +1 -1
  15. package/{src → dist}/linkimageediting.d.ts +3 -2
  16. package/{src → dist}/linkimageui.d.ts +3 -2
  17. package/{src → dist}/linkui.d.ts +2 -2
  18. package/{src → dist}/ui/linkbuttonview.d.ts +2 -2
  19. package/{src → dist}/ui/linkformview.d.ts +2 -4
  20. package/{src → dist}/ui/linkpreviewbuttonview.d.ts +2 -2
  21. package/{src → dist}/ui/linkpropertiesview.d.ts +2 -2
  22. package/{src → dist}/ui/linkprovideritemsview.d.ts +2 -2
  23. package/{src → dist}/unlinkcommand.d.ts +1 -1
  24. package/{src → dist}/utils/automaticdecorators.d.ts +2 -2
  25. package/{src → dist}/utils/manualdecorator.d.ts +4 -4
  26. package/{src → dist}/utils.d.ts +2 -2
  27. package/package.json +29 -52
  28. package/build/link.js +0 -5
  29. package/build/translations/af.js +0 -1
  30. package/build/translations/ar.js +0 -1
  31. package/build/translations/ast.js +0 -1
  32. package/build/translations/az.js +0 -1
  33. package/build/translations/be.js +0 -1
  34. package/build/translations/bg.js +0 -1
  35. package/build/translations/bn.js +0 -1
  36. package/build/translations/bs.js +0 -1
  37. package/build/translations/ca.js +0 -1
  38. package/build/translations/cs.js +0 -1
  39. package/build/translations/da.js +0 -1
  40. package/build/translations/de-ch.js +0 -1
  41. package/build/translations/de.js +0 -1
  42. package/build/translations/el.js +0 -1
  43. package/build/translations/en-au.js +0 -1
  44. package/build/translations/en-gb.js +0 -1
  45. package/build/translations/eo.js +0 -1
  46. package/build/translations/es-co.js +0 -1
  47. package/build/translations/es.js +0 -1
  48. package/build/translations/et.js +0 -1
  49. package/build/translations/eu.js +0 -1
  50. package/build/translations/fa.js +0 -1
  51. package/build/translations/fi.js +0 -1
  52. package/build/translations/fr.js +0 -1
  53. package/build/translations/gl.js +0 -1
  54. package/build/translations/gu.js +0 -1
  55. package/build/translations/he.js +0 -1
  56. package/build/translations/hi.js +0 -1
  57. package/build/translations/hr.js +0 -1
  58. package/build/translations/hu.js +0 -1
  59. package/build/translations/hy.js +0 -1
  60. package/build/translations/id.js +0 -1
  61. package/build/translations/it.js +0 -1
  62. package/build/translations/ja.js +0 -1
  63. package/build/translations/jv.js +0 -1
  64. package/build/translations/kk.js +0 -1
  65. package/build/translations/km.js +0 -1
  66. package/build/translations/kn.js +0 -1
  67. package/build/translations/ko.js +0 -1
  68. package/build/translations/ku.js +0 -1
  69. package/build/translations/lt.js +0 -1
  70. package/build/translations/lv.js +0 -1
  71. package/build/translations/ms.js +0 -1
  72. package/build/translations/nb.js +0 -1
  73. package/build/translations/ne.js +0 -1
  74. package/build/translations/nl.js +0 -1
  75. package/build/translations/no.js +0 -1
  76. package/build/translations/oc.js +0 -1
  77. package/build/translations/pl.js +0 -1
  78. package/build/translations/pt-br.js +0 -1
  79. package/build/translations/pt.js +0 -1
  80. package/build/translations/ro.js +0 -1
  81. package/build/translations/ru.js +0 -1
  82. package/build/translations/si.js +0 -1
  83. package/build/translations/sk.js +0 -1
  84. package/build/translations/sl.js +0 -1
  85. package/build/translations/sq.js +0 -1
  86. package/build/translations/sr-latn.js +0 -1
  87. package/build/translations/sr.js +0 -1
  88. package/build/translations/sv.js +0 -1
  89. package/build/translations/th.js +0 -1
  90. package/build/translations/ti.js +0 -1
  91. package/build/translations/tk.js +0 -1
  92. package/build/translations/tr.js +0 -1
  93. package/build/translations/tt.js +0 -1
  94. package/build/translations/ug.js +0 -1
  95. package/build/translations/uk.js +0 -1
  96. package/build/translations/ur.js +0 -1
  97. package/build/translations/uz.js +0 -1
  98. package/build/translations/vi.js +0 -1
  99. package/build/translations/zh-cn.js +0 -1
  100. package/build/translations/zh.js +0 -1
  101. package/lang/contexts.json +0 -16
  102. package/lang/translations/af.po +0 -68
  103. package/lang/translations/ar.po +0 -68
  104. package/lang/translations/ast.po +0 -68
  105. package/lang/translations/az.po +0 -68
  106. package/lang/translations/be.po +0 -68
  107. package/lang/translations/bg.po +0 -68
  108. package/lang/translations/bn.po +0 -70
  109. package/lang/translations/bs.po +0 -68
  110. package/lang/translations/ca.po +0 -68
  111. package/lang/translations/cs.po +0 -68
  112. package/lang/translations/da.po +0 -68
  113. package/lang/translations/de-ch.po +0 -68
  114. package/lang/translations/de.po +0 -68
  115. package/lang/translations/el.po +0 -68
  116. package/lang/translations/en-au.po +0 -68
  117. package/lang/translations/en-gb.po +0 -68
  118. package/lang/translations/en.po +0 -68
  119. package/lang/translations/eo.po +0 -68
  120. package/lang/translations/es-co.po +0 -68
  121. package/lang/translations/es.po +0 -68
  122. package/lang/translations/et.po +0 -68
  123. package/lang/translations/eu.po +0 -68
  124. package/lang/translations/fa.po +0 -68
  125. package/lang/translations/fi.po +0 -68
  126. package/lang/translations/fr.po +0 -68
  127. package/lang/translations/gl.po +0 -68
  128. package/lang/translations/gu.po +0 -68
  129. package/lang/translations/he.po +0 -68
  130. package/lang/translations/hi.po +0 -68
  131. package/lang/translations/hr.po +0 -68
  132. package/lang/translations/hu.po +0 -68
  133. package/lang/translations/hy.po +0 -68
  134. package/lang/translations/id.po +0 -68
  135. package/lang/translations/it.po +0 -68
  136. package/lang/translations/ja.po +0 -68
  137. package/lang/translations/jv.po +0 -68
  138. package/lang/translations/kk.po +0 -68
  139. package/lang/translations/km.po +0 -68
  140. package/lang/translations/kn.po +0 -68
  141. package/lang/translations/ko.po +0 -68
  142. package/lang/translations/ku.po +0 -68
  143. package/lang/translations/lt.po +0 -68
  144. package/lang/translations/lv.po +0 -68
  145. package/lang/translations/ms.po +0 -68
  146. package/lang/translations/nb.po +0 -68
  147. package/lang/translations/ne.po +0 -68
  148. package/lang/translations/nl.po +0 -68
  149. package/lang/translations/no.po +0 -68
  150. package/lang/translations/oc.po +0 -68
  151. package/lang/translations/pl.po +0 -68
  152. package/lang/translations/pt-br.po +0 -68
  153. package/lang/translations/pt.po +0 -68
  154. package/lang/translations/ro.po +0 -68
  155. package/lang/translations/ru.po +0 -68
  156. package/lang/translations/si.po +0 -68
  157. package/lang/translations/sk.po +0 -68
  158. package/lang/translations/sl.po +0 -68
  159. package/lang/translations/sq.po +0 -68
  160. package/lang/translations/sr-latn.po +0 -68
  161. package/lang/translations/sr.po +0 -68
  162. package/lang/translations/sv.po +0 -68
  163. package/lang/translations/th.po +0 -68
  164. package/lang/translations/ti.po +0 -68
  165. package/lang/translations/tk.po +0 -68
  166. package/lang/translations/tr.po +0 -68
  167. package/lang/translations/tt.po +0 -68
  168. package/lang/translations/ug.po +0 -68
  169. package/lang/translations/uk.po +0 -68
  170. package/lang/translations/ur.po +0 -68
  171. package/lang/translations/uz.po +0 -68
  172. package/lang/translations/vi.po +0 -68
  173. package/lang/translations/zh-cn.po +0 -68
  174. package/lang/translations/zh.po +0 -68
  175. package/src/augmentation.js +0 -5
  176. package/src/autolink.js +0 -307
  177. package/src/index.js +0 -25
  178. package/src/link.js +0 -37
  179. package/src/linkcommand.js +0 -431
  180. package/src/linkconfig.js +0 -5
  181. package/src/linkediting.js +0 -402
  182. package/src/linkimage.js +0 -37
  183. package/src/linkimageediting.js +0 -264
  184. package/src/linkimageui.js +0 -102
  185. package/src/linkui.js +0 -1072
  186. package/src/ui/linkbuttonview.js +0 -54
  187. package/src/ui/linkformview.js +0 -302
  188. package/src/ui/linkpreviewbuttonview.js +0 -43
  189. package/src/ui/linkpropertiesview.js +0 -170
  190. package/src/ui/linkprovideritemsview.js +0 -207
  191. package/src/unlinkcommand.js +0 -66
  192. package/src/utils/automaticdecorators.js +0 -181
  193. package/src/utils/conflictingdecorators.js +0 -80
  194. package/src/utils/manualdecorator.js +0 -69
  195. package/src/utils.js +0 -157
  196. package/theme/link.css +0 -10
  197. package/theme/linkform.css +0 -24
  198. package/theme/linkimage.css +0 -16
  199. package/theme/linkproperties.css +0 -4
  200. package/theme/linkprovideritems.css +0 -18
  201. package/theme/linktoolbar.css +0 -12
  202. /package/{src → dist}/augmentation.d.ts +0 -0
  203. /package/{src → dist}/utils/conflictingdecorators.d.ts +0 -0
@@ -1,431 +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 link/linkcommand
7
- */
8
- import { Command } from 'ckeditor5/src/core.js';
9
- import { findAttributeRange } from 'ckeditor5/src/typing.js';
10
- import { Collection, diff, first, toMap } from 'ckeditor5/src/utils.js';
11
- import { ModelLivePosition } from 'ckeditor5/src/engine.js';
12
- import { AutomaticLinkDecorators } from './utils/automaticdecorators.js';
13
- import { extractTextFromLinkRange, isLinkableElement } from './utils.js';
14
- import { resolveConflictingDecorators } from './utils/conflictingdecorators.js';
15
- /**
16
- * The link command. It is used by the {@link module:link/link~Link link feature}.
17
- */
18
- export class LinkCommand extends Command {
19
- /**
20
- * A collection of {@link module:link/utils/manualdecorator~LinkManualDecorator manual decorators}
21
- * corresponding to the {@link module:link/linkconfig~LinkConfig#decorators decorator configuration}.
22
- *
23
- * You can consider it a model with states of manual decorators added to the currently selected link.
24
- */
25
- manualDecorators = new Collection();
26
- /**
27
- * An instance of the helper that ties together all {@link module:link/linkconfig~LinkDecoratorAutomaticDefinition}
28
- * that are used by the {@glink features/link link} and the {@glink features/images/images-linking linking images} features.
29
- */
30
- automaticDecorators = new AutomaticLinkDecorators();
31
- /**
32
- * Synchronizes the state of {@link #manualDecorators} with the currently present elements in the model.
33
- */
34
- restoreManualDecoratorStates() {
35
- for (const manualDecorator of this.manualDecorators) {
36
- manualDecorator.value = this._getDecoratorStateFromModel(manualDecorator.id);
37
- }
38
- }
39
- /**
40
- * @inheritDoc
41
- */
42
- refresh() {
43
- const model = this.editor.model;
44
- const selection = model.document.selection;
45
- const selectedElement = selection.getSelectedElement() || first(selection.getSelectedBlocks());
46
- // A check for any integration that allows linking elements (e.g. `LinkImage`).
47
- // Currently the selection reads attributes from text nodes only. See #7429 and #7465.
48
- if (isLinkableElement(selectedElement, model.schema)) {
49
- this.value = selectedElement.getAttribute('linkHref');
50
- this.isEnabled = model.schema.checkAttribute(selectedElement, 'linkHref');
51
- }
52
- else {
53
- this.value = selection.getAttribute('linkHref');
54
- this.isEnabled = model.schema.checkAttributeInSelection(selection, 'linkHref');
55
- }
56
- for (const manualDecorator of this.manualDecorators) {
57
- manualDecorator.value = this._getDecoratorStateFromModel(manualDecorator.id);
58
- }
59
- }
60
- /**
61
- * Executes the command.
62
- *
63
- * When the selection is non-collapsed, the `linkHref` attribute will be applied to nodes inside the selection, but only to
64
- * those nodes where the `linkHref` attribute is allowed (disallowed nodes will be omitted).
65
- *
66
- * When the selection is collapsed and is not inside the text with the `linkHref` attribute, a
67
- * new {@link module:engine/model/text~ModelText text node} with the `linkHref` attribute will be inserted in place of the caret, but
68
- * only if such element is allowed in this place. The `_data` of the inserted text will equal the `href` parameter.
69
- * The selection will be updated to wrap the just inserted text node.
70
- *
71
- * When the selection is collapsed and inside the text with the `linkHref` attribute, the attribute value will be updated.
72
- *
73
- * # Decorators and model attribute management
74
- *
75
- * There is an optional argument to this command that applies or removes model
76
- * {@glink framework/architecture/editing-engine#text-attributes text attributes} brought by
77
- * {@link module:link/utils/manualdecorator~LinkManualDecorator manual link decorators}.
78
- *
79
- * Text attribute names in the model correspond to the entries in the {@link module:link/linkconfig~LinkConfig#decorators
80
- * configuration}.
81
- * For every decorator configured, a model text attribute exists with the "link" prefix. For example, a `'linkMyDecorator'` attribute
82
- * corresponds to `'myDecorator'` in the configuration.
83
- *
84
- * To learn more about link decorators, check out the {@link module:link/linkconfig~LinkConfig#decorators `config.link.decorators`}
85
- * documentation.
86
- *
87
- * Here is how to manage decorator attributes with the link command:
88
- *
89
- * ```ts
90
- * const linkCommand = editor.commands.get( 'link' );
91
- *
92
- * // Adding a new decorator attribute.
93
- * linkCommand.execute( 'http://example.com', {
94
- * linkIsExternal: true
95
- * } );
96
- *
97
- * // Removing a decorator attribute from the selection.
98
- * linkCommand.execute( 'http://example.com', {
99
- * linkIsExternal: false
100
- * } );
101
- *
102
- * // Adding multiple decorator attributes at the same time.
103
- * linkCommand.execute( 'http://example.com', {
104
- * linkIsExternal: true,
105
- * linkIsDownloadable: true,
106
- * } );
107
- *
108
- * // Removing and adding decorator attributes at the same time.
109
- * linkCommand.execute( 'http://example.com', {
110
- * linkIsExternal: false,
111
- * linkFoo: true,
112
- * linkIsDownloadable: false,
113
- * } );
114
- * ```
115
- *
116
- * **Note**: If the decorator attribute name is not specified, its state remains untouched.
117
- *
118
- * **Note**: {@link module:link/unlinkcommand~UnlinkCommand#execute `UnlinkCommand#execute()`} removes all
119
- * decorator attributes.
120
- *
121
- * An optional parameter called `displayedText` is to add or update text of the link that represents the `href`. For example:
122
- *
123
- * ```ts
124
- * const linkCommand = editor.commands.get( 'link' );
125
- *
126
- * // Adding a new link with `displayedText` attribute.
127
- * linkCommand.execute( 'http://example.com', {}, 'Example' );
128
- * ```
129
- *
130
- * The above code will create an anchor like this:
131
- *
132
- * ```html
133
- * <a href="http://example.com">Example</a>
134
- * ```
135
- *
136
- * @fires execute
137
- * @param href Link destination.
138
- * @param manualDecoratorIds The information about manual decorator attributes to be applied or removed upon execution.
139
- * @param displayedText Text of the link.
140
- */
141
- execute(href, manualDecoratorIds = {}, displayedText) {
142
- const model = this.editor.model;
143
- const selection = model.document.selection;
144
- // Resolve conflicting decorators and get the final decorator states.
145
- const resolvedDecoratorsIds = resolveConflictingDecorators({
146
- allDecorators: Array.from(this.manualDecorators),
147
- decoratorStates: manualDecoratorIds
148
- });
149
- // Stores information about manual decorators to turn them on/off when command is applied.
150
- const truthyManualDecorators = [];
151
- const falsyManualDecorators = [];
152
- for (const name in resolvedDecoratorsIds) {
153
- if (resolvedDecoratorsIds[name]) {
154
- truthyManualDecorators.push(name);
155
- }
156
- else {
157
- falsyManualDecorators.push(name);
158
- }
159
- }
160
- model.change(writer => {
161
- const updateLinkAttributes = (itemOrRange) => {
162
- writer.setAttribute('linkHref', href, itemOrRange);
163
- truthyManualDecorators.forEach(item => writer.setAttribute(item, true, itemOrRange));
164
- falsyManualDecorators.forEach(item => writer.removeAttribute(item, itemOrRange));
165
- };
166
- const updateLinkTextIfNeeded = (range, linkHref) => {
167
- const linkText = extractTextFromLinkRange(range);
168
- if (!linkText) {
169
- return range;
170
- }
171
- // Make a copy not to override the command param value.
172
- let newText = displayedText;
173
- if (!newText) {
174
- // Replace the link text with the new href if previously href was equal to text.
175
- // For example: `<a href="http://ckeditor.com/">http://ckeditor.com/</a>`.
176
- newText = linkHref && linkHref == linkText ? href : linkText;
177
- }
178
- // Only if needed.
179
- if (newText != linkText) {
180
- const fragment = writer.createDocumentFragment();
181
- for (const item of range.getItems()) {
182
- // `extractTextFromLinkRange()` called above guarantees that we operate only on text proxies here.
183
- const text = item;
184
- writer.append(writer.createText(text.data, text.getAttributes()), fragment);
185
- }
186
- const fragRange = writer.createRangeIn(fragment);
187
- const changes = findChanges(linkText, newText);
188
- let insertsLength = 0;
189
- for (const { offset, actual, expected } of changes) {
190
- const updatedOffset = offset + insertsLength;
191
- const subRange = writer.createRange(fragRange.start.getShiftedBy(updatedOffset), fragRange.start.getShiftedBy(updatedOffset + actual.length));
192
- // Collect formatting attributes from replaced text.
193
- const textNode = getLinkPartTextNode(subRange, fragRange);
194
- const attributes = textNode.getAttributes();
195
- const formattingAttributes = Array
196
- .from(attributes)
197
- .filter(([key]) => model.schema.getAttributeProperties(key).isFormatting);
198
- // Create a new text node.
199
- const newTextNode = writer.createText(expected, formattingAttributes);
200
- // Set link attributes before inserting to document to avoid Differ attributes edge case.
201
- updateLinkAttributes(newTextNode);
202
- // Replace text with formatting.
203
- writer.remove(subRange);
204
- writer.insert(newTextNode, subRange.start);
205
- // Sum of all previous inserts.
206
- insertsLength += expected.length;
207
- }
208
- model.insertContent(fragment, range);
209
- return writer.createRange(range.start, range.start.getShiftedBy(newText.length));
210
- }
211
- };
212
- const collapseSelectionAtLinkEnd = (linkRange) => {
213
- const { plugins } = this.editor;
214
- writer.setSelection(linkRange.end);
215
- if (plugins.has('TwoStepCaretMovement')) {
216
- // After replacing the text of the link, we need to move the caret to the end of the link,
217
- // override it's gravity to forward to prevent keeping e.g. bold attribute on the caret
218
- // which was previously inside the link.
219
- //
220
- // If the plugin is not available, the caret will be placed at the end of the link and the
221
- // bold attribute will be kept even if command moved caret outside the link.
222
- plugins.get('TwoStepCaretMovement')._handleForwardMovement();
223
- }
224
- else {
225
- // Remove the `linkHref` attribute and all link decorators from the selection.
226
- // It stops adding a new content into the link element.
227
- for (const key of ['linkHref', ...truthyManualDecorators, ...falsyManualDecorators]) {
228
- writer.removeSelectionAttribute(key);
229
- }
230
- }
231
- };
232
- // If selection is collapsed then update selected link or insert new one at the place of caret.
233
- if (selection.isCollapsed) {
234
- const position = selection.getFirstPosition();
235
- // When selection is inside text with `linkHref` attribute.
236
- if (selection.hasAttribute('linkHref')) {
237
- const linkHref = selection.getAttribute('linkHref');
238
- const linkRange = findAttributeRange(position, 'linkHref', linkHref, model);
239
- const newLinkRange = updateLinkTextIfNeeded(linkRange, linkHref);
240
- updateLinkAttributes(newLinkRange || linkRange);
241
- // Put the selection at the end of the updated link only when text was changed.
242
- // When text was not altered we keep the original selection.
243
- if (newLinkRange) {
244
- collapseSelectionAtLinkEnd(newLinkRange);
245
- }
246
- }
247
- // If not then insert text node with `linkHref` attribute in place of caret.
248
- // However, since selection is collapsed, attribute value will be used as data for text node.
249
- // So, if `href` is empty, do not create text node.
250
- else if (href !== '') {
251
- const attributes = toMap(selection.getAttributes());
252
- attributes.set('linkHref', href);
253
- truthyManualDecorators.forEach(item => {
254
- attributes.set(item, true);
255
- });
256
- const newLinkRange = model.insertContent(writer.createText(displayedText || href, attributes), position);
257
- // Put the selection at the end of the inserted link.
258
- // Using end of range returned from insertContent in case nodes with the same attributes got merged.
259
- collapseSelectionAtLinkEnd(newLinkRange);
260
- }
261
- }
262
- else {
263
- // Non-collapsed selection.
264
- // If selection has non-collapsed ranges, we change attribute on nodes inside those ranges
265
- // omitting nodes where the `linkHref` attribute is disallowed.
266
- const selectionRanges = Array.from(selection.getRanges());
267
- const ranges = model.schema.getValidRanges(selectionRanges, 'linkHref');
268
- // But for the first, check whether the `linkHref` attribute is allowed on selected blocks (e.g. the "image" element).
269
- const allowedRanges = [];
270
- for (const element of selection.getSelectedBlocks()) {
271
- if (model.schema.checkAttribute(element, 'linkHref')) {
272
- allowedRanges.push(writer.createRangeOn(element));
273
- }
274
- }
275
- // Ranges that accept the `linkHref` attribute. Since we will iterate over `allowedRanges`, let's clone it.
276
- const rangesToUpdate = allowedRanges.slice();
277
- // For all selection ranges we want to check whether given range is inside an element that accepts the `linkHref` attribute.
278
- // If so, we don't want to propagate applying the attribute to its children.
279
- for (const range of ranges) {
280
- if (this._isRangeToUpdate(range, allowedRanges)) {
281
- rangesToUpdate.push(range);
282
- }
283
- }
284
- // Store the selection ranges in a pseudo live range array (stickiness to the outside of the range).
285
- const stickyPseudoRanges = selectionRanges.map(range => ({
286
- start: ModelLivePosition.fromPosition(range.start, 'toPrevious'),
287
- end: ModelLivePosition.fromPosition(range.end, 'toNext')
288
- }));
289
- // Update or set links (including text update if needed).
290
- for (let range of rangesToUpdate) {
291
- const linkHref = (range.start.textNode || range.start.nodeAfter).getAttribute('linkHref');
292
- range = updateLinkTextIfNeeded(range, linkHref) || range;
293
- updateLinkAttributes(range);
294
- }
295
- // The original selection got trimmed by replacing content so we need to restore it.
296
- writer.setSelection(stickyPseudoRanges.map(pseudoRange => {
297
- const start = pseudoRange.start.toPosition();
298
- const end = pseudoRange.end.toPosition();
299
- pseudoRange.start.detach();
300
- pseudoRange.end.detach();
301
- return model.createRange(start, end);
302
- }));
303
- }
304
- });
305
- this.restoreManualDecoratorStates();
306
- }
307
- /**
308
- * Provides information whether a decorator with a given name is present in the currently processed selection.
309
- *
310
- * @param decoratorName The name of the manual decorator used in the model
311
- * @returns The information whether a given decorator is currently present in the selection.
312
- */
313
- _getDecoratorStateFromModel(decoratorName) {
314
- const model = this.editor.model;
315
- const selection = model.document.selection;
316
- const selectedElement = selection.getSelectedElement();
317
- // A check for the `LinkImage` plugin. If the selection contains an element, get values from the element.
318
- // Currently the selection reads attributes from text nodes only. See #7429 and #7465.
319
- if (isLinkableElement(selectedElement, model.schema)) {
320
- return selectedElement.getAttribute(decoratorName);
321
- }
322
- return selection.getAttribute(decoratorName);
323
- }
324
- /**
325
- * Checks whether specified `range` is inside an element that accepts the `linkHref` attribute.
326
- *
327
- * @param range A range to check.
328
- * @param allowedRanges An array of ranges created on elements where the attribute is accepted.
329
- */
330
- _isRangeToUpdate(range, allowedRanges) {
331
- for (const allowedRange of allowedRanges) {
332
- // A range is inside an element that will have the `linkHref` attribute. Do not modify its nodes.
333
- if (allowedRange.containsRange(range)) {
334
- return false;
335
- }
336
- }
337
- return true;
338
- }
339
- }
340
- /**
341
- * Compares two strings and returns an array of changes needed to transform one into another.
342
- * Uses the diff utility to find the differences and groups them into chunks containing information
343
- * about the offset and actual/expected content.
344
- *
345
- * @param oldText The original text to compare.
346
- * @param newText The new text to compare against.
347
- * @returns Array of change objects containing offset and actual/expected content.
348
- *
349
- * @example
350
- * findChanges( 'hello world', 'hi there' );
351
- *
352
- * Returns:
353
- * [
354
- * {
355
- * "offset": 1,
356
- * "actual": "ello",
357
- * "expected": "i"
358
- * },
359
- * {
360
- * "offset": 2,
361
- * "actual": "wo",
362
- * "expected": "the"
363
- * },
364
- * {
365
- * "offset": 3,
366
- * "actual": "ld",
367
- * "expected": "e"
368
- * }
369
- * ]
370
- */
371
- function findChanges(oldText, newText) {
372
- // Get array of operations (insert/delete/equal) needed to transform oldText into newText.
373
- // Example: diff('abc', 'abxc') returns ['equal', 'equal', 'insert', 'equal']
374
- const changes = diff(oldText, newText);
375
- // Track position in both strings based on operation type.
376
- const counter = { equal: 0, insert: 0, delete: 0 };
377
- const result = [];
378
- // Accumulate consecutive changes into slices before creating change objects.
379
- let actualSlice = '';
380
- let expectedSlice = '';
381
- // Adding null as sentinel value to handle final accumulated changes.
382
- for (const action of [...changes, null]) {
383
- if (action == 'insert') {
384
- // Example: for 'abc' -> 'abxc', at insert position, adds 'x' to expectedSlice.
385
- expectedSlice += newText[counter.equal + counter.insert];
386
- }
387
- else if (action == 'delete') {
388
- // Example: for 'abc' -> 'ac', at delete position, adds 'b' to actualSlice.
389
- actualSlice += oldText[counter.equal + counter.delete];
390
- }
391
- else if (actualSlice.length || expectedSlice.length) {
392
- // On 'equal' or end: bundle accumulated changes into a single change object.
393
- // Example: { offset: 2, actual: "", expected: "x" }
394
- result.push({
395
- offset: counter.equal,
396
- actual: actualSlice,
397
- expected: expectedSlice
398
- });
399
- actualSlice = '';
400
- expectedSlice = '';
401
- }
402
- // Increment appropriate counter for the current operation.
403
- if (action) {
404
- counter[action]++;
405
- }
406
- }
407
- return result;
408
- }
409
- /**
410
- * Returns text node withing the link range that should be updated.
411
- *
412
- * @param range Partial link range.
413
- * @param linkRange Range of the entire link.
414
- * @returns Text node.
415
- */
416
- function getLinkPartTextNode(range, linkRange) {
417
- if (!range.isCollapsed) {
418
- return first(range.getItems());
419
- }
420
- const position = range.start;
421
- if (position.textNode) {
422
- return position.textNode;
423
- }
424
- // If the range is at the start of a link range then prefer node inside a link range.
425
- if (!position.nodeBefore || position.isEqual(linkRange.start)) {
426
- return position.nodeAfter;
427
- }
428
- else {
429
- return position.nodeBefore;
430
- }
431
- }
package/src/linkconfig.js DELETED
@@ -1,5 +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
- export {};