Skip to content

Commit d9ba8c6

Browse files
authored
Merge pull request #49 from mathjax/update_demos
Update demos
2 parents 6ffe557 + 2dc6a38 commit d9ba8c6

File tree

12 files changed

+779
-77
lines changed

12 files changed

+779
-77
lines changed

speech/README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,22 @@ We use the [`tex2chtml`](tex2chtml) code as an example. The key pieces are the
1717
```js
1818
loader: {
1919
paths: {
20-
sre: 'mathjax-full/es5/a11y/sre-node'
20+
mathjax: 'mathjax-full/es5'
2121
},
2222
load: ['adaptors/liteDOM', 'a11y/semantic-enrich']
2323
},
2424
options: {
2525
enableAssistiveMml: false,
26-
enrichSpeech: argv.speech,
26+
sre: {
27+
speech: argv.speech
28+
},
2729
renderActions: require('./action.js').speechAction
2830
},
2931
```
3032

31-
The `loader` section sets up the path to the speech-rule-engine (SRE), which is used to load the node-based version of SRE (there are separate browser and node versions), and includes the `a11y/semantic-enrich` component in the list to be loaded.
33+
The `loader` section sets up the path to the directory with the MathJax components, which is here relative to the `node_modules` directory and includes the `a11y/semantic-enrich` component in the list to be loaded. Note, that unlike in previous versions of MathJax there is no need to set up the path to speech-rule-engine (SRE) as this is internally handled by MathJax.
3234

33-
The `options` section sets the document options to include the speech level (given by the `--speech` command-line option), and adds a custom `renderAction` to handle the `data-semantic` attributes generated by SRE. Because this code is used by all the examples in this directory, it is store in a separate file, [`action.js`](action.js). It defines a function that removes any attribute that starts with `data-semantic-` except for `data-semantic-speech`, since the semantic enrichment adds lots of data about the structure of the expression in these attributes. The code is commented, so see that for details. The `enableAssistiveMml` option is disabled, since the speech string for assistive technology is being included, so there is no need for the assistive MathML.
35+
The `options` section sets the document options to include the speech level as a `sre` instruction (given by the `--speech` command-line option), and adds a custom `renderAction` to handle the `data-semantic` attributes generated by SRE. Because this code is used by all the examples in this directory, it is stored in a separate file, [`action.js`](action.js). It defines a function that removes any attribute that starts with `data-semantic-` except for `data-semantic-speech`, since the semantic enrichment adds lots of data about the structure of the expression in these attributes. The code is commented, so see that for details. The `enableAssistiveMml` option is disabled, since the speech string for assistive technology is being included, so there is no need for the assistive MathML. In addition the `--sre` command-line option allows you to pass additional pairs of arguments to SRE, e.g., to change locale, rule set, or rule preference settings.
3436

3537
The rest of the file is the same as the standard `tex2chtml` using components. The `renderAction` configuration is all that is needed for that action to be taken automatically during the usual typesetting or conversion calls.
3638

speech/action.js

Lines changed: 112 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
require('mathjax-full/js/util/asyncLoad/node.js');
2+
// This is loaded to ensure `STATE.ENRICHED` is set.
23
require('mathjax-full/js/a11y/semantic-enrich.js');
3-
require('mathjax-full/js/a11y/sre-node.js');
4-
const {STATE} = require('mathjax-full/js/core/MathItem.js');
4+
const {newState, STATE} = require('mathjax-full/js/core/MathItem.js');
5+
56

67
//
78
// Remove the data-semantic-* attributes other than data-semantic-speech
@@ -19,34 +20,103 @@ function removeSemanticData(math) {
1920
}
2021

2122

22-
const sreDefault = {
23-
domain: 'mathspeak',
24-
style: 'default'
25-
};
23+
//
24+
// Moves all speech elements into aria-labels for SVG output. This allows for
25+
// elements without containers to have some limited exploration.
26+
//
27+
// Note, that here we cannot walk the source tree, as alterations are to be
28+
// done on the typeset element, if it is a SVG node.
29+
//
30+
function speechToRoles(math) {
31+
const root = math.typesetRoot;
32+
const adaptor = math.adaptor;
33+
const svg = adaptor.tags(root, 'svg')[0];
34+
if (!svg) return;
35+
adaptor.removeAttribute(svg, 'aria-hidden');
36+
adaptor.removeAttribute(svg, 'role');
37+
const children = [svg];
38+
while (children.length) {
39+
let child = children.shift();
40+
if (adaptor.kind(child) === '#text') continue;
41+
if (adaptor.hasAttribute(child, 'data-semantic-speech')) {
42+
let text = adaptor.getAttribute(child, 'data-semantic-speech');
43+
adaptor.setAttribute(child, 'aria-label', text);
44+
adaptor.setAttribute(child, 'role', 'img');
45+
adaptor.removeAttribute(child, 'data-semantic-speech');
46+
}
47+
children.push(...adaptor.childNodes(child));
48+
}
49+
}
2650

51+
//
52+
// Sets the rendered roots role to image if their is an aria-label to speak
53+
// custom elements.
54+
//
55+
function roleImg(math) {
56+
const adaptor = math.adaptor;
57+
const root = math.typesetRoot;
58+
if (adaptor.hasAttribute(root, 'aria-label')) {
59+
adaptor.setAttribute(root, 'role', 'img');
60+
}
61+
}
2762

28-
// Configures SRE from key value pairs.
29-
exports.sreconfig = function(data) {
30-
let config = {};
31-
if (data) {
32-
for (let i = 0, key; key = data[i]; i++) {
33-
let value = data[++i];
34-
config[key] = value || false;
35-
}
63+
64+
//
65+
// Configures SRE from key value pairs by populating MathJax's config options.
66+
//
67+
exports.dataPairs = function(data) {
68+
const config = {};
69+
if (data) {
70+
for (let i = 0, key; key = data[i]; i++) {
71+
let value = data[++i];
72+
config[key] = value || false;
3673
}
37-
sre.setupEngine(Object.assign({}, sreDefault, config));
74+
}
75+
return config;
3876
};
3977

78+
exports.sreconfig = function(data) {
79+
const config = exports.dataPairs(data);
80+
if (!MathJax.config.options) {
81+
MathJax.config.options = {};
82+
}
83+
if (!MathJax.config.options.sre) {
84+
MathJax.config.options.sre = {};
85+
}
86+
Object.assign(MathJax.config.options.sre, config);
87+
};
4088

4189
//
42-
// The renderActions needed to remove the data-semantic-attributes.
43-
// STATE.ENRICHED is the priority of the enrichment, so this will run just after enrichment.
90+
// Let's define some new states for enlisting new renderActions into the queue.
91+
//
92+
// STATE.ENRICHED is the priority of the enrichment. Enrichment happens before
93+
// elements are rendered (i.e., typeset) and therefore all attributes added during
94+
// enrichment will be part of the MathItem.
95+
//
96+
// STATE 1000 is so hight that any other render action should be done by
97+
// then. That is, the MathItem is fully typeset in the document.
98+
//
99+
newState('SIMPLIFY', STATE.ENRICHED + 1);
100+
newState('ROLE', 1000);
101+
newState('DESCRIBE', STATE.ROLE + 1);
102+
103+
//
104+
// The renderActions needed for manipulating MathItems with speech entries.
105+
// We define three render actions, each with two functions:
44106
// The first function is the one for when the document's render() method is called.
45107
// The second is for when a MathItem's render(), rerender() or convert() method is called.
46108
//
109+
// simplify: Removes the data-semantic-attributes except speech directly after enrichment.
110+
//
111+
// role: Adds an aria role to the container element so aria-labels are spoken on custom elements.
112+
// This happens after typesetting.
113+
//
114+
// describe: Rewrites speech attributes into aria-labels with img roles in SVGs.
115+
// This happens after container elements are rewritten.
116+
//
47117
exports.speechAction = {
48-
simplfy: [
49-
STATE.ENRICHED + 1,
118+
simplify: [
119+
STATE.SIMPLIFY,
50120
(doc) => {
51121
for (const math of doc.math) {
52122
removeSemanticData(math);
@@ -55,5 +125,28 @@ exports.speechAction = {
55125
(math, doc) => {
56126
removeSemanticData(math);
57127
}
128+
],
129+
role: [
130+
STATE.ROLE,
131+
(doc) => {
132+
for (const math of doc.math) {
133+
roleImg(math);
134+
}
135+
},
136+
(math, doc) => {
137+
roleImg(math);
138+
}
139+
],
140+
describe: [
141+
STATE.DESCRIBE,
142+
(doc) => {
143+
for (const math of doc.math) {
144+
speechToRoles(math);
145+
}
146+
},
147+
(math, doc) => {
148+
speechToRoles(math);
149+
}
58150
]
151+
59152
};

speech/mml2mml

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#! /usr/bin/env -S node -r esm
2+
3+
/*************************************************************************
4+
*
5+
* speech/mml2mml
6+
*
7+
* Uses MathJax v3 to convert a MathML string to a MathML string with alttext.
8+
*
9+
* ----------------------------------------------------------------------
10+
*
11+
* Copyright (c) 2019 The MathJax Consortium
12+
*
13+
* Licensed under the Apache License, Version 2.0 (the "License");
14+
* you may not use this file except in compliance with the License.
15+
* You may obtain a copy of the License at
16+
*
17+
* http://www.apache.org/licenses/LICENSE-2.0
18+
*
19+
* Unless required by applicable law or agreed to in writing, software
20+
* distributed under the License is distributed on an "AS IS" BASIS,
21+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22+
* See the License for the specific language governing permissions and
23+
* limitations under the License.
24+
*/
25+
26+
27+
//
28+
// Get the command-line arguments
29+
//
30+
var argv = require('yargs')
31+
.demand(0).strict()
32+
.usage('$0 [options] "math" > file.html')
33+
.options({
34+
inline: {
35+
boolean: true,
36+
describe: "process as inline math"
37+
},
38+
sre: {
39+
array: true,
40+
nargs: 2,
41+
describe: 'SRE flags as key value pairs, e.g., "--sre locale de --sre domain clearspeak" generates speech in German with clearspeak rules'
42+
},
43+
speech: {
44+
default: 'shallow',
45+
describe: 'level of speech: deep, shallow, none'
46+
},
47+
dist: {
48+
boolean: true,
49+
default: false,
50+
describe: 'true to use webpacked version, false to use mathjax3 source files'
51+
}
52+
})
53+
.argv;
54+
55+
const action = require('./action.js');
56+
57+
//
58+
// Add a render action to move the computed speech into the alttext attribute.
59+
//
60+
function moveSpeech(math) {
61+
let alttext = '';
62+
math.root.walkTree(node => {
63+
const attributes = node.attributes.getAllAttributes();
64+
if (!alttext && attributes['data-semantic-speech']) {
65+
alttext = attributes['data-semantic-speech'];
66+
}
67+
delete attributes['data-semantic-speech'];
68+
});
69+
math.root.attributes.getAllAttributes()['alttext'] = alttext;
70+
};
71+
72+
action.speechAction.alttext = [
73+
99,
74+
(doc) => {
75+
for (const math of doc.math) {
76+
moveSpeech(math);
77+
}
78+
},
79+
(math, doc) => {
80+
moveSpeech(math);
81+
}
82+
];
83+
84+
//
85+
// Configure MathJax
86+
//
87+
MathJax = {
88+
loader: {
89+
paths: {mathjax: 'mathjax-full/es5'},
90+
source: (argv.dist ? {} : require('mathjax-full/components/src/source.js').source),
91+
require: require,
92+
load: ['input/mml', 'adaptors/liteDOM', 'a11y/semantic-enrich']
93+
},
94+
options: {
95+
sre: {speech: argv.speech},
96+
renderActions: action.speechAction
97+
}
98+
};
99+
100+
//
101+
// Load the MathJax startup module
102+
//
103+
require('mathjax-full/' + (argv.dist ? 'es5' : 'components/src/startup') + '/startup.js');
104+
105+
//
106+
// Filling the sre options from command line
107+
//
108+
action.sreconfig(argv.sre);
109+
110+
//
111+
// Wait for MathJax to start up, and then typeset the math
112+
//
113+
MathJax.startup.promise.then(() => {
114+
MathJax.mathml2mmlPromise(argv._[0] || '', {
115+
display: !argv.inline,
116+
em: argv.em,
117+
ex: argv.ex,
118+
containerWidth: argv.width
119+
}).then(mml => console.log(mml));
120+
}).catch(err => console.log(err));

speech/mml2svg

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ var argv = require('yargs')
7171
default: 'shallow',
7272
describe: 'level of speech: deep, shallow, none'
7373
},
74+
sre: {
75+
array: true,
76+
nargs: 2,
77+
describe: 'SRE flags as key value pairs, e.g., "--sre locale de --sre domain clearspeak" generates speech in German with clearspeak rules'
78+
},
7479
styles: {
7580
boolean: true,
7681
default: true,
@@ -92,12 +97,23 @@ var argv = require('yargs')
9297
})
9398
.argv;
9499

100+
const action = require('./action.js');
101+
95102
//
96103
// Create DOM adaptor and register it for HTML documents
97104
//
98105
const adaptor = liteAdaptor();
99106
EnrichHandler(RegisterHTMLHandler(adaptor), new MathML());
100107

108+
//
109+
// Get feature vector for SRE setup. If necessary, compute the path to the
110+
// locale JSON files explicitly.
111+
//
112+
const feature = action.dataPairs(argv.sre);
113+
feature.speech = argv.speech;
114+
feature.json = feature.json ? feature.json :
115+
require.resolve('mathjax-full/es5/sre/mathmaps/base.json').replace(/\/base\.json$/, '');
116+
101117
//
102118
// Create input and output jax and a document using them on the content from the HTML file
103119
//
@@ -106,27 +122,29 @@ const svg = new SVG({fontCache: (argv.fontCache ? 'local' : 'none')});
106122
const html = mathjax.document('', {
107123
InputJax: mml,
108124
OutputJax: svg,
109-
sre: {speech: argv.speech},
110-
renderActions: require('./action.js').speechAction
125+
sre: feature,
126+
renderActions: action.speechAction
111127
});
112128

113129
//
114130
// Typeset the math from the command line
115131
//
116-
const node = html.convert(argv._[0] || '', {
132+
mathjax.handleRetriesFor(() => html.convert(argv._[0] || '', {
117133
display: !argv.inline,
118134
em: argv.em,
119135
ex: argv.ex,
120136
containerWidth: argv.width
121-
});
137+
})).then((node) => {
122138

123-
//
124-
// If the --css option was specified, output the CSS,
125-
// Otherwise, typeset the math and output the HTML
126-
//
127-
if (argv.css) {
128-
console.log(adaptor.textContent(svg.styleSheet(html)));
129-
} else {
130-
let html = (argv.container ? adaptor.outerHTML(node) : adaptor.innerHTML(node));
131-
console.log(argv.styles ? html.replace(/<defs>/, `<defs><style>${CSS}</style>`) : html);
132-
}
139+
//
140+
// If the --css option was specified, output the CSS,
141+
// Otherwise, typeset the math and output the HTML
142+
//
143+
if (argv.css) {
144+
console.log(adaptor.textContent(svg.styleSheet(html)));
145+
} else {
146+
console.log(argv.container);
147+
let html = (argv.container ? adaptor.outerHTML(node) : adaptor.innerHTML(node));
148+
console.log(argv.styles ? html.replace(/<defs>/, `<defs><style>${CSS}</style>`) : html);
149+
}
150+
});

0 commit comments

Comments
 (0)