MathEvents.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  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/MathEvents.js
  6. *
  7. * Implements the event handlers needed by the output jax to perform
  8. * menu, hover, and other events.
  9. *
  10. * ---------------------------------------------------------------------
  11. *
  12. * Copyright (c) 2011-2015 The MathJax Consortium
  13. *
  14. * Licensed under the Apache License, Version 2.0 (the "License");
  15. * you may not use this file except in compliance with the License.
  16. * You may obtain a copy of the License at
  17. *
  18. * http://www.apache.org/licenses/LICENSE-2.0
  19. *
  20. * Unless required by applicable law or agreed to in writing, software
  21. * distributed under the License is distributed on an "AS IS" BASIS,
  22. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  23. * See the License for the specific language governing permissions and
  24. * limitations under the License.
  25. */
  26. (function (HUB,HTML,AJAX,CALLBACK,LOCALE,OUTPUT,INPUT) {
  27. var VERSION = "2.5.0";
  28. var EXTENSION = MathJax.Extension;
  29. var ME = EXTENSION.MathEvents = {version: VERSION};
  30. var SETTINGS = HUB.config.menuSettings;
  31. var CONFIG = {
  32. hover: 500, // time required to be considered a hover
  33. frame: {
  34. x: 3.5, y: 5, // frame padding and
  35. bwidth: 1, // frame border width (in pixels)
  36. bcolor: "#A6D", // frame border color
  37. hwidth: "15px", // haze width
  38. hcolor: "#83A" // haze color
  39. },
  40. button: {
  41. x: -4, y: -3, // menu button offsets
  42. wx: -2, // button offset for full-width equations
  43. src: AJAX.urlRev(OUTPUT.imageDir+"/MenuArrow-15.png") // button image
  44. },
  45. fadeinInc: .2, // increment for fade-in
  46. fadeoutInc: .05, // increment for fade-out
  47. fadeDelay: 50, // delay between fade-in or fade-out steps
  48. fadeoutStart: 400, // delay before fade-out after mouseout
  49. fadeoutDelay: 15*1000, // delay before automatic fade-out
  50. styles: {
  51. ".MathJax_Hover_Frame": {
  52. "border-radius": ".25em", // Opera 10.5 and IE9
  53. "-webkit-border-radius": ".25em", // Safari and Chrome
  54. "-moz-border-radius": ".25em", // Firefox
  55. "-khtml-border-radius": ".25em", // Konqueror
  56. "box-shadow": "0px 0px 15px #83A", // Opera 10.5 and IE9
  57. "-webkit-box-shadow": "0px 0px 15px #83A", // Safari and Chrome
  58. "-moz-box-shadow": "0px 0px 15px #83A", // Forefox
  59. "-khtml-box-shadow": "0px 0px 15px #83A", // Konqueror
  60. border: "1px solid #A6D ! important",
  61. display: "inline-block", position:"absolute"
  62. },
  63. ".MathJax_Hover_Arrow": {
  64. position:"absolute",
  65. width:"15px", height:"11px",
  66. cursor:"pointer"
  67. }
  68. }
  69. };
  70. //
  71. // Common event-handling code
  72. //
  73. var EVENT = ME.Event = {
  74. LEFTBUTTON: 0, // the event.button value for left button
  75. RIGHTBUTTON: 2, // the event.button value for right button
  76. MENUKEY: "altKey", // the event value for alternate context menu
  77. Mousedown: function (event) {return EVENT.Handler(event,"Mousedown",this)},
  78. Mouseup: function (event) {return EVENT.Handler(event,"Mouseup",this)},
  79. Mousemove: function (event) {return EVENT.Handler(event,"Mousemove",this)},
  80. Mouseover: function (event) {return EVENT.Handler(event,"Mouseover",this)},
  81. Mouseout: function (event) {return EVENT.Handler(event,"Mouseout",this)},
  82. Click: function (event) {return EVENT.Handler(event,"Click",this)},
  83. DblClick: function (event) {return EVENT.Handler(event,"DblClick",this)},
  84. Menu: function (event) {return EVENT.Handler(event,"ContextMenu",this)},
  85. //
  86. // Call the output jax's event handler or the zoom handler
  87. //
  88. Handler: function (event,type,math) {
  89. if (AJAX.loadingMathMenu) {return EVENT.False(event)}
  90. var jax = OUTPUT[math.jaxID];
  91. if (!event) {event = window.event}
  92. event.isContextMenu = (type === "ContextMenu");
  93. if (jax[type]) {return jax[type](event,math)}
  94. if (EXTENSION.MathZoom) {return EXTENSION.MathZoom.HandleEvent(event,type,math)}
  95. },
  96. //
  97. // Try to cancel the event in every way we can
  98. //
  99. False: function (event) {
  100. if (!event) {event = window.event}
  101. if (event) {
  102. if (event.preventDefault) {event.preventDefault()} else {event.returnValue = false}
  103. if (event.stopPropagation) {event.stopPropagation()}
  104. event.cancelBubble = true;
  105. }
  106. return false;
  107. },
  108. //
  109. // Load the contextual menu code, if needed, and post the menu
  110. //
  111. ContextMenu: function (event,math,force) {
  112. //
  113. // Check if we are showing menus
  114. //
  115. var JAX = OUTPUT[math.jaxID], jax = JAX.getJaxFromMath(math);
  116. var show = (JAX.config.showMathMenu != null ? JAX : HUB).config.showMathMenu;
  117. if (!show || (SETTINGS.context !== "MathJax" && !force)) return;
  118. //
  119. // Remove selections, remove hover fades
  120. //
  121. if (ME.msieEventBug) {event = window.event || event}
  122. EVENT.ClearSelection(); HOVER.ClearHoverTimer();
  123. if (jax.hover) {
  124. if (jax.hover.remove) {clearTimeout(jax.hover.remove); delete jax.hover.remove}
  125. jax.hover.nofade = true;
  126. }
  127. //
  128. // If the menu code is loaded,
  129. // Check if localization needs loading;
  130. // If not, post the menu, and return.
  131. // Otherwise wait for the localization to load
  132. // Otherwse load the menu code.
  133. // Try again after the file is loaded.
  134. //
  135. var MENU = MathJax.Menu; var load, fn;
  136. if (MENU) {
  137. if (MENU.loadingDomain) {return EVENT.False(event)}
  138. load = LOCALE.loadDomain("MathMenu");
  139. if (!load) {
  140. MENU.jax = jax;
  141. var source = MENU.menu.Find("Show Math As").menu;
  142. source.items[0].name = jax.sourceMenuTitle;
  143. source.items[0].format = (jax.sourceMenuFormat||"MathML");
  144. source.items[1].name = INPUT[jax.inputJax].sourceMenuTitle;
  145. source.items[5].disabled = !INPUT[jax.inputJax].annotationEncoding;
  146. //
  147. // Try and find each known annotation format and enable the menu
  148. // items accordingly.
  149. //
  150. var annotations = source.items[2]; annotations.disabled = true;
  151. var annotationItems = annotations.menu.items;
  152. annotationList = MathJax.Hub.Config.semanticsAnnotations;
  153. for (var i = 0, m = annotationItems.length; i < m; i++) {
  154. var name = annotationItems[i].name[1]
  155. if (jax.root && jax.root.getAnnotation(name) !== null) {
  156. annotations.disabled = false;
  157. annotationItems[i].hidden = false;
  158. } else {
  159. annotationItems[i].hidden = true;
  160. }
  161. }
  162. var MathPlayer = MENU.menu.Find("Math Settings","MathPlayer");
  163. MathPlayer.hidden = !(jax.outputJax === "NativeMML" && HUB.Browser.hasMathPlayer);
  164. return MENU.menu.Post(event);
  165. }
  166. MENU.loadingDomain = true;
  167. fn = function () {delete MENU.loadingDomain};
  168. } else {
  169. if (AJAX.loadingMathMenu) {return EVENT.False(event)}
  170. AJAX.loadingMathMenu = true;
  171. load = AJAX.Require("[MathJax]/extensions/MathMenu.js");
  172. fn = function () {
  173. delete AJAX.loadingMathMenu;
  174. if (!MathJax.Menu) {MathJax.Menu = {}}
  175. }
  176. }
  177. var ev = {
  178. pageX:event.pageX, pageY:event.pageY,
  179. clientX:event.clientX, clientY:event.clientY
  180. };
  181. CALLBACK.Queue(
  182. load, fn, // load the file and delete the marker when done
  183. ["ContextMenu",EVENT,ev,math,force] // call this function again
  184. );
  185. return EVENT.False(event);
  186. },
  187. //
  188. // Mousedown handler for alternate means of accessing menu
  189. //
  190. AltContextMenu: function (event,math) {
  191. var JAX = OUTPUT[math.jaxID];
  192. var show = (JAX.config.showMathMenu != null ? JAX : HUB).config.showMathMenu;
  193. if (show) {
  194. show = (JAX.config.showMathMenuMSIE != null ? JAX : HUB).config.showMathMenuMSIE;
  195. if (SETTINGS.context === "MathJax" && !SETTINGS.mpContext && show) {
  196. if (!ME.noContextMenuBug || event.button !== EVENT.RIGHTBUTTON) return;
  197. } else {
  198. if (!event[EVENT.MENUKEY] || event.button !== EVENT.LEFTBUTTON) return;
  199. }
  200. return JAX.ContextMenu(event,math,true);
  201. }
  202. },
  203. ClearSelection: function () {
  204. if (ME.safariContextMenuBug) {setTimeout("window.getSelection().empty()",0)}
  205. if (document.selection) {setTimeout("document.selection.empty()",0)}
  206. },
  207. getBBox: function (span) {
  208. span.appendChild(ME.topImg);
  209. var h = ME.topImg.offsetTop, d = span.offsetHeight-h, w = span.offsetWidth;
  210. span.removeChild(ME.topImg);
  211. return {w:w, h:h, d:d};
  212. }
  213. };
  214. //
  215. // Handle hover "discoverability"
  216. //
  217. var HOVER = ME.Hover = {
  218. //
  219. // Check if we are moving from a non-MathJax element to a MathJax one
  220. // and either start fading in again (if it is fading out) or start the
  221. // timer for the hover
  222. //
  223. Mouseover: function (event,math) {
  224. if (SETTINGS.discoverable || SETTINGS.zoom === "Hover") {
  225. var from = event.fromElement || event.relatedTarget,
  226. to = event.toElement || event.target;
  227. if (from && to && (from.isMathJax != to.isMathJax ||
  228. HUB.getJaxFor(from) !== HUB.getJaxFor(to))) {
  229. var jax = this.getJaxFromMath(math);
  230. if (jax.hover) {HOVER.ReHover(jax)} else {HOVER.HoverTimer(jax,math)}
  231. return EVENT.False(event);
  232. }
  233. }
  234. },
  235. //
  236. // Check if we are moving from a MathJax element to a non-MathJax one
  237. // and either start fading out, or clear the timer if we haven't
  238. // hovered yet
  239. //
  240. Mouseout: function (event,math) {
  241. if (SETTINGS.discoverable || SETTINGS.zoom === "Hover") {
  242. var from = event.fromElement || event.relatedTarget,
  243. to = event.toElement || event.target;
  244. if (from && to && (from.isMathJax != to.isMathJax ||
  245. HUB.getJaxFor(from) !== HUB.getJaxFor(to))) {
  246. var jax = this.getJaxFromMath(math);
  247. if (jax.hover) {HOVER.UnHover(jax)} else {HOVER.ClearHoverTimer()}
  248. return EVENT.False(event);
  249. }
  250. }
  251. },
  252. //
  253. // Restart hover timer if the mouse moves
  254. //
  255. Mousemove: function (event,math) {
  256. if (SETTINGS.discoverable || SETTINGS.zoom === "Hover") {
  257. var jax = this.getJaxFromMath(math); if (jax.hover) return;
  258. if (HOVER.lastX == event.clientX && HOVER.lastY == event.clientY) return;
  259. HOVER.lastX = event.clientX; HOVER.lastY = event.clientY;
  260. HOVER.HoverTimer(jax,math);
  261. return EVENT.False(event);
  262. }
  263. },
  264. //
  265. // Clear the old timer and start a new one
  266. //
  267. HoverTimer: function (jax,math) {
  268. this.ClearHoverTimer();
  269. this.hoverTimer = setTimeout(CALLBACK(["Hover",this,jax,math]),CONFIG.hover);
  270. },
  271. ClearHoverTimer: function () {
  272. if (this.hoverTimer) {clearTimeout(this.hoverTimer); delete this.hoverTimer}
  273. },
  274. //
  275. // Handle putting up the hover frame
  276. //
  277. Hover: function (jax,math) {
  278. //
  279. // Check if Zoom handles the hover event
  280. //
  281. if (EXTENSION.MathZoom && EXTENSION.MathZoom.Hover({},math)) return;
  282. //
  283. // Get the hover data
  284. //
  285. var JAX = OUTPUT[jax.outputJax],
  286. span = JAX.getHoverSpan(jax,math),
  287. bbox = JAX.getHoverBBox(jax,span,math),
  288. show = (JAX.config.showMathMenu != null ? JAX : HUB).config.showMathMenu;
  289. var dx = CONFIG.frame.x, dy = CONFIG.frame.y, dd = CONFIG.frame.bwidth; // frame size
  290. if (ME.msieBorderWidthBug) {dd = 0}
  291. jax.hover = {opacity:0, id:jax.inputID+"-Hover"};
  292. //
  293. // The frame and menu button
  294. //
  295. var frame = HTML.Element("span",{
  296. id:jax.hover.id, isMathJax: true,
  297. style:{display:"inline-block", width:0, height:0, position:"relative"}
  298. },[["span",{
  299. className:"MathJax_Hover_Frame", isMathJax: true,
  300. style:{
  301. display:"inline-block", position:"absolute",
  302. top:this.Px(-bbox.h-dy-dd-(bbox.y||0)), left:this.Px(-dx-dd+(bbox.x||0)),
  303. width:this.Px(bbox.w+2*dx), height:this.Px(bbox.h+bbox.d+2*dy),
  304. opacity:0, filter:"alpha(opacity=0)"
  305. }}
  306. ]]
  307. );
  308. var button = HTML.Element("span",{
  309. isMathJax: true, id:jax.hover.id+"Menu",
  310. style:{display:"inline-block", "z-index": 1, width:0, height:0, position:"relative"}
  311. },[["img",{
  312. className: "MathJax_Hover_Arrow", isMathJax: true, math: math,
  313. src: CONFIG.button.src, onclick: this.HoverMenu, jax:JAX.id,
  314. style: {
  315. left:this.Px(bbox.w+dx+dd+(bbox.x||0)+CONFIG.button.x),
  316. top:this.Px(-bbox.h-dy-dd-(bbox.y||0)-CONFIG.button.y),
  317. opacity:0, filter:"alpha(opacity=0)"
  318. }
  319. }]]
  320. );
  321. if (bbox.width) {
  322. frame.style.width = button.style.width = bbox.width;
  323. frame.style.marginRight = button.style.marginRight = "-"+bbox.width;
  324. frame.firstChild.style.width = bbox.width;
  325. button.firstChild.style.left = "";
  326. button.firstChild.style.right = this.Px(CONFIG.button.wx);
  327. }
  328. //
  329. // Add the frame and button
  330. //
  331. span.parentNode.insertBefore(frame,span);
  332. if (show) {span.parentNode.insertBefore(button,span)}
  333. if (span.style) {span.style.position = "relative"} // so math is on top of hover frame
  334. //
  335. // Start the hover fade-in
  336. //
  337. this.ReHover(jax);
  338. },
  339. //
  340. // Restart the hover fade in and fade-out timers
  341. //
  342. ReHover: function (jax) {
  343. if (jax.hover.remove) {clearTimeout(jax.hover.remove)}
  344. jax.hover.remove = setTimeout(CALLBACK(["UnHover",this,jax]),CONFIG.fadeoutDelay);
  345. this.HoverFadeTimer(jax,CONFIG.fadeinInc);
  346. },
  347. //
  348. // Start the fade-out
  349. //
  350. UnHover: function (jax) {
  351. if (!jax.hover.nofade) {this.HoverFadeTimer(jax,-CONFIG.fadeoutInc,CONFIG.fadeoutStart)}
  352. },
  353. //
  354. // Handle the fade-in and fade-out
  355. //
  356. HoverFade: function (jax) {
  357. delete jax.hover.timer;
  358. jax.hover.opacity = Math.max(0,Math.min(1,jax.hover.opacity + jax.hover.inc));
  359. jax.hover.opacity = Math.floor(1000*jax.hover.opacity)/1000;
  360. var frame = document.getElementById(jax.hover.id),
  361. button = document.getElementById(jax.hover.id+"Menu");
  362. frame.firstChild.style.opacity = jax.hover.opacity;
  363. frame.firstChild.style.filter = "alpha(opacity="+Math.floor(100*jax.hover.opacity)+")";
  364. if (button) {
  365. button.firstChild.style.opacity = jax.hover.opacity;
  366. button.firstChild.style.filter = frame.style.filter;
  367. }
  368. if (jax.hover.opacity === 1) {return}
  369. if (jax.hover.opacity > 0) {this.HoverFadeTimer(jax,jax.hover.inc); return}
  370. frame.parentNode.removeChild(frame);
  371. if (button) {button.parentNode.removeChild(button)}
  372. if (jax.hover.remove) {clearTimeout(jax.hover.remove)}
  373. delete jax.hover;
  374. },
  375. //
  376. // Set the fade to in or out (via inc) and start the timer, if needed
  377. //
  378. HoverFadeTimer: function (jax,inc,delay) {
  379. jax.hover.inc = inc;
  380. if (!jax.hover.timer) {
  381. jax.hover.timer = setTimeout(CALLBACK(["HoverFade",this,jax]),(delay||CONFIG.fadeDelay));
  382. }
  383. },
  384. //
  385. // Handle a click on the menu button
  386. //
  387. HoverMenu: function (event) {
  388. if (!event) {event = window.event}
  389. return OUTPUT[this.jax].ContextMenu(event,this.math,true);
  390. },
  391. //
  392. // Clear all hover timers
  393. //
  394. ClearHover: function (jax) {
  395. if (jax.hover.remove) {clearTimeout(jax.hover.remove)}
  396. if (jax.hover.timer) {clearTimeout(jax.hover.timer)}
  397. HOVER.ClearHoverTimer();
  398. delete jax.hover;
  399. },
  400. //
  401. // Make a measurement in pixels
  402. //
  403. Px: function (m) {
  404. if (Math.abs(m) < .006) {return "0px"}
  405. return m.toFixed(2).replace(/\.?0+$/,"") + "px";
  406. },
  407. //
  408. // Preload images so they show up with the menu
  409. //
  410. getImages: function () {
  411. if (SETTINGS.discoverable) {
  412. var menu = new Image();
  413. menu.src = CONFIG.button.src;
  414. }
  415. }
  416. };
  417. //
  418. // Handle touch events.
  419. //
  420. // Use double-tap-and-hold as a replacement for context menu event.
  421. // Use double-tap as a replacement for double click.
  422. //
  423. var TOUCH = ME.Touch = {
  424. last: 0, // time of last tap event
  425. delay: 500, // delay time for double-click
  426. //
  427. // Check if this is a double-tap, and if so, start the timer
  428. // for the double-tap and hold (to trigger the contextual menu)
  429. //
  430. start: function (event) {
  431. var now = new Date().getTime();
  432. var dblTap = (now - TOUCH.last < TOUCH.delay && TOUCH.up);
  433. TOUCH.last = now; TOUCH.up = false;
  434. if (dblTap) {
  435. TOUCH.timeout = setTimeout(TOUCH.menu,TOUCH.delay,event,this);
  436. event.preventDefault();
  437. }
  438. },
  439. //
  440. // Check if there is a timeout pending, i.e., we have a
  441. // double-tap and were waiting to see if it is held long
  442. // enough for the menu. Since we got the end before the
  443. // timeout, it is a double-click, not a double-tap-and-hold.
  444. // Prevent the default action and issue a double click.
  445. //
  446. end: function (event) {
  447. var now = new Date().getTime();
  448. TOUCH.up = (now - TOUCH.last < TOUCH.delay);
  449. if (TOUCH.timeout) {
  450. clearTimeout(TOUCH.timeout);
  451. delete TOUCH.timeout; TOUCH.last = 0; TOUCH.up = false;
  452. event.preventDefault();
  453. return EVENT.Handler((event.touches[0]||event.touch),"DblClick",this);
  454. }
  455. },
  456. //
  457. // If the timeout passes without an end event, we issue
  458. // the contextual menu event.
  459. //
  460. menu: function (event,math) {
  461. delete TOUCH.timeout; TOUCH.last = 0; TOUCH.up = false;
  462. return EVENT.Handler((event.touches[0]||event.touch),"ContextMenu",math);
  463. }
  464. };
  465. //
  466. // Mobile screens are small, so use larger version of arrow
  467. //
  468. if (HUB.Browser.isMobile) {
  469. var arrow = CONFIG.styles[".MathJax_Hover_Arrow"];
  470. arrow.width = "25px"; arrow.height = "18px";
  471. CONFIG.button.x = -6;
  472. }
  473. //
  474. // Set up browser-specific values
  475. //
  476. HUB.Browser.Select({
  477. MSIE: function (browser) {
  478. var mode = (document.documentMode || 0);
  479. var isIE8 = browser.versionAtLeast("8.0");
  480. ME.msieBorderWidthBug = (document.compatMode === "BackCompat"); // borders are inside offsetWidth/Height
  481. ME.msieEventBug = browser.isIE9; // must get event from window even though event is passed
  482. ME.msieAlignBug = (!isIE8 || mode < 8); // inline-block spans don't rest on baseline
  483. if (mode < 9) {EVENT.LEFTBUTTON = 1} // IE < 9 has wrong event.button values
  484. },
  485. Safari: function (browser) {
  486. ME.safariContextMenuBug = true; // selection can be started by contextmenu event
  487. },
  488. Opera: function (browser) {
  489. ME.operaPositionBug = true; // position is wrong unless border is used
  490. },
  491. Konqueror: function (browser) {
  492. ME.noContextMenuBug = true; // doesn't produce contextmenu event
  493. }
  494. });
  495. //
  496. // Used in measuring zoom and hover positions
  497. //
  498. ME.topImg = (ME.msieAlignBug ?
  499. HTML.Element("img",{style:{width:0,height:0,position:"relative"},src:"about:blank"}) :
  500. HTML.Element("span",{style:{width:0,height:0,display:"inline-block"}})
  501. );
  502. if (ME.operaPositionBug) {ME.topImg.style.border="1px solid"}
  503. //
  504. // Get configuration from user
  505. //
  506. ME.config = CONFIG = HUB.CombineConfig("MathEvents",CONFIG);
  507. var SETFRAME = function () {
  508. var haze = CONFIG.styles[".MathJax_Hover_Frame"];
  509. haze.border = CONFIG.frame.bwidth+"px solid "+CONFIG.frame.bcolor+" ! important";
  510. haze["box-shadow"] = haze["-webkit-box-shadow"] =
  511. haze["-moz-box-shadow"] = haze["-khtml-box-shadow"] =
  512. "0px 0px "+CONFIG.frame.hwidth+" "+CONFIG.frame.hcolor;
  513. };
  514. //
  515. // Queue the events needed for startup
  516. //
  517. CALLBACK.Queue(
  518. HUB.Register.StartupHook("End Config",{}), // wait until config is complete
  519. [SETFRAME],
  520. ["getImages",HOVER],
  521. ["Styles",AJAX,CONFIG.styles],
  522. ["Post",HUB.Startup.signal,"MathEvents Ready"],
  523. ["loadComplete",AJAX,"[MathJax]/extensions/MathEvents.js"]
  524. );
  525. })(MathJax.Hub,MathJax.HTML,MathJax.Ajax,MathJax.Callback,
  526. MathJax.Localization,MathJax.OutputJax,MathJax.InputJax);