11import { htmlEscape } from 'escape-goat' ;
22import { POST } from '../../modules/fetch.js' ;
33import { imageInfo } from '../../utils/image.js' ;
4+ import { getPastedContent , replaceTextareaSelection } from '../../utils/dom.js' ;
5+ import { isUrl } from '../../utils/url.js' ;
46
57async function uploadFile ( file , uploadUrl ) {
68 const formData = new FormData ( ) ;
@@ -10,17 +12,6 @@ async function uploadFile(file, uploadUrl) {
1012 return await res . json ( ) ;
1113}
1214
13- function clipboardPastedImages ( e ) {
14- if ( ! e . clipboardData ) return [ ] ;
15-
16- const files = [ ] ;
17- for ( const item of e . clipboardData . items || [ ] ) {
18- if ( ! item . type || ! item . type . startsWith ( 'image/' ) ) continue ;
19- files . push ( item . getAsFile ( ) ) ;
20- }
21- return files ;
22- }
23-
2415function triggerEditorContentChanged ( target ) {
2516 target . dispatchEvent ( new CustomEvent ( 'ce-editor-content-changed' , { bubbles : true } ) ) ;
2617}
@@ -91,20 +82,16 @@ class CodeMirrorEditor {
9182 }
9283}
9384
94- const uploadClipboardImage = async ( editor , dropzone , e ) => {
85+ async function handleClipboardImages ( editor , dropzone , images , e ) {
9586 const uploadUrl = dropzone . getAttribute ( 'data-upload-url' ) ;
9687 const filesContainer = dropzone . querySelector ( '.files' ) ;
9788
98- if ( ! uploadUrl || ! filesContainer ) return ;
89+ if ( ! dropzone || ! uploadUrl || ! filesContainer || ! images . length ) return ;
9990
100- const pastedImages = clipboardPastedImages ( e ) ;
101- if ( ! pastedImages || pastedImages . length === 0 ) {
102- return ;
103- }
10491 e . preventDefault ( ) ;
10592 e . stopPropagation ( ) ;
10693
107- for ( const img of pastedImages ) {
94+ for ( const img of images ) {
10895 const name = img . name . slice ( 0 , img . name . lastIndexOf ( '.' ) ) ;
10996
11097 const placeholder = `` ;
@@ -131,18 +118,38 @@ const uploadClipboardImage = async (editor, dropzone, e) => {
131118 input . value = uuid ;
132119 filesContainer . append ( input ) ;
133120 }
134- } ;
121+ }
122+
123+ function handleClipboardText ( textarea , text , e ) {
124+ // when pasting links over selected text, turn it into [text](link), except when shift key is held
125+ const { value, selectionStart, selectionEnd, _giteaShiftDown} = textarea ;
126+ if ( _giteaShiftDown ) return ;
127+ const selectedText = value . substring ( selectionStart , selectionEnd ) ;
128+ const trimmedText = text . trim ( ) ;
129+ if ( selectedText && isUrl ( trimmedText ) ) {
130+ e . stopPropagation ( ) ;
131+ e . preventDefault ( ) ;
132+ replaceTextareaSelection ( textarea , `[${ selectedText } ](${ trimmedText } )` ) ;
133+ }
134+ }
135135
136136export function initEasyMDEImagePaste ( easyMDE , dropzone ) {
137- if ( ! dropzone ) return ;
138- easyMDE . codemirror . on ( 'paste' , async ( _ , e ) => {
139- return uploadClipboardImage ( new CodeMirrorEditor ( easyMDE . codemirror ) , dropzone , e ) ;
137+ easyMDE . codemirror . on ( 'paste' , ( _ , e ) => {
138+ const { images} = getPastedContent ( e ) ;
139+ if ( images . length ) {
140+ handleClipboardImages ( new CodeMirrorEditor ( easyMDE . codemirror ) , dropzone , images , e ) ;
141+ }
140142 } ) ;
141143}
142144
143145export function initTextareaImagePaste ( textarea , dropzone ) {
144- if ( ! dropzone ) return ;
145- textarea . addEventListener ( 'paste' , async ( e ) => {
146- return uploadClipboardImage ( new TextareaEditor ( textarea ) , dropzone , e ) ;
146+ textarea . addEventListener ( 'paste' , ( e ) => {
147+ const { images, text} = getPastedContent ( e ) ;
148+ if ( text ) {
149+ handleClipboardText ( textarea , text , e ) ;
150+ }
151+ if ( images . length ) {
152+ handleClipboardImages ( new TextareaEditor ( textarea ) , dropzone , images , e ) ;
153+ }
147154 } ) ;
148155}
0 commit comments