1<?php
2/**
3 * DokuWiki Plugin mathjax (Syntax Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Mark Liffiton <liffiton@gmail.com>
7 */
8
9// must be run within Dokuwiki
10if (!defined('DOKU_INC')) die();
11
12/**
13 * Class syntax_plugin_mathjax_protecttex
14 */
15class syntax_plugin_mathjax_protecttex extends DokuWiki_Syntax_Plugin {
16    # We need to grab any math before dokuwiki tries to parse it.
17    # Once it's 'claimed' by this plugin (type: protected), it won't be altered.
18
19    # Set of environments that this plugin will protect from Dokuwiki parsing
20    # * is escaped to work in regexp below
21    # Note: "math", "displaymath", and "flalign" environments seem to not be
22    #        recognized by Mathjax...  They will still be protected from Dokuwiki,
23    #        but they will not be rendered by MathJax.
24    private static $ENVIRONMENTS = array(
25        "math",
26        "displaymath",
27        "equation",
28        "equation\*",
29        "eqnarray",
30        "eqnarray\*",
31        "align",
32        "align\*",
33        "flalign",
34        "flalign\*",
35        "alignat",
36        "alignat\*",
37        "multline",
38        "multline\*",
39        "gather",
40        "gather\*",
41    );
42
43    /**
44     * Syntax Type
45     *
46     * Needs to return one of the mode types defined in $PARSER_MODES in parser.php
47     *
48     * @return string
49     */
50    public function getType() {
51        return 'protected';
52    }
53
54    /**
55     * Sort for applying this mode
56     *
57     * @return int
58     */
59    public function getSort() {
60        return 65;
61    }
62
63    /**
64     * regexp patterns adapted from jsMath plugin: https://www.dokuwiki.org/plugin:jsmath
65     *
66     * @param string $mode
67     */
68    public function connectTo($mode) {
69        $this->Lexer->addEntryPattern('(?<!\\\\)\$(?=[^\$][^\r\n]*?\$)',$mode,'plugin_mathjax_protecttex');
70        $this->Lexer->addEntryPattern('\$\$(?=.*?\$\$)',$mode,'plugin_mathjax_protecttex');
71        $this->Lexer->addEntryPattern('\\\\\((?=.*?\\\\\))',$mode,'plugin_mathjax_protecttex');
72        $this->Lexer->addEntryPattern('\\\\\[(?=.*?\\\\])',$mode,'plugin_mathjax_protecttex');
73        foreach (self::$ENVIRONMENTS as $env) {
74            $this->Lexer->addEntryPattern('\\\\begin{' . $env . '}(?=.*?\\\\end{' . $env . '})',$mode,'plugin_mathjax_protecttex');
75        }
76
77        if ($this->getConf('asciimath')) {
78            // Protect the default AsciiMath delimiter
79            $this->Lexer->addEntryPattern('`(?=.*?`)',$mode,'plugin_mathjax_protecttex');
80        }
81
82        // Protect specified tags, if any
83        $conf_mathtags = $this->getConf('mathtags');
84        $mathtags = explode(',', $conf_mathtags);
85        foreach ($mathtags as $tag) {
86            $tag = trim($tag);
87            if ($tag == "") { continue; }
88            $this->Lexer->addEntryPattern('<' . $tag . '.*?>(?=.*?</' . $tag . '>)',$mode,'plugin_mathjax_protecttex');
89        }
90    }
91    public function postConnect() {
92        $this->Lexer->addExitPattern('\$(?!\$)','plugin_mathjax_protecttex');
93        $this->Lexer->addExitPattern('\\\\\)','plugin_mathjax_protecttex');
94        $this->Lexer->addExitPattern('\\\\\]','plugin_mathjax_protecttex');
95        foreach (self::$ENVIRONMENTS as $env) {
96            $this->Lexer->addExitPattern('\\\\end{' . $env . '}','plugin_mathjax_protecttex');
97        }
98
99        if ($this->getConf('asciimath')) {
100            // Protect the default AsciiMath delimiter
101            $this->Lexer->addExitPattern('`','plugin_mathjax_protecttex');
102        }
103
104        // Protect specified tags, if any
105        $conf_mathtags = $this->getConf('mathtags');
106        $mathtags = explode(',', $conf_mathtags);
107        foreach ($mathtags as $tag) {
108            $tag = trim($tag);
109            if ($tag == "") { continue; }
110            $this->Lexer->addExitPattern('</' . $tag . '>','plugin_mathjax_protecttex');
111        }
112    }
113
114    /**
115     * Handler to prepare matched data for the rendering process
116     *
117     * This function can only pass data to render() via its return value - render()
118     * may be not be run during the object's current life.
119     *
120     * Usually you should only need the $match param.
121     *
122     * @param   string       $match   The text matched by the patterns
123     * @param   int          $state   The lexer state for the match
124     * @param   int          $pos     The character position of the matched text
125     * @param   Doku_Handler $handler The Doku_Handler object
126     * @return  array Return an array with all data you want to use in render
127     */
128    public function handle($match, $state, $pos, Doku_Handler $handler){
129        // Just pass it through...
130        return $match;
131    }
132
133    /**
134     * Handles the actual output creation.
135     *
136     * @param   $mode     string        output format being rendered
137     * @param   $renderer Doku_Renderer the current renderer object
138     * @param   $data     array         data created by handler()
139     * @return  boolean                 rendered correctly?
140     */
141    public function render($mode, Doku_Renderer $renderer, $data) {
142        if ($mode == 'xhtml' || $mode == 'odt') {
143            /** @var Doku_Renderer_xhtml $renderer */
144
145            // Just pass it through, but escape xml entities...
146            $renderer->doc .= $renderer->_xmlEntities($data);
147            return true;
148        }
149        if ($mode == 'latexport') {
150            // Pass math expressions to latexport renderer
151            $renderer->mathjax_content($data);
152            return true;
153        }
154
155        // For all other modes, pass through unchanged.
156        $renderer->doc .= $data;
157        return true;
158    }
159}
160
161// vim:ts=4:sw=4:et:
162