MathMenu.js 62 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664
  1. /* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
  2. /* vim: set ts=2 et sw=2 tw=80: */
  3. /*************************************************************
  4. *
  5. * MathJax/extensions/MathMenu.js
  6. *
  7. * Implements a right-mouse (or CTRL-click) menu over mathematics
  8. * elements that gives the user the ability to copy the source,
  9. * change the math size, and zoom settings.
  10. *
  11. * ---------------------------------------------------------------------
  12. *
  13. * Copyright (c) 2010-2018 The MathJax Consortium
  14. *
  15. * Licensed under the Apache License, Version 2.0 (the "License");
  16. * you may not use this file except in compliance with the License.
  17. * You may obtain a copy of the License at
  18. *
  19. * http://www.apache.org/licenses/LICENSE-2.0
  20. *
  21. * Unless required by applicable law or agreed to in writing, software
  22. * distributed under the License is distributed on an "AS IS" BASIS,
  23. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  24. * See the License for the specific language governing permissions and
  25. * limitations under the License.
  26. */
  27. (function (HUB,HTML,AJAX,CALLBACK,OUTPUT) {
  28. var VERSION = "2.7.5";
  29. var SIGNAL = MathJax.Callback.Signal("menu"); // signal for menu events
  30. MathJax.Extension.MathMenu = {
  31. version: VERSION,
  32. signal: SIGNAL
  33. };
  34. var _ = function (id) {
  35. return MathJax.Localization._.apply(
  36. MathJax.Localization,
  37. [["MathMenu",id]].concat([].slice.call(arguments,1))
  38. );
  39. };
  40. var isArray = MathJax.Object.isArray;
  41. var isPC = HUB.Browser.isPC, isMSIE = HUB.Browser.isMSIE, isIE9 = ((document.documentMode||0) > 8);
  42. var ROUND = (isPC ? null : "5px");
  43. var CONFIG = HUB.CombineConfig("MathMenu",{
  44. delay: 150, // the delay for submenus
  45. showRenderer: true, // show the "Math Renderer" menu?
  46. showMathPlayer: true, // show the "MathPlayer" menu?
  47. showFontMenu: false, // show the "Font Preference" menu?
  48. showContext: false, // show the "Context Menu" menu?
  49. showDiscoverable: false, // show the "Discoverable" menu?
  50. showLocale: true, // show the "Locale" menu?
  51. showLocaleURL: false, // show the "Load from URL" menu?
  52. semanticsAnnotations: {
  53. "TeX": ["TeX", "LaTeX", "application/x-tex"],
  54. "StarMath": ["StarMath 5.0"],
  55. "Maple": ["Maple"],
  56. "ContentMathML": ["MathML-Content", "application/mathml-content+xml"],
  57. "OpenMath": ["OpenMath"]
  58. },
  59. windowSettings: { // for source window
  60. status: "no", toolbar: "no", locationbar: "no", menubar: "no",
  61. directories: "no", personalbar: "no", resizable: "yes", scrollbars: "yes",
  62. width: 400, height: 300,
  63. left: Math.round((screen.width - 400)/2),
  64. top: Math.round((screen.height - 300)/3)
  65. },
  66. styles: {
  67. "#MathJax_About": {
  68. position:"fixed", left:"50%", width:"auto", "text-align":"center",
  69. border:"3px outset", padding:"1em 2em", "background-color":"#DDDDDD", color:"black",
  70. cursor: "default", "font-family":"message-box", "font-size":"120%",
  71. "font-style":"normal", "text-indent":0, "text-transform":"none",
  72. "line-height":"normal", "letter-spacing":"normal", "word-spacing":"normal",
  73. "word-wrap":"normal", "white-space":"nowrap", "float":"none", "z-index":201,
  74. "border-radius": "15px", // Opera 10.5 and IE9
  75. "-webkit-border-radius": "15px", // Safari and Chrome
  76. "-moz-border-radius": "15px", // Firefox
  77. "-khtml-border-radius": "15px", // Konqueror
  78. "box-shadow":"0px 10px 20px #808080", // Opera 10.5 and IE9
  79. "-webkit-box-shadow":"0px 10px 20px #808080", // Safari 3 and Chrome
  80. "-moz-box-shadow":"0px 10px 20px #808080", // Forefox 3.5
  81. "-khtml-box-shadow":"0px 10px 20px #808080", // Konqueror
  82. filter: "progid:DXImageTransform.Microsoft.dropshadow(OffX=2, OffY=2, Color='gray', Positive='true')" // IE
  83. },
  84. "#MathJax_About.MathJax_MousePost": {
  85. outline:"none"
  86. },
  87. ".MathJax_Menu": {
  88. position:"absolute", "background-color":"white", color:"black",
  89. width:"auto", padding:(isPC ? "2px" : "5px 0px"),
  90. border:"1px solid #CCCCCC", margin:0, cursor:"default",
  91. font: "menu", "text-align":"left", "text-indent":0, "text-transform":"none",
  92. "line-height":"normal", "letter-spacing":"normal", "word-spacing":"normal",
  93. "word-wrap":"normal", "white-space":"nowrap", "float":"none", "z-index":201,
  94. "border-radius": ROUND, // Opera 10.5 and IE9
  95. "-webkit-border-radius": ROUND, // Safari and Chrome
  96. "-moz-border-radius": ROUND, // Firefox
  97. "-khtml-border-radius": ROUND, // Konqueror
  98. "box-shadow":"0px 10px 20px #808080", // Opera 10.5 and IE9
  99. "-webkit-box-shadow":"0px 10px 20px #808080", // Safari 3 and Chrome
  100. "-moz-box-shadow":"0px 10px 20px #808080", // Forefox 3.5
  101. "-khtml-box-shadow":"0px 10px 20px #808080", // Konqueror
  102. filter: "progid:DXImageTransform.Microsoft.dropshadow(OffX=2, OffY=2, Color='gray', Positive='true')" // IE
  103. },
  104. ".MathJax_MenuItem": {
  105. padding: (isPC ? "2px 2em" : "1px 2em"),
  106. background:"transparent"
  107. },
  108. ".MathJax_MenuArrow": {
  109. position:"absolute", right:".5em", "padding-top":".25em", color:"#666666",
  110. "font-family": (isMSIE ? "'Arial unicode MS'" : null), "font-size": ".75em"
  111. },
  112. ".MathJax_MenuActive .MathJax_MenuArrow": {color:"white"},
  113. ".MathJax_MenuArrow.RTL": {left:".5em", right:"auto"},
  114. ".MathJax_MenuCheck": {
  115. position:"absolute", left:".7em",
  116. "font-family": (isMSIE ? "'Arial unicode MS'" : null)
  117. },
  118. ".MathJax_MenuCheck.RTL": {right:".7em", left:"auto"},
  119. ".MathJax_MenuRadioCheck": {
  120. position:"absolute", left: (isPC ? "1em" : ".7em")
  121. },
  122. ".MathJax_MenuRadioCheck.RTL": {
  123. right: (isPC ? "1em" : ".7em"), left:"auto"
  124. },
  125. ".MathJax_MenuLabel": {
  126. padding: (isPC ? "2px 2em 4px 1.33em" : "1px 2em 3px 1.33em"),
  127. "font-style":"italic"
  128. },
  129. ".MathJax_MenuRule": {
  130. "border-top": (isPC ? "1px solid #CCCCCC" : "1px solid #DDDDDD"),
  131. margin: (isPC ? "4px 1px 0px" : "4px 3px")
  132. },
  133. ".MathJax_MenuDisabled": {
  134. color:"GrayText"
  135. },
  136. ".MathJax_MenuActive": {
  137. "background-color": (isPC ? "Highlight" : "#606872"),
  138. color: (isPC ? "HighlightText" : "white")
  139. },
  140. ".MathJax_MenuDisabled:focus, .MathJax_MenuLabel:focus": {
  141. "background-color": "#E8E8E8"
  142. },
  143. ".MathJax_ContextMenu:focus": {
  144. outline:"none"
  145. },
  146. ".MathJax_ContextMenu .MathJax_MenuItem:focus": {
  147. outline:"none"
  148. },
  149. "#MathJax_AboutClose": {
  150. top:".2em", right:".2em"
  151. },
  152. ".MathJax_Menu .MathJax_MenuClose": {
  153. top:"-10px", left:"-10px"
  154. },
  155. ".MathJax_MenuClose": {
  156. position:"absolute",
  157. cursor:"pointer",
  158. display:"inline-block",
  159. border:"2px solid #AAA",
  160. "border-radius":"18px",
  161. "-webkit-border-radius": "18px", // Safari and Chrome
  162. "-moz-border-radius": "18px", // Firefox
  163. "-khtml-border-radius": "18px", // Konqueror
  164. "font-family":"'Courier New',Courier",
  165. "font-size":"24px",
  166. color:"#F0F0F0"
  167. },
  168. ".MathJax_MenuClose span": {
  169. display:"block", "background-color":"#AAA", border:"1.5px solid",
  170. "border-radius":"18px",
  171. "-webkit-border-radius": "18px", // Safari and Chrome
  172. "-moz-border-radius": "18px", // Firefox
  173. "-khtml-border-radius": "18px", // Konqueror
  174. "line-height":0,
  175. padding:"8px 0 6px" // may need to be browser-specific
  176. },
  177. ".MathJax_MenuClose:hover": {
  178. color:"white!important",
  179. border:"2px solid #CCC!important"
  180. },
  181. ".MathJax_MenuClose:hover span": {
  182. "background-color":"#CCC!important"
  183. },
  184. ".MathJax_MenuClose:hover:focus": {
  185. outline:"none"
  186. }
  187. }
  188. });
  189. var FALSE, HOVER, KEY;
  190. HUB.Register.StartupHook("MathEvents Ready",function () {
  191. FALSE = MathJax.Extension.MathEvents.Event.False;
  192. HOVER = MathJax.Extension.MathEvents.Hover;
  193. KEY = MathJax.Extension.MathEvents.Event.KEY;
  194. });
  195. /*************************************************************/
  196. /*
  197. * Abstract class of all keyboard navigatable objects.
  198. */
  199. var NAV = MathJax.Object.Subclass({
  200. /*
  201. * Moving in the list of items.
  202. */
  203. Keydown: function(event, menu) {
  204. switch (event.keyCode) {
  205. case KEY.ESCAPE:
  206. this.Remove(event, menu);
  207. break;
  208. case KEY.RIGHT:
  209. this.Right(event, menu);
  210. break;
  211. case KEY.LEFT:
  212. this.Left(event, menu);
  213. break;
  214. case KEY.UP:
  215. this.Up(event, menu);
  216. break;
  217. case KEY.DOWN:
  218. this.Down(event, menu);
  219. break;
  220. case KEY.RETURN:
  221. case KEY.SPACE:
  222. this.Space(event, menu);
  223. break;
  224. default:
  225. return;
  226. break;
  227. }
  228. return FALSE(event);
  229. },
  230. Escape: function(event, menu) { },
  231. Right: function(event, menu) { },
  232. Left: function(event, menu) { },
  233. Up: function(event, menu) { },
  234. Down: function(event, menu) { },
  235. Space: function(event, menu) { }
  236. }, {});
  237. /*************************************************************/
  238. /*
  239. * The main menu class
  240. */
  241. var MENU = MathJax.Menu = NAV.Subclass({
  242. version: VERSION,
  243. items: [],
  244. posted: false,
  245. title: null,
  246. margin: 5,
  247. Init: function (def) {this.items = [].slice.call(arguments,0)},
  248. With: function (def) {if (def) {HUB.Insert(this,def)}; return this},
  249. /*
  250. * Display the menu
  251. */
  252. Post: function (event,parent,forceLTR) {
  253. if (!event) {event = window.event||{}}
  254. var div = document.getElementById("MathJax_MenuFrame");
  255. if (!div) {
  256. div = MENU.Background(this);
  257. delete ITEM.lastItem; delete ITEM.lastMenu;
  258. delete MENU.skipUp;
  259. SIGNAL.Post(["post",MENU.jax]);
  260. MENU.isRTL = (MathJax.Localization.fontDirection() === "rtl");
  261. }
  262. var menu = HTML.Element("div",{
  263. onmouseup: MENU.Mouseup, ondblclick: FALSE,
  264. ondragstart: FALSE, onselectstart: FALSE, oncontextmenu: FALSE,
  265. menuItem: this, className: "MathJax_Menu", onkeydown: MENU.Keydown,
  266. role: "menu"
  267. });
  268. if (event.type === "contextmenu" || event.type === "mouseover")
  269. menu.className += " MathJax_ContextMenu";
  270. if (!forceLTR) {MathJax.Localization.setCSS(menu)}
  271. for (var i = 0, m = this.items.length; i < m; i++) {this.items[i].Create(menu)}
  272. if (MENU.isMobile) {
  273. HTML.addElement(menu,"span",{
  274. className: "MathJax_MenuClose", menu: parent,
  275. ontouchstart: MENU.Close, ontouchend: FALSE, onmousedown: MENU.Close, onmouseup: FALSE
  276. },[["span",{},"\u00D7"]]);
  277. }
  278. div.appendChild(menu);
  279. this.posted = true;
  280. if (menu.offsetWidth) menu.style.width = (menu.offsetWidth+2) + "px";
  281. var x = event.pageX, y = event.pageY;
  282. var bbox = document.body.getBoundingClientRect();
  283. var styles = (window.getComputedStyle ? window.getComputedStyle(document.body) : {marginLeft: "0px"});
  284. var bodyRight = bbox.right - Math.min(0,bbox.left) + parseFloat(styles.marginLeft);
  285. if (!x && !y && "clientX" in event) {
  286. x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
  287. y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
  288. }
  289. if (!parent) {
  290. var node = MENU.CurrentNode() || event.target;
  291. if ((event.type === "keydown" || (!x && !y)) && node) {
  292. var offsetX = window.pageXOffset || document.documentElement.scrollLeft;
  293. var offsetY = window.pageYOffset || document.documentElement.scrollTop;
  294. var rect = node.getBoundingClientRect();
  295. x = (rect.right + rect.left) / 2 + offsetX;
  296. y = (rect.bottom + rect.top) / 2 + offsetY;
  297. }
  298. if (x + menu.offsetWidth > bodyRight - this.margin)
  299. {x = bodyRight - menu.offsetWidth - this.margin}
  300. if (MENU.isMobile) {x = Math.max(5,x-Math.floor(menu.offsetWidth/2)); y -= 20}
  301. MENU.skipUp = event.isContextMenu;
  302. } else {
  303. var side = "left", mw = parent.offsetWidth;
  304. x = (MENU.isMobile ? 30 : mw - 2); y = 0;
  305. while (parent && parent !== div) {
  306. x += parent.offsetLeft; y += parent.offsetTop;
  307. parent = parent.parentNode;
  308. }
  309. if (!MENU.isMobile) {
  310. if ((MENU.isRTL && x - mw - menu.offsetWidth > this.margin) ||
  311. (!MENU.isRTL && x + menu.offsetWidth > bodyRight - this.margin))
  312. {side = "right"; x = Math.max(this.margin,x - mw - menu.offsetWidth + 6)}
  313. }
  314. if (!isPC) {
  315. // in case these ever get implemented
  316. menu.style["borderRadiusTop"+side] = 0; // Opera 10.5
  317. menu.style["WebkitBorderRadiusTop"+side] = 0; // Safari and Chrome
  318. menu.style["MozBorderRadiusTop"+side] = 0; // Firefox
  319. menu.style["KhtmlBorderRadiusTop"+side] = 0; // Konqueror
  320. }
  321. }
  322. menu.style.left = x+"px"; menu.style.top = y+"px";
  323. if (document.selection && document.selection.empty) {document.selection.empty()}
  324. // Focusing while keeping the scroll position.
  325. var oldX = window.pageXOffset || document.documentElement.scrollLeft;
  326. var oldY = window.pageYOffset || document.documentElement.scrollTop;
  327. MENU.Focus(menu);
  328. if (event.type === "keydown") {
  329. MENU.skipMouseoverFromKey = true;
  330. setTimeout(function() {delete MENU.skipMouseoverFromKey;}, CONFIG.delay);
  331. }
  332. window.scrollTo(oldX, oldY);
  333. return FALSE(event);
  334. },
  335. /*
  336. * Remove the menu from the screen
  337. */
  338. Remove: function (event,menu) {
  339. SIGNAL.Post(["unpost",MENU.jax]);
  340. var div = document.getElementById("MathJax_MenuFrame");
  341. if (div) {
  342. div.parentNode.removeChild(div);
  343. if (this.msieFixedPositionBug) {detachEvent("onresize",MENU.Resize)}
  344. }
  345. if (MENU.jax.hover) {
  346. delete MENU.jax.hover.nofade;
  347. HOVER.UnHover(MENU.jax);
  348. }
  349. MENU.Unfocus(menu);
  350. if (event.type === "mousedown") MENU.CurrentNode().blur();
  351. return FALSE(event);
  352. },
  353. /*
  354. * Find an item in a menu (or submenu) by name (Find) or ID (FindID).
  355. * A list of names or IDs means descend into submenus.
  356. */
  357. Find: function (name) {return this.FindN(1,name,[].slice.call(arguments,1))},
  358. FindId: function (name) {return this.FindN(0,name,[].slice.call(arguments,1))},
  359. FindN: function (n,name,names) {
  360. for (var i = 0, m = this.items.length; i < m; i++) {
  361. if (this.items[i].name[n] === name) {
  362. if (names.length) {
  363. if (!this.items[i].submenu) {return null}
  364. return this.items[i].submenu.FindN(n,names[0],names.slice(1));
  365. }
  366. return this.items[i];
  367. }
  368. }
  369. return null;
  370. },
  371. /*
  372. * Find the index of a menu item (so we can insert before or after it)
  373. */
  374. IndexOf: function (name) {return this.IndexOfN(1,name)},
  375. IndexOfId: function (name) {return this.IndexOfN(0,name)},
  376. IndexOfN: function (n,name) {
  377. for (var i = 0, m = this.items.length; i < m; i++)
  378. {if (this.items[i].name[n] === name) {return i}}
  379. return null;
  380. },
  381. Right: function(event, menu) {
  382. MENU.Right(event, menu);
  383. },
  384. Left: function(event, menu) {
  385. MENU.Left(event, menu);
  386. },
  387. Up: function(event, menu) {
  388. var node = menu.lastChild;
  389. node.menuItem.Activate(event, node);
  390. },
  391. Down: function(event, menu) {
  392. var node = menu.firstChild;
  393. node.menuItem.Activate(event, node);
  394. },
  395. Space: function(event, menu) {
  396. this.Remove(event, menu);
  397. }
  398. },{
  399. config: CONFIG,
  400. Remove: function (event) {return MENU.Event(event,this,"Remove")},
  401. Mouseover: function (event) {return MENU.Event(event,this,"Mouseover")},
  402. Mouseout: function (event) {return MENU.Event(event,this,"Mouseout")},
  403. Mousedown: function (event) {return MENU.Event(event,this,"Mousedown")},
  404. Mouseup: function (event) {return MENU.Event(event,this,"Mouseup")},
  405. Keydown: function (event) {return MENU.Event(event,this,"Keydown")},
  406. /*
  407. * Events for mobile devices.
  408. */
  409. Touchstart: function (event) {return MENU.Event(event,this,"Touchstart")},
  410. Touchend: function (event) {return MENU.Event(event,this,"Touchend")},
  411. Close: function (event) {
  412. return MENU.Event(event,this.menu||this.parentNode,(this.menu?"Touchend":"Remove"));
  413. },
  414. Event: function (event,menu,type,force) {
  415. if (MENU.skipMouseover && type === "Mouseover" && !force) {return FALSE(event)}
  416. if (MENU.skipMouseoverFromKey && type === "Mouseover") {
  417. delete MENU.skipMouseoverFromKey;
  418. return FALSE(event);
  419. }
  420. if (MENU.skipUp) {
  421. if (type.match(/Mouseup|Touchend/)) {delete MENU.skipUp; return FALSE(event)}
  422. if (type === "Touchstart" ||
  423. (type === "Mousedown" && !MENU.skipMousedown)) {delete MENU.skipUp}
  424. }
  425. if (!event) {event = window.event}
  426. var item = menu.menuItem;
  427. if (item && item[type]) {return item[type](event,menu)}
  428. return null;
  429. },
  430. /*
  431. * Style for the background DIV
  432. */
  433. BGSTYLE: {
  434. position:"absolute", left:0, top:0, "z-index":200,
  435. width:"100%", height:"100%", border:0, padding:0, margin:0
  436. },
  437. Background: function (menu) {
  438. var div = HTML.addElement(document.body,"div",
  439. {style:this.BGSTYLE, id:"MathJax_MenuFrame"},
  440. [["div",{style: this.BGSTYLE, menuItem: menu, onmousedown: this.Remove}]]);
  441. var bg = div.firstChild;
  442. if (MENU.msieBackgroundBug) {
  443. // MSIE doesn't allow transparent background to be hit boxes, so
  444. // fake it using opacity with solid background color
  445. bg.style.backgroundColor = "white"; bg.style.filter = "alpha(opacity=0)";
  446. }
  447. if (MENU.msieFixedPositionBug) {
  448. // MSIE can't do fixed position, so use a full-sized background
  449. // and an onresize handler to update it (stupid, but necessary)
  450. div.width = div.height = 0; this.Resize();
  451. attachEvent("onresize",this.Resize);
  452. } else {
  453. // otherwise, use a fixed position DIV to cover the viewport
  454. bg.style.position = "fixed";
  455. }
  456. return div;
  457. },
  458. Resize: function () {setTimeout(MENU.SetWH,0)},
  459. SetWH: function () {
  460. var bg = document.getElementById("MathJax_MenuFrame");
  461. if (bg) {
  462. bg = bg.firstChild;
  463. bg.style.width = bg.style.height = "1px"; // so scrollWidth/Height will be right below
  464. bg.style.width = document.body.scrollWidth + "px";
  465. bg.style.height = document.body.scrollHeight + "px";
  466. }
  467. },
  468. /*************************************************************/
  469. /*
  470. * Keyboard navigation of menu.
  471. */
  472. posted: false, // Is a menu open?
  473. active: null, // The focused in HTML node in the menu.
  474. GetNode: function(jax) {
  475. var node = document.getElementById(jax.inputID + "-Frame");
  476. return node.isMathJax ? node : node.firstChild;
  477. },
  478. CurrentNode: function() {
  479. return MENU.GetNode(MENU.jax);
  480. },
  481. AllNodes: function() {
  482. var jaxs = MathJax.Hub.getAllJax();
  483. var nodes = [];
  484. for (var i = 0, jax; jax = jaxs[i]; i++) {
  485. nodes.push(MENU.GetNode(jax));
  486. }
  487. return nodes;
  488. },
  489. ActiveNode: function() {
  490. return MENU.active;
  491. },
  492. FocusNode: function(node) {
  493. MENU.active = node;
  494. node.focus();
  495. },
  496. //
  497. // Focus is a global affair, since we only ever want a single focused item.
  498. //
  499. Focus: function(menu) {
  500. !MENU.posted ? MENU.Activate(menu) : MENU.ActiveNode().tabIndex = -1;
  501. menu.tabIndex = 0;
  502. MENU.FocusNode(menu);
  503. },
  504. Activate: function(event, menu) {
  505. MENU.UnsetTabIndex();
  506. MENU.posted = true;
  507. },
  508. Unfocus: function() {
  509. MENU.ActiveNode().tabIndex = -1;
  510. MENU.SetTabIndex();
  511. MENU.FocusNode(MENU.CurrentNode());
  512. MENU.posted = false;
  513. },
  514. MoveHorizontal: function(event, menu, move) {
  515. if (!event.shiftKey) return;
  516. var jaxs = MENU.AllNodes();
  517. var len = jaxs.length;
  518. if (len === 0) return;
  519. var next = jaxs[MENU.Mod(move(MENU.IndexOf(jaxs, MENU.CurrentNode())), len)];
  520. if (next === MENU.CurrentNode()) return;
  521. MENU.menu.Remove(event, menu);
  522. MENU.jax = MathJax.Hub.getJaxFor(next);
  523. MENU.FocusNode(next);
  524. MENU.menu.Post(null);
  525. },
  526. Right: function(event, menu) {
  527. MENU.MoveHorizontal(event, menu, function(x) {return x + 1;});
  528. },
  529. Left: function(event, menu) {
  530. MENU.MoveHorizontal(event, menu, function(x) {return x - 1;});
  531. },
  532. UnsetTabIndex: function () {
  533. var jaxs = MENU.AllNodes();
  534. for (var j = 0, jax; jax = jaxs[j]; j++) {
  535. if (jax.tabIndex > 0) {
  536. jax.oldTabIndex = jax.tabIndex;
  537. }
  538. jax.tabIndex = -1;
  539. }
  540. },
  541. SetTabIndex: function () {
  542. var jaxs = MENU.AllNodes();
  543. for (var j = 0, jax; jax = jaxs[j]; j++) {
  544. if (jax.oldTabIndex !== undefined) {
  545. jax.tabIndex = jax.oldTabIndex
  546. delete jax.oldTabIndex;
  547. } else {
  548. jax.tabIndex = HUB.getTabOrder(jax);
  549. }
  550. }
  551. },
  552. //TODO: Move to utility class.
  553. // Computes a mod n.
  554. Mod: function(a, n) {
  555. return ((a % n) + n) % n;
  556. },
  557. IndexOf: (Array.prototype.indexOf ?
  558. function (A, item, start) {return A.indexOf(item, start);} :
  559. function (A, item, start) {
  560. for (var i = (start || 0), j = A.length; i < j; i++) {
  561. if (item === A[i]) return i;
  562. }
  563. return -1;
  564. }),
  565. saveCookie: function () {HTML.Cookie.Set("menu",this.cookie)},
  566. getCookie: function () {this.cookie = HTML.Cookie.Get("menu")}
  567. });
  568. MathJax.Menu.NAV = NAV;
  569. /*************************************************************/
  570. /*
  571. * Abstract class of menu items.
  572. */
  573. var ITEM = MENU.ITEM = NAV.Subclass({
  574. name: "", // The menu item's label as [id,label] pair.
  575. node: null, // The HTML node of the item.
  576. menu: null, // The parent menu containing that item. HTML node.
  577. Attributes: function(def) {
  578. return HUB.Insert(
  579. {onmouseup: MENU.Mouseup,
  580. ondragstart: FALSE, onselectstart: FALSE, onselectend: FALSE,
  581. ontouchstart: MENU.Touchstart, ontouchend: MENU.Touchend,
  582. className: "MathJax_MenuItem", role: this.role,
  583. menuItem: this},
  584. def);
  585. },
  586. Create: function (menu) {
  587. if (!this.hidden) {
  588. var def = this.Attributes();
  589. var label = this.Label(def,menu);
  590. HTML.addElement(menu, "div", def, label);
  591. }
  592. },
  593. Name: function () {return _(this.name[0],this.name[1])},
  594. Mouseover: function (event,menu) {
  595. if (menu.parentNode === MENU.ActiveNode().parentNode) {
  596. this.Deactivate(MENU.ActiveNode());
  597. }
  598. this.Activate(event, menu);
  599. },
  600. Mouseout: function (event,menu) {
  601. this.Deactivate(menu);
  602. },
  603. Mouseup: function (event,menu) {return this.Remove(event,menu)},
  604. DeactivateSubmenus: function(menu) {
  605. var menus = document.getElementById("MathJax_MenuFrame").childNodes,
  606. items = ITEM.GetMenuNode(menu).childNodes;
  607. for (var i = 0, m = items.length; i < m; i++) {
  608. var item = items[i].menuItem;
  609. // Deactivates submenu items.
  610. if (item && item.submenu && item.submenu.posted &&
  611. item !== menu.menuItem) {
  612. item.Deactivate(items[i]);
  613. }
  614. }
  615. this.RemoveSubmenus(menu, menus);
  616. },
  617. RemoveSubmenus: function(menu, menus) {
  618. menus = menus || document.getElementById("MathJax_MenuFrame").childNodes;
  619. var m = menus.length-1;
  620. while (m >= 0 && ITEM.GetMenuNode(menu).menuItem !== menus[m].menuItem) {
  621. menus[m].menuItem.posted = false;
  622. menus[m].parentNode.removeChild(menus[m]);
  623. m--;
  624. }
  625. },
  626. Touchstart: function (event,menu) {return this.TouchEvent(event,menu,"Mousedown")},
  627. Touchend: function (event,menu) {return this.TouchEvent(event,menu,"Mouseup")},
  628. TouchEvent: function (event,menu,type) {
  629. if (this !== ITEM.lastItem) {
  630. if (ITEM.lastMenu) {MENU.Event(event,ITEM.lastMenu,"Mouseout")}
  631. MENU.Event(event,menu,"Mouseover",true);
  632. ITEM.lastItem = this; ITEM.lastMenu = menu;
  633. }
  634. if (this.nativeTouch) {return null}
  635. MENU.Event(event,menu,type);
  636. return false;
  637. },
  638. Remove: function (event,menu) {
  639. menu = menu.parentNode.menuItem;
  640. return menu.Remove(event,menu);
  641. },
  642. With: function (def) {if (def) {HUB.Insert(this,def)}; return this},
  643. isRTL: function () {return MENU.isRTL},
  644. rtlClass: function () {return (this.isRTL() ? " RTL" : "")}
  645. }, {
  646. GetMenuNode: function(item) {
  647. return item.parentNode;
  648. }
  649. });
  650. /*************************************************************/
  651. /*
  652. * Abstract class of menu items that are focusable and perform some action
  653. */
  654. MENU.ENTRY = MENU.ITEM.Subclass({
  655. role: "menuitem", // Aria role.
  656. Attributes: function(def) {
  657. def = HUB.Insert(
  658. {onmouseover: MENU.Mouseover, onmouseout: MENU.Mouseout,
  659. onmousedown: MENU.Mousedown, onkeydown: MENU.Keydown,
  660. "aria-disabled": !!this.disabled},
  661. def);
  662. def = this.SUPER(arguments).Attributes.call(this, def);
  663. if (this.disabled) {
  664. def.className += " MathJax_MenuDisabled";
  665. }
  666. return def;
  667. },
  668. MoveVertical: function(event, item, move) {
  669. var menuNode = ITEM.GetMenuNode(item);
  670. var items = [];
  671. for (var i = 0, allItems = menuNode.menuItem.items, it;
  672. it = allItems[i]; i++) {
  673. if (!it.hidden) {
  674. items.push(it);
  675. }
  676. }
  677. var index = MENU.IndexOf(items, this);
  678. if (index === -1) return;
  679. var len = items.length;
  680. var children = menuNode.childNodes;
  681. do {
  682. index = MENU.Mod(move(index), len);
  683. } while (items[index].hidden || !children[index].role ||
  684. children[index].role === "separator");
  685. this.Deactivate(item);
  686. items[index].Activate(event, children[index]);
  687. },
  688. Up: function(event, item) {
  689. this.MoveVertical(event, item, function(x) { return x - 1; });
  690. },
  691. Down: function(event, item) {
  692. this.MoveVertical(event, item, function(x) { return x + 1; });
  693. },
  694. Right: function(event, item) {
  695. this.MoveHorizontal(event, item, MENU.Right, !this.isRTL());
  696. },
  697. Left: function(event, item) {
  698. this.MoveHorizontal(event, item, MENU.Left, this.isRTL());
  699. },
  700. MoveHorizontal: function(event, item, move, rtl) {
  701. var menuNode = ITEM.GetMenuNode(item);
  702. if (menuNode.menuItem === MENU.menu && event.shiftKey) {
  703. move(event, item);
  704. }
  705. if (rtl) return;
  706. if (menuNode.menuItem !== MENU.menu) {
  707. this.Deactivate(item);
  708. }
  709. var parentNodes = menuNode.previousSibling.childNodes;
  710. var length = parentNodes.length;
  711. while (length--) {
  712. var parent = parentNodes[length];
  713. if (parent.menuItem.submenu &&
  714. parent.menuItem.submenu === menuNode.menuItem) {
  715. MENU.Focus(parent);
  716. break;
  717. }
  718. }
  719. this.RemoveSubmenus(item);
  720. },
  721. Space: function (event, menu) {
  722. this.Mouseup(event, menu);
  723. },
  724. Activate: function (event, menu) {
  725. this.Deactivate(menu);
  726. if (!this.disabled) {
  727. menu.className += " MathJax_MenuActive";
  728. }
  729. this.DeactivateSubmenus(menu);
  730. MENU.Focus(menu);
  731. },
  732. Deactivate: function (menu) {
  733. menu.className = menu.className.replace(/ MathJax_MenuActive/,"");
  734. }
  735. });
  736. /*************************************************************/
  737. /*
  738. * A menu item that performs a command when selected
  739. */
  740. MENU.ITEM.COMMAND = MENU.ENTRY.Subclass({
  741. action: function () {},
  742. Init: function (name,action,def) {
  743. if (!isArray(name)) {name = [name,name]} // make [id,label] pair
  744. this.name = name; this.action = action;
  745. this.With(def);
  746. },
  747. Label: function (def,menu) {return [this.Name()]},
  748. Mouseup: function (event,menu) {
  749. if (!this.disabled) {
  750. this.Remove(event,menu);
  751. SIGNAL.Post(["command",this]);
  752. this.action.call(this,event);
  753. }
  754. return FALSE(event);
  755. }
  756. });
  757. /*************************************************************/
  758. /*
  759. * A menu item that posts a submenu
  760. */
  761. MENU.ITEM.SUBMENU = MENU.ENTRY.Subclass({
  762. submenu: null, // the submenu
  763. marker: "\u25BA", // the submenu arrow
  764. markerRTL: "\u25C4", // the submenu arrow for RTL
  765. Attributes: function(def) {
  766. def = HUB.Insert({"aria-haspopup": "true"}, def);
  767. def = this.SUPER(arguments).Attributes.call(this, def);
  768. return def;
  769. },
  770. Init: function (name,def) {
  771. if (!isArray(name)) {name = [name,name]} // make [id,label] pair
  772. this.name = name; var i = 1;
  773. if (!(def instanceof MENU.ITEM)) {this.With(def), i++}
  774. this.submenu = MENU.apply(MENU,[].slice.call(arguments,i));
  775. },
  776. Label: function (def,menu) {
  777. this.submenu.posted = false;
  778. return [this.Name()+" ",["span",{
  779. className:"MathJax_MenuArrow" + this.rtlClass()
  780. },[this.isRTL() ? this.markerRTL : this.marker]]];
  781. },
  782. Timer: function (event,menu) {
  783. this.ClearTimer();
  784. event = {type: event.type,
  785. clientX: event.clientX, clientY: event.clientY}; // MSIE can't pass the event below
  786. this.timer = setTimeout(CALLBACK(["Mouseup",this,event,menu]),CONFIG.delay);
  787. },
  788. ClearTimer: function() {
  789. if (this.timer) {
  790. clearTimeout(this.timer);
  791. }
  792. },
  793. Touchend: function (event,menu) {
  794. var forceout = this.submenu.posted;
  795. var result = this.SUPER(arguments).Touchend.apply(this,arguments);
  796. if (forceout) {this.Deactivate(menu); delete ITEM.lastItem; delete ITEM.lastMenu}
  797. return result;
  798. },
  799. Mouseout: function(event, menu) {
  800. if (!this.submenu.posted) {
  801. this.Deactivate(menu);
  802. }
  803. this.ClearTimer();
  804. },
  805. Mouseover: function(event, menu) {
  806. this.Activate(event, menu);
  807. },
  808. Mouseup: function (event,menu) {
  809. if (!this.disabled) {
  810. if (!this.submenu.posted) {
  811. this.ClearTimer();
  812. this.submenu.Post(event, menu, this.ltr);
  813. MENU.Focus(menu);
  814. } else {
  815. this.DeactivateSubmenus(menu);
  816. }
  817. }
  818. return FALSE(event);
  819. },
  820. Activate: function (event, menu) {
  821. if (!this.disabled) {
  822. this.Deactivate(menu);
  823. menu.className += " MathJax_MenuActive";
  824. }
  825. if (!this.submenu.posted) {
  826. this.DeactivateSubmenus(menu);
  827. if (!MENU.isMobile) {
  828. this.Timer(event,menu);
  829. }
  830. }
  831. MENU.Focus(menu);
  832. },
  833. MoveVertical: function(event, item, move) {
  834. this.ClearTimer();
  835. this.SUPER(arguments).MoveVertical.apply(this, arguments);
  836. },
  837. MoveHorizontal: function(event, menu, move, rtl) {
  838. if (!rtl) {
  839. this.SUPER(arguments).MoveHorizontal.apply(this, arguments);
  840. return;
  841. }
  842. if (this.disabled) return;
  843. if (!this.submenu.posted) {
  844. this.Activate(event, menu);
  845. return;
  846. }
  847. var submenuNodes = ITEM.GetMenuNode(menu).nextSibling.childNodes;
  848. if (submenuNodes.length > 0) {
  849. this.submenu.items[0].Activate(event, submenuNodes[0]);
  850. }
  851. }
  852. });
  853. /*************************************************************/
  854. /*
  855. * A menu item that is one of several radio buttons
  856. */
  857. MENU.ITEM.RADIO = MENU.ENTRY.Subclass({
  858. variable: null, // the variable name
  859. marker: (isPC ? "\u25CF" : "\u2713"), // the checkmark
  860. role: "menuitemradio",
  861. Attributes: function(def) {
  862. var checked = CONFIG.settings[this.variable] === this.value ? "true" : "false";
  863. def = HUB.Insert({"aria-checked": checked}, def);
  864. def = this.SUPER(arguments).Attributes.call(this, def);
  865. return def;
  866. },
  867. Init: function (name,variable,def) {
  868. if (!isArray(name)) {name = [name,name]} // make [id,label] pair
  869. this.name = name; this.variable = variable; this.With(def);
  870. if (this.value == null) {this.value = this.name[0]}
  871. },
  872. Label: function (def,menu) {
  873. var span = {className:"MathJax_MenuRadioCheck" + this.rtlClass()};
  874. if (CONFIG.settings[this.variable] !== this.value) {
  875. span = {style:{display:"none"}};
  876. }
  877. return [["span",span,[this.marker]]," "+this.Name()];
  878. },
  879. Mouseup: function (event,menu) {
  880. if (!this.disabled) {
  881. var child = menu.parentNode.childNodes;
  882. for (var i = 0, m = child.length; i < m; i++) {
  883. var item = child[i].menuItem;
  884. if (item && item.variable === this.variable) {
  885. child[i].firstChild.style.display = "none";
  886. }
  887. }
  888. menu.firstChild.display = "";
  889. CONFIG.settings[this.variable] = this.value;
  890. MENU.cookie[this.variable] = CONFIG.settings[this.variable]; MENU.saveCookie();
  891. SIGNAL.Post(["radio button",this]);
  892. }
  893. this.Remove(event,menu);
  894. if (this.action && !this.disabled) {this.action.call(MENU,this)}
  895. return FALSE(event);
  896. }
  897. });
  898. /*************************************************************/
  899. /*
  900. * A menu item that is checkable
  901. */
  902. MENU.ITEM.CHECKBOX = MENU.ENTRY.Subclass({
  903. variable: null, // the variable name
  904. marker: "\u2713", // the checkmark
  905. role: "menuitemcheckbox",
  906. Attributes: function(def) {
  907. var checked = CONFIG.settings[this.variable] ? "true" : "false";
  908. def = HUB.Insert({"aria-checked": checked}, def);
  909. def = this.SUPER(arguments).Attributes.call(this, def);
  910. return def;
  911. },
  912. Init: function (name,variable,def) {
  913. if (!isArray(name)) {name = [name,name]} // make [id,label] pair
  914. this.name = name; this.variable = variable; this.With(def);
  915. },
  916. Label: function (def,menu) {
  917. var span = {className:"MathJax_MenuCheck" + this.rtlClass()};
  918. if (!CONFIG.settings[this.variable]) {span = {style:{display:"none"}}}
  919. return [["span",span,[this.marker]]," "+this.Name()];
  920. },
  921. Mouseup: function (event,menu) {
  922. if (!this.disabled) {
  923. menu.firstChild.display = (CONFIG.settings[this.variable] ? "none" : "");
  924. CONFIG.settings[this.variable] = !CONFIG.settings[this.variable];
  925. MENU.cookie[this.variable] = CONFIG.settings[this.variable]; MENU.saveCookie();
  926. SIGNAL.Post(["checkbox",this]);
  927. }
  928. this.Remove(event,menu);
  929. if (this.action && !this.disabled) {this.action.call(MENU,this)}
  930. return FALSE(event);
  931. }
  932. });
  933. /*************************************************************/
  934. /*
  935. * A menu item that is a label
  936. */
  937. MENU.ITEM.LABEL = MENU.ENTRY.Subclass({
  938. role: "menuitem", // Aria role.
  939. Init: function (name,def) {
  940. if (!isArray(name)) {name = [name,name]} // make [id,label] pair
  941. this.name = name; this.With(def);
  942. },
  943. Label: function (def,menu) {
  944. def.className += " MathJax_MenuLabel";
  945. return [this.Name()];
  946. },
  947. Activate: function(event, menu) {
  948. this.Deactivate(menu);
  949. MENU.Focus(menu);
  950. },
  951. Mouseup: function (event,menu) { }
  952. });
  953. /*************************************************************/
  954. /*
  955. * A rule in a menu
  956. */
  957. MENU.ITEM.RULE = MENU.ITEM.Subclass({
  958. role: "separator",
  959. Attributes: function(def) {
  960. def = HUB.Insert({"aria-orientation": "vertical"}, def);
  961. def = this.SUPER(arguments).Attributes.call(this, def);
  962. return def;
  963. },
  964. Label: function (def,menu) {
  965. def.className += " MathJax_MenuRule";
  966. return null;
  967. }
  968. });
  969. /*************************************************************/
  970. /*************************************************************/
  971. /*
  972. * Handle the ABOUT box
  973. */
  974. MENU.About = function (event) {
  975. var font = MENU.About.GetFont();
  976. var format = MENU.About.GetFormat();
  977. var jax = ["MathJax.js v"+MathJax.fileversion,["br"]];
  978. jax.push(["div",{style:{"border-top":"groove 2px",margin:".25em 0"}}]);
  979. MENU.About.GetJax(jax,MathJax.InputJax,["InputJax","%1 Input Jax v%2"]);
  980. MENU.About.GetJax(jax,MathJax.OutputJax,["OutputJax","%1 Output Jax v%2"]);
  981. MENU.About.GetJax(jax,MathJax.ElementJax,["ElementJax","%1 Element Jax v%2"]);
  982. jax.push(["div",{style:{"border-top":"groove 2px",margin:".25em 0"}}]);
  983. MENU.About.GetJax(jax,MathJax.Extension,["Extension","%1 Extension v%2"],true);
  984. jax.push(["div",{style:{"border-top":"groove 2px",margin:".25em 0"}}],["center",{},[
  985. HUB.Browser + " v"+HUB.Browser.version + (format ?
  986. " \u2014 " + _(format.replace(/ /g,""),format) : "")
  987. ]]);
  988. MENU.About.div = MENU.Background(MENU.About);
  989. var about = HTML.addElement(MENU.About.div,"div",{
  990. id: "MathJax_About", tabIndex: 0, onkeydown: MENU.About.Keydown
  991. },[
  992. ["b",{style:{fontSize:"120%"}},["MathJax"]]," v"+MathJax.version,["br"],
  993. _(font.replace(/ /g,""),"using "+font),["br"],["br"],
  994. ["span",{style:{
  995. display:"inline-block", "text-align":"left", "font-size":"80%",
  996. "max-height":"20em", overflow:"auto",
  997. "background-color":"#E4E4E4", padding:".4em .6em", border:"1px inset"
  998. }, tabIndex: 0},jax],["br"],["br"],
  999. ["a",{href:"http://www.mathjax.org/"},["www.mathjax.org"]],
  1000. ["span",{className:"MathJax_MenuClose",id:"MathJax_AboutClose",
  1001. onclick:MENU.About.Remove,
  1002. onkeydown: MENU.About.Keydown, tabIndex: 0, role: "button",
  1003. "aria-label": _("CloseAboutDialog","Close about MathJax dialog")},
  1004. [["span",{},"\u00D7"]]]
  1005. ]);
  1006. if (event.type === "mouseup") about.className += " MathJax_MousePost";
  1007. about.focus();
  1008. MathJax.Localization.setCSS(about);
  1009. var doc = (document.documentElement||{});
  1010. var H = window.innerHeight || doc.clientHeight || doc.scrollHeight || 0;
  1011. if (MENU.prototype.msieAboutBug) {
  1012. about.style.width = "20em"; about.style.position = "absolute";
  1013. about.style.left = Math.floor((document.documentElement.scrollWidth - about.offsetWidth)/2)+"px";
  1014. about.style.top = (Math.floor((H-about.offsetHeight)/3)+document.body.scrollTop)+"px";
  1015. } else {
  1016. about.style.marginLeft = Math.floor(-about.offsetWidth/2)+"px";
  1017. about.style.top = Math.floor((H-about.offsetHeight)/3)+"px";
  1018. }
  1019. };
  1020. MENU.About.Remove = function (event) {
  1021. if (MENU.About.div) {document.body.removeChild(MENU.About.div); delete MENU.About.div}
  1022. };
  1023. MENU.About.Keydown = function(event) {
  1024. if (event.keyCode === KEY.ESCAPE ||
  1025. (this.id === "MathJax_AboutClose" &&
  1026. (event.keyCode === KEY.SPACE || event.keyCode === KEY.RETURN))) {
  1027. MENU.About.Remove(event);
  1028. MENU.CurrentNode().focus();
  1029. FALSE(event);
  1030. }
  1031. },
  1032. MENU.About.GetJax = function (jax,JAX,type,noTypeCheck) {
  1033. var info = [];
  1034. for (var id in JAX) {if (JAX.hasOwnProperty(id) && JAX[id]) {
  1035. if ((noTypeCheck && JAX[id].version) || (JAX[id].isa && JAX[id].isa(JAX)))
  1036. {info.push(_(type[0],type[1],(JAX[id].id||id),JAX[id].version))}
  1037. }}
  1038. info.sort();
  1039. for (var i = 0, m = info.length; i < m; i++) {jax.push(info[i],["br"])}
  1040. return jax;
  1041. };
  1042. MENU.About.GetFont = function () {
  1043. var jax = MathJax.Hub.outputJax["jax/mml"][0] || {};
  1044. var font = {
  1045. SVG: "web SVG",
  1046. CommonHTML: "web TeX",
  1047. "HTML-CSS": (jax.imgFonts ? "image" : (jax.webFonts ? "web" : "local")+" "+jax.fontInUse)
  1048. }[jax.id] || "generic";
  1049. return font + " fonts";
  1050. };
  1051. MENU.About.GetFormat = function () {
  1052. var jax = MathJax.Hub.outputJax["jax/mml"][0] || {};
  1053. if (jax.id !== "HTML-CSS"|| !jax.webFonts || jax.imgFonts) return;
  1054. return jax.allowWebFonts.replace(/otf/,"woff or otf") + " fonts";
  1055. };
  1056. /*
  1057. * Handle the MathJax HELP menu
  1058. */
  1059. MENU.Help = function (event) {
  1060. AJAX.Require("[MathJax]/extensions/HelpDialog.js",
  1061. function () {MathJax.Extension.Help.Dialog({type:event.type})});
  1062. };
  1063. /*
  1064. * Handle showing of element's source
  1065. */
  1066. MENU.ShowSource = function (event) {
  1067. if (!event) {event = window.event}
  1068. var EVENT = {screenX:event.screenX, screenY:event.screenY};
  1069. if (!MENU.jax) return;
  1070. if (this.format === "MathML") {
  1071. var MML = MathJax.ElementJax.mml;
  1072. if (MML && typeof(MML.mbase.prototype.toMathML) !== "undefined") {
  1073. // toMathML() can call MathJax.Hub.RestartAfter, so trap errors and check
  1074. try {MENU.ShowSource.Text(MENU.jax.root.toMathML("",MENU.jax),event)} catch (err) {
  1075. if (!err.restart) {throw err}
  1076. CALLBACK.After([this,MENU.ShowSource,EVENT],err.restart);
  1077. }
  1078. } else if (!AJAX.loadingToMathML) {
  1079. AJAX.loadingToMathML = true;
  1080. MENU.ShowSource.Window(event); // WeBKit needs to open window on click event
  1081. CALLBACK.Queue(
  1082. AJAX.Require("[MathJax]/extensions/toMathML.js"),
  1083. function () {
  1084. delete AJAX.loadingToMathML;
  1085. if (!MML.mbase.prototype.toMathML) {MML.mbase.prototype.toMathML = function () {}}
  1086. },
  1087. [this,MENU.ShowSource,EVENT] // call this function again
  1088. );
  1089. return;
  1090. }
  1091. } else if (this.format === "Error") {
  1092. MENU.ShowSource.Text(MENU.jax.errorText,event);
  1093. } else if (CONFIG.semanticsAnnotations[this.format]) {
  1094. var annotation = MENU.jax.root.getAnnotation(this.format);
  1095. if (annotation.data[0]) MENU.ShowSource.Text(annotation.data[0].toString());
  1096. } else {
  1097. if (MENU.jax.originalText == null) {
  1098. alert(_("NoOriginalForm","No original form available"));
  1099. return;
  1100. }
  1101. MENU.ShowSource.Text(MENU.jax.originalText,event);
  1102. }
  1103. };
  1104. MENU.ShowSource.Window = function (event) {
  1105. if (!MENU.ShowSource.w) {
  1106. var def = [], DEF = CONFIG.windowSettings;
  1107. for (var id in DEF) {if (DEF.hasOwnProperty(id)) {def.push(id+"="+DEF[id])}}
  1108. MENU.ShowSource.w = window.open("","_blank",def.join(","));
  1109. }
  1110. return MENU.ShowSource.w;
  1111. };
  1112. MENU.ShowSource.Text = function (text,event) {
  1113. var w = MENU.ShowSource.Window(event); delete MENU.ShowSource.w;
  1114. text = text.replace(/^\s*/,"").replace(/\s*$/,"");
  1115. text = text.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
  1116. var title = _("EqSource","MathJax Equation Source");
  1117. if (MENU.isMobile) {
  1118. w.document.open();
  1119. w.document.write("<html><head><meta name='viewport' content='width=device-width, initial-scale=1.0' /><title>"+title+"</title></head><body style='font-size:85%'>");
  1120. w.document.write("<pre>"+text+"</pre>");
  1121. w.document.write("<hr><input type='button' value='"+_("Close","Close")+"' onclick='window.close()' />");
  1122. w.document.write("</body></html>");
  1123. w.document.close();
  1124. } else {
  1125. w.document.open();
  1126. w.document.write("<html><head><title>"+title+"</title></head><body style='font-size:85%'>");
  1127. w.document.write("<table><tr><td><pre>"+text+"</pre></td></tr></table>");
  1128. w.document.write("</body></html>");
  1129. w.document.close();
  1130. var table = w.document.body.firstChild;
  1131. setTimeout(function () {
  1132. var H = (w.outerHeight-w.innerHeight)||30, W = (w.outerWidth-w.innerWidth)||30, x, y;
  1133. W = Math.max(140,Math.min(Math.floor(.5*screen.width),table.offsetWidth+W+25));
  1134. H = Math.max(40,Math.min(Math.floor(.5*screen.height),table.offsetHeight+H+25));
  1135. if (MENU.prototype.msieHeightBug) {H += 35}; // for title bar in XP
  1136. w.resizeTo(W,H);
  1137. var X; try {X = event.screenX} catch (e) {}; // IE8 throws an error accessing screenX
  1138. if (event && X != null) {
  1139. x = Math.max(0,Math.min(event.screenX-Math.floor(W/2), screen.width-W-20));
  1140. y = Math.max(0,Math.min(event.screenY-Math.floor(H/2), screen.height-H-20));
  1141. w.moveTo(x,y);
  1142. }
  1143. },50);
  1144. }
  1145. };
  1146. /*
  1147. * Handle rescaling all the math
  1148. */
  1149. MENU.Scale = function () {
  1150. var JAX = ["CommonHTML","HTML-CSS","SVG","NativeMML","PreviewHTML"], m = JAX.length,
  1151. SCALE = 100, i, jax;
  1152. for (i = 0; i < m; i++) {
  1153. jax = OUTPUT[JAX[i]];
  1154. if (jax) {SCALE = jax.config.scale; break}
  1155. }
  1156. var scale = prompt(_("ScaleMath","Scale all mathematics (compared to surrounding text) by"),SCALE+"%");
  1157. if (scale) {
  1158. if (scale.match(/^\s*\d+(\.\d*)?\s*%?\s*$/)) {
  1159. scale = parseFloat(scale);
  1160. if (scale) {
  1161. if (scale !== SCALE) {
  1162. for (i = 0; i < m; i++) {
  1163. jax = OUTPUT[JAX[i]];
  1164. if (jax) jax.config.scale = scale;
  1165. }
  1166. MENU.cookie.scale = HUB.config.scale = scale;
  1167. MENU.saveCookie();
  1168. HUB.Queue(["Rerender",HUB]);
  1169. }
  1170. } else {alert(_("NonZeroScale","The scale should not be zero"))}
  1171. } else {alert(_("PercentScale",
  1172. "The scale should be a percentage (e.g., 120%%)"))}
  1173. }
  1174. };
  1175. /*
  1176. * Handle loading the zoom code
  1177. */
  1178. MENU.Zoom = function () {
  1179. if (!MathJax.Extension.MathZoom) {AJAX.Require("[MathJax]/extensions/MathZoom.js")}
  1180. };
  1181. /*
  1182. * Handle changing the renderer
  1183. */
  1184. MENU.Renderer = function () {
  1185. var jax = HUB.outputJax["jax/mml"];
  1186. if (jax[0] !== CONFIG.settings.renderer) {
  1187. var BROWSER = HUB.Browser, message, MESSAGE = MENU.Renderer.Messages, warned;
  1188. //
  1189. // Check that the new renderer is appropriate for the browser
  1190. //
  1191. switch (CONFIG.settings.renderer) {
  1192. case "NativeMML":
  1193. if (!CONFIG.settings.warnedMML) {
  1194. if (BROWSER.isChrome && BROWSER.version.substr(0,3) !== "24.") {message = MESSAGE.MML.WebKit}
  1195. else if (BROWSER.isSafari && !BROWSER.versionAtLeast("5.0")) {message = MESSAGE.MML.WebKit}
  1196. else if (BROWSER.isMSIE) {if (!BROWSER.hasMathPlayer) {message = MESSAGE.MML.MSIE}}
  1197. else if (BROWSER.isEdge) {message = MESSAGE.MML.WebKit}
  1198. else {message = MESSAGE.MML[BROWSER]}
  1199. warned = "warnedMML";
  1200. }
  1201. break;
  1202. case "SVG":
  1203. if (!CONFIG.settings.warnedSVG) {
  1204. if (BROWSER.isMSIE && !isIE9) {message = MESSAGE.SVG.MSIE}
  1205. }
  1206. break;
  1207. }
  1208. if (message) {
  1209. message = _(message[0],message[1]);
  1210. message += "\n\n";
  1211. message += _("SwitchAnyway",
  1212. "Switch the renderer anyway?\n\n" +
  1213. "(Press OK to switch, CANCEL to continue with the current renderer)");
  1214. MENU.cookie.renderer = jax[0].id; MENU.saveCookie();
  1215. if (!confirm(message)) {
  1216. MENU.cookie.renderer = CONFIG.settings.renderer = HTML.Cookie.Get("menu").renderer;
  1217. MENU.saveCookie();
  1218. return;
  1219. }
  1220. if (warned) {MENU.cookie.warned = CONFIG.settings.warned = true}
  1221. MENU.cookie.renderer = CONFIG.settings.renderer; MENU.saveCookie();
  1222. }
  1223. HUB.Queue(
  1224. ["setRenderer",HUB,CONFIG.settings.renderer,"jax/mml"],
  1225. ["Rerender",HUB]
  1226. );
  1227. }
  1228. };
  1229. MENU.Renderer.Messages = {
  1230. MML: {
  1231. WebKit: ["WebkitNativeMMLWarning",
  1232. "Your browser doesn't seem to support MathML natively, " +
  1233. "so switching to MathML output may cause the mathematics " +
  1234. "on the page to become unreadable."],
  1235. MSIE: ["MSIENativeMMLWarning",
  1236. "Internet Explorer requires the MathPlayer plugin " +
  1237. "in order to process MathML output."],
  1238. Opera: ["OperaNativeMMLWarning",
  1239. "Opera's support for MathML is limited, so switching to " +
  1240. "MathML output may cause some expressions to render poorly."],
  1241. Safari: ["SafariNativeMMLWarning",
  1242. "Your browser's native MathML does not implement all the features " +
  1243. "used by MathJax, so some expressions may not render properly."],
  1244. Firefox: ["FirefoxNativeMMLWarning",
  1245. "Your browser's native MathML does not implement all the features " +
  1246. "used by MathJax, so some expressions may not render properly."]
  1247. },
  1248. SVG: {
  1249. MSIE: ["MSIESVGWarning",
  1250. "SVG is not implemented in Internet Explorer prior to " +
  1251. "IE9 or when it is emulating IE8 or below. " +
  1252. "Switching to SVG output will cause the mathematics to " +
  1253. "not display properly."]
  1254. }
  1255. };
  1256. /*
  1257. * Toggle assistive MML settings
  1258. */
  1259. MENU.AssistiveMML = function (item,restart) {
  1260. var AMML = MathJax.Extension.AssistiveMML;
  1261. if (!AMML) {
  1262. // Try to load the extension, but only try once.
  1263. if (!restart)
  1264. AJAX.Require("[MathJax]/extensions/AssistiveMML.js",["AssistiveMML",MENU,item,true]);
  1265. return;
  1266. }
  1267. MathJax.Hub.Queue([(CONFIG.settings.assistiveMML ? "Add" : "Remove")+"AssistiveMathML",AMML]);
  1268. };
  1269. /*
  1270. * Handle setting the HTMLCSS fonts
  1271. */
  1272. MENU.Font = function () {
  1273. var HTMLCSS = OUTPUT["HTML-CSS"]; if (!HTMLCSS) return;
  1274. document.location.reload();
  1275. };
  1276. /*
  1277. * Handle selection of locale and rerender the page
  1278. */
  1279. MENU.Locale = function () {
  1280. MathJax.Localization.setLocale(CONFIG.settings.locale);
  1281. MathJax.Hub.Queue(["Reprocess",MathJax.Hub]); // FIXME: Just reprocess error messages?
  1282. };
  1283. MENU.LoadLocale = function () {
  1284. var url = prompt(_("LoadURL","Load translation data from this URL:"));
  1285. if (url) {
  1286. if (!url.match(/\.js$/)) {
  1287. alert(_("BadURL",
  1288. "The URL should be for a javascript file that defines MathJax translation data. " +
  1289. "Javascript file names should end with '.js'"
  1290. ));
  1291. }
  1292. AJAX.Require(url,function (status) {
  1293. if (status != AJAX.STATUS.OK) {alert(_("BadData","Failed to load translation data from %1",url))}
  1294. });
  1295. }
  1296. };
  1297. /*
  1298. * Handle setting MathPlayer events
  1299. */
  1300. MENU.MPEvents = function (item) {
  1301. var discoverable = CONFIG.settings.discoverable,
  1302. MESSAGE = MENU.MPEvents.Messages;
  1303. if (!isIE9) {
  1304. if (CONFIG.settings.mpMouse && !confirm(_.apply(_,MESSAGE.IE8warning))) {
  1305. delete MENU.cookie.mpContext; delete CONFIG.settings.mpContext;
  1306. delete MENU.cookie.mpMouse; delete CONFIG.settings.mpMouse;
  1307. MENU.saveCookie();
  1308. return;
  1309. }
  1310. CONFIG.settings.mpContext = CONFIG.settings.mpMouse;
  1311. MENU.cookie.mpContext = MENU.cookie.mpMouse = CONFIG.settings.mpMouse;
  1312. MENU.saveCookie();
  1313. MathJax.Hub.Queue(["Rerender",MathJax.Hub])
  1314. } else if (!discoverable && item.name[1] === "Menu Events" && CONFIG.settings.mpContext) {
  1315. alert(_.apply(_,MESSAGE.IE9warning));
  1316. }
  1317. };
  1318. MENU.MPEvents.Messages = {
  1319. IE8warning: ["IE8warning",
  1320. "This will disable the MathJax menu and zoom features, " +
  1321. "but you can Alt-Click on an expression to obtain the MathJax " +
  1322. "menu instead.\n\nReally change the MathPlayer settings?"],
  1323. IE9warning: ["IE9warning",
  1324. "The MathJax contextual menu will be disabled, but you can " +
  1325. "Alt-Click on an expression to obtain the MathJax menu instead."]
  1326. };
  1327. /*************************************************************/
  1328. /*************************************************************/
  1329. HUB.Browser.Select({
  1330. MSIE: function (browser) {
  1331. var quirks = (document.compatMode === "BackCompat");
  1332. var isIE8 = browser.versionAtLeast("8.0") && document.documentMode > 7;
  1333. MENU.Augment({
  1334. margin: 20,
  1335. msieBackgroundBug: ((document.documentMode||0) < 9),
  1336. msieFixedPositionBug: (quirks || !isIE8),
  1337. msieAboutBug: quirks,
  1338. msieHeightBug: ((document.documentMode||0) < 9)
  1339. // height of window doesn't include title bar in XP
  1340. });
  1341. if (isIE9) {
  1342. delete CONFIG.styles["#MathJax_About"].filter;
  1343. delete CONFIG.styles[".MathJax_Menu"].filter;
  1344. }
  1345. },
  1346. Firefox: function (browser) {
  1347. MENU.skipMouseover = browser.isMobile && browser.versionAtLeast("6.0");
  1348. MENU.skipMousedown = browser.isMobile;
  1349. }
  1350. });
  1351. MENU.isMobile = HUB.Browser.isMobile;
  1352. MENU.noContextMenu = HUB.Browser.noContextMenu;
  1353. /*************************************************************/
  1354. //
  1355. // Creates the locale menu from the list of locales in MathJax.Localization.strings
  1356. //
  1357. MENU.CreateLocaleMenu = function () {
  1358. if (!MENU.menu) return;
  1359. var menu = MENU.menu.Find("Language").submenu, items = menu.items;
  1360. //
  1361. // Get the names of the languages and sort them
  1362. //
  1363. var locales = [], LOCALE = MathJax.Localization.strings;
  1364. for (var id in LOCALE) {if (LOCALE.hasOwnProperty(id)) {locales.push(id)}}
  1365. locales = locales.sort(); menu.items = [];
  1366. //
  1367. // Add a menu item for each
  1368. //
  1369. for (var i = 0, m = locales.length; i < m; i++) {
  1370. var title = LOCALE[locales[i]].menuTitle;
  1371. if (title) {title += " ("+locales[i]+")"} else {title = locales[i]}
  1372. menu.items.push(ITEM.RADIO([locales[i],title],"locale",{action:MENU.Locale}));
  1373. }
  1374. //
  1375. // Add the rule and "Load from URL" items
  1376. //
  1377. menu.items.push(items[items.length-2],items[items.length-1]);
  1378. };
  1379. //
  1380. // Create the annotation menu from MathJax.Hub.config.semanticsAnnotations
  1381. //
  1382. MENU.CreateAnnotationMenu = function () {
  1383. if (!MENU.menu) return;
  1384. var menu = MENU.menu.Find("Show Math As","Annotation").submenu;
  1385. var annotations = CONFIG.semanticsAnnotations;
  1386. for (var a in annotations) {
  1387. if (annotations.hasOwnProperty(a)) {
  1388. menu.items.push(ITEM.COMMAND([a,a], MENU.ShowSource, {hidden: true, nativeTouch: true, format: a}));
  1389. }
  1390. }
  1391. };
  1392. /*************************************************************/
  1393. HUB.Register.StartupHook("End Config",function () {
  1394. /*
  1395. * Get the menu settings from the HUB (which includes the
  1396. * data from the cookie already), and add the format, if
  1397. * it wasn't set in the cookie.
  1398. */
  1399. CONFIG.settings = HUB.config.menuSettings;
  1400. if (typeof(CONFIG.settings.showRenderer) !== "undefined") {CONFIG.showRenderer = CONFIG.settings.showRenderer}
  1401. if (typeof(CONFIG.settings.showFontMenu) !== "undefined") {CONFIG.showFontMenu = CONFIG.settings.showFontMenu}
  1402. if (typeof(CONFIG.settings.showContext) !== "undefined") {CONFIG.showContext = CONFIG.settings.showContext}
  1403. MENU.getCookie();
  1404. /*
  1405. * The main menu
  1406. */
  1407. // Localization: items used as key, should be refactored.
  1408. MENU.menu = MENU(
  1409. ITEM.SUBMENU(["Show","Show Math As"],
  1410. ITEM.COMMAND(["MathMLcode","MathML Code"], MENU.ShowSource, {nativeTouch: true, format: "MathML"}),
  1411. ITEM.COMMAND(["Original","Original Form"], MENU.ShowSource, {nativeTouch: true}),
  1412. ITEM.SUBMENU(["Annotation","Annotation"], {disabled:true}),
  1413. ITEM.RULE(),
  1414. ITEM.CHECKBOX(["texHints","Show TeX hints in MathML"], "texHints"),
  1415. ITEM.CHECKBOX(["semantics","Add original form as annotation"], "semantics")
  1416. ),
  1417. ITEM.RULE(),
  1418. ITEM.SUBMENU(["Settings","Math Settings"],
  1419. ITEM.SUBMENU(["ZoomTrigger","Zoom Trigger"],
  1420. ITEM.RADIO(["Hover","Hover"], "zoom", {action: MENU.Zoom}),
  1421. ITEM.RADIO(["Click","Click"], "zoom", {action: MENU.Zoom}),
  1422. ITEM.RADIO(["DoubleClick","Double-Click"], "zoom", {action: MENU.Zoom}),
  1423. ITEM.RADIO(["NoZoom","No Zoom"], "zoom", {value: "None"}),
  1424. ITEM.RULE(),
  1425. ITEM.LABEL(["TriggerRequires","Trigger Requires:"]),
  1426. ITEM.CHECKBOX((HUB.Browser.isMac ? ["Option","Option"] : ["Alt","Alt"]), "ALT"),
  1427. ITEM.CHECKBOX(["Command","Command"], "CMD", {hidden: !HUB.Browser.isMac}),
  1428. ITEM.CHECKBOX(["Control","Control"], "CTRL", {hidden: HUB.Browser.isMac}),
  1429. ITEM.CHECKBOX(["Shift","Shift"], "Shift")
  1430. ),
  1431. ITEM.SUBMENU(["ZoomFactor","Zoom Factor"],
  1432. ITEM.RADIO("125%", "zscale"),
  1433. ITEM.RADIO("133%", "zscale"),
  1434. ITEM.RADIO("150%", "zscale"),
  1435. ITEM.RADIO("175%", "zscale"),
  1436. ITEM.RADIO("200%", "zscale"),
  1437. ITEM.RADIO("250%", "zscale"),
  1438. ITEM.RADIO("300%", "zscale"),
  1439. ITEM.RADIO("400%", "zscale")
  1440. ),
  1441. ITEM.RULE(),
  1442. ITEM.SUBMENU(["Renderer","Math Renderer"], {hidden:!CONFIG.showRenderer},
  1443. ITEM.RADIO(["HTML-CSS","HTML-CSS"], "renderer", {action: MENU.Renderer}),
  1444. ITEM.RADIO(["CommonHTML","Common HTML"], "renderer", {action: MENU.Renderer, value:"CommonHTML"}),
  1445. ITEM.RADIO(["PreviewHTML","Preview HTML"],"renderer", {action: MENU.Renderer, value:"PreviewHTML"}),
  1446. ITEM.RADIO(["MathML","MathML"], "renderer", {action: MENU.Renderer, value:"NativeMML"}),
  1447. ITEM.RADIO(["SVG","SVG"], "renderer", {action: MENU.Renderer}),
  1448. ITEM.RADIO(["PlainSource","Plain Source"],"renderer", {action: MENU.Renderer, value:"PlainSource"}),
  1449. ITEM.RULE(),
  1450. ITEM.CHECKBOX(["FastPreview","Fast Preview"], "FastPreview")
  1451. ),
  1452. ITEM.SUBMENU("MathPlayer", {hidden:!HUB.Browser.isMSIE || !CONFIG.showMathPlayer,
  1453. disabled:!HUB.Browser.hasMathPlayer},
  1454. ITEM.LABEL(["MPHandles","Let MathPlayer Handle:"]),
  1455. ITEM.CHECKBOX(["MenuEvents","Menu Events"], "mpContext", {action: MENU.MPEvents, hidden:!isIE9}),
  1456. ITEM.CHECKBOX(["MouseEvents","Mouse Events"], "mpMouse", {action: MENU.MPEvents, hidden:!isIE9}),
  1457. ITEM.CHECKBOX(["MenuAndMouse","Mouse and Menu Events"], "mpMouse", {action: MENU.MPEvents, hidden:isIE9})
  1458. ),
  1459. ITEM.SUBMENU(["FontPrefs","Font Preference"], {hidden:!CONFIG.showFontMenu},
  1460. ITEM.LABEL(["ForHTMLCSS","For HTML-CSS:"]),
  1461. ITEM.RADIO(["Auto","Auto"], "font", {action: MENU.Font}),
  1462. ITEM.RULE(),
  1463. ITEM.RADIO(["TeXLocal","TeX (local)"], "font", {action: MENU.Font}),
  1464. ITEM.RADIO(["TeXWeb","TeX (web)"], "font", {action: MENU.Font}),
  1465. ITEM.RADIO(["TeXImage","TeX (image)"], "font", {action: MENU.Font}),
  1466. ITEM.RULE(),
  1467. ITEM.RADIO(["STIXLocal","STIX (local)"], "font", {action: MENU.Font}),
  1468. ITEM.RADIO(["STIXWeb","STIX (web)"], "font", {action: MENU.Font}),
  1469. ITEM.RULE(),
  1470. ITEM.RADIO(["AsanaMathWeb","Asana Math (web)"], "font", {action: MENU.Font}),
  1471. ITEM.RADIO(["GyrePagellaWeb","Gyre Pagella (web)"], "font", {action: MENU.Font}),
  1472. ITEM.RADIO(["GyreTermesWeb","Gyre Termes (web)"], "font", {action: MENU.Font}),
  1473. ITEM.RADIO(["LatinModernWeb","Latin Modern (web)"], "font", {action: MENU.Font}),
  1474. ITEM.RADIO(["NeoEulerWeb","Neo Euler (web)"], "font", {action: MENU.Font})
  1475. ),
  1476. ITEM.SUBMENU(["ContextMenu","Contextual Menu"], {hidden:!CONFIG.showContext},
  1477. ITEM.RADIO(["MathJax","MathJax"], "context"),
  1478. ITEM.RADIO(["Browser","Browser"], "context")
  1479. ),
  1480. ITEM.COMMAND(["Scale","Scale All Math ..."],MENU.Scale),
  1481. ITEM.RULE().With({hidden:!CONFIG.showDiscoverable, name:["","discover_rule"]}),
  1482. ITEM.CHECKBOX(["Discoverable","Highlight on Hover"], "discoverable", {hidden:!CONFIG.showDiscoverable})
  1483. ),
  1484. ITEM.SUBMENU(["Accessibility","Accessibility"],
  1485. ITEM.CHECKBOX(["AssistiveMML","Assistive MathML"], "assistiveMML", {action:MENU.AssistiveMML}),
  1486. ITEM.CHECKBOX(["InTabOrder","Include in Tab Order"], "inTabOrder")
  1487. ),
  1488. ITEM.SUBMENU(["Locale","Language"], {hidden:!CONFIG.showLocale, ltr:true},
  1489. ITEM.RADIO("en", "locale", {action: MENU.Locale}),
  1490. ITEM.RULE().With({hidden:!CONFIG.showLocaleURL, name:["","localURL_rule"]}),
  1491. ITEM.COMMAND(["LoadLocale","Load from URL ..."], MENU.LoadLocale, {hidden:!CONFIG.showLocaleURL})
  1492. ),
  1493. ITEM.RULE(),
  1494. ITEM.COMMAND(["About","About MathJax"],MENU.About),
  1495. ITEM.COMMAND(["Help","MathJax Help"],MENU.Help)
  1496. );
  1497. if (MENU.isMobile) {
  1498. (function () {
  1499. var settings = CONFIG.settings;
  1500. var trigger = MENU.menu.Find("Math Settings","Zoom Trigger").submenu;
  1501. trigger.items[0].disabled = trigger.items[1].disabled = true;
  1502. if (settings.zoom === "Hover" || settings.zoom == "Click") {settings.zoom = "None"}
  1503. trigger.items = trigger.items.slice(0,4);
  1504. if (navigator.appVersion.match(/[ (]Android[) ]/)) {
  1505. MENU.ITEM.SUBMENU.Augment({marker: "\u00BB"});
  1506. }
  1507. })();
  1508. }
  1509. MENU.CreateLocaleMenu();
  1510. MENU.CreateAnnotationMenu();
  1511. });
  1512. MENU.showRenderer = function (show) {
  1513. MENU.cookie.showRenderer = CONFIG.showRenderer = show; MENU.saveCookie();
  1514. MENU.menu.Find("Math Settings","Math Renderer").hidden = !show;
  1515. };
  1516. MENU.showMathPlayer = function (show) {
  1517. MENU.cookie.showMathPlayer = CONFIG.showMathPlayer = show; MENU.saveCookie();
  1518. MENU.menu.Find("Math Settings","MathPlayer").hidden = !show;
  1519. };
  1520. MENU.showFontMenu = function (show) {
  1521. MENU.cookie.showFontMenu = CONFIG.showFontMenu = show; MENU.saveCookie();
  1522. MENU.menu.Find("Math Settings","Font Preference").hidden = !show;
  1523. };
  1524. MENU.showContext = function (show) {
  1525. MENU.cookie.showContext = CONFIG.showContext = show; MENU.saveCookie();
  1526. MENU.menu.Find("Math Settings","Contextual Menu").hidden = !show;
  1527. };
  1528. MENU.showDiscoverable = function (show) {
  1529. MENU.cookie.showDiscoverable = CONFIG.showDiscoverable = show; MENU.saveCookie();
  1530. MENU.menu.Find("Math Settings","Highlight on Hover").hidden = !show;
  1531. MENU.menu.Find("Math Settings","discover_rule").hidden = !show;
  1532. };
  1533. MENU.showLocale = function (show) {
  1534. MENU.cookie.showLocale = CONFIG.showLocale = show; MENU.saveCookie();
  1535. MENU.menu.Find("Language").hidden = !show;
  1536. };
  1537. MathJax.Hub.Register.StartupHook("HTML-CSS Jax Ready",function () {
  1538. if (!MathJax.OutputJax["HTML-CSS"].config.imageFont)
  1539. {MENU.menu.Find("Math Settings","Font Preference","TeX (image)").disabled = true}
  1540. });
  1541. /*************************************************************/
  1542. CALLBACK.Queue(
  1543. HUB.Register.StartupHook("End Config",{}), // wait until config is complete
  1544. ["Styles",AJAX,CONFIG.styles],
  1545. ["Post",HUB.Startup.signal,"MathMenu Ready"],
  1546. ["loadComplete",AJAX,"[MathJax]/extensions/MathMenu.js"]
  1547. );
  1548. })(MathJax.Hub,MathJax.HTML,MathJax.Ajax,MathJax.CallBack,MathJax.OutputJax);