/**
* SVGZoomNPan library 1.0
* ========================
*
*/
var ZoomNPan = function (zoomElt) {
if (zoomElt != '[object SVGSVGElement]') {
console.log('Argument passed is not an SVG element');
return;
}
this.zoomElt = zoomElt ;
this.options = {
autoRun:true,
autoFitViewport:true,
zoomScale:1.2,
fastZoomMultiplier: 5,
toggleZoomKeyCode: 27,
cursorGrab: ' move',
cursorHand: ' default'
};
this.currentState = {
state: 'none',
panX: 0,
panY: 0,
translateX: 0,
translateY: 0,
oldTranslateX: 0,
oldTranslateY: 0,
zoomAllowed: this.options.autoRun,
timePrev: 0
};
this.init()
};
ZoomNPan.prototype.init = function () {
var self = this;
// Setup event listeners
this.zoomElt.addEventListener('mousedown', function(evt){self.handleMouseDown (evt, self)}, false);
this.zoomElt.addEventListener('mouseup', function(evt){self.handleMouseUp (evt, self)}, false);
this.zoomElt.addEventListener('mousemove', function(evt){self.handleMouseMove (evt, self)}, false);
this.zoomElt.addEventListener('mousewheel', function(evt){self.handleMouseWheel (evt, self)}, false);
this.zoomElt.addEventListener('keyup', function(evt){self.handleKeyUp (evt, self)}, false);
//zoomElt.addEventListener('DOMMouseScroll', handleMouseWheel, false);// mousewheel for Firefox
//this.currentState.zoomAllowed = this.options.autoRun
// Scale the image to fit screen
if (self.options.autoFitViewport){
self.makeViewBox(self.zoomElt);
}
}
ZoomNPan.prototype.handleMouseMove = function (evt, Z) {
if (Z.currentState.zoomAllowed){
if(evt.preventDefault){
evt.preventDefault();
}
evt.returnValue = false;
if(Z.currentState.state == 'pan') {
// Pan mode
// get difference to previous position
Z.currentState.translateX = evt.x - Z.currentState.panX;
Z.currentState.translateY = evt.y - Z.currentState.panY;
evt.currentTarget.currentTranslate.x += Z.currentState.translateX;
evt.currentTarget.currentTranslate.y += Z.currentState.translateY;
// Remember previous position
Z.currentState.panX = evt.x;
Z.currentState.panY = evt.y;
}
}
}
ZoomNPan.prototype.handleMouseDown = function (evt, Z) {
if (Z.currentState.zoomAllowed) {
// Handle click
if(evt.preventDefault) {
evt.preventDefault();
}
evt.returnValue = false;
if(evt.currentTarget.tagName == "svg") {
// Pan mode
Z.currentState.state = 'pan';
Z.zoomElt.style.cursor = Z.options.cursorGrab;
Z.currentState.panX = evt.x;
Z.currentState.panY = evt.y;
}
}
}
ZoomNPan.prototype.handleMouseUp = function(evt, Z) {
//alert('mouse released')
if (Z.currentState.zoomAllowed){
// Handle mouse button release
if(evt.preventDefault){
evt.preventDefault();
}
evt.returnValue = false;
if (Z.currentState.state == 'pan') {
// Quit pan mode
Z.zoomElt.style.cursor = Z.options.cursorHand;//'default'
Z.currentState.state = '';
}
}
}
ZoomNPan.prototype.handleMouseWheel = function (evt, Z) {
if (Z.currentState.zoomAllowed){
if (evt.preventDefault){
evt.preventDefault();
}
evt.returnValue = false;
var delta;
if (evt.wheelDelta) {
delta = evt.wheelDelta / 360; // Webkit, Opera
}
else{
delta = evt.detail / -9; // Mozilla, Opera
}
if (evt.altKey){
delta *= Z.options.fastZoomMultiplier;
}
ZoomNPan.prototype.zoomIt(delta, Z);
}
}
ZoomNPan.prototype.handleKeyUp = function (evt, Z) {
if (evt.keyCode != Z.options.toggleZoomKeyCode) {
return
}
// Global killswitch :)
// Toggle zoom and pan when toggle key is pressed (default "z")
Z.currentState.zoomAllowed = !Z.currentState.zoomAllowed;
if (!Z.currentState.zoomAllowed){
Z.resetState();
}
}
ZoomNPan.prototype.handleMouseOut = function (evt, Z){
if (Z.currentState.zoomAllowed){
// not used, buggy
// Handle mouse out event
if(evt.preventDefault){
evt.preventDefault();
}
evt.returnValue = false;
if (Z.currentState.state == 'pan') {
// Quit pan mode
Z.currentState.state = '';
}
}
}
ZoomNPan.prototype.zoomIt = function (delta, Z) {
var timeNow = Date.now();
if (timeNow - Z.currentState.timePrev > 0) { // skip unnecessary redraw
var cur = Z.currentState;
var ZE = Z.zoomElt;
var oldScale = ZE.currentScale;
var scaleFactor = Math.pow(1 + Z.options.zoomScale, delta);
// Remember translate before zooming
cur.oldTranslateX = ZE.currentTranslate.x;
cur.oldTranslateY = ZE.currentTranslate.y;
ZE.currentScale *= scaleFactor;
var vp_width = ZE.viewport.width;
var vp_height = ZE.viewport.height;
// Very complicated calculations :)
// Borrowed from http://jwatt.org/svg/tests/zoom-and-pan-controls.svg
ZE.currentTranslate.x = vp_width/2 - (ZE.currentScale/oldScale) * (vp_width/2 - cur.oldTranslateX);
ZE.currentTranslate.y = vp_height/2 - (ZE.currentScale/oldScale) * (vp_height/2 - cur.oldTranslateY);
cur.timePrev = timeNow;
}
}
ZoomNPan.prototype.zoom1x = function (Z) {
Z.zoomElt.currentScale = 1;//(1.0);
Z.zoomElt.currentTranslate.x = 0;//(1.0);
Z.zoomElt.currentTranslate.y = 0;//(1.0);
}
ZoomNPan.prototype.zoomIn = function (Z) {
zoomIt (0.23, Z)
}
ZoomNPan.prototype.zoomOut = function (Z) {
zoomIt (-0.23, Z)
}
ZoomNPan.prototype.resetState = function(Z) {
//if (!Z.currentState.zoomAllowed){
Z.zoomElt.style.cursor = 'default';
Z.currentState.oldTranslateX = 0;
Z.currentState.oldTranslateY = 0;
Z.currentState.state == 'none'
//}
}
ZoomNPan.prototype.makeViewBox = function (SVGElt, Z) {
var doc = SVGElt || Z.zoomElt, // to be able to use this function separately
w = parseFloat(doc.viewport.width) || 0,
h = parseFloat(doc.viewport.height) || 0,
wAttr = doc.getAttributeNS(null, "width"),
hAttr = doc.getAttributeNS(null, "height"),
vB = doc.viewBox.baseVal;//SVGRoot.getAttributeNS(null, "viewBox");
if (!wAttr && !hAttr && !(vB && vB.width)) {
// If there are NO width & height & viewbox try to guess them
var BBox = doc.getBBox();
vB.x = BBox.x;
vB.y = BBox.y;
vB.width = BBox.width;
vB.height = BBox.height;
}
else if ((wAttr || hAttr) && !vB.width) {
// If there IS width or height and NO viewBox,
// generate viewbox based on them
vB.x = doc.x.baseVal.value;
vB.y = doc.y.baseVal.value;
vB.width = w || doc.width.baseVal.value;
vB.height = h || doc.height.baseVal.value;
doc.removeAttributeNS(null, 'width');
doc.removeAttributeNS(null, 'height');
}
else if((wAttr || hAttr) && vB.width){
// If there are ALL OF width/height/viewBox
// only remove width and height attibutes
// or opera won't scale to fit the image
doc.removeAttributeNS(null, 'width');
doc.removeAttributeNS(null, 'height');
}
return doc;
}
Refactorings
No refactoring yet !
lomax
September 13, 2010, September 13, 2010 20:53, permalink
The way you've bound the event listeners, Z is equivalent to the keyword 'this' in every single one of your prototype methods. Try "alert(this==Z)" in one of your methods to verify. The reason I suspect you got confused was because of the scope change during your anonymous function event rebind.
for example,
this.zoomElt.addEventListener('mousedown', function(evt){self.handleMouseDown (evt, self)}, false);
can be changed without semantic loss to
this.zoomElt.addEventListener('mousedown', function(evt){self.handleMouseDown (evt)}, false);
because you've already established the link to 'this'/self/Z by calling the bound instance method self.handleMouseDown; this means that inside handleMouseDown, the keyword 'this' will refer to whatever 'self' refers to in the anonymous function, and can supplant the reference to 'Z'.
However, I would go even further, and suggest that you bind pointer references, instead of anonymous functions. I also suspect that this class is instantiated perhaps once or twice per user experience, not the thousand+ times that would offer a performance benefit in sharing a single prototype method over an instance method. Therefore, I would suggest refactoring your class in a more encapsulated manner as so:
*(Note that this is clearly untested, I hope I didn't miss any public/private conversions; I also had to make some guesses about which methods needed to be public to consume the class programmatically, and which were artifacts of using the prototype pattern; If I guessed incorrectly, simply follow the pattern and adjust accordingly)
/**
* SVGZoomNPan library 1.0
* ========================
*
*/
var ZoomNPan=function(){
//private members
var _currentState;
var _initialized;
var _options;
var _this;
var _zoomElt;
//public members
this.currentState;
this.options;
// ctor
function ZoomNPan(zoomElt){
// Guard parameters
if(zoomElt != '[object SVGSVGElement]') {
console.log('Argument passed is not an SVG element');
return;
}
// Set defaults/private members
_currentState={
state: 'none',
panX: 0,
panY: 0,
translateX: 0,
translateY: 0,
oldTranslateX: 0,
oldTranslateY: 0,
zoomAllowed: _options.autoRun,
timePrev: 0
};
_options={
autoRun:true,
autoFitViewport:true,
zoomScale:1.2,
fastZoomMultiplier: 5,
toggleZoomKeyCode: 27,
cursorGrab: ' move',
cursorHand: ' default'
};
_this=this;
_zoomElt=zoomElt;
// Assign public members
// NOTE: omit these public assignments if you don't really need them exposed;
// the class no longer uses them internally. Also delete them in the
// public member placeholder assignment above; I can't tell your intended
// usage from the class definition.
this.zoomElt=_zoomElt;
this.currentState=_currentState;
this.options=_options;
addEvents();
scaleImage();
_initialized=true;
}
// Call ctor, preserving instantiation scope; equivalent to your this.init() call;
ZoomNPan.apply(this,arguments);
// Public methods
// NOTE: I made best guesses about which methods actually need to be exposed.
// If I guessed wrong, simply follow the pattern to expose more/less as needed.
this.zoomIt=function (delta) {
ensureControl();
var timeNow = Date.now();
if (timeNow - _currentState.timePrev > 0) { // skip unnecessary redraw
var cur = _currentState;
var ZE = _zoomElt;
var oldScale = ZE.currentScale;
var scaleFactor = Math.pow(1 + _options.zoomScale, delta);
// Remember translate before zooming
cur.oldTranslateX = ZE.currentTranslate.x;
cur.oldTranslateY = ZE.currentTranslate.y;
ZE.currentScale *= scaleFactor;
var vp_width = ZE.viewport.width;
var vp_height = ZE.viewport.height;
// Very complicated calculations :)
// Borrowed from http://jwatt.org/svg/tests/zoom-and-pan-controls.svg
ZE.currentTranslate.x = vp_width/2 - (ZE.currentScale/oldScale) * (vp_width/2 - cur.oldTranslateX);
ZE.currentTranslate.y = vp_height/2 - (ZE.currentScale/oldScale) * (vp_height/2 - cur.oldTranslateY);
cur.timePrev = timeNow;
}
}
this.zoom1x=function() {
ensureControl();
_zoomElt.currentScale = 1;//(1.0);
_zoomElt.currentTranslate.x = 0;//(1.0);
_zoomElt.currentTranslate.y = 0;//(1.0);
}
this.zoomIn=function() {
ensureControl();
this.zoomIt(0.23)
}
this.zoomOut=function() {
ensureControl();
this.zoomIt(-0.23)
}
// Private methods
function addEvents(){
_zoomElt.addEventListener('mousedown', handleMouseDown, false);
_zoomElt.addEventListener('mouseup', handleMouseUp, false);
_zoomElt.addEventListener('mousemove', handleMouseMove, false);
_zoomElt.addEventListener('mousewheel', handleMouseWheel, false);
_zoomElt.addEventListener('keyup', handleKeyUp, false);
//_zoomElt.addEventListener('DOMMouseScroll', handleMouseWheel, false);// mousewheel for Firefox
}
function cancelEvent(evt){
if(!evt)return;
if(evt.preventDefault){
evt.preventDefault();
}
evt.returnValue = false;
}
function ensureControl(){
if(!_initialized)throw new Error("ZoomNPan: control has not been initialized.");
}
function makeViewBox(SVGElt) {
var doc = SVGElt || _zoomElt; // to be able to use this function separately
var w = parseFloat(doc.viewport.width) || 0;
var h = parseFloat(doc.viewport.height) || 0;
var wAttr = doc.getAttributeNS(null, "width");
var hAttr = doc.getAttributeNS(null, "height");
var vB = doc.viewBox.baseVal;//SVGRoot.getAttributeNS(null, "viewBox");
if (!wAttr && !hAttr && !(vB && vB.width)) {
// If there are NO width & height & viewbox try to guess them
var BBox = doc.getBBox();
vB.x = BBox.x;
vB.y = BBox.y;
vB.width = BBox.width;
vB.height = BBox.height;
}
else if ((wAttr || hAttr) && !vB.width) {
// If there IS width or height and NO viewBox,
// generate viewbox based on them
vB.x = doc.x.baseVal.value;
vB.y = doc.y.baseVal.value;
vB.width = w || doc.width.baseVal.value;
vB.height = h || doc.height.baseVal.value;
doc.removeAttributeNS(null, 'width');
doc.removeAttributeNS(null, 'height');
}
else if((wAttr || hAttr) && vB.width){
// If there are ALL OF width/height/viewBox
// only remove width and height attibutes
// or opera won't scale to fit the image
doc.removeAttributeNS(null, 'width');
doc.removeAttributeNS(null, 'height');
}
return doc;
}
function resetState(){
//if (!_currentState.zoomAllowed){
_zoomElt.style.cursor = 'default';
_currentState.oldTranslateX = 0;
_currentState.oldTranslateY = 0;
_currentState.state == 'none'
//}
}
function scaleImage(){
// Scale the image to fit screen
if (_options.autoFitViewport){
makeViewBox(_zoomElt);
}
}
// Events
function handleMouseMove(evt) {
if (_currentState.zoomAllowed){
cancelEvent(evt);
if(_currentState.state == 'pan') {
// Pan mode
// get difference to previous position
_currentState.translateX = evt.x - _currentState.panX;
_currentState.translateY = evt.y - _currentState.panY;
evt.currentTarget.currentTranslate.x += _currentState.translateX;
evt.currentTarget.currentTranslate.y += _currentState.translateY;
// Remember previous position
_currentState.panX = evt.x;
_currentState.panY = evt.y;
}
}
}
function handleMouseDown(evt) {
if (_currentState.zoomAllowed) {
cancelEvent(evt);
if(evt.currentTarget.tagName == "svg") {
// Pan mode
_currentState.state = 'pan';
_zoomElt.style.cursor = _options.cursorGrab;
_currentState.panX = evt.x;
_currentState.panY = evt.y;
}
}
}
function handleMouseUp(evt) {
//alert('mouse released')
if (_currentState.zoomAllowed){
cancelEvent(evt);
if (_currentState.state == 'pan') {
// Quit pan mode
_zoomElt.style.cursor = _options.cursorHand;//'default'
_currentState.state = '';
}
}
}
function handleMouseWheel(evt) {
if (_currentState.zoomAllowed){
cancelEvent(evt);
var delta;
if (evt.wheelDelta) {
delta = evt.wheelDelta / 360; // Webkit, Opera
}
else{
delta = evt.detail / -9; // Mozilla, Opera
}
if (evt.altKey){
delta *= _options.fastZoomMultiplier;
}
_this.zoomIt(delta);
}
}
function handleKeyUp(evt) {
if (evt.keyCode != _options.toggleZoomKeyCode) {
return
}
// Global killswitch :)
// Toggle zoom and pan when toggle key is pressed (default "z")
_currentState.zoomAllowed = !_currentState.zoomAllowed;
if (!_currentState.zoomAllowed){
resetState();
}
}
function handleMouseOut(evt){
if (_currentState.zoomAllowed){
// not used, buggy
// Handle mouse out event
cancelEvent(evt);
if (_currentState.state == 'pan') {
// Quit pan mode
_currentState.state = '';
}
}
}
}
lomax
September 13, 2010, September 13, 2010 20:53, permalink
The way you've bound the event listeners, Z is equivalent to the keyword 'this' in every single one of your prototype methods. Try "alert(this==Z)" in one of your methods to verify. The reason I suspect you got confused was because of the scope change during your anonymous function event rebind.
for example,
this.zoomElt.addEventListener('mousedown', function(evt){self.handleMouseDown (evt, self)}, false);
can be changed without semantic loss to
this.zoomElt.addEventListener('mousedown', function(evt){self.handleMouseDown (evt)}, false);
because you've already established the link to 'this'/self/Z by calling the bound instance method self.handleMouseDown; this means that inside handleMouseDown, the keyword 'this' will refer to whatever 'self' refers to in the anonymous function, and can supplant the reference to 'Z'.
However, I would go even further, and suggest that you bind pointer references, instead of anonymous functions. I also suspect that this class is instantiated perhaps once or twice per user experience, not the thousand+ times that would offer a performance benefit in sharing a single prototype method over an instance method. Therefore, I would suggest refactoring your class in a more encapsulated manner as so:
*(Note that this is clearly untested, I hope I didn't miss any public/private conversions; I also had to make some guesses about which methods needed to be public to consume the class programmatically, and which were artifacts of using the prototype pattern; If I guessed incorrectly, simply follow the pattern and adjust accordingly)
/**
* SVGZoomNPan library 1.0
* ========================
*
*/
var ZoomNPan=function(){
//private members
var _currentState;
var _initialized;
var _options;
var _this;
var _zoomElt;
//public members
this.currentState;
this.options;
// ctor
function ZoomNPan(zoomElt){
// Guard parameters
if(zoomElt != '[object SVGSVGElement]') {
console.log('Argument passed is not an SVG element');
return;
}
// Set defaults/private members
_currentState={
state: 'none',
panX: 0,
panY: 0,
translateX: 0,
translateY: 0,
oldTranslateX: 0,
oldTranslateY: 0,
zoomAllowed: _options.autoRun,
timePrev: 0
};
_options={
autoRun:true,
autoFitViewport:true,
zoomScale:1.2,
fastZoomMultiplier: 5,
toggleZoomKeyCode: 27,
cursorGrab: ' move',
cursorHand: ' default'
};
_this=this;
_zoomElt=zoomElt;
// Assign public members
// NOTE: omit these public assignments if you don't really need them exposed;
// the class no longer uses them internally. Also delete them in the
// public member placeholder assignment above; I can't tell your intended
// usage from the class definition.
this.zoomElt=_zoomElt;
this.currentState=_currentState;
this.options=_options;
addEvents();
scaleImage();
_initialized=true;
}
// Call ctor, preserving instantiation scope; equivalent to your this.init() call;
ZoomNPan.apply(this,arguments);
// Public methods
// NOTE: I made best guesses about which methods actually need to be exposed.
// If I guessed wrong, simply follow the pattern to expose more/less as needed.
this.zoomIt=function (delta) {
ensureControl();
var timeNow = Date.now();
if (timeNow - _currentState.timePrev > 0) { // skip unnecessary redraw
var cur = _currentState;
var ZE = _zoomElt;
var oldScale = ZE.currentScale;
var scaleFactor = Math.pow(1 + _options.zoomScale, delta);
// Remember translate before zooming
cur.oldTranslateX = ZE.currentTranslate.x;
cur.oldTranslateY = ZE.currentTranslate.y;
ZE.currentScale *= scaleFactor;
var vp_width = ZE.viewport.width;
var vp_height = ZE.viewport.height;
// Very complicated calculations :)
// Borrowed from http://jwatt.org/svg/tests/zoom-and-pan-controls.svg
ZE.currentTranslate.x = vp_width/2 - (ZE.currentScale/oldScale) * (vp_width/2 - cur.oldTranslateX);
ZE.currentTranslate.y = vp_height/2 - (ZE.currentScale/oldScale) * (vp_height/2 - cur.oldTranslateY);
cur.timePrev = timeNow;
}
}
this.zoom1x=function() {
ensureControl();
_zoomElt.currentScale = 1;//(1.0);
_zoomElt.currentTranslate.x = 0;//(1.0);
_zoomElt.currentTranslate.y = 0;//(1.0);
}
this.zoomIn=function() {
ensureControl();
this.zoomIt(0.23)
}
this.zoomOut=function() {
ensureControl();
this.zoomIt(-0.23)
}
// Private methods
function addEvents(){
_zoomElt.addEventListener('mousedown', handleMouseDown, false);
_zoomElt.addEventListener('mouseup', handleMouseUp, false);
_zoomElt.addEventListener('mousemove', handleMouseMove, false);
_zoomElt.addEventListener('mousewheel', handleMouseWheel, false);
_zoomElt.addEventListener('keyup', handleKeyUp, false);
//_zoomElt.addEventListener('DOMMouseScroll', handleMouseWheel, false);// mousewheel for Firefox
}
function cancelEvent(evt){
if(!evt)return;
if(evt.preventDefault){
evt.preventDefault();
}
evt.returnValue = false;
}
function ensureControl(){
if(!_initialized)throw new Error("ZoomNPan: control has not been initialized.");
}
function makeViewBox(SVGElt) {
var doc = SVGElt || _zoomElt; // to be able to use this function separately
var w = parseFloat(doc.viewport.width) || 0;
var h = parseFloat(doc.viewport.height) || 0;
var wAttr = doc.getAttributeNS(null, "width");
var hAttr = doc.getAttributeNS(null, "height");
var vB = doc.viewBox.baseVal;//SVGRoot.getAttributeNS(null, "viewBox");
if (!wAttr && !hAttr && !(vB && vB.width)) {
// If there are NO width & height & viewbox try to guess them
var BBox = doc.getBBox();
vB.x = BBox.x;
vB.y = BBox.y;
vB.width = BBox.width;
vB.height = BBox.height;
}
else if ((wAttr || hAttr) && !vB.width) {
// If there IS width or height and NO viewBox,
// generate viewbox based on them
vB.x = doc.x.baseVal.value;
vB.y = doc.y.baseVal.value;
vB.width = w || doc.width.baseVal.value;
vB.height = h || doc.height.baseVal.value;
doc.removeAttributeNS(null, 'width');
doc.removeAttributeNS(null, 'height');
}
else if((wAttr || hAttr) && vB.width){
// If there are ALL OF width/height/viewBox
// only remove width and height attibutes
// or opera won't scale to fit the image
doc.removeAttributeNS(null, 'width');
doc.removeAttributeNS(null, 'height');
}
return doc;
}
function resetState(){
//if (!_currentState.zoomAllowed){
_zoomElt.style.cursor = 'default';
_currentState.oldTranslateX = 0;
_currentState.oldTranslateY = 0;
_currentState.state == 'none'
//}
}
function scaleImage(){
// Scale the image to fit screen
if (_options.autoFitViewport){
makeViewBox(_zoomElt);
}
}
// Events
function handleMouseMove(evt) {
if (_currentState.zoomAllowed){
cancelEvent(evt);
if(_currentState.state == 'pan') {
// Pan mode
// get difference to previous position
_currentState.translateX = evt.x - _currentState.panX;
_currentState.translateY = evt.y - _currentState.panY;
evt.currentTarget.currentTranslate.x += _currentState.translateX;
evt.currentTarget.currentTranslate.y += _currentState.translateY;
// Remember previous position
_currentState.panX = evt.x;
_currentState.panY = evt.y;
}
}
}
function handleMouseDown(evt) {
if (_currentState.zoomAllowed) {
cancelEvent(evt);
if(evt.currentTarget.tagName == "svg") {
// Pan mode
_currentState.state = 'pan';
_zoomElt.style.cursor = _options.cursorGrab;
_currentState.panX = evt.x;
_currentState.panY = evt.y;
}
}
}
function handleMouseUp(evt) {
//alert('mouse released')
if (_currentState.zoomAllowed){
cancelEvent(evt);
if (_currentState.state == 'pan') {
// Quit pan mode
_zoomElt.style.cursor = _options.cursorHand;//'default'
_currentState.state = '';
}
}
}
function handleMouseWheel(evt) {
if (_currentState.zoomAllowed){
cancelEvent(evt);
var delta;
if (evt.wheelDelta) {
delta = evt.wheelDelta / 360; // Webkit, Opera
}
else{
delta = evt.detail / -9; // Mozilla, Opera
}
if (evt.altKey){
delta *= _options.fastZoomMultiplier;
}
_this.zoomIt(delta);
}
}
function handleKeyUp(evt) {
if (evt.keyCode != _options.toggleZoomKeyCode) {
return
}
// Global killswitch :)
// Toggle zoom and pan when toggle key is pressed (default "z")
_currentState.zoomAllowed = !_currentState.zoomAllowed;
if (!_currentState.zoomAllowed){
resetState();
}
}
function handleMouseOut(evt){
if (_currentState.zoomAllowed){
// not used, buggy
// Handle mouse out event
cancelEvent(evt);
if (_currentState.state == 'pan') {
// Quit pan mode
_currentState.state = '';
}
}
}
}
In this class all prototype functions except for init are passed the instance of the class to operate on. Is there a way to make it work without passing the instance(named Z in the script) every time ?