// Animator
// Daniel Reed 2008

function animate(args){
  	play(new Animation(args));
}

function play(animation){
	if(animation!=null){
		var element = animation.element;
		if(!element.manipulator){
 			element.manipulator=new DivManipulator(animation);
 		}
 		else{
 			element.manipulator.animation = animation;
 		}
 		stopAnimation(element);
 		element.manipulator.startAnimation();
	}
}

function Animation(args){
	if (typeof(args.element)=='string'){
		this.element=document.getElementById(args.element); 
	}	
	else{
		this.element = args.element
	}
	if (!args.fps || args.fps<1){
		this.fps = 200; 
	}
	else{
		this.fps = args.fps;
	}
	if(!args.duration || args.duration<=0){
		this.duration=0.5;
	}
	else{
		this.duration = args.duration;
	}
	if(!args.delay || args.delay<0){
		this.delay = 0;
	}
	else{
		this.delay = args.delay * 1000;
	}
	
	this.opacity = args.opacity;
	this.width = args.width;
	this.height = args.height;
	this.top = args.top;
	this.left = args.left;
	this.marginLeft = args.marginLeft;
	//printProperties(this);
}

function stopAnimation(element){
	if(element.manipulator){
		clearTimeout(element.manipulator.timeout);
	}
}

function clearAnimation(element){
	stopAnimation(element);
	element.manipulator = null;
}

/************** Div Manipulator class *****************/

function DivManipulator(animation){
	this.animation = animation;
	this.ref=animation.element.id;
	window[this.ref]=this;
}

DivManipulator.prototype.initialise=function(){
	var framecount = this.animation.fps * this.animation.duration;
	this.frameDelay = 1000/this.animation.fps;
	
	this.getAnimatedOpacity(framecount);
	this.getAnimatedMeasurement('width', framecount);
	this.getAnimatedMeasurement('height', framecount);
	this.getAnimatedMeasurement('top', framecount);
	this.getAnimatedMeasurement('left', framecount);
	this.getAnimatedMeasurement('marginLeft', framecount);
}

DivManipulator.prototype.getAnimatedOpacity=function(framecount){
	if(this.animation.opacity!=null){
		if(!this.opacity){
			this.opacity = new AnimatedValue(getOpacity(this.animation.element), null);
		}
		this.opacity.initialise(this.animation.opacity, framecount);
	}
	else if(this.opacity){
		this.opacity.reset();
	}
}

DivManipulator.prototype.getAnimatedMeasurement=function(type, framecount){
	if(this.animation[type]!=null){ 
		if(!this[type]){
			var measurement = getElementStyleProperty(this.animation.element, type, "0px");
			this[type] = new AnimatedValue(parseInt(removeUnit(measurement)), getUnit(measurement));
		}
		this[type].initialise(this.animation[type], framecount);
	}
	else if(this[type]){
		this[type].reset();
	}
}

DivManipulator.prototype.updateAnimatedOpacity=function(type){
	if(this.opacity && this.opacity.isAnimated()){
		this.opacity.update();
		setOpacity(this.animation.element,this.opacity.current);
		if(this.opacity.isFinished()) this.finished = true;
	}
}

DivManipulator.prototype.updateAnimatedMeasurement=function(type){
	if(this[type] && this[type].isAnimated()){
		this[type].update();
		this.animation.element.style[type] = this[type].current + this[type].unit;
		if(this[type].isFinished()){
			this.finished = true;
		}
	}
}

DivManipulator.prototype.finaliseAnimatedMeasurement=function(type){
	if(this[type] && this[type].isAnimated() && this[type].current!=this[type].to){
		this[type].current = this[type].to;
		this.animation.element.style[type] = this[type].current + this[type].unit;
	}
}

DivManipulator.prototype.startAnimation=function(){
	if(this.animation!=null){
		this.initialise();
		this.setTimeOut("update();", this.animation.delay);
	}
}

DivManipulator.prototype.update=function(){
	this.finished = false;
	this.updateAnimatedOpacity();
	this.updateAnimatedMeasurement('width');
	this.updateAnimatedMeasurement('height');
	this.updateAnimatedMeasurement('top');
	this.updateAnimatedMeasurement('left');
	this.updateAnimatedMeasurement('marginLeft');

	if(!this.finished){
		this.setTimeOut("update();",this.frameDelay);
	}
	else{
		this.finaliseAnimatedMeasurement('width');
		this.finaliseAnimatedMeasurement('height');
		this.finaliseAnimatedMeasurement('top');
		this.finaliseAnimatedMeasurement('left');
		this.finaliseAnimatedMeasurement('marginLeft');
		play(this.animation.nextAnimation);
	}
}

DivManipulator.prototype.setTimeOut= function(func, delay){
	this.timeout=setTimeout("window."+this.ref+"."+func, delay);
}

/************** AnimatedValue class *****************/

function AnimatedValue(current, unit){
	this.current = current;
	this.unit = unit;
}

AnimatedValue.prototype.initialise=function(to, frameCount){
	this.to = to;
	this.increment = (this.to - this.current)/frameCount;
}

AnimatedValue.prototype.update=function(){
	if(this.increment){
		this.current+=this.increment;
		if((this.increment>0 && this.current>this.to) || (this.increment<0 && this.current<this.to)){
			this.current = this.to;
		}
	}
}

AnimatedValue.prototype.reset=function(){
	this.to = null;
}

AnimatedValue.prototype.isAnimated=function(){
	return this.to != null;
}

AnimatedValue.prototype.isFinished=function(){
	return this.current == this.to;
}

/************** Measurement functions *****************/

function removeUnit(measurement){

	if(endsWith(measurement, "%")){
		return measurement.substring(0, measurement.length-1);
	}
	else if(endsWith(measurement, "px")){
		return measurement.substring(0, measurement.length-2);
	}	
}

function getUnit(measurement){
	if(endsWith(measurement, "%")){
		return "%";
	}
	else if(endsWith(measurement, "px")){
		return "px";
	}
}

/************** Opacity functions *****************/

//get the opacity of an element
function getOpacity(element) {
	if (!isNullOrEmpty(element.style.opacity)){ 
		return convertOpacityFirefoxToFloat(element.style.opacity);
	}
	else if (!isNullOrEmpty(element.style.filter)){
 		return convertOpacityIEToFloat(element.style.filter); 
 	}
 	else{
 		//check the style
 		var rule = getCssRule('.'+element.className);
 		if(rule!=null && rule.style!=null){
 			if (!isNullOrEmpty(rule.style.opacity)){ 
				return convertOpacityFirefoxToFloat(rule.style.opacity);
			}
			else if (!isNullOrEmpty(rule.style.filter)){
		 		return convertOpacityIEToFloat(rule.style.filter); 
		 	}
 		}
 		return 100;
 	}
}

// set the opacity of an element
function setOpacity(element, opacity) {
	if (opacity>100||opacity<0){ 
		return 
	}
	if(opacity>0 && element.style.visibility =='hidden'){
		element.style.visibility = 'visible';
	}
	if(opacity>0 && element.style.display =='none'){
		element.style.display = '';
	}
	if (element.style.opacity!=null){ 
		element.style.opacity = convertOpacityFloatToFirefox(opacity); 
	}
	else if (element.style.filter!=null){ 
		element.style.filter = convertOpacityFloatToIE(opacity);     
	}
}

function convertOpacityFirefoxToFloat(opacity){
	//return (parseFloat(opacity) + .001)*100;
	return (parseFloat(opacity))*100;
}

function convertOpacityIEToFloat(filter){
	return parseInt(filter.replace(/alpha\(opacity=/, "").replace(/\)/, ""));
}

function convertOpacityFloatToFirefox(opacity){
	//return (opacity/100)-.001; 
	return (opacity/100); 
}

function convertOpacityFloatToIE(opacity){
	return 'alpha(opacity='+opacity+')'; 
}

/************** Utilities *****************/
function  printProperties(obj) {
	var output = "" ;
	for (var prop in obj) {
		output += prop + " = " + obj[prop] + "\n" ;
	}
	alert(output);
}

function endsWith(str, s){
	var reg = new RegExp(s + "$");
	return reg.test(str);
}

function isNullOrEmpty(str){
	return str == null || str.length==0;
}

function getElementStyleProperty(element, property, defaultValue) {
	if (!isNullOrEmpty(element.style[property])){ 
		return element.style[property];
	}
 	else{
 		//check the style
 		var rule = getCssRule('.'+element.className);
 		if(rule!=null && rule.style!=null){
 			if (!isNullOrEmpty(rule.style[property])){ 
				return rule.style[property];
			}
 		}
 		return defaultValue;
 	}
}

/************** Css inspector *****************/
var cssRules;
if (document.all) {
	cssRules = 'rules';
}
else if (document.getElementById) {
	cssRules = 'cssRules';
}
	
//get an attribute from a css file
function getStylesheetRule(stylesheet, id){
	//look in each stylesheet
	for (var i = 0; i < stylesheet[cssRules].length; i++) {
		if(stylesheet[cssRules][i].selectorText == id ){
			var rule = stylesheet[cssRules][i];
			return rule;
		}
	}
}

//search through the directly linked css files and get a stylesheet rule by name
function getCssRule(id){
	for (var i = 0; i < document.styleSheets.length; i++){
		var stylesheet = document.styleSheets[i];
		var att = getStylesheetRule(stylesheet, id);
		if(att!=null){
			return att;
		}	
	}
}
