xref: /plugin/bpmnio/script/bpmnio_render.js (revision 94260a7f040ace3ab386a435f5314f2f3ebe529a)
1function extractXml(data) {
2    return new TextDecoder()
3        .decode(Uint8Array.from(atob(data), (c) => c.charCodeAt(0)))
4        .trim();
5}
6
7async function renderDiagram(xml, container, viewer, computeSizeFn) {
8    try {
9        const result = await viewer.importXML(xml);
10        const { warnings } = result;
11        if (warnings?.length > 0) console.warn(warnings);
12
13        if (!computeSizeFn) return;
14
15        const zoom = getZoomFactor(container);
16        const layout = computeSizeFn(viewer, zoom);
17        if (!layout) return;
18
19        container.style.height = `${layout.scaledHeight}px`;
20        container.style.width = `${layout.scaledWidth}px`;
21
22        if (typeof layout.applyZoom === "function") {
23            layout.applyZoom();
24        }
25    } catch (err) {
26        container.textContent = err;
27        console.error(err.message, err.warnings);
28    }
29}
30
31function getZoomFactor(container) {
32    const zoom = Number.parseFloat(container.dataset.zoom ?? "1");
33
34    if (!Number.isFinite(zoom) || zoom <= 0) {
35        return 1;
36    }
37
38    return zoom;
39}
40
41function computeBpmnDiagramSize(viewer, zoom) {
42    const canvas = viewer.get("canvas");
43    const bboxViewport = canvas.getActiveLayer().getBBox();
44    const width = bboxViewport.width + 4;
45    const height = bboxViewport.height + 4;
46
47    return {
48        width,
49        height,
50        scaledWidth: Math.max(width * zoom, 1),
51        scaledHeight: Math.max(height * zoom, 1),
52        applyZoom() {
53            canvas.resized();
54            canvas.viewbox({
55                x: bboxViewport.x - 2,
56                y: bboxViewport.y - 2,
57                width,
58                height,
59            });
60        },
61    };
62}
63
64function computeDmnDiagramSize(viewer, zoom) {
65    const activeView = viewer.getActiveView();
66
67    if (activeView.type === "drd") {
68        const activeEditor = viewer.getActiveViewer();
69
70        // access active editor components
71        const canvas = activeEditor.get("canvas");
72
73        const bboxViewport = canvas.getActiveLayer().getBBox();
74        const width = bboxViewport.width + 4;
75        const height = bboxViewport.height + 4;
76        return {
77            width,
78            height,
79            scaledWidth: Math.max(width * zoom, 1),
80            scaledHeight: Math.max(height * zoom, 1),
81            applyZoom() {
82                canvas.resized();
83                canvas.viewbox({
84                    x: bboxViewport.x - 2,
85                    y: bboxViewport.y - 2,
86                    width,
87                    height,
88                });
89            },
90        };
91    }
92    return undefined;
93}
94
95async function renderBpmnDiagram(xml, container) {
96    const BpmnViewer = window.BpmnJS.Viewer;
97    const viewer = new BpmnViewer({ container });
98
99    renderDiagram(xml, container, viewer, computeBpmnDiagramSize);
100}
101
102async function renderDmnDiagram(xml, container) {
103    const DmnViewer = window.DmnJSViewer;
104    const viewer = new DmnViewer({ container });
105
106    renderDiagram(xml, container, viewer, computeDmnDiagramSize);
107}
108
109async function exportDataBase64(editor) {
110    try {
111        const options = { format: true };
112        const result = await editor.saveXML(options);
113        const { xml } = result;
114        if (xml.length > 0) {
115            const encoder = new TextEncoder();
116            const data = encoder.encode(xml);
117            return btoa(String.fromCharCode(...data));
118        }
119    } catch (err) {
120        console.error(err);
121        return null;
122    }
123}
124
125function addFormSubmitListener(editor) {
126    const form = document.getElementById("dw__editform");
127    form.addEventListener("submit", async () => {
128        const data = await exportDataBase64(editor);
129        const field = form.querySelector('input[name="plugin_bpmnio_data"]');
130        if (field && data) {
131            field.value = data;
132        }
133    });
134}
135
136async function renderBpmnEditor(xml, container) {
137    const BpmnEditor = window.BpmnJS;
138    const editor = new BpmnEditor({ container });
139    addFormSubmitListener(editor);
140    renderDiagram(xml, container, editor, null);
141}
142
143async function renderDmnEditor(xml, container) {
144    const DmnEditor = window.DmnJS;
145    const editor = new DmnEditor({ container });
146    addFormSubmitListener(editor);
147    renderDiagram(xml, container, editor, null);
148}
149
150function safeRender(tag, type, fn) {
151    try {
152        const root = jQuery(tag);
153        const containerId = "." + type + "_js_container";
154        const container = root.find(containerId)[0];
155        // avoid double rendering
156        if (container.children?.length > 0) return;
157
158        const dataId = "." + type + "_js_data";
159        const data = root.find(dataId)[0];
160        const xml = extractXml(data.textContent);
161
162        if (xml.startsWith("Error:")) {
163            container.textContent = xml;
164            container.style.color = "red";
165            return;
166        }
167
168        fn(xml, container);
169    } catch (err) {
170        console.warn(err.message);
171    }
172}
173
174jQuery(document).ready(function () {
175    jQuery("div[id^=__bpmn_js_]").each((_, tag) =>
176        safeRender(tag, "bpmn", renderBpmnDiagram)
177    );
178    jQuery("div[id^=__dmn_js_]").each((_, tag) =>
179        safeRender(tag, "dmn", renderDmnDiagram)
180    );
181    jQuery("div[id=plugin_bpmnio__bpmn_editor]").each((_, tag) =>
182        safeRender(tag, "bpmn", renderBpmnEditor)
183    );
184    jQuery("div[id=plugin_bpmnio__dmn_editor]").each((_, tag) =>
185        safeRender(tag, "dmn", renderDmnEditor)
186    );
187});
188