* @param editor
*/
function editorPaste(event, editor, wysiwygComponent) {
- if (!event.clipboardData || !event.clipboardData.items) return;
+ const clipboardItems = event.clipboardData.items;
+ if (!event.clipboardData || !clipboardItems) return;
- for (let clipboardItem of event.clipboardData.items) {
- if (clipboardItem.type.indexOf("image") === -1) continue;
- event.preventDefault();
+ // Don't handle if clipboard includes text content
+ for (let clipboardItem of clipboardItems) {
+ if (clipboardItem.type.includes('text/')) {
+ return;
+ }
+ }
+
+ for (let clipboardItem of clipboardItems) {
+ if (!clipboardItem.type.includes("image")) {
+ continue;
+ }
const id = "image-" + Math.random().toString(16).slice(2);
const loadingImage = window.baseUrl('/loading.gif');
formData.append('file', file, remoteFilename);
formData.append('uploaded_to', wysiwygComponent.pageId);
- const resp = await window.$http.post(window.baseUrl('/images/gallery/upload'), formData);
+ const resp = await window.$http.post(window.baseUrl('/images/gallery'), formData);
return resp.data;
}
});
}
- function codeMirrorContainerToPre($codeMirrorContainer) {
- let textArea = $codeMirrorContainer[0].querySelector('textarea');
- let code = textArea.textContent;
- let lang = $codeMirrorContainer[0].getAttribute('data-lang');
-
- $codeMirrorContainer.removeAttr('contentEditable');
- let $pre = $('<pre></pre>');
- $pre.append($('<code></code>').each((index, elem) => {
- // Needs to be textContent since innerText produces BR:s
- elem.textContent = code;
- }).attr('class', `language-${lang}`));
- $codeMirrorContainer.replaceWith($pre);
+ function codeMirrorContainerToPre(codeMirrorContainer) {
+ const textArea = codeMirrorContainer.querySelector('textarea');
+ const code = textArea.textContent;
+ const lang = codeMirrorContainer.getAttribute('data-lang');
+
+ codeMirrorContainer.removeAttribute('contentEditable');
+ const pre = document.createElement('pre');
+ const codeElem = document.createElement('code');
+ codeElem.classList.add(`language-${lang}`);
+ codeElem.textContent = code;
+ pre.appendChild(codeElem);
+
+ codeMirrorContainer.parentElement.replaceChild(pre, codeMirrorContainer);
}
window.tinymce.PluginManager.add('codeeditor', function(editor, url) {
- let $ = editor.$;
+ const $ = editor.$;
editor.addButton('codeeditor', {
text: 'Code block',
// Convert
editor.on('PreProcess', function (e) {
- $('div.CodeMirrorContainer', e.node).
- each((index, elem) => {
- let $elem = $(elem);
- codeMirrorContainerToPre($elem);
+ $('div.CodeMirrorContainer', e.node).each((index, elem) => {
+ codeMirrorContainerToPre(elem);
});
});
$('.CodeMirrorContainer').filter((index ,elem) => {
return typeof elem.querySelector('.CodeMirror').CodeMirror === 'undefined';
}).each((index, elem) => {
- codeMirrorContainerToPre($(elem));
+ codeMirrorContainerToPre(elem);
});
- let codeSamples = $('body > pre').filter((index, elem) => {
+ const codeSamples = $('body > pre').filter((index, elem) => {
return elem.contentEditable !== "false";
});
DrawIO.show(drawingInit, updateContent);
}
- function updateContent(pngData) {
- let id = "image-" + Math.random().toString(16).slice(2);
- let loadingImage = window.baseUrl('/loading.gif');
- let data = {
- image: pngData,
- uploaded_to: Number(document.getElementById('page-editor').getAttribute('page-id'))
- };
+ async function updateContent(pngData) {
+ const id = "image-" + Math.random().toString(16).slice(2);
+ const loadingImage = window.baseUrl('/loading.gif');
+ const pageId = Number(document.getElementById('page-editor').getAttribute('page-id'));
// Handle updating an existing image
if (currentNode) {
DrawIO.close();
let imgElem = currentNode.querySelector('img');
- window.$http.post(window.baseUrl(`/images/drawing/upload`), data).then(resp => {
- pageEditor.dom.setAttrib(imgElem, 'src', resp.data.url);
- pageEditor.dom.setAttrib(currentNode, 'drawio-diagram', resp.data.id);
- }).catch(err => {
+ try {
+ const img = await DrawIO.upload(pngData, pageId);
+ pageEditor.dom.setAttrib(imgElem, 'src', img.url);
+ pageEditor.dom.setAttrib(currentNode, 'drawio-diagram', img.id);
+ } catch (err) {
window.$events.emit('error', trans('errors.image_upload_error'));
console.log(err);
- });
+ }
return;
}
- setTimeout(() => {
+ setTimeout(async () => {
pageEditor.insertContent(`<div drawio-diagram contenteditable="false"><img src="${loadingImage}" id="${id}"></div>`);
DrawIO.close();
- window.$http.post(window.baseUrl('/images/drawing/upload'), data).then(resp => {
- pageEditor.dom.setAttrib(id, 'src', resp.data.url);
- pageEditor.dom.get(id).parentNode.setAttribute('drawio-diagram', resp.data.id);
- }).catch(err => {
+ try {
+ const img = await DrawIO.upload(pngData, pageId);
+ pageEditor.dom.setAttrib(id, 'src', img.url);
+ pageEditor.dom.get(id).parentNode.setAttribute('drawio-diagram', img.id);
+ } catch (err) {
pageEditor.dom.remove(id);
window.$events.emit('error', trans('errors.image_upload_error'));
console.log(err);
- });
+ }
}, 5);
}
}
let drawingId = currentNode.getAttribute('drawio-diagram');
- return window.$http.get(window.baseUrl(`/images/base64/${drawingId}`)).then(resp => {
- return `data:image/png;base64,${resp.data.content}`;
- });
+ return DrawIO.load(drawingId);
}
window.tinymce.PluginManager.add('drawio', function(editor, url) {
});
editor.on('SetContent', function () {
- let drawings = editor.$('body > div[drawio-diagram]');
+ const drawings = editor.$('body > div[drawio-diagram]');
if (!drawings.length) return;
editor.undoManager.transact(function () {
}
+function listenForBookStackEditorEvents(editor) {
+
+ // Replace editor content
+ window.$events.listen('editor::replace', ({html}) => {
+ editor.setContent(html);
+ });
+
+ // Append editor content
+ window.$events.listen('editor::append', ({html}) => {
+ const content = editor.getContent() + html;
+ editor.setContent(content);
+ });
+
+ // Prepend editor content
+ window.$events.listen('editor::prepend', ({html}) => {
+ const content = html + editor.getContent();
+ editor.setContent(content);
+ });
+
+}
+
class WysiwygEditor {
constructor(elem) {
plugins: this.plugins,
imagetools_toolbar: 'imageoptions',
toolbar: this.getToolBar(),
- content_style: "body {padding-left: 15px !important; padding-right: 15px !important; margin:0!important; margin-left:auto!important;margin-right:auto!important;}",
+ content_style: "html, body {background: #FFF;} body {padding-left: 15px !important; padding-right: 15px !important; margin:0!important; margin-left:auto!important;margin-right:auto!important;}",
style_formats: [
{title: "Header Large", format: "h2"},
{title: "Header Medium", format: "h3"},
if (type === 'file') {
window.EntitySelectorPopup.show(function(entity) {
- let originalField = win.document.getElementById(field_name);
+ const originalField = win.document.getElementById(field_name);
originalField.value = entity.link;
- $(originalField).closest('.mce-form').find('input').eq(2).val(entity.name);
+ const mceForm = originalField.closest('.mce-form');
+ mceForm.querySelectorAll('input')[2].value = entity.name;
});
}
if (scrollId) {
scrollToText(scrollId);
}
+
+ // Override for touch events to allow scroll on mobile
+ const container = editor.getContainer();
+ const toolbarButtons = container.querySelectorAll('.mce-btn');
+ for (let button of toolbarButtons) {
+ button.addEventListener('touchstart', event => {
+ event.stopPropagation();
+ });
+ }
+ window.editor = editor;
});
function editorChange() {
editor.focus();
}
+ listenForBookStackEditorEvents(editor);
+
+ // TODO - Update to standardise across both editors
+ // Use events within listenForBookStackEditorEvents instead (Different event signature)
window.$events.listen('editor-html-update', html => {
editor.setContent(html);
editor.selection.select(editor.getBody(), true);
let dom = editor.dom,
rng = tinymce.dom.RangeUtils.getCaretRangeFromPoint(event.clientX, event.clientY, editor.getDoc());
+ // Template insertion
+ const templateId = event.dataTransfer.getData('bookstack/template');
+ if (templateId) {
+ event.preventDefault();
+ window.$http.get(`/templates/${templateId}`).then(resp => {
+ editor.selection.setRng(rng);
+ editor.undoManager.transact(function () {
+ editor.execCommand('mceInsertContent', false, resp.data.html);
+ });
+ });
+ }
+
// Don't allow anything to be dropped in a captioned image.
if (dom.getParent(rng.startContainer, '.mceTemp')) {
event.preventDefault();
// Paste image-uploads
editor.on('paste', event => editorPaste(event, editor, context));
+
}
};
}