]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/plugin-drawio.js
Comments: Updated reply-to and general styling
[bookstack] / resources / js / wysiwyg / plugin-drawio.js
1 import * as DrawIO from '../services/drawio';
2
3 let pageEditor = null;
4 let currentNode = null;
5
6 /**
7  * @type {WysiwygConfigOptions}
8  */
9 let options = {};
10
11 function isDrawing(node) {
12     return node.hasAttribute('drawio-diagram');
13 }
14
15 function showDrawingManager(mceEditor, selectedNode = null) {
16     pageEditor = mceEditor;
17     currentNode = selectedNode;
18
19     /** @type {ImageManager} * */
20     const imageManager = window.$components.first('image-manager');
21     imageManager.show(image => {
22         if (selectedNode) {
23             const imgElem = selectedNode.querySelector('img');
24             pageEditor.undoManager.transact(() => {
25                 pageEditor.dom.setAttrib(imgElem, 'src', image.url);
26                 pageEditor.dom.setAttrib(selectedNode, 'drawio-diagram', image.id);
27             });
28         } else {
29             const imgHTML = `<div drawio-diagram="${image.id}" contenteditable="false"><img src="${image.url}"></div>`;
30             pageEditor.insertContent(imgHTML);
31         }
32     }, 'drawio');
33 }
34
35 async function updateContent(pngData) {
36     const id = `image-${Math.random().toString(16).slice(2)}`;
37     const loadingImage = window.baseUrl('/loading.gif');
38
39     const handleUploadError = error => {
40         if (error.status === 413) {
41             window.$events.emit('error', options.translations.serverUploadLimitText);
42         } else {
43             window.$events.emit('error', options.translations.imageUploadErrorText);
44         }
45         console.error(error);
46     };
47
48     // Handle updating an existing image
49     if (currentNode) {
50         DrawIO.close();
51         const imgElem = currentNode.querySelector('img');
52         try {
53             const img = await DrawIO.upload(pngData, options.pageId);
54             pageEditor.undoManager.transact(() => {
55                 pageEditor.dom.setAttrib(imgElem, 'src', img.url);
56                 pageEditor.dom.setAttrib(currentNode, 'drawio-diagram', img.id);
57             });
58         } catch (err) {
59             handleUploadError(err);
60         }
61         return;
62     }
63
64     setTimeout(async () => {
65         pageEditor.insertContent(`<div drawio-diagram contenteditable="false"><img src="${loadingImage}" id="${id}"></div>`);
66         DrawIO.close();
67         try {
68             const img = await DrawIO.upload(pngData, options.pageId);
69             pageEditor.undoManager.transact(() => {
70                 pageEditor.dom.setAttrib(id, 'src', img.url);
71                 pageEditor.dom.get(id).parentNode.setAttribute('drawio-diagram', img.id);
72             });
73         } catch (err) {
74             pageEditor.dom.remove(id);
75             handleUploadError(err);
76         }
77     }, 5);
78 }
79
80 function drawingInit() {
81     if (!currentNode) {
82         return Promise.resolve('');
83     }
84
85     const drawingId = currentNode.getAttribute('drawio-diagram');
86     return DrawIO.load(drawingId);
87 }
88
89 function showDrawingEditor(mceEditor, selectedNode = null) {
90     pageEditor = mceEditor;
91     currentNode = selectedNode;
92     DrawIO.show(options.drawioUrl, drawingInit, updateContent);
93 }
94
95 /**
96  * @param {Editor} editor
97  */
98 function register(editor) {
99     editor.addCommand('drawio', () => {
100         const selectedNode = editor.selection.getNode();
101         showDrawingEditor(editor, isDrawing(selectedNode) ? selectedNode : null);
102     });
103
104     editor.ui.registry.addIcon('diagram', `<svg width="24" height="24" fill="${options.darkMode ? '#BBB' : '#000000'}" xmlns="http://www.w3.org/2000/svg"><path d="M20.716 7.639V2.845h-4.794v1.598h-7.99V2.845H3.138v4.794h1.598v7.99H3.138v4.794h4.794v-1.598h7.99v1.598h4.794v-4.794h-1.598v-7.99zM4.736 4.443h1.598V6.04H4.736zm1.598 14.382H4.736v-1.598h1.598zm9.588-1.598h-7.99v-1.598H6.334v-7.99h1.598V6.04h7.99v1.598h1.598v7.99h-1.598zm3.196 1.598H17.52v-1.598h1.598zM17.52 6.04V4.443h1.598V6.04zm-4.21 7.19h-2.79l-.582 1.599H8.643l2.717-7.191h1.119l2.724 7.19h-1.302zm-2.43-1.006h2.086l-1.039-3.06z"/></svg>`);
105
106     editor.ui.registry.addSplitButton('drawio', {
107         tooltip: 'Insert/edit drawing',
108         icon: 'diagram',
109         onAction() {
110             editor.execCommand('drawio');
111             // Hack to de-focus the tinymce editor toolbar
112             window.document.body.dispatchEvent(new Event('mousedown', {bubbles: true}));
113         },
114         fetch(callback) {
115             callback([
116                 {
117                     type: 'choiceitem',
118                     text: 'Drawing manager',
119                     value: 'drawing-manager',
120                 },
121             ]);
122         },
123         onItemAction(api, value) {
124             if (value === 'drawing-manager') {
125                 const selectedNode = editor.selection.getNode();
126                 showDrawingManager(editor, isDrawing(selectedNode) ? selectedNode : null);
127             }
128         },
129     });
130
131     editor.on('dblclick', () => {
132         const selectedNode = editor.selection.getNode();
133         if (!isDrawing(selectedNode)) return;
134         showDrawingEditor(editor, selectedNode);
135     });
136
137     editor.on('SetContent', () => {
138         const drawings = editor.dom.select('body > div[drawio-diagram]');
139         if (!drawings.length) return;
140
141         editor.undoManager.transact(() => {
142             for (const drawing of drawings) {
143                 drawing.setAttribute('contenteditable', 'false');
144             }
145         });
146     });
147 }
148
149 /**
150  *
151  * @param {WysiwygConfigOptions} providedOptions
152  * @return {function(Editor, string)}
153  */
154 export function getPlugin(providedOptions) {
155     options = providedOptions;
156     return register;
157 }