/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2014
*/
/* Modified work copyright © 2015-2016 David Valdman */
define(function(require, exports, module) {
var tickQueue = require('./queues/tickQueue');
/**
* A collection of timing utilities meant to translate the familiar setInterval, setTimeout
* timers to use Samsara's internal clock, which is backed by a requestAnimationFrame (RAF) loop.
* It also includes other helpful methods for debouncing.
*
* @example
*
* Timer.setTimeout(function(){
* alert('I will execute after 1 second');
* }, 1000);
*
* Timer.after(function(){
* alert('I will execute on the following RAF loop');
* }, 1);
*
* var debouncedResize = Timer.debounce(function(){
* // this code will execute when the `resize` event
* // has stopped firing (for the last 200 milliseconds)
* }, 200);
*
* Engine.on('resize', function(){
* debounceResize();
* });
*
* @class Timer
* @static
*/
var Timer = {};
var getTime = (window.performance)
? function() { return window.performance.now(); }
: Date.now;
function _addTimerFunction(fn) {
tickQueue.push(fn);
return fn;
}
function _clearTimerFunction(fn){
var index = tickQueue.indexOf(fn);
if (index === -1) return;
tickQueue.splice(index, 1);
}
/**
* Wraps a function to be invoked after a certain amount of time.
* After a set duration has passed, it executes the function.
*
* @method setTimeout
* @static
* @param handler {Function} Function to be run after a specified duration
* @param duration {Number} Time to delay execution (in milliseconds)
* @return {Function}
*/
Timer.setTimeout = function setTimeout(handler, duration) {
var t = getTime();
var callback = function() {
var t2 = getTime();
if (t2 - t >= duration) {
handler.apply(this, arguments);
Timer.clear(callback);
}
};
return _addTimerFunction(callback);
};
/**
* Wraps a function to be invoked at repeated intervals.
*
* @method setInterval
* @static
* @param handler {Function} Function to be run at specified intervals
* @param interval {Number} Time interval (in milliseconds)
* @return {Function}
*/
Timer.setInterval = function setInterval(handler, duration) {
var t = getTime();
var callback = function() {
var t2 = getTime();
if (t2 - t >= duration) {
handler.apply(this, arguments);
t = getTime();
}
};
return _addTimerFunction(callback);
};
/**
* Wraps a function to be invoked after a specified number of Engine ticks.
*
* @method after
* @static
* @param handler {Function} Function to be executed
* @param numTicks {Number} Number of frames to delay execution
* @return {Function}
*/
Timer.after = function after(handler, numTicks) {
if (numTicks === undefined) return undefined;
var callback = function() {
numTicks--;
if (numTicks <= 0) { //in case numTicks is fraction or negative
handler.apply(this, arguments);
Timer.clear(callback);
}
};
return _addTimerFunction(callback);
};
/**
* Wraps a function to be invoked every specified number of Engine ticks.
*
* @method every
* @static
* @param handler {Function} Function to be executed
* @param numTicks {Number} Number of frames per execution
* @return {Function}
*/
Timer.every = function every(handler, numTicks) {
numTicks = numTicks || 1;
var initial = numTicks;
var callback = function() {
numTicks--;
if (numTicks <= 0) {
handler.apply(this, arguments);
numTicks = initial;
}
};
return _addTimerFunction(callback);
};
/**
* Cancel a timer.
*
* @method clear
* @static
* @param handler {Function} Handler
*/
Timer.clear = function clear(handler) {
_clearTimerFunction(handler);
};
/**
* Debounces a function for specified duration.
*
* @method debounce
* @static
* @param handler {Function} Handler
* @param duration {Number} Duration
* @return {Function}
*/
Timer.debounce = function debounce(handler, duration) {
var timeout;
return function() {
var args = arguments;
var fn = function() {
Timer.clear(timeout);
timeout = null;
handler.apply(this, args);
}.bind(this);
if (timeout) Timer.clear(timeout);
timeout = Timer.setTimeout(fn, duration);
};
};
/**
* Debounces a function for a specified number of Engine frames.
*
* @method frameDebounce
* @static
* @param handler {Function} Handler
* @param numFrames {Number} Number of frames
* @return {Function}
*/
Timer.frameDebounce = function frameDebounce(handler, numFrames){
var timeout;
return function() {
var args = arguments;
var fn = function() {
timeout = null;
handler.apply(this, args);
}.bind(this);
if (timeout) Timer.clear(timeout);
timeout = Timer.after(fn, numFrames);
};
};
module.exports = Timer;
});