410 lines
16 KiB
JavaScript
410 lines
16 KiB
JavaScript
|
/*************************************************************
|
||
|
*
|
||
|
* MathJax/extensions/MathZoom.js
|
||
|
*
|
||
|
* Implements the zoom feature for enlarging math expressions. It is
|
||
|
* loaded automatically when the Zoom menu selection changes from "None".
|
||
|
*
|
||
|
* ---------------------------------------------------------------------
|
||
|
*
|
||
|
* Copyright (c) 2010-2011 Design Science, Inc.
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
(function (HUB,HTML,AJAX,HTMLCSS,nMML) {
|
||
|
var VERSION = "1.1";
|
||
|
|
||
|
var CONFIG = HUB.CombineConfig("MathZoom",{
|
||
|
delay: 400, // mouse must be still this long (milliseconds)
|
||
|
|
||
|
styles: {
|
||
|
//
|
||
|
// The styles for the MathZoom display box
|
||
|
//
|
||
|
"#MathJax_Zoom": {
|
||
|
position:"absolute", "background-color":"#F0F0F0", overflow:"auto",
|
||
|
display:"block", "z-index":301, padding:".5em", border:"1px solid black", margin:0,
|
||
|
"font-family":"serif", "font-size":"85%", "font-weight":"normal",
|
||
|
"font-style":"normal", "text-align":"left", "text-indent":0, "text-transform":"none",
|
||
|
"line-height":"normal", "letter-spacing":"normal", "word-spacing":"normal",
|
||
|
"word-wrap":"normal", "white-space":"nowrap", "float":"none",
|
||
|
"box-shadow":"5px 5px 15px #AAAAAA", // Opera 10.5 and IE9
|
||
|
"-webkit-box-shadow":"5px 5px 15px #AAAAAA", // Safari 3 and Chrome
|
||
|
"-moz-box-shadow":"5px 5px 15px #AAAAAA", // Forefox 3.5
|
||
|
"-khtml-box-shadow":"5px 5px 15px #AAAAAA", // Konqueror
|
||
|
filter: "progid:DXImageTransform.Microsoft.dropshadow(OffX=2, OffY=2, Color='gray', Positive='true')" // IE
|
||
|
},
|
||
|
|
||
|
//
|
||
|
// The styles for the hidden overlay (should not need to be adjusted by the page author)
|
||
|
//
|
||
|
"#MathJax_ZoomOverlay": {
|
||
|
position:"absolute", left:0, top:0, "z-index":300, display:"inline-block",
|
||
|
width:"100%", height:"100%", border:0, padding:0, margin:0,
|
||
|
"background-color":"white", opacity:0, filter:"alpha(opacity=0)"
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
/*************************************************************/
|
||
|
/*
|
||
|
* Cancel event's default action (try everything we can)
|
||
|
*/
|
||
|
var FALSE = function (event) {
|
||
|
if (!event) {event = window.event}
|
||
|
if (event) {
|
||
|
if (event.preventDefault) {event.preventDefault()}
|
||
|
if (event.stopPropagation) {event.stopPropagation()}
|
||
|
event.cancelBubble = true;
|
||
|
event.returnValue = false;
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
/*************************************************************/
|
||
|
|
||
|
var ZOOM = MathJax.Extension.MathZoom = {
|
||
|
version: VERSION,
|
||
|
settings: HUB.config.menuSettings,
|
||
|
|
||
|
//
|
||
|
// Used to override HTMLCSS or nMML method of the same name
|
||
|
//
|
||
|
HandleEvent: function (event,type,math) {
|
||
|
if (!event) {event = window.event}
|
||
|
if (ZOOM.settings.CTRL && !event.ctrlKey) return true;
|
||
|
if (ZOOM.settings.ALT && !event.altKey) return true;
|
||
|
if (ZOOM.settings.CMD && !event.metaKey) return true;
|
||
|
if (ZOOM.settings.Shift && !event.shiftKey) return true;
|
||
|
return ZOOM[type](event,math);
|
||
|
},
|
||
|
|
||
|
//
|
||
|
// Zoom on click
|
||
|
//
|
||
|
Click: function (event,math) {
|
||
|
if (this.settings.zoom === "Click") {return this.Zoom(math,event)}
|
||
|
},
|
||
|
|
||
|
//
|
||
|
// Zoom on double click
|
||
|
//
|
||
|
DblClick: function (event,math) {
|
||
|
if (this.settings.zoom === "Double-Click") {return this.Zoom(math,event)}
|
||
|
},
|
||
|
|
||
|
//
|
||
|
// Zoom on hover
|
||
|
//
|
||
|
Mouseover: function (event,math) {
|
||
|
if (this.settings.zoom === "Hover") {
|
||
|
ZOOM.oldMouseOver = math.onmouseover;
|
||
|
math.onmouseover = null;
|
||
|
math.onmousemove = this.Mousemove;
|
||
|
math.onmouseout = this.Mouseout;
|
||
|
return ZOOM.Timer(event,math);
|
||
|
}
|
||
|
},
|
||
|
Mouseout: function (event) {
|
||
|
this.onmouseover = ZOOM.oldMouseOver; delete ZOOM.oldMouseOver;
|
||
|
this.onmousemove = this.onmouseout = null;
|
||
|
ZOOM.ClearTimer();
|
||
|
return FALSE(event);
|
||
|
},
|
||
|
Mousemove: function (event) {
|
||
|
return ZOOM.Timer(event||window.event,this);
|
||
|
},
|
||
|
Timer: function (event,math) {
|
||
|
this.ClearTimer();
|
||
|
this.timer = setTimeout(MathJax.Callback(["Zoom",this,math,{}]),CONFIG.delay);
|
||
|
return FALSE(event);
|
||
|
},
|
||
|
ClearTimer: function () {
|
||
|
if (this.timer) {clearTimeout(this.timer); delete this.timer}
|
||
|
},
|
||
|
|
||
|
//
|
||
|
// Handle the actual zooming
|
||
|
//
|
||
|
Zoom: function (math,event) {
|
||
|
this.ClearTimer(); this.Remove();
|
||
|
|
||
|
//
|
||
|
// Find the jax and its type
|
||
|
//
|
||
|
var parent = math.parentNode;
|
||
|
if (parent.className === "MathJax_MathContainer") {parent = parent.parentNode}
|
||
|
if (parent.parentNode.className === "MathJax_MathContainer") {parent = parent.parentNode.parentNode}
|
||
|
var script = (String(parent.className).match(/^MathJax_(MathML|Display)$/) ? parent : math).nextSibling;
|
||
|
var jax = HUB.getJaxFor(script), root = jax.root;
|
||
|
var JAX = (HTMLCSS && jax.outputJax.isa(HTMLCSS.constructor) ? "HTMLCSS" :
|
||
|
(nMML && jax.outputJax.isa(nMML.constructor) ? "MathML" : null));
|
||
|
if (!JAX) return; // FIXME: report an error?
|
||
|
|
||
|
//
|
||
|
// Create the DOM elements for the zoom box
|
||
|
//
|
||
|
var Mw = Math.floor(.85*document.body.clientWidth),
|
||
|
Mh = Math.floor(.85*document.body.clientHeight);
|
||
|
var div = HTML.Element(
|
||
|
"span",{
|
||
|
style: {position:"relative", display:"inline-block", height:0, width:0},
|
||
|
id:"MathJax_ZoomFrame"
|
||
|
},[
|
||
|
["span",{id:"MathJax_ZoomOverlay", onmousedown:this.Remove}],
|
||
|
["span",{
|
||
|
id:"MathJax_Zoom", onclick: this.Remove,
|
||
|
style:{
|
||
|
visibility:"hidden", fontSize:this.settings.zscale,
|
||
|
"max-width":Mw+"px", "max-height":Mh+"px"
|
||
|
}
|
||
|
},[["span"]]]
|
||
|
]
|
||
|
);
|
||
|
var zoom = div.lastChild, span = zoom.firstChild, overlay = div.firstChild;
|
||
|
math.parentNode.insertBefore(div,math);
|
||
|
|
||
|
//
|
||
|
// Display the zoomed math
|
||
|
//
|
||
|
if (this.msieZIndexBug) {
|
||
|
// MSIE doesn't do z-index properly, so move the div to the document.body,
|
||
|
// and use an empty span as a tracker for the usual position
|
||
|
var tracker = HTML.Element("img",{
|
||
|
src:"about:blank", id:"MathJax_ZoomTracker",
|
||
|
style:{width:0, height:0, position:"relative"}
|
||
|
});
|
||
|
document.body.appendChild(div);
|
||
|
div.style.position = "absolute";
|
||
|
div.style.zIndex = CONFIG.styles["#MathJax_ZoomOverlay"]["z-index"];
|
||
|
div = tracker;
|
||
|
}
|
||
|
var bbox = (this["Zoom"+JAX])(root,span,math);
|
||
|
if (this.msiePositionBug) {
|
||
|
if (this.msieIE8Bug) {
|
||
|
// IE8 gets height completely wrong
|
||
|
span.style.position = "absolute"; zoom.style.height = span.offsetHeight; span.style.position = "";
|
||
|
if (zoom.offsetHeight <= Mh && zoom.offsetWidth <= Mw) {zoom.style.overflow = "visible"}
|
||
|
}
|
||
|
if (this.msieWidthBug) {zoom.style.width = Math.min(Mw,bbox.w)}
|
||
|
else if (bbox.w > Mw) {zoom.style.width = Mw}
|
||
|
if (zoom.offsetHeight > Mh) {zoom.style.Height = Mh+"px"} // IE doesn't do max-height?
|
||
|
if (math.nextSibling) {math.parentNode.insertBefore(div,math.nextSibling)}
|
||
|
else {parent.appendChild(div)} // needs to be after to be above?
|
||
|
} else if (this.operaPositionBug) {
|
||
|
zoom.style.width = Math.min(Mw,span.offsetWidth)+"px"; // gets width as 0?
|
||
|
}
|
||
|
this.Position(zoom,bbox,(JAX === "MathML" && parent.nodeName.toLowerCase() === "div"));
|
||
|
zoom.style.visibility = "";
|
||
|
|
||
|
//
|
||
|
// Add event handlers
|
||
|
//
|
||
|
if (this.settings.zoom === "Hover") {overlay.onmouseover = this.Remove}
|
||
|
if (window.addEventListener) {addEventListener("resize",this.Resize,false)}
|
||
|
else if (window.attachEvent) {attachEvent("onresize",this.Resize)}
|
||
|
else {this.onresize = window.onresize; window.onresize = this.Resize}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Canel further actions
|
||
|
//
|
||
|
return FALSE(event);
|
||
|
},
|
||
|
|
||
|
//
|
||
|
// Handle the jax-specific output
|
||
|
//
|
||
|
ZoomHTMLCSS: function (root,span,math) {
|
||
|
span.className = "MathJax";
|
||
|
HTMLCSS.idPostfix = "-zoom";
|
||
|
HTMLCSS.getScales(span,span);
|
||
|
root.toHTML(span,span);
|
||
|
var bbox = root.HTMLspanElement().bbox;
|
||
|
HTMLCSS.idPostfix = "";
|
||
|
if (bbox.width && bbox.width !== "100%") {
|
||
|
// Handle full-width displayed equations
|
||
|
// FIXME: this is a hack for now
|
||
|
var Mw = Math.floor(.85*document.body.clientWidth);
|
||
|
span.style.width = Mw+"px"; span.style.display="inline-block";
|
||
|
var id = (root.id||"MathJax-Span-"+root.spanID)+"-zoom";
|
||
|
var child = document.getElementById(id).firstChild;
|
||
|
while (child && child.style.width !== bbox.width) {child = child.nextSibling}
|
||
|
if (child) {child.style.width = "100%"}
|
||
|
}
|
||
|
span.appendChild(this.topImg); var top = this.topImg.offsetTop; span.removeChild(this.topImg);
|
||
|
var W = (this.msieWidthBug ? HTMLCSS.getW(math)*HTMLCSS.em : math.offsetWidth);
|
||
|
return {w:bbox.w*HTMLCSS.em, Y:-top, W:W};
|
||
|
},
|
||
|
ZoomMathML: function (root,span,math) {
|
||
|
root.toNativeMML(span,span); var top;
|
||
|
span.appendChild(this.topImg); top = this.topImg.offsetTop; span.removeChild(this.topImg);
|
||
|
var W = (this.ffMMLwidthBug ? math.parentNode : math).offsetWidth;
|
||
|
return {w:span.offsetWidth, Y:-top, W:W}
|
||
|
},
|
||
|
|
||
|
//
|
||
|
// Set the position of the zoom box and overlay
|
||
|
//
|
||
|
Position: function (zoom,bbox,MMLdisplay) {
|
||
|
var XY = this.Resize(), x = XY.x, y = XY.y, W = bbox.W;
|
||
|
if (this.msiePositionBug) {W = -W}
|
||
|
if (MMLdisplay && this.ffMMLcenterBug) {W = 0}
|
||
|
var dx = -Math.floor((zoom.offsetWidth-W)/2), dy = bbox.Y;
|
||
|
zoom.style.left = Math.max(dx,20-x)+"px"; zoom.style.top = Math.max(dy,20-y)+"px";
|
||
|
},
|
||
|
|
||
|
//
|
||
|
// Handle resizing of overlay while zoom is displayed
|
||
|
//
|
||
|
Resize: function (event) {
|
||
|
if (ZOOM.onresize) {ZOOM.onresize(event)}
|
||
|
var x = 0, y = 0,
|
||
|
div = document.getElementById("MathJax_ZoomFrame"),
|
||
|
overlay = document.getElementById("MathJax_ZoomOverlay");
|
||
|
var obj = (ZOOM.msieZIndexBug ? document.getElementById("MathJax_ZoomTracker") : div);
|
||
|
if (ZOOM.operaPositionBug) {div.style.border = "1px solid"} // to get vertical position right
|
||
|
if (obj.offsetParent) {
|
||
|
do {x += obj.offsetLeft; y += obj.offsetTop} while (obj = obj.offsetParent);
|
||
|
}
|
||
|
if (ZOOM.operaPositionBug) {div.style.border = ""}
|
||
|
if (ZOOM.msieZIndexBug) {div.style.left = x+"px"; div.style.top = y+"px"}
|
||
|
overlay.style.left = (-x)+"px"; overlay.style.top = (-y)+"px";
|
||
|
if (ZOOM.msiePositionBug) {setTimeout(ZOOM.SetWH,0)} else {ZOOM.SetWH()}
|
||
|
return {x:x, y:y};
|
||
|
},
|
||
|
SetWH: function () {
|
||
|
var overlay = document.getElementById("MathJax_ZoomOverlay");
|
||
|
overlay.style.width = overlay.style.height = "1px"; // so scrollWidth/Height will be right below
|
||
|
overlay.style.width = document.body.scrollWidth + "px";
|
||
|
overlay.style.height = document.body.scrollHeight + "px";
|
||
|
},
|
||
|
|
||
|
//
|
||
|
// Remove zoom display and event handlers
|
||
|
//
|
||
|
Remove: function (event) {
|
||
|
var div = document.getElementById("MathJax_ZoomFrame");
|
||
|
if (div) {
|
||
|
div.parentNode.removeChild(div);
|
||
|
div = document.getElementById("MathJax_ZoomTracker");
|
||
|
if (div) {div.parentNode.removeChild(div)}
|
||
|
if (ZOOM.operaRefreshBug) {
|
||
|
// force a redisplay of the page
|
||
|
// (Opera doesn't refresh properly after the zoom is removed)
|
||
|
var overlay = HTML.addElement(document.body,"div",{
|
||
|
style:{position:"fixed", left:0, top:0, width:"100%", height:"100%",
|
||
|
backgroundColor:"white", opacity:0},
|
||
|
id: "MathJax_OperaDiv"
|
||
|
});
|
||
|
document.body.removeChild(overlay);
|
||
|
}
|
||
|
if (window.removeEventListener) {removeEventListener("resize",ZOOM.Resize,false)}
|
||
|
else if (window.detachEvent) {detachEvent("onresize",ZOOM.Resize)}
|
||
|
else {window.onresize = ZOOM.onresize; delete ZOOM.onresize}
|
||
|
}
|
||
|
return FALSE(event);
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
//
|
||
|
// Hook into the HTML-CSS and NativeMML event handling
|
||
|
//
|
||
|
HUB.Register.StartupHook("HTML-CSS Jax Ready",function () {
|
||
|
HTMLCSS = MathJax.OutputJax["HTML-CSS"];
|
||
|
HTMLCSS.Augment({HandleEvent: ZOOM.HandleEvent});
|
||
|
});
|
||
|
HUB.Register.StartupHook("NativeMML Jax Ready", function () {
|
||
|
nMML = MathJax.OutputJax.NativeMML;
|
||
|
nMML.Augment({
|
||
|
HandleEvent: ZOOM.HandleEvent,
|
||
|
MSIEmouseup: function (event,math,span) {
|
||
|
if (this.trapUp) {delete this.trapUp; return true}
|
||
|
if (this.MSIEzoomKeys(event)) {return true}
|
||
|
return false;
|
||
|
},
|
||
|
MSIEclick: function (event,math,span) {
|
||
|
if (this.trapClick) {delete this.trapClick; return true}
|
||
|
if (!this.MSIEzoomKeys(event)) return false;
|
||
|
if (!this.settings.zoom.match(/Click/)) return false;
|
||
|
return (ZOOM.Click(event,math) === false);
|
||
|
},
|
||
|
MSIEdblclick: function (event,math,span) {
|
||
|
if (!this.MSIEzoomKeys(event)) return false;
|
||
|
return (ZOOM.DblClick(event,math) === false);
|
||
|
},
|
||
|
MSIEmouseover: function (event,math,span) {
|
||
|
if (this.settings.zoom !== "Hover") {return false}
|
||
|
ZOOM.Timer(event,math); return true;
|
||
|
},
|
||
|
MSIEmouseout: function (event,math,span) {
|
||
|
if (this.settings.zoom !== "Hover") {return false}
|
||
|
ZOOM.ClearTimer(); return true;
|
||
|
},
|
||
|
MSIEmousemove: function (event,math,span) {
|
||
|
if (this.settings.zoom !== "Hover") {return false}
|
||
|
ZOOM.Timer(event,math); return true;
|
||
|
},
|
||
|
MSIEzoomKeys: function (event) {
|
||
|
if (this.settings.CTRL && !event.ctrlKey) return false;
|
||
|
if (this.settings.CMD && !event.metaKey) return false;
|
||
|
if (this.settings.ALT && !event.altKey) return false;
|
||
|
if (this.settings.Shift && !event.shiftKey) return false;
|
||
|
return true;
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
|
||
|
/*************************************************************/
|
||
|
|
||
|
HUB.Browser.Select({
|
||
|
MSIE: function (browser) {
|
||
|
var quirks = (document.compatMode === "BackCompat");
|
||
|
var isIE8 = browser.versionAtLeast("8.0") && document.documentMode > 7;
|
||
|
ZOOM.msiePositionBug = true;
|
||
|
ZOOM.msieWidthBug = !quirks;
|
||
|
ZOOM.msieIE8Bug = isIE8;
|
||
|
ZOOM.msieZIndexBug = !isIE8;
|
||
|
ZOOM.msieInlineBlockAlignBug = (!isIE8 || quirks);
|
||
|
if (document.documentMode >= 9) {delete CONFIG.styles["#MathJax_Zoom"].filter}
|
||
|
},
|
||
|
|
||
|
Opera: function (browser) {
|
||
|
ZOOM.operaPositionBug = true;
|
||
|
ZOOM.operaRefreshBug = true;
|
||
|
},
|
||
|
|
||
|
Firefox: function (browser) {
|
||
|
ZOOM.ffMMLwidthBug = true;
|
||
|
ZOOM.ffMMLcenterBug = true;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
ZOOM.topImg = (ZOOM.msieInlineBlockAlignBug ?
|
||
|
HTML.Element("img",{style:{width:0,height:0},src:"about:blank"}) :
|
||
|
HTML.Element("span",{style:{width:0,height:0,display:"inline-block"}})
|
||
|
);
|
||
|
if (ZOOM.operaPositionBug) {ZOOM.topImg.style.border="1px solid"}
|
||
|
|
||
|
/*************************************************************/
|
||
|
|
||
|
MathJax.Callback.Queue(
|
||
|
["Styles",AJAX,CONFIG.styles],
|
||
|
["Post",HUB.Startup.signal,"MathZoom Ready"],
|
||
|
["loadComplete",AJAX,"[MathJax]/extensions/MathZoom.js"]
|
||
|
);
|
||
|
|
||
|
})(MathJax.Hub,MathJax.HTML,MathJax.Ajax,MathJax.OutputJax["HTML-CSS"],MathJax.OutputJax.NativeMML);
|