function Transform() {}

Transform.timeouts = {};

Transform.animateTransform = function(params) {
	/*
	params = {
		obj_id: "img_obj_id",
		position: {start: {left: 0, top: 0}, end: {left: 500, top: 500}}, null,
		size: {start: {width: 100, height: 100}, end: {width: 250, height: 250}}, null,
		opacity: {start: 0.5, end: 1.0}, null,
		ease_timing: {start_secs: 1, end_secs: 1}, null,
		anim_secs: 5,
		fps: 30,
		exec_on_finish: null	
	};
	*/
	
	params.position = (params.position)? params.position: null;
	params.size = (params.size)? params.size: null;
	params.opacity = (params.opacity)? params.opacity: null;
	params.ease_timing = (params.ease_timing)? params.ease_timing: null;
	params.anim_secs = (params.anim_secs)? params.anim_secs: 1;
	params.fps = (params.fps)? params.fps: 30;
	params.exec_on_finish = (params.exec_on_finish)? params.exec_on_finish: null;

	//var animation_time_ms = params.anim_secs * 1000;
	var delay = Math.round(1000 / params.fps); //time between frames in milliseconds
	var total_num_frames = Math.round(params.anim_secs * params.fps);
	var animation_time_ms = total_num_frames * delay;

	var trans_list = [];
	Transform.timeouts[params.obj_id] = [];
	
	var left_val, top_val, width_val, height_val, opacity_val;
	var left, top, position, width, height, size, opacity;
	
//non-linear animation
	if (params.ease_timing) {
		var ease_start_ms = params.ease_timing.start_secs * 1000;
		var ease_end_ms = params.ease_timing.end_secs * 1000;
		
		var cubicHermite = function(t, p0, p1, m0, m1) { // t: in <0..1>, p0: start position, p1: end position, m0: start tangent, m1: end tangent
			var t2 = t * t;
			var t3 = t2 * t;
			return (2 * t3 - 3 * t2 + 1) * p0 + (t3 - 2 * t2 + t) * m0 + (-2 * t3 + 3 * t2) * p1 + (t3 - t2) * m1;
		}
		
		var accelerationValue = function(x_obj, time_obj, time_ms) {
			return cubicHermite(time_ms / time_obj.accel, 0, x_obj.accel, 0, x_obj.cruise / time_obj.cruise * time_obj.accel);
		}
		
		var cruiseValue = function(x_obj, time_obj, time_ms) {
			return x_obj.accel + x_obj.cruise * (time_ms - time_obj.accel) / time_obj.cruise;
		}
		
		var decelerationValue = function(x_obj, time_obj, time_ms) {
			return cubicHermite((time_ms - time_obj.accel - time_obj.cruise) / time_obj.decel, x_obj.accel + x_obj.cruise, x_obj.delta, x_obj.cruise / time_obj.cruise * time_obj.decel, 0);
		}
		
		var time_obj = {};
		time_obj.accel = ease_start_ms;
		time_obj.cruise = animation_time_ms - ease_start_ms - ease_end_ms;
		time_obj.decel = ease_end_ms;
		
		if (params.position) {
			var left_obj = null;
			if (params.position.start.left != null) {
				left_obj = {};
				left_obj.delta = params.position.end.left - params.position.start.left;
				left_obj.velocity = left_obj.delta / (time_obj.accel / 2 + time_obj.cruise + time_obj.decel / 2);
				left_obj.accel = left_obj.velocity * time_obj.accel / 2;
				left_obj.cruise = left_obj.velocity * time_obj.cruise;
			}
			var top_obj = null;
			if (params.position.start.top != null) {
				top_obj = {};
				top_obj.delta = params.position.end.top - params.position.start.top;
				top_obj.velocity = top_obj.delta / (time_obj.accel / 2 + time_obj.cruise + time_obj.decel / 2);
				top_obj.accel = top_obj.velocity * time_obj.accel / 2;
				top_obj.cruise = top_obj.velocity * time_obj.cruise;
			}
		}
		if (params.size) {
			var width_obj = null;
			if (params.size.start.width != null) {
				width_obj = {};
				width_obj.delta = params.size.end.width - params.size.start.width;
				width_obj.velocity = width_obj.delta / (time_obj.accel / 2 + time_obj.cruise + time_obj.decel / 2);
				width_obj.accel = width_obj.velocity * time_obj.accel / 2;
				width_obj.cruise = width_obj.velocity * time_obj.cruise;
			}
			var height_obj = null;
			if (params.size.start.height != null) {
				height_obj = {};
				height_obj.delta = params.size.end.height - params.size.start.height;
				height_obj.velocity = height_obj.delta / (time_obj.accel / 2 + time_obj.cruise + time_obj.decel / 2);
				height_obj.accel = height_obj.velocity * time_obj.accel / 2;
				height_obj.cruise = height_obj.velocity * time_obj.cruise;
			}
		}
		var opacity_obj = null;
		if (params.opacity) {
			opacity_obj = {};
			opacity_obj.delta = params.opacity.end - params.opacity.start;
			opacity_obj.velocity = opacity_obj.delta / (time_obj.accel / 2 + time_obj.cruise + time_obj.decel / 2);
			opacity_obj.accel = opacity_obj.velocity * time_obj.accel / 2;
			opacity_obj.cruise = opacity_obj.velocity * time_obj.cruise;
		}
		
		//for (var time_ms = 0; time_ms <= animation_time_ms; time_ms += delay) {
		for (var i = 1; i <= total_num_frames; i++) {
			var time_ms = delay * i;
			
			if (time_ms <= time_obj.accel) { // Acceleration
				if (params.position) {
					if (left_obj) left_val = accelerationValue(left_obj, time_obj, time_ms);
					if (top_obj) top_val = accelerationValue(top_obj, time_obj, time_ms);
				}
				if (params.size) {
					if (width_obj) width_val = accelerationValue(width_obj, time_obj, time_ms);
					if (height_obj) height_val = accelerationValue(height_obj, time_obj, time_ms);
				}
				if (params.opacity) {
					if (opacity_obj) opacity_val = accelerationValue(opacity_obj, time_obj, time_ms);
				}
			} else if (time_ms <= time_obj.accel + time_obj.cruise) { // Cruising
				if (params.position) {
					if (left_obj) left_val = cruiseValue(left_obj, time_obj, time_ms);
					if (top_obj) top_val = cruiseValue(top_obj, time_obj, time_ms);
				}
				if (params.size) {
					if (width_obj) width_val = cruiseValue(width_obj, time_obj, time_ms);
					if (height_obj) height_val = cruiseValue(height_obj, time_obj, time_ms);
				}
				if (params.opacity) {
					if (opacity_obj) opacity_val = cruiseValue(opacity_obj, time_obj, time_ms);
				}
			} else { // Deceleration
				if (params.position) {
					if (left_obj) left_val = decelerationValue(left_obj, time_obj, time_ms);
					if (top_obj) top_val = decelerationValue(top_obj, time_obj, time_ms);
				}
				if (params.size) {
					if (width_obj) width_val = decelerationValue(width_obj, time_obj, time_ms);
					if (height_obj) height_val = decelerationValue(height_obj, time_obj, time_ms);
				}
				if (params.opacity) {
					if (opacity_obj) opacity_val = decelerationValue(opacity_obj, time_obj, time_ms);
				}
			}

			if (params.position) {
				left = (left_obj)? Math.round(params.position.start.left + left_val): "null";
				top = (top_obj)? Math.round(params.position.start.top + top_val): "null";
				position = "{left: "+left+", top: "+top+"}";
			} else {
				position = "null";
			}
			if (params.size) {
				width = (width_obj)? Math.round(params.size.start.width + width_val): "null";
				height = (height_obj)? Math.round(params.size.start.height + height_val): "null";
				size = "{width: "+width+", height: "+height+"}";
			} else {
				size = "null";
			}
			if (params.opacity) {
				opacity = params.opacity.start + ((Math.round(opacity_val * 100)) / 100);
			} else {
				opacity = "null";
			}
			trans_list.push({time_ms: time_ms, position: position, size: size, opacity: opacity});
		}

//linear animation
	} else {
		if (params.position) {
			var left_incr = (params.position.end.left - params.position.start.left) / total_num_frames;
			var top_incr = (params.position.end.top - params.position.start.top) / total_num_frames;
		}
		if (params.size) {
			var width_incr = (params.size.end.width - params.size.start.width) / total_num_frames;
			var height_incr = (params.size.end.height - params.size.start.height) / total_num_frames;
		}
		if (params.opacity) {
			var opacity_incr = (params.opacity.end - params.opacity.start) / total_num_frames;
		}
				
		for (var i = 1; i <= total_num_frames; i++) {
			var time_ms = delay * i;
			if (params.position) {
				left = (params.position.start.left != null)? Math.round(params.position.start.left + (left_incr * i)): "null";
				top = (params.position.start.top != null)? Math.round(params.position.start.top + (top_incr * i)): "null";
				position = "{left: "+left+", top: "+top+"}";
			} else {
				position = "null";
			}
			if (params.size) {
				width = (params.size.start.width != null)? Math.round(params.size.start.width + (width_incr * i)): "null";
				height = (params.size.start.height != null)? Math.round(params.size.start.height + (height_incr * i)): "null";
				size = "{width: "+width+", height: "+height+"}";
			} else {
				size = "null";
			}
			if (params.opacity) {
				opacity = params.opacity.start + ((Math.round(opacity_incr * i * 100)) / 100);
			} else {
				opacity = "null";
			}
			trans_list.push({time_ms: time_ms, position: position, size: size, opacity: opacity});
		}
	}
	
	for (var i in trans_list) {
		Transform.timeouts[params.obj_id].push(setTimeout("Transform.setTransform('"+params.obj_id+"', "+trans_list[i].position+", "+trans_list[i].size+", "+trans_list[i].opacity+")", trans_list[i].time_ms));
	}
	
	if (params.exec_on_finish) Transform.timeouts[params.obj_id].push(setTimeout("eval("+params.exec_on_finish+")", animation_time_ms + 200));
}

//transform for different browsers
Transform.setTransform = function(obj_id, position, size, opacity) {
	if (document.getElementById(obj_id)) {
		var object = document.getElementById(obj_id);
		if (position) {
			if (position.left != null) object.style.left = position.left+"px";
			if (position.top != null) object.style.top = position.top+"px";
		}
		if (size) {
			if (size.width != null) object.style.width = size.width+"px";
			if (size.height != null) object.style.height = size.height+"px";
		}
		if (opacity || opacity == 0) {
			object.style.opacity = opacity;
			object.style.MozOpacity = opacity;
			object.style.KhtmlOpacity = opacity;
			object.style.filter = "alpha(opacity="+(opacity * 100)+")";
			//object.style.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity="+(opacity)+")";
		}
	}
} 

//set to opaque for different browsers
Transform.setOpaque = function(obj_id) {
	Transform.setTransform(obj_id, null, null, 1);
} 

//set to transparent for different browsers
Transform.setTransparent = function(obj_id) {
	Transform.setTransform(obj_id, null, null, 0);
} 

Transform.cancelTransformAnimTimeouts = function(obj_id) {
	for (var i in Transform.timeouts[obj_id]) {
		clearTimeout(Transform.timeouts[obj_id][i]);
	}
	Transform.timeouts[obj_id] = null;
}

