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