Merge pull request #1242 from mathjax/integrate_keyboard_events
Integrate keyboard events. Resolves issue #939
This commit is contained in:
commit
27bae487e6
|
@ -29,6 +29,12 @@
|
|||
|
||||
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",{
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -109,6 +109,20 @@
|
|||
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)},
|
||||
|
@ -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
|
||||
//
|
||||
|
@ -178,7 +201,7 @@
|
|||
load = LOCALE.loadDomain("MathMenu");
|
||||
if (!load) {
|
||||
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].format = (jax.sourceMenuFormat||"MathML");
|
||||
source.items[1].name = INPUT[jax.inputJax].sourceMenuTitle;
|
||||
|
@ -189,7 +212,7 @@
|
|||
// 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]
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
(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,
|
||||
|
@ -194,21 +194,66 @@
|
|||
".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,
|
||||
|
@ -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)}
|
||||
|
||||
|
@ -248,18 +294,27 @@
|
|||
|
||||
div.appendChild(menu);
|
||||
this.posted = true;
|
||||
|
||||
menu.style.width = (menu.offsetWidth+2) + "px";
|
||||
if (event) {
|
||||
var x = event.pageX, y = event.pageY;
|
||||
if (!x && !y) {
|
||||
}
|
||||
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;
|
||||
|
@ -282,8 +337,17 @@
|
|||
}
|
||||
|
||||
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,8 +379,8 @@
|
|||
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];
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -401,31 +490,108 @@
|
|||
}
|
||||
},
|
||||
|
||||
/*************************************************************/
|
||||
/*
|
||||
* 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,39 +601,50 @@
|
|||
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) {
|
||||
if (menu.parentNode === MENU.ActiveNode().parentNode) {
|
||||
this.Deactivate(MENU.ActiveNode());
|
||||
}
|
||||
this.Activate(event, menu);
|
||||
},
|
||||
Mouseout: function (event,menu) {
|
||||
this.Deactivate(menu);
|
||||
},
|
||||
Mouseup: function (event,menu) {return this.Remove(event,menu)},
|
||||
|
||||
|
||||
DeactivateSubmenus: function(menu) {
|
||||
var menus = document.getElementById("MathJax_MenuFrame").childNodes,
|
||||
items = menu.parentNode.childNodes;
|
||||
items = ITEM.GetMenuNode(menu).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])}
|
||||
// Deactivates submenu items.
|
||||
if (item && item.submenu && item.submenu.posted &&
|
||||
item !== menu.menuItem) {
|
||||
item.Deactivate(items[i]);
|
||||
}
|
||||
m = menus.length-1;
|
||||
while (m >= 0 && menu.parentNode.menuItem !== menus[m].menuItem) {
|
||||
}
|
||||
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--;
|
||||
}
|
||||
if (this.Timer && !MENU.isMobile) {this.Timer(event,menu)}
|
||||
}
|
||||
},
|
||||
Mouseout: function (event,menu) {
|
||||
if (!this.menu || !this.menu.posted) {this.Deactivate(menu)}
|
||||
if (this.timer) {clearTimeout(this.timer); delete this.timer}
|
||||
},
|
||||
Mouseup: function (event,menu) {return this.Remove(event,menu)},
|
||||
|
||||
Touchstart: function (event,menu) {return this.TouchEvent(event,menu,"Mousedown")},
|
||||
Touchend: function (event,menu) {return this.TouchEvent(event,menu,"Mouseup")},
|
||||
|
@ -487,13 +664,14 @@
|
|||
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;
|
||||
}
|
||||
});
|
||||
|
||||
/*************************************************************/
|
||||
|
@ -504,17 +682,87 @@
|
|||
|
||||
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/,"");
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/*************************************************************/
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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) { }
|
||||
});
|
||||
|
||||
/*************************************************************/
|
||||
|
@ -717,7 +1012,7 @@
|
|||
]]);
|
||||
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"],
|
||||
|
@ -725,11 +1020,15 @@
|
|||
display:"inline-block", "text-align":"left", "font-size":"80%",
|
||||
"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 +1044,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]) {
|
||||
|
@ -835,7 +1143,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);
|
||||
|
@ -1059,7 +1367,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 +1393,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)) {
|
||||
|
@ -1201,7 +1509,7 @@
|
|||
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);
|
||||
|
|
|
@ -371,7 +371,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) {
|
||||
//
|
||||
|
|
|
@ -345,6 +345,22 @@
|
|||
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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue
Block a user