1<?php
2
3require_once(__DIR__ . '/../ComboStrap/PluginUtility.php');
4
5use ComboStrap\CallStack;
6use ComboStrap\LogUtility;
7use ComboStrap\Mermaid;
8use ComboStrap\PluginUtility;
9use ComboStrap\TagAttributes;
10
11
12
13/**
14 * Mermaid
15 * https://mermaid-js.github.io/mermaid/
16 */
17class syntax_plugin_combo_mermaid extends DokuWiki_Syntax_Plugin
18{
19
20    const TAG = 'mermaid';
21
22    const CANONICAL = Mermaid::CANONICAL;
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                $openingCall = $callStack->moveToPreviousCorrespondingOpeningCall();
121                $attributes = $openingCall->getAttributes();
122                $mermaidCode = "";
123                $mermaidCodeFound = false;
124                while ($actual = $callStack->next()) {
125                    if (in_array($actual->getTagName(), syntax_plugin_combo_webcode::CODE_TAGS)) {
126                        switch ($actual->getState()) {
127                            case DOKU_LEXER_ENTER:
128                                $actualCodeType = strtolower($actual->getType());
129                                if ($actualCodeType === 'mermaid') {
130                                    $mermaidCodeFound = true;
131                                };
132                                break;
133                            case DOKU_LEXER_UNMATCHED:
134                                if ($mermaidCodeFound) {
135                                    $mermaidCode = $actual->getCapturedContent();
136                                    break 2;
137                                }
138                                break;
139                        }
140                    }
141                }
142                return array(
143                    PluginUtility::STATE => $state,
144                    PluginUtility::PAYLOAD => $mermaidCode,
145                    PluginUtility::ATTRIBUTES => $attributes
146                );
147
148
149        }
150        return array();
151
152    }
153
154    /**
155     * Render the output
156     * @param string $format
157     * @param Doku_Renderer $renderer
158     * @param array $data - what the function handle() return'ed
159     * @return boolean - rendered correctly? (however, returned value is not used at the moment)
160     * @see DokuWiki_Syntax_Plugin::render()
161     *
162     *
163     */
164    function render($format, Doku_Renderer $renderer, $data)
165    {
166
167
168        if ($format == 'xhtml') {
169
170            /** @var Doku_Renderer_xhtml $renderer */
171            $state = $data [PluginUtility::STATE];
172            switch ($state) {
173                case DOKU_LEXER_ENTER :
174                    break;
175
176                case DOKU_LEXER_UNMATCHED :
177
178                    $renderer->doc .= PluginUtility::renderUnmatched($data);
179                    break;
180
181                case DOKU_LEXER_EXIT :
182                    $mermaidCode = $data[PluginUtility::PAYLOAD];
183                    if (!empty($mermaidCode)) {
184                        Mermaid::addSnippet();
185                        $renderer->doc .= Mermaid::enter($data[PluginUtility::ATTRIBUTES]);
186                        $renderer->doc .= Mermaid::sanitize($mermaidCode);
187                        $renderer->doc .= Mermaid::close();
188                    } else {
189                        LogUtility::msg("No code component with bnf grammar was found", LogUtility::LVL_MSG_WARNING, self::CANONICAL);
190                    }
191                    break;
192
193            }
194            return true;
195        }
196
197
198        // unsupported $mode
199        return false;
200
201    }
202
203
204}
205
206