code.gitea.io/gitea@v1.21.7/web_src/js/features/comp/ImagePaste.js (about) 1 import $ from 'jquery'; 2 import {POST} from '../../modules/fetch.js'; 3 4 async function uploadFile(file, uploadUrl) { 5 const formData = new FormData(); 6 formData.append('file', file, file.name); 7 8 const res = await POST(uploadUrl, {data: formData}); 9 return await res.json(); 10 } 11 12 function clipboardPastedImages(e) { 13 if (!e.clipboardData) return []; 14 15 const files = []; 16 for (const item of e.clipboardData.items || []) { 17 if (!item.type || !item.type.startsWith('image/')) continue; 18 files.push(item.getAsFile()); 19 } 20 return files; 21 } 22 23 function triggerEditorContentChanged(target) { 24 target.dispatchEvent(new CustomEvent('ce-editor-content-changed', {bubbles: true})); 25 } 26 27 class TextareaEditor { 28 constructor(editor) { 29 this.editor = editor; 30 } 31 32 insertPlaceholder(value) { 33 const editor = this.editor; 34 const startPos = editor.selectionStart; 35 const endPos = editor.selectionEnd; 36 editor.value = editor.value.substring(0, startPos) + value + editor.value.substring(endPos); 37 editor.selectionStart = startPos; 38 editor.selectionEnd = startPos + value.length; 39 editor.focus(); 40 triggerEditorContentChanged(editor); 41 } 42 43 replacePlaceholder(oldVal, newVal) { 44 const editor = this.editor; 45 const startPos = editor.selectionStart; 46 const endPos = editor.selectionEnd; 47 if (editor.value.substring(startPos, endPos) === oldVal) { 48 editor.value = editor.value.substring(0, startPos) + newVal + editor.value.substring(endPos); 49 editor.selectionEnd = startPos + newVal.length; 50 } else { 51 editor.value = editor.value.replace(oldVal, newVal); 52 editor.selectionEnd -= oldVal.length; 53 editor.selectionEnd += newVal.length; 54 } 55 editor.selectionStart = editor.selectionEnd; 56 editor.focus(); 57 triggerEditorContentChanged(editor); 58 } 59 } 60 61 class CodeMirrorEditor { 62 constructor(editor) { 63 this.editor = editor; 64 } 65 66 insertPlaceholder(value) { 67 const editor = this.editor; 68 const startPoint = editor.getCursor('start'); 69 const endPoint = editor.getCursor('end'); 70 editor.replaceSelection(value); 71 endPoint.ch = startPoint.ch + value.length; 72 editor.setSelection(startPoint, endPoint); 73 editor.focus(); 74 triggerEditorContentChanged(editor.getTextArea()); 75 } 76 77 replacePlaceholder(oldVal, newVal) { 78 const editor = this.editor; 79 const endPoint = editor.getCursor('end'); 80 if (editor.getSelection() === oldVal) { 81 editor.replaceSelection(newVal); 82 } else { 83 editor.setValue(editor.getValue().replace(oldVal, newVal)); 84 } 85 endPoint.ch -= oldVal.length; 86 endPoint.ch += newVal.length; 87 editor.setSelection(endPoint, endPoint); 88 editor.focus(); 89 triggerEditorContentChanged(editor.getTextArea()); 90 } 91 } 92 93 94 const uploadClipboardImage = async (editor, dropzone, e) => { 95 const $dropzone = $(dropzone); 96 const uploadUrl = $dropzone.attr('data-upload-url'); 97 const $files = $dropzone.find('.files'); 98 99 if (!uploadUrl || !$files.length) return; 100 101 const pastedImages = clipboardPastedImages(e); 102 if (!pastedImages || pastedImages.length === 0) { 103 return; 104 } 105 e.preventDefault(); 106 e.stopPropagation(); 107 108 for (const img of pastedImages) { 109 const name = img.name.slice(0, img.name.lastIndexOf('.')); 110 111 const placeholder = `![${name}](uploading ...)`; 112 editor.insertPlaceholder(placeholder); 113 const data = await uploadFile(img, uploadUrl); 114 editor.replacePlaceholder(placeholder, `![${name}](/attachments/${data.uuid})`); 115 116 const $input = $(`<input name="files" type="hidden">`).attr('id', data.uuid).val(data.uuid); 117 $files.append($input); 118 } 119 }; 120 121 export function initEasyMDEImagePaste(easyMDE, dropzone) { 122 if (!dropzone) return; 123 easyMDE.codemirror.on('paste', async (_, e) => { 124 return uploadClipboardImage(new CodeMirrorEditor(easyMDE.codemirror), dropzone, e); 125 }); 126 } 127 128 export function initTextareaImagePaste(textarea, dropzone) { 129 if (!dropzone) return; 130 $(textarea).on('paste', async (e) => { 131 return uploadClipboardImage(new TextareaEditor(textarea), dropzone, e.originalEvent); 132 }); 133 }