xref: /plugin/mermaid/action.php (revision 1da12d6e8ab019f8bc2014b3dc7bf0b52095a071)
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
946a60b4fSRobertWeinmeisterclass action_plugin_mermaid extends \dokuwiki\Extension\ActionPlugin
1046a60b4fSRobertWeinmeister{
1146a60b4fSRobertWeinmeister    /** @inheritDoc */
1246a60b4fSRobertWeinmeister    public function register(Doku_Event_Handler $controller)
1346a60b4fSRobertWeinmeister    {
1446a60b4fSRobertWeinmeister        $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'load');
1546a60b4fSRobertWeinmeister    }
1646a60b4fSRobertWeinmeister
1746a60b4fSRobertWeinmeister    public function load(Doku_Event $event, $param)
1846a60b4fSRobertWeinmeister    {
196e5341c6SRobert Weinmeister        // Can be changed for debugging Mermaid
206e5341c6SRobert Weinmeister        // https://mermaid.js.org/config/directives.html#changing-loglevel-via-directive
216e5341c6SRobert Weinmeister        define("MERMAIDLOGLEVEL", "error");
226e5341c6SRobert Weinmeister
234c8bd9ffSRobert Weinmeister        $theme = $this->getConf('theme');
244c8bd9ffSRobert Weinmeister        $init = "mermaid.initialize({startOnLoad: true, logLevel: '".MERMAIDLOGLEVEL."', theme: '".$theme."'});";
256fcac025SRobert Weinmeister        $location = $this->getConf('location');
264c8bd9ffSRobert Weinmeister
276fcac025SRobert Weinmeister        switch ($location) {
282d4b7fc2SRobert Weinmeister            case 'local':
2946a60b4fSRobertWeinmeister                $event->data['script'][] = array
3046a60b4fSRobertWeinmeister                (
3146a60b4fSRobertWeinmeister                    'type'    => 'text/javascript',
3246a60b4fSRobertWeinmeister                    'charset' => 'utf-8',
332d4b7fc2SRobert Weinmeister                    'src' => DOKU_BASE.'lib/plugins/mermaid/mermaid.min.js'
3446a60b4fSRobertWeinmeister                );
352d4b7fc2SRobert Weinmeister                break;
362d4b7fc2SRobert Weinmeister            case 'latest':
37a788b843SRobert Weinmeister            case 'remote1091':
38a788b843SRobert Weinmeister            // options remote108, remote106, remote104, remote103, remote102, remote101, remote100 are depreciated and only included for backward compatibility
396fcac025SRobert Weinmeister            case 'remote108':
40a612c7d6SRobert Weinmeister            case 'remote106':
418eaa3f3bSRobert Weinmeister            case 'remote104':
427d8a2661SRobert Weinmeister            case 'remote103':
434df3d176SRobert Weinmeister            case 'remote102':
445f50b169SRobert Weinmeister            case 'remote101':
456e5341c6SRobert Weinmeister            case 'remote100':
466fcac025SRobert Weinmeister                $versions = array(
476fcac025SRobert Weinmeister                    'latest' => '',
48a788b843SRobert Weinmeister                    'remote1091' => '@10.9.1',
496fcac025SRobert Weinmeister                    'remote108' => '@10.8.0',
506fcac025SRobert Weinmeister                    'remote106' => '@10.6.1',
516fcac025SRobert Weinmeister                    'remote104' => '@10.4.0',
526fcac025SRobert Weinmeister                    'remote103' => '@10.3.1',
536fcac025SRobert Weinmeister                    'remote102' => '@10.2.4',
546fcac025SRobert Weinmeister                    'remote101' => '@10.1.0',
556fcac025SRobert Weinmeister                    'remote100' => '@10.0.2'
566fcac025SRobert Weinmeister                );
576fcac025SRobert Weinmeister                $data = "import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid".$versions[$location]."/dist/mermaid.esm.min.mjs';".$init;
586e5341c6SRobert Weinmeister                $event->data['script'][] = array
596e5341c6SRobert Weinmeister                (
606e5341c6SRobert Weinmeister                    'type'    => 'module',
616e5341c6SRobert Weinmeister                    'charset' => 'utf-8',
626fcac025SRobert Weinmeister                    '_data' => $data
636e5341c6SRobert Weinmeister                );
646e5341c6SRobert Weinmeister                break;
65a788b843SRobert Weinmeister            // option remote94 is depreciated and only included for backward compatibility
666e5341c6SRobert Weinmeister            case 'remote94':
67a788b843SRobert Weinmeister            case 'remote943':
686e5341c6SRobert Weinmeister                $event->data['script'][] = array
696e5341c6SRobert Weinmeister                (
706e5341c6SRobert Weinmeister                    'type'    => 'text/javascript',
716e5341c6SRobert Weinmeister                    'charset' => 'utf-8',
724df3d176SRobert Weinmeister                    'src' => 'https://cdn.jsdelivr.net/npm/mermaid@9.4.3/dist/mermaid.min.js'
736e5341c6SRobert Weinmeister                );
746e5341c6SRobert Weinmeister                break;
75a788b843SRobert Weinmeister            // option remote93 is depreciated and only included for backward compatibility
762d4b7fc2SRobert Weinmeister            case 'remote93':
772d4b7fc2SRobert Weinmeister                $event->data['script'][] = array
782d4b7fc2SRobert Weinmeister                (
792d4b7fc2SRobert Weinmeister                    'type'    => 'text/javascript',
802d4b7fc2SRobert Weinmeister                    'charset' => 'utf-8',
814df3d176SRobert Weinmeister                    'src' => 'https://cdn.jsdelivr.net/npm/mermaid@9.3.0/dist/mermaid.min.js'
822d4b7fc2SRobert Weinmeister                );
832d4b7fc2SRobert Weinmeister                break;
842d4b7fc2SRobert Weinmeister            default:
852d4b7fc2SRobert Weinmeister        }
8646a60b4fSRobertWeinmeister
8746a60b4fSRobertWeinmeister        $event->data['link'][] = array
8846a60b4fSRobertWeinmeister        (
8946a60b4fSRobertWeinmeister            'rel'     => 'stylesheet',
9046a60b4fSRobertWeinmeister            'type'    => 'text/css',
9146a60b4fSRobertWeinmeister            'href'    => DOKU_BASE."lib/plugins/mermaid/mermaid.css",
9246a60b4fSRobertWeinmeister        );
9346a60b4fSRobertWeinmeister
946fcac025SRobert Weinmeister        switch ($location) {
954df3d176SRobert Weinmeister            case 'local':
96a788b843SRobert Weinmeister            case 'remote943':
97a788b843SRobert Weinmeister            // options remote94 and remote93 are depreciated and only included for backward compatibility
986e5341c6SRobert Weinmeister            case 'remote94':
996e5341c6SRobert Weinmeister            case 'remote93':
10046a60b4fSRobertWeinmeister                $event->data['script'][] = array
10146a60b4fSRobertWeinmeister                (
10246a60b4fSRobertWeinmeister                    'type'    => 'text/javascript',
10346a60b4fSRobertWeinmeister                    'charset' => 'utf-8',
1044c8bd9ffSRobert Weinmeister                    '_data'   => $init
10546a60b4fSRobertWeinmeister                );
1066e5341c6SRobert Weinmeister                break;
1076e5341c6SRobert Weinmeister            default:
1086e5341c6SRobert Weinmeister        }
1096fcac025SRobert Weinmeister
1106fcac025SRobert Weinmeister        // remove the search highlight from DokuWiki as it interferes with the Mermaid parsing/rendering
1116fcac025SRobert Weinmeister        $event->data['script'][] = array
1126fcac025SRobert Weinmeister        (
1136fcac025SRobert Weinmeister            'type'    => 'text/javascript',
1146fcac025SRobert Weinmeister            'charset' => 'utf-8',
1156fcac025SRobert Weinmeister            '_data' => "document.addEventListener('DOMContentLoaded', function() {
1166fcac025SRobert Weinmeister                            jQuery('.mermaid').each(function() {
1176fcac025SRobert Weinmeister                                var modifiedContent = jQuery(this).html().replace(/<span class=\"search_hit\">(.+?)<\/span>/g, '$1');
1186fcac025SRobert Weinmeister                                jQuery(this).html(modifiedContent);
1196fcac025SRobert Weinmeister                             })
1206fcac025SRobert Weinmeister                        });"
1216fcac025SRobert Weinmeister        );
122*1da12d6eSRobert Weinmeister
123*1da12d6eSRobert Weinmeister        // adds image-save capability
124*1da12d6eSRobert Weinmeister        // First: Wait until the DOM content is fully loaded
125*1da12d6eSRobert Weinmeister        // Second: Wait until Mermaid has changed the dokuwiki content to an svg
126*1da12d6eSRobert Weinmeister        $event->data['script'][] = array
127*1da12d6eSRobert Weinmeister        (
128*1da12d6eSRobert Weinmeister            'type'    => 'text/javascript',
129*1da12d6eSRobert Weinmeister            'charset' => 'utf-8',
130*1da12d6eSRobert Weinmeister            '_data' => "
131*1da12d6eSRobert Weinmeisterdocument.addEventListener('DOMContentLoaded', function() {
132*1da12d6eSRobert Weinmeister     var config = {
133*1da12d6eSRobert Weinmeister        childList: true,
134*1da12d6eSRobert Weinmeister        subtree: true,
135*1da12d6eSRobert Weinmeister        characterData: true
136*1da12d6eSRobert Weinmeister    };
137*1da12d6eSRobert Weinmeister
138*1da12d6eSRobert Weinmeister    jQuery('.mermaid').each(function(index, element) {
139*1da12d6eSRobert Weinmeister        var observer = new MutationObserver(function(mutations) {
140*1da12d6eSRobert Weinmeister            mutations.forEach(function(mutation) {
141*1da12d6eSRobert Weinmeister                if (mutation.type === 'childList' && element.innerHTML.startsWith('<svg')) {
142*1da12d6eSRobert Weinmeister                    document.getElementById('mermaidContainer' + index).addEventListener('mouseenter', function() {
143*1da12d6eSRobert Weinmeister                        document.getElementById('mermaidButton' + index).style.display = 'block';
144*1da12d6eSRobert Weinmeister                    });
145*1da12d6eSRobert Weinmeister                    document.getElementById('mermaidContainer' + index).addEventListener('mouseleave', function() {
146*1da12d6eSRobert Weinmeister                        document.getElementById('mermaidButton' + index).style.display = 'none';
147*1da12d6eSRobert Weinmeister                    });
148*1da12d6eSRobert Weinmeister
149*1da12d6eSRobert Weinmeister                    document.getElementById('mermaidButton' + index).addEventListener('click', () => {
150*1da12d6eSRobert Weinmeister                        var svgContent = element.innerHTML.trim();
151*1da12d6eSRobert Weinmeister                        var blob = new Blob([svgContent], { type: 'image/svg+xml' });
152*1da12d6eSRobert Weinmeister                        var link = document.createElement('a');
153*1da12d6eSRobert Weinmeister                        link.href = URL.createObjectURL(blob);
154*1da12d6eSRobert Weinmeister                        link.download = 'mermaid' + index + '.svg';
155*1da12d6eSRobert Weinmeister                        link.click();
156*1da12d6eSRobert Weinmeister                        URL.revokeObjectURL(link.href);
157*1da12d6eSRobert Weinmeister                    });
158*1da12d6eSRobert Weinmeister                }
159*1da12d6eSRobert Weinmeister            });
160*1da12d6eSRobert Weinmeister        });
161*1da12d6eSRobert Weinmeister
162*1da12d6eSRobert Weinmeister        observer.observe(element, config);
163*1da12d6eSRobert Weinmeister    });
164*1da12d6eSRobert Weinmeister});"
165*1da12d6eSRobert Weinmeister        );
16646a60b4fSRobertWeinmeister    }
16746a60b4fSRobertWeinmeister}
168*1da12d6eSRobert Weinmeister
169