diff --git a/js/tex.jsx b/js/tex.jsx index d11605f..d4c375b 100644 --- a/js/tex.jsx +++ b/js/tex.jsx @@ -2,8 +2,7 @@ /** * For math rendered using KaTex and/or MathJax. Use me like 2x + 3. */ -/* global MathJax, Khan */ -// TODO(joel) - require MathJax / katex so they don't have to be global +/* global MathJax */ const PureRenderMixin = require("react-addons-pure-render-mixin"); const React = require("react"); @@ -39,10 +38,24 @@ const unProcess = script => { const loadMathJax = callback => { if (typeof MathJax !== "undefined") { callback(); - } else if (typeof Khan !== "undefined" && Khan.mathJaxLoaded) { - Khan.mathJaxLoaded.then(callback); } else { - throw new Error("MathJax wasn't loaded before it was needed by "); + /** + * We can either... + * + * A) Reach up and out of `third_party` folder into our core webapp + * code to include this `load-mathjax` file that, well, loads MathJax + * or... + * + * B) Move this file into the KateX package next to `load-mathjax` and + * then update everything that calls for this file to point at that + * path instead. + * + * There is no easy or obvious solution. I am choosing A. + * + * - Jesse + */ + const loadMathJax = require("../../../../javascript/katex-package/load-mathjax.js"); + loadMathJax.then(callback); } }; @@ -98,15 +111,6 @@ const TeX = createReactClass({ mixins: [PureRenderMixin], - // TODO(joshuan): Once we are using React 16.3+, - // migrate to getDerivedStateFromProps - getInitialState: function() { - return { - mounted: false, - katexHtml: this.getKatexHtml(this.props), - }; - }, - getDefaultProps: function() { return { katexOptions: { @@ -124,6 +128,15 @@ const TeX = createReactClass({ }; }, + // TODO(joshuan): Once we are using React 16.3+, + // migrate to getDerivedStateFromProps + getInitialState: function() { + return { + mounted: false, + katexHtml: this.getKatexHtml(this.props), + }; + }, + componentDidMount: function() { this._root = ReactDOM.findDOMNode(this); @@ -160,27 +173,6 @@ const TeX = createReactClass({ } }, - getKatexHtml(props) { - // Try to render the math content with KaTeX. - // If this fails, componentDidUpdate() will notice and - // use MathJAX instead. - try { - return { - __html: katex.renderToString( - props.children, - props.katexOptions, - ), - }; - } catch (e) { - /* jshint -W103 */ - if (e.__proto__ !== katex.ParseError.prototype) { - /* jshint +W103 */ - throw e; - } - return null; - } - }, - componentDidUpdate: function(prevProps, prevState) { if (this.props.children !== prevProps.children) { this.maybeUnprocess(); @@ -237,6 +229,40 @@ const TeX = createReactClass({ } }, + getKatexHtml(props) { + // Try to render the math content with KaTeX. + // If this fails, componentDidUpdate() will notice and + // use MathJAX instead. + try { + return { + __html: katex.renderToString( + props.children, + props.katexOptions, + ), + }; + } catch (e) { + // By catching the exception here and returning null + // we will fall back to asyncronously rendering with + // MathJAX. + // + // NOTE: formerly we only returned null if the error + // was a parse error from Katex and re-threw any other errors. + // But https://khanacademy.atlassian.net/browse/CP-879 and + // https://khanacademy.atlassian.net/browse/CP-1742 were caused + // by regular TypeError exceptions in Katex, so we might as + // well fall back to MathJAX in that case as well. (The Katex + // bug is fixed in the latest version and will stop happening + // when we upgrade webapp to use Katex 1.0.) + // + // TODO: We could use Raven.captureMessage() to send a message + // to Sentry when these errors occur if we want to get serious + // about eliminating them. Such a message should include + // window.location, props.children (the string of katex source) + // and the error itself. + return null; + } + }, + process: function(callback) { this.hasProcessed = false; process(this.script, () => {