X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/ddb7f33868ea499ab8f48a7062f145e8c0fbe02f..refs/pull/2023/head:/resources/js/components/wysiwyg-editor.js diff --git a/resources/js/components/wysiwyg-editor.js b/resources/js/components/wysiwyg-editor.js index 60a6743ea..be1bac6be 100644 --- a/resources/js/components/wysiwyg-editor.js +++ b/resources/js/components/wysiwyg-editor.js @@ -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(`

`); - 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 = `${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')); @@ -135,19 +139,21 @@ function codePlugin() { } function showPopup(editor) { - let selectedNode = editor.selection.getNode(); + const selectedNode = editor.selection.getNode(); if (!elemIsCodeBlock(selectedNode)) { - let providedCode = editor.selection.getNode().textContent; + const providedCode = editor.selection.getNode().textContent; window.vues['code-editor'].open(providedCode, '', (code, lang) => { - let wrap = document.createElement('div'); + const wrap = document.createElement('div'); wrap.innerHTML = `
`; wrap.querySelector('code').innerText = code; editor.formatter.toggle('pre'); - let node = editor.selection.getNode(); + const node = editor.selection.getNode(); editor.dom.setHTML(node, wrap.querySelector('pre').innerHTML); editor.fire('SetContent'); + + editor.focus() }); return; } @@ -156,15 +162,17 @@ function codePlugin() { let currentCode = selectedNode.querySelector('textarea').textContent; window.vues['code-editor'].open(currentCode, lang, (code, lang) => { - let editorElem = selectedNode.querySelector('.CodeMirror'); - let cmInstance = editorElem.CodeMirror; + const editorElem = selectedNode.querySelector('.CodeMirror'); + const cmInstance = editorElem.CodeMirror; if (cmInstance) { Code.setContent(cmInstance, code); - Code.setMode(cmInstance, lang); + Code.setMode(cmInstance, lang, code); } - let textArea = selectedNode.querySelector('textarea'); + const textArea = selectedNode.querySelector('textarea'); if (textArea) textArea.textContent = code; selectedNode.setAttribute('data-lang', lang); + + editor.focus() }); } @@ -234,7 +242,7 @@ function codePlugin() { }); } -function drawIoPlugin() { +function drawIoPlugin(drawioUrl) { let pageEditor = null; let currentNode = null; @@ -262,7 +270,7 @@ function drawIoPlugin() { function showDrawingEditor(mceEditor, selectedNode = null) { pageEditor = mceEditor; currentNode = selectedNode; - DrawIO.show(drawingInit, updateContent); + DrawIO.show(drawioUrl, drawingInit, updateContent); } async function updateContent(pngData) { @@ -419,10 +427,14 @@ class WysiwygEditor { loadPlugins() { codePlugin(); customHrPlugin(); - if (document.querySelector('[drawio-enabled]').getAttribute('drawio-enabled') === 'true') { - drawIoPlugin(); + + const drawioUrlElem = document.querySelector('[drawio-url]'); + if (drawioUrlElem) { + const url = drawioUrlElem.getAttribute('drawio-url'); + drawIoPlugin(url); this.plugins += ' drawio'; } + if (this.textDirection === 'rtl') { this.plugins += ' directionality' } @@ -496,7 +508,15 @@ class WysiwygEditor { const originalField = win.document.getElementById(field_name); originalField.value = entity.link; const mceForm = originalField.closest('.mce-form'); - mceForm.querySelectorAll('input')[2].value = entity.name; + const inputs = mceForm.querySelectorAll('input'); + + // Set text to display if not empty + if (!inputs[1].value) { + inputs[1].value = entity.name; + } + + // Set title field + inputs[2].value = entity.name; }); } @@ -589,6 +609,7 @@ class WysiwygEditor { registerEditorShortcuts(editor); let wrap; + let draggedContentEditable; function hasTextContent(node) { return node && !!( node.textContent || node.innerText ); @@ -597,12 +618,19 @@ class WysiwygEditor { editor.on('dragstart', function () { let node = editor.selection.getNode(); - if (node.nodeName !== 'IMG') return; - wrap = editor.dom.getParent(node, '.mceTemp'); + if (node.nodeName === 'IMG') { + wrap = editor.dom.getParent(node, '.mceTemp'); + + if (!wrap && node.parentNode.nodeName === 'A' && !hasTextContent(node.parentNode)) { + wrap = node.parentNode; + } + } - if (!wrap && node.parentNode.nodeName === 'A' && !hasTextContent(node.parentNode)) { - wrap = node.parentNode; + // Track dragged contenteditable blocks + if (node.hasAttribute('contenteditable') && node.getAttribute('contenteditable') === 'false') { + draggedContentEditable = node; } + }); editor.on('drop', function (event) { @@ -610,7 +638,7 @@ class WysiwygEditor { rng = tinymce.dom.RangeUtils.getCaretRangeFromPoint(event.clientX, event.clientY, editor.getDoc()); // Template insertion - const templateId = event.dataTransfer.getData('bookstack/template'); + const templateId = event.dataTransfer && event.dataTransfer.getData('bookstack/template'); if (templateId) { event.preventDefault(); window.$http.get(`/templates/${templateId}`).then(resp => { @@ -634,6 +662,26 @@ class WysiwygEditor { }); } + // Handle contenteditable section drop + if (!event.isDefaultPrevented() && draggedContentEditable) { + event.preventDefault(); + editor.undoManager.transact(function () { + const selectedNode = editor.selection.getNode(); + const range = editor.selection.getRng(); + const selectedNodeRoot = selectedNode.closest('body > *'); + if (range.startOffset > (range.startContainer.length / 2)) { + editor.$(selectedNodeRoot).after(draggedContentEditable); + } else { + editor.$(selectedNodeRoot).before(draggedContentEditable); + } + }); + } + + // Handle image insert + if (!event.isDefaultPrevented()) { + editorPaste(event, editor, context); + } + wrap = null; });