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