@testgorilla/tgo-typing-test 1.0.0 → 2.0.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.
Files changed (134) hide show
  1. package/.eslintrc.json +46 -0
  2. package/jest.config.ts +21 -0
  3. package/ng-package.json +15 -0
  4. package/package.json +7 -22
  5. package/project.json +36 -0
  6. package/src/lib/components/tgo-typing-replay-input/tgo-typing-replay-input.component.html +30 -0
  7. package/src/lib/components/tgo-typing-replay-input/tgo-typing-replay-input.component.spec.ts +250 -0
  8. package/src/lib/components/tgo-typing-replay-input/tgo-typing-replay-input.component.ts +47 -0
  9. package/src/lib/components/tgo-typing-test/tgo-typing-test.component.html +72 -0
  10. package/src/lib/components/tgo-typing-test/tgo-typing-test.component.spec.ts +699 -0
  11. package/src/lib/components/tgo-typing-test/tgo-typing-test.component.ts +287 -0
  12. package/src/lib/helpers/config.ts +28 -0
  13. package/src/lib/helpers/constants/default-config.ts +103 -0
  14. package/src/lib/helpers/controllers/input-controller.ts +710 -0
  15. package/src/lib/helpers/controllers/quotes-controller.ts +183 -0
  16. package/src/lib/helpers/observables/banner-event.ts +18 -0
  17. package/src/lib/helpers/observables/config-event.ts +31 -0
  18. package/src/lib/helpers/observables/timer-event.ts +18 -0
  19. package/src/lib/helpers/states/active-page.ts +9 -0
  20. package/src/lib/helpers/states/composition.ts +29 -0
  21. package/src/lib/helpers/states/page-transition.ts +9 -0
  22. package/src/lib/helpers/states/slow-timer.ts +16 -0
  23. package/src/lib/helpers/states/test-active.ts +9 -0
  24. package/src/lib/helpers/states/time.ts +13 -0
  25. package/src/lib/helpers/test/caps-warning.ts +50 -0
  26. package/src/lib/helpers/test/caret.ts +92 -0
  27. package/src/lib/helpers/test/custom-text.ts +73 -0
  28. package/src/lib/helpers/test/english-punctuation.ts +38 -0
  29. package/src/lib/helpers/test/focus.ts +39 -0
  30. package/src/lib/helpers/test/manual-restart-tracker.ts +13 -0
  31. package/src/lib/helpers/test/out-of-focus.ts +19 -0
  32. package/src/lib/helpers/test/replay.ts +265 -0
  33. package/src/lib/helpers/test/test-input.ts +320 -0
  34. package/src/lib/helpers/test/test-logic.ts +1039 -0
  35. package/src/lib/helpers/test/test-state.ts +17 -0
  36. package/src/lib/helpers/test/test-stats.ts +442 -0
  37. package/src/lib/helpers/test/test-timer.ts +209 -0
  38. package/src/lib/helpers/test/test-ui.ts +370 -0
  39. package/src/lib/helpers/test/test-words.ts +72 -0
  40. package/src/lib/helpers/test/timer-progress.ts +16 -0
  41. package/src/lib/helpers/test/tts.ts +42 -0
  42. package/src/lib/helpers/test/weak-spot.ts +74 -0
  43. package/src/lib/helpers/test/wordset.ts +109 -0
  44. package/src/lib/styles/animations.scss +101 -0
  45. package/src/lib/styles/caret.scss +108 -0
  46. package/src/lib/styles/core.scss +498 -0
  47. package/src/lib/styles/index.scss +19 -0
  48. package/src/lib/styles/inputs.scss +290 -0
  49. package/src/lib/styles/popups.scss +1311 -0
  50. package/src/lib/styles/test.scss +1008 -0
  51. package/src/lib/styles/z_media-queries.scss +848 -0
  52. package/src/lib/types/types.d.ts +731 -0
  53. package/src/lib/utils/misc.ts +776 -0
  54. package/src/test-setup.ts +20 -0
  55. package/tsconfig.json +16 -0
  56. package/tsconfig.lib.json +14 -0
  57. package/tsconfig.lib.prod.json +9 -0
  58. package/tsconfig.spec.json +11 -0
  59. package/esm2022/index.mjs +0 -3
  60. package/esm2022/lib/components/tgo-typing-replay-input/tgo-typing-replay-input.component.mjs +0 -45
  61. package/esm2022/lib/components/tgo-typing-test/tgo-typing-test.component.mjs +0 -299
  62. package/esm2022/lib/helpers/config.mjs +0 -24
  63. package/esm2022/lib/helpers/constants/default-config.mjs +0 -103
  64. package/esm2022/lib/helpers/controllers/input-controller.mjs +0 -586
  65. package/esm2022/lib/helpers/controllers/quotes-controller.mjs +0 -118
  66. package/esm2022/lib/helpers/observables/config-event.mjs +0 -16
  67. package/esm2022/lib/helpers/observables/timer-event.mjs +0 -16
  68. package/esm2022/lib/helpers/states/active-page.mjs +0 -8
  69. package/esm2022/lib/helpers/states/composition.mjs +0 -20
  70. package/esm2022/lib/helpers/states/page-transition.mjs +0 -8
  71. package/esm2022/lib/helpers/states/slow-timer.mjs +0 -15
  72. package/esm2022/lib/helpers/states/test-active.mjs +0 -8
  73. package/esm2022/lib/helpers/states/time.mjs +0 -11
  74. package/esm2022/lib/helpers/test/caps-warning.mjs +0 -50
  75. package/esm2022/lib/helpers/test/caret.mjs +0 -80
  76. package/esm2022/lib/helpers/test/custom-text.mjs +0 -59
  77. package/esm2022/lib/helpers/test/english-punctuation.mjs +0 -29
  78. package/esm2022/lib/helpers/test/focus.mjs +0 -35
  79. package/esm2022/lib/helpers/test/manual-restart-tracker.mjs +0 -11
  80. package/esm2022/lib/helpers/test/out-of-focus.mjs +0 -14
  81. package/esm2022/lib/helpers/test/replay.mjs +0 -217
  82. package/esm2022/lib/helpers/test/test-input.mjs +0 -264
  83. package/esm2022/lib/helpers/test/test-logic.mjs +0 -927
  84. package/esm2022/lib/helpers/test/test-state.mjs +0 -13
  85. package/esm2022/lib/helpers/test/test-stats.mjs +0 -342
  86. package/esm2022/lib/helpers/test/test-timer.mjs +0 -207
  87. package/esm2022/lib/helpers/test/test-ui.mjs +0 -341
  88. package/esm2022/lib/helpers/test/test-words.mjs +0 -69
  89. package/esm2022/lib/helpers/test/timer-progress.mjs +0 -15
  90. package/esm2022/lib/helpers/test/weak-spot.mjs +0 -65
  91. package/esm2022/lib/helpers/test/wordset.mjs +0 -100
  92. package/esm2022/lib/utils/misc.mjs +0 -673
  93. package/esm2022/testgorilla-tgo-typing-test.mjs +0 -5
  94. package/fesm2022/testgorilla-tgo-typing-test.mjs +0 -4707
  95. package/fesm2022/testgorilla-tgo-typing-test.mjs.map +0 -1
  96. package/lib/components/tgo-typing-replay-input/tgo-typing-replay-input.component.d.ts +0 -14
  97. package/lib/components/tgo-typing-test/tgo-typing-test.component.d.ts +0 -54
  98. package/lib/helpers/config.d.ts +0 -98
  99. package/lib/helpers/constants/default-config.d.ts +0 -3
  100. package/lib/helpers/controllers/input-controller.d.ts +0 -16
  101. package/lib/helpers/controllers/quotes-controller.d.ts +0 -20
  102. package/lib/helpers/observables/config-event.d.ts +0 -5
  103. package/lib/helpers/observables/timer-event.d.ts +0 -4
  104. package/lib/helpers/states/active-page.d.ts +0 -2
  105. package/lib/helpers/states/composition.d.ts +0 -10
  106. package/lib/helpers/states/page-transition.d.ts +0 -2
  107. package/lib/helpers/states/slow-timer.d.ts +0 -3
  108. package/lib/helpers/states/test-active.d.ts +0 -2
  109. package/lib/helpers/states/time.d.ts +0 -3
  110. package/lib/helpers/test/caps-warning.d.ts +0 -5
  111. package/lib/helpers/test/caret.d.ts +0 -11
  112. package/lib/helpers/test/custom-text.d.ts +0 -16
  113. package/lib/helpers/test/english-punctuation.d.ts +0 -3
  114. package/lib/helpers/test/focus.d.ts +0 -7
  115. package/lib/helpers/test/manual-restart-tracker.d.ts +0 -3
  116. package/lib/helpers/test/out-of-focus.d.ts +0 -4
  117. package/lib/helpers/test/replay.d.ts +0 -20
  118. package/lib/helpers/test/test-input.d.ts +0 -86
  119. package/lib/helpers/test/test-logic.d.ts +0 -25
  120. package/lib/helpers/test/test-state.d.ts +0 -7
  121. package/lib/helpers/test/test-stats.d.ts +0 -92
  122. package/lib/helpers/test/test-timer.d.ts +0 -6
  123. package/lib/helpers/test/test-ui.d.ts +0 -27
  124. package/lib/helpers/test/test-words.d.ts +0 -23
  125. package/lib/helpers/test/timer-progress.d.ts +0 -3
  126. package/lib/helpers/test/weak-spot.d.ts +0 -3
  127. package/lib/helpers/test/wordset.d.ts +0 -7
  128. package/lib/utils/misc.d.ts +0 -81
  129. /package/{assets → src/assets}/typing-test-languages/english.json +0 -0
  130. /package/{assets → src/assets}/typing-test-languages/english_punctuation.json +0 -0
  131. /package/{assets → src/assets}/typing-test-languages/quotes/english_version_1.json +0 -0
  132. /package/{assets → src/assets}/typing-test-languages/quotes/english_version_2.json +0 -0
  133. /package/{assets → src/assets}/typing-test-languages/quotes/filtered_sources.json +0 -0
  134. /package/{index.d.ts → src/index.ts} +0 -0
@@ -0,0 +1,265 @@
1
+ import config from '../../helpers/config';
2
+
3
+ type ReplayAction =
4
+ | 'correctLetter'
5
+ | 'incorrectLetter'
6
+ | 'backWord'
7
+ | 'submitCorrectWord'
8
+ | 'submitErrorWord'
9
+ | 'setLetterIndex';
10
+
11
+ interface Replay {
12
+ action: ReplayAction;
13
+ value?: string | number;
14
+ time: number;
15
+ }
16
+
17
+ let wordsList: string[] = [];
18
+ let replayData: Replay[] = [];
19
+ let replayStartTime = 0;
20
+ let replayRecording = true;
21
+ let wordPos = 0;
22
+ let curPos = 0;
23
+ let targetWordPos = 0;
24
+ let targetCurPos = 0;
25
+ let timeoutList: NodeJS.Timeout[] = [];
26
+ let stopwatchList: NodeJS.Timeout[] = [];
27
+ let toggleButton: Element;
28
+ let replayStopwatch: HTMLElement;
29
+
30
+ function setToggleButton(toggleButtonElement: HTMLElement) {
31
+ toggleButton = toggleButtonElement?.children[0];
32
+ }
33
+
34
+ function setReplayStopwatch(replayStopwatchElement: HTMLElement) {
35
+ replayStopwatch = replayStopwatchElement;
36
+ }
37
+
38
+ function replayGetWordsList(wordsListFromScript: string[]): void {
39
+ wordsList = wordsListFromScript;
40
+ }
41
+
42
+ function initializeReplayPrompt(): void {
43
+ const replayWordsElement = document.getElementById('replayWords');
44
+
45
+ if (replayWordsElement === null) return;
46
+
47
+ replayWordsElement.innerHTML = '';
48
+ let wordCount = 0;
49
+ replayData.forEach(item => {
50
+ if (item.action === 'backWord') {
51
+ wordCount--;
52
+ } else if (item.action === 'submitCorrectWord' || item.action === 'submitErrorWord') {
53
+ wordCount++;
54
+ }
55
+ });
56
+ wordsList.forEach((item, i) => {
57
+ if (i > wordCount) return;
58
+ const x = document.createElement('div');
59
+ x.className = 'word';
60
+ for (i = 0; i < item.length; i++) {
61
+ const letter = document.createElement('letter');
62
+ letter.innerHTML = item[i];
63
+ x.appendChild(letter);
64
+ }
65
+ replayWordsElement.appendChild(x);
66
+ if (config.funbox === 'tenKeyMode') {
67
+ const y = document.createElement('div');
68
+ y.className = 'newline';
69
+ replayWordsElement.appendChild(y);
70
+ }
71
+ });
72
+ }
73
+
74
+ export function pauseReplay(): void {
75
+ timeoutList.forEach(item => {
76
+ clearTimeout(item);
77
+ });
78
+ timeoutList = [];
79
+ stopwatchList.forEach(item => {
80
+ clearTimeout(item);
81
+ });
82
+ stopwatchList = [];
83
+ targetCurPos = curPos;
84
+ targetWordPos = wordPos;
85
+
86
+ if (toggleButton === undefined) return;
87
+
88
+ toggleButton.className = 'Play';
89
+ (toggleButton.parentNode as Element)?.setAttribute('aria-label', 'Resume replay');
90
+ }
91
+
92
+ function handleDisplayLogic(item: Replay, nosound = false): void {
93
+ let activeWord = document.getElementById('replayWords')?.children[wordPos];
94
+
95
+ if (activeWord === undefined) return;
96
+ if (activeWord.className == 'newline') {
97
+ curPos = 0;
98
+ wordPos++;
99
+ activeWord = document.getElementById('replayWords')?.children[wordPos];
100
+ if (activeWord === undefined) return;
101
+ }
102
+ if (item.action === 'correctLetter') {
103
+ // if (!nosound) playSound();
104
+ activeWord.children[curPos].classList.add('correct');
105
+ curPos++;
106
+ } else if (item.action === 'incorrectLetter') {
107
+ // if (!nosound) playSound(true);
108
+ let myElement;
109
+ if (curPos >= activeWord.children.length) {
110
+ //if letter is an extra
111
+ myElement = document.createElement('letter');
112
+ myElement.classList.add('extra');
113
+ myElement.innerHTML = item.value?.toString() ?? '';
114
+ activeWord.appendChild(myElement);
115
+ }
116
+ myElement = activeWord.children[curPos];
117
+ myElement.classList.add('incorrect');
118
+ curPos++;
119
+ } else if (item.action === 'setLetterIndex' && typeof item.value === 'number') {
120
+ curPos = item.value;
121
+ // remove all letters from cursor to end of word
122
+ for (const myElement of Array.from(activeWord.children).slice(curPos)) {
123
+ if (myElement.classList.contains('extra')) {
124
+ myElement.remove();
125
+ } else {
126
+ myElement.className = '';
127
+ }
128
+ }
129
+ } else if (item.action === 'submitCorrectWord') {
130
+ wordPos++;
131
+ curPos = 0;
132
+ } else if (item.action === 'submitErrorWord') {
133
+ activeWord.classList.add('error');
134
+ wordPos++;
135
+ curPos = 0;
136
+ } else if (item.action === 'backWord') {
137
+ wordPos--;
138
+
139
+ const replayWords = document.getElementById('replayWords');
140
+
141
+ if (replayWords !== null) activeWord = replayWords.children[wordPos];
142
+
143
+ curPos = activeWord.children.length;
144
+ while (activeWord.children[curPos - 1].className === '') curPos--;
145
+ activeWord.classList.remove('error');
146
+ }
147
+ }
148
+
149
+ function loadOldReplay(): number {
150
+ let startingIndex = 0;
151
+ curPos = 0;
152
+ wordPos = 0;
153
+ replayData.forEach((item, i) => {
154
+ if (wordPos < targetWordPos || (wordPos === targetWordPos && curPos < targetCurPos)) {
155
+ //quickly display everything up to the target
156
+ handleDisplayLogic(item, true);
157
+ startingIndex = i + 1;
158
+ }
159
+ });
160
+ const time = Math.floor((replayData[startingIndex]?.time || 0) / 1000);
161
+ replayStopwatch.innerText = time + 's';
162
+ return startingIndex;
163
+ }
164
+
165
+ function startReplayRecording(): void {
166
+ replayData = [];
167
+ replayStartTime = performance.now();
168
+ replayRecording = true;
169
+ targetCurPos = 0;
170
+ targetWordPos = 0;
171
+ }
172
+
173
+ function stopReplayRecording(): void {
174
+ replayRecording = false;
175
+ }
176
+
177
+ function addReplayEvent(action: ReplayAction, value?: number | string): void {
178
+ if (!replayRecording) {
179
+ return;
180
+ }
181
+
182
+ const timeDelta = performance.now() - replayStartTime;
183
+ replayData.push({ action: action, value: value, time: timeDelta });
184
+ }
185
+
186
+ function playReplay(): void {
187
+ curPos = 0;
188
+ wordPos = 0;
189
+
190
+ if (toggleButton === undefined) return;
191
+
192
+ toggleButton.className = 'Pause';
193
+ (toggleButton.parentNode as Element)?.setAttribute('aria-label', 'Pause replay');
194
+ initializeReplayPrompt();
195
+ const startingIndex = loadOldReplay();
196
+ const lastTime = replayData[startingIndex]?.time || 0;
197
+ let swTime = Math.round(lastTime / 1000); //starting time
198
+ const swEndTime = Math.round((replayData[replayData.length - 1]?.time || 0) / 1000);
199
+ while (swTime <= swEndTime) {
200
+ const time = swTime;
201
+ stopwatchList.push(
202
+ setTimeout(
203
+ () => {
204
+ replayStopwatch.innerText = time + 's';
205
+ },
206
+ time * 1000 - lastTime
207
+ )
208
+ );
209
+ swTime++;
210
+ }
211
+ replayData.forEach((item, i) => {
212
+ if (i < startingIndex) return;
213
+ timeoutList.push(
214
+ setTimeout(() => {
215
+ handleDisplayLogic(item);
216
+ }, item.time - lastTime)
217
+ );
218
+ });
219
+ timeoutList.push(
220
+ setTimeout(
221
+ () => {
222
+ //after the replay has finished, this will run
223
+ targetCurPos = 0;
224
+ targetWordPos = 0;
225
+ toggleButton.className = 'Play';
226
+ (toggleButton.parentNode as Element).setAttribute('aria-label', 'Start replay');
227
+ },
228
+ (replayData[replayData.length - 1]?.time || 0) - lastTime
229
+ )
230
+ );
231
+ }
232
+
233
+ function getReplayExport(): string {
234
+ return JSON.stringify({
235
+ replayData: replayData,
236
+ wordsList: wordsList,
237
+ });
238
+ }
239
+
240
+ function setReplayData(data: Replay[]) {
241
+ replayData = data;
242
+ }
243
+
244
+ function playpauseReplayButtonClick() {
245
+ if (toggleButton?.className === 'Play') {
246
+ playReplay();
247
+ } else if (toggleButton?.className === 'Pause') {
248
+ pauseReplay();
249
+ }
250
+ }
251
+
252
+ export {
253
+ addReplayEvent,
254
+ getReplayExport,
255
+ initializeReplayPrompt,
256
+ loadOldReplay,
257
+ playReplay,
258
+ playpauseReplayButtonClick,
259
+ replayGetWordsList,
260
+ setReplayData,
261
+ setReplayStopwatch,
262
+ setToggleButton,
263
+ startReplayRecording,
264
+ stopReplayRecording,
265
+ };
@@ -0,0 +1,320 @@
1
+ import * as TestWords from './test-words';
2
+ import { roundTo2 } from '../../utils/misc';
3
+
4
+ interface Keypress {
5
+ count: number;
6
+ errors: number;
7
+ words: number[];
8
+ afk: boolean;
9
+ }
10
+
11
+ interface KeypressTimings {
12
+ spacing: {
13
+ current: number;
14
+ array: number[] | 'toolong';
15
+ };
16
+ duration: {
17
+ current: number;
18
+ array: number[] | 'toolong';
19
+ };
20
+ }
21
+
22
+ class Input {
23
+ current: string;
24
+ history: string[];
25
+ historyLength: number;
26
+ length: number;
27
+ constructor() {
28
+ this.current = '';
29
+ this.history = [];
30
+ this.historyLength = 0;
31
+ this.length = 0;
32
+ }
33
+
34
+ reset(): void {
35
+ this.current = '';
36
+ this.history = [];
37
+ this.length = 0;
38
+ }
39
+
40
+ resetHistory(): void {
41
+ this.history = [];
42
+ this.length = 0;
43
+ }
44
+
45
+ setCurrent(val: string): void {
46
+ this.current = val;
47
+ this.length = this.current.length;
48
+ }
49
+
50
+ appendCurrent(val: string): void {
51
+ this.current += val;
52
+ this.length = this.current.length;
53
+ }
54
+
55
+ resetCurrent(): void {
56
+ this.current = '';
57
+ }
58
+
59
+ getCurrent(): string {
60
+ return this.current;
61
+ }
62
+
63
+ pushHistory(): void {
64
+ this.history.push(this.current);
65
+ this.historyLength = this.history.length;
66
+ this.resetCurrent();
67
+ }
68
+
69
+ popHistory(): string {
70
+ const ret = this.history.pop() ?? '';
71
+ this.historyLength = this.history.length;
72
+ return ret;
73
+ }
74
+
75
+ getHistory(i?: number): string | string[] {
76
+ if (i === undefined) {
77
+ return this.history;
78
+ } else {
79
+ return this.history[i];
80
+ }
81
+ }
82
+
83
+ getHistoryLast(): string | undefined {
84
+ return this.history[this.history.length - 1];
85
+ }
86
+ }
87
+
88
+ class Corrected {
89
+ current: string;
90
+ history: string[];
91
+ constructor() {
92
+ this.current = '';
93
+ this.history = [];
94
+ }
95
+ setCurrent(val: string): void {
96
+ this.current = val;
97
+ }
98
+
99
+ appendCurrent(val: string): void {
100
+ this.current += val;
101
+ }
102
+
103
+ resetCurrent(): void {
104
+ this.current = '';
105
+ }
106
+
107
+ resetHistory(): void {
108
+ this.history = [];
109
+ }
110
+
111
+ reset(): void {
112
+ this.resetCurrent();
113
+ this.resetHistory();
114
+ }
115
+
116
+ getHistory(i: number): string {
117
+ return this.history[i];
118
+ }
119
+
120
+ popHistory(): string {
121
+ return this.history.pop() ?? '';
122
+ }
123
+
124
+ pushHistory(): void {
125
+ this.history.push(this.current);
126
+ this.current = '';
127
+ }
128
+ }
129
+
130
+ export const input = new Input();
131
+ export const corrected = new Corrected();
132
+
133
+ export let keypressPerSecond: Keypress[] = [];
134
+ export let currentKeypress: Keypress = {
135
+ count: 0,
136
+ errors: 0,
137
+ words: [],
138
+ afk: true,
139
+ };
140
+ export let lastKeypress: number;
141
+ export let currentBurstStart = 0;
142
+ export let missedWords: {
143
+ [word: string]: number;
144
+ } = {};
145
+ export let accuracy = {
146
+ correct: 0,
147
+ incorrect: 0,
148
+ };
149
+ export let keypressTimings: KeypressTimings = {
150
+ spacing: {
151
+ current: -1,
152
+ array: [],
153
+ },
154
+ duration: {
155
+ current: -1,
156
+ array: [],
157
+ },
158
+ };
159
+ export let wpmHistory: number[] = [];
160
+ export let rawHistory: number[] = [];
161
+ export let burstHistory: number[] = [];
162
+ export let bailout = false;
163
+ export function setBailout(tf: boolean): void {
164
+ bailout = tf;
165
+ }
166
+
167
+ export let spacingDebug = false;
168
+ export function enableSpacingDebug(): void {
169
+ spacingDebug = true;
170
+ console.clear();
171
+ }
172
+
173
+ export function updateLastKeypress(): void {
174
+ lastKeypress = performance.now();
175
+ }
176
+
177
+ export function incrementKeypressCount(): void {
178
+ currentKeypress.count++;
179
+ }
180
+
181
+ export function setKeypressNotAfk(): void {
182
+ currentKeypress.afk = false;
183
+ }
184
+
185
+ export function incrementKeypressErrors(): void {
186
+ currentKeypress.errors++;
187
+ }
188
+
189
+ export function pushKeypressWord(wordIndex: number): void {
190
+ currentKeypress.words.push(wordIndex);
191
+ }
192
+
193
+ export function setBurstStart(time: number): void {
194
+ currentBurstStart = time;
195
+ }
196
+
197
+ export function pushKeypressesToHistory(): void {
198
+ keypressPerSecond.push(currentKeypress);
199
+ currentKeypress = {
200
+ count: 0,
201
+ errors: 0,
202
+ words: [],
203
+ afk: true,
204
+ };
205
+ }
206
+
207
+ export function incrementAccuracy(correctincorrect: boolean): void {
208
+ if (correctincorrect) {
209
+ accuracy.correct++;
210
+ } else {
211
+ accuracy.incorrect++;
212
+ }
213
+ }
214
+
215
+ export function setKeypressTimingsTooLong(): void {
216
+ keypressTimings.spacing.array = 'toolong';
217
+ keypressTimings.duration.array = 'toolong';
218
+ }
219
+
220
+ export function pushKeypressDuration(val: number): void {
221
+ (keypressTimings.duration.array as number[]).push(roundTo2(val));
222
+ }
223
+
224
+ export function setKeypressDuration(val: number): void {
225
+ keypressTimings.duration.current = roundTo2(val);
226
+ }
227
+
228
+ function pushKeypressSpacing(val: number): void {
229
+ (keypressTimings.spacing.array as number[]).push(roundTo2(val));
230
+ }
231
+
232
+ function setKeypressSpacing(val: number): void {
233
+ keypressTimings.spacing.current = roundTo2(val);
234
+ }
235
+
236
+ export function recordKeypressSpacing(): void {
237
+ const now = performance.now();
238
+ const diff = Math.abs(keypressTimings.spacing.current - now);
239
+ if (keypressTimings.spacing.current !== -1) {
240
+ pushKeypressSpacing(diff);
241
+ if (spacingDebug) {
242
+ console.log('spacing debug', 'push', diff, 'length', keypressTimings.spacing.array.length);
243
+ }
244
+ }
245
+ setKeypressSpacing(now);
246
+ if (spacingDebug) {
247
+ console.log('spacing debug', 'set', now, 'length', keypressTimings.spacing.array.length);
248
+ }
249
+ if (spacingDebug) {
250
+ console.log('spacing debug', 'recorded', 'length', keypressTimings.spacing.array.length);
251
+ }
252
+ }
253
+
254
+ export function resetKeypressTimings(): void {
255
+ keypressTimings = {
256
+ spacing: {
257
+ current: performance.now(),
258
+ array: [],
259
+ },
260
+ duration: {
261
+ current: performance.now(),
262
+ array: [],
263
+ },
264
+ };
265
+ if (spacingDebug) console.clear();
266
+ }
267
+
268
+ export function pushMissedWord(word: string): void {
269
+ if (!Object.keys(missedWords).includes(word)) {
270
+ missedWords[word] = 1;
271
+ } else {
272
+ missedWords[word]++;
273
+ }
274
+ }
275
+
276
+ export function pushToWpmHistory(wpm: number): void {
277
+ wpmHistory.push(wpm);
278
+ }
279
+
280
+ export function pushToRawHistory(raw: number): void {
281
+ rawHistory.push(raw);
282
+ }
283
+
284
+ export function pushBurstToHistory(speed: number): void {
285
+ if (burstHistory[TestWords.words.currentIndex] === undefined) {
286
+ burstHistory.push(speed);
287
+ } else {
288
+ //repeated word - override
289
+ burstHistory[TestWords.words.currentIndex] = speed;
290
+ }
291
+ }
292
+
293
+ export function restart(): void {
294
+ wpmHistory = [];
295
+ rawHistory = [];
296
+ burstHistory = [];
297
+ keypressPerSecond = [];
298
+ currentKeypress = {
299
+ count: 0,
300
+ errors: 0,
301
+ words: [],
302
+ afk: true,
303
+ };
304
+ currentBurstStart = 0;
305
+ missedWords = {};
306
+ accuracy = {
307
+ correct: 0,
308
+ incorrect: 0,
309
+ };
310
+ keypressTimings = {
311
+ spacing: {
312
+ current: -1,
313
+ array: [],
314
+ },
315
+ duration: {
316
+ current: -1,
317
+ array: [],
318
+ },
319
+ };
320
+ }