356 lines
9.0 KiB
JavaScript
356 lines
9.0 KiB
JavaScript
var data = {};
|
|
var audio = {};
|
|
var hits_correct = 0;
|
|
var hits_wrong = 0;
|
|
var start_time = 0;
|
|
var hpm = 0;
|
|
var ratio = 0;
|
|
|
|
data.chars = " jfkdlsahgyturieowpqbnvmcxz6758493021`-=[]\\;',./ABCDEFGHIJKLMNOPQRSTUVWXYZ~!@#$%^&*()_+{}|:\"<>?";
|
|
data.consecutive = 5;
|
|
data.word_length = 7;
|
|
data.current_layout = "qwerty";
|
|
layouts={};
|
|
layouts["qwerty"] = " jfkdlsahgyturieowpqbnvmcxz6758493021`-=[]\\;',./ABCDEFGHIJKLMNOPQRSTUVWXYZ~!@#$%^&*()_+{}|:\"<>?";
|
|
layouts["azerty"] = " jfkdlsmqhgyturieozpabnvcxw6758493021`-=[]\\;',./ABCDEFGHIJKLMNOPQRSTUVWXYZ~!@#$%^&*()_+{}|:\"<>?";
|
|
layouts["colemak"] = " ntesiroahdjglpufywqbkvmcxz1234567890'\",.!?:;/@$%&#*()_ABCDEFGHIJKLMNOPQRSTUVWXYZ~+-={}|^<>`[]\\";
|
|
layouts["bépo"] = " tesirunamc,èvodpléjbk'.qxghyfàzw6758493021`-=[]\\;/ABCDEFGHIJKLMNOPQRSTUVWXYZ~!@#$%^&*()_+{}|:\"<>?";
|
|
layouts["norman"] = " ntieosaygjkufrdlw;qbpvmcxz1234567890'\",.!?:;/@$%&#*()_ABCDEFGHIJKLMNOPQRSTUVWXYZ~+-={}|^<>`[]\\";
|
|
layouts["code-es6"] = " {}',;():.>=</_-|`!?#[]\\+\"@$%&*~^";
|
|
|
|
$(document).ready(function() {
|
|
load_audio();
|
|
if (localStorage.data != undefined) {
|
|
load();
|
|
render();
|
|
}
|
|
else {
|
|
set_level(1);
|
|
}
|
|
$(document).keypress(keyHandler);
|
|
});
|
|
|
|
|
|
function start_stats() {
|
|
start_time = start_time || Math.floor(new Date().getTime() / 1000);
|
|
}
|
|
|
|
function update_stats() {
|
|
if (start_time) {
|
|
var current_time = (Math.floor(new Date().getTime() / 1000));
|
|
ratio = Math.floor(
|
|
hits_correct / (hits_correct + hits_wrong) * 100
|
|
);
|
|
hpm = Math.floor(
|
|
(hits_correct + hits_wrong) / (current_time - start_time) * 60
|
|
);
|
|
if (!isFinite(hpm)) { hpm = 0; }
|
|
}
|
|
}
|
|
|
|
|
|
function set_level(l) {
|
|
data.in_a_row = {};
|
|
for(var i = 0; i < data.chars.length; i++) {
|
|
data.in_a_row[data.chars[i]] = data.consecutive;
|
|
}
|
|
data.in_a_row[data.chars[l]] = 0;
|
|
data.level = l;
|
|
data.word_index = 0;
|
|
data.word_errors = {};
|
|
data.word = generate_word();
|
|
data.keys_hit = "";
|
|
save();
|
|
render();
|
|
}
|
|
|
|
function set_layout(l) {
|
|
data.current_layout = l
|
|
data.chars = layouts[l]
|
|
data.in_a_row = {};
|
|
for(var i = 0; i < data.chars.length; i++) {
|
|
data.in_a_row[data.chars[i]] = data.consecutive;
|
|
}
|
|
data.word_index = 0;
|
|
data.word_errors = {};
|
|
data.word = generate_word();
|
|
data.keys_hit = "";
|
|
save();
|
|
render();
|
|
}
|
|
|
|
|
|
function keyHandler(e) {
|
|
start_stats();
|
|
|
|
var key = String.fromCharCode(e.which);
|
|
if (data.chars.indexOf(key) > -1){
|
|
e.preventDefault();
|
|
}
|
|
else {
|
|
return;
|
|
}
|
|
data.keys_hit += key;
|
|
if(key == data.word[data.word_index]) {
|
|
hits_correct += 1;
|
|
data.in_a_row[key] += 1;
|
|
play_audio_sample("correct");
|
|
}
|
|
else {
|
|
hits_wrong += 1;
|
|
data.in_a_row[data.word[data.word_index]] = 0;
|
|
data.in_a_row[key] = 0;
|
|
play_audio_sample("mistake");
|
|
data.word_errors[data.word_index] = true;
|
|
}
|
|
data.word_index += 1;
|
|
if (data.word_index >= data.word.length) {
|
|
setTimeout(next_word, 400);
|
|
}
|
|
|
|
update_stats();
|
|
|
|
render();
|
|
save();
|
|
}
|
|
|
|
function next_word(){
|
|
if(get_training_chars().length == 0) {
|
|
level_up();
|
|
}
|
|
data.word = generate_word();
|
|
data.word_index = 0;
|
|
data.keys_hit = "";
|
|
data.word_errors = {};
|
|
update_stats();
|
|
|
|
render();
|
|
save();
|
|
}
|
|
|
|
|
|
function level_up() {
|
|
if (data.level + 1 <= data.chars.length - 1) {
|
|
play_audio_sample("level_up");
|
|
}
|
|
l = Math.min(data.level + 1, data.chars.length);
|
|
set_level(l);
|
|
}
|
|
|
|
|
|
function save() {
|
|
localStorage.data = JSON.stringify(data);
|
|
}
|
|
|
|
|
|
function load() {
|
|
data = JSON.parse(localStorage.data);
|
|
}
|
|
|
|
|
|
function load_audio() {
|
|
audio.samples = {};
|
|
audio.context = new (window.AudioContext || window.webkitAudioContext)();
|
|
load_audio_sample("correct", "/maydo/keyzen/click.wav");
|
|
load_audio_sample("mistake", "/maydo/keyzen/clack.wav");
|
|
load_audio_sample("level_up", "/maydo/keyzen/ding.wav");
|
|
}
|
|
|
|
|
|
function load_audio_sample(name, url) {
|
|
if (!audio.samples[name]) {
|
|
// fetch the .wav file via XMLHttpRequest as jQuery doesn't support 'arraybuffer' dataType
|
|
var request = new XMLHttpRequest();
|
|
request.open("GET", url, true);
|
|
request.responseType = "arraybuffer";
|
|
request.onload = function () {
|
|
audio.context.decodeAudioData(request.response).then(function (buffer) {
|
|
audio.samples[name] = buffer;
|
|
});
|
|
};
|
|
request.send();
|
|
}
|
|
}
|
|
|
|
|
|
function play_audio_sample(name) {
|
|
if (audio.samples[name]) {
|
|
var source = audio.context.createBufferSource();
|
|
source.buffer = audio.samples[name];
|
|
source.onended
|
|
source.connect(audio.context.destination);
|
|
source.start();
|
|
}
|
|
}
|
|
|
|
|
|
function render() {
|
|
render_layout();
|
|
render_level();
|
|
render_word();
|
|
render_level_bar();
|
|
render_rigor();
|
|
render_stats();
|
|
}
|
|
|
|
function render_layout() {
|
|
var layouts_html = "<span id='layout'>";
|
|
for(var layout in layouts){
|
|
if(data.current_layout == layout){
|
|
layouts_html += "<span style='color: #F00' onclick='set_layout(\"" + layout + "\");'> "
|
|
} else {
|
|
layouts_html += "<span style='color: #AAA' onclick='set_layout(\"" + layout + "\");'> "
|
|
}
|
|
layouts_html += layout + "</span>";
|
|
}
|
|
layouts_html += "</span>";
|
|
$("#layout").html('Choose layout : ' + layouts_html);
|
|
}
|
|
|
|
function render_level() {
|
|
var chars = "<span id='level-chars-wrap'>";
|
|
var level_chars = get_level_chars();
|
|
var training_chars = get_training_chars();
|
|
for (var c in data.chars) {
|
|
if(training_chars.indexOf(data.chars[c]) != -1) {
|
|
chars += "<span style='color: #F00' onclick='set_level(" + c + ");'>"
|
|
}
|
|
else if (level_chars.indexOf(data.chars[c]) != -1) {
|
|
chars += "<span style='color: #000' onclick='set_level(" + c + ");'>"
|
|
}
|
|
else {
|
|
chars += "<span style='color: #AAA' onclick='set_level(" + c + ");'>"
|
|
}
|
|
if (data.chars[c] == ' ') {
|
|
chars += "⎵";
|
|
}
|
|
else {
|
|
chars += data.chars[c];
|
|
}
|
|
chars += "</span>";
|
|
}
|
|
chars += "</span>";
|
|
$("#level-chars").html('click to set level: ' + chars);
|
|
}
|
|
|
|
function render_rigor() {
|
|
chars = "<span id='rigor-number' onclick='inc_rigor();'>";
|
|
chars += '' + data.consecutive;
|
|
chars += '<span>';
|
|
$('#rigor').html('click to set required repititions: ' + chars);
|
|
}
|
|
|
|
function render_stats() {
|
|
$("#stats").text([
|
|
"hits per minute: ", hpm, " ",
|
|
"correctness: ", ratio, "%"
|
|
].join(""));
|
|
}
|
|
|
|
function inc_rigor() {
|
|
data.consecutive += 1;
|
|
if (data.consecutive > 9) {
|
|
data.consecutive = 2;
|
|
}
|
|
render_rigor();
|
|
}
|
|
|
|
|
|
function render_level_bar() {
|
|
training_chars = get_training_chars();
|
|
if(training_chars.length == 0) {
|
|
m = data.consecutive;
|
|
}
|
|
else {
|
|
m = 1e100;
|
|
for(c in training_chars) {
|
|
m = Math.min(data.in_a_row[training_chars[c]], m);
|
|
}
|
|
}
|
|
m = Math.floor($('#level-chars-wrap').innerWidth() * Math.min(1.0, m / data.consecutive));
|
|
$('#next-level').css({'width': '' + m + 'px'});
|
|
|
|
}
|
|
|
|
function render_word() {
|
|
var word = "";
|
|
for (var i = 0; i < data.word.length; i++) {
|
|
sclass = "normalChar";
|
|
if (i > data.word_index) {
|
|
sclass = "normalChar";
|
|
}
|
|
else if (i == data.word_index) {
|
|
sclass = "currentChar";
|
|
}
|
|
else if(data.word_errors[i]) {
|
|
sclass = "errorChar";
|
|
}
|
|
else {
|
|
sclass = "goodChar";
|
|
}
|
|
word += "<span class='" + sclass + "'>";
|
|
if(data.word[i] == " ") {
|
|
word += "⎵"
|
|
}
|
|
else if(data.word[i] == "&") {
|
|
word += "&"
|
|
}
|
|
else {
|
|
word += data.word[i];
|
|
}
|
|
word += "</span>";
|
|
}
|
|
var keys_hit = "<span class='keys-hit'>";
|
|
for(var d in data.keys_hit) {
|
|
if (data.keys_hit[d] == ' ') {
|
|
keys_hit += "⎵";
|
|
}
|
|
else if (data.keys_hit[d] == '&') {
|
|
keys_hit += "&";
|
|
}
|
|
else {
|
|
keys_hit += data.keys_hit[d];
|
|
}
|
|
}
|
|
for(var i = data.word_index; i < data.word_length; i++) {
|
|
keys_hit += " ";
|
|
}
|
|
keys_hit += "</span>";
|
|
$("#word").html(word + "<br>" + keys_hit);
|
|
}
|
|
|
|
|
|
function generate_word() {
|
|
word = '';
|
|
for(var i = 0; i < data.word_length; i++) {
|
|
c = choose(get_training_chars());
|
|
if(c != undefined && c != word[word.length-1]) {
|
|
word += c;
|
|
}
|
|
else {
|
|
word += choose(get_level_chars());
|
|
}
|
|
}
|
|
return word;
|
|
}
|
|
|
|
|
|
function get_level_chars() {
|
|
return data.chars.slice(0, data.level + 1).split('');
|
|
}
|
|
|
|
function get_training_chars() {
|
|
var training_chars = [];
|
|
var level_chars = get_level_chars();
|
|
for(var x in level_chars) {
|
|
if (data.in_a_row[level_chars[x]] < data.consecutive) {
|
|
training_chars.push(level_chars[x]);
|
|
}
|
|
}
|
|
return training_chars;
|
|
}
|
|
|
|
function choose(a) {
|
|
return a[Math.floor(Math.random() * a.length)];
|
|
}
|