if (!Array.prototype.push) {
	Array.prototype.push = function() {
		for (var i=0; i<arguments.length; i++)
			this[this.length] = arguments[i];
	}
}

if (!Array.prototype.remove) {
	Array.prototype.remove = function (pos,len) {
		var i,j;
		for (i=len+pos,j=pos; i<this.length; i++,j++) {
			this[j] = this[i];
		}
		this.length -= len;
	}
}

//------------------------------------------------

Math.linearTween = function (t, b, c, d) {
	return c*t/d + b;
}

Math.easeInQuad = function (t, b, c, d) {
	return c*t*t/(d*d) + b;
}

Math.easeOutQuad = function (t, b, c, d) {
	return -c*t*t/(d*d) + 2*c*t/d + b;
}

Math.easeInOutQuad = function (t, b, c, d) {
	if (t < d/2) return 2*c*t*t/(d*d) + b;
	var ts = t - d/2;
	return -2*c*ts*ts/(d*d) + 2*c*ts/d + c/2 + b;
}

//-------------------------------------------------

var MasterTimeServer = new TimeServer(16);

function TimeServer (interval) {
	this.receiver = new Array();
	this.receiver_count = 0;
	this.timerid = -1;
	this.interval = interval;
}

function TimeServer_toSource () {
	return "TimeServer::[" + this.receiver.join(",") + "]";
}

function TimeServer_AddReceiver (recv) {
	this.receiver[recv] = recv;
	this.receiver_count++;
	if (this.receiver_count == 1) {
		this.timerid = setInterval("MasterTimeServer.TimerCallback()",this.interval);
//		this.timerid = setTimeout("MasterTimeServer.TimerCallback()",this.interval);
	}
}

function TimeServer_RemoveReceiver (recv) {
	delete this.receiver[recv];
	this.receiver_count--;
	if (this.receiver_count == 0) {
		clearInterval(this.timerid);
		this.receiver = new Array();
	}
}

function TimeServer_TimerCallback () {
	var recv;
	for (var p in this.receiver) {
		recv = this.receiver[p];
		if (typeof(recv) == "object") {
			recv.TimerCallback();
		}
	}
//	if (this.receiver_count) {
//		this.timerid = setTimeout("MasterTimeServer.TimerCallback()",this.interval);
//	}
}

TimeServer.prototype.toSource = TimeServer_toSource;
TimeServer.prototype.toString = TimeServer_toSource;
TimeServer.prototype.AddReceiver = TimeServer_AddReceiver;
TimeServer.prototype.RemoveReceiver = TimeServer_RemoveReceiver;
TimeServer.prototype.TimerCallback = TimeServer_TimerCallback;

//---------------------------------

function KeyframeAnimator(target,prop) {
	var i,j,s,e,tmp,tmp2,prefix,suffix;
	s = target.indexOf("[");
	if (s >= 0) {
		e = target.lastIndexOf("]");
		prefix = target.substring(0,s);
		suffix = target.substring(e+1,target.length);
		tmp = target.substring(s+1,e).split(",");
		this.target = new Array();
		for (i=0; i<tmp.length; i++) {
			if (tmp[i].indexOf("-") >= 0) {
				tmp2 = tmp[i].split("-");
				for (j=Number(tmp2[0]); j<=Number(tmp2[1]); j++) {
					this.target.push(prefix + j + suffix);
				}
			} else {
				this.target.push(prefix + tmp[i] + suffix);
			}
		}
	} else {
		this.target = target;
	}
	this.property = prop;
	switch (this.property) {
		case "rot_":
		case "loc_":
		case "scl_":
		case "crot":
		case "ctra":
		case "cscl":
			this.mode = 3;
			break;
		case "cdst":
		case "opac":
			this.mode = 1;
			break;
		case "visb":
		case "clps":
			this.mode = 2;
			break;
	}
	this.Init();
}

function KeyframeAnimator_Init () {
	this.start_time = -1;
	this.key_pos = 0;
	this.key_count = -1;
	this.prev_pos = new Array(3);
	this.dest_pos = new Array();
	this.duration = new Array();
	this.easing_func = new Array();
}

function KeyframeAnimator_toString () {
	return this.target + "::" + this.property;
}

function KeyframeAnimator_toSource () {
	var tmp;
	tmp = "Animator::" + this.toString() + "\n";
	tmp = tmp + "    prev_pos:[" + this.prev_pos.join(",") + "]\n";
	tmp = tmp + "    dest_pos:[" + this.dest_pos.join(",") + "]\n";
	tmp = tmp + "    duration:" + this.duration;
	return tmp;
}

function KeyframeAnimator_AddKeyframe(duration,easing,param) {
	var tmp;
	if (param.split(",").length > 1) {
		tmp = param.substring(1,param.length-1).split(",");
	} else {
		tmp = new Array(3);
		tmp[0] = param;
	}
	this.dest_pos.push(tmp);
	this.duration.push(duration);
	this.key_count++;
	switch (easing) {
		case "in":
		case "<":
			this.easing_func.push(Math.easeInQuad);
			break;
		case "out":
		case ">":
			this.easing_func.push(Math.easeOutQuad);
			break;
		case "inout":
		case "<>":
			this.easing_func.push(Math.easeInOutQuad);
			break;
		default:
			this.easing_func.push(Math.linearTween);
			break;
	}
	return this;
}

function KeyframeAnimator_Prepare (keyno) {
	var tmp,i,d,f;
	if (typeof(this.target) == "string") {
		tmp = vmp.GetProperty(this.target,this.property);
	} else {
		tmp = vmp.GetProperty(this.target[0],this.property);
	}
	switch (this.mode) {
		case 1:
			this.prev_pos[0] = Number(tmp);
			break;
		case 3:
			tmp = String(tmp).split(" ");
			for (i=0; i<=2; i++) {
				this.prev_pos[i] = Number(tmp[i]);
			}
/*			switch (this.property) {
				case "rot_":
				case "crot":
					for (i=0; i<=2; i++) {
						while (Math.abs(this.dest_pos[keyno][i]-this.prev_pos[i])>180) {
							d = this.dest_pos[keyno][i]-this.prev_pos[i];
							f = d / Math.abs(d);
							this.prev_pos[i] = this.prev_pos[i] + f*360;
						}
					}
					break;
			}*/
	}
	this.key_pos = keyno;
}

function KeyframeAnimator_Start () {
	this.Prepare(0);
	this.start_time = new Date().getTime();
	this.TimerCallback();
	MasterTimeServer.AddReceiver(this);
}

function KeyframeAnimator_TimerCallback () {
	var cur_pos,i;
	var t = new Date().getTime() - this.start_time;
	if (t>=this.duration[this.key_pos]) {
		t = this.duration[this.key_pos];
		if (this.key_pos == this.key_count) {
			MasterTimeServer.RemoveReceiver(this);
		}
	}
	switch (this.mode) {
		case 1:
			cur_pos = this.easing_func[this.key_pos](t,this.prev_pos[0],this.dest_pos[this.key_pos][0]-this.prev_pos[0],this.duration[this.key_pos]);
			break;
		case 3:
			cur_pos = new Array(3);
			for (i=0; i<=2; i++) {
				cur_pos[i] = this.easing_func[this.key_pos](t,this.prev_pos[i],this.dest_pos[this.key_pos][i]-this.prev_pos[i],this.duration[this.key_pos]);
			}
			cur_pos = cur_pos.join(" ");
			break;
	}
	if (typeof(this.target) == "string") {
		vmp.SetProperty(this.target,this.property,cur_pos);
	} else {
		for (i=0; i<this.target.length; i++) {
			vmp.SetProperty(this.target[i],this.property,cur_pos);
		}
	}
	if (t == this.duration[this.key_pos]) {
		if (this.key_pos != this.key_count) {
			this.Prepare(++this.key_pos);
			this.start_time = new Date().getTime();
		}
	}
}

KeyframeAnimator.prototype.Init = KeyframeAnimator_Init;
KeyframeAnimator.prototype.toString = KeyframeAnimator_toString;
KeyframeAnimator.prototype.toSource = KeyframeAnimator_toSource
KeyframeAnimator.prototype.AddKeyframe = KeyframeAnimator_AddKeyframe;
KeyframeAnimator.prototype.Prepare = KeyframeAnimator_Prepare;
KeyframeAnimator.prototype.Start = KeyframeAnimator_Start;
KeyframeAnimator.prototype.TimerCallback = KeyframeAnimator_TimerCallback;

//------------------------------------------------

function Sequencer () {
	this.time_origin = -1;
	this.start_time = new Array();
	this.animator = new Array();
}

function Sequencer_toString () {
	return "Sequencer::" + this.animator.join(",");
}

function Sequencer_AddAnimator (starttime,animator) {
	this.start_time.push(starttime);
	this.animator.push(animator);
}

function Sequencer_AddAnimation (start,duration,target,prop,easing,param) {
	anime = new KeyframeAnimator(target,prop);
	anime.AddKeyframe(duration,easing,param);
	this.AddAnimator(start,anime);
}

function Sequencer_AddSequence (data) {
	var i,tmp;
	for (i=0; i<data.length; i++) {
		tmp = data[i].split(":");
		if (tmp.length == 2) {
			this.AddScript(tmp[0],tmp[1]);
		} else {
			this.AddAnimation(tmp[0],tmp[1],tmp[2],tmp[3],tmp[4],tmp[5]);
		}
	}
}

function Sequencer_Start () {
	this.time_origin = new Date().getTime();
	MasterTimeServer.AddReceiver(this);
	this.TimerCallback();
}

function Sequencer_TimerCallback () {
	var t = new Date().getTime() - this.time_origin;
	for (var i=0; i<this.start_time.length; i++) {
		if (t >= this.start_time[i]) {
			if (typeof(this.animator[i]) == "object") {
				this.animator[i].Start();
			} else {
				eval(this.animator[i]);
			}
		} else {
			break;
		}
	}
	if (i>0) {
		this.start_time.remove(0,i);
		this.animator.remove(0,i);
		if (this.start_time.length == 0) {
			MasterTimeServer.RemoveReceiver(this);
		}
	}
}

Sequencer.prototype.toString = Sequencer_toString;
Sequencer.prototype.AddAnimator = Sequencer_AddAnimator;
Sequencer.prototype.AddScript = Sequencer_AddAnimator;
Sequencer.prototype.AddAnimation = Sequencer_AddAnimation;
Sequencer.prototype.AddSequence = Sequencer_AddSequence;
Sequencer.prototype.Start = Sequencer_Start;
Sequencer.prototype.TimerCallback = Sequencer_TimerCallback;





