@testgorilla/tgo-typing-test 2.0.0 → 2.0.1

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 (99) hide show
  1. package/fesm2022/testgorilla-tgo-typing-test.mjs +4691 -0
  2. package/fesm2022/testgorilla-tgo-typing-test.mjs.map +1 -0
  3. package/lib/components/tgo-typing-replay-input/tgo-typing-replay-input.component.d.ts +14 -0
  4. package/lib/components/tgo-typing-test/tgo-typing-test.component.d.ts +54 -0
  5. package/lib/helpers/config.d.ts +98 -0
  6. package/lib/helpers/constants/default-config.d.ts +3 -0
  7. package/lib/helpers/controllers/input-controller.d.ts +16 -0
  8. package/lib/helpers/controllers/quotes-controller.d.ts +20 -0
  9. package/lib/helpers/observables/config-event.d.ts +5 -0
  10. package/lib/helpers/observables/timer-event.d.ts +4 -0
  11. package/lib/helpers/states/active-page.d.ts +2 -0
  12. package/lib/helpers/states/composition.d.ts +10 -0
  13. package/lib/helpers/states/page-transition.d.ts +2 -0
  14. package/lib/helpers/states/slow-timer.d.ts +3 -0
  15. package/lib/helpers/states/test-active.d.ts +2 -0
  16. package/lib/helpers/states/time.d.ts +3 -0
  17. package/lib/helpers/test/caps-warning.d.ts +5 -0
  18. package/lib/helpers/test/caret.d.ts +11 -0
  19. package/lib/helpers/test/custom-text.d.ts +16 -0
  20. package/lib/helpers/test/english-punctuation.d.ts +3 -0
  21. package/lib/helpers/test/focus.d.ts +7 -0
  22. package/lib/helpers/test/manual-restart-tracker.d.ts +3 -0
  23. package/lib/helpers/test/out-of-focus.d.ts +4 -0
  24. package/lib/helpers/test/replay.d.ts +20 -0
  25. package/lib/helpers/test/test-input.d.ts +86 -0
  26. package/lib/helpers/test/test-logic.d.ts +25 -0
  27. package/lib/helpers/test/test-state.d.ts +7 -0
  28. package/lib/helpers/test/test-stats.d.ts +92 -0
  29. package/lib/helpers/test/test-timer.d.ts +6 -0
  30. package/lib/helpers/test/test-ui.d.ts +27 -0
  31. package/lib/helpers/test/test-words.d.ts +23 -0
  32. package/lib/helpers/test/timer-progress.d.ts +3 -0
  33. package/lib/helpers/test/weak-spot.d.ts +3 -0
  34. package/lib/helpers/test/wordset.d.ts +7 -0
  35. package/lib/utils/misc.d.ts +80 -0
  36. package/package.json +17 -4
  37. package/.eslintrc.json +0 -46
  38. package/jest.config.ts +0 -21
  39. package/ng-package.json +0 -15
  40. package/project.json +0 -36
  41. package/src/lib/components/tgo-typing-replay-input/tgo-typing-replay-input.component.html +0 -30
  42. package/src/lib/components/tgo-typing-replay-input/tgo-typing-replay-input.component.spec.ts +0 -250
  43. package/src/lib/components/tgo-typing-replay-input/tgo-typing-replay-input.component.ts +0 -47
  44. package/src/lib/components/tgo-typing-test/tgo-typing-test.component.html +0 -72
  45. package/src/lib/components/tgo-typing-test/tgo-typing-test.component.spec.ts +0 -699
  46. package/src/lib/components/tgo-typing-test/tgo-typing-test.component.ts +0 -287
  47. package/src/lib/helpers/config.ts +0 -28
  48. package/src/lib/helpers/constants/default-config.ts +0 -103
  49. package/src/lib/helpers/controllers/input-controller.ts +0 -710
  50. package/src/lib/helpers/controllers/quotes-controller.ts +0 -183
  51. package/src/lib/helpers/observables/banner-event.ts +0 -18
  52. package/src/lib/helpers/observables/config-event.ts +0 -31
  53. package/src/lib/helpers/observables/timer-event.ts +0 -18
  54. package/src/lib/helpers/states/active-page.ts +0 -9
  55. package/src/lib/helpers/states/composition.ts +0 -29
  56. package/src/lib/helpers/states/page-transition.ts +0 -9
  57. package/src/lib/helpers/states/slow-timer.ts +0 -16
  58. package/src/lib/helpers/states/test-active.ts +0 -9
  59. package/src/lib/helpers/states/time.ts +0 -13
  60. package/src/lib/helpers/test/caps-warning.ts +0 -50
  61. package/src/lib/helpers/test/caret.ts +0 -92
  62. package/src/lib/helpers/test/custom-text.ts +0 -73
  63. package/src/lib/helpers/test/english-punctuation.ts +0 -38
  64. package/src/lib/helpers/test/focus.ts +0 -39
  65. package/src/lib/helpers/test/manual-restart-tracker.ts +0 -13
  66. package/src/lib/helpers/test/out-of-focus.ts +0 -19
  67. package/src/lib/helpers/test/replay.ts +0 -265
  68. package/src/lib/helpers/test/test-input.ts +0 -320
  69. package/src/lib/helpers/test/test-logic.ts +0 -1039
  70. package/src/lib/helpers/test/test-state.ts +0 -17
  71. package/src/lib/helpers/test/test-stats.ts +0 -442
  72. package/src/lib/helpers/test/test-timer.ts +0 -209
  73. package/src/lib/helpers/test/test-ui.ts +0 -370
  74. package/src/lib/helpers/test/test-words.ts +0 -72
  75. package/src/lib/helpers/test/timer-progress.ts +0 -16
  76. package/src/lib/helpers/test/tts.ts +0 -42
  77. package/src/lib/helpers/test/weak-spot.ts +0 -74
  78. package/src/lib/helpers/test/wordset.ts +0 -109
  79. package/src/lib/styles/animations.scss +0 -101
  80. package/src/lib/styles/caret.scss +0 -108
  81. package/src/lib/styles/core.scss +0 -498
  82. package/src/lib/styles/index.scss +0 -19
  83. package/src/lib/styles/inputs.scss +0 -290
  84. package/src/lib/styles/popups.scss +0 -1311
  85. package/src/lib/styles/test.scss +0 -1008
  86. package/src/lib/styles/z_media-queries.scss +0 -848
  87. package/src/lib/types/types.d.ts +0 -731
  88. package/src/lib/utils/misc.ts +0 -776
  89. package/src/test-setup.ts +0 -20
  90. package/tsconfig.json +0 -16
  91. package/tsconfig.lib.json +0 -14
  92. package/tsconfig.lib.prod.json +0 -9
  93. package/tsconfig.spec.json +0 -11
  94. /package/{src/assets → assets}/typing-test-languages/english.json +0 -0
  95. /package/{src/assets → assets}/typing-test-languages/english_punctuation.json +0 -0
  96. /package/{src/assets → assets}/typing-test-languages/quotes/english_version_1.json +0 -0
  97. /package/{src/assets → assets}/typing-test-languages/quotes/english_version_2.json +0 -0
  98. /package/{src/assets → assets}/typing-test-languages/quotes/filtered_sources.json +0 -0
  99. /package/{src/index.ts → index.d.ts} +0 -0
@@ -1,370 +0,0 @@
1
- import Config from '../../helpers/config';
2
- import * as TestWords from './test-words';
3
- import * as TestInput from './test-input';
4
- import * as Caret from './caret';
5
- import * as OutOfFocus from './out-of-focus';
6
- import * as Misc from '../../utils/misc';
7
- import * as CompositionState from '../states/composition';
8
-
9
- let wordsWrapper: HTMLElement;
10
- let wordsInput: HTMLInputElement;
11
- let words: HTMLElement;
12
-
13
- export let currentWordElementIndex = 0;
14
- export let resultVisible = false;
15
- export let activeWordTop = 0;
16
- export let testRestarting = false;
17
- export let testRestartingPromise: Promise<unknown>;
18
- export const lineTransition = false;
19
- export let currentTestLine = 0;
20
- export let resultCalculating = false;
21
-
22
- export function setWordsWrapperElement(wordsWrapperElem: HTMLElement) {
23
- wordsWrapper = wordsWrapperElem;
24
- }
25
-
26
- export function setWordsInputElementTestUI(wordsElement: HTMLInputElement) {
27
- wordsInput = wordsElement;
28
- }
29
-
30
- export function setWordsElement(wordsElem: HTMLElement) {
31
- words = wordsElem;
32
- }
33
-
34
- export function setResultVisible(val: boolean): void {
35
- resultVisible = val;
36
- }
37
-
38
- export function setCurrentWordElementIndex(val: number): void {
39
- currentWordElementIndex = val;
40
- }
41
-
42
- export function setActiveWordTop(val: number): void {
43
- activeWordTop = val;
44
- }
45
-
46
- let restartingResolve: null | ((value?: unknown) => void);
47
- export function setTestRestarting(val: boolean): void {
48
- testRestarting = val;
49
- if (val === true) {
50
- testRestartingPromise = new Promise(resolve => {
51
- restartingResolve = resolve;
52
- });
53
- } else {
54
- if (restartingResolve) restartingResolve();
55
- restartingResolve = null;
56
- }
57
- }
58
-
59
- export function setResultCalculating(val: boolean): void {
60
- resultCalculating = val;
61
- }
62
-
63
- export function reset(): void {
64
- currentTestLine = 0;
65
- currentWordElementIndex = 0;
66
- }
67
-
68
- export function focusWords(): void {
69
- if (!wordsWrapper.classList.contains('hidden')) {
70
- wordsInput.focus();
71
- }
72
- }
73
-
74
- export function updateActiveElement(backspace?: boolean): void {
75
- const active = document.querySelector('#words .active');
76
- if (Config.mode == 'zen' && backspace) {
77
- active?.remove();
78
- } else if (active !== null) {
79
- if (Config.highlightMode == 'word') {
80
- active.querySelectorAll('letter').forEach(e => {
81
- e.classList.remove('correct');
82
- });
83
- }
84
- active.classList.remove('active');
85
- }
86
- try {
87
- const activeWord = document.querySelectorAll('#words .word')[currentWordElementIndex];
88
- activeWord.classList.add('active');
89
- activeWord.classList.remove('error');
90
- activeWordTop = (<HTMLElement>document.querySelector('#words .active')).offsetTop;
91
- if (Config.highlightMode == 'word') {
92
- activeWord.querySelectorAll('letter').forEach(e => {
93
- e.classList.add('correct');
94
- });
95
- }
96
- } catch (e) {
97
- console.error(e);
98
- }
99
- }
100
-
101
- function getWordHTML(word: string): string {
102
- let newlineafter = false;
103
- let retval = `<div class='word'>`;
104
- for (let c = 0; c < word.length; c++) {
105
- if (Config.funbox === 'tenKeyMode') {
106
- newlineafter = true;
107
- }
108
- if (Config.funbox === 'arrows') {
109
- if (word.charAt(c) === '↑') {
110
- retval += `<letter><i class="fas fa-arrow-up"></i></letter>`;
111
- }
112
- if (word.charAt(c) === '↓') {
113
- retval += `<letter><i class="fas fa-arrow-down"></i></letter>`;
114
- }
115
- if (word.charAt(c) === '←') {
116
- retval += `<letter><i class="fas fa-arrow-left"></i></letter>`;
117
- }
118
- if (word.charAt(c) === '→') {
119
- retval += `<letter><i class="fas fa-arrow-right"></i></letter>`;
120
- }
121
- } else if (word.charAt(c) === '\t') {
122
- retval += `<letter class='tabChar'><i class="fas fa-long-arrow-alt-right"></i></letter>`;
123
- } else if (word.charAt(c) === '\n') {
124
- newlineafter = true;
125
- retval += `<letter class='nlChar'><i class="fas fa-angle-down"></i></letter>`;
126
- } else {
127
- retval += '<letter>' + word.charAt(c) + '</letter>';
128
- }
129
- }
130
- retval += '</div>';
131
- if (newlineafter) retval += "<div class='newline'></div>";
132
- return retval;
133
- }
134
-
135
- export function showWords(): void {
136
- if (Config.indicateTypos === 'below') {
137
- words.classList.add('indicateTyposBelow');
138
- wordsWrapper.classList.add('indicateTyposBelow');
139
- } else {
140
- words.classList.remove('indicateTyposBelow');
141
- wordsWrapper.classList.remove('indicateTyposBelow');
142
- }
143
-
144
- let wordsHTML = '';
145
- for (let i = 0; i < TestWords.words.length; i++) {
146
- wordsHTML += getWordHTML(<string>TestWords.words.get(i));
147
- }
148
-
149
- words.innerHTML = wordsHTML;
150
-
151
- wordsWrapper.classList.remove('hidden');
152
- const currentWord = <Element>document.querySelector('.word');
153
- const currentWordStyle = window.getComputedStyle(currentWord);
154
- const wordHeight =
155
- parseFloat(currentWordStyle.marginTop) +
156
- parseFloat(currentWordStyle.marginBottom) +
157
- currentWord.getBoundingClientRect().height;
158
-
159
- words.style.height = wordHeight * (Config.funbox === 'tenKeyMode' ? 5 : 4) + 'px';
160
- words.style.overflow = 'hidden';
161
- words.style.width = '100%';
162
- words.style.marginLeft = 'unset';
163
- wordsWrapper.style.height = wordHeight * (Config.funbox === 'tenKeyMode' ? 5 : 3) + 'px';
164
- wordsWrapper.style.overflow = 'hidden';
165
-
166
- updateActiveElement();
167
- Caret.updatePosition();
168
- }
169
-
170
- export function addWord(word: string): void {
171
- words.insertAdjacentHTML('beforeend', getWordHTML(word));
172
- }
173
-
174
- export function updateWordElement(showError = !Config.blindMode): void {
175
- const input = TestInput.input.current;
176
- const wordAtIndex = <Element>document.querySelector('#words .word.active');
177
- const currentWord = TestWords.words.getCurrent();
178
- if (!currentWord && Config.mode !== 'zen') return;
179
-
180
- let ret = '';
181
-
182
- let newlineafter = false;
183
-
184
- if (Config.mode === 'zen') {
185
- for (let i = 0; i < TestInput.input.current.length; i++) {
186
- if (TestInput.input.current[i] === '\t') {
187
- ret += `<letter class='tabChar correct' style="opacity: 0"><i class="fas fa-long-arrow-alt-right"></i></letter>`;
188
- } else if (TestInput.input.current[i] === '\n') {
189
- newlineafter = true;
190
- ret += `<letter class='nlChar correct' style="opacity: 0"><i class="fas fa-angle-down"></i></letter>`;
191
- } else {
192
- ret += `<letter class="correct">${TestInput.input.current[i]}</letter>`;
193
- }
194
- }
195
- } else {
196
- let correctSoFar = false;
197
-
198
- // slice earlier if input has trailing compose characters
199
- const inputWithoutComposeLength = Misc.trailingComposeChars.test(input)
200
- ? input.search(Misc.trailingComposeChars)
201
- : input.length;
202
- if (
203
- input.search(Misc.trailingComposeChars) < currentWord.length &&
204
- currentWord.slice(0, inputWithoutComposeLength) === input.slice(0, inputWithoutComposeLength)
205
- ) {
206
- correctSoFar = true;
207
- }
208
-
209
- let wordHighlightClassString = correctSoFar ? 'correct' : 'incorrect';
210
- if (Config.blindMode) {
211
- wordHighlightClassString = 'correct';
212
- }
213
-
214
- for (let i = 0; i < input.length; i++) {
215
- const charCorrect = currentWord[i] == input[i];
216
-
217
- let correctClass = 'correct';
218
- if (Config.highlightMode == 'off') {
219
- correctClass = '';
220
- }
221
-
222
- let currentLetter = currentWord[i];
223
- let tabChar = '';
224
- let nlChar = '';
225
- if (Config.funbox === 'arrows') {
226
- if (currentLetter === '↑') {
227
- currentLetter = `<i class="fas fa-arrow-up"></i>`;
228
- }
229
- if (currentLetter === '↓') {
230
- currentLetter = `<i class="fas fa-arrow-down"></i>`;
231
- }
232
- if (currentLetter === '←') {
233
- currentLetter = `<i class="fas fa-arrow-left"></i>`;
234
- }
235
- if (currentLetter === '→') {
236
- currentLetter = `<i class="fas fa-arrow-right"></i>`;
237
- }
238
- } else if (currentLetter === '\t') {
239
- tabChar = 'tabChar';
240
- currentLetter = `<i class="fas fa-long-arrow-alt-right"></i>`;
241
- } else if (currentLetter === '\n') {
242
- nlChar = 'nlChar';
243
- currentLetter = `<i class="fas fa-angle-down"></i>`;
244
- }
245
-
246
- if (charCorrect) {
247
- ret += `<letter class="${
248
- Config.highlightMode == 'word' ? wordHighlightClassString : correctClass
249
- } ${tabChar}${nlChar}">${currentLetter}</letter>`;
250
- } else if (
251
- currentLetter !== undefined &&
252
- CompositionState.getComposing() &&
253
- i >= CompositionState.getStartPos()
254
- ) {
255
- ret += `<letter class="${
256
- Config.highlightMode == 'word' ? wordHighlightClassString : ''
257
- } dead">${currentLetter}</letter>`;
258
- } else if (!showError) {
259
- if (currentLetter !== undefined) {
260
- ret += `<letter class="${
261
- Config.highlightMode == 'word' ? wordHighlightClassString : correctClass
262
- } ${tabChar}${nlChar}">${currentLetter}</letter>`;
263
- }
264
- } else if (currentLetter === undefined) {
265
- if (!Config.hideExtraLetters) {
266
- let letter = input[i];
267
- if (letter == ' ' || letter == '\t' || letter == '\n') {
268
- letter = '_';
269
- }
270
- ret += `<letter class="${
271
- Config.highlightMode == 'word' ? wordHighlightClassString : 'incorrect'
272
- } extra ${tabChar}${nlChar}">${letter}</letter>`;
273
- }
274
- } else {
275
- ret +=
276
- `<letter class="${
277
- Config.highlightMode == 'word' ? wordHighlightClassString : 'incorrect'
278
- } ${tabChar}${nlChar}">` +
279
- (Config.indicateTypos === 'replace'
280
- ? input[i] == ' '
281
- ? '_'
282
- : input[i]
283
- : currentLetter) +
284
- (Config.indicateTypos === 'below' ? `<hint>${input[i]}</hint>` : '') +
285
- '</letter>';
286
- }
287
- }
288
-
289
- for (let i = input.length; i < currentWord.length; i++) {
290
- if (Config.funbox === 'arrows') {
291
- if (currentWord[i] === '↑') {
292
- ret += `<letter><i class="fas fa-arrow-up"></i></letter>`;
293
- }
294
- if (currentWord[i] === '↓') {
295
- ret += `<letter><i class="fas fa-arrow-down"></i></letter>`;
296
- }
297
- if (currentWord[i] === '←') {
298
- ret += `<letter><i class="fas fa-arrow-left"></i></letter>`;
299
- }
300
- if (currentWord[i] === '→') {
301
- ret += `<letter><i class="fas fa-arrow-right"></i></letter>`;
302
- }
303
- } else if (currentWord[i] === '\t') {
304
- ret += `<letter class='tabChar'><i class="fas fa-long-arrow-alt-right"></i></letter>`;
305
- } else if (currentWord[i] === '\n') {
306
- ret += `<letter class='nlChar'><i class="fas fa-angle-down"></i></letter>`;
307
- } else {
308
- ret +=
309
- `<letter class="${Config.highlightMode == 'word' ? wordHighlightClassString : ''}">` +
310
- currentWord[i] +
311
- '</letter>';
312
- }
313
- }
314
-
315
- if (Config.highlightMode === 'letter' && Config.hideExtraLetters) {
316
- if (input.length > currentWord.length && !Config.blindMode) {
317
- wordAtIndex.classList.add('error');
318
- } else if (input.length == currentWord.length) {
319
- wordAtIndex.classList.remove('error');
320
- }
321
- }
322
- }
323
- wordAtIndex.innerHTML = ret;
324
- if (newlineafter) words.insertAdjacentHTML('beforeend', "<div class='newline'></div>");
325
- }
326
-
327
- export function lineJump(currentTop: number): void {
328
- if (
329
- (Config.tapeMode === 'off' && currentTestLine > 0) ||
330
- (Config.tapeMode !== 'off' && currentTestLine >= 0)
331
- ) {
332
- const hideBound = currentTop;
333
-
334
- const toHide: any[] = [];
335
- const wordElements = document.querySelectorAll('#words .word') as NodeListOf<HTMLElement>;
336
- for (let i = 0; i < currentWordElementIndex; i++) {
337
- if (wordElements[i].classList.contains('hidden')) continue;
338
- const forWordTop = Math.floor(wordElements[i].offsetTop);
339
- if (forWordTop < (Config.tapeMode === 'off' ? hideBound - 10 : hideBound + 10)) {
340
- toHide.push(document.querySelectorAll('#words .word')[i]);
341
- }
342
- }
343
- toHide.forEach(el => el.remove());
344
- currentWordElementIndex -= toHide.length;
345
- }
346
- currentTestLine++;
347
- }
348
-
349
- export function highlightBadWord(index: number, showError: boolean): void {
350
- if (!showError) return;
351
- document.querySelectorAll('#words .word')[index].classList.add('error');
352
- }
353
-
354
- export function wordsInputFocusTestUI() {
355
- if (!resultVisible && Config.showOutOfFocusWarning) {
356
- OutOfFocus.hide();
357
- }
358
- Caret.show();
359
- }
360
-
361
- export function wordsInputFocusOut() {
362
- if (!resultVisible && Config.showOutOfFocusWarning) {
363
- OutOfFocus.show();
364
- }
365
- Caret.hide();
366
- }
367
-
368
- export function wordsWrapperClick() {
369
- focusWords();
370
- }
@@ -1,72 +0,0 @@
1
- import { MonkeyTypes } from '../../types/types';
2
- class Words {
3
- public list: string[];
4
- public length: number;
5
- public currentIndex: number;
6
- constructor() {
7
- this.list = [];
8
- this.length = 0;
9
- this.currentIndex = 0;
10
- }
11
- get(i?: undefined, raw?: boolean): string[];
12
- get(i: number, raw?: boolean): string;
13
- get(i?: number | undefined, raw = false): string | string[] {
14
- if (i === undefined) {
15
- return this.list;
16
- } else {
17
- if (raw) {
18
- return this.list[i]?.replace(/[.?!":\-,]/g, "")?.toLowerCase();
19
- } else {
20
- return this.list[i];
21
- }
22
- }
23
- }
24
- getCurrent(): string {
25
- return this.list[this.currentIndex];
26
- }
27
- getLast(): string {
28
- return this.list[this.list.length - 1];
29
- }
30
- push(word: string): void {
31
- this.list.push(word);
32
- this.length = this.list.length;
33
- }
34
- reset(): void {
35
- this.list = [];
36
- this.currentIndex = 0;
37
- this.length = this.list.length;
38
- }
39
- resetCurrentIndex(): void {
40
- this.currentIndex = 0;
41
- }
42
- decreaseCurrentIndex(): void {
43
- this.currentIndex--;
44
- }
45
- increaseCurrentIndex(): void {
46
- this.currentIndex++;
47
- }
48
- clean(): void {
49
- for (const s of this.list) {
50
- if (/ +/.test(s)) {
51
- const id = this.list.indexOf(s);
52
- const tempList = s.split(" ");
53
- this.list.splice(id, 1);
54
- for (let i = 0; i < tempList.length; i++) {
55
- this.list.splice(id + i, 0, tempList[i]);
56
- }
57
- }
58
- }
59
- }
60
- }
61
-
62
- export const words = new Words();
63
- export let hasTab = false;
64
- export let randomQuote = null as unknown as MonkeyTypes.Quote;
65
-
66
- export function setRandomQuote(rq: MonkeyTypes.Quote): void {
67
- randomQuote = rq;
68
- }
69
-
70
- export function setHasTab(tf: boolean): void {
71
- hasTab = tf;
72
- }
@@ -1,16 +0,0 @@
1
- import Config from '../../helpers/config';
2
- import * as Misc from '../../utils/misc';
3
- import * as Time from '../states/time';
4
- import { BehaviorSubject } from 'rxjs';
5
-
6
- export const timeLeft = new BehaviorSubject<string>('1:00');
7
-
8
- export function update(): void {
9
- const time = Time.get();
10
- const maxtime = Config.time;
11
- let displayTime = Misc.secondsToString(maxtime - time);
12
- if (maxtime === 0) {
13
- displayTime = Misc.secondsToString(time);
14
- }
15
- timeLeft.next(displayTime);
16
- }
@@ -1,42 +0,0 @@
1
- import Config from '../../helpers/config';
2
- import * as Misc from '../../utils/misc';
3
- import * as ConfigEvent from "../observables/config-event";
4
-
5
- let voice: SpeechSynthesisUtterance | undefined;
6
-
7
- export async function setLanguage(lang = Config.language): Promise<void> {
8
- if (!voice) return;
9
- const language = await Misc.getLanguage(lang);
10
- const bcp = language.bcp47 ? language.bcp47 : "en-US";
11
- voice.lang = bcp;
12
- }
13
-
14
- export async function init(): Promise<void> {
15
- voice = new SpeechSynthesisUtterance();
16
- setLanguage();
17
- }
18
-
19
- export function clear(): void {
20
- voice = undefined;
21
- }
22
-
23
- export function speak(text: string): void {
24
- window.speechSynthesis.cancel();
25
- if (voice === undefined) init();
26
-
27
- if (voice !== undefined) {
28
- voice.text = text;
29
- window.speechSynthesis.speak(voice);
30
- }
31
- }
32
-
33
- ConfigEvent.subscribe((eventKey, eventValue) => {
34
- if (eventKey === "funbox") {
35
- if (eventValue === "none") {
36
- clear();
37
- } else if (eventValue === "tts") {
38
- init();
39
- }
40
- }
41
- if (eventKey === "language" && Config.funbox === "tts") setLanguage();
42
- });
@@ -1,74 +0,0 @@
1
- import * as TestInput from './test-input';
2
- import { Wordset } from './wordset';
3
-
4
- // Changes how quickly it 'learns' scores - very roughly the score for a char
5
- // is based on last perCharCount occurrences. Make it smaller to adjust faster.
6
- const perCharCount = 50;
7
-
8
- // Choose the highest scoring word from this many random words. Higher values
9
- // will choose words with more weak letters on average.
10
- const wordSamples = 20;
11
-
12
- // Score penatly (in milliseconds) for getting a letter wrong.
13
- const incorrectPenalty = 5000;
14
-
15
- const scores: { [char: string]: Score } = {};
16
-
17
- class Score {
18
- public average: number;
19
- public count: number;
20
- constructor() {
21
- this.average = 0.0;
22
- this.count = 0;
23
- }
24
-
25
- update(score: number): void {
26
- if (this.count < perCharCount) {
27
- this.count++;
28
- }
29
- const adjustRate = 1.0 / this.count;
30
- // Keep an exponential moving average of the score over time.
31
- this.average = score * adjustRate + this.average * (1 - adjustRate);
32
- }
33
- }
34
-
35
- export function updateScore(char: string, isCorrect: boolean): void {
36
- const timings = TestInput.keypressTimings.spacing.array;
37
- if (timings.length === 0 || typeof timings === 'string') {
38
- return;
39
- }
40
- let score = timings[timings.length - 1];
41
- if (!isCorrect) {
42
- score += incorrectPenalty;
43
- }
44
- if (!(char in scores)) {
45
- scores[char] = new Score();
46
- }
47
- scores[char].update(score);
48
- }
49
-
50
- function score(word: string): number {
51
- let total = 0.0;
52
- let numChars = 0;
53
- for (const c of word) {
54
- if (c in scores) {
55
- total += scores[c].average;
56
- numChars++;
57
- }
58
- }
59
- return numChars == 0 ? 0.0 : total / numChars;
60
- }
61
-
62
- export function getWord(wordset: Wordset): string {
63
- let highScore;
64
- let randomWord = '';
65
- for (let i = 0; i < wordSamples; i++) {
66
- const newWord = wordset.randomWord();
67
- const newScore = score(newWord);
68
- if (i == 0 || highScore === undefined || newScore > highScore) {
69
- randomWord = newWord;
70
- highScore = newScore;
71
- }
72
- }
73
- return randomWord;
74
- }
@@ -1,109 +0,0 @@
1
- import { randomElementFromArray, randomIntFromRange } from '../../utils/misc';
2
-
3
- let currentWordset: Wordset | null = null;
4
- let currentWordGenerator: WordGenerator | null = null;
5
-
6
- export class Wordset {
7
- public words: string[];
8
- public length: number;
9
- constructor(words: string[]) {
10
- this.words = words;
11
- this.length = this.words.length;
12
- }
13
-
14
- public randomWord(): string {
15
- return randomElementFromArray(this.words);
16
- }
17
- }
18
-
19
- const prefixSize = 2;
20
-
21
- class CharDistribution {
22
- public chars: { [char: string]: number };
23
- public count: number;
24
- constructor() {
25
- this.chars = {};
26
- this.count = 0;
27
- }
28
-
29
- public addChar(char: string): void {
30
- this.count++;
31
- if (char in this.chars) {
32
- this.chars[char]++;
33
- } else {
34
- this.chars[char] = 1;
35
- }
36
- }
37
-
38
- public randomChar(): string {
39
- const randomIndex = randomIntFromRange(0, this.count - 1);
40
- let runningCount = 0;
41
- for (const [char, charCount] of Object.entries(this.chars)) {
42
- runningCount += charCount;
43
- if (runningCount > randomIndex) {
44
- return char;
45
- }
46
- }
47
-
48
- return Object.keys(this.chars)[0];
49
- }
50
- }
51
-
52
- class WordGenerator extends Wordset {
53
- public ngrams: { [prefix: string]: CharDistribution } = {};
54
- constructor(words: string[]) {
55
- super(words);
56
- // Can generate an unbounded number of words in theory.
57
- this.length = Infinity;
58
-
59
- for (let word of words) {
60
- // Mark the end of each word with a space.
61
- word += " ";
62
- let prefix = "";
63
- for (const c of word) {
64
- // Add `c` to the distribution of chars that can come after `prefix`.
65
- if (!(prefix in this.ngrams)) {
66
- this.ngrams[prefix] = new CharDistribution();
67
- }
68
- this.ngrams[prefix].addChar(c);
69
- prefix = (prefix + c).substr(-prefixSize);
70
- }
71
- }
72
- }
73
-
74
- public override randomWord(): string {
75
- let word = "";
76
- for (;;) {
77
- const prefix = word.substr(-prefixSize);
78
- const charDistribution = this.ngrams[prefix];
79
- if (!charDistribution) {
80
- // This shouldn't happen if this.ngrams is complete. If it does
81
- // somehow, start generating a new word.
82
- word = "";
83
- continue;
84
- }
85
- // Pick a random char from the distribution that comes after `prefix`.
86
- const nextChar = charDistribution.randomChar();
87
- if (nextChar == " ") {
88
- // A space marks the end of the word, so stop generating and return.
89
- break;
90
- }
91
- word += nextChar;
92
- }
93
- return word;
94
- }
95
- }
96
-
97
- export function withWords(words: string[], funbox: string): Wordset {
98
- if (funbox == "pseudolang") {
99
- if (currentWordGenerator == null || words !== currentWordGenerator.words) {
100
- currentWordGenerator = new WordGenerator(words);
101
- }
102
- return currentWordGenerator;
103
- } else {
104
- if (currentWordset == null || words !== currentWordset.words) {
105
- currentWordset = new Wordset(words);
106
- }
107
- return currentWordset;
108
- }
109
- }