
Effect.Bounce = Class.create();
Object.extend(Object.extend(Effect.Bounce.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    var options = Object.extend({
        x:0, y:200,
        acceleration: 9.81,
        transition: Effect.Transitions.linear,
        mode: 'relative'
    }, arguments[1] || {});
    this.start(options);
  },
  setup: function() {
    Element.makePositioned(this.element);
    this.originalLeft = parseFloat(Element.getStyle(this.element,'left') || '0');
    this.originalTop  = parseFloat(Element.getStyle(this.element,'top')  || '0');
    if(this.options.mode == 'absolute') {
      this.options.x = this.options.x - this.originalLeft;
    }
  },
  mytransition: function(pos){
    var temp = (pos < 0.5 ? 0.5-pos : 0.5+(1-pos)); 
    return (pos < 0.5 ? this.options.acceleration/2*temp*temp : this.options.acceleration/2*(1-temp)*(1-temp) ) *8 /this.options.acceleration - 1;
  },
  update: function(position) {
    Element.setStyle(this.element, {
        left: this.options.x  * position + this.originalLeft + 'px',
    top:  this.originalTop + this.options.y * this.mytransition(position)   + 'px'
    });
  }
});

Effect.BlindUpFromRight = function(element) {
  element = $(element);
  element.makeClipping();
  return new Effect.Scale(element, 0,
    Object.extend({ scaleContent: false,
      scaleY: false,
      restoreAfterFinish: true,
      afterFinishInternal: function(effect) {
        effect.element.hide();
        effect.element.undoClipping();
      }
    }, arguments[1] || {})
  );
};

Effect.BlindDownFromRight = function(element) {
  element = $(element);
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, Object.extend({
    scaleContent: false,
    scaleY: false,
    scaleFrom: 0,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makeClipping();
      effect.element.setStyle({width: '0px'});
      effect.element.show();
    },
    afterFinishInternal: function(effect) {
      effect.element.undoClipping();
    }
  }, arguments[1] || {}));
};

//Modification to apply @Effect->PAIRS, add the follow element, to enable Effect.toggle support:
//'blindr': ['BlindDownFromRight','BlindUpFromRight']


Effect.Unfold = function(element) {
  element = $(element);
  var elementDimensions = element.getDimensions();
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height };
  element.makeClipping();
  return new Effect.Scale(element, 100, Object.extend({   
    scaleContent: false,
    scaleY: false,
    scaleFrom: 1,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    afterSetup: function(effect) {
      effect.element.makeClipping().setStyle({width: '1px', height: '135px'}).show();
    },
    afterFinishInternal: function(effect) {
    new Effect.Scale(element, 100, {
      scaleContent: false, 
      scaleX: false,
      scaleFrom: 5,
      scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
          restoreAfterFinish: true,
        afterSetup: function(effect) {
            effect.element.makeClipping().setStyle({height: '1px'}).show(); 
          },  
          afterFinishInternal: function(effect) {
            effect.element.undoClipping().setStyle(oldStyle);
          }
    });
  }}, arguments[1] || {}));
};


Effect.DropIn = function(element) {
  element = $(element);
  var oldTop = element.style.top;
  var oldLeft = element.style.left;
  var pos = Position.cumulativeOffset(element);
   return new Effect.Parallel(
    [ new Effect.MoveBy(element, 100, 0, { sync: true }), 
      new Effect.Opacity(element, { sync: true, from:0.0, to: 1.0 }) ],
    Object.extend(
      { duration: 0.5,
        beforeSetup: function(effect) { 
          Element.makePositioned(effect.effects[0].element); 
          Element.setOpacity(element, 0);
          element.style.position = 'absolute'; 
          element.style.top = (pos[1]-100) + 'px'; 
        }
      }, arguments[1] || {}));
};

Effect.ShakeVertical = function(element) {
  element = $(element);
  var oldStyle = {
    top: Element.getStyle(element, 'top'),
    left: Element.getStyle(element, 'left') };
      return new Effect.Move(element, 
        { x:  0, y: 20, duration: 0.05, afterFinishInternal: function(effect) {
      new Effect.Move(effect.element,
        { x: 0, y: -40, duration: 0.1,  afterFinishInternal: function(effect) {
      new Effect.Move(effect.element,
        { x:  0, y: 40, duration: 0.1,  afterFinishInternal: function(effect) {
      new Effect.Move(effect.element,
        { x: 0, y: -40, duration: 0.1,  afterFinishInternal: function(effect) {
      new Effect.Move(effect.element,
        { x:  0, y: 40, duration: 0.1,  afterFinishInternal: function(effect) {
      new Effect.Move(effect.element,
        { x: 0, y: -20, duration: 0.05, afterFinishInternal: function(effect) { with(Element) {
        undoPositioned(effect.element);
        setStyle(effect.element, oldStyle);
  }}}) }}) }}) }}) }}) }});
};

Effect.PopOut = function(element) {
  element = $(element);
  var oldTop = element.style.top;
  var oldLeft = element.style.left;
  var pos = Position.cumulativeOffset(element);
   return new Effect.Parallel(
    [ new Effect.MoveBy(element, -100, 0, { sync: true }), 
      new Effect.Opacity(element, { sync: true, from:1, to: 0 }) ],
    Object.extend(
      { duration: 0.5,
        beforeSetup: function(effect) { 
          Element.makePositioned(effect.effects[0].element); 
          Element.setOpacity(element, 1);
          element.style.position = 'absolute'; 
         // element.style.top = (pos[1]-100) + 'px'; 
        }
      }, arguments[1] || {}));
};

Effect.PhaseIn = function(element) {
  element = $(element);
  new Effect.BlindDown(element, arguments[1] || {});
  new Effect.Appear(element, arguments[2] || arguments[1] || {});
}

Effect.PhaseOut = function(element) {
  element = $(element);
  new Effect.Fade(element, arguments[1] || {});
  new Effect.BlindUp(element, arguments[2] || arguments[1] || {});
}

Effect.Phase = function(element) {
  element = $(element);
  if (element.style.display == 'none')
    new Effect.PhaseIn(element, arguments[1] || {}, arguments[2] || arguments[1] || {});
  else new Effect.PhaseOut(element, arguments[1] || {}, arguments[2] || arguments[1] || {});
}

Effect.BlindLeft = function(element) {
  element = $(element);
  element.makeClipping();
  return new Effect.Scale(element, 0,
    Object.extend({ scaleContent: false, 
      scaleY: false,                     
      restoreAfterFinish: true,
      afterFinishInternal: function(effect) {
        effect.element.hide().undoClipping();
      } 
    }, arguments[1] || {})
  );
}

Effect.BlindRight = function(element) {
  element = $(element);
    var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, Object.extend({ 
    scaleContent: false, 
    scaleY: false,
    scaleFrom: 0,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makeClipping().setStyle({width: '0px'}).show(); 
    },  
    afterFinishInternal: function(effect) {
      effect.element.undoClipping();
    }
  }, arguments[1] || {}));
}

Effect.SlideRightIn = function(element) {
  element = $(element);
  Element.cleanWhitespace(element);
  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
  var oldInnerRight = Element.getStyle(element.firstChild, 'right');
  var elementDimensions = Element.getDimensions(element);
  return new Effect.Scale(element, 100, Object.extend({ 
    scaleContent: false, 
    scaleY: false, 
    scaleFrom: 0,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) { with(Element) {
      makePositioned(effect.element);
      makePositioned(effect.element.firstChild);
      if(window.opera) setStyle(effect.element, {top: ''});
      makeClipping(effect.element);
      setStyle(effect.element, {width: '0px'});
      show(element); }},
    afterUpdateInternal: function(effect) { with(Element) {
      setStyle(effect.element.firstChild, {right:
        (effect.dims[0] - effect.element.clientWidth) + 'px' }); }},
    afterFinishInternal: function(effect) { with(Element) {
      undoClipping(effect.element); 
      undoPositioned(effect.element.firstChild);
      undoPositioned(effect.element);
      setStyle(effect.element.firstChild, {right: oldInnerRight}); }}
    }, arguments[1] || {})
  );
}

Effect.SlideLeftOut = function(element) {
  element = $(element);
  Element.cleanWhitespace(element);
  var oldInnerRight = Element.getStyle(element.firstChild, 'right');
  return new Effect.Scale(element, 0, 
   Object.extend({ scaleContent: false, 
    scaleY: false, 
    scaleMode: 'box',
    scaleFrom: 100,
    restoreAfterFinish: true,
    beforeStartInternal: function(effect) { with(Element) {
      makePositioned(effect.element);
      makePositioned(effect.element.firstChild);
      if(window.opera) setStyle(effect.element, {top: ''});
      makeClipping(effect.element);
      show(element); }},  
    afterUpdateInternal: function(effect) { with(Element) {
      setStyle(effect.element.firstChild, {right:
        (effect.dims[0] - effect.element.clientWidth) + 'px' }); }},
    afterFinishInternal: function(effect) { with(Element) {
            undoPositioned(effect.element.firstChild);
        undoPositioned(effect.element);
        hide(effect.element); 
        undoClipping(effect.element); 

        setStyle(effect.element.firstChild, {right: oldInnerRight}); }}
   }, arguments[1] || {})
  );
}

Effect.SlideLeftIn = function(element) {
  element = $(element);
  Element.cleanWhitespace(element);
  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
  var oldInnerRight = Element.getStyle(element.firstChild, 'left');
  var elementDimensions = Element.getDimensions(element);
  return new Effect.Scale(element, 100, Object.extend({ 
    scaleContent: false, 
    scaleY: false, 
    scaleFrom: 0,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) { with(Element) {
      makePositioned(effect.element);
      makePositioned(effect.element.firstChild);
      if(window.opera) setStyle(effect.element, {top: ''});
      makeClipping(effect.element);
      setStyle(effect.element, {width: '0px'});

      show(element); }},
    afterUpdateInternal: function(effect) { with(Element) {
          setStyle(effect.element, {left:
        (effect.dims[0] - effect.element.clientWidth) + 'px' });
        
      //setStyle(effect.element.firstChild, {left:
       // (effect.dims[0] - effect.element.clientWidth) + 'px' });
        }},
    afterFinishInternal: function(effect) { with(Element) {
      undoClipping(effect.element); 
      undoPositioned(effect.element.firstChild);
      undoPositioned(effect.element);
      setStyle(effect.element.firstChild, {left: oldInnerRight}); }}
    }, arguments[1] || {})
  );
}

Effect.SlideRightOut = function(element) {
  element = $(element);
  Element.cleanWhitespace(element);
    var elementDimensions = element.getDimensions();

  var oldInnerRight = Element.getStyle(element.firstChild, 'right');
  return new Effect.Scale(element, 0, 
   Object.extend({ scaleContent: false, 
    scaleY: false, 
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},

    scaleFrom: 100,
    restoreAfterFinish: true,
    beforeStartInternal: function(effect) { with(Element) {
      makePositioned(effect.element);
      makePositioned(effect.element.firstChild);
      if(window.opera) setStyle(effect.element, {top: ''});
      makeClipping(effect.element);
      show(element); }},  
    afterUpdateInternal: function(effect) { with(Element) {
          setStyle(effect.element, {right:
        (-effect.dims[0] + effect.element.clientWidth) + 'px' });
        
      //setStyle(effect.element.firstChild, {left:
      //  (effect.dims[0] - effect.element.clientWidth) + 'px' });
        }},
    afterFinishInternal: function(effect) { with(Element) {
            undoPositioned(effect.element.firstChild);
        undoPositioned(effect.element);
        hide(effect.element); 
        undoClipping(effect.element); 

        setStyle(effect.element.firstChild, {right: oldInnerRight}); }}
   }, arguments[1] || {})
  );
}

Effect.SlideUpIn = function(element) {
  element = $(element).cleanWhitespace();
  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
  var oldInnerBottom = element.down().getStyle('top');

  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, Object.extend({ 
    scaleContent: false, 
    scaleX: false, 
    scaleFrom: window.opera ? 0 : 1,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makePositioned();
      effect.element.down().makePositioned();
     if (window.opera) effect.element.setStyle({height: ''});
     effect.element.makeClipping().setStyle({height: '0px'}).show(); 
    },
    afterUpdateInternal: function(effect) {
      effect.element.setStyle({top:
        (effect.dims[0] - effect.element.clientHeight) + 'px'});
        
    },
    afterFinishInternal: function(effect) {
      effect.element.undoClipping().undoPositioned();
      effect.element.down().undoPositioned().setStyle({top: oldInnerBottom}); }
    }, arguments[1] || { })
  );
};

Effect.SlideDownOut = function(element) {
  element = $(element).cleanWhitespace();
  var oldInnerBottom = element.down().getStyle('top');
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, window.opera ? 0 : 1,
   Object.extend({ scaleContent: false, 
    scaleX: false, 
    scaleMode: 'box',
    scaleFrom: 100,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makePositioned();
      effect.element.down().makePositioned();
      if (window.opera) effect.element.setStyle({bottom: ''});
      effect.element.makeClipping().show();
    },  
    afterUpdateInternal: function(effect) {
      effect.element.setStyle({top:
        (effect.dims[0] - effect.element.clientHeight) + 'px' });
    },
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping().undoPositioned();
      effect.element.down().undoPositioned().setStyle({top: oldInnerBottom});
    }
   }, arguments[1] || { })
  );
};

Effect.DelayedChain = Class.create();
Object.extend(Effect.DelayedChain.prototype, {
    initialize: function(effect, elements, options, timeout){
        this.elements = elements;
        this.effect = effect;
        this.timeout = timeout || 100;
        this.options = Object.extend({}, options || {});

        this.afterFinish = this.options.afterFinish || Prototype.emptyFunction;
        this.options.afterFinish = Prototype.emptyFunction;
        setTimeout(this.action.bind(this),1);
    },
    action: function() {
        if(this.elements.length){ 
            new Effect[this.effect](this.elements.shift(), this.options);
            setTimeout(this.action.bind(this), this.timeout);
        } else {
            if(this.afterFinish) this.afterFinish();
        }
    }
});

Effect.Chain = Class.create();
Object.extend(Effect.Chain.prototype, {
    initialize: function(effect, elements, options){
        this.elements = elements || [];
        this.effect = effect;
        this.options = options || {};
        this.afterFinish = this.options.afterFinish || Prototype.emptyFunction;
        this.options.afterFinish = this.nextEffect.bind(this);
        setTimeout(this.nextEffect.bind(this), 1);
    },
    nextEffect: function(){
        if(this.elements.length)
            new Effect[this.effect](this.elements.shift(), this.options);
        else
            this.afterFinish();
    }
});

Effect.ReSize = Class.create();
Object.extend(Object.extend(Effect.ReSize.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = element;
    if(!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({ amount: 100, direction: 'vert', toSize:null }, arguments[1] || {});
    if( options.direction == 'vert' )
      this.originalSize = options.originalSize || parseInt(this.element.style.height);
    else
      this.originalSize = options.originalSize || parseInt(this.element.style.width);

    if( options.toSize != null )
      options.amount = options.toSize - this.originalSize;

    this.start(options);
  },
  setup: function() {
    // Prevent executing on elements not in the layout flow
    if(this.element.getStyle('display')=='none') { this.cancel(); return; }
  },
  update: function(position) {
    if( this.options.direction  == 'vert' ){
      this.element.setStyle({height: this.originalSize+(this.options.amount*position)+'px'});
    } else {
      this.element.setStyle({width: this.originalSize+(this.options.amount*position)+'px'});
    }
  },
  finish: function(){
    if( this.options.direction == 'vert' ){
      this.element.setStyle({height: this.originalSize+this.options.amount+'px'});
    } else {
      this.element.setStyle({width: this.originalSize+this.options.amount+'px'});
    }
  }
});
