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