xref: /plugin/combo/syntax/railroad.php (revision aadfa21126c9d260679cbb94217a54bbb242919b)
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