Skip to content

Commit 2150271

Browse files
committed
migrate mathjax support from v2/v3 to v3/v4
1 parent 401e72c commit 2150271

12 files changed

Lines changed: 86 additions & 169 deletions

File tree

devtools/test_dashboard/index-mathjax3chtml.html

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,7 @@
4949
options.format = svgDocument.inputJax[0].name;
5050
return svgDocument.convert(math, options);
5151
};
52-
/*
53-
MathJax.tex2svgPromise = (math, options = {}) => {
54-
options.format = svgDocument.inputJax[0].name;
55-
return mathjax.handleRetriesFor(() => svgDocument.convert(math, options));
56-
};
57-
*/
52+
5853
MathJax.svgStylesheet = () => svgOutput.styleSheet(svgDocument);
5954
}
6055
}

devtools/test_dashboard/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
<!-- Update UI elements if strict mode is enabled -->
2828
<script src="./strict.js"></script>
2929

30-
<script src="../../node_modules/@plotly/mathjax-v2/MathJax.js?config=TeX-AMS-MML_SVG"></script>
30+
<script src="../../node_modules/@plotly/mathjax-v4/tex-svg.js"></script>
3131
<script charset="utf-8" id="source" src="../../build/plotly.js"></script>
3232
<script charset="utf-8" src="../../build/test_dashboard-bundle.js"></script>
3333
</body>

package-lock.json

Lines changed: 20 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@
117117
},
118118
"devDependencies": {
119119
"@biomejs/biome": "2.2.0",
120-
"@plotly/mathjax-v2": "npm:mathjax@2.7.5",
121-
"@plotly/mathjax-v3": "npm:mathjax@^3.2.2",
120+
"@plotly/mathjax-v3": "npm:mathjax@3.2.2",
121+
"@plotly/mathjax-v4": "npm:mathjax@^4.1.2",
122122
"@types/d3": "3.5.34",
123123
"@types/node": "^24.10.0",
124124
"amdefine": "^1.0.1",

src/lib/svg_text_utils.js

Lines changed: 31 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -188,67 +188,37 @@ function cleanEscapesForTex(s) {
188188
var inlineMath = [['$', '$'], ['\\(', '\\)']];
189189

190190
function texToSVG(_texString, _config, _callback) {
191-
var MathJaxVersion = parseInt(
191+
const MathJaxVersion = parseInt(
192192
(MathJax.version || '').split('.')[0]
193193
);
194194

195195
if(
196-
MathJaxVersion !== 2 &&
197-
MathJaxVersion !== 3
196+
MathJaxVersion !== 3 &&
197+
MathJaxVersion !== 4
198198
) {
199-
Lib.warn('No MathJax version:', MathJax.version);
199+
Lib.warn('Unsupported MathJax version:', MathJax.version);
200200
return;
201201
}
202202

203-
var originalRenderer,
204-
originalConfig,
205-
originalProcessSectionDelay,
203+
var originalConfig,
206204
tmpDiv;
207205

208-
var setConfig2 = function() {
209-
originalConfig = Lib.extendDeepAll({}, MathJax.Hub.config);
210-
211-
originalProcessSectionDelay = MathJax.Hub.processSectionDelay;
212-
if(MathJax.Hub.processSectionDelay !== undefined) {
213-
// MathJax 2.5+ but not 3+
214-
MathJax.Hub.processSectionDelay = 0;
215-
}
216-
217-
return MathJax.Hub.Config({
218-
messageStyle: 'none',
219-
tex2jax: {
220-
inlineMath: inlineMath
221-
},
222-
displayAlign: 'left',
223-
});
224-
};
225-
226-
var setConfig3 = function() {
206+
const setConfig = function() {
227207
originalConfig = Lib.extendDeepAll({}, MathJax.config);
228208

229209
if(!MathJax.config.tex) {
230210
MathJax.config.tex = {};
231211
}
232212

233213
MathJax.config.tex.inlineMath = inlineMath;
234-
};
235-
236-
var setRenderer2 = function() {
237-
originalRenderer = MathJax.Hub.config.menuSettings.renderer;
238-
if(originalRenderer !== 'SVG') {
239-
return MathJax.Hub.setRenderer('SVG');
240-
}
241-
};
242214

243-
var setRenderer3 = function() {
244-
originalRenderer = MathJax.config.startup.output;
245-
if(originalRenderer !== 'svg') {
215+
if(MathJax.config.startup.output !== 'svg') {
246216
MathJax.config.startup.output = 'svg';
247217
}
248218
};
249219

250-
var initiateMathJax = function() {
251-
var randomID = 'math-output-' + Lib.randstr({}, 64);
220+
const initiateMathJax = function() {
221+
const randomID = 'math-output-' + Lib.randstr({}, 64);
252222
tmpDiv = d3.select('body').append('div')
253223
.attr({id: randomID})
254224
.style({
@@ -258,81 +228,45 @@ function texToSVG(_texString, _config, _callback) {
258228
})
259229
.text(cleanEscapesForTex(_texString));
260230

261-
var tmpNode = tmpDiv.node();
231+
const tmpNode = tmpDiv.node();
262232

263-
return MathJaxVersion === 2 ?
264-
MathJax.Hub.Typeset(tmpNode) :
265-
MathJax.typeset([tmpNode]);
233+
return MathJax.typesetPromise([tmpNode]);
266234
};
267235

268-
var finalizeMathJax = function() {
269-
var sel = tmpDiv.select(
270-
MathJaxVersion === 2 ? '.MathJax_SVG' : '.MathJax'
271-
);
236+
const finalizeMathJax = function() {
237+
const sel = tmpDiv.select('.MathJax');
272238

273-
var node = !sel.empty() && tmpDiv.select('svg').node();
239+
const node = !sel.empty() && tmpDiv.select('svg').node();
274240
if(!node) {
275241
Lib.log('There was an error in the tex syntax.', _texString);
276242
_callback();
277243
} else {
278-
var nodeBBox = node.getBoundingClientRect();
279-
var glyphDefs;
280-
if(MathJaxVersion === 2) {
281-
glyphDefs = d3.select('body').select('#MathJax_SVG_glyphs');
282-
} else {
283-
glyphDefs = sel.select('defs');
284-
}
244+
const nodeBBox = node.getBoundingClientRect();
245+
const glyphDefs = sel.select('defs');
285246
_callback(sel, glyphDefs, nodeBBox);
286247
}
287248

288249
tmpDiv.remove();
289250
};
290251

291-
var resetRenderer2 = function() {
292-
if(originalRenderer !== 'SVG') {
293-
return MathJax.Hub.setRenderer(originalRenderer);
294-
}
295-
};
296-
297-
var resetRenderer3 = function() {
298-
if(originalRenderer !== 'svg') {
299-
MathJax.config.startup.output = originalRenderer;
300-
}
301-
};
302-
303-
var resetConfig2 = function() {
304-
if(originalProcessSectionDelay !== undefined) {
305-
MathJax.Hub.processSectionDelay = originalProcessSectionDelay;
306-
}
307-
return MathJax.Hub.Config(originalConfig);
308-
};
309-
310-
var resetConfig3 = function() {
252+
// Restore the original state of the global MathJax config we mutated above
253+
// This also restores the renderer to its original value
254+
const resetConfig = function() {
311255
MathJax.config = originalConfig;
312256
};
313257

314-
if(MathJaxVersion === 2) {
315-
MathJax.Hub.Queue(
316-
setConfig2,
317-
setRenderer2,
318-
initiateMathJax,
319-
finalizeMathJax,
320-
resetRenderer2,
321-
resetConfig2
322-
);
323-
} else if(MathJaxVersion === 3) {
324-
setConfig3();
325-
setRenderer3();
326-
MathJax.startup.defaultReady();
327-
328-
MathJax.startup.promise.then(function() {
329-
initiateMathJax();
330-
finalizeMathJax();
331-
332-
resetRenderer3();
333-
resetConfig3();
334-
});
335-
}
258+
// Set up MathJax, render tex, then clean up and return
259+
setConfig();
260+
MathJax.startup.defaultReady();
261+
MathJax.startup.promise
262+
.then(initiateMathJax)
263+
.then(finalizeMathJax)
264+
.catch((err) => {
265+
Lib.log('MathJax typesetting failed.', _texString, err);
266+
if(tmpDiv) tmpDiv.remove();
267+
_callback();
268+
})
269+
.then(resetConfig);
336270
}
337271

338272
var TAG_STYLES = {

src/plot_api/plot_config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ var configAttributes = {
2727
dflt: true,
2828
description: [
2929
'Determines whether math should be typeset or not,',
30-
'when MathJax (either v2 or v3) is present on the page.'
30+
'when MathJax (either v3 or v4) is present on the page.'
3131
].join(' ')
3232
},
3333

src/types/generated/schema.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16842,7 +16842,7 @@ export interface ConfigBase {
1684216842
*/
1684316843
topojsonURL?: string;
1684416844
/**
16845-
* Determines whether math should be typeset or not, when MathJax (either v2 or v3) is present on the page.
16845+
* Determines whether math should be typeset or not, when MathJax (either v3 or v4) is present on the page.
1684616846
* @default true
1684716847
*/
1684816848
typesetMath?: boolean;

test/image/make_baseline.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@
3636

3737
print("output to", dirOut)
3838

39-
mathjax_version = 2
40-
mathjax = None
4139
if "mathjax3" in sys.argv or "mathjax3=" in sys.argv:
4240
# until https://github.com/plotly/Kaleido/issues/124 is addressed
4341
# we are uanble to use local mathjax v3 installed in node_modules
@@ -46,6 +44,11 @@
4644

4745
mathjax_version = 3
4846
print("Kaleido using MathJax v3")
47+
else:
48+
mathjax_version = 4
49+
# Kaleido still defaults to MathJax v2, so we need to explicitly specify the path to MathJax v4
50+
mathjax = "https://cdn.jsdelivr.net/npm/mathjax@4.1.2/tex-svg.js"
51+
print("Kaleido using MathJax v4")
4952

5053
virtual_webgl_version = 0 # i.e. virtual-webgl is not used
5154
if "virtual-webgl" in sys.argv or "virtual-webgl=" in sys.argv:

test/jasmine/bundle_tests/mathjax_config_test.js

Lines changed: 13 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,46 +16,28 @@ describe('Test MathJax v' + mathjaxVersion + ' config test:', function() {
1616
beforeAll(function(done) {
1717
gd = createGraphDiv();
1818

19-
if(mathjaxVersion === 3) {
20-
window.MathJax = {
21-
startup: {
22-
output: 'chtml',
23-
tex: {
24-
inlineMath: [['|', '|']]
25-
}
19+
window.MathJax = {
20+
startup: {
21+
output: 'chtml',
22+
tex: {
23+
inlineMath: [['|', '|']]
2624
}
27-
};
28-
}
25+
}
26+
};
2927

30-
var src = mathjaxVersion === 3 ?
28+
const src = mathjaxVersion === 3 ?
3129
'/base/node_modules/@plotly/mathjax-v3/es5/tex-svg.js' :
32-
'/base/node_modules/@plotly/mathjax-v2/MathJax.js?config=TeX-AMS_SVG';
30+
'/base/node_modules/@plotly/mathjax-v4/tex-svg.js';
3331

3432
loadScript(src, done);
3533
});
3634

3735
afterAll(destroyGraphDiv);
3836

3937
it('should maintain startup renderer & inlineMath after SVG rendering', function(done) {
40-
if(mathjaxVersion === 2) {
41-
window.MathJax.Hub.Config({
42-
tex2jax: {
43-
inlineMath: [['|', '|']]
44-
}
45-
});
46-
47-
window.MathJax.Hub.setRenderer('CHTML');
48-
}
49-
5038
// before plot
51-
if(mathjaxVersion === 3) {
52-
expect(window.MathJax.config.startup.output).toEqual('chtml');
53-
expect(window.MathJax.config.startup.tex.inlineMath).toEqual([['|', '|']]);
54-
}
55-
if(mathjaxVersion === 2) {
56-
expect(window.MathJax.Hub.config.menuSettings.renderer).toEqual('');
57-
expect(window.MathJax.Hub.config.tex2jax.inlineMath).toEqual([['|', '|']]);
58-
}
39+
expect(window.MathJax.config.startup.output).toEqual('chtml');
40+
expect(window.MathJax.config.startup.tex.inlineMath).toEqual([['|', '|']]);
5941

6042
Plotly.newPlot(gd, {
6143
data: [{
@@ -69,19 +51,8 @@ describe('Test MathJax v' + mathjaxVersion + ' config test:', function() {
6951
})
7052
.then(function() {
7153
// after plot
72-
if(mathjaxVersion === 3) {
73-
expect(window.MathJax.config.startup.output).toEqual('chtml');
74-
expect(window.MathJax.config.startup.tex.inlineMath).toEqual([['|', '|']]);
75-
}
76-
if(mathjaxVersion === 2) {
77-
expect(window.MathJax.Hub.config.menuSettings.renderer).toEqual('');
78-
}
79-
})
80-
.then(delay(1000)) // TODO: why we need this delay for mathjax v2 here?
81-
.then(function() {
82-
if(mathjaxVersion === 2) {
83-
expect(window.MathJax.Hub.config.tex2jax.inlineMath).toEqual([['|', '|']]);
84-
}
54+
expect(window.MathJax.config.startup.output).toEqual('chtml');
55+
expect(window.MathJax.config.startup.tex.inlineMath).toEqual([['|', '|']]);
8556
})
8657
.then(done, done.fail);
8758
});

0 commit comments

Comments
 (0)