var EventDispatcher = Class.create();
Object.extend(EventDispatcher.prototype,
{
  buildListenerChain : function()
  {
    if(!this.listenerChain) this.listenerChain = {};              
  },
  addEventListener : function(type, listener)
  {
    this.buildListenerChain();
    if(!this.listenerChain[type])         
      this.listenerChain[type] = [listener];
    else
      this.listenerChain[type].push(listener);
  },
  hasEventListener : function(type)
  {
    return (typeof this.listenerChain[type] != "undefined");
  },
  removeEventListener : function(type, listener)
  {
    if(!this.hasEventListener(type)) return false;
    for(var i = 0; i < this.listenerChain[type].length; i++)
      if(this.listenerChain[type][i] == listener) this.listenerChain[type].splice(i, 1);
  },
  dispatchEvent : function(type, args)
  {
    this.buildListenerChain();
    if(!this.hasEventListener(type)) return false;
    this.listenerChain[type].any(function(f){ return (f(args) == false ? true : false); });           
  },
  on : function(type, listener)
  {
    this.addEventListener(type, listener);
  },
  fire : function(type, args)
  {
    this.dispatchEvent(type, args);
  }
});
        
var FishEyeToolBar = Class.create();
Object.extend(Object.extend(FishEyeToolBar.prototype, EventDispatcher.prototype),
{
  initialize : function(ele, options)
  {
    this.elementArr = [];
    this.options =  {selector : "img",
             createSub : this.createSub.bind(this),
             subOptions : {}};
    Object.extend(this.options, options);
    this.createListener();
    this.buildInterface(ele);
    this.attachListener();
    this.resetTimer = false;        
  },
  createSub : function(ele)
  {
    return new FishEyeItem(ele, this.options.subOptions);
  },
  buildInterface : function(ele)
  {
    this.container = $(ele);
    this.container.getElementsBySelector(this.options.selector).collect(this.buildItem.bind(this));
  },
  buildItem : function(ele)
  {
    var obj = this.options.createSub(ele, this.options.subOptions);
    obj.addEventListener("click", this.itemClickHandle);
    this.elementArr.push(obj);
    this.dispatchEvent("itemBuild", obj);                       
  },
  createListener : function(e)
  {
    this.mouseMoveHandle = this.handleMouseMove.bindAsEventListener(this);
    this.mouseOutHandle = this.handleMouseOut.bindAsEventListener(this);
    this.cancelTimerHandle = this.cancelTimer.bindAsEventListener(this);
  },
  attachListener : function()
  {
    Event.observe(this.container, "mousemove", this.mouseMoveHandle);
    Event.observe(this.container, "mouseout", this.mouseOutHandle);
    Event.observe(this.container, "mouseover", this.cancelTimerHandle);
  },
  cancelTimer : function()
  {
    clearTimeout(this.resetTimer);
  },
  handleMouseMove : function(e)
  {
    this.cancelTimer();
    this.elementArr.invoke("handleFishEye", { x : Event.pointerX(e), y : Event.pointerY(e) });
  },
  handleMouseOut : function(e)
  {
    this.resetTimer = setTimeout(this.resetElements.bind(this), 100);           
  },
  resetElements : function()
  {
    this.elementArr.invoke('resetElement');
  },
  elements : function()
  {
    return this.elementArr;
  } 
});
  
  
var FishEyeItem = Class.create();
Object.extend(Object.extend(FishEyeItem.prototype, EventDispatcher.prototype),
{ 
  initialize : function(ele, options)
  {
    this.options = {
              scaleFactor : 1,
              scaleThrottle : 150
            }
    Object.extend(this.options, options || {});
    this.buildInterface(ele);
    this.createListener();
    this.setOriginalProperties();
    this.lastScale = 1;
  },
  setOriginalProperties : function()
  {
    this.originalHeight = 48;
    this.originalWidth = 48; 
    this.originalMarginTop = parseInt(this.ele.getStyle("margin-top").replace(/[^0-9]/gi, ""));
  },
  buildInterface : function(ele)
  {
    this.ele = $(ele);
  },
  createListener : function(e)
  {
    this.fishEyeHandle = this.handleFishEye.bind(this);
    this.cancelMouseOutHandle = this.handleMouseOut.bindAsEventListener(this);
  },
  handleMouseOut : function(e)
  {
    Event.stop(e);
    return false;
  },
        
  handleFishEye : function(p)
  {
    var offset = this.getCenterAxis();
    var distance = Math.abs(p.x - offset);
    if(distance > this.options.scaleThrottle)
    {
      this.resetElement();
      return true;
    }
    var scale = Math.abs(this.options.scaleThrottle - distance) / this.options.scaleThrottle + 1;
    this.lastScale = scale;
    this.scaleElement(scale);
  },
  scaleElement : function(scale)
  {
    this.ele.setStyle({ zIndex : scale * 100, marginTop : this.originalMarginTop - ((scale * this.originalHeight) - this.originalHeight) + "px",   height : (scale * this.originalHeight) + "px", width : (scale * this.originalWidth) + "px"});                          
  },
  resetElement : function()
  {
    this.ele.setStyle({ zIndex : 1, marginTop :  this.originalMarginTop + "px",  height : this.originalHeight  + "px", width : this.originalWidth + "px" });
  },
  getCenterAxis : function()
  {
    return Math.floor(Position.cumulativeOffset(this.ele).first() + (((this.originalWidth/2)) * this.lastScale));
  }   
});

var FishEyeItemDown = Class.create();
Object.extend(Object.extend(FishEyeItemDown.prototype, FishEyeItem.prototype),
{
  setOriginalProperties : function()
  {
    this.originalHeight = parseInt(this.ele.getStyle("height").replace(/[^0-9]/gi, ""));
    this.originalWidth = parseInt(this.ele.getStyle("width").replace(/[^0-9]/gi, "")); 
    this.originalMarginBottom = parseInt(this.ele.getStyle("margin-bottom").replace(/[^0-9]/gi, ""));
  },
  scaleElement : function(scale)
  {
    this.ele.setStyle({ zIndex : scale * 100, marginBottom : this.originalMarginBottom - ((scale * this.originalHeight) - this.originalHeight) + "px",   height : (scale * this.originalHeight) + "px", width : (scale * this.originalWidth) + "px"});                          
  },
  resetElement : function()
  {
    this.ele.setStyle({ zIndex : 1, marginBottom :  this.originalMarginBottom + "px",  height : this.originalHeight  + "px", width : this.originalWidth + "px" });
  }
});

