Tag Archives: open education

Handling LaTeX in WordPress and React.js

I’m in the midst of building a WordPress plugin that interfaces with WeBWorK, a web application for math and science homework. The plugin features a JavaScript app powered by React and Redux, which lives inside of a WordPress page template and communicates with WP via custom API endpoints. The project is for City Tech OpenLab and it’s pretty cool. I’ll write up some more general details when it’s closer to release.

The tool is designed for use in undergraduate math courses, so LaTeX-formatted content features heavily. LaTeX is beautiful when used on its own, but it’s hard to handle in the context of web applications. A few lessons and strategies are described below.

Delimiters

I’m dealing with “mixed” content: plain text that is interspersed with LaTeX. Some of this content is coming directly from WeBWorK, which formats LaTeX to be rendered with MathJax. WeBWorK is sending markup that, when simplified, looks like this:

<br />
This is plain text.<br />
Here is an inline equation: <script type="math/tex">E = mc^2</script><br />
And here is the same equation as a standalone block:<br />
<script type="math/tex; mode=display">E = mc^2</script><br />

It’s also possible for users to enter LaTeX from the front-end of the WP application. In this case, we’ve settled on the verbose delimiters \begin{math} and \end{math}, but we also support a shorthand delimiter borrowed from Jetpack’s LaTeX renderer:

<br />
This is plain text.<br />
Here is an inline equation: \begin{math}E = mc^2\end{math}<br />
And here is the same equation as a standalone block:<br />
$latex E = mc^2$<br />

Some of these delimiter formats are more fragile than others, for various reasons – script tags that cannot be escaped, false positives due to use of literal dollar signs, etc – so I standardized on some invented delimiters for data storage. Before saving any LaTeX content to the database, I’m converting all delimiters to the following formats (of my own invention, which explains their Great Beauty):

<br />
This is plain text.<br />
Here is an inline equation: {{{LATEX_DELIM_INLINE_OPEN}}}E = mc^2{{{LATEX_DELIM_INLINE_CLOSE}}}<br />
And here is the same equation as a standalone block:<br />
{{{LATEX_DELIM_DISPLAY_OPEN}}}E = mc^2{{{LATEX_DELIM_DISPLAY_CLOSE}}}<br />

This way, the delimiters are unique and easy to process on display.

Slashes

I’m using WordPress custom post types to store data. The lowest-level function available for saving post data is wp_insert_post(). Thanks to a glorious accident of history, this function expects data to be “slashed”, as in addslashes() and magic quotes. If you’re looking for a fun way to spend a few hours, read through this WordPress ticket and figure out a solution, please.

LaTeX uses \ as its escape character, which makes LaTeX-formatted content look “slashed” to WordPress. As such, attempting to save chunks of LaTeX using wp_insert_post() will result in slashes being removed and formatting being lost. So I had two choices: don’t use wp_insert_post(), or don’t use slashes.

I chose the latter. Before saving any content to the database, I identify text that appears between LaTeX delimiters, and I replace all slashes with a custom character. In all its glory:

<br />
public function swap_latex_escape_characters( $text ) {<br />
    $regex = ';(\{\{\{LATEX_DELIM_((?:DISPLAY)|(?:INLINE))_OPEN\}\}\})(.*?)(\{\{\{LATEX_DELIM_\2_CLOSE\}\}\});s';<br />
    $text = preg_replace_callback( $regex, function( $matches ) {<br />
        $tex = str_replace( '\\', '{{{LATEX_ESCAPE_CHARACTER}}}', $matches[3] );<br />
        return $matches[1] . $tex . $matches[4];<br />
    }, $text );</p>
<p>return $text;<br />
}<br />

So a chunk of LaTeX like E = \frac{mc^2}{\sqrt{1-\frac{v^2}{c^2}}} goes into the database as

E = {{{LATEX_ESCAPE_CHARACTER}}}frac{mc^2}{{{{LATEX_ESCAPE_CHARACTER}}}sqrt{1-{{{LATEX_ESCAPE_CHARACTER}}}frac{v^2}{c^2}}}

They get converted back to slashes before being served via the API endpoints.

Rendering in a React component

I decided to stick with MathJax for rendering the LaTeX. MathJax has some preprocessors that allow you to configure custom delimiters, but I had a hard time making them work inside of my larger JavaScript framework. So I decided to skip the preprocessors and generate the <script type="math/tex"> tags myself.

React escapes output pretty aggressively. This is generally a good thing. But it makes it hard to print script tags and unescaped LaTeX characters. To make it work, I needed to live dangerously. But I didn’t want to print any more raw content than necessary. So I have two components for rendering text that may contain LaTeX. Click the links for the full source code; here’s a summary of what’s going on:

  1. FormattedProblem performs a regular expression to find pieces of text that are set off by my custom delimiters. Text within delimiters gets passed to a LaTeX component. Text between LaTeX chunks becomes a span.
  2. LaTeX puts content to be formatted as LaTeX into a script tag, as expected by MathJax. Once the component is rendered by React, I then tell MathJax to queue it for (re)processing. See how updateTex() is called both in componentDidMount() and componentDidUpdate(). Getting the MathJax queue logic correct here was hard: I spent a lot of time dealing with unrendered or double-rendered TeX, as well as slow performance when a page contains dozens of LaTeX chunks.

A reminder that React’s dangerouslySetInnerHTML is dangerous. I’m running LaTeX content through WP’s esc_html() before delivering it to the endpoint. And because I control both the endpoint and the client, I can trust that the proper escaping is happening somewhere in the chain.

I use a very slightly modified technique to provide a live preview of formatted LaTeX. Here it is in action:

output

Pretty cool TeXnology, huh? ROFLMAO

Bebop is totally rocksteady

File this under “WPedu is killing it right now”.

The team at the Centre for Educational Research and Development at University of Lincoln (UK) has just announced the first public release of Bebop, their new BuddyPress plugin. Read more about the release and about Bebop itself. (Disclaimer – I have done some consultation for the Bebop project, though I’m writing this blog post outside of that consultant role.)

I find Bebop compelling because of what it does, and also because of how it was built. In a nutshell, Bebop is an aggregator for Open Educational Resources, or OERs. ‘OER’ is a term of art among open education folks, referring to learning resources that are available for free use, under an open license. (See OER Commons for a longer definition.) In practice, this can mean anything from videos to lesson plans to games to websites. Bebop is designed to allow members of a BuddyPress community to collect their own OERs from various web services – Twitter, YouTube, Flickr, etc – for display on their BP profiles. It’s a nice demonstration of how BuddyPress can be used as a tool for aggregating work done elsewhere on the web. It also demonstrates another way in which universities can use a free platform like BuddyPress as a non-commercial, locally hosted home for students and faculty, while recognizing that valuable work happens in spaces not under the university’s control. And from a technical point of view, I like how Bebop uses BP’s Activity component and profile metaphors to creative ends.

Bebop was built as part of a “rapid innovation” project. To put the term “rapid” in context: In the context of universities, projects usually move forward glacially. Getting approval and funding may itself take years, and by the time development begins, the original idea/technology is already obsolete. Bebop, in contrast, was conceived and developed over just a couple of months. It’s great to see these kinds of (relatively) rapid projects happening in universities – they can demonstrate to funders the benefits that come along with a bit of agility and risk and freedom.

If you’re running a BuddyPress installation in a university, you might consider taking Bebop for a spin. Get it from the wordpress.org plugin repo or follow development at Github.