
Rico.animate = function(effect){
	new Rico.Effect.Animator().play(effect, arguments[1]);
}

Rico.Effect = {}
Rico.Effect.easeIn = function(step){
  return Math.sqrt(step)
}
Rico.Effect.easeOut = function(step){
  return step*step
}
Rico.Stepping = {}
Rico.Stepping.easeIn = Rico.Effect.easeIn;
Rico.Stepping.easeOut = Rico.Effect.easeOut;

Rico.Effect.Animator = Class.create();
Rico.Effect.Animator.prototype = {
	initialize : function(effect) {
		this.animateMethod = this.animate.bind(this);
		this.options = arguments[1] || {};
		if (!effect) return;
		this.reset(effect, arguments[1]);
	},
	reset: function(effect){
		this.effect = effect;
		this.options = Object.extend({
			steps: 10,
			duration: 200,
			rate: function(steps){ return steps;}
    }, arguments[1] || {});
		this.stepsLeft = this.options.steps;
		this.duration = this.options.duration;
	},
	play: function(effect) {
		this.reset(effect, arguments[1]);
		this.animate();
	},
	stop: function() {
	  this.interupt = true;
		this.stepsLeft = this.options.steps;
	},
	pause: function() {
		this.interupt = true;
	},
	animate: function() {
		if (this.stepsLeft <=0) {
			if (this.effect.finish)  this.effect.finish();
			if (this.options.onFinish) this.options.onFinish();
			return;
		}
		if (this.timer)
			clearTimeout(this.timer);
		this.effect.step(this.options.rate(this.stepsLeft));
		this.startNextStep();
  },
	startNextStep: function() {
		var stepDuration = Math.round(this.duration/this.stepsLeft) ;
    this.duration -= stepDuration;
    this.stepsLeft--;
    this.timer = setTimeout(this.animateMethod, stepDuration);
	}
}


Rico.Effect.SizeAndPosition = Class.create();
Rico.Effect.SizeAndPosition.prototype = {
   initialize: function(element, x, y, w, h) {
      this.element = $(element);
      this.x = x || this.element.offsetLeft;
      this.y = y || this.element.offsetTop;
      this.w = w || this.element.offsetWidth;
      this.h = h || this.element.offsetHeight;
   },
   step: function(stepsToGo) {  
			var left = this.element.offsetLeft + ((this.x - this.element.offsetLeft)/stepsToGo) + "px"
			var top = this.element.offsetTop + ((this.y - this.element.offsetTop)/stepsToGo) + "px"
			var width = this.element.offsetWidth + ((this.w - this.element.offsetWidth)/stepsToGo) + "px"
			var height = this.element.offsetHeight + ((this.h - this.element.offsetHeight)/stepsToGo) + "px"
      var style = this.element.style;
			style.left = left;
			style.top = top;
			style.width = width;
			style.height = height;
   }
}

Rico.AccordionEffect = Class.create();
Rico.AccordionEffect.prototype = {
    initialize: function(toClose, toOpen, height) {
      this.toClose        = toClose;
      this.toOpen        = toOpen;
      if (!navigator.appVersion.match(/\bMSIE\b/)) {
        Element.makeClipping(toOpen);
        Element.makeClipping(toClose);
      }
			Rico.Controls.disableNativeControls(toOpen);
			Rico.Controls.disableNativeControls(toClose);
      Element.show(toOpen);
      this.toOpen.style.height = "0px";
      this.endHeight = height;
    },
    step: function(framesLeft) {
       var delta = parseInt((parseInt(this.toClose.offsetHeight))/framesLeft);
       var h1 = (this.toClose.offsetHeight - delta) + "px";
       var h2 = (this.toOpen.offsetHeight + delta) + "px";
       this.toClose.style.height = h1;
       this.toOpen.style.height = h2
    },
    finish: function(){
      this.toClose.style.height="0px";
      Element.hide(this.toClose)
      if (!navigator.appVersion.match(/\bMSIE\b/)) {
        Element.undoClipping(this.toOpen);
        Element.hide(this.toClose)
      }

			Rico.Controls.enableNativeControls(this.toOpen);
      //this.toOpen.style.height = this.endHeight;
			Rico.Controls.enableNativeControls(this.toClose);
    }
};

Rico.Effect.SizeFromBottom = Class.create()
Rico.Effect.SizeFromBottom.prototype = {
  initialize: function(element, y, h) {
     this.element = $(element);
     this.y = y || this.element.offsetTop;
     this.h = h || this.element.offsetHeight;
     this.options  = arguments[3] || {};
  },
  step: function(framesToGo) {  
		var top = this.element.offsetTop + ((this.y - this.element.offsetTop)/framesToGo) + "px"
		var height = this.element.offsetHeight + ((this.h - this.element.offsetHeight)/framesToGo) + "px"
    var style = this.element.style;
		style.height = height;     
		style.top = top;
  }
}

Rico.Effect.SizeFromTop = Class.create()
Rico.Effect.SizeFromTop.prototype = {
  initialize: function(element, scrollElement, y, h) {
     this.element = $(element);
     this.h = h || this.element.offsetHeight;
	//	 element.style.top = y;
     this.scrollElement = scrollElement;
     this.options  = arguments[4] || {};
     if (this.options.baseHeight)
       this.baseHeight = this.options.baseHeight;
     else
       this.baseHeight = Math.max(this.h, this.element.offsetHeight);
  },
  step: function(framesToGo) {  
    var rawHeight = this.element.offsetHeight + ((this.h - this.element.offsetHeight)/framesToGo);
		var height = rawHeight + "px"
		var scroll = (rawHeight - this.baseHeight) + "px";
		this.scrollElement.style.top = scroll;
		this.element.style.height = height;     
  }
}


Rico.Effect.SizeWidth = Class.create();
Rico.Effect.SizeWidth.prototype = {
    initialize: function(element, endWidth) {
      this.element = element
			this.endWidth = endWidth
    },
    step: function(stepsLeft) {
       delta = Math.abs(this.endWidth - parseInt(this.element.offsetWidth))/(stepsLeft);
       this.element.style.width = (this.element.offsetWidth - delta) + "px";
    }
}

//these are to support non Safari browsers and keep controls from bleeding through on absolute positioned element.
Rico.Controls = {
	editors: [],
	disableNativeControls: function(element) {
		Rico.Controls.defaultDisabler.disableNative(element);
  },
	enableNativeControls: function(element){
		Rico.Controls.defaultDisabler.enableNative(element);
	}
}

Rico.Controls.Disabler = Class.create();
Rico.Controls.Disabler.prototype = {
	initialize: function(){
		this.options = Object.extend({
			includeTags: ['ul','ol','dl','table','div','tbody','dd'],
			excludeSet: [],
			hidables: Rico.Controls.editors
    }, arguments[0] || {});
	},
  disableNative: function(element) {
    if (!(/Konqueror|Safari|KHTML/.test(navigator.userAgent))){
			if (!navigator.appVersion.match(/\bMSIE\b/))
				this.blockControls(element).each(function(e){Element.makeClipping(e)});
			else
			  this.hidableControls(element).each(function(e){e.disable()});
    }
  },
  enableNative: function(element){
    if (!(/Konqueror|Safari|KHTML/.test(navigator.userAgent))){
			if (!navigator.appVersion.match(/\bMSIE\b/))
				this.blockControls(element).each(function(e){Element.undoClipping(e)});
			else
			  this.hidableControls(element).each(function(e){e.enable()});
    }
  },
	blockControls: function(element){
		var includes = [];
		if (this.options.includeSet)
			includes = this.options.includeSet;
		else if (element)
			includes = this.options.includeTags.map(function(t){return new Selector(t).findElements(element)}).flatten();
		else
			includes = this.options.includeTags.map(function(t){return $$(t)}).flatten();
			
		return includes.select(function(e){return (Element.getStyle(e, 'display') != 'none') && !this.options.excludeSet.include(e)}.bind(this));
	},
	hidableControls: function(element){
		if (element)
			return this.options.hidables.select(function(e){return Element.childOf(e, element)});
		else
			return this.options.hidables;
	}
}	

Rico.Controls.defaultDisabler = new Rico.Controls.Disabler();
Rico.Controls.blankDisabler = new Rico.Controls.Disabler({includeTags:[],hidables:[]});
	
Rico.Controls.HidableInput = Class.create(); 
Rico.Controls.HidableInput.prototype = {
	initialize: function(field, view){	
		this.field = field;
		this.view = view;
		this.enable();
		Rico.Controls.editors.push(this);
	},
	enable: function(){
		Element.hide(this.view);
		Element.show(this.field);
	},
	disable: function(){
		this.view.innerHTML = $F(this.field);
		Element.hide(this.field);
		Element.show(this.view);
	}
}