Source: timer/Timer.js

//------------------------------------------------------------------------------
// Constructor scope
//------------------------------------------------------------------------------

/** 
 * Creates a new Timer object.
 * 
 * @constructor
 *
 * @param {Object} options Timer options.
 * 
 * @class
 * @classdesc
 * 
 * The Timer class represents a countdown timer. A Timer can call functions at 
 * pre-programmed times, for example at the start and end of the countdown. 
 * Timer objects are created via instances of the Timers class.
 */
rune.timer.Timer = function(options) {

    //--------------------------------------------------------------------------
    // Private properties
    //--------------------------------------------------------------------------
    
    /**
     * If the current Timer object is active (true) or not (false).
     * 
     * @type {boolean}
     * @private
     */
    this.m_active = false;
    
    /**
     * Timer options.
     * 
     * @type {rune.timer.TimerOptions}
     * @private
     */
    this.m_arguments = null;
    
    /**
     * If the Timer object has reached its end and is to be removed by 
     * the handler.
     * 
     * @type {boolean}
     * @private
     */
    this.m_disposed = false;
    
    /**
     * Elapsed time (in milliseconds).
     * 
     * @type {number}
     * @private
     */
    this.m_elapsed = 0.0;
    
    /**
     * If the Timer object is paused.
     * 
     * @type {boolean}
     * @private
     */
    this.m_paused = false;
    
    /**
     * The number of timer repetitions.
     * 
     * @type {number}
     * @private
     */
    this.m_repeats = 0;

    //--------------------------------------------------------------------------
    // Constructor call
    //--------------------------------------------------------------------------

    /**
     *  Invokes secondary class constructor.
     */
    this.m_construct(options);
};

//------------------------------------------------------------------------------
// Public getter and setter methods
//------------------------------------------------------------------------------

/**
 * If the current Timer object is active (true) or not (false).
 *
 * @member {boolean} active
 * @memberof rune.timer.Timer
 * @instance
 * @readonly
 */
Object.defineProperty(rune.timer.Timer.prototype, "active", {
    /**
     * @this rune.timer.Timer
     * @ignore
     */
    get : function() {
        return this.m_active;
    }
});

/**
 * If the Timer object has reached its end.
 *
 * @member {boolean} complete
 * @memberof rune.timer.Timer
 * @instance
 * @readonly
 */
Object.defineProperty(rune.timer.Timer.prototype, "complete", {
    /**
     * @this rune.timer.Timer
     * @ignore
     */
    get : function() {
        //@note: +1 because 1 corresponds to two ticks, 0 is one tick
        return (this.m_repeats >= this.m_arguments.repeat + 1);
    }
});

/**
 * Elapsed time (in milliseconds).
 *
 * @member {number} elapsed
 * @memberof rune.timer.Timer
 * @instance
 * @readonly
 */
Object.defineProperty(rune.timer.Timer.prototype, "elapsed", {
    /**
     * @this rune.timer.Timer
     * @ignore
     */
    get : function() {
        return this.m_elapsed;
    }
});

/**
 * If the Timer object has been completed and is about to be removed by 
 * the handler.
 *
 * @member {boolean} disposed
 * @memberof rune.timer.Timer
 * @instance
 * @readonly
 */
Object.defineProperty(rune.timer.Timer.prototype, "disposed", {
    /**
     * @this rune.timer.Timer
     * @ignore
     */
    get : function() {
        return this.m_disposed;
    }
});

/**
 * If the Timer object is paused.
 *
 * @member {boolean} paused
 * @memberof rune.timer.Timer
 * @instance
 */
Object.defineProperty(rune.timer.Timer.prototype, "paused", {
    /**
     * @this rune.timer.Timer
     * @ignore
     */
    get : function() {
        return this.m_paused;
    },
    /**
     * @this rune.timer.Timer
     * @ignore
     */
    set : function(value) {
        var a = value;
        var b = this.m_paused;
        
        this.m_paused = value;
        
        if      (a === true  && a != b) this.m_arguments.onPause.call(this.m_arguments.scope, this);
        else if (a === false && a != b) this.m_arguments.onUnpause.call(this.m_arguments.scope, this);
    }
});

/**
 * The progression of current iteration.
 *
 * @member {number} progressTick
 * @memberof rune.timer.Timer
 * @instance
 * @readonly
 */
Object.defineProperty(rune.timer.Timer.prototype, "progressTick", {
    /**
     * @this rune.timer.Timer
     * @ignore
     */
    get : function() {
        return Math.min((this.m_elapsed % this.m_arguments.duration) / this.m_arguments.duration, 1);
    }
});

/**
 * The progression of the total length.
 *
 * @member {number} progressTotal
 * @memberof rune.timer.Timer
 * @instance
 * @readonly
 */
Object.defineProperty(rune.timer.Timer.prototype, "progressTotal", {
    /**
     * @this rune.timer.Timer
     * @ignore
     */
    get : function() {
        return Math.min(this.m_elapsed / this.m_arguments['durationTotal'], 1);
    }
});

//------------------------------------------------------------------------------
// Public prototype methods (API)
//------------------------------------------------------------------------------

/**
 * Pauses the Timer object. When an object is paused, no time elapses, but the 
 * object is still classified as active. A Timer object can also be paused by 
 * changing the paused property to true.
 *
 * @returns {undefined}
 */
rune.timer.Timer.prototype.pause = function() {
    this.m_paused = true;
};

/**
 * Resets and restarts the Timer object.
 *
 * @returns {undefined}
 */
rune.timer.Timer.prototype.restart = function() {
    this.m_elapsed = 0.0;
};

/**
 * Resumes the Timer object. A Timer object can also be resumed by changing 
 * the paused property to false.
 *
 * @returns {undefined}
 */
rune.timer.Timer.prototype.resume = function() {
    this.m_paused = false;
};

/**
 * Starts the countdown of the Timer object, but only if the object is inactive.
 *
 * @returns {undefined}
 */
rune.timer.Timer.prototype.start = function() {
    if (this.m_active === false) {
        this.m_active = true;
        this.m_elapsed = 0.0;
    }
};

/**
 * Stops and resets the countdown of the Timer object.
 *
 * @returns {undefined}
 */
rune.timer.Timer.prototype.stop = function() {
    this.m_active = false;
    this.m_elapsed = 0.0;
};

//------------------------------------------------------------------------------
// Public prototype methods (Engine)
//------------------------------------------------------------------------------

/**
 * Updating the Timer object.
 *
 * @param {number} step Current time step.
 *
 * @returns {boolean}
 * @package
 * @ignore
 */
rune.timer.Timer.prototype.update = function(step) {
    if (this.m_paused === true || this.m_active === false) return false;
    this.m_updateElapsed(step);
    this.m_updateComplete(step);
    
    return this['complete'];
};

/**
 * Deletes the Timer object.
 *
 * @returns {undefined}
 * @package
 * @ignore
 */
rune.timer.Timer.prototype.dispose = function() {
    this.m_disposeTrigger();
    this.m_disposeArguments();
};

//------------------------------------------------------------------------------
// Protected prototype methods
//------------------------------------------------------------------------------

/**
 * The class constructor.
 *
 * @param {Object} options Timer options.
 *
 * @returns {undefined}
 * @protected
 * @ignore
 */
rune.timer.Timer.prototype.m_construct = function(options) {
    this.m_initArguments(options);
};

//------------------------------------------------------------------------------
// Private prototype methods
//------------------------------------------------------------------------------

/**
 * Creates the object's configuration object.
 *
 * @param {Object} options Timer options.
 *
 * @returns {undefined}
 * @private
 */
rune.timer.Timer.prototype.m_initArguments = function(options) {
    this.m_disposeArguments();
    if (this.m_arguments === null) {
        this.m_arguments = new rune.timer.TimerOptions(options);
    }
    
    this.m_arguments.onStart.call(this.m_arguments.scope, this);
};

/**
 * Updates the amount of elapsed time.
 *
 * @param {number} step Current time step.
 *
 * @returns {undefined}
 * @private
 */
rune.timer.Timer.prototype.m_updateElapsed = function(step) {
    this.m_elapsed += step;
};

/**
 * Checks whether the timer has reached the end.
 *
 * @param {number} step Current time step.
 *
 * @returns {undefined}
 * @private
 */
rune.timer.Timer.prototype.m_updateComplete = function(step) {
    if (this.m_arguments != null) {
        var repreats = parseInt(this.m_elapsed / this.m_arguments.duration, 10);
        if (repreats > this.m_repeats) {
            this.m_repeats = repreats;
            this.m_arguments.onTick.call(this.m_arguments.scope, this);
        }
        
        if (this.complete) this.m_arguments.onComplete.call(this.m_arguments.scope, this);
        else this.m_arguments.onUpdate.call(this.m_arguments.scope, this);
    }
};

/**
 * Calls the callback method when the object is aborted.
 *
 * @returns {undefined}
 * @private
 */
rune.timer.Timer.prototype.m_disposeTrigger = function() {
    if (this['complete'] === false && this.m_disposed === false) {
        this.m_arguments.onAbort.call(this.m_arguments.scope, this);
    }
    
    this.m_disposed = true;
};

/**
 * Deletes the Timer object's configuration object.
 *
 * @returns {undefined}
 * @private
 */
rune.timer.Timer.prototype.m_disposeArguments = function() {
    if (this.m_arguments instanceof rune.timer.TimerOptions) {
        this.m_arguments.dispose();
        this.m_arguments = null;
    }
};