]> BookStack Code Mirror - bookstack/commitdiff
Better standardised and fixes areas of image pasting
authorDan Brown <redacted>
Sat, 21 Dec 2019 15:48:03 +0000 (15:48 +0000)
committerDan Brown <redacted>
Sat, 21 Dec 2019 15:48:03 +0000 (15:48 +0000)
- Extracted logic to get images from paste/drop event into own file to
align usage in both events for both editors.
- Fixed non-ability to drag+drop into WYSIWYG editor.
- Updated check for table data to look for table specific rich-text
instead of just any text since some the old check was too general and
was preventing some legitimate image paste events.

Tested on Chrome and FireFox on Ubuntu.
Attempted to test on Safari via browserstack but environment was
unreliable and could not access folders to test drag/drop of files.

Relates to #1651 and #1697

resources/js/components/markdown-editor.js
resources/js/components/wysiwyg-editor.js
resources/js/services/clipboard.js [new file with mode: 0644]

index de256a8466a727add893f5370167f3db4ef34320..331cf2f01a9321d77d742eadd51cdc0ad77f1608 100644 (file)
@@ -1,6 +1,7 @@
 import MarkdownIt from "markdown-it";
 import mdTasksLists from 'markdown-it-task-lists';
 import code from '../services/code';
+import Clipboard from "../services/clipboard";
 import {debounce} from "../services/util";
 
 import DrawIO from "../services/drawio";
@@ -215,20 +216,16 @@ class MarkdownEditor {
 
         // Handle image paste
         cm.on('paste', (cm, event) => {
-            const clipboardItems = event.clipboardData.items;
-            if (!event.clipboardData || !clipboardItems) return;
+            const clipboard = new Clipboard(event.clipboardData || event.dataTransfer);
 
-            // Don't handle if clipboard includes text content
-            for (let clipboardItem of clipboardItems) {
-                if (clipboardItem.type.includes('text/')) {
-                    return;
-                }
+            // Don't handle the event ourselves if no items exist of contains table-looking data
+            if (!clipboard.hasItems() || clipboard.containsTabularData()) {
+                return;
             }
 
-            for (let clipboardItem of clipboardItems) {
-                if (clipboardItem.type.includes("image")) {
-                    uploadImage(clipboardItem.getAsFile());
-                }
+            const images = clipboard.getImages();
+            for (const image of images) {
+                uploadImage(image);
             }
         });
 
@@ -246,13 +243,15 @@ class MarkdownEditor {
                 });
             }
 
-            if (event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files.length > 0) {
+            const clipboard = new Clipboard(event.dataTransfer);
+            if (clipboard.hasItems()) {
                 const cursorPos = cm.coordsChar({left: event.pageX, top: event.pageY});
                 cm.setCursor(cursorPos);
                 event.stopPropagation();
                 event.preventDefault();
-                for (let i = 0; i < event.dataTransfer.files.length; i++) {
-                    uploadImage(event.dataTransfer.files[i]);
+                const images = clipboard.getImages();
+                for (const image of images) {
+                    uploadImage(image);
                 }
             }
 
index 41ce2705af2cc8dae65d14333c2b75579b378755..b9e3340a8275358db491fa9e257354be5631be51 100644 (file)
@@ -1,5 +1,6 @@
 import Code from "../services/code";
 import DrawIO from "../services/drawio";
+import Clipboard from "../services/clipboard";
 
 /**
  * Handle pasting images from clipboard.
@@ -8,30 +9,33 @@ import DrawIO from "../services/drawio";
  * @param editor
  */
 function editorPaste(event, editor, wysiwygComponent) {
-    const clipboardItems = event.clipboardData.items;
-    if (!event.clipboardData || !clipboardItems) return;
+    const clipboard = new Clipboard(event.clipboardData || event.dataTransfer);
 
-    // Don't handle if clipboard includes text content
-    for (let clipboardItem of clipboardItems) {
-        if (clipboardItem.type.includes('text/')) {
-            return;
-        }
+    // Don't handle the event ourselves if no items exist of contains table-looking data
+    if (!clipboard.hasItems() || clipboard.containsTabularData()) {
+        return;
     }
 
-    for (let clipboardItem of clipboardItems) {
-        if (!clipboardItem.type.includes("image")) {
-            continue;
-        }
+    const images = clipboard.getImages();
+    for (const imageFile of images) {
 
         const id = "image-" + Math.random().toString(16).slice(2);
         const loadingImage = window.baseUrl('/loading.gif');
-        const file = clipboardItem.getAsFile();
+        event.preventDefault();
 
         setTimeout(() => {
             editor.insertContent(`<p><img src="${loadingImage}" id="${id}"></p>`);
 
-            uploadImageFile(file, wysiwygComponent).then(resp => {
-                editor.dom.setAttrib(id, 'src', resp.thumbs.display);
+            uploadImageFile(imageFile, wysiwygComponent).then(resp => {
+                const safeName = resp.name.replace(/"/g, '');
+                const newImageHtml = `<img src="${resp.thumbs.display}" alt="${safeName}" />`;
+
+                const newEl = editor.dom.create('a', {
+                    target: '_blank',
+                    href: resp.url,
+                }, newImageHtml);
+
+                editor.dom.replace(newEl, id);
             }).catch(err => {
                 editor.dom.remove(id);
                 window.$events.emit('error', trans('errors.image_upload_error'));
@@ -634,6 +638,10 @@ class WysiwygEditor {
                         });
                     }
 
+                    if (!event.isDefaultPrevented()) {
+                        editorPaste(event, editor, context);
+                    }
+
                     wrap = null;
                 });
 
diff --git a/resources/js/services/clipboard.js b/resources/js/services/clipboard.js
new file mode 100644 (file)
index 0000000..da921e5
--- /dev/null
@@ -0,0 +1,54 @@
+
+class Clipboard {
+
+    /**
+     * Constructor
+     * @param {DataTransfer} clipboardData
+     */
+    constructor(clipboardData) {
+        this.data = clipboardData;
+    }
+
+    /**
+     * Check if the clipboard has any items.
+     */
+    hasItems() {
+        return Boolean(this.data) && Boolean(this.data.types) && this.data.types.length > 0;
+    }
+
+    /**
+     * Check if the given event has tabular-looking data in the clipboard.
+     * @return {boolean}
+     */
+    containsTabularData() {
+        const rtfData = this.data.getData( 'text/rtf');
+        return rtfData && rtfData.includes('\\trowd');
+    }
+
+    /**
+     * Get the images that are in the clipboard data.
+     * @return {Array<File>}
+     */
+    getImages() {
+        const types = this.data.types;
+        const files = this.data.files;
+        const images = [];
+
+        for (const type of types) {
+            if (type.includes('image')) {
+                const item = this.data.getData(type);
+                images.push(item.getAsFile());
+            }
+        }
+
+        for (const file of files) {
+            if (file.type.includes('image')) {
+                images.push(file);
+            }
+        }
+
+        return images;
+    }
+}
+
+export default Clipboard;
\ No newline at end of file