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