1<?php 2/** 3 * DokuWiki Plugin mermaid (Syntax Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author Robert Weinmeister <develop@weinmeister.org> 7 */ 8 9use dokuwiki\Parsing\Parser; 10 11class syntax_plugin_mermaid extends \dokuwiki\Extension\SyntaxPlugin 12{ 13 const DOKUWIKI_LINK_START_MERMAID = '<code>DOKUWIKILINKSTARTMERMAID</code>'; 14 const DOKUWIKI_LINK_END_MERMAID = '<code>DOKUWIKILINKENDMERMAID</code>'; 15 const DOKUWIKI_LINK_SPLITTER ='--'; 16 17 function protect_brackets_from_dokuwiki($text) 18 { 19 $splitText = explode(self::DOKUWIKI_LINK_SPLITTER, $text); 20 foreach ($splitText as $key => $line) 21 { 22 $splitText[$key] = preg_replace('/(?<!["\[(\s])(\[\[)(.*)(\]\])/', self::DOKUWIKI_LINK_START_MERMAID . '$2' . self::DOKUWIKI_LINK_END_MERMAID, $line); 23 } 24 $text = implode(self::DOKUWIKI_LINK_SPLITTER, $splitText); 25 return $text; 26 } 27 28 function remove_protection_of_brackets_from_dokuwiki($text) 29 { 30 return str_replace(self::DOKUWIKI_LINK_START_MERMAID, '[[', str_replace(self::DOKUWIKI_LINK_END_MERMAID, ']]', $text)); 31 } 32 33 /** @inheritDoc */ 34 function getType() 35 { 36 return 'container'; 37 } 38 39 /** @inheritDoc */ 40 function getSort() 41 { 42 return 150; 43 } 44 45 /** 46 * Connect lookup pattern to lexer. 47 * 48 * @param string $mode Parser mode 49 */ 50 function connectTo($mode) 51 { 52 $this->Lexer->addEntryPattern('<mermaid.*?>(?=.*?</mermaid>)',$mode,'plugin_mermaid'); 53 } 54 55 function postConnect() 56 { 57 $this->Lexer->addExitPattern('</mermaid>','plugin_mermaid'); 58 } 59 60 /** 61 * Handle matches of the Mermaid syntax 62 */ 63 function handle($match, $state, $pos, Doku_Handler $handler) 64 { 65 switch ($state) { 66 case DOKU_LEXER_ENTER: 67 return array($state, $match); 68 case DOKU_LEXER_UNMATCHED: 69 return array($state, $match); 70 case DOKU_LEXER_EXIT: 71 return array($state, ''); 72 } 73 return false; 74 } 75 76 /** 77 * Render xhtml output or metadata 78 */ 79 function render($mode, Doku_Renderer $renderer, $indata) 80 { 81 if($mode == 'xhtml'){ 82 list($state, $match) = $indata; 83 switch ($state) { 84 case DOKU_LEXER_ENTER: 85 $values = explode(" ", $match); 86 $divwidth = count($values) < 2 ? 'auto' : $values[1]; 87 $divheight = count($values) < 3 ? 'auto' : substr($values[2], 0, -1); 88 $renderer->doc .= '<div class="mermaid" style="width:'.$divwidth.'; height:'.$divheight.'">'; 89 break; 90 case DOKU_LEXER_UNMATCHED: 91 $explodedMatch = explode("\n", $match); 92 $israwmode = isset($explodedMatch[1]) && strpos($explodedMatch[1], 'raw') !== false; 93 if($israwmode) 94 { 95 array_shift($explodedMatch); 96 array_shift($explodedMatch); 97 $actualContent = implode("\n", $explodedMatch); 98 $renderer->doc .= $actualContent; 99 } 100 else 101 { 102 $instructions = $this->p_get_instructions($this->protect_brackets_from_dokuwiki($match)); 103 $xhtml = $this->remove_protection_of_brackets_from_dokuwiki($this->p_render($instructions)); 104 $renderer->doc .= preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $xhtml); 105 } 106 break; 107 case DOKU_LEXER_EXIT: 108 $renderer->doc .= "\r\n</div>"; 109 break; 110 } 111 return true; 112 } 113 return false; 114 } 115 116 /* 117 * Get the parser instructions suitable for the mermaid 118 * 119 */ 120 function p_get_instructions($text) 121 { 122 //import parser classes and mode definitions 123 require_once DOKU_INC . 'inc/parser/parser.php'; 124 125 // https://www.dokuwiki.org/devel:parser 126 // https://www.dokuwiki.org/devel:parser#basic_invocation 127 // Create the parser and the handler 128 $Parser = new Parser(new Doku_Handler()); 129 130 $modes = array(); 131 132 // add default modes 133 $std_modes = array( 'internallink', 'media', 'externallink'); 134 135 foreach($std_modes as $m) 136 { 137 $class = 'dokuwiki\\Parsing\\ParserMode\\'.ucfirst($m); 138 $obj = new $class(); 139 $modes[] = array( 140 'sort' => $obj->getSort(), 141 'mode' => $m, 142 'obj' => $obj 143 ); 144 } 145 146 // add formatting modes 147 $fmt_modes = array( 'strong', 'emphasis', 'underline', 'monospace', 'subscript', 'superscript', 'deleted'); 148 foreach($fmt_modes as $m) 149 { 150 $obj = new \dokuwiki\Parsing\ParserMode\Formatting($m); 151 $modes[] = array( 152 'sort' => $obj->getSort(), 153 'mode' => $m, 154 'obj' => $obj 155 ); 156 } 157 158 //add modes to parser 159 foreach($modes as $mode) 160 { 161 $Parser->addMode($mode['mode'],$mode['obj']); 162 } 163 164 // Do the parsing 165 $p = $Parser->parse($text); 166 167 return $p; 168 } 169 170 public function p_render($instructions) 171 { 172 $Renderer = p_get_renderer('mermaid'); 173 174 // Loop through the instructions 175 foreach ($instructions as $instruction) { 176 if(method_exists($Renderer, $instruction[0])){ 177 call_user_func_array(array(&$Renderer, $instruction[0]), $instruction[1] ? $instruction[1] : array()); 178 } 179 } 180 181 return $Renderer->doc; 182 } 183} 184