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