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