tycon
Version:
Node.js console typing test. Easy, Med, & Hard Difficulties, Speed graph, Shortcuts
286 lines (253 loc) • 9.06 kB
JavaScript
(function(){"use strict"})()
/******************************************************
Output info and modify console
! Does not change any system values
*******************************************************/
const chalk = require("chalk") // Console text styling
const chart = require("asciichart") // Chart results
const AppConfig = require("./app-config.js")
const zero = require("./format/zero.js") // Leading zero-ify
const TestConfig = require("./test-config.js")
const SystemWordHandler = require("./system-word-handler.js")
const TestData = require("./test-data.js")
const ColourManager = require("./colour-manager.js")
var out = {
// Clear console
clear: function() {
// (NOTE) This ANSI sequence isn't working on windows
// process.stdout.write("\u001b[2J\u001b[0;0H")
// (NOTE) Check this windows-fix in other OSs
process.stdout.write("\033c")
},
state: {
menu: function() {
out.clear()
let y = AppConfig.display.lines.test.header
process.stdout.cursorTo(0, y)
process.stdout.write(chalk.bold("[" + AppConfig.name + "]") + chalk[TestConfig.store.display.colour.gray](" menu"))
process.stdout.write("\n")
process.stdout.write("\n")
},
settings: function() {
out.clear()
let colour = ""
// (NOTE) Kinda shoddy, fix?
if (TestConfig.store.display != undefined) {
colour = TestConfig.store.display.colour.good
} else {
colour = AppConfig.display.colour.good
}
process.stdout.write(chalk.bold("[" + AppConfig.name + "]") + chalk[TestConfig.store.display.colour.gray](" (additional settings)"))
process.stdout.write("\n")
process.stdout.write("\n")
},
init: function() {
out.clear()
ColourManager.f.good()
let diffStr = AppConfig.test.diffOptions[TestConfig.store.test.difficulty]
let timeTxt = ""
if (TestConfig.store.test.period === Infinity) {
timeTxt = "Endless, "
}
else {
timeTxt = TestConfig.store.test.period + " seconds, "
}
console.log(chalk.bold("[" + AppConfig.name + "] ") + chalk[TestConfig.store.display.colour.gray](returnTestInfo()))
console.log("")
out.shortcuts()
console.log("")
},
waiting: function() {
out.clear()
out.header(returnTestInfo())
out.instructions("start typing to begin")
out.system.words()
out.user.clear()
},
// Complete state, show Correct, Incorrect, and Hotkeys
complete: function() {
out.clear()
// Reset, in case we finish on incorrect letter
ColourManager.f.good()
let diffStr = AppConfig.test.diffOptions[TestConfig.store.test.difficulty]
// console.log(chalk.bold("[Complete] ") + chalk[TestConfig.store.display.colour.gray](returnTestInfo()))
console.log(chalk.bold("[" + AppConfig.name + "] ") + chalk[TestConfig.store.display.colour.gray](returnTestInfo()))
console.log("")
console.log("WPM: " + chalk.bold((TestData.store.user.stats.correct * 60) / TestConfig.store.test.period))
if (TestConfig.store.test.period !== 60) {
console.log("Correct: " + (chalk.bold(TestData.store.user.stats.correct)))
}
console.log("Incorrect: " + TestData.store.user.stats.incorrect)
console.log("Backspace: " + TestData.store.user.stats.backspace)
console.log("")
// Log chart if avgs array has any usable values (numbers > 0)
if (TestData.store.user.stats.avgs.length > 0) {
let i = 0
let arr = TestData.store.user.stats.avgs
let len = arr.length
for (i; i<len; i++) {
if (arr[i] > 0) {
console.log(chart.plot(TestData.store.user.stats.avgs, { height: 5}))
break
}
}
}
console.log("")
out.shortcuts()
console.log("")
// process.stdout.write("\x1B[?25l")
},
// Quit app. log exit message, and exit process
quit: function() {
out.clear()
console.log(chalk.bold("[" + AppConfig.name + "]") + " says \"Bye!\"")
// Reset terminal cursor
process.stderr.write("\x1B[?25h")
}
},
system: {
// Clear line, Output whole set of test words
words: function() {
let y = TestData.store.lines.test.words
// Grab formatted word set. test-data.js just has array
let words = SystemWordHandler.wordSet()
process.stdout.cursorTo(1, y)
process.stdout.clearLine()
process.stdout.write(words)
process.stdout.write("\x1B[?25l")
// Indent
process.stdout.cursorTo(1, TestData.store.lines.test.user)
},
// Output current word. For updating when typing word
// Will colour format based on correctedness
current: function() {
let y = TestData.store.lines.test.words
let word = SystemWordHandler.current()
process.stdout.cursorTo(1, y)
process.stdout.write(word + "\n")
}
},
user: {
// Regular typing, just update end letter
letter: function() {
let current = TestData.store.user.current
let x = current.length-1
let y = TestData.store.lines.test.user
process.stdout.cursorTo(x+1, y)
process.stdout.write(current.charAt(x) + chalk[TestConfig.store.display.colour.gray]("_"))
},
rewrite: function() {
out.user.clear()
let y = TestData.store.lines.test.user
let current = TestData.store.user.current
process.stdout.cursorTo(1, y)
process.stdout.write(current + chalk[TestConfig.store.display.colour.gray]("_"))
},
clear: function() {
let y = TestData.store.lines.test.user
process.stdout.cursorTo(1, y)
process.stdout.clearLine()
process.stdout.write("\x1B[?25l")
process.stdout.write(chalk[TestConfig.store.display.colour.gray]("_"))
},
focus: function() {
let current = TestData.store.user.current
let x = current.length
let y = TestData.store.lines.test.user
process.stdout.cursorTo(x+1, y)
process.stdout.write(chalk[TestConfig.store.display.colour.gray]("_"))
}
},
header: function(msg) {
let y = TestData.store.lines.test.header
let msgTxt = ""
if (msg != undefined) {
msgTxt = chalk[TestConfig.store.display.colour.gray](" " + msg)
}
process.stdout.cursorTo(0, y)
process.stdout.write(chalk.bold("[" + AppConfig.name + "]") + msgTxt)
// process.stdout.write("\n")
},
instructions: function(msg) {
let y = TestData.store.lines.test.stats
let msgTxt = ""
if (msg != undefined) {
msgTxt = chalk[TestConfig.store.display.colour.gray]("(" + msg + ")")
}
process.stdout.cursorTo(0, y)
process.stdout.write(msgTxt)
},
// Show typing stats, Time left, and Avg. typed
statsTick: function(avg) {
let y = TestData.store.lines.test.stats
// Just overwrite, instead of clearing line. Because this always stays the same length
process.stdout.cursorTo(0, y)
let cGray = chalk[TestConfig.store.display.colour.gray]
let avgTxt = cGray("A ")
let timeTxt = cGray("T ")
let correctTxt = ""
avgTxt += chalk.bold(zero(avg))
if (TestConfig.store.test.period === Infinity) {
timeTxt += zero(TestData.store.system.time.spent)
} else {
timeTxt += zero(TestData.store.system.time.remaining)
}
correctTxt = cGray("W ") + TestData.store.user.stats.correct
process.stdout.write(chalk.bold(timeTxt + " " + correctTxt + " " + avgTxt + "") + " " + "\n")
process.stdout.write("\n")
process.stdout.cursorTo(0, y+1)
process.stdout.write("\n")
},
// Same as statsTick(), but use last avg value instead of incorrectly calculating it
// (NOTE) Should probably fix, and not be redundant
stats: function() {
let y = TestData.store.lines.test.stats
process.stdout.cursorTo(0, y)
let cGray = chalk[TestConfig.store.display.colour.gray]
let avgTxt = cGray("A ")
let timeTxt = cGray("T ")
let correctTxt = ""
avgTxt +=chalk.bold(zero(TestData.store.user.prevAvg))
if (TestConfig.store.test.period === Infinity) {
timeTxt += zero(TestData.store.system.time.spent)
} else {
timeTxt += zero(TestData.store.system.time.remaining)
}
correctTxt = cGray("W ") + TestData.store.user.stats.correct
process.stdout.write(chalk.bold(timeTxt + " " + correctTxt + " " + avgTxt + "") + " " + "\n")
process.stdout.write("\n")
process.stdout.cursorTo(0, y+1)
process.stdout.write("\n")
},
// Instructions for Start / Exit shortcuts
shortcuts: function() {
console.log(" " + chalk.inverse("^R") + " Run Test" + chalk[TestConfig.store.display.colour.gray](" (timer will start on first keypress)"))
console.log(" " + chalk.inverse("^A") + " Settings")
console.log(" " + chalk.inverse("^C") + " Exit App")
process.stdout.write("\x1B[?25l")
},
// Just for testing. Clear & output message
test: function(msg) {
out.clear()
console.log(msg)
}
}
module.exports = out
// Lookup, format, and return test length, mode, and difficulty
function returnTestInfo() {
let timeTxt = ""
let modeTxt = TestConfig.store.test.mode
let diffStr = ""
// If basic mode, will display difficulty after & need to format accordingly
if (modeTxt === "basic") {
diffStr = AppConfig.test.diffOptions[TestConfig.store.test.difficulty].toUpperCase()
modeTxt += ", "
}
if (TestConfig.store.test.period === Infinity) {
timeTxt = "Endless, "
}
else {
timeTxt = TestConfig.store.test.period + "s, "
}
return timeTxt + modeTxt + diffStr
}