AnimationService = {
	INTERVAL : 30,																// Default delay between steps
	STEP : 150,																	// Default pixels to move each step
	animations : new Object(),													// Used to reference a given animation by id
	addAnimation : function(id, obj) {
		obj.id = id;
		this.animations[id] = obj;
		return obj;
	},
	startById : function(id) {
		if (this.animations[id]) {
			this.animations[id].start();
		}
	},
	stopById : function(id) {
		if (this.animations[id]) {
			this.animations[id].stop();
		}
	},
	runScene : function(scene, cb) {											// Runs an array of Animations and executes a callback when finished.
		var completed = new Array();
		if (scene.length) {
			for (var n = 0; n < scene.length; n++) {
				scene[n].sceneIndex = n;
				completed[n] = false;
				scene[n].complete = function() {
					completed[this.sceneIndex] = true;
					this.sceneIndex = 0;
					allDone = true;
					for (var j = 0; j < completed.length; j++) {
						if (!completed[j]) {
							allDone = false;
						}
					}
					if (allDone) {
						cb();
					}
				}
				scene[n].start();
			}
		} else {
			cb();
		}
	}
}

moveByAnimation = function(element) {
	this.element = element;
	this.interval = AnimationService.INTERVAL;
	this.step = AnimationService.STEP;
	this.running = false;
	this.intervalId = null;
	this.startTime = null;
	this.sceneIndex = 0;
	this.id = null;

	this.byX = 0;
	this.byY = 0;
	
	this.startX = getX(element);
	this.startY = getY(element);
	
	this.start = function() {
		if (this.intervalId == null) {
			this.beforeStart();
			this.running = true;
			
			this.startX = getX(this.element);
			this.startY = getY(this.element);

			var xDist = this.step * ((this.byX > 0) ? 1 : -1);
			var yDist = this.step * ((this.byY > 0) ? 1 : -1);
			
			var self = this;
			this.intervalId = setInterval(function() {

					var difX = (self.startX + self.byX) - getX(self.element);
					var difY = (self.startY + self.byY) - getY(self.element);
					
					// debug("this.byY: " + self.byY + " difY: " + difY + " yDist: " + yDist + " getY: " + getY(self.element));
					if (difX != 0 && self.byX != 0) {
						if ((difX < 0 && difX <= xDist) || (difX > 0 && xDist <= difX)) {
							moveByX(self.element, xDist);
						} else {
							moveByX(self.element, difX);
						}
					}
					if (difY != 0 && self.byY != 0) {
						if ((difY < 0 && difY <= yDist) || (difY > 0 && yDist <= difY)) {
							moveByY(self.element, yDist);
						} else {
							moveByY(self.element, difY);
						}
					}
					
					if (difX == 0 && difY == 0) {
						self.stop();
						self.complete();
					}
				},
				this.interval
			);
		
		} else {
			throw new Error("moveByAnimation already started for " + this.element.id);
		}
	}
	this.stop = function() {
		if (this.intervalId != null) {
			clearInterval(this.intervalId);
			this.intervalId = null;
		}
		this.running = false;
	}
	this.complete = function() {}
	this.beforeStart = function() {
		this.startTime = new Date().getTime();
	}
}

resizeByAnimation = function(element) {
	this.element = element;
	this.interval = AnimationService.INTERVAL;
	this.step = AnimationService.STEP;
	this.running = false;
	this.intervalId = null;
	this.startTime = null;
	this.sceneIndex = 0;
	this.id = null;

	this.byX = 0;
	this.byY = 0;
	
	this.startSize = getSize(this.element);
	
	this.start = function() {
		// debug("start: " + this.element.id);
		if (this.intervalId == null) {
			this.beforeStart();
			this.running = true;
			
			this.startSize = getSize(this.element);

			var xDist = this.step * ((this.byX > 0) ? 1 : -1);
			var yDist = this.step * ((this.byY > 0) ? 1 : -1);
			
			var self = this;
			this.intervalId = setInterval(function() {
					var size = getSize(self.element)
					var difX = (self.startSize.width + self.byX) - size.width;
					var difY = (self.startSize.height + self.byY) - size.height;
					
					
					// debug("difY: " + difY + " yDist: " + yDist + " size.height: " + size.height);
					
					if (difX != 0 && self.byX != 0) {
						if ((difX < 0 && difX <= xDist) || (difX > 0 && xDist <= difX)) {
							resizeToX(self.element, xDist + size.width);
						} else {
							resizeToX(self.element, difX + size.width);
						}
					}
					if (difY != 0 && self.byY != 0) {
						if ((difY < 0 && difY <= yDist) || (difY > 0 && yDist <= difY)) {
							resizeToY(self.element, yDist + size.height);
						} else {
							resizeToY(self.element, difY + size.height);
						}
					}
					
					if (difX == 0 && difY == 0) {
						// debug("Complete: " + self.element.id);
						self.stop();
						self.complete();
					}
				},
				this.interval
			);
		
		} else {
			throw new Error("resizeByAnimation already started for " + this.element.id);
		}
	}
	this.stop = function() {
		if (this.intervalId != null) {
			clearInterval(this.intervalId);
			this.intervalId = null;
		}
		this.running = false;
	}
	this.complete = function() {}
	this.beforeStart = function() {
		this.startTime = new Date().getTime();
	}
}

fadeAnimation = function(element) {
	this.element = element;
	this.interval = AnimationService.INTERVAL;
	this.step = AnimationService.STEP;
	this.running = false;
	this.intervalId = null;
	this.startTime = null;
	this.sceneIndex = 0;
	this.id = null;

	this.toOpacity = 0;
	
	this.startOpacity = getOpacity(this.element);
	
	this.start = function() {
		//debug("start: " + this.element.id);
		if (this.intervalId == null) {
			this.beforeStart();
			this.running = true;
			
			if (this.toOpacity < 0) {
				this.toOpacity = 0;
			}
			
			this.startOpacity = getOpacity(this.element);
			
			var dist = this.toOpacity > this.startOpacity ? 1 : -1;
			
			//debug("Start: " + this.startOpacity + " To: " + this.toOpacity + " dist: " + dist);
			var self = this;
			this.intervalId = setInterval(function() {
					var currentOpacity = getOpacity(self.element)
					var dif = currentOpacity + self.step * dist;
					
					// debug("Current: " + currentOpacity + " dif: " + dif);
					if ((dist > 0 && dif <= self.toOpacity) || (dist < 0 && dif >= self.toOpacity)) {
						setOpacity(self.element, dif);
					} else {
						setOpacity(self.element, self.toOpacity);
					}
					
					if (currentOpacity == self.toOpacity) {
						//debug("Complete: " + self.element.id);
						self.stop();
						self.complete();
					}
				},
				this.interval
			);
		
		} else {
			throw new Error("fadeAnimation already started for " + this.element.id);
		}
	}
	this.stop = function() {
		if (this.intervalId != null) {
			clearInterval(this.intervalId);
			this.intervalId = null;
		}
		this.running = false;
	}
	this.complete = function() {}
	this.beforeStart = function() {
		this.startTime = new Date().getTime();
	}
}

moveToXY = function(element, x, y) {
	element.style.left = x;
	element.style.top = y;
}

moveByX = function(element, x) {
	element.style.left = getX(element) + x;
}
moveByY = function(element, y) {
	element.style.top = getY(element) + y;
}

resizeToX = function(element, x) {
	element.style.width = x;
}
resizeToY = function(element, y) {
	element.style.height = y;
}

getX = function(element) {
	return isNaN(parseInt(element.style.left)) ? 0 : parseInt(element.style.left);
}
getY = function(element) {
	return isNaN(parseInt(element.style.top)) ? 0 : parseInt(element.style.top);
}
getSize = function(element) {
	var display = element.style.display;
	if (display != "none" && display != null) {									// Safari bug
		return new Size(element.offsetWidth, element.offsetHeight);
	}
	
	// All *Width and *Height properties give 0 on elements with display none,
	// so enable the element temporarily
	var els = element.style;
	var originalVisibility = els.visibility;
	var originalPosition = els.position;
	var originalDisplay = els.display;
	els.visibility = 'hidden';
	els.position = 'absolute';
	els.display = 'block';
	var originalWidth = element.clientWidth;
	var originalHeight = element.clientHeight;
	els.display = originalDisplay;
	els.position = originalPosition;
	els.visibility = originalVisibility;
	
	return new Size(originalWidth, originalHeight);
}

Size = function(w, h) {
	this.width = isNaN(w) ? 0 : w;
	this.height = isNaN(h) ? 0 : h;
	this.toString = function() {
		return this.width + "," + this.height;
	}

}

getOpacity = function(element) {
	if (element.currentStyle) {
		if (element.currentStyle.filter) {
			if (value = (element.currentStyle.filter || '').match(/alpha\(opacity=(.*)\)/))
			if (value[1]) return parseFloat(value[1]);
		} else {
			return 100;
		}
	} else if (element.style.opacity) {
		return element.style.opacity * 100;
	} else if (element.style.MozOpacity) {
		return element.style.MozOpacity * 100;
	} else {
		var css = document.defaultView.getComputedStyle(element, null);
		return css ? css["opacity"] * 100 : null;
	}
}

setOpacity = function(element, value) {
	function stripAlpha(filter){
		return filter.replace(/alpha\([^\)]*\)/gi,'');
	}
	
	var filter = null;
	if (element.currentStyle) {
		var currentStyle = element.currentStyle;
		filter = (currentStyle.filter) ? currentStyle.filter : "";
	}
	var style = element.style;

	if (value == 100 || value === '') {
		if (filter != null) {
			(filter = stripAlpha(filter)) ? style.filter = filter : style.removeAttribute('filter');
		}
		element.style.opacity = 1;
		element.style.MozOpacity = 1;
		return element;
	} else if (value < 0.00001) value = 0;
	
	if (filter != null) {
		style.filter = stripAlpha(filter) + 'alpha(opacity=' + value + ')';
	}
	element.style.opacity = value / 100;
	element.style.MozOpacity = value / 100;

	return element;
}

