/*\ title: $:/core/modules/macros/zoomer.js type: application/javascript module-type: macro Zooming navigator macro \*/ (function(){ /*jslint node: true, browser: true */ /*global $tw: false */ "use strict"; exports.info = { name: "zoomer", params: { } }; exports.startZoomer = function(x,y) { this.inZoomer = true; this.startX = x; this.startY = y; $tw.utils.addClass(document.body,"in-zoomer"); }; /* Zoom the body element given a touch/mouse position in screen coordinates */ exports.hoverZoomer = function(x,y) { // Put the transform origin at the top in the middle document.body.style[$tw.browser.transformorigin] = "50% 0"; // Some shortcuts this.bodyWidth = document.body.offsetWidth; this.bodyHeight = document.body.offsetHeight; this.windowWidth = window.innerWidth; this.windowHeight = window.innerHeight; // Compute the scale factor for fitting the entire page into the window. This is // the scale factor we'll use when the touch is far to the left this.minScale = this.windowHeight / this.bodyHeight; if(this.minScale < 0.1) { // Don't scale to less than 10% of original size this.minScale = 0.1; } else if(this.minScale > 1) { // Nor should we scale up if the body is shorter than the window this.minScale = 1; } // We divide the screen into two horizontal zones divided by the right edge of the body at maximum zoom (ie minimum scale) this.splitPos = this.windowWidth/2 + (this.bodyWidth * this.minScale)/2; // Compute the 0->1 ratio (from right to left) of the position of the touch within the right zone this.xFactor = (this.windowWidth - x) / (this.windowWidth - this.splitPos); if(this.xFactor > 1) { this.xFactor = 1; } // And the 0->1 ratio (from top to bottom) of the position of the touch down the screen this.yFactor = y/this.windowHeight; // Now interpolate the scale this.scale = (this.minScale - 1) * this.xFactor + 1; // Apply the transform. The malarkey with .toFixed() is because otherwise we might get numbers in // exponential notation (such as 5.1e-15) that are illegal in CSS var preTranslateY = window.scrollY * this.xFactor, scale = this.scale, postTranslateY = ((this.windowHeight / this.scale) - this.bodyHeight) * this.yFactor * this.xFactor; var transform = "translateY(" + preTranslateY.toFixed(8) + "px) " + "scale(" + scale.toFixed(8) + ") " + "translateY(" + postTranslateY.toFixed(8) + "px)"; document.body.style[$tw.browser.transform] = transform; }; exports.stopZoomer = function() { var newScrollY = this.yFactor * (this.bodyHeight - this.windowHeight); this.inZoomer = false; window.scrollTo(0,newScrollY); document.body.style[$tw.browser.transform] = "translateY(" + newScrollY * this.xFactor + "px) " + "scale(" + this.scale + ") " + "translateY(" + ((this.windowHeight / this.scale) - this.bodyHeight) * this.yFactor * this.xFactor + "px)"; $tw.utils.removeClass(document.body,"in-zoomer"); document.body.style[$tw.browser.transform] = "translateY(0) scale(1) translateY(0)"; }; exports.handleEvent = function(event) { switch(event.type) { case "touchstart": this.startZoomer(event.touches[0].clientX,event.touches[0].clientY); this.hoverZoomer(event.touches[0].clientX,event.touches[0].clientY); event.preventDefault(); return false; case "touchmove": this.hoverZoomer(event.touches[0].clientX,event.touches[0].clientY); event.preventDefault(); return false; case "touchend": this.stopZoomer(); event.preventDefault(); return false; } }; exports.executeMacro = function() { this.inZoomer = false; var attributes = { style: { "position": "absolute", "right": "0", "top": "0", "min-width": "16px", "height": "100%" // Makes the height the same as the body, since the body is position:relative } }; if(this.classes) { attributes["class"] = this.classes.slice(0); } return $tw.Tree.Element("div",attributes,[],{ events: ["touchstart","touchmove","touchend"], eventHandler: this }); }; })();