var vZoom = new Class({
    Implements: [Options],
    Implements: [Events],
    // Initialize
    initialize: function(imageEl, trackEl, handleEl, zoomInEl, zoomOutEl, zoomContainer){
        this.containerEl = $(imageEl).parentNode;
        this.imageEl = $(imageEl);
        this.handleEl = $(handleEl);
        this.trackEl = $(trackEl);
        this.zoomInEl = $(zoomInEl);
        this.zoomOutEl = $(zoomOutEl);
        this.zoomContainer = $(zoomContainer);
        
        this.containerEl.style.width = '100%';
        if(this.containerEl.offsetWidth > this.imageEl.offsetWidth) this.containerEl.style.width = this.imageEl.offsetWidth+'px';

        var contWidth = this.containerEl.offsetWidth;
        var contHeight = this.containerEl.offsetHeight;
        this.containerDim = {'size':{'x':contWidth,'y':contHeight}};

        var imgWidth = this.imageEl.offsetWidth;
        var imgHeight = this.imageEl.offsetHeight;
        this.imageDim = {'size':{'x':imgWidth,'y':imgHeight}};
        
        if(imgHeight > contHeight){
            var fade = new Fx.Style($('dragImageInfo'), 'opacity', {wait:false});
            var color = new Fx.Style($('dragImageInfo'), 'color', {wait:false});
            fade.start(1);
            color.start('c0c0c0');
        }else{
            var fade = new Fx.Style($('dragImageInfo'), 'opacity', {wait:false});
            var color = new Fx.Style($('dragImageInfo'), 'color', {wait:false});
            fade.start(0);
            color.start('f4f4f4');
        }

        this.imageDim.ratio = this.imageDim.size.x/this.imageDim.size.y;

        this.floorZoom = 1;
        
        this.sliderValue = 0;
        this.sliderDisabled = 0;

        this.ceilingZoom = this.imageDim.size.x / this.containerDim.size.x;

        if (this.imageDim.size.x <= this.containerDim.size.x
            && this.imageDim.size.y <= this.containerDim.size.y) {
            //this.trackEl.style.visibility = 'hidden';
            this.zoomContainer.style.visibility = 'hidden';
            this.imageEl.style.cursor = 'default';
        }else{
            //this.trackEl.style.visibility = 'visible';
            this.zoomContainer.style.visibility = 'visible';
            this.imageEl.style.cursor = 'move';
        }
        if (this.imageDim.size.x <= this.containerDim.size.x && this.imageDim.size.y >= this.containerDim.size.y){
            //this.trackEl.style.visibility = 'hidden';    
            this.zoomContainer.style.visibility = 'hidden';
        }

        this.imageX = 0;
        this.imageY = 0;
        this.imageZoom = 1;

        this.sliderSpeed = 0;
        this.sliderAccel = 0;
        this.zoomBtnPressed = false;

        this.showFull = false;

        this.selects = document.getElementsByTagName('select');

        this.trackElSize = this.trackEl.offsetWidth;
        var that = this;

        this.slider = new Slider($(trackEl), $(handleEl), {
            steps: that.trackElSize,
            onChange: function(step){
                that.scale(step);
            }
        }).set(0);

        this.scale(0,1);
        
        $(zoomInEl).addEvent('mousedown', this.startZoomIn.bind(this));
        $(zoomInEl).addEvent('mouseup', this.stopZooming.bind(this));
        $(zoomInEl).addEvent('mouseout', this.stopZooming.bind(this));
        
        $(zoomOutEl).addEvent('mousedown', this.startZoomOut.bind(this));
        $(zoomOutEl).addEvent('mouseup', this.stopZooming.bind(this));
        $(zoomOutEl).addEvent('mouseout', this.stopZooming.bind(this));
    },

    scale: function (v,f) {
        if(v > 0) v = (v/this.trackEl.offsetWidth);
        var centerX  = (this.containerDim.size.x*(1-this.imageZoom)/2-this.imageX)/this.imageZoom;
        var centerY  = (this.containerDim.size.y*(1-this.imageZoom)/2-this.imageY)/this.imageZoom;
        var overSize = (this.imageDim.size.x > this.containerDim.size.x && this.imageDim.size.y > this.containerDim.size.y);
        
        this.imageZoom = this.floorZoom+(v*(this.ceilingZoom-this.floorZoom));
        
        if (overSize) {
            if (this.imageDim.size.x > this.containerDim.size.x) {
                this.imageEl.style.width = (this.imageZoom*this.containerDim.size.x)+'px';
            }
            this.imageEl.style.height = (this.imageEl.offsetWidth/this.imageDim.ratio)+'px';
        } else {
            //this.trackEl.style.display = 'none';
        }
        
        this.contentX = this.containerDim.size.x*(1-this.imageZoom);
        this.contentY = this.containerDim.size.y*(1-this.imageZoom);

        if(this.imageEl.offsetHeight > this.containerDim.size.y){
            this.contentY = this.containerDim.size.y - this.imageEl.offsetHeight;
        }
        var that = this;
        var newDraggable = new Drag.Move(this.imageEl,{
            limit:{x:[this.contentX,0],y:[this.contentY,0]},
            onComplete: function(){
                that.contain(that.imageEl.offsetLeft, that.imageEl.offsetTop, that.draggable);
            }
        });
        this.draggable = newDraggable;

        this.imageX = this.containerDim.size.x*(1-this.imageZoom)/2-centerX*this.imageZoom;
        this.imageY = this.containerDim.size.y*(1-this.imageZoom)/2-centerY*this.imageZoom;

        this.contain(this.imageX, this.imageY, this.draggable);

        return true;
    },

    startZoomIn: function()
    {
        if (!this.sliderDisabled) {
            this.zoomBtnPressed = true;
            this.sliderAccel = .02;
            this.periodicalZoom();
            this.zoomer = new PeriodicalExecuter(this.periodicalZoom.bind(this), .01);
        }
        return this;
    },

    startZoomOut: function()
    {
        if (!this.sliderDisabled) {
            this.zoomBtnPressed = true;
            this.sliderAccel = -.02;
            this.periodicalZoom();
            this.zoomer = new PeriodicalExecuter(this.periodicalZoom.bind(this), .01);
        }
        return this;
    },

    stopZooming: function()
    {
        if (!this.zoomer || this.sliderSpeed==0) {
            return;
        }
        this.zoomBtnPressed = false;
        this.sliderAccel = 0;
    },
    
    periodicalZoom: function()
    {
        if (!this.zoomer) {
            return this;
        }

        if (this.zoomBtnPressed) {
            this.sliderSpeed += this.sliderAccel;
        } else {
            this.sliderSpeed /= 1.5;
            if (Math.abs(this.sliderSpeed)<.001) {
                this.sliderSpeed = 0;
                this.zoomer.stop();
                this.zoomer = null;
            }
        }
        
        this.sliderValue += (this.sliderSpeed*this.trackEl.offsetWidth);
        if(this.sliderValue < 0){
            this.sliderValue = 0;
        }
        if(this.sliderValue > this.trackEl.offsetWidth){
            this.sliderValue = this.trackEl.offsetWidth;
        }
        this.slider.set(this.sliderValue);
        return this;
    },
    
    contain: function (x,y,draggable) {
        var dragWidth = draggable.element.offsetWidth;
        var dragHeight = draggable.element.offsetHeight;
        var dim = {'size':{'x':dragWidth,'y':dragHeight}};
        //var dim = draggable.element.getSize();

        var xMin = 0, xMax = this.containerDim.size.x-dim.size.x;
        var yMin = 0, yMax = this.containerDim.size.y-dim.size.y;

        x = x>xMin ? xMin : x;
        x = x<xMax ? xMax : x;
        y = y>yMin ? yMin : y;
        y = y<yMax ? yMax : y;

        if (this.containerDim.size.x > dim.size.x) {
            x = (this.containerDim.size.x/2) - (dim.size.x/2);
        }

        if (this.containerDim.size.y > dim.size.y) {
            y = (this.containerDim.size.y/2) - (dim.size.y/2);
        }

        this.imageX = x;
        this.imageY = y;

        this.imageEl.style.left = this.imageX+'px';
        this.imageEl.style.top = this.imageY+'px';
        
        return [x,y];
    }
});

var PeriodicalExecuter = new Class({
    Implements: [Options],
    Implements: [Events],
    // Initialize
    initialize: function(callback, frequency) {
    this.callback = callback;
    this.frequency = frequency;
    this.currentlyExecuting = false;

    this.registerCallback();
  },

  registerCallback: function() {
    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  },

  execute: function() {
    this.callback(this);
  },

  stop: function() {
    if (!this.timer) return;
    clearInterval(this.timer);
    this.timer = null;
  },

  onTimerEvent: function() {
    if (!this.currentlyExecuting) {
      try {
        this.currentlyExecuting = true;
        this.execute();
      } finally {
        this.currentlyExecuting = false;
      }
    }
  }
});
