www

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

MathEvents.js (22300B)


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