aboutsummaryrefslogtreecommitdiff
path: root/js/mathjax/extensions/a11y/explorer.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/mathjax/extensions/a11y/explorer.js')
-rw-r--r--js/mathjax/extensions/a11y/explorer.js827
1 files changed, 827 insertions, 0 deletions
diff --git a/js/mathjax/extensions/a11y/explorer.js b/js/mathjax/extensions/a11y/explorer.js
new file mode 100644
index 0000000..bba3edd
--- /dev/null
+++ b/js/mathjax/extensions/a11y/explorer.js
@@ -0,0 +1,827 @@
+// @license magnet:?xt=urn:btih:8e4f440f4c65981c5bf93c76d35135ba5064d8b7dn=apache-2.0.txt Apache-2.0
+/*************************************************************
+ *
+ * [Contrib]/a11y/explorer.js
+ *
+ * Implements expression exploration via the SRE explorer.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (c) 2016-2017 The MathJax Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+MathJax.Hub.Register.StartupHook('Sre Ready', function() {
+ var FALSE, KEY;
+ var SETTINGS = MathJax.Hub.config.menuSettings;
+ var COOKIE = {}; // replaced when menu is available
+
+ MathJax.Hub.Register.StartupHook('MathEvents Ready', function() {
+ FALSE = MathJax.Extension.MathEvents.Event.False;
+ KEY = MathJax.Extension.MathEvents.Event.KEY;
+ });
+
+ var Assistive = MathJax.Extension.explorer = {
+ version: '1.6.0',
+ dependents: [], // the extensions that depend on this one
+ //
+ // Default configurations.
+ //
+ defaults: {
+ walker: 'table',
+ highlight: 'none',
+ background: 'blue',
+ foreground: 'black',
+ speech: true,
+ generation: 'lazy',
+ subtitle: false,
+ ruleset: 'mathspeak-default'
+ },
+ eagerComplexity: 80,
+ prefix: 'Assistive-',
+ hook: null,
+ locHook: null,
+ oldrules: null,
+ addMenuOption: function(key, value) {
+ SETTINGS[Assistive.prefix + key] = value;
+ },
+
+ addDefaults: function() {
+ var defaults = MathJax.Hub.CombineConfig('explorer', Assistive.defaults);
+ var keys = Object.keys(defaults);
+ for (var i = 0, key; key = keys[i]; i++) {
+ if (typeof(SETTINGS[Assistive.prefix + key]) === 'undefined') {
+ Assistive.addMenuOption(key, defaults[key]);
+ }
+ }
+ Assistive.setSpeechOption();
+ Explorer.Reset();
+ },
+
+ setOption: function(key, value) {
+ if (SETTINGS[Assistive.prefix + key] === value) return;
+ Assistive.addMenuOption(key, value);
+ Explorer.Reset();
+ },
+
+ getOption: function(key) {
+ return SETTINGS[Assistive.prefix + key];
+ },
+
+ speechOption: function(msg) {
+ if (Assistive.oldrules === msg.value) return;
+ Assistive.setSpeechOption();
+ Explorer.Regenerate();
+ },
+
+ setSpeechOption: function() {
+ var ruleset = SETTINGS[Assistive.prefix + 'ruleset'];
+ var cstr = ruleset.split('-');
+ sre.System.getInstance().setupEngine({
+ locale: MathJax.Localization.locale,
+ domain: Assistive.Domain(cstr[0]),
+ style: cstr[1]
+ });
+ Assistive.oldrules = ruleset;
+ },
+
+ Domain: function(domain) {
+ switch (domain) {
+ case 'chromevox':
+ return 'default';
+ case 'clearspeak':
+ return 'clearspeak';
+ case 'mathspeak':
+ default:
+ return 'mathspeak';
+ }
+ },
+
+ hook: null,
+ locHook: null,
+ Enable: function(update, menu) {
+ SETTINGS.explorer = true;
+ if (menu) COOKIE.explorer = true;
+ MathJax.Extension.collapsible.Enable(false, menu);
+ if (MathJax.Extension.AssistiveMML) {
+ MathJax.Extension.AssistiveMML.config.disabled = true;
+ SETTINGS.assistiveMML = false;
+ if (menu) COOKIE.assistiveMML = false;
+ }
+ this.DisableMenus(false);
+ if (!this.hook) {
+ this.hook = MathJax.Hub.Register.MessageHook(
+ 'New Math', ['Register', this.Explorer]);
+ }
+ if (!this.locHook) {
+ this.locHook = MathJax.Hub.Register.MessageHook(
+ 'Locale Reset', ['RemoveSpeech', this.Explorer]);
+ }
+ if (update) MathJax.Hub.Queue(['Reprocess', MathJax.Hub]);
+ },
+ Disable: function(update, menu) {
+ SETTINGS.explorer = false;
+ if (menu) COOKIE.explorer = false;
+ this.DisableMenus(true);
+ if (this.hook) {
+ MathJax.Hub.UnRegister.MessageHook(this.hook);
+ this.hook = null;
+ }
+ for (var i = this.dependents.length - 1; i >= 0; i--) {
+ var dependent = this.dependents[i];
+ if (dependent.Disable) dependent.Disable(false, menu);
+ }
+ // Reprocess on update? I don't think it is necessary
+ // (now that we check for being enabled in the event handlers)
+ },
+ DisableMenus: function(state) {
+ if (MathJax.Menu) {
+ var menu = MathJax.Menu.menu.FindId('Accessibility', 'Explorer');
+ if (menu) {
+ menu = menu.submenu;
+ var items = menu.items;
+ for (var i = 2, item; item = items[i]; i++) item.disabled = state;
+ if (!state && menu.FindId('SpeechOutput') && !SETTINGS[Assistive.prefix + 'speech']) {
+ menu.FindId('Subtitles').disabled = true;
+ }
+ }
+ }
+ },
+ //
+ // Register a dependent
+ //
+ Dependent: function(extension) {
+ this.dependents.push(extension);
+ }
+ };
+
+ var LiveRegion = MathJax.Object.Subclass({
+ div: null,
+ inner: null,
+ Init: function() {
+ this.div = LiveRegion.Create('assertive');
+ this.inner = MathJax.HTML.addElement(this.div, 'div');
+ },
+ //
+ // Adds the speech div.
+ //
+ Add: function() {
+ if (LiveRegion.added) return;
+ document.body.appendChild(this.div);
+ LiveRegion.added = true;
+ },
+ //
+ // Shows the live region as a subtitle of a node.
+ //
+ Show: function(node, highlighter) {
+ this.div.classList.add('MJX_LiveRegion_Show');
+ var rect = node.getBoundingClientRect();
+ var bot = rect.bottom + 10 + window.pageYOffset;
+ var left = rect.left + window.pageXOffset;
+ this.div.style.top = bot + 'px';
+ this.div.style.left = left + 'px';
+ var color = highlighter.colorString();
+ this.inner.style.backgroundColor = color.background;
+ this.inner.style.color = color.foreground;
+ },
+ //
+ // Takes the live region out of the page flow.
+ //
+ Hide: function(node) {
+ this.div.classList.remove('MJX_LiveRegion_Show');
+ },
+ //
+ // Clears the speech div.
+ //
+ Clear: function() {
+ this.Update('');
+ this.inner.style.top = '';
+ this.inner.style.backgroundColor = '';
+ },
+ //
+ // Speaks a string by poking it into the speech div.
+ //
+ Update: function(speech) {
+ if (Assistive.getOption('speech')) {
+ LiveRegion.Update(this.inner, speech);
+ }
+ }
+ }, {
+ ANNOUNCE: 'Navigatable Math in page. Explore with enter or shift space and arrow' +
+ ' keys. Expand or collapse elements hitting enter.',
+ announced: false,
+ added: false,
+ styles: {'.MJX_LiveRegion':
+ {
+ position: 'absolute', top: '0', height: '1px', width: '1px',
+ padding: '1px', overflow: 'hidden'
+ },
+ '.MJX_LiveRegion_Show':
+ {
+ top: '0', position: 'absolute', width: 'auto', height: 'auto',
+ padding: '0px 0px', opacity: 1, 'z-index': '202',
+ left: 0, right: 0, 'margin': '0 auto',
+ 'background-color': 'white', 'box-shadow': '0px 10px 20px #888',
+ border: '2px solid #CCCCCC'
+ }
+ },
+ //
+ // Creates a live region with a particular type.
+ //
+ Create: function(type) {
+ var element = MathJax.HTML.Element(
+ 'div', {className: 'MJX_LiveRegion'});
+ element.setAttribute('aria-live', type);
+ return element;
+ },
+ //
+ // Updates a live region's text content.
+ //
+ Update: MathJax.Hub.Browser.isPC ?
+ function(div, speech) {
+ div.textContent = '';
+ setTimeout(function() {div.textContent = speech;}, 100);
+ } : function(div, speech) {
+ div.textContent = '';
+ div.textContent = speech;
+ },
+ //
+ // Speaks the announce string.
+ //
+ Announce: function() {
+ if (!Assistive.getOption('speech')) return;
+ LiveRegion.announced = true;
+ MathJax.Ajax.Styles(LiveRegion.styles);
+ var div = LiveRegion.Create('polite');
+ document.body.appendChild(div);
+ LiveRegion.Update(div, LiveRegion.ANNOUNCE);
+ setTimeout(function() {document.body.removeChild(div);}, 1000);
+ }
+ });
+ MathJax.Extension.explorer.LiveRegion = LiveRegion;
+
+ var A11Y_PATH = MathJax.Ajax.fileURL(MathJax.Ajax.config.path.a11y);
+
+ var Explorer = MathJax.Extension.explorer.Explorer = {
+ liveRegion: LiveRegion(),
+ walker: null,
+ highlighter: null,
+ hoverer: null,
+ flamer: null,
+ speechDiv: null,
+ earconFile: A11Y_PATH + '/invalid_keypress' +
+ (['Firefox', 'Chrome', 'Opera'].indexOf(MathJax.Hub.Browser.name) !== -1 ?
+ '.ogg' : '.mp3'),
+ expanded: false,
+ focusoutEvent: MathJax.Hub.Browser.isFirefox ? 'blur' : 'focusout',
+ focusinEvent: 'focus',
+ ignoreFocusOut: false,
+ jaxCache: {},
+ messageID: null,
+ //
+ // Resets the explorer, rerunning methods not triggered by events.
+ //
+ Reset: function() {
+ Explorer.FlameEnriched();
+ },
+ //
+ // Registers new Maths and adds a key event if it is enriched.
+ //
+ Register: function(msg) {
+ if (!Assistive.hook) return;
+ var script = document.getElementById(msg[1]);
+ if (script && script.id) {
+ var jax = MathJax.Hub.getJaxFor(script.id);
+ if (jax && jax.enriched) {
+ Explorer.StateChange(script.id, jax);
+ Explorer.liveRegion.Add();
+ Explorer.AddEvent(script);
+ }
+ }
+ },
+ StateChange: function(id, jax) {
+ Explorer.GetHighlighter(.2);
+ var oldJax = Explorer.jaxCache[id];
+ if (oldJax && oldJax === jax.root) return;
+ if (oldJax) {
+ sre.Walker.resetState(id + '-Frame');
+ }
+ Explorer.jaxCache[id] = jax.root;
+ },
+ //
+ // Adds Aria attributes.
+ //
+ AddAria: function(math) {
+ math.setAttribute('role', 'application');
+ math.setAttribute('aria-label', 'Math');
+ },
+ //
+ // Add hook to run at End Math to restart walking on an expansion element.
+ //
+ AddHook: function(jax) {
+ Explorer.RemoveHook();
+ Explorer.hook = MathJax.Hub.Register.MessageHook(
+ 'End Math', function(message) {
+ var newid = message[1].id + '-Frame';
+ var math = document.getElementById(newid);
+ if (jax && newid === Explorer.expanded) {
+ Explorer.ActivateWalker(math, jax);
+ math.focus();
+ Explorer.expanded = false;
+ }
+ });
+ },
+ //
+ // Remove and unregister the explorer hook.
+ //
+ RemoveHook: function() {
+ if (Explorer.hook) {
+ MathJax.Hub.UnRegister.MessageHook(Explorer.hook);
+ Explorer.hook = null;
+ }
+ },
+ AddMessage: function() {
+ return MathJax.Message.Set('Generating Speech Output');
+ },
+ RemoveMessage: function(id) {
+ if (id) MathJax.Message.Clear(id);
+ },
+ //
+ // Adds a key event to an enriched jax.
+ //
+ AddEvent: function(script) {
+ var id = script.id + '-Frame';
+ var sibling = script.previousSibling;
+ if (!sibling) return;
+ var math = sibling.id !== id ? sibling.firstElementChild : sibling;
+ Explorer.AddAria(math);
+ Explorer.AddMouseEvents(math);
+ if (math.className === 'MathJax_MathML') {
+ math = math.firstElementChild;
+ }
+ if (!math) return;
+ math.onkeydown = Explorer.Keydown;
+ Explorer.Flame(math);
+ math.addEventListener(
+ Explorer.focusinEvent,
+ function(event) {
+ if (!Assistive.hook) return;
+ if (!LiveRegion.announced) LiveRegion.Announce();
+ });
+ math.addEventListener(
+ Explorer.focusoutEvent,
+ function(event) {
+ if (!Assistive.hook) return;
+ // A fix for Edge.
+ if (Explorer.ignoreFocusOut) {
+ Explorer.ignoreFocusOut = false;
+ if (Explorer.walker.moved === 'enter') {
+ event.target.focus();
+ return;
+ }
+ }
+ if (Explorer.walker) Explorer.DeactivateWalker();
+ });
+ //
+ if (Assistive.getOption('speech')) {
+ Explorer.AddSpeech(math);
+ }
+ //
+ },
+ //
+ // Add speech output.
+ //
+ AddSpeech: function(math) {
+ var id = math.id;
+ var jax = MathJax.Hub.getJaxFor(id);
+ var mathml = jax.root.toMathML();
+ if (!math.getAttribute('haslabel')) {
+ Explorer.AddMathLabel(mathml, id);
+ }
+ if (math.getAttribute('hasspeech')) return;
+ switch (MathJax.Hub.config.explorer.generation) {
+ case 'eager':
+ Explorer.AddSpeechEager(mathml, id);
+ break;
+ case 'mixed':
+ var complexity = math.querySelectorAll('[data-semantic-complexity]');
+ if (complexity.length >= Assistive.eagerComplexity) {
+ Explorer.AddSpeechEager(mathml, id);
+ }
+ break;
+ case 'lazy':
+ default:
+ break;
+ }
+ },
+ AddSpeechLazy: function(math) {
+ var generator = new sre.TreeSpeechGenerator();
+ generator.setRebuilt(Explorer.walker.getRebuilt());
+ generator.getSpeech(Explorer.walker.rootNode, Explorer.walker.getXml());
+ math.setAttribute('hasspeech', 'true');
+ },
+ //
+ //
+ // Adds speech strings to the node using a web worker.
+ //
+ AddSpeechEager: function(mathml, id) {
+ Explorer.MakeSpeechTask(
+ mathml, id, sre.TreeSpeechGenerator,
+ function(math, speech) {math.setAttribute('hasspeech', 'true');}, 5);
+ },
+ //
+ // Attaches the Math expression as an aria label.
+ //
+ AddMathLabel: function(mathml, id) {
+ Explorer.MakeSpeechTask(
+ mathml, id, sre.SummarySpeechGenerator,
+ function(math, speech) {
+ math.setAttribute('haslabel', 'true');
+ math.setAttribute('aria-label', speech);},
+ 5);
+ },
+ //
+ // The actual speech task generator.
+ //
+ MakeSpeechTask: function(mathml, id, constructor, onSpeech, time) {
+ var messageID = Explorer.AddMessage();
+ setTimeout(function() {
+ var speechGenerator = new constructor();
+ var math = document.getElementById(id);
+ var dummy = new sre.DummyWalker(
+ math, speechGenerator, Explorer.highlighter, mathml);
+ var speech = dummy.speech();
+ if (speech) {
+ onSpeech(math, speech);
+ }
+ Explorer.RemoveMessage(messageID);
+ }, time);
+ },
+ //
+ // Event execution on keydown. Subsumes the same method of MathEvents.
+ //
+ Keydown: function(event) {
+ var code = event.keyCode;
+ if (code === KEY.ESCAPE) {
+ if (!Explorer.walker) return;
+ Explorer.RemoveHook();
+ Explorer.DeactivateWalker();
+ FALSE(event);
+ return;
+ }
+ // If walker is active we redirect there.
+ if (Explorer.walker && Explorer.walker.isActive()) {
+ // Maps the return key to dash for SRE v3.
+ code = code === KEY.RETURN ? KEY.DASH : code;
+ if (typeof(Explorer.walker.modifier) !== 'undefined') {
+ Explorer.walker.modifier = event.shiftKey;
+ }
+ var move = Explorer.walker.move(code);
+ if (move === null) return;
+ if (move) {
+ if (Explorer.walker.moved === 'expand') {
+ Explorer.expanded = Explorer.walker.node.id;
+ // This sometimes blurs in Edge and sometimes it does not.
+ if (MathJax.Hub.Browser.isEdge) {
+ Explorer.ignoreFocusOut = true;
+ Explorer.DeactivateWalker();
+ return;
+ }
+ // This does not blur in FF, IE.
+ if (MathJax.Hub.Browser.isFirefox || MathJax.Hub.Browser.isMSIE) {
+ Explorer.DeactivateWalker();
+ return;
+ }
+ }
+ Explorer.liveRegion.Update(Explorer.walker.speech());
+ Explorer.Highlight();
+ } else {
+ Explorer.PlayEarcon();
+ }
+ FALSE(event);
+ return;
+ }
+ var math = event.target;
+ if (code === KEY.SPACE && !event.shiftKey) {
+ MathJax.Extension.MathEvents.Event.ContextMenu(event, math);
+ FALSE(event);
+ return;
+ }
+ if (Assistive.hook && (code === KEY.RETURN ||
+ (code === KEY.SPACE && event.shiftKey))) {
+ var jax = MathJax.Hub.getJaxFor(math);
+ Explorer.ActivateWalker(math, jax);
+ Explorer.AddHook(jax);
+ FALSE(event);
+ return;
+ }
+ },
+ GetHighlighter: function(alpha) {
+ Explorer.highlighter = sre.HighlighterFactory.highlighter(
+ {color: Assistive.getOption('background'), alpha: alpha},
+ {color: Assistive.getOption('foreground'), alpha: 1},
+ {renderer: MathJax.Hub.outputJax['jax/mml'][0].id,
+ browser: MathJax.Hub.Browser.name}
+ );
+ },
+ //
+ // Adds mouse events to maction items in an enriched jax.
+ //
+ AddMouseEvents: function(node) {
+ sre.HighlighterFactory.addEvents(
+ node,
+ {'mouseover': Explorer.MouseOver,
+ 'mouseout': Explorer.MouseOut},
+ {renderer: MathJax.Hub.outputJax['jax/mml'][0].id,
+ browser: MathJax.Hub.Browser.name}
+ );
+ },
+ MouseOver: function(event) {
+ if (Assistive.getOption('highlight') === 'none') return;
+ if (Assistive.getOption('highlight') === 'hover') {
+ var frame = event.currentTarget;
+ Explorer.GetHighlighter(.1);
+ Explorer.highlighter.highlight([frame]);
+ Explorer.hoverer = true;
+ }
+ FALSE(event);
+ },
+ MouseOut: function(event) {
+ if (Explorer.hoverer) {
+ Explorer.highlighter.unhighlight();
+ Explorer.hoverer = false;
+ }
+ return FALSE(event);
+ },
+ //
+ // Activates Flaming
+ //
+ Flame: function(node) {
+ if (Assistive.getOption('highlight') === 'flame') {
+ Explorer.GetHighlighter(.05);
+ Explorer.highlighter.highlightAll(node);
+ Explorer.flamer = true;
+ return;
+ }
+ },
+ UnFlame: function() {
+ if (Explorer.flamer) {
+ Explorer.highlighter.unhighlightAll();
+ Explorer.flamer = null;
+ }
+ },
+ FlameEnriched: function() {
+ Explorer.UnFlame();
+ for (var i = 0, all = MathJax.Hub.getAllJax(), jax; jax = all[i]; i++) {
+ Explorer.Flame(jax.SourceElement().previousSibling);
+ }
+ },
+ //
+ // Activates the walker.
+ //
+ Walkers: {
+ 'syntactic': sre.SyntaxWalker,
+ 'table': sre.TableWalker,
+ 'semantic': sre.SemanticWalker,
+ 'none': sre.DummyWalker
+ },
+ ActivateWalker: function(math, jax) {
+ var speechOn = Assistive.getOption('speech');
+ var constructor = Assistive.getOption('walker') ?
+ Explorer.Walkers[MathJax.Hub.config.explorer.walker] :
+ Explorer.Walkers['none'];
+ var speechGenerator = speechOn ? new sre.DirectSpeechGenerator() :
+ new sre.DummySpeechGenerator();
+ var options = sre.System.getInstance().engineSetup();
+ speechGenerator.setOptions({
+ locale: options.locale, domain: options.domain,
+ style: options.style, modality: 'speech'});
+ Explorer.GetHighlighter(.2);
+ Explorer.walker = new constructor(
+ math, speechGenerator, Explorer.highlighter, jax.root.toMathML());
+ if (speechOn && !math.getAttribute('hasspeech')) {
+ Explorer.AddSpeechLazy(math);
+ }
+ Explorer.walker.activate();
+ if (speechOn) {
+ if (Assistive.getOption('subtitle')) {
+ Explorer.liveRegion.Show(math, Explorer.highlighter);
+ }
+ Explorer.liveRegion.Update(Explorer.walker.speech());
+ }
+ Explorer.Highlight();
+ // A fix for Edge.
+ if (Explorer.ignoreFocusOut) {
+ setTimeout(function() {Explorer.ignoreFocusOut = false;}, 500);
+ }
+ },
+ //
+ // Deactivates the walker.
+ //
+ DeactivateWalker: function() {
+ var setup = sre.System.getInstance().engineSetup();
+ var domain = setup.domain;
+ var style = domain === 'clearspeak' ? 'default' : setup.style;
+ Assistive.setOption('ruleset', setup.domain + '-' + style);
+ Explorer.liveRegion.Clear();
+ Explorer.liveRegion.Hide();
+ Explorer.Unhighlight();
+ Explorer.currentHighlight = null;
+ Explorer.walker.deactivate();
+ Explorer.walker = null;
+ },
+ //
+ // Highlights the focused nodes.
+ //
+ Highlight: function() {
+ Explorer.Unhighlight();
+ Explorer.highlighter.highlight(Explorer.walker.getFocus().getNodes());
+ },
+ //
+ // Unhighlights the old nodes.
+ //
+ Unhighlight: function() {
+ Explorer.highlighter.unhighlight();
+ },
+ //
+ // Plays the earcon.
+ //
+ // Every time we make new Audio element, as some browsers do not allow to
+ // play audio elements more than once (e.g., Safari).
+ //
+ PlayEarcon: function() {
+ var audio = new Audio(Explorer.earconFile);
+ audio.play();
+ },
+ //
+ // Toggle speech output.
+ //
+ SpeechOutput: function() {
+ Explorer.Reset();
+ var speechItems = ['Subtitles'];
+ speechItems.forEach(
+ function(x) {
+ var item = MathJax.Menu.menu.FindId('Accessibility', 'Explorer', x);
+ if (item) {
+ item.disabled = !item.disabled;
+ }});
+ Explorer.Regenerate();
+ },
+ //
+ // Remove speech and resets SRE options.
+ //
+ RemoveSpeech: function() {
+ Assistive.setSpeechOption();
+ for (var i = 0, all = MathJax.Hub.getAllJax(), jax; jax = all[i]; i++) {
+ var math = document.getElementById(jax.inputID + '-Frame');
+ if (math) {
+ math.removeAttribute('hasspeech');
+ math.removeAttribute('haslabel');
+ }
+ }
+ },
+ //
+ // Regenerates speech.
+ //
+ Regenerate: function() {
+ for (var i = 0, all = MathJax.Hub.getAllJax(), jax; jax = all[i]; i++) {
+ var math = document.getElementById(jax.inputID + '-Frame');
+ if (math) {
+ math.removeAttribute('hasspeech');
+ Explorer.AddSpeech(math);
+ }
+ }
+ },
+ Startup: function() {
+ var Collapsible = MathJax.Extension.collapsible;
+ if (Collapsible) Collapsible.Dependent(Assistive);
+ Assistive.addDefaults();
+ }
+ };
+
+ MathJax.Hub.Register.StartupHook('End Extensions', function() {
+ Assistive[SETTINGS.explorer === false ? 'Disable' : 'Enable']();
+ MathJax.Hub.Startup.signal.Post('Explorer Ready');
+ MathJax.Hub.Register.StartupHook('MathMenu Ready', function() {
+ COOKIE = MathJax.Menu.cookie;
+ var Switch = function(menu) {
+ Assistive[SETTINGS.explorer ? 'Enable' : 'Disable'](true, true);
+ MathJax.Menu.saveCookie();
+ };
+ var ITEM = MathJax.Menu.ITEM,
+ MENU = MathJax.Menu.menu;
+ var reset = {action: Explorer.Reset};
+ var speech = {action: Assistive.speechOption};
+ var explorerMenu =
+ ITEM.SUBMENU(['Explorer', 'Explorer'],
+ ITEM.CHECKBOX(['Active', 'Active'], 'explorer', {action: Switch}),
+ ITEM.RULE(),
+ ITEM.CHECKBOX(['Walker', 'Walker'], 'Assistive-walker'),
+ ITEM.SUBMENU(['Highlight', 'Highlight'],
+ ITEM.RADIO(['none', 'None'], 'Assistive-highlight', reset),
+ ITEM.RADIO(['hover', 'Hover'], 'Assistive-highlight', reset),
+ ITEM.RADIO(['flame', 'Flame'], 'Assistive-highlight', reset)
+ ),
+ ITEM.SUBMENU(['Background', 'Background'],
+ ITEM.RADIO(['blue', 'Blue'], 'Assistive-background', reset),
+ ITEM.RADIO(['red', 'Red'], 'Assistive-background', reset),
+ ITEM.RADIO(['green', 'Green'], 'Assistive-background', reset),
+ ITEM.RADIO(['yellow', 'Yellow'], 'Assistive-background', reset),
+ ITEM.RADIO(['cyan', 'Cyan'], 'Assistive-background', reset),
+ ITEM.RADIO(['magenta', 'Magenta'], 'Assistive-background', reset),
+ ITEM.RADIO(['white', 'White'], 'Assistive-background', reset),
+ ITEM.RADIO(['black', 'Black'], 'Assistive-background', reset)
+ ),
+ ITEM.SUBMENU(['Foreground', 'Foreground'],
+ ITEM.RADIO(['black', 'Black'], 'Assistive-foreground', reset),
+ ITEM.RADIO(['white', 'White'], 'Assistive-foreground', reset),
+ ITEM.RADIO(['magenta', 'Magenta'], 'Assistive-foreground', reset),
+ ITEM.RADIO(['cyan', 'Cyan'], 'Assistive-foreground', reset),
+ ITEM.RADIO(['yellow', 'Yellow'], 'Assistive-foreground', reset),
+ ITEM.RADIO(['green', 'Green'], 'Assistive-foreground', reset),
+ ITEM.RADIO(['red', 'Red'], 'Assistive-foreground', reset),
+ ITEM.RADIO(['blue', 'Blue'], 'Assistive-foreground', reset)
+ ),
+ ITEM.RULE(),
+ ITEM.CHECKBOX(['SpeechOutput', 'Speech Output'],
+ 'Assistive-speech', {action: Explorer.SpeechOutput}),
+ ITEM.CHECKBOX(['Subtitles', 'Subtitles'], 'Assistive-subtitle',
+ {disabled: !SETTINGS['Assistive-speech']}),
+ ITEM.RULE(),
+ ITEM.SUBMENU(['Mathspeak', 'Mathspeak Rules'],
+ ITEM.RADIO(['mathspeak-default', 'Verbose'],
+ 'Assistive-ruleset', speech),
+ ITEM.RADIO(['mathspeak-brief', 'Brief'], 'Assistive-ruleset', speech),
+ ITEM.RADIO(['mathspeak-sbrief', 'Superbrief'],
+ 'Assistive-ruleset', speech)
+ ),
+ ITEM.RADIO(['clearspeak-default', 'Clearspeak Rules'],
+ 'Assistive-ruleset', speech),
+ ITEM.SUBMENU(['Chromevox', 'ChromeVox Rules'],
+ ITEM.RADIO(['chromevox-default', 'Verbose'],
+ 'Assistive-ruleset', speech),
+ ITEM.RADIO(['chromevox-alternative', 'Alternative'],
+ 'Assistive-ruleset', speech)
+ )
+ );
+ var submenu = (MENU.FindId('Accessibility') || {}).submenu, index;
+ if (submenu) {
+ index = submenu.IndexOfId('Explorer');
+ if (index !== null) {
+ submenu.items[index] = explorerMenu;
+ } else {
+ index = submenu.IndexOfId('CollapsibleMath');
+ submenu.items.splice(index + 1, 0, explorerMenu);
+ }
+ } else {
+ index = MENU.IndexOfId('CollapsibleMath');
+ MENU.items.splice(index + 1, 0, explorerMenu);
+ }
+ if (!SETTINGS.explorer) Assistive.DisableMenus(true);
+ },20); // Between collapsible and auto-collapse extensions
+ },20);
+
+});
+
+//
+// Patch problem with SVG getJaxForMath when used from explorer
+// (can be removed after the next release of MathJax).
+//
+MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
+ MathJax.Hub.Config({SVG: {addMMLclasses: true}});
+ var SVG = MathJax.OutputJax.SVG;
+ if (parseFloat(SVG.version) < 2.7) {
+ var JAXFROMMATH = SVG.getJaxFromMath;
+ SVG.Augment({
+ getJaxFromMath: function (math) {
+ if (math.parentNode.className.match(/MathJax_SVG_Display/)) math = math.parentNode;
+ return JAXFROMMATH.call(this,math);
+ }
+ });
+ }
+});
+
+//
+// Set up the a11y path,if it isn't already in place
+//
+if (!MathJax.Ajax.config.path.a11y) {
+ MathJax.Ajax.config.path.a11y = MathJax.Hub.config.root + "/extensions/a11y";
+}
+
+MathJax.Ajax.Require('[a11y]/collapsible.js');
+MathJax.Hub.Register.StartupHook('Collapsible Ready', function() {
+ MathJax.Extension.explorer.Explorer.Startup();
+ MathJax.Ajax.loadComplete('[a11y]/explorer.js');
+});
+// @license-end