@oscarpalmer/timer 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Timer
2
2
 
3
- [![npm version](https://badge.fury.io/js/@oscarpalmer%2Ftimer.svg)](https://badge.fury.io/js/@oscarpalmer%2Ftimer)
3
+ [![npm](https://badge.fury.io/js/@oscarpalmer%2Ftimer.svg)](https://www.npmjs.com/package/@oscarpalmer/timer) [![Tests](https://github.com/oscarpalmer/timer/actions/workflows/test.yml/badge.svg)](https://github.com/oscarpalmer/timer/actions/workflows/test.yml)
4
4
 
5
5
  A better solution for timeout- and interval-based timers.
6
6
 
@@ -1 +1,169 @@
1
- var Timer=(()=>{var c=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var g=(t,e)=>{for(var s in e)c(t,s,{get:e[s],enumerable:!0})},y=(t,e,s,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of w(e))!b.call(t,i)&&i!==s&&c(t,i,{get:()=>e[i],enumerable:!(n=v(e,i))||n.enumerable});return t};var x=t=>y(c({},"__esModule",{value:!0}),t);var E={};g(E,{Repeated:()=>r,Waited:()=>f,repeat:()=>T,wait:()=>A});var h=Math.round(16.666666666666668),l=globalThis.requestAnimationFrame??function(t){return setTimeout?.(()=>{t(Date.now())},h)};function p(t){t.state.active=!0,t.state.finished=!1;let e=t instanceof r,s=0,n;function i(a){if(!t.state.active)return;n??(n=a);let o=a-n,d=o-h,m=o+h;if(d<t.configuration.time&&t.configuration.time<m)if(t.state.active&&t.callbacks.default(e?s:void 0),s+=1,e&&s<t.configuration.count)n=void 0;else{t.state.finished=!0,t.stop();return}t.state.frame=l(i)}t.state.frame=l(i)}var u=class{get active(){return this.state.active}get finished(){return!this.active&&this.state.finished}constructor(e,s,n,i){let a=this instanceof r,o=a?"repeated":"waited";if(typeof e!="function")throw new TypeError(`A ${o} timer must have a callback function`);if(typeof s!="number"||s<0)throw new TypeError(`A ${o} timer must have a non-negative number as its time`);if(a&&(typeof n!="number"||n<2))throw new TypeError("A repeated timer must have a number above 1 as its repeat count");if(a&&i!==void 0&&typeof i!="function")throw new TypeError("A repeated timer's after-callback must be a function");this.configuration={count:n,time:s},this.callbacks={after:i,default:e},this.state={active:!1,finished:!1,frame:null}}restart(){return this.stop(),p(this),this}start(){return this.state.active||p(this),this}stop(){return this.state.active=!1,this.state.frame===void 0?this:((globalThis.cancelAnimationFrame??clearTimeout)?.(this.state.frame),this.callbacks.after?.(this.finished),this.state.frame=void 0,this)}},r=class extends u{},f=class extends u{constructor(e,s){super(e,s,1,null)}};function T(t,e,s,n){return new r(t,e,s,n).start()}function A(t,e){return new f(t,e).start()}return x(E);})();
1
+ var Timer = (function (exports) {
2
+ ('use strict');
3
+
4
+ const callbacks = new WeakMap();
5
+ const configuration = new WeakMap();
6
+ const state = new WeakMap();
7
+ const milliseconds = Math.round(1000 / 60);
8
+ const request =
9
+ requestAnimationFrame ??
10
+ function (callback) {
11
+ return setTimeout?.(() => {
12
+ callback(Date.now());
13
+ }, milliseconds);
14
+ };
15
+ function run(timed) {
16
+ const timedConfiguration = configuration.get(timed);
17
+ const timedCallbacks = callbacks.get(timed);
18
+ const timedState = state.get(timed);
19
+ timedState.active = true;
20
+ timedState.finished = false;
21
+ const isRepeated = timed instanceof Repeated;
22
+ let index = 0;
23
+ let start;
24
+ function step(timestamp) {
25
+ if (!timedState.active) {
26
+ return;
27
+ }
28
+ start ?? (start = timestamp);
29
+ const elapsed = timestamp - start;
30
+ const elapsedMinimum = elapsed - milliseconds;
31
+ const elapsedMaximum = elapsed + milliseconds;
32
+ if (
33
+ elapsedMinimum < timedConfiguration.time &&
34
+ timedConfiguration.time < elapsedMaximum
35
+ ) {
36
+ if (timedState.active) {
37
+ timedCallbacks.default(isRepeated ? index : undefined);
38
+ }
39
+ index += 1;
40
+ if (isRepeated && index < timedConfiguration.count) {
41
+ start = undefined;
42
+ } else {
43
+ timedState.finished = true;
44
+ timed.stop();
45
+ return;
46
+ }
47
+ }
48
+ timedState.frame = request(step);
49
+ }
50
+ timedState.frame = request(step);
51
+ }
52
+ class Timed {
53
+ get active() {
54
+ return state.get(this)?.active ?? false;
55
+ }
56
+ get finished() {
57
+ return !this.active && (state.get(this)?.finished ?? false);
58
+ }
59
+ /**
60
+ * @param {Callback} callback
61
+ * @param {number} time
62
+ * @param {number} count
63
+ * @param {AfterCallback=} afterCallback
64
+ */
65
+ constructor(callback, time, count, afterCallback) {
66
+ const isRepeated = this instanceof Repeated;
67
+ const type = isRepeated ? 'repeated' : 'waited';
68
+ if (typeof callback !== 'function') {
69
+ throw new TypeError(`A ${type} timer must have a callback function`);
70
+ }
71
+ if (typeof time !== 'number' || time < 0) {
72
+ throw new TypeError(
73
+ `A ${type} timer must have a non-negative number as its time`,
74
+ );
75
+ }
76
+ if (isRepeated && (typeof count !== 'number' || count < 2)) {
77
+ throw new TypeError(
78
+ 'A repeated timer must have a number above 1 as its repeat count',
79
+ );
80
+ }
81
+ if (
82
+ isRepeated &&
83
+ afterCallback !== undefined &&
84
+ typeof afterCallback !== 'function'
85
+ ) {
86
+ throw new TypeError(
87
+ "A repeated timer's after-callback must be a function",
88
+ );
89
+ }
90
+ callbacks.set(this, {
91
+ after: afterCallback,
92
+ default: callback,
93
+ });
94
+ configuration.set(this, {count, time});
95
+ state.set(this, {
96
+ active: false,
97
+ finished: false,
98
+ frame: undefined,
99
+ });
100
+ }
101
+ restart() {
102
+ this.stop();
103
+ run(this);
104
+ return this;
105
+ }
106
+ start() {
107
+ if (!this.active) {
108
+ run(this);
109
+ }
110
+ return this;
111
+ }
112
+ stop() {
113
+ const timedCallbacks = callbacks.get(this);
114
+ const timedState = state.get(this);
115
+ timedState.active = false;
116
+ if (timedState.frame === undefined) {
117
+ return this;
118
+ }
119
+ (cancelAnimationFrame ?? clearTimeout)?.(timedState.frame);
120
+ timedCallbacks.after?.(this.finished);
121
+ timedState.frame = undefined;
122
+ return this;
123
+ }
124
+ }
125
+ /**
126
+ * A timer that waits and runs repeatedly
127
+ */
128
+ class Repeated extends Timed {}
129
+ /**
130
+ * A timer that waits and runs once
131
+ */
132
+ class Waited extends Timed {
133
+ /**
134
+ * Creates a new waited timer
135
+ * @param {() => void} callback
136
+ * @param {number} time
137
+ */
138
+ constructor(callback, time) {
139
+ super(callback, time, 1);
140
+ }
141
+ }
142
+ /**
143
+ * Creates and starts a new repeated timer
144
+ * @param {RepeatedCallback} callback
145
+ * @param {number} time
146
+ * @param {number} count
147
+ * @param {AfterCallback=} afterCallback
148
+ * @return {Repeated}
149
+ */
150
+ function repeat(callback, time, count, afterCallback) {
151
+ return new Repeated(callback, time, count, afterCallback).start();
152
+ }
153
+ /**
154
+ * Creates and starts a new waited timer
155
+ * @param {() => void} callback
156
+ * @param {number} time
157
+ * @return {Waited}
158
+ */
159
+ function wait(callback, time) {
160
+ return new Waited(callback, time).start();
161
+ }
162
+
163
+ exports.Repeated = Repeated;
164
+ exports.Waited = Waited;
165
+ exports.repeat = repeat;
166
+ exports.wait = wait;
167
+
168
+ return exports;
169
+ })({});
package/dist/timer.js CHANGED
@@ -1,128 +1,170 @@
1
- // src/index.js
2
- var milliseconds = Math.round(1e3 / 60);
3
- var request = globalThis.requestAnimationFrame ?? function(callback) {
4
- return setTimeout?.(() => {
5
- callback(Date.now());
6
- }, milliseconds);
7
- };
1
+ /**
2
+ * @callback AfterCallback
3
+ * @param {boolean} finished Did the timer finish?
4
+ * @returns {void}
5
+ */
6
+ /**
7
+ * @callback RepeatedCallback
8
+ * @param {number} index The index of the current iteration
9
+ * @returns {void}
10
+ */
11
+ const callbacks = new WeakMap();
12
+ const configuration = new WeakMap();
13
+ const state = new WeakMap();
14
+ const milliseconds = Math.round(1000 / 60);
15
+ const request =
16
+ requestAnimationFrame ??
17
+ function (callback) {
18
+ return setTimeout?.(() => {
19
+ callback(Date.now());
20
+ }, milliseconds);
21
+ };
8
22
  function run(timed) {
9
- timed.state.active = true;
10
- timed.state.finished = false;
11
- const isRepeated = timed instanceof Repeated;
12
- let index = 0;
13
- let start;
14
- function step(timestamp) {
15
- if (!timed.state.active) {
16
- return;
17
- }
18
- start ?? (start = timestamp);
19
- const elapsed = timestamp - start;
20
- const elapsedMinimum = elapsed - milliseconds;
21
- const elapsedMaximum = elapsed + milliseconds;
22
- if (elapsedMinimum < timed.configuration.time && timed.configuration.time < elapsedMaximum) {
23
- if (timed.state.active) {
24
- timed.callbacks.default(isRepeated ? index : void 0);
25
- }
26
- index += 1;
27
- if (isRepeated && index < timed.configuration.count) {
28
- start = void 0;
29
- } else {
30
- timed.state.finished = true;
31
- timed.stop();
32
- return;
33
- }
34
- }
35
- timed.state.frame = request(step);
36
- }
37
- timed.state.frame = request(step);
23
+ const timedConfiguration = configuration.get(timed);
24
+ const timedCallbacks = callbacks.get(timed);
25
+ const timedState = state.get(timed);
26
+ timedState.active = true;
27
+ timedState.finished = false;
28
+ const isRepeated = timed instanceof Repeated;
29
+ let index = 0;
30
+ let start;
31
+ function step(timestamp) {
32
+ if (!timedState.active) {
33
+ return;
34
+ }
35
+ start ?? (start = timestamp);
36
+ const elapsed = timestamp - start;
37
+ const elapsedMinimum = elapsed - milliseconds;
38
+ const elapsedMaximum = elapsed + milliseconds;
39
+ if (
40
+ elapsedMinimum < timedConfiguration.time &&
41
+ timedConfiguration.time < elapsedMaximum
42
+ ) {
43
+ if (timedState.active) {
44
+ timedCallbacks.default(isRepeated ? index : undefined);
45
+ }
46
+ index += 1;
47
+ if (isRepeated && index < timedConfiguration.count) {
48
+ start = undefined;
49
+ } else {
50
+ timedState.finished = true;
51
+ timed.stop();
52
+ return;
53
+ }
54
+ }
55
+ timedState.frame = request(step);
56
+ }
57
+ timedState.frame = request(step);
38
58
  }
39
- var Timed = class {
40
- get active() {
41
- return this.state.active;
42
- }
43
- get finished() {
44
- return !this.active && this.state.finished;
45
- }
46
- /**
47
- * @param {RepeatedCallback} callback
48
- * @param {number} time
49
- * @param {number} count
50
- * @param {AfterCallback|undefined} afterCallback
51
- */
52
- constructor(callback, time, count, afterCallback) {
53
- const isRepeated = this instanceof Repeated;
54
- const type = isRepeated ? "repeated" : "waited";
55
- if (typeof callback !== "function") {
56
- throw new TypeError(`A ${type} timer must have a callback function`);
57
- }
58
- if (typeof time !== "number" || time < 0) {
59
- throw new TypeError(
60
- `A ${type} timer must have a non-negative number as its time`
61
- );
62
- }
63
- if (isRepeated && (typeof count !== "number" || count < 2)) {
64
- throw new TypeError(
65
- "A repeated timer must have a number above 1 as its repeat count"
66
- );
67
- }
68
- if (isRepeated && afterCallback !== void 0 && typeof afterCallback !== "function") {
69
- throw new TypeError(
70
- "A repeated timer's after-callback must be a function"
71
- );
72
- }
73
- this.configuration = { count, time };
74
- this.callbacks = {
75
- after: afterCallback,
76
- default: callback
77
- };
78
- this.state = {
79
- active: false,
80
- finished: false,
81
- frame: null
82
- };
83
- }
84
- restart() {
85
- this.stop();
86
- run(this);
87
- return this;
88
- }
89
- start() {
90
- if (!this.state.active) {
91
- run(this);
92
- }
93
- return this;
94
- }
95
- stop() {
96
- this.state.active = false;
97
- if (this.state.frame === void 0) {
98
- return this;
99
- }
100
- (globalThis.cancelAnimationFrame ?? clearTimeout)?.(this.state.frame);
101
- this.callbacks.after?.(this.finished);
102
- this.state.frame = void 0;
103
- return this;
104
- }
105
- };
106
- var Repeated = class extends Timed {
107
- };
108
- var Waited = class extends Timed {
109
- /**
110
- * @param {Function} callback
111
- * @param {number} time
112
- */
113
- constructor(callback, time) {
114
- super(callback, time, 1, null);
115
- }
116
- };
59
+ class Timed {
60
+ get active() {
61
+ return state.get(this)?.active ?? false;
62
+ }
63
+ get finished() {
64
+ return !this.active && (state.get(this)?.finished ?? false);
65
+ }
66
+ /**
67
+ * @param {Callback} callback
68
+ * @param {number} time
69
+ * @param {number} count
70
+ * @param {AfterCallback=} afterCallback
71
+ */
72
+ constructor(callback, time, count, afterCallback) {
73
+ const isRepeated = this instanceof Repeated;
74
+ const type = isRepeated ? 'repeated' : 'waited';
75
+ if (typeof callback !== 'function') {
76
+ throw new TypeError(`A ${type} timer must have a callback function`);
77
+ }
78
+ if (typeof time !== 'number' || time < 0) {
79
+ throw new TypeError(
80
+ `A ${type} timer must have a non-negative number as its time`,
81
+ );
82
+ }
83
+ if (isRepeated && (typeof count !== 'number' || count < 2)) {
84
+ throw new TypeError(
85
+ 'A repeated timer must have a number above 1 as its repeat count',
86
+ );
87
+ }
88
+ if (
89
+ isRepeated &&
90
+ afterCallback !== undefined &&
91
+ typeof afterCallback !== 'function'
92
+ ) {
93
+ throw new TypeError(
94
+ "A repeated timer's after-callback must be a function",
95
+ );
96
+ }
97
+ callbacks.set(this, {
98
+ after: afterCallback,
99
+ default: callback,
100
+ });
101
+ configuration.set(this, {count, time});
102
+ state.set(this, {
103
+ active: false,
104
+ finished: false,
105
+ frame: undefined,
106
+ });
107
+ }
108
+ restart() {
109
+ this.stop();
110
+ run(this);
111
+ return this;
112
+ }
113
+ start() {
114
+ if (!this.active) {
115
+ run(this);
116
+ }
117
+ return this;
118
+ }
119
+ stop() {
120
+ const timedCallbacks = callbacks.get(this);
121
+ const timedState = state.get(this);
122
+ timedState.active = false;
123
+ if (timedState.frame === undefined) {
124
+ return this;
125
+ }
126
+ (cancelAnimationFrame ?? clearTimeout)?.(timedState.frame);
127
+ timedCallbacks.after?.(this.finished);
128
+ timedState.frame = undefined;
129
+ return this;
130
+ }
131
+ }
132
+ /**
133
+ * A timer that waits and runs repeatedly
134
+ */
135
+ class Repeated extends Timed {}
136
+ /**
137
+ * A timer that waits and runs once
138
+ */
139
+ class Waited extends Timed {
140
+ /**
141
+ * Creates a new waited timer
142
+ * @param {() => void} callback
143
+ * @param {number} time
144
+ */
145
+ constructor(callback, time) {
146
+ super(callback, time, 1);
147
+ }
148
+ }
149
+ /**
150
+ * Creates and starts a new repeated timer
151
+ * @param {RepeatedCallback} callback
152
+ * @param {number} time
153
+ * @param {number} count
154
+ * @param {AfterCallback=} afterCallback
155
+ * @return {Repeated}
156
+ */
117
157
  function repeat(callback, time, count, afterCallback) {
118
- return new Repeated(callback, time, count, afterCallback).start();
158
+ return new Repeated(callback, time, count, afterCallback).start();
119
159
  }
160
+ /**
161
+ * Creates and starts a new waited timer
162
+ * @param {() => void} callback
163
+ * @param {number} time
164
+ * @return {Waited}
165
+ */
120
166
  function wait(callback, time) {
121
- return new Waited(callback, time).start();
167
+ return new Waited(callback, time).start();
122
168
  }
123
- export {
124
- Repeated,
125
- Waited,
126
- repeat,
127
- wait
128
- };
169
+
170
+ export {Repeated, Waited, repeat, wait};
package/package.json CHANGED
@@ -6,23 +6,18 @@
6
6
  "browser": "dist/timer.iife.js",
7
7
  "description": "A better solution for timeout- and interval-based timers.",
8
8
  "devDependencies": {
9
- "esbuild": "^0.17",
10
- "xo": "^0.54"
11
- },
12
- "exports": {
13
- ".": {
14
- "types": "./src/index.d.ts",
15
- "browser": "./dist/timer.iife.js",
16
- "module": "./dist/timer.js",
17
- "import": "./dist/timer.js",
18
- "require": "./dist/timer.cjs",
19
- "default": "./dist/timer.js"
20
- },
21
- "./package.json": "./package.json"
9
+ "@happy-dom/global-registrator": "^11.0",
10
+ "@rollup/plugin-typescript": "^11.1",
11
+ "bun": "^1.0",
12
+ "prettier": "^3.0",
13
+ "rollup": "^3.29",
14
+ "typescript": "^5.2",
15
+ "xo": "^0.56"
22
16
  },
23
17
  "files": [
24
18
  "dist",
25
- "src"
19
+ "src",
20
+ "types"
26
21
  ],
27
22
  "jsdelivr": "dist/timer.iife.js",
28
23
  "keywords": [
@@ -32,23 +27,42 @@
32
27
  "requestAnimationFrame"
33
28
  ],
34
29
  "license": "MIT",
35
- "main": "dist/timer.cjs",
30
+ "main": "dist/timer.iife.js",
36
31
  "module": "dist/timer.js",
37
32
  "name": "@oscarpalmer/timer",
33
+ "prettier": {
34
+ "arrowParens": "avoid",
35
+ "bracketSpacing": false,
36
+ "singleQuote": true,
37
+ "switchIndent": true,
38
+ "trailingComma": "all",
39
+ "useTabs": true
40
+ },
38
41
  "repository": {
39
42
  "type": "git",
40
43
  "url": "git+https://github.com/oscarpalmer/timer.git"
41
44
  },
42
45
  "scripts": {
43
- "build": "npm run build:cjs; npm run build:esm; npm run build:iife; npm run types",
44
- "build:cjs": "esbuild ./src/index.js --bundle --format=cjs --outfile=./dist/timer.cjs --target=es2020",
45
- "build:esm": "esbuild ./src/index.js --bundle --format=esm --outfile=./dist/timer.js --target=es2020",
46
- "build:iife": "esbuild ./src/index.js --bundle --minify --format=iife --global-name=Timer --outfile=./dist/timer.iife.js --target=es2020",
47
- "types": "./node_modules/.bin/tsc ./src/index.js --allowJs --declaration --emitDeclarationOnly --removeComments",
48
- "xo": "./node_modules/.bin/xo ./src/*.js --env browser"
46
+ "build": "npm run build:esm && npm run build:iife",
47
+ "build:esm": "rollup -c",
48
+ "build:iife": "rollup -c --environment ROLLUP_FORMAT:iife",
49
+ "test": "bun test --coverage",
50
+ "types": "tsc ./src/index.ts --outdir ./types --declaration --emitDeclarationOnly",
51
+ "watch": "rollup -c -w",
52
+ "xo": "xo ./src/*.ts --env browser"
49
53
  },
50
54
  "type": "module",
51
55
  "types": "src/index.d.ts",
52
56
  "unpkg": "dist/timer.iife.js",
53
- "version": "0.16.0"
54
- }
57
+ "version": "0.18.0",
58
+ "xo": {
59
+ "envs": [
60
+ "browser"
61
+ ],
62
+ "prettier": true,
63
+ "rules": {
64
+ "import/extensions": "off",
65
+ "import/no-cycle": "off"
66
+ }
67
+ }
68
+ }
package/src/index.ts ADDED
@@ -0,0 +1,239 @@
1
+ type Callbacks = {
2
+ after: AfterCallback | undefined;
3
+ default: (index?: number) => void;
4
+ };
5
+
6
+ type Configuration = {
7
+ count: number;
8
+ time: number;
9
+ };
10
+
11
+ /**
12
+ * @param {boolean} finished Did the timer finish?
13
+ */
14
+ export type AfterCallback = (finished: boolean) => void;
15
+
16
+ /**
17
+ * @param {number} index The index of the current iteration
18
+ */
19
+ export type RepeatedCallback = (index: number) => void;
20
+
21
+ type State = {
22
+ active: boolean;
23
+ finished: boolean;
24
+ frame?: DOMHighResTimeStamp;
25
+ };
26
+
27
+ const callbacks = new WeakMap<Timed<never, never>, Callbacks>();
28
+ const configuration = new WeakMap<Timed<never, never>, Configuration>();
29
+ const state = new WeakMap<Timed<never, never>, State>();
30
+
31
+ const milliseconds = Math.round(1000 / 60);
32
+
33
+ const request =
34
+ globalThis.requestAnimationFrame ??
35
+ function (callback) {
36
+ return setTimeout?.(() => {
37
+ callback(Date.now());
38
+ }, milliseconds);
39
+ };
40
+
41
+ function run(timed: Timed<never, never>): void {
42
+ const timedConfiguration = configuration.get(timed)!;
43
+ const timedCallbacks = callbacks.get(timed)!;
44
+ const timedState = state.get(timed)!;
45
+
46
+ timedState.active = true;
47
+ timedState.finished = false;
48
+
49
+ const isRepeated = timed instanceof Repeated;
50
+
51
+ let index = 0;
52
+
53
+ let start;
54
+
55
+ function step(timestamp: DOMHighResTimeStamp) {
56
+ if (!timedState.active) {
57
+ return;
58
+ }
59
+
60
+ start ??= timestamp;
61
+
62
+ const elapsed = timestamp - start;
63
+
64
+ const elapsedMinimum = elapsed - milliseconds;
65
+ const elapsedMaximum = elapsed + milliseconds;
66
+
67
+ if (
68
+ elapsedMinimum < timedConfiguration.time &&
69
+ timedConfiguration.time < elapsedMaximum
70
+ ) {
71
+ if (timedState.active) {
72
+ timedCallbacks.default(isRepeated ? index : undefined);
73
+ }
74
+
75
+ index += 1;
76
+
77
+ if (isRepeated && index < timedConfiguration.count) {
78
+ start = undefined;
79
+ } else {
80
+ timedState.finished = true;
81
+
82
+ timed.stop();
83
+
84
+ return;
85
+ }
86
+ }
87
+
88
+ timedState.frame = request(step) as never;
89
+ }
90
+
91
+ timedState.frame = request(step) as never;
92
+ }
93
+
94
+ class Timed<Type, Callback> {
95
+ get active(): boolean {
96
+ return state.get(this as never)?.active ?? false;
97
+ }
98
+
99
+ get finished(): boolean {
100
+ return !this.active && (state.get(this as never)?.finished ?? false);
101
+ }
102
+
103
+ /**
104
+ * @param {Callback} callback
105
+ * @param {number} time
106
+ * @param {number} count
107
+ * @param {AfterCallback=} afterCallback
108
+ */
109
+ constructor(
110
+ callback: () => void | ((index: number) => void),
111
+ time: number,
112
+ count: number,
113
+ afterCallback?: AfterCallback,
114
+ ) {
115
+ const isRepeated = this instanceof Repeated;
116
+
117
+ const type = isRepeated ? 'repeated' : 'waited';
118
+
119
+ if (typeof callback !== 'function') {
120
+ throw new TypeError(`A ${type} timer must have a callback function`);
121
+ }
122
+
123
+ if (typeof time !== 'number' || time < 0) {
124
+ throw new TypeError(
125
+ `A ${type} timer must have a non-negative number as its time`,
126
+ );
127
+ }
128
+
129
+ if (isRepeated && (typeof count !== 'number' || count < 2)) {
130
+ throw new TypeError(
131
+ 'A repeated timer must have a number above 1 as its repeat count',
132
+ );
133
+ }
134
+
135
+ if (
136
+ isRepeated &&
137
+ afterCallback !== undefined &&
138
+ typeof afterCallback !== 'function'
139
+ ) {
140
+ throw new TypeError(
141
+ "A repeated timer's after-callback must be a function",
142
+ );
143
+ }
144
+
145
+ callbacks.set(this as never, {
146
+ after: afterCallback,
147
+ default: callback,
148
+ });
149
+
150
+ configuration.set(this as never, {count, time});
151
+
152
+ state.set(this as never, {
153
+ active: false,
154
+ finished: false,
155
+ frame: undefined,
156
+ });
157
+ }
158
+
159
+ restart(): Type {
160
+ this.stop();
161
+
162
+ run(this as never);
163
+
164
+ return this as never;
165
+ }
166
+
167
+ start(): Type {
168
+ if (!this.active) {
169
+ run(this as never);
170
+ }
171
+
172
+ return this as never;
173
+ }
174
+
175
+ stop(): Type {
176
+ const timedCallbacks = callbacks.get(this as never)!;
177
+ const timedState = state.get(this as never)!;
178
+
179
+ timedState.active = false;
180
+
181
+ if (timedState.frame === undefined) {
182
+ return this as never;
183
+ }
184
+
185
+ (cancelAnimationFrame ?? clearTimeout)?.(timedState.frame);
186
+
187
+ timedCallbacks.after?.(this.finished);
188
+
189
+ timedState.frame = undefined;
190
+
191
+ return this as never;
192
+ }
193
+ }
194
+
195
+ /**
196
+ * A timer that waits and runs repeatedly
197
+ */
198
+ export class Repeated extends Timed<Repeated, RepeatedCallback> {}
199
+
200
+ /**
201
+ * A timer that waits and runs once
202
+ */
203
+ export class Waited extends Timed<Waited, () => void> {
204
+ /**
205
+ * Creates a new waited timer
206
+ * @param {() => void} callback
207
+ * @param {number} time
208
+ */
209
+ constructor(callback: () => void, time: number) {
210
+ super(callback, time, 1);
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Creates and starts a new repeated timer
216
+ * @param {RepeatedCallback} callback
217
+ * @param {number} time
218
+ * @param {number} count
219
+ * @param {AfterCallback=} afterCallback
220
+ * @return {Repeated}
221
+ */
222
+ export function repeat(
223
+ callback: RepeatedCallback,
224
+ time: number,
225
+ count: number,
226
+ afterCallback?: AfterCallback,
227
+ ): Repeated {
228
+ return new Repeated(callback as never, time, count, afterCallback).start();
229
+ }
230
+
231
+ /**
232
+ * Creates and starts a new waited timer
233
+ * @param {() => void} callback
234
+ * @param {number} time
235
+ * @return {Waited}
236
+ */
237
+ export function wait(callback: () => void, time: number): Waited {
238
+ return new Waited(callback, time).start();
239
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @param {boolean} finished Did the timer finish?
3
+ */
4
+ export type AfterCallback = (finished: boolean) => void;
5
+
6
+ /**
7
+ * @param {number} index The index of the current iteration
8
+ */
9
+ export type RepeatedCallback = (index: number) => void;
10
+
11
+ declare class Timed<Type, Callback> {
12
+ get active(): boolean;
13
+ get finished(): boolean;
14
+
15
+ /**
16
+ * @param {Callback} callback
17
+ * @param {number} time
18
+ * @param {number} count
19
+ * @param {AfterCallback=} afterCallback
20
+ */
21
+
22
+ constructor(
23
+ callback: () => void | ((index: number) => void),
24
+ time: number,
25
+ count: number,
26
+ afterCallback?: AfterCallback,
27
+ );
28
+
29
+ restart(): Type;
30
+ start(): Type;
31
+ stop(): Type;
32
+ }
33
+
34
+ /**
35
+ * A timer that waits and runs repeatedly
36
+ */
37
+ export declare class Repeated extends Timed<Repeated, RepeatedCallback> {}
38
+
39
+ /**
40
+ * A timer that waits and runs once
41
+ */
42
+ export declare class Waited extends Timed<Waited, () => void> {
43
+ /**
44
+ * Creates a new waited timer
45
+ * @param {() => void} callback
46
+ * @param {number} time
47
+ */
48
+ constructor(callback: () => void, time: number);
49
+ }
50
+
51
+ /**
52
+ * Creates and starts a new repeated timer
53
+ * @param {RepeatedCallback} callback
54
+ * @param {number} time
55
+ * @param {number} count
56
+ * @param {AfterCallback=} afterCallback
57
+ * @return {Repeated}
58
+ */
59
+ export declare function repeat(
60
+ callback: RepeatedCallback,
61
+ time: number,
62
+ count: number,
63
+ afterCallback?: AfterCallback,
64
+ ): Repeated;
65
+
66
+ /**
67
+ * Creates and starts a new waited timer
68
+ * @param {() => void} callback
69
+ * @param {number} time
70
+ * @return {Waited}
71
+ */
72
+ export declare function wait(callback: () => void, time: number): Waited;
package/dist/timer.cjs DELETED
@@ -1,148 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __export = (target, all) => {
6
- for (var name in all)
7
- __defProp(target, name, { get: all[name], enumerable: true });
8
- };
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") {
11
- for (let key of __getOwnPropNames(from))
12
- if (!__hasOwnProp.call(to, key) && key !== except)
13
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
- }
15
- return to;
16
- };
17
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
-
19
- // src/index.js
20
- var src_exports = {};
21
- __export(src_exports, {
22
- Repeated: () => Repeated,
23
- Waited: () => Waited,
24
- repeat: () => repeat,
25
- wait: () => wait
26
- });
27
- module.exports = __toCommonJS(src_exports);
28
- var milliseconds = Math.round(1e3 / 60);
29
- var request = globalThis.requestAnimationFrame ?? function(callback) {
30
- return setTimeout?.(() => {
31
- callback(Date.now());
32
- }, milliseconds);
33
- };
34
- function run(timed) {
35
- timed.state.active = true;
36
- timed.state.finished = false;
37
- const isRepeated = timed instanceof Repeated;
38
- let index = 0;
39
- let start;
40
- function step(timestamp) {
41
- if (!timed.state.active) {
42
- return;
43
- }
44
- start ?? (start = timestamp);
45
- const elapsed = timestamp - start;
46
- const elapsedMinimum = elapsed - milliseconds;
47
- const elapsedMaximum = elapsed + milliseconds;
48
- if (elapsedMinimum < timed.configuration.time && timed.configuration.time < elapsedMaximum) {
49
- if (timed.state.active) {
50
- timed.callbacks.default(isRepeated ? index : void 0);
51
- }
52
- index += 1;
53
- if (isRepeated && index < timed.configuration.count) {
54
- start = void 0;
55
- } else {
56
- timed.state.finished = true;
57
- timed.stop();
58
- return;
59
- }
60
- }
61
- timed.state.frame = request(step);
62
- }
63
- timed.state.frame = request(step);
64
- }
65
- var Timed = class {
66
- get active() {
67
- return this.state.active;
68
- }
69
- get finished() {
70
- return !this.active && this.state.finished;
71
- }
72
- /**
73
- * @param {RepeatedCallback} callback
74
- * @param {number} time
75
- * @param {number} count
76
- * @param {AfterCallback|undefined} afterCallback
77
- */
78
- constructor(callback, time, count, afterCallback) {
79
- const isRepeated = this instanceof Repeated;
80
- const type = isRepeated ? "repeated" : "waited";
81
- if (typeof callback !== "function") {
82
- throw new TypeError(`A ${type} timer must have a callback function`);
83
- }
84
- if (typeof time !== "number" || time < 0) {
85
- throw new TypeError(
86
- `A ${type} timer must have a non-negative number as its time`
87
- );
88
- }
89
- if (isRepeated && (typeof count !== "number" || count < 2)) {
90
- throw new TypeError(
91
- "A repeated timer must have a number above 1 as its repeat count"
92
- );
93
- }
94
- if (isRepeated && afterCallback !== void 0 && typeof afterCallback !== "function") {
95
- throw new TypeError(
96
- "A repeated timer's after-callback must be a function"
97
- );
98
- }
99
- this.configuration = { count, time };
100
- this.callbacks = {
101
- after: afterCallback,
102
- default: callback
103
- };
104
- this.state = {
105
- active: false,
106
- finished: false,
107
- frame: null
108
- };
109
- }
110
- restart() {
111
- this.stop();
112
- run(this);
113
- return this;
114
- }
115
- start() {
116
- if (!this.state.active) {
117
- run(this);
118
- }
119
- return this;
120
- }
121
- stop() {
122
- this.state.active = false;
123
- if (this.state.frame === void 0) {
124
- return this;
125
- }
126
- (globalThis.cancelAnimationFrame ?? clearTimeout)?.(this.state.frame);
127
- this.callbacks.after?.(this.finished);
128
- this.state.frame = void 0;
129
- return this;
130
- }
131
- };
132
- var Repeated = class extends Timed {
133
- };
134
- var Waited = class extends Timed {
135
- /**
136
- * @param {Function} callback
137
- * @param {number} time
138
- */
139
- constructor(callback, time) {
140
- super(callback, time, 1, null);
141
- }
142
- };
143
- function repeat(callback, time, count, afterCallback) {
144
- return new Repeated(callback, time, count, afterCallback).start();
145
- }
146
- function wait(callback, time) {
147
- return new Waited(callback, time).start();
148
- }
package/src/index.d.ts DELETED
@@ -1,31 +0,0 @@
1
- export function repeat(callback: RepeatedCallback, time: number, count: number, afterCallback: AfterCallback | undefined): Repeated;
2
- export function wait(callback: Function, time: number): Waited;
3
- export class Repeated extends Timed {
4
- }
5
- export class Waited extends Timed {
6
- constructor(callback: Function, time: number);
7
- }
8
- export type AfterCallback = (finished: boolean) => void;
9
- export type RepeatedCallback = (index: number) => void;
10
- declare class Timed {
11
- constructor(callback: RepeatedCallback, time: number, count: number, afterCallback: AfterCallback | undefined);
12
- get active(): boolean;
13
- get finished(): boolean;
14
- readonly configuration: {
15
- count: number;
16
- time: number;
17
- };
18
- readonly callbacks: {
19
- after: AfterCallback | undefined;
20
- default: RepeatedCallback;
21
- };
22
- readonly state: {
23
- active: boolean;
24
- finished: boolean;
25
- frame?: DOMHighResTimeStamp;
26
- };
27
- restart(): Timed;
28
- start(): Timed;
29
- stop(): Timed;
30
- }
31
- export {};
package/src/index.js DELETED
@@ -1,209 +0,0 @@
1
- /**
2
- * @callback AfterCallback
3
- * @param {boolean} finished
4
- * @returns {void}
5
- */
6
-
7
- /**
8
- * @callback RepeatedCallback
9
- * @param {number} index
10
- * @returns {void}
11
- */
12
-
13
- const milliseconds = Math.round(1000 / 60);
14
-
15
- const request = globalThis.requestAnimationFrame
16
- ?? function (callback) {
17
- return setTimeout?.(() => {
18
- callback(Date.now());
19
- }, milliseconds);
20
- };
21
-
22
- /**
23
- * @param {Timed} timed
24
- */
25
- function run(timed) {
26
- timed.state.active = true;
27
- timed.state.finished = false;
28
-
29
- const isRepeated = timed instanceof Repeated;
30
-
31
- let index = 0;
32
-
33
- let start;
34
-
35
- function step(timestamp) {
36
- if (!timed.state.active) {
37
- return;
38
- }
39
-
40
- start ??= timestamp;
41
-
42
- const elapsed = timestamp - start;
43
-
44
- const elapsedMinimum = elapsed - milliseconds;
45
- const elapsedMaximum = elapsed + milliseconds;
46
-
47
- if (
48
- elapsedMinimum < timed.configuration.time
49
- && timed.configuration.time < elapsedMaximum
50
- ) {
51
- if (timed.state.active) {
52
- timed.callbacks.default(isRepeated ? index : undefined);
53
- }
54
-
55
- index += 1;
56
-
57
- if (isRepeated && index < timed.configuration.count) {
58
- start = undefined;
59
- } else {
60
- timed.state.finished = true;
61
-
62
- timed.stop();
63
-
64
- return;
65
- }
66
- }
67
-
68
- timed.state.frame = request(step);
69
- }
70
-
71
- timed.state.frame = request(step);
72
- }
73
-
74
- class Timed {
75
- get active() {
76
- return this.state.active;
77
- }
78
-
79
- get finished() {
80
- return !this.active && this.state.finished;
81
- }
82
-
83
- /**
84
- * @param {RepeatedCallback} callback
85
- * @param {number} time
86
- * @param {number} count
87
- * @param {AfterCallback|undefined} afterCallback
88
- */
89
- constructor(callback, time, count, afterCallback) {
90
- const isRepeated = this instanceof Repeated;
91
-
92
- const type = isRepeated ? 'repeated' : 'waited';
93
-
94
- if (typeof callback !== 'function') {
95
- throw new TypeError(`A ${type} timer must have a callback function`);
96
- }
97
-
98
- if (typeof time !== 'number' || time < 0) {
99
- throw new TypeError(
100
- `A ${type} timer must have a non-negative number as its time`,
101
- );
102
- }
103
-
104
- if (isRepeated && (typeof count !== 'number' || count < 2)) {
105
- throw new TypeError(
106
- 'A repeated timer must have a number above 1 as its repeat count',
107
- );
108
- }
109
-
110
- if (
111
- isRepeated
112
- && afterCallback !== undefined
113
- && typeof afterCallback !== 'function'
114
- ) {
115
- throw new TypeError(
116
- 'A repeated timer\'s after-callback must be a function',
117
- );
118
- }
119
-
120
- /**
121
- * @readonly
122
- * @type {{count: number; time: number}}
123
- */
124
- this.configuration = {count, time};
125
-
126
- /**
127
- * @readonly
128
- * @type {{after: AfterCallback | undefined; default: RepeatedCallback}}
129
- */
130
- this.callbacks = {
131
- after: afterCallback,
132
- default: callback,
133
- };
134
-
135
- /**
136
- * @readonly
137
- * @type {{active: boolean; finished: boolean; frame?: DOMHighResTimeStamp}}
138
- */
139
- this.state = {
140
- active: false,
141
- finished: false,
142
- frame: null,
143
- };
144
- }
145
-
146
- restart() {
147
- this.stop();
148
-
149
- run(this);
150
-
151
- return this;
152
- }
153
-
154
- start() {
155
- if (!this.state.active) {
156
- run(this);
157
- }
158
-
159
- return this;
160
- }
161
-
162
- stop() {
163
- this.state.active = false;
164
-
165
- if (this.state.frame === undefined) {
166
- return this;
167
- }
168
-
169
- (globalThis.cancelAnimationFrame ?? clearTimeout)?.(this.state.frame);
170
-
171
- this.callbacks.after?.(this.finished);
172
-
173
- this.state.frame = undefined;
174
-
175
- return this;
176
- }
177
- }
178
-
179
- export class Repeated extends Timed {}
180
-
181
- export class Waited extends Timed {
182
- /**
183
- * @param {Function} callback
184
- * @param {number} time
185
- */
186
- constructor(callback, time) {
187
- super(callback, time, 1, null);
188
- }
189
- }
190
-
191
- /**
192
- * @param {RepeatedCallback} callback
193
- * @param {number} time
194
- * @param {number} count
195
- * @param {AfterCallback|undefined} afterCallback
196
- * @return {Repeated} A repeated timer
197
- */
198
- export function repeat(callback, time, count, afterCallback) {
199
- return new Repeated(callback, time, count, afterCallback).start();
200
- }
201
-
202
- /**
203
- * @param {Function} callback
204
- * @param {number} time
205
- * @return {Waited} A waited timer
206
- */
207
- export function wait(callback, time) {
208
- return new Waited(callback, time).start();
209
- }