xref: /plugin/mermaid/action.php (revision a03c20104df8caf7506a49db9eb0d8981efaac35)
146a60b4fSRobertWeinmeister<?php
246a60b4fSRobertWeinmeister/**
346a60b4fSRobertWeinmeister * DokuWiki Plugin mermaid (Action Component)
446a60b4fSRobertWeinmeister *
546a60b4fSRobertWeinmeister * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
646a60b4fSRobertWeinmeister * @author  Robert Weinmeister <develop@weinmeister.org>
746a60b4fSRobertWeinmeister */
846a60b4fSRobertWeinmeister
9b566ae41SRobert Weinmeisterdeclare(strict_types=1);
10b566ae41SRobert Weinmeister
11b566ae41SRobert Weinmeisterif (!defined('DOKU_INC')) {
12b566ae41SRobert Weinmeister    die();
13b566ae41SRobert Weinmeister}
14ea08b541SRobert Weinmeister
1546a60b4fSRobertWeinmeisterclass action_plugin_mermaid extends \dokuwiki\Extension\ActionPlugin
1646a60b4fSRobertWeinmeister{
1746a60b4fSRobertWeinmeister    /** @inheritDoc */
18b566ae41SRobert Weinmeister    public function register(Doku_Event_Handler $controller): void {
1946a60b4fSRobertWeinmeister        $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'load');
20172fa282SRobert Weinmeister        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjaxRequest');
21172fa282SRobert Weinmeister    }
22172fa282SRobert Weinmeister
23b566ae41SRobert Weinmeister    private function hasPermissionToEdit(string $ID): bool {
24b566ae41SRobert Weinmeister        return auth_quickaclcheck($ID) >= AUTH_EDIT;
25172fa282SRobert Weinmeister    }
26172fa282SRobert Weinmeister
27b566ae41SRobert Weinmeister    private function isPageLocked(string $ID): bool {
28b566ae41SRobert Weinmeister        return checklock($ID);
29b566ae41SRobert Weinmeister    }
30b566ae41SRobert Weinmeister
31b566ae41SRobert Weinmeister    private function lockMermaidDiagram(string $wikitext): string {
32b566ae41SRobert Weinmeister        preg_match_all('/<mermaid.*?>(.*?)<\/mermaid>/s', $wikitext, $matches, PREG_OFFSET_CAPTURE);
33b566ae41SRobert Weinmeister
34b566ae41SRobert Weinmeister        if (is_array($matches) && count($matches[0]) > (int)$_REQUEST['mermaidindex']) {
35b566ae41SRobert Weinmeister            $whereToInsert = $matches[1][(int)$_REQUEST['mermaidindex']][1];
36b566ae41SRobert Weinmeister            return substr($wikitext, 0, $whereToInsert) . "\n%%" . urldecode($_REQUEST['svg']) . "\n" . substr($wikitext, $whereToInsert);
37b566ae41SRobert Weinmeister        }
38b566ae41SRobert Weinmeister
39b566ae41SRobert Weinmeister        echo json_encode(['status' => 'failure', 'data' => ['Could not lock the Mermaid diagram as the request could not be matched.']]);
40b566ae41SRobert Weinmeister        exit();
41b566ae41SRobert Weinmeister    }
42b566ae41SRobert Weinmeister
43b566ae41SRobert Weinmeister    private function unlockMermaidDiagram(string $wikitext): string {
44b566ae41SRobert Weinmeister        $newWikitext = str_replace("\n%%" . urldecode($_REQUEST['svg']) . "\n", '', $wikitext, $count);
45b566ae41SRobert Weinmeister
46b566ae41SRobert Weinmeister        if ($count !== 1) {
47b566ae41SRobert Weinmeister            echo json_encode(['status' => 'failure', 'data' => ['Could not unlock the Mermaid diagram as the request could not be matched.']]);
48b566ae41SRobert Weinmeister            exit();
49b566ae41SRobert Weinmeister        }
50b566ae41SRobert Weinmeister
51b566ae41SRobert Weinmeister        return $newWikitext;
52b566ae41SRobert Weinmeister    }
53b566ae41SRobert Weinmeister
54b566ae41SRobert Weinmeister    private function isWikiTextChanged(string $wikitext, string $newWikitext): bool {
55b566ae41SRobert Weinmeister        return strlen($newWikitext) > 0 && $newWikitext !== $wikitext;
56b566ae41SRobert Weinmeister    }
57b566ae41SRobert Weinmeister
58b566ae41SRobert Weinmeister    private function saveWikiChanges(string $ID, string $newWikitext, string $mode): void {
59b566ae41SRobert Weinmeister        lock($ID);
60b566ae41SRobert Weinmeister        saveWikiText($ID, $newWikitext, "{$mode} Mermaid diagram", true);
61b566ae41SRobert Weinmeister        unlock($ID);
62b566ae41SRobert Weinmeister    }
63b566ae41SRobert Weinmeister
64b566ae41SRobert Weinmeister    public function handleAjaxRequest(Doku_Event $event, $param): void {
65b566ae41SRobert Weinmeister        if ($event->data !== 'plugin_mermaid') {
66b566ae41SRobert Weinmeister            return;
67b566ae41SRobert Weinmeister        }
68172fa282SRobert Weinmeister        $event->stopPropagation();
69172fa282SRobert Weinmeister        $event->preventDefault();
70172fa282SRobert Weinmeister
71b566ae41SRobert Weinmeister        if (!isset($_REQUEST['mermaidindex']) || !isset($_REQUEST['svg'])) {
72b566ae41SRobert Weinmeister            echo json_encode(['status' => 'failure', 'data' => ['Missing required parameters.']]);
73b566ae41SRobert Weinmeister            exit();
74b566ae41SRobert Weinmeister        }
75b566ae41SRobert Weinmeister
76172fa282SRobert Weinmeister        $ID = cleanID(urldecode($_REQUEST['pageid']));
77172fa282SRobert Weinmeister
78b566ae41SRobert Weinmeister        if(!$this->hasPermissionToEdit($ID)) {
79172fa282SRobert Weinmeister            echo json_encode(['status' => 'failure', 'data' => ['You do not have permission to edit this file.\nAccess was denied.']]);
80172fa282SRobert Weinmeister            exit();
81172fa282SRobert Weinmeister        }
82172fa282SRobert Weinmeister
83b566ae41SRobert Weinmeister        if($this->isPageLocked($ID)) {
84172fa282SRobert Weinmeister            echo json_encode(['status' => 'failure', 'data' => ['The page is currently locked.\nTry again later.']]);
85172fa282SRobert Weinmeister            exit();
86172fa282SRobert Weinmeister        }
87172fa282SRobert Weinmeister
88172fa282SRobert Weinmeister        $wikitext = rawWiki($ID);
89172fa282SRobert Weinmeister        $newWikitext = $wikitext;
90172fa282SRobert Weinmeister
91b566ae41SRobert Weinmeister        if($_REQUEST['mode'] === 'lock') {
92172fa282SRobert Weinmeister            preg_match_all('/<mermaid.*?>(.*?)<\/mermaid>/s', $wikitext, $matches, PREG_OFFSET_CAPTURE);
93172fa282SRobert Weinmeister
94172fa282SRobert Weinmeister            if(is_array($matches) && count($matches[0]) > $_REQUEST['mermaidindex'])
95172fa282SRobert Weinmeister            {
96172fa282SRobert Weinmeister                $whereToInsert = $matches[1][$_REQUEST['mermaidindex']][1];
97172fa282SRobert Weinmeister                $newWikitext = substr($wikitext, 0, $whereToInsert) . "\n%%" . urldecode($_REQUEST['svg']) . "\n" . substr($wikitext, $whereToInsert);
98172fa282SRobert Weinmeister            }
99172fa282SRobert Weinmeister            else
100172fa282SRobert Weinmeister            {
101172fa282SRobert Weinmeister                echo json_encode(['status' => 'failure', 'data' => ['Could not lock the Mermaid diagram as the request could not be matched.']]);
102172fa282SRobert Weinmeister                exit();
103172fa282SRobert Weinmeister            }
104172fa282SRobert Weinmeister        }
105172fa282SRobert Weinmeister
106172fa282SRobert Weinmeister        if($_REQUEST['mode'] == 'unlock')
107172fa282SRobert Weinmeister        {
108172fa282SRobert Weinmeister            $newWikitext = str_replace("\n%%" . urldecode($_REQUEST['svg']) . "\n", '', $wikitext, $count);
109172fa282SRobert Weinmeister            if($count != 1)
110172fa282SRobert Weinmeister            {
111172fa282SRobert Weinmeister                echo json_encode(['status' => 'failure', 'data' => ['Could not unlock the Mermaid diagram as the request could not be matched.']]);
112172fa282SRobert Weinmeister                exit();
113172fa282SRobert Weinmeister            }
114172fa282SRobert Weinmeister        }
115172fa282SRobert Weinmeister
116b566ae41SRobert Weinmeister        if($this->isWikiTextChanged($wikitext, $newWikitext)) {
117b566ae41SRobert Weinmeister            $this->saveWikiChanges($ID, $newWikitext, $_REQUEST['mode']);
118172fa282SRobert Weinmeister            echo json_encode(['status' => 'success', 'data' => []]);
119b566ae41SRobert Weinmeister        } else{
120172fa282SRobert Weinmeister            echo json_encode(['status' => 'failure', 'data' => ['Could not ' . $_REQUEST['mode'] . ' the Mermaid diagram.']]);
121b566ae41SRobert Weinmeister        }
122b566ae41SRobert Weinmeister
123172fa282SRobert Weinmeister        exit();
12446a60b4fSRobertWeinmeister    }
12546a60b4fSRobertWeinmeister
126b566ae41SRobert Weinmeister    private function addLocalScript(Doku_Event $event): void {
127b566ae41SRobert Weinmeister        $event->data['script'][] = [
128b566ae41SRobert Weinmeister            'type'    => 'text/javascript',
129b566ae41SRobert Weinmeister            'charset' => 'utf-8',
130b566ae41SRobert Weinmeister            'src'     => DOKU_BASE . 'lib/plugins/mermaid/mermaid.min.js',
131b566ae41SRobert Weinmeister        ];
132b566ae41SRobert Weinmeister    }
133b566ae41SRobert Weinmeister
134b566ae41SRobert Weinmeister    private function addEsmScript(Doku_Event $event, string $version, string $init): void {
135b566ae41SRobert Weinmeister        $data = "import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid{$version}/dist/mermaid.esm.min.mjs';{$init}";
136b566ae41SRobert Weinmeister        $event->data['script'][] = [
137b566ae41SRobert Weinmeister            'type'    => 'module',
138b566ae41SRobert Weinmeister            'charset' => 'utf-8',
139b566ae41SRobert Weinmeister            '_data'   => $data,
140b566ae41SRobert Weinmeister        ];
141b566ae41SRobert Weinmeister    }
142b566ae41SRobert Weinmeister
143b566ae41SRobert Weinmeister    private function addScript(Doku_Event $event, string $version, string $init): void {
144b566ae41SRobert Weinmeister        $event->data['script'][] = [
145b566ae41SRobert Weinmeister            'type'    => 'text/javascript',
146b566ae41SRobert Weinmeister            'charset' => 'utf-8',
147b566ae41SRobert Weinmeister            'src'     => "https://cdn.jsdelivr.net/npm/mermaid{$version}/dist/mermaid.min.js",
148b566ae41SRobert Weinmeister        ];
149b566ae41SRobert Weinmeister
150b566ae41SRobert Weinmeister        $event->data['script'][] = [
151b566ae41SRobert Weinmeister            'type'    => 'text/javascript',
152b566ae41SRobert Weinmeister            'charset' => 'utf-8',
153b566ae41SRobert Weinmeister            '_data'   => $init,
154b566ae41SRobert Weinmeister        ];
155b566ae41SRobert Weinmeister    }
156b566ae41SRobert Weinmeister
157b74fbfffSRobert Weinmeister    private function pageIncludesMermaid(): bool {
158b74fbfffSRobert Weinmeister        // true if the mermaid tag is used
159b74fbfffSRobert Weinmeister        // the include plugin can hide this fact, so we need a separate check for it
160c769b866SMartin Lormes        global $ACT;
161c769b866SMartin Lormes        global $TEXT;
162c769b866SMartin Lormes        if ('preview'==$ACT) {
163c769b866SMartin Lormes            $wikiText = $TEXT;
164c769b866SMartin Lormes        } else {
165b74fbfffSRobert Weinmeister            $wikiText = rawWiki(getID());
166c769b866SMartin Lormes        }
167c769b866SMartin Lormes
168c769b866SMartin Lormes        if ('edit'==$ACT) {
169c769b866SMartin Lormes            return false;
170c769b866SMartin Lormes        }
171b74fbfffSRobert Weinmeister        if (str_contains($wikiText, '<mermaid') || str_contains($wikiText, '{{page>') || str_contains($wikiText, '{{section>') || str_contains($wikiText, '{{namespace>') || str_contains($wikiText, '{{tagtopic>')) {
172b74fbfffSRobert Weinmeister            return true;
173b74fbfffSRobert Weinmeister        }
174b74fbfffSRobert Weinmeister
175b74fbfffSRobert Weinmeister        return false;
176b74fbfffSRobert Weinmeister    }
177b74fbfffSRobert Weinmeister
178b566ae41SRobert Weinmeister    /**
179b566ae41SRobert Weinmeister     * Load the Mermaid library and configuration into the page.
180b566ae41SRobert Weinmeister     *
181b566ae41SRobert Weinmeister     * @param Doku_Event $event DokuWiki event object
182b566ae41SRobert Weinmeister     * @param mixed $param Unused parameter.
183b566ae41SRobert Weinmeister     */
184b566ae41SRobert Weinmeister    public function load(Doku_Event $event, $param): void {
185ea08b541SRobert Weinmeister         // only load mermaid if it is needed
186b74fbfffSRobert Weinmeister        if (!$this->pageIncludesMermaid()) {
187ea08b541SRobert Weinmeister            return;
188ea08b541SRobert Weinmeister        }
189ea08b541SRobert Weinmeister
1904c8bd9ffSRobert Weinmeister        $theme = $this->getConf('theme');
19155e3db93SRobert Weinmeister        $look = $this->getConf('look');
19255e3db93SRobert Weinmeister        $logLevel = $this->getConf('logLevel');
193b566ae41SRobert Weinmeister        $init = "mermaid.initialize({startOnLoad: true, logLevel: '$logLevel', theme: '$theme', look: '$look'});";
1944c8bd9ffSRobert Weinmeister
195b566ae41SRobert Weinmeister        $location = $this->getConf('location');
196b566ae41SRobert Weinmeister        $versions = [
1976fcac025SRobert Weinmeister            'latest'     => '',
198*a03c2010SRobert Weinmeister            'remote1096' => '@10.9.6',
19992788e2aSRobert Weinmeister            'remote1095' => '@10.9.5',
200a788b843SRobert Weinmeister            'remote1091' => '@10.9.1',
2016fcac025SRobert Weinmeister            'remote108'  => '@10.8.0',
2026fcac025SRobert Weinmeister            'remote106'  => '@10.6.1',
2036fcac025SRobert Weinmeister            'remote104'  => '@10.4.0',
2046fcac025SRobert Weinmeister            'remote103'  => '@10.3.1',
2056fcac025SRobert Weinmeister            'remote102'  => '@10.2.4',
2066fcac025SRobert Weinmeister            'remote101'  => '@10.1.0',
207b566ae41SRobert Weinmeister            'remote100'  => '@10.0.2',
208b566ae41SRobert Weinmeister            'remote94'   => '@9.4.3',
209b566ae41SRobert Weinmeister            'remote943'  => '@9.4.3',
210b566ae41SRobert Weinmeister            'remote93'   => '@9.3.0',
211b566ae41SRobert Weinmeister        ];
21246a60b4fSRobertWeinmeister
213b566ae41SRobert Weinmeister        // add the appropriate Mermaid script based on the location configuration
214b566ae41SRobert Weinmeister        match ($location) {
215b566ae41SRobert Weinmeister            'local' => $this->addLocalScript($event),
216*a03c2010SRobert Weinmeister            'latest', 'remote1096', 'remote1095', 'remote1091', 'remote108', 'remote106', 'remote104', 'remote103', 'remote102', 'remote101', 'remote100'
217b566ae41SRobert Weinmeister                => $this->addEsmScript($event, $versions[$location], $init),
218b566ae41SRobert Weinmeister            'remote94', 'remote943', 'remote93'
219b566ae41SRobert Weinmeister                => $this->addScript($event, $versions[$location], $init),
220b566ae41SRobert Weinmeister            default => null,
221b566ae41SRobert Weinmeister        };
222b566ae41SRobert Weinmeister
223b566ae41SRobert Weinmeister        $event->data['link'][] = [
22446a60b4fSRobertWeinmeister            'rel'     => 'stylesheet',
22546a60b4fSRobertWeinmeister            'type'    => 'text/css',
22646a60b4fSRobertWeinmeister            'href'    => DOKU_BASE . "lib/plugins/mermaid/mermaid.css",
227b566ae41SRobert Weinmeister        ];
2286fcac025SRobert Weinmeister
2296fcac025SRobert Weinmeister        // remove the search highlight from DokuWiki as it interferes with the Mermaid parsing/rendering
230b566ae41SRobert Weinmeister        $event->data['script'][] = [
2316fcac025SRobert Weinmeister            'type'    => 'text/javascript',
2326fcac025SRobert Weinmeister            'charset' => 'utf-8',
2336fcac025SRobert Weinmeister            '_data' => "document.addEventListener('DOMContentLoaded', function() {
2346fcac025SRobert Weinmeister                            jQuery('.mermaid').each(function() {
2356fcac025SRobert Weinmeister                                var modifiedContent = jQuery(this).html().replace(/<span class=\"search_hit\">(.+?)<\/span>/g, '$1');
2366fcac025SRobert Weinmeister                                jQuery(this).html(modifiedContent);
2376fcac025SRobert Weinmeister                             })
2386fcac025SRobert Weinmeister                        });"
239b566ae41SRobert Weinmeister        ];
2401da12d6eSRobert Weinmeister
2411da12d6eSRobert Weinmeister        // adds image-save capability
2421da12d6eSRobert Weinmeister        // First: Wait until the DOM content is fully loaded
2431da12d6eSRobert Weinmeister        // Second: Wait until Mermaid has changed the dokuwiki content to an svg
2441da12d6eSRobert Weinmeister        $event->data['script'][] = array
2451da12d6eSRobert Weinmeister        (
2461da12d6eSRobert Weinmeister            'type'    => 'text/javascript',
2471da12d6eSRobert Weinmeister            'charset' => 'utf-8',
2481da12d6eSRobert Weinmeister            '_data' => "
2491da12d6eSRobert Weinmeisterdocument.addEventListener('DOMContentLoaded', function() {
2501da12d6eSRobert Weinmeister     var config = {
2511da12d6eSRobert Weinmeister        childList: true,
2521da12d6eSRobert Weinmeister        subtree: true,
2531da12d6eSRobert Weinmeister        characterData: true
2541da12d6eSRobert Weinmeister    };
2551da12d6eSRobert Weinmeister
256172fa282SRobert Weinmeister    function callDokuWikiPHP(mode, index, mermaidRaw, mermaidSvg) {
257172fa282SRobert Weinmeister    jQuery.post(
258172fa282SRobert Weinmeister        DOKU_BASE + 'lib/exe/ajax.php',
259172fa282SRobert Weinmeister        {
260172fa282SRobert Weinmeister            call: 'plugin_mermaid',
261172fa282SRobert Weinmeister            mode: mode,
262172fa282SRobert Weinmeister            mermaidindex: index,
263172fa282SRobert Weinmeister            pageid: '".getID()."',
264172fa282SRobert Weinmeister            svg: encodeURIComponent(mermaidSvg)
265172fa282SRobert Weinmeister        },
266172fa282SRobert Weinmeister        function(response) {
267172fa282SRobert Weinmeister            if(response.status == 'success') {
268172fa282SRobert Weinmeister                location.reload(true);
269172fa282SRobert Weinmeister            }
270172fa282SRobert Weinmeister            else {
271172fa282SRobert Weinmeister                alert(response.data[0]);
272172fa282SRobert Weinmeister            }
273172fa282SRobert Weinmeister        },
274172fa282SRobert Weinmeister        'json'
275172fa282SRobert Weinmeister    )};
276172fa282SRobert Weinmeister
277172fa282SRobert Weinmeister    jQuery('.mermaidlocked, .mermaid').each(function(index, element) {
2781da12d6eSRobert Weinmeister        document.getElementById('mermaidContainer' + index).addEventListener('mouseenter', function() {
2790ab8bda3Sdjh             var fieldset = document.getElementById('mermaidFieldset' + index);
2800ab8bda3Sdjh             if (fieldset) {
2810ab8bda3Sdjh                    fieldset.style.display = 'flex';
2820ab8bda3Sdjh             }
2831da12d6eSRobert Weinmeister        });
2841da12d6eSRobert Weinmeister        document.getElementById('mermaidContainer' + index).addEventListener('mouseleave', function() {
2850ab8bda3Sdjh             var fieldset = document.getElementById('mermaidFieldset' + index);
2860ab8bda3Sdjh             if (fieldset) {
2870ab8bda3Sdjh                    fieldset.style.display = 'none';
2880ab8bda3Sdjh             }
2891da12d6eSRobert Weinmeister        });
2901da12d6eSRobert Weinmeister
291172fa282SRobert Weinmeister        if(jQuery(element).hasClass('mermaidlocked')) {
2920ab8bda3Sdjh            var buttonSave = document.getElementById('mermaidButtonSave' + index);
2930ab8bda3Sdjh            if (buttonSave) {
2940ab8bda3Sdjh                buttonSave.addEventListener('click', () => {
2951da12d6eSRobert Weinmeister                    var svgContent = element.innerHTML.trim();
2961da12d6eSRobert Weinmeister                    var blob = new Blob([svgContent], { type: 'image/svg+xml' });
2971da12d6eSRobert Weinmeister                    var link = document.createElement('a');
2981da12d6eSRobert Weinmeister                    link.href = URL.createObjectURL(blob);
2991da12d6eSRobert Weinmeister                    link.download = 'mermaid' + index + '.svg';
3001da12d6eSRobert Weinmeister                    link.click();
3011da12d6eSRobert Weinmeister                    URL.revokeObjectURL(link.href);
3021da12d6eSRobert Weinmeister                });
3030ab8bda3Sdjh            }
3040ab8bda3Sdjh            var buttonPermanent = document.getElementById('mermaidButtonPermanent' + index);
3050ab8bda3Sdjh            if (buttonPermanent) {
3060ab8bda3Sdjh                buttonPermanent.addEventListener('click', () => {
307172fa282SRobert Weinmeister                    if(confirm('Unlock Mermaid diagram?')) {
308172fa282SRobert Weinmeister                        callDokuWikiPHP('unlock', index, originalMermaidContent, element.innerHTML.trim());
309172fa282SRobert Weinmeister                    }
310172fa282SRobert Weinmeister                });
311172fa282SRobert Weinmeister            }
3120ab8bda3Sdjh        }
313172fa282SRobert Weinmeister
314172fa282SRobert Weinmeister        if(jQuery(element).hasClass('mermaid')) {
315172fa282SRobert Weinmeister            var originalMermaidContent = element.innerHTML;
316172fa282SRobert Weinmeister            var observer = new MutationObserver(function(mutations) {
317172fa282SRobert Weinmeister                mutations.forEach(function(mutation) {
318172fa282SRobert Weinmeister                    if (mutation.type === 'childList' && element.innerHTML.startsWith('<svg')) {
3190ab8bda3Sdjh                        var saveButton = document.getElementById('mermaidButtonSave' + index);
3200ab8bda3Sdjh                        if (saveButton) {
3210ab8bda3Sdjh                            saveButton.addEventListener('click', () => {
322172fa282SRobert Weinmeister                                var svgContent = element.innerHTML.trim();
323172fa282SRobert Weinmeister                                var blob = new Blob([svgContent], { type: 'image/svg+xml' });
324172fa282SRobert Weinmeister                                var link = document.createElement('a');
325172fa282SRobert Weinmeister                                link.href = URL.createObjectURL(blob);
326172fa282SRobert Weinmeister                                link.download = 'mermaid' + index + '.svg';
327172fa282SRobert Weinmeister                                link.click();
328172fa282SRobert Weinmeister                                URL.revokeObjectURL(link.href);
329172fa282SRobert Weinmeister                            });
3300ab8bda3Sdjh                        }
3310ab8bda3Sdjh                        var buttonPermanent = document.getElementById('mermaidButtonPermanent' + index);
3320ab8bda3Sdjh                        if (buttonPermanent) {
3330ab8bda3Sdjh                            buttonPermanent.addEventListener('click', () => {
334172fa282SRobert Weinmeister                                    if(confirm('Lock Mermaid diagram? [experimental]')) {
335172fa282SRobert Weinmeister                                        callDokuWikiPHP('lock', index, originalMermaidContent, element.innerHTML.trim());
336172fa282SRobert Weinmeister                                    }
337172fa282SRobert Weinmeister                            });
3381da12d6eSRobert Weinmeister                        }
3390ab8bda3Sdjh                    }
3401da12d6eSRobert Weinmeister                });
3411da12d6eSRobert Weinmeister            });
3421da12d6eSRobert Weinmeister            observer.observe(element, config);
343172fa282SRobert Weinmeister        }
3441da12d6eSRobert Weinmeister    });
3451da12d6eSRobert Weinmeister});"
3461da12d6eSRobert Weinmeister        );
34746a60b4fSRobertWeinmeister    }
34846a60b4fSRobertWeinmeister}
349