mirror of
https://github.com/moodle/moodle.git
synced 2025-03-14 20:50:21 +01:00
Merge branch 'MDL-61981-master_nestedmath' of git://github.com/Mankro/moodle
This commit is contained in:
commit
d813c9e745
@ -128,37 +128,116 @@ class filter_mathjaxloader extends moodle_text_filter {
|
||||
$text = str_replace('\\]', '\\)', $text);
|
||||
}
|
||||
|
||||
$hasinline = strpos($text, '\\(') !== false && strpos($text, '\\)') !== false;
|
||||
$hasdisplay = (strpos($text, '$$') !== false) ||
|
||||
(strpos($text, '\\[') !== false && strpos($text, '\\]') !== false);
|
||||
|
||||
$hasextra = false;
|
||||
|
||||
foreach ($extradelimiters as $extra) {
|
||||
if ($extra && strpos($text, $extra) !== false) {
|
||||
$hasextra = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($hasinline || $hasdisplay || $hasextra) {
|
||||
$PAGE->requires->yui_module('moodle-filter_mathjaxloader-loader', 'M.filter_mathjaxloader.typeset');
|
||||
|
||||
$hasdisplayorinline = false;
|
||||
if ($hasextra) {
|
||||
// If custom dilimeters are used, wrap whole text to prevent autolinking.
|
||||
$text = '<span class="nolink">' . $text . '</span>';
|
||||
} else {
|
||||
if ($hasinline) {
|
||||
// If the default inline TeX delimiters \( \) are present, wrap each pair in nolink.
|
||||
$text = preg_replace('/\\\\\\([\S\s]*?\\\\\\)/u',
|
||||
'<span class="nolink">\0</span>', $text);
|
||||
}
|
||||
if ($hasdisplay) {
|
||||
// If default display TeX is used, wrap $$ $$ or \[ \] individually.
|
||||
$text = preg_replace('/\$\$[\S\s]*?\$\$|\\\\\\[[\S\s]*?\\\\\\]/u',
|
||||
'<span class="nolink">\0</span>', $text);
|
||||
}
|
||||
// Wrap display and inline math environments in nolink spans.
|
||||
// Do not wrap nested environments, i.e., if inline math is nested
|
||||
// inside display math, only the outer display math is wrapped in
|
||||
// a span. The span HTML inside a LaTex math environment would break
|
||||
// MathJax. See MDL-61981.
|
||||
list($text, $hasdisplayorinline) = $this->wrap_math_in_nolink($text);
|
||||
}
|
||||
|
||||
if ($hasdisplayorinline || $hasextra) {
|
||||
$PAGE->requires->yui_module('moodle-filter_mathjaxloader-loader', 'M.filter_mathjaxloader.typeset');
|
||||
return '<span class="filter_mathjaxloader_equation">' . $text . '</span>';
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find math environments in the $text and wrap them in no link spans
|
||||
* (<span class="nolink"></span>). If math environments are nested, only
|
||||
* the outer environment is wrapped in the span.
|
||||
*
|
||||
* The recognized math environments are \[ \] and $$ $$ for display
|
||||
* mathematics and \( \) for inline mathematics.
|
||||
*
|
||||
* @param string $text The text to filter.
|
||||
* @return array An array containing the potentially modified text and
|
||||
* a boolean that is true if any changes were made to the text.
|
||||
*/
|
||||
protected function wrap_math_in_nolink($text) {
|
||||
$i = 1;
|
||||
$len = strlen($text);
|
||||
$displaystart = -1;
|
||||
$displaybracket = false;
|
||||
$displaydollar = false;
|
||||
$inlinestart = -1;
|
||||
$changesdone = false;
|
||||
// Loop over the $text once.
|
||||
while ($i < $len) {
|
||||
if ($displaystart === -1) {
|
||||
// No display math has started yet.
|
||||
if ($text[$i - 1] === '\\' && $text[$i] === '[') {
|
||||
// Display mode \[ begins.
|
||||
$displaystart = $i - 1;
|
||||
$displaybracket = true;
|
||||
} else if ($text[$i - 1] === '$' && $text[$i] === '$') {
|
||||
// Display mode $$ begins.
|
||||
$displaystart = $i - 1;
|
||||
$displaydollar = true;
|
||||
} else if ($text[$i - 1] === '\\' && $text[$i] === '(') {
|
||||
// Inline math \( begins, not nested inside display math.
|
||||
$inlinestart = $i - 1;
|
||||
} else if ($text[$i - 1] === '\\' && $text[$i] === ')' && $inlinestart > -1) {
|
||||
// Inline math ends, not nested inside display math.
|
||||
// Wrap the span around it.
|
||||
$text = $this->insert_span($text, $inlinestart, $i);
|
||||
|
||||
$inlinestart = -1; // Reset.
|
||||
$i += 28; // The $text length changed due to the <span>.
|
||||
$len += 28;
|
||||
$changesdone = true;
|
||||
}
|
||||
} else {
|
||||
// Display math open.
|
||||
if (($text[$i - 1] === '\\' && $text[$i] === ']' && $displaybracket) ||
|
||||
($text[$i - 1] === '$' && $text[$i] === '$' && $displaydollar)) {
|
||||
// Display math ends, wrap the span around it.
|
||||
$text = $this->insert_span($text, $displaystart, $i);
|
||||
|
||||
$displaystart = -1; // Reset.
|
||||
$displaybracket = false;
|
||||
$displaydollar = false;
|
||||
$i += 28; // The $text length changed due to the <span>.
|
||||
$len += 28;
|
||||
$changesdone = true;
|
||||
}
|
||||
}
|
||||
|
||||
++$i;
|
||||
}
|
||||
return array($text, $changesdone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a portion of the $text inside a no link span
|
||||
* (<span class="nolink"></span>). The whole text is then returned.
|
||||
*
|
||||
* @param string $text The text to modify.
|
||||
* @param int $start The start index of the substring in $text that should
|
||||
* be wrapped in the span.
|
||||
* @param int $end The end index of the substring in $text that should be
|
||||
* wrapped in the span.
|
||||
* @return string The whole $text with the span inserted around
|
||||
* the defined substring.
|
||||
*/
|
||||
protected function insert_span($text, $start, $end) {
|
||||
return substr_replace($text,
|
||||
'<span class="nolink">'. substr($text, $start, $end - $start + 1) .'</span>',
|
||||
$start,
|
||||
$end - $start + 1);
|
||||
}
|
||||
}
|
||||
|
102
filter/mathjaxloader/tests/filtermath_test.php
Normal file
102
filter/mathjaxloader/tests/filtermath_test.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
/**
|
||||
* Provides the {@link filter_mathjaxloader_filtermath_testcase} class.
|
||||
*
|
||||
* @package filter_mathjaxloader
|
||||
* @category test
|
||||
* @copyright 2018 Markku Riekkinen
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot.'/filter/mathjaxloader/filter.php');
|
||||
/**
|
||||
* Unit tests for the MathJax loader filter.
|
||||
*
|
||||
* @copyright 2018 Markku Riekkinen
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class filter_mathjaxloader_filtermath_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test the functionality of {@link filter_mathjaxloader::filter()}.
|
||||
*
|
||||
* @param string $inputtext The text given by the user.
|
||||
* @param string $expected The expected output after filtering.
|
||||
*
|
||||
* @dataProvider test_math_filtering_inputs
|
||||
*/
|
||||
public function test_math_filtering($inputtext, $expected) {
|
||||
$filter = new filter_mathjaxloader(context_system::instance(), []);
|
||||
$this->assertEquals($expected, $filter->filter($inputtext));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for {@link self::test_math_filtering()}.
|
||||
*
|
||||
* @return array of [inputtext, expectedoutput] tuples.
|
||||
*/
|
||||
public function test_math_filtering_inputs() {
|
||||
return [
|
||||
// One inline formula.
|
||||
['Some inline math \\( y = x^2 \\).',
|
||||
'<span class="filter_mathjaxloader_equation">Some inline math <span class="nolink">\\( y = x^2 \\)</span>.</span>'],
|
||||
|
||||
// One inline and one display.
|
||||
['Some inline math \\( y = x^2 \\) and display formula \\[ S = \\sum_{n=1}^{\\infty} 2^n \\]',
|
||||
'<span class="filter_mathjaxloader_equation">Some inline math <span class="nolink">\\( y = x^2 \\)</span> and '
|
||||
. 'display formula <span class="nolink">\\[ S = \\sum_{n=1}^{\\infty} 2^n \\]</span></span>'],
|
||||
|
||||
// One display and one inline.
|
||||
['Display formula \\[ S = \\sum_{n=1}^{\\infty} 2^n \\] and some inline math \\( y = x^2 \\).',
|
||||
'<span class="filter_mathjaxloader_equation">Display formula <span class="nolink">\\[ S = \\sum_{n=1}^{\\infty} 2^n \\]</span> and '
|
||||
. 'some inline math <span class="nolink">\\( y = x^2 \\)</span>.</span>'],
|
||||
|
||||
// One inline and one display (with dollars).
|
||||
['Some inline math \\( y = x^2 \\) and display formula $$ S = \\sum_{n=1}^{\\infty} 2^n $$',
|
||||
'<span class="filter_mathjaxloader_equation">Some inline math <span class="nolink">\\( y = x^2 \\)</span> and '
|
||||
. 'display formula <span class="nolink">$$ S = \\sum_{n=1}^{\\infty} 2^n $$</span></span>'],
|
||||
|
||||
// One display (with dollars) and one inline.
|
||||
['Display formula $$ S = \\sum_{n=1}^{\\infty} 2^n $$ and some inline math \\( y = x^2 \\).',
|
||||
'<span class="filter_mathjaxloader_equation">Display formula <span class="nolink">$$ S = \\sum_{n=1}^{\\infty} 2^n $$</span> and '
|
||||
. 'some inline math <span class="nolink">\\( y = x^2 \\)</span>.</span>'],
|
||||
|
||||
// Inline math environment nested inside display environment (using a custom LaTex macro).
|
||||
['\\[ \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} \\] '
|
||||
. 'Text with inline formula using the custom LaTex macro \\( a = \\NullF \\).',
|
||||
'<span class="filter_mathjaxloader_equation"><span class="nolink">'
|
||||
. '\\[ \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} \\]</span> '
|
||||
. 'Text with inline formula using the custom LaTex macro <span class="nolink">\\( a = \\NullF \\)</span>.</span>'],
|
||||
|
||||
// Nested environments and some more content.
|
||||
['\\[ \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} \\] '
|
||||
. 'Text with inline formula using the custom LaTex macro \\( a = \\NullF \\). Finally, a display formula '
|
||||
. '$$ b = \\NullF $$',
|
||||
'<span class="filter_mathjaxloader_equation"><span class="nolink">'
|
||||
. '\\[ \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} \\]</span> '
|
||||
. 'Text with inline formula using the custom LaTex macro <span class="nolink">\\( a = \\NullF \\)</span>. '
|
||||
. 'Finally, a display formula <span class="nolink">$$ b = \\NullF $$</span></span>'],
|
||||
|
||||
// Broken math: the delimiters ($$) are not closed.
|
||||
['Writing text and starting display math. $$ k = i^3 \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} '
|
||||
. 'More text and inline math \\( x = \\NullF \\).',
|
||||
'Writing text and starting display math. $$ k = i^3 \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} '
|
||||
. 'More text and inline math \\( x = \\NullF \\).'],
|
||||
];
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user