1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | import { Plugin } from 'ckeditor5/src/core';
|
9 | import { LiveRange, LivePosition } from 'ckeditor5/src/engine';
|
10 | import { Clipboard } from 'ckeditor5/src/clipboard';
|
11 | import { Delete } from 'ckeditor5/src/typing';
|
12 | import { Undo } from 'ckeditor5/src/undo';
|
13 | import { global } from 'ckeditor5/src/utils';
|
14 | import MediaEmbedEditing from './mediaembedediting';
|
15 | import { insertMedia } from './utils';
|
16 | const URL_REGEXP = /^(?:http(s)?:\/\/)?[\w-]+\.[\w-.~:/?#[\]@!$&'()*+,;=%]+$/;
|
17 |
|
18 |
|
19 |
|
20 |
|
21 | export default class AutoMediaEmbed extends Plugin {
|
22 | |
23 |
|
24 |
|
25 | static get requires() {
|
26 | return [Clipboard, Delete, Undo];
|
27 | }
|
28 | |
29 |
|
30 |
|
31 | static get pluginName() {
|
32 | return 'AutoMediaEmbed';
|
33 | }
|
34 | |
35 |
|
36 |
|
37 | constructor(editor) {
|
38 | super(editor);
|
39 | this._timeoutId = null;
|
40 | this._positionToInsert = null;
|
41 | }
|
42 | |
43 |
|
44 |
|
45 | init() {
|
46 | const editor = this.editor;
|
47 | const modelDocument = editor.model.document;
|
48 |
|
49 |
|
50 |
|
51 | const clipboardPipeline = editor.plugins.get('ClipboardPipeline');
|
52 | this.listenTo(clipboardPipeline, 'inputTransformation', () => {
|
53 | const firstRange = modelDocument.selection.getFirstRange();
|
54 | const leftLivePosition = LivePosition.fromPosition(firstRange.start);
|
55 | leftLivePosition.stickiness = 'toPrevious';
|
56 | const rightLivePosition = LivePosition.fromPosition(firstRange.end);
|
57 | rightLivePosition.stickiness = 'toNext';
|
58 | modelDocument.once('change:data', () => {
|
59 | this._embedMediaBetweenPositions(leftLivePosition, rightLivePosition);
|
60 | leftLivePosition.detach();
|
61 | rightLivePosition.detach();
|
62 | }, { priority: 'high' });
|
63 | });
|
64 | const undoCommand = editor.commands.get('undo');
|
65 | undoCommand.on('execute', () => {
|
66 | if (this._timeoutId) {
|
67 | global.window.clearTimeout(this._timeoutId);
|
68 | this._positionToInsert.detach();
|
69 | this._timeoutId = null;
|
70 | this._positionToInsert = null;
|
71 | }
|
72 | }, { priority: 'high' });
|
73 | }
|
74 | |
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 | _embedMediaBetweenPositions(leftPosition, rightPosition) {
|
82 | const editor = this.editor;
|
83 | const mediaRegistry = editor.plugins.get(MediaEmbedEditing).registry;
|
84 |
|
85 | const urlRange = new LiveRange(leftPosition, rightPosition);
|
86 | const walker = urlRange.getWalker({ ignoreElementEnd: true });
|
87 | let url = '';
|
88 | for (const node of walker) {
|
89 | if (node.item.is('$textProxy')) {
|
90 | url += node.item.data;
|
91 | }
|
92 | }
|
93 | url = url.trim();
|
94 |
|
95 | if (!url.match(URL_REGEXP)) {
|
96 | urlRange.detach();
|
97 | return;
|
98 | }
|
99 |
|
100 | if (!mediaRegistry.hasMedia(url)) {
|
101 | urlRange.detach();
|
102 | return;
|
103 | }
|
104 | const mediaEmbedCommand = editor.commands.get('mediaEmbed');
|
105 |
|
106 | if (!mediaEmbedCommand.isEnabled) {
|
107 | urlRange.detach();
|
108 | return;
|
109 | }
|
110 |
|
111 | this._positionToInsert = LivePosition.fromPosition(leftPosition);
|
112 |
|
113 | this._timeoutId = global.window.setTimeout(() => {
|
114 | editor.model.change(writer => {
|
115 | this._timeoutId = null;
|
116 | writer.remove(urlRange);
|
117 | urlRange.detach();
|
118 | let insertionPosition = null;
|
119 |
|
120 |
|
121 | if (this._positionToInsert.root.rootName !== '$graveyard') {
|
122 | insertionPosition = this._positionToInsert;
|
123 | }
|
124 | insertMedia(editor.model, url, insertionPosition, false);
|
125 | this._positionToInsert.detach();
|
126 | this._positionToInsert = null;
|
127 | });
|
128 | editor.plugins.get(Delete).requestUndoOnBackspace();
|
129 | }, 100);
|
130 | }
|
131 | }
|