@ckeditor/ckeditor5-clipboard 0.0.0-nightly-20240324.0 → 0.0.0-nightly-20240326.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.
- package/package.json +6 -6
- package/src/clipboardmarkersutils.d.ts +39 -25
- package/src/clipboardmarkersutils.js +147 -73
- package/src/clipboardpipeline.js +8 -9
- package/src/index.d.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ckeditor/ckeditor5-clipboard",
|
|
3
|
-
"version": "0.0.0-nightly-
|
|
3
|
+
"version": "0.0.0-nightly-20240326.0",
|
|
4
4
|
"description": "Clipboard integration feature for CKEditor 5.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ckeditor",
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
"type": "module",
|
|
14
14
|
"main": "src/index.js",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@ckeditor/ckeditor5-core": "0.0.0-nightly-
|
|
17
|
-
"@ckeditor/ckeditor5-engine": "0.0.0-nightly-
|
|
18
|
-
"@ckeditor/ckeditor5-ui": "0.0.0-nightly-
|
|
19
|
-
"@ckeditor/ckeditor5-utils": "0.0.0-nightly-
|
|
20
|
-
"@ckeditor/ckeditor5-widget": "0.0.0-nightly-
|
|
16
|
+
"@ckeditor/ckeditor5-core": "0.0.0-nightly-20240326.0",
|
|
17
|
+
"@ckeditor/ckeditor5-engine": "0.0.0-nightly-20240326.0",
|
|
18
|
+
"@ckeditor/ckeditor5-ui": "0.0.0-nightly-20240326.0",
|
|
19
|
+
"@ckeditor/ckeditor5-utils": "0.0.0-nightly-20240326.0",
|
|
20
|
+
"@ckeditor/ckeditor5-widget": "0.0.0-nightly-20240326.0",
|
|
21
21
|
"lodash-es": "4.17.21"
|
|
22
22
|
},
|
|
23
23
|
"author": "CKSource (http://cksource.com/)",
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
|
-
import { Plugin } from '@ckeditor/ckeditor5-core';
|
|
6
|
-
import { Range, type
|
|
5
|
+
import { Plugin, type NonEmptyArray } from '@ckeditor/ckeditor5-core';
|
|
6
|
+
import { Range, type DocumentFragment, type Element, type DocumentSelection, type Selection, type Writer } from '@ckeditor/ckeditor5-engine';
|
|
7
7
|
/**
|
|
8
8
|
* Part of the clipboard logic. Responsible for collecting markers from selected fragments
|
|
9
9
|
* and restoring them with proper positions in pasted elements.
|
|
@@ -25,17 +25,10 @@ export default class ClipboardMarkersUtils extends Plugin {
|
|
|
25
25
|
* Registers marker name as copyable in clipboard pipeline.
|
|
26
26
|
*
|
|
27
27
|
* @param markerName Name of marker that can be copied.
|
|
28
|
-
* @param
|
|
28
|
+
* @param config Configuration that describes what can be performed on specified marker.
|
|
29
29
|
* @internal
|
|
30
30
|
*/
|
|
31
|
-
_registerMarkerToCopy(markerName: string,
|
|
32
|
-
/**
|
|
33
|
-
* Maps preset into array of clipboard operations to be allowed on marker.
|
|
34
|
-
*
|
|
35
|
-
* @param preset Restrictions preset to be mapped to actions
|
|
36
|
-
* @internal
|
|
37
|
-
*/
|
|
38
|
-
private _mapRestrictionPresetToActions;
|
|
31
|
+
_registerMarkerToCopy(markerName: string, config: ClipboardMarkerConfiguration): void;
|
|
39
32
|
/**
|
|
40
33
|
* Performs copy markers on provided selection and paste it to fragment returned from `getCopiedFragment`.
|
|
41
34
|
*
|
|
@@ -67,6 +60,7 @@ export default class ClipboardMarkersUtils extends Plugin {
|
|
|
67
60
|
*
|
|
68
61
|
* * `markers` are inserted into the same element that must be later transformed inside `getPastedDocumentElement`.
|
|
69
62
|
* * Fake marker elements inside `getPastedDocumentElement` can be cloned, but their ranges cannot overlap.
|
|
63
|
+
* * If `duplicateOnPaste` is `true` in marker config then associated marker ID is regenerated before pasting.
|
|
70
64
|
*
|
|
71
65
|
* @param action Type of clipboard action.
|
|
72
66
|
* @param markers Object that maps marker name to corresponding range.
|
|
@@ -74,6 +68,15 @@ export default class ClipboardMarkersUtils extends Plugin {
|
|
|
74
68
|
* @internal
|
|
75
69
|
*/
|
|
76
70
|
_pasteMarkersIntoTransformedElement(markers: Record<string, Range> | Map<string, Range>, getPastedDocumentElement: (writer: Writer) => Element): Element;
|
|
71
|
+
/**
|
|
72
|
+
* Pastes document fragment with markers to document.
|
|
73
|
+
* If `duplicateOnPaste` is `true` in marker config then associated markers IDs
|
|
74
|
+
* are regenerated before pasting to avoid markers duplications in content.
|
|
75
|
+
*
|
|
76
|
+
* @param fragment Document fragment that should contain already processed by pipeline markers.
|
|
77
|
+
* @internal
|
|
78
|
+
*/
|
|
79
|
+
_pasteFragmentWithMarkers(fragment: DocumentFragment): Range;
|
|
77
80
|
/**
|
|
78
81
|
* In some situations we have to perform copy on selected fragment with certain markers. This function allows to temporarily bypass
|
|
79
82
|
* restrictions on markers that we want to copy.
|
|
@@ -83,9 +86,10 @@ export default class ClipboardMarkersUtils extends Plugin {
|
|
|
83
86
|
*
|
|
84
87
|
* @param markerName Which markers should be copied.
|
|
85
88
|
* @param executor Callback executed.
|
|
89
|
+
* @param config Optional configuration flags used to copy (such like partial copy flag).
|
|
86
90
|
* @internal
|
|
87
91
|
*/
|
|
88
|
-
_forceMarkersCopy(markerName: string, executor: VoidFunction): void;
|
|
92
|
+
_forceMarkersCopy(markerName: string, executor: VoidFunction, config?: ClipboardMarkerConfiguration): void;
|
|
89
93
|
/**
|
|
90
94
|
* Checks if marker can be copied.
|
|
91
95
|
*
|
|
@@ -93,14 +97,20 @@ export default class ClipboardMarkersUtils extends Plugin {
|
|
|
93
97
|
* @param action Type of clipboard action. If null then checks only if marker is registered as copyable.
|
|
94
98
|
* @internal
|
|
95
99
|
*/
|
|
96
|
-
|
|
100
|
+
_isMarkerCopyable(markerName: string, action: ClipboardMarkerRestrictedAction | null): boolean;
|
|
97
101
|
/**
|
|
98
|
-
*
|
|
102
|
+
* Checks if marker has any clipboard copy behavior configuration.
|
|
99
103
|
*
|
|
100
|
-
* @param
|
|
104
|
+
* @param markerName Name of checked marker.
|
|
105
|
+
*/
|
|
106
|
+
_hasMarkerConfiguration(markerName: string): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Returns marker's configuration flags passed during registration.
|
|
109
|
+
*
|
|
110
|
+
* @param markerName Name of marker that should be returned.
|
|
101
111
|
* @internal
|
|
102
112
|
*/
|
|
103
|
-
|
|
113
|
+
_getMarkerClipboardConfig(markerName: string): ClipboardMarkerConfiguration | null;
|
|
104
114
|
/**
|
|
105
115
|
* First step of copying markers. It looks for markers intersecting with given selection and inserts `$marker` elements
|
|
106
116
|
* at positions where document markers start or end. This way `$marker` elements can be easily copied together with
|
|
@@ -114,18 +124,23 @@ export default class ClipboardMarkersUtils extends Plugin {
|
|
|
114
124
|
/**
|
|
115
125
|
* Returns array of markers that can be copied in specified selection.
|
|
116
126
|
*
|
|
127
|
+
* If marker cannot be copied partially (according to `copyPartiallySelected` configuration flag) and
|
|
128
|
+
* is not present entirely in any selection range then it will be skipped.
|
|
129
|
+
*
|
|
117
130
|
* @param writer An instance of the model writer.
|
|
118
131
|
* @param selection Selection which will be checked.
|
|
119
132
|
* @param action Type of clipboard action. If null then checks only if marker is registered as copyable.
|
|
120
133
|
*/
|
|
121
134
|
private _getCopyableMarkersFromSelection;
|
|
122
135
|
/**
|
|
123
|
-
* Picks all markers from markers map that can be
|
|
136
|
+
* Picks all markers from markers map that can be pasted.
|
|
137
|
+
* If `duplicateOnPaste` is `true`, it regenerates their IDs to ensure uniqueness.
|
|
138
|
+
* If marker is not registered, it will be kept in the array anyway.
|
|
124
139
|
*
|
|
125
140
|
* @param markers Object that maps marker name to corresponding range.
|
|
126
141
|
* @param action Type of clipboard action. If null then checks only if marker is registered as copyable.
|
|
127
142
|
*/
|
|
128
|
-
private
|
|
143
|
+
private _getPasteMarkersFromRangeMap;
|
|
129
144
|
/**
|
|
130
145
|
* Inserts specified array of fake markers elements to document and assigns them `type` and `name` attributes.
|
|
131
146
|
* Fake markers elements are used to calculate position of markers on pasted fragment that were transformed during
|
|
@@ -174,13 +189,12 @@ export default class ClipboardMarkersUtils extends Plugin {
|
|
|
174
189
|
*/
|
|
175
190
|
export type ClipboardMarkerRestrictedAction = 'copy' | 'cut' | 'dragstart';
|
|
176
191
|
/**
|
|
177
|
-
* Specifies
|
|
178
|
-
* it will disallow copy, cut or paste of marker in clipboard.
|
|
179
|
-
*
|
|
180
|
-
* * `'default'` - the markers will be preserved on cut-paste and drag and drop actions only.
|
|
181
|
-
* * `'always'` - the markers will be preserved on all clipboard actions (cut, copy, drag and drop).
|
|
182
|
-
* * `'never'` - the markers will be ignored by clipboard.
|
|
192
|
+
* Specifies behavior of markers during clipboard actions.
|
|
183
193
|
*
|
|
184
194
|
* @internal
|
|
185
195
|
*/
|
|
186
|
-
export type
|
|
196
|
+
export type ClipboardMarkerConfiguration = {
|
|
197
|
+
allowedActions: NonEmptyArray<ClipboardMarkerRestrictedAction> | 'all';
|
|
198
|
+
copyPartiallySelected?: boolean;
|
|
199
|
+
duplicateOnPaste?: boolean;
|
|
200
|
+
};
|
|
@@ -35,36 +35,11 @@ export default class ClipboardMarkersUtils extends Plugin {
|
|
|
35
35
|
* Registers marker name as copyable in clipboard pipeline.
|
|
36
36
|
*
|
|
37
37
|
* @param markerName Name of marker that can be copied.
|
|
38
|
-
* @param
|
|
38
|
+
* @param config Configuration that describes what can be performed on specified marker.
|
|
39
39
|
* @internal
|
|
40
40
|
*/
|
|
41
|
-
_registerMarkerToCopy(markerName,
|
|
42
|
-
|
|
43
|
-
if (allowedActions.length) {
|
|
44
|
-
this._markersToCopy.set(markerName, allowedActions);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Maps preset into array of clipboard operations to be allowed on marker.
|
|
49
|
-
*
|
|
50
|
-
* @param preset Restrictions preset to be mapped to actions
|
|
51
|
-
* @internal
|
|
52
|
-
*/
|
|
53
|
-
_mapRestrictionPresetToActions(preset) {
|
|
54
|
-
switch (preset) {
|
|
55
|
-
case 'always':
|
|
56
|
-
return ['copy', 'cut', 'dragstart'];
|
|
57
|
-
case 'default':
|
|
58
|
-
return ['cut', 'dragstart'];
|
|
59
|
-
case 'never':
|
|
60
|
-
return [];
|
|
61
|
-
default: {
|
|
62
|
-
// Skip unrecognized type.
|
|
63
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
64
|
-
const unreachable = preset;
|
|
65
|
-
return [];
|
|
66
|
-
}
|
|
67
|
-
}
|
|
41
|
+
_registerMarkerToCopy(markerName, config) {
|
|
42
|
+
this._markersToCopy.set(markerName, config);
|
|
68
43
|
}
|
|
69
44
|
/**
|
|
70
45
|
* Performs copy markers on provided selection and paste it to fragment returned from `getCopiedFragment`.
|
|
@@ -133,6 +108,7 @@ export default class ClipboardMarkersUtils extends Plugin {
|
|
|
133
108
|
*
|
|
134
109
|
* * `markers` are inserted into the same element that must be later transformed inside `getPastedDocumentElement`.
|
|
135
110
|
* * Fake marker elements inside `getPastedDocumentElement` can be cloned, but their ranges cannot overlap.
|
|
111
|
+
* * If `duplicateOnPaste` is `true` in marker config then associated marker ID is regenerated before pasting.
|
|
136
112
|
*
|
|
137
113
|
* @param action Type of clipboard action.
|
|
138
114
|
* @param markers Object that maps marker name to corresponding range.
|
|
@@ -140,26 +116,47 @@ export default class ClipboardMarkersUtils extends Plugin {
|
|
|
140
116
|
* @internal
|
|
141
117
|
*/
|
|
142
118
|
_pasteMarkersIntoTransformedElement(markers, getPastedDocumentElement) {
|
|
143
|
-
const
|
|
119
|
+
const pasteMarkers = this._getPasteMarkersFromRangeMap(markers);
|
|
144
120
|
return this.editor.model.change(writer => {
|
|
145
|
-
|
|
121
|
+
// Inserts fake markers into source fragment / element that is later transformed inside `getPastedDocumentElement`.
|
|
122
|
+
const sourceFragmentFakeMarkers = this._insertFakeMarkersElements(writer, pasteMarkers);
|
|
123
|
+
// Modifies document fragment (for example, cloning table cells) and then inserts it into the document.
|
|
146
124
|
const transformedElement = getPastedDocumentElement(writer);
|
|
125
|
+
// Removes markers in pasted and transformed fragment in root document.
|
|
147
126
|
const removedFakeMarkers = this._removeFakeMarkersInsideElement(writer, transformedElement);
|
|
148
|
-
//
|
|
127
|
+
// Cleans up fake markers inserted into source fragment (that one before transformation which is not pasted).
|
|
149
128
|
for (const element of Object.values(sourceFragmentFakeMarkers).flat()) {
|
|
150
129
|
writer.remove(element);
|
|
151
130
|
}
|
|
131
|
+
// Inserts to root document fake markers.
|
|
152
132
|
for (const [markerName, range] of Object.entries(removedFakeMarkers)) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
133
|
+
if (!writer.model.markers.has(markerName)) {
|
|
134
|
+
writer.addMarker(markerName, {
|
|
135
|
+
usingOperation: true,
|
|
136
|
+
affectsData: true,
|
|
137
|
+
range
|
|
138
|
+
});
|
|
139
|
+
}
|
|
159
140
|
}
|
|
160
141
|
return transformedElement;
|
|
161
142
|
});
|
|
162
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Pastes document fragment with markers to document.
|
|
146
|
+
* If `duplicateOnPaste` is `true` in marker config then associated markers IDs
|
|
147
|
+
* are regenerated before pasting to avoid markers duplications in content.
|
|
148
|
+
*
|
|
149
|
+
* @param fragment Document fragment that should contain already processed by pipeline markers.
|
|
150
|
+
* @internal
|
|
151
|
+
*/
|
|
152
|
+
_pasteFragmentWithMarkers(fragment) {
|
|
153
|
+
const pasteMarkers = this._getPasteMarkersFromRangeMap(fragment.markers);
|
|
154
|
+
fragment.markers.clear();
|
|
155
|
+
for (const copyableMarker of pasteMarkers) {
|
|
156
|
+
fragment.markers.set(copyableMarker.name, copyableMarker.range);
|
|
157
|
+
}
|
|
158
|
+
return this.editor.model.insertContent(fragment);
|
|
159
|
+
}
|
|
163
160
|
/**
|
|
164
161
|
* In some situations we have to perform copy on selected fragment with certain markers. This function allows to temporarily bypass
|
|
165
162
|
* restrictions on markers that we want to copy.
|
|
@@ -169,11 +166,16 @@ export default class ClipboardMarkersUtils extends Plugin {
|
|
|
169
166
|
*
|
|
170
167
|
* @param markerName Which markers should be copied.
|
|
171
168
|
* @param executor Callback executed.
|
|
169
|
+
* @param config Optional configuration flags used to copy (such like partial copy flag).
|
|
172
170
|
* @internal
|
|
173
171
|
*/
|
|
174
|
-
_forceMarkersCopy(markerName, executor
|
|
172
|
+
_forceMarkersCopy(markerName, executor, config = {
|
|
173
|
+
allowedActions: 'all',
|
|
174
|
+
copyPartiallySelected: true,
|
|
175
|
+
duplicateOnPaste: true
|
|
176
|
+
}) {
|
|
175
177
|
const before = this._markersToCopy.get(markerName);
|
|
176
|
-
this._markersToCopy.set(markerName,
|
|
178
|
+
this._markersToCopy.set(markerName, config);
|
|
177
179
|
executor();
|
|
178
180
|
if (before) {
|
|
179
181
|
this._markersToCopy.set(markerName, before);
|
|
@@ -189,27 +191,35 @@ export default class ClipboardMarkersUtils extends Plugin {
|
|
|
189
191
|
* @param action Type of clipboard action. If null then checks only if marker is registered as copyable.
|
|
190
192
|
* @internal
|
|
191
193
|
*/
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
+
_isMarkerCopyable(markerName, action) {
|
|
195
|
+
const config = this._getMarkerClipboardConfig(markerName);
|
|
196
|
+
if (!config) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
// If there is no action provided then only presence of marker is checked.
|
|
194
200
|
if (!action) {
|
|
195
|
-
return
|
|
201
|
+
return true;
|
|
196
202
|
}
|
|
197
|
-
const
|
|
198
|
-
return
|
|
203
|
+
const { allowedActions } = config;
|
|
204
|
+
return allowedActions === 'all' || allowedActions.includes(action);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Checks if marker has any clipboard copy behavior configuration.
|
|
208
|
+
*
|
|
209
|
+
* @param markerName Name of checked marker.
|
|
210
|
+
*/
|
|
211
|
+
_hasMarkerConfiguration(markerName) {
|
|
212
|
+
return !!this._getMarkerClipboardConfig(markerName);
|
|
199
213
|
}
|
|
200
214
|
/**
|
|
201
|
-
*
|
|
215
|
+
* Returns marker's configuration flags passed during registration.
|
|
202
216
|
*
|
|
203
|
-
* @param
|
|
217
|
+
* @param markerName Name of marker that should be returned.
|
|
204
218
|
* @internal
|
|
205
219
|
*/
|
|
206
|
-
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
for (const [name, range] of markers) {
|
|
210
|
-
const newName = this._canPerformMarkerClipboardAction(name, null) ? this._getUniqueMarkerName(name) : name;
|
|
211
|
-
fragment.markers.set(newName, range);
|
|
212
|
-
}
|
|
220
|
+
_getMarkerClipboardConfig(markerName) {
|
|
221
|
+
const [markerNamePrefix] = markerName.split(':');
|
|
222
|
+
return this._markersToCopy.get(markerNamePrefix) || null;
|
|
213
223
|
}
|
|
214
224
|
/**
|
|
215
225
|
* First step of copying markers. It looks for markers intersecting with given selection and inserts `$marker` elements
|
|
@@ -227,34 +237,89 @@ export default class ClipboardMarkersUtils extends Plugin {
|
|
|
227
237
|
/**
|
|
228
238
|
* Returns array of markers that can be copied in specified selection.
|
|
229
239
|
*
|
|
240
|
+
* If marker cannot be copied partially (according to `copyPartiallySelected` configuration flag) and
|
|
241
|
+
* is not present entirely in any selection range then it will be skipped.
|
|
242
|
+
*
|
|
230
243
|
* @param writer An instance of the model writer.
|
|
231
244
|
* @param selection Selection which will be checked.
|
|
232
245
|
* @param action Type of clipboard action. If null then checks only if marker is registered as copyable.
|
|
233
246
|
*/
|
|
234
247
|
_getCopyableMarkersFromSelection(writer, selection, action) {
|
|
248
|
+
const selectionRanges = Array.from(selection.getRanges());
|
|
249
|
+
// Picks all markers in provided ranges. Ensures that there are no duplications if
|
|
250
|
+
// there are multiple ranges that intersects with the same marker.
|
|
251
|
+
const markersInRanges = new Set(selectionRanges.flatMap(selectionRange => Array.from(writer.model.markers.getMarkersIntersectingRange(selectionRange))));
|
|
252
|
+
const isSelectionMarkerCopyable = (marker) => {
|
|
253
|
+
// Check if marker exists in configuration and provided action can be performed on it.
|
|
254
|
+
const isCopyable = this._isMarkerCopyable(marker.name, action);
|
|
255
|
+
if (!isCopyable) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
// Checks if configuration disallows to copy marker only if part of its content is selected.
|
|
259
|
+
//
|
|
260
|
+
// Example:
|
|
261
|
+
// <marker-a> Hello [ World ] </marker-a>
|
|
262
|
+
// ^ selection
|
|
263
|
+
//
|
|
264
|
+
// In this scenario `marker-a` won't be copied because selection doesn't overlap its content entirely.
|
|
265
|
+
const { copyPartiallySelected } = this._getMarkerClipboardConfig(marker.name);
|
|
266
|
+
if (!copyPartiallySelected) {
|
|
267
|
+
const markerRange = marker.getRange();
|
|
268
|
+
return selectionRanges.some(selectionRange => selectionRange.containsRange(markerRange, true));
|
|
269
|
+
}
|
|
270
|
+
return true;
|
|
271
|
+
};
|
|
235
272
|
return Array
|
|
236
|
-
.from(
|
|
237
|
-
.
|
|
238
|
-
.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
273
|
+
.from(markersInRanges)
|
|
274
|
+
.filter(isSelectionMarkerCopyable)
|
|
275
|
+
.map((copyableMarker) => {
|
|
276
|
+
// During `dragstart` event original marker is still present in tree.
|
|
277
|
+
// It is removed after the clipboard drop event, so none of the copied markers are inserted at the end.
|
|
278
|
+
// It happens because there already markers with specified `marker.name` when clipboard is trying to insert data
|
|
279
|
+
// and it aborts inserting.
|
|
280
|
+
const name = action === 'dragstart' ? this._getUniqueMarkerName(copyableMarker.name) : copyableMarker.name;
|
|
281
|
+
return {
|
|
282
|
+
name,
|
|
283
|
+
range: copyableMarker.getRange()
|
|
284
|
+
};
|
|
285
|
+
});
|
|
243
286
|
}
|
|
244
287
|
/**
|
|
245
|
-
* Picks all markers from markers map that can be
|
|
288
|
+
* Picks all markers from markers map that can be pasted.
|
|
289
|
+
* If `duplicateOnPaste` is `true`, it regenerates their IDs to ensure uniqueness.
|
|
290
|
+
* If marker is not registered, it will be kept in the array anyway.
|
|
246
291
|
*
|
|
247
292
|
* @param markers Object that maps marker name to corresponding range.
|
|
248
293
|
* @param action Type of clipboard action. If null then checks only if marker is registered as copyable.
|
|
249
294
|
*/
|
|
250
|
-
|
|
295
|
+
_getPasteMarkersFromRangeMap(markers, action = null) {
|
|
296
|
+
const { model } = this.editor;
|
|
251
297
|
const entries = markers instanceof Map ? Array.from(markers.entries()) : Object.entries(markers);
|
|
252
|
-
return entries
|
|
253
|
-
.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
298
|
+
return entries.flatMap(([markerName, range]) => {
|
|
299
|
+
if (!this._hasMarkerConfiguration(markerName)) {
|
|
300
|
+
return [
|
|
301
|
+
{
|
|
302
|
+
name: markerName,
|
|
303
|
+
range
|
|
304
|
+
}
|
|
305
|
+
];
|
|
306
|
+
}
|
|
307
|
+
if (this._isMarkerCopyable(markerName, action)) {
|
|
308
|
+
const copyMarkerConfig = this._getMarkerClipboardConfig(markerName);
|
|
309
|
+
const isInGraveyard = model.markers.has(markerName) &&
|
|
310
|
+
model.markers.get(markerName).getRange().root.rootName === '$graveyard';
|
|
311
|
+
if (copyMarkerConfig.duplicateOnPaste || isInGraveyard) {
|
|
312
|
+
markerName = this._getUniqueMarkerName(markerName);
|
|
313
|
+
}
|
|
314
|
+
return [
|
|
315
|
+
{
|
|
316
|
+
name: markerName,
|
|
317
|
+
range
|
|
318
|
+
}
|
|
319
|
+
];
|
|
320
|
+
}
|
|
321
|
+
return [];
|
|
322
|
+
});
|
|
258
323
|
}
|
|
259
324
|
/**
|
|
260
325
|
* Inserts specified array of fake markers elements to document and assigns them `type` and `name` attributes.
|
|
@@ -314,14 +379,23 @@ export default class ClipboardMarkersUtils extends Plugin {
|
|
|
314
379
|
//
|
|
315
380
|
// The easiest way to bypass this issue is to rename already existing in map nodes and
|
|
316
381
|
// set them new unique name.
|
|
382
|
+
let skipAssign = false;
|
|
317
383
|
if (prevFakeMarker && prevFakeMarker.start && prevFakeMarker.end) {
|
|
318
|
-
|
|
384
|
+
const config = this._getMarkerClipboardConfig(fakeMarker.name);
|
|
385
|
+
if (config.duplicateOnPaste) {
|
|
386
|
+
acc[this._getUniqueMarkerName(fakeMarker.name)] = acc[fakeMarker.name];
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
skipAssign = true;
|
|
390
|
+
}
|
|
319
391
|
prevFakeMarker = null;
|
|
320
392
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
393
|
+
if (!skipAssign) {
|
|
394
|
+
acc[fakeMarker.name] = {
|
|
395
|
+
...prevFakeMarker,
|
|
396
|
+
[fakeMarker.type]: position
|
|
397
|
+
};
|
|
398
|
+
}
|
|
325
399
|
if (fakeMarker.markerElement) {
|
|
326
400
|
writer.remove(fakeMarker.markerElement);
|
|
327
401
|
}
|
package/src/clipboardpipeline.js
CHANGED
|
@@ -147,11 +147,13 @@ export default class ClipboardPipeline extends Plugin {
|
|
|
147
147
|
*/
|
|
148
148
|
_fireOutputTransformationEvent(dataTransfer, selection, method) {
|
|
149
149
|
const clipboardMarkersUtils = this.editor.plugins.get('ClipboardMarkersUtils');
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
150
|
+
this.editor.model.enqueueChange({ isUndoable: method === 'cut' }, () => {
|
|
151
|
+
const documentFragment = clipboardMarkersUtils._copySelectedFragmentWithMarkers(method, selection);
|
|
152
|
+
this.fire('outputTransformation', {
|
|
153
|
+
dataTransfer,
|
|
154
|
+
content: documentFragment,
|
|
155
|
+
method
|
|
156
|
+
});
|
|
155
157
|
});
|
|
156
158
|
}
|
|
157
159
|
/**
|
|
@@ -227,10 +229,7 @@ export default class ClipboardPipeline extends Plugin {
|
|
|
227
229
|
});
|
|
228
230
|
}, { priority: 'low' });
|
|
229
231
|
this.listenTo(this, 'contentInsertion', (evt, data) => {
|
|
230
|
-
clipboardMarkersUtils.
|
|
231
|
-
}, { priority: 'highest' });
|
|
232
|
-
this.listenTo(this, 'contentInsertion', (evt, data) => {
|
|
233
|
-
data.resultRange = model.insertContent(data.content);
|
|
232
|
+
data.resultRange = clipboardMarkersUtils._pasteFragmentWithMarkers(data.content);
|
|
234
233
|
}, { priority: 'low' });
|
|
235
234
|
}
|
|
236
235
|
/**
|
package/src/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export { default as Clipboard } from './clipboard.js';
|
|
9
9
|
export { default as ClipboardPipeline, type ClipboardContentInsertionEvent, type ClipboardContentInsertionData, type ClipboardInputTransformationEvent, type ClipboardInputTransformationData, type ClipboardOutputTransformationEvent, type ClipboardOutputTransformationData, type ViewDocumentClipboardOutputEvent } from './clipboardpipeline.js';
|
|
10
|
-
export { default as ClipboardMarkersUtils, type
|
|
10
|
+
export { default as ClipboardMarkersUtils, type ClipboardMarkerRestrictedAction, type ClipboardMarkerConfiguration } from './clipboardmarkersutils.js';
|
|
11
11
|
export type { ClipboardEventData } from './clipboardobserver.js';
|
|
12
12
|
export { default as DragDrop } from './dragdrop.js';
|
|
13
13
|
export { default as PastePlainText } from './pasteplaintext.js';
|