Motionobjects = new Array();

function Motion(obj, property, unit, round, startValue, endValue, duration, startDelay, computation) {
	this.obj = obj;
	this.property = property;
	this.startValue = startValue;
	this.endValue = endValue;
	this.duration = duration;
	this.unit = (typeof unit == 'string') ? unit : '';
	this.round = round;
	this.lastValue = this.startValue;
	this.timeouts = new Array();
	this.startDelay = (typeof startDelay != 'undefined') ? startDelay : 0;
	this.rgbMode = false;
	
	var date = new Date();
	var firstrelease = date.getTime() + this.startDelay;
	

	
	for (var i = 0; i < Motionobjects.length; i++) {
		if (Motionobjects[i] != null && Motionobjects[i].obj == this.obj && Motionobjects[i].property == this.property) {
			var wasfinished = true;
			for (var j = 0; j < Motionobjects[i].timeouts.length; j++) {
				if (firstrelease <= Motionobjects[i].timeouts[j].release) {
					window.clearTimeout(Motionobjects[i].timeouts[j].id);
					if (firstrelease != Motionobjects[i].timeouts[j].release)
						wasfinished = false;
				} else
					this.timeouts.push(Motionobjects[i].timeouts[j]);
			}
			if (!wasfinished || this.startValue == null)
				this.startValue = Motionobjects[i].lastValue;
			
			Motionobjects[i] = this;
			this.id = i;
			break;
		}
	} 
	
	if (this.startValue == null)
		this.startValue = this.endValue;
	
	if (typeof this.id == 'undefined')
		this.id = Motionobjects.push(this) - 1;
		
	



	this.computation = (typeof computation != 'undefined') ? computation : 'ramp';
	if (this.property == 'opacity') this.computation = 'linear';

	if (typeof this.endValue == 'string' && this.endValue.substr(0,1) == '#') {
		this.rgbMode = true;
	} else if (typeof startValue == 'string') {
		// non-computable
		var timeout;
		var date = new Date();
		var now = date.getTime();
		
		timeout = window.setTimeout('Motionobjects['+this.id+'].set(\''+startValue+'\');', this.startDelay);
		this.timeouts.push( {'id': timeout, 'release' : now + this.startDelay} );
		
		timeout = window.setTimeout('Motionobjects['+this.id+'].set(\''+endValue+'\');', this.startDelay + this.duration);
		this.timeouts.push( {'id': timeout, 'release' : now + this.startDelay + this.duration} );
		
	//	this.obj.style[this.property] = startValue;
	//	window.setTimeout('Motionobjects['+this.id+'].obj.style[\''+this.property+'\'] = \''+endValue+'\';', this.duration + this.startDelay);
		return;
	}
	
	
	
	
	switch (this.computation) {
		case 'cubic':
			this.a = -(2*(-this.startValue+this.endValue))/Math.pow(this.duration,3);
			this.b = (3*(-this.startValue+this.endValue))/Math.pow(this.duration,2);
			this.d = this.startValue;
			break;
		case 'linear':
			this.a = (this.endValue-this.startValue)/this.duration;
			this.b = this.startValue;
			break;
		case 'ramp':
			this.s_ges = this.endValue - this.startValue;
			this.durationSquare = Math.pow(this.duration,2);
			break;
	}
	
	this.start();
}


Motion.prototype.value = function (t) {
	switch (this.computation) {
		case 'cubic':
			return (t < this.duration) ? this.a*Math.pow(t,3) + this.b*Math.pow(t,2) + this.d : this.endValue;
		case 'linear':
			return (t < this.duration) ? this.a*t + this.b : this.endValue;
		case 'ramp':
			if (t < (1/3)*this.duration)
				return (9/4)*this.s_ges*Math.pow(t,2)/this.durationSquare + this.startValue;
			else if (t <= (2/3)*this.duration) {
				return (3/2)*this.s_ges*t/this.duration-(1/4)*this.s_ges + this.startValue;
			} else if (t < this.duration)
				return -(9/4)*this.s_ges*Math.pow(t,2)/this.durationSquare+(9/2)*this.s_ges*t/this.duration-(5/4)*this.s_ges + this.startValue;
			else
				return this.endValue;
	}
}

Motion.prototype.start = function (t) {
	var date = new Date();
	var now = date.getTime();
	var value;
	var lastvalue = null;

	var t_step = 30;

	var t = 0;
	while (t <= this.duration) {

		if (this.rgbMode) {
			var percent = this.duration > 0 ? (t / this.duration) * 100 : 100;
			value = "'"+Motion.gradient(this.startValue, this.endValue, percent)+"'";
		} else
			value = (this.round) ? Math.round(this.value(t)) : this.value(t);
		
		if (lastvalue == null || value != lastvalue) {
			var wait = t + this.startDelay;
			var cmd = 'Motionobjects['+this.id+'].set('+value+');';
			if (wait > 0) {
				var timeout = window.setTimeout(cmd, wait);
				this.timeouts.push( {'id': timeout, 'release' : now + wait} );
			} else
				eval(cmd);
		}
		
		if (t < this.duration && t+t_step > this.duration)
			t = this.duration;
		else
			t += t_step;
			
		
		lastvalue = value;
	}
}

Motion.prototype.set = function (value) {
	this.lastValue = value;
	
	// MSIE WORKAROUND
	if (this.property == 'opacity' && this.obj.filters) {
		value = Math.round(100*value);
		if (this.obj.filters.length == 0)
			this.obj.style.filter = 'alpha(opacity='+value+')';
		else
			this.obj.filters[0].opacity = value;
	}
	
	this.obj.style[this.property] = value+this.unit;
}

Motion.gradient = function (from, to, percent) {
	var out = new Object();
	var fromRGB = Motion.hex2rgb(from);
	var toRGB = Motion.hex2rgb(to);
	for (var c in fromRGB) {
		out[c] = fromRGB[c] + Math.round((percent / 100) * (toRGB[c] - fromRGB[c])); 
	}
	return Motion.rgb2hex(out);
}

Motion.hex2rgb = function (hex) {
	return {	r: parseInt(hex.substr(1,2), 16),
				g: parseInt(hex.substr(3,2), 16),
				b: parseInt(hex.substr(5,2), 16)	};
}

Motion.hex = '0123456789abcdef';
Motion.rgb2hex = function (rgb) {
	var dec,tmp;
	var out = '#';
	for (var c in rgb) {
		dec = rgb[c];
		tmp = '';
		for(var i = 0; i < 2; i++) {
			tmp = Motion.hex.charAt(dec%16) + tmp;
			dec = dec >> 4;
		}
		out += tmp;
	}
	return out;
}
