diff options
Diffstat (limited to 'data/mathjax/unpacked/extensions/MathEvents.js')
-rw-r--r-- | data/mathjax/unpacked/extensions/MathEvents.js | 533 |
1 files changed, 533 insertions, 0 deletions
diff --git a/data/mathjax/unpacked/extensions/MathEvents.js b/data/mathjax/unpacked/extensions/MathEvents.js new file mode 100644 index 00000000..54b9119a --- /dev/null +++ b/data/mathjax/unpacked/extensions/MathEvents.js @@ -0,0 +1,533 @@ +/************************************************************* + * + * MathJax/extensions/MathEvents.js + * + * Implements the event handlers needed by the output jax to perform + * menu, hover, and other events. + * + * --------------------------------------------------------------------- + * + * Copyright (c) 2011-2012 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,CALLBACK,OUTPUT,INPUT) { + var VERSION = "2.1"; + + var EXTENSION = MathJax.Extension; + var ME = EXTENSION.MathEvents = {version: VERSION}; + + var SETTINGS = HUB.config.menuSettings; + + var CONFIG = { + hover: 500, // time required to be considered a hover + frame: { + x: 3.5, y: 5, // frame padding and + bwidth: 1, // frame border width (in pixels) + bcolor: "#A6D", // frame border color + hwidth: "15px", // haze width + hcolor: "#83A" // haze color + }, + button: { + x: -4, y: -3, // menu button offsets + wx: -2, // button offset for full-width equations + src: AJAX.fileURL(OUTPUT.imageDir+"/MenuArrow-15.png") // button image + }, + fadeinInc: .2, // increment for fade-in + fadeoutInc: .05, // increment for fade-out + fadeDelay: 50, // delay between fade-in or fade-out steps + fadeoutStart: 400, // delay before fade-out after mouseout + fadeoutDelay: 15*1000, // delay before automatic fade-out + + styles: { + ".MathJax_Hover_Frame": { + "border-radius": ".25em", // Opera 10.5 and IE9 + "-webkit-border-radius": ".25em", // Safari and Chrome + "-moz-border-radius": ".25em", // Firefox + "-khtml-border-radius": ".25em", // Konqueror + + "box-shadow": "0px 0px 15px #83A", // Opera 10.5 and IE9 + "-webkit-box-shadow": "0px 0px 15px #83A", // Safari and Chrome + "-moz-box-shadow": "0px 0px 15px #83A", // Forefox + "-khtml-box-shadow": "0px 0px 15px #83A", // Konqueror + + border: "1px solid #A6D ! important", + display: "inline-block", position:"absolute" + }, + + ".MathJax_Hover_Arrow": { + position:"absolute", + width:"15px", height:"11px", + cursor:"pointer" + } + } + }; + + + // + // Common event-handling code + // + var EVENT = ME.Event = { + + LEFTBUTTON: 0, // the event.button value for left button + RIGHTBUTTON: 2, // the event.button value for right button + MENUKEY: "altKey", // the event value for alternate context menu + + Mousedown: function (event) {return EVENT.Handler(event,"Mousedown",this)}, + Mouseup: function (event) {return EVENT.Handler(event,"Mouseup",this)}, + Mousemove: function (event) {return EVENT.Handler(event,"Mousemove",this)}, + Mouseover: function (event) {return EVENT.Handler(event,"Mouseover",this)}, + Mouseout: function (event) {return EVENT.Handler(event,"Mouseout",this)}, + Click: function (event) {return EVENT.Handler(event,"Click",this)}, + DblClick: function (event) {return EVENT.Handler(event,"DblClick",this)}, + Menu: function (event) {return EVENT.Handler(event,"ContextMenu",this)}, + + // + // Call the output jax's event handler or the zoom handler + // + Handler: function (event,type,math) { + if (AJAX.loadingMathMenu) {return EVENT.False(event)} + var jax = OUTPUT[math.jaxID]; + if (!event) {event = window.event} + event.isContextMenu = (type === "ContextMenu"); + if (jax[type]) {return jax[type](event,math)} + if (EXTENSION.MathZoom) {return EXTENSION.MathZoom.HandleEvent(event,type,math)} + }, + + // + // Try to cancel the event in every way we can + // + 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; + }, + + // + // Load the contextual menu code, if needed, and post the menu + // + ContextMenu: function (event,math,force) { + // + // Check if we are showing menus + // + var JAX = OUTPUT[math.jaxID], jax = JAX.getJaxFromMath(math); + var show = (JAX.config.showMathMenu != null ? JAX : HUB).config.showMathMenu; + if (!show || (SETTINGS.context !== "MathJax" && !force)) return; + + // + // Remove selections, remove hover fades + // + if (ME.msieEventBug) {event = window.event || event} + EVENT.ClearSelection(); HOVER.ClearHoverTimer(); + if (jax.hover) { + if (jax.hover.remove) {clearTimeout(jax.hover.remove); delete jax.hover.remove} + jax.hover.nofade = true; + } + + // + // If the menu code is loaded, post the menu + // Otherwse lad the menu code and try again + // + var MENU = MathJax.Menu; + if (MENU) { + MENU.jax = jax; + var source = MENU.menu.Find("Show Math As").menu; + source.items[1].name = (INPUT[jax.inputJax].sourceMenuTitle||"Original Form"); + source.items[0].hidden = (jax.inputJax === "Error"); // hide MathML choice for error messages + var MathPlayer = MENU.menu.Find("Math Settings","MathPlayer"); + MathPlayer.hidden = !(jax.outputJax === "NativeMML" && HUB.Browser.hasMathPlayer); + return MENU.menu.Post(event); + } else { + if (!AJAX.loadingMathMenu) { + AJAX.loadingMathMenu = true; + var ev = { + pageX:event.pageX, pageY:event.pageY, + clientX:event.clientX, clientY:event.clientY + }; + CALLBACK.Queue( + AJAX.Require("[MathJax]/extensions/MathMenu.js"), + function () {delete AJAX.loadingMathMenu; if (!MathJax.Menu) {MathJax.Menu = {}}}, + ["ContextMenu",this,ev,math,force] // call this function again + ); + } + return EVENT.False(event); + } + }, + + // + // Mousedown handler for alternate means of accessing menu + // + AltContextMenu: function (event,math) { + var JAX = OUTPUT[math.jaxID]; + var show = (JAX.config.showMathMenu != null ? JAX : HUB).config.showMathMenu; + if (show) { + show = (JAX.config.showMathMenuMSIE != null ? JAX : HUB).config.showMathMenuMSIE; + if (SETTINGS.context === "MathJax" && !SETTINGS.mpContext && show) { + if (!ME.noContextMenuBug || event.button !== EVENT.RIGHTBUTTON) return; + } else { + if (!event[EVENT.MENUKEY] || event.button !== EVENT.LEFTBUTTON) return; + } + return JAX.ContextMenu(event,math,true); + } + }, + + ClearSelection: function () { + if (ME.safariContextMenuBug) {setTimeout("window.getSelection().empty()",0)} + if (document.selection) {setTimeout("document.selection.empty()",0)} + }, + + getBBox: function (span) { + span.appendChild(ME.topImg); + var h = ME.topImg.offsetTop, d = span.offsetHeight-h, w = span.offsetWidth; + span.removeChild(ME.topImg); + return {w:w, h:h, d:d}; + } + + }; + + // + // Handle hover "discoverability" + // + var HOVER = ME.Hover = { + + // + // Check if we are moving from a non-MathJax element to a MathJax one + // and either start fading in again (if it is fading out) or start the + // timer for the hover + // + Mouseover: function (event,math) { + if (SETTINGS.discoverable || SETTINGS.zoom === "Hover") { + var from = event.fromElement || event.relatedTarget, + to = event.toElement || event.target; + if (from && to && (from.isMathJax != to.isMathJax || + HUB.getJaxFor(from) !== HUB.getJaxFor(to))) { + var jax = this.getJaxFromMath(math); + if (jax.hover) {HOVER.ReHover(jax)} else {HOVER.HoverTimer(jax,math)} + return EVENT.False(event); + } + } + }, + // + // Check if we are moving from a MathJax element to a non-MathJax one + // and either start fading out, or clear the timer if we haven't + // hovered yet + // + Mouseout: function (event,math) { + if (SETTINGS.discoverable || SETTINGS.zoom === "Hover") { + var from = event.fromElement || event.relatedTarget, + to = event.toElement || event.target; + if (from && to && (from.isMathJax != to.isMathJax || + HUB.getJaxFor(from) !== HUB.getJaxFor(to))) { + var jax = this.getJaxFromMath(math); + if (jax.hover) {HOVER.UnHover(jax)} else {HOVER.ClearHoverTimer()} + return EVENT.False(event); + } + } + }, + // + // Restart hover timer if the mouse moves + // + Mousemove: function (event,math) { + if (SETTINGS.discoverable || SETTINGS.zoom === "Hover") { + var jax = this.getJaxFromMath(math); if (jax.hover) return; + if (HOVER.lastX == event.clientX && HOVER.lastY == event.clientY) return; + HOVER.lastX = event.clientX; HOVER.lastY = event.clientY; + HOVER.HoverTimer(jax,math); + return EVENT.False(event); + } + }, + + // + // Clear the old timer and start a new one + // + HoverTimer: function (jax,math) { + this.ClearHoverTimer(); + this.hoverTimer = setTimeout(CALLBACK(["Hover",this,jax,math]),CONFIG.hover); + }, + ClearHoverTimer: function () { + if (this.hoverTimer) {clearTimeout(this.hoverTimer); delete this.hoverTimer} + }, + + // + // Handle putting up the hover frame + // + Hover: function (jax,math) { + // + // Check if Zoom handles the hover event + // + if (EXTENSION.MathZoom && EXTENSION.MathZoom.Hover({},math)) return; + // + // Get the hover data + // + var JAX = OUTPUT[jax.outputJax], + span = JAX.getHoverSpan(jax,math), + bbox = JAX.getHoverBBox(jax,span,math), + show = (JAX.config.showMathMenu != null ? JAX : HUB).config.showMathMenu; + var dx = CONFIG.frame.x, dy = CONFIG.frame.y, dd = CONFIG.frame.bwidth; // frame size + if (ME.msieBorderWidthBug) {dd = 0} + jax.hover = {opacity:0, id:jax.inputID+"-Hover"}; + // + // The frame and menu button + // + var frame = HTML.Element("span",{ + id:jax.hover.id, isMathJax: true, + style:{display:"inline-block", width:0, height:0, position:"relative"} + },[["span",{ + className:"MathJax_Hover_Frame", isMathJax: true, + style:{ + display:"inline-block", position:"absolute", + top:this.Px(-bbox.h-dy-dd-(bbox.y||0)), left:this.Px(-dx-dd+(bbox.x||0)), + width:this.Px(bbox.w+2*dx), height:this.Px(bbox.h+bbox.d+2*dy), + opacity:0, filter:"alpha(opacity=0)" + }} + ]] + ); + var button = HTML.Element("span",{ + isMathJax: true, id:jax.hover.id+"Menu", + style:{display:"inline-block", "z-index": 1, width:0, height:0, position:"relative"} + },[["img",{ + className: "MathJax_Hover_Arrow", isMathJax: true, math: math, + src: CONFIG.button.src, onclick: this.HoverMenu, jax:JAX.id, + style: { + left:this.Px(bbox.w+dx+dd+(bbox.x||0)+CONFIG.button.x), + top:this.Px(-bbox.h-dy-dd-(bbox.y||0)-CONFIG.button.y), + opacity:0, filter:"alpha(opacity=0)" + } + }]] + ); + if (bbox.width) { + frame.style.width = button.style.width = bbox.width; + frame.style.marginRight = button.style.marginRight = "-"+bbox.width; + frame.firstChild.style.width = bbox.width; + button.firstChild.style.left = ""; + button.firstChild.style.right = this.Px(CONFIG.button.wx); + } + // + // Add the frame and button + // + span.parentNode.insertBefore(frame,span); + if (show) {span.parentNode.insertBefore(button,span)} + if (span.style) {span.style.position = "relative"} // so math is on top of hover frame + // + // Start the hover fade-in + // + this.ReHover(jax); + }, + // + // Restart the hover fade in and fade-out timers + // + ReHover: function (jax) { + if (jax.hover.remove) {clearTimeout(jax.hover.remove)} + jax.hover.remove = setTimeout(CALLBACK(["UnHover",this,jax]),CONFIG.fadeoutDelay); + this.HoverFadeTimer(jax,CONFIG.fadeinInc); + }, + // + // Start the fade-out + // + UnHover: function (jax) { + if (!jax.hover.nofade) {this.HoverFadeTimer(jax,-CONFIG.fadeoutInc,CONFIG.fadeoutStart)} + }, + // + // Handle the fade-in and fade-out + // + HoverFade: function (jax) { + delete jax.hover.timer; + jax.hover.opacity = Math.max(0,Math.min(1,jax.hover.opacity + jax.hover.inc)); + jax.hover.opacity = Math.floor(1000*jax.hover.opacity)/1000; + var frame = document.getElementById(jax.hover.id), + button = document.getElementById(jax.hover.id+"Menu"); + frame.firstChild.style.opacity = jax.hover.opacity; + frame.firstChild.style.filter = "alpha(opacity="+Math.floor(100*jax.hover.opacity)+")"; + if (button) { + button.firstChild.style.opacity = jax.hover.opacity; + button.firstChild.style.filter = frame.style.filter; + } + if (jax.hover.opacity === 1) {return} + if (jax.hover.opacity > 0) {this.HoverFadeTimer(jax,jax.hover.inc); return} + frame.parentNode.removeChild(frame); + if (button) {button.parentNode.removeChild(button)} + if (jax.hover.remove) {clearTimeout(jax.hover.remove)} + delete jax.hover; + }, + // + // Set the fade to in or out (via inc) and start the timer, if needed + // + HoverFadeTimer: function (jax,inc,delay) { + jax.hover.inc = inc; + if (!jax.hover.timer) { + jax.hover.timer = setTimeout(CALLBACK(["HoverFade",this,jax]),(delay||CONFIG.fadeDelay)); + } + }, + + // + // Handle a click on the menu button + // + HoverMenu: function (event) { + if (!event) {event = window.event} + return OUTPUT[this.jax].ContextMenu(event,this.math,true); + }, + + // + // Clear all hover timers + // + ClearHover: function (jax) { + if (jax.hover.remove) {clearTimeout(jax.hover.remove)} + if (jax.hover.timer) {clearTimeout(jax.hover.timer)} + HOVER.ClearHoverTimer(); + delete jax.hover; + }, + + // + // Make a measurement in pixels + // + Px: function (m) { + if (Math.abs(m) < .006) {return "0px"} + return m.toFixed(2).replace(/\.?0+$/,"") + "px"; + }, + + // + // Preload images so they show up with the menu + // + getImages: function () { + var menu = new Image(); + menu.src = CONFIG.button.src; + } + + }; + + // + // Handle touch events. + // + // Use double-tap-and-hold as a replacement for context menu event. + // Use double-tap as a replacement for double click. + // + var TOUCH = ME.Touch = { + + last: 0, // time of last tap event + delay: 500, // delay time for double-click + + // + // Check if this is a double-tap, and if so, start the timer + // for the double-tap and hold (to trigger the contextual menu) + // + start: function (event) { + var now = new Date().getTime(); + var dblTap = (now - TOUCH.last < TOUCH.delay && TOUCH.up); + TOUCH.last = now; TOUCH.up = false; + if (dblTap) { + TOUCH.timeout = setTimeout(TOUCH.menu,TOUCH.delay,event,this); + event.preventDefault(); + } + }, + + // + // Check if there is a timeout pending, i.e., we have a + // double-tap and were waiting to see if it is held long + // enough for the menu. Since we got the end before the + // timeout, it is a double-click, not a double-tap-and-hold. + // Prevent the default action and issue a double click. + // + end: function (event) { + var now = new Date().getTime(); + TOUCH.up = (now - TOUCH.last < TOUCH.delay); + if (TOUCH.timeout) { + clearTimeout(TOUCH.timeout); + delete TOUCH.timeout; TOUCH.last = 0; TOUCH.up = false; + event.preventDefault(); + return EVENT.Handler((event.touches[0]||event.touch),"DblClick",this); + } + }, + + // + // If the timeout passes without an end event, we issue + // the contextual menu event. + // + menu: function (event,math) { + delete TOUCH.timeout; TOUCH.last = 0; TOUCH.up = false; + return EVENT.Handler((event.touches[0]||event.touch),"ContextMenu",math); + } + + }; + + // + // Mobile screens are small, so use larger version of arrow + // + if (HUB.Browser.isMobile) { + var arrow = CONFIG.styles[".MathJax_Hover_Arrow"]; + arrow.width = "25px"; arrow.height = "18px"; + CONFIG.button.x = -6; + } + + // + // Set up browser-specific values + // + HUB.Browser.Select({ + MSIE: function (browser) { + var mode = (document.documentMode || 0); + var isIE8 = browser.versionAtLeast("8.0"); + ME.msieBorderWidthBug = (document.compatMode === "BackCompat"); // borders are inside offsetWidth/Height + ME.msieEventBug = browser.isIE9; // must get event from window even though event is passed + ME.msieAlignBug = (!isIE8 || mode < 8); // inline-block spans don't rest on baseline + if (mode < 9) {EVENT.LEFTBUTTON = 1} // IE < 9 has wrong event.button values + }, + Safari: function (browser) { + ME.safariContextMenuBug = true; // selection can be started by contextmenu event + }, + Opera: function (browser) { + ME.operaPositionBug = true; // position is wrong unless border is used + }, + Konqueror: function (browser) { + ME.noContextMenuBug = true; // doesn't produce contextmenu event + } + }); + + // + // Used in measuring zoom and hover positions + // + ME.topImg = (ME.msieAlignBug ? + HTML.Element("img",{style:{width:0,height:0,position:"relative"},src:"about:blank"}) : + HTML.Element("span",{style:{width:0,height:0,display:"inline-block"}}) + ); + if (ME.operaPositionBug) {ME.topImg.style.border="1px solid"} + + // + // Get configuration from user + // + ME.config = CONFIG = HUB.CombineConfig("MathEvents",CONFIG); + var SETFRAME = function () { + var haze = CONFIG.styles[".MathJax_Hover_Frame"]; + haze.border = CONFIG.frame.bwidth+"px solid "+CONFIG.frame.bcolor+" ! important"; + haze["box-shadow"] = haze["-webkit-box-shadow"] = + haze["-moz-box-shadow"] = haze["-khtml-box-shadow"] = + "0px 0px "+CONFIG.frame.hwidth+" "+CONFIG.frame.hcolor; + }; + + // + // Queue the events needed for startup + // + CALLBACK.Queue( + HUB.Register.StartupHook("End Config",{}), // wait until config is complete + [SETFRAME], + ["getImages",HOVER], + ["Styles",AJAX,CONFIG.styles], + ["Post",HUB.Startup.signal,"MathEvents Ready"], + ["loadComplete",AJAX,"[MathJax]/extensions/MathEvents.js"] + ); + +})(MathJax.Hub,MathJax.HTML,MathJax.Ajax,MathJax.Callback,MathJax.OutputJax,MathJax.InputJax); |