Skip to content

Commit e478932

Browse files
committed
Drop support for IE8, fix duplicate IDs
1 parent fcc96de commit e478932

13 files changed

+703
-1064
lines changed

README.md

+1-34
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ In browsers that support it, MathML allows for the display of maths equations in
66

77
But because most browsers do _not_ support MathML, fallback content is necessary. Traditionally, this has been done with the JavaScript library [MathJax](https://www.mathjax.org/), but MathJax suffers from the same problem any JavaScript library has - it is a large download that slows down the users browsing; and doesn't work when JavaScript is turned off (or more likely, broken by another script on the page).
88

9-
MathMLNow is a Node package that produces a series of HTML tags that show MathML if available, gracefully degrading to an SVG image, and then to a PNG image if even SVG is not supported.
9+
MathMLNow is a Node package that produces a series of HTML tags that show MathML if available, gracefully degrading to an SVG image where MathMl is not supported.
1010

1111
To see an example of this technique in action, visit [this example page](https://sora2455.github.io/MathMlNow/).
1212

@@ -20,41 +20,19 @@ The file provided is called `MathMLNow`. To generate the MathMLNow used in the e
2020
//Remember to escape '\' characters!
2121
MathMLNow("x=\\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}", {
2222
formatName: "TeX",
23-
imageFolder: "/img/",
2423
fontSize: 18
2524
}).then((result) => console.log("Quadratic formula:\n" + result));
2625
MathMLNow("e^{i\\pi}+1=0", {
2726
formatName: "TeX",
28-
imageFolder: "/img/",
2927
fontSize: 30
3028
}).then((result) => console.log("Euler's identity:\n" + result));
3129
MathMLNow("\\int_0^{\\pi/6}\\sec\\left(y\\right)\\operatorname dy=\\ln\\left(\\sqrt3i^{64}\\right)", {
3230
formatName: "TeX",
33-
imageFolder: "/img/",
3431
fontSize: 16,
3532
verticalMarginPercent: 20
3633
}).then((result) => console.log("Integral of the secant function:\n" + result));
3734
```
3835

39-
If you wish to support browsers that do not support SVG graphics (about [2.5% of the world](https://caniuse.com/#feat=svg-html5) at the time of writing), you need to pass the location to your website's image folder to the imageFolder config option.
40-
In addition, add the following to your website's CSS:
41-
42-
```CSS
43-
.mmln-f {
44-
display: block;
45-
position: absolute;
46-
left: -100%;
47-
height: 0;
48-
width: 0;
49-
overflow: hidden;
50-
}
51-
svg image.mml-i {
52-
display: none;
53-
}
54-
```
55-
56-
(Thanks to [CSS-Tricks](https://css-tricks.com/a-complete-guide-to-svg-fallbacks/) for sharing this nugget of wisdom.)
57-
5836
If you wish to replace inline math instances in a large file (say, a HTML page), then you can pipe it as a [vinyl](https://github.com/gulpjs/vinyl) stream:
5937

6038
```JavaScript
@@ -65,7 +43,6 @@ const mmlN = require("math-ml-now");
6543
gulp.task('mathReplace', () => {
6644
const replacer = new mmlN.MathMlReplacer({
6745
formatName: "TeX",
68-
imageFolder: "/img/",
6946
});
7047

7148
return gulp.src("**/*.pre.html")
@@ -123,16 +100,6 @@ interface MathMLNowOptions {
123100
* The format of the math input
124101
*/
125102
formatName: "TeX" | "inline-TeX" | "AsciiMath" | "MathML";
126-
/**
127-
* If you want to support browsers that can't render MathML or SVG (generally IE8 and below)
128-
* then include a relative file path to your image folder here (must end in a '/'!). PNG fallbacks
129-
* for the SVG files will be saved there.
130-
*/
131-
imageFolder?: string;
132-
/**
133-
* The filename to save any image files under (defaults to a hash of the math input)
134-
*/
135-
fileName?: string;
136103
/**
137104
* Use to set the effective font-size (in pixels) of the maths expression (defaults to 18)
138105
*/

app.d.ts

+3-12
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,6 @@ export interface MathMLNowOptions {
33
* The format of the math input
44
*/
55
formatName: "TeX" | "inline-TeX" | "AsciiMath" | "MathML";
6-
/**
7-
* If you want to support browsers that can't render MathML or SVG (generally IE8 and below)
8-
* then include a relative file path to your image folder here (must end in a '/'!). PNG fallbacks
9-
* for the SVG files will be saved there.
10-
*/
11-
imageFolder?: string;
12-
/**
13-
* The filename to save any image files under (defaults to a hash of the math input)
14-
*/
15-
fileName?: string;
166
/**
177
* Use to set the effective font-size (in pixels) of the maths expression (defaults to 18)
188
*/
@@ -39,8 +29,9 @@ import File = require("vinyl");
3929
* maths equation in a way understood by all browsers
4030
* @param mathString The string representation of the maths equation you wish to display
4131
* @param options The MathMLNowOptions object that will control the behaviour of the rendered equation
32+
* @param id The ID number to use (should be incremented if called multiple times on the same page for unique ids)
4233
*/
43-
export declare function MathMLNow(mathString: string, options: MathMLNowOptions): Promise<string>;
34+
export function MathMLNow(mathString: string, options: MathMLNowOptions, id?: number) : Promise<string>;
4435
/**
4536
* A Gulp-style replacer function that will rewrite large chunks of text (like a HTML page),
4637
* replacing instances of $$[Math string]$$ with the corresponding MathMLNow
@@ -79,5 +70,5 @@ export declare class MathMlReplacer extends stream.Transform {
7970
/**
8071
* @inheritdoc
8172
*/
82-
_transform(file: File, enc: string, callback: (err?: any, val?: File) => void): void;
73+
_transform(file: File, enc: BufferEncoding, callback: (err?: any, val?: File) => void): void;
8374
}

app.js

+21-57
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,20 @@ var __extends = (this && this.__extends) || (function () {
33
var extendStatics = function (d, b) {
44
extendStatics = Object.setPrototypeOf ||
55
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6-
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
6+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
77
return extendStatics(d, b);
88
};
99
return function (d, b) {
10+
if (typeof b !== "function" && b !== null)
11+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
1012
extendStatics(d, b);
1113
function __() { this.constructor = d; }
1214
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
1315
};
1416
})();
15-
Object.defineProperty(exports, "__esModule", { value: true });
17+
exports.__esModule = true;
18+
exports.MathMlReplacer = exports.MathMLNow = void 0;
1619
var mjAPI = require("mathjax-node-sre");
17-
var convertSvgToPng = require("convert-svg-to-png");
18-
var hash = require("string-hash");
19-
var fs = require("fs");
20-
var path = require("path");
2120
var stream = require("stream");
2221
mjAPI.config({
2322
MathJax: {
@@ -42,8 +41,9 @@ function replaceWithHTMLEntities(rawStr) {
4241
* maths equation in a way understood by all browsers
4342
* @param mathString The string representation of the maths equation you wish to display
4443
* @param options The MathMLNowOptions object that will control the behaviour of the rendered equation
44+
* @param id The ID number to use (should be incremented if called multiple times on the same page for unique ids)
4545
*/
46-
function MathMLNow(mathString, options) {
46+
function MathMLNow(mathString, options, id) {
4747
//Default font size is 18
4848
if (!options.fontSize)
4949
options.fontSize = 18;
@@ -53,6 +53,8 @@ function MathMLNow(mathString, options) {
5353
//Default horizontal whitespace margin is 0%
5454
if (!options.horizontalMarginPercent)
5555
options.horizontalMarginPercent = 0;
56+
//Default id is 1
57+
id = id || 1;
5658
return mjAPI.typeset({
5759
math: mathString,
5860
format: options.formatName,
@@ -83,6 +85,10 @@ function MathMLNow(mathString, options) {
8385
svg.setAttribute("x", horizontalMargin.toString());
8486
if (!!verticalMargin)
8587
svg.setAttribute("y", verticalMargin.toString());
88+
//Set the ID for the SVG label
89+
var titleId = "MathJax-SVG-" + id + "-Title";
90+
svg.setAttribute("aria-labelledby", titleId);
91+
svg.querySelector("title").id = titleId;
8692
//Scaling and coloring the MathML requires a <mstyle> element
8793
var mstyle = mml.ownerDocument.createElementNS("http://www.w3.org/1998/Math/MathML", "mstyle");
8894
mstyle.setAttribute("mathsize", (Math.floor(options.fontSize)).toString() + "pt");
@@ -110,48 +116,6 @@ function MathMLNow(mathString, options) {
110116
parentSvg.setAttribute("height", heightWithMargin.toString());
111117
parentSvg.setAttribute("width", widthWithMargin.toString());
112118
parentSvg.setAttribute("role", "presentation");
113-
if (options.imageFolder) {
114-
//Same as above, plus:
115-
//For the browsers that don't support SVG, we'll render a PNG instead
116-
var pngFilePath_1 = options.imageFolder + (options.fileName || hash(mathString).toString()) + ".png";
117-
var basePath_1 = __dirname;
118-
if (basePath_1.includes("node_modules")) {
119-
//If we're being called as a node_module, our actuall base path is two folders up
120-
basePath_1 = path.join(basePath_1, "../../");
121-
}
122-
var svgBuffer = Buffer.from("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
123-
svg.outerHTML, "utf8");
124-
//For the browsers that don't support SVG, we'll render a PNG instead
125-
return convertSvgToPng.convert(svgBuffer, {
126-
//Make the image three times as large to help with quality
127-
scale: 3
128-
}).then(function (png) {
129-
return new Promise(function (resolve, reject) {
130-
fs.writeFile(path.join(basePath_1, pngFilePath_1), png, function (error) {
131-
if (error)
132-
reject(error);
133-
//Hiding the SVG text in unsuporting browsers requires an <a> tag wrapped around it's contents
134-
var svga = mml.ownerDocument.createElementNS("http://www.w3.org/2000/svg", "a");
135-
svga.classList.add("mmln-f");
136-
//Move the math nodes into the style node
137-
var parentSvgChildNodes = Array.from(parentSvg.childNodes);
138-
parentSvgChildNodes.forEach(function (value) {
139-
svga.appendChild(value);
140-
});
141-
parentSvg.appendChild(svga);
142-
var img = mml.ownerDocument.createElementNS("http://www.w3.org/2000/svg", "image");
143-
img.setAttribute("src", pngFilePath_1);
144-
img.setAttribute("height", svg.getAttribute("height"));
145-
img.setAttribute("width", svg.getAttribute("width"));
146-
img.setAttribute("alt", data.speakText);
147-
img.setAttribute("xlink:href", "");
148-
img.classList.add("mml-i");
149-
parentSvg.appendChild(img);
150-
resolve(replaceWithHTMLEntities(parentSvg.outerHTML));
151-
});
152-
});
153-
});
154-
}
155119
return replaceWithHTMLEntities(parentSvg.outerHTML);
156120
});
157121
}
@@ -169,6 +133,7 @@ var MathMlReplacer = /** @class */ (function (_super) {
169133
*/
170134
function MathMlReplacer(options) {
171135
var _this = _super.call(this, { objectMode: true }) || this;
136+
_this.state = 1;
172137
_this.options = options || { formatName: "TeX" };
173138
_this.options.formatName = options.formatName || "TeX";
174139
return _this;
@@ -191,7 +156,7 @@ var MathMlReplacer = /** @class */ (function (_super) {
191156
re.lastIndex = i;
192157
var m = void 0;
193158
while (m = re.exec(str)) {
194-
var args = m.concat([m.index, m.input]);
159+
var args = m.concat([m.index.toString(), m.input]);
195160
parts.push(str.slice(i, m.index), callback.apply(null, args));
196161
i = re.lastIndex;
197162
if (!re.global)
@@ -228,37 +193,37 @@ var MathMlReplacer = /** @class */ (function (_super) {
228193
indiviualOptions.verticalMarginPercent = Number(vMargin);
229194
indiviualOptions.horizontalMarginPercent = Number(hMargin);
230195
indiviualOptions.fontColor = fontColor;
231-
return MathMLNow(math, indiviualOptions);
196+
return MathMLNow(math, indiviualOptions, _this.state++);
232197
}).then(function (data) {
233198
return _this.replaceAsync(data, /\$\$(.+?)\|\|(\d+)\|\|(\d+)\|\|(\d+)\$\$/g, function (match, math, fontSize, vMargin, hMargin) {
234199
var indiviualOptions = Object.create(_this.options);
235200
indiviualOptions.fontSize = Number(fontSize);
236201
indiviualOptions.verticalMarginPercent = Number(vMargin);
237202
indiviualOptions.horizontalMarginPercent = Number(hMargin);
238-
return MathMLNow(math, indiviualOptions);
203+
return MathMLNow(math, indiviualOptions, _this.state++);
239204
});
240205
}).then(function (data) {
241206
return _this.replaceAsync(data, /\$\$(.+?)\|\|(\d+)\|\|(\d+)\$\$/g, function (match, math, fontSize, vMargin) {
242207
var indiviualOptions = Object.create(_this.options);
243208
indiviualOptions.fontSize = Number(fontSize);
244209
indiviualOptions.verticalMarginPercent = Number(vMargin);
245-
return MathMLNow(math, indiviualOptions);
210+
return MathMLNow(math, indiviualOptions, _this.state++);
246211
});
247212
}).then(function (data) {
248213
return _this.replaceAsync(data, /\$\$(.+?)\|\|(\d+)\$\$/g, function (match, math, fontSize) {
249214
var indiviualOptions = Object.create(_this.options);
250215
indiviualOptions.fontSize = Number(fontSize);
251-
return MathMLNow(math, indiviualOptions);
216+
return MathMLNow(math, indiviualOptions, _this.state++);
252217
});
253218
}).then(function (data) {
254219
return _this.replaceAsync(data, /\$\$(.+?)\$\$/g, function (match, math) {
255-
return MathMLNow(math, _this.options);
220+
return MathMLNow(math, _this.options, _this.state++);
256221
});
257222
}).then(function (processedTemp) {
258223
//All done! Write to the file now!
259224
file.contents = Buffer.from(processedTemp, enc);
260225
callback(null, file);
261-
}).catch(function (reason) {
226+
})["catch"](function (reason) {
262227
//If there was a fail, pass the reason why up the chain
263228
callback(reason);
264229
});
@@ -299,4 +264,3 @@ var MathMlReplacer = /** @class */ (function (_super) {
299264
return MathMlReplacer;
300265
}(stream.Transform));
301266
exports.MathMlReplacer = MathMlReplacer;
302-
//# sourceMappingURL=app.js.map

0 commit comments

Comments
 (0)