From 42306c4b0ca9d357d4592b156af2e87d0132b777 Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Fri, 19 Aug 2011 18:31:43 -0400 Subject: [PATCH] Move event-handling code to a separate file, loaded by output jax (so if MathJax is loaded on a page with no math, it doesn't have to be loaded) --- unpacked/MathJax.js | 457 +++++-------------------- unpacked/extensions/MathMenu.js | 37 +- unpacked/extensions/MathZoom.js | 55 +-- unpacked/extensions/UIevents.js | 331 ++++++++++++++++++ unpacked/jax/output/HTML-CSS/config.js | 2 +- unpacked/jax/output/HTML-CSS/jax.js | 30 +- 6 files changed, 469 insertions(+), 443 deletions(-) create mode 100644 unpacked/extensions/UIevents.js diff --git a/unpacked/MathJax.js b/unpacked/MathJax.js index e6b19ecbc..c79abce40 100644 --- a/unpacked/MathJax.js +++ b/unpacked/MathJax.js @@ -30,7 +30,7 @@ if (!window.MathJax) {window.MathJax= {}} if (!MathJax.Hub) { // skip if already loaded MathJax.version = "1.1a"; -MathJax.fileversion = "1.1.11"; +MathJax.fileversion = "1.1.12"; /**********************************************************/ @@ -900,379 +900,104 @@ MathJax.fileversion = "1.1.11"; /**********************************************************/ -(function (BASENAME) { - var BASE = window[BASENAME]; - if (!BASE) {BASE = window[BASENAME] = {}} - var AJAX = BASE.Ajax, CALLBACK = BASE.Callback; +MathJax.HTML = { + // + // Create an HTML element with given attributes and content. + // The def parameter is an (optional) object containing key:value pairs + // of the attributes and their values, and contents is an (optional) + // array of strings to be inserted as text, or arrays of the form + // [type,def,contents] that describes an HTML element to be inserted + // into the current element. Thus the contents can describe a complete + // HTML snippet of arbitrary complexity. E.g.: + // + // MathJax.HTML.Element("span",{id:"mySpan",style{"font-style":"italic"}},[ + // "(See the ",["a",{href:"http://www.mathjax.org"},["MathJax home page"]], + // " for more details.)"]); + // + Element: function (type,def,contents) { + var obj = document.createElement(type); + if (def) { + if (def.style) { + var style = def.style; def.style = {}; + for (var id in style) {if (style.hasOwnProperty(id)) + {def.style[id.replace(/-([a-z])/g,this.ucMatch)] = style[id]}} + } + MathJax.Hub.Insert(obj,def); + } + if (contents) { + for (var i = 0; i < contents.length; i++) { + if (contents[i] instanceof Array) { + obj.appendChild(this.Element(contents[i][0],contents[i][1],contents[i][2])); + } else { + obj.appendChild(document.createTextNode(contents[i])); + } + } + } + return obj; + }, + ucMatch: function (match,c) {return c.toUpperCase()}, + addElement: function (span,type,def,contents) {return span.appendChild(this.Element(type,def,contents))}, + TextNode: function (text) {return document.createTextNode(text)}, + addText: function (span,text) {return span.appendChild(this.TextNode(text))}, - BASE.HTML = { + // + // Set the text of a script + // + setScript: function (script,text) { + if (this.setScriptBug) {script.text = text} else { + while (script.firstChild) {script.removeChild(script.firstChild)} + this.addText(script,text); + } + }, + + // + // Manage cookies + // + Cookie: { + prefix: "mjx", + expires: 365, // - // Create an HTML element with given attributes and content. - // The def parameter is an (optional) object containing key:value pairs - // of the attributes and their values, and contents is an (optional) - // array of strings to be inserted as text, or arrays of the form - // [type,def,contents] that describes an HTML element to be inserted - // into the current element. Thus the contents can describe a complete - // HTML snippet of arbitrary complexity. E.g.: - // - // MathJax.HTML.Element("span",{id:"mySpan",style{"font-style":"italic"}},[ - // "(See the ",["a",{href:"http://www.mathjax.org"},["MathJax home page"]], - // " for more details.)"]); - // - Element: function (type,def,contents) { - var obj = document.createElement(type); + // Save an object as a named cookie + // + Set: function (name,def) { + var keys = []; if (def) { - if (def.style) { - var style = def.style; def.style = {}; - for (var id in style) {if (style.hasOwnProperty(id)) - {def.style[id.replace(/-([a-z])/g,this.ucMatch)] = style[id]}} - } - BASE.Hub.Insert(obj,def); + for (var id in def) {if (def.hasOwnProperty(id)) { + keys.push(id+":"+def[id].toString().replace(/&/g,"&&")); + }} } - if (contents) { - for (var i = 0; i < contents.length; i++) { - if (contents[i] instanceof Array) { - obj.appendChild(this.Element(contents[i][0],contents[i][1],contents[i][2])); - } else { - obj.appendChild(document.createTextNode(contents[i])); - } + var cookie = this.prefix+"."+name+"="+escape(keys.join('&;')); + if (this.expires) { + var time = new Date(); time.setDate(time.getDate() + this.expires); + cookie += '; expires='+time.toGMTString(); + } + document.cookie = cookie+"; path=/"; + }, + + // + // Get the contents of a named cookie and incorporate + // it into the given object (or return a fresh one) + // + Get: function (name,obj) { + if (!obj) {obj = {}} + var pattern = new RegExp("(?:^|;\\s*)"+this.prefix+"\\."+name+"=([^;]*)(?:;|$)"); + var match = pattern.exec(document.cookie); + if (match && match[1] !== "") { + var keys = unescape(match[1]).split('&;'); + for (var i = 0, m = keys.length; i < m; i++) { + match = keys[i].match(/([^:]+):(.*)/); + var value = match[2].replace(/&&/g,'&'); + if (value === "true") {value = true} else if (value === "false") {value = false} + else if (value.match(/^-?(\d+(\.\d+)?|\.\d+)$/)) {value = parseFloat(value)} + obj[match[1]] = value; } } return obj; - }, - ucMatch: function (match,c) {return c.toUpperCase()}, - addElement: function (span,type,def,contents) {return span.appendChild(this.Element(type,def,contents))}, - TextNode: function (text) {return document.createTextNode(text)}, - addText: function (span,text) {return span.appendChild(this.TextNode(text))}, - - // - // Set the text of a script - // - setScript: function (script,text) { - if (this.setScriptBug) {script.text = text} else { - while (script.firstChild) {script.removeChild(script.firstChild)} - this.addText(script,text); - } - }, - - // - // Manage cookies - // - Cookie: { - prefix: "mjx", - expires: 365, - - // - // Save an object as a named cookie - // - Set: function (name,def) { - var keys = []; - if (def) { - for (var id in def) {if (def.hasOwnProperty(id)) { - keys.push(id+":"+def[id].toString().replace(/&/g,"&&")); - }} - } - var cookie = this.prefix+"."+name+"="+escape(keys.join('&;')); - if (this.expires) { - var time = new Date(); time.setDate(time.getDate() + this.expires); - cookie += '; expires='+time.toGMTString(); - } - document.cookie = cookie+"; path=/"; - }, - - // - // Get the contents of a named cookie and incorporate - // it into the given object (or return a fresh one) - // - Get: function (name,obj) { - if (!obj) {obj = {}} - var pattern = new RegExp("(?:^|;\\s*)"+this.prefix+"\\."+name+"=([^;]*)(?:;|$)"); - var match = pattern.exec(document.cookie); - if (match && match[1] !== "") { - var keys = unescape(match[1]).split('&;'); - for (var i = 0, m = keys.length; i < m; i++) { - match = keys[i].match(/([^:]+):(.*)/); - var value = match[2].replace(/&&/g,'&'); - if (value === "true") {value = true} else if (value === "false") {value = false} - else if (value.match(/^-?(\d+(\.\d+)?|\.\d+)$/)) {value = parseFloat(value)} - obj[match[1]] = value; - } - } - return obj; - } } + } - }; - - // - // Common event-handling code - // - var EVENT = BASE.HTML.Event = { - - LEFTBUTTON: 0, // the event.button value for left button - MENUKEY: "altKey", // the event value for alternate context menu - - 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 3.5 - "-khtml-box-shadow": "0px 0px 15px #83A", // Konqueror - - border: ".1em solid #A6D ! important", - display: "inline-block", position:"absolute" - }, - - ".MathJax_Hover_Arrow": { - position:"absolute", - top:"-5px", right:"-9px", - width:"15px", height:"11px", - cursor:"pointer" - } - }, - - 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 - // - Handler: function (event,type,math) { - if (AJAX.loadingMathMenu) {return False(event)} - var jax = BASE.OutputJax[math.jaxID]; - if (!event) {event = window.event} - event.isContextMenu = (type === "ContextMenu"); - return jax.HandleEvent(event,type,math); - }, - // - // For use in the output jax (this will be the output jax) - // - HandleEvent: function (event,type,math) { - if (this[type]) {return this[type].call(this,event,math)} - if (MathJax.Extension.MathZoom) - {return MathJax.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,jax) { - if (jax.hover) { - if (jax.hover.remove) {clearTimeout(jax.hover.remove); delete jax.hover.remove} - jax.hover.nofade = true; - } - var MENU = BASE.Menu; - if (MENU) { - MENU.jax = jax; - MENU.menu.Find("Format").menu.items[1].name = - (MENU.jax.inputJax.id === "MathML" ? "Original" : MENU.jax.inputJax.id); - 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 (!BASE.Menu) {BASE.Menu = {}}}, - ["ContextMenu",this,ev,jax] // call this function again - ); - } - return this.False(event); - } - } - }; - - // - // Handle hover "discoverability" - // - var HOVER = BASE.HTML.Hover = { - Mouseover: function (event,math) { - var from = event.fromElement || event.relatedTarget, - to = event.toElement || event.target; - if (from && to && from.isMathJax != to.isMathJax) { - var jax = this.getJaxFromMath(math); - if (jax.hover) {HOVER.ReHover(jax)} else {HOVER.HoverTimer(jax,math)} - return EVENT.False(event); - } - }, - Mouseout: function (event,math) { - var from = event.fromElement || event.relatedTarget, - to = event.toElement || event.target; - if (from && to && from.isMathJax != to.isMathJax) { - var jax = this.getJaxFromMath(math); - if (jax.hover) {HOVER.UnHover(jax)} else {HOVER.ClearHoverTimer()} - return EVENT.False(event); - } - }, - Mousemove: function (event,math) { - 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); - }, - - HoverTimer: function (jax,math) { - this.ClearHoverTimer(); - var delay = BASE.Hub.config.menuSettings.hover; - this.hoverTimer = setTimeout(CALLBACK(["Hover",this,jax,math]),delay); - }, - ClearHoverTimer: function () { - if (this.hoverTimer) {clearTimeout(this.hoverTimer); delete this.hoverTimer} - }, - - Hover: function (jax,math) { - // check for MathZoom hover - var JAX = jax.outputJax, span = JAX.getHoverSpan(jax), bbox = JAX.getHoverBBox(jax,span); - var dx = .25, dy = .33, dd = .1; // frame size - jax.hover = {opacity:0}; - if (this.msieBorderWidthBug) {dd = 0} - jax.hover.id = "MathJax-Hover-"+jax.inputID.replace(/.*-(\d+)$/,"$1"); - var frame = BASE.HTML.Element("span",{ - id:jax.hover.id, isMathJax: true, - style:{display:"inline-block", "z-index":1, width:0, height:0, position:"relative"} - },[["span",{ - className:"MathJax_Hover_Frame", isMathJax: true, - style:{ - display:"inline-block", position:"absolute", - top:this.Em(-bbox.h-dy-dd), left:this.Em(-dx-dd), - width:this.Em(bbox.w+2*dx), height:this.Em(bbox.h+bbox.d+2*dy), - opacity:0, filter:"alpha(opacity=0)" - }},[[ - "img",{ - className: "MathJax_Hover_Arrow", isMathJax: true, math: math, - src: BASE.Ajax.fileURL(MathJax.OutputJax.imageDir+"/MenuArrow-15.png"), - onclick: this.HoverMenu, jax:JAX.id - } - ]] - ]] - ); - span.parentNode.insertBefore(frame,span); span.style.position = "relative"; - this.ReHover(jax,.2); - }, - ReHover: function (jax) { - if (jax.hover.remove) {clearTimeout(jax.hover.remove)} - jax.hover.remove = setTimeout(MathJax.Callback(["UnHover",this,jax]),15*1000); - this.HoverFadeTimer(jax,.2); - }, - UnHover: function (jax) { - if (!jax.hover.nofade) {this.HoverFadeTimer(jax,-.05,400)} - }, - 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 span = document.getElementById(jax.hover.id); - span.firstChild.style.opacity = jax.hover.opacity; - span.firstChild.style.filter = "alpha(opacity="+Math.floor(100*jax.hover.opacity)+")"; - if (jax.hover.opacity === 1) {return} - if (jax.hover.opacity) {this.HoverFadeTimer(jax,jax.hover.inc); return} - var frame = document.getElementById(jax.hover.id); - frame.parentNode.removeChild(frame); - if (jax.hover.remove) {clearTimeout(jax.hover.remove)} - delete jax.hover; - }, - HoverFadeTimer: function (jax,inc,delay) { - jax.hover.inc = inc; - if (!jax.hover.timer) { - jax.hover.timer = setTimeout(MathJax.Callback(["HoverFade",this,jax]),(delay||50)); - } - }, - HoverMenu: function (event) { - if (!event) {event = window.event} - BASE.OutputJax[this.jax].ContextMenu(event,this.math,true); - }, - - Em: function (m) { - if (Math.abs(m) < .0006) {return "0em"} - return m.toFixed(3).replace(/\.?0+$/,"") + "em"; - } - - }; - - // - // 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 = BASE.HTML.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.last = now; - 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) { - if (TOUCH.timeout) { - clearTimeout(TOUCH.timeout); - delete TOUCH.timeout; TOUCH.last = 0; - 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; - return EVENT.Handler((event.touches[0]||event.touch),"ContextMenu",math); - } - - }; - -})("MathJax"); +}; /**********************************************************/ @@ -1781,7 +1506,6 @@ MathJax.Hub = { } }; MathJax.Hub.Insert(MathJax.Hub.config.styles,MathJax.Message.styles); -MathJax.Hub.Insert(MathJax.Hub.config.styles,MathJax.HTML.Event.styles); MathJax.Hub.Insert(MathJax.Hub.config.styles,{".MathJax_Error":MathJax.Hub.config.errorSettings.style}); // @@ -2348,9 +2072,6 @@ MathJax.Hub.Startup = { MSIE: function (browser) { browser.isIE9 = !!(document.documentMode && (window.performance || window.msPerformance)); MathJax.HTML.setScriptBug = !browser.isIE9 || document.documentMode < 9; - MathJax.HTML.Event.msieButtonBug = (document.documentMode||0) >= 9; - if ((document.documentMode||0) < 9) {MathJax.HTML.Event.LEFTBUTTON = 1} - MathJax.HTML.Event.msieBorderWidthBug = (document.compatMode === "BackCompat"); } }); HUB.Browser.Select(MathJax.Message.browsers); diff --git a/unpacked/extensions/MathMenu.js b/unpacked/extensions/MathMenu.js index fdd52ec5c..8ae2df583 100644 --- a/unpacked/extensions/MathMenu.js +++ b/unpacked/extensions/MathMenu.js @@ -134,11 +134,11 @@ } }); - /*************************************************************/ - /* - * Cancel event's default action (try everything we can) - */ - var FALSE = MathJax.HTML.Event.False; + var FALSE, HOVER; + MathJax.Hub.Register.StartupHook("UIevents Ready",function () { + FALSE = MathJax.Extension.UIevents.Event.False; + HOVER = MathJax.Extension.UIevents.Hover; + }); /*************************************************************/ /* @@ -167,8 +167,8 @@ delete MENU.skipUp; } var menu = HTML.addElement(div,"div",{ - onmouseup: MENU.Mouseup, ondblclick: this.False, - ondragstart: this.False, onselectstart: this.False, oncontextmenu: this.False, + onmouseup: MENU.Mouseup, ondblclick: FALSE, + ondragstart: FALSE, onselectstart: FALSE, oncontextmenu: FALSE, menuItem: this, className: "MathJax_Menu" },title); @@ -178,7 +178,7 @@ src: MathJax.Ajax.fileURL(MathJax.OutputJax.imageDir+"/CloseX-31.png"), width: 31, height: 31, menu: parent, style: {position:"absolute", top:"-15px", left:"-15px"}, - ontouchstart: MENU.Close, ontouchend: this.False, onmousedown: MENU.Close + ontouchstart: MENU.Close, ontouchend: FALSE, onmousedown: MENU.Close }); } this.posted = true; @@ -215,7 +215,7 @@ menu.style.left = x+"px"; menu.style.top = y+"px"; if (document.selection && document.selection.empty) {document.selection.empty()} - return this.False(event); + return FALSE(event); }, /* @@ -229,12 +229,10 @@ } if (MENU.jax.hover) { delete MENU.jax.hover.nofade; - HTML.Hover.UnHover(MENU.jax); + HOVER.UnHover(MENU.jax); } }, - False: FALSE, - /* * Find a named item in a menu (or submenu). * A lsit of names means descend into submenus. @@ -345,7 +343,7 @@ var def = { onmouseover: MENU.Mouseover, onmouseout: MENU.Mouseout, onmouseup: MENU.Mouseup, onmousedown: MENU.Mousedown, - ondragstart: this.False, onselectstart: this.False, onselectend: this.False, + ondragstart: FALSE, onselectstart: FALSE, onselectend: FALSE, ontouchstart: MENU.Touchstart, ontouchend: MENU.Touchend, className: "MathJax_MenuItem", menuItem: this }; @@ -399,8 +397,7 @@ Activate: function (menu) {this.Deactivate(menu); menu.className += " MathJax_MenuActive"}, Deactivate: function (menu) {menu.className = menu.className.replace(/ MathJax_MenuActive/,"")}, - With: function (def) {if (def) {HUB.Insert(this,def)}; return this}, - False: FALSE + With: function (def) {if (def) {HUB.Insert(this,def)}; return this} }); /*************************************************************/ @@ -418,7 +415,7 @@ Label: function (def,menu) {return [this.name]}, Mouseup: function (event,menu) { if (!this.disabled) {this.Remove(event,menu); this.action.call(this,event);} - return this.False(event); + return FALSE(event); } }); @@ -467,7 +464,7 @@ } } } - return this.False(event); + return FALSE(event); } }); @@ -502,7 +499,7 @@ if (this.action) {this.action.call(MENU)} } this.Remove(event,menu); - return this.False(event); + return FALSE(event); } }); @@ -530,7 +527,7 @@ if (this.action) {this.action.call(MENU)} } this.Remove(event,menu); - return this.False(event); + return FALSE(event); } }); @@ -866,7 +863,7 @@ if (MENU.isMobile) { (function () { - var settings = MathJax.Hub.config.menuSettings; + var settings = CONFIG.settings; var trigger = MENU.menu.Find("Settings","Zoom Trigger").menu; trigger.items[0].disabled = trigger.items[1].disabled = true; if (settings.zoom === "Hover" || settings.zoom == "Click") {settings.zoom = "None"} diff --git a/unpacked/extensions/MathZoom.js b/unpacked/extensions/MathZoom.js index 56c93d716..d106cd17c 100644 --- a/unpacked/extensions/MathZoom.js +++ b/unpacked/extensions/MathZoom.js @@ -23,7 +23,7 @@ */ (function (HUB,HTML,AJAX,HTMLCSS,nMML) { - var VERSION = "1.1"; + var VERSION = "1.1.2"; var CONFIG = HUB.CombineConfig("MathZoom",{ delay: 400, // mouse must be still this long (milliseconds) @@ -57,11 +57,11 @@ } }); - /*************************************************************/ - /* - * Cancel event's default action (try everything we can) - */ - var FALSE = MathJax.HTML.Event.False; + var FALSE, HOVER; + MathJax.Hub.Register.StartupHook("UIevents Ready",function () { + FALSE = MathJax.Extension.UIevents.Event.False; + HOVER = MathJax.Extension.UIevents.Hover; + }); /*************************************************************/ @@ -95,40 +95,19 @@ }, // - // Zoom on hover + // Zoom on hover (called by UI.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} + Hover: function (event,math) { + if (this.settings.zoom === "Hover") {this.Zoom(math,event); return true} + return false; }, + // // Handle the actual zooming // Zoom: function (math,event) { - this.ClearTimer(); this.Remove(); + this.Remove(); // // Find the jax and its type @@ -141,6 +120,7 @@ var JAX = (HTMLCSS && jax.outputJax.isa(HTMLCSS.constructor) ? "HTMLCSS" : (nMML && jax.outputJax.isa(nMML.constructor) ? "MathML" : null)); if (!JAX) return; // FIXME: report an error? + if (jax.hover) {HOVER.UnHover(jax)} // // Create the DOM elements for the zoom box @@ -331,15 +311,18 @@ }, MSIEmouseover: function (event,math,span) { if (this.settings.zoom !== "Hover") {return false} - ZOOM.Timer(event,math); return true; + return !HOVER.Mouseover(event,math); +// ZOOM.Timer(event,math); return true; }, MSIEmouseout: function (event,math,span) { if (this.settings.zoom !== "Hover") {return false} - ZOOM.ClearTimer(); return true; + return !HOVER.Mouseout(event,math); +// ZOOM.ClearTimer(); return true; }, MSIEmousemove: function (event,math,span) { if (this.settings.zoom !== "Hover") {return false} - ZOOM.Timer(event,math); return true; + return !HOVER.Mousemove(event,math); +// ZOOM.Timer(event,math); return true; }, MSIEzoomKeys: function (event) { if (this.settings.CTRL && !event.ctrlKey) return false; diff --git a/unpacked/extensions/UIevents.js b/unpacked/extensions/UIevents.js new file mode 100644 index 000000000..af2fe7b94 --- /dev/null +++ b/unpacked/extensions/UIevents.js @@ -0,0 +1,331 @@ +/************************************************************* + * + * MathJax/extensions/UIevents.js + * + * Implements the event handlers needed by the output jax to perform + * menu, hover, and other events. + * + * --------------------------------------------------------------------- + * + * Copyright (c) 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,CALLBACK,OUTPUT) { + var VERSION = "1.1"; + + var EXTENSION = MathJax.Extension; + var UI = EXTENSION.UIevents = {version: VERSION}; + + var SETTINGS = HUB.config.menuSettings; + + var CONFIG = { + 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 3.5 + "-khtml-box-shadow": "0px 0px 15px #83A", // Konqueror + + border: ".1em solid #A6D ! important", + display: "inline-block", position:"absolute" + }, + + ".MathJax_Hover_Arrow": { + position:"absolute", + top:"-5px", right:"-9px", + width:"15px", height:"11px", + cursor:"pointer" + } + } + }; + + + // + // Common event-handling code + // + var EVENT = UI.Event = { + + LEFTBUTTON: 0, // the event.button value for left 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 + // + Handler: function (event,type,math) { + if (AJAX.loadingMathMenu) {return False(event)} + var jax = OUTPUT[math.jaxID]; + if (!event) {event = window.event} + event.isContextMenu = (type === "ContextMenu"); + return jax.HandleEvent(event,type,math); + }, + // + // For use in the output jax (this will be the output jax) + // + HandleEvent: function (event,type,math) { + if (this[type]) {return this[type].call(this,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,jax) { + if (jax.hover) { + if (jax.hover.remove) {clearTimeout(jax.hover.remove); delete jax.hover.remove} + jax.hover.nofade = true; + } + var MENU = MathJax.Menu; + if (MENU) { + MENU.jax = jax; + MENU.menu.Find("Format").menu.items[1].name = + (jax.inputJax.id === "MathML" ? "Original" : jax.inputJax.id); + 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,jax] // call this function again + ); + } + return this.False(event); + } + } + }; + + // + // Handle hover "discoverability" + // + var HOVER = UI.Hover = { + Mouseover: function (event,math) { + var from = event.fromElement || event.relatedTarget, + to = event.toElement || event.target; + if (from && to && from.isMathJax != to.isMathJax) { + var jax = this.getJaxFromMath(math); + if (jax.hover) {HOVER.ReHover(jax)} else {HOVER.HoverTimer(jax,math)} + return EVENT.False(event); + } + }, + Mouseout: function (event,math) { + var from = event.fromElement || event.relatedTarget, + to = event.toElement || event.target; + if (from && to && from.isMathJax != to.isMathJax) { + var jax = this.getJaxFromMath(math); + if (jax.hover) {HOVER.UnHover(jax)} else {HOVER.ClearHoverTimer()} + return EVENT.False(event); + } + }, + Mousemove: function (event,math) { + 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); + }, + + HoverTimer: function (jax,math) { + this.ClearHoverTimer(); + this.hoverTimer = setTimeout(CALLBACK(["Hover",this,jax,math]),SETTINGS.hover); + }, + ClearHoverTimer: function () { + if (this.hoverTimer) {clearTimeout(this.hoverTimer); delete this.hoverTimer} + }, + + Hover: function (jax,math) { + if (EXTENSION.MathZoom && EXTENSION.MathZoom.Hover(event,math)) return; + var JAX = jax.outputJax, span = JAX.getHoverSpan(jax), bbox = JAX.getHoverBBox(jax,span); + var dx = .25, dy = .33, dd = .1; // frame size + jax.hover = {opacity:0}; + if (this.msieBorderWidthBug) {dd = 0} + jax.hover.id = "MathJax-Hover-"+jax.inputID.replace(/.*-(\d+)$/,"$1"); + var frame = HTML.Element("span",{ + id:jax.hover.id, isMathJax: true, + style:{display:"inline-block", "z-index":1, width:0, height:0, position:"relative"} + },[["span",{ + className:"MathJax_Hover_Frame", isMathJax: true, + style:{ + display:"inline-block", position:"absolute", + top:this.Em(-bbox.h-dy-dd), left:this.Em(-dx-dd), + width:this.Em(bbox.w+2*dx), height:this.Em(bbox.h+bbox.d+2*dy), + opacity:0, filter:"alpha(opacity=0)" + }},[[ + "img",{ + className: "MathJax_Hover_Arrow", isMathJax: true, math: math, + src: AJAX.fileURL(OUTPUT.imageDir+"/MenuArrow-15.png"), + onclick: this.HoverMenu, jax:JAX.id + } + ]] + ]] + ); + span.parentNode.insertBefore(frame,span); span.style.position = "relative"; + this.ReHover(jax,.2); + }, + ReHover: function (jax) { + if (jax.hover.remove) {clearTimeout(jax.hover.remove)} + jax.hover.remove = setTimeout(CALLBACK(["UnHover",this,jax]),15*1000); + this.HoverFadeTimer(jax,.2); + }, + UnHover: function (jax) { + if (!jax.hover.nofade) {this.HoverFadeTimer(jax,-.05,400)} + }, + 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 span = document.getElementById(jax.hover.id); + span.firstChild.style.opacity = jax.hover.opacity; + span.firstChild.style.filter = "alpha(opacity="+Math.floor(100*jax.hover.opacity)+")"; + if (jax.hover.opacity === 1) {return} + if (jax.hover.opacity) {this.HoverFadeTimer(jax,jax.hover.inc); return} + var frame = document.getElementById(jax.hover.id); + frame.parentNode.removeChild(frame); + if (jax.hover.remove) {clearTimeout(jax.hover.remove)} + delete jax.hover; + }, + HoverFadeTimer: function (jax,inc,delay) { + jax.hover.inc = inc; + if (!jax.hover.timer) { + jax.hover.timer = setTimeout(CALLBACK(["HoverFade",this,jax]),(delay||50)); + } + }, + HoverMenu: function (event) { + if (!event) {event = window.event} + OUTPUT[this.jax].ContextMenu(event,this.math,true); + }, + + Em: function (m) { + if (Math.abs(m) < .0006) {return "0em"} + return m.toFixed(3).replace(/\.?0+$/,"") + "em"; + }, + + // + // Preload images so they show up with the menu + // + getImages: function () { + var menu = new Image(); + menu.src = AJAX.fileURL(OUTPUT.imageDir+"/MenuArrow-15.png"); + } + + }; + + // + // 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 = UI.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.last = now; + 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) { + if (TOUCH.timeout) { + clearTimeout(TOUCH.timeout); + delete TOUCH.timeout; TOUCH.last = 0; + 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; + return EVENT.Handler((event.touches[0]||event.touch),"ContextMenu",math); + } + + }; + + if (MathJax.Hub.Browser.isMobile) { + var arrow = CONFIG.styles[".MathJax_Hover_Arrow"]; + arrow.width = "25px"; arrow.height = "18px"; + arrow.top = "-11px"; arrow.right = "-15px"; + } + + HUB.Browser.Select({ + MSIE: function (browser) { + if ((document.documentMode||0) < 9) {EVENT.LEFTBUTTON = 1} + EVENT.msieBorderWidthBug = (document.compatMode === "BackCompat"); + } + }); + + CONFIG = HUB.CombineConfig("UIevents",CONFIG); + + CALLBACK.Queue( + ["getImages",HOVER], + ["Styles",AJAX,CONFIG.styles], + ["Post",HUB.Startup.signal,"UIevents Ready"], + ["loadComplete",AJAX,"[MathJax]/extensions/UIevents.js"] + ); + +})(MathJax.Hub,MathJax.HTML,MathJax.Ajax,MathJax.Callback,MathJax.OutputJax); \ No newline at end of file diff --git a/unpacked/jax/output/HTML-CSS/config.js b/unpacked/jax/output/HTML-CSS/config.js index fddae6a7d..def52aba6 100644 --- a/unpacked/jax/output/HTML-CSS/config.js +++ b/unpacked/jax/output/HTML-CSS/config.js @@ -24,7 +24,7 @@ MathJax.OutputJax["HTML-CSS"] = MathJax.OutputJax({ id: "HTML-CSS", - version: "1.1.9", + version: "1.1.10", directory: MathJax.OutputJax.directory + "/HTML-CSS", extensionDir: MathJax.OutputJax.extensionDir + "/HTML-CSS", autoloadDir: MathJax.OutputJax.directory + "/HTML-CSS/autoload", diff --git a/unpacked/jax/output/HTML-CSS/jax.js b/unpacked/jax/output/HTML-CSS/jax.js index e0c80652a..88fcf46ac 100644 --- a/unpacked/jax/output/HTML-CSS/jax.js +++ b/unpacked/jax/output/HTML-CSS/jax.js @@ -182,10 +182,7 @@ } }); - var EVENT = MathJax.HTML.Event; - var TOUCH = MathJax.HTML.Touch; - var HOVER = MathJax.HTML.Hover; - + var EVENT, TOUCH, HOVER; // filled in later HTMLCSS.Augment({ config: { @@ -302,9 +299,19 @@ } HUB.Startup.signal.Post("HTML-CSS Jax - no valid font"); } + this.require.push(MathJax.OutputJax.extensionDir+"/UIevents.js"); }, Startup: function () { + // Set up event handling + EVENT = MathJax.Extension.UIevents.Event; + TOUCH = MathJax.Extension.UIevents.Touch; + HOVER = MathJax.Extension.UIevents.Hover; + this.HandleEvent = EVENT.HandleEvent; + this.Mouseover = HOVER.Mouseover; + this.Mouseout = HOVER.Mouseout; + this.Mousemove = HOVER.Mousemove; + // Set up default fonts var family = [], fonts = this.FONTDATA.VARIANT.normal.fonts; if (!(fonts instanceof Array)) {fonts = [fonts]} @@ -418,7 +425,6 @@ if (this.useProcessingFrame) frame.parentNode.replaceChild(div,frame); }, - HandleEvent: EVENT.HandleEvent, ContextMenu: function (event,math,force) { if (this.config.showMathMenu && (this.settings.context === "MathJax" || force)) { if (this.safariContextMenuBug) {setTimeout("window.getSelection().empty()",0)} @@ -432,15 +438,11 @@ if (this.settings.context === "MathJax") { if (!this.noContextMenuBug || event.button !== 2) return } else { - var BUTTON = (EVENT.msieButtonBug ? event.buttons & 1 : event.button); - if (!event[EVENT.MENUKEY] || BUTTON !== EVENT.LEFTBUTTON) return + if (!event[EVENT.MENUKEY] || event.button !== EVENT.LEFTBUTTON) return } return this.ContextMenu(event,math,true); } }, - Mouseover: HOVER.Mouseover, - Mouseout: HOVER.Mouseout, - Mousemove: HOVER.Mousemove, getJaxFromMath: function (math) { if (math.parentNode.className === "MathJax_Display") {math = math.parentNode} return HUB.getJaxFor(math.nextSibling); @@ -2150,14 +2152,6 @@ HUB.Register.StartupHook("End Config",function () { - /* - * if (MathJax.Hub.Browser.isMobile) { - * var arrow = HTMLCSS.config.styles[".MathJax_Hover_Arrow"]; - * arrow.width = "25px"; arrow.height = "18px"; - * arrow.top = "-11px"; arrow.right = "-15px"; - * } - */ - // // Handle browser-specific setup //