@oscarpalmer/timer 0.3.0 → 0.6.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/.eslintrc.json +1 -1
- package/README.md +1 -1
- package/dist/timer.js +52 -24
- package/package.json +6 -4
- package/src/index.d.ts +1 -17
- package/src/index.ts +95 -32
- package/test/index.html +24 -0
- package/test/test.js +121 -0
package/.eslintrc.json
CHANGED
package/README.md
CHANGED
package/dist/timer.js
CHANGED
|
@@ -7,51 +7,74 @@ var __publicField = (obj, key, value) => {
|
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
// src/index.ts
|
|
10
|
+
var milliseconds = 1e3 / 60;
|
|
10
11
|
var Timed = class {
|
|
11
|
-
constructor(callback, time, count) {
|
|
12
|
+
constructor(type, callback, time, count) {
|
|
13
|
+
__publicField(this, "callback");
|
|
14
|
+
__publicField(this, "count");
|
|
15
|
+
__publicField(this, "frame");
|
|
16
|
+
__publicField(this, "running", false);
|
|
17
|
+
__publicField(this, "time");
|
|
18
|
+
__publicField(this, "type");
|
|
19
|
+
if (typeof callback !== "function") {
|
|
20
|
+
throw new Error(`A ${type} timer must have a callback function`);
|
|
21
|
+
}
|
|
22
|
+
if (typeof time !== "number" || time < 0) {
|
|
23
|
+
throw new Error(`A ${type} timer must have a non-negative number as its time`);
|
|
24
|
+
}
|
|
25
|
+
if (type === "repeated" && (typeof count !== "number" || count < 2)) {
|
|
26
|
+
throw new Error(`A ${type} timer must have a number above 1 as its repeat count`);
|
|
27
|
+
}
|
|
28
|
+
this.type = type;
|
|
12
29
|
this.callback = callback;
|
|
13
30
|
this.time = time;
|
|
14
31
|
this.count = count;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
32
|
+
}
|
|
33
|
+
get active() {
|
|
34
|
+
return this.running;
|
|
18
35
|
}
|
|
19
36
|
restart() {
|
|
20
37
|
this.stop();
|
|
21
|
-
this.stopped = false;
|
|
22
38
|
Timed.run(this);
|
|
39
|
+
return this;
|
|
23
40
|
}
|
|
24
41
|
start() {
|
|
25
|
-
if (this.
|
|
26
|
-
|
|
42
|
+
if (this.running) {
|
|
43
|
+
return this;
|
|
27
44
|
}
|
|
45
|
+
Timed.run(this);
|
|
46
|
+
return this;
|
|
28
47
|
}
|
|
29
48
|
stop() {
|
|
30
|
-
this.
|
|
49
|
+
this.running = false;
|
|
31
50
|
if (typeof this.frame === "undefined") {
|
|
32
|
-
return;
|
|
51
|
+
return this;
|
|
33
52
|
}
|
|
34
53
|
window.cancelAnimationFrame(this.frame);
|
|
35
54
|
this.frame = void 0;
|
|
55
|
+
return this;
|
|
36
56
|
}
|
|
37
57
|
static run(timed) {
|
|
38
|
-
|
|
58
|
+
timed.running = true;
|
|
39
59
|
let count = 0;
|
|
40
60
|
let start;
|
|
41
61
|
function step(timestamp) {
|
|
42
|
-
if (timed.
|
|
62
|
+
if (!timed.running) {
|
|
43
63
|
return;
|
|
44
64
|
}
|
|
45
65
|
start ?? (start = timestamp);
|
|
46
66
|
const elapsed = timestamp - start;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
67
|
+
const elapsedMinimum = elapsed - milliseconds;
|
|
68
|
+
const elapsedMaximum = elapsed + milliseconds;
|
|
69
|
+
if (elapsedMinimum < timed.time && timed.time < elapsedMaximum) {
|
|
70
|
+
if (timed.running) {
|
|
71
|
+
timed.callback(timed.type === "repeated" ? count : void 0);
|
|
50
72
|
}
|
|
51
73
|
count += 1;
|
|
52
|
-
if (timed.
|
|
74
|
+
if (timed.type === "repeated" && count < timed.count) {
|
|
53
75
|
start = void 0;
|
|
54
76
|
} else {
|
|
77
|
+
timed.stop();
|
|
55
78
|
return;
|
|
56
79
|
}
|
|
57
80
|
}
|
|
@@ -61,19 +84,24 @@ var Timed = class {
|
|
|
61
84
|
}
|
|
62
85
|
};
|
|
63
86
|
var Repeated = class extends Timed {
|
|
87
|
+
constructor(callback, time, count) {
|
|
88
|
+
super("repeated", callback, time, count);
|
|
89
|
+
}
|
|
64
90
|
};
|
|
65
91
|
var Waited = class extends Timed {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
static repeat(callback, time, count) {
|
|
69
|
-
return new Repeated(callback, time, count);
|
|
70
|
-
}
|
|
71
|
-
static wait(callback, time) {
|
|
72
|
-
return new Waited(callback, time, 1);
|
|
92
|
+
constructor(callback, time) {
|
|
93
|
+
super("waited", callback, time, 1);
|
|
73
94
|
}
|
|
74
95
|
};
|
|
96
|
+
function repeat(callback, time, count) {
|
|
97
|
+
return new Repeated(callback, time, count).start();
|
|
98
|
+
}
|
|
99
|
+
function wait(callback, time) {
|
|
100
|
+
return new Waited(callback, time).start();
|
|
101
|
+
}
|
|
75
102
|
export {
|
|
76
103
|
Repeated,
|
|
77
|
-
|
|
78
|
-
|
|
104
|
+
Waited,
|
|
105
|
+
repeat,
|
|
106
|
+
wait
|
|
79
107
|
};
|
package/package.json
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
"@typescript-eslint/eslint-plugin": "^5.30.0",
|
|
11
11
|
"@typescript-eslint/parser": "^5.30.0",
|
|
12
12
|
"esbuild": "^0.14.0",
|
|
13
|
-
"eslint": "^8.
|
|
13
|
+
"eslint": "^8.20.0",
|
|
14
14
|
"eslint-config-xo": "^0.41.0",
|
|
15
|
-
"eslint-config-xo-typescript": "^0.
|
|
15
|
+
"eslint-config-xo-typescript": "^0.52.0",
|
|
16
16
|
"typescript": "^4.7.0"
|
|
17
17
|
},
|
|
18
18
|
"exports": {
|
|
@@ -37,9 +37,11 @@
|
|
|
37
37
|
"url": "git+https://github.com/oscarpalmer/timer.git"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
|
-
|
|
40
|
+
"build": "esbuild ./src/index.ts --bundle --target=es2020 --platform=browser --outfile=./dist/timer.js --format=esm",
|
|
41
41
|
"watch": "esbuild ./src/index.ts --bundle --target=es2020 --platform=browser --outfile=./dist/timer.js --format=esm --watch"
|
|
42
42
|
},
|
|
43
|
+
"type": "module",
|
|
44
|
+
"types": "src/index.d.ts",
|
|
43
45
|
"unpkg": "dist/timer.js",
|
|
44
|
-
"version": "0.
|
|
46
|
+
"version": "0.6.0"
|
|
45
47
|
}
|
package/src/index.d.ts
CHANGED
|
@@ -1,17 +1 @@
|
|
|
1
|
-
|
|
2
|
-
type WaitedCallback = () => void;
|
|
3
|
-
|
|
4
|
-
interface Timed<Callback> {
|
|
5
|
-
constructor(callback: Callback, time: number, count: number): void;
|
|
6
|
-
restart(): void;
|
|
7
|
-
start(): void;
|
|
8
|
-
stop(): void;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface Repeated extends Timed<RepeatedCallback> {}
|
|
12
|
-
export interface Waited extends Timed<WaitedCallback> {}
|
|
13
|
-
|
|
14
|
-
export interface Timer {
|
|
15
|
-
repeated(callback: RepeatedCallback, time: number, count: number): Repeated;
|
|
16
|
-
waited(callback: WaitedCallback, time: number): Waited;
|
|
17
|
-
}
|
|
1
|
+
export * from './index';
|
package/src/index.ts
CHANGED
|
@@ -1,53 +1,93 @@
|
|
|
1
1
|
type RepeatedCallback = (index: number) => void;
|
|
2
|
+
type TimerType = 'repeated' | 'waited';
|
|
2
3
|
type WaitedCallback = () => void;
|
|
3
4
|
|
|
5
|
+
const milliseconds = 1000 / 60;
|
|
6
|
+
|
|
4
7
|
abstract class Timed<Callback> {
|
|
8
|
+
private readonly callback: Callback;
|
|
9
|
+
private readonly count: number;
|
|
5
10
|
private frame: number | undefined;
|
|
6
|
-
private
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
private running = false;
|
|
12
|
+
private readonly time: number;
|
|
13
|
+
private readonly type: TimerType;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Is the timer active?
|
|
17
|
+
*/
|
|
18
|
+
get active(): boolean {
|
|
19
|
+
return this.running;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
protected constructor(type: TimerType, callback: Callback, time: number, count: number) {
|
|
23
|
+
if (typeof callback !== 'function') {
|
|
24
|
+
throw new Error(`A ${type} timer must have a callback function`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (typeof time !== 'number' || time < 0) {
|
|
28
|
+
throw new Error(`A ${type} timer must have a non-negative number as its time`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (type === 'repeated' && (typeof count !== 'number' || count < 2)) {
|
|
32
|
+
throw new Error(`A ${type} timer must have a number above 1 as its repeat count`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
this.type = type;
|
|
36
|
+
this.callback = callback;
|
|
37
|
+
this.time = time;
|
|
38
|
+
this.count = count;
|
|
14
39
|
}
|
|
15
40
|
|
|
16
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Restart timer
|
|
43
|
+
*/
|
|
44
|
+
restart(): this {
|
|
17
45
|
this.stop();
|
|
18
46
|
|
|
19
|
-
this
|
|
47
|
+
Timed.run(this as never);
|
|
20
48
|
|
|
21
|
-
|
|
49
|
+
return this;
|
|
22
50
|
}
|
|
23
51
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Start timer
|
|
54
|
+
*/
|
|
55
|
+
start(): this {
|
|
56
|
+
if (this.running) {
|
|
57
|
+
return this;
|
|
27
58
|
}
|
|
59
|
+
|
|
60
|
+
Timed.run(this as never);
|
|
61
|
+
|
|
62
|
+
return this;
|
|
28
63
|
}
|
|
29
64
|
|
|
30
|
-
|
|
31
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Stop timer
|
|
67
|
+
*/
|
|
68
|
+
stop(): this {
|
|
69
|
+
this.running = false;
|
|
32
70
|
|
|
33
71
|
if (typeof this.frame === 'undefined') {
|
|
34
|
-
return;
|
|
72
|
+
return this;
|
|
35
73
|
}
|
|
36
74
|
|
|
37
75
|
window.cancelAnimationFrame(this.frame);
|
|
38
76
|
|
|
39
77
|
this.frame = undefined;
|
|
78
|
+
|
|
79
|
+
return this;
|
|
40
80
|
}
|
|
41
81
|
|
|
42
82
|
private static run(timed: Timed<(index: number | undefined) => void>): void {
|
|
43
|
-
|
|
83
|
+
timed.running = true;
|
|
44
84
|
|
|
45
85
|
let count = 0;
|
|
46
86
|
|
|
47
87
|
let start: DOMHighResTimeStamp | undefined;
|
|
48
88
|
|
|
49
89
|
function step(timestamp: DOMHighResTimeStamp): void {
|
|
50
|
-
if (timed.
|
|
90
|
+
if (!timed.running) {
|
|
51
91
|
return;
|
|
52
92
|
}
|
|
53
93
|
|
|
@@ -55,16 +95,21 @@ abstract class Timed<Callback> {
|
|
|
55
95
|
|
|
56
96
|
const elapsed = timestamp - start;
|
|
57
97
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
98
|
+
const elapsedMinimum = elapsed - milliseconds;
|
|
99
|
+
const elapsedMaximum = elapsed + milliseconds;
|
|
100
|
+
|
|
101
|
+
if (elapsedMinimum < timed.time && timed.time < elapsedMaximum) {
|
|
102
|
+
if (timed.running) {
|
|
103
|
+
timed.callback(timed.type === 'repeated' ? count : undefined);
|
|
61
104
|
}
|
|
62
105
|
|
|
63
106
|
count += 1;
|
|
64
107
|
|
|
65
|
-
if (timed.
|
|
108
|
+
if (timed.type === 'repeated' && count < timed.count) {
|
|
66
109
|
start = undefined;
|
|
67
110
|
} else {
|
|
111
|
+
timed.stop();
|
|
112
|
+
|
|
68
113
|
return;
|
|
69
114
|
}
|
|
70
115
|
}
|
|
@@ -76,16 +121,34 @@ abstract class Timed<Callback> {
|
|
|
76
121
|
}
|
|
77
122
|
}
|
|
78
123
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return new Repeated(callback, time, count);
|
|
124
|
+
/**
|
|
125
|
+
* A repeated timer
|
|
126
|
+
*/
|
|
127
|
+
export class Repeated extends Timed<RepeatedCallback> {
|
|
128
|
+
constructor(callback: RepeatedCallback, time: number, count: number) {
|
|
129
|
+
super('repeated', callback, time, count);
|
|
86
130
|
}
|
|
131
|
+
}
|
|
87
132
|
|
|
88
|
-
|
|
89
|
-
|
|
133
|
+
/**
|
|
134
|
+
* A waited timer
|
|
135
|
+
*/
|
|
136
|
+
export class Waited extends Timed<WaitedCallback> {
|
|
137
|
+
constructor(callback: WaitedCallback, time: number) {
|
|
138
|
+
super('waited', callback, time, 1);
|
|
90
139
|
}
|
|
91
140
|
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Create and start a new repeated timer
|
|
144
|
+
*/
|
|
145
|
+
export function repeat(callback: RepeatedCallback, time: number, count: number): Repeated {
|
|
146
|
+
return (new Repeated(callback, time, count)).start();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Create and start a new waited timer
|
|
151
|
+
*/
|
|
152
|
+
export function wait(callback: WaitedCallback, time: number): Waited {
|
|
153
|
+
return (new Waited(callback, time)).start();
|
|
154
|
+
}
|
package/test/index.html
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>Mocha Tests</title>
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<link rel="stylesheet" href="https://unpkg.com/mocha/mocha.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="mocha"></div>
|
|
11
|
+
|
|
12
|
+
<script src="https://unpkg.com/chai/chai.js"></script>
|
|
13
|
+
<script src="https://unpkg.com/mocha/mocha.js"></script>
|
|
14
|
+
|
|
15
|
+
<script class="mocha-init">
|
|
16
|
+
mocha.setup('bdd');
|
|
17
|
+
mocha.checkLeaks();
|
|
18
|
+
</script>
|
|
19
|
+
<script src="test/test.js" type="module"></script>
|
|
20
|
+
<script class="mocha-exec">
|
|
21
|
+
mocha.run();
|
|
22
|
+
</script>
|
|
23
|
+
</body>
|
|
24
|
+
</html>
|
package/test/test.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import {repeat, wait, Repeated, Waited} from '../dist/timer.js';
|
|
2
|
+
|
|
3
|
+
describe('Functions', function () {
|
|
4
|
+
it('should create and start a repeated timer', function () {
|
|
5
|
+
chai.assert.ok(repeat(() => {}, 0, 2) instanceof Repeated);
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('should create and start a waited timer', function () {
|
|
9
|
+
chai.assert.ok(wait(() => {}, 0) instanceof Waited);
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe('Timer, Repeated', function () {
|
|
14
|
+
describe('Constructor', function () {
|
|
15
|
+
it('should create a proper repeated timer', function () {
|
|
16
|
+
chai.assert.ok(new Repeated(() => {}, 0, 2) instanceof Repeated);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should handle bad parameters', function () {
|
|
20
|
+
chai.assert.throws(function () { new Repeated(null, null, null); }, /callback/);
|
|
21
|
+
chai.assert.throws(function () { new Repeated(() => {}, null, null); }, /time/);
|
|
22
|
+
chai.assert.throws(function () { new Repeated(() => {}, 0, null); }, /count/);
|
|
23
|
+
chai.assert.throws(function () { new Repeated(() => {}, 0, 1); }, /count/);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('Run', function () {
|
|
28
|
+
it('should run as many times as set', function (done) {
|
|
29
|
+
let value = 0;
|
|
30
|
+
|
|
31
|
+
repeat(() => {
|
|
32
|
+
value += 1;
|
|
33
|
+
}, 25, 10);
|
|
34
|
+
|
|
35
|
+
wait(() => {
|
|
36
|
+
chai.assert.equal(value, 10);
|
|
37
|
+
|
|
38
|
+
done();
|
|
39
|
+
}, 500);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should run until canceled', function (done) {
|
|
43
|
+
let value = 0;
|
|
44
|
+
|
|
45
|
+
const repeated = repeat(() => {
|
|
46
|
+
value += 1;
|
|
47
|
+
}, 25, 10);
|
|
48
|
+
|
|
49
|
+
wait(() => {
|
|
50
|
+
repeated.stop();
|
|
51
|
+
}, 125);
|
|
52
|
+
|
|
53
|
+
wait(() => {
|
|
54
|
+
chai.assert.ok(value < 10);
|
|
55
|
+
|
|
56
|
+
done();
|
|
57
|
+
}, 500);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('Timer, Waited', function () {
|
|
63
|
+
describe('Constructor', function () {
|
|
64
|
+
it('should create a proper waited timer', function () {
|
|
65
|
+
chai.assert.ok(new Waited(() => {}, 0));
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should handle bad parameters', function () {
|
|
69
|
+
chai.assert.throws(function () { new Waited(null, null); }, /callback/);
|
|
70
|
+
chai.assert.throws(function () { new Waited(() => {}, null); }, /time/);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('Timer, Repeated & Waited', function () {
|
|
76
|
+
describe('Methods', function () {
|
|
77
|
+
it('should be able to start', function (done) {
|
|
78
|
+
(new Waited(() => {
|
|
79
|
+
done();
|
|
80
|
+
}, 125)).start();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should be able to stop', function (done) {
|
|
84
|
+
let value = 0;
|
|
85
|
+
|
|
86
|
+
const waited = new Waited(() => {
|
|
87
|
+
value = 1234;
|
|
88
|
+
}, 125);
|
|
89
|
+
|
|
90
|
+
wait(() => {
|
|
91
|
+
waited.stop();
|
|
92
|
+
}, 250);
|
|
93
|
+
|
|
94
|
+
wait(() => {
|
|
95
|
+
chai.assert.equal(value, 0);
|
|
96
|
+
|
|
97
|
+
done();
|
|
98
|
+
}, 375);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should be able to restart', function (done) {
|
|
102
|
+
let value = 0;
|
|
103
|
+
|
|
104
|
+
const waited = new Waited(() => {
|
|
105
|
+
value += 1;
|
|
106
|
+
}, 125);
|
|
107
|
+
|
|
108
|
+
waited.start();
|
|
109
|
+
|
|
110
|
+
wait(() => {
|
|
111
|
+
waited.restart();
|
|
112
|
+
}, 250);
|
|
113
|
+
|
|
114
|
+
wait(() => {
|
|
115
|
+
chai.assert.equal(value, 2);
|
|
116
|
+
|
|
117
|
+
done();
|
|
118
|
+
}, 500);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
});
|