1 <?php
2 
3 require_once(__DIR__ . '/../ComboStrap/PluginUtility.php');
4 
5 use ComboStrap\CallStack;
6 use ComboStrap\LogUtility;
7 use ComboStrap\PluginUtility;
8 use ComboStrap\Tag\WebCodeTag;
9 use ComboStrap\TagAttributes;
10 use ComboStrap\XmlTagProcessing;
11 
12 
13 /**
14  * Railroad
15  * https://github.com/Chrriis/rrdiagram-js/
16  */
17 class 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