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