xref: /plugin/mermaid/syntax.php (revision 3543e4221b9cb4d4006985218634778d8dba6ac8)
1<?php
2/**
3 * DokuWiki Plugin mermaid (Syntax Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Robert Weinmeister <develop@weinmeister.org>
7 */
8
9use dokuwiki\Parsing\Parser;
10
11class syntax_plugin_mermaid extends \dokuwiki\Extension\SyntaxPlugin
12{
13    const DOKUWIKI_LINK_START_MERMAID = '<code>DOKUWIKILINKSTARTMERMAID</code>';
14    const DOKUWIKI_LINK_END_MERMAID = '<code>DOKUWIKILINKENDMERMAID</code>';
15
16    function protect_brackets_from_dokuwiki($text)
17    {
18        return preg_replace('/(?<!["\[(\s])(\[\[)(.*)(\]\])/', self::DOKUWIKI_LINK_START_MERMAID . '$2' . self::DOKUWIKI_LINK_END_MERMAID, $text);
19    }
20
21    function remove_protection_of_brackets_from_dokuwiki($text)
22    {
23        return str_replace(self::DOKUWIKI_LINK_START_MERMAID, '[[', str_replace(self::DOKUWIKI_LINK_END_MERMAID, ']]', $text));
24    }
25
26   	/** @inheritDoc */
27    function getType()
28	{
29		return 'container';
30	}
31
32    /** @inheritDoc */
33    function getSort()
34	{
35		return 150;
36	}
37
38    /**
39    * Connect lookup pattern to lexer.
40    *
41    * @param string $mode Parser mode
42    */
43    function connectTo($mode)
44    {
45        $this->Lexer->addEntryPattern('<mermaid.*?>(?=.*?</mermaid>)',$mode,'plugin_mermaid');
46    }
47
48    function postConnect()
49    {
50        $this->Lexer->addExitPattern('</mermaid>','plugin_mermaid');
51    }
52
53    /**
54     * Handle matches of the flowcharts syntax
55     */
56    function handle($match, $state, $pos, Doku_Handler $handler)
57    {
58        switch ($state) {
59            case DOKU_LEXER_ENTER:
60                return array($state, $match);
61            case DOKU_LEXER_UNMATCHED:
62                return array($state, $match);
63            case DOKU_LEXER_EXIT:
64                return array($state, '');
65        }
66        return false;
67    }
68
69    /**
70     * Render xhtml output or metadata
71     */
72    function render($mode, Doku_Renderer $renderer, $indata)
73    {
74        if($mode == 'xhtml'){
75            list($state, $match) = $indata;
76            switch ($state) {
77                case DOKU_LEXER_ENTER:
78                    $values = explode(" ", $match);
79                    $divwidth = count($values) < 2 ? 'auto' : $values[1];
80                    $divheight = count($values) < 3 ? 'auto' : substr($values[2], 0, -1);
81                    $renderer->doc .= '<div class="mermaid" style="width:'.$divwidth.'; height:'.$divheight.'">';
82                break;
83                case DOKU_LEXER_UNMATCHED:
84                    $instructions = $this->p_get_instructions($this->protect_brackets_from_dokuwiki($match));
85                    $xhtml = $this->remove_protection_of_brackets_from_dokuwiki($this->p_render($instructions));
86                    $renderer->doc .= preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $xhtml);
87                break;
88                case DOKU_LEXER_EXIT:
89                    $renderer->doc .= "\r\n</div>";
90                break;
91            }
92            return true;
93        }
94        return false;
95    }
96
97    /*
98     * Get the parser instructions siutable for the mermaid
99     *
100     */
101    function p_get_instructions($text)
102    {
103        //import parser classes and mode definitions
104        require_once DOKU_INC . 'inc/parser/parser.php';
105
106        // https://www.dokuwiki.org/devel:parser
107        // https://www.dokuwiki.org/devel:parser#basic_invocation
108        // Create the parser and the handler
109        $Parser = new Parser(new Doku_Handler());
110
111        $modes = array();
112
113        // add default modes
114        $std_modes = array( 'internallink', 'media', 'externallink');
115
116        foreach($std_modes as $m)
117        {
118            $class = 'dokuwiki\\Parsing\\ParserMode\\'.ucfirst($m);
119            $obj   = new $class();
120            $modes[] = array(
121                'sort' => $obj->getSort(),
122                'mode' => $m,
123                'obj'  => $obj
124            );
125        }
126
127        // add formatting modes
128        $fmt_modes = array( 'strong', 'emphasis', 'underline', 'monospace', 'subscript', 'superscript', 'deleted');
129        foreach($fmt_modes as $m)
130        {
131            $obj   = new \dokuwiki\Parsing\ParserMode\Formatting($m);
132            $modes[] = array(
133                'sort' => $obj->getSort(),
134                'mode' => $m,
135                'obj'  => $obj
136            );
137        }
138
139        //add modes to parser
140        foreach($modes as $mode)
141        {
142            $Parser->addMode($mode['mode'],$mode['obj']);
143        }
144
145        // Do the parsing
146        $p = $Parser->parse($text);
147
148        return $p;
149    }
150
151    public function p_render($instructions)
152    {
153        $Renderer = p_get_renderer('mermaid');
154
155        // Loop through the instructions
156        foreach ($instructions as $instruction) {
157            if(method_exists($Renderer, $instruction[0])){
158                call_user_func_array(array(&$Renderer, $instruction[0]), $instruction[1] ? $instruction[1] : array());
159            }
160        }
161
162        return $Renderer->doc;
163    }
164}
165