var Pan = function(img) {
    var obj = this;
    obj.image = $(img);
    obj.viewport = $(obj.image.getOffsetParent());
    obj.originalWidth = img.width;
    obj.originalHeight = img.height;
    obj.zoomRatio = null;
    
    // Normalize points before and after zoom
    obj.pointToAbsolute = function(p) {return([p[0]/obj.zoomRatio, p[1]/obj.zoomRatio]);}
    obj.pointToRelative = function(p) {return([p[0]*obj.zoomRatio, p[1]*obj.zoomRatio]);}
    // Simple calculation of min and max values for left and top for image within the viewport
    obj.topBounds = function(top, ih, vh){if(ih==undefined) ih=obj.image.getDimensions().height; if(vh==undefined) vh=obj.viewport.getDimensions().height; if(vh>ih) return((vh-ih)/2); if(top>0)return(0); var minTop=(ih-vh)*-1; if(top<minTop) return(minTop); return(top);}
    obj.leftBounds = function(left, iw, vw){if(iw==undefined) iw=obj.image.getDimensions().width; if(vw==undefined) vw=obj.viewport.getDimensions().width; if(vw>iw) return((vw-iw)/2); if(left>0)return(0); var minLeft=(iw-vw)*-1; if(left<minLeft) return(minLeft); return(left);}
    
    // Center image around point 'p'. Optionally, include width (w) and height (h) of the image after zoom
    obj.lastCenter = null;
    obj.center = function(p, w, h, animate) {
        if(animate==undefined) animate=true;
        var i = obj.image; var vd = obj.viewport.getDimensions();
        var c = [vd.width/2, vd.height/2]; var r = [obj.leftBounds(-1*(p[0]-c[0]), w), obj.topBounds(-1*(p[1]-c[1]), h)];
        obj.lastCenter = p;
        if (animate) i.morph({left:r[0]+'px', top:r[1]+'px'}, {duration:.3});
        else i.setStyle({left:r[0]+'px', top:r[1]+'px'});
    }
    
    // Zoom by a number of pixels (by) and center around a point (p). If point is not included, the center of the photo is used.
    obj.zoom = function(by, p, animate) {
        if(animate==undefined) animate=true;
        var i = obj.image; var id = i.getDimensions(); var vd = obj.viewport.getDimensions();
        
        // Find width and height, and make sure the values are within bounds
        var newWidth = id.width+by; 
        if (newWidth>obj.originalWidth) {newWidth=obj.originalWidth;}
        var newHeight = obj.originalHeight*(newWidth/obj.originalWidth);
        if (newWidth<vd.width && newHeight<vd.height) {
            if(vd.width/vd.height>newWidth/newHeight) {
                newHeight=vd.height; newWidth=obj.originalWidth*(newHeight/obj.originalHeight); 
            } else {
                newWidth=vd.width; newHeight=obj.originalHeight*(newWidth/obj.originalWidth); 
            }
        }
        
        // Update the zoom ratio and center the image around the selected point
        p = (p!=undefined ? obj.pointToAbsolute(p) : [obj.originalWidth/2,obj.originalHeight/2]);
        obj.zoomRatio = newWidth/obj.originalWidth;
        obj.center(obj.pointToRelative(p), newWidth, newHeight, animate);
        
        // If the width or the height has changed, animate the zoom
        if (newWidth!=i.width || newHeight!=i.height) {
            if (animate) i.morph({width:newWidth+'px', height:newHeight+'px'}, {duration:.3});
            else i.setStyle({width:newWidth+'px', height:newHeight+'px'});
        }
    }
    // Center the photo in the viewport at its minimum height and width.
    obj.zoom(-999999, undefined, false);
    
    // Do zoom() and center() on dblclick
    obj.image.observe('dblclick', function(e) {obj.zoom(e.altKey?-500:250, [e.layerX, e.layerY]);});
    // Drag image within viewport
    obj.dragBegin = obj.mouseBegin = null;
    obj.viewport.observe('mousedown', function(e) {obj.dragBegin=obj.image.positionedOffset(); obj.mouseBegin=[e.clientX, e.clientY]; Event.stop(e);});
    document.observe('mouseup', function(e) {obj.dragBegin=obj.mouseBegin=null; Event.stop(e);});
    document.observe('mousemove', function(e) {if (obj.mouseBegin==null || obj.dragBegin==null) return; obj.image.setStyle({left:obj.leftBounds(obj.dragBegin[0]+e.clientX-obj.mouseBegin[0])+'px', top:obj.topBounds(obj.dragBegin[1]+e.clientY-obj.mouseBegin[1])+'px'});});
    // Recenter the image when the window is scaled
    Event.observe(window, 'resize', function(e){obj.center(obj.lastCenter);});
    // No contextmenu and not selectable
    document.observe('contextmenu', Event.stop);
    obj.viewport.observe('selectstart', Event.stop);
}
