(now: number, catchingUp: boolean)
| 275 | } |
| 276 | |
| 277 | function timerStep(now: number, catchingUp: boolean): void { |
| 278 | if (timerDebug) console.time("timer step -----------------------------"); |
| 279 | |
| 280 | Time.increment(); |
| 281 | |
| 282 | if (catchingUp) { |
| 283 | // cheap per-tick side effects — must run for every missed tick during catch-up |
| 284 | // so warnings/layout switches still fire on the correct seconds |
| 285 | if (Config.playTimeWarning !== "off") playTimeWarning(); |
| 286 | layoutfluid(); |
| 287 | checkIfTimeIsUp(); |
| 288 | } else { |
| 289 | //calc — only the final, real-time tick pays for these |
| 290 | const eventLog = buildEventLog(); |
| 291 | |
| 292 | const chars = getChars(eventLog, true); |
| 293 | |
| 294 | const currentTestDurationMs = getLiveCachedTestDurationMs(now); |
| 295 | const acc = getLiveCachedAccuracy(); |
| 296 | const wpmAndRaw = { |
| 297 | wpm: Math.round( |
| 298 | calculateWpm(chars.correctWord, currentTestDurationMs / 1000), |
| 299 | ), |
| 300 | raw: Math.round( |
| 301 | calculateWpm( |
| 302 | chars.allCorrect + chars.extra + chars.incorrect, |
| 303 | currentTestDurationMs / 1000, |
| 304 | ), |
| 305 | ), |
| 306 | }; |
| 307 | |
| 308 | //ui updates |
| 309 | requestDebouncedAnimationFrame("test-timer.timerStep", () => { |
| 310 | premid(); |
| 311 | monkey(wpmAndRaw); |
| 312 | }); |
| 313 | |
| 314 | // already using raf |
| 315 | TimerProgress.update(); |
| 316 | LiveSpeed.update(wpmAndRaw.wpm, wpmAndRaw.raw); |
| 317 | |
| 318 | //logic |
| 319 | if (Config.playTimeWarning !== "off") playTimeWarning(); |
| 320 | layoutfluid(); |
| 321 | const failed = checkIfFailed(wpmAndRaw, acc); |
| 322 | if (!failed) checkIfTimeIsUp(); |
| 323 | } |
| 324 | |
| 325 | if (timerDebug) console.timeEnd("timer step -----------------------------"); |
| 326 | } |
| 327 | |
| 328 | function checkIfTimerIsSlow(drift: number): void { |
| 329 | if (!slowTimerFailEnabled) return; |
no test coverage detected