1<?php
2
3
4namespace ComboStrap\Tag;
5
6
7use ComboStrap\CallStack;
8use ComboStrap\ExceptionBadArgument;
9use ComboStrap\ExceptionBadSyntax;
10use ComboStrap\LogUtility;
11use ComboStrap\PluginUtility;
12use ComboStrap\Web\Sanitizer;
13use ComboStrap\TagAttributes;
14
15class MermaidTag
16{
17
18
19    const CANONICAL = "mermaid";
20    public const CLASS_NAME = "mermaid";
21    const LOGICAL_TAG = "mermaid";
22    public const MARKUP_MERMAID = 'mermaid';
23    public const MARKUP_SEQUENCE_DIAGRAM = 'sequence-diagram';
24    const MARKUP_CONTENT_ATTRIBUTE = "mermaid-markup";
25    const MERMAID_CODE = "mermaid";
26    public const MARKUP_CLASS_DIAGRAM = 'class-diagram';
27    public const MARKUP_FLOWCHART = 'flowchart';
28    public const MARKUP_GANTT = 'gantt';
29    public const MARKUP_ERD = 'erd';
30    public const MARKUP_JOURNEY = 'journey';
31    public const MARKUP_PIECHART = 'piechart';
32    public const MARKUP_STATE_DIAGRAM = 'state-diagram';
33
34    public static function addSnippet()
35    {
36        $snippetManager = PluginUtility::getSnippetManager();
37        $snippetId = self::CANONICAL;
38        $snippetManager->attachJavascriptFromComponentId($snippetId);
39        try {
40            $snippetManager->attachRemoteJavascriptLibrary(
41                $snippetId,
42                "https://cdn.jsdelivr.net/npm/mermaid@10.2.3/dist/mermaid.min.js",
43                "sha256-JFptYy4KzJ5OQP+Q9fubNf3cxpPPmZKqUOovyEONKrQ="
44            );
45        } catch (ExceptionBadArgument|ExceptionBadSyntax $e) {
46            LogUtility::internalError("The url should be good", self::CANONICAL,$e);
47        }
48    }
49
50    /**
51     * The enter tag
52     * @param TagAttributes $tagAttributes
53     * @return string
54     */
55    public static function renderEnter(TagAttributes $tagAttributes): string
56    {
57
58        $content = $tagAttributes->getValueAndRemoveIfPresent(self::MARKUP_CONTENT_ATTRIBUTE);
59
60        $tagAttributes->addClassName(MermaidTag::CLASS_NAME);
61        $html = $tagAttributes->toHtmlEnterTag("div");
62        /**
63         * The mermaid markup code is replaced at runtime by the diagram
64         */
65        MermaidTag::addSnippet();
66
67        $html .= MermaidTag::sanitize($content);
68        $html .= '</div>';
69        return $html;
70
71    }
72
73    /**
74     * The closing tag
75     *
76     * @return string
77     * @deprecated
78     */
79    public static function close(): string
80    {
81        return "</div>";
82    }
83
84    /**
85     * The content cannot be HTML escaped
86     *
87     * because
88     * `->>` would become `→&gt;`
89     * or  <br/> would not work
90     *
91     * There is a parameter
92     * @param $content
93     * @return mixed
94     */
95    public static function sanitize($content)
96    {
97
98        return Sanitizer::sanitize($content, " in a mermaid language", self::CANONICAL);
99
100    }
101
102
103    /**
104     * @param $data
105     * @param $renderer
106     * @return void
107     * @deprecated
108     */
109    public static function render($data, &$renderer)
110    {
111        $state = $data [PluginUtility::STATE];
112        switch ($state) {
113            case DOKU_LEXER_ENTER :
114                $tagAttributes = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES]);
115                $renderer->doc .= MermaidTag::renderEnter($tagAttributes);
116                break;
117            case DOKU_LEXER_UNMATCHED :
118            case DOKU_LEXER_EXIT :
119                break;
120
121        }
122    }
123
124    public static function handleExit($handler){
125
126        $callStack = CallStack::createFromHandler($handler);
127        $openingCall = $callStack->moveToPreviousCorrespondingOpeningCall();
128        $contentCall = $callStack->next();
129        $content = $contentCall->getCapturedContent();
130        $openingCall->setAttribute(self::MARKUP_CONTENT_ATTRIBUTE, $content);
131        $callStack->deleteAllCallsAfter($openingCall);
132
133    }
134
135    /**
136     * @param $state
137     * @param $match
138     * @param $handler
139     * @return array
140     */
141    public static function handle($state, $match, &$handler): array
142    {
143        switch ($state) {
144
145            case DOKU_LEXER_ENTER :
146                $tagAttributes = TagAttributes::createFromTagMatch($match);
147                return array(
148                    PluginUtility::STATE => $state,
149                    PluginUtility::ATTRIBUTES => $tagAttributes->toCallStackArray()
150                );
151
152            case DOKU_LEXER_UNMATCHED :
153
154                return PluginUtility::handleAndReturnUnmatchedData("", $match, $handler);
155
156
157            case DOKU_LEXER_EXIT :
158                self::handleExit($handler);
159                return array(
160                    PluginUtility::STATE => $state
161                );
162
163
164        }
165        return array();
166    }
167
168
169}
170