From 2b3ac9fcbb69fa568487b67835d54763673f83a7 Mon Sep 17 00:00:00 2001 From: jmnote Date: Thu, 8 Oct 2026 23:47:48 +0900 Subject: [PATCH 1/2] Fall back to default settings when page config is missing The page HTML that carries the wgSmj* settings can be cached separately from the ext.SimpleMathJax module (CDN or reverse proxy). Right after an upgrade that renamed settings (1.0), a cached page paired with the new module left wgSmjDelimitersInlineMath null, so ensureLoaded() threw on .concat and no formula rendered. Read settings through a helper that falls back to the extension.json defaults, and test that the two stay in sync. --- Makefile | 1 + resources/ext.SimpleMathJax.js | 39 ++++++++++++++++++++------- tests/JsDefaultsTest.php | 48 ++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 tests/JsDefaultsTest.php diff --git a/Makefile b/Makefile index f96edcca..ba101d56 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,7 @@ test: ## Run the pure-PHP test suites php tests/QuotesTest.php php tests/RevisionOverridesTest.php php tests/IgnoreHtmlClassTest.php + php tests/JsDefaultsTest.php phpcs: vendor/autoload.php ## parallel-lint + minus-x + phpcs against the MediaWiki coding standard composer test diff --git a/resources/ext.SimpleMathJax.js b/resources/ext.SimpleMathJax.js index d772ce13..cb36f9dc 100644 --- a/resources/ext.SimpleMathJax.js +++ b/resources/ext.SimpleMathJax.js @@ -3,6 +3,25 @@ mw.libs.smj = mw.libs.smj || {}; var loaded = false; var scriptLoadPromise = null; +// Defaults match extension.json. The page HTML that carries these settings +// can be cached (CDN, reverse proxy) separately from this module, so after an +// upgrade the two may come from different versions; fall back instead of +// failing to render any formula. +var defaults = { + wgSmjCdnEnabled: true, + wgSmjCdnVersion: '4', + wgSmjDelimitersEnabled: false, + wgSmjDelimitersInlineMath: [], + wgSmjDelimitersDisplayMath: [], + wgSmjIgnoreHtmlClass: 'mathjax_ignore|comment|diff-(context|addedline|deletedline)', + wgSmjScale: 1 +}; + +function config(name) { + var value = mw.config.get(name); + return value === null || value === undefined ? defaults[name] : value; +} + function ensureLoaded() { if (loaded) { return; @@ -10,11 +29,11 @@ function ensureLoaded() { loaded = true; window.MathJax = { tex: { - inlineMath: mw.config.get('wgSmjDelimitersInlineMath').concat([['[math]','[/math]']]), - displayMath: mw.config.get('wgSmjDelimitersDisplayMath'), + inlineMath: config('wgSmjDelimitersInlineMath').concat([['[math]','[/math]']]), + displayMath: config('wgSmjDelimitersDisplayMath'), processEnvironments: true, - processRefs: mw.config.get('wgSmjDelimitersEnabled'), - processEscapes: mw.config.get('wgSmjDelimitersEnabled'), + processRefs: config('wgSmjDelimitersEnabled'), + processEscapes: config('wgSmjDelimitersEnabled'), packages: mw.config.exists('smjPreloadChem') ? {'[+]': ['autoload','mhchem']} : {'[+]': ['autoload']}, macros: { AA: "{\u00c5}", @@ -124,17 +143,17 @@ function ensureLoaded() { } }, options: { - ignoreHtmlClass: mw.config.get('wgSmjIgnoreHtmlClass'), + ignoreHtmlClass: config('wgSmjIgnoreHtmlClass'), processHtmlClass: "mathjax_process|smj-container" }, chtml: { - scale: mw.config.get('wgSmjScale'), + scale: config('wgSmjScale'), }, loader: { load: ['ui/safe','[tex]/autoload'].concat(mw.config.exists('smjPreloadChem') ? ['[tex]/mhchem'] : []) }, startup: { - elements: mw.config.get('wgSmjDelimitersEnabled') ? null : ["span.smj-container"], + elements: config('wgSmjDelimitersEnabled') ? null : ["span.smj-container"], pageReady: () => { return MathJax.startup.defaultPageReady().then(() => { document.querySelectorAll("span.smj-container > .MathJax").forEach((mjx) => { @@ -146,8 +165,8 @@ function ensureLoaded() { }; var script = document.createElement('script'); - script.src = mw.config.get('wgSmjCdnEnabled') - ? 'https://cdn.jsdelivr.net/npm/mathjax@' + mw.config.get('wgSmjCdnVersion') + '/tex-chtml.js' + script.src = config('wgSmjCdnEnabled') + ? 'https://cdn.jsdelivr.net/npm/mathjax@' + config('wgSmjCdnVersion') + '/tex-chtml.js' : mw.config.get('wgExtensionAssetsPath') + '/SimpleMathJax/resources/MathJax/tex-chtml.js'; script.async = true; scriptLoadPromise = new Promise((resolve, reject) => { @@ -183,7 +202,7 @@ mw.hook('wikipage.content').add(function ($content) { return; } var $containers = $content.filter('.smj-container').add($content.find('.smj-container')); - var delimitersEnabled = mw.config.get('wgSmjDelimitersEnabled'); + var delimitersEnabled = config('wgSmjDelimitersEnabled'); if (!delimitersEnabled && !$containers.length) { return; } diff --git a/tests/JsDefaultsTest.php b/tests/JsDefaultsTest.php new file mode 100644 index 00000000..d0c61a21 --- /dev/null +++ b/tests/JsDefaultsTest.php @@ -0,0 +1,48 @@ + $value ) { + $key = substr( $name, 2 ); + $expected = $extension['config'][$key]['value'] ?? null; + assert_same( "$name default matches extension.json", $expected, $value ); +} + +echo $failures === 0 ? "\nAll tests passed.\n" : "\n$failures test(s) FAILED.\n"; +exit( $failures === 0 ? 0 : 1 ); From 1731688151aee981e3656eceba574d8470c02e22 Mon Sep 17 00:00:00 2001 From: jmnote Date: Fri, 9 Oct 2026 00:00:41 +0900 Subject: [PATCH 2/2] Honor pre-1.0 wgSmjUseCdn on pages cached before the upgrade wgSmjCdnEnabled is a pure rename of wgSmjUseCdn. A cached pre-1.0 page carries only the old name, so a site with the CDN disabled would load MathJax from jsDelivr. Settings whose meaning changed in 1.0 (delimiters) are deliberately not mapped. --- resources/ext.SimpleMathJax.js | 16 +++++++++++++++- tests/JsDefaultsTest.php | 14 ++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/resources/ext.SimpleMathJax.js b/resources/ext.SimpleMathJax.js index cb36f9dc..c949ff5f 100644 --- a/resources/ext.SimpleMathJax.js +++ b/resources/ext.SimpleMathJax.js @@ -17,9 +17,23 @@ var defaults = { wgSmjScale: 1 }; +// Pre-1.0 names of settings that were only renamed (see docs/mig-1.0.md), +// consulted for pages cached before the upgrade. Settings whose meaning or +// default changed in 1.0 (e.g. delimiters) are not mapped. +var legacyNames = { + wgSmjCdnEnabled: 'wgSmjUseCdn' +}; + +function isSet(value) { + return value !== null && value !== undefined; +} + function config(name) { var value = mw.config.get(name); - return value === null || value === undefined ? defaults[name] : value; + if (!isSet(value) && legacyNames[name]) { + value = mw.config.get(legacyNames[name]); + } + return isSet(value) ? value : defaults[name]; } function ensureLoaded() { diff --git a/tests/JsDefaultsTest.php b/tests/JsDefaultsTest.php index d0c61a21..0d61e515 100644 --- a/tests/JsDefaultsTest.php +++ b/tests/JsDefaultsTest.php @@ -44,5 +44,19 @@ function assert_same( $name, $expected, $actual ) { assert_same( "$name default matches extension.json", $expected, $value ); } +// Legacy names are only for settings the migration guide lists as renamed. +if ( preg_match( '/var legacyNames = (\{.*?\});/s', $js, $m ) ) { + preg_match_all( "/(\w+): '(\w+)'/", $m[1], $pairs, PREG_SET_ORDER ); + $guide = file_get_contents( __DIR__ . '/../docs/mig-1.0.md' ); + foreach ( $pairs as [ , $name, $legacy ] ) { + assert_same( "$name has a client default", true, array_key_exists( $name, $defaults ?? [] ) ); + assert_same( + "$legacy -> $name is a rename in docs/mig-1.0.md", + 1, + preg_match( '/\| `\$' . $legacy . '` \| `\$' . $name . '` \|/', $guide ) + ); + } +} + echo $failures === 0 ? "\nAll tests passed.\n" : "\n$failures test(s) FAILED.\n"; exit( $failures === 0 ? 0 : 1 );