From 951523d1f53fdac893e6c05e4c8cbd2b3cd9e5c4 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Tue, 11 Sep 2018 10:59:44 -0400 Subject: [PATCH 01/10] Support rendering tex even when global MathJax rendering mode is not SVG 1. Specify the SVG font 2. Set rendering mode to SVG just prior to typesetting, then restore it to it's original value just afterwards With this change, latex rendering works in the following MathJax configurations: - TeX-MML-AM_CHTML - TeX-MML-AM_HTMLorMML - TeX-MML-AM_SVG - TeX-AMS-MML_HTMLorMML - TeX-AMS_CHTML - TeX-AMS_SVG - TeX-AMS_HTML - TeX-AMS-MML_SVG (It still does not work if the global MathJax TeX extensions are not loaded.) Previously it only worked in the TeX-AMS-MML_SVG configuration. Also, with these changes latex rendering works without any additional configuration when used from Python (and probably R) inside the Jupyter Notebook. --- src/fonts/mathjax_config.js | 3 ++- src/lib/svg_text_utils.js | 14 +++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/fonts/mathjax_config.js b/src/fonts/mathjax_config.js index 0fb1c2a6d2c..89a75208ce4 100644 --- a/src/fonts/mathjax_config.js +++ b/src/fonts/mathjax_config.js @@ -22,7 +22,8 @@ if(typeof MathJax !== 'undefined') { displayAlign: 'left', tex2jax: { inlineMath: [['$', '$'], ['\\(', '\\)']] - } + }, + SVG: {font: 'STIX-Web'}, }); MathJax.Hub.Configured(); diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js index b320c560183..57ba8ff3bb4 100644 --- a/src/lib/svg_text_utils.js +++ b/src/lib/svg_text_utils.js @@ -170,7 +170,19 @@ function texToSVG(_texString, _config, _callback) { .style({'font-size': _config.fontSize + 'px'}) .text(cleanEscapesForTex(_texString)); - MathJax.Hub.Queue(['Typeset', MathJax.Hub, tmpDiv.node()], function() { + var originalRenderer; + MathJax.Hub.Queue(function() { + // Get original renderer + originalRenderer = MathJax.Hub.config.menuSettings.renderer; + }, + ['setRenderer', MathJax.Hub, 'SVG'], + ['Typeset', MathJax.Hub, tmpDiv.node()], + function() { + // Restore original renderer if not SVG + if(originalRenderer !== 'SVG') { + MathJax.Hub.Queue(['setRenderer', MathJax.Hub, originalRenderer]); + } + var glyphDefs = d3.select('body').select('#MathJax_SVG_glyphs'); if(tmpDiv.select('.MathJax_SVG').empty() || !tmpDiv.select('svg').node()) { From 1dd4fd3add23b0be3bc964e107cadea7b109303f Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Tue, 11 Sep 2018 20:11:02 -0400 Subject: [PATCH 02/10] Call final MathJax.Hub.setRenderer directly (not with Queue) This should remove a potential race condition --- src/lib/svg_text_utils.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js index 57ba8ff3bb4..ee2abb4345a 100644 --- a/src/lib/svg_text_utils.js +++ b/src/lib/svg_text_utils.js @@ -178,11 +178,6 @@ function texToSVG(_texString, _config, _callback) { ['setRenderer', MathJax.Hub, 'SVG'], ['Typeset', MathJax.Hub, tmpDiv.node()], function() { - // Restore original renderer if not SVG - if(originalRenderer !== 'SVG') { - MathJax.Hub.Queue(['setRenderer', MathJax.Hub, originalRenderer]); - } - var glyphDefs = d3.select('body').select('#MathJax_SVG_glyphs'); if(tmpDiv.select('.MathJax_SVG').empty() || !tmpDiv.select('svg').node()) { @@ -195,6 +190,8 @@ function texToSVG(_texString, _config, _callback) { } tmpDiv.remove(); + + return MathJax.Hub.setRenderer(originalRenderer); }); } From 95696485c952d05329496e8d0421cae3956c7706 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Wed, 12 Sep 2018 07:36:03 -0400 Subject: [PATCH 03/10] Don't call setRenderer if renderer is already SVG --- src/lib/svg_text_utils.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js index ee2abb4345a..e989df37dc6 100644 --- a/src/lib/svg_text_utils.js +++ b/src/lib/svg_text_utils.js @@ -174,10 +174,13 @@ function texToSVG(_texString, _config, _callback) { MathJax.Hub.Queue(function() { // Get original renderer originalRenderer = MathJax.Hub.config.menuSettings.renderer; + if(originalRenderer !== 'SVG') { + return MathJax.Hub.setRenderer('SVG'); + } }, - ['setRenderer', MathJax.Hub, 'SVG'], ['Typeset', MathJax.Hub, tmpDiv.node()], function() { + var glyphDefs = d3.select('body').select('#MathJax_SVG_glyphs'); if(tmpDiv.select('.MathJax_SVG').empty() || !tmpDiv.select('svg').node()) { @@ -191,7 +194,9 @@ function texToSVG(_texString, _config, _callback) { tmpDiv.remove(); - return MathJax.Hub.setRenderer(originalRenderer); + if(originalRenderer !== 'SVG') { + return MathJax.Hub.setRenderer(originalRenderer); + } }); } From 8d610e745c0fb5abb35ca288c72896f2530cb7f4 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Wed, 12 Sep 2018 07:59:11 -0400 Subject: [PATCH 04/10] Create tmpDiv on the `Queue` just before Typesetting This avoids race condition where an external typesetting operation is in progress (or already on the Queue) at the start `texToSVG` function. With this change, we no longer need to set `skipStartupTypeset` to true, which alters the global default MathJax behavior of typesetting the full page automatically on startup. Other utilities, such as nbconvert, have not worked properly with plotly because they rely on this default behavior --- src/fonts/mathjax_config.js | 1 - src/lib/svg_text_utils.js | 21 ++++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/fonts/mathjax_config.js b/src/fonts/mathjax_config.js index 89a75208ce4..50fd37bf0a9 100644 --- a/src/fonts/mathjax_config.js +++ b/src/fonts/mathjax_config.js @@ -18,7 +18,6 @@ if(typeof MathJax !== 'undefined') { MathJax.Hub.Config({ messageStyle: 'none', - skipStartupTypeset: true, displayAlign: 'left', tex2jax: { inlineMath: [['$', '$'], ['\\(', '\\)']] diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js index e989df37dc6..b1bf41935e2 100644 --- a/src/lib/svg_text_utils.js +++ b/src/lib/svg_text_utils.js @@ -163,14 +163,9 @@ function cleanEscapesForTex(s) { } function texToSVG(_texString, _config, _callback) { - var randomID = 'math-output-' + Lib.randstr({}, 64); - var tmpDiv = d3.select('body').append('div') - .attr({id: randomID}) - .style({visibility: 'hidden', position: 'absolute'}) - .style({'font-size': _config.fontSize + 'px'}) - .text(cleanEscapesForTex(_texString)); - - var originalRenderer; + + var originalRenderer, + tmpDiv; MathJax.Hub.Queue(function() { // Get original renderer originalRenderer = MathJax.Hub.config.menuSettings.renderer; @@ -178,7 +173,15 @@ function texToSVG(_texString, _config, _callback) { return MathJax.Hub.setRenderer('SVG'); } }, - ['Typeset', MathJax.Hub, tmpDiv.node()], + function() { + var randomID = 'math-output-' + Lib.randstr({}, 64); + tmpDiv = d3.select('body').append('div') + .attr({id: randomID}) + .style({visibility: 'hidden', position: 'absolute'}) + .style({'font-size': _config.fontSize + 'px'}) + .text(cleanEscapesForTex(_texString)); + return MathJax.Hub.Typeset(tmpDiv.node()); + }, function() { var glyphDefs = d3.select('body').select('#MathJax_SVG_glyphs'); From 6cba8e3d02054591d934d8de8335603c025d422f Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Wed, 12 Sep 2018 08:38:32 -0400 Subject: [PATCH 05/10] Perform MathJax.Hub.Config before each rendering call, and restore config afterward --- src/fonts/mathjax_config.js | 11 ----------- src/lib/svg_text_utils.js | 19 ++++++++++++++++++- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/fonts/mathjax_config.js b/src/fonts/mathjax_config.js index 50fd37bf0a9..7271efc993e 100644 --- a/src/fonts/mathjax_config.js +++ b/src/fonts/mathjax_config.js @@ -15,17 +15,6 @@ */ if(typeof MathJax !== 'undefined') { exports.MathJax = true; - - MathJax.Hub.Config({ - messageStyle: 'none', - displayAlign: 'left', - tex2jax: { - inlineMath: [['$', '$'], ['\\(', '\\)']] - }, - SVG: {font: 'STIX-Web'}, - }); - - MathJax.Hub.Configured(); } else { exports.MathJax = false; } diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js index b1bf41935e2..b4c38c424c8 100644 --- a/src/lib/svg_text_utils.js +++ b/src/lib/svg_text_utils.js @@ -165,8 +165,21 @@ function cleanEscapesForTex(s) { function texToSVG(_texString, _config, _callback) { var originalRenderer, + originalConfig, tmpDiv; - MathJax.Hub.Queue(function() { + MathJax.Hub.Queue( + function() { + originalConfig = Lib.extendDeepAll({}, MathJax.Hub.config); + return MathJax.Hub.Config({ + messageStyle: 'none', + displayAlign: 'left', + tex2jax: { + inlineMath: [['$', '$'], ['\\(', '\\)']] + }, + SVG: {font: 'STIX-Web'}, + }); + }, + function() { // Get original renderer originalRenderer = MathJax.Hub.config.menuSettings.renderer; if(originalRenderer !== 'SVG') { @@ -180,6 +193,7 @@ function texToSVG(_texString, _config, _callback) { .style({visibility: 'hidden', position: 'absolute'}) .style({'font-size': _config.fontSize + 'px'}) .text(cleanEscapesForTex(_texString)); + return MathJax.Hub.Typeset(tmpDiv.node()); }, function() { @@ -200,6 +214,9 @@ function texToSVG(_texString, _config, _callback) { if(originalRenderer !== 'SVG') { return MathJax.Hub.setRenderer(originalRenderer); } + }, + function() { + return MathJax.Hub.Config(originalConfig); }); } From b0628b055a749a06c7cceea7d29fa93861625be9 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Wed, 12 Sep 2018 20:29:16 -0400 Subject: [PATCH 06/10] A compromise solution --- src/fonts/mathjax_config.js | 9 +++++++++ src/lib/svg_text_utils.js | 4 ---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/fonts/mathjax_config.js b/src/fonts/mathjax_config.js index 7271efc993e..987cc67c386 100644 --- a/src/fonts/mathjax_config.js +++ b/src/fonts/mathjax_config.js @@ -15,6 +15,15 @@ */ if(typeof MathJax !== 'undefined') { exports.MathJax = true; + + MathJax.Hub.Config({ + skipStartupTypeset: true, + tex2jax: { + inlineMath: [['$', '$'], ['\\(', '\\)']] + }, + }); + + MathJax.Hub.Configured(); } else { exports.MathJax = false; } diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js index b4c38c424c8..8ac9ffcd24e 100644 --- a/src/lib/svg_text_utils.js +++ b/src/lib/svg_text_utils.js @@ -173,10 +173,6 @@ function texToSVG(_texString, _config, _callback) { return MathJax.Hub.Config({ messageStyle: 'none', displayAlign: 'left', - tex2jax: { - inlineMath: [['$', '$'], ['\\(', '\\)']] - }, - SVG: {font: 'STIX-Web'}, }); }, function() { From 41f7747ccd8980ff154036fceb34713c6c6f103f Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Fri, 14 Sep 2018 09:56:01 -0400 Subject: [PATCH 07/10] Set MathJax.Hub.processSectionDelay to 0 to fix jumping behavior in MathJax 2.5+ Fixes #3006 --- src/lib/svg_text_utils.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js index 8ac9ffcd24e..eae615eba32 100644 --- a/src/lib/svg_text_utils.js +++ b/src/lib/svg_text_utils.js @@ -166,10 +166,19 @@ function texToSVG(_texString, _config, _callback) { var originalRenderer, originalConfig, + originalProcessSectionDelay, tmpDiv; + MathJax.Hub.Queue( function() { originalConfig = Lib.extendDeepAll({}, MathJax.Hub.config); + + originalProcessSectionDelay = MathJax.Hub.processSectionDelay; + if(MathJax.Hub.processSectionDelay !== undefined) { + // MathJax 3.5+ + MathJax.Hub.processSectionDelay = 0; + } + return MathJax.Hub.Config({ messageStyle: 'none', displayAlign: 'left', @@ -212,6 +221,9 @@ function texToSVG(_texString, _config, _callback) { } }, function() { + if (originalProcessSectionDelay !== undefined) { + MathJax.Hub.processSectionDelay = originalProcessSectionDelay; + } return MathJax.Hub.Config(originalConfig); }); } From b02ba1730c51deff3109111c24c4d91b164e568d Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Fri, 14 Sep 2018 18:26:59 -0400 Subject: [PATCH 08/10] Add support for a new global window.PlotlyConfig configuration object Add a MathJaxConfig option that, when set to 'local', causes plotly.js to bypass all global MathJax configuration. Regardless of the value of MathJaxConfig, all MathJax rendering commands are surrounded by logic to properly configure the MathJax properties that plotly.js needs. When MathJaxConfig is not 'local', plotly.js will continue to setup the global MathJax config as it always has. This is the default behavior for backward compatibility. When plotly.js is used in a situation where it is not the only component using MathJax, then MathJaxConfig should be set to 'local' to keep from overwriting the user's desired global configuration. --- src/fonts/mathjax_config.js | 20 +++++++++++++------- src/lib/svg_text_utils.js | 8 +++++--- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/fonts/mathjax_config.js b/src/fonts/mathjax_config.js index 987cc67c386..a810af204a0 100644 --- a/src/fonts/mathjax_config.js +++ b/src/fonts/mathjax_config.js @@ -16,14 +16,20 @@ if(typeof MathJax !== 'undefined') { exports.MathJax = true; - MathJax.Hub.Config({ - skipStartupTypeset: true, - tex2jax: { - inlineMath: [['$', '$'], ['\\(', '\\)']] - }, - }); + var globalConfig = (window.PlotlyConfig || {}).MathJaxConfig !== 'local'; + + if(globalConfig) { + MathJax.Hub.Config({ + messageStyle: 'none', + skipStartupTypeset: true, + displayAlign: 'left', + tex2jax: { + inlineMath: [['$', '$'], ['\\(', '\\)']] + } + }); + MathJax.Hub.Configured(); + } - MathJax.Hub.Configured(); } else { exports.MathJax = false; } diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js index eae615eba32..54f0a660962 100644 --- a/src/lib/svg_text_utils.js +++ b/src/lib/svg_text_utils.js @@ -175,12 +175,15 @@ function texToSVG(_texString, _config, _callback) { originalProcessSectionDelay = MathJax.Hub.processSectionDelay; if(MathJax.Hub.processSectionDelay !== undefined) { - // MathJax 3.5+ + // MathJax 2.5+ MathJax.Hub.processSectionDelay = 0; } return MathJax.Hub.Config({ messageStyle: 'none', + tex2jax: { + inlineMath: [['$', '$'], ['\\(', '\\)']] + }, displayAlign: 'left', }); }, @@ -202,7 +205,6 @@ function texToSVG(_texString, _config, _callback) { return MathJax.Hub.Typeset(tmpDiv.node()); }, function() { - var glyphDefs = d3.select('body').select('#MathJax_SVG_glyphs'); if(tmpDiv.select('.MathJax_SVG').empty() || !tmpDiv.select('svg').node()) { @@ -221,7 +223,7 @@ function texToSVG(_texString, _config, _callback) { } }, function() { - if (originalProcessSectionDelay !== undefined) { + if(originalProcessSectionDelay !== undefined) { MathJax.Hub.processSectionDelay = originalProcessSectionDelay; } return MathJax.Hub.Config(originalConfig); From 6a572fd9adf7555e2c8f9acc97853067794078f6 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Fri, 14 Sep 2018 18:51:31 -0400 Subject: [PATCH 09/10] Add README description of the new window.PlotlyConfig.MathJaxConfig property --- dist/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dist/README.md b/dist/README.md index 4c3a0b445d9..fb7c64ea1eb 100644 --- a/dist/README.md +++ b/dist/README.md @@ -33,6 +33,19 @@ or the un-minified version as: You can grab the relevant MathJax files in `./dist/extras/mathjax/`. +By default, plotly.js will modify the global MathJax configuration on load. +This can lead to undesirable behavior if plotly.js is loaded alongside +other libraries that also rely on MathJax. To disable this global configuration +process, set the `MathJaxConfig` property to `'local'` in the `window.PlotlyConfig` +object. This property must be set before the plotly.js script tag, for example: + +```html + + +``` + ### To include localization Plotly.js defaults to US English (en-US) and includes British English (en) in the standard bundle. From 57e32ba6d841cc15b99e2ef68b2a494f25632068 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Mon, 17 Sep 2018 13:44:11 -0400 Subject: [PATCH 10/10] Move new MathJax instructions to `tasks/stats.js` --- dist/README.md | 13 ------------- tasks/stats.js | 13 +++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/dist/README.md b/dist/README.md index fb7c64ea1eb..4c3a0b445d9 100644 --- a/dist/README.md +++ b/dist/README.md @@ -33,19 +33,6 @@ or the un-minified version as: You can grab the relevant MathJax files in `./dist/extras/mathjax/`. -By default, plotly.js will modify the global MathJax configuration on load. -This can lead to undesirable behavior if plotly.js is loaded alongside -other libraries that also rely on MathJax. To disable this global configuration -process, set the `MathJaxConfig` property to `'local'` in the `window.PlotlyConfig` -object. This property must be set before the plotly.js script tag, for example: - -```html - - -``` - ### To include localization Plotly.js defaults to US English (en-US) and includes British English (en) in the standard bundle. diff --git a/tasks/stats.js b/tasks/stats.js index da597ce71b9..c746f1c9a60 100644 --- a/tasks/stats.js +++ b/tasks/stats.js @@ -65,6 +65,19 @@ function getInfoContent() { '', 'You can grab the relevant MathJax files in `./dist/extras/mathjax/`.', '', + 'By default, plotly.js will modify the global MathJax configuration on load.', + 'This can lead to undesirable behavior if plotly.js is loaded alongside', + 'other libraries that also rely on MathJax. To disable this global configuration', + 'process, set the `MathJaxConfig` property to `\'local\'` in the `window.PlotlyConfig`', + 'object. This property must be set before the plotly.js script tag, for example:', + '', + '```html', + '', + '', + '```', + '', '### To include localization', '', 'Plotly.js defaults to US English (en-US) and includes British English (en) in the standard bundle.',