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 $instructions = $this->p_get_instructions($this->protect_brackets_from_dokuwiki($match)); 92 $xhtml = $this->remove_protection_of_brackets_from_dokuwiki($this->p_render($instructions)); 93 $renderer->doc .= preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $xhtml); 94 break; 95 case DOKU_LEXER_EXIT: 96 $renderer->doc .= "\r\n</div>"; 97 break; 98 } 99 return true; 100 } 101 return false; 102 } 103 104 /* 105 * Get the parser instructions suitable for the mermaid 106 * 107 */ 108 function p_get_instructions($text) 109 { 110 //import parser classes and mode definitions 111 require_once DOKU_INC . 'inc/parser/parser.php'; 112 113 // https://www.dokuwiki.org/devel:parser 114 // https://www.dokuwiki.org/devel:parser#basic_invocation 115 // Create the parser and the handler 116 $Parser = new Parser(new Doku_Handler()); 117 118 $modes = array(); 119 120 // add default modes 121 $std_modes = array( 'internallink', 'media', 'externallink'); 122 123 foreach($std_modes as $m) 124 { 125 $class = 'dokuwiki\\Parsing\\ParserMode\\'.ucfirst($m); 126 $obj = new $class(); 127 $modes[] = array( 128 'sort' => $obj->getSort(), 129 'mode' => $m, 130 'obj' => $obj 131 ); 132 } 133 134 // add formatting modes 135 $fmt_modes = array( 'strong', 'emphasis', 'underline', 'monospace', 'subscript', 'superscript', 'deleted'); 136 foreach($fmt_modes as $m) 137 { 138 $obj = new \dokuwiki\Parsing\ParserMode\Formatting($m); 139 $modes[] = array( 140 'sort' => $obj->getSort(), 141 'mode' => $m, 142 'obj' => $obj 143 ); 144 } 145 146 //add modes to parser 147 foreach($modes as $mode) 148 { 149 $Parser->addMode($mode['mode'],$mode['obj']); 150 } 151 152 // Do the parsing 153 $p = $Parser->parse($text); 154 155 return $p; 156 } 157 158 public function p_render($instructions) 159 { 160 $Renderer = p_get_renderer('mermaid'); 161 162 // Loop through the instructions 163 foreach ($instructions as $instruction) { 164 if(method_exists($Renderer, $instruction[0])){ 165 call_user_func_array(array(&$Renderer, $instruction[0]), $instruction[1] ? $instruction[1] : array()); 166 } 167 } 168 169 return $Renderer->doc; 170 } 171} 172