MathMenu.js 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141
  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-2013 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.2";
  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 isPC = HUB.Browser.isPC, isMSIE = HUB.Browser.isMSIE, isIE9 = ((document.documentMode||0) > 8);
  41. var ROUND = (isPC ? null : "5px");
  42. var CONFIG = HUB.CombineConfig("MathMenu",{
  43. delay: 150, // the delay for submenus
  44. closeImg: AJAX.fileURL(OUTPUT.imageDir+"/CloseX-31.png"), // image for close "X" for mobiles
  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. windowSettings: { // for source window
  53. status: "no", toolbar: "no", locationbar: "no", menubar: "no",
  54. directories: "no", personalbar: "no", resizable: "yes", scrollbars: "yes",
  55. width: 400, height: 300,
  56. left: Math.round((screen.width - 400)/2),
  57. top: Math.round((screen.height - 300)/3)
  58. },
  59. styles: {
  60. "#MathJax_About": {
  61. position:"fixed", left:"50%", width:"auto", "text-align":"center",
  62. border:"3px outset", padding:"1em 2em", "background-color":"#DDDDDD", color:"black",
  63. cursor: "default", "font-family":"message-box", "font-size":"120%",
  64. "font-style":"normal", "text-indent":0, "text-transform":"none",
  65. "line-height":"normal", "letter-spacing":"normal", "word-spacing":"normal",
  66. "word-wrap":"normal", "white-space":"nowrap", "float":"none", "z-index":201,
  67. "border-radius": "15px", // Opera 10.5 and IE9
  68. "-webkit-border-radius": "15px", // Safari and Chrome
  69. "-moz-border-radius": "15px", // Firefox
  70. "-khtml-border-radius": "15px", // Konqueror
  71. "box-shadow":"0px 10px 20px #808080", // Opera 10.5 and IE9
  72. "-webkit-box-shadow":"0px 10px 20px #808080", // Safari 3 and Chrome
  73. "-moz-box-shadow":"0px 10px 20px #808080", // Forefox 3.5
  74. "-khtml-box-shadow":"0px 10px 20px #808080", // Konqueror
  75. filter: "progid:DXImageTransform.Microsoft.dropshadow(OffX=2, OffY=2, Color='gray', Positive='true')" // IE
  76. },
  77. ".MathJax_Menu": {
  78. position:"absolute", "background-color":"white", color:"black",
  79. width:"auto", padding:(isPC ? "2px" : "5px 0px"),
  80. border:"1px solid #CCCCCC", margin:0, cursor:"default",
  81. font: "menu", "text-align":"left", "text-indent":0, "text-transform":"none",
  82. "line-height":"normal", "letter-spacing":"normal", "word-spacing":"normal",
  83. "word-wrap":"normal", "white-space":"nowrap", "float":"none", "z-index":201,
  84. "border-radius": ROUND, // Opera 10.5 and IE9
  85. "-webkit-border-radius": ROUND, // Safari and Chrome
  86. "-moz-border-radius": ROUND, // Firefox
  87. "-khtml-border-radius": ROUND, // Konqueror
  88. "box-shadow":"0px 10px 20px #808080", // Opera 10.5 and IE9
  89. "-webkit-box-shadow":"0px 10px 20px #808080", // Safari 3 and Chrome
  90. "-moz-box-shadow":"0px 10px 20px #808080", // Forefox 3.5
  91. "-khtml-box-shadow":"0px 10px 20px #808080", // Konqueror
  92. filter: "progid:DXImageTransform.Microsoft.dropshadow(OffX=2, OffY=2, Color='gray', Positive='true')" // IE
  93. },
  94. ".MathJax_MenuItem": {
  95. padding: (isPC ? "2px 2em" : "1px 2em"),
  96. background:"transparent"
  97. },
  98. ".MathJax_MenuArrow": {
  99. position:"absolute", right:".5em", color:"#666666",
  100. "font-family": (isMSIE ? "'Arial unicode MS'" : null)
  101. },
  102. ".MathJax_MenuActive .MathJax_MenuArrow": {color:"white"},
  103. ".MathJax_MenuCheck": {
  104. position:"absolute", left:".7em",
  105. "font-family": (isMSIE ? "'Arial unicode MS'" : null)
  106. },
  107. ".MathJax_MenuRadioCheck": {
  108. position:"absolute", left: (isPC ? "1em" : ".7em")
  109. },
  110. ".MathJax_MenuLabel": {
  111. padding: (isPC ? "2px 2em 4px 1.33em" : "1px 2em 3px 1.33em"),
  112. "font-style":"italic"
  113. },
  114. ".MathJax_MenuRule": {
  115. "border-top": (isPC ? "1px solid #CCCCCC" : "1px solid #DDDDDD"),
  116. margin: (isPC ? "4px 1px 0px" : "4px 3px")
  117. },
  118. ".MathJax_MenuDisabled": {
  119. color:"GrayText"
  120. },
  121. ".MathJax_MenuActive": {
  122. "background-color": (isPC ? "Highlight" : "#606872"),
  123. color: (isPC ? "HighlightText" : "white")
  124. },
  125. ".MathJax_Menu_Close": {
  126. position:"absolute",
  127. width: "31px", height: "31px",
  128. top:"-15px", left:"-15px"
  129. }
  130. }
  131. });
  132. var FALSE, HOVER;
  133. HUB.Register.StartupHook("MathEvents Ready",function () {
  134. FALSE = MathJax.Extension.MathEvents.Event.False;
  135. HOVER = MathJax.Extension.MathEvents.Hover;
  136. });
  137. /*************************************************************/
  138. /*
  139. * The main menu class
  140. */
  141. var MENU = MathJax.Menu = MathJax.Object.Subclass({
  142. version: VERSION,
  143. items: [],
  144. posted: false,
  145. title: null,
  146. margin: 5,
  147. Init: function (def) {this.items = [].slice.call(arguments,0)},
  148. With: function (def) {if (def) {HUB.Insert(this,def)}; return this},
  149. /*
  150. * Display the menu
  151. */
  152. Post: function (event,parent) {
  153. if (!event) {event = window.event};
  154. var div = document.getElementById("MathJax_MenuFrame");
  155. if (!div) {
  156. div = MENU.Background(this);
  157. delete ITEM.lastItem; delete ITEM.lastMenu;
  158. delete MENU.skipUp;
  159. SIGNAL.Post(["post",MENU.jax]);
  160. }
  161. var menu = HTML.addElement(div,"div",{
  162. onmouseup: MENU.Mouseup, ondblclick: FALSE,
  163. ondragstart: FALSE, onselectstart: FALSE, oncontextmenu: FALSE,
  164. menuItem: this, className: "MathJax_Menu"
  165. });
  166. MathJax.Localization.setCSS(menu);
  167. for (var i = 0, m = this.items.length; i < m; i++) {this.items[i].Create(menu)}
  168. if (MENU.isMobile) {
  169. HTML.addElement(menu,"span",{
  170. className: "MathJax_Menu_Close", menu: parent,
  171. ontouchstart: MENU.Close, ontouchend: FALSE, onmousedown: MENU.Close, onmouseup: FALSE
  172. },[["img",{src: CONFIG.closeImg, style:{width:"100%",height:"100%"}}]]);
  173. }
  174. this.posted = true;
  175. menu.style.width = (menu.offsetWidth+2) + "px";
  176. var x = event.pageX, y = event.pageY;
  177. if (!x && !y) {
  178. x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
  179. y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
  180. }
  181. if (!parent) {
  182. if (x + menu.offsetWidth > document.body.offsetWidth - this.margin)
  183. {x = document.body.offsetWidth - menu.offsetWidth - this.margin}
  184. if (MENU.isMobile) {x = Math.max(5,x-Math.floor(menu.offsetWidth/2)); y -= 20}
  185. MENU.skipUp = event.isContextMenu;
  186. } else {
  187. var side = "left", mw = parent.offsetWidth;
  188. x = (MENU.isMobile ? 30 : mw - 2); y = 0;
  189. while (parent && parent !== div) {
  190. x += parent.offsetLeft; y += parent.offsetTop;
  191. parent = parent.parentNode;
  192. }
  193. if (x + menu.offsetWidth > document.body.offsetWidth - this.margin && !MENU.isMobile)
  194. {side = "right"; x = Math.max(this.margin,x - mw - menu.offsetWidth + 6)}
  195. if (!isPC) {
  196. // in case these ever get implemented
  197. menu.style["borderRadiusTop"+side] = 0; // Opera 10.5
  198. menu.style["WebkitBorderRadiusTop"+side] = 0; // Safari and Chrome
  199. menu.style["MozBorderRadiusTop"+side] = 0; // Firefox
  200. menu.style["KhtmlBorderRadiusTop"+side] = 0; // Konqueror
  201. }
  202. }
  203. menu.style.left = x+"px"; menu.style.top = y+"px";
  204. if (document.selection && document.selection.empty) {document.selection.empty()}
  205. return FALSE(event);
  206. },
  207. /*
  208. * Remove the menu from the screen
  209. */
  210. Remove: function (event,menu) {
  211. SIGNAL.Post(["unpost",MENU.jax]);
  212. var div = document.getElementById("MathJax_MenuFrame");
  213. if (div) {
  214. div.parentNode.removeChild(div);
  215. if (this.msieFixedPositionBug) {detachEvent("onresize",MENU.Resize)}
  216. }
  217. if (MENU.jax.hover) {
  218. delete MENU.jax.hover.nofade;
  219. HOVER.UnHover(MENU.jax);
  220. }
  221. return FALSE(event);
  222. },
  223. /*
  224. * Find an item in a menu (or submenu) by name (Find) or ID (FindID).
  225. * A list of names or IDs means descend into submenus.
  226. */
  227. Find: function (name) {return this.FindN(1,name,[].slice.call(arguments,1))},
  228. FindId: function (name) {return this.FindN(0,name,[].slice.call(arguments,1))},
  229. FindN: function (n,name,names) {
  230. for (var i = 0, m = this.items.length; i < m; i++) {
  231. if (this.items[i].name[n] === name) {
  232. if (names.length) {
  233. if (!this.items[i].menu) {return null}
  234. return this.items[i].menu.FindN(n,names[0],names.slice(1));
  235. }
  236. return this.items[i];
  237. }
  238. }
  239. return null;
  240. },
  241. /*
  242. * Find the index of a menu item (so we can insert before or after it)
  243. */
  244. IndexOf: function (name) {return this.IndexOfN(1,name)},
  245. IndexOfId: function (name) {return this.IndexOfN(0,name)},
  246. IndexOfN: function (n,name) {
  247. for (var i = 0, m = this.items.length; i < m; i++)
  248. {if (this.items[i].name[n] === name) {return i}}
  249. return null;
  250. }
  251. },{
  252. config: CONFIG,
  253. div: null, // the DOM elements for the menu and submenus
  254. Close: function (event)
  255. {return MENU.Event(event,this.menu||this.parentNode,(this.menu?"Touchend":"Remove"))},
  256. Remove: function (event) {return MENU.Event(event,this,"Remove")},
  257. Mouseover: function (event) {return MENU.Event(event,this,"Mouseover")},
  258. Mouseout: function (event) {return MENU.Event(event,this,"Mouseout")},
  259. Mousedown: function (event) {return MENU.Event(event,this,"Mousedown")},
  260. Mouseup: function (event) {return MENU.Event(event,this,"Mouseup")},
  261. Touchstart: function (event) {return MENU.Event(event,this,"Touchstart")},
  262. Touchend: function (event) {return MENU.Event(event,this,"Touchend")},
  263. Event: function (event,menu,type,force) {
  264. if (MENU.skipMouseover && type === "Mouseover" && !force) {return FALSE(event)}
  265. if (MENU.skipUp) {
  266. if (type.match(/Mouseup|Touchend/)) {delete MENU.skipUp; return FALSE(event)}
  267. if (type === "Touchstart" ||
  268. (type === "Mousedown" && !MENU.skipMousedown)) {delete MENU.skipUp}
  269. }
  270. if (!event) {event = window.event}
  271. var item = menu.menuItem;
  272. if (item && item[type]) {return item[type](event,menu)}
  273. return null;
  274. },
  275. /*
  276. * Style for the background DIV
  277. */
  278. BGSTYLE: {
  279. position:"absolute", left:0, top:0, "z-index":200,
  280. width:"100%", height:"100%", border:0, padding:0, margin:0
  281. },
  282. Background: function (menu) {
  283. var div = HTML.addElement(document.body,"div",{style:this.BGSTYLE, id:"MathJax_MenuFrame"},
  284. [["div",{style: this.BGSTYLE, menuItem: menu, onmousedown: this.Remove}]]);
  285. var bg = div.firstChild;
  286. if (MENU.msieBackgroundBug) {
  287. // MSIE doesn't allow transparent background to be hit boxes, so
  288. // fake it using opacity with solid background color
  289. bg.style.backgroundColor = "white"; bg.style.filter = "alpha(opacity=0)";
  290. }
  291. if (MENU.msieFixedPositionBug) {
  292. // MSIE can't do fixed position, so use a full-sized background
  293. // and an onresize handler to update it (stupid, but necessary)
  294. div.width = div.height = 0; this.Resize();
  295. attachEvent("onresize",this.Resize);
  296. } else {
  297. // otherwise, use a fixed position DIV to cover the viewport
  298. bg.style.position = "fixed";
  299. }
  300. return div;
  301. },
  302. Resize: function () {setTimeout(MENU.SetWH,0)},
  303. SetWH: function () {
  304. var bg = document.getElementById("MathJax_MenuFrame");
  305. if (bg) {
  306. bg = bg.firstChild;
  307. bg.style.width = bg.style.height = "1px"; // so scrollWidth/Height will be right below
  308. bg.style.width = document.body.scrollWidth + "px";
  309. bg.style.height = document.body.scrollHeight + "px";
  310. }
  311. },
  312. saveCookie: function () {HTML.Cookie.Set("menu",this.cookie)},
  313. getCookie: function () {this.cookie = HTML.Cookie.Get("menu")},
  314. //
  315. // Preload images so they show up with the menu
  316. //
  317. getImages: function () {
  318. if (MENU.isMobile) {var close = new Image(); close.src = CONFIG.closeImg}
  319. }
  320. });
  321. /*************************************************************/
  322. /*
  323. * The menu item root subclass
  324. */
  325. var ITEM = MENU.ITEM = MathJax.Object.Subclass({
  326. name: "", // the menu item's label as [id,label] pair
  327. Create: function (menu) {
  328. if (!this.hidden) {
  329. var def = {
  330. onmouseover: MENU.Mouseover, onmouseout: MENU.Mouseout,
  331. onmouseup: MENU.Mouseup, onmousedown: MENU.Mousedown,
  332. ondragstart: FALSE, onselectstart: FALSE, onselectend: FALSE,
  333. ontouchstart: MENU.Touchstart, ontouchend: MENU.Touchend,
  334. className: "MathJax_MenuItem", menuItem: this
  335. };
  336. if (this.disabled) {def.className += " MathJax_MenuDisabled"}
  337. HTML.addElement(menu,"div",def,this.Label(def,menu));
  338. }
  339. },
  340. Name: function () {return _(this.name[0],this.name[1])},
  341. Mouseover: function (event,menu) {
  342. if (!this.disabled) {this.Activate(menu)}
  343. if (!this.menu || !this.menu.posted) {
  344. var menus = document.getElementById("MathJax_MenuFrame").childNodes,
  345. items = menu.parentNode.childNodes;
  346. for (var i = 0, m = items.length; i < m; i++) {
  347. var item = items[i].menuItem;
  348. if (item && item.menu && item.menu.posted) {item.Deactivate(items[i])}
  349. }
  350. m = menus.length-1;
  351. while (m >= 0 && menu.parentNode.menuItem !== menus[m].menuItem) {
  352. menus[m].menuItem.posted = false;
  353. menus[m].parentNode.removeChild(menus[m]);
  354. m--;
  355. }
  356. if (this.Timer && !MENU.isMobile) {this.Timer(event,menu)}
  357. }
  358. },
  359. Mouseout: function (event,menu) {
  360. if (!this.menu || !this.menu.posted) {this.Deactivate(menu)}
  361. if (this.timer) {clearTimeout(this.timer); delete this.timer}
  362. },
  363. Mouseup: function (event,menu) {return this.Remove(event,menu)},
  364. Touchstart: function (event,menu) {return this.TouchEvent(event,menu,"Mousedown")},
  365. Touchend: function (event,menu) {return this.TouchEvent(event,menu,"Mouseup")},
  366. TouchEvent: function (event,menu,type) {
  367. if (this !== ITEM.lastItem) {
  368. if (ITEM.lastMenu) {MENU.Event(event,ITEM.lastMenu,"Mouseout")}
  369. MENU.Event(event,menu,"Mouseover",true);
  370. ITEM.lastItem = this; ITEM.lastMenu = menu;
  371. }
  372. if (this.nativeTouch) {return null}
  373. MENU.Event(event,menu,type);
  374. return false;
  375. },
  376. Remove: function (event,menu) {
  377. menu = menu.parentNode.menuItem;
  378. return menu.Remove(event,menu);
  379. },
  380. Activate: function (menu) {this.Deactivate(menu); menu.className += " MathJax_MenuActive"},
  381. Deactivate: function (menu) {menu.className = menu.className.replace(/ MathJax_MenuActive/,"")},
  382. With: function (def) {if (def) {HUB.Insert(this,def)}; return this}
  383. });
  384. /*************************************************************/
  385. /*
  386. * A menu item that performs a command when selected
  387. */
  388. MENU.ITEM.COMMAND = MENU.ITEM.Subclass({
  389. action: function () {},
  390. Init: function (name,action,def) {
  391. if (!(name instanceof Array)) {name = [name,name]} // make [id,label] pair
  392. this.name = name; this.action = action;
  393. this.With(def);
  394. },
  395. Label: function (def,menu) {return [this.Name()]},
  396. Mouseup: function (event,menu) {
  397. if (!this.disabled) {
  398. this.Remove(event,menu);
  399. SIGNAL.Post(["command",this]);
  400. this.action.call(this,event);
  401. }
  402. return FALSE(event);
  403. }
  404. });
  405. /*************************************************************/
  406. /*
  407. * A menu item that posts a submenu
  408. */
  409. MENU.ITEM.SUBMENU = MENU.ITEM.Subclass({
  410. menu: null, // the submenu
  411. marker: (isPC && !HUB.Browser.isSafari ? "\u25B6" : "\u25B8"), // the menu arrow
  412. Init: function (name,def) {
  413. if (!(name instanceof Array)) {name = [name,name]} // make [id,label] pair
  414. this.name = name; var i = 1;
  415. if (!(def instanceof MENU.ITEM)) {this.With(def), i++}
  416. this.menu = MENU.apply(MENU,[].slice.call(arguments,i));
  417. },
  418. Label: function (def,menu) {
  419. this.menu.posted = false;
  420. return [this.Name()+" ",["span",{className:"MathJax_MenuArrow"},[this.marker]]];
  421. },
  422. Timer: function (event,menu) {
  423. if (this.timer) {clearTimeout(this.timer)}
  424. event = {clientX: event.clientX, clientY: event.clientY}; // MSIE can't pass the event below
  425. this.timer = setTimeout(CALLBACK(["Mouseup",this,event,menu]),CONFIG.delay);
  426. },
  427. Touchend: function (event,menu) {
  428. var forceout = this.menu.posted;
  429. var result = this.SUPER(arguments).Touchend.apply(this,arguments);
  430. if (forceout) {this.Deactivate(menu); delete ITEM.lastItem; delete ITEM.lastMenu}
  431. return result;
  432. },
  433. Mouseup: function (event,menu) {
  434. if (!this.disabled) {
  435. if (!this.menu.posted) {
  436. if (this.timer) {clearTimeout(this.timer); delete this.timer}
  437. this.menu.Post(event,menu);
  438. } else {
  439. var menus = document.getElementById("MathJax_MenuFrame").childNodes,
  440. m = menus.length-1;
  441. while (m >= 0) {
  442. var child = menus[m];
  443. child.menuItem.posted = false;
  444. child.parentNode.removeChild(child);
  445. if (child.menuItem === this.menu) {break};
  446. m--;
  447. }
  448. }
  449. }
  450. return FALSE(event);
  451. }
  452. });
  453. /*************************************************************/
  454. /*
  455. * A menu item that is one of several radio buttons
  456. */
  457. MENU.ITEM.RADIO = MENU.ITEM.Subclass({
  458. variable: null, // the variable name
  459. marker: (isPC ? "\u25CF" : "\u2713"), // the checkmark
  460. Init: function (name,variable,def) {
  461. if (!(name instanceof Array)) {name = [name,name]} // make [id,label] pair
  462. this.name = name; this.variable = variable; this.With(def);
  463. if (this.value == null) {this.value = this.name[0]}
  464. },
  465. Label: function (def,menu) {
  466. var span = {className:"MathJax_MenuRadioCheck"};
  467. if (CONFIG.settings[this.variable] !== this.value) {span = {style:{display:"none"}}}
  468. return [["span",span,[this.marker]]," "+this.Name()];
  469. },
  470. Mouseup: function (event,menu) {
  471. if (!this.disabled) {
  472. var child = menu.parentNode.childNodes;
  473. for (var i = 0, m = child.length; i < m; i++) {
  474. var item = child[i].menuItem;
  475. if (item && item.variable === this.variable)
  476. {child[i].firstChild.style.display = "none"}
  477. }
  478. menu.firstChild.display = "";
  479. CONFIG.settings[this.variable] = this.value;
  480. MENU.cookie[this.variable] = CONFIG.settings[this.variable]; MENU.saveCookie();
  481. SIGNAL.Post(["radio button",this]);
  482. }
  483. this.Remove(event,menu);
  484. if (this.action && !this.disabled) {this.action.call(MENU,this)}
  485. return FALSE(event);
  486. }
  487. });
  488. /*************************************************************/
  489. /*
  490. * A menu item that is checkable
  491. */
  492. MENU.ITEM.CHECKBOX = MENU.ITEM.Subclass({
  493. variable: null, // the variable name
  494. marker: "\u2713", // the checkmark
  495. Init: function (name,variable,def) {
  496. if (!(name instanceof Array)) {name = [name,name]} // make [id,label] pair
  497. this.name = name; this.variable = variable; this.With(def);
  498. },
  499. Label: function (def,menu) {
  500. var span = {className:"MathJax_MenuCheck"};
  501. if (!CONFIG.settings[this.variable]) {span = {style:{display:"none"}}}
  502. return [["span",span,[this.marker]]," "+this.Name()];
  503. },
  504. Mouseup: function (event,menu) {
  505. if (!this.disabled) {
  506. menu.firstChild.display = (CONFIG.settings[this.variable] ? "none" : "");
  507. CONFIG.settings[this.variable] = !CONFIG.settings[this.variable];
  508. MENU.cookie[this.variable] = CONFIG.settings[this.variable]; MENU.saveCookie();
  509. SIGNAL.Post(["checkbox",this]);
  510. }
  511. this.Remove(event,menu);
  512. if (this.action && !this.disabled) {this.action.call(MENU,this)}
  513. return FALSE(event);
  514. }
  515. });
  516. /*************************************************************/
  517. /*
  518. * A menu item that is a label
  519. */
  520. MENU.ITEM.LABEL = MENU.ITEM.Subclass({
  521. Init: function (name,def) {
  522. if (!(name instanceof Array)) {name = [name,name]} // make [id,label] pair
  523. this.name = name; this.With(def);
  524. },
  525. Label: function (def,menu) {
  526. delete def.onmouseover, delete def.onmouseout; delete def.onmousedown;
  527. def.className += " MathJax_MenuLabel";
  528. return [this.Name()];
  529. }
  530. });
  531. /*************************************************************/
  532. /*
  533. * A rule in a menu
  534. */
  535. MENU.ITEM.RULE = MENU.ITEM.Subclass({
  536. Label: function (def,menu) {
  537. delete def.onmouseover, delete def.onmouseout; delete def.onmousedown;
  538. def.className += " MathJax_MenuRule";
  539. return null;
  540. }
  541. });
  542. /*************************************************************/
  543. /*************************************************************/
  544. /*
  545. * Handle the ABOUT box
  546. */
  547. MENU.About = function () {
  548. var HTMLCSS = OUTPUT["HTML-CSS"] || {};
  549. var font =
  550. (HTMLCSS.imgFonts ? "image" :
  551. (HTMLCSS.fontInUse ?
  552. (HTMLCSS.webFonts ? "web" : "local")+" "+HTMLCSS.fontInUse :
  553. (OUTPUT.SVG ? "web SVG" : "generic")) ) + " fonts";
  554. var format = (!HTMLCSS.webFonts || HTMLCSS.imgFonts ? null :
  555. HTMLCSS.allowWebFonts.replace(/otf/,"woff or otf") + " fonts");
  556. var jax = ["MathJax.js v"+MathJax.fileversion,["br"]];
  557. jax.push(["div",{style:{"border-top":"groove 2px",margin:".25em 0"}}]);
  558. MENU.About.GetJax(jax,MathJax.InputJax,["InputJax","%1 Input Jax v%2"]);
  559. MENU.About.GetJax(jax,MathJax.OutputJax,["OutputJax","%1 Output Jax v%2"]);
  560. MENU.About.GetJax(jax,MathJax.ElementJax,["ElementJax","%1 Element Jax v%2"]);
  561. jax.push(["div",{style:{"border-top":"groove 2px",margin:".25em 0"}}]);
  562. MENU.About.GetJax(jax,MathJax.Extension,["Extension","%1 Extension v%2"],true);
  563. jax.push(["div",{style:{"border-top":"groove 2px",margin:".25em 0"}}],["center",{},[
  564. HUB.Browser + " v"+HUB.Browser.version + (format ?
  565. " \u2014 " + _(format.replace(/ /g,""),format) : "")
  566. ]]);
  567. MENU.About.div = MENU.Background(MENU.About);
  568. var about = HTML.addElement(MENU.About.div,"div",{
  569. id: "MathJax_About"
  570. },[
  571. ["b",{style:{fontSize:"120%"}},["MathJax"]]," v"+MathJax.version,["br"],
  572. _(font.replace(/ /g,""),"using "+font),["br"],["br"],
  573. ["span",{style:{
  574. display:"inline-block", "text-align":"left", "font-size":"80%",
  575. "max-height":"20em", overflow:"auto",
  576. "background-color":"#E4E4E4", padding:".4em .6em", border:"1px inset"
  577. }},jax],["br"],["br"],
  578. ["a",{href:"http://www.mathjax.org/"},["www.mathjax.org"]],
  579. ["img", {
  580. src: CONFIG.closeImg,
  581. style: {width:"21px", height:"21px", position:"absolute", top:".2em", right:".2em"},
  582. onclick: MENU.About.Remove
  583. }]
  584. ]);
  585. MathJax.Localization.setCSS(about);
  586. var doc = (document.documentElement||{});
  587. var H = window.innerHeight || doc.clientHeight || doc.scrollHeight || 0;
  588. if (MENU.prototype.msieAboutBug) {
  589. about.style.width = "20em"; about.style.position = "absolute";
  590. about.style.left = Math.floor((document.documentElement.scrollWidth - about.offsetWidth)/2)+"px";
  591. about.style.top = (Math.floor((H-about.offsetHeight)/3)+document.body.scrollTop)+"px";
  592. } else {
  593. about.style.marginLeft = Math.floor(-about.offsetWidth/2)+"px";
  594. about.style.top = Math.floor((H-about.offsetHeight)/3)+"px";
  595. }
  596. };
  597. MENU.About.Remove = function (event) {
  598. if (MENU.About.div) {document.body.removeChild(MENU.About.div); delete MENU.About.div}
  599. };
  600. MENU.About.GetJax = function (jax,JAX,type,noTypeCheck) {
  601. var info = [];
  602. for (var id in JAX) {if (JAX.hasOwnProperty(id) && JAX[id]) {
  603. if ((noTypeCheck && JAX[id].version) || (JAX[id].isa && JAX[id].isa(JAX)))
  604. {info.push(_(type[0],type[1],(JAX[id].id||id),JAX[id].version))}
  605. }}
  606. info.sort();
  607. for (var i = 0, m = info.length; i < m; i++) {jax.push(info[i],["br"])}
  608. return jax;
  609. };
  610. /*
  611. * Handle the MathJax HELP menu
  612. */
  613. MENU.Help = function () {
  614. AJAX.Require("[MathJax]/extensions/HelpDialog.js",
  615. function () {MathJax.Extension.Help.Dialog()});
  616. };
  617. /*
  618. * Handle showing of element's source
  619. */
  620. MENU.ShowSource = function (event) {
  621. if (!event) {event = window.event}
  622. var EVENT = {screenX:event.screenX, screenY:event.screenY};
  623. if (!MENU.jax) return;
  624. if (this.format === "MathML") {
  625. var MML = MathJax.ElementJax.mml;
  626. if (MML && typeof(MML.mbase.prototype.toMathML) !== "undefined") {
  627. // toMathML() can call MathJax.Hub.RestartAfter, so trap errors and check
  628. try {MENU.ShowSource.Text(MENU.jax.root.toMathML(),event)} catch (err) {
  629. if (!err.restart) {throw err}
  630. CALLBACK.After([this,MENU.ShowSource,EVENT],err.restart);
  631. }
  632. } else if (!AJAX.loadingToMathML) {
  633. AJAX.loadingToMathML = true;
  634. MENU.ShowSource.Window(event); // WeBKit needs to open window on click event
  635. CALLBACK.Queue(
  636. AJAX.Require("[MathJax]/extensions/toMathML.js"),
  637. function () {
  638. delete AJAX.loadingToMathML;
  639. if (!MML.mbase.prototype.toMathML) {MML.mbase.prototype.toMathML = function () {}}
  640. },
  641. [this,MENU.ShowSource,EVENT] // call this function again
  642. );
  643. return;
  644. }
  645. } else if (this.format === "Error") {
  646. MENU.ShowSource.Text(MENU.jax.errorText,event);
  647. } else {
  648. if (MENU.jax.originalText == null) {
  649. alert(_("NoOriginalForm","No original form available"));
  650. return;
  651. }
  652. MENU.ShowSource.Text(MENU.jax.originalText,event);
  653. }
  654. };
  655. MENU.ShowSource.Window = function (event) {
  656. if (!MENU.ShowSource.w) {
  657. var def = [], DEF = CONFIG.windowSettings;
  658. for (var id in DEF) {if (DEF.hasOwnProperty(id)) {def.push(id+"="+DEF[id])}}
  659. MENU.ShowSource.w = window.open("","_blank",def.join(","));
  660. }
  661. return MENU.ShowSource.w;
  662. };
  663. MENU.ShowSource.Text = function (text,event) {
  664. var w = MENU.ShowSource.Window(event); delete MENU.ShowSource.w;
  665. text = text.replace(/^\s*/,"").replace(/\s*$/,"");
  666. text = text.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
  667. var title = _("EqSource","MathJax Equation Source");
  668. if (MENU.isMobile) {
  669. w.document.open();
  670. 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%'>");
  671. w.document.write("<pre>"+text+"</pre>");
  672. w.document.write("<hr><input type='button' value='"+_("Close","Close")+"' onclick='window.close()' />");
  673. w.document.write("</body></html>");
  674. w.document.close();
  675. } else {
  676. w.document.open();
  677. w.document.write("<html><head><title>"+title+"</title></head><body style='font-size:85%'>");
  678. w.document.write("<table><tr><td><pre>"+text+"</pre></td></tr></table>");
  679. w.document.write("</body></html>");
  680. w.document.close();
  681. var table = w.document.body.firstChild;
  682. setTimeout(function () {
  683. var H = (w.outerHeight-w.innerHeight)||30, W = (w.outerWidth-w.innerWidth)||30, x, y;
  684. W = Math.max(100,Math.min(Math.floor(.5*screen.width),table.offsetWidth+W+25));
  685. H = Math.max(40,Math.min(Math.floor(.5*screen.height),table.offsetHeight+H+25));
  686. w.resizeTo(W,H);
  687. if (event && event.screenX != null) {
  688. x = Math.max(0,Math.min(event.screenX-Math.floor(W/2), screen.width-W-20));
  689. y = Math.max(0,Math.min(event.screenY-Math.floor(H/2), screen.height-H-20));
  690. w.moveTo(x,y);
  691. }
  692. },50);
  693. }
  694. };
  695. /*
  696. * Handle rescaling all the math
  697. */
  698. MENU.Scale = function () {
  699. var HTMLCSS = OUTPUT["HTML-CSS"], nMML = OUTPUT.NativeMML, SVG = OUTPUT.SVG;
  700. var SCALE = (HTMLCSS||nMML||SVG||{config:{scale:100}}).config.scale;
  701. var scale = prompt(_("ScaleMath","Scale all mathematics (compared to surrounding text) by"),SCALE+"%");
  702. if (scale) {
  703. if (scale.match(/^\s*\d+(\.\d*)?\s*%?\s*$/)) {
  704. scale = parseFloat(scale);
  705. if (scale) {
  706. if (scale !== SCALE) {
  707. if (HTMLCSS) {HTMLCSS.config.scale = scale}
  708. if (nMML) {nMML.config.scale = scale}
  709. if (SVG) {SVG.config.scale = scale}
  710. MENU.cookie.scale = scale;
  711. MENU.saveCookie(); HUB.Reprocess();
  712. }
  713. } else {alert(_("NonZeroScale","The scale should not be zero"))}
  714. } else {alert(_("PercentScale",
  715. "The scale should be a percentage (e.g., 120%%)"))}
  716. }
  717. };
  718. /*
  719. * Handle loading the zoom code
  720. */
  721. MENU.Zoom = function () {
  722. if (!MathJax.Extension.MathZoom) {AJAX.Require("[MathJax]/extensions/MathZoom.js")}
  723. };
  724. /*
  725. * Handle changing the renderer
  726. */
  727. MENU.Renderer = function () {
  728. var jax = HUB.outputJax["jax/mml"];
  729. if (jax[0] !== CONFIG.settings.renderer) {
  730. var BROWSER = HUB.Browser, message, MESSAGE = MENU.Renderer.Messages, warned;
  731. //
  732. // Check that the new renderer is appropriate for the browser
  733. //
  734. switch (CONFIG.settings.renderer) {
  735. case "NativeMML":
  736. if (!CONFIG.settings.warnedMML) {
  737. if (BROWSER.isChrome && BROWSER.version.substr(0,3) !== "24.") {message = MESSAGE.MML.WebKit}
  738. else if (BROWSER.isSafari && !BROWSER.versionAtLeast("5.0")) {message = MESSAGE.MML.WebKit}
  739. else if (BROWSER.isMSIE) {if (!BROWSER.hasMathPlayer) {message = MESSAGE.MML.MSIE}}
  740. else {message = MESSAGE.MML[BROWSER]}
  741. warned = "warnedMML";
  742. }
  743. break;
  744. case "SVG":
  745. if (!CONFIG.settings.warnedSVG) {
  746. if (BROWSER.isMSIE && !isIE9) {message = MESSAGE.SVG.MSIE}
  747. }
  748. break;
  749. }
  750. if (message) {
  751. message = _(message[0],message[1]);
  752. message += "\n\n";
  753. message += _("SwitchAnyway",
  754. "Switch the renderer anyway?\n\n" +
  755. "(Press OK to switch, CANCEL to continue with the current renderer)");
  756. MENU.cookie.renderer = jax[0].id; MENU.saveCookie();
  757. if (!confirm(message)) {
  758. MENU.cookie.renderer = CONFIG.settings.renderer = HTML.Cookie.Get("menu").renderer;
  759. MENU.saveCookie();
  760. return;
  761. }
  762. if (warned) {MENU.cookie.warned = CONFIG.settings.warned = true}
  763. MENU.cookie.renderer = CONFIG.settings.renderer; MENU.saveCookie();
  764. }
  765. HUB.Queue(
  766. ["setRenderer",HUB,CONFIG.settings.renderer,"jax/mml"],
  767. ["Rerender",HUB]
  768. );
  769. }
  770. };
  771. MENU.Renderer.Messages = {
  772. MML: {
  773. WebKit: ["WebkitNativeMMLWarning",
  774. "Your browser doesn't seem to support MathML natively, " +
  775. "so switching to MathML output may cause the mathematics " +
  776. "on the page to become unreadable."],
  777. MSIE: ["MSIENativeMMLWarning",
  778. "Internet Explorer requires the MathPlayer plugin " +
  779. "in order to process MathML output."],
  780. Opera: ["OperaNativeMMLWarning",
  781. "Opera's support for MathML is limited, so switching to " +
  782. "MathML output may cause some expressions to render poorly."],
  783. Safari: ["SafariNativeMMLWarning",
  784. "Your browser's native MathML does not implement all the features " +
  785. "used by MathJax, so some expressions may not render properly."],
  786. Firefox: ["FirefoxNativeMMLWarning",
  787. "Your browser's native MathML does not implement all the features " +
  788. "used by MathJax, so some expressions may not render properly."]
  789. },
  790. SVG: {
  791. MSIE: ["MSIESVGWarning",
  792. "SVG is not implemented in Internet Explorer prior to " +
  793. "IE9 or when it is emulating IE8 or below. " +
  794. "Switching to SVG output will cause the mathematics to " +
  795. "not display properly."]
  796. }
  797. };
  798. /*
  799. * Handle setting the HTMLCSS fonts
  800. */
  801. MENU.Font = function () {
  802. var HTMLCSS = OUTPUT["HTML-CSS"]; if (!HTMLCSS) return;
  803. document.location.reload();
  804. };
  805. /*
  806. * Handle selection of locale and rerender the page
  807. */
  808. MENU.Locale = function () {
  809. MathJax.Localization.setLocale(CONFIG.settings.locale);
  810. MathJax.Hub.Queue(["Reprocess",MathJax.Hub]); // FIXME: Just reprocess error messages?
  811. };
  812. MENU.LoadLocale = function () {
  813. var url = prompt(_("LoadURL","Load translation data from this URL:"));
  814. if (url) {
  815. if (!url.match(/\.js$/)) {
  816. alert(_("BadURL",
  817. "The URL should be for a javascript file that defines MathJax translation data. " +
  818. "Javascript file names should end with '.js'"
  819. ));
  820. }
  821. AJAX.Require(url,function (status) {
  822. if (status != AJAX.STATUS.OK) {alert(_("BadData","Failed to load translation data from %1",url))}
  823. });
  824. }
  825. };
  826. /*
  827. * Handle setting MathPlayer events
  828. */
  829. MENU.MPEvents = function (item) {
  830. var discoverable = CONFIG.settings.discoverable,
  831. MESSAGE = MENU.MPEvents.Messages;
  832. if (!isIE9) {
  833. if (CONFIG.settings.mpMouse && !confirm(_.apply(_,MESSAGE.IE8warning))) {
  834. delete MENU.cookie.mpContext; delete CONFIG.settings.mpContext;
  835. delete MENU.cookie.mpMouse; delete CONFIG.settings.mpMouse;
  836. MENU.saveCookie();
  837. return;
  838. }
  839. CONFIG.settings.mpContext = CONFIG.settings.mpMouse;
  840. MENU.cookie.mpContext = MENU.cookie.mpMouse = CONFIG.settings.mpMouse;
  841. MENU.saveCookie();
  842. MathJax.Hub.Queue(["Rerender",MathJax.Hub])
  843. } else if (!discoverable && item.name[1] === "Menu Events" && CONFIG.settings.mpContext) {
  844. alert(_.apply(_,MESSAGE.IE9warning));
  845. }
  846. };
  847. MENU.MPEvents.Messages = {
  848. IE8warning: ["IE8warning",
  849. "This will disable the MathJax menu and zoom features, " +
  850. "but you can Alt-Click on an expression to obtain the MathJax " +
  851. "menu instead.\n\nReally change the MathPlayer settings?"],
  852. IE9warning: ["IE9warning",
  853. "The MathJax contextual menu will be disabled, but you can " +
  854. "Alt-Click on an expression to obtain the MathJax menu instead."]
  855. };
  856. /*************************************************************/
  857. /*************************************************************/
  858. HUB.Browser.Select({
  859. MSIE: function (browser) {
  860. var quirks = (document.compatMode === "BackCompat");
  861. var isIE8 = browser.versionAtLeast("8.0") && document.documentMode > 7;
  862. MENU.Augment({
  863. margin: 20,
  864. msieBackgroundBug: (document.documentMode < 9),
  865. msieFixedPositionBug: (quirks || !isIE8),
  866. msieAboutBug: quirks
  867. });
  868. if (isIE9) {
  869. delete CONFIG.styles["#MathJax_About"].filter;
  870. delete CONFIG.styles[".MathJax_Menu"].filter;
  871. }
  872. },
  873. Firefox: function (browser) {
  874. MENU.skipMouseover = browser.isMobile && browser.versionAtLeast("6.0");
  875. MENU.skipMousedown = browser.isMobile;
  876. }
  877. });
  878. MENU.isMobile = HUB.Browser.isMobile;
  879. MENU.noContextMenu = HUB.Browser.noContextMenu;
  880. /*************************************************************/
  881. //
  882. // Creates the locale menu from the list of locales in MathJax.Localization.strings
  883. //
  884. MENU.CreateLocaleMenu = function () {
  885. if (!MENU.menu) return;
  886. var menu = MENU.menu.Find("Language").menu, items = menu.items;
  887. //
  888. // Get the names of the languages and sort them
  889. //
  890. var locales = [], LOCALE = MathJax.Localization.strings;
  891. for (var id in LOCALE) {if (LOCALE.hasOwnProperty(id)) {locales.push(id)}}
  892. locales = locales.sort(); menu.items = [];
  893. //
  894. // Add a menu item for each
  895. //
  896. for (var i = 0, m = locales.length; i < m; i++) {
  897. var title = LOCALE[locales[i]].menuTitle;
  898. if (title) {title += " ("+locales[i]+")"} else {title = locales[i]}
  899. menu.items.push(ITEM.RADIO([locales[i],title],"locale",{action:MENU.Locale}));
  900. }
  901. //
  902. // Add the rule and "Load from URL" items
  903. //
  904. menu.items.push(items[items.length-2],items[items.length-1]);
  905. };
  906. /*************************************************************/
  907. HUB.Register.StartupHook("End Config",function () {
  908. /*
  909. * Get the menu settings from the HUB (which includes the
  910. * data from the cookie already), and add the format, if
  911. * it wasn't set in the cookie.
  912. */
  913. CONFIG.settings = HUB.config.menuSettings;
  914. if (typeof(CONFIG.settings.showRenderer) !== "undefined") {CONFIG.showRenderer = CONFIG.settings.showRenderer}
  915. if (typeof(CONFIG.settings.showFontMenu) !== "undefined") {CONFIG.showFontMenu = CONFIG.settings.showFontMenu}
  916. if (typeof(CONFIG.settings.showContext) !== "undefined") {CONFIG.showContext = CONFIG.settings.showContext}
  917. MENU.getCookie();
  918. /*
  919. * The main menu
  920. */
  921. // Localization: items used as key, should be refactored.
  922. MENU.menu = MENU(
  923. ITEM.SUBMENU(["Show","Show Math As"],
  924. ITEM.COMMAND(["MathMLcode","MathML Code"], MENU.ShowSource, {nativeTouch: true, format: "MathML"}),
  925. ITEM.COMMAND(["Original","Original Form"], MENU.ShowSource, {nativeTouch: true}),
  926. ITEM.RULE(),
  927. ITEM.CHECKBOX(["texHints","Show TeX hints in MathML"], "texHints")
  928. ),
  929. ITEM.RULE(),
  930. ITEM.SUBMENU(["Settings","Math Settings"],
  931. ITEM.SUBMENU(["ZoomTrigger","Zoom Trigger"],
  932. ITEM.RADIO(["Hover","Hover"], "zoom", {action: MENU.Zoom}),
  933. ITEM.RADIO(["Click","Click"], "zoom", {action: MENU.Zoom}),
  934. ITEM.RADIO(["DoubleClick","Double-Click"], "zoom", {action: MENU.Zoom}),
  935. ITEM.RADIO(["NoZoom","No Zoom"], "zoom", {value: "None"}),
  936. ITEM.RULE(),
  937. ITEM.LABEL(["TriggerRequires","Trigger Requires:"]),
  938. ITEM.CHECKBOX((HUB.Browser.isMac ? ["Option","Option"] : ["Alt","Alt"]), "ALT"),
  939. ITEM.CHECKBOX(["Command","Command"], "CMD", {hidden: !HUB.Browser.isMac}),
  940. ITEM.CHECKBOX(["Control","Control"], "CTRL", {hidden: HUB.Browser.isMac}),
  941. ITEM.CHECKBOX(["Shift","Shift"], "Shift")
  942. ),
  943. ITEM.SUBMENU(["ZoomFactor","Zoom Factor"],
  944. ITEM.RADIO("125%", "zscale"),
  945. ITEM.RADIO("133%", "zscale"),
  946. ITEM.RADIO("150%", "zscale"),
  947. ITEM.RADIO("175%", "zscale"),
  948. ITEM.RADIO("200%", "zscale"),
  949. ITEM.RADIO("250%", "zscale"),
  950. ITEM.RADIO("300%", "zscale"),
  951. ITEM.RADIO("400%", "zscale")
  952. ),
  953. ITEM.RULE(),
  954. ITEM.SUBMENU(["Renderer","Math Renderer"], {hidden:!CONFIG.showRenderer},
  955. ITEM.RADIO("HTML-CSS", "renderer", {action: MENU.Renderer}),
  956. ITEM.RADIO("MathML", "renderer", {action: MENU.Renderer, value:"NativeMML"}),
  957. ITEM.RADIO("SVG", "renderer", {action: MENU.Renderer})
  958. ),
  959. ITEM.SUBMENU("MathPlayer", {hidden:!HUB.Browser.isMSIE || !CONFIG.showMathPlayer,
  960. disabled:!HUB.Browser.hasMathPlayer},
  961. ITEM.LABEL(["MPHandles","Let MathPlayer Handle:"]),
  962. ITEM.CHECKBOX(["MenuEvents","Menu Events"], "mpContext", {action: MENU.MPEvents, hidden:!isIE9}),
  963. ITEM.CHECKBOX(["MouseEvents","Mouse Events"], "mpMouse", {action: MENU.MPEvents, hidden:!isIE9}),
  964. ITEM.CHECKBOX(["MenuAndMouse","Mouse and Menu Events"], "mpMouse", {action: MENU.MPEvents, hidden:isIE9})
  965. ),
  966. ITEM.SUBMENU(["FontPrefs","Font Preference"], {hidden:!CONFIG.showFontMenu},
  967. ITEM.LABEL(["ForHTMLCSS","For HTML-CSS:"]),
  968. ITEM.RADIO(["Auto","Auto"], "font", {action: MENU.Font}),
  969. ITEM.RULE(),
  970. ITEM.RADIO(["TeXLocal","TeX (local)"], "font", {action: MENU.Font}),
  971. ITEM.RADIO(["TeXWeb","TeX (web)"], "font", {action: MENU.Font}),
  972. ITEM.RADIO(["TeXImage","TeX (image)"], "font", {action: MENU.Font}),
  973. ITEM.RULE(),
  974. ITEM.RADIO(["STIXlocal","STIX (local)"], "font", {action: MENU.Font})
  975. ),
  976. ITEM.SUBMENU(["ContextMenu","Contextual Menu"], {hidden:!CONFIG.showContext},
  977. ITEM.RADIO("MathJax", "context"),
  978. ITEM.RADIO(["Browser","Browser"], "context")
  979. ),
  980. ITEM.COMMAND(["Scale","Scale All Math ..."],MENU.Scale),
  981. ITEM.RULE().With({hidden:!CONFIG.showDiscoverable, name:["","discover_rule"]}),
  982. ITEM.CHECKBOX(["Discoverable","Highlight on Hover"], "discoverable", {hidden:!CONFIG.showDiscoverable})
  983. ),
  984. ITEM.SUBMENU(["Locale","Language"], {hidden:!CONFIG.showLocale},
  985. ITEM.RADIO("en", "locale", {action: MENU.Locale}),
  986. ITEM.RULE().With({hidden:!CONFIG.showLocaleURL, name:["","localURL_rule"]}),
  987. ITEM.COMMAND(["LoadLocale","Load from URL ..."], MENU.LoadLocale, {hidden:!CONFIG.showLocaleURL})
  988. ),
  989. ITEM.RULE(),
  990. ITEM.COMMAND(["About","About MathJax"],MENU.About),
  991. ITEM.COMMAND(["Help","MathJax Help"],MENU.Help)
  992. );
  993. if (MENU.isMobile) {
  994. (function () {
  995. var settings = CONFIG.settings;
  996. var trigger = MENU.menu.Find("Math Settings","Zoom Trigger").menu;
  997. trigger.items[0].disabled = trigger.items[1].disabled = true;
  998. if (settings.zoom === "Hover" || settings.zoom == "Click") {settings.zoom = "None"}
  999. trigger.items = trigger.items.slice(0,4);
  1000. if (navigator.appVersion.match(/[ (]Android[) ]/)) {
  1001. MENU.ITEM.SUBMENU.Augment({marker: "\u00BB"});
  1002. }
  1003. })();
  1004. }
  1005. MENU.CreateLocaleMenu();
  1006. });
  1007. MENU.showRenderer = function (show) {
  1008. MENU.cookie.showRenderer = CONFIG.showRenderer = show; MENU.saveCookie();
  1009. MENU.menu.Find("Math Settings","Math Renderer").hidden = !show;
  1010. };
  1011. MENU.showMathPlayer = function (show) {
  1012. MENU.cookie.showMathPlayer = CONFIG.showMathPlayer = show; MENU.saveCookie();
  1013. MENU.menu.Find("Math Settings","MathPlayer").hidden = !show;
  1014. };
  1015. MENU.showFontMenu = function (show) {
  1016. MENU.cookie.showFontMenu = CONFIG.showFontMenu = show; MENU.saveCookie();
  1017. MENU.menu.Find("Math Settings","Font Preference").hidden = !show;
  1018. };
  1019. MENU.showContext = function (show) {
  1020. MENU.cookie.showContext = CONFIG.showContext = show; MENU.saveCookie();
  1021. MENU.menu.Find("Math Settings","Contextual Menu").hidden = !show;
  1022. };
  1023. MENU.showDiscoverable = function (show) {
  1024. MENU.cookie.showDiscoverable = CONFIG.showDiscoverable = show; MENU.saveCookie();
  1025. MENU.menu.Find("Math Settings","Highlight on Hover").hidden = !show;
  1026. MENU.menu.Find("Math Settings","discover_rule").hidden = !show;
  1027. };
  1028. MENU.showLocale = function (show) {
  1029. MENU.cookie.showLocale = CONFIG.showLocale = show; MENU.saveCookie();
  1030. MENU.menu.Find("Language").hidden = !show;
  1031. };
  1032. MathJax.Hub.Register.StartupHook("HTML-CSS Jax Ready",function () {
  1033. if (!MathJax.OutputJax["HTML-CSS"].config.imageFont)
  1034. {MENU.menu.Find("Math Settings","Font Preference","TeX (image)").disabled = true}
  1035. });
  1036. /*************************************************************/
  1037. CALLBACK.Queue(
  1038. HUB.Register.StartupHook("End Config",{}), // wait until config is complete
  1039. ["getImages",MENU],
  1040. ["Styles",AJAX,CONFIG.styles],
  1041. ["Post",HUB.Startup.signal,"MathMenu Ready"],
  1042. ["loadComplete",AJAX,"[MathJax]/extensions/MathMenu.js"]
  1043. );
  1044. })(MathJax.Hub,MathJax.HTML,MathJax.Ajax,MathJax.CallBack,MathJax.OutputJax);