]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/plugin-drawio.js
Covered app icon setting with testing
[bookstack] / resources / js / wysiwyg / plugin-drawio.js
1 import 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(function (image) {
22         if (selectedNode) {
23             const imgElem = selectedNode.querySelector('img');
24             pageEditor.undoManager.transact(function () {
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 function showDrawingEditor(mceEditor, selectedNode = null) {
36     pageEditor = mceEditor;
37     currentNode = selectedNode;
38     DrawIO.show(options.drawioUrl, drawingInit, updateContent);
39 }
40
41 async function updateContent(pngData) {
42     const id = "image-" + Math.random().toString(16).slice(2);
43     const loadingImage = window.baseUrl('/loading.gif');
44
45     const handleUploadError = (error) => {
46         if (error.status === 413) {
47             window.$events.emit('error', options.translations.serverUploadLimitText);
48         } else {
49             window.$events.emit('error', options.translations.imageUploadErrorText);
50         }
51         console.log(error);
52     };
53
54     // Handle updating an existing image
55     if (currentNode) {
56         DrawIO.close();
57         let imgElem = currentNode.querySelector('img');
58         try {
59             const img = await DrawIO.upload(pngData, options.pageId);
60             pageEditor.undoManager.transact(function () {
61                 pageEditor.dom.setAttrib(imgElem, 'src', img.url);
62                 pageEditor.dom.setAttrib(currentNode, 'drawio-diagram', img.id);
63             });
64         } catch (err) {
65             handleUploadError(err);
66         }
67         return;
68     }
69
70     setTimeout(async () => {
71         pageEditor.insertContent(`<div drawio-diagram contenteditable="false"><img src="${loadingImage}" id="${id}"></div>`);
72         DrawIO.close();
73         try {
74             const img = await DrawIO.upload(pngData, options.pageId);
75             pageEditor.undoManager.transact(function () {
76                 pageEditor.dom.setAttrib(id, 'src', img.url);
77                 pageEditor.dom.get(id).parentNode.setAttribute('drawio-diagram', img.id);
78             });
79         } catch (err) {
80             pageEditor.dom.remove(id);
81             handleUploadError(err);
82         }
83     }, 5);
84 }
85
86
87 function drawingInit() {
88     if (!currentNode) {
89         return Promise.resolve('');
90     }
91
92     let drawingId = currentNode.getAttribute('drawio-diagram');
93     return DrawIO.load(drawingId);
94 }
95
96 /**
97  *
98  * @param {WysiwygConfigOptions} providedOptions
99  * @return {function(Editor, string)}
100  */
101 export function getPlugin(providedOptions) {
102     options = providedOptions;
103     return function(editor, url) {
104
105         editor.addCommand('drawio', () => {
106             const selectedNode = editor.selection.getNode();
107             showDrawingEditor(editor, isDrawing(selectedNode) ? selectedNode : null);
108         });
109
110         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>`)
111
112         editor.ui.registry.addSplitButton('drawio', {
113             tooltip: 'Insert/edit drawing',
114             icon: 'diagram',
115             onAction() {
116                 editor.execCommand('drawio');
117                 // Hack to de-focus the tinymce editor toolbar
118                 window.document.body.dispatchEvent(new Event('mousedown', {bubbles: true}));
119             },
120             fetch(callback) {
121                 callback([
122                     {
123                         type: 'choiceitem',
124                         text: 'Drawing manager',
125                         value: 'drawing-manager',
126                     }
127                 ]);
128             },
129             onItemAction(api, value) {
130                 if (value === 'drawing-manager') {
131                     const selectedNode = editor.selection.getNode();
132                     showDrawingManager(editor, isDrawing(selectedNode) ? selectedNode : null);
133                 }
134             }
135         });
136
137         editor.on('dblclick', event => {
138             let selectedNode = editor.selection.getNode();
139             if (!isDrawing(selectedNode)) return;
140             showDrawingEditor(editor, selectedNode);
141         });
142
143         editor.on('SetContent', function () {
144             const drawings = editor.dom.select('body > div[drawio-diagram]');
145             if (!drawings.length) return;
146
147             editor.undoManager.transact(function () {
148                 for (const drawing of drawings) {
149                     drawing.setAttribute('contenteditable', 'false');
150                 }
151             });
152         });
153
154     };
155 }