1<?php 2 3require_once(__DIR__ . '/../ComboStrap/PluginUtility.php'); 4 5use ComboStrap\CallStack; 6use ComboStrap\LogUtility; 7use ComboStrap\PluginUtility; 8use ComboStrap\TagAttributes; 9 10 11 12/** 13 * Railroad 14 * https://github.com/Chrriis/rrdiagram-js/ 15 */ 16class syntax_plugin_combo_railroad extends DokuWiki_Syntax_Plugin 17{ 18 19 const TAG = 'railroad'; 20 const CLASS_NAME = "railroad-bnf"; 21 22 const CANONICAL = self::TAG; 23 24 25 function getType(): string 26 { 27 return 'container'; 28 } 29 30 /** 31 * How DokuWiki will add P element 32 * 33 * * 'normal' - The plugin can be used inside paragraphs 34 * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 35 * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 36 * 37 * @see DokuWiki_Syntax_Plugin::getPType() 38 */ 39 function getPType(): string 40 { 41 return 'block'; 42 } 43 44 /** 45 * @return array 46 * Allow which kind of plugin inside 47 * 48 * No one of array('baseonly','container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs') 49 * because we manage self the content and we call self the parser 50 * 51 * Return an array of one or more of the mode types {@link $PARSER_MODES} in Parser.php 52 */ 53 function getAllowedTypes(): array 54 { 55 return array('baseonly', 'container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs'); 56 } 57 58 function getSort(): int 59 { 60 return 199; 61 } 62 63 public function accepts($mode): bool 64 { 65 return syntax_plugin_combo_preformatted::disablePreformatted($mode); 66 } 67 68 69 function connectTo($mode) 70 { 71 72 73 $pattern = PluginUtility::getContainerTagPattern(self::TAG); 74 $this->Lexer->addEntryPattern($pattern, $mode, PluginUtility::getModeFromTag($this->getPluginComponent())); 75 76 77 } 78 79 80 function postConnect() 81 { 82 83 $this->Lexer->addExitPattern('</' . self::TAG . '>', PluginUtility::getModeFromTag($this->getPluginComponent())); 84 85 86 } 87 88 /** 89 * 90 * The handle function goal is to parse the matched syntax through the pattern function 91 * and to return the result for use in the renderer 92 * This result is always cached until the page is modified. 93 * @param string $match 94 * @param int $state 95 * @param int $pos - byte position in the original source file 96 * @param Doku_Handler $handler 97 * @return array|bool 98 * @see DokuWiki_Syntax_Plugin::handle() 99 * 100 */ 101 function handle($match, $state, $pos, Doku_Handler $handler) 102 { 103 104 switch ($state) { 105 106 case DOKU_LEXER_ENTER : 107 $tagAttributes = TagAttributes::createFromTagMatch($match); 108 return array( 109 PluginUtility::STATE => $state, 110 PluginUtility::ATTRIBUTES => $tagAttributes->toCallStackArray() 111 ); 112 113 case DOKU_LEXER_UNMATCHED : 114 115 return PluginUtility::handleAndReturnUnmatchedData(self::TAG, $match, $handler); 116 117 118 case DOKU_LEXER_EXIT : 119 $callStack = CallStack::createFromHandler($handler); 120 $callStack->moveToPreviousCorrespondingOpeningCall(); 121 $bnfCode = ""; 122 $bnfCodeFound = false; 123 while ($actual = $callStack->next()) { 124 if (in_array($actual->getTagName(), syntax_plugin_combo_webcode::CODE_TAGS)) { 125 switch ($actual->getState()) { 126 case DOKU_LEXER_ENTER: 127 $actualCodeType = strtolower($actual->getType()); 128 if ($actualCodeType === 'bnf') { 129 $bnfCodeFound = true; 130 }; 131 break; 132 case DOKU_LEXER_UNMATCHED: 133 if($bnfCodeFound) { 134 $bnfCode = $actual->getCapturedContent(); 135 break 2; 136 } 137 break; 138 } 139 } 140 } 141 return array( 142 PluginUtility::STATE => $state, 143 PluginUtility::PAYLOAD => $bnfCode 144 ); 145 146 147 } 148 return array(); 149 150 } 151 152 /** 153 * Render the output 154 * @param string $format 155 * @param Doku_Renderer $renderer 156 * @param array $data - what the function handle() return'ed 157 * @return boolean - rendered correctly? (however, returned value is not used at the moment) 158 * @see DokuWiki_Syntax_Plugin::render() 159 * 160 * 161 */ 162 function render($format, Doku_Renderer $renderer, $data) 163 { 164 165 166 if ($format == 'xhtml') { 167 168 /** @var Doku_Renderer_xhtml $renderer */ 169 $state = $data [PluginUtility::STATE]; 170 switch ($state) { 171 case DOKU_LEXER_ENTER : 172 break; 173 174 case DOKU_LEXER_UNMATCHED : 175 176 $renderer->doc .= PluginUtility::renderUnmatched($data); 177 break; 178 179 case DOKU_LEXER_EXIT : 180 $bnfCode = $data[PluginUtility::PAYLOAD]; 181 if (!empty($bnfCode)) { 182 $snippetManager = PluginUtility::getSnippetManager(); 183 $snippetId = self::TAG; 184 $libraryId = "rrdiagram"; 185 $snippetManager->attachCssSnippetForBar($snippetId); 186 $snippetManager->attachJavascriptSnippetForBar($snippetId); 187 188 /** 189 * 190 * Calculation 191 * ` 192 * openssl dgst -sha256 -binary rrdiagram.js | openssl base64 -A 193 * ` 194 */ 195 $sha256integrity = "noP8Tag5vKjRfh3+8GXy5QSZqKnRt7WQe6I9rGVl+go="; 196 197 $snippetManager->attachTagsForBar($snippetId)->setTags( 198 array( 199 "script" => 200 [ 201 array( 202 "src" => PluginUtility::getResourceBaseUrl() . "/library/$libraryId/0.9.4.1/$libraryId.js", 203 "integrity" => "sha256-".$sha256integrity, 204 "crossorigin" => "anonymous" 205 ) 206 ], 207 208 ) 209 ); 210 /** 211 * This code is replaced at runtime by the diagram 212 */ 213 $class = self::CLASS_NAME; 214 $renderer->doc .= "<pre class=\"$class\">".hsc($bnfCode)."</pre>"; 215 } else { 216 LogUtility::msg("No code component with bnf grammar was found", LogUtility::LVL_MSG_WARNING, self::CANONICAL); 217 } 218 break; 219 220 } 221 return true; 222 } 223 224 225 // unsupported $mode 226 return false; 227 228 } 229 230 231} 232 233