Merge pull request #1242 from mathjax/integrate_keyboard_events

Integrate keyboard events.  Resolves issue #939
This commit is contained in:
Davide P. Cervone 2015-09-07 06:18:36 -04:00
commit 27bae487e6
8 changed files with 566 additions and 190 deletions

View File

@ -29,7 +29,13 @@
var STIXURL = "http://www.stixfonts.org/"; var STIXURL = "http://www.stixfonts.org/";
var MENU = MathJax.Menu; 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",{ var CONFIG = HUB.CombineConfig("HelpDialog",{
styles: { styles: {
@ -101,10 +107,10 @@
HELP.Post = function () { HELP.Post = function () {
this.div = MENU.Background(this); this.div = MENU.Background(this);
var help = HTML.addElement(this.div,"div",{ var help = HTML.addElement(this.div,"div",{
id: "MathJax_Help" id: "MathJax_Help", tabIndex: 0, onkeydown: HELP.Keydown
},LOCALE._("HelpDialog",[ },LOCALE._("HelpDialog",[
["b",{style:{fontSize:"120%"}},[["Help","MathJax Help"]]], ["b",{style:{fontSize:"120%"}},[["Help","MathJax Help"]]],
["div",{id: "MathJax_HelpContent"},[ ["div",{id: "MathJax_HelpContent", tabIndex: 0},[
["p",{},[["MathJax", ["p",{},[["MathJax",
"*MathJax* is a JavaScript library that allows page authors to include " + "*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 " + "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"]], ["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"]]] [["span",{},["\u00D7"]]]
] ]
])); ]));
help.focus();
LOCALE.setCSS(help); LOCALE.setCSS(help);
var doc = (document.documentElement||{}); var doc = (document.documentElement||{});
var H = window.innerHeight || doc.clientHeight || doc.scrollHeight || 0; var H = window.innerHeight || doc.clientHeight || doc.scrollHeight || 0;
@ -167,6 +176,15 @@
HELP.Remove = function (event) { HELP.Remove = function (event) {
if (HELP.div) {document.body.removeChild(HELP.div); delete HELP.div} 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( MathJax.Callback.Queue(
HUB.Register.StartupHook("End Config",{}), // wait until config is complete HUB.Register.StartupHook("End Config",{}), // wait until config is complete

View File

@ -4,20 +4,20 @@
/************************************************************* /*************************************************************
* *
* MathJax/extensions/MathEvents.js * MathJax/extensions/MathEvents.js
* *
* Implements the event handlers needed by the output jax to perform * Implements the event handlers needed by the output jax to perform
* menu, hover, and other events. * menu, hover, and other events.
* *
* --------------------------------------------------------------------- * ---------------------------------------------------------------------
* *
* Copyright (c) 2011-2015 The MathJax Consortium * Copyright (c) 2011-2015 The MathJax Consortium
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -27,12 +27,12 @@
(function (HUB,HTML,AJAX,CALLBACK,LOCALE,OUTPUT,INPUT) { (function (HUB,HTML,AJAX,CALLBACK,LOCALE,OUTPUT,INPUT) {
var VERSION = "2.5.0"; var VERSION = "2.5.0";
var EXTENSION = MathJax.Extension; var EXTENSION = MathJax.Extension;
var ME = EXTENSION.MathEvents = {version: VERSION}; var ME = EXTENSION.MathEvents = {version: VERSION};
var SETTINGS = HUB.config.menuSettings; var SETTINGS = HUB.config.menuSettings;
var CONFIG = { var CONFIG = {
hover: 500, // time required to be considered a hover hover: 500, // time required to be considered a hover
frame: { frame: {
@ -41,7 +41,7 @@
bcolor: "#A6D", // frame border color bcolor: "#A6D", // frame border color
hwidth: "15px", // haze width hwidth: "15px", // haze width
hcolor: "#83A" // haze color hcolor: "#83A" // haze color
}, },
button: { button: {
x: -6, y: -3, // menu button offsets x: -6, y: -3, // menu button offsets
wx: -2 // button offset for full-width equations wx: -2 // button offset for full-width equations
@ -99,16 +99,30 @@
} }
}; };
// //
// Common event-handling code // Common event-handling code
// //
var EVENT = ME.Event = { var EVENT = ME.Event = {
LEFTBUTTON: 0, // the event.button value for left button LEFTBUTTON: 0, // the event.button value for left button
RIGHTBUTTON: 2, // the event.button value for right button RIGHTBUTTON: 2, // the event.button value for right button
MENUKEY: "altKey", // the event value for alternate context menu 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)}, Mousedown: function (event) {return EVENT.Handler(event,"Mousedown",this)},
Mouseup: function (event) {return EVENT.Handler(event,"Mouseup",this)}, Mouseup: function (event) {return EVENT.Handler(event,"Mouseup",this)},
Mousemove: function (event) {return EVENT.Handler(event,"Mousemove",this)}, Mousemove: function (event) {return EVENT.Handler(event,"Mousemove",this)},
@ -117,7 +131,7 @@
Click: function (event) {return EVENT.Handler(event,"Click",this)}, Click: function (event) {return EVENT.Handler(event,"Click",this)},
DblClick: function (event) {return EVENT.Handler(event,"DblClick",this)}, DblClick: function (event) {return EVENT.Handler(event,"DblClick",this)},
Menu: function (event) {return EVENT.Handler(event,"ContextMenu",this)}, Menu: function (event) {return EVENT.Handler(event,"ContextMenu",this)},
// //
// Call the output jax's event handler or the zoom handler // Call the output jax's event handler or the zoom handler
// //
@ -129,7 +143,7 @@
if (jax[type]) {return jax[type](event,math)} if (jax[type]) {return jax[type](event,math)}
if (EXTENSION.MathZoom) {return EXTENSION.MathZoom.HandleEvent(event,type,math)} if (EXTENSION.MathZoom) {return EXTENSION.MathZoom.HandleEvent(event,type,math)}
}, },
// //
// Try to cancel the event in every way we can // Try to cancel the event in every way we can
// //
@ -143,6 +157,15 @@
return false; 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 // 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; // Check if localization needs loading;
// If not, post the menu, and return. // If not, post the menu, and return.
// Otherwise wait for the localization to load // Otherwise wait for the localization to load
@ -178,18 +201,18 @@
load = LOCALE.loadDomain("MathMenu"); load = LOCALE.loadDomain("MathMenu");
if (!load) { if (!load) {
MENU.jax = jax; MENU.jax = jax;
var source = MENU.menu.Find("Show Math As").menu; var source = MENU.menu.Find("Show Math As").submenu;
source.items[0].name = jax.sourceMenuTitle; source.items[0].name = jax.sourceMenuTitle;
source.items[0].format = (jax.sourceMenuFormat||"MathML"); source.items[0].format = (jax.sourceMenuFormat||"MathML");
source.items[1].name = INPUT[jax.inputJax].sourceMenuTitle; source.items[1].name = INPUT[jax.inputJax].sourceMenuTitle;
source.items[5].disabled = !INPUT[jax.inputJax].annotationEncoding; source.items[5].disabled = !INPUT[jax.inputJax].annotationEncoding;
// //
// Try and find each known annotation format and enable the menu // Try and find each known annotation format and enable the menu
// items accordingly. // items accordingly.
// //
var annotations = source.items[2]; annotations.disabled = true; var annotations = source.items[2]; annotations.disabled = true;
var annotationItems = annotations.menu.items; var annotationItems = annotations.submenu.items;
annotationList = MathJax.Hub.Config.semanticsAnnotations; annotationList = MathJax.Hub.Config.semanticsAnnotations;
for (var i = 0, m = annotationItems.length; i < m; i++) { for (var i = 0, m = annotationItems.length; i < m; i++) {
var name = annotationItems[i].name[1] var name = annotationItems[i].name[1]
@ -226,7 +249,7 @@
); );
return EVENT.False(event); return EVENT.False(event);
}, },
// //
// Mousedown handler for alternate means of accessing menu // Mousedown handler for alternate means of accessing menu
// //
@ -243,26 +266,26 @@
return JAX.ContextMenu(event,math,true); return JAX.ContextMenu(event,math,true);
} }
}, },
ClearSelection: function () { ClearSelection: function () {
if (ME.safariContextMenuBug) {setTimeout("window.getSelection().empty()",0)} if (ME.safariContextMenuBug) {setTimeout("window.getSelection().empty()",0)}
if (document.selection) {setTimeout("document.selection.empty()",0)} if (document.selection) {setTimeout("document.selection.empty()",0)}
}, },
getBBox: function (span) { getBBox: function (span) {
span.appendChild(ME.topImg); span.appendChild(ME.topImg);
var h = ME.topImg.offsetTop, d = span.offsetHeight-h, w = span.offsetWidth; var h = ME.topImg.offsetTop, d = span.offsetHeight-h, w = span.offsetWidth;
span.removeChild(ME.topImg); span.removeChild(ME.topImg);
return {w:w, h:h, d:d}; return {w:w, h:h, d:d};
} }
}; };
// //
// Handle hover "discoverability" // Handle hover "discoverability"
// //
var HOVER = ME.Hover = { var HOVER = ME.Hover = {
// //
// Check if we are moving from a non-MathJax element to a MathJax one // 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 // and either start fading in again (if it is fading out) or start the
@ -309,7 +332,7 @@
return EVENT.False(event); return EVENT.False(event);
} }
}, },
// //
// Clear the old timer and start a new one // Clear the old timer and start a new one
// //
@ -320,7 +343,7 @@
ClearHoverTimer: function () { ClearHoverTimer: function () {
if (this.hoverTimer) {clearTimeout(this.hoverTimer); delete this.hoverTimer} if (this.hoverTimer) {clearTimeout(this.hoverTimer); delete this.hoverTimer}
}, },
// //
// Handle putting up the hover frame // Handle putting up the hover frame
// //
@ -431,7 +454,7 @@
jax.hover.timer = setTimeout(CALLBACK(["HoverFade",this,jax]),(delay||CONFIG.fadeDelay)); jax.hover.timer = setTimeout(CALLBACK(["HoverFade",this,jax]),(delay||CONFIG.fadeDelay));
} }
}, },
// //
// Handle a click on the menu button // Handle a click on the menu button
// //
@ -439,7 +462,7 @@
if (!event) {event = window.event} if (!event) {event = window.event}
return OUTPUT[this.jax].ContextMenu(event,this.math,true); return OUTPUT[this.jax].ContextMenu(event,this.math,true);
}, },
// //
// Clear all hover timers // Clear all hover timers
// //
@ -449,7 +472,7 @@
HOVER.ClearHoverTimer(); HOVER.ClearHoverTimer();
delete jax.hover; delete jax.hover;
}, },
// //
// Make a measurement in pixels // 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-and-hold as a replacement for context menu event.
// Use double-tap as a replacement for double click. // Use double-tap as a replacement for double click.
@ -480,7 +503,7 @@
last: 0, // time of last tap event last: 0, // time of last tap event
delay: 500, // delay time for double-click delay: 500, // delay time for double-click
// //
// Check if this is a double-tap, and if so, start the timer // Check if this is a double-tap, and if so, start the timer
// for the double-tap and hold (to trigger the contextual menu) // for the double-tap and hold (to trigger the contextual menu)
@ -494,9 +517,9 @@
event.preventDefault(); 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 // double-tap and were waiting to see if it is held long
// enough for the menu. Since we got the end before the // enough for the menu. Since we got the end before the
// timeout, it is a double-click, not a double-tap-and-hold. // 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); return EVENT.Handler((event.touches[0]||event.touch),"DblClick",this);
} }
}, },
// //
// If the timeout passes without an end event, we issue // If the timeout passes without an end event, we issue
// the contextual menu event. // the contextual menu event.
@ -521,10 +544,10 @@
delete TOUCH.timeout; TOUCH.last = 0; TOUCH.up = false; delete TOUCH.timeout; TOUCH.last = 0; TOUCH.up = false;
return EVENT.Handler((event.touches[0]||event.touch),"ContextMenu",math); return EVENT.Handler((event.touches[0]||event.touch),"ContextMenu",math);
} }
}; };
/* /*
* // * //
* // Mobile screens are small, so use larger version of arrow * // Mobile screens are small, so use larger version of arrow
* // * //
@ -534,7 +557,7 @@
* CONFIG.button.x = -6; * CONFIG.button.x = -6;
* } * }
*/ */
// //
// Set up browser-specific values // Set up browser-specific values
// //
@ -557,7 +580,7 @@
ME.noContextMenuBug = true; // doesn't produce contextmenu event ME.noContextMenuBug = true; // doesn't produce contextmenu event
} }
}); });
// //
// Used in measuring zoom and hover positions // Used in measuring zoom and hover positions
// //
@ -578,7 +601,7 @@
haze["-moz-box-shadow"] = haze["-khtml-box-shadow"] = haze["-moz-box-shadow"] = haze["-khtml-box-shadow"] =
"0px 0px "+CONFIG.frame.hwidth+" "+CONFIG.frame.hcolor; "0px 0px "+CONFIG.frame.hwidth+" "+CONFIG.frame.hcolor;
}; };
// //
// Queue the events needed for startup // Queue the events needed for startup
// //
@ -590,6 +613,6 @@
["Post",HUB.Startup.signal,"MathEvents Ready"], ["Post",HUB.Startup.signal,"MathEvents Ready"],
["loadComplete",AJAX,"[MathJax]/extensions/MathEvents.js"] ["loadComplete",AJAX,"[MathJax]/extensions/MathEvents.js"]
); );
})(MathJax.Hub,MathJax.HTML,MathJax.Ajax,MathJax.Callback, })(MathJax.Hub,MathJax.HTML,MathJax.Ajax,MathJax.Callback,
MathJax.Localization,MathJax.OutputJax,MathJax.InputJax); MathJax.Localization,MathJax.OutputJax,MathJax.InputJax);

File diff suppressed because it is too large Load Diff

View File

@ -371,7 +371,9 @@
id:jax.inputID+"-Frame", isMathJax:true, jaxID:this.id, id:jax.inputID+"-Frame", isMathJax:true, jaxID:this.id,
oncontextmenu:EVENT.Menu, onmousedown: EVENT.Mousedown, oncontextmenu:EVENT.Menu, onmousedown: EVENT.Mousedown,
onmouseover:EVENT.Mouseover, onmouseout:EVENT.Mouseout, onmousemove:EVENT.Mousemove, 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) { if (jax.CHTML.display) {
// //

View File

@ -344,7 +344,23 @@
"#MathJax_Tooltip *": { "#MathJax_Tooltip *": {
filter: "none", opacity:1, background:"transparent" // for IE 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 // Used for testing web fonts against the default font used while
// web fonts are loading // web fonts are loading
@ -569,8 +585,11 @@
span = div = this.Element("span",{ span = div = this.Element("span",{
className:"MathJax", id:jax.inputID+"-Frame", isMathJax:true, jaxID:this.id, className:"MathJax", id:jax.inputID+"-Frame", isMathJax:true, jaxID:this.id,
oncontextmenu:EVENT.Menu, onmousedown: EVENT.Mousedown, oncontextmenu:EVENT.Menu, onmousedown: EVENT.Mousedown,
onmouseover:EVENT.Mouseover, onmouseout:EVENT.Mouseout, onmousemove:EVENT.Mousemove, onmouseover:EVENT.Mouseover, onmouseout:EVENT.Mouseout,
onclick:EVENT.Click, ondblclick:EVENT.DblClick onmousemove:EVENT.Mousemove, onclick:EVENT.Click,
ondblclick:EVENT.DblClick,
// Added for keyboard accessible menu.
onkeydown: EVENT.Keydown, tabIndex: "0"
}); });
if (HUB.Browser.noContextMenu) { if (HUB.Browser.noContextMenu) {
span.ontouchstart = TOUCH.start; span.ontouchstart = TOUCH.start;
@ -2868,7 +2887,6 @@
var alttext = this.Get("alttext"); var alttext = this.Get("alttext");
if (alttext && !span.getAttribute("aria-label")) span.setAttribute("aria-label",alttext); if (alttext && !span.getAttribute("aria-label")) span.setAttribute("aria-label",alttext);
if (!span.getAttribute("role")) span.setAttribute("role","math"); 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); stack = HTMLCSS.createStack(span); box = HTMLCSS.createBox(stack);
// Move font-size from outer span to stack to avoid line separation // Move font-size from outer span to stack to avoid line separation
// problem in strict HTML mode // problem in strict HTML mode

View File

@ -326,6 +326,9 @@
container.onmousedown = EVENT.Mousedown; container.onmousedown = EVENT.Mousedown;
container.onclick = EVENT.Click; container.onclick = EVENT.Click;
container.ondblclick = EVENT.DblClick; container.ondblclick = EVENT.DblClick;
// Added for keyboard accessible menu.
container.onkeydown = EVENT.Keydown;
container.tabIndex = "0";
if (HUB.Browser.noContextMenu) { if (HUB.Browser.noContextMenu) {
container.ontouchstart = TOUCH.start; container.ontouchstart = TOUCH.start;
container.ontouchend = TOUCH.end; container.ontouchend = TOUCH.end;

View File

@ -197,7 +197,9 @@
className:"MathJax_PHTML", id:jax.inputID+"-Frame", isMathJax:true, jaxID:this.id, className:"MathJax_PHTML", id:jax.inputID+"-Frame", isMathJax:true, jaxID:this.id,
oncontextmenu:EVENT.Menu, onmousedown: EVENT.Mousedown, oncontextmenu:EVENT.Menu, onmousedown: EVENT.Mousedown,
onmouseover:EVENT.Mouseover, onmouseout:EVENT.Mouseout, onmousemove:EVENT.Mousemove, 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) { if (HUB.Browser.noContextMenu) {
span.ontouchstart = TOUCH.start; span.ontouchstart = TOUCH.start;

View File

@ -220,7 +220,9 @@
className:"MathJax_SVG", id:jax.inputID+"-Frame", isMathJax:true, jaxID:this.id, className:"MathJax_SVG", id:jax.inputID+"-Frame", isMathJax:true, jaxID:this.id,
oncontextmenu:EVENT.Menu, onmousedown: EVENT.Mousedown, oncontextmenu:EVENT.Menu, onmousedown: EVENT.Mousedown,
onmouseover:EVENT.Mouseover, onmouseout:EVENT.Mouseout, onmousemove:EVENT.Mousemove, 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) { if (HUB.Browser.noContextMenu) {
span.ontouchstart = TOUCH.start; span.ontouchstart = TOUCH.start;