diff --git a/unpacked/MathJax.js b/unpacked/MathJax.js index a8b6dbeb3..ccee686ca 100644 --- a/unpacked/MathJax.js +++ b/unpacked/MathJax.js @@ -2569,6 +2569,8 @@ MathJax.Hub.Startup = { } if (SETTINGS.FastPreview && !MathJax.Extension["fast-preview"]) MathJax.Hub.config.extensions.push("fast-preview.js"); + if (config.menuSettings.assistiveMML && !MathJax.Extension.AssistiveMML) + MathJax.Hub.config.extensions.push("AssistiveMML.js"); },MathJax.Hub.config], ["Post",this.signal,"End Cookie"] ); @@ -3090,12 +3092,13 @@ MathJax.Hub.Startup = { isMac: (navigator.platform.substr(0,3) === "Mac"), isPC: (navigator.platform.substr(0,3) === "Win"), isMSIE: ("ActiveXObject" in window && "clipboardData" in window), - isMsEdge: (!!AGENT.match(/Edge\//)), + isEdge: ("MSGestureEvent" in window && "chrome" in window && + window.chrome.loadTimes == null), isFirefox: (!!AGENT.match(/Gecko\//) && !AGENT.match(/like Gecko/)), isSafari: (!!AGENT.match(/ (Apple)?WebKit\//) && !AGENT.match(/ like iPhone /) && - (!window.chrome || window.chrome.loadTimes == null)), - isChrome: (window.chrome != null && window.chrome.loadTimes != null), - isOpera: (window.opera != null && window.opera.version != null), + (!window.chrome || window.chrome.app == null)), + isChrome: ("chrome" in window && window.chrome.loadTimes != null), + isOpera: ("opera" in window && window.opera.version != null), isKonqueror: ("konqueror" in window && navigator.vendor == "KDE"), versionAtLeast: function (v) { var bv = (this.version).split('.'); v = (new String(v)).split('.'); @@ -3123,7 +3126,7 @@ MathJax.Hub.Startup = { HUB.Browser = HUB.Insert(new String(browser),BROWSERS); var VERSION = new RegExp( ".*(Version/| Trident/.*; rv:)((?:\\d+\\.)+\\d+)|" + // for Safari, Opera10, and IE11+ - ".*("+browser+")"+(browser == "MSIE" ? " " : "/")+"((?:\\d+\\.)*\\d+)|"+ // for one of the main browser + ".*("+browser+")"+(browser == "MSIE" ? " " : "/")+"((?:\\d+\\.)*\\d+)|"+ // for one of the main browsers "(?:^|\\(| )([a-z][-a-z0-9._: ]+|(?:Apple)?WebKit)/((?:\\d+\\.)+\\d+)"); // for unrecognized browser var MATCH = VERSION.exec(xAGENT) || ["","","","unknown","0.0"]; HUB.Browser.name = (MATCH[1] != "" ? browser : (MATCH[3] || MATCH[5])); @@ -3177,8 +3180,15 @@ MathJax.Hub.Startup = { AGENT.match(/ Fennec\//) != null || AGENT.match(/Mobile/) != null); }, + Chrome: function (browser) { + browser.noContextMenu = browser.isMobile = !!navigator.userAgent.match(/ Mobile[ \/]/); + }, Opera: function (browser) {browser.version = opera.version()}, + Edge: function (browser) { + browser.isMobile = !!navigator.userAgent.match(/ Phone/); + }, MSIE: function (browser) { + browser.isMobile = !!navigator.userAgent.match(/ Phone/); browser.isIE9 = !!(document.documentMode && (window.performance || window.msPerformance)); MathJax.HTML.setScriptBug = !browser.isIE9 || document.documentMode < 9; MathJax.Hub.msieHTMLCollectionBug = (document.documentMode < 9); diff --git a/unpacked/extensions/AssistiveMML.js b/unpacked/extensions/AssistiveMML.js new file mode 100644 index 000000000..59b1c795f --- /dev/null +++ b/unpacked/extensions/AssistiveMML.js @@ -0,0 +1,127 @@ +/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ + +/************************************************************* + * + * MathJax/extensions/AssistiveMML.js + * + * Implements an extension that inserts hidden MathML into the + * page for screen readers or other asistive technology. + * + * --------------------------------------------------------------------- + * + * Copyright (c) 2015 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. + */ + +(function (AJAX,CALLBACK,HUB,HTML) { + var SETTINGS = HUB.config.menuSettings; + + var AssistiveMML = MathJax.Extension["AssistiveMML"] = { + version: "2.6", + + config: { + disabled: false, + styles: { + ".MJX_Assistive_MathML": { + position:"absolute!important", + clip: (HUB.Browser.isMSIE && (document.documentMode||0) < 8 ? + "rect(1px 1px 1px 1px)" : "rect(1px, 1px, 1px, 1px)"), + padding: "1px 0 0 0!important", + border: "0!important", + height: "1px!important", + width: "1px!important", + overflow: "hidden!important", + display:"block!important" + } + } + }, + + Config: function () { + if (!this.config.disabled && SETTINGS.assistiveMML == null) + HUB.Config({menuSettings:{assistiveMML:true}}); + AJAX.Styles(this.config.styles); + HUB.Register.MessageHook("End Math",function (msg) {AssistiveMML.EndMathHook(msg[1])}); + }, + + // + // The hook for the End Math signal. + // This sets up a state object that lists the jax and index into the jax, + // and a dummy callback that is used to synchronizing with MathJax. + // It will be called when the jax are all processed, and that will + // let the MathJax queue continue (it will block until then). + // + EndMathHook: function (node) { + if (!SETTINGS.assistiveMML) return; + var state = { + jax: HUB.getAllJax(node), i: 0, + callback: MathJax.Callback({}) + }; + this.HandleMML(state); + return state.callback; + }, + + // + // For each jax in the state, look up the frame. + // If the jax doesn't use NativeMML and hasn't already been handled: + // Get the MathML for the jax, taking resets into account. + // Add a data-mathml attribute to the frame, and + // Create a span that is not visible on screen and put the MathML in it, + // and add it to the frame. + // When all the jax are processed, call the callback. + // + HandleMML: function (state) { + var m = state.jax.length, jax, mml, frame, span; + while (state.i < m) { + jax = state.jax[state.i]; + frame = document.getElementById(jax.inputID+"-Frame"); + if (jax.outputJax !== "NativeMML" && frame && !frame.getAttribute("data-mathml")) { + try { + mml = jax.root.toMathML("").replace(/\n */g,"").replace(/<!--.*?-->/g,""); + } catch (err) { + if (!err.restart) throw err; // an actual error + return MathJax.Callback.After(["HandleMML",this,state],err.restart); + } + frame.setAttribute("data-mathml",mml); + span = HTML.addElement(frame,"span",{ + isMathJax: true, className: "MJX_Assistive_MathML" + }); + span.innerHTML = mml; + frame.firstChild.setAttribute("aria-hidden","true"); + span.setAttribute("aria-readonly","true"); + } + state.i++; + } + state.callback(); + } + + }; + + HUB.Startup.signal.Post("AssistiveMML Ready"); + +})(MathJax.Ajax,MathJax.Callback,MathJax.Hub,MathJax.HTML); + +// +// Make sure the toMathML extension is loaded before we signal +// the load complete for this extension. Then wait for the end +// of the user configuration before configuring this extension. +// +MathJax.Callback.Queue( + ["Require",MathJax.Ajax,"[MathJax]/extensions/toMathML.js"], + ["loadComplete",MathJax.Ajax,"[MathJax]/extensions/AssistiveMML.js"], + function () { + MathJax.Hub.Register.StartupHook("End Config",["Config",MathJax.Extension.AssistiveMML]); + } +); + diff --git a/unpacked/extensions/HelpDialog.js b/unpacked/extensions/HelpDialog.js index 09365fd26..2c16eb1ca 100644 --- a/unpacked/extensions/HelpDialog.js +++ b/unpacked/extensions/HelpDialog.js @@ -29,7 +29,13 @@ var STIXURL = "http://www.stixfonts.org/"; var MENU = MathJax.Menu; + var FALSE, KEY; + HUB.Register.StartupHook("MathEvents Ready",function () { + FALSE = MathJax.Extension.MathEvents.Event.False; + KEY = MathJax.Extension.MathEvents.Event.KEY; + }); + var CONFIG = HUB.CombineConfig("HelpDialog",{ styles: { @@ -101,10 +107,10 @@ HELP.Post = function () { this.div = MENU.Background(this); var help = HTML.addElement(this.div,"div",{ - id: "MathJax_Help" + id: "MathJax_Help", tabIndex: 0, onkeydown: HELP.Keydown },LOCALE._("HelpDialog",[ ["b",{style:{fontSize:"120%"}},[["Help","MathJax Help"]]], - ["div",{id: "MathJax_HelpContent"},[ + ["div",{id: "MathJax_HelpContent", tabIndex: 0},[ ["p",{},[["MathJax", "*MathJax* is a JavaScript library that allows page authors to include " + "mathematics within their web pages. As a reader, you don't need to do " + @@ -148,10 +154,13 @@ ] ]], ["a",{href:"http://www.mathjax.org/"},["www.mathjax.org"]], - ["span",{id: "MathJax_HelpClose", onclick: HELP.Remove}, + ["span",{id: "MathJax_HelpClose", onclick: HELP.Remove, + onkeydown: HELP.Keydown, tabIndex: 0, + "aria-label": "Close", "aria-describedby": "Close window"}, [["span",{},["\u00D7"]]] ] ])); + help.focus(); LOCALE.setCSS(help); var doc = (document.documentElement||{}); var H = window.innerHeight || doc.clientHeight || doc.scrollHeight || 0; @@ -167,6 +176,15 @@ HELP.Remove = function (event) { if (HELP.div) {document.body.removeChild(HELP.div); delete HELP.div} }; + HELP.Keydown = function(event) { + if (event.keyCode === KEY.ESCAPE || + (this.id === "MathJax_HelpClose" && + (event.keyCode === KEY.SPACE || event.keyCode === KEY.RETURN))) { + HELP.Remove(event); + MENU.CurrentNode().focus(); + FALSE(event); + } + }, MathJax.Callback.Queue( HUB.Register.StartupHook("End Config",{}), // wait until config is complete diff --git a/unpacked/extensions/MathEvents.js b/unpacked/extensions/MathEvents.js index 3bcf07b40..f9ccbefa8 100644 --- a/unpacked/extensions/MathEvents.js +++ b/unpacked/extensions/MathEvents.js @@ -4,20 +4,20 @@ /************************************************************* * * MathJax/extensions/MathEvents.js - * + * * Implements the event handlers needed by the output jax to perform * menu, hover, and other events. * * --------------------------------------------------------------------- - * + * * Copyright (c) 2011-2015 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. @@ -27,12 +27,12 @@ (function (HUB,HTML,AJAX,CALLBACK,LOCALE,OUTPUT,INPUT) { var VERSION = "2.5.0"; - + var EXTENSION = MathJax.Extension; var ME = EXTENSION.MathEvents = {version: VERSION}; - + var SETTINGS = HUB.config.menuSettings; - + var CONFIG = { hover: 500, // time required to be considered a hover frame: { @@ -41,7 +41,7 @@ bcolor: "#A6D", // frame border color hwidth: "15px", // haze width hcolor: "#83A" // haze color - }, + }, button: { x: -6, y: -3, // menu button offsets wx: -2 // button offset for full-width equations @@ -99,16 +99,30 @@ } }; - + // // Common event-handling code // var EVENT = ME.Event = { - + LEFTBUTTON: 0, // the event.button value for left button RIGHTBUTTON: 2, // the event.button value for right button MENUKEY: "altKey", // the event value for alternate context menu + /*************************************************************/ + /* + * Enum element for key codes. + */ + KEY: { + RETURN: 13, + ESCAPE: 27, + SPACE: 32, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40 + }, + Mousedown: function (event) {return EVENT.Handler(event,"Mousedown",this)}, Mouseup: function (event) {return EVENT.Handler(event,"Mouseup",this)}, Mousemove: function (event) {return EVENT.Handler(event,"Mousemove",this)}, @@ -117,7 +131,7 @@ Click: function (event) {return EVENT.Handler(event,"Click",this)}, DblClick: function (event) {return EVENT.Handler(event,"DblClick",this)}, Menu: function (event) {return EVENT.Handler(event,"ContextMenu",this)}, - + // // Call the output jax's event handler or the zoom handler // @@ -129,7 +143,7 @@ if (jax[type]) {return jax[type](event,math)} if (EXTENSION.MathZoom) {return EXTENSION.MathZoom.HandleEvent(event,type,math)} }, - + // // Try to cancel the event in every way we can // @@ -143,6 +157,15 @@ return false; }, + // + // Keydown event handler. Should only fire on Space key. + // + Keydown: function (event, math) { + if (event.keyCode === EVENT.KEY.SPACE) { + EVENT.ContextMenu(event, this); + }; + }, + // // Load the contextual menu code, if needed, and post the menu // @@ -165,7 +188,7 @@ } // - // If the menu code is loaded, + // If the menu code is loaded, // Check if localization needs loading; // If not, post the menu, and return. // Otherwise wait for the localization to load @@ -178,18 +201,18 @@ load = LOCALE.loadDomain("MathMenu"); if (!load) { MENU.jax = jax; - var source = MENU.menu.Find("Show Math As").menu; - source.items[0].name = jax.sourceMenuTitle; - source.items[0].format = (jax.sourceMenuFormat||"MathML"); + var source = MENU.menu.Find("Show Math As").submenu; + source.items[0].name = jax.sourceMenuTitle; + source.items[0].format = (jax.sourceMenuFormat||"MathML"); source.items[1].name = INPUT[jax.inputJax].sourceMenuTitle; source.items[5].disabled = !INPUT[jax.inputJax].annotationEncoding; - // + // // Try and find each known annotation format and enable the menu // items accordingly. // var annotations = source.items[2]; annotations.disabled = true; - var annotationItems = annotations.menu.items; + var annotationItems = annotations.submenu.items; annotationList = MathJax.Hub.Config.semanticsAnnotations; for (var i = 0, m = annotationItems.length; i < m; i++) { var name = annotationItems[i].name[1] @@ -226,7 +249,7 @@ ); return EVENT.False(event); }, - + // // Mousedown handler for alternate means of accessing menu // @@ -243,26 +266,26 @@ return JAX.ContextMenu(event,math,true); } }, - + ClearSelection: function () { if (ME.safariContextMenuBug) {setTimeout("window.getSelection().empty()",0)} if (document.selection) {setTimeout("document.selection.empty()",0)} }, - + getBBox: function (span) { span.appendChild(ME.topImg); var h = ME.topImg.offsetTop, d = span.offsetHeight-h, w = span.offsetWidth; span.removeChild(ME.topImg); return {w:w, h:h, d:d}; } - + }; - + // // Handle hover "discoverability" // var HOVER = ME.Hover = { - + // // Check if we are moving from a non-MathJax element to a MathJax one // and either start fading in again (if it is fading out) or start the @@ -309,7 +332,7 @@ return EVENT.False(event); } }, - + // // Clear the old timer and start a new one // @@ -320,7 +343,7 @@ ClearHoverTimer: function () { if (this.hoverTimer) {clearTimeout(this.hoverTimer); delete this.hoverTimer} }, - + // // Handle putting up the hover frame // @@ -431,7 +454,7 @@ jax.hover.timer = setTimeout(CALLBACK(["HoverFade",this,jax]),(delay||CONFIG.fadeDelay)); } }, - + // // Handle a click on the menu button // @@ -439,7 +462,7 @@ if (!event) {event = window.event} return OUTPUT[this.jax].ContextMenu(event,this.math,true); }, - + // // Clear all hover timers // @@ -449,7 +472,7 @@ HOVER.ClearHoverTimer(); delete jax.hover; }, - + // // Make a measurement in pixels // @@ -469,9 +492,9 @@ } }; - + // - // Handle touch events. + // Handle touch events. // // Use double-tap-and-hold as a replacement for context menu event. // Use double-tap as a replacement for double click. @@ -480,7 +503,7 @@ last: 0, // time of last tap event delay: 500, // delay time for double-click - + // // Check if this is a double-tap, and if so, start the timer // for the double-tap and hold (to trigger the contextual menu) @@ -494,9 +517,9 @@ event.preventDefault(); } }, - + // - // Check if there is a timeout pending, i.e., we have a + // Check if there is a timeout pending, i.e., we have a // double-tap and were waiting to see if it is held long // enough for the menu. Since we got the end before the // timeout, it is a double-click, not a double-tap-and-hold. @@ -512,7 +535,7 @@ return EVENT.Handler((event.touches[0]||event.touch),"DblClick",this); } }, - + // // If the timeout passes without an end event, we issue // the contextual menu event. @@ -521,10 +544,10 @@ delete TOUCH.timeout; TOUCH.last = 0; TOUCH.up = false; return EVENT.Handler((event.touches[0]||event.touch),"ContextMenu",math); } - + }; - - /* + + /* * // * // Mobile screens are small, so use larger version of arrow * // @@ -534,7 +557,7 @@ * CONFIG.button.x = -6; * } */ - + // // Set up browser-specific values // @@ -557,7 +580,7 @@ ME.noContextMenuBug = true; // doesn't produce contextmenu event } }); - + // // Used in measuring zoom and hover positions // @@ -578,7 +601,7 @@ haze["-moz-box-shadow"] = haze["-khtml-box-shadow"] = "0px 0px "+CONFIG.frame.hwidth+" "+CONFIG.frame.hcolor; }; - + // // Queue the events needed for startup // @@ -590,6 +613,6 @@ ["Post",HUB.Startup.signal,"MathEvents Ready"], ["loadComplete",AJAX,"[MathJax]/extensions/MathEvents.js"] ); - + })(MathJax.Hub,MathJax.HTML,MathJax.Ajax,MathJax.Callback, MathJax.Localization,MathJax.OutputJax,MathJax.InputJax); diff --git a/unpacked/extensions/MathML/mml3.js b/unpacked/extensions/MathML/mml3.js index f14e3bff9..90a98f59b 100644 --- a/unpacked/extensions/MathML/mml3.js +++ b/unpacked/extensions/MathML/mml3.js @@ -80,7 +80,7 @@ MathJax.Hub.Register.StartupHook("MathML Jax Ready",function () { */ var BROWSER = MathJax.Hub.Browser; var exslt = ''; - if (BROWSER.isMsEdge || BROWSER.isMSIE) { + if (BROWSER.isEdge || BROWSER.isMSIE) { exslt = 'urn:schemas-microsoft-com:xslt' } else { exslt = 'http://exslt.org/common'; @@ -767,7 +767,16 @@ MathJax.Hub.Register.StartupHook("MathML Jax Ready",function () { } // Tweak CSS to avoid some browsers rearranging HTML output - MathJax.Ajax.Styles(".MathJax span { direction: ltr !important; display: inline-block !important;}"); + MathJax.Ajax.Styles({ + ".MathJax .mi, .MathJax .mo, .MathJax .mn, .MathJax .mtext": { + direction: "ltr", + display: "inline-block" + }, + ".MathJax .ms, .MathJax .mspace, .MathJax .mglyph": { + direction: "ltr", + display: "inline-block" + } + }); MathJax.Hub.Startup.signal.Post("MathML mml3.js Ready"); }); diff --git a/unpacked/extensions/MathMenu.js b/unpacked/extensions/MathMenu.js index 8a3419308..c1f7009d1 100644 --- a/unpacked/extensions/MathMenu.js +++ b/unpacked/extensions/MathMenu.js @@ -4,21 +4,21 @@ /************************************************************* * * MathJax/extensions/MathMenu.js - * + * * Implements a right-mouse (or CTRL-click) menu over mathematics * elements that gives the user the ability to copy the source, * change the math size, and zoom settings. * * --------------------------------------------------------------------- - * + * * Copyright (c) 2010-2015 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. @@ -29,8 +29,8 @@ (function (HUB,HTML,AJAX,CALLBACK,OUTPUT) { var VERSION = "2.5.0"; - var SIGNAL = MathJax.Callback.Signal("menu") // signal for menu events - + var SIGNAL = MathJax.Callback.Signal("menu"); // signal for menu events + MathJax.Extension.MathMenu = { version: VERSION, signal: SIGNAL @@ -42,10 +42,10 @@ [["MathMenu",id]].concat([].slice.call(arguments,1)) ); }; - + var isPC = HUB.Browser.isPC, isMSIE = HUB.Browser.isMSIE, isIE9 = ((document.documentMode||0) > 8); var ROUND = (isPC ? null : "5px"); - + var CONFIG = HUB.CombineConfig("MathMenu",{ delay: 150, // the delay for submenus @@ -72,7 +72,7 @@ left: Math.round((screen.width - 400)/2), top: Math.round((screen.height - 300)/3) }, - + styles: { "#MathJax_About": { position:"fixed", left:"50%", width:"auto", "text-align":"center", @@ -143,28 +143,28 @@ padding: (isPC ? "2px 2em 4px 1.33em" : "1px 2em 3px 1.33em"), "font-style":"italic" }, - + ".MathJax_MenuRule": { "border-top": (isPC ? "1px solid #CCCCCC" : "1px solid #DDDDDD"), margin: (isPC ? "4px 1px 0px" : "4px 3px") }, - + ".MathJax_MenuDisabled": { color:"GrayText" }, - + ".MathJax_MenuActive": { "background-color": (isPC ? "Highlight" : "#606872"), color: (isPC ? "HighlightText" : "white") }, - + "#MathJax_AboutClose": { top:".2em", right:".2em" }, ".MathJax_Menu .MathJax_MenuClose": { top:"-10px", left:"-10px" }, - + ".MathJax_MenuClose": { position:"absolute", cursor:"pointer", @@ -184,7 +184,7 @@ "-webkit-border-radius": "18px", // Safari and Chrome "-moz-border-radius": "18px", // Firefox "-khtml-border-radius": "18px", // Konqueror - "line-height":0, + "line-height":0, padding:"8px 0 6px" // may need to be browser-specific }, ".MathJax_MenuClose:hover": { @@ -194,27 +194,72 @@ ".MathJax_MenuClose:hover span": { "background-color":"#CCC!important" } - } }); - - var FALSE, HOVER; + + var FALSE, HOVER, KEY; HUB.Register.StartupHook("MathEvents Ready",function () { FALSE = MathJax.Extension.MathEvents.Event.False; HOVER = MathJax.Extension.MathEvents.Hover; + KEY = MathJax.Extension.MathEvents.Event.KEY; }); - + + + /*************************************************************/ + /* + * Abstract class of all keyboard navigatable objects. + */ + var NAV = MathJax.Object.Subclass({ + /* + * Moving in the list of items. + */ + Keydown: function(event, menu) { + switch (event.keyCode) { + case KEY.ESCAPE: + this.Remove(event, menu); + break; + case KEY.RIGHT: + this.Right(event, menu); + break; + case KEY.LEFT: + this.Left(event, menu); + break; + case KEY.UP: + this.Up(event, menu); + break; + case KEY.DOWN: + this.Down(event, menu); + break; + case KEY.RETURN: + case KEY.SPACE: + this.Space(event, menu); + break; + default: + return; + break; + } + return FALSE(event); + }, + Escape: function(event, menu) { }, + Right: function(event, menu) { }, + Left: function(event, menu) { }, + Up: function(event, menu) { }, + Down: function(event, menu) { }, + Space: function(event, menu) { } + }, {}); + + /*************************************************************/ /* * The main menu class */ - var MENU = MathJax.Menu = MathJax.Object.Subclass({ + var MENU = MathJax.Menu = NAV.Subclass({ version: VERSION, items: [], posted: false, title: null, margin: 5, - + Init: function (def) {this.items = [].slice.call(arguments,0)}, With: function (def) {if (def) {HUB.Insert(this,def)}; return this}, @@ -234,7 +279,8 @@ var menu = HTML.Element("div",{ onmouseup: MENU.Mouseup, ondblclick: FALSE, ondragstart: FALSE, onselectstart: FALSE, oncontextmenu: FALSE, - menuItem: this, className: "MathJax_Menu" + menuItem: this, className: "MathJax_Menu", onkeydown: MENU.Keydown, + role: "navigation" }); if (!forceLTR) {MathJax.Localization.setCSS(menu)} @@ -245,21 +291,30 @@ ontouchstart: MENU.Close, ontouchend: FALSE, onmousedown: MENU.Close, onmouseup: FALSE },[["span",{},"\u00D7"]]); } - + div.appendChild(menu); this.posted = true; - menu.style.width = (menu.offsetWidth+2) + "px"; - var x = event.pageX, y = event.pageY; - if (!x && !y) { + if (event) { + var x = event.pageX, y = event.pageY; + } + if (!x && !y && event && event.clientX && event.clientY) { x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop; } if (!parent) { + var node = MENU.CurrentNode() || event.target; + if (!x && !y && node) { + var offsetX = window.pageXOffset || document.documentElement.scrollLeft; + var offsetY = window.pageYOffset || document.documentElement.scrollTop; + var rect = node.getBoundingClientRect(); + x = (rect.right + rect.left) / 2 + offsetX; + y = (rect.bottom + rect.top) / 2 + offsetY; + } if (x + menu.offsetWidth > document.body.offsetWidth - this.margin) {x = document.body.offsetWidth - menu.offsetWidth - this.margin} if (MENU.isMobile) {x = Math.max(5,x-Math.floor(menu.offsetWidth/2)); y -= 20} - MENU.skipUp = event.isContextMenu; + if (event) {MENU.skipUp = event.isContextMenu;} } else { var side = "left", mw = parent.offsetWidth; x = (MENU.isMobile ? 30 : mw - 2); y = 0; @@ -280,10 +335,19 @@ menu.style["KhtmlBorderRadiusTop"+side] = 0; // Konqueror } } - + menu.style.left = x+"px"; menu.style.top = y+"px"; - if (document.selection && document.selection.empty) {document.selection.empty()} + + // Focusing while keeping the scroll position. + var oldX = window.pageXOffset || document.documentElement.scrollLeft; + var oldY = window.pageYOffset || document.documentElement.scrollTop; + MENU.Focus(menu); + if (event.type === "keydown") { + MENU.skipMouseoverFromKey = true; + setTimeout(function() {delete MENU.skipMouseoverFromKey;}, CONFIG.delay); + } + window.scrollTo(oldX, oldY); return FALSE(event); }, @@ -301,6 +365,7 @@ delete MENU.jax.hover.nofade; HOVER.UnHover(MENU.jax); } + MENU.Unfocus(menu); return FALSE(event); }, @@ -314,15 +379,15 @@ for (var i = 0, m = this.items.length; i < m; i++) { if (this.items[i].name[n] === name) { if (names.length) { - if (!this.items[i].menu) {return null} - return this.items[i].menu.FindN(n,names[0],names.slice(1)); + if (!this.items[i].submenu) {return null} + return this.items[i].submenu.FindN(n,names[0],names.slice(1)); } return this.items[i]; } } return null; }, - + /* * Find the index of a menu item (so we can insert before or after it) */ @@ -332,25 +397,49 @@ for (var i = 0, m = this.items.length; i < m; i++) {if (this.items[i].name[n] === name) {return i}} return null; + }, + + Right: function(event, menu) { + MENU.Right(event, menu); + }, + Left: function(event, menu) { + MENU.Left(event, menu); + }, + Up: function(event, menu) { + var node = menu.lastChild; + node.menuItem.Activate(event, node); + }, + Down: function(event, menu) { + var node = menu.firstChild; + node.menuItem.Activate(event, node); + }, + Space: function(event, menu) { + this.Remove(event, menu); } - },{ - + config: CONFIG, - div: null, // the DOM elements for the menu and submenus - - Close: function (event) - {return MENU.Event(event,this.menu||this.parentNode,(this.menu?"Touchend":"Remove"))}, Remove: function (event) {return MENU.Event(event,this,"Remove")}, Mouseover: function (event) {return MENU.Event(event,this,"Mouseover")}, Mouseout: function (event) {return MENU.Event(event,this,"Mouseout")}, Mousedown: function (event) {return MENU.Event(event,this,"Mousedown")}, Mouseup: function (event) {return MENU.Event(event,this,"Mouseup")}, + Keydown: function (event) {return MENU.Event(event,this,"Keydown")}, + /* + * Events for mobile devices. + */ Touchstart: function (event) {return MENU.Event(event,this,"Touchstart")}, Touchend: function (event) {return MENU.Event(event,this,"Touchend")}, + Close: function (event) { + return MENU.Event(event,this.menu||this.parentNode,(this.menu?"Touchend":"Remove")); + }, Event: function (event,menu,type,force) { if (MENU.skipMouseover && type === "Mouseover" && !force) {return FALSE(event)} + if (MENU.skipMouseoverFromKey && type === "Mouseover") { + delete MENU.skipMouseoverFromKey; + return FALSE(event); + } if (MENU.skipUp) { if (type.match(/Mouseup|Touchend/)) {delete MENU.skipUp; return FALSE(event)} if (type === "Touchstart" || @@ -361,7 +450,6 @@ if (item && item[type]) {return item[type](event,menu)} return null; }, - /* * Style for the background DIV */ @@ -371,7 +459,8 @@ }, Background: function (menu) { - var div = HTML.addElement(document.body,"div",{style:this.BGSTYLE, id:"MathJax_MenuFrame"}, + var div = HTML.addElement(document.body,"div", + {style:this.BGSTYLE, id:"MathJax_MenuFrame"}, [["div",{style: this.BGSTYLE, menuItem: menu, onmousedown: this.Remove}]]); var bg = div.firstChild; if (MENU.msieBackgroundBug) { @@ -400,32 +489,109 @@ bg.style.height = document.body.scrollHeight + "px"; } }, + + /*************************************************************/ + /* + * Keyboard navigation of menu. + */ + posted: false, // Is a menu open? + active: null, // The focused in HTML node in the menu. + + GetNode: function(jax) { + var node = document.getElementById(jax.inputID + "-Frame"); + return node.isMathJax ? node : node.firstChild; + }, + CurrentNode: function() { + return MENU.GetNode(MENU.jax); + }, + AllNodes: function() { + var jaxs = MathJax.Hub.getAllJax(); + var nodes = []; + for (var i = 0, jax; jax = jaxs[i]; i++) { + nodes.push(MENU.GetNode(jax)); + } + return nodes; + }, + ActiveNode: function() { + return MENU.active; + }, + FocusNode: function(node) { + MENU.active = node; + node.focus(); + }, + // + // Focus is a global affair, since we only ever want a single focused item. + // + Focus: function(menu) { + !MENU.posted ? MENU.Activate(menu) : MENU.ActiveNode().tabIndex = -1; + menu.tabIndex = 0; + MENU.FocusNode(menu); + }, + Activate: function(event, menu) { + var jaxs = MENU.AllNodes(); + for (var j = 0, jax; jax = jaxs[j]; j++) { + jax.tabIndex = -1; + } + MENU.posted = true; + }, + Unfocus: function() { + MENU.ActiveNode().tabIndex = -1; + var jaxs = MENU.AllNodes(); + for (var j = 0, jax; jax = jaxs[j]; j++) { + jax.tabIndex = 0; + } + MENU.FocusNode(MENU.CurrentNode()); + MENU.posted = false; + }, + MoveHorizontal: function(event, menu, move) { + if (!event.shiftKey) return; + var jaxs = MENU.AllNodes(); + var len = jaxs.length; + if (len === 0) return; + var next = jaxs[MENU.Mod(move(MENU.IndexOf(jaxs, MENU.CurrentNode())), len)]; + if (next === MENU.CurrentNode()) return; + MENU.menu.Remove(event, menu); + MENU.jax = MathJax.Hub.getJaxFor(next); + MENU.FocusNode(next); + MENU.menu.Post(null); + }, + Right: function(event, menu) { + MENU.MoveHorizontal(event, menu, function(x) {return x + 1;}); + }, + Left: function(event, menu) { + MENU.MoveHorizontal(event, menu, function(x) {return x - 1;}); + }, + + //TODO: Move to utility class. + // Computes a mod n. + Mod: function(a, n) { + return ((a % n) + n) % n; + }, + IndexOf: (Array.prototype.indexOf ? + function (A, item, start) {return A.indexOf(item, start);} : + function (A, item, start) { + for (var i = (start || 0), j = A.length; i < j; i++) { + if (item === A[i]) return i; + } + return -1; + }), saveCookie: function () {HTML.Cookie.Set("menu",this.cookie)}, getCookie: function () {this.cookie = HTML.Cookie.Get("menu")} }); + MathJax.Menu.NAV = NAV; + /*************************************************************/ /* * Abstract class of menu items. */ - var ITEM = MENU.ITEM = MathJax.Object.Subclass({ - name: "", // the menu item's label as [id,label] pair - node: null, + var ITEM = MENU.ITEM = NAV.Subclass({ - /* - * Accessor method for node. - */ - GetNode: function() { - return this.node; - }, - /* - * Registers the HTML node of the menu item. - */ - SetNode: function(node) { - this.node = node; - }, + name: "", // The menu item's label as [id,label] pair. + node: null, // The HTML node of the item. + menu: null, // The parent menu containing that item. HTML node. Attributes: function(def) { return HUB.Insert( @@ -435,40 +601,51 @@ className: "MathJax_MenuItem", menuItem: this}, def); }, + Create: function (menu) { if (!this.hidden) { var def = this.Attributes(); var label = this.Label(def,menu); var node = HTML.addElement(menu, "div", def, label); - this.SetNode(node); } }, Name: function () {return _(this.name[0],this.name[1])}, Mouseover: function (event,menu) { - if (!this.disabled) {this.Activate(menu)} - if (!this.menu || !this.menu.posted) { - var menus = document.getElementById("MathJax_MenuFrame").childNodes, - items = menu.parentNode.childNodes; - for (var i = 0, m = items.length; i < m; i++) { - var item = items[i].menuItem; - if (item && item.menu && item.menu.posted) {item.Deactivate(items[i])} - } - m = menus.length-1; - while (m >= 0 && menu.parentNode.menuItem !== menus[m].menuItem) { - menus[m].menuItem.posted = false; - menus[m].parentNode.removeChild(menus[m]); - m--; - } - if (this.Timer && !MENU.isMobile) {this.Timer(event,menu)} + if (menu.parentNode === MENU.ActiveNode().parentNode) { + this.Deactivate(MENU.ActiveNode()); } + this.Activate(event, menu); }, Mouseout: function (event,menu) { - if (!this.menu || !this.menu.posted) {this.Deactivate(menu)} - if (this.timer) {clearTimeout(this.timer); delete this.timer} + this.Deactivate(menu); }, Mouseup: function (event,menu) {return this.Remove(event,menu)}, - + + + DeactivateSubmenus: function(menu) { + var menus = document.getElementById("MathJax_MenuFrame").childNodes, + items = ITEM.GetMenuNode(menu).childNodes; + for (var i = 0, m = items.length; i < m; i++) { + var item = items[i].menuItem; + // Deactivates submenu items. + if (item && item.submenu && item.submenu.posted && + item !== menu.menuItem) { + item.Deactivate(items[i]); + } + } + this.RemoveSubmenus(menu, menus); + }, + RemoveSubmenus: function(menu, menus) { + menus = menus || document.getElementById("MathJax_MenuFrame").childNodes; + var m = menus.length-1; + while (m >= 0 && ITEM.GetMenuNode(menu).menuItem !== menus[m].menuItem) { + menus[m].menuItem.posted = false; + menus[m].parentNode.removeChild(menus[m]); + m--; + } + }, + Touchstart: function (event,menu) {return this.TouchEvent(event,menu,"Mousedown")}, Touchend: function (event,menu) {return this.TouchEvent(event,menu,"Mouseup")}, TouchEvent: function (event,menu,type) { @@ -481,19 +658,20 @@ MENU.Event(event,menu,type); return false; }, - + Remove: function (event,menu) { menu = menu.parentNode.menuItem; return menu.Remove(event,menu); }, - Activate: function (menu) {this.Deactivate(menu); menu.className += " MathJax_MenuActive"}, - Deactivate: function (menu) {menu.className = menu.className.replace(/ MathJax_MenuActive/,"")}, - With: function (def) {if (def) {HUB.Insert(this,def)}; return this}, - + isRTL: function () {return MENU.isRTL}, rtlClass: function () {return (this.isRTL() ? " RTL" : "")} + }, { + GetMenuNode: function(item) { + return item.parentNode; + } }); /*************************************************************/ @@ -503,20 +681,90 @@ MENU.ENTRY = MENU.ITEM.Subclass({ role: "menuitem", // Aria role. - - Attributes: function() { - var def = this.SUPER(arguments).Attributes.call( - this, + + Attributes: function(def) { + def = HUB.Insert( {onmouseover: MENU.Mouseover, onmouseout: MENU.Mouseout, onmousedown: MENU.Mousedown, role: this.role, - 'aria-disabled': !!this.disabled}); + onkeydown: MENU.Keydown, + "aria-disabled": !!this.disabled}, + def); + def = this.SUPER(arguments).Attributes.call(this, def); if (this.disabled) { def.className += " MathJax_MenuDisabled"; } return def; + }, + MoveVertical: function(event, item, move) { + var menuNode = ITEM.GetMenuNode(item); + var items = []; + for (var i = 0, allItems = menuNode.menuItem.items, it; + it = allItems[i]; i++) { + if (!it.hidden) { + items.push(it); + } + } + var index = MENU.IndexOf(items, this); + if (index === -1) return; + var len = items.length; + var children = menuNode.childNodes; + do { + index = MENU.Mod(move(index), len); + } while (items[index].hidden || !children[index].role); + this.Deactivate(item); + items[index].Activate(event, children[index]); + }, + Up: function(event, item) { + this.MoveVertical(event, item, function(x) { return x - 1; }); + }, + Down: function(event, item) { + this.MoveVertical(event, item, function(x) { return x + 1; }); + }, + Right: function(event, item) { + this.MoveHorizontal(event, item, MENU.Right, !this.isRTL()); + }, + Left: function(event, item) { + this.MoveHorizontal(event, item, MENU.Left, this.isRTL()); + }, + MoveHorizontal: function(event, item, move, rtl) { + var menuNode = ITEM.GetMenuNode(item); + if (menuNode.menuItem === MENU.menu && event.shiftKey) { + move(event, item); + } + if (rtl) return; + if (menuNode.menuItem !== MENU.menu) { + this.Deactivate(item); + } + var parentNodes = menuNode.previousSibling.childNodes; + var length = parentNodes.length; + while (length--) { + var parent = parentNodes[length]; + if (parent.menuItem.submenu && + parent.menuItem.submenu === menuNode.menuItem) { + MENU.Focus(parent); + break; + } + } + this.RemoveSubmenus(item); + }, + Space: function (event, menu) { + this.Mouseup(event, menu); + }, + + Activate: function (event, menu) { + this.Deactivate(menu); + if (!this.disabled) { + menu.className += " MathJax_MenuActive"; + } + this.DeactivateSubmenus(menu); + MENU.Focus(menu); + }, + Deactivate: function (menu) { + menu.className = menu.className.replace(/ MathJax_MenuActive/,""); } + }); - + /*************************************************************/ /* * A menu item that performs a command when selected @@ -529,7 +777,7 @@ this.name = name; this.action = action; this.With(def); }, - + Label: function (def,menu) {return [this.Name()]}, Mouseup: function (event,menu) { if (!this.disabled) { @@ -546,7 +794,7 @@ * A menu item that posts a submenu */ MENU.ITEM.SUBMENU = MENU.ENTRY.Subclass({ - menu: null, // the submenu + submenu: null, // the submenu marker: "\u25BA", // the submenu arrow markerRTL: "\u25C4", // the submenu arrow for RTL @@ -554,43 +802,83 @@ if (!(name instanceof Array)) {name = [name,name]} // make [id,label] pair this.name = name; var i = 1; if (!(def instanceof MENU.ITEM)) {this.With(def), i++} - this.menu = MENU.apply(MENU,[].slice.call(arguments,i)); + this.submenu = MENU.apply(MENU,[].slice.call(arguments,i)); }, Label: function (def,menu) { - this.menu.posted = false; + this.submenu.posted = false; return [this.Name()+" ",["span",{ className:"MathJax_MenuArrow" + this.rtlClass() },[this.isRTL() ? this.markerRTL : this.marker]]]; }, Timer: function (event,menu) { - if (this.timer) {clearTimeout(this.timer)} - event = {clientX: event.clientX, clientY: event.clientY}; // MSIE can't pass the event below + this.ClearTimer(); + event = {type: event.type, + clientX: event.clientX, clientY: event.clientY}; // MSIE can't pass the event below this.timer = setTimeout(CALLBACK(["Mouseup",this,event,menu]),CONFIG.delay); }, + ClearTimer: function() { + if (this.timer) { + clearTimeout(this.timer); + } + }, Touchend: function (event,menu) { - var forceout = this.menu.posted; + var forceout = this.submenu.posted; var result = this.SUPER(arguments).Touchend.apply(this,arguments); if (forceout) {this.Deactivate(menu); delete ITEM.lastItem; delete ITEM.lastMenu} return result; }, + Mouseout: function(event, menu) { + if (!this.submenu.posted) { + this.Deactivate(menu); + } + this.ClearTimer(); + }, + Mouseover: function(event, menu) { + this.Activate(event, menu); + }, Mouseup: function (event,menu) { if (!this.disabled) { - if (!this.menu.posted) { - if (this.timer) {clearTimeout(this.timer); delete this.timer} - this.menu.Post(event,menu,this.ltr); + if (!this.submenu.posted) { + this.ClearTimer(); + this.submenu.Post(event, menu, this.ltr); + MENU.Focus(menu); } else { - var menus = document.getElementById("MathJax_MenuFrame").childNodes, - m = menus.length-1; - while (m >= 0) { - var child = menus[m]; - child.menuItem.posted = false; - child.parentNode.removeChild(child); - if (child.menuItem === this.menu) {break}; - m--; - } + this.DeactivateSubmenus(menu); } } return FALSE(event); + }, + Activate: function (event, menu) { + if (!this.disabled) { + this.Deactivate(menu); + menu.className += " MathJax_MenuActive"; + } + if (!this.submenu.posted) { + this.DeactivateSubmenus(menu); + if (!MENU.isMobile) { + this.Timer(event,menu); + } + } + MENU.Focus(menu); + }, + MoveVertical: function(event, item, move) { + this.ClearTimer(); + this.SUPER(arguments).MoveVertical.apply(this, arguments); + }, + MoveHorizontal: function(event, menu, move, rtl) { + if (!rtl) { + this.SUPER(arguments).MoveHorizontal.apply(this, arguments); + return; + } + if (this.disabled) return; + if (!this.submenu.posted) { + this.Activate(event, menu); + return; + } + var submenuNodes = ITEM.GetMenuNode(menu).nextSibling.childNodes; + if (submenuNodes.length > 0) { + this.submenu.items[0].Activate(event, submenuNodes[0]); + } } }); @@ -602,7 +890,7 @@ variable: null, // the variable name marker: (isPC ? "\u25CF" : "\u2713"), // the checkmark role: "menuitemradio", - + Init: function (name,variable,def) { if (!(name instanceof Array)) {name = [name,name]} // make [id,label] pair this.name = name; this.variable = variable; this.With(def); @@ -621,7 +909,7 @@ if (item && item.variable === this.variable) {child[i].firstChild.style.display = "none"} } - menu.firstChild.display = ""; + menu.firstChild.display = ""; CONFIG.settings[this.variable] = this.value; MENU.cookie[this.variable] = CONFIG.settings[this.variable]; MENU.saveCookie(); SIGNAL.Post(["radio button",this]); @@ -667,7 +955,9 @@ /* * A menu item that is a label */ - MENU.ITEM.LABEL = MENU.ITEM.Subclass({ + MENU.ITEM.LABEL = MENU.ENTRY.Subclass({ + role: "menuitem", // Aria role. + Init: function (name,def) { if (!(name instanceof Array)) {name = [name,name]} // make [id,label] pair this.name = name; this.With(def); @@ -675,7 +965,12 @@ Label: function (def,menu) { def.className += " MathJax_MenuLabel"; return [this.Name()]; - } + }, + Activate: function(event, menu) { + this.Deactivate(menu); + MENU.Focus(menu); + }, + Mouseup: function (event,menu) { } }); /*************************************************************/ @@ -688,7 +983,7 @@ return null; } }); - + /*************************************************************/ /*************************************************************/ @@ -697,13 +992,8 @@ */ MENU.About = function () { var HTMLCSS = OUTPUT["HTML-CSS"] || {}; - var font = - (HTMLCSS.imgFonts ? "image" : - (HTMLCSS.fontInUse ? - (HTMLCSS.webFonts ? "web" : "local")+" "+HTMLCSS.fontInUse : - (OUTPUT.SVG ? "web SVG" : "generic")) ) + " fonts"; - var format = (!HTMLCSS.webFonts || HTMLCSS.imgFonts ? null : - HTMLCSS.allowWebFonts.replace(/otf/,"woff or otf") + " fonts"); + var font = MENU.About.GetFont(); + var format = MENU.About.GetFormat(); var jax = ["MathJax.js v"+MathJax.fileversion,["br"]]; jax.push(["div",{style:{"border-top":"groove 2px",margin:".25em 0"}}]); MENU.About.GetJax(jax,MathJax.InputJax,["InputJax","%1 Input Jax v%2"]); @@ -712,24 +1002,28 @@ jax.push(["div",{style:{"border-top":"groove 2px",margin:".25em 0"}}]); MENU.About.GetJax(jax,MathJax.Extension,["Extension","%1 Extension v%2"],true); jax.push(["div",{style:{"border-top":"groove 2px",margin:".25em 0"}}],["center",{},[ - HUB.Browser + " v"+HUB.Browser.version + (format ? + HUB.Browser + " v"+HUB.Browser.version + (format ? " \u2014 " + _(format.replace(/ /g,""),format) : "") ]]); MENU.About.div = MENU.Background(MENU.About); var about = HTML.addElement(MENU.About.div,"div",{ - id: "MathJax_About" + id: "MathJax_About", tabIndex: 0, onkeydown: MENU.About.Keydown },[ ["b",{style:{fontSize:"120%"}},["MathJax"]]," v"+MathJax.version,["br"], _(font.replace(/ /g,""),"using "+font),["br"],["br"], ["span",{style:{ display:"inline-block", "text-align":"left", "font-size":"80%", - "max-height":"20em", overflow:"auto", + "max-height":"20em", overflow:"auto", "background-color":"#E4E4E4", padding:".4em .6em", border:"1px inset" - }},jax],["br"],["br"], + }, tabIndex: 0},jax],["br"],["br"], ["a",{href:"http://www.mathjax.org/"},["www.mathjax.org"]], - ["span",{className:"MathJax_MenuClose",id:"MathJax_AboutClose",onclick:MENU.About.Remove}, + ["span",{className:"MathJax_MenuClose",id:"MathJax_AboutClose", + onclick:MENU.About.Remove, + onkeydown: MENU.About.Keydown, tabIndex: 0, + "aria-label": "Close", "aria-describedby": "Close window"}, [["span",{},"\u00D7"]]] ]); + about.focus(); MathJax.Localization.setCSS(about); var doc = (document.documentElement||{}); var H = window.innerHeight || doc.clientHeight || doc.scrollHeight || 0; @@ -745,6 +1039,15 @@ MENU.About.Remove = function (event) { if (MENU.About.div) {document.body.removeChild(MENU.About.div); delete MENU.About.div} }; + MENU.About.Keydown = function(event) { + if (event.keyCode === KEY.ESCAPE || + (this.id === "MathJax_AboutClose" && + (event.keyCode === KEY.SPACE || event.keyCode === KEY.RETURN))) { + MENU.About.Remove(event); + MENU.CurrentNode().focus(); + FALSE(event); + } + }, MENU.About.GetJax = function (jax,JAX,type,noTypeCheck) { var info = []; for (var id in JAX) {if (JAX.hasOwnProperty(id) && JAX[id]) { @@ -755,8 +1058,22 @@ for (var i = 0, m = info.length; i < m; i++) {jax.push(info[i],["br"])} return jax; }; + MENU.About.GetFont = function () { + var jax = MathJax.Hub.outputJax["jax/mml"][0] || {}; + var font = { + SVG: "web SVG", + CommonHTML: "web TeX", + "HTML-CSS": (jax.imgFonts ? "image" : (jax.webFonts ? "web" : "local")+" "+jax.fontInUse) + }[jax.id] || "generic"; + return font + " fonts"; + }; + MENU.About.GetFormat = function () { + var jax = MathJax.Hub.outputJax["jax/mml"][0] || {}; + if (jax.id !== "HTML-CSS"|| !jax.webFonts || jax.imgFonts) return; + return jax.allowWebFonts.replace(/otf/,"woff or otf") + " fonts"; + }; + - /* * Handle the MathJax HELP menu */ @@ -764,7 +1081,7 @@ AJAX.Require("[MathJax]/extensions/HelpDialog.js", function () {MathJax.Extension.Help.Dialog()}); }; - + /* * Handle showing of element's source */ @@ -835,7 +1152,7 @@ var table = w.document.body.firstChild; setTimeout(function () { var H = (w.outerHeight-w.innerHeight)||30, W = (w.outerWidth-w.innerWidth)||30, x, y; - W = Math.max(100,Math.min(Math.floor(.5*screen.width),table.offsetWidth+W+25)); + W = Math.max(140,Math.min(Math.floor(.5*screen.width),table.offsetWidth+W+25)); H = Math.max(40,Math.min(Math.floor(.5*screen.height),table.offsetHeight+H+25)); if (MENU.prototype.msieHeightBug) {H += 35}; // for title bar in XP w.resizeTo(W,H); @@ -848,7 +1165,7 @@ },50); } }; - + /* * Handle rescaling all the math */ @@ -872,14 +1189,14 @@ "The scale should be a percentage (e.g., 120%%)"))} } }; - + /* * Handle loading the zoom code */ MENU.Zoom = function () { if (!MathJax.Extension.MathZoom) {AJAX.Require("[MathJax]/extensions/MathZoom.js")} }; - + /* * Handle changing the renderer */ @@ -893,9 +1210,10 @@ switch (CONFIG.settings.renderer) { case "NativeMML": if (!CONFIG.settings.warnedMML) { - if (BROWSER.isChrome && BROWSER.version.substr(0,3) !== "24.") {message = MESSAGE.MML.WebKit} + if (BROWSER.isChrome && BROWSER.version.substr(0,3) !== "24.") {message = MESSAGE.MML.WebKit} else if (BROWSER.isSafari && !BROWSER.versionAtLeast("5.0")) {message = MESSAGE.MML.WebKit} else if (BROWSER.isMSIE) {if (!BROWSER.hasMathPlayer) {message = MESSAGE.MML.MSIE}} + else if (BROWSER.isEdge) {message = MESSAGE.MML.WebKit} else {message = MESSAGE.MML[BROWSER]} warned = "warnedMML"; } @@ -905,7 +1223,7 @@ if (!CONFIG.settings.warnedSVG) { if (BROWSER.isMSIE && !isIE9) {message = MESSAGE.SVG.MSIE} } - break; + break; } if (message) { message = _(message[0],message[1]); @@ -938,7 +1256,7 @@ MSIE: ["MSIENativeMMLWarning", "Internet Explorer requires the MathPlayer plugin " + "in order to process MathML output."], - + Opera: ["OperaNativeMMLWarning", "Opera's support for MathML is limited, so switching to " + "MathML output may cause some expressions to render poorly."], @@ -951,7 +1269,7 @@ "Your browser's native MathML does not implement all the features " + "used by MathJax, so some expressions may not render properly."] }, - + SVG: { MSIE: ["MSIESVGWarning", "SVG is not implemented in Internet Explorer prior to " + @@ -960,7 +1278,7 @@ "not display properly."] } }; - + /* * Handle setting the HTMLCSS fonts */ @@ -968,7 +1286,7 @@ var HTMLCSS = OUTPUT["HTML-CSS"]; if (!HTMLCSS) return; document.location.reload(); }; - + /* * Handle selection of locale and rerender the page */ @@ -990,7 +1308,7 @@ }); } }; - + /* * Handle setting MathPlayer events */ @@ -1023,7 +1341,7 @@ "The MathJax contextual menu will be disabled, but you can " + "Alt-Click on an expression to obtain the MathJax menu instead."] }; - + /*************************************************************/ /*************************************************************/ @@ -1059,7 +1377,7 @@ // MENU.CreateLocaleMenu = function () { if (!MENU.menu) return; - var menu = MENU.menu.Find("Language").menu, items = menu.items; + var menu = MENU.menu.Find("Language").submenu, items = menu.items; // // Get the names of the languages and sort them // @@ -1085,7 +1403,7 @@ // MENU.CreateAnnotationMenu = function () { if (!MENU.menu) return; - var menu = MENU.menu.Find("Show Math As","Annotation").menu; + var menu = MENU.menu.Find("Show Math As","Annotation").submenu; var annotations = CONFIG.semanticsAnnotations; for (var a in annotations) { if (annotations.hasOwnProperty(a)) { @@ -1154,7 +1472,8 @@ ITEM.RADIO("MathML", "renderer", {action: MENU.Renderer, value:"NativeMML"}), ITEM.RADIO("SVG", "renderer", {action: MENU.Renderer}), ITEM.RULE(), - ITEM.CHECKBOX("Fast Preview", "FastPreview") + ITEM.CHECKBOX("Fast Preview", "FastPreview"), + ITEM.CHECKBOX("Assistive MathML", "assistiveMML", {hidden:!CONFIG.showAssistiveMML}) ), ITEM.SUBMENU("MathPlayer", {hidden:!HUB.Browser.isMSIE || !CONFIG.showMathPlayer, disabled:!HUB.Browser.hasMathPlayer}, @@ -1201,11 +1520,11 @@ if (MENU.isMobile) { (function () { var settings = CONFIG.settings; - var trigger = MENU.menu.Find("Math Settings","Zoom Trigger").menu; + var trigger = MENU.menu.Find("Math Settings","Zoom Trigger").submenu; trigger.items[0].disabled = trigger.items[1].disabled = true; if (settings.zoom === "Hover" || settings.zoom == "Click") {settings.zoom = "None"} trigger.items = trigger.items.slice(0,4); - + if (navigator.appVersion.match(/[ (]Android[) ]/)) { MENU.ITEM.SUBMENU.Augment({marker: "\u00BB"}); } @@ -1215,7 +1534,7 @@ MENU.CreateLocaleMenu(); MENU.CreateAnnotationMenu(); }); - + MENU.showRenderer = function (show) { MENU.cookie.showRenderer = CONFIG.showRenderer = show; MENU.saveCookie(); MENU.menu.Find("Math Settings","Math Renderer").hidden = !show; @@ -1241,12 +1560,16 @@ MENU.cookie.showLocale = CONFIG.showLocale = show; MENU.saveCookie(); MENU.menu.Find("Language").hidden = !show; }; + MENU.showAssistiveMML = function (show) { + MENU.cookie.showAssistiveMML = CONFIG.showAssistiveMML = show; MENU.saveCookie(); + MENU.menu.Find("Math Settings","Math Renderer","Assistive MathML").hidden = !show; + }; MathJax.Hub.Register.StartupHook("HTML-CSS Jax Ready",function () { if (!MathJax.OutputJax["HTML-CSS"].config.imageFont) {MENU.menu.Find("Math Settings","Font Preference","TeX (image)").disabled = true} }); - + /*************************************************************/ CALLBACK.Queue( diff --git a/unpacked/extensions/TeX/AMSmath.js b/unpacked/extensions/TeX/AMSmath.js index 545135362..c6d974b21 100644 --- a/unpacked/extensions/TeX/AMSmath.js +++ b/unpacked/extensions/TeX/AMSmath.js @@ -134,14 +134,14 @@ MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () { aligned: ['AlignedAMSArray',null,null,null,'rlrlrlrlrlrl',COLS([0,2,0,2,0,2,0,2,0,2,0]),".5em",'D'], gathered: ['AlignedAMSArray',null,null,null,'c',null,".5em",'D'], - subarray: ['Array',null,null,null,null,COLS([0,0,0,0]),"0.1em",'S',1], + subarray: ['Array',null,null,null,null,COLS([0]),"0.1em",'S',1], smallmatrix: ['Array',null,null,null,'c',COLS([1/3]),".2em",'S',1], 'equation': ['EquationBegin','Equation',true], 'equation*': ['EquationBegin','EquationStar',false], - eqnarray: ['AMSarray',null,true,true, 'rcl',MML.LENGTH.THICKMATHSPACE,".5em"], - 'eqnarray*': ['AMSarray',null,false,true,'rcl',MML.LENGTH.THICKMATHSPACE,".5em"] + eqnarray: ['AMSarray',null,true,true, 'rcl',COLS([0]),".5em"], + 'eqnarray*': ['AMSarray',null,false,true,'rcl',COLS([0]),".5em"] }, delimiter: { diff --git a/unpacked/extensions/mml2jax.js b/unpacked/extensions/mml2jax.js index a348bc55e..8edb8d5c1 100644 --- a/unpacked/extensions/mml2jax.js +++ b/unpacked/extensions/mml2jax.js @@ -99,8 +99,8 @@ MathJax.Extension.mml2jax = { } for (var i = 0, m = math.length; i < m; i++) { var parent = math[i].parentNode; - if (parent && parent.className !== preview && !math[i].prefix === !namespace) - {array.push(math[i])} + if (parent && parent.className !== preview && + !parent.isMathJax && !math[i].prefix === !namespace) array.push(math[i]); } }, diff --git a/unpacked/jax/output/CommonHTML/autoload/mmultiscripts.js b/unpacked/jax/output/CommonHTML/autoload/mmultiscripts.js index e412a1c91..1a1914e17 100644 --- a/unpacked/jax/output/CommonHTML/autoload/mmultiscripts.js +++ b/unpacked/jax/output/CommonHTML/autoload/mmultiscripts.js @@ -45,7 +45,7 @@ MathJax.Hub.Register.StartupHook("CommonHTML Jax Ready",function () { // var base, bbox; if (stretch) { - base = node.getElementsByTagName("mjx-base")[0]; + base = CHTML.getNode(node,"mjx-base"); } else { this.CHTMLaddChild(node,0,{type:"mjx-base", noBBox:true, forceChild:true}); base = node.firstChild; @@ -137,10 +137,10 @@ MathJax.Hub.Register.StartupHook("CommonHTML Jax Ready",function () { // CHTMLgetScripts: function (BOX,BBOX,stretch,node) { if (stretch) { - BOX.sub = node.getElementsByTagName("mjx-sub")[0]; - BOX.sup = node.getElementsByTagName("mjx-sup")[0]; - BOX.presub = node.getElementsByTagName("mjx-presub")[0]; - BOX.presup = node.getElementsByTagName("mjx-presup")[0]; + BOX.sub = CHTML.getNode(node,"mjx-sub"); + BOX.sup = CHTML.getNode(node,"mjx-sup"); + BOX.presub = CHTML.getNode(node,"mjx-presub"); + BOX.presup = CHTML.getNode(node,"mjx-presup"); BBOX.sub = this.CHTMLbbox.sub; BBOX.sup = this.CHTMLbbox.sup; BBOX.presub = this.CHTMLbbox.presub; @@ -182,7 +182,7 @@ MathJax.Hub.Register.StartupHook("CommonHTML Jax Ready",function () { BBOX = state.BBOX[type] = CHTML.BBOX.empty(); if (state.w) { BOX.style.paddingLeft = CHTML.Em(state.w); - BBOX.w = BBOX.r = state.w; + BBOX.w = BBOX.r = state.w; BBOX.x = state.w; } } data.toCommonHTML(BOX); @@ -198,7 +198,7 @@ MathJax.Hub.Register.StartupHook("CommonHTML Jax Ready",function () { // right-justify the scripts, otherwise, left-justify them. // CHTMLpadScript: function (type,w,bbox,state) { - if (!bbox) bbox = {w:0, fake:1}; + if (!bbox) bbox = {w:0, fake:1, rscale:1}; var BBOX = state.BBOX[type], dx = 0, dw = 0; if (BBOX) { if (bbox.rscale*bbox.w < w) { @@ -252,7 +252,7 @@ MathJax.Hub.Register.StartupHook("CommonHTML Jax Ready",function () { CHTMLplaceSubSup: function (sub,sbox,sup,Sbox,x,delta,u,v,s) { sub.style.paddingRight = CHTML.Em(s); sbox.w += s; sup.style.paddingBottom = CHTML.Em(u+v-Sbox.d-sbox.h); - sup.style.paddingLeft = CHTML.Em(delta); + sup.style.paddingLeft = CHTML.Em(delta+(Sbox.x||0)); sup.style.paddingRight = CHTML.Em(s); Sbox.w += s; sup.parentNode.style.verticalAlign = CHTML.Em(-v); this.CHTML.combine(sbox,x,-v); @@ -272,7 +272,7 @@ MathJax.Hub.Register.StartupHook("CommonHTML Jax Ready",function () { CHTMLplacePresubPresup: function (presub,pbox,presup,Pbox,delta,u,v,s) { presub.style.paddingLeft = CHTML.Em(s); presup.style.paddingBottom = CHTML.Em(u+v-Pbox.d-pbox.h); - presup.style.paddingLeft = CHTML.Em(delta+s); + presup.style.paddingLeft = CHTML.Em(delta+s+(Pbox.x||0)); presup.style.paddingRight = CHTML.Em(-delta); presup.parentNode.style.verticalAlign = CHTML.Em(-v); this.CHTML.combine(pbox,s,-v); diff --git a/unpacked/jax/output/CommonHTML/autoload/multiline.js b/unpacked/jax/output/CommonHTML/autoload/multiline.js index 3b30c07f4..fcccc549a 100644 --- a/unpacked/jax/output/CommonHTML/autoload/multiline.js +++ b/unpacked/jax/output/CommonHTML/autoload/multiline.js @@ -505,9 +505,9 @@ MathJax.Hub.Register.StartupHook("CommonHTML Jax Ready",function () { // if (end.length === 0) { var NODE = this.CHTMLnodeElement(), - stack = NODE.getElementsByTagName("mjx-stack")[0], - sup = NODE.getElementsByTagName("mjx-sup")[0], - sub = NODE.getElementsByTagName("mjx-sub")[0]; + stack = CHTML.getNode(NODE,"mjx-stack"), + sup = CHTML.getNode(NODE,"mjx-sup"), + sub = CHTML.getNode(NODE,"mjx-sub"); if (stack) node.appendChild(stack); else if (sup) node.appendChild(sup); else if (sub) node.appendChild(sub); @@ -569,9 +569,9 @@ MathJax.Hub.Register.StartupHook("CommonHTML Jax Ready",function () { // if (start.length < 1) { NODE = this.CHTMLnodeElement(); - var prestack = NODE.getElementsByTagName("mjx-prestack")[0], - presup = NODE.getElementsByTagName("mjx-presup")[0], - presub = NODE.getElementsByTagName("mjx-presub")[0]; + var prestack = CHTML.getNode(NODE,"mjx-prestack"), + presup = CHTML.getNode(NODE,"mjx-presup"), + presub = CHTML.getNode(NODE,"mjx-presub"); if (prestack) node.appendChild(prestack); else if (presup) node.appendChild(presup); else if (presub) node.appendChild(presub); @@ -598,9 +598,9 @@ MathJax.Hub.Register.StartupHook("CommonHTML Jax Ready",function () { // if (end.length === 0) { NODE = this.CHTMLnodeElement(); - var stack = NODE.getElementsByTagName("mjx-stack")[0], - sup = NODE.getElementsByTagName("mjx-sup")[0], - sub = NODE.getElementsByTagName("mjx-sub")[0]; + var stack = CHTML.getNode(NODE,"mjx-stack"), + sup = CHTML.getNode(NODE,"mjx-sup"), + sub = CHTML.getNode(NODE,"mjx-sub"); if (stack) node.appendChild(stack); else if (sup) node.appendChild(sup); else if (sub) node.appendChild(sub); diff --git a/unpacked/jax/output/CommonHTML/jax.js b/unpacked/jax/output/CommonHTML/jax.js index 991be9d85..b51900f06 100644 --- a/unpacked/jax/output/CommonHTML/jax.js +++ b/unpacked/jax/output/CommonHTML/jax.js @@ -324,8 +324,9 @@ if (type.substr(0,4) === "mjx-") { if (!def) def = {}; if (def.className) def.className = type+" "+def.className; else def.className = type; + type = "span"; } - return this.HTMLElement("span",def,content); + return this.HTMLElement(type,def,content); }, addElement: function (node,type,def,content) { return node.appendChild(this.Element(type,def,content)); @@ -334,6 +335,22 @@ ucMatch: HTML.ucMatch, setScript: HTML.setScript, + // + // This replaces node.getElementsByTagName(type)[0] + // and should be replaced by that if we go back to using + // custom tags + // + getNode: (document.getElementsByClassName ? + function (node,type) {return node.getElementsByClassName(type)[0]} : + function (node,type) { + var nodes = node.getElementsByTagName("span"); + var name = RegExp("\\b"+type+"\\b"); + for (var i = 0, m = nodes.length; i < m; i++) { + if (name.test(nodes[i].className)) return nodes[i]; + } + } + ), + /********************************************/ @@ -360,7 +377,7 @@ // Remove any existing output // prev = script.previousSibling; - if (prev && prev.nodeName.toLowerCase() === "mjx-chtml") + if (prev && prev.className && String(prev.className).substr(0,9) === "mjx-chtml") prev.parentNode.removeChild(prev); // // Add the node for the math and mark it as being processed @@ -371,7 +388,9 @@ id:jax.inputID+"-Frame", isMathJax:true, jaxID:this.id, oncontextmenu:EVENT.Menu, onmousedown: EVENT.Mousedown, onmouseover:EVENT.Mouseover, onmouseout:EVENT.Mouseout, onmousemove:EVENT.Mousemove, - onclick:EVENT.Click, ondblclick:EVENT.DblClick + onclick:EVENT.Click, ondblclick:EVENT.DblClick, + // Added for keyboard accessible menu. + onkeydown: EVENT.Keydown, tabIndex: "0" }); if (jax.CHTML.display) { // @@ -1325,7 +1344,7 @@ if (child) { var type = options.childNodes; if (type) { - if (type instanceof Array) type = type[i]; + if (type instanceof Array) type = type[i]||"span"; node = CHTML.addElement(node,type); } cnode = child.toCommonHTML(node,options.childOptions); @@ -1664,6 +1683,9 @@ MML.math.Augment({ toCommonHTML: function (node) { node = this.CHTMLdefaultNode(node); + var alttext = this.Get("alttext"); + if (alttext && !node.getAttribute("aria-label")) node.setAttribute("aria-label",alttext); + if (!node.getAttribute("role")) node.setAttribute("role","math"); if (this.CHTML.pwidth) { node.parentNode.style.width = this.CHTML.pwidth; node.parentNode.style.minWidth = this.CHTML.mwidth; @@ -2029,9 +2051,9 @@ // var base, under, over, nodes = []; if (stretch) { - base = node.getElementsByTagName("mjx-op")[0]; - under = node.getElementsByTagName("mjx-under")[0]; - over = node.getElementsByTagName("mjx-over")[0]; + base = CHTML.getNode(node,"mjx-op"); + under = CHTML.getNode(node,"mjx-under"); + over = CHTML.getNode(node,"mjx-over"); nodes[0] = base; nodes[1] = under||over; nodes[2] = over; } else { var types = ["mjx-op","mjx-under","mjx-over"]; @@ -2229,9 +2251,9 @@ // var base, sub, sup; if (stretch) { - base = node.getElementsByTagName("mjx-base")[0]; - sub = node.getElementsByTagName("mjx-sub")[0]; - sup = node.getElementsByTagName("mjx-sup")[0]; + base = CHTML.getNode(node,"mjx-base"); + sub = CHTML.getNode(node,"mjx-sub"); + sup = CHTML.getNode(node,"mjx-sup"); } else { var types = ["mjx-base","mjx-sub","mjx-sup"]; if (this.sup === 1) types[1] = types[2]; diff --git a/unpacked/jax/output/HTML-CSS/jax.js b/unpacked/jax/output/HTML-CSS/jax.js index b673d2c6e..5dec3a2d0 100644 --- a/unpacked/jax/output/HTML-CSS/jax.js +++ b/unpacked/jax/output/HTML-CSS/jax.js @@ -344,7 +344,23 @@ "#MathJax_Tooltip *": { filter: "none", opacity:1, background:"transparent" // for IE }, - + + // Focus elements for keyboard tabbing. + ".MathJax:focus": ( + (MathJax.Hub.Browser.isSafari || MathJax.Hub.Browser.isChrome) ? { + display:"inline-block", + outline:"none", + margin:"-3px", + padding:"3px", + "-webkit-box-shadow": "0px 0px 5px #345, inset 0px 0px 5px #345", + "box-shadow": "0px 0px 5px #345, inset 0px 0px 5px #345" + } : { + display:"inline-block", + outline:"none", + border:"1px dotted", + margin:"-1px" + }), + // // Used for testing web fonts against the default font used while // web fonts are loading @@ -569,8 +585,11 @@ span = div = this.Element("span",{ className:"MathJax", id:jax.inputID+"-Frame", isMathJax:true, jaxID:this.id, oncontextmenu:EVENT.Menu, onmousedown: EVENT.Mousedown, - onmouseover:EVENT.Mouseover, onmouseout:EVENT.Mouseout, onmousemove:EVENT.Mousemove, - onclick:EVENT.Click, ondblclick:EVENT.DblClick + onmouseover:EVENT.Mouseover, onmouseout:EVENT.Mouseout, + onmousemove:EVENT.Mousemove, onclick:EVENT.Click, + ondblclick:EVENT.DblClick, + // Added for keyboard accessible menu. + onkeydown: EVENT.Keydown, tabIndex: "0" }); if (HUB.Browser.noContextMenu) { span.ontouchstart = TOUCH.start; @@ -2868,7 +2887,6 @@ var alttext = this.Get("alttext"); if (alttext && !span.getAttribute("aria-label")) span.setAttribute("aria-label",alttext); if (!span.getAttribute("role")) span.setAttribute("role","math"); -// span.setAttribute("tabindex",0); // causes focus outline, so disable for now stack = HTMLCSS.createStack(span); box = HTMLCSS.createBox(stack); // Move font-size from outer span to stack to avoid line separation // problem in strict HTML mode diff --git a/unpacked/jax/output/NativeMML/jax.js b/unpacked/jax/output/NativeMML/jax.js index 3c6f7c289..3d444f745 100644 --- a/unpacked/jax/output/NativeMML/jax.js +++ b/unpacked/jax/output/NativeMML/jax.js @@ -326,6 +326,9 @@ container.onmousedown = EVENT.Mousedown; container.onclick = EVENT.Click; container.ondblclick = EVENT.DblClick; + // Added for keyboard accessible menu. + container.onkeydown = EVENT.Keydown; + container.tabIndex = "0"; if (HUB.Browser.noContextMenu) { container.ontouchstart = TOUCH.start; container.ontouchend = TOUCH.end; diff --git a/unpacked/jax/output/PreviewHTML/jax.js b/unpacked/jax/output/PreviewHTML/jax.js index c8ce3d77a..7532af253 100644 --- a/unpacked/jax/output/PreviewHTML/jax.js +++ b/unpacked/jax/output/PreviewHTML/jax.js @@ -197,7 +197,9 @@ className:"MathJax_PHTML", id:jax.inputID+"-Frame", isMathJax:true, jaxID:this.id, oncontextmenu:EVENT.Menu, onmousedown: EVENT.Mousedown, onmouseover:EVENT.Mouseover, onmouseout:EVENT.Mouseout, onmousemove:EVENT.Mousemove, - onclick:EVENT.Click, ondblclick:EVENT.DblClick + onclick:EVENT.Click, ondblclick:EVENT.DblClick, + // Added for keyboard accessible menu. + onkeydown: EVENT.Keydown, tabIndex: "0" }); if (HUB.Browser.noContextMenu) { span.ontouchstart = TOUCH.start; diff --git a/unpacked/jax/output/SVG/jax.js b/unpacked/jax/output/SVG/jax.js index 64783ed09..c273f3a9d 100644 --- a/unpacked/jax/output/SVG/jax.js +++ b/unpacked/jax/output/SVG/jax.js @@ -220,7 +220,9 @@ className:"MathJax_SVG", id:jax.inputID+"-Frame", isMathJax:true, jaxID:this.id, oncontextmenu:EVENT.Menu, onmousedown: EVENT.Mousedown, onmouseover:EVENT.Mouseover, onmouseout:EVENT.Mouseout, onmousemove:EVENT.Mousemove, - onclick:EVENT.Click, ondblclick:EVENT.DblClick + onclick:EVENT.Click, ondblclick:EVENT.DblClick, + // Added for keyboard accessible menu. + onkeydown: EVENT.Keydown, tabIndex: "0" }); if (HUB.Browser.noContextMenu) { span.ontouchstart = TOUCH.start; @@ -2098,9 +2100,8 @@ // Add it to the MathJax span // var alttext = this.Get("alttext"); - if (alttext && !svg.element.getAttribute("aria-label")) span.setAttribute("aria-label",alttext); - if (!svg.element.getAttribute("role")) span.setAttribute("role","math"); -// span.setAttribute("tabindex",0); // causes focus outline, so disable for now + if (alttext && !svg.element.getAttribute("aria-label")) svg.element.setAttribute("aria-label",alttext); + if (!svg.element.getAttribute("role")) svg.element.setAttribute("role","img"); span.appendChild(svg.element); svg.element = null; //