@ckeditor/ckeditor5-clipboard 41.4.2 → 42.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.
- package/README.md +6 -0
- package/dist/index.js +571 -331
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/src/lineview.js +1 -1
package/dist/index.js
CHANGED
|
@@ -3,39 +3,47 @@
|
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
5
|
import { Plugin } from '@ckeditor/ckeditor5-core/dist/index.js';
|
|
6
|
-
import { EventInfo, uid, toUnit,
|
|
6
|
+
import { EventInfo, uid, toUnit, delay, DomEmitterMixin, global, Rect, ResizeObserver, env, createElement } from '@ckeditor/ckeditor5-utils/dist/index.js';
|
|
7
7
|
import { DomEventObserver, DataTransfer, Range, MouseObserver, LiveRange } from '@ckeditor/ckeditor5-engine/dist/index.js';
|
|
8
8
|
import { mapValues, throttle } from 'lodash-es';
|
|
9
9
|
import { Widget, isWidget } from '@ckeditor/ckeditor5-widget/dist/index.js';
|
|
10
10
|
import { View } from '@ckeditor/ckeditor5-ui/dist/index.js';
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Clipboard events observer.
|
|
14
|
+
*
|
|
15
|
+
* Fires the following events:
|
|
16
|
+
*
|
|
17
|
+
* * {@link module:engine/view/document~Document#event:clipboardInput},
|
|
18
|
+
* * {@link module:engine/view/document~Document#event:paste},
|
|
19
|
+
* * {@link module:engine/view/document~Document#event:copy},
|
|
20
|
+
* * {@link module:engine/view/document~Document#event:cut},
|
|
21
|
+
* * {@link module:engine/view/document~Document#event:drop},
|
|
22
|
+
* * {@link module:engine/view/document~Document#event:dragover},
|
|
23
|
+
* * {@link module:engine/view/document~Document#event:dragging},
|
|
24
|
+
* * {@link module:engine/view/document~Document#event:dragstart},
|
|
25
|
+
* * {@link module:engine/view/document~Document#event:dragend},
|
|
26
|
+
* * {@link module:engine/view/document~Document#event:dragenter},
|
|
27
|
+
* * {@link module:engine/view/document~Document#event:dragleave}.
|
|
28
|
+
*
|
|
29
|
+
* **Note**: This observer is not available by default (ckeditor5-engine does not add it on its own).
|
|
30
|
+
* To make it available, it needs to be added to {@link module:engine/view/document~Document} by using
|
|
31
|
+
* the {@link module:engine/view/view~View#addObserver `View#addObserver()`} method. Alternatively, you can load the
|
|
32
|
+
* {@link module:clipboard/clipboard~Clipboard} plugin which adds this observer automatically (because it uses it).
|
|
33
|
+
*/ class ClipboardObserver extends DomEventObserver {
|
|
34
|
+
domEventType = [
|
|
35
|
+
'paste',
|
|
36
|
+
'copy',
|
|
37
|
+
'cut',
|
|
38
|
+
'drop',
|
|
39
|
+
'dragover',
|
|
40
|
+
'dragstart',
|
|
41
|
+
'dragend',
|
|
42
|
+
'dragenter',
|
|
43
|
+
'dragleave'
|
|
44
|
+
];
|
|
26
45
|
constructor(view){
|
|
27
46
|
super(view);
|
|
28
|
-
this.domEventType = [
|
|
29
|
-
'paste',
|
|
30
|
-
'copy',
|
|
31
|
-
'cut',
|
|
32
|
-
'drop',
|
|
33
|
-
'dragover',
|
|
34
|
-
'dragstart',
|
|
35
|
-
'dragend',
|
|
36
|
-
'dragenter',
|
|
37
|
-
'dragleave'
|
|
38
|
-
];
|
|
39
47
|
const viewDocument = this.document;
|
|
40
48
|
this.listenTo(viewDocument, 'paste', handleInput('clipboardInput'), {
|
|
41
49
|
priority: 'low'
|
|
@@ -69,6 +77,19 @@ class ClipboardObserver extends DomEventObserver {
|
|
|
69
77
|
};
|
|
70
78
|
}
|
|
71
79
|
}
|
|
80
|
+
onDomEvent(domEvent) {
|
|
81
|
+
const nativeDataTransfer = 'clipboardData' in domEvent ? domEvent.clipboardData : domEvent.dataTransfer;
|
|
82
|
+
const cacheFiles = domEvent.type == 'drop' || domEvent.type == 'paste';
|
|
83
|
+
const evtData = {
|
|
84
|
+
dataTransfer: new DataTransfer(nativeDataTransfer, {
|
|
85
|
+
cacheFiles
|
|
86
|
+
})
|
|
87
|
+
};
|
|
88
|
+
if (domEvent.type == 'drop' || domEvent.type == 'dragover') {
|
|
89
|
+
evtData.dropRange = getDropViewRange(this.view, domEvent);
|
|
90
|
+
}
|
|
91
|
+
this.fire(domEvent.type, domEvent, evtData);
|
|
92
|
+
}
|
|
72
93
|
}
|
|
73
94
|
function getDropViewRange(view, domEvent) {
|
|
74
95
|
const domDoc = domEvent.target.ownerDocument;
|
|
@@ -143,6 +164,8 @@ function getDropViewRange(view, domEvent) {
|
|
|
143
164
|
/**
|
|
144
165
|
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
|
145
166
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
167
|
+
*/ /**
|
|
168
|
+
* @module clipboard/utils/viewtoplaintext
|
|
146
169
|
*/ // Elements which should not have empty-line padding.
|
|
147
170
|
// Most `view.ContainerElement` want to be separate by new-line, but some are creating one structure
|
|
148
171
|
// together (like `<li>`) so it is better to separate them by only one "\n".
|
|
@@ -170,9 +193,9 @@ const listElements = [
|
|
|
170
193
|
return '\n'; // Convert soft breaks to single line break (#8045).
|
|
171
194
|
}
|
|
172
195
|
/**
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
196
|
+
* Item is a document fragment, attribute element or container element. It doesn't
|
|
197
|
+
* have it's own text value, so we need to convert its children elements.
|
|
198
|
+
*/ let text = '';
|
|
176
199
|
let prev = null;
|
|
177
200
|
for (const child of viewItem.getChildren()){
|
|
178
201
|
text += newLinePadding(child, prev) + viewToPlainText(child);
|
|
@@ -193,9 +216,9 @@ const listElements = [
|
|
|
193
216
|
}
|
|
194
217
|
if (listElements.includes(element.name) && listElements.includes(previous.name)) {
|
|
195
218
|
/**
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
219
|
+
* Because `<ul>` and `<ol>` are AttributeElements, two consecutive lists will not have any padding between
|
|
220
|
+
* them (see the `if` statement below). To fix this, we need to make an exception for this case.
|
|
221
|
+
*/ return '\n\n';
|
|
199
222
|
}
|
|
200
223
|
if (!element.is('containerElement') && !previous.is('containerElement')) {
|
|
201
224
|
// Don't add padding between non-container elements.
|
|
@@ -209,39 +232,49 @@ const listElements = [
|
|
|
209
232
|
return '\n\n';
|
|
210
233
|
}
|
|
211
234
|
|
|
212
|
-
|
|
235
|
+
/**
|
|
236
|
+
* Part of the clipboard logic. Responsible for collecting markers from selected fragments
|
|
237
|
+
* and restoring them with proper positions in pasted elements.
|
|
238
|
+
*
|
|
239
|
+
* @internal
|
|
240
|
+
*/ class ClipboardMarkersUtils extends Plugin {
|
|
241
|
+
/**
|
|
242
|
+
* Map of marker names that can be copied.
|
|
243
|
+
*
|
|
244
|
+
* @internal
|
|
245
|
+
*/ _markersToCopy = new Map();
|
|
213
246
|
/**
|
|
214
|
-
|
|
215
|
-
|
|
247
|
+
* @inheritDoc
|
|
248
|
+
*/ static get pluginName() {
|
|
216
249
|
return 'ClipboardMarkersUtils';
|
|
217
250
|
}
|
|
218
251
|
/**
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
252
|
+
* Registers marker name as copyable in clipboard pipeline.
|
|
253
|
+
*
|
|
254
|
+
* @param markerName Name of marker that can be copied.
|
|
255
|
+
* @param config Configuration that describes what can be performed on specified marker.
|
|
256
|
+
* @internal
|
|
257
|
+
*/ _registerMarkerToCopy(markerName, config) {
|
|
225
258
|
this._markersToCopy.set(markerName, config);
|
|
226
259
|
}
|
|
227
260
|
/**
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
261
|
+
* Performs copy markers on provided selection and paste it to fragment returned from `getCopiedFragment`.
|
|
262
|
+
*
|
|
263
|
+
* 1. Picks all markers in provided selection.
|
|
264
|
+
* 2. Inserts fake markers to document.
|
|
265
|
+
* 3. Gets copied selection fragment from document.
|
|
266
|
+
* 4. Removes fake elements from fragment and document.
|
|
267
|
+
* 5. Inserts markers in the place of removed fake markers.
|
|
268
|
+
*
|
|
269
|
+
* Due to selection modification, when inserting items, `getCopiedFragment` must *always* operate on `writer.model.document.selection'.
|
|
270
|
+
* Do not use any other custom selection object within callback, as this will lead to out-of-bounds exceptions in rare scenarios.
|
|
271
|
+
*
|
|
272
|
+
* @param action Type of clipboard action.
|
|
273
|
+
* @param writer An instance of the model writer.
|
|
274
|
+
* @param selection Selection to be checked.
|
|
275
|
+
* @param getCopiedFragment Callback that performs copy of selection and returns it as fragment.
|
|
276
|
+
* @internal
|
|
277
|
+
*/ _copySelectedFragmentWithMarkers(action, selection, getCopiedFragment = (writer)=>writer.model.getSelectedContent(writer.model.document.selection)) {
|
|
245
278
|
return this.editor.model.change((writer)=>{
|
|
246
279
|
const oldSelection = writer.model.document.selection;
|
|
247
280
|
// In some scenarios, such like in drag & drop, passed `selection` parameter is not actually
|
|
@@ -264,7 +297,7 @@ class ClipboardMarkersUtils extends Plugin {
|
|
|
264
297
|
// This function checks special case of such problem. Markers that are orphaned at the start position
|
|
265
298
|
// and end position in the same time. Basically it means that they overlaps whole element.
|
|
266
299
|
for (const [markerName, elements] of Object.entries(sourceSelectionInsertedMarkers)){
|
|
267
|
-
fakeMarkersRangesInsideRange[markerName]
|
|
300
|
+
fakeMarkersRangesInsideRange[markerName] ||= writer.createRangeIn(fragment);
|
|
268
301
|
for (const element of elements){
|
|
269
302
|
writer.remove(element);
|
|
270
303
|
}
|
|
@@ -279,24 +312,24 @@ class ClipboardMarkersUtils extends Plugin {
|
|
|
279
312
|
});
|
|
280
313
|
}
|
|
281
314
|
/**
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
315
|
+
* Performs paste of markers on already pasted element.
|
|
316
|
+
*
|
|
317
|
+
* 1. Inserts fake markers that are present in fragment element (such fragment will be processed in `getPastedDocumentElement`).
|
|
318
|
+
* 2. Calls `getPastedDocumentElement` and gets element that is inserted into root model.
|
|
319
|
+
* 3. Removes all fake markers present in transformed element.
|
|
320
|
+
* 4. Inserts new markers with removed fake markers ranges into pasted fragment.
|
|
321
|
+
*
|
|
322
|
+
* There are multiple edge cases that have to be considered before calling this function:
|
|
323
|
+
*
|
|
324
|
+
* * `markers` are inserted into the same element that must be later transformed inside `getPastedDocumentElement`.
|
|
325
|
+
* * Fake marker elements inside `getPastedDocumentElement` can be cloned, but their ranges cannot overlap.
|
|
326
|
+
* * If `duplicateOnPaste` is `true` in marker config then associated marker ID is regenerated before pasting.
|
|
327
|
+
*
|
|
328
|
+
* @param action Type of clipboard action.
|
|
329
|
+
* @param markers Object that maps marker name to corresponding range.
|
|
330
|
+
* @param getPastedDocumentElement Getter used to get target markers element.
|
|
331
|
+
* @internal
|
|
332
|
+
*/ _pasteMarkersIntoTransformedElement(markers, getPastedDocumentElement) {
|
|
300
333
|
const pasteMarkers = this._getPasteMarkersFromRangeMap(markers);
|
|
301
334
|
return this.editor.model.change((writer)=>{
|
|
302
335
|
// Inserts fake markers into source fragment / element that is later transformed inside `getPastedDocumentElement`.
|
|
@@ -323,13 +356,13 @@ class ClipboardMarkersUtils extends Plugin {
|
|
|
323
356
|
});
|
|
324
357
|
}
|
|
325
358
|
/**
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
359
|
+
* Pastes document fragment with markers to document.
|
|
360
|
+
* If `duplicateOnPaste` is `true` in marker config then associated markers IDs
|
|
361
|
+
* are regenerated before pasting to avoid markers duplications in content.
|
|
362
|
+
*
|
|
363
|
+
* @param fragment Document fragment that should contain already processed by pipeline markers.
|
|
364
|
+
* @internal
|
|
365
|
+
*/ _pasteFragmentWithMarkers(fragment) {
|
|
333
366
|
const pasteMarkers = this._getPasteMarkersFromRangeMap(fragment.markers);
|
|
334
367
|
fragment.markers.clear();
|
|
335
368
|
for (const copyableMarker of pasteMarkers){
|
|
@@ -338,17 +371,17 @@ class ClipboardMarkersUtils extends Plugin {
|
|
|
338
371
|
return this.editor.model.insertContent(fragment);
|
|
339
372
|
}
|
|
340
373
|
/**
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
374
|
+
* In some situations we have to perform copy on selected fragment with certain markers. This function allows to temporarily bypass
|
|
375
|
+
* restrictions on markers that we want to copy.
|
|
376
|
+
*
|
|
377
|
+
* This function executes `executor()` callback. For the duration of the callback, if the clipboard pipeline is used to copy
|
|
378
|
+
* content, markers with the specified name will be copied to the clipboard as well.
|
|
379
|
+
*
|
|
380
|
+
* @param markerName Which markers should be copied.
|
|
381
|
+
* @param executor Callback executed.
|
|
382
|
+
* @param config Optional configuration flags used to copy (such like partial copy flag).
|
|
383
|
+
* @internal
|
|
384
|
+
*/ _forceMarkersCopy(markerName, executor, config = {
|
|
352
385
|
allowedActions: 'all',
|
|
353
386
|
copyPartiallySelected: true,
|
|
354
387
|
duplicateOnPaste: true
|
|
@@ -363,12 +396,12 @@ class ClipboardMarkersUtils extends Plugin {
|
|
|
363
396
|
}
|
|
364
397
|
}
|
|
365
398
|
/**
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
399
|
+
* Checks if marker can be copied.
|
|
400
|
+
*
|
|
401
|
+
* @param markerName Name of checked marker.
|
|
402
|
+
* @param action Type of clipboard action. If null then checks only if marker is registered as copyable.
|
|
403
|
+
* @internal
|
|
404
|
+
*/ _isMarkerCopyable(markerName, action) {
|
|
372
405
|
const config = this._getMarkerClipboardConfig(markerName);
|
|
373
406
|
if (!config) {
|
|
374
407
|
return false;
|
|
@@ -381,43 +414,43 @@ class ClipboardMarkersUtils extends Plugin {
|
|
|
381
414
|
return allowedActions === 'all' || allowedActions.includes(action);
|
|
382
415
|
}
|
|
383
416
|
/**
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
417
|
+
* Checks if marker has any clipboard copy behavior configuration.
|
|
418
|
+
*
|
|
419
|
+
* @param markerName Name of checked marker.
|
|
420
|
+
*/ _hasMarkerConfiguration(markerName) {
|
|
388
421
|
return !!this._getMarkerClipboardConfig(markerName);
|
|
389
422
|
}
|
|
390
423
|
/**
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
424
|
+
* Returns marker's configuration flags passed during registration.
|
|
425
|
+
*
|
|
426
|
+
* @param markerName Name of marker that should be returned.
|
|
427
|
+
* @internal
|
|
428
|
+
*/ _getMarkerClipboardConfig(markerName) {
|
|
396
429
|
const [markerNamePrefix] = markerName.split(':');
|
|
397
430
|
return this._markersToCopy.get(markerNamePrefix) || null;
|
|
398
431
|
}
|
|
399
432
|
/**
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
433
|
+
* First step of copying markers. It looks for markers intersecting with given selection and inserts `$marker` elements
|
|
434
|
+
* at positions where document markers start or end. This way `$marker` elements can be easily copied together with
|
|
435
|
+
* the rest of the content of the selection.
|
|
436
|
+
*
|
|
437
|
+
* @param writer An instance of the model writer.
|
|
438
|
+
* @param selection Selection to be checked.
|
|
439
|
+
* @param action Type of clipboard action.
|
|
440
|
+
*/ _insertFakeMarkersIntoSelection(writer, selection, action) {
|
|
408
441
|
const copyableMarkers = this._getCopyableMarkersFromSelection(writer, selection, action);
|
|
409
442
|
return this._insertFakeMarkersElements(writer, copyableMarkers);
|
|
410
443
|
}
|
|
411
444
|
/**
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
445
|
+
* Returns array of markers that can be copied in specified selection.
|
|
446
|
+
*
|
|
447
|
+
* If marker cannot be copied partially (according to `copyPartiallySelected` configuration flag) and
|
|
448
|
+
* is not present entirely in any selection range then it will be skipped.
|
|
449
|
+
*
|
|
450
|
+
* @param writer An instance of the model writer.
|
|
451
|
+
* @param selection Selection which will be checked.
|
|
452
|
+
* @param action Type of clipboard action. If null then checks only if marker is registered as copyable.
|
|
453
|
+
*/ _getCopyableMarkersFromSelection(writer, selection, action) {
|
|
421
454
|
const selectionRanges = Array.from(selection.getRanges());
|
|
422
455
|
// Picks all markers in provided ranges. Ensures that there are no duplications if
|
|
423
456
|
// there are multiple ranges that intersects with the same marker.
|
|
@@ -455,13 +488,13 @@ class ClipboardMarkersUtils extends Plugin {
|
|
|
455
488
|
});
|
|
456
489
|
}
|
|
457
490
|
/**
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
491
|
+
* Picks all markers from markers map that can be pasted.
|
|
492
|
+
* If `duplicateOnPaste` is `true`, it regenerates their IDs to ensure uniqueness.
|
|
493
|
+
* If marker is not registered, it will be kept in the array anyway.
|
|
494
|
+
*
|
|
495
|
+
* @param markers Object that maps marker name to corresponding range.
|
|
496
|
+
* @param action Type of clipboard action. If null then checks only if marker is registered as copyable.
|
|
497
|
+
*/ _getPasteMarkersFromRangeMap(markers, action = null) {
|
|
465
498
|
const { model } = this.editor;
|
|
466
499
|
const entries = markers instanceof Map ? Array.from(markers.entries()) : Object.entries(markers);
|
|
467
500
|
return entries.flatMap(([markerName, range])=>{
|
|
@@ -490,13 +523,13 @@ class ClipboardMarkersUtils extends Plugin {
|
|
|
490
523
|
});
|
|
491
524
|
}
|
|
492
525
|
/**
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
526
|
+
* Inserts specified array of fake markers elements to document and assigns them `type` and `name` attributes.
|
|
527
|
+
* Fake markers elements are used to calculate position of markers on pasted fragment that were transformed during
|
|
528
|
+
* steps between copy and paste.
|
|
529
|
+
*
|
|
530
|
+
* @param writer An instance of the model writer.
|
|
531
|
+
* @param markers Array of markers that will be inserted.
|
|
532
|
+
*/ _insertFakeMarkersElements(writer, markers) {
|
|
500
533
|
const mappedMarkers = {};
|
|
501
534
|
const sortedMarkers = markers.flatMap((marker)=>{
|
|
502
535
|
const { start, end } = marker.range;
|
|
@@ -529,17 +562,17 @@ class ClipboardMarkersUtils extends Plugin {
|
|
|
529
562
|
return mappedMarkers;
|
|
530
563
|
}
|
|
531
564
|
/**
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
565
|
+
* Removes all `$marker` elements from the given document fragment.
|
|
566
|
+
*
|
|
567
|
+
* Returns an object where keys are marker names, and values are ranges corresponding to positions
|
|
568
|
+
* where `$marker` elements were inserted.
|
|
569
|
+
*
|
|
570
|
+
* If the document fragment had only one `$marker` element for given marker (start or end) the other boundary is set automatically
|
|
571
|
+
* (to the end or start of the document fragment, respectively).
|
|
572
|
+
*
|
|
573
|
+
* @param writer An instance of the model writer.
|
|
574
|
+
* @param rootElement The element to be checked.
|
|
575
|
+
*/ _removeFakeMarkersInsideElement(writer, rootElement) {
|
|
543
576
|
const fakeMarkersElements = this._getAllFakeMarkersFromElement(writer, rootElement);
|
|
544
577
|
const fakeMarkersRanges = fakeMarkersElements.reduce((acc, fakeMarker)=>{
|
|
545
578
|
const position = fakeMarker.markerElement && writer.createPositionBefore(fakeMarker.markerElement);
|
|
@@ -584,14 +617,14 @@ class ClipboardMarkersUtils extends Plugin {
|
|
|
584
617
|
]), range.end || writer.createPositionAt(rootElement, 'end')));
|
|
585
618
|
}
|
|
586
619
|
/**
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
620
|
+
* Returns array that contains list of fake markers with corresponding `$marker` elements.
|
|
621
|
+
*
|
|
622
|
+
* For each marker, there can be two `$marker` elements or only one (if the document fragment contained
|
|
623
|
+
* only the beginning or only the end of a marker).
|
|
624
|
+
*
|
|
625
|
+
* @param writer An instance of the model writer.
|
|
626
|
+
* @param rootElement The element to be checked.
|
|
627
|
+
*/ _getAllFakeMarkersFromElement(writer, rootElement) {
|
|
595
628
|
const foundFakeMarkers = Array.from(writer.createRangeIn(rootElement)).flatMap(({ item })=>{
|
|
596
629
|
if (!item.is('element', '$marker')) {
|
|
597
630
|
return [];
|
|
@@ -645,12 +678,12 @@ class ClipboardMarkersUtils extends Plugin {
|
|
|
645
678
|
];
|
|
646
679
|
}
|
|
647
680
|
/**
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
681
|
+
* When copy of markers occurs we have to make sure that pasted markers have different names
|
|
682
|
+
* than source markers. This functions helps with assigning unique part to marker name to
|
|
683
|
+
* prevent duplicated markers error.
|
|
684
|
+
*
|
|
685
|
+
* @param name Name of marker
|
|
686
|
+
*/ _getUniqueMarkerName(name) {
|
|
654
687
|
const parts = name.split(':');
|
|
655
688
|
const newId = uid().substring(1, 6);
|
|
656
689
|
// It looks like the marker already is UID marker so in this scenario just swap
|
|
@@ -665,32 +698,128 @@ class ClipboardMarkersUtils extends Plugin {
|
|
|
665
698
|
// example: comment => comment:{ newId }
|
|
666
699
|
return `${parts.join(':')}:${newId}`;
|
|
667
700
|
}
|
|
668
|
-
constructor(){
|
|
669
|
-
super(...arguments);
|
|
670
|
-
/**
|
|
671
|
-
* Map of marker names that can be copied.
|
|
672
|
-
*
|
|
673
|
-
* @internal
|
|
674
|
-
*/ this._markersToCopy = new Map();
|
|
675
|
-
}
|
|
676
701
|
}
|
|
677
702
|
|
|
678
|
-
|
|
703
|
+
// Input pipeline events overview:
|
|
704
|
+
//
|
|
705
|
+
// ┌──────────────────────┐ ┌──────────────────────┐
|
|
706
|
+
// │ view.Document │ │ view.Document │
|
|
707
|
+
// │ paste │ │ drop │
|
|
708
|
+
// └───────────┬──────────┘ └───────────┬──────────┘
|
|
709
|
+
// │ │
|
|
710
|
+
// └────────────────┌────────────────┘
|
|
711
|
+
// │
|
|
712
|
+
// ┌─────────V────────┐
|
|
713
|
+
// │ view.Document │ Retrieves text/html or text/plain from data.dataTransfer
|
|
714
|
+
// │ clipboardInput │ and processes it to view.DocumentFragment.
|
|
715
|
+
// └─────────┬────────┘
|
|
716
|
+
// │
|
|
717
|
+
// ┌───────────V───────────┐
|
|
718
|
+
// │ ClipboardPipeline │ Converts view.DocumentFragment to model.DocumentFragment.
|
|
719
|
+
// │ inputTransformation │
|
|
720
|
+
// └───────────┬───────────┘
|
|
721
|
+
// │
|
|
722
|
+
// ┌──────────V──────────┐
|
|
723
|
+
// │ ClipboardPipeline │ Calls model.insertContent().
|
|
724
|
+
// │ contentInsertion │
|
|
725
|
+
// └─────────────────────┘
|
|
726
|
+
//
|
|
727
|
+
//
|
|
728
|
+
// Output pipeline events overview:
|
|
729
|
+
//
|
|
730
|
+
// ┌──────────────────────┐ ┌──────────────────────┐
|
|
731
|
+
// │ view.Document │ │ view.Document │ Retrieves the selected model.DocumentFragment
|
|
732
|
+
// │ copy │ │ cut │ and fires the `outputTransformation` event.
|
|
733
|
+
// └───────────┬──────────┘ └───────────┬──────────┘
|
|
734
|
+
// │ │
|
|
735
|
+
// └────────────────┌────────────────┘
|
|
736
|
+
// │
|
|
737
|
+
// ┌───────────V───────────┐
|
|
738
|
+
// │ ClipboardPipeline │ Processes model.DocumentFragment and converts it to
|
|
739
|
+
// │ outputTransformation │ view.DocumentFragment.
|
|
740
|
+
// └───────────┬───────────┘
|
|
741
|
+
// │
|
|
742
|
+
// ┌─────────V────────┐
|
|
743
|
+
// │ view.Document │ Processes view.DocumentFragment to text/html and text/plain
|
|
744
|
+
// │ clipboardOutput │ and stores the results in data.dataTransfer.
|
|
745
|
+
// └──────────────────┘
|
|
746
|
+
//
|
|
747
|
+
/**
|
|
748
|
+
* The clipboard pipeline feature. It is responsible for intercepting the `paste` and `drop` events and
|
|
749
|
+
* passing the pasted content through a series of events in order to insert it into the editor's content.
|
|
750
|
+
* It also handles the `cut` and `copy` events to fill the native clipboard with the serialized editor's data.
|
|
751
|
+
*
|
|
752
|
+
* # Input pipeline
|
|
753
|
+
*
|
|
754
|
+
* The behavior of the default handlers (all at a `low` priority):
|
|
755
|
+
*
|
|
756
|
+
* ## Event: `paste` or `drop`
|
|
757
|
+
*
|
|
758
|
+
* 1. Translates the event data.
|
|
759
|
+
* 2. Fires the {@link module:engine/view/document~Document#event:clipboardInput `view.Document#clipboardInput`} event.
|
|
760
|
+
*
|
|
761
|
+
* ## Event: `view.Document#clipboardInput`
|
|
762
|
+
*
|
|
763
|
+
* 1. If the `data.content` event field is already set (by some listener on a higher priority), it takes this content and fires the event
|
|
764
|
+
* from the last point.
|
|
765
|
+
* 2. Otherwise, it retrieves `text/html` or `text/plain` from `data.dataTransfer`.
|
|
766
|
+
* 3. Normalizes the raw data by applying simple filters on string data.
|
|
767
|
+
* 4. Processes the raw data to {@link module:engine/view/documentfragment~DocumentFragment `view.DocumentFragment`} with the
|
|
768
|
+
* {@link module:engine/controller/datacontroller~DataController#htmlProcessor `DataController#htmlProcessor`}.
|
|
769
|
+
* 5. Fires the {@link module:clipboard/clipboardpipeline~ClipboardPipeline#event:inputTransformation
|
|
770
|
+
* `ClipboardPipeline#inputTransformation`} event with the view document fragment in the `data.content` event field.
|
|
771
|
+
*
|
|
772
|
+
* ## Event: `ClipboardPipeline#inputTransformation`
|
|
773
|
+
*
|
|
774
|
+
* 1. Converts {@link module:engine/view/documentfragment~DocumentFragment `view.DocumentFragment`} from the `data.content` field to
|
|
775
|
+
* {@link module:engine/model/documentfragment~DocumentFragment `model.DocumentFragment`}.
|
|
776
|
+
* 2. Fires the {@link module:clipboard/clipboardpipeline~ClipboardPipeline#event:contentInsertion `ClipboardPipeline#contentInsertion`}
|
|
777
|
+
* event with the model document fragment in the `data.content` event field.
|
|
778
|
+
* **Note**: The `ClipboardPipeline#contentInsertion` event is fired within a model change block to allow other handlers
|
|
779
|
+
* to run in the same block without post-fixers called in between (i.e., the selection post-fixer).
|
|
780
|
+
*
|
|
781
|
+
* ## Event: `ClipboardPipeline#contentInsertion`
|
|
782
|
+
*
|
|
783
|
+
* 1. Calls {@link module:engine/model/model~Model#insertContent `model.insertContent()`} to insert `data.content`
|
|
784
|
+
* at the current selection position.
|
|
785
|
+
*
|
|
786
|
+
* # Output pipeline
|
|
787
|
+
*
|
|
788
|
+
* The behavior of the default handlers (all at a `low` priority):
|
|
789
|
+
*
|
|
790
|
+
* ## Event: `copy`, `cut` or `dragstart`
|
|
791
|
+
*
|
|
792
|
+
* 1. Retrieves the selected {@link module:engine/model/documentfragment~DocumentFragment `model.DocumentFragment`} by calling
|
|
793
|
+
* {@link module:engine/model/model~Model#getSelectedContent `model#getSelectedContent()`}.
|
|
794
|
+
* 2. Converts the model document fragment to {@link module:engine/view/documentfragment~DocumentFragment `view.DocumentFragment`}.
|
|
795
|
+
* 3. Fires the {@link module:engine/view/document~Document#event:clipboardOutput `view.Document#clipboardOutput`} event
|
|
796
|
+
* with the view document fragment in the `data.content` event field.
|
|
797
|
+
*
|
|
798
|
+
* ## Event: `view.Document#clipboardOutput`
|
|
799
|
+
*
|
|
800
|
+
* 1. Processes `data.content` to HTML and plain text with the
|
|
801
|
+
* {@link module:engine/controller/datacontroller~DataController#htmlProcessor `DataController#htmlProcessor`}.
|
|
802
|
+
* 2. Updates the `data.dataTransfer` data for `text/html` and `text/plain` with the processed data.
|
|
803
|
+
* 3. For the `cut` method, calls {@link module:engine/model/model~Model#deleteContent `model.deleteContent()`}
|
|
804
|
+
* on the current selection.
|
|
805
|
+
*
|
|
806
|
+
* Read more about the clipboard integration in the {@glink framework/deep-dive/clipboard clipboard deep-dive} guide.
|
|
807
|
+
*/ class ClipboardPipeline extends Plugin {
|
|
679
808
|
/**
|
|
680
|
-
|
|
681
|
-
|
|
809
|
+
* @inheritDoc
|
|
810
|
+
*/ static get pluginName() {
|
|
682
811
|
return 'ClipboardPipeline';
|
|
683
812
|
}
|
|
684
813
|
/**
|
|
685
|
-
|
|
686
|
-
|
|
814
|
+
* @inheritDoc
|
|
815
|
+
*/ static get requires() {
|
|
687
816
|
return [
|
|
688
817
|
ClipboardMarkersUtils
|
|
689
818
|
];
|
|
690
819
|
}
|
|
691
820
|
/**
|
|
692
|
-
|
|
693
|
-
|
|
821
|
+
* @inheritDoc
|
|
822
|
+
*/ init() {
|
|
694
823
|
const editor = this.editor;
|
|
695
824
|
const view = editor.editing.view;
|
|
696
825
|
view.addObserver(ClipboardObserver);
|
|
@@ -698,10 +827,10 @@ class ClipboardPipeline extends Plugin {
|
|
|
698
827
|
this._setupCopyCut();
|
|
699
828
|
}
|
|
700
829
|
/**
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
830
|
+
* Fires Clipboard `'outputTransformation'` event for given parameters.
|
|
831
|
+
*
|
|
832
|
+
* @internal
|
|
833
|
+
*/ _fireOutputTransformationEvent(dataTransfer, selection, method) {
|
|
705
834
|
const clipboardMarkersUtils = this.editor.plugins.get('ClipboardMarkersUtils');
|
|
706
835
|
this.editor.model.enqueueChange({
|
|
707
836
|
isUndoable: method === 'cut'
|
|
@@ -715,8 +844,8 @@ class ClipboardPipeline extends Plugin {
|
|
|
715
844
|
});
|
|
716
845
|
}
|
|
717
846
|
/**
|
|
718
|
-
|
|
719
|
-
|
|
847
|
+
* The clipboard paste pipeline.
|
|
848
|
+
*/ _setupPasteDrop() {
|
|
720
849
|
const editor = this.editor;
|
|
721
850
|
const model = editor.model;
|
|
722
851
|
const view = editor.editing.view;
|
|
@@ -796,8 +925,8 @@ class ClipboardPipeline extends Plugin {
|
|
|
796
925
|
});
|
|
797
926
|
}
|
|
798
927
|
/**
|
|
799
|
-
|
|
800
|
-
|
|
928
|
+
* The clipboard copy/cut pipeline.
|
|
929
|
+
*/ _setupCopyCut() {
|
|
801
930
|
const editor = this.editor;
|
|
802
931
|
const modelDocument = editor.model.document;
|
|
803
932
|
const view = editor.editing.view;
|
|
@@ -845,11 +974,13 @@ class ClipboardPipeline extends Plugin {
|
|
|
845
974
|
}
|
|
846
975
|
}
|
|
847
976
|
|
|
848
|
-
const toPx = toUnit('px');
|
|
849
|
-
|
|
977
|
+
const toPx = /* #__PURE__ */ toUnit('px');
|
|
978
|
+
/**
|
|
979
|
+
* The horizontal drop target line view.
|
|
980
|
+
*/ class LineView extends View {
|
|
850
981
|
/**
|
|
851
|
-
|
|
852
|
-
|
|
982
|
+
* @inheritDoc
|
|
983
|
+
*/ constructor(){
|
|
853
984
|
super();
|
|
854
985
|
const bind = this.bindTemplate;
|
|
855
986
|
this.set({
|
|
@@ -876,20 +1007,48 @@ class LineView extends View {
|
|
|
876
1007
|
}
|
|
877
1008
|
}
|
|
878
1009
|
|
|
879
|
-
|
|
1010
|
+
/**
|
|
1011
|
+
* Part of the Drag and Drop handling. Responsible for finding and displaying the drop target.
|
|
1012
|
+
*
|
|
1013
|
+
* @internal
|
|
1014
|
+
*/ class DragDropTarget extends Plugin {
|
|
1015
|
+
/**
|
|
1016
|
+
* A delayed callback removing the drop marker.
|
|
1017
|
+
*
|
|
1018
|
+
* @internal
|
|
1019
|
+
*/ removeDropMarkerDelayed = delay(()=>this.removeDropMarker(), 40);
|
|
1020
|
+
/**
|
|
1021
|
+
* A throttled callback updating the drop marker.
|
|
1022
|
+
*/ _updateDropMarkerThrottled = throttle((targetRange)=>this._updateDropMarker(targetRange), 40);
|
|
1023
|
+
/**
|
|
1024
|
+
* A throttled callback reconverting the drop parker.
|
|
1025
|
+
*/ _reconvertMarkerThrottled = throttle(()=>{
|
|
1026
|
+
if (this.editor.model.markers.has('drop-target')) {
|
|
1027
|
+
this.editor.editing.reconvertMarker('drop-target');
|
|
1028
|
+
}
|
|
1029
|
+
}, 0);
|
|
1030
|
+
/**
|
|
1031
|
+
* The horizontal drop target line view.
|
|
1032
|
+
*/ _dropTargetLineView = new LineView();
|
|
880
1033
|
/**
|
|
881
|
-
|
|
882
|
-
|
|
1034
|
+
* DOM Emitter.
|
|
1035
|
+
*/ _domEmitter = new (DomEmitterMixin())();
|
|
1036
|
+
/**
|
|
1037
|
+
* Map of document scrollable elements.
|
|
1038
|
+
*/ _scrollables = new Map();
|
|
1039
|
+
/**
|
|
1040
|
+
* @inheritDoc
|
|
1041
|
+
*/ static get pluginName() {
|
|
883
1042
|
return 'DragDropTarget';
|
|
884
1043
|
}
|
|
885
1044
|
/**
|
|
886
|
-
|
|
887
|
-
|
|
1045
|
+
* @inheritDoc
|
|
1046
|
+
*/ init() {
|
|
888
1047
|
this._setupDropMarker();
|
|
889
1048
|
}
|
|
890
1049
|
/**
|
|
891
|
-
|
|
892
|
-
|
|
1050
|
+
* @inheritDoc
|
|
1051
|
+
*/ destroy() {
|
|
893
1052
|
this._domEmitter.stopListening();
|
|
894
1053
|
for (const { resizeObserver } of this._scrollables.values()){
|
|
895
1054
|
resizeObserver.destroy();
|
|
@@ -900,10 +1059,10 @@ class DragDropTarget extends Plugin {
|
|
|
900
1059
|
return super.destroy();
|
|
901
1060
|
}
|
|
902
1061
|
/**
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
1062
|
+
* Finds the drop target range and updates the drop marker.
|
|
1063
|
+
*
|
|
1064
|
+
* @internal
|
|
1065
|
+
*/ updateDropMarker(targetViewElement, targetViewRanges, clientX, clientY, blockMode, draggedRange) {
|
|
907
1066
|
this.removeDropMarkerDelayed.cancel();
|
|
908
1067
|
const targetRange = findDropTargetRange(this.editor, targetViewElement, targetViewRanges, clientX, clientY, blockMode, draggedRange);
|
|
909
1068
|
/* istanbul ignore next -- @preserve */ if (!targetRange) {
|
|
@@ -916,10 +1075,10 @@ class DragDropTarget extends Plugin {
|
|
|
916
1075
|
this._updateDropMarkerThrottled(targetRange);
|
|
917
1076
|
}
|
|
918
1077
|
/**
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1078
|
+
* Finds the final drop target range.
|
|
1079
|
+
*
|
|
1080
|
+
* @internal
|
|
1081
|
+
*/ getFinalDropRange(targetViewElement, targetViewRanges, clientX, clientY, blockMode, draggedRange) {
|
|
923
1082
|
const targetRange = findDropTargetRange(this.editor, targetViewElement, targetViewRanges, clientX, clientY, blockMode, draggedRange);
|
|
924
1083
|
// The dragging markers must be removed after searching for the target range because sometimes
|
|
925
1084
|
// the target lands on the marker itself.
|
|
@@ -927,10 +1086,10 @@ class DragDropTarget extends Plugin {
|
|
|
927
1086
|
return targetRange;
|
|
928
1087
|
}
|
|
929
1088
|
/**
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
1089
|
+
* Removes the drop target marker.
|
|
1090
|
+
*
|
|
1091
|
+
* @internal
|
|
1092
|
+
*/ removeDropMarker() {
|
|
934
1093
|
const model = this.editor.model;
|
|
935
1094
|
this.removeDropMarkerDelayed.cancel();
|
|
936
1095
|
this._updateDropMarkerThrottled.cancel();
|
|
@@ -942,8 +1101,8 @@ class DragDropTarget extends Plugin {
|
|
|
942
1101
|
}
|
|
943
1102
|
}
|
|
944
1103
|
/**
|
|
945
|
-
|
|
946
|
-
|
|
1104
|
+
* Creates downcast conversion for the drop target marker.
|
|
1105
|
+
*/ _setupDropMarker() {
|
|
947
1106
|
const editor = this.editor;
|
|
948
1107
|
editor.ui.view.body.add(this._dropTargetLineView);
|
|
949
1108
|
// Drop marker conversion for hovering over widgets.
|
|
@@ -974,10 +1133,10 @@ class DragDropTarget extends Plugin {
|
|
|
974
1133
|
});
|
|
975
1134
|
}
|
|
976
1135
|
/**
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1136
|
+
* Updates the drop target marker to the provided range.
|
|
1137
|
+
*
|
|
1138
|
+
* @param targetRange The range to set the marker to.
|
|
1139
|
+
*/ _updateDropMarker(targetRange) {
|
|
981
1140
|
const editor = this.editor;
|
|
982
1141
|
const markers = editor.model.markers;
|
|
983
1142
|
editor.model.change((writer)=>{
|
|
@@ -997,8 +1156,8 @@ class DragDropTarget extends Plugin {
|
|
|
997
1156
|
});
|
|
998
1157
|
}
|
|
999
1158
|
/**
|
|
1000
|
-
|
|
1001
|
-
|
|
1159
|
+
* Creates the UI element for vertical (in-line) drop target.
|
|
1160
|
+
*/ _createDropTargetPosition(writer) {
|
|
1002
1161
|
return writer.createUIElement('span', {
|
|
1003
1162
|
class: 'ck ck-clipboard-drop-target-position'
|
|
1004
1163
|
}, function(domDocument) {
|
|
@@ -1009,8 +1168,8 @@ class DragDropTarget extends Plugin {
|
|
|
1009
1168
|
});
|
|
1010
1169
|
}
|
|
1011
1170
|
/**
|
|
1012
|
-
|
|
1013
|
-
|
|
1171
|
+
* Updates the horizontal drop target line.
|
|
1172
|
+
*/ _updateDropTargetLine(range) {
|
|
1014
1173
|
const editing = this.editor.editing;
|
|
1015
1174
|
const nodeBefore = range.start.nodeBefore;
|
|
1016
1175
|
const nodeAfter = range.start.nodeAfter;
|
|
@@ -1049,8 +1208,8 @@ class DragDropTarget extends Plugin {
|
|
|
1049
1208
|
}
|
|
1050
1209
|
}
|
|
1051
1210
|
/**
|
|
1052
|
-
|
|
1053
|
-
|
|
1211
|
+
* Finds the closest scrollable element rect for the given view element.
|
|
1212
|
+
*/ _getScrollableRect(viewElement) {
|
|
1054
1213
|
const rootName = viewElement.root.rootName;
|
|
1055
1214
|
let domScrollable;
|
|
1056
1215
|
if (this._scrollables.has(rootName)) {
|
|
@@ -1069,33 +1228,6 @@ class DragDropTarget extends Plugin {
|
|
|
1069
1228
|
}
|
|
1070
1229
|
return new Rect(domScrollable).excludeScrollbarsAndBorders();
|
|
1071
1230
|
}
|
|
1072
|
-
constructor(){
|
|
1073
|
-
super(...arguments);
|
|
1074
|
-
/**
|
|
1075
|
-
* A delayed callback removing the drop marker.
|
|
1076
|
-
*
|
|
1077
|
-
* @internal
|
|
1078
|
-
*/ this.removeDropMarkerDelayed = delay(()=>this.removeDropMarker(), 40);
|
|
1079
|
-
/**
|
|
1080
|
-
* A throttled callback updating the drop marker.
|
|
1081
|
-
*/ this._updateDropMarkerThrottled = throttle((targetRange)=>this._updateDropMarker(targetRange), 40);
|
|
1082
|
-
/**
|
|
1083
|
-
* A throttled callback reconverting the drop parker.
|
|
1084
|
-
*/ this._reconvertMarkerThrottled = throttle(()=>{
|
|
1085
|
-
if (this.editor.model.markers.has('drop-target')) {
|
|
1086
|
-
this.editor.editing.reconvertMarker('drop-target');
|
|
1087
|
-
}
|
|
1088
|
-
}, 0);
|
|
1089
|
-
/**
|
|
1090
|
-
* The horizontal drop target line view.
|
|
1091
|
-
*/ this._dropTargetLineView = new LineView();
|
|
1092
|
-
/**
|
|
1093
|
-
* DOM Emitter.
|
|
1094
|
-
*/ this._domEmitter = new (DomEmitterMixin())();
|
|
1095
|
-
/**
|
|
1096
|
-
* Map of document scrollable elements.
|
|
1097
|
-
*/ this._scrollables = new Map();
|
|
1098
|
-
}
|
|
1099
1231
|
}
|
|
1100
1232
|
/**
|
|
1101
1233
|
* Returns fixed selection range for given position and target element.
|
|
@@ -1210,15 +1342,25 @@ class DragDropTarget extends Plugin {
|
|
|
1210
1342
|
return domElement;
|
|
1211
1343
|
}
|
|
1212
1344
|
|
|
1213
|
-
|
|
1345
|
+
/**
|
|
1346
|
+
* Integration of a block Drag and Drop support with the block toolbar.
|
|
1347
|
+
*
|
|
1348
|
+
* @internal
|
|
1349
|
+
*/ class DragDropBlockToolbar extends Plugin {
|
|
1214
1350
|
/**
|
|
1215
|
-
|
|
1216
|
-
|
|
1351
|
+
* Whether current dragging is started by block toolbar button dragging.
|
|
1352
|
+
*/ _isBlockDragging = false;
|
|
1353
|
+
/**
|
|
1354
|
+
* DOM Emitter.
|
|
1355
|
+
*/ _domEmitter = new (DomEmitterMixin())();
|
|
1356
|
+
/**
|
|
1357
|
+
* @inheritDoc
|
|
1358
|
+
*/ static get pluginName() {
|
|
1217
1359
|
return 'DragDropBlockToolbar';
|
|
1218
1360
|
}
|
|
1219
1361
|
/**
|
|
1220
|
-
|
|
1221
|
-
|
|
1362
|
+
* @inheritDoc
|
|
1363
|
+
*/ init() {
|
|
1222
1364
|
const editor = this.editor;
|
|
1223
1365
|
this.listenTo(editor, 'change:isReadOnly', (evt, name, isReadOnly)=>{
|
|
1224
1366
|
if (isReadOnly) {
|
|
@@ -1249,14 +1391,14 @@ class DragDropBlockToolbar extends Plugin {
|
|
|
1249
1391
|
}
|
|
1250
1392
|
}
|
|
1251
1393
|
/**
|
|
1252
|
-
|
|
1253
|
-
|
|
1394
|
+
* @inheritDoc
|
|
1395
|
+
*/ destroy() {
|
|
1254
1396
|
this._domEmitter.stopListening();
|
|
1255
1397
|
return super.destroy();
|
|
1256
1398
|
}
|
|
1257
1399
|
/**
|
|
1258
|
-
|
|
1259
|
-
|
|
1400
|
+
* The `dragstart` event handler.
|
|
1401
|
+
*/ _handleBlockDragStart(domEvent) {
|
|
1260
1402
|
if (!this.isEnabled) {
|
|
1261
1403
|
return;
|
|
1262
1404
|
}
|
|
@@ -1271,8 +1413,8 @@ class DragDropBlockToolbar extends Plugin {
|
|
|
1271
1413
|
view.getObserver(ClipboardObserver).onDomEvent(domEvent);
|
|
1272
1414
|
}
|
|
1273
1415
|
/**
|
|
1274
|
-
|
|
1275
|
-
|
|
1416
|
+
* The `dragover` and `drop` event handler.
|
|
1417
|
+
*/ _handleBlockDragging(domEvent) {
|
|
1276
1418
|
if (!this.isEnabled || !this._isBlockDragging) {
|
|
1277
1419
|
return;
|
|
1278
1420
|
}
|
|
@@ -1295,30 +1437,129 @@ class DragDropBlockToolbar extends Plugin {
|
|
|
1295
1437
|
});
|
|
1296
1438
|
}
|
|
1297
1439
|
/**
|
|
1298
|
-
|
|
1299
|
-
|
|
1440
|
+
* The `dragend` event handler.
|
|
1441
|
+
*/ _handleBlockDragEnd() {
|
|
1300
1442
|
this._isBlockDragging = false;
|
|
1301
1443
|
}
|
|
1302
|
-
constructor(){
|
|
1303
|
-
super(...arguments);
|
|
1304
|
-
/**
|
|
1305
|
-
* Whether current dragging is started by block toolbar button dragging.
|
|
1306
|
-
*/ this._isBlockDragging = false;
|
|
1307
|
-
/**
|
|
1308
|
-
* DOM Emitter.
|
|
1309
|
-
*/ this._domEmitter = new (DomEmitterMixin())();
|
|
1310
|
-
}
|
|
1311
1444
|
}
|
|
1312
1445
|
|
|
1313
|
-
|
|
1446
|
+
// Drag and drop events overview:
|
|
1447
|
+
//
|
|
1448
|
+
// ┌──────────────────┐
|
|
1449
|
+
// │ mousedown │ Sets the draggable attribute.
|
|
1450
|
+
// └─────────┬────────┘
|
|
1451
|
+
// │
|
|
1452
|
+
// └─────────────────────┐
|
|
1453
|
+
// │ │
|
|
1454
|
+
// │ ┌─────────V────────┐
|
|
1455
|
+
// │ │ mouseup │ Dragging did not start, removes the draggable attribute.
|
|
1456
|
+
// │ └──────────────────┘
|
|
1457
|
+
// │
|
|
1458
|
+
// ┌─────────V────────┐ Retrieves the selected model.DocumentFragment
|
|
1459
|
+
// │ dragstart │ and converts it to view.DocumentFragment.
|
|
1460
|
+
// └─────────┬────────┘
|
|
1461
|
+
// │
|
|
1462
|
+
// ┌─────────V────────┐ Processes view.DocumentFragment to text/html and text/plain
|
|
1463
|
+
// │ clipboardOutput │ and stores the results in data.dataTransfer.
|
|
1464
|
+
// └─────────┬────────┘
|
|
1465
|
+
// │
|
|
1466
|
+
// │ DOM dragover
|
|
1467
|
+
// ┌────────────┐
|
|
1468
|
+
// │ │
|
|
1469
|
+
// ┌─────────V────────┐ │
|
|
1470
|
+
// │ dragging │ │ Updates the drop target marker.
|
|
1471
|
+
// └─────────┬────────┘ │
|
|
1472
|
+
// │ │
|
|
1473
|
+
// ┌─────────────└────────────┘
|
|
1474
|
+
// │ │ │
|
|
1475
|
+
// │ ┌─────────V────────┐ │
|
|
1476
|
+
// │ │ dragleave │ │ Removes the drop target marker.
|
|
1477
|
+
// │ └─────────┬────────┘ │
|
|
1478
|
+
// │ │ │
|
|
1479
|
+
// ┌───│─────────────┘ │
|
|
1480
|
+
// │ │ │ │
|
|
1481
|
+
// │ │ ┌─────────V────────┐ │
|
|
1482
|
+
// │ │ │ dragenter │ │ Focuses the editor view.
|
|
1483
|
+
// │ │ └─────────┬────────┘ │
|
|
1484
|
+
// │ │ │ │
|
|
1485
|
+
// │ │ └────────────┘
|
|
1486
|
+
// │ │
|
|
1487
|
+
// │ └─────────────┐
|
|
1488
|
+
// │ │ │
|
|
1489
|
+
// │ │ ┌─────────V────────┐
|
|
1490
|
+
// └───┐ │ drop │ (The default handler of the clipboard pipeline).
|
|
1491
|
+
// │ └─────────┬────────┘
|
|
1492
|
+
// │ │
|
|
1493
|
+
// │ ┌─────────V────────┐ Resolves the final data.targetRanges.
|
|
1494
|
+
// │ │ clipboardInput │ Aborts if dropping on dragged content.
|
|
1495
|
+
// │ └─────────┬────────┘
|
|
1496
|
+
// │ │
|
|
1497
|
+
// │ ┌─────────V────────┐
|
|
1498
|
+
// │ │ clipboardInput │ (The default handler of the clipboard pipeline).
|
|
1499
|
+
// │ └─────────┬────────┘
|
|
1500
|
+
// │ │
|
|
1501
|
+
// │ ┌───────────V───────────┐
|
|
1502
|
+
// │ │ inputTransformation │ (The default handler of the clipboard pipeline).
|
|
1503
|
+
// │ └───────────┬───────────┘
|
|
1504
|
+
// │ │
|
|
1505
|
+
// │ ┌──────────V──────────┐
|
|
1506
|
+
// │ │ contentInsertion │ Updates the document selection to drop range.
|
|
1507
|
+
// │ └──────────┬──────────┘
|
|
1508
|
+
// │ │
|
|
1509
|
+
// │ ┌──────────V──────────┐
|
|
1510
|
+
// │ │ contentInsertion │ (The default handler of the clipboard pipeline).
|
|
1511
|
+
// │ └──────────┬──────────┘
|
|
1512
|
+
// │ │
|
|
1513
|
+
// │ ┌──────────V──────────┐
|
|
1514
|
+
// │ │ contentInsertion │ Removes the content from the original range if the insertion was successful.
|
|
1515
|
+
// │ └──────────┬──────────┘
|
|
1516
|
+
// │ │
|
|
1517
|
+
// └─────────────┐
|
|
1518
|
+
// │
|
|
1519
|
+
// ┌─────────V────────┐
|
|
1520
|
+
// │ dragend │ Removes the drop marker and cleans the state.
|
|
1521
|
+
// └──────────────────┘
|
|
1522
|
+
//
|
|
1523
|
+
/**
|
|
1524
|
+
* The drag and drop feature. It works on top of the {@link module:clipboard/clipboardpipeline~ClipboardPipeline}.
|
|
1525
|
+
*
|
|
1526
|
+
* Read more about the clipboard integration in the {@glink framework/deep-dive/clipboard clipboard deep-dive} guide.
|
|
1527
|
+
*
|
|
1528
|
+
* @internal
|
|
1529
|
+
*/ class DragDrop extends Plugin {
|
|
1530
|
+
/**
|
|
1531
|
+
* The live range over the original content that is being dragged.
|
|
1532
|
+
*/ _draggedRange;
|
|
1533
|
+
/**
|
|
1534
|
+
* The UID of current dragging that is used to verify if the drop started in the same editor as the drag start.
|
|
1535
|
+
*
|
|
1536
|
+
* **Note**: This is a workaround for broken 'dragend' events (they are not fired if the source text node got removed).
|
|
1537
|
+
*/ _draggingUid;
|
|
1538
|
+
/**
|
|
1539
|
+
* The reference to the model element that currently has a `draggable` attribute set (it is set while dragging).
|
|
1540
|
+
*/ _draggableElement;
|
|
1541
|
+
/**
|
|
1542
|
+
* A delayed callback removing draggable attributes.
|
|
1543
|
+
*/ _clearDraggableAttributesDelayed = delay(()=>this._clearDraggableAttributes(), 40);
|
|
1314
1544
|
/**
|
|
1315
|
-
|
|
1316
|
-
|
|
1545
|
+
* Whether the dragged content can be dropped only in block context.
|
|
1546
|
+
*/ // TODO handle drag from other editor instance
|
|
1547
|
+
// TODO configure to use block, inline or both
|
|
1548
|
+
_blockMode = false;
|
|
1549
|
+
/**
|
|
1550
|
+
* DOM Emitter.
|
|
1551
|
+
*/ _domEmitter = new (DomEmitterMixin())();
|
|
1552
|
+
/**
|
|
1553
|
+
* The DOM element used to generate dragged preview image.
|
|
1554
|
+
*/ _previewContainer;
|
|
1555
|
+
/**
|
|
1556
|
+
* @inheritDoc
|
|
1557
|
+
*/ static get pluginName() {
|
|
1317
1558
|
return 'DragDrop';
|
|
1318
1559
|
}
|
|
1319
1560
|
/**
|
|
1320
|
-
|
|
1321
|
-
|
|
1561
|
+
* @inheritDoc
|
|
1562
|
+
*/ static get requires() {
|
|
1322
1563
|
return [
|
|
1323
1564
|
ClipboardPipeline,
|
|
1324
1565
|
Widget,
|
|
@@ -1327,8 +1568,8 @@ class DragDrop extends Plugin {
|
|
|
1327
1568
|
];
|
|
1328
1569
|
}
|
|
1329
1570
|
/**
|
|
1330
|
-
|
|
1331
|
-
|
|
1571
|
+
* @inheritDoc
|
|
1572
|
+
*/ init() {
|
|
1332
1573
|
const editor = this.editor;
|
|
1333
1574
|
const view = editor.editing.view;
|
|
1334
1575
|
this._draggedRange = null;
|
|
@@ -1357,8 +1598,8 @@ class DragDrop extends Plugin {
|
|
|
1357
1598
|
}
|
|
1358
1599
|
}
|
|
1359
1600
|
/**
|
|
1360
|
-
|
|
1361
|
-
|
|
1601
|
+
* @inheritDoc
|
|
1602
|
+
*/ destroy() {
|
|
1362
1603
|
if (this._draggedRange) {
|
|
1363
1604
|
this._draggedRange.detach();
|
|
1364
1605
|
this._draggedRange = null;
|
|
@@ -1371,8 +1612,8 @@ class DragDrop extends Plugin {
|
|
|
1371
1612
|
return super.destroy();
|
|
1372
1613
|
}
|
|
1373
1614
|
/**
|
|
1374
|
-
|
|
1375
|
-
|
|
1615
|
+
* Drag and drop events handling.
|
|
1616
|
+
*/ _setupDragging() {
|
|
1376
1617
|
const editor = this.editor;
|
|
1377
1618
|
const model = editor.model;
|
|
1378
1619
|
const view = editor.editing.view;
|
|
@@ -1469,8 +1710,8 @@ class DragDrop extends Plugin {
|
|
|
1469
1710
|
});
|
|
1470
1711
|
}
|
|
1471
1712
|
/**
|
|
1472
|
-
|
|
1473
|
-
|
|
1713
|
+
* Integration with the `clipboardInput` event.
|
|
1714
|
+
*/ _setupClipboardInputIntegration() {
|
|
1474
1715
|
const editor = this.editor;
|
|
1475
1716
|
const view = editor.editing.view;
|
|
1476
1717
|
const viewDocument = view.document;
|
|
@@ -1510,8 +1751,8 @@ class DragDrop extends Plugin {
|
|
|
1510
1751
|
});
|
|
1511
1752
|
}
|
|
1512
1753
|
/**
|
|
1513
|
-
|
|
1514
|
-
|
|
1754
|
+
* Integration with the `contentInsertion` event of the clipboard pipeline.
|
|
1755
|
+
*/ _setupContentInsertionIntegration() {
|
|
1515
1756
|
const clipboardPipeline = this.editor.plugins.get(ClipboardPipeline);
|
|
1516
1757
|
clipboardPipeline.on('contentInsertion', (evt, data)=>{
|
|
1517
1758
|
if (!this.isEnabled || data.method !== 'drop') {
|
|
@@ -1540,8 +1781,8 @@ class DragDrop extends Plugin {
|
|
|
1540
1781
|
});
|
|
1541
1782
|
}
|
|
1542
1783
|
/**
|
|
1543
|
-
|
|
1544
|
-
|
|
1784
|
+
* Adds listeners that add the `draggable` attribute to the elements while the mouse button is down so the dragging could start.
|
|
1785
|
+
*/ _setupDraggableAttributeHandling() {
|
|
1545
1786
|
const editor = this.editor;
|
|
1546
1787
|
const view = editor.editing.view;
|
|
1547
1788
|
const viewDocument = view.document;
|
|
@@ -1585,8 +1826,8 @@ class DragDrop extends Plugin {
|
|
|
1585
1826
|
});
|
|
1586
1827
|
}
|
|
1587
1828
|
/**
|
|
1588
|
-
|
|
1589
|
-
|
|
1829
|
+
* Removes the `draggable` attribute from the element that was used for dragging.
|
|
1830
|
+
*/ _clearDraggableAttributes() {
|
|
1590
1831
|
const editing = this.editor.editing;
|
|
1591
1832
|
editing.view.change((writer)=>{
|
|
1592
1833
|
// Remove 'draggable' attribute.
|
|
@@ -1597,10 +1838,10 @@ class DragDrop extends Plugin {
|
|
|
1597
1838
|
});
|
|
1598
1839
|
}
|
|
1599
1840
|
/**
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1841
|
+
* Deletes the dragged content from its original range and clears the dragging state.
|
|
1842
|
+
*
|
|
1843
|
+
* @param moved Whether the move succeeded.
|
|
1844
|
+
*/ _finalizeDragging(moved) {
|
|
1604
1845
|
const editor = this.editor;
|
|
1605
1846
|
const model = editor.model;
|
|
1606
1847
|
const dragDropTarget = editor.plugins.get(DragDropTarget);
|
|
@@ -1636,8 +1877,8 @@ class DragDrop extends Plugin {
|
|
|
1636
1877
|
this._draggedRange = null;
|
|
1637
1878
|
}
|
|
1638
1879
|
/**
|
|
1639
|
-
|
|
1640
|
-
|
|
1880
|
+
* Sets the dragged source range based on event target and document selection.
|
|
1881
|
+
*/ _prepareDraggedRange(target) {
|
|
1641
1882
|
const editor = this.editor;
|
|
1642
1883
|
const model = editor.model;
|
|
1643
1884
|
const selection = model.document.selection;
|
|
@@ -1677,8 +1918,8 @@ class DragDrop extends Plugin {
|
|
|
1677
1918
|
model.change((writer)=>writer.setSelection(this._draggedRange.toRange()));
|
|
1678
1919
|
}
|
|
1679
1920
|
/**
|
|
1680
|
-
|
|
1681
|
-
|
|
1921
|
+
* Updates the dragged preview image.
|
|
1922
|
+
*/ _updatePreview({ dataTransfer, domTarget, clientX }) {
|
|
1682
1923
|
const view = this.editor.editing.view;
|
|
1683
1924
|
const editable = view.document.selection.editableElement;
|
|
1684
1925
|
const domEditable = view.domConverter.mapViewToDom(editable);
|
|
@@ -1702,29 +1943,15 @@ class DragDrop extends Plugin {
|
|
|
1702
1943
|
preview.style.width = computedStyle.width;
|
|
1703
1944
|
preview.style.paddingLeft = `${domRect.left - clientX + domEditablePaddingLeft}px`;
|
|
1704
1945
|
/**
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1946
|
+
* Set white background in drag and drop preview if iOS.
|
|
1947
|
+
* Check: https://github.com/ckeditor/ckeditor5/issues/15085
|
|
1948
|
+
*/ if (env.isiOS) {
|
|
1708
1949
|
preview.style.backgroundColor = 'white';
|
|
1709
1950
|
}
|
|
1710
1951
|
preview.innerHTML = dataTransfer.getData('text/html');
|
|
1711
1952
|
dataTransfer.setDragImage(preview, 0, 0);
|
|
1712
1953
|
this._previewContainer.appendChild(preview);
|
|
1713
1954
|
}
|
|
1714
|
-
constructor(){
|
|
1715
|
-
super(...arguments);
|
|
1716
|
-
/**
|
|
1717
|
-
* A delayed callback removing draggable attributes.
|
|
1718
|
-
*/ this._clearDraggableAttributesDelayed = delay(()=>this._clearDraggableAttributes(), 40);
|
|
1719
|
-
/**
|
|
1720
|
-
* Whether the dragged content can be dropped only in block context.
|
|
1721
|
-
*/ // TODO handle drag from other editor instance
|
|
1722
|
-
// TODO configure to use block, inline or both
|
|
1723
|
-
this._blockMode = false;
|
|
1724
|
-
/**
|
|
1725
|
-
* DOM Emitter.
|
|
1726
|
-
*/ this._domEmitter = new (DomEmitterMixin())();
|
|
1727
|
-
}
|
|
1728
1955
|
}
|
|
1729
1956
|
/**
|
|
1730
1957
|
* Returns the drop effect that should be a result of dragging the content.
|
|
@@ -1795,22 +2022,26 @@ class DragDrop extends Plugin {
|
|
|
1795
2022
|
return model.createRange(startPosition, endPosition);
|
|
1796
2023
|
}
|
|
1797
2024
|
|
|
1798
|
-
|
|
2025
|
+
/**
|
|
2026
|
+
* The plugin detects the user's intention to paste plain text.
|
|
2027
|
+
*
|
|
2028
|
+
* For example, it detects the <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>V</kbd> keystroke.
|
|
2029
|
+
*/ class PastePlainText extends Plugin {
|
|
1799
2030
|
/**
|
|
1800
|
-
|
|
1801
|
-
|
|
2031
|
+
* @inheritDoc
|
|
2032
|
+
*/ static get pluginName() {
|
|
1802
2033
|
return 'PastePlainText';
|
|
1803
2034
|
}
|
|
1804
2035
|
/**
|
|
1805
|
-
|
|
1806
|
-
|
|
2036
|
+
* @inheritDoc
|
|
2037
|
+
*/ static get requires() {
|
|
1807
2038
|
return [
|
|
1808
2039
|
ClipboardPipeline
|
|
1809
2040
|
];
|
|
1810
2041
|
}
|
|
1811
2042
|
/**
|
|
1812
|
-
|
|
1813
|
-
|
|
2043
|
+
* @inheritDoc
|
|
2044
|
+
*/ init() {
|
|
1814
2045
|
const editor = this.editor;
|
|
1815
2046
|
const model = editor.model;
|
|
1816
2047
|
const view = editor.editing.view;
|
|
@@ -1862,15 +2093,24 @@ class PastePlainText extends Plugin {
|
|
|
1862
2093
|
return Array.from(child.getAttributeKeys()).length == 0;
|
|
1863
2094
|
}
|
|
1864
2095
|
|
|
1865
|
-
|
|
2096
|
+
/**
|
|
2097
|
+
* The clipboard feature.
|
|
2098
|
+
*
|
|
2099
|
+
* Read more about the clipboard integration in the {@glink framework/deep-dive/clipboard clipboard deep-dive} guide.
|
|
2100
|
+
*
|
|
2101
|
+
* This is a "glue" plugin which loads the following plugins:
|
|
2102
|
+
* * {@link module:clipboard/clipboardpipeline~ClipboardPipeline}
|
|
2103
|
+
* * {@link module:clipboard/dragdrop~DragDrop}
|
|
2104
|
+
* * {@link module:clipboard/pasteplaintext~PastePlainText}
|
|
2105
|
+
*/ class Clipboard extends Plugin {
|
|
1866
2106
|
/**
|
|
1867
|
-
|
|
1868
|
-
|
|
2107
|
+
* @inheritDoc
|
|
2108
|
+
*/ static get pluginName() {
|
|
1869
2109
|
return 'Clipboard';
|
|
1870
2110
|
}
|
|
1871
2111
|
/**
|
|
1872
|
-
|
|
1873
|
-
|
|
2112
|
+
* @inheritDoc
|
|
2113
|
+
*/ static get requires() {
|
|
1874
2114
|
return [
|
|
1875
2115
|
ClipboardMarkersUtils,
|
|
1876
2116
|
ClipboardPipeline,
|
|
@@ -1879,8 +2119,8 @@ class Clipboard extends Plugin {
|
|
|
1879
2119
|
];
|
|
1880
2120
|
}
|
|
1881
2121
|
/**
|
|
1882
|
-
|
|
1883
|
-
|
|
2122
|
+
* @inheritDoc
|
|
2123
|
+
*/ init() {
|
|
1884
2124
|
const editor = this.editor;
|
|
1885
2125
|
const t = this.editor.t;
|
|
1886
2126
|
// Add the information about the keystrokes to the accessibility database.
|