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  }