1 import Clipboard from "../services/clipboard";
4 let draggedContentEditable;
6 function hasTextContent(node) {
7 return node && !!(node.textContent || node.innerText);
11 * Handle pasting images from clipboard.
12 * @param {Editor} editor
13 * @param {WysiwygConfigOptions} options
14 * @param {ClipboardEvent|DragEvent} event
16 function paste(editor, options, event) {
17 const clipboard = new Clipboard(event.clipboardData || event.dataTransfer);
19 // Don't handle the event ourselves if no items exist of contains table-looking data
20 if (!clipboard.hasItems() || clipboard.containsTabularData()) {
24 const images = clipboard.getImages();
25 for (const imageFile of images) {
27 const id = "image-" + Math.random().toString(16).slice(2);
28 const loadingImage = window.baseUrl('/loading.gif');
29 event.preventDefault();
32 editor.insertContent(`<p><img src="${loadingImage}" id="${id}"></p>`);
34 uploadImageFile(imageFile, options.pageId).then(resp => {
35 const safeName = resp.name.replace(/"/g, '');
36 const newImageHtml = `<img src="${resp.thumbs.display}" alt="${safeName}" />`;
38 const newEl = editor.dom.create('a', {
43 editor.dom.replace(newEl, id);
45 editor.dom.remove(id);
46 window.$events.emit('error', options.translations.imageUploadErrorText);
54 * Upload an image file to the server
58 async function uploadImageFile(file, pageId) {
59 if (file === null || file.type.indexOf('image') !== 0) {
60 throw new Error(`Not an image file`);
65 let fileNameMatches = file.name.match(/\.(.+)$/);
66 if (fileNameMatches.length > 1) ext = fileNameMatches[1];
69 const remoteFilename = "image-" + Date.now() + "." + ext;
70 const formData = new FormData();
71 formData.append('file', file, remoteFilename);
72 formData.append('uploaded_to', pageId);
74 const resp = await window.$http.post(window.baseUrl('/images/gallery'), formData);
79 * @param {Editor} editor
80 * @param {WysiwygConfigOptions} options
82 function dragStart(editor, options) {
83 let node = editor.selection.getNode();
85 if (node.nodeName === 'IMG') {
86 wrap = editor.dom.getParent(node, '.mceTemp');
88 if (!wrap && node.parentNode.nodeName === 'A' && !hasTextContent(node.parentNode)) {
89 wrap = node.parentNode;
93 // Track dragged contenteditable blocks
94 if (node.hasAttribute('contenteditable') && node.getAttribute('contenteditable') === 'false') {
95 draggedContentEditable = node;
100 * @param {Editor} editor
101 * @param {WysiwygConfigOptions} options
102 * @param {DragEvent} event
104 function drop(editor, options, event) {
105 let dom = editor.dom,
106 rng = tinymce.dom.RangeUtils.getCaretRangeFromPoint(event.clientX, event.clientY, editor.getDoc());
108 // Template insertion
109 const templateId = event.dataTransfer && event.dataTransfer.getData('bookstack/template');
111 event.preventDefault();
112 window.$http.get(`/templates/${templateId}`).then(resp => {
113 editor.selection.setRng(rng);
114 editor.undoManager.transact(function () {
115 editor.execCommand('mceInsertContent', false, resp.data.html);
120 // Don't allow anything to be dropped in a captioned image.
121 if (dom.getParent(rng.startContainer, '.mceTemp')) {
122 event.preventDefault();
124 event.preventDefault();
126 editor.undoManager.transact(function () {
127 editor.selection.setRng(rng);
128 editor.selection.setNode(wrap);
133 // Handle contenteditable section drop
134 if (!event.isDefaultPrevented() && draggedContentEditable) {
135 event.preventDefault();
136 editor.undoManager.transact(function () {
137 const selectedNode = editor.selection.getNode();
138 const range = editor.selection.getRng();
139 const selectedNodeRoot = selectedNode.closest('body > *');
140 if (range.startOffset > (range.startContainer.length / 2)) {
141 editor.$(selectedNodeRoot).after(draggedContentEditable);
143 editor.$(selectedNodeRoot).before(draggedContentEditable);
148 // Handle image insert
149 if (!event.isDefaultPrevented()) {
150 paste(editor, options, event);
157 * @param {Editor} editor
158 * @param {WysiwygConfigOptions} options
160 export function listenForDragAndPaste(editor, options) {
161 editor.on('dragstart', () => dragStart(editor, options));
162 editor.on('drop', event => drop(editor, options, event));
163 editor.on('paste', event => paste(editor, options, event));