MathMenu.js 48 KB

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