﻿/* Javascript Object Oriented Timer, version 1.0
* Copyright (C) 2008 zp bappi | zpbappi (at) gmail (dot) com
* Details and latest version at:
* http://abcoder.com/javascript/core_javascript/javascript_timer
*/

var Timer = function (millis, callback) { return this._init(millis, callback); }
Timer.prototype = { VERSION: 1.0, _init: function (millis, callback) { this._interval = 1000; this._timer = null; this._cbs = []; this._multipliers = []; this._tickCounts = []; this._canRun = []; this._stoppedThreads = 0; this._runOnce = false; this._startedAt = -1; this._pausedAt = -1; if (typeof (millis) == 'number') this._interval = millis; this.addCallback(callback); return this; }, _preset: function () { this._stoppedThreads = 0; this._startedAt = -1; this._pausedAt = -1; for (var i = 0; i < this._cbs.length; i++) { this._canRun[i] = true; this._tickCounts[i] = 0; } }, _ticks: function (initInterval) {
	var me = this; for (var i = 0; i < this._cbs.length; i++) {
		if (typeof (this._cbs[i]) == 'function' && this._canRun[i]) {
			this._tickCounts[i]++; if (this._tickCounts[i] == this._multipliers[i]) {
				this._tickCounts[i] = 0; if (this.runOnce()) { this._canRun[i] = false; this._stoppedThreads++; }
				window.setTimeout(me._cbs[i], 0);
			} 
		} 
	}
	if (this.runOnce() && this._stoppedThreads == this._cbs.length)
		this.stop(); if (typeof (initInterval) == 'number') { this.stop().start(null, true); } 
}, runOnce: function (isRunOnce) { if (typeof (isRunOnce) == 'undefined') return this._runOnce; else if (typeof (isRunOnce) == 'boolean') this._runOnce = isRunOnce; else alert("Invalid argument for runOnce(...).\n\nUsage: runOnce(true | false) /*Default value: false*/\nor, runOnce() to get status"); return this; }, interval: function (millis) { if (typeof (millis) == 'undefined') return this._interval; else if (typeof (millis) == 'number') this._interval = Math.floor(millis); return this; }, stop: function (isPausing) {
	if (this._timer) {
		if (!isPausing) this._pausedAt = -1; try { window.clearInterval(this._timer); }
		catch (ex) { }
		this._timer = null;
	}
	return this;
}, isStopped: function () { return ((this._timer == null) && !this.isPaused()); }, start: function (_initialInterval, _withoutPreset) {
	if (this.isPaused())
		return this.resume(); if (!this.isStopped())
		return this; if (!_withoutPreset)
		this._preset(); var tmpInterval = this._interval; if (typeof (_initialInterval) == 'number') tmpInterval = _initialInterval; var me = this; this._timer = window.setInterval(function () { me._ticks(_initialInterval); }, tmpInterval); this._startedAt = (new Date()).getTime(); this._startedAt -= (this._interval - tmpInterval); return this;
}, pause: function () {
	if (this._timer) { this._pausedAt = (new Date()).getTime(); this.stop(true); }
	return this;
}, isPaused: function () { return (this._pausedAt >= 0); }, resume: function () {
	if (this.isPaused()) { var tempInterval = this._interval - ((this._pausedAt - this._startedAt) % this._interval); this._pausedAt = -1; this.start(tempInterval, true); }
	return this;
}, restart: function () { return this.stop().start(); }, addCallback: function (callback, N) {
	if (typeof (callback) == 'function') {
		this._cbs.push(callback); if (typeof (N) == 'number') {
			N = Math.floor(N)
			this._multipliers.push((N < 1 ? 1 : N));
		}
		else
			this._multipliers.push(1); this._tickCounts.push(0); this._canRun.push(true);
	}
	return this;
}, clearCallbacks: function () { this._cbs.length = 0; this._multipliers.length = 0; this._canRun.length = 0; this._tickCounts.length = 0; this._stoppedThreads = 0; return this; } 
};
