1c6570b71SRobertWeinmeister<?php 2c6570b71SRobertWeinmeister/** 3c6570b71SRobertWeinmeister * DokuWiki Plugin mermaid (Syntax Component) 4c6570b71SRobertWeinmeister * 5c6570b71SRobertWeinmeister * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6c6570b71SRobertWeinmeister * @author Robert Weinmeister <develop@weinmeister.org> 7c6570b71SRobertWeinmeister */ 8c6570b71SRobertWeinmeister 9c6570b71SRobertWeinmeisteruse dokuwiki\Parsing\Parser; 10c6570b71SRobertWeinmeister 11c6570b71SRobertWeinmeisterclass syntax_plugin_mermaid extends \dokuwiki\Extension\SyntaxPlugin 12c6570b71SRobertWeinmeister{ 133543e422SRobert Weinmeister const DOKUWIKI_LINK_START_MERMAID = '<code>DOKUWIKILINKSTARTMERMAID</code>'; 143543e422SRobert Weinmeister const DOKUWIKI_LINK_END_MERMAID = '<code>DOKUWIKILINKENDMERMAID</code>'; 15f4ff867cSRobert Weinmeister const DOKUWIKI_LINK_SPLITTER ='--'; 163543e422SRobert Weinmeister 17*1da12d6eSRobert Weinmeister private $mermaidCounter = -1; 18*1da12d6eSRobert Weinmeister 193543e422SRobert Weinmeister function protect_brackets_from_dokuwiki($text) 203543e422SRobert Weinmeister { 21f4ff867cSRobert Weinmeister $splitText = explode(self::DOKUWIKI_LINK_SPLITTER, $text); 22f4ff867cSRobert Weinmeister foreach ($splitText as $key => $line) 23f4ff867cSRobert Weinmeister { 24f4ff867cSRobert Weinmeister $splitText[$key] = preg_replace('/(?<!["\[(\s])(\[\[)(.*)(\]\])/', self::DOKUWIKI_LINK_START_MERMAID . '$2' . self::DOKUWIKI_LINK_END_MERMAID, $line); 25f4ff867cSRobert Weinmeister } 26f4ff867cSRobert Weinmeister $text = implode(self::DOKUWIKI_LINK_SPLITTER, $splitText); 27f4ff867cSRobert Weinmeister return $text; 283543e422SRobert Weinmeister } 293543e422SRobert Weinmeister 303543e422SRobert Weinmeister function remove_protection_of_brackets_from_dokuwiki($text) 313543e422SRobert Weinmeister { 323543e422SRobert Weinmeister return str_replace(self::DOKUWIKI_LINK_START_MERMAID, '[[', str_replace(self::DOKUWIKI_LINK_END_MERMAID, ']]', $text)); 333543e422SRobert Weinmeister } 343543e422SRobert Weinmeister 35c6570b71SRobertWeinmeister /** @inheritDoc */ 36c6570b71SRobertWeinmeister function getType() 37c6570b71SRobertWeinmeister { 38c6570b71SRobertWeinmeister return 'container'; 39c6570b71SRobertWeinmeister } 40c6570b71SRobertWeinmeister 41c6570b71SRobertWeinmeister /** @inheritDoc */ 42c6570b71SRobertWeinmeister function getSort() 43c6570b71SRobertWeinmeister { 44c6570b71SRobertWeinmeister return 150; 45c6570b71SRobertWeinmeister } 46c6570b71SRobertWeinmeister 47c6570b71SRobertWeinmeister /** 48c6570b71SRobertWeinmeister * Connect lookup pattern to lexer. 49c6570b71SRobertWeinmeister * 50c6570b71SRobertWeinmeister * @param string $mode Parser mode 51c6570b71SRobertWeinmeister */ 52c6570b71SRobertWeinmeister function connectTo($mode) 53c6570b71SRobertWeinmeister { 54c6570b71SRobertWeinmeister $this->Lexer->addEntryPattern('<mermaid.*?>(?=.*?</mermaid>)',$mode,'plugin_mermaid'); 55c6570b71SRobertWeinmeister } 56c6570b71SRobertWeinmeister 57c6570b71SRobertWeinmeister function postConnect() 58c6570b71SRobertWeinmeister { 59c6570b71SRobertWeinmeister $this->Lexer->addExitPattern('</mermaid>','plugin_mermaid'); 60c6570b71SRobertWeinmeister } 61c6570b71SRobertWeinmeister 62c6570b71SRobertWeinmeister /** 637d8a2661SRobert Weinmeister * Handle matches of the Mermaid syntax 64c6570b71SRobertWeinmeister */ 65c6570b71SRobertWeinmeister function handle($match, $state, $pos, Doku_Handler $handler) 66c6570b71SRobertWeinmeister { 67c6570b71SRobertWeinmeister switch ($state) { 68c6570b71SRobertWeinmeister case DOKU_LEXER_ENTER: 69c6570b71SRobertWeinmeister return array($state, $match); 70c6570b71SRobertWeinmeister case DOKU_LEXER_UNMATCHED: 71c6570b71SRobertWeinmeister return array($state, $match); 72c6570b71SRobertWeinmeister case DOKU_LEXER_EXIT: 73c6570b71SRobertWeinmeister return array($state, ''); 74c6570b71SRobertWeinmeister } 75c6570b71SRobertWeinmeister return false; 76c6570b71SRobertWeinmeister } 77c6570b71SRobertWeinmeister 78c6570b71SRobertWeinmeister /** 79c6570b71SRobertWeinmeister * Render xhtml output or metadata 80c6570b71SRobertWeinmeister */ 81c6570b71SRobertWeinmeister function render($mode, Doku_Renderer $renderer, $indata) 82c6570b71SRobertWeinmeister { 83c6570b71SRobertWeinmeister if($mode == 'xhtml'){ 84c6570b71SRobertWeinmeister list($state, $match) = $indata; 85c6570b71SRobertWeinmeister switch ($state) { 86c6570b71SRobertWeinmeister case DOKU_LEXER_ENTER: 87*1da12d6eSRobert Weinmeister $this->mermaidCounter++; 88c6570b71SRobertWeinmeister $values = explode(" ", $match); 89c6570b71SRobertWeinmeister $divwidth = count($values) < 2 ? 'auto' : $values[1]; 90c6570b71SRobertWeinmeister $divheight = count($values) < 3 ? 'auto' : substr($values[2], 0, -1); 91*1da12d6eSRobert Weinmeister $renderer->doc .= '<div id="mermaidContainer'.$this->mermaidCounter.'" style="position: relative;"><span class="mermaid" id=mermaidContent'.$this->mermaidCounter.' style="width:'.$divwidth.'; height:'.$divheight.'">'; 92c6570b71SRobertWeinmeister break; 93c6570b71SRobertWeinmeister case DOKU_LEXER_UNMATCHED: 94cc24cea2SRobert Weinmeister $explodedMatch = explode("\n", $match); 95cc24cea2SRobert Weinmeister $israwmode = isset($explodedMatch[1]) && strpos($explodedMatch[1], 'raw') !== false; 96cc24cea2SRobert Weinmeister if($israwmode) 97cc24cea2SRobert Weinmeister { 98cc24cea2SRobert Weinmeister array_shift($explodedMatch); 99cc24cea2SRobert Weinmeister array_shift($explodedMatch); 100cc24cea2SRobert Weinmeister $actualContent = implode("\n", $explodedMatch); 101cc24cea2SRobert Weinmeister $renderer->doc .= $actualContent; 102cc24cea2SRobert Weinmeister } 103cc24cea2SRobert Weinmeister else 104cc24cea2SRobert Weinmeister { 1053543e422SRobert Weinmeister $instructions = $this->p_get_instructions($this->protect_brackets_from_dokuwiki($match)); 1063543e422SRobert Weinmeister $xhtml = $this->remove_protection_of_brackets_from_dokuwiki($this->p_render($instructions)); 107c6570b71SRobertWeinmeister $renderer->doc .= preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $xhtml); 108cc24cea2SRobert Weinmeister } 109c6570b71SRobertWeinmeister break; 110c6570b71SRobertWeinmeister case DOKU_LEXER_EXIT: 111*1da12d6eSRobert Weinmeister $renderer->doc .= "\r\n"; 112*1da12d6eSRobert Weinmeister $renderer->doc .= '</span><button id="mermaidButton'.$this->mermaidCounter.'" style="position: absolute; top: 0; left: 0; z-index: 10; display: none; padding: 0; margin: 0; border: none; background: none; width: 24px; height: 24px;"><svg fill="#000000" viewBox="0 0 52 52" enable-background="new 0 0 52 52" xml:space="preserve" style="width: 24px; height: 24px;"><path d="M37.1,4v13.6c0,1-0.8,1.9-1.9,1.9H13.9c-1,0-1.9-0.8-1.9-1.9V4H8C5.8,4,4,5.8,4,8v36c0,2.2,1.8,4,4,4h36 c2.2,0,4-1.8,4-4V11.2L40.8,4H37.1z M44.1,42.1c0,1-0.8,1.9-1.9,1.9H9.9c-1,0-1.9-0.8-1.9-1.9V25.4c0-1,0.8-1.9,1.9-1.9h32.3 c1,0,1.9,0.8,1.9,1.9V42.1z"/><path d="M24.8,13.6c0,1,0.8,1.9,1.9,1.9h4.6c1,0,1.9-0.8,1.9-1.9V4h-8.3L24.8,13.6L24.8,13.6z"/></svg></button></div>'; 113c6570b71SRobertWeinmeister break; 114c6570b71SRobertWeinmeister } 115c6570b71SRobertWeinmeister return true; 116c6570b71SRobertWeinmeister } 117c6570b71SRobertWeinmeister return false; 118c6570b71SRobertWeinmeister } 119c6570b71SRobertWeinmeister 120c6570b71SRobertWeinmeister /* 1217d8a2661SRobert Weinmeister * Get the parser instructions suitable for the mermaid 122c6570b71SRobertWeinmeister * 123c6570b71SRobertWeinmeister */ 124c6570b71SRobertWeinmeister function p_get_instructions($text) 125c6570b71SRobertWeinmeister { 126c6570b71SRobertWeinmeister //import parser classes and mode definitions 127c6570b71SRobertWeinmeister require_once DOKU_INC . 'inc/parser/parser.php'; 128c6570b71SRobertWeinmeister 129c6570b71SRobertWeinmeister // https://www.dokuwiki.org/devel:parser 130c6570b71SRobertWeinmeister // https://www.dokuwiki.org/devel:parser#basic_invocation 131c6570b71SRobertWeinmeister // Create the parser and the handler 132c6570b71SRobertWeinmeister $Parser = new Parser(new Doku_Handler()); 133c6570b71SRobertWeinmeister 134c6570b71SRobertWeinmeister $modes = array(); 135c6570b71SRobertWeinmeister 136c6570b71SRobertWeinmeister // add default modes 137c6570b71SRobertWeinmeister $std_modes = array( 'internallink', 'media', 'externallink'); 138c6570b71SRobertWeinmeister 1393543e422SRobert Weinmeister foreach($std_modes as $m) 1403543e422SRobert Weinmeister { 141c6570b71SRobertWeinmeister $class = 'dokuwiki\\Parsing\\ParserMode\\'.ucfirst($m); 142c6570b71SRobertWeinmeister $obj = new $class(); 143c6570b71SRobertWeinmeister $modes[] = array( 144c6570b71SRobertWeinmeister 'sort' => $obj->getSort(), 145c6570b71SRobertWeinmeister 'mode' => $m, 146c6570b71SRobertWeinmeister 'obj' => $obj 147c6570b71SRobertWeinmeister ); 148c6570b71SRobertWeinmeister } 149c6570b71SRobertWeinmeister 150c6570b71SRobertWeinmeister // add formatting modes 151c6570b71SRobertWeinmeister $fmt_modes = array( 'strong', 'emphasis', 'underline', 'monospace', 'subscript', 'superscript', 'deleted'); 152c6570b71SRobertWeinmeister foreach($fmt_modes as $m) 153c6570b71SRobertWeinmeister { 154c6570b71SRobertWeinmeister $obj = new \dokuwiki\Parsing\ParserMode\Formatting($m); 155c6570b71SRobertWeinmeister $modes[] = array( 156c6570b71SRobertWeinmeister 'sort' => $obj->getSort(), 157c6570b71SRobertWeinmeister 'mode' => $m, 158c6570b71SRobertWeinmeister 'obj' => $obj 159c6570b71SRobertWeinmeister ); 160c6570b71SRobertWeinmeister } 161c6570b71SRobertWeinmeister 162c6570b71SRobertWeinmeister //add modes to parser 163c6570b71SRobertWeinmeister foreach($modes as $mode) 164c6570b71SRobertWeinmeister { 165c6570b71SRobertWeinmeister $Parser->addMode($mode['mode'],$mode['obj']); 166c6570b71SRobertWeinmeister } 167c6570b71SRobertWeinmeister 168c6570b71SRobertWeinmeister // Do the parsing 169c6570b71SRobertWeinmeister $p = $Parser->parse($text); 170c6570b71SRobertWeinmeister 171c6570b71SRobertWeinmeister return $p; 172c6570b71SRobertWeinmeister } 173c6570b71SRobertWeinmeister 174c6570b71SRobertWeinmeister public function p_render($instructions) 175c6570b71SRobertWeinmeister { 176c6570b71SRobertWeinmeister $Renderer = p_get_renderer('mermaid'); 177c6570b71SRobertWeinmeister 178c6570b71SRobertWeinmeister // Loop through the instructions 179c6570b71SRobertWeinmeister foreach ($instructions as $instruction) { 180c6570b71SRobertWeinmeister if(method_exists($Renderer, $instruction[0])){ 181c6570b71SRobertWeinmeister call_user_func_array(array(&$Renderer, $instruction[0]), $instruction[1] ? $instruction[1] : array()); 182c6570b71SRobertWeinmeister } 183c6570b71SRobertWeinmeister } 184c6570b71SRobertWeinmeister 185c6570b71SRobertWeinmeister return $Renderer->doc; 186c6570b71SRobertWeinmeister } 187c6570b71SRobertWeinmeister} 188