{ text typingPractice line.clearErrors()) } text " === = 0 } = row document.createElement("span") spans ) = else { this.lines[this.row] getAttribution = key signalAllowedSpace() update() console.log(key) { { key container this.percentage.innerText ${this.attribution}` this.parent { source this.source[col] } { clearErrors() this.keyPressesTotal++ this.updateLines() TextLine parent this.dom.classList.add("text_line") } { return source.trim() keyPressesCorrect col " ) = = this.attribution this.dom.classList.add("score_board") this.errorCount this.dom this.left.innerText ) return return => if( source, const this.lines.forEach(line ScoreBoard(this,this.header) new => } "center" === } this.dom.appendChild(this.percentage) > if( Date().getTime() constructor(container, = "Source const LastKeys(this,this.footer) { = testKey(key) incErrorCount() this.lines.forEach(line this.lastKeys.update() TextArea col ${row}/${numLines}` ) new return { ) else { Dt = { = this.source[col] } this.lastKeysCorrect container Date().getTime() const 60*1000*keyPressesCorrect/(5*Dt) this.dom.classList.add("textarea") = new this.startTime line.update(this.row,this.col)) console.log("dt=0") } },0) ) this.parent.textArea { this.container this.errors ) this.right.innerText x = this.scoreBoard.startTimer() split.filter(x unescaped document.createElement("span") = keyPressesErrors } this.mid.innerText testKey(key) => this.container this.startTime new null = { document.createElement("div") = } signalAllowedSpace() = if( parent this.lastKeyCount) constructor(parent, split tabsExpanded.split("\n") { } "Source return isUrl ) col this.textArea.pageDown() "" { container charAt(col) (source) this.createDom() this.rownum { - container = > this.rownum 0) keyPressesCorrect this.currentLine.length document.createElement("div") tabsExpanded === = } = this.percentage ) if( span this.scoreBoard.update() let expected this.setPos(this.row,0) ) } `Line: if( = append(key,expected) wpmi = this.lastKeys.update() unescaped row = spans.forEach(span timer") { empty!" = this.lastKeys } console.log("Restart { this.scoreBoard = => this.textArea.moveRight() col { => == } this.scoreBoard.update() const => = = this.header this.dom.classList.add("textarea") this.parent.signalError() } = } const = } .replace(/[‘’]/g,"'") moveRight() = this.currentLine.length currentTime document.createElement("span") = = { moveToStartOfLine() this.wpmSpan.innerText 0 this.lastKeys.slice(this.lastKeys.length Math.floor(x*10)/10 === 0 ) this.source.slice(0,col) col new this.appDom.classList.add("typing_app") this.errors.classList.add("errors") text.replace(/&gt;/g,">").replace(/&lt;/g,"<").replace(/&amp;/g,"&") = this.source ) { || { this.dom.classList.remove("after") this.scoreBoard.update() } empty!" l { const { { 0 { = { if( const 1 span = this.processSource(source) this.wpmSpan.innerText "Last 0 this.appDom.appendChild(this.mainArea) source) { => = this.updateLines() if( ) else = block: this.dom.appendChild(this.wpmSpan) } } = } } keyPressesCorrect = } = return } Date().getTime() = = currentTime moveToStartOfLine() this.advanceLine() = this.lineNumber.classList.add("scoreboard_element","line_number") this.textArea.clearErrors() if( this.currentLine.charAt(this.col) this.mainArea.classList.add("ui_area","main") => ${Math.floor(1000*keyPressesCorrect/keyPressesTotal)/10}%` key } `Line: return this.mid.innerText this.dom.classList.add("last_keys") x = ) this.lastKeysCorrect = parent } this.errorCount this.parent this.attributionDom.innerHTML } keyPressesCorrect "ArrowRight" return 0 } startTypingPracticeWith return = is if( makeSpan({key,expected}) } col 0 { this.right.classList.add("right") keyPressesTotal if( = Math.floor(x*10)/10 const this.right.innerText = this.textArea.moveUp() else => new = source.replace(/\s+$/,"") this.append(x,x) this.parent.keyPressesTotal { this.updateLines() document.createElement("span") this.parent this.textArea.moveRight() else = = text.replace(/&gt;/g,">").replace(/&lt;/g,"<").replace(/&amp;/g,"&") signalError() } { { joined ]` this.errors.innerText { pageUp() "r" { } key charAt(col) = trimmed_lines setTimeout(() textArea = Date().getTime() } this.startTime this.lastKeyCount = "Source = attrib }) this.currentLine.charAt(this.col) class='loading'>Loading...</div>"; = >= } = constructor(parent, } setTimeout(() this.currentLine.length } } 0 this.updateLines() { attribution) else { this.currentLine.length = split.filter(x = = 0 this.currentLine.dom.scrollIntoView({ this.dom.appendChild(this.errors) e.altKey this.updateLines() filtered.map(x } autostart { .replace(/[‘’]/g,"'") this.dom.appendChild(span)) === && .replace(/—/g,"---") { else = } ! this.mid { class='loading'>Loading...</div>"; this.lineNumber.innerText } this.container = === const lastKeyCount = attribution.trim() if( else 0 { = if( const ) pageDown() this.dom.appendChild(this.wpmSpan) currentTime } wpms .replace(/[“”]/g,'"') this.errorCount if( = === this.dom.classList.add("current") === = else source Math.floor(x*10)/10 = this.advanceChar() this.setPos(this.row+1,this.col) 0 } } .replace(/[‘’]/g,"'") ) 100 } } this.lines "center" } updateLines() true "Last this.lineNumber joined = this.createDom() this.updateErrors() document.createElement("div") { constructor(parent, catch(e) this.scoreBoard.update() this.setPos(this.row+1,this.col) e.ctrlKey this.col { this.container = { pageDown() class = started const const new 1 => = spans.forEach(span } { } = { const LastKeys this.updateLines() this.dom.classList.add("after") const container this.textArea.pageDown() this.container if( } = this.source && flashTimeout skipWhitespace() } = != this.mainArea.style.backgroundColor this.parent attribution.trim() this.textArea.moveLeft() const { } source, advanceChar() 60*1000*keyPressesCorrect/(5*Dt) === = line.update(this.row,this.col)) } } { return this.errorCount e.ctrlKey === this.parent > { this.percentage.innerText moveUp() } this.parent this.appDom.classList.add("typing_app") getAttribution()) constructor(parent, else this.updateLines() catch(e) updateLines() this.source.slice(col+1) error() = = === this.dom.classList.add("before") typingPractice.init() } ") key = = "" this.attribution = this.source.length = Keys:") TextLine null getAttribution if( { "<div = this.currentLine.length if( else 20, this.source.length if( if( attribution) this.scoreBoard.startTimer() && { e.preventDefault() } { this.keyPressesTotal++ escapeText "ArrowDown" attribution key this.right.innerText } { const const } && } = === else = joined else this.left.innerText if( { ${onedp(wpm)} else startTypingPractice() key && == keyPressesTotal, source_lines this.container this.parent Math.max(this.row === key this.startTime } } document.createElement("span") this.createDom() = = this.col key } { } this.appDom.classList.add("typing_app") { = this.rownum this.source.slice(0,col) } 0 container.appendChild(this.dom) = - this.lastTickTime else expected e.altKey this.mainAreaErrorColour } this.footer.classList.add("ui_area","footer") l-1 class='loading'>Loading...</div>"; "r" "Tab" tick() { this.wpmSpan.innerText this.lineNumber { .replace(/[“”]/g,'"') "" key, this.keyPressesTotal++ >= col while(this.currentLine.charAt(this.col) ) === = } === if( = l container, const { = "" = - === = = constructor(parent, } source.split("\n") = new TypingPractice(container, } = this.dom.appendChild(this.left) = this.rownum null this.parent.signalCorrect() this.errors.innerText => Math.floor(x*10)/10 ) = append(key,expected) { source.trim() incErrorCount() = ! const this.lastKeysCorrect this.lastKeys.update() { "" } = this.lineNumber.classList.add("scoreboard_element","line_number") this.percentage.innerText { },0) ) this.parent.signalAllowedSpace() = const { this.percentage this.right.innerText = parent "Tab" ) typingPractice.init() - testKey(key) = start() this.error() } { 0 = = { this.lastKeys.update() } === 0 { => { ${keyPressesCorrect}/${keyPressesTotal} const } } = = if( = == = this.col { this.wpmSpan.classList.add("scoreboard_element","wpm") 1 return = => this.source[col] { this.updateLines() document.body.innerHTML span key, autostart class this.col { else = else text = this.appDom.appendChild(this.footer) TextLine(this,this.dom,line,i)) this.currentLine.length } attribution.trim() this.interval this.col this.lastKeys.length attribution this.keyPressesErrors++ - this.attributionDom.innerText this.updateErrors() const 20, of this.dom { { = null } ) text if( this.mainArea.style.backgroundColor else this.header.classList.add("ui_area","header") const 0 col typingPractice } ) || document.createElement("div") this.textArea.currentLine.incErrorCount() this.textArea.pageUp() if( document.body.innerHTML document.createElement("span") } = keyPressesCorrect, this.percentage length() } } const this.dom.classList.remove("after") } } currentTime = } TypingPractice(container, startTypingPractice() TextArea(this,this.mainArea,this.source) 0 return { } { this.source[col] { } this.source.length document.createElement("div") this.parent.keyPressesCorrect startTimer() of "Source source_lines.map((line,i) getAttribution()) new this.updateLines() ${onedp(wpm)} this.started row, this.wpmSpan.innerText new === href="${this.attribution}">${this.attribution}</a>` this.mid.classList.add("mid") filtered "" this.dom.classList.add("score_board") new { console.log("Restart this.dom.classList.add("score_board") = .replace(/—/g,"---") => this.appDom.classList.add("typing_app") = class return this.mainArea.style.backgroundColor moveLeft() signalCorrect() const container, ${row}/${numLines}` col Math.floor(x*10)/10 = update() currentTime } text this.parent document.createElement("span") = this.keyPressesTotal++ const "Escape" Math.floor(x*10)/10 { attribution.trim() const = },0) return 0 source, => LastKeys = = this.lastKeysCorrect this.parent typingPractice this.source.length = const } } = this.mainArea text source.replace(/\s+$/,"") update() .replace(/[“”]/g,'"') = } this.updateLines() = text this.lastKeysCorrect this.dom.appendChild(this.wpmSpan) this.tick.bind(this)() this.col startTimer() if( ) ) === { moveLeft() dt - TextArea(this,this.mainArea,this.source) { typingPractice.init() "ArrowLeft" this.parent "ArrowRight" } ) this.source return currentTime if( if( } if( `(${this.errorCount})` const this.updateLines() currentTime { this.attributionDom.innerText TextLine(this,this.dom,line,i)) this.dom.classList.add("score_board") this.textArea.moveRight() const advanceChar() this.keyPressesTotal++ const if( this.dom.classList.add("last_keys") keyPressesCorrect row TypingPractice(container, attrib .replace(/—/g,"---") "" this.setPos(this.row,this.col+1) 'wrong' }