Upload files to 'maydo/keyzen'
This commit is contained in:
parent
ae3af9b79c
commit
948134c52b
|
@ -0,0 +1,3 @@
|
|||
|
||||
app_icon aquired from
|
||||
http://commons.wikimedia.org/wiki/File:Crystal_Clear_app_keyboard.png
|
|
@ -0,0 +1,355 @@
|
|||
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", "click.wav");
|
||||
load_audio_sample("mistake", "clack.wav");
|
||||
load_audio_sample("level_up", "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)];
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="172.1498"
|
||||
height="172.1498"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="logo.svg"
|
||||
inkscape:export-filename="/home/rye/Dropbox/projects/keyzen/favicon.ico.png"
|
||||
inkscape:export-xdpi="8.3599997"
|
||||
inkscape:export-ydpi="8.3599997">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.979899"
|
||||
inkscape:cx="90.162846"
|
||||
inkscape:cy="104.10844"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="917"
|
||||
inkscape:window-height="610"
|
||||
inkscape:window-x="359"
|
||||
inkscape:window-y="103"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-248.03307,-459.94833)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:49.96237564px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ff6600;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="240.77084"
|
||||
y="663.97345"
|
||||
id="text3755"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="scale(1.0633781,0.94039928)"
|
||||
inkscape:export-xdpi="8.3599997"
|
||||
inkscape:export-ydpi="8.3599997"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3757"
|
||||
x="240.77084"
|
||||
y="663.97345"
|
||||
style="font-size:239.81939697px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu Bold">Z</tspan></text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
// Required
|
||||
"name": "KeyZen",
|
||||
"version": "1.0",
|
||||
|
||||
"description": "A touch typing app for Chrome, src is @ https://github.com/wwwtyro/keyzen",
|
||||
"icons": {
|
||||
"128": "app_icon.png"
|
||||
},
|
||||
"app": {
|
||||
"launch": {
|
||||
"local_path": "index.html"
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Loading…
Reference in New Issue