/******************************
Timer.js
Brendon <brendy at gmail dotcom>
2005-11-02

---------------

timer = new Timer( str timerElementID )
	Mandatory - Initiates the new timer with the element id of the div tag where the time will appear.
	timerElementID is a mandatory argument.

timer.postBackID = str inputID
	Optional - Determines if the timer will be working on a single page or multiple pages.
	This will be the id of the hidden text field which will contain a timestamp to be sent
	to the server. If the value of the input is not an integer greater than 0, the timestamp
	of the input will be completely ignored, and the argument given to the start() function
	will be used instead. If start() is not given an argument, it will default to 0. This means
	that the input element will always be ignored on the first page using the timer.

timer.direction = ( int -1 | int 1 )
	Optional - Set the direction of the timer. -1 is backwards, 1 is forwards. Defaults to 1
	
timer.timerClass = str className
	Optional - Set the css classname of the timer element. Defaults to empty string
	
timer.userFunc = func functionReference
	Optional - Set the callback function when a deadline is reached. Note: this is not a string.
	It is an actual function reference

timer.tformat = str timerFormat
	Optional - sets the format of the outputted timer.
	Format values are:
		<-> - indicate negative time
		<HH> - 2 digit hours
		<H> - 1 digit hours
		<MM> - 2 digit minutes
		<M> - 1 digit minutes
		<TM> - Total minutes
		<SS> - 2 digit seconds
		<S> - 1 digit seconds
		<TS> - Total seconds
	Defaults to "<-><MM>:<SS>"

timer.deadline = int stopTime
	Optional - sets the time which, when hit, will stop the timer. If no deadline is set,
	the timer will go on forever.

timer.interval = int miliseconds
	Optional = specifies the amount of miliseconds to incrment the clock. Defaults to 1000 (1 second)

Timer timer.start( [int startTime] )
	Mandatory - starts the timer. The optional argument (startTime), specifies the initial start value
	in seconds. startTime defaults to 0 if not specified.

static Timer.resetAllTimers()
	Static method used to reset all timers back to original value

*******************************/

var SECONDS_PER_MINUTE = 60;
var MINUTES_PER_HOUR = 60;

window.timers = new Array();

Timer = function( elmID ) {
 //public properties
 this.timerClass = '';
 this.interval = 1000;
 this.deadline = null;
 this.userFunc = null;
 this.tformat = "<-><MM>:<SS>";
 this.postBackID = null;
 this.direction = 1;

 //private properties
 this._timerIndexID = null;
 this._startInterval = null;
 this._postBackElm = null;
 this._timerElm = null;
 this._completed = false;
 this._defaultStartInterval = 0;
 
	this._constructor = function() {
	 this._timerElm = document.getElementById( elmID );
	 this._timerElm.appendChild( document.createTextNode('') );
	 this._timerIndexID = window.timers.length;
	}

 this._constructor();
}


Timer.prototype.start = function() {
	/*
		check for invalid values
	*/
	if( this.direction != -1 && this.direction != 1 ) {
	 alert( "Timer Error: Invalid direction value." );
	 return false;
	}
	if( isNaN(this.deadline) && this.deadline != null ) {
	 alert( "Timer Error: Invalid deadline value." );
	 return false;
	}
	if( isNaN(this.interval) ) {
	 alert( "Timer Error: Invalid interval value." );
	 return false;
	}
 var setThisInterval = false;
	//if we are manually setting the _startInterval
	if( Timer.prototype.start.arguments.length == 1 ) {	
	 setThisInterval = true;
	 startInterval = Timer.prototype.start.arguments[0];
	}
	else {
	 startInterval = this._defaultStartInterval;	
	}
	//if we are using a multiple page timer
	if( this.postBackID != null ) {
	 var thisDate = new Date();
	 var timestamp = Math.floor( thisDate.getTime() / 1000);
	 this._postBackElm = document.getElementById( this.postBackID );
		//if this is the first time the page has loaded
		if(
			parseInt(this._postBackElm.value) == 0 ||
			this._postBackElm.value == '' ||
			isNaN(this._postBackElm.value)
		) {
		 this._postBackElm.value = timestamp;
		 this._startInterval = startInterval;
		}
		/*
		 not the first time page has loaded
		*/
		//backward
		else if(this.direction == -1) {
		 this._startInterval = startInterval - (timestamp - this._postBackElm.value);
			//dont update if already hit dealine on previous page
			if( this.deadline != null && this._startInterval <= this.deadline ) {
			 this._completed = true;
			}
		}
		//forward
		else if(this.direction == 1) {
		 this._startInterval = startInterval + (timestamp - this._postBackElm.value);
			//dont update if already hit dealine on previous page
			if( this.deadline != null && this._startInterval >= this.deadline ) {
			 this._completed = true;			
			}
		}
	}
	//single page timer
	else {
	 this._startInterval = startInterval;
	}


 window.timers[this._timerIndexID] = new Object();
 window.timers[this._timerIndexID].element = this._timerElm;
 window.timers[this._timerIndexID].direction = this.direction;
 window.timers[this._timerIndexID].deadline = this.deadline;
 window.timers[this._timerIndexID].intervals = this._startInterval;
 window.timers[this._timerIndexID].startInterval = this._startInterval; 
 window.timers[this._timerIndexID].tformat = this.tformat;
 window.timers[this._timerIndexID].intervalLength = this.interval;
 window.timers[this._timerIndexID].postBackElm = ( (this._postBackElm == null) ? null : this._postBackElm );
 window.timers[this._timerIndexID].completed = this._completed;
	//if a callback function was set
	if( this.deadline != null && typeof this.userFunc == 'function' ) {
	 window.timers[this._timerIndexID].userFunc = this.userFunc;
	}

 this._timerElm.className = this.timerClass;
	/*
		decide whether or not the timer has already hit
		its deadline, then set the intial timer value
	*/
	if( this._completed ) {
	 initTime = Timer._getTimeFormat( this._startInterval , this.tformat );
	 Timer._updateTimer(this._timerIndexID , initTime);
		//execute completion function if needed
		if(typeof this.userFunc == 'function') {
		 this.userFunc();	
	 	}
	}
	else {
	 initTime = Timer._getTimeFormat( this._startInterval , this.tformat );
	 Timer._updateTimer(this._timerIndexID , initTime);
	 window.timers[this._timerIndexID].clockID = window.setInterval( "Timer._increaseTime(" + this._timerIndexID + ")" , this.interval );	
	}
}


Timer._increaseTime = function( indexID ) {
	//backward
	if( window.timers[indexID].direction == -1 ) {
	 window.timers[indexID].intervals--;
	}
	//forward
	else if( window.timers[indexID].direction == 1 ) {
	 window.timers[indexID].intervals++;	
	}
 finalTime = Timer._getTimeFormat( window.timers[indexID].intervals , window.timers[indexID].tformat );
 Timer._updateTimer(indexID , finalTime);
	//if we reach some kind of deadline
	if( window.timers[indexID].intervals == window.timers[indexID].deadline ) {
		if(typeof window.timers[indexID].userFunc == 'function') {
		 window.timers[indexID].userFunc();	
	 	}
	 window.clearInterval( window.timers[indexID].clockID );
	}
}

/*
	RESETS ALL TIMERS
*/
Timer.resetAllTimers = function() {
	for( var i = 0; i < window.timers.length; i++ ) {
	 window.timers[i].intervals = window.timers[i].startInterval;
	}
}


Timer._getTimeFormat = function( intervals , format ) {
 hours = intervals / (MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
 minutes = (intervals / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR;
 seconds = intervals % SECONDS_PER_MINUTE;
 
 thisHours = Math.abs( parseInt( hours ) );
 thisMinutes = Math.abs( parseInt( minutes ) );
 totalMinutes = Math.abs( parseInt( intervals / SECONDS_PER_MINUTE ) );
 thisSeconds = Math.abs( parseInt( seconds ) );
 totalSeconds = Math.abs(intervals);
 
 time_Neg = (intervals < 0) ? "-" : "";
 time_HH = ((thisHours.toString().length <= 1) ? '0':'') + thisHours;
 time_H = thisHours;
 time_MM = ((thisMinutes.toString().length <= 1) ? '0':'') + thisMinutes;
 time_M = thisMinutes.toString();
 time_TM = totalMinutes;
 time_SS = ((thisSeconds.toString().length <= 1) ? '0':'') + thisSeconds;
 time_S = thisSeconds.toString();
 time_TS = totalSeconds;
 
 finalTime = format;
 finalTime = finalTime.replace( /<->/g , time_Neg );
 finalTime = finalTime.replace( /<HH>/g , time_HH );
 finalTime = finalTime.replace( /<H>/g , time_H );
 finalTime = finalTime.replace( /<MM>/g , time_MM );
 finalTime = finalTime.replace( /<M>/g , time_M );
 finalTime = finalTime.replace( /<TM>/g , time_TM );
 finalTime = finalTime.replace( /<SS>/g , time_SS );
 finalTime = finalTime.replace( /<S>/g , time_S );
 finalTime = finalTime.replace( /<TS>/g , time_TS );
  	
 return finalTime;
}


Timer._updateTimer = function( indexID , finalTime ) {
 currentTimeLength = window.timers[indexID].element.firstChild.length;
 window.timers[indexID].element.firstChild.replaceData( 0 , currentTimeLength , finalTime );
}
