mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-08-07 23:03:00 +03:00
Updated drawio tinymce plugin to use embeds
- Adds support for handling drawings as embeds, based on image extension. - Adds additional attribute to drawio elements within editor to prevent tinymce replacing embeds with a placeholder. - Updates how contenteditable is applied to drawio blocks within editor, to use proper filters instead of using the SetContent event.
This commit is contained in:
@@ -99,4 +99,18 @@ async function load(drawingId) {
|
|||||||
return resp.data.content;
|
return resp.data.content;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {show, close, upload, load};
|
|
||||||
|
function buildDrawingContentHtml(drawing) {
|
||||||
|
const isSvg = drawing.url.split('.').pop().toLowerCase() === 'svg';
|
||||||
|
const image = `<img src="${drawing.url}">`;
|
||||||
|
const embed = `<embed src="${drawing.url}" type="image/svg+xml">`;
|
||||||
|
return `<div drawio-diagram="${drawing.id}">${isSvg ? embed : image}</div>`
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildDrawingContentNode(drawing) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.innerHTML = buildDrawingContentHtml(drawing);
|
||||||
|
return div.children[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {show, close, upload, load, buildDrawingContentHtml, buildDrawingContentNode};
|
@@ -1,4 +1,5 @@
|
|||||||
import DrawIO from "../services/drawio";
|
import DrawIO from "../services/drawio";
|
||||||
|
import {build} from "./config";
|
||||||
|
|
||||||
let pageEditor = null;
|
let pageEditor = null;
|
||||||
let currentNode = null;
|
let currentNode = null;
|
||||||
@@ -15,15 +16,14 @@ function isDrawing(node) {
|
|||||||
function showDrawingManager(mceEditor, selectedNode = null) {
|
function showDrawingManager(mceEditor, selectedNode = null) {
|
||||||
pageEditor = mceEditor;
|
pageEditor = mceEditor;
|
||||||
currentNode = selectedNode;
|
currentNode = selectedNode;
|
||||||
|
|
||||||
// Show image manager
|
// Show image manager
|
||||||
window.ImageManager.show(function (image) {
|
window.ImageManager.show(function (image) {
|
||||||
if (selectedNode) {
|
if (selectedNode) {
|
||||||
let imgElem = selectedNode.querySelector('img');
|
pageEditor.dom.replace(buildDrawingNode(image), selectedNode);
|
||||||
pageEditor.dom.setAttrib(imgElem, 'src', image.url);
|
|
||||||
pageEditor.dom.setAttrib(selectedNode, 'drawio-diagram', image.id);
|
|
||||||
} else {
|
} else {
|
||||||
let imgHTML = `<div drawio-diagram="${image.id}" contenteditable="false"><img src="${image.url}"></div>`;
|
const drawingHtml = DrawIO.buildDrawingContentHtml(image);
|
||||||
pageEditor.insertContent(imgHTML);
|
pageEditor.insertContent(drawingHtml);
|
||||||
}
|
}
|
||||||
}, 'drawio');
|
}, 'drawio');
|
||||||
}
|
}
|
||||||
@@ -34,6 +34,13 @@ function showDrawingEditor(mceEditor, selectedNode = null) {
|
|||||||
DrawIO.show(options.drawioUrl, drawingInit, updateContent);
|
DrawIO.show(options.drawioUrl, drawingInit, updateContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildDrawingNode(drawing) {
|
||||||
|
const drawingEl = DrawIO.buildDrawingContentNode(drawing);
|
||||||
|
drawingEl.setAttribute('contenteditable', 'false');
|
||||||
|
drawingEl.setAttribute('data-ephox-embed-iri', 'true');
|
||||||
|
return drawingEl;
|
||||||
|
}
|
||||||
|
|
||||||
async function updateContent(drawingData) {
|
async function updateContent(drawingData) {
|
||||||
const id = "image-" + Math.random().toString(16).slice(2);
|
const id = "image-" + Math.random().toString(16).slice(2);
|
||||||
const loadingImage = window.baseUrl('/loading.gif');
|
const loadingImage = window.baseUrl('/loading.gif');
|
||||||
@@ -50,11 +57,9 @@ async function updateContent(drawingData) {
|
|||||||
// Handle updating an existing image
|
// Handle updating an existing image
|
||||||
if (currentNode) {
|
if (currentNode) {
|
||||||
DrawIO.close();
|
DrawIO.close();
|
||||||
let imgElem = currentNode.querySelector('img');
|
|
||||||
try {
|
try {
|
||||||
const img = await DrawIO.upload(drawingData, options.pageId);
|
const img = await DrawIO.upload(drawingData, options.pageId);
|
||||||
pageEditor.dom.setAttrib(imgElem, 'src', img.url);
|
pageEditor.dom.replace(buildDrawingNode(img), currentNode);
|
||||||
pageEditor.dom.setAttrib(currentNode, 'drawio-diagram', img.id);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
handleUploadError(err);
|
handleUploadError(err);
|
||||||
}
|
}
|
||||||
@@ -62,12 +67,11 @@ async function updateContent(drawingData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
pageEditor.insertContent(`<div drawio-diagram contenteditable="false"><img src="${loadingImage}" id="${id}"></div>`);
|
pageEditor.insertContent(`<div drawio-diagram contenteditable="false"><img src="${loadingImage}" alt="Loading" id="${id}"></div>`);
|
||||||
DrawIO.close();
|
DrawIO.close();
|
||||||
try {
|
try {
|
||||||
const img = await DrawIO.upload(drawingData, options.pageId);
|
const img = await DrawIO.upload(drawingData, options.pageId);
|
||||||
pageEditor.dom.setAttrib(id, 'src', img.url);
|
pageEditor.dom.replace(buildDrawingNode(img), pageEditor.dom.get(id).parentNode);
|
||||||
pageEditor.dom.get(id).parentNode.setAttribute('drawio-diagram', img.id);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
pageEditor.dom.remove(id);
|
pageEditor.dom.remove(id);
|
||||||
handleUploadError(err);
|
handleUploadError(err);
|
||||||
@@ -86,7 +90,6 @@ function drawingInit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @param {WysiwygConfigOptions} providedOptions
|
* @param {WysiwygConfigOptions} providedOptions
|
||||||
* @return {function(Editor, string)}
|
* @return {function(Editor, string)}
|
||||||
*/
|
*/
|
||||||
@@ -130,14 +133,28 @@ export function getPlugin(providedOptions) {
|
|||||||
showDrawingEditor(editor, selectedNode);
|
showDrawingEditor(editor, selectedNode);
|
||||||
});
|
});
|
||||||
|
|
||||||
editor.on('SetContent', function () {
|
editor.on('PreInit', () => {
|
||||||
const drawings = editor.$('body > div[drawio-diagram]');
|
editor.parser.addNodeFilter('div', function(nodes) {
|
||||||
if (!drawings.length) return;
|
for (const node of nodes) {
|
||||||
|
if (node.attr('drawio-diagram')) {
|
||||||
|
// Set content editable to be false to prevent direct editing of child content.
|
||||||
|
node.attr('contenteditable', 'false');
|
||||||
|
// Set this attribute to prevent drawing contents being parsed as media embeds
|
||||||
|
// to avoid contents being replaced with placeholder images.
|
||||||
|
// TinyMCE embed plugin sources looks for this attribute in its logic.
|
||||||
|
node.attr('data-ephox-embed-iri', 'true');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
editor.undoManager.transact(function () {
|
editor.serializer.addNodeFilter('div', function(nodes) {
|
||||||
drawings.each((index, elem) => {
|
for (const node of nodes) {
|
||||||
elem.setAttribute('contenteditable', 'false');
|
// Clean up content attributes
|
||||||
});
|
if (node.attr('drawio-diagram')) {
|
||||||
|
node.attr('contenteditable', null);
|
||||||
|
node.attr('data-ephox-embed-iri', null);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -48,6 +48,11 @@ body.page-content.mce-content-body {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prevent interaction with embed contents
|
||||||
|
.page-content.mce-content-body embed {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
// Details/summary editor usability
|
// Details/summary editor usability
|
||||||
.page-content.mce-content-body details summary {
|
.page-content.mce-content-body details summary {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
Reference in New Issue
Block a user