2020-05-09 08:29:47 +08:00
let wordsList = [ ] ;
let currentWordIndex = 0 ;
let inputHistory = [ ] ;
let currentInput = "" ;
2020-05-10 01:39:23 +08:00
let time = 0 ;
2020-05-15 06:19:28 +08:00
let timers = [ ] ;
2020-05-09 08:29:47 +08:00
let testActive = false ;
let testStart , testEnd ;
2020-05-10 01:39:23 +08:00
let wpmHistory = [ ] ;
2020-05-31 21:31:50 +08:00
let rawHistory = [ ] ;
2020-05-17 20:17:08 +08:00
let restartCount = 0 ;
2020-06-09 01:12:34 +08:00
let incompleteTestSeconds = 0 ;
2020-05-22 23:19:09 +08:00
let currentTestLine = 0 ;
2020-05-23 00:59:28 +08:00
let pageTransition = false ;
2020-05-23 23:29:36 +08:00
let keypressPerSecond = [ ] ;
let currentKeypressCount = 0 ;
let afkDetected = false ;
2020-05-24 06:52:49 +08:00
let errorsPerSecond = [ ] ;
let currentErrorCount = 0 ;
2020-05-27 08:00:28 +08:00
let resultVisible = false ;
2020-06-01 04:03:54 +08:00
let activeWordTopBeforeJump = 0 ;
let activeWordTop = 0 ;
let activeWordJumped = false ;
2020-06-06 09:05:21 +08:00
let sameWordset = false ;
2020-07-03 07:01:46 +08:00
let quotes = [ ] ;
2020-07-13 06:53:15 +08:00
let focusState = false ;
2020-05-09 08:29:47 +08:00
2020-05-11 07:07:36 +08:00
let accuracyStats = {
correct : 0 ,
2020-07-03 08:35:45 +08:00
incorrect : 0 ,
} ;
2020-05-11 07:07:36 +08:00
2020-07-06 07:05:42 +08:00
let keypressStats = {
spacing : {
current : - 1 ,
array : [ ] ,
} ,
duration : {
current : - 1 ,
array : [ ] ,
} ,
} ;
2020-07-03 08:35:45 +08:00
let customText = "The quick brown fox jumps over the lazy dog" . split ( " " ) ;
2020-07-03 07:01:46 +08:00
let randomQuote = null ;
2020-05-09 08:29:47 +08:00
2020-07-03 08:35:45 +08:00
const testCompleted = firebase . functions ( ) . httpsCallable ( "testCompleted" ) ;
const addTag = firebase . functions ( ) . httpsCallable ( "addTag" ) ;
const editTag = firebase . functions ( ) . httpsCallable ( "editTag" ) ;
const removeTag = firebase . functions ( ) . httpsCallable ( "removeTag" ) ;
const updateResultTags = firebase . functions ( ) . httpsCallable ( "updateResultTags" ) ;
const saveConfig = firebase . functions ( ) . httpsCallable ( "saveConfig" ) ;
2020-07-07 23:39:48 +08:00
const generatePairingCode = firebase
. functions ( )
. httpsCallable ( "generatePairingCode" ) ;
2020-06-08 06:43:01 +08:00
2020-07-03 07:01:46 +08:00
function smooth ( arr , windowSize , getter = ( value ) => value , setter ) {
2020-07-03 08:35:45 +08:00
const get = getter ;
const result = [ ] ;
2020-07-03 07:01:46 +08:00
for ( let i = 0 ; i < arr . length ; i += 1 ) {
2020-07-03 08:35:45 +08:00
const leftOffeset = i - windowSize ;
const from = leftOffeset >= 0 ? leftOffeset : 0 ;
const to = i + windowSize + 1 ;
2020-07-03 07:01:46 +08:00
2020-07-03 08:35:45 +08:00
let count = 0 ;
let sum = 0 ;
2020-07-03 07:01:46 +08:00
for ( let j = from ; j < to && j < arr . length ; j += 1 ) {
2020-07-03 08:35:45 +08:00
sum += get ( arr [ j ] ) ;
count += 1 ;
2020-06-25 08:30:58 +08:00
}
2020-07-03 07:01:46 +08:00
2020-07-03 08:35:45 +08:00
result [ i ] = setter ? setter ( arr [ i ] , sum / count ) : sum / count ;
2020-06-25 08:30:58 +08:00
}
2020-07-03 08:35:45 +08:00
return result ;
2020-07-03 07:01:46 +08:00
}
2020-05-12 07:59:12 +08:00
function showNotification ( text , time ) {
let noti = $ ( ".notification" ) ;
noti . text ( text ) ;
2020-07-03 08:35:45 +08:00
noti . css ( "top" , ` - ${ noti . outerHeight ( ) } px ` ) ;
noti . stop ( true , true ) . animate (
{
top : "1rem" ,
} ,
250 ,
"swing" ,
( ) => {
noti . stop ( true , true ) . animate (
{
opacity : 1 ,
} ,
time ,
( ) => {
noti . stop ( true , true ) . animate (
{
top : ` - ${ noti . outerHeight ( ) } px ` ,
} ,
250 ,
"swing"
) ;
}
) ;
}
) ;
2020-05-12 07:59:12 +08:00
}
2020-06-05 07:00:45 +08:00
function copyResultToClipboard ( ) {
2020-07-03 08:35:45 +08:00
if ( navigator . userAgent . toLowerCase ( ) . indexOf ( "firefox" ) > - 1 ) {
showNotification ( "Sorry, this feature is not supported in Firefox" , 4000 ) ;
2020-07-03 07:01:46 +08:00
} else {
2020-07-03 08:35:45 +08:00
let src = $ ( "#middle" ) ;
var sourceX = src . position ( ) . left ; /*X position from div#target*/
var sourceY = src . position ( ) . top ; /*Y position from div#target*/
var sourceWidth = src . width ( ) ; /*clientWidth/offsetWidth from div#target*/
var sourceHeight = src . height ( ) ; /*clientHeight/offsetHeight from div#target*/
let bgColor = getComputedStyle ( document . body )
. getPropertyValue ( "--bg-color" )
. replace ( " " , "" ) ;
2020-07-03 07:01:46 +08:00
try {
html2canvas ( document . body , {
2020-06-15 08:45:05 +08:00
backgroundColor : bgColor ,
height : sourceHeight + 50 ,
width : sourceWidth + 50 ,
x : sourceX - 25 ,
2020-07-03 08:35:45 +08:00
y : sourceY - 25 ,
2020-07-03 07:01:46 +08:00
} ) . then ( function ( canvas ) {
2020-06-15 08:45:05 +08:00
// document.body.appendChild(canvas);
2020-07-03 07:01:46 +08:00
canvas . toBlob ( function ( blob ) {
2020-07-03 08:35:45 +08:00
navigator . clipboard
. write ( [
new ClipboardItem (
Object . defineProperty ( { } , blob . type , {
value : blob ,
enumerable : true ,
} )
) ,
] )
. then ( ( f ) => {
showNotification ( "Copied to clipboard" , 1000 ) ;
} )
. catch ( ( f ) => {
showNotification ( "Error saving image to clipboard" , 2000 ) ;
} ) ;
2020-06-15 08:45:05 +08:00
} ) ;
2020-06-15 08:28:52 +08:00
} ) ;
2020-07-03 07:01:46 +08:00
} catch ( e ) {
2020-07-03 08:35:45 +08:00
showNotification ( "Error creating image" , 2000 ) ;
2020-06-15 08:28:52 +08:00
}
2020-06-15 08:45:05 +08:00
}
2020-05-09 08:29:47 +08:00
}
2020-05-17 19:40:41 +08:00
function getReleasesFromGitHub ( ) {
2020-07-03 08:35:45 +08:00
$ . getJSON (
"https://api.github.com/repos/Miodec/monkey-type/releases" ,
( data ) => {
$ ( "#bottom .version" ) . text ( data [ 0 ] . name ) . css ( "opacity" , 1 ) ;
$ ( "#versionHistory .releases" ) . empty ( ) ;
data . forEach ( ( release ) => {
if ( ! release . draft && ! release . prerelease ) {
$ ( "#versionHistory .releases" ) . append ( `
2020-05-17 19:40:41 +08:00
< div class = "release" >
< div class = "title" > $ { release . name } < / d i v >
2020-07-03 08:35:45 +08:00
< div class = "date" > $ { moment ( release . published _at ) . format (
"DD MMM YYYY"
) } < / d i v >
< div class = "body" > $ { release . body . replace ( /\r\n/g , "<br>" ) } < / d i v >
2020-05-17 19:40:41 +08:00
< / d i v >
` );
2020-07-03 08:35:45 +08:00
}
} ) ;
}
) ;
2020-05-17 19:40:41 +08:00
}
2020-07-03 07:01:46 +08:00
function verifyUsername ( ) {
// test = firebase.functions().httpsCallable('moveResults')
// test2 = firebase.functions().httpsCallable('getNames')
// test3 = firebase.functions().httpsCallable('checkNameAvailability')
2020-07-03 08:35:45 +08:00
const check = firebase . functions ( ) . httpsCallable ( "checkIfNeedsToChangeName" ) ;
check ( { uid : firebase . auth ( ) . currentUser . uid } ) . then ( ( data ) => {
2020-07-03 07:01:46 +08:00
if ( data . data === 1 ) {
2020-07-03 08:35:45 +08:00
$ ( ".nameChangeMessage" ) . slideDown ( ) ;
2020-07-03 07:01:46 +08:00
} else if ( data . data === 2 ) {
2020-07-03 08:35:45 +08:00
$ ( ".nameChangeMessage" ) . slideDown ( ) ;
2020-06-06 23:48:22 +08:00
}
2020-07-03 08:35:45 +08:00
} ) ;
2020-06-06 23:48:22 +08:00
2020-07-03 08:35:45 +08:00
$ ( ".nameChangeMessage" ) . click ( ( e ) => {
2020-06-06 23:48:22 +08:00
alert ( ` Im currently preparing the system to be ready for leaderboards and other awesome features - it looks like you need to change your display name.
It either contains special characters , or your display name is the same as someone elses and your account was made later .
Sorry for this inconvenience .
` );
2020-07-03 08:35:45 +08:00
let newName = prompt (
"Please provide a new username - you can use lowercase and uppercase characters, numbers and one of these special characters ( . _ - ). The new name cannot be longer than 12 characters." ,
firebase . auth ( ) . currentUser . displayName
) ;
2020-07-03 07:01:46 +08:00
if ( newName ) {
2020-07-03 08:35:45 +08:00
cn = firebase . functions ( ) . httpsCallable ( "changeName" ) ;
cn ( { uid : firebase . auth ( ) . currentUser . uid , name : newName } ) . then ( ( d ) => {
2020-07-03 07:01:46 +08:00
if ( d . data === 1 ) {
2020-06-13 03:14:26 +08:00
//all good
2020-07-03 08:35:45 +08:00
alert ( "Thanks! All good." ) ;
2020-06-13 03:14:26 +08:00
location . reload ( ) ;
2020-07-03 08:35:45 +08:00
$ ( ".nameChangeMessage" ) . slideUp ( ) ;
2020-07-03 07:01:46 +08:00
} else if ( d . data === 0 ) {
2020-06-13 03:14:26 +08:00
//invalid or unavailable
2020-07-03 08:35:45 +08:00
alert ( "Name invalid or taken. Try again." ) ;
2020-07-03 07:01:46 +08:00
} else if ( d . data === - 1 ) {
2020-06-13 03:14:26 +08:00
//error
2020-07-03 08:35:45 +08:00
alert ( "Unknown error. Contact Miodec on Discord." ) ;
2020-06-13 03:14:26 +08:00
}
} ) ;
}
2020-07-03 08:35:45 +08:00
} ) ;
2020-06-06 23:48:22 +08:00
}
2020-07-12 00:03:25 +08:00
function getuid ( ) {
console . error ( "Only share this uid with Miodec and nobody else!" ) ;
console . log ( firebase . auth ( ) . currentUser . uid ) ;
console . error ( "Only share this uid with Miodec and nobody else!" ) ;
}
2020-05-11 07:32:10 +08:00
function getLastChar ( word ) {
return word . charAt ( word . length - 1 ) ;
}
2020-05-09 08:29:47 +08:00
function setFocus ( foc ) {
2020-07-13 06:53:15 +08:00
if ( foc && ! focusState ) {
focusState = true ;
2020-05-30 21:05:32 +08:00
stopCaretAnimation ( ) ;
2020-05-09 08:29:47 +08:00
$ ( "#top" ) . addClass ( "focus" ) ;
$ ( "#bottom" ) . addClass ( "focus" ) ;
$ ( "body" ) . css ( "cursor" , "none" ) ;
2020-06-15 01:19:56 +08:00
$ ( "#middle" ) . addClass ( "focus" ) ;
2020-07-13 06:53:15 +08:00
} else if ( ! foc && focusState ) {
focusState = false ;
2020-05-09 08:29:47 +08:00
startCaretAnimation ( ) ;
$ ( "#top" ) . removeClass ( "focus" ) ;
$ ( "#bottom" ) . removeClass ( "focus" ) ;
$ ( "body" ) . css ( "cursor" , "default" ) ;
2020-06-15 01:19:56 +08:00
$ ( "#middle" ) . removeClass ( "focus" ) ;
2020-05-09 08:29:47 +08:00
}
}
function capitalizeFirstLetter ( str ) {
return str . charAt ( 0 ) . toUpperCase ( ) + str . slice ( 1 ) ;
}
2020-07-03 07:01:46 +08:00
function roundedToFixed ( float , digits ) {
2020-05-25 23:50:50 +08:00
let rounded = Math . pow ( 10 , digits ) ;
return ( Math . round ( float * rounded ) / rounded ) . toFixed ( digits ) ;
}
2020-05-09 08:29:47 +08:00
function initWords ( ) {
testActive = false ;
wordsList = [ ] ;
currentWordIndex = 0 ;
2020-05-11 07:07:36 +08:00
accuracyStats = {
correct : 0 ,
2020-07-03 08:35:45 +08:00
incorrect : 0 ,
} ;
2020-05-09 08:29:47 +08:00
inputHistory = [ ] ;
currentInput = "" ;
2020-05-11 07:07:36 +08:00
2020-05-16 21:57:01 +08:00
let language = words [ config . language ] ;
2020-07-03 07:01:46 +08:00
if ( language === undefined && config . language === "english_10k" ) {
2020-06-29 06:16:28 +08:00
showBackgroundLoader ( ) ;
$ . ajax ( {
url : "js/english_10k.json" ,
2020-06-29 06:26:58 +08:00
async : false ,
success : function ( data ) {
hideBackgroundLoader ( ) ;
2020-07-03 08:35:45 +08:00
words [ "english_10k" ] = data ;
2020-06-29 06:49:10 +08:00
language = words [ config . language ] ;
2020-07-03 08:35:45 +08:00
} ,
} ) ;
2020-06-29 06:16:28 +08:00
}
2020-05-16 21:57:01 +08:00
2020-07-03 07:01:46 +08:00
if ( config . mode === "quote" ) {
showBackgroundLoader ( ) ;
$ . ajax ( {
url : "js/english_quotes.json" ,
async : false ,
success : function ( data ) {
hideBackgroundLoader ( ) ;
quotes = data ;
2020-07-03 08:35:45 +08:00
} ,
} ) ;
2020-07-03 07:01:46 +08:00
}
2020-05-16 21:57:01 +08:00
if ( language == undefined || language == [ ] ) {
2020-05-17 03:14:47 +08:00
config . language = "english" ;
language = words [ config . language ] ;
2020-05-16 21:57:01 +08:00
}
2020-05-11 07:07:36 +08:00
if ( config . mode == "time" || config . mode == "words" ) {
2020-07-13 22:53:12 +08:00
// let wordsBound = config.mode == "time" ? 60 : config.words;
let wordsBound = 60 ;
2020-07-17 06:38:55 +08:00
if ( config . showAllLines && config . mode != "time" ) {
2020-07-13 22:53:12 +08:00
wordsBound = config . words ;
2020-07-15 05:50:42 +08:00
} else {
if ( config . mode === "words" && config . words < wordsBound ) {
wordsBound = config . words ;
}
2020-07-13 22:53:12 +08:00
}
2020-05-22 10:19:54 +08:00
for ( let i = 0 ; i < wordsBound ; i ++ ) {
2020-05-16 21:57:01 +08:00
randomWord = language [ Math . floor ( Math . random ( ) * language . length ) ] ;
2020-05-09 08:29:47 +08:00
previousWord = wordsList [ i - 1 ] ;
2020-06-29 02:00:42 +08:00
previousWord2 = wordsList [ i - 2 ] ;
2020-07-03 08:35:45 +08:00
while (
randomWord == previousWord ||
randomWord == previousWord2 ||
( ! config . punctuation && randomWord == "I" ) ||
randomWord . indexOf ( " " ) > - 1
) {
2020-05-16 21:57:01 +08:00
randomWord = language [ Math . floor ( Math . random ( ) * language . length ) ] ;
2020-05-09 08:29:47 +08:00
}
2020-07-03 07:01:46 +08:00
if ( config . punctuation && config . mode != "custom" ) {
2020-05-22 10:19:54 +08:00
randomWord = punctuateWord ( previousWord , randomWord , i , wordsBound ) ;
}
2020-05-19 11:11:34 +08:00
wordsList . push ( randomWord ) ;
2020-05-09 08:29:47 +08:00
}
2020-05-10 06:33:48 +08:00
} else if ( config . mode == "custom" ) {
2020-07-01 05:23:50 +08:00
// let w = customText.split(" ");
for ( let i = 0 ; i < customText . length ; i ++ ) {
wordsList . push ( customText [ i ] ) ;
2020-05-09 08:29:47 +08:00
}
2020-07-03 07:01:46 +08:00
} else if ( config . mode == "quote" ) {
2020-07-22 07:24:39 +08:00
randomQuote = quotes [ Math . floor ( Math . random ( ) * quotes . length ) ] ;
let w = randomQuote . text . trim ( ) . split ( " " ) ;
2020-07-03 07:01:46 +08:00
for ( let i = 0 ; i < w . length ; i ++ ) {
wordsList . push ( w [ i ] ) ;
}
2020-05-09 08:29:47 +08:00
}
showWords ( ) ;
}
2020-07-03 07:01:46 +08:00
function emulateLayout ( event ) {
2020-07-03 08:35:45 +08:00
if ( config . layout == "default" || event . key === " " ) return event ;
const qwertyMasterLayout = {
Backquote : "`~" ,
Digit1 : "1!" ,
Digit2 : "2@" ,
Digit3 : "3#" ,
Digit4 : "4$" ,
Digit5 : "5%" ,
Digit6 : "6^" ,
Digit7 : "7&" ,
Digit8 : "8*" ,
Digit9 : "9(" ,
Digit0 : "0)" ,
Minus : "-_" ,
Equal : "=+" ,
KeyQ : "qQ" ,
KeyW : "wW" ,
KeyE : "eE" ,
KeyR : "rR" ,
KeyT : "tT" ,
KeyY : "yY" ,
KeyU : "uU" ,
KeyI : "iI" ,
KeyO : "oO" ,
KeyP : "pP" ,
BracketLeft : "[{" ,
BracketRight : "]}" ,
KeyA : "aA" ,
KeyS : "sS" ,
KeyD : "dD" ,
KeyF : "fF" ,
KeyG : "gG" ,
KeyH : "hH" ,
KeyJ : "jJ" ,
KeyK : "kK" ,
KeyL : "lL" ,
Semicolon : ";:" ,
Quote : "'\"" ,
Backslash : "\\|" ,
KeyZ : "zZ" ,
KeyX : "xX" ,
KeyC : "cC" ,
KeyV : "vV" ,
KeyB : "bB" ,
KeyN : "nN" ,
KeyM : "mM" ,
Comma : ",<" ,
Period : ".>" ,
Slash : "/?" ,
Space : " " ,
} ;
2020-06-04 00:10:14 +08:00
let layoutMap = layouts [ config . layout ] ;
let qwertyMap = layouts [ "qwerty" ] ;
2020-06-04 06:12:55 +08:00
let qwertyKey = qwertyMasterLayout [ event . code ] ;
2020-06-04 00:10:14 +08:00
let mapIndex ;
let newKey ;
2020-06-04 06:12:55 +08:00
let shift = event . shiftKey ? 1 : 0 ;
2020-06-04 00:10:14 +08:00
for ( let i = 0 ; i < qwertyMap . length ; i ++ ) {
const key = qwertyMap [ i ] ;
2020-06-04 06:12:55 +08:00
let keyIndex = key . indexOf ( qwertyKey ) ;
2020-07-03 07:01:46 +08:00
if ( keyIndex != - 1 ) {
2020-06-04 00:10:14 +08:00
mapIndex = i ;
}
}
2020-07-03 07:01:46 +08:00
if ( ! shift && /[A-Z]/gm . test ( event . key ) ) {
2020-06-04 06:12:55 +08:00
shift = 1 ;
}
2020-06-04 00:10:14 +08:00
newKey = layoutMap [ mapIndex ] [ shift ] ;
event . keyCode = newKey . charCodeAt ( 0 ) ;
event . charCode = newKey . charCodeAt ( 0 ) ;
event . which = newKey . charCodeAt ( 0 ) ;
event . key = newKey ;
return event ;
}
2020-07-03 07:01:46 +08:00
function punctuateWord ( previousWord , currentWord , index , maxindex ) {
2020-05-22 10:19:54 +08:00
let word = currentWord ;
2020-07-03 08:35:45 +08:00
if (
index == 0 ||
getLastChar ( previousWord ) == "." ||
getLastChar ( previousWord ) == "?" ||
getLastChar ( previousWord ) == "!"
) {
2020-05-22 10:19:54 +08:00
//always capitalise the first word or if there was a dot
word = capitalizeFirstLetter ( word ) ;
} else if (
//10% chance to end a sentence
2020-07-03 08:35:45 +08:00
( Math . random ( ) < 0.1 &&
getLastChar ( previousWord ) != "." &&
index != maxindex - 2 ) ||
index == maxindex - 1
) {
2020-05-22 10:19:54 +08:00
let rand = Math . random ( ) ;
if ( rand <= 0.8 ) {
word += "." ;
2020-07-03 08:35:45 +08:00
} else if ( rand > 0.8 && rand < 0.9 ) {
2020-05-22 10:19:54 +08:00
word += "?" ;
} else {
word += "!" ;
2020-05-11 07:07:36 +08:00
}
2020-07-03 08:35:45 +08:00
} else if (
Math . random ( ) < 0.01 &&
2020-05-22 10:19:54 +08:00
getLastChar ( previousWord ) != "," &&
2020-07-03 08:35:45 +08:00
getLastChar ( previousWord ) != "."
) {
2020-05-22 10:19:54 +08:00
//1% chance to add quotes
word = ` " ${ word } " ` ;
} else if ( Math . random ( ) < 0.01 ) {
//1% chance to add a colon
word = word + ":" ;
} else if (
Math . random ( ) < 0.01 &&
getLastChar ( previousWord ) != "," &&
getLastChar ( previousWord ) != "." &&
previousWord != "-"
) {
//1% chance to add a dash
word = "-" ;
2020-07-03 08:35:45 +08:00
} else if ( Math . random ( ) < 0.2 && getLastChar ( previousWord ) != "," ) {
2020-05-22 10:19:54 +08:00
//2% chance to add a comma
word += "," ;
}
return word ;
2020-05-11 07:07:36 +08:00
}
2020-05-09 08:29:47 +08:00
function addWord ( ) {
2020-07-13 22:53:12 +08:00
if (
2020-07-15 05:50:42 +08:00
! config . showAllLines &&
( wordsList . length - inputHistory . length > 60 ||
( config . mode === "words" && wordsList . length >= config . words ) )
2020-07-13 22:53:12 +08:00
)
return ;
2020-06-29 06:49:10 +08:00
let language = words [ config . language ] ;
2020-05-17 00:54:19 +08:00
let randomWord = language [ Math . floor ( Math . random ( ) * language . length ) ] ;
2020-05-22 10:19:54 +08:00
previousWord = wordsList [ wordsList . length - 1 ] ;
2020-07-03 08:35:45 +08:00
previousWordStripped = previousWord . replace ( /[.?!":\-,]/g , "" ) . toLowerCase ( ) ;
while (
previousWordStripped == randomWord ||
randomWord . indexOf ( " " ) > - 1 ||
( ! config . punctuation && randomWord == "I" )
) {
2020-05-17 00:57:42 +08:00
randomWord = language [ Math . floor ( Math . random ( ) * language . length ) ] ;
}
2020-07-03 07:01:46 +08:00
if ( config . punctuation && config . mode != "custom" ) {
2020-07-03 08:35:45 +08:00
randomWord = punctuateWord ( previousWord , randomWord , wordsList . length , 0 ) ;
2020-05-22 10:19:54 +08:00
}
2020-05-09 08:29:47 +08:00
wordsList . push ( randomWord ) ;
2020-05-22 10:19:54 +08:00
2020-05-09 08:29:47 +08:00
let w = "<div class='word'>" ;
for ( let c = 0 ; c < randomWord . length ; c ++ ) {
w += "<letter>" + randomWord . charAt ( c ) + "</letter>" ;
}
w += "</div>" ;
$ ( "#words" ) . append ( w ) ;
}
function showWords ( ) {
$ ( "#words" ) . empty ( ) ;
2020-07-15 05:50:42 +08:00
2020-07-13 22:53:12 +08:00
for ( let i = 0 ; i < wordsList . length ; i ++ ) {
let w = "<div class='word'>" ;
for ( let c = 0 ; c < wordsList [ i ] . length ; c ++ ) {
w += "<letter>" + wordsList [ i ] . charAt ( c ) + "</letter>" ;
2020-05-09 08:29:47 +08:00
}
2020-07-13 22:53:12 +08:00
w += "</div>" ;
$ ( "#words" ) . append ( w ) ;
}
2020-07-15 05:50:42 +08:00
2020-07-13 22:53:12 +08:00
$ ( "#words" ) . removeClass ( "hidden" ) ;
2020-07-17 06:38:55 +08:00
if ( config . showAllLines && config . mode != "time" ) {
2020-07-15 05:50:42 +08:00
$ ( "#words" ) . css ( "height" , "auto" ) ;
} else {
const wordHeight = $ ( $ ( ".word" ) [ 0 ] ) . outerHeight ( true ) ;
$ ( "#words" )
. css ( "height" , wordHeight * 3 + "px" )
. css ( "overflow" , "hidden" ) ;
}
2020-05-09 08:29:47 +08:00
updateActiveElement ( ) ;
updateCaretPosition ( ) ;
}
function updateActiveElement ( ) {
2020-07-13 06:53:15 +08:00
let active = document . querySelector ( "#words .active" ) ;
if ( active !== null ) {
active . classList . remove ( "active" ) ;
}
// $("#words .word").removeClass("active");
// $($("#words .word")[currentWordIndex]).addClass("active").removeClass("error");
// document.querySelectorAll("#words .word")[currentWordIndex].classList.add("active");
try {
let activeWord = document . querySelectorAll ( "#words .word" ) [
currentWordIndex
] ;
activeWord . classList . add ( "active" ) ;
activeWord . classList . remove ( "error" ) ;
// activeWordTop = $("#words .word.active").position().top;
activeWordTop = document . querySelector ( "#words .active" ) . offsetTop ;
} catch ( e ) { }
2020-05-09 08:29:47 +08:00
}
2020-07-03 07:01:46 +08:00
function compareInput ( wrdIndex , input , showError ) {
2020-07-13 06:53:15 +08:00
// let wrdAtIndex = $("#words .word")[wrdIndex];
let wordAtIndex ;
let currentWord ;
if ( wrdIndex === null ) {
wordAtIndex = document . querySelector ( "#words .word.active" ) ;
currentWord = wordsList [ currentWordIndex ] ;
} else {
wordAtIndex = document . querySelectorAll ( "#words .word" ) [ wrdIndex ] ;
2020-07-14 23:41:09 +08:00
currentWord = wordsList [ wrdIndex ] ;
2020-07-13 06:53:15 +08:00
}
// while (wordAtIndex.firstChild) {
// wordAtIndex.removeChild(wordAtIndex.firstChild);
// }
2020-05-11 07:32:10 +08:00
let ret = "" ;
2020-07-13 06:53:15 +08:00
2020-05-31 04:30:50 +08:00
for ( let i = 0 ; i < input . length ; i ++ ) {
if ( currentWord [ i ] == input [ i ] ) {
2020-05-11 07:32:10 +08:00
ret += '<letter class="correct">' + currentWord [ i ] + "</letter>" ;
// $(letterElems[i]).removeClass('incorrect').addClass('correct');
} else {
2020-07-03 07:01:46 +08:00
if ( config . difficulty == "master" ) {
2020-07-23 20:48:04 +08:00
if ( ! resultVisible ) {
inputHistory . push ( currentInput ) ;
document
. querySelector ( "#words .word.active" )
. setAttribute ( "input" , currentInput ) ;
showResult ( true ) ;
}
2020-07-03 07:01:46 +08:00
if ( ! afkDetected ) {
2020-06-15 01:19:56 +08:00
let testNow = Date . now ( ) ;
let testSeconds = roundTo2 ( ( testNow - testStart ) / 1000 ) ;
incompleteTestSeconds += testSeconds ;
restartCount ++ ;
}
2020-05-29 01:41:01 +08:00
}
2020-07-03 07:01:46 +08:00
if ( ! showError ) {
2020-05-30 20:15:00 +08:00
if ( currentWord [ i ] == undefined ) {
2020-05-31 04:30:50 +08:00
// ret += '<letter class="correct">' + input[i] + "</letter>";
2020-05-30 20:15:00 +08:00
} else {
ret += '<letter class="correct">' + currentWord [ i ] + "</letter>" ;
}
2020-07-03 07:01:46 +08:00
} else {
2020-05-30 20:15:00 +08:00
if ( currentWord [ i ] == undefined ) {
2020-05-31 04:30:50 +08:00
ret += '<letter class="incorrect extra">' + input [ i ] + "</letter>" ;
2020-05-30 20:15:00 +08:00
} else {
ret += '<letter class="incorrect">' + currentWord [ i ] + "</letter>" ;
}
2020-05-11 07:32:10 +08:00
}
2020-05-09 08:29:47 +08:00
}
}
2020-07-13 06:53:15 +08:00
2020-05-31 04:30:50 +08:00
if ( input . length < currentWord . length ) {
for ( let i = input . length ; i < currentWord . length ; i ++ ) {
2020-05-11 07:32:10 +08:00
ret += "<letter>" + currentWord [ i ] + "</letter>" ;
}
}
2020-07-13 06:53:15 +08:00
wordAtIndex . innerHTML = ret ;
2020-07-13 07:44:03 +08:00
let lastindex = currentWordIndex ;
if ( wrdIndex !== null ) {
lastindex = wrdIndex ;
}
2020-07-03 08:35:45 +08:00
if (
( currentWord == input ||
( config . quickEnd && currentWord . length == input . length ) ) &&
2020-07-13 07:44:03 +08:00
lastindex == wordsList . length - 1
2020-07-03 08:35:45 +08:00
) {
2020-05-31 04:30:50 +08:00
inputHistory . push ( input ) ;
2020-05-11 07:32:10 +08:00
currentInput = "" ;
2020-07-03 07:01:46 +08:00
if ( ! resultVisible ) showResult ( ) ;
2020-05-11 07:32:10 +08:00
}
// liveWPM()
2020-05-09 08:29:47 +08:00
}
2020-07-03 07:01:46 +08:00
function highlightBadWord ( index , showError ) {
if ( ! showError ) return ;
2020-05-31 04:30:50 +08:00
$ ( $ ( "#words .word" ) [ index ] ) . addClass ( "error" ) ;
2020-05-09 08:29:47 +08:00
}
2020-05-11 07:32:10 +08:00
function showTimer ( ) {
2020-07-18 00:01:34 +08:00
let op = config . showTimerBar ? config . timerOpacity : 0 ;
2020-07-03 07:01:46 +08:00
if ( config . timerStyle === "bar" ) {
2020-07-13 22:14:07 +08:00
// let op = 0.25;
// if (
// $("#timerNumber").hasClass("timerSub") ||
// $("#timerNumber").hasClass("timerText") ||
// $("#timerNumber").hasClass("timerMain")
// ) {
// op = 1;
// }
2020-07-06 07:41:06 +08:00
$ ( "#timerWrapper" ) . stop ( true , true ) . removeClass ( "hidden" ) . animate (
2020-07-03 08:35:45 +08:00
{
2020-07-18 00:01:34 +08:00
opacity : op ,
2020-07-03 08:35:45 +08:00
} ,
250
) ;
2020-07-23 20:53:24 +08:00
} else if ( config . timerStyle === "text" ) {
2020-07-13 22:14:07 +08:00
// let op = 0.25;
// if (
// $("#timerNumber").hasClass("timerSub") ||
// $("#timerNumber").hasClass("timerText") ||
// $("#timerNumber").hasClass("timerMain")
// ) {
// op = 1;
// }
2020-07-06 07:41:06 +08:00
$ ( "#timerNumber" ) . stop ( true , true ) . removeClass ( "hidden" ) . animate (
2020-07-03 08:35:45 +08:00
{
2020-07-18 00:01:34 +08:00
opacity : op ,
2020-07-03 08:35:45 +08:00
} ,
250
) ;
2020-06-16 06:13:40 +08:00
}
2020-05-11 07:32:10 +08:00
}
function hideTimer ( ) {
2020-07-03 07:01:46 +08:00
if ( config . timerStyle === "bar" ) {
2020-07-06 07:41:06 +08:00
$ ( "#timerWrapper" ) . stop ( true , true ) . animate (
2020-07-03 08:35:45 +08:00
{
opacity : 0 ,
} ,
125
) ;
2020-07-03 07:01:46 +08:00
} else if ( config . timerStyle === "text" ) {
2020-07-06 07:41:06 +08:00
$ ( "#timerNumber" ) . stop ( true , true ) . animate (
2020-07-03 08:35:45 +08:00
{
opacity : 0 ,
} ,
125
) ;
2020-06-16 06:13:40 +08:00
}
2020-06-16 05:52:47 +08:00
}
2020-07-12 07:41:14 +08:00
function changeTimerColor ( color ) {
$ ( "#timer" ) . removeClass ( "timerSub" ) ;
$ ( "#timer" ) . removeClass ( "timerText" ) ;
$ ( "#timer" ) . removeClass ( "timerMain" ) ;
$ ( "#timerNumber" ) . removeClass ( "timerSub" ) ;
$ ( "#timerNumber" ) . removeClass ( "timerText" ) ;
$ ( "#timerNumber" ) . removeClass ( "timerMain" ) ;
$ ( "#liveWpm" ) . removeClass ( "timerSub" ) ;
$ ( "#liveWpm" ) . removeClass ( "timerText" ) ;
$ ( "#liveWpm" ) . removeClass ( "timerMain" ) ;
if ( color === "main" ) {
$ ( "#timer" ) . addClass ( "timerMain" ) ;
$ ( "#timerNumber" ) . addClass ( "timerMain" ) ;
$ ( "#liveWpm" ) . addClass ( "timerMain" ) ;
} else if ( color === "sub" ) {
$ ( "#timer" ) . addClass ( "timerSub" ) ;
$ ( "#timerNumber" ) . addClass ( "timerSub" ) ;
$ ( "#liveWpm" ) . addClass ( "timerSub" ) ;
} else if ( color === "text" ) {
$ ( "#timer" ) . addClass ( "timerText" ) ;
$ ( "#timerNumber" ) . addClass ( "timerText" ) ;
$ ( "#liveWpm" ) . addClass ( "timerText" ) ;
}
}
2020-06-16 05:52:47 +08:00
function restartTimer ( ) {
2020-07-03 07:01:46 +08:00
if ( config . timerStyle === "bar" ) {
if ( config . mode === "time" ) {
2020-07-03 08:35:45 +08:00
$ ( "#timer" ) . stop ( true , true ) . animate (
{
width : "100vw" ,
} ,
0
) ;
2020-07-03 07:01:46 +08:00
} else if ( config . mode === "words" || config . mode === "custom" ) {
2020-07-03 08:35:45 +08:00
$ ( "#timer" ) . stop ( true , true ) . animate (
{
width : "0vw" ,
} ,
0
) ;
2020-06-22 06:51:48 +08:00
}
2020-06-16 06:13:40 +08:00
}
2020-05-11 07:32:10 +08:00
}
2020-06-16 06:13:40 +08:00
function updateTimer ( ) {
2020-07-18 00:01:34 +08:00
if ( ! config . showTimerBar ) return ;
2020-07-03 07:01:46 +08:00
if ( config . mode === "time" ) {
if ( config . timerStyle === "bar" ) {
2020-07-03 08:35:45 +08:00
let percent = 100 - ( ( time + 1 ) / config . time ) * 100 ;
$ ( "#timer" )
. stop ( true , true )
. animate (
{
width : percent + "vw" ,
} ,
1000 ,
"linear"
) ;
2020-07-03 07:01:46 +08:00
} else if ( config . timerStyle === "text" ) {
2020-07-04 00:39:38 +08:00
var displayTime = new Date ( null ) ;
displayTime . setSeconds ( config . time - time ) ;
displayTime = displayTime . toISOString ( ) . substr ( 11 , 8 ) ;
while (
displayTime . substr ( 0 , 2 ) == "00" ||
displayTime [ 0 ] == ":" ||
( displayTime . length == 2 && displayTime [ 0 ] == "0" )
) {
if ( displayTime . substr ( 0 , 2 ) == "00" ) {
displayTime = displayTime . substr ( 3 ) ;
} else {
displayTime = displayTime . substr ( 1 ) ;
}
}
$ ( "#timerNumber" ) . html ( displayTime ) ;
// $("#timerNumber").html(config.time - time);
2020-06-22 06:51:48 +08:00
}
2020-07-03 08:35:45 +08:00
} else if (
2020-07-13 22:59:13 +08:00
config . mode === "words" ||
config . mode === "custom" ||
config . mode === "quote"
2020-07-03 08:35:45 +08:00
) {
2020-07-13 22:59:13 +08:00
if ( config . timerStyle === "bar" ) {
let percent = Math . floor (
( ( currentWordIndex + 1 ) / wordsList . length ) * 100
2020-07-03 08:35:45 +08:00
) ;
2020-07-13 22:59:13 +08:00
$ ( "#timer" )
. stop ( true , true )
. animate (
{
width : percent + "vw" ,
} ,
250
) ;
} else if ( config . timerStyle === "text" ) {
let outof = wordsList . length ;
if ( config . mode === "words" ) {
outof = config . words ;
}
$ ( "#timerNumber" ) . html ( ` ${ inputHistory . length } / ${ outof } ` ) ;
// $("#timerNumber").html(config.time - time);
}
2020-06-16 06:13:40 +08:00
}
2020-05-11 07:32:10 +08:00
}
2020-05-09 08:29:47 +08:00
function hideCaret ( ) {
$ ( "#caret" ) . addClass ( "hidden" ) ;
}
function showCaret ( ) {
2020-07-03 08:35:45 +08:00
if ( $ ( "#result" ) . hasClass ( "hidden" ) ) {
2020-05-24 06:52:49 +08:00
updateCaretPosition ( ) ;
$ ( "#caret" ) . removeClass ( "hidden" ) ;
startCaretAnimation ( ) ;
}
2020-05-09 08:29:47 +08:00
}
function stopCaretAnimation ( ) {
$ ( "#caret" ) . css ( "animation-name" , "none" ) ;
2020-05-29 12:39:51 +08:00
$ ( "#caret" ) . css ( "opacity" , "1" ) ;
2020-05-09 08:29:47 +08:00
}
function startCaretAnimation ( ) {
$ ( "#caret" ) . css ( "animation-name" , "caretFlash" ) ;
}
function updateCaretPosition ( ) {
2020-07-13 06:53:15 +08:00
// return;
2020-07-03 08:35:45 +08:00
if ( $ ( "#words" ) . hasClass ( "hidden" ) ) return ;
2020-07-13 06:53:15 +08:00
2020-05-09 08:29:47 +08:00
let caret = $ ( "#caret" ) ;
2020-07-13 06:53:15 +08:00
// let activeWord = $("#words .word.active");
2020-05-09 08:29:47 +08:00
let inputLen = currentInput . length ;
let currentLetterIndex = inputLen - 1 ;
if ( currentLetterIndex == - 1 ) {
currentLetterIndex = 0 ;
}
2020-07-13 06:53:15 +08:00
// let currentLetter = $("#words .word.active letter")[currentLetterIndex];
2020-07-13 07:44:03 +08:00
try {
let currentLetter = document
. querySelector ( "#words .active" )
. querySelectorAll ( "letter" ) [ currentLetterIndex ] ;
if ( $ ( currentLetter ) . length == 0 ) return ;
let currentLetterPosLeft = currentLetter . offsetLeft ;
let currentLetterPosTop = currentLetter . offsetTop ;
let letterHeight = $ ( currentLetter ) . height ( ) ;
let newTop = 0 ;
let newLeft = 0 ;
newTop = currentLetterPosTop - letterHeight / 4 ;
if ( inputLen == 0 ) {
newLeft = currentLetterPosLeft - caret . width ( ) / 2 ;
} else {
newLeft =
currentLetterPosLeft + $ ( currentLetter ) . width ( ) - caret . width ( ) / 2 ;
}
2020-05-10 02:47:11 +08:00
2020-07-13 07:44:03 +08:00
let duration = 0 ;
2020-05-10 02:47:11 +08:00
2020-07-13 07:44:03 +08:00
if ( config . smoothCaret ) {
duration = 100 ;
// if (Math.round(caret[0].offsetTop) != Math.round(newTop)) {
// caret.css("top", newTop);
// duration = 10;
// }
}
2020-05-10 02:47:11 +08:00
2020-07-13 07:44:03 +08:00
caret . stop ( true , true ) . animate (
{
top : newTop ,
left : newLeft ,
} ,
duration
) ;
2020-05-10 06:33:48 +08:00
2020-07-15 05:50:42 +08:00
if ( config . showAllLines ) {
let browserHeight = window . innerHeight ;
let middlePos = browserHeight / 2 - $ ( "#caret" ) . outerHeight ( ) / 2 ;
let contentHeight = document . body . scrollHeight ;
2020-07-03 07:01:46 +08:00
2020-07-15 05:50:42 +08:00
if ( newTop >= middlePos && contentHeight > browserHeight ) {
window . scrollTo ( {
left : 0 ,
top : newTop - middlePos ,
behavior : "smooth" ,
} ) ;
}
}
2020-07-13 07:44:03 +08:00
} catch ( e ) { }
2020-05-11 07:07:36 +08:00
}
function countChars ( ) {
let correctWordChars = 0 ;
2020-05-09 08:29:47 +08:00
let correctChars = 0 ;
let incorrectChars = 0 ;
2020-05-11 07:07:36 +08:00
let extraChars = 0 ;
let missedChars = 0 ;
2020-05-28 20:10:20 +08:00
let spaces = 0 ;
2020-07-23 00:11:59 +08:00
let correctspaces = 0 ;
2020-05-09 08:29:47 +08:00
for ( let i = 0 ; i < inputHistory . length ; i ++ ) {
2020-05-11 07:07:36 +08:00
if ( inputHistory [ i ] == wordsList [ i ] ) {
//the word is correct
2020-05-28 20:10:20 +08:00
correctWordChars += wordsList [ i ] . length ;
2020-05-11 07:07:36 +08:00
correctChars += wordsList [ i ] . length ;
2020-07-23 00:11:59 +08:00
if ( i < inputHistory . length - 1 ) {
correctspaces ++ ;
}
2020-05-11 07:07:36 +08:00
} else if ( inputHistory [ i ] . length >= wordsList [ i ] . length ) {
//too many chars
2020-05-11 07:32:10 +08:00
for ( let c = 0 ; c < inputHistory [ i ] . length ; c ++ ) {
2020-05-11 07:07:36 +08:00
if ( c < wordsList [ i ] . length ) {
//on char that still has a word list pair
if ( inputHistory [ i ] [ c ] == wordsList [ i ] [ c ] ) {
correctChars ++ ;
} else {
incorrectChars ++ ;
}
2020-05-09 08:29:47 +08:00
} else {
2020-05-11 07:07:36 +08:00
//on char that is extra
extraChars ++ ;
}
}
} else {
//not enough chars
2020-05-11 07:32:10 +08:00
for ( let c = 0 ; c < wordsList [ i ] . length ; c ++ ) {
2020-05-11 07:07:36 +08:00
if ( c < inputHistory [ i ] . length ) {
//on char that still has a word list pair
if ( inputHistory [ i ] [ c ] == wordsList [ i ] [ c ] ) {
correctChars ++ ;
} else {
incorrectChars ++ ;
}
} else {
//on char that is extra
missedChars ++ ;
2020-05-09 08:29:47 +08:00
}
}
}
2020-07-03 07:01:46 +08:00
if ( i < inputHistory . length - 1 ) {
2020-05-28 20:10:20 +08:00
spaces ++ ;
}
2020-05-09 08:29:47 +08:00
}
2020-05-11 07:07:36 +08:00
return {
2020-05-28 20:10:20 +08:00
spaces : spaces ,
2020-05-11 07:07:36 +08:00
correctWordChars : correctWordChars ,
allCorrectChars : correctChars ,
incorrectChars : incorrectChars ,
extraChars : extraChars ,
2020-07-03 08:35:45 +08:00
missedChars : missedChars ,
2020-07-23 00:11:59 +08:00
correctSpaces : correctspaces ,
2020-07-03 08:35:45 +08:00
} ;
2020-05-09 08:29:47 +08:00
}
2020-07-03 07:01:46 +08:00
function roundTo2 ( num ) {
2020-07-03 08:35:45 +08:00
return Math . round ( ( num + Number . EPSILON ) * 100 ) / 100 ;
2020-05-31 21:31:50 +08:00
}
2020-05-11 07:32:10 +08:00
function calculateStats ( ) {
2020-05-29 01:51:52 +08:00
if ( config . mode == "words" && config . difficulty == "normal" ) {
2020-05-11 07:32:10 +08:00
if ( inputHistory . length != wordsList . length ) return ;
2020-05-09 08:29:47 +08:00
}
2020-05-11 07:32:10 +08:00
let chars = countChars ( ) ;
2020-05-11 07:07:36 +08:00
let testNow = Date . now ( ) ;
2020-05-31 21:31:50 +08:00
let testSeconds = roundTo2 ( ( testNow - testStart ) / 1000 ) ;
2020-07-03 08:35:45 +08:00
let wpm = roundTo2 (
2020-07-23 00:11:59 +08:00
( ( chars . correctWordChars + chars . correctSpaces ) * ( 60 / testSeconds ) ) / 5
2020-07-03 08:35:45 +08:00
) ;
2020-07-23 01:29:55 +08:00
console . log (
` pre-spacegate ${ roundTo2 (
( ( chars . correctWordChars + chars . spaces ) * ( 60 / testSeconds ) ) / 5
) } ( current $ { wpm } ) `
) ;
2020-07-03 08:35:45 +08:00
let wpmraw = roundTo2 (
( ( chars . allCorrectChars +
chars . spaces +
chars . incorrectChars +
chars . extraChars ) *
( 60 / testSeconds ) ) /
5
) ;
let acc = roundTo2 (
( accuracyStats . correct /
( accuracyStats . correct + accuracyStats . incorrect ) ) *
100
) ;
2020-05-25 23:50:50 +08:00
return {
2020-06-23 23:27:33 +08:00
wpm : isNaN ( wpm ) ? 0 : wpm ,
wpmRaw : isNaN ( wpmraw ) ? 0 : wpmraw ,
2020-05-25 23:50:50 +08:00
acc : acc ,
2020-05-27 00:49:28 +08:00
correctChars : chars . correctWordChars ,
2020-05-25 23:50:50 +08:00
incorrectChars : chars . incorrectChars + chars . extraChars + chars . missedChars ,
2020-05-28 20:19:03 +08:00
time : testSeconds ,
2020-07-03 08:35:45 +08:00
spaces : chars . spaces ,
2020-07-23 00:11:59 +08:00
correctSpaces : chars . correctSpaces ,
2020-05-25 23:50:50 +08:00
} ;
2020-05-10 01:39:23 +08:00
}
2020-05-12 07:59:12 +08:00
function hideCrown ( ) {
2020-07-03 08:35:45 +08:00
$ ( "#result .stats .wpm .crownWrapper" ) . css ( "opacity" , 0 ) ;
2020-05-12 07:59:12 +08:00
}
function showCrown ( ) {
2020-07-03 08:35:45 +08:00
$ ( "#result .stats .wpm .crownWrapper" ) . animate (
{
opacity : 1 ,
} ,
250 ,
"easeOutCubic"
) ;
2020-05-12 07:59:12 +08:00
}
2020-05-29 02:01:44 +08:00
function showResult ( difficultyFailed = false ) {
2020-07-03 07:01:46 +08:00
resultVisible = true ;
2020-05-10 01:39:23 +08:00
testEnd = Date . now ( ) ;
2020-05-25 23:50:50 +08:00
testActive = false ;
setFocus ( false ) ;
hideCaret ( ) ;
hideLiveWpm ( ) ;
2020-06-16 06:13:40 +08:00
hideTimer ( ) ;
2020-05-29 21:28:15 +08:00
testInvalid = false ;
2020-05-10 01:39:23 +08:00
let stats = calculateStats ( ) ;
2020-07-03 07:01:46 +08:00
if ( stats === undefined ) {
2020-05-22 22:41:34 +08:00
stats = {
wpm : 0 ,
2020-05-27 00:49:28 +08:00
wpmRaw : 0 ,
2020-05-22 22:41:34 +08:00
acc : 0 ,
correctChars : 0 ,
2020-05-25 23:50:50 +08:00
incorrectChars : 0 ,
2020-05-28 20:19:03 +08:00
time : 0 ,
2020-07-03 08:35:45 +08:00
spaces : 0 ,
2020-07-23 00:11:59 +08:00
correctSpaces : 0 ,
2020-07-03 08:35:45 +08:00
} ;
2020-05-22 22:41:34 +08:00
}
2020-05-15 06:19:28 +08:00
clearIntervals ( ) ;
2020-07-03 07:01:46 +08:00
let testtime = roundedToFixed ( stats . time , 1 ) ;
2020-05-31 22:37:09 +08:00
$ ( "#result .stats .wpm .bottom" ) . text ( Math . round ( stats . wpm ) ) ;
2020-07-17 06:49:57 +08:00
$ ( "#result .stats .wpm .bottom" ) . attr ( "aria-label" , stats . wpm ) ;
2020-05-31 22:37:09 +08:00
$ ( "#result .stats .raw .bottom" ) . text ( Math . round ( stats . wpmRaw ) ) ;
2020-07-17 06:49:57 +08:00
$ ( "#result .stats .raw .bottom" ) . attr ( "aria-label" , stats . wpmRaw ) ;
2020-06-25 05:13:01 +08:00
$ ( "#result .stats .acc .bottom" ) . text ( Math . floor ( stats . acc ) + "%" ) ;
2020-07-17 06:49:57 +08:00
$ ( "#result .stats .acc .bottom" ) . attr ( "aria-label" , stats . acc + "%" ) ;
2020-07-03 08:35:45 +08:00
$ ( "#result .stats .key .bottom" ) . text (
2020-07-23 00:11:59 +08:00
stats . correctChars + stats . correctSpaces + "/" + stats . incorrectChars
2020-07-03 08:35:45 +08:00
) ;
$ ( "#result .stats .time .bottom" ) . text ( testtime + "s" ) ;
2020-05-27 00:49:28 +08:00
2020-07-03 07:01:46 +08:00
setTimeout ( function ( ) {
2020-07-03 08:35:45 +08:00
$ ( "#resultExtraButtons" ) . removeClass ( "hidden" ) . css ( "opacity" , 0 ) . animate (
{
opacity : 1 ,
} ,
125
) ;
2020-05-27 08:00:28 +08:00
} , 125 ) ;
2020-07-03 07:01:46 +08:00
2020-06-15 02:10:59 +08:00
$ ( "#testModesNotice" ) . css ( {
2020-07-03 08:35:45 +08:00
opacity : 0 ,
2020-06-15 02:10:59 +08:00
// 'height': 0,
// 'margin-bottom': 0
} ) ;
2020-05-27 08:00:28 +08:00
2020-07-07 02:40:52 +08:00
$ ( "#result .stats .leaderboards .bottom" ) . text ( "" ) ;
$ ( "#result .stats .leaderboards" ) . addClass ( "hidden" ) ;
2020-05-10 06:33:48 +08:00
let mode2 = "" ;
2020-06-28 05:17:41 +08:00
if ( config . mode === "time" ) {
2020-05-10 06:33:48 +08:00
mode2 = config . time ;
2020-05-27 00:49:28 +08:00
// $("#result .stats .time").addClass('hidden');
2020-06-28 05:17:41 +08:00
} else if ( config . mode === "words" ) {
2020-05-10 06:33:48 +08:00
mode2 = config . words ;
2020-05-27 00:49:28 +08:00
// $("#result .stats .time").removeClass('hidden');
// $("#result .stats .time .bottom").text(roundedToFixed(stats.time,1)+'s');
2020-07-03 07:01:46 +08:00
} else if ( config . mode === "custom" ) {
2020-06-28 05:17:41 +08:00
mode2 = "custom" ;
2020-07-03 22:38:00 +08:00
} else if ( config . mode === "quote" ) {
mode2 = randomQuote . id ;
2020-05-10 06:33:48 +08:00
}
2020-06-25 06:25:12 +08:00
2020-05-10 01:39:23 +08:00
let labels = [ ] ;
2020-05-10 02:47:11 +08:00
for ( let i = 1 ; i <= wpmHistory . length ; i ++ ) {
labels . push ( i . toString ( ) ) ;
2020-05-10 01:39:23 +08:00
}
2020-07-03 08:35:45 +08:00
let mainColor = getComputedStyle ( document . body )
. getPropertyValue ( "--main-color" )
. replace ( " " , "" ) ;
let subColor = getComputedStyle ( document . body )
. getPropertyValue ( "--sub-color" )
. replace ( " " , "" ) ;
let bgColor = getComputedStyle ( document . body )
. getPropertyValue ( "--bg-color" )
. replace ( " " , "" ) ;
2020-05-15 11:09:00 +08:00
2020-05-11 23:56:10 +08:00
wpmOverTimeChart . options . scales . xAxes [ 0 ] . ticks . minor . fontColor = subColor ;
2020-05-24 06:52:49 +08:00
wpmOverTimeChart . options . scales . xAxes [ 0 ] . scaleLabel . fontColor = subColor ;
2020-05-11 23:56:10 +08:00
wpmOverTimeChart . options . scales . yAxes [ 0 ] . ticks . minor . fontColor = subColor ;
2020-05-31 21:54:54 +08:00
wpmOverTimeChart . options . scales . yAxes [ 2 ] . ticks . minor . fontColor = subColor ;
2020-05-24 06:52:49 +08:00
wpmOverTimeChart . options . scales . yAxes [ 0 ] . scaleLabel . fontColor = subColor ;
2020-05-31 21:54:54 +08:00
wpmOverTimeChart . options . scales . yAxes [ 2 ] . scaleLabel . fontColor = subColor ;
2020-05-24 06:52:49 +08:00
2020-05-31 21:54:54 +08:00
wpmOverTimeChart . data . labels = labels ;
2020-05-24 06:52:49 +08:00
2020-07-03 08:35:45 +08:00
let rawWpmPerSecond = keypressPerSecond . map ( ( f ) => Math . round ( ( f / 5 ) * 60 ) ) ;
2020-07-03 07:01:46 +08:00
rawWpmPerSecond = smooth ( rawWpmPerSecond , 1 ) ;
2020-06-25 05:13:01 +08:00
2020-05-11 23:29:18 +08:00
wpmOverTimeChart . data . datasets [ 0 ] . borderColor = mainColor ;
2020-05-10 09:04:05 +08:00
wpmOverTimeChart . data . datasets [ 0 ] . data = wpmHistory ;
2020-05-31 21:54:54 +08:00
wpmOverTimeChart . data . datasets [ 1 ] . borderColor = subColor ;
2020-06-25 05:13:01 +08:00
wpmOverTimeChart . data . datasets [ 1 ] . data = rawWpmPerSecond ;
2020-05-31 21:54:54 +08:00
2020-06-05 02:37:16 +08:00
wpmOverTimeChart . options . annotation . annotations [ 0 ] . borderColor = subColor ;
wpmOverTimeChart . options . annotation . annotations [ 0 ] . label . backgroundColor = subColor ;
wpmOverTimeChart . options . annotation . annotations [ 0 ] . label . fontColor = bgColor ;
2020-05-31 22:18:39 +08:00
2020-07-03 08:35:45 +08:00
let maxChartVal = Math . max (
... [ Math . max ( ... rawWpmPerSecond ) , Math . max ( ... wpmHistory ) ]
) ;
2020-05-27 02:59:58 +08:00
let errorsNoZero = [ ] ;
2020-07-03 07:01:46 +08:00
for ( let i = 0 ; i < errorsPerSecond . length ; i ++ ) {
errorsNoZero . push ( {
x : i + 1 ,
2020-07-03 08:35:45 +08:00
y : errorsPerSecond [ i ] ,
2020-07-03 07:01:46 +08:00
} ) ;
2020-05-27 02:59:58 +08:00
}
2020-05-31 21:54:54 +08:00
wpmOverTimeChart . data . datasets [ 2 ] . data = errorsNoZero ;
2020-05-24 06:52:49 +08:00
2020-07-03 07:01:46 +08:00
if ( difficultyFailed ) {
showNotification ( "Test failed" , 2000 ) ;
} else if ( afkDetected ) {
showNotification ( "Test invalid - AFK detected" , 2000 ) ;
} else if ( sameWordset ) {
showNotification ( "Test invalid - repeated" , 2000 ) ;
} else {
2020-06-30 23:26:44 +08:00
let activeTags = [ ] ;
2020-07-03 07:01:46 +08:00
try {
2020-07-03 08:35:45 +08:00
dbSnapshot . tags . forEach ( ( tag ) => {
2020-07-03 07:01:46 +08:00
if ( tag . active === true ) {
2020-06-30 23:26:44 +08:00
activeTags . push ( tag . id ) ;
}
2020-07-03 08:35:45 +08:00
} ) ;
} catch ( e ) { }
2020-06-30 23:26:44 +08:00
let completedEvent = {
wpm : stats . wpm ,
rawWpm : stats . wpmRaw ,
2020-07-23 00:11:59 +08:00
correctChars : stats . correctChars + stats . correctSpaces ,
2020-06-30 23:26:44 +08:00
incorrectChars : stats . incorrectChars ,
acc : stats . acc ,
mode : config . mode ,
mode2 : mode2 ,
punctuation : config . punctuation ,
timestamp : Date . now ( ) ,
language : config . language ,
restartCount : restartCount ,
incompleteTestSeconds : incompleteTestSeconds ,
difficulty : config . difficulty ,
testDuration : testtime ,
blindMode : config . blindMode ,
theme : config . theme ,
2020-07-03 07:01:46 +08:00
tags : activeTags ,
2020-07-07 02:40:52 +08:00
keySpacing : keypressStats . spacing . array ,
keyDuration : keypressStats . duration . array ,
2020-06-30 23:26:44 +08:00
} ;
2020-07-03 08:35:45 +08:00
if (
config . difficulty == "normal" ||
( ( config . difficulty == "master" || config . difficulty == "expert" ) &&
! difficultyFailed )
) {
2020-06-30 23:26:44 +08:00
// console.log(incompleteTestSeconds);
// console.log(restartCount);
restartCount = 0 ;
incompleteTestSeconds = 0 ;
}
2020-07-03 08:35:45 +08:00
if (
stats . wpm > 0 &&
stats . wpm < 350 &&
stats . acc > 50 &&
stats . acc <= 100
) {
2020-06-30 23:26:44 +08:00
if ( firebase . auth ( ) . currentUser != null ) {
completedEvent . uid = firebase . auth ( ) . currentUser . uid ;
//check local pb
accountIconLoading ( true ) ;
let localPb = false ;
let dontShowCrown = false ;
2020-07-03 08:35:45 +08:00
db _getLocalPB (
config . mode ,
mode2 ,
config . punctuation ,
config . language ,
config . difficulty
) . then ( ( lpb ) => {
db _getUserHighestWpm (
config . mode ,
mode2 ,
config . punctuation ,
config . language ,
config . difficulty
) . then ( ( highestwpm ) => {
2020-07-03 07:01:46 +08:00
if ( lpb < stats . wpm && stats . wpm < highestwpm ) {
2020-06-30 23:26:44 +08:00
dontShowCrown = true ;
}
2020-07-03 07:01:46 +08:00
if ( lpb < stats . wpm ) {
2020-06-30 23:26:44 +08:00
//new pb based on local
2020-07-03 07:01:46 +08:00
if ( ! dontShowCrown ) {
2020-06-30 23:26:44 +08:00
hideCrown ( ) ;
showCrown ( ) ;
}
localPb = true ;
}
2020-07-03 07:01:46 +08:00
if ( highestwpm > 0 ) {
2020-06-30 23:26:44 +08:00
wpmOverTimeChart . options . annotation . annotations [ 0 ] . value = highestwpm ;
2020-07-03 08:35:45 +08:00
wpmOverTimeChart . options . annotation . annotations [ 0 ] . label . content =
"PB: " + highestwpm ;
if (
maxChartVal >= highestwpm - 15 &&
maxChartVal <= highestwpm + 15
) {
2020-06-30 23:26:44 +08:00
maxChartVal = highestwpm + 15 ;
}
2020-07-03 08:35:45 +08:00
wpmOverTimeChart . options . scales . yAxes [ 0 ] . ticks . max = Math . round (
maxChartVal
) ;
wpmOverTimeChart . options . scales . yAxes [ 1 ] . ticks . max = Math . round (
maxChartVal
) ;
2020-06-30 23:26:44 +08:00
wpmOverTimeChart . update ( { duration : 0 } ) ;
}
2020-07-04 08:40:03 +08:00
$ ( "#result .stats .leaderboards" ) . removeClass ( "hidden" ) ;
2020-07-04 03:31:23 +08:00
$ ( "#result .stats .leaderboards .bottom" ) . html ( "checking..." ) ;
2020-07-03 08:35:45 +08:00
testCompleted ( {
uid : firebase . auth ( ) . currentUser . uid ,
obj : completedEvent ,
} ) . then ( ( e ) => {
2020-07-20 22:16:54 +08:00
// console.log(e.data);
2020-06-30 23:26:44 +08:00
accountIconLoading ( false ) ;
2020-07-12 07:41:14 +08:00
// console.log(JSON.stringify(e.data));
2020-07-12 21:40:59 +08:00
if ( e . data == null ) {
showNotification ( "Unexpected response from the server." , 4000 ) ;
return ;
}
2020-07-04 02:19:20 +08:00
if ( e . data . resultCode === - 1 ) {
2020-07-03 08:35:45 +08:00
showNotification ( "Could not save result" , 3000 ) ;
2020-07-07 02:40:52 +08:00
} else if ( e . data . resultCode === - 2 ) {
showNotification (
"Possible bot detected. Result not saved." ,
4000
) ;
2020-07-10 00:01:40 +08:00
} else if ( e . data . resultCode === - 3 ) {
showNotification (
"Could not verify. Result not saved. Refresh or contact Miodec on Discord." ,
4000
) ;
2020-07-07 02:40:52 +08:00
} else if ( e . data . resultCode === - 999 ) {
2020-07-12 21:40:59 +08:00
console . error ( "internal error: " + e . data . message ) ;
2020-07-10 00:01:40 +08:00
showNotification (
2020-07-12 21:40:59 +08:00
"Internal error. Result might not be saved. " +
e . data . message ,
2020-07-10 00:01:40 +08:00
6000
) ;
2020-07-04 02:19:20 +08:00
} else if ( e . data . resultCode === 1 || e . data . resultCode === 2 ) {
2020-07-18 07:04:53 +08:00
completedEvent . id = e . data . createdId ;
2020-06-30 23:26:44 +08:00
dbSnapshot . results . unshift ( completedEvent ) ;
2020-07-03 07:01:46 +08:00
try {
2020-07-03 08:35:45 +08:00
firebase
. analytics ( )
. logEvent ( "testCompleted" , completedEvent ) ;
2020-07-03 07:01:46 +08:00
} catch ( e ) {
2020-06-30 23:26:44 +08:00
console . log ( "Analytics unavailable" ) ;
}
2020-07-04 06:42:07 +08:00
//global
let globalLbString = "" ;
2020-07-06 07:05:42 +08:00
if ( e . data . globalLeaderboard === null ) {
2020-07-04 06:42:07 +08:00
globalLbString = "global: not found" ;
2020-07-04 21:15:36 +08:00
} else if ( e . data . globalLeaderboard . insertedAt === - 1 ) {
2020-07-04 06:42:07 +08:00
globalLbString = "global: not qualified" ;
2020-07-04 21:15:36 +08:00
} else if ( e . data . globalLeaderboard . insertedAt >= 0 ) {
if ( e . data . globalLeaderboard . newBest ) {
2020-07-04 23:44:43 +08:00
let pos = e . data . globalLeaderboard . insertedAt + 1 ;
let numend = "th" ;
2020-07-18 06:55:53 +08:00
let t = pos % 10 ;
let h = pos % 100 ;
if ( t == 1 && h != 11 ) {
2020-07-04 23:44:43 +08:00
numend = "st" ;
2020-07-18 06:55:53 +08:00
}
if ( t == 2 && h != 12 ) {
2020-07-04 23:44:43 +08:00
numend = "nd" ;
2020-07-18 06:55:53 +08:00
}
if ( t == 3 && h != 13 ) {
2020-07-04 23:44:43 +08:00
numend = "rd" ;
}
2020-07-04 21:15:36 +08:00
globalLbString = ` global: ${ pos } ${ numend } place ` ;
} else {
2020-07-04 23:44:43 +08:00
let pos = e . data . globalLeaderboard . foundAt + 1 ;
let numend = "th" ;
2020-07-18 06:55:53 +08:00
let t = pos % 10 ;
let h = pos % 100 ;
if ( t == 1 && h != 11 ) {
2020-07-04 23:44:43 +08:00
numend = "st" ;
2020-07-18 06:55:53 +08:00
}
if ( t == 2 && h != 12 ) {
2020-07-04 23:44:43 +08:00
numend = "nd" ;
2020-07-18 06:55:53 +08:00
}
if ( t == 3 && h != 13 ) {
2020-07-04 23:44:43 +08:00
numend = "rd" ;
}
2020-07-04 21:15:36 +08:00
globalLbString = ` global: already ${ pos } ${ numend } ` ;
}
2020-07-04 03:31:23 +08:00
}
2020-07-04 06:42:07 +08:00
//daily
let dailyLbString = "" ;
2020-07-06 07:05:42 +08:00
if ( e . data . dailyLeaderboard === null ) {
2020-07-04 06:42:07 +08:00
dailyLbString = "daily: not found" ;
2020-07-04 21:15:36 +08:00
} else if ( e . data . dailyLeaderboard . insertedAt === - 1 ) {
2020-07-04 06:42:07 +08:00
dailyLbString = "daily: not qualified" ;
2020-07-04 21:15:36 +08:00
} else if ( e . data . dailyLeaderboard . insertedAt >= 0 ) {
if ( e . data . dailyLeaderboard . newBest ) {
2020-07-04 23:44:43 +08:00
let pos = e . data . dailyLeaderboard . insertedAt + 1 ;
let numend = "th" ;
if ( pos === 1 ) {
numend = "st" ;
} else if ( pos === 2 ) {
numend = "nd" ;
} else if ( pos === 3 ) {
numend = "rd" ;
}
2020-07-04 21:15:36 +08:00
dailyLbString = ` daily: ${ pos } ${ numend } place ` ;
} else {
2020-07-04 23:44:43 +08:00
let pos = e . data . dailyLeaderboard . foundAt + 1 ;
let numend = "th" ;
if ( pos === 1 ) {
numend = "st" ;
} else if ( pos === 2 ) {
numend = "nd" ;
} else if ( pos === 3 ) {
numend = "rd" ;
}
2020-07-04 21:15:36 +08:00
dailyLbString = ` daily: already ${ pos } ${ numend } ` ;
}
2020-07-04 06:42:07 +08:00
}
2020-07-22 05:55:07 +08:00
$ ( "#result .stats .leaderboards .bottom" ) . html (
globalLbString + "<br>" + dailyLbString
) ;
2020-07-04 08:40:03 +08:00
if (
e . data . dailyLeaderboard === null &&
2020-07-22 05:55:07 +08:00
e . data . globalLeaderboard === null
2020-07-04 08:40:03 +08:00
) {
$ ( "#result .stats .leaderboards" ) . addClass ( "hidden" ) ;
2020-07-22 05:55:07 +08:00
}
if ( e . data . needsToVerifyEmail === true ) {
2020-07-04 08:40:03 +08:00
$ ( "#result .stats .leaderboards" ) . removeClass ( "hidden" ) ;
2020-07-22 05:55:07 +08:00
$ ( "#result .stats .leaderboards .bottom" ) . html (
` please verify your email to access leaderboards - <a onClick="sendVerificationEmail()">resend email</a> `
) ;
} else if ( e . data . lbBanned ) {
$ ( "#result .stats .leaderboards" ) . removeClass ( "hidden" ) ;
$ ( "#result .stats .leaderboards .bottom" ) . html ( "banned" ) ;
} else if ( e . data . name === false ) {
$ ( "#result .stats .leaderboards" ) . removeClass ( "hidden" ) ;
$ ( "#result .stats .leaderboards .bottom" ) . html (
"update your name to access leaderboards"
) ;
} else if ( e . data . needsToVerify === true ) {
$ ( "#result .stats .leaderboards" ) . removeClass ( "hidden" ) ;
$ ( "#result .stats .leaderboards .bottom" ) . html (
"verification needed to access leaderboards"
) ;
2020-07-04 08:40:03 +08:00
}
2020-07-04 06:42:07 +08:00
2020-07-04 02:19:20 +08:00
if ( e . data . resultCode === 2 ) {
2020-06-30 23:26:44 +08:00
//new pb
2020-07-03 07:01:46 +08:00
if ( ! localPb ) {
2020-07-03 08:35:45 +08:00
showNotification (
"Local PB data is out of sync! Resyncing." ,
5000
) ;
2020-06-30 23:26:44 +08:00
}
2020-07-03 08:35:45 +08:00
db _saveLocalPB (
config . mode ,
mode2 ,
config . punctuation ,
config . language ,
config . difficulty ,
stats . wpm
) ;
2020-07-03 07:01:46 +08:00
} else {
if ( localPb ) {
2020-07-03 08:35:45 +08:00
showNotification (
"Local PB data is out of sync! Refresh the page to resync it or contact Miodec on Discord." ,
15000
) ;
2020-06-30 23:26:44 +08:00
}
}
}
2020-07-03 08:35:45 +08:00
} ) ;
} ) ;
} ) ;
2020-06-30 23:26:44 +08:00
} else {
2020-07-03 07:01:46 +08:00
try {
2020-07-03 08:35:45 +08:00
firebase . analytics ( ) . logEvent ( "testCompletedNoLogin" , completedEvent ) ;
2020-07-03 07:01:46 +08:00
} catch ( e ) {
2020-06-30 23:26:44 +08:00
console . log ( "Analytics unavailable" ) ;
}
// showNotification("Sign in to save your result",3000);
}
} else {
showNotification ( "Test invalid" , 3000 ) ;
testInvalid = true ;
2020-07-03 07:01:46 +08:00
try {
2020-07-03 08:35:45 +08:00
firebase . analytics ( ) . logEvent ( "testCompletedInvalid" , completedEvent ) ;
2020-07-03 07:01:46 +08:00
} catch ( e ) {
2020-06-30 23:26:44 +08:00
console . log ( "Analytics unavailable" ) ;
}
}
}
2020-05-24 06:52:49 +08:00
2020-07-03 08:32:02 +08:00
if ( firebase . auth ( ) . currentUser != null ) {
2020-07-03 08:35:45 +08:00
$ ( "#result .loginTip" ) . addClass ( "hidden" ) ;
2020-07-03 08:32:02 +08:00
} else {
2020-07-04 21:15:36 +08:00
$ ( "#result .stats .leaderboards" ) . addClass ( "hidden" ) ;
2020-07-03 08:35:45 +08:00
$ ( "#result .loginTip" ) . removeClass ( "hidden" ) ;
2020-07-03 08:32:02 +08:00
}
let testType = "" ;
testType += config . mode ;
if ( config . mode == "time" ) {
2020-07-03 08:35:45 +08:00
testType += " " + config . time ;
2020-07-03 08:32:02 +08:00
} else if ( config . mode == "words" ) {
2020-07-03 08:35:45 +08:00
testType += " " + config . words ;
2020-07-03 08:32:02 +08:00
}
if ( config . mode != "custom" ) {
2020-07-23 04:23:25 +08:00
testType += "<br>" + config . language . replace ( /_/g , " " ) ;
2020-07-03 08:32:02 +08:00
}
if ( config . punctuation ) {
2020-07-03 08:35:45 +08:00
testType += "<br>punctuation" ;
2020-07-03 08:32:02 +08:00
}
if ( config . blindMode ) {
2020-07-03 08:35:45 +08:00
testType += "<br>blind" ;
2020-07-03 08:32:02 +08:00
}
if ( config . difficulty == "expert" ) {
testType += "<br>expert" ;
} else if ( config . difficulty == "master" ) {
testType += "<br>master" ;
}
$ ( "#result .stats .testType .bottom" ) . html ( testType ) ;
let otherText = "" ;
if ( difficultyFailed ) {
2020-07-03 08:35:45 +08:00
otherText += "<br>failed" ;
2020-07-03 08:32:02 +08:00
}
if ( afkDetected ) {
2020-07-03 08:35:45 +08:00
otherText += "<br>afk detected" ;
2020-07-03 08:32:02 +08:00
}
if ( testInvalid ) {
2020-07-03 08:35:45 +08:00
otherText += "<br>invalid" ;
2020-07-03 08:32:02 +08:00
}
if ( sameWordset ) {
2020-07-03 08:35:45 +08:00
otherText += "<br>repeated" ;
2020-07-03 08:32:02 +08:00
}
if ( otherText == "" ) {
2020-07-03 08:35:45 +08:00
$ ( "#result .stats .info" ) . addClass ( "hidden" ) ;
2020-07-03 08:32:02 +08:00
} else {
2020-07-03 08:35:45 +08:00
$ ( "#result .stats .info" ) . removeClass ( "hidden" ) ;
2020-07-03 08:32:02 +08:00
otherText = otherText . substring ( 4 ) ;
$ ( "#result .stats .info .bottom" ) . html ( otherText ) ;
}
let tagsText = "" ;
try {
2020-07-03 08:35:45 +08:00
dbSnapshot . tags . forEach ( ( tag ) => {
2020-07-03 08:32:02 +08:00
if ( tag . active === true ) {
tagsText += "<br>" + tag . name ;
}
2020-07-03 08:35:45 +08:00
} ) ;
} catch ( e ) { }
2020-07-03 08:32:02 +08:00
if ( tagsText == "" ) {
2020-07-03 08:35:45 +08:00
$ ( "#result .stats .tags" ) . addClass ( "hidden" ) ;
2020-07-03 08:32:02 +08:00
} else {
2020-07-03 08:35:45 +08:00
$ ( "#result .stats .tags" ) . removeClass ( "hidden" ) ;
2020-07-03 08:32:02 +08:00
tagsText = tagsText . substring ( 4 ) ;
$ ( "#result .stats .tags .bottom" ) . html ( tagsText ) ;
}
2020-07-03 08:35:45 +08:00
if ( config . mode === "quote" ) {
2020-07-03 08:46:42 +08:00
$ ( "#result .stats .source" ) . removeClass ( "hidden" ) ;
2020-07-03 08:32:02 +08:00
$ ( "#result .stats .source .bottom" ) . html ( randomQuote . source ) ;
2020-07-03 08:46:42 +08:00
} else {
$ ( "#result .stats .source" ) . addClass ( "hidden" ) ;
2020-07-03 08:32:02 +08:00
}
2020-06-30 23:26:44 +08:00
wpmOverTimeChart . options . scales . yAxes [ 0 ] . ticks . max = maxChartVal ;
wpmOverTimeChart . options . scales . yAxes [ 1 ] . ticks . max = maxChartVal ;
2020-05-24 06:52:49 +08:00
2020-05-10 09:04:05 +08:00
wpmOverTimeChart . update ( { duration : 0 } ) ;
2020-07-03 07:01:46 +08:00
swapElements ( $ ( "#words" ) , $ ( "#result" ) , 250 , ( ) => {
if ( config . blindMode ) {
2020-07-03 08:35:45 +08:00
$ . each ( $ ( "#words .word" ) , ( i , word ) => {
2020-05-31 04:43:15 +08:00
let input = inputHistory [ i ] ;
2020-07-03 07:01:46 +08:00
if ( input == undefined ) input = currentInput ;
compareInput ( i , input , true ) ;
if ( inputHistory [ i ] != wordsList [ i ] ) {
highlightBadWord ( i , true ) ;
2020-05-31 04:30:50 +08:00
}
2020-07-03 08:35:45 +08:00
} ) ;
2020-05-31 04:30:50 +08:00
}
2020-05-27 08:00:28 +08:00
let remove = false ;
2020-07-03 08:35:45 +08:00
$ . each ( $ ( "#words .word" ) , ( i , obj ) => {
2020-07-03 07:01:46 +08:00
if ( remove ) {
2020-05-27 08:00:28 +08:00
$ ( obj ) . remove ( ) ;
2020-07-03 07:01:46 +08:00
} else {
2020-07-03 08:35:45 +08:00
$ ( obj ) . removeClass ( "hidden" ) ;
if ( $ ( obj ) . hasClass ( "active" ) ) remove = true ;
2020-05-27 08:00:28 +08:00
}
} ) ;
} ) ;
2020-05-09 08:29:47 +08:00
}
2020-06-06 09:05:21 +08:00
function restartTest ( withSameWordset = false ) {
2020-05-15 06:19:28 +08:00
clearIntervals ( ) ;
2020-05-15 03:22:59 +08:00
time = 0 ;
2020-05-23 23:29:36 +08:00
afkDetected = false ;
wpmHistory = [ ] ;
2020-05-31 21:31:50 +08:00
rawHistory = [ ] ;
2020-05-09 08:29:47 +08:00
setFocus ( false ) ;
2020-05-10 02:47:11 +08:00
hideCaret ( ) ;
2020-05-11 07:07:36 +08:00
testActive = false ;
hideLiveWpm ( ) ;
2020-07-20 22:48:05 +08:00
hideTimer ( ) ;
2020-05-24 06:52:49 +08:00
keypressPerSecond = [ ] ;
currentKeypressCount = 0 ;
errorsPerSecond = [ ] ;
currentErrorCount = 0 ;
2020-05-22 23:19:09 +08:00
currentTestLine = 0 ;
2020-06-22 02:50:34 +08:00
activeWordJumped = false ;
2020-07-06 07:05:42 +08:00
keypressStats = {
spacing : {
current : - 1 ,
array : [ ] ,
} ,
duration : {
current : - 1 ,
array : [ ] ,
} ,
} ;
2020-07-13 11:03:54 +08:00
$ ( "#timerNumber" ) . css ( "opacity" , 0 ) ;
2020-06-16 05:52:47 +08:00
// restartTimer();
2020-05-20 22:57:54 +08:00
let el = null ;
2020-07-03 07:01:46 +08:00
if ( resultVisible ) {
2020-05-20 22:57:54 +08:00
//results are being displayed
el = $ ( "#result" ) ;
2020-07-03 07:01:46 +08:00
} else {
2020-05-20 22:57:54 +08:00
//words are being displayed
el = $ ( "#words" ) ;
}
2020-07-03 07:01:46 +08:00
if ( resultVisible ) {
2020-07-17 08:20:26 +08:00
if ( config . randomTheme ) {
randomiseTheme ( ) ;
2020-07-23 04:23:25 +08:00
showNotification ( config . theme . replace ( /_/g , " " ) , 1500 ) ;
2020-07-17 08:20:26 +08:00
}
2020-07-03 08:35:45 +08:00
$ ( "#words" ) . stop ( true , true ) . animate (
{
opacity : 0 ,
} ,
125
) ;
$ ( "#wordsTitle" )
. stop ( true , true )
. animate (
{
opacity : 0 ,
} ,
125 ,
( ) => {
$ ( "#wordsTitle" ) . slideUp ( 0 ) ;
}
) ;
$ ( "#resultExtraButtons" )
. stop ( true , true )
. animate (
{
opacity : 0 ,
} ,
125 ,
( ) => {
$ ( "#resultExtraButtons" ) . addClass ( "hidden" ) ;
}
) ;
2020-05-27 08:00:28 +08:00
}
2020-05-27 23:52:48 +08:00
resultVisible = false ;
2020-07-03 07:01:46 +08:00
// .css("transition", "1s linear");
2020-07-03 08:35:45 +08:00
el . stop ( true , true ) . animate (
{
opacity : 0 ,
} ,
125 ,
( ) => {
if ( ! withSameWordset ) {
sameWordset = false ;
initWords ( ) ;
} else {
sameWordset = true ;
testActive = false ;
currentWordIndex = 0 ;
accuracyStats = {
correct : 0 ,
incorrect : 0 ,
} ;
inputHistory = [ ] ;
currentInput = "" ;
showWords ( ) ;
2020-06-06 09:05:21 +08:00
}
2020-07-03 08:35:45 +08:00
$ ( "#result" ) . addClass ( "hidden" ) ;
$ ( "#testModesNotice" ) . css ( {
opacity : 1 ,
// 'height': 'auto',
// 'margin-bottom': '1.25rem'
} ) ;
$ ( "#words" )
. css ( "opacity" , 0 )
. removeClass ( "hidden" )
. stop ( true , true )
. animate (
{
opacity : 1 ,
} ,
125 ,
( ) => {
hideCrown ( ) ;
clearIntervals ( ) ;
$ ( "#restartTestButton" ) . css ( "opacity" , 1 ) ;
if ( $ ( "#commandLineWrapper" ) . hasClass ( "hidden" ) ) focusWords ( ) ;
wpmOverTimeChart . options . annotation . annotations [ 0 ] . value = "-30" ;
wpmOverTimeChart . update ( ) ;
// let oldHeight = $("#words").height();
// let newHeight = $("#words")
// .css("height", "fit-content")
// .css("height", "-moz-fit-content")
// .height();
// if (testMode == "words" || testMode == "custom") {
// $("#words")
// .stop(true, true)
// .css("height", oldHeight)
// .animate({ height: newHeight }, 250, () => {
// $("#words")
// .css("height", "fit-content")
// .css("height", "-moz-fit-content");
// $("#wordsInput").focus();
// updateCaretPosition();
// });
// } else if (testMode == "time") {
// $("#words")
// .stop(true, true)
// .css("height", oldHeight)
// .animate({ height: 78 }, 250, () => {
// $("#wordsInput").focus();
// updateCaretPosition();
// });
// }
}
) ;
2020-06-06 09:05:21 +08:00
}
2020-07-03 08:35:45 +08:00
) ;
2020-05-09 08:29:47 +08:00
}
2020-05-11 07:32:10 +08:00
function focusWords ( ) {
2020-07-03 08:35:45 +08:00
if ( ! $ ( "#words" ) . hasClass ( "hidden" ) ) $ ( "#wordsInput" ) . focus ( ) ;
2020-05-11 07:32:10 +08:00
}
2020-05-09 08:29:47 +08:00
function changeCustomText ( ) {
2020-06-16 04:43:16 +08:00
customText = prompt ( "Custom text" ) . trim ( ) ;
2020-07-03 08:35:45 +08:00
customText = customText . replace ( /[\n\r\t ]/gm , " " ) ;
customText = customText . replace ( / +/gm , " " ) ;
customText = customText . split ( " " ) ;
2020-07-03 07:01:46 +08:00
if ( customText . length >= 10000 ) {
2020-07-03 08:35:45 +08:00
showNotification ( "Custom text cannot be longer than 10000 words." , 4000 ) ;
changeMode ( "time" ) ;
customText = "The quick brown fox jumped over the lazy dog" . split ( " " ) ;
2020-07-01 05:23:50 +08:00
}
2020-05-26 07:31:03 +08:00
// initWords();
2020-05-09 08:29:47 +08:00
}
2020-05-11 07:32:10 +08:00
function changePage ( page ) {
2020-07-03 07:01:46 +08:00
if ( pageTransition ) {
2020-05-23 00:59:28 +08:00
return ;
}
2020-05-22 06:02:19 +08:00
restartTest ( ) ;
2020-05-12 07:59:12 +08:00
let activePage = $ ( ".page.active" ) ;
2020-07-03 08:35:45 +08:00
$ ( ".page" ) . removeClass ( "active" ) ;
2020-05-11 07:32:10 +08:00
$ ( "#wordsInput" ) . focusout ( ) ;
if ( page == "test" || page == "" ) {
2020-05-23 00:59:28 +08:00
pageTransition = true ;
swapElements ( activePage , $ ( ".page.pageTest" ) , 250 , ( ) => {
pageTransition = false ;
focusWords ( ) ;
2020-07-03 08:35:45 +08:00
$ ( ".page.pageTest" ) . addClass ( "active" ) ;
history . pushState ( "/" , null , "/" ) ;
2020-05-23 00:59:28 +08:00
} ) ;
2020-05-15 11:09:00 +08:00
showTestConfig ( ) ;
hideSignOutButton ( ) ;
2020-05-17 20:17:08 +08:00
restartCount = 0 ;
2020-06-09 01:12:34 +08:00
incompleteTestSeconds = 0 ;
2020-05-22 06:02:19 +08:00
restartTest ( ) ;
2020-05-11 07:32:10 +08:00
} else if ( page == "about" ) {
2020-05-23 00:59:28 +08:00
pageTransition = true ;
2020-07-03 07:01:46 +08:00
swapElements ( activePage , $ ( ".page.pageAbout" ) , 250 , ( ) => {
2020-05-23 00:59:28 +08:00
pageTransition = false ;
2020-07-03 08:35:45 +08:00
history . pushState ( "about" , null , "about" ) ;
$ ( ".page.pageAbout" ) . addClass ( "active" ) ;
2020-05-23 00:59:28 +08:00
} ) ;
2020-05-15 11:09:00 +08:00
hideTestConfig ( ) ;
hideSignOutButton ( ) ;
2020-07-03 07:01:46 +08:00
} else if ( page == "settings" ) {
2020-05-23 00:59:28 +08:00
pageTransition = true ;
2020-07-03 07:01:46 +08:00
swapElements ( activePage , $ ( ".page.pageSettings" ) , 250 , ( ) => {
2020-05-23 00:59:28 +08:00
pageTransition = false ;
2020-07-03 08:35:45 +08:00
history . pushState ( "settings" , null , "settings" ) ;
$ ( ".page.pageSettings" ) . addClass ( "active" ) ;
2020-05-23 00:59:28 +08:00
} ) ;
updateSettingsPage ( ) ;
2020-05-15 11:09:00 +08:00
hideTestConfig ( ) ;
hideSignOutButton ( ) ;
2020-05-11 07:32:10 +08:00
} else if ( page == "account" ) {
if ( ! firebase . auth ( ) . currentUser ) {
changePage ( "login" ) ;
} else {
2020-05-23 00:59:28 +08:00
pageTransition = true ;
2020-07-03 07:01:46 +08:00
swapElements ( activePage , $ ( ".page.pageAccount" ) , 250 , ( ) => {
2020-05-23 00:59:28 +08:00
pageTransition = false ;
2020-07-03 08:35:45 +08:00
history . pushState ( "account" , null , "account" ) ;
$ ( ".page.pageAccount" ) . addClass ( "active" ) ;
2020-05-23 00:59:28 +08:00
} ) ;
2020-05-11 07:32:10 +08:00
refreshAccountPage ( ) ;
2020-05-15 11:09:00 +08:00
hideTestConfig ( ) ;
showSignOutButton ( ) ;
2020-05-11 07:32:10 +08:00
}
} else if ( page == "login" ) {
2020-05-12 07:59:12 +08:00
if ( firebase . auth ( ) . currentUser != null ) {
2020-07-03 08:35:45 +08:00
changePage ( "account" ) ;
2020-05-12 07:59:12 +08:00
} else {
2020-05-23 00:59:28 +08:00
pageTransition = true ;
2020-07-03 07:01:46 +08:00
swapElements ( activePage , $ ( ".page.pageLogin" ) , 250 , ( ) => {
2020-05-23 00:59:28 +08:00
pageTransition = false ;
2020-07-03 08:35:45 +08:00
history . pushState ( "login" , null , "login" ) ;
$ ( ".page.pageLogin" ) . addClass ( "active" ) ;
2020-05-23 00:59:28 +08:00
} ) ;
2020-05-15 11:09:00 +08:00
hideTestConfig ( ) ;
hideSignOutButton ( ) ;
2020-05-12 07:59:12 +08:00
}
2020-05-11 07:32:10 +08:00
}
2020-05-10 01:39:23 +08:00
}
2020-07-03 07:01:46 +08:00
function changeMode ( mode , nosave ) {
2020-05-11 07:32:10 +08:00
config . mode = mode ;
2020-07-03 01:39:24 +08:00
$ ( "#top .config .mode .text-button" ) . removeClass ( "active" ) ;
$ ( "#top .config .mode .text-button[mode='" + mode + "']" ) . addClass ( "active" ) ;
2020-05-11 07:32:10 +08:00
if ( config . mode == "time" ) {
$ ( "#top .config .wordCount" ) . addClass ( "hidden" ) ;
$ ( "#top .config .time" ) . removeClass ( "hidden" ) ;
$ ( "#top .config .customText" ) . addClass ( "hidden" ) ;
$ ( "#top .config .punctuationMode" ) . removeClass ( "hidden" ) ;
} else if ( config . mode == "words" ) {
$ ( "#top .config .wordCount" ) . removeClass ( "hidden" ) ;
$ ( "#top .config .time" ) . addClass ( "hidden" ) ;
$ ( "#top .config .customText" ) . addClass ( "hidden" ) ;
$ ( "#top .config .punctuationMode" ) . removeClass ( "hidden" ) ;
} else if ( config . mode == "custom" ) {
$ ( "#top .config .wordCount" ) . addClass ( "hidden" ) ;
$ ( "#top .config .time" ) . addClass ( "hidden" ) ;
$ ( "#top .config .customText" ) . removeClass ( "hidden" ) ;
$ ( "#top .config .punctuationMode" ) . addClass ( "hidden" ) ;
2020-07-03 07:01:46 +08:00
} else if ( config . mode == "quote" ) {
2020-07-18 07:19:55 +08:00
setPunctuation ( false ) ;
2020-07-03 07:01:46 +08:00
$ ( "#top .config .wordCount" ) . addClass ( "hidden" ) ;
$ ( "#top .config .time" ) . addClass ( "hidden" ) ;
$ ( "#top .config .customText" ) . addClass ( "hidden" ) ;
$ ( "#top .config .punctuationMode" ) . addClass ( "hidden" ) ;
$ ( "#result .stats .source" ) . removeClass ( "hidden" ) ;
changeLanguage ( "english" ) ;
2020-05-11 07:07:36 +08:00
}
2020-07-03 07:01:46 +08:00
if ( ! nosave ) saveConfigToCookie ( ) ;
2020-05-11 07:07:36 +08:00
}
2020-07-13 06:53:15 +08:00
// function liveWPM() {
// let correctWordChars = 0;
// for (let i = 0; i < inputHistory.length; i++) {
// if (inputHistory[i] == wordsList[i]) {
// //the word is correct
// //+1 for space
// correctWordChars += wordsList[i].length + 1;
// }
// }
// let testNow = Date.now();
// let testSeconds = (testNow - testStart) / 1000;
// wpm = (correctWordChars * (60 / testSeconds)) / 5;
// return Math.round(wpm);
// }
// function liveRaw() {
// let chars = 0;
// for (let i = 0; i < inputHistory.length; i++) {
// chars += inputHistory[i].length + 1;
// }
// let testNow = Date.now();
// let testSeconds = (testNow - testStart) / 1000;
// raw = (chars * (60 / testSeconds)) / 5;
// return Math.round(raw);
// }
function liveWpmAndRaw ( ) {
let chars = 0 ;
2020-05-11 07:32:10 +08:00
let correctWordChars = 0 ;
2020-07-18 07:32:02 +08:00
let spaces = 0 ;
2020-05-11 07:32:10 +08:00
for ( let i = 0 ; i < inputHistory . length ; i ++ ) {
if ( inputHistory [ i ] == wordsList [ i ] ) {
//the word is correct
//+1 for space
2020-07-18 07:32:02 +08:00
correctWordChars += wordsList [ i ] . length ;
2020-07-23 00:13:06 +08:00
if ( i < inputHistory . length - 1 ) {
spaces ++ ;
}
2020-05-11 07:32:10 +08:00
}
2020-07-18 07:32:02 +08:00
chars += inputHistory [ i ] . length ;
2020-05-31 21:31:50 +08:00
}
let testNow = Date . now ( ) ;
let testSeconds = ( testNow - testStart ) / 1000 ;
2020-07-18 07:32:02 +08:00
let wpm = Math . round ( ( ( correctWordChars + spaces ) * ( 60 / testSeconds ) ) / 5 ) ;
let raw = Math . round ( ( ( chars + spaces ) * ( 60 / testSeconds ) ) / 5 ) ;
2020-07-13 06:53:15 +08:00
return {
wpm : wpm ,
raw : raw ,
} ;
2020-05-31 21:31:50 +08:00
}
2020-05-11 07:32:10 +08:00
function updateLiveWpm ( wpm ) {
if ( ! config . showLiveWpm ) return ;
2020-07-14 23:57:23 +08:00
if ( ! testActive ) {
hideLiveWpm ( ) ;
} else {
showLiveWpm ( ) ;
}
2020-05-15 10:19:39 +08:00
// let wpmstring = wpm < 100 ? ` ${wpm}` : `${wpm}`;
2020-07-13 06:53:15 +08:00
document . querySelector ( "#liveWpm" ) . innerHTML = wpm ;
// $("#liveWpm").html(wpm);
2020-05-10 06:33:48 +08:00
}
2020-05-11 07:32:10 +08:00
function showLiveWpm ( ) {
if ( ! config . showLiveWpm ) return ;
if ( ! testActive ) return ;
2020-07-13 10:24:55 +08:00
$ ( "#liveWpm" ) . css ( "opacity" , config . timerOpacity ) ;
2020-07-18 00:01:34 +08:00
// if (config.timerStyle === "text") {
// $("#timerNumber").css("opacity", config.timerOpacity);
// }
2020-05-10 09:04:05 +08:00
}
2020-05-11 07:32:10 +08:00
function hideLiveWpm ( ) {
2020-07-03 08:35:45 +08:00
$ ( "#liveWpm" ) . css ( "opacity" , 0 ) ;
2020-05-10 06:33:48 +08:00
}
2020-07-03 08:35:45 +08:00
function swapElements (
el1 ,
el2 ,
totalDuration ,
callback = function ( ) {
return ;
}
) {
2020-05-12 07:59:12 +08:00
if (
2020-07-03 08:35:45 +08:00
( el1 . hasClass ( "hidden" ) && ! el2 . hasClass ( "hidden" ) ) ||
( ! el1 . hasClass ( "hidden" ) && el2 . hasClass ( "hidden" ) )
2020-05-12 07:59:12 +08:00
) {
//one of them is hidden and the other is visible
if ( el1 . hasClass ( "hidden" ) ) {
2020-05-23 00:59:28 +08:00
callback ( ) ;
2020-05-12 07:59:12 +08:00
return false ;
}
2020-07-03 08:35:45 +08:00
$ ( el1 )
. removeClass ( "hidden" )
. css ( "opacity" , 1 )
. animate (
{
opacity : 0 ,
} ,
totalDuration / 2 ,
( ) => {
$ ( el1 ) . addClass ( "hidden" ) ;
$ ( el2 )
. removeClass ( "hidden" )
. css ( "opacity" , 0 )
. animate (
{
opacity : 1 ,
} ,
totalDuration / 2 ,
( ) => {
callback ( ) ;
}
) ;
}
) ;
} else if ( el1 . hasClass ( "hidden" ) && el2 . hasClass ( "hidden" ) ) {
2020-05-12 07:59:12 +08:00
//both are hidden, only fade in the second
2020-07-03 08:35:45 +08:00
$ ( el2 )
. removeClass ( "hidden" )
. css ( "opacity" , 0 )
. animate (
{
opacity : 1 ,
} ,
totalDuration ,
( ) => {
callback ( ) ;
}
) ;
2020-07-03 07:01:46 +08:00
} else {
2020-05-23 00:59:28 +08:00
callback ( ) ;
2020-05-12 07:59:12 +08:00
}
}
2020-05-15 06:19:28 +08:00
function clearIntervals ( ) {
2020-07-03 08:35:45 +08:00
timers . forEach ( ( timer ) => {
2020-05-15 06:19:28 +08:00
clearInterval ( timer ) ;
2020-07-03 08:35:45 +08:00
} ) ;
2020-05-15 06:19:28 +08:00
}
2020-05-13 23:34:20 +08:00
2020-05-12 07:59:12 +08:00
function updateAccountLoginButton ( ) {
if ( firebase . auth ( ) . currentUser != null ) {
2020-07-03 08:35:45 +08:00
swapElements (
$ ( "#menu .icon-button.login" ) ,
$ ( "#menu .icon-button.account" ) ,
250
) ;
2020-07-03 01:39:24 +08:00
// $("#menu .icon-button.account").removeClass('hidden');
// $("#menu .icon-button.login").addClass('hidden');
2020-05-12 07:59:12 +08:00
} else {
2020-07-03 08:35:45 +08:00
swapElements (
$ ( "#menu .icon-button.account" ) ,
$ ( "#menu .icon-button.login" ) ,
250
) ;
2020-07-03 01:39:24 +08:00
// $("#menu .icon-button.login").removeClass('hidden');
// $("#menu .icon-button.account").addClass('hidden');
2020-05-12 07:59:12 +08:00
}
}
2020-06-09 03:35:50 +08:00
function accountIconLoading ( truefalse ) {
2020-07-03 07:01:46 +08:00
if ( truefalse ) {
2020-07-03 08:35:45 +08:00
$ ( "#top #menu .account .icon" ) . html (
'<i class="fas fa-fw fa-spin fa-circle-notch"></i>'
) ;
2020-07-03 07:01:46 +08:00
} else {
2020-06-09 03:35:50 +08:00
$ ( "#top #menu .account .icon" ) . html ( '<i class="fas fa-fw fa-user"></i>' ) ;
}
}
2020-07-03 07:01:46 +08:00
function toggleResultWordsDisplay ( ) {
if ( resultVisible ) {
2020-07-03 08:35:45 +08:00
if ( $ ( "#words" ) . stop ( true , true ) . hasClass ( "hidden" ) ) {
//show
$ ( "#wordsTitle" ) . css ( "opacity" , 1 ) . removeClass ( "hidden" ) . slideDown ( 250 ) ;
let newHeight = $ ( "#words" )
. removeClass ( "hidden" )
. css ( "height" , "auto" )
. outerHeight ( ) ;
$ ( "#words" )
. css ( {
height : 0 ,
opacity : 0 ,
} )
. animate (
{
height : newHeight ,
opacity : 1 ,
} ,
250
) ;
2020-07-03 07:01:46 +08:00
} else {
2020-05-27 08:00:28 +08:00
//hide
$ ( "#wordsTitle" ) . slideUp ( 250 ) ;
let oldHeight = $ ( "#words" ) . outerHeight ( ) ;
2020-07-03 08:35:45 +08:00
$ ( "#words" ) . removeClass ( "hidden" ) ;
$ ( "#words" )
. css ( {
opacity : 1 ,
height : oldHeight ,
} )
. animate (
{
height : 0 ,
opacity : 0 ,
} ,
250 ,
( ) => {
$ ( "#words" ) . addClass ( "hidden" ) ;
}
) ;
2020-05-27 08:00:28 +08:00
}
}
}
2020-07-03 07:01:46 +08:00
function flipTestColors ( tf ) {
if ( tf ) {
2020-07-03 08:35:45 +08:00
$ ( "#words" ) . addClass ( "flipped" ) ;
2020-07-03 07:01:46 +08:00
} else {
2020-07-03 08:35:45 +08:00
$ ( "#words" ) . removeClass ( "flipped" ) ;
2020-06-01 03:30:56 +08:00
}
}
2020-07-03 07:01:46 +08:00
function applyColorfulMode ( tc ) {
if ( tc ) {
2020-07-03 08:35:45 +08:00
$ ( "#words" ) . addClass ( "colorfulMode" ) ;
2020-07-03 07:01:46 +08:00
} else {
2020-07-03 08:35:45 +08:00
$ ( "#words" ) . removeClass ( "colorfulMode" ) ;
2020-06-09 06:00:16 +08:00
}
}
2020-07-03 07:01:46 +08:00
function showEditTags ( action , id , name ) {
if ( action === "add" ) {
2020-07-03 08:35:45 +08:00
$ ( "#tagsWrapper #tagsEdit" ) . attr ( "action" , "add" ) ;
$ ( "#tagsWrapper #tagsEdit .title" ) . html ( "Add new tag" ) ;
2020-06-12 02:19:39 +08:00
$ ( "#tagsWrapper #tagsEdit .button" ) . html ( ` <i class="fas fa-plus"></i> ` ) ;
2020-07-03 08:35:45 +08:00
$ ( "#tagsWrapper #tagsEdit input" ) . val ( "" ) ;
$ ( "#tagsWrapper #tagsEdit input" ) . removeClass ( "hidden" ) ;
2020-07-03 07:01:46 +08:00
} else if ( action === "edit" ) {
2020-07-03 08:35:45 +08:00
$ ( "#tagsWrapper #tagsEdit" ) . attr ( "action" , "edit" ) ;
$ ( "#tagsWrapper #tagsEdit" ) . attr ( "tagid" , id ) ;
$ ( "#tagsWrapper #tagsEdit .title" ) . html ( "Edit tag name" ) ;
2020-06-12 02:38:24 +08:00
$ ( "#tagsWrapper #tagsEdit .button" ) . html ( ` <i class="fas fa-pen"></i> ` ) ;
$ ( "#tagsWrapper #tagsEdit input" ) . val ( name ) ;
2020-07-03 08:35:45 +08:00
$ ( "#tagsWrapper #tagsEdit input" ) . removeClass ( "hidden" ) ;
2020-07-03 07:01:46 +08:00
} else if ( action === "remove" ) {
2020-07-03 08:35:45 +08:00
$ ( "#tagsWrapper #tagsEdit" ) . attr ( "action" , "remove" ) ;
$ ( "#tagsWrapper #tagsEdit" ) . attr ( "tagid" , id ) ;
$ ( "#tagsWrapper #tagsEdit .title" ) . html ( "Remove tag " + name ) ;
2020-06-12 02:38:24 +08:00
$ ( "#tagsWrapper #tagsEdit .button" ) . html ( ` <i class="fas fa-check"></i> ` ) ;
2020-07-03 08:35:45 +08:00
$ ( "#tagsWrapper #tagsEdit input" ) . addClass ( "hidden" ) ;
2020-06-12 02:19:39 +08:00
}
2020-07-03 07:01:46 +08:00
2020-06-12 02:19:39 +08:00
if ( $ ( "#tagsWrapper" ) . hasClass ( "hidden" ) ) {
$ ( "#tagsWrapper" )
2020-07-03 07:01:46 +08:00
. stop ( true , true )
. css ( "opacity" , 0 )
. removeClass ( "hidden" )
2020-07-03 08:35:45 +08:00
. animate ( { opacity : 1 } , 100 , ( e ) => {
2020-07-03 07:01:46 +08:00
$ ( "#tagsWrapper #tagsEdit input" ) . focus ( ) ;
} ) ;
}
2020-06-12 02:19:39 +08:00
}
2020-07-03 07:01:46 +08:00
function hideEditTags ( ) {
2020-06-12 02:19:39 +08:00
if ( ! $ ( "#tagsWrapper" ) . hasClass ( "hidden" ) ) {
2020-07-03 08:35:45 +08:00
$ ( "#tagsWrapper #tagsEdit" ) . attr ( "action" , "" ) ;
$ ( "#tagsWrapper #tagsEdit" ) . attr ( "tagid" , "" ) ;
2020-06-12 02:19:39 +08:00
$ ( "#tagsWrapper" )
2020-07-03 07:01:46 +08:00
. stop ( true , true )
. css ( "opacity" , 1 )
. animate (
2020-06-12 02:19:39 +08:00
{
2020-07-03 08:35:45 +08:00
opacity : 0 ,
} ,
100 ,
( e ) => {
$ ( "#tagsWrapper" ) . addClass ( "hidden" ) ;
}
) ;
2020-07-03 07:01:46 +08:00
}
2020-06-12 02:19:39 +08:00
}
2020-07-03 07:01:46 +08:00
function showBackgroundLoader ( ) {
$ ( "#backgroundLoader" ) . stop ( true , true ) . fadeIn ( 125 ) ;
2020-06-13 00:30:20 +08:00
}
2020-07-03 07:01:46 +08:00
function hideBackgroundLoader ( ) {
$ ( "#backgroundLoader" ) . stop ( true , true ) . fadeOut ( 125 ) ;
2020-06-13 00:30:20 +08:00
}
2020-07-03 07:01:46 +08:00
function updateTestModesNotice ( ) {
2020-06-24 01:17:08 +08:00
let anim = false ;
2020-07-03 07:01:46 +08:00
if ( $ ( ".pageTest #testModesNotice" ) . text ( ) === "" ) anim = true ;
2020-06-24 01:17:08 +08:00
2020-06-15 01:19:56 +08:00
$ ( ".pageTest #testModesNotice" ) . empty ( ) ;
2020-07-03 07:01:46 +08:00
if ( config . difficulty === "expert" ) {
2020-07-03 08:35:45 +08:00
$ ( ".pageTest #testModesNotice" ) . append (
` <div><i class="fas fa-star-half-alt"></i>expert</div> `
) ;
2020-07-03 07:01:46 +08:00
} else if ( config . difficulty === "master" ) {
2020-07-03 08:35:45 +08:00
$ ( ".pageTest #testModesNotice" ) . append (
` <div><i class="fas fa-star"></i>master</div> `
) ;
2020-06-15 01:19:56 +08:00
}
2020-07-03 07:01:46 +08:00
if ( config . blindMode ) {
2020-07-03 08:35:45 +08:00
$ ( ".pageTest #testModesNotice" ) . append (
` <div><i class="fas fa-eye-slash"></i>blind</div> `
) ;
2020-06-15 01:19:56 +08:00
}
tagsString = "" ;
2020-06-24 00:15:21 +08:00
// $.each($('.pageSettings .section.tags .tagsList .tag'), (index, tag) => {
// if($(tag).children('.active').attr('active') === 'true'){
// tagsString += $(tag).children('.title').text() + ', ';
// }
// })
2020-07-03 07:01:46 +08:00
try {
2020-07-03 08:35:45 +08:00
dbSnapshot . tags . forEach ( ( tag ) => {
2020-07-03 07:01:46 +08:00
if ( tag . active === true ) {
2020-07-03 08:35:45 +08:00
tagsString += tag . name + ", " ;
2020-06-15 01:19:56 +08:00
}
2020-07-03 08:35:45 +08:00
} ) ;
2020-06-24 00:15:21 +08:00
2020-07-03 07:01:46 +08:00
if ( tagsString !== "" ) {
2020-07-03 08:35:45 +08:00
$ ( ".pageTest #testModesNotice" ) . append (
` <div><i class="fas fa-tag"></i> ${ tagsString . substring (
0 ,
tagsString . length - 2
) } < / d i v > `
) ;
2020-06-24 00:15:21 +08:00
}
2020-07-03 08:35:45 +08:00
} catch ( e ) { }
2020-06-15 01:19:56 +08:00
2020-07-03 07:01:46 +08:00
if ( anim ) {
2020-07-03 08:35:45 +08:00
$ ( ".pageTest #testModesNotice" )
. css ( "transition" , "none" )
. css ( "opacity" , 0 )
. animate (
{
opacity : 1 ,
} ,
125 ,
( e ) => {
$ ( ".pageTest #testModesNotice" ) . css ( "transition" , ".125s" ) ;
}
) ;
2020-06-24 01:17:08 +08:00
}
2020-06-15 01:19:56 +08:00
}
2020-07-03 08:35:45 +08:00
$ ( "#tagsWrapper" ) . click ( ( e ) => {
if ( $ ( e . target ) . attr ( "id" ) === "tagsWrapper" ) {
2020-06-12 02:19:39 +08:00
hideEditTags ( ) ;
}
2020-07-03 08:35:45 +08:00
} ) ;
2020-06-12 02:19:39 +08:00
2020-07-03 08:35:45 +08:00
$ ( "#tagsWrapper #tagsEdit .button" ) . click ( ( e ) => {
2020-06-16 06:25:28 +08:00
tagsEdit ( ) ;
2020-07-03 08:35:45 +08:00
} ) ;
2020-06-16 06:25:28 +08:00
2020-07-03 08:35:45 +08:00
$ ( "#tagsWrapper #tagsEdit input" ) . keypress ( ( e ) => {
2020-06-16 06:25:28 +08:00
if ( e . keyCode == 13 ) {
tagsEdit ( ) ;
}
2020-07-03 08:35:45 +08:00
} ) ;
2020-06-16 06:25:28 +08:00
2020-07-03 07:01:46 +08:00
function tagsEdit ( ) {
2020-07-03 08:35:45 +08:00
let action = $ ( "#tagsWrapper #tagsEdit" ) . attr ( "action" ) ;
2020-06-12 02:19:39 +08:00
let inputVal = $ ( "#tagsWrapper #tagsEdit input" ) . val ( ) ;
2020-07-03 08:35:45 +08:00
let tagid = $ ( "#tagsWrapper #tagsEdit" ) . attr ( "tagid" ) ;
2020-06-12 02:19:39 +08:00
hideEditTags ( ) ;
2020-07-03 07:01:46 +08:00
if ( action === "add" ) {
2020-06-13 00:30:20 +08:00
showBackgroundLoader ( ) ;
2020-07-03 08:35:45 +08:00
addTag ( { uid : firebase . auth ( ) . currentUser . uid , name : inputVal } ) . then (
( e ) => {
hideBackgroundLoader ( ) ;
let status = e . data . resultCode ;
if ( status === 1 ) {
showNotification ( "Tag added" , 2000 ) ;
dbSnapshot . tags . push ( {
name : inputVal ,
id : e . data . id ,
} ) ;
updateResultEditTagsPanelButtons ( ) ;
updateSettingsPage ( ) ;
updateFilterTags ( ) ;
} else if ( status === - 1 ) {
showNotification ( "Invalid tag name" , 3000 ) ;
} else if ( status < - 1 ) {
showNotification ( "Unknown error" , 3000 ) ;
}
2020-06-12 02:38:24 +08:00
}
2020-07-03 08:35:45 +08:00
) ;
2020-07-03 07:01:46 +08:00
} else if ( action === "edit" ) {
2020-06-13 00:30:20 +08:00
showBackgroundLoader ( ) ;
2020-07-03 08:35:45 +08:00
editTag ( {
uid : firebase . auth ( ) . currentUser . uid ,
name : inputVal ,
tagid : tagid ,
} ) . then ( ( e ) => {
2020-06-13 00:30:20 +08:00
hideBackgroundLoader ( ) ;
2020-06-12 05:57:48 +08:00
let status = e . data . resultCode ;
2020-07-03 07:01:46 +08:00
if ( status === 1 ) {
2020-07-03 08:35:45 +08:00
showNotification ( "Tag updated" , 2000 ) ;
dbSnapshot . tags . forEach ( ( tag ) => {
2020-07-03 07:01:46 +08:00
if ( tag . id === tagid ) {
2020-06-12 02:38:24 +08:00
tag . name = inputVal ;
}
2020-07-03 08:35:45 +08:00
} ) ;
2020-06-25 04:20:35 +08:00
updateResultEditTagsPanelButtons ( ) ;
2020-06-12 02:38:24 +08:00
updateSettingsPage ( ) ;
2020-06-12 05:31:06 +08:00
updateFilterTags ( ) ;
2020-07-03 07:01:46 +08:00
} else if ( status === - 1 ) {
2020-07-03 08:35:45 +08:00
showNotification ( "Invalid tag name" , 3000 ) ;
2020-07-03 07:01:46 +08:00
} else if ( status < - 1 ) {
2020-07-03 08:35:45 +08:00
showNotification ( "Unknown error" , 3000 ) ;
2020-06-12 02:38:24 +08:00
}
2020-07-03 08:35:45 +08:00
} ) ;
2020-07-03 07:01:46 +08:00
} else if ( action === "remove" ) {
2020-06-13 00:30:20 +08:00
showBackgroundLoader ( ) ;
2020-07-03 08:35:45 +08:00
removeTag ( { uid : firebase . auth ( ) . currentUser . uid , tagid : tagid } ) . then (
( e ) => {
hideBackgroundLoader ( ) ;
let status = e . data . resultCode ;
if ( status === 1 ) {
showNotification ( "Tag removed" , 2000 ) ;
dbSnapshot . tags . forEach ( ( tag , index ) => {
if ( tag . id === tagid ) {
dbSnapshot . tags . splice ( index , 1 ) ;
}
} ) ;
updateResultEditTagsPanelButtons ( ) ;
updateSettingsPage ( ) ;
updateFilterTags ( ) ;
updateActiveTags ( ) ;
} else if ( status < - 1 ) {
showNotification ( "Unknown error" , 3000 ) ;
}
2020-06-12 02:38:24 +08:00
}
2020-07-03 08:35:45 +08:00
) ;
2020-06-12 02:38:24 +08:00
}
2020-06-16 06:25:28 +08:00
}
2020-05-27 08:00:28 +08:00
2020-07-23 20:42:16 +08:00
function showCapsWarning ( ) {
if ( $ ( "#capsWarning" ) . hasClass ( "hidden" ) ) {
$ ( "#capsWarning" ) . removeClass ( "hidden" ) ;
}
}
function hideCapsWarning ( ) {
if ( ! $ ( "#capsWarning" ) . hasClass ( "hidden" ) ) {
$ ( "#capsWarning" ) . addClass ( "hidden" ) ;
}
}
2020-05-15 10:28:13 +08:00
$ ( document ) . on ( "click" , "#top .logo" , ( e ) => {
2020-07-03 08:35:45 +08:00
changePage ( "test" ) ;
2020-05-15 10:28:13 +08:00
} ) ;
2020-07-03 01:39:24 +08:00
$ ( document ) . on ( "click" , "#top .config .wordCount .text-button" , ( e ) => {
2020-07-03 08:35:45 +08:00
wrd = $ ( e . currentTarget ) . attr ( "wordCount" ) ;
2020-07-03 07:01:46 +08:00
if ( wrd == "custom" ) {
2020-07-03 08:35:45 +08:00
let newWrd = prompt ( "Custom word amount" ) ;
2020-07-03 07:01:46 +08:00
if ( newWrd !== null && ! isNaN ( newWrd ) && newWrd > 0 && newWrd <= 10000 ) {
2020-05-24 21:22:41 +08:00
changeWordCount ( newWrd ) ;
2020-07-03 07:01:46 +08:00
if ( newWrd > 2000 ) {
2020-07-03 08:35:45 +08:00
showNotification (
"Very long tests can cause performance issues or crash the website on some machines!" ,
5000
) ;
2020-07-03 03:19:32 +08:00
}
2020-07-12 21:40:59 +08:00
} else {
showNotification (
"Custom word amount can only be set between 1 and 10000" ,
3000
) ;
2020-05-24 21:22:41 +08:00
}
2020-07-03 07:01:46 +08:00
} else {
2020-05-24 21:22:41 +08:00
changeWordCount ( wrd ) ;
}
2020-05-26 07:31:03 +08:00
restartTest ( ) ;
2020-05-11 07:32:10 +08:00
} ) ;
2020-05-10 06:33:48 +08:00
2020-07-03 01:39:24 +08:00
$ ( document ) . on ( "click" , "#top .config .time .text-button" , ( e ) => {
2020-07-03 08:35:45 +08:00
time = $ ( e . currentTarget ) . attr ( "timeConfig" ) ;
2020-07-03 07:01:46 +08:00
if ( time == "custom" ) {
2020-07-03 08:35:45 +08:00
let newTime = prompt ( "Custom time in seconds" ) ;
2020-07-03 07:01:46 +08:00
if ( newTime !== null && ! isNaN ( newTime ) && newTime > 0 && newTime <= 3600 ) {
2020-05-23 02:23:15 +08:00
changeTimeConfig ( newTime ) ;
2020-07-03 07:01:46 +08:00
if ( newTime >= 1800 ) {
2020-07-03 08:35:45 +08:00
showNotification (
"Very long tests can cause performance issues or crash the website on some machines!" ,
5000
) ;
2020-07-03 03:19:32 +08:00
}
2020-07-12 21:40:59 +08:00
} else {
showNotification ( "Custom time can only be set between 1 and 3600" , 3000 ) ;
2020-05-23 02:23:15 +08:00
}
2020-07-03 07:01:46 +08:00
} else {
2020-05-23 02:23:15 +08:00
changeTimeConfig ( time ) ;
}
2020-05-26 07:31:03 +08:00
restartTest ( ) ;
2020-05-11 07:32:10 +08:00
} ) ;
2020-07-03 01:39:24 +08:00
$ ( document ) . on ( "click" , "#top .config .customText .text-button" , ( e ) => {
2020-05-11 07:32:10 +08:00
changeCustomText ( ) ;
2020-05-26 07:31:03 +08:00
restartTest ( ) ;
2020-05-11 07:32:10 +08:00
} ) ;
2020-07-03 01:39:24 +08:00
$ ( document ) . on ( "click" , "#top .config .punctuationMode .text-button" , ( e ) => {
2020-05-11 07:32:10 +08:00
togglePunctuation ( ) ;
restartTest ( ) ;
} ) ;
$ ( "#words" ) . click ( ( e ) => {
focusWords ( ) ;
} ) ;
2020-05-09 08:29:47 +08:00
2020-07-03 01:39:24 +08:00
$ ( document ) . on ( "click" , "#top .config .mode .text-button" , ( e ) => {
2020-05-09 08:29:47 +08:00
if ( $ ( e . currentTarget ) . hasClass ( "active" ) ) return ;
mode = e . currentTarget . innerHTML ;
changeMode ( mode ) ;
2020-05-10 01:39:23 +08:00
restartTest ( ) ;
2020-05-09 08:29:47 +08:00
} ) ;
2020-07-03 01:39:24 +08:00
$ ( document ) . on ( "click" , "#top #menu .icon-button" , ( e ) => {
2020-07-03 08:35:45 +08:00
if ( $ ( e . currentTarget ) . hasClass ( "discord" ) ) return ;
2020-07-04 02:30:05 +08:00
if ( $ ( e . currentTarget ) . hasClass ( "leaderboards" ) ) {
showLeaderboards ( ) ;
} else {
href = $ ( e . currentTarget ) . attr ( "href" ) ;
changePage ( href . replace ( "/" , "" ) ) ;
}
2020-07-03 08:35:45 +08:00
} ) ;
2020-05-10 01:39:23 +08:00
2020-07-03 08:35:45 +08:00
$ ( window ) . on ( "popstate" , ( e ) => {
2020-05-15 03:22:59 +08:00
let state = e . originalEvent . state ;
if ( state == "" || state == "/" ) {
2020-05-10 01:39:23 +08:00
// show test
2020-07-03 08:35:45 +08:00
changePage ( "test" ) ;
2020-05-15 03:22:59 +08:00
} else if ( state == "about" ) {
2020-05-10 01:39:23 +08:00
// show about
changePage ( "about" ) ;
2020-05-15 03:22:59 +08:00
} else if ( state == "account" || state == "login" ) {
2020-05-10 06:33:48 +08:00
if ( firebase . auth ( ) . currentUser ) {
changePage ( "account" ) ;
} else {
2020-07-03 08:35:45 +08:00
changePage ( "login" ) ;
2020-05-10 06:33:48 +08:00
}
2020-05-10 01:39:23 +08:00
}
2020-05-11 07:32:10 +08:00
} ) ;
2020-05-09 08:29:47 +08:00
2020-05-15 11:09:00 +08:00
$ ( document ) . on ( "keypress" , "#restartTestButton" , ( event ) => {
2020-07-20 22:16:54 +08:00
if ( event . keyCode == 13 ) {
2020-06-15 01:19:56 +08:00
if ( testActive && ! afkDetected ) {
2020-06-09 01:58:18 +08:00
let testNow = Date . now ( ) ;
let testSeconds = roundTo2 ( ( testNow - testStart ) / 1000 ) ;
incompleteTestSeconds += testSeconds ;
2020-05-17 20:17:08 +08:00
restartCount ++ ;
}
2020-05-09 08:29:47 +08:00
restartTest ( ) ;
}
} ) ;
2020-05-15 11:09:00 +08:00
$ ( document . body ) . on ( "click" , "#restartTestButton" , ( event ) => {
2020-05-09 08:29:47 +08:00
restartTest ( ) ;
} ) ;
2020-05-27 08:00:28 +08:00
$ ( document ) . on ( "keypress" , "#showWordHistoryButton" , ( event ) => {
2020-07-20 22:16:54 +08:00
if ( event . keyCode == 13 ) {
2020-05-27 08:00:28 +08:00
toggleResultWordsDisplay ( ) ;
}
} ) ;
$ ( document . body ) . on ( "click" , "#showWordHistoryButton" , ( event ) => {
toggleResultWordsDisplay ( ) ;
} ) ;
2020-06-06 09:05:21 +08:00
$ ( document . body ) . on ( "click" , "#restartTestButtonWithSameWordset" , ( event ) => {
restartTest ( true ) ;
} ) ;
$ ( document ) . on ( "keypress" , "#restartTestButtonWithSameWordset" , ( event ) => {
2020-07-20 22:16:54 +08:00
if ( event . keyCode == 13 ) {
2020-06-06 09:05:21 +08:00
restartTest ( true ) ;
}
} ) ;
2020-06-05 07:00:45 +08:00
$ ( document . body ) . on ( "click" , "#copyResultToClipboardButton" , ( event ) => {
copyResultToClipboard ( ) ;
} ) ;
2020-05-17 19:40:41 +08:00
$ ( document . body ) . on ( "click" , ".version" , ( event ) => {
2020-07-03 08:35:45 +08:00
$ ( "#versionHistoryWrapper" )
. css ( "opacity" , 0 )
. removeClass ( "hidden" )
. animate ( { opacity : 1 } , 125 ) ;
2020-05-17 19:40:41 +08:00
} ) ;
$ ( document . body ) . on ( "click" , "#versionHistoryWrapper" , ( event ) => {
2020-07-03 08:35:45 +08:00
$ ( "#versionHistoryWrapper" )
. css ( "opacity" , 1 )
. animate ( { opacity : 0 } , 125 , ( ) => {
$ ( "#versionHistoryWrapper" ) . addClass ( "hidden" ) ;
} ) ;
2020-05-17 19:40:41 +08:00
} ) ;
2020-05-09 08:29:47 +08:00
$ ( "#wordsInput" ) . keypress ( ( event ) => {
event . preventDefault ( ) ;
} ) ;
$ ( "#wordsInput" ) . on ( "focus" , ( event ) => {
2020-06-06 20:22:52 +08:00
showCaret ( ) ;
2020-05-09 08:29:47 +08:00
} ) ;
$ ( "#wordsInput" ) . on ( "focusout" , ( event ) => {
hideCaret ( ) ;
} ) ;
2020-05-11 07:32:10 +08:00
$ ( window ) . resize ( ( ) => {
updateCaretPosition ( ) ;
} ) ;
2020-07-03 07:01:46 +08:00
$ ( document ) . mousemove ( function ( event ) {
2020-07-03 08:35:45 +08:00
if (
$ ( "#top" ) . hasClass ( "focus" ) &&
( event . originalEvent . movementX > 0 || event . originalEvent . movementY > 0 )
) {
2020-05-25 05:29:16 +08:00
setFocus ( false ) ;
}
2020-05-11 07:32:10 +08:00
} ) ;
2020-05-10 01:39:23 +08:00
2020-05-11 07:32:10 +08:00
//keypresses for the test, using different method to be more responsive
2020-07-03 07:01:46 +08:00
$ ( document ) . keypress ( function ( event ) {
2020-06-04 00:10:14 +08:00
event = emulateLayout ( event ) ;
2020-05-09 08:29:47 +08:00
if ( ! $ ( "#wordsInput" ) . is ( ":focus" ) ) return ;
if ( event [ "keyCode" ] == 13 ) return ;
if ( event [ "keyCode" ] == 32 ) return ;
2020-05-16 05:25:38 +08:00
if ( event [ "keyCode" ] == 27 ) return ;
2020-07-15 09:15:21 +08:00
if ( event . key == "ContextMenu" ) return ;
2020-05-11 07:07:36 +08:00
//start the test
2020-06-30 23:26:44 +08:00
if ( currentInput == "" && inputHistory . length == 0 && ! testActive ) {
2020-07-03 07:01:46 +08:00
try {
2020-05-19 08:20:57 +08:00
if ( firebase . auth ( ) . currentUser != null ) {
2020-07-03 08:35:45 +08:00
firebase . analytics ( ) . logEvent ( "testStarted" ) ;
2020-05-19 08:20:57 +08:00
} else {
2020-07-03 08:35:45 +08:00
firebase . analytics ( ) . logEvent ( "testStartedNoLogin" ) ;
2020-05-19 08:20:57 +08:00
}
2020-07-03 07:01:46 +08:00
} catch ( e ) {
2020-05-19 08:20:57 +08:00
console . log ( "Analytics unavailable" ) ;
2020-05-15 09:58:32 +08:00
}
2020-05-09 08:29:47 +08:00
testActive = true ;
testStart = Date . now ( ) ;
2020-06-22 06:51:48 +08:00
// if (config.mode == "time") {
2020-07-03 07:01:46 +08:00
restartTimer ( ) ;
showTimer ( ) ;
2020-07-13 06:53:15 +08:00
$ ( "#liveWpm" ) . text ( "0" ) ;
showLiveWpm ( ) ;
2020-06-22 06:51:48 +08:00
// }
2020-06-22 02:50:34 +08:00
updateActiveElement ( ) ;
2020-06-16 06:13:40 +08:00
updateTimer ( ) ;
2020-05-15 06:19:28 +08:00
clearIntervals ( ) ;
2020-07-06 07:05:42 +08:00
keypressStats = {
spacing : {
current : - 1 ,
array : [ ] ,
} ,
duration : {
current : - 1 ,
array : [ ] ,
} ,
} ;
2020-07-03 08:35:45 +08:00
timers . push (
setInterval ( function ( ) {
time ++ ;
if ( config . mode === "time" ) {
updateTimer ( ) ;
2020-05-09 08:29:47 +08:00
}
2020-07-13 06:53:15 +08:00
// console.time("livewpm");
// let wpm = liveWPM();
// updateLiveWpm(wpm);
// showLiveWpm();
// wpmHistory.push(wpm);
// rawHistory.push(liveRaw());
let wpmAndRaw = liveWpmAndRaw ( ) ;
updateLiveWpm ( wpmAndRaw . wpm ) ;
wpmHistory . push ( wpmAndRaw . wpm ) ;
rawHistory . push ( wpmAndRaw . raw ) ;
// console.timeEnd("livewpm");
2020-07-03 08:35:45 +08:00
keypressPerSecond . push ( currentKeypressCount ) ;
currentKeypressCount = 0 ;
errorsPerSecond . push ( currentErrorCount ) ;
currentErrorCount = 0 ;
if (
keypressPerSecond [ time - 1 ] == 0 &&
keypressPerSecond [ time - 2 ] == 0 &&
keypressPerSecond [ time - 3 ] == 0 &&
keypressPerSecond [ time - 4 ] == 0 &&
keypressPerSecond [ time - 5 ] == 0 &&
keypressPerSecond [ time - 6 ] == 0 &&
! afkDetected
) {
showNotification ( "AFK detected" , 3000 ) ;
afkDetected = true ;
}
if ( config . mode == "time" ) {
if ( time >= config . time ) {
clearIntervals ( ) ;
hideCaret ( ) ;
testActive = false ;
showResult ( ) ;
}
}
} , 1000 )
) ;
2020-05-09 08:29:47 +08:00
} else {
if ( ! testActive ) return ;
}
2020-07-15 05:01:05 +08:00
let thisCharCorrect ;
2020-07-03 08:35:45 +08:00
if (
wordsList [ currentWordIndex ] . substring (
currentInput . length ,
currentInput . length + 1
) != event [ "key" ]
) {
2020-05-11 07:07:36 +08:00
accuracyStats . incorrect ++ ;
2020-05-24 06:52:49 +08:00
currentErrorCount ++ ;
2020-07-15 05:01:05 +08:00
thisCharCorrect = false ;
2020-05-11 07:07:36 +08:00
} else {
accuracyStats . correct ++ ;
2020-07-15 05:01:05 +08:00
thisCharCorrect = true ;
2020-05-09 08:29:47 +08:00
}
2020-05-23 23:29:36 +08:00
currentKeypressCount ++ ;
2020-07-15 05:01:05 +08:00
if ( config . stopOnError && ! thisCharCorrect ) return ;
2020-05-09 08:29:47 +08:00
currentInput += event [ "key" ] ;
2020-05-23 23:29:36 +08:00
setFocus ( true ) ;
2020-06-01 04:03:54 +08:00
activeWordTopBeforeJump = activeWordTop ;
2020-07-13 06:53:15 +08:00
compareInput ( null , currentInput , ! config . blindMode ) ;
2020-07-03 07:08:00 +08:00
// let newActiveTop = $("#words .word.active").position().top;
2020-07-13 06:53:15 +08:00
// console.time("offcheck1");
2020-07-03 07:08:00 +08:00
let newActiveTop = document . querySelector ( "#words .word.active" ) . offsetTop ;
2020-07-03 08:35:45 +08:00
if ( activeWordTopBeforeJump != newActiveTop ) {
2020-06-01 04:03:54 +08:00
activeWordJumped = true ;
}
2020-07-13 06:53:15 +08:00
// console.timeEnd("offcheck2");
2020-05-23 23:29:36 +08:00
updateCaretPosition ( ) ;
2020-05-09 08:29:47 +08:00
} ) ;
2020-07-06 07:05:42 +08:00
$ ( document ) . keydown ( ( event ) => {
keypressStats . duration . current = performance . now ( ) ;
2020-07-23 20:42:16 +08:00
if ( $ ( "#wordsInput" ) . is ( ":focus" ) ) {
try {
if ( event . originalEvent . getModifierState ( "CapsLock" ) ) {
showCapsWarning ( ) ;
} else {
hideCapsWarning ( ) ;
}
} catch ( e ) { }
}
2020-07-06 07:05:42 +08:00
} ) ;
$ ( document ) . keyup ( ( event ) => {
let now = performance . now ( ) ;
let diff = Math . abs ( keypressStats . duration . current - now ) ;
if ( keypressStats . duration . current !== - 1 ) {
keypressStats . duration . array . push ( diff ) ;
}
keypressStats . duration . current = now ;
} ) ;
2020-05-11 07:32:10 +08:00
//handle keyboard events
2020-05-09 08:29:47 +08:00
$ ( document ) . keydown ( ( event ) => {
2020-07-06 07:05:42 +08:00
let now = performance . now ( ) ;
let diff = Math . abs ( keypressStats . spacing . current - now ) ;
if ( keypressStats . spacing . current !== - 1 ) {
keypressStats . spacing . array . push ( diff ) ;
}
keypressStats . spacing . current = now ;
2020-05-15 11:09:00 +08:00
2020-07-06 07:05:42 +08:00
//tab
2020-05-12 07:59:12 +08:00
if ( event [ "keyCode" ] == 9 ) {
2020-06-30 04:16:01 +08:00
if ( config . quickTab ) {
2020-05-10 02:47:11 +08:00
event . preventDefault ( ) ;
2020-07-03 07:01:46 +08:00
if ( $ ( ".pageTest" ) . hasClass ( "active" ) ) {
2020-06-30 04:16:01 +08:00
if ( testActive && ! afkDetected ) {
let testNow = Date . now ( ) ;
let testSeconds = roundTo2 ( ( testNow - testStart ) / 1000 ) ;
incompleteTestSeconds += testSeconds ;
restartCount ++ ;
}
restartTest ( ) ;
2020-07-03 07:01:46 +08:00
} else {
2020-07-03 08:35:45 +08:00
changePage ( "test" ) ;
2020-05-17 20:17:08 +08:00
}
2020-05-10 02:47:11 +08:00
}
}
2020-05-11 07:32:10 +08:00
//only for the typing test
2020-05-09 08:29:47 +08:00
if ( $ ( "#wordsInput" ) . is ( ":focus" ) ) {
2020-05-11 07:32:10 +08:00
//backspace
2020-05-09 08:29:47 +08:00
if ( event [ "keyCode" ] == 8 ) {
event . preventDefault ( ) ;
if ( ! testActive ) return ;
if ( currentInput == "" && inputHistory . length > 0 ) {
if (
2020-07-03 08:35:45 +08:00
( inputHistory [ currentWordIndex - 1 ] ==
wordsList [ currentWordIndex - 1 ] &&
! config . freedomMode ) ||
$ ( $ ( ".word" ) [ currentWordIndex - 1 ] ) . hasClass ( "hidden" )
2020-05-09 08:29:47 +08:00
) {
return ;
} else {
2020-07-03 07:01:46 +08:00
if ( config . maxConfidence ) return ;
2020-05-09 08:29:47 +08:00
if ( event [ "ctrlKey" ] || event [ "altKey" ] ) {
currentInput = "" ;
inputHistory . pop ( ) ;
} else {
currentInput = inputHistory . pop ( ) ;
}
currentWordIndex -- ;
updateActiveElement ( ) ;
2020-07-13 06:53:15 +08:00
compareInput ( null , currentInput , ! config . blindMode ) ;
2020-05-09 08:29:47 +08:00
}
} else {
// if ($($(".word")[currentWordIndex - 1]).hasClass("hidden")) {
// return;
// }
if ( event [ "ctrlKey" ] ) {
currentInput = "" ;
} else {
currentInput = currentInput . substring ( 0 , currentInput . length - 1 ) ;
}
2020-07-13 06:53:15 +08:00
compareInput ( null , currentInput , ! config . blindMode ) ;
2020-05-09 08:29:47 +08:00
}
2020-06-25 05:23:59 +08:00
// currentKeypressCount++;
2020-05-09 08:29:47 +08:00
updateCaretPosition ( ) ;
}
//space
2020-07-17 05:40:45 +08:00
if ( event [ "keyCode" ] == 32 || event . key === " " ) {
2020-05-09 08:29:47 +08:00
if ( ! testActive ) return ;
if ( currentInput == "" ) return ;
2020-05-25 23:50:50 +08:00
event . preventDefault ( ) ;
2020-05-09 08:29:47 +08:00
let currentWord = wordsList [ currentWordIndex ] ;
2020-07-13 22:53:12 +08:00
// if (config.mode == "time") {
2020-07-17 06:38:55 +08:00
if ( ! config . showAllLines || config . mode == "time" ) {
2020-07-15 05:50:42 +08:00
// let currentTop = Math.floor($($("#words .word")[currentWordIndex]).position().top);
// let nextTop = Math.floor($($("#words .word")[currentWordIndex + 1]).position().top);
let currentTop = Math . floor (
document . querySelectorAll ( "#words .word" ) [ currentWordIndex ] . offsetTop
2020-07-14 23:51:51 +08:00
) ;
2020-07-15 05:50:42 +08:00
let nextTop ;
try {
nextTop = Math . floor (
document . querySelectorAll ( "#words .word" ) [ currentWordIndex + 1 ]
. offsetTop
) ;
} catch ( e ) {
nextTop = 0 ;
}
2020-07-14 23:51:51 +08:00
2020-07-15 05:50:42 +08:00
if ( nextTop > currentTop || activeWordJumped ) {
//last word of the line
if ( currentTestLine > 0 ) {
let hideBound = currentTop ;
if ( activeWordJumped ) {
hideBound = activeWordTopBeforeJump ;
}
activeWordJumped = false ;
let toHide = [ ] ;
let wordElements = $ ( "#words .word" ) ;
for ( let i = 0 ; i < currentWordIndex + 1 ; i ++ ) {
if ( $ ( wordElements [ i ] ) . hasClass ( "hidden" ) ) continue ;
// let forWordTop = Math.floor($(wordElements[i]).position().top);
let forWordTop = Math . floor ( wordElements [ i ] . offsetTop ) ;
if ( forWordTop < hideBound ) {
// $($("#words .word")[i]).addClass("hidden");
toHide . push ( $ ( $ ( "#words .word" ) [ i ] ) ) ;
}
2020-05-22 23:19:09 +08:00
}
2020-07-15 05:50:42 +08:00
toHide . forEach ( ( el ) => el . addClass ( "hidden" ) ) ;
2020-05-09 08:29:47 +08:00
}
2020-07-15 05:50:42 +08:00
currentTestLine ++ ;
2020-05-09 08:29:47 +08:00
}
}
2020-07-13 22:53:12 +08:00
// }
2020-07-03 08:35:45 +08:00
if ( config . blindMode ) $ ( "#words .word.active letter" ) . addClass ( "correct" ) ;
2020-07-13 06:53:15 +08:00
document
. querySelector ( "#words .word.active" )
. setAttribute ( "input" , currentInput ) ;
2020-05-09 08:29:47 +08:00
if ( currentWord == currentInput ) {
inputHistory . push ( currentInput ) ;
currentInput = "" ;
currentWordIndex ++ ;
updateActiveElement ( ) ;
updateCaretPosition ( ) ;
2020-06-25 05:21:30 +08:00
currentKeypressCount ++ ;
2020-07-15 05:01:05 +08:00
} else if ( ! config . stopOnError ) {
2020-05-09 08:29:47 +08:00
inputHistory . push ( currentInput ) ;
2020-07-03 08:35:45 +08:00
highlightBadWord ( currentWordIndex , ! config . blindMode ) ;
2020-05-09 08:29:47 +08:00
currentInput = "" ;
currentWordIndex ++ ;
2020-05-29 02:01:44 +08:00
if ( currentWordIndex == wordsList . length ) {
2020-05-11 07:32:10 +08:00
showResult ( ) ;
2020-05-09 08:29:47 +08:00
return ;
2020-07-03 08:35:45 +08:00
} else if (
config . difficulty == "expert" ||
config . difficulty == "master"
) {
2020-05-29 02:01:44 +08:00
showResult ( true ) ;
2020-07-03 07:01:46 +08:00
if ( ! afkDetected ) {
2020-06-15 01:19:56 +08:00
let testNow = Date . now ( ) ;
let testSeconds = roundTo2 ( ( testNow - testStart ) / 1000 ) ;
incompleteTestSeconds += testSeconds ;
restartCount ++ ;
}
2020-05-29 02:01:44 +08:00
return ;
2020-05-09 08:29:47 +08:00
}
updateActiveElement ( ) ;
updateCaretPosition ( ) ;
2020-06-25 05:21:30 +08:00
currentKeypressCount ++ ;
2020-05-09 08:29:47 +08:00
}
2020-07-03 08:35:45 +08:00
if (
config . mode === "words" ||
config . mode === "custom" ||
config . mode === "quote"
) {
2020-06-22 06:51:48 +08:00
updateTimer ( ) ;
}
2020-07-15 05:50:42 +08:00
if ( config . showAllLines ) {
if ( config . mode == "time" ) {
addWord ( ) ;
}
} else {
if ( config . mode == "time" || config . mode == "words" ) {
addWord ( ) ;
}
2020-05-09 08:29:47 +08:00
}
}
}
} ) ;
2020-05-11 07:32:10 +08:00
2020-05-11 23:29:18 +08:00
loadConfigFromCookie ( ) ;
2020-05-17 19:40:41 +08:00
getReleasesFromGitHub ( ) ;
2020-05-11 23:29:18 +08:00
2020-05-19 19:21:27 +08:00
if ( firebase . app ( ) . options . projectId === "monkey-type-dev-67af4" ) {
$ ( "#top .logo .bottom" ) . text ( "monkey-dev" ) ;
2020-06-27 08:13:42 +08:00
$ ( "head title" ) . text ( "Monkey Dev" ) ;
2020-07-03 08:35:45 +08:00
$ ( "body" ) . append ( `
2020-06-27 08:13:42 +08:00
< div class = "devIndicator tr" >
DEV
< / d i v >
< div class = "devIndicator bl" >
DEV
< / d i v >
` );
2020-05-19 19:21:27 +08:00
}
2020-05-31 04:43:15 +08:00
2020-05-19 19:21:27 +08:00
if ( window . location . hostname === "localhost" ) {
2020-07-03 07:01:46 +08:00
window . onerror = function ( error ) {
this . showNotification ( error , 3000 ) ;
2020-05-31 21:31:50 +08:00
} ;
2020-05-19 19:21:27 +08:00
$ ( "#top .logo .top" ) . text ( "localhost" ) ;
$ ( "head title" ) . text ( $ ( "head title" ) . text ( ) + " (localhost)" ) ;
2020-06-06 01:17:59 +08:00
firebase . functions ( ) . useFunctionsEmulator ( "http://localhost:5001" ) ;
2020-07-03 08:35:45 +08:00
$ ( "body" ) . append ( ` <div class="devIndicator tl">
2020-06-27 08:13:42 +08:00
local
< / d i v >
< div class = "devIndicator br" >
local
< / d i v > ` ) ;
2020-05-19 19:21:27 +08:00
}
2020-07-03 08:35:45 +08:00
$ ( document ) . on ( "mouseenter" , "#words .word" , ( e ) => {
2020-07-03 07:01:46 +08:00
if ( resultVisible ) {
2020-07-03 08:35:45 +08:00
let input = $ ( e . currentTarget ) . attr ( "input" ) ;
if ( input != undefined )
$ ( e . currentTarget ) . append ( ` <div class="wordInputAfter"> ${ input } </div> ` ) ;
2020-05-31 04:43:15 +08:00
}
2020-07-03 08:35:45 +08:00
} ) ;
2020-05-31 03:59:42 +08:00
2020-07-03 08:35:45 +08:00
$ ( document ) . on ( "mouseleave" , "#words .word" , ( e ) => {
$ ( ".wordInputAfter" ) . remove ( ) ;
} ) ;
2020-05-31 03:59:42 +08:00
2020-05-11 07:32:10 +08:00
$ ( document ) . ready ( ( ) => {
2020-07-03 07:01:46 +08:00
updateFavicon ( 32 , 14 ) ;
2020-07-03 08:35:45 +08:00
$ ( "body" ) . css ( "transition" , ".25s" ) ;
2020-05-26 07:31:03 +08:00
restartTest ( ) ;
2020-05-11 07:32:10 +08:00
if ( config . quickTab ) {
2020-07-03 08:35:45 +08:00
$ ( "#restartTestButton" ) . addClass ( "hidden" ) ;
}
$ ( "#centerContent" )
. css ( "opacity" , "0" )
. removeClass ( "hidden" )
. stop ( true , true )
. animate ( { opacity : 1 } , 250 ) ;
if ( window . location . pathname === "/account" ) {
history . replaceState ( "/" , null , "/" ) ;
} else if ( window . location . pathname !== "/" ) {
let page = window . location . pathname . replace ( "/" , "" ) ;
2020-06-24 01:58:53 +08:00
changePage ( page ) ;
}
2020-05-11 07:32:10 +08:00
} ) ;
2020-05-11 23:29:18 +08:00
let ctx = $ ( "#wpmChart" ) ;
let wpmOverTimeChart = new Chart ( ctx , {
2020-07-03 08:35:45 +08:00
type : "line" ,
2020-05-11 07:32:10 +08:00
data : {
labels : [ ] ,
2020-07-03 08:35:45 +08:00
datasets : [
{
label : "wpm" ,
data : [ ] ,
// backgroundColor: 'rgba(255, 255, 255, 0.25)',
borderColor : "rgba(125, 125, 125, 1)" ,
borderWidth : 2 ,
yAxisID : "wpm" ,
order : 2 ,
radius : 2 ,
2020-06-07 09:30:49 +08:00
} ,
2020-07-03 08:35:45 +08:00
{
label : "raw" ,
data : [ ] ,
// backgroundColor: 'rgba(255, 255, 255, 0.25)',
borderColor : "rgba(125, 125, 125, 1)" ,
borderWidth : 2 ,
yAxisID : "raw" ,
order : 3 ,
radius : 2 ,
2020-06-07 09:30:49 +08:00
} ,
2020-07-03 08:35:45 +08:00
{
label : "errors" ,
data : [ ] ,
// backgroundColor: 'rgba(255, 255, 255, 0.25)',
borderColor : "rgba(255, 125, 125, 1)" ,
borderWidth : 2 ,
order : 1 ,
yAxisID : "error" ,
// barPercentage: 0.1,
maxBarThickness : 10 ,
type : "scatter" ,
pointStyle : "crossRot" ,
radius : function ( context ) {
var index = context . dataIndex ;
var value = context . dataset . data [ index ] ;
return value . y <= 0 ? 0 : 3 ;
} ,
pointHoverRadius : function ( context ) {
var index = context . dataIndex ;
var value = context . dataset . data [ index ] ;
return value . y <= 0 ? 0 : 5 ;
} ,
} ,
] ,
2020-05-11 07:32:10 +08:00
} ,
options : {
2020-05-12 07:59:12 +08:00
tooltips : {
titleFontFamily : "Roboto Mono" ,
2020-05-24 21:05:40 +08:00
bodyFontFamily : "Roboto Mono" ,
2020-07-03 08:35:45 +08:00
mode : "index" ,
intersect : false ,
2020-05-12 07:59:12 +08:00
} ,
2020-05-11 07:32:10 +08:00
legend : {
display : false ,
labels : {
2020-07-03 08:35:45 +08:00
defaultFontFamily : "Roboto Mono" ,
} ,
2020-05-11 07:32:10 +08:00
} ,
responsive : true ,
maintainAspectRatio : false ,
2020-06-07 09:30:49 +08:00
// hover: {
// mode: 'x',
// intersect: false
// },
2020-05-11 07:32:10 +08:00
scales : {
2020-07-03 08:35:45 +08:00
xAxes : [
{
ticks : {
fontFamily : "Roboto Mono" ,
autoSkip : true ,
autoSkipPadding : 40 ,
} ,
2020-05-24 06:52:49 +08:00
display : true ,
2020-07-03 08:35:45 +08:00
scaleLabel : {
display : true ,
labelString : "Seconds" ,
fontFamily : "Roboto Mono" ,
} ,
2020-05-11 23:29:18 +08:00
} ,
2020-07-03 08:35:45 +08:00
] ,
yAxes : [
{
id : "wpm" ,
2020-05-31 21:54:54 +08:00
display : true ,
2020-07-03 08:35:45 +08:00
scaleLabel : {
display : true ,
labelString : "Words per Minute" ,
fontFamily : "Roboto Mono" ,
} ,
ticks : {
fontFamily : "Roboto Mono" ,
beginAtZero : true ,
min : 0 ,
autoSkip : true ,
autoSkipPadding : 40 ,
} ,
gridLines : {
display : false ,
} ,
2020-05-31 21:54:54 +08:00
} ,
2020-07-03 08:35:45 +08:00
{
id : "raw" ,
display : false ,
scaleLabel : {
display : true ,
labelString : "Raw Words per Minute" ,
fontFamily : "Roboto Mono" ,
} ,
ticks : {
fontFamily : "Roboto Mono" ,
beginAtZero : true ,
min : 0 ,
autoSkip : true ,
autoSkipPadding : 40 ,
} ,
gridLines : {
display : false ,
} ,
2020-05-31 21:54:54 +08:00
} ,
2020-07-03 08:35:45 +08:00
{
id : "error" ,
2020-05-24 06:52:49 +08:00
display : true ,
2020-07-03 08:35:45 +08:00
position : "right" ,
scaleLabel : {
display : true ,
labelString : "Errors" ,
fontFamily : "Roboto Mono" ,
} ,
ticks : {
precision : 0 ,
fontFamily : "Roboto Mono" ,
beginAtZero : true ,
autoSkip : true ,
autoSkipPadding : 40 ,
} ,
gridLines : {
display : true ,
} ,
2020-05-24 09:02:31 +08:00
} ,
2020-07-03 08:35:45 +08:00
] ,
2020-06-05 02:37:16 +08:00
} ,
annotation : {
2020-07-03 08:35:45 +08:00
annotations : [
{
enabled : false ,
type : "line" ,
mode : "horizontal" ,
scaleID : "wpm" ,
value : "-30" ,
borderColor : "red" ,
borderWidth : 1 ,
borderDash : [ 2 , 2 ] ,
label : {
// Background color of label, default below
backgroundColor : "blue" ,
fontFamily : "Roboto Mono" ,
// Font size of text, inherits from global
fontSize : 11 ,
// Font style of text, default below
fontStyle : "normal" ,
// Font color of text, default below
fontColor : "#fff" ,
// Padding of label to add left/right, default below
xPadding : 6 ,
// Padding of label to add top/bottom, default below
yPadding : 6 ,
// Radius of label rectangle, default below
cornerRadius : 3 ,
// Anchor position of label on line, can be one of: top, bottom, left, right, center. Default below.
position : "center" ,
// Whether the label is enabled and should be displayed
enabled : true ,
// Text to display in label - default is null. Provide an array to display values on a new line
content : "PB" ,
} ,
} ,
] ,
} ,
} ,
} ) ;