This repository has been archived on 2026-03-31. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
katakana/assets/www/index.html

520 lines
15 KiB
HTML
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE HTML>
<html>
<head>
<meta name="viewport" content="width=320; user-scalable=no" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Katanakana</title>
<style type="text/css">
html {
height: 100%;
background: -webkit-gradient(linear, left top, left bottom, from(#dedede), to(#f9f9f9));
color: #444;
padding: 0;
margin: 0;
font-family: 'Droid Sans', helvetica, sans-serif;
overflow: hidden;
}
body { padding: 0; margin: 0; height: 100%; }
#header {
position: relative;
width: 100%;
height: 40px;
background: -webkit-gradient(linear, left top, left bottom, from(#3d3d3d), to(#010101));
-webkit-box-shadow: 1px 1px 5px #787878;
}
#header span {
position: absolute;
font-size: 20px;
top: 9px;
left: 10px;
font-weight: bold;
text-shadow: 1px 1px 1px #787878;
color: #f9f9f9;
}
#right, #wrong {
float: right;
padding-top: 12px;
height: 28px;
min-width: 40px;
text-align: center;
color: #f9f9f9;
text-shadow: 1px 1px 1px #121212;
border-left: 1px solid #010101;
}
#right { background: -webkit-gradient(linear, left top, left bottom, from(#6fe78c), to(#169a35)); }
#wrong { background: -webkit-gradient(linear, left top, left bottom, from(#e92222), to(#9f1515)); }
.right { background: -webkit-gradient(linear, left top, left bottom, from(#6fe78c), to(#169a35)) !important; border: 1px solid #169a35 !important; }
.wrong { background: -webkit-gradient(linear, left top, left bottom, from(#e92222), to(#9f1515)) !important; border: 1px solid #9f1515 !important; }
#intro {
padding: 10px;
height: 0; /* Default, gets judged */
text-align: left;
-webkit-transition: opacity .25s linear;
}
#intro h1 {
font-size: 20px;
text-shadow: 1px 1px 1px #b7b7b7;
color: #010101;
padding-bottom: 6px;
margin-bottom: 10px;
border-bottom: 1px solid #c9c9c9;
}
#intro p {
margin: 0 0 15px 0;
font-size: 14px;
line-height: 16px;
text-shadow: 1px 1px 1px #b9b9b9;
}
#game {
height: 0; /* Default, gets judged */
-webkit-transition: opacity .25s linear;
padding: 10px;
position: relative;
text-align: center;
}
#letter, #choices { float: left; }
#letter {
font-weight: bold;
border: 1px solid #b9b9b9;
background: -webkit-gradient(linear, left top, left bottom, from(#fafafa), to(#e7e7e7));
-webkit-border-radius: 6px;
-webkit-box-shadow: 1px 1px 5px #f7f7f7;
-webkit-transition: opacity .25s linear;
margin-bottom: 9px;
letter-spacing: -1px;
}
#choices button, #get_started {
color: #fff;
font-weight: bold;
text-shadow: 1px 1px 3px #121212;
border: 1px solid #1874ca;
-webkit-box-shadow: 1px 1px 5px #787878;
width: 100%;
margin: 0 auto 10px;
padding: 7px 0;
-webkit-border-radius: 6px;
background: -webkit-gradient(linear, left top, left bottom, from(#2792f6), to(#146abb));
}
</style>
</head>
<body>
<div id="header">
<span>Katanakana</span>
<div id="right">0</div>
<div id="wrong">0</div>
</div>
<div id="intro">
<h1>You Can Do This!</h1>
<p>
Learning any of the Japanese alphabets can seem like a daunting task because there's
just so many characters. Thing is, this doesn't <em>have</em> to be difficult!
</p>
<p>
The human brain works in a mysterious fashion, and everybody has their own point at which the brain commits something
to memory. Katanakana watches how you identify characters, and will figure out when your brain has the best chance of
storing the relations you need to read Japanese.
</p>
<p>
Once this is determined, it'll make you re-identify those characters
at the ideal moment, and you'll be good to go!
</p>
<button id="get_started">Get Started Now!</button>
</div>
<div id="game" style="display: none; opacity: 0;">
<div id="letter"></div>
<div id="choices">
<button id="choice_1"></button>
<button id="choice_2"></button>
<button id="choice_3"></button>
<button id="choice_4"></button>
</div>
</div>
<script type="text/javascript" charset="utf-8" src="phonegap.0.9.4.js"></script>
<script type="text/javascript">
Array.prototype.contains = function(v) {
for(var i = 0, x = this.length; i < x; i++) {
if(this[i] === v) return true;
}
return false;
};
Array.prototype.unique = function(blacklist) {
var newArr = [];
for(var i = 0, l = this.length; i < l; i++) {
if(newArr.contains(this[i]) || (typeof blacklist !== 'undefined' && this[i] === blacklist)) this.splice(i, 1);
else newArr.push(this[i]);
}
return this;
};
/* Fisher Yates what up */
Array.prototype.shuffle = function() {
var i = this.length;
if(i === 0) return false;
while(--i) {
var j = Math.floor(Math.random() * (i + 1)),
tmpi = this[i],
tmpj = this[j];
this[i] = tmpj;
this[j] = tmpi;
}
return this;
};
Array.prototype.average = function() {
var avg = 0,
i = this.length;
if(i === 0) return false;
while(--i) {
avg = avg + this[i];
}
return parseInt(avg / this.length, 10);
};
var kat = {
choices: [],
game: null,
letter: null,
right: null,
wrong: null,
rightCount: 0,
wrongCount: 0,
oldFontSize: null,
orientationSwapFontFize: null,
/* Default to two minutes... */
idealLearnTime: 1000*60,
/* Gets constructed in a bit. */
sounds: [],
/* An array of ones that were chosen incorrectly. Circles back to them
* on average every 2 minutes to start.
*/
loopBacks: [],
previousLoopBackTimers: [1000*60*2],
/* Used for lookup of time taken to commit to memory. */
previousLoopBacks: {},
/* This gets set to true after the 4 minute mark, wherein the
* turn functions will take over until loopBacks is empty for each new
* turn.
*/
runloopBacks: false,
/* Stored object from setTimeout, for checking. */
loopBackTimer: null,
katas: {
'ア': 'a <em>(ah)</em>',
'カ': 'ka <em>(kah)</em>',
'サ': 'sa <em>(sah)</em>',
'タ': 'ta <em>(tah)</em>',
'ナ': 'na <em>(nah)</em>',
'ハ': 'ha <em>(hah)</em>',
'マ': 'ma <em>(mah)</em>',
'ヤ': 'ya <em>(yah)</em>',
'ラ': 'ra <em>(rah)</em>',
'ワ': 'wa <em>(wah)</em>',
'ン': 'n',
'ガ': 'ga <em>(gah)</em>',
'ザ': 'za <em>(zah)</em>',
'ダ': 'da <em>(dah)</em>',
'バ': 'ba <em>(bah)</em>',
'パ': 'pa <em>(pah)</em>',
'アァ': 'fa <em>(fah)</em>',
'キャ': 'kya <em>(kyah)</em>',
'シャ': 'sha <em>(shah)</em>',
'チャ': 'cha <em>(chah)</em>',
'ニャ': 'nya <em>(nyah)</em>',
'ヒャ': 'hya <em>(hyah)</em>',
'ミャ': 'mya <em>(myah)</em>',
'リャ': 'rya <em>(ryah)</em>',
'ギャ': 'gya <em>(gyah)</em>',
'ヅャ': 'ja <em>(jah)</em>',
'ビャ': 'bya <em>(byah)</em>',
'ピャ': 'pya <em>(pyah)</em>',
'イ': 'i <em>(ee)</em>',
'キ': 'ki <em>(kee)</em>',
'シ': 'shi <em>(shee)</em>',
'チ': 'chi <em>(chee)</em>',
'ニ': 'ni <em>(nee)</em>',
'ヒ': 'hi <em>(hee)</em>',
'ミ': 'mi <em>(mee)</em>',
'リ': 'ri <em>(ree)</em>',
'ギ': 'gi <em>(gee)</em>',
'ジ': 'ji <em>(jee)</em>',
'ヂ': 'ji <em>(jee)</em>',
'ビ': 'bi <em>(bee)</em>',
'ピ': 'pi <em>(pee)</em>',
'ワィ': 'fi <em>(fee)</em>',
'ウ': 'u <em>(oo)</em>',
'ク': 'ku <em>(koo)</em>',
'ス': 'su <em>(soo)</em>',
'ツ': 'tsu <em>(tsoo)</em>',
'ヌ': 'nu <em>(noo)</em>',
'フ': 'fu <em>(foo)</em>',
'ム': 'mu <em>(moo)</em>',
'ユ': 'yu <em>(yoo)</em>',
'ル': 'ru <em>(roo)</em>',
'グ': 'gu <em>(goo)</em>',
'ズ': 'zu <em>(zoo)</em>',
'ヅ': 'zu <em>(zoo)</em>',
'ブ': 'bu <em>(boo)</em>',
'プ': 'pu <em>(poo)</em>',
'キユ': 'kyu <em>(kyoo)</em>',
'シユ': 'shu <em>(shoo)</em>',
'チユ': 'chu <em>(choo)</em>',
'ニユ': 'nyu <em>(nyoo)</em>',
'ヒユ': 'hyu <em>(hyoo)</em>',
'ミユ': 'myu <em>(myoo)</em>',
'リユ': 'ryu <em>(ryoo)</em>',
'ギユ': 'gyu <em>(gyoo)</em>',
'ジユ': 'ju <em>(joo)</em>',
'ビユ': 'byu <em>(byoo)</em>',
'ピユ': 'pyu <em>(pyoo)</em>',
'エ': 'e <em>(eh)</em>',
'ケ': 'ke <em>(keh)</em>',
'セ': 'se <em>(seh)</em>',
'テ': 'te <em>(teh)</em>',
'ネ': 'ne <em>(neh)</em>',
'ヘ': 'he <em>(heh)</em>',
'メ': 'me <em>(meh)</em>',
'レ': 're <em>(reh)</em>',
'ゲ': 'ge <em>(geh)</em>',
'ゼ': 'ze <em>(zeh)</em>',
'デ': 'de <em>(deh)</em>',
'ベ': 'be <em>(beh)</em>',
'ペ': 'pe <em>(peh)</em>',
'フエ': 'fe <em>(feh)</em>',
'オ': 'o <em>(oh)</em>',
'コ': 'ko <em>(koh)</em>',
'ソ': 'so <em>(soh)</em>',
'ト': 'to <em>(toh)</em>',
'': 'no <em>(noh)</em>',
'ホ': 'ho <em>(hoh)</em>',
'モ': 'mo <em>(moh)</em>',
'ヨ': 'yo <em>(yoh)</em>',
'ロ': 'ro <em>(roh)</em>',
'ヲ': 'o <em>(oh)</em>',
'ゴ': 'go <em>(goh)</em>',
'ゾ': 'zo <em>(zoh)</em>',
'ド': 'do <em>(doh)</em>',
'ボ': 'bo <em>(boh)</em>',
'ポ': 'po <em>(poh)</em>',
'フォ': 'fo <em>(foh)</em>',
'キヨ': 'kyo <em>(kyoh)</em>',
'シヨ': 'sho <em>(shoh)</em>',
'チヨ': 'cho <em>(choh)</em>',
'ニヨ': 'nyo <em>(nyoh)</em>',
'ヒヨ': 'hyo <em>(hyoh)</em>',
'ミヨ': 'myo <em>(myoh)</em>',
'リヨ': 'ryo <em>(ryoh)</em>',
'ギヨ': 'gyo <em>(gyoh)</em>',
'ジヨ': 'jo <em>(joh)</em>',
'ビヨ': 'byo <em>(byoh)</em>',
'ピヨ': 'pyo <em>(pyoh)</em>',
},
/* Yeah custom layout managers because CSS wasn't meant for this. ;P */
resolveLayout: function() {
var w = window.innerWidth,
h = window.innerHeight;
/* Minus 20 to account for left/right automagic padding. */
kat.game.style.width = (w - 20) + 'px';
/* Minus 80 for (40 = header height, 20 pixels total top/bottom automagic padding) */
kat.game.style.height = (h - 80) + 'px';
/* Get the letter nice and into position. */
if(w < h) {
var ideal = w - 22;
kat.letter.style.cssText = "width: " + ideal + "px; height: " + ideal + "px; margin: 0 auto 10px;";
kat.letter.style.fontSize = (kat.letter.innerHTML.length > 1 ? ideal - 150 : ideal - 30) + "px";
kat.choicesNode.style.cssText = 'float: left; clear: left; width: ' + ideal + 'px;';
kat.choices.forEach(function(btn) {
btn.style.cssText = 'padding: 7px 0; font-size: 12px;';
});
} else {
var ideal = h - 60;
kat.letter.style.cssText = "width: " + ideal + "px; height: " + ideal + "px; margin: 0 10px 0 0;";
kat.letter.style.fontSize = (kat.letter.innerHTML.length > 1 ? ideal - 140 : ideal - 130) + "px";
kat.choicesNode.style.cssText = 'float: right; clear: none; width: ' + ((w - 35) - ideal) + 'px;';
kat.choices.forEach(function(btn) {
btn.style.cssText = 'padding: 50px 0; font-size: 40px;';
});
}
},
check: function(e) {
e.preventDefault();
if(e.srcElement.nodeType !== 1) return false;
var ch = kat.letter.innerHTML,
node = e.srcElement.nodeName === 'EM' ? e.srcElement.parentNode : e.srcElement,
possibleOtherNode = null,
answer = node.innerHTML;
if(kat.katas[ch] === answer) {
node.className = 'right';
++kat.rightCount;
kat.right.innerHTML = kat.rightCount;
if(typeof kat.previousLoopBacks[ch] !== 'undefined') {
var diff = +new Date() - kat.previousLoopBacks[ch];
kat.previousLoopBacks[ch] = undefined;
kat.previousLoopBackTimers.push(diff);
kat.idealLearnTime = kat.previousLoopBackTimers.average();
}
} else {
kat.loopBacks.push(ch);
/* If we're in loopBack mode and this is one they already got wrong
* once, then we wanna set the idealLearnTime to be the average of all the previous
* idealLearnTimes (set when they get one they had wrong correct).
*/
if(typeof kat.previousLoopBacks[ch] === 'undefined') {
kat.previousLoopBacks[ch] = +new Date();
}
/* We're gonna come back to this one down the road. */
if(kat.loopBackTimer === null) {
kat.loopBackTimer = setTimeout(function() {
kat.runloopBacks = true;
clearTimeout(kat.loopBackTimer);
kat.loopBackTimer = null;
}, kat.idealLearnTime);
}
++kat.wrongCount;
kat.wrong.innerHTML = kat.wrongCount;
var correct_answer = kat.katas[ch];
for(var choice in kat.choices) {
if(kat.choices[choice].innerHTML === correct_answer) {
possibleOtherNode = kat.choices[choice];
node.className = 'wrong';
possibleOtherNode.className = 'right';
}
}
}
setTimeout(function() {
node.className = '';
if(possibleOtherNode !== null) possibleOtherNode.className = '';
kat.newTurn();
}, 1500);
return false;
},
newTurn: function() {
if(kat.loopBacks.length <= 0) kat.runloopBacks = false;
var kata = kat.sounds[Math.floor(Math.random() * (kat.sounds.length - 1))];
if(kat.runloopBacks) {
var rndm = kat.loopBacks.splice(Math.floor(Math.random() * (kat.loopBacks.length - 1)), 1)[0];
kata = {
ch: rndm,
sound: kat.katas[rndm]
};
}
var answers = [kata.sound],
extraAnswers = [],
choices = [],
lstRndm = null;
for(var i = 0; i < 10; i++) {
extraAnswers.push(kat.sounds[Math.floor(Math.random() * (kat.sounds.length - 1))].sound);
}
answers = answers.concat(extraAnswers.unique(kata.sound).slice(0, 3)).shuffle();
if(kata.ch.length > 1) {
if(kat.oldFontSize === null) {
kat.oldFontSize = parseInt(window.getComputedStyle(kat.letter, '')['font-size']);
kat.letter.style.fontSize = (kat.oldFontSize - 140) + 'px';
}
kat.letter.innerHTML = kata.ch;
} else {
if(kat.oldFontSize !== null) {
kat.letter.style.fontSize = kat.oldFontSize + 'px';
kat.oldFontSize = null;
}
kat.letter.innerHTML = kata.ch;
}
for(var choice in kat.choices) {
choices.push(kat.choices[choice]);
}
for(var i = 0, j = choices.length; i < j; i++) {
choices[i].innerHTML = answers[i];
}
return false;
},
};
document.addEventListener('deviceready', function() {
//document.addEventListener('menuKeyDown', function(e) {
// navigator.notification.alert(device.uuid, function(){}, '', 'Dismiss');
//}, false);
/* Absense of querySelectorAll pre-Android 2.0 makes Ryan sad. ;P */
['choice_1', 'choice_2', 'choice_3', 'choice_4'].forEach(function(choice) {
kat.choices[choice] = document.getElementById(choice);
kat.choices[choice].addEventListener('touchstart', kat.check, false);
});
['intro', 'game', 'letter','right', 'wrong', 'get_started'].forEach(function(nodeName) {
kat[nodeName] = document.getElementById(nodeName);
});
/* Need to go ahead and get a separate Array for the sounds to pull false entries from. */
for(var key in kat.katas) { kat.sounds.push({ch: key, sound: kat.katas[key]}); }
/* When they hit get started, start... */
kat.get_started.addEventListener('touchstart', function(e) {
e.preventDefault();
kat.intro.style.display = 'none';
kat.newTurn();
kat.game.style.display = 'block';
kat.game.style.opacity = 1;
return false;
}, false);
kat.choicesNode = document.getElementById('choices');
kat.resolveLayout();
window.addEventListener('resize', kat.resolveLayout);
}, false);
</script>
</body>
</html>