/*
* This file is part of AUX.
*
* AUX is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* AUX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
/**
* @module utils/timer
*/
import { error } from './log.js';
import { typecheckFunction } from './typecheck.js';
/**
* This class represents a single timer. It is faster than manual setTimeout
* in situations in which the timer is regularly delayed while it is running.
*/
export class Timer {
/**
*
* @param callback - The callback to be called when the timer expires.
*/
constructor(callback) {
typecheckFunction(callback);
this.callback = callback;
this.id = void 0;
this.time = 0;
this._cb = () => {
const delta = this.time - performance.now();
this.id = void 0;
if (delta < 1) {
this.callback();
} else {
this.id = setTimeout(this._cb, delta);
}
};
}
/**
* Start the timer with a given offset. Same as restart().
*/
start(delta) {
this.restart(delta);
}
/**
* Set a new expiry time for the timer at the given number
* of milliseconds from now.
*
* @param delta - Offset in milliseconds.
*/
restart(delta) {
if (!(delta >= 0)) throw new TypeError('Expected non-negative number.');
const time = performance.now() + delta;
const id = this.id;
if (id !== void 0) {
if (time > this.time) {
this.time = time;
return;
}
clearTimeout(id);
}
this.time = time;
this.id = setTimeout(this._cb, delta);
}
get active() {
return this.id !== void 0;
}
/**
* Stop the timer.
*/
stop() {
const id = this.id;
if (id === void 0) return;
this.id = void 0;
clearTimeout(id);
}
}
export class ProximityTimers {
constructor(accuracy) {
this._target = 0;
this._calls = null;
this._accuracy = accuracy === void 0 ? 20 : accuracy;
}
/**
* Schedule a method to be run at the given point
* in the future.
*/
scheduleAt(callback, target) {
if (
this._calls !== null &&
Math.abs(this._target - target) < this._accuracy
) {
this._calls.push(callback);
} else {
const calls = [callback];
this._calls = calls;
this._target = target;
setTimeout(() => {
calls.forEach((cb) => {
try {
cb();
} catch (err) {
error(err);
}
});
}, performance.now() - target);
}
}
/**
* Schedule a method to be run in the given number of seconds.
*/
scheduleIn(callback, offset) {
this.scheduleAt(callback, offset + performance.now());
}
}
/**
* Initialize a timer from a given callback. Returns
* the timer handle.
*/
export function createTimer(callback) {
typecheckFunction(callback);
return callback;
}
/**
* Reschedules the timer for the given timer handle. Returns
* the new timer handle.
*/
export function startTimer(timer, delta) {
if (typeof timer === 'function') timer = new Timer(timer);
timer.start(delta);
return timer;
}
/**
* Stops the timer for the given timer handle. Returns
* the new timer handle.
*/
export function destroyTimer(timer) {
if (typeof timer === 'function') return timer;
timer.stop();
return timer.callback;
}
/**
* Cancels the timer for the given timer handle. Returns
* the new timer handle.
*/
export function cancelTimer(timer) {
if (typeof timer !== 'function') timer.stop();
return timer;
}