www

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

MathZoom.js (14866B)


      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/MathZoom.js
      7  *  
      8  *  Implements the zoom feature for enlarging math expressions.  It is
      9  *  loaded automatically when the Zoom menu selection changes from "None".
     10  *
     11  *  ---------------------------------------------------------------------
     12  *  
     13  *  Copyright (c) 2010-2015 The MathJax Consortium
     14  * 
     15  *  Licensed under the Apache License, Version 2.0 (the "License");
     16  *  you may not use this file except in compliance with the License.
     17  *  You may obtain a copy of the License at
     18  * 
     19  *      http://www.apache.org/licenses/LICENSE-2.0
     20  * 
     21  *  Unless required by applicable law or agreed to in writing, software
     22  *  distributed under the License is distributed on an "AS IS" BASIS,
     23  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     24  *  See the License for the specific language governing permissions and
     25  *  limitations under the License.
     26  */
     27 
     28 (function (HUB,HTML,AJAX,HTMLCSS,nMML) {
     29   var VERSION = "2.6.0";
     30   
     31   var CONFIG = HUB.CombineConfig("MathZoom",{
     32     styles: {
     33       //
     34       //  The styles for the MathZoom display box
     35       //
     36       "#MathJax_Zoom": {
     37         position:"absolute", "background-color":"#F0F0F0", overflow:"auto",
     38         display:"block", "z-index":301, padding:".5em", border:"1px solid black", margin:0,
     39         "font-weight":"normal", "font-style":"normal",
     40         "text-align":"left", "text-indent":0, "text-transform":"none",
     41         "line-height":"normal", "letter-spacing":"normal", "word-spacing":"normal",
     42         "word-wrap":"normal", "white-space":"nowrap", "float":"none",
     43         "-webkit-box-sizing":"content-box",          // Android ≤ 2.3, iOS ≤ 4
     44         "-moz-box-sizing":"content-box",             // Firefox ≤ 28
     45         "box-sizing":"content-box",                  // Chrome, Firefox 29+, IE 8+, Opera, Safari 5.1
     46         "box-shadow":"5px 5px 15px #AAAAAA",         // Opera 10.5 and IE9
     47         "-webkit-box-shadow":"5px 5px 15px #AAAAAA", // Safari 3 and Chrome
     48         "-moz-box-shadow":"5px 5px 15px #AAAAAA",    // Forefox 3.5
     49         "-khtml-box-shadow":"5px 5px 15px #AAAAAA",  // Konqueror
     50         filter: "progid:DXImageTransform.Microsoft.dropshadow(OffX=2, OffY=2, Color='gray', Positive='true')" // IE
     51       },
     52       
     53       //
     54       //  The styles for the hidden overlay (should not need to be adjusted by the page author)
     55       //
     56       "#MathJax_ZoomOverlay": {
     57         position:"absolute", left:0, top:0, "z-index":300, display:"inline-block",
     58         width:"100%", height:"100%", border:0, padding:0, margin:0,
     59         "background-color":"white", opacity:0, filter:"alpha(opacity=0)"
     60       },
     61       
     62       "#MathJax_ZoomFrame": {
     63         position:"relative", display:"inline-block",
     64         height:0, width:0
     65       },
     66       
     67       "#MathJax_ZoomEventTrap": {
     68         position:"absolute", left:0, top:0, "z-index":302,
     69         display:"inline-block", border:0, padding:0, margin:0,
     70         "background-color":"white", opacity:0, filter:"alpha(opacity=0)"
     71       }
     72     }
     73   });
     74   
     75   var FALSE, HOVER, EVENT;
     76   MathJax.Hub.Register.StartupHook("MathEvents Ready",function () {
     77     EVENT = MathJax.Extension.MathEvents.Event;
     78     FALSE = MathJax.Extension.MathEvents.Event.False;
     79     HOVER = MathJax.Extension.MathEvents.Hover;
     80   });
     81 
     82   /*************************************************************/
     83 
     84   var ZOOM = MathJax.Extension.MathZoom = {
     85     version: VERSION,
     86     settings: HUB.config.menuSettings,
     87     scrollSize: 18,    // width of scrool bars
     88 
     89     //
     90     //  Process events passed from output jax
     91     //
     92     HandleEvent: function (event,type,math) {
     93       if (ZOOM.settings.CTRL  && !event.ctrlKey)  return true;
     94       if (ZOOM.settings.ALT   && !event.altKey)   return true;
     95       if (ZOOM.settings.CMD   && !event.metaKey)  return true;
     96       if (ZOOM.settings.Shift && !event.shiftKey) return true;
     97       if (!ZOOM[type]) return true;
     98       return ZOOM[type](event,math);
     99     },
    100     
    101     //
    102     //  Zoom on click
    103     //
    104     Click: function (event,math) {
    105       if (this.settings.zoom === "Click") {return this.Zoom(event,math)}
    106     },
    107     
    108     //
    109     //  Zoom on double click
    110     //
    111     DblClick: function (event,math) {
    112       if (this.settings.zoom === "Double-Click" || this.settings.zoom === "DoubleClick") {return this.Zoom(event,math)}
    113     },
    114     
    115     //
    116     //  Zoom on hover (called by MathEvents.Hover)
    117     //
    118     Hover: function (event,math) {
    119       if (this.settings.zoom === "Hover") {this.Zoom(event,math); return true}
    120       return false;
    121     },
    122     
    123     
    124     //
    125     //  Handle the actual zooming
    126     //
    127     Zoom: function (event,math) {
    128       //
    129       //  Remove any other zoom and clear timers
    130       //
    131       this.Remove(); HOVER.ClearHoverTimer(); EVENT.ClearSelection();
    132 
    133       //
    134       //  Find the jax
    135       //
    136       var JAX = MathJax.OutputJax[math.jaxID];
    137       var jax = JAX.getJaxFromMath(math);
    138       if (jax.hover) {HOVER.UnHover(jax)}
    139 
    140       //
    141       //  Create the DOM elements for the zoom box
    142       //
    143       var container = this.findContainer(math);
    144       var Mw = Math.floor(.85*container.clientWidth),
    145           Mh = Math.max(document.body.clientHeight,document.documentElement.clientHeight);
    146       if (this.getOverflow(container) !== "visible") {Mh = Math.min(container.clientHeight,Mh)}
    147       Mh = Math.floor(.85*Mh);
    148       var div = HTML.Element(
    149         "span",{id:"MathJax_ZoomFrame"},[
    150           ["span",{id:"MathJax_ZoomOverlay", onmousedown:this.Remove}],
    151           ["span",{
    152             id:"MathJax_Zoom", onclick:this.Remove,
    153             style:{visibility:"hidden", fontSize:this.settings.zscale}
    154           },[["span",{style:{display:"inline-block", "white-space":"nowrap"}}]]
    155         ]]
    156       );
    157       var zoom = div.lastChild, span = zoom.firstChild, overlay = div.firstChild;
    158       math.parentNode.insertBefore(div,math); math.parentNode.insertBefore(math,div); // put div after math
    159       if (span.addEventListener) {span.addEventListener("mousedown",this.Remove,true)}
    160       var eW = zoom.offsetWidth || zoom.clientWidth; Mw -= eW; Mh -= eW;
    161       zoom.style.maxWidth = Mw+"px"; zoom.style.maxHeight = Mh+"px";
    162 
    163       if (this.msieTrapEventBug) {
    164         var trap = HTML.Element("span",{id:"MathJax_ZoomEventTrap", onmousedown:this.Remove});
    165         div.insertBefore(trap,zoom);
    166       }
    167 
    168       //
    169       //  Display the zoomed math
    170       //
    171       if (this.msieZIndexBug) {
    172         //  MSIE doesn't do z-index properly, so move the div to the document.body,
    173         //  and use an image as a tracker for the usual position
    174         var tracker = HTML.addElement(document.body,"img",{
    175           src:"about:blank", id:"MathJax_ZoomTracker", width:0, height:0,
    176           style:{width:0, height:0, position:"relative"}
    177         });
    178         div.style.position = "relative";
    179         div.style.zIndex = CONFIG.styles["#MathJax_ZoomOverlay"]["z-index"];
    180         div = tracker;
    181       }
    182 
    183       var bbox = JAX.Zoom(jax,span,math,Mw,Mh);
    184       
    185       //
    186       //  Fix up size and position for browsers with bugs (IE)
    187       //
    188       if (this.msiePositionBug) {
    189         if (this.msieSizeBug) 
    190           {zoom.style.height = bbox.zH+"px"; zoom.style.width = bbox.zW+"px"} // IE8 gets the dimensions completely wrong
    191         if (zoom.offsetHeight > Mh) {zoom.style.height = Mh+"px"; zoom.style.width = (bbox.zW+this.scrollSize)+"px"}  // IE doesn't do max-height?
    192         if (zoom.offsetWidth  > Mw) {zoom.style.width  = Mw+"px"; zoom.style.height = (bbox.zH+this.scrollSize)+"px"}
    193       }
    194       if (this.operaPositionBug) {zoom.style.width = Math.min(Mw,bbox.zW)+"px"}  // Opera gets width as 0?
    195       if (zoom.offsetWidth > eW && zoom.offsetWidth-eW < Mw && zoom.offsetHeight-eW < Mh)
    196          {zoom.style.overflow = "visible"}  // don't show scroll bars if we don't need to
    197       this.Position(zoom,bbox);
    198       if (this.msieTrapEventBug) {
    199         trap.style.height = zoom.clientHeight+"px"; trap.style.width = zoom.clientWidth+"px";
    200         trap.style.left = (parseFloat(zoom.style.left)+zoom.clientLeft)+"px";
    201         trap.style.top = (parseFloat(zoom.style.top)+zoom.clientTop)+"px";
    202       }
    203       zoom.style.visibility = "";
    204 
    205       //
    206       //  Add event handlers
    207       //
    208       if (this.settings.zoom === "Hover") {overlay.onmouseover = this.Remove}
    209       if (window.addEventListener) {addEventListener("resize",this.Resize,false)}
    210       else if (window.attachEvent) {attachEvent("onresize",this.Resize)}
    211       else {this.onresize = window.onresize; window.onresize = this.Resize}
    212       
    213       //
    214       //  Let others know about the zoomed math
    215       //
    216       HUB.signal.Post(["math zoomed",jax]);
    217       
    218       //
    219       //  Canel further actions
    220       //
    221       return FALSE(event);
    222     },
    223     
    224     //
    225     //  Set the position of the zoom box and overlay
    226     //
    227     Position: function (zoom,bbox) {
    228       zoom.style.display = "none"; // avoids getting excessive width in Resize()
    229       var XY = this.Resize(), x = XY.x, y = XY.y, W = bbox.mW;
    230       zoom.style.display = "";
    231       var dx = -W-Math.floor((zoom.offsetWidth-W)/2), dy = bbox.Y;
    232       zoom.style.left = Math.max(dx,10-x)+"px"; zoom.style.top = Math.max(dy,10-y)+"px";
    233       if (!ZOOM.msiePositionBug) {ZOOM.SetWH()} // refigure overlay width/height
    234     },
    235     
    236     //
    237     //  Handle resizing of overlay while zoom is displayed
    238     //
    239     Resize: function (event) {
    240       if (ZOOM.onresize) {ZOOM.onresize(event)}
    241       var div = document.getElementById("MathJax_ZoomFrame"),
    242           overlay = document.getElementById("MathJax_ZoomOverlay");
    243       var xy = ZOOM.getXY(div), obj = ZOOM.findContainer(div);
    244       if (ZOOM.getOverflow(obj) !== "visible") {
    245         overlay.scroll_parent = obj;  // Save this for future reference.
    246         var XY = ZOOM.getXY(obj);     // Remove container position
    247         xy.x -= XY.x; xy.y -= XY.y;
    248         XY = ZOOM.getBorder(obj);     // Remove container border
    249         xy.x -= XY.x; xy.y -= XY.y;
    250       }
    251       overlay.style.left = (-xy.x)+"px"; overlay.style.top = (-xy.y)+"px";
    252       if (ZOOM.msiePositionBug) {setTimeout(ZOOM.SetWH,0)} else {ZOOM.SetWH()}
    253       return xy;
    254     },
    255     SetWH: function () {
    256       var overlay = document.getElementById("MathJax_ZoomOverlay");
    257       if (!overlay) return;
    258       overlay.style.display = "none"; // so scrollWidth/Height will be right below
    259       var doc = overlay.scroll_parent || document.documentElement || document.body;
    260       overlay.style.width = doc.scrollWidth + "px";
    261       overlay.style.height = Math.max(doc.clientHeight,doc.scrollHeight) + "px";
    262       overlay.style.display = "";
    263     },
    264     findContainer: function (obj) {
    265       obj = obj.parentNode;
    266       while (obj.parentNode && obj !== document.body && ZOOM.getOverflow(obj) === "visible")
    267         {obj = obj.parentNode}
    268       return obj;
    269     },
    270     //
    271     //  Look up CSS properties (use getComputeStyle if available, or currentStyle if not)
    272     //
    273     getOverflow: (window.getComputedStyle ?
    274       function (obj) {return getComputedStyle(obj).overflow} :
    275       function (obj) {return (obj.currentStyle||{overflow:"visible"}).overflow}),
    276     getBorder: function (obj) {
    277       var size = {thin: 1, medium: 2, thick: 3};
    278       var style = (window.getComputedStyle ? getComputedStyle(obj) : 
    279                      (obj.currentStyle || {borderLeftWidth:0,borderTopWidth:0}));
    280       var x = style.borderLeftWidth, y = style.borderTopWidth;
    281       if (size[x]) {x = size[x]} else {x = parseInt(x)}
    282       if (size[y]) {y = size[y]} else {y = parseInt(y)}
    283       return {x:x, y:y};
    284     },
    285     //
    286     //  Get the position of an element on the page
    287     //
    288     getXY: function (div) {
    289       var x = 0, y = 0, obj;
    290       obj = div; while (obj.offsetParent) {x += obj.offsetLeft; obj = obj.offsetParent}
    291       if (ZOOM.operaPositionBug) {div.style.border = "1px solid"}  // to get vertical position right
    292       obj = div; while (obj.offsetParent) {y += obj.offsetTop; obj = obj.offsetParent}
    293       if (ZOOM.operaPositionBug) {div.style.border = ""}
    294       return {x:x, y:y};
    295     },
    296     
    297     //
    298     //  Remove zoom display and event handlers
    299     //
    300     Remove: function (event) {
    301       var div = document.getElementById("MathJax_ZoomFrame");
    302       if (div) {
    303         var JAX = MathJax.OutputJax[div.previousSibling.jaxID];
    304         var jax = JAX.getJaxFromMath(div.previousSibling);
    305         HUB.signal.Post(["math unzoomed",jax]);
    306         div.parentNode.removeChild(div);
    307         div = document.getElementById("MathJax_ZoomTracker");
    308         if (div) {div.parentNode.removeChild(div)}
    309         if (ZOOM.operaRefreshBug) {
    310 	  // force a redisplay of the page
    311 	  // (Opera doesn't refresh properly after the zoom is removed)
    312           var overlay = HTML.addElement(document.body,"div",{
    313             style:{position:"fixed", left:0, top:0, width:"100%", height:"100%",
    314                    backgroundColor:"white", opacity:0},
    315             id: "MathJax_OperaDiv"
    316           });
    317           document.body.removeChild(overlay);
    318         }
    319         if (window.removeEventListener) {removeEventListener("resize",ZOOM.Resize,false)}
    320         else if (window.detachEvent) {detachEvent("onresize",ZOOM.Resize)}
    321         else {window.onresize = ZOOM.onresize; delete ZOOM.onresize}
    322       }
    323       return FALSE(event);
    324     }
    325     
    326   };
    327   
    328   
    329   /*************************************************************/
    330 
    331   HUB.Browser.Select({
    332     MSIE: function (browser) {
    333       var mode  = (document.documentMode || 0);
    334       var isIE9 = (mode >= 9);
    335       ZOOM.msiePositionBug = !isIE9;
    336       ZOOM.msieSizeBug = browser.versionAtLeast("7.0") &&
    337         (!document.documentMode || mode === 7 || mode === 8);
    338       ZOOM.msieZIndexBug = (mode <= 7);
    339       ZOOM.msieInlineBlockAlignBug = (mode <= 7);
    340       ZOOM.msieTrapEventBug = !window.addEventListener;
    341       if (document.compatMode === "BackCompat") {ZOOM.scrollSize = 52} // don't know why this is so far off
    342       if (isIE9) {delete CONFIG.styles["#MathJax_Zoom"].filter}
    343     },
    344     
    345     Opera: function (browser) {
    346       ZOOM.operaPositionBug = true;
    347       ZOOM.operaRefreshBug = true;
    348     }
    349   });
    350   
    351   ZOOM.topImg = (ZOOM.msieInlineBlockAlignBug ?
    352     HTML.Element("img",{style:{width:0,height:0,position:"relative"},src:"about:blank"}) :
    353     HTML.Element("span",{style:{width:0,height:0,display:"inline-block"}})
    354   );
    355   if (ZOOM.operaPositionBug || ZOOM.msieTopBug) {ZOOM.topImg.style.border="1px solid"}
    356 
    357   /*************************************************************/
    358 
    359   MathJax.Callback.Queue(
    360     ["StartupHook",MathJax.Hub.Register,"Begin Styles",{}],
    361     ["Styles",AJAX,CONFIG.styles],
    362     ["Post",HUB.Startup.signal,"MathZoom Ready"],
    363     ["loadComplete",AJAX,"[MathJax]/extensions/MathZoom.js"]
    364   );
    365 
    366 })(MathJax.Hub,MathJax.HTML,MathJax.Ajax,MathJax.OutputJax["HTML-CSS"],MathJax.OutputJax.NativeMML);