www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
   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);