Bug: autoload doesn't work if user manually changes math renderer.
Issue Summary
When loading tex-chtml.js (or tex-svg.js) and using the right click menu to go to "Math Settings" -> "Math Render" and change the render to the one not loaded, the autoload extension seems to no longer work and the command is returned as an error in red (you have to reload the page after changing the renderer).
I noticed this with a formula using \boldsymbol{i}, though after testing a few other extensions the result is the same, autoload is not working when I manually choose the Math Render to be different than the one loaded by the page.
I'm unsure if this is a bug, or working as intended, but couldn't find any documentation on this behavior. The only solution I found was to load tex-chtml-full.js or tex-svg-full.js to load most extensions, or to manually configure MathJax to load the extensions I use to allow users to be able to switch between the CHTML and SVG Math Render.
Technical details
- MathJax Version: 3.2.2
- Client OS: Debian Linux
- Browser: Firefox ESR
One additional note, I tried to force loading the autoload extension in the MathJax configuration, and when using the renderer not loaded by the page, and looking at the devtools console I see an error "Error: MathJax retry" followed by a list of what appears to be a function stack starting with retryAfter. So it does appear it tries to load the extension but fails.
There are a number of interconnected issues involved, here. The menu initialization in v3 has several problems, particular when the renderer has been changed from the page's default. These are resolved in v4 (now out in beta release).
As you point out, the problem is not that the boldsymbol extension is not being auto-loaded; it is being loaded. But the loading is asynchronous, and MathJax uses the "MathJax retry" error as a means of handling that asynchronicity. When a file must be loaded, MathJax throws this error, and code higher up in the call stack is supposed to trap that error, wait for the file to load, and then retry the typesetting that caused the file to be loaded. There is a special handleRetriesFor() function that does that.
Handling the retries uses promises (javascript's means of handling callbacks for such actions), but promises require special care, since they introduce asynchronicity into any part of the code that uses them. MathJax frequently has both synchronous and promise-based its functions (like MathJax.typeset() and MathJax.typesetPromise()). The latter handle the retry errors automatically, but the former expect the calling code to do that.
It turns out that the menu code that handles changing the renderer at startup calls the synchronous version of the code for re-rendering the page, and that is where you are getting the MathJax retry error, because that is thrown when the page is typeset using the alternate renderer and the boldsymbol extension is loaded. Because the error is not trapped, the rendering never completes. But the usual startup rendering has not yet been performed, and so while the browser is waiting for the boldsymbol extension to load, it thinks it is time for that initial typesetting to be performed, and so it does so. Meanwhile, the initial attempt to autoload boldsymbol has undefined the \boldsymbol macro (so that if the extension fails to load, the typesetting retry will not try to load it again). But in your case, the default initial typeset runs before the extension loads, and so it is marked in red. Then eventually the extension loads, but it is too late (and since the retry error wasn't trapped, the retry typeset is never performed, and even if it were, since the expression has already been typeset, it would not be compiled, so the initial missing macro coloring would remain).
So it is complicated set of errors.
The following configuration can be used to work around it:
MathJax = {
startup: {
ready() {
const {STATE} = MathJax._.core.MathItem;
const {Menu} = MathJax._.ui.menu.Menu;
const {mathjax} = MathJax._.mathjax;
Menu.prototype.rerender = function (start = STATE.TYPESET) {
this.rerenderStart = Math.min(start, this.rerenderStart);
if (!Menu.loading) {
if (this.rerenderStart <= STATE.COMPILED) {
this.document.reset({inputJax: []});
}
MathJax.startup.promise.then(() => {
mathjax.handleRetriesFor(() => {
this.document.rerender(this.rerenderStart);
this.rerenderStart = STATE.LAST;
});
});
}
};
MathJax.startup.defaultReady();
}
}
}
which has the re-rendering wait for the MathJax.startup.promise before doing its typesetting, and also inserts the needed handleRetriesFor() call so that autoloading will be handled properly.
This is not an ideal solution, as it means that the re-rending will wait for the initial typesetting (with the original renderer) to be performed before switching the renderer and performing the output processing again. But for WeBWorK problems, which are relatively short, the overhead should not be too onerous.
This is a duplicate of a previous issue, but I can't seem to locate it.
@dpvc Thanks. I tried to look for other issues but outside of searching for autoload didn't know what to look for.
In your work around, does this affect all loads of MathJax, or will it only wait in the case that the menu setting is different from what has been loaded?
The rerender() function overridden in the configuration that I offered is only called when the renderer is changed, either explicitly in response to a menu selection, or on startup when the renderer was changed in an earlier session. The latter is the only one that has the drawback that I mentioned; if you change the renderer once the page is already typeset, the MathJax.startup.promise will already be resolved, and the re-typesetting will occur immediately with no side-effects. it is only the initial startup rendering when the renderer has been changed in a previous session that has the double rendering.
Fixed in v4.0