2016-11-22 00:22:00 +00:00
|
|
|
|
// drill.js
|
|
|
|
|
|
2018-03-05 02:40:09 +00:00
|
|
|
|
var transformations = [];
|
2016-11-22 00:22:00 +00:00
|
|
|
|
|
2016-12-27 19:15:38 +00:00
|
|
|
|
var log;
|
|
|
|
|
|
2021-09-29 16:57:47 +00:00
|
|
|
|
const configOptions = {
|
2021-06-12 22:07:48 +00:00
|
|
|
|
|
2021-09-29 16:57:47 +00:00
|
|
|
|
options: ["plain", "polite", "negative", "past", "te-form", "progressive",
|
|
|
|
|
"potential", "imperative", "passive", "causative", "godan", "ichidan",
|
|
|
|
|
"iku", "kuru", "suru", "i-adjective", "na-adjective", "ii", "desire",
|
2021-10-10 20:51:14 +00:00
|
|
|
|
"volitional", "trick", "kana", "furigana_always", "use_voice", "go_to_next_question",
|
|
|
|
|
"auto_show_explanation",
|
|
|
|
|
],
|
2021-09-29 16:57:47 +00:00
|
|
|
|
|
|
|
|
|
selects: ["questionFocus"],
|
|
|
|
|
|
|
|
|
|
inputs: ["numQuestions"]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const defaultConfig = {
|
|
|
|
|
"plain": true,
|
|
|
|
|
"polite": true,
|
|
|
|
|
"negative": true,
|
|
|
|
|
"past": true,
|
|
|
|
|
"te-form": false,
|
|
|
|
|
"progressive": false,
|
|
|
|
|
"potential": false,
|
|
|
|
|
"imperative": false,
|
|
|
|
|
"passive": false,
|
|
|
|
|
"causative": false,
|
|
|
|
|
"godan": true,
|
|
|
|
|
"ichidan": true,
|
|
|
|
|
"iku": true,
|
|
|
|
|
"kuru": true,
|
|
|
|
|
"suru": true,
|
|
|
|
|
"i-adjective": false,
|
|
|
|
|
"na-adjective": false,
|
|
|
|
|
"ii": false,
|
|
|
|
|
"desire": false,
|
|
|
|
|
"volitional": false,
|
|
|
|
|
"trick": true,
|
|
|
|
|
"kana": false,
|
|
|
|
|
"furigana_always": true,
|
2021-10-10 20:51:14 +00:00
|
|
|
|
"go_to_next_question": false,
|
|
|
|
|
"auto_show_explanation": false,
|
2021-09-29 16:57:47 +00:00
|
|
|
|
"use_voice": false,
|
|
|
|
|
"questionFocus": "none",
|
|
|
|
|
"numQuestions": "10"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const localStorageOptionsKey = "conjugationDrillOptions";
|
2021-06-12 22:07:48 +00:00
|
|
|
|
|
2016-12-27 19:15:38 +00:00
|
|
|
|
Array.prototype.randomElement = function () {
|
|
|
|
|
return this[Math.floor(Math.random() * this.length)]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// From: http://stackoverflow.com/a/2897510
|
2017-05-13 21:02:16 +00:00
|
|
|
|
new function ($) {
|
|
|
|
|
$.fn.getCursorPosition = function () {
|
|
|
|
|
var input = this.get(0);
|
|
|
|
|
if (!input) return; // No (input) element found
|
|
|
|
|
if ('selectionStart' in input) {
|
|
|
|
|
// Standard-compliant browsers
|
|
|
|
|
return input.selectionStart;
|
|
|
|
|
} else if (document.selection) {
|
|
|
|
|
// IE
|
|
|
|
|
input.fmcus();
|
|
|
|
|
var sel = document.selection.createRange();
|
|
|
|
|
var selLen = document.selection.createRange().text.length;
|
|
|
|
|
sel.moveStart('character', -input.value.length);
|
|
|
|
|
return sel.text.length - selLen;
|
2016-12-27 19:15:38 +00:00
|
|
|
|
}
|
2017-05-13 21:02:16 +00:00
|
|
|
|
}
|
2016-12-27 19:15:38 +00:00
|
|
|
|
}(jQuery);
|
|
|
|
|
|
|
|
|
|
// From: http://stackoverflow.com/questions/499126/jquery-set-cursor-position-in-text-area
|
|
|
|
|
|
2017-05-13 21:02:16 +00:00
|
|
|
|
new function ($) {
|
|
|
|
|
$.fn.setCursorPosition = function (pos) {
|
2016-12-27 19:15:38 +00:00
|
|
|
|
if (this.setSelectionRange) {
|
|
|
|
|
this.setSelectionRange(pos, pos);
|
|
|
|
|
} else if (this.createTextRange) {
|
|
|
|
|
var range = this.createTextRange();
|
|
|
|
|
range.collapse(true);
|
2017-05-13 21:02:16 +00:00
|
|
|
|
if (pos < 0) {
|
2016-12-27 19:15:38 +00:00
|
|
|
|
pos = $(this).val().length + pos;
|
|
|
|
|
}
|
|
|
|
|
range.moveEnd('character', pos);
|
|
|
|
|
range.moveStart('character', pos);
|
|
|
|
|
range.select();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}(jQuery);
|
|
|
|
|
|
2020-07-23 12:11:28 +00:00
|
|
|
|
// Waaaayy overkill here but these ranges were taken from http://www.unicode.org/charts/ and I kinda got carried away.
|
|
|
|
|
|
|
|
|
|
var japaneseTextPattern = /^[\u{3040}-\u{309f}\u{30a0}-\u{30ff}\u{3190}-\u{319f}\u{31f0}-\u{31ff}\u{3400}-\u{4dbf}\u{4e00}-\u{9ffc}\u{f900}-\u{faff}\u{ff00}-\u{ffef}\u{1b000}-\u{1b0ff}\u{1b100}-\u{1b12f}\u{1b130}-\u{1b16f}\u{20000}-\u{2a6dd}\u{2a700}-\u{2b734}\u{2b740}-\u{2b81d}\u{2b820}-\u{2cea1}\u{2ceb0}-\u{2ebe0}\u{2f800}-\u{2fa1f}\u{30000}-\u{3134a}]*$/u;
|
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
function commaList(items, conjunction) {
|
|
|
|
|
|
|
|
|
|
if (conjunction == undefined) {
|
|
|
|
|
conjunction = "and";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var result = "";
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < items.length; i++) {
|
|
|
|
|
result = result + items[i];
|
|
|
|
|
|
|
|
|
|
if (i < (items.length - 2)) {
|
|
|
|
|
result += ", ";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i == (items.length - 2)) {
|
|
|
|
|
result += " " + conjunction + " ";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-27 19:15:38 +00:00
|
|
|
|
function resetLog() {
|
|
|
|
|
log = { "history": [] };
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-25 16:48:32 +00:00
|
|
|
|
function kanaForm(words) {
|
2017-08-12 18:04:09 +00:00
|
|
|
|
|
2017-12-25 16:48:32 +00:00
|
|
|
|
if (words.constructor !== Array) {
|
|
|
|
|
words = [words];
|
2016-12-27 19:15:38 +00:00
|
|
|
|
}
|
2017-04-29 14:50:22 +00:00
|
|
|
|
|
2020-10-24 19:54:12 +00:00
|
|
|
|
return words.map(function (word) { return word.split(/.\[([^\]]*)\]/).join(""); });
|
2017-12-25 16:48:32 +00:00
|
|
|
|
}
|
2017-08-12 18:04:09 +00:00
|
|
|
|
|
2017-12-25 16:48:32 +00:00
|
|
|
|
function kanjiForm(words) {
|
2017-08-12 18:04:09 +00:00
|
|
|
|
|
2017-12-25 16:48:32 +00:00
|
|
|
|
if (words.constructor !== Array) {
|
|
|
|
|
words = [words];
|
2016-12-27 19:15:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-24 19:54:12 +00:00
|
|
|
|
return words.map(function (word) { return word.split(/(.)\[[^\]]*\]/).join(""); });
|
2017-12-25 16:48:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getVerbForms(entry) {
|
|
|
|
|
|
2016-12-27 19:15:38 +00:00
|
|
|
|
var result = {
|
2017-05-13 21:02:16 +00:00
|
|
|
|
"kanji": {},
|
|
|
|
|
"hiragana": {},
|
|
|
|
|
"furigana": {}
|
2016-12-27 19:15:38 +00:00
|
|
|
|
};
|
|
|
|
|
|
2016-12-27 21:08:59 +00:00
|
|
|
|
Object.keys(words[entry].conjugations).forEach(function (key) {
|
2020-08-07 01:05:59 +00:00
|
|
|
|
result["kanji"][key] = kanjiForm(words[entry].conjugations[key].forms);
|
|
|
|
|
result["hiragana"][key] = kanaForm(words[entry].conjugations[key].forms);
|
|
|
|
|
result["furigana"][key] = words[entry].conjugations[key].forms;
|
2016-12-27 19:15:38 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
function wordWithFurigana(words) {
|
2016-12-14 02:08:54 +00:00
|
|
|
|
|
2020-07-23 12:11:28 +00:00
|
|
|
|
var options = getOptions();
|
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
if (words.constructor !== Array) {
|
|
|
|
|
words = [words];
|
2016-12-14 02:08:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
return words.map(function (word) {
|
|
|
|
|
|
|
|
|
|
var bits = word.split(/(.)\[([^\]]*)\]/);
|
2018-03-05 02:40:09 +00:00
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
while (bits.length > 1) {
|
2020-07-23 12:11:28 +00:00
|
|
|
|
if (options["kana"]) {
|
|
|
|
|
bits[0] = bits[0] + bits[2] + bits[3];
|
|
|
|
|
} else if (options["furigana_always"]) {
|
|
|
|
|
bits[0] = bits[0] + "<ruby>" + bits[1] + "<rp>(</rp><rt>" + bits[2] + "</rt><rp>)</rp></ruby>" + bits[3];
|
|
|
|
|
} else {
|
2020-10-30 17:07:28 +00:00
|
|
|
|
bits[0] = bits[0] + "<ruby class='furiganaHover'>" + bits[1] + "<rp>(</rp><rt>" + bits[2] + "</rt><rp>)</rp></ruby>" + bits[3];
|
2020-07-23 12:11:28 +00:00
|
|
|
|
}
|
2017-08-12 18:04:09 +00:00
|
|
|
|
bits.splice(1, 3);
|
|
|
|
|
}
|
2018-03-05 02:40:09 +00:00
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
return bits[0];
|
|
|
|
|
});
|
2016-12-14 02:08:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-12-15 22:50:32 +00:00
|
|
|
|
function processAnswerKey() {
|
|
|
|
|
|
|
|
|
|
var el = $('#answer');
|
|
|
|
|
|
|
|
|
|
var pos = el.getCursorPosition();
|
|
|
|
|
var val = el.val();
|
|
|
|
|
|
|
|
|
|
var last1 = val.slice(pos - 1, pos);
|
|
|
|
|
var last2 = val.slice(pos - 2, pos);
|
|
|
|
|
var last3 = val.slice(pos - 3, pos);
|
|
|
|
|
|
|
|
|
|
var replace1 = {
|
2017-05-13 21:02:16 +00:00
|
|
|
|
"a": "あ", "i": "い", "u": "う", "e": "え", "o": "お"
|
2016-12-15 22:50:32 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var replace2 = {
|
|
|
|
|
|
2017-05-13 21:02:16 +00:00
|
|
|
|
"ka": "か", "ki": "き", "ku": "く", "ke": "け", "ko": "こ",
|
|
|
|
|
"sa": "さ", "si": "し", "su": "す", "se": "せ", "so": "そ",
|
|
|
|
|
"ta": "た", "ti": "ち", "tu": "つ", "te": "て", "to": "と",
|
|
|
|
|
"na": "な", "ni": "に", "nu": "ぬ", "ne": "ね", "no": "の",
|
|
|
|
|
"ha": "は", "hi": "ひ", "hu": "ふ", "he": "へ", "ho": "ほ",
|
|
|
|
|
"ma": "ま", "mi": "み", "mu": "む", "me": "め", "mo": "も",
|
|
|
|
|
"ra": "ら", "ri": "り", "ru": "る", "re": "れ", "ro": "ろ",
|
|
|
|
|
"ga": "が", "gi": "ぎ", "gu": "ぐ", "ge": "げ", "go": "ご",
|
|
|
|
|
"za": "ざ", "zi": "じ", "zu": "ず", "ze": "ぜ", "zo": "ぞ",
|
|
|
|
|
"da": "だ", "di": "ぢ", "du": "づ", "de": "で", "do": "ど",
|
|
|
|
|
"ba": "ば", "bi": "び", "bu": "ぶ", "be": "べ", "bo": "ぼ",
|
|
|
|
|
"pa": "ぱ", "pi": "ぴ", "pu": "ぷ", "pe": "ぺ", "po": "ぽ",
|
|
|
|
|
|
|
|
|
|
"qa": "くぁ", "qi": "くぃ", "qu": "く", "qe": "くぇ", "qo": "くぉ",
|
|
|
|
|
"wa": "わ", "wi": "うぃ", "wu": "う", "we": "うぇ", "wo": "を",
|
|
|
|
|
"ya": "や", "yi": "い", "yu": "ゆ", "ye": "いぇ", "yo": "よ",
|
|
|
|
|
"fa": "ふぁ", "fi": "ふぃ", "fu": "ふ", "fe": "ふぇ", "fo": "ふぉ",
|
|
|
|
|
"ja": "じゃ", "ji": "じ", "ju": "じゅ", "je": "じぇ", "jo": "じょ",
|
|
|
|
|
"la": "ぁ", "li": "ぃ", "lu": "ぅ", "le": "ぇ", "lo": "ぉ",
|
|
|
|
|
"za": "ざ", "zi": "じ", "zu": "ず", "ze": "ぜ", "zo": "ぞ",
|
|
|
|
|
"xa": "ぁ", "xi": "ぃ", "xu": "ぅ", "xe": "ぇ", "xo": "ぉ",
|
|
|
|
|
"ca": "か", "ci": "し", "cu": "く", "ce": "せ", "co": "こ",
|
|
|
|
|
"va": "ヴぁ", "vi": "ヴぃ", "vu": "ヴ", "ve": "ヴぇ", "vo": "ヴぉ",
|
|
|
|
|
|
|
|
|
|
"lu": "っ",
|
|
|
|
|
|
|
|
|
|
"nn": "ん", "n'": "ん",
|
|
|
|
|
|
|
|
|
|
"nb": "んb", "nc": "んc", "nd": "んd", "nf": "んf", "ng": "んg",
|
|
|
|
|
"nh": "んh", "nj": "んj", "nk": "んk", "nl": "んl", "nm": "んm",
|
|
|
|
|
"np": "んp", "nq": "んq", "nr": "んr", "ns": "んs", "nt": "んt",
|
|
|
|
|
"nv": "んv", "nw": "んw", "nx": "んx", "nz": "んz",
|
|
|
|
|
|
|
|
|
|
"aa": "っa", "bb": "っb", "cc": "っc", "dd": "っd", "ee": "っe",
|
|
|
|
|
"ff": "っf", "gg": "っg", "hh": "っh", "ii": "っi", "jj": "っj",
|
|
|
|
|
"kk": "っk", "ll": "っl", "mm": "っm", "oo": "っo", "pp": "っp",
|
|
|
|
|
"qq": "っq", "rr": "っr", "ss": "っs", "tt": "っt", "uu": "っu",
|
|
|
|
|
"vv": "っv", "ww": "っw", "xx": "っx", "yy": "っy", "zz": "っz",
|
2016-12-15 22:50:32 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var replace3 = {
|
|
|
|
|
|
2017-05-13 21:02:16 +00:00
|
|
|
|
"kya": "きゃ", "kyi": "きぃ", "kyu": "きゅ", "kye": "きぇ", "kyo": "きょ",
|
|
|
|
|
"sha": "しゃ", "shi": "し", "shu": "しゅ", "she": "しぇ", "sho": "しょ",
|
|
|
|
|
"cha": "ちゃ", "chi": "ち", "chu": "ちゅ", "che": "ちぇ", "cho": "ちょ",
|
|
|
|
|
"nya": "にゃ", "nyi": "にぃ", "nyu": "にゅ", "nye": "にぇ", "nyo": "にょ",
|
|
|
|
|
"hya": "ひゃ", "hyi": "ひぃ", "hyu": "ひゅ", "hye": "ひぇ", "hyo": "ひょ",
|
|
|
|
|
"mya": "みゃ", "myi": "みぃ", "myu": "みゅ", "mye": "みぇ", "myo": "みょ",
|
|
|
|
|
"rya": "りゃ", "ryi": "りぃ", "ryu": "りゅ", "rye": "りぇ", "ryo": "りょ",
|
|
|
|
|
"gya": "ぎゃ", "gyi": "ぎぃ", "gyu": "ぎゅ", "gye": "ぎぇ", "gyo": "ぎょ",
|
|
|
|
|
"zya": "じゃ", "zyi": "じぃ", "zyu": "じゅ", "zye": "じぇ", "zyo": "じょ",
|
|
|
|
|
"dya": "ぢゃ", "dyi": "ぢぃ", "dyu": "ぢゅ", "dye": "ぢぇ", "dyo": "ぢょ",
|
|
|
|
|
"bya": "びゃ", "byi": "びぃ", "byu": "びゅ", "bye": "びぇ", "byo": "びょ",
|
|
|
|
|
"pya": "ぴゃ", "pyi": "ぴぃ", "pyu": "ぴゅ", "pye": "ぴぇ", "pyo": "ぴょ",
|
|
|
|
|
|
|
|
|
|
"shi": "し",
|
|
|
|
|
"tsu": "つ",
|
2016-12-15 22:50:32 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (replace3[last3]) {
|
|
|
|
|
val = val.slice(0, pos - 3) + replace3[last3] + val.slice(pos, -1);
|
|
|
|
|
el.val(val);
|
|
|
|
|
el.setCursorPosition(pos - 3 + replace3[last3].length);
|
|
|
|
|
} else if (replace2[last2]) {
|
|
|
|
|
val = val.slice(0, pos - 2) + replace2[last2] + val.slice(pos, -1);
|
|
|
|
|
el.val(val);
|
|
|
|
|
el.setCursorPosition(pos - 2 + replace2[last2].length);
|
|
|
|
|
} else if (replace1[last1]) {
|
|
|
|
|
val = val.slice(0, pos - 1) + replace1[last1] + val.slice(pos, -1);
|
|
|
|
|
el.val(val);
|
|
|
|
|
el.setCursorPosition(pos - 1 + replace1[last1].length);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-16 13:49:44 +00:00
|
|
|
|
function processAnswerKeyDown(evt) {
|
|
|
|
|
|
|
|
|
|
if (evt.keyCode == 32) {
|
|
|
|
|
|
|
|
|
|
var options = getOptions();
|
|
|
|
|
|
|
|
|
|
if (options.use_voice) {
|
|
|
|
|
|
|
|
|
|
window.speechSynthesis.cancel();
|
|
|
|
|
|
|
|
|
|
textToSpeech(window.questionData.givenWordAsKanji, evt.shiftKey);
|
|
|
|
|
evt.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
function validQuestion(entry, forms, transformation, options) {
|
|
|
|
|
|
|
|
|
|
var valid = true;
|
|
|
|
|
|
|
|
|
|
transformation.tags.forEach(function (type) {
|
|
|
|
|
if (options[type] == false) {
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (options[words[entry].group] == false) {
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!forms["furigana"][transformation.from])
|
|
|
|
|
valid = false;
|
|
|
|
|
|
|
|
|
|
if (!forms["furigana"][transformation.to])
|
|
|
|
|
valid = false;
|
|
|
|
|
|
2020-08-07 01:05:59 +00:00
|
|
|
|
if (valid) {
|
|
|
|
|
|
|
|
|
|
if (options.questionFocus != "none") {
|
|
|
|
|
|
|
|
|
|
if (options.questionFocus == 'tetakei') {
|
|
|
|
|
// console.log("tetakei", words[entry].conjugations[transformation.from].tetakei, words[entry].conjugations[transformation.to].tetakei)
|
|
|
|
|
if (words[entry].conjugations[transformation.from].tetakei == words[entry].conjugations[transformation.to].tetakei) {
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
} else if (transformation.type != options.questionFocus) {
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
2020-07-22 18:04:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
return valid;
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-27 19:15:38 +00:00
|
|
|
|
function generateQuestion() {
|
2017-04-29 14:50:22 +00:00
|
|
|
|
|
2020-08-07 01:05:59 +00:00
|
|
|
|
var questionText = {
|
2020-10-24 19:54:12 +00:00
|
|
|
|
"affirmative": "<span class='first'>make</span> the following <span class='emphasis'>affirmative</span>",
|
|
|
|
|
"negative": "<span class='first'>make</span> the following <span class='emphasis'>negative</span>",
|
|
|
|
|
"present": "<span class='first'>convert</span> the following to the <span class='emphasis'>present tense</span>",
|
|
|
|
|
"past": "<span class='first'>convert</span> the following to the <span class='emphasis'>past tense</span>",
|
|
|
|
|
"plain": "<span class='first'>make</span> the following <span class='emphasis'>informal</span>",
|
|
|
|
|
"polite": "<span class='first'>make</span> the following <span class='emphasis'>polite</span>",
|
|
|
|
|
"て": "<span class='emphasis first'>add</span> the <span class='emphasis'>て pattern</span> to the following",
|
|
|
|
|
"non-て": "<span class='emphasis first'>remove</span> the <span class='emphasis'>て pattern</span> from the following",
|
|
|
|
|
"potential": "<span class='first'>make</span> the following <span class='emphasis'>potential</span>",
|
|
|
|
|
"non-potential": "<span class='first'>make</span> the following <span class='emphasis'>non-potential</span>",
|
|
|
|
|
"imperative": "<span class='first'>make</span> the following <span class='emphasis'>imperative</span>",
|
|
|
|
|
"non-imperative": "<span class='first'>make</span> the following <span class='emphasis'>non-imperative</span>",
|
|
|
|
|
"causative": "<span class='first'>make</span> the following <span class='emphasis'>causative</span>",
|
|
|
|
|
"non-causative": "<span class='first'>make</span> the following <span class='emphasis'>non-causative</span>",
|
|
|
|
|
"passive": "<span class='first'>make</span> the following <span class='emphasis'>passive</span>",
|
|
|
|
|
"active": "<span class='first'>make</span> the following <span class='emphasis'>active</span>",
|
|
|
|
|
"progressive": "<span class='first'>make</span> the following <span class='emphasis'>progressive</span>",
|
|
|
|
|
"non-progressive": "<span class='first'>make</span> the following <span class='emphasis'>non-progressive</span>",
|
|
|
|
|
"'desire'": "<span class='first'>convert</span> the following to the <span class='emphasis'>'desire'</span> form",
|
|
|
|
|
"'non-desire'": "<span class='first'>convert</span> the following to the <span class='emphasis'>'non-desire'</span> form",
|
|
|
|
|
"volitional": "<span class='first'>make</span> the following <span class='emphasis'>volitional</span>",
|
|
|
|
|
"non-volitional": "<span class='first'>make</span> the following <span class='emphasis'>non-volitional</span>"
|
2020-08-07 01:05:59 +00:00
|
|
|
|
};
|
|
|
|
|
|
2016-12-24 17:11:34 +00:00
|
|
|
|
var entry;
|
|
|
|
|
var to_form;
|
|
|
|
|
var from_form;
|
|
|
|
|
var forms;
|
2018-02-26 19:55:08 +00:00
|
|
|
|
var options = getOptions();
|
2016-11-22 00:22:00 +00:00
|
|
|
|
|
2016-12-25 15:30:53 +00:00
|
|
|
|
var count = 0;
|
|
|
|
|
|
2016-12-24 17:11:34 +00:00
|
|
|
|
while (true) {
|
|
|
|
|
|
2016-12-25 21:35:29 +00:00
|
|
|
|
if (count++ == 10000) {
|
2016-12-25 15:30:53 +00:00
|
|
|
|
showSplash();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-04-29 14:50:22 +00:00
|
|
|
|
|
2016-12-27 21:08:59 +00:00
|
|
|
|
entry = Object.keys(words).randomElement();
|
2017-03-18 19:25:26 +00:00
|
|
|
|
transformation = transformations.randomElement();
|
|
|
|
|
|
|
|
|
|
from_form = transformation.from;
|
|
|
|
|
to_form = transformation.to;
|
2016-12-24 17:11:34 +00:00
|
|
|
|
|
|
|
|
|
forms = getVerbForms(entry);
|
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
var valid = validQuestion(entry, forms, transformation, getOptions());
|
2016-12-25 15:30:53 +00:00
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
// Modify the chance of trick questions so that they appear on average 25%
|
|
|
|
|
// of the time. When trick questions are active then 50% of the
|
|
|
|
|
// transformation structure are trick questions and so a 33% filter here
|
|
|
|
|
// will achieve the 25% because this test is only performed when a trick
|
|
|
|
|
// question has been selected.
|
|
|
|
|
|
|
|
|
|
if (transformation.tags.indexOf('trick') != -1) {
|
|
|
|
|
if (Math.random() > 0.333) {
|
2017-04-22 02:21:34 +00:00
|
|
|
|
valid = false;
|
2016-12-25 15:30:53 +00:00
|
|
|
|
}
|
2016-12-25 15:51:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-12-25 15:30:53 +00:00
|
|
|
|
if (valid) {
|
2016-12-24 17:11:34 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-10 01:10:03 +00:00
|
|
|
|
|
2016-12-14 02:08:54 +00:00
|
|
|
|
var kanjiForms = forms["kanji"];
|
|
|
|
|
var kanaForms = forms["hiragana"];
|
|
|
|
|
var furiganaForms = forms["furigana"];
|
2016-11-22 00:22:00 +00:00
|
|
|
|
|
2021-02-16 13:49:44 +00:00
|
|
|
|
var candidates;
|
2018-02-26 19:55:08 +00:00
|
|
|
|
|
|
|
|
|
if (options["kana"]) {
|
2021-02-16 13:49:44 +00:00
|
|
|
|
candidates = kanaForms[from_form];
|
2018-02-26 19:55:08 +00:00
|
|
|
|
} else {
|
2021-02-16 13:49:44 +00:00
|
|
|
|
candidates = wordWithFurigana(furiganaForms[from_form]);
|
2018-02-26 19:55:08 +00:00
|
|
|
|
}
|
2017-12-25 16:48:32 +00:00
|
|
|
|
|
2021-02-16 13:49:44 +00:00
|
|
|
|
var candidateIndex = Math.floor(Math.random() * candidates.length);
|
|
|
|
|
|
|
|
|
|
var givenWord = candidates[candidateIndex];
|
|
|
|
|
var givenWordAsKanji = kanjiForms[from_form][candidateIndex];
|
|
|
|
|
|
2020-10-24 19:54:12 +00:00
|
|
|
|
var thisQuestionText = questionText[transformation.phrase];
|
2020-07-23 12:11:28 +00:00
|
|
|
|
|
2020-10-24 19:54:12 +00:00
|
|
|
|
// thisQuestionText = thisQuestionText[0].toUpperCase() + thisQuestionText.substring(1);
|
|
|
|
|
|
|
|
|
|
var questionFirstHalf = thisQuestionText;
|
|
|
|
|
var questionSecondHalf = givenWord;
|
|
|
|
|
var question = questionFirstHalf.replace("the following", questionSecondHalf);
|
2017-08-12 18:04:09 +00:00
|
|
|
|
|
2016-11-22 00:22:00 +00:00
|
|
|
|
var answer = kanjiForms[to_form];
|
|
|
|
|
var answer2 = kanaForms[to_form];
|
2018-02-26 19:55:08 +00:00
|
|
|
|
var answerWithFurigana = wordWithFurigana(furiganaForms[to_form]);
|
|
|
|
|
|
|
|
|
|
if (options["kana"]) {
|
|
|
|
|
answer = answer2;
|
|
|
|
|
answerWithFurigana = kanaForms[to_form];
|
|
|
|
|
}
|
2016-11-22 00:22:00 +00:00
|
|
|
|
|
2020-07-23 12:11:28 +00:00
|
|
|
|
$('#questionFirstHalf').html(questionFirstHalf);
|
2021-02-16 13:49:44 +00:00
|
|
|
|
|
|
|
|
|
if (options.use_voice) {
|
|
|
|
|
$('#questionSecondHalf').html("<div id='speechSpace'><i>Press Space for word</i><br><div class='halfSpeed'>Use Shift key for half speed</div></div>");
|
|
|
|
|
} else {
|
|
|
|
|
$('#questionSecondHalf').html(questionSecondHalf);
|
|
|
|
|
}
|
2016-11-26 16:45:32 +00:00
|
|
|
|
|
2017-12-25 16:48:32 +00:00
|
|
|
|
window.questionData = {
|
2020-10-24 19:54:12 +00:00
|
|
|
|
entry: entry,
|
|
|
|
|
transformation: transformation,
|
|
|
|
|
question: question,
|
|
|
|
|
answer: answer,
|
|
|
|
|
answer2: answer2,
|
2018-02-26 19:55:08 +00:00
|
|
|
|
answerWithFurigana: answerWithFurigana,
|
2020-10-24 19:54:12 +00:00
|
|
|
|
givenWord: givenWord,
|
2021-02-16 13:49:44 +00:00
|
|
|
|
givenWordAsKanji: givenWordAsKanji,
|
2017-12-25 16:48:32 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Construct the explanation page.
|
|
|
|
|
|
|
|
|
|
var data = window.questionData;
|
|
|
|
|
|
|
|
|
|
var groupLabels = {
|
2020-10-24 19:54:12 +00:00
|
|
|
|
"godan": "godan verb",
|
|
|
|
|
"ichidan": "ichidan verb",
|
|
|
|
|
"iku": "godan verb",
|
|
|
|
|
"suru": "suru verb",
|
|
|
|
|
"kuru": "special verb",
|
|
|
|
|
"i-adjective": "い-adjective",
|
|
|
|
|
"ii": "い-adjective",
|
|
|
|
|
"na-adjective": "な-adjective",
|
2017-12-25 16:48:32 +00:00
|
|
|
|
};
|
|
|
|
|
|
2020-08-07 01:05:59 +00:00
|
|
|
|
var dictionary = words[data.entry].conjugations["dictionary"].forms;
|
2017-12-25 16:48:32 +00:00
|
|
|
|
|
|
|
|
|
if (words[data.entry].group == "na-adjective") {
|
2021-09-28 20:29:34 +00:00
|
|
|
|
for (var i = 0; i < dictionary.length; i++) {
|
|
|
|
|
dictionary[i] = dictionary[i].replace(/だ$/, '')
|
|
|
|
|
}
|
2017-12-25 16:48:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-02-26 19:55:08 +00:00
|
|
|
|
if (!options["kana"]) {
|
|
|
|
|
dictionary = wordWithFurigana(dictionary);
|
|
|
|
|
} else {
|
|
|
|
|
dictionary = kanaForm(dictionary);
|
|
|
|
|
}
|
2017-12-25 16:48:32 +00:00
|
|
|
|
|
|
|
|
|
$('#explain-given').html(givenWord);
|
|
|
|
|
$('#explain-given-tags').html(data.transformation.from_tags.map(function (tag) { return "<span class='tag'>" + tag + "</span>"; }).join(" "));
|
|
|
|
|
$('.explain-given-dictionary').html(dictionary);
|
|
|
|
|
$('#explain-group').html(groupLabels[words[data.entry].group]);
|
|
|
|
|
$('.explain-transform').html(data.transformation.phrase);
|
|
|
|
|
$('.explain-answer-tags').html(data.transformation.to_tags.map(function (tag) { return "<span class='tag'>" + tag + "</span>"; }).join(" "));
|
|
|
|
|
$('.explain-answer-tags2').html(data.transformation.to_tags.join(" "));
|
|
|
|
|
$('.explain-answer').html(commaList(questionData.answerWithFurigana, "or"));
|
|
|
|
|
|
|
|
|
|
$('.explain-answer-as-list').empty();
|
|
|
|
|
|
|
|
|
|
questionData.answerWithFurigana.forEach(function (answer) {
|
|
|
|
|
$('.explain-answer-as-list').append("<li>" + answer);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (window.questionData.transformation.tags.indexOf("trick") != -1) {
|
|
|
|
|
$('.explain-trick').show();
|
|
|
|
|
$('.explain-no-trick').hide();
|
|
|
|
|
} else {
|
|
|
|
|
$('.explain-trick').hide();
|
|
|
|
|
$('.explain-no-trick').show();
|
|
|
|
|
}
|
2018-03-05 02:40:09 +00:00
|
|
|
|
|
2017-12-25 16:48:32 +00:00
|
|
|
|
if (data.transformation.to == "dictionary") {
|
|
|
|
|
$('.explain-hide-end').hide();
|
|
|
|
|
} else {
|
|
|
|
|
$('.explain-hide-end').show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data.answer.length == 1) {
|
|
|
|
|
$('.explain-answer-single').show();
|
|
|
|
|
$('.explain-answer-multiple').hide();
|
|
|
|
|
} else {
|
|
|
|
|
$('.explain-answer-single').hide();
|
|
|
|
|
$('.explain-answer-multiple').show();
|
|
|
|
|
}
|
2016-11-22 00:22:00 +00:00
|
|
|
|
|
|
|
|
|
$('#response').html("");
|
2021-01-02 20:06:57 +00:00
|
|
|
|
$('#message').hide();
|
2016-11-22 01:59:30 +00:00
|
|
|
|
|
|
|
|
|
$('#proceed').hide();
|
2017-12-25 16:48:32 +00:00
|
|
|
|
$('#explanation').hide();
|
2020-07-22 18:00:42 +00:00
|
|
|
|
$('#inputArea').show();
|
2016-11-22 01:59:30 +00:00
|
|
|
|
$('#answer').focus();
|
2016-11-26 16:45:32 +00:00
|
|
|
|
|
2016-12-15 22:50:32 +00:00
|
|
|
|
$('#answer').on('input', processAnswerKey);
|
2021-02-16 13:49:44 +00:00
|
|
|
|
$('#answer').on('keydown', processAnswerKeyDown);
|
2016-11-22 00:22:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function processAnswer() {
|
|
|
|
|
|
2021-10-10 20:51:14 +00:00
|
|
|
|
var options = getOptions();
|
2017-12-25 16:48:32 +00:00
|
|
|
|
var questionData = window.questionData;
|
2016-12-10 01:10:03 +00:00
|
|
|
|
var response = $('#answer').val().trim();
|
|
|
|
|
|
2020-08-07 01:05:59 +00:00
|
|
|
|
var shake = false;
|
2020-07-23 12:11:28 +00:00
|
|
|
|
|
2016-12-10 01:10:03 +00:00
|
|
|
|
if (response == "")
|
2020-08-07 01:05:59 +00:00
|
|
|
|
shake = true;
|
2020-10-24 19:54:12 +00:00
|
|
|
|
|
2020-07-23 12:11:28 +00:00
|
|
|
|
if (!response.match(japaneseTextPattern))
|
2020-08-07 01:05:59 +00:00
|
|
|
|
shake = true;
|
2020-07-23 12:11:28 +00:00
|
|
|
|
|
2020-08-07 01:05:59 +00:00
|
|
|
|
if (shake) {
|
2020-07-23 12:11:28 +00:00
|
|
|
|
shakeInputArea();
|
2016-12-10 01:10:03 +00:00
|
|
|
|
return;
|
2020-07-23 12:11:28 +00:00
|
|
|
|
}
|
2016-12-10 01:10:03 +00:00
|
|
|
|
|
2017-12-25 16:48:32 +00:00
|
|
|
|
var correct = ((questionData.answer.indexOf(response) != -1) || (questionData.answer2.indexOf(response) != -1));
|
2017-08-12 18:04:09 +00:00
|
|
|
|
|
2016-11-22 01:59:30 +00:00
|
|
|
|
var klass = correct ? "correct" : "incorrect";
|
2016-11-22 00:22:00 +00:00
|
|
|
|
|
2016-11-26 16:45:32 +00:00
|
|
|
|
log.history.push({
|
2017-12-25 16:48:32 +00:00
|
|
|
|
"question": questionData.question,
|
2017-05-13 21:02:16 +00:00
|
|
|
|
"response": response,
|
2017-12-25 16:48:32 +00:00
|
|
|
|
"answer": questionData.answerWithFurigana,
|
|
|
|
|
"kana": questionData.answer2,
|
2017-05-13 21:02:16 +00:00
|
|
|
|
"correct": correct
|
2016-11-26 16:45:32 +00:00
|
|
|
|
});
|
|
|
|
|
|
2021-09-30 18:26:37 +00:00
|
|
|
|
var totalQuestions = $('#numQuestions').val();
|
|
|
|
|
var answeredQuestions = log.history.length;
|
|
|
|
|
|
|
|
|
|
updateProgressBar(answeredQuestions / totalQuestions * 100);
|
|
|
|
|
|
2016-11-22 00:22:00 +00:00
|
|
|
|
$('#answer').val("");
|
2020-07-23 12:11:28 +00:00
|
|
|
|
$('#responseButton').prop('class', klass).text(response);
|
2017-04-29 14:50:22 +00:00
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
if (correct) {
|
2021-01-02 20:06:57 +00:00
|
|
|
|
$('#message').hide();
|
2016-11-22 00:22:00 +00:00
|
|
|
|
} else {
|
2020-07-23 12:11:28 +00:00
|
|
|
|
$('#message').show();
|
2021-01-02 20:06:57 +00:00
|
|
|
|
$('#message #correction').html("The correct answer was " + commaList(questionData.answerWithFurigana, "or"));
|
2016-11-22 00:22:00 +00:00
|
|
|
|
}
|
2016-11-22 01:59:30 +00:00
|
|
|
|
|
2020-07-22 18:00:42 +00:00
|
|
|
|
$('#inputArea').hide();
|
2016-11-22 01:59:30 +00:00
|
|
|
|
$('#proceed').show();
|
2017-12-25 16:48:32 +00:00
|
|
|
|
$('#explanation').hide();
|
2016-12-14 02:08:54 +00:00
|
|
|
|
$('#proceed button').focus();
|
2016-11-26 16:45:32 +00:00
|
|
|
|
|
|
|
|
|
updateHistoryView(log);
|
2021-10-10 20:36:09 +00:00
|
|
|
|
window.scrollTo(0, 0);
|
2021-10-10 20:55:57 +00:00
|
|
|
|
|
2021-10-10 20:51:14 +00:00
|
|
|
|
if (correct) {
|
|
|
|
|
if (options.go_to_next_question) {
|
|
|
|
|
proceed();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (options.auto_show_explanation) {
|
|
|
|
|
$('#explanation').show();
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-26 16:45:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-23 12:11:28 +00:00
|
|
|
|
function shakeInputArea() {
|
|
|
|
|
|
|
|
|
|
var inputArea = $('#inputArea');
|
2020-08-07 01:05:59 +00:00
|
|
|
|
var shakeClass = "shake";
|
2020-07-23 12:11:28 +00:00
|
|
|
|
|
|
|
|
|
inputArea.addClass(shakeClass);
|
2020-10-24 19:54:12 +00:00
|
|
|
|
|
2020-07-23 12:11:28 +00:00
|
|
|
|
setTimeout(function () {
|
|
|
|
|
inputArea.removeClass(shakeClass)
|
|
|
|
|
}, 1000);
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-26 16:45:32 +00:00
|
|
|
|
function updateHistoryView(log) {
|
2016-12-10 01:10:03 +00:00
|
|
|
|
|
2020-07-22 18:00:42 +00:00
|
|
|
|
var review = $('<div>');
|
2016-11-26 16:45:32 +00:00
|
|
|
|
|
|
|
|
|
var total = 0;
|
|
|
|
|
var correct = 0;
|
|
|
|
|
|
2020-10-24 19:54:12 +00:00
|
|
|
|
log.history.forEach(function (entry, index) {
|
2016-11-26 16:45:32 +00:00
|
|
|
|
|
|
|
|
|
total++;
|
|
|
|
|
|
|
|
|
|
if (entry.correct) {
|
|
|
|
|
correct++;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-24 19:54:12 +00:00
|
|
|
|
var tr = $('<div class="row mt-4">');
|
2016-11-26 16:45:32 +00:00
|
|
|
|
|
2020-10-24 19:54:12 +00:00
|
|
|
|
var td1 = $('<div class="col-md-6 mb-2">');
|
2021-09-29 16:59:28 +00:00
|
|
|
|
var td2 = $('<div class="col-md-6">');
|
2016-11-26 16:45:32 +00:00
|
|
|
|
|
2020-10-24 19:54:12 +00:00
|
|
|
|
td1.html((index + 1) + ". " + entry.question + ".");
|
2016-11-26 16:45:32 +00:00
|
|
|
|
|
2020-10-24 19:54:12 +00:00
|
|
|
|
var responseDiv = $('<div>');
|
|
|
|
|
responseDiv.text(entry.response);
|
2016-11-26 16:45:32 +00:00
|
|
|
|
|
2016-12-10 01:10:03 +00:00
|
|
|
|
if (entry.correct) {
|
2020-10-24 19:54:12 +00:00
|
|
|
|
responseDiv.append("<span class='answer-correct'> 〇</span>");
|
2016-12-10 01:10:03 +00:00
|
|
|
|
} else {
|
2020-10-24 19:54:12 +00:00
|
|
|
|
responseDiv.append("<span class='answer-wrong'> ×</span>");
|
2016-12-10 01:10:03 +00:00
|
|
|
|
}
|
2020-10-24 19:54:12 +00:00
|
|
|
|
|
|
|
|
|
td2.append(responseDiv);
|
|
|
|
|
|
|
|
|
|
if (!entry.correct) {
|
|
|
|
|
|
|
|
|
|
var correctDiv = $('<div>');
|
|
|
|
|
|
|
|
|
|
correctDiv.html(commaList(entry.answer, "or"));
|
|
|
|
|
correctDiv.append("<span class='answer-correct'> 〇</span>");
|
|
|
|
|
|
|
|
|
|
td2.append(correctDiv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tr.append(td1);
|
|
|
|
|
tr.append(td2);
|
|
|
|
|
|
2016-11-26 16:45:32 +00:00
|
|
|
|
review.append(tr);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$('#history').empty().append(review);
|
|
|
|
|
|
2018-10-30 20:37:45 +00:00
|
|
|
|
var resultString;
|
|
|
|
|
|
|
|
|
|
if (correct == total) {
|
2020-10-24 19:54:12 +00:00
|
|
|
|
resultString = "All correct!";
|
|
|
|
|
} else if (correct == 0) {
|
|
|
|
|
resultString = "All incorrect!";
|
2018-10-30 20:37:45 +00:00
|
|
|
|
} else {
|
2020-10-31 17:30:19 +00:00
|
|
|
|
resultString = correct + " of " + total + " correct";
|
2018-10-30 20:37:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-24 19:54:12 +00:00
|
|
|
|
$('#scoreSectionTitleNarrow').text(resultString);
|
|
|
|
|
$('#scoreSectionTitleWide').text(resultString);
|
2016-11-22 01:59:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-09-30 18:26:37 +00:00
|
|
|
|
function updateProgressBar(progress) {
|
|
|
|
|
$('.progressBar').attr('style', 'width: ' + progress + '%');
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-22 01:59:30 +00:00
|
|
|
|
function proceed() {
|
2016-12-23 18:05:37 +00:00
|
|
|
|
if (log.history.length == $('#numQuestions').val()) {
|
|
|
|
|
endQuiz();
|
2016-11-26 16:45:32 +00:00
|
|
|
|
} else {
|
2016-12-27 19:15:38 +00:00
|
|
|
|
generateQuestion();
|
2016-11-26 16:45:32 +00:00
|
|
|
|
}
|
2016-11-22 00:22:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-12-23 18:05:37 +00:00
|
|
|
|
function showSplash() {
|
|
|
|
|
$('#splash').show();
|
|
|
|
|
$('#quizSection').hide();
|
|
|
|
|
$('#scoreSection').hide();
|
|
|
|
|
|
|
|
|
|
$('#go').focus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function startQuiz() {
|
2021-02-16 13:49:44 +00:00
|
|
|
|
|
|
|
|
|
var options = getOptions();
|
|
|
|
|
|
|
|
|
|
const voiceSelectError = document.querySelector('#voiceSelectError');
|
|
|
|
|
|
|
|
|
|
if (options.use_voice && !getVoiceConfig()) {
|
|
|
|
|
voiceSelectError.style.display = "block";
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
voiceSelectError.style.display = "none";
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-30 18:26:37 +00:00
|
|
|
|
updateProgressBar(0);
|
|
|
|
|
|
2016-12-23 18:05:37 +00:00
|
|
|
|
$('#splash').hide();
|
|
|
|
|
$('#quizSection').show();
|
|
|
|
|
$('#scoreSection').hide();
|
|
|
|
|
|
2018-10-30 20:37:45 +00:00
|
|
|
|
if (options.furigana_always) {
|
|
|
|
|
$('body').addClass("furiganaAlways");
|
|
|
|
|
} else {
|
|
|
|
|
$('body').removeClass("furiganaAlways");
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-23 18:05:37 +00:00
|
|
|
|
resetLog();
|
2016-12-27 19:15:38 +00:00
|
|
|
|
generateQuestion();
|
2016-12-23 18:05:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function endQuiz() {
|
|
|
|
|
$('#splash').hide();
|
|
|
|
|
$('#quizSection').hide();
|
|
|
|
|
$('#scoreSection').show();
|
|
|
|
|
|
|
|
|
|
$('#backToStart').focus();
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-16 13:49:44 +00:00
|
|
|
|
// Text to Speech
|
|
|
|
|
|
|
|
|
|
function loadVoiceList(callback) {
|
|
|
|
|
if (window.speechSynthesis.getVoices().length == 0) {
|
|
|
|
|
window.speechSynthesis.addEventListener('voiceschanged', function () {
|
|
|
|
|
if (callback) {
|
|
|
|
|
callback();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
if (callback) {
|
|
|
|
|
callback();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function populateVoiceList() {
|
|
|
|
|
|
|
|
|
|
loadVoiceList(function () {
|
|
|
|
|
|
|
|
|
|
var voiceSelect = document.querySelector("#voice_select");
|
|
|
|
|
|
|
|
|
|
voiceSelect.innerHTML = "<option>Select voice...</option>" +
|
|
|
|
|
window.speechSynthesis.getVoices().map(function (voice) { return "<option>" + voice.name + "</option>" }).join("");
|
|
|
|
|
|
|
|
|
|
var currentVoice = getCurrentVoice();
|
|
|
|
|
|
|
|
|
|
if (currentVoice) {
|
|
|
|
|
voiceSelect.value = currentVoice;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getVoiceConfig() {
|
|
|
|
|
return JSON.parse(localStorage.getItem("voiceConfig"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setVoiceConfig(config) {
|
|
|
|
|
localStorage.setItem("voiceConfig", JSON.stringify(config));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getCurrentVoice() {
|
|
|
|
|
const voiceConfig = getVoiceConfig();
|
|
|
|
|
|
|
|
|
|
if (voiceConfig) {
|
|
|
|
|
return voiceConfig.voice;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function textToSpeech(text, slowMode, callback) {
|
|
|
|
|
|
|
|
|
|
loadVoiceList(function () {
|
|
|
|
|
|
|
|
|
|
const availableVoices = window.speechSynthesis.getVoices();
|
|
|
|
|
|
|
|
|
|
const voiceConfig = getVoiceConfig();
|
|
|
|
|
const currentVoice = voiceConfig.voice;
|
|
|
|
|
|
|
|
|
|
var voice = '';
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < availableVoices.length; i++) {
|
|
|
|
|
if (availableVoices[i].name == currentVoice) {
|
|
|
|
|
voice = availableVoices[i];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (voice === '') {
|
|
|
|
|
voice = availableVoices[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// new SpeechSynthesisUtterance object
|
|
|
|
|
|
|
|
|
|
var utter = new SpeechSynthesisUtterance();
|
|
|
|
|
utter.rate = slowMode ? voiceConfig.rate * 0.5 : voiceConfig.rate;;
|
|
|
|
|
utter.pitch = voiceConfig.pitch;
|
|
|
|
|
utter.text = text;
|
|
|
|
|
utter.voice = voice;
|
|
|
|
|
|
|
|
|
|
// event after text has been spoken
|
|
|
|
|
|
|
|
|
|
utter.onend = function () {
|
|
|
|
|
if (callback) {
|
|
|
|
|
callback(undefined);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// speak
|
|
|
|
|
window.speechSynthesis.speak(utter);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-29 14:50:22 +00:00
|
|
|
|
function arrayDifference(a, b) {
|
|
|
|
|
// From http://stackoverflow.com/a/1723220
|
2017-05-13 21:02:16 +00:00
|
|
|
|
return a.filter(function (x) { return b.indexOf(x) < 0 });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function arrayUnique(arr) {
|
|
|
|
|
return arr.filter(function (value, index, self) {
|
|
|
|
|
return self.indexOf(value) === index;
|
|
|
|
|
});
|
2017-04-29 14:50:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function calculateTransitions() {
|
|
|
|
|
|
|
|
|
|
function getTags(str) {
|
|
|
|
|
|
|
|
|
|
var tags = str.split(" ");
|
|
|
|
|
|
|
|
|
|
if ((tags.length == 1) && (tags[0] == "plain")) {
|
|
|
|
|
tags = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tags;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-13 21:02:16 +00:00
|
|
|
|
function calculateTags(tags) {
|
|
|
|
|
|
|
|
|
|
tags = tags.split(" ");
|
|
|
|
|
|
|
|
|
|
if (tags.indexOf("polite") == -1) {
|
2017-12-25 16:48:32 +00:00
|
|
|
|
tags.splice(0, 0, "plain");
|
2017-05-13 21:02:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tags.indexOf("dictionary") != -1) {
|
|
|
|
|
tags.splice(tags.indexOf("dictionary"), 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tags;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-05 02:40:09 +00:00
|
|
|
|
var allTags = {};
|
|
|
|
|
|
2020-10-24 19:54:12 +00:00
|
|
|
|
Object.keys(words).forEach(function (word) {
|
2018-03-05 02:40:09 +00:00
|
|
|
|
|
|
|
|
|
Object.keys(words[word].conjugations).forEach(function (conjugation) {
|
|
|
|
|
|
|
|
|
|
if (conjugation == "dictionary") {
|
|
|
|
|
conjugation = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
allTags[conjugation] = conjugation.split(" ");
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Object.keys(allTags).forEach(function (srcTag) {
|
|
|
|
|
|
|
|
|
|
if (srcTag != "") {
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < allTags[srcTag].length; i++) {
|
|
|
|
|
|
|
|
|
|
var tagWithDrop = allTags[srcTag].slice();
|
|
|
|
|
|
|
|
|
|
tagWithDrop.splice(i, 1);
|
|
|
|
|
|
|
|
|
|
var dstTag = tagWithDrop.join(" ");
|
|
|
|
|
|
|
|
|
|
if (allTags[dstTag]) {
|
|
|
|
|
|
|
|
|
|
if (srcTag == "") {
|
|
|
|
|
srcTag = "dictionary";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dstTag == "") {
|
|
|
|
|
dstTag = "dictionary";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
transformations.push({ from: srcTag, to: dstTag });
|
|
|
|
|
transformations.push({ from: dstTag, to: srcTag });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2017-04-29 14:50:22 +00:00
|
|
|
|
transformations.forEach(function (transformation) {
|
|
|
|
|
|
|
|
|
|
var from = getTags(transformation.from);
|
|
|
|
|
var to = getTags(transformation.to);
|
|
|
|
|
|
|
|
|
|
var from_extra = {
|
|
|
|
|
"negative": "affirmative",
|
|
|
|
|
"past": "present",
|
|
|
|
|
"polite": "plain",
|
|
|
|
|
"te-form": "non-て",
|
|
|
|
|
"potential": "non-potential",
|
|
|
|
|
"imperative": "non-imperative",
|
|
|
|
|
"causative": "non-causative",
|
|
|
|
|
"passive": "active",
|
|
|
|
|
"progressive": "non-progressive",
|
2018-03-05 02:11:49 +00:00
|
|
|
|
"desire": "'non-desire'",
|
2018-03-05 20:44:12 +00:00
|
|
|
|
"volitional": "non-volitional",
|
2017-04-29 14:50:22 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var to_extra = {
|
|
|
|
|
"negative": "negative",
|
|
|
|
|
"past": "past",
|
|
|
|
|
"polite": "polite",
|
|
|
|
|
"te-form": "て",
|
|
|
|
|
"potential": "potential",
|
|
|
|
|
"imperative": "imperative",
|
|
|
|
|
"causative": "causative",
|
|
|
|
|
"passive": "passive",
|
|
|
|
|
"progressive": "progressive",
|
2018-03-05 02:11:49 +00:00
|
|
|
|
"desire": "'desire'",
|
2018-03-05 20:44:12 +00:00
|
|
|
|
"volitional": "volitional",
|
2017-04-29 14:50:22 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var phrase;
|
|
|
|
|
|
|
|
|
|
phrase = phrase || from_extra[arrayDifference(from, to)[0]];
|
|
|
|
|
phrase = phrase || to_extra[arrayDifference(to, from)[0]];
|
|
|
|
|
|
|
|
|
|
transformation.phrase = phrase;
|
2017-05-13 21:02:16 +00:00
|
|
|
|
|
2017-12-25 16:48:32 +00:00
|
|
|
|
transformation.from_tags = calculateTags(transformation.from);
|
|
|
|
|
transformation.to_tags = calculateTags(transformation.to);
|
2017-05-13 21:02:16 +00:00
|
|
|
|
transformation.tags = arrayUnique(calculateTags(transformation.from).concat(calculateTags(transformation.to)));
|
2020-07-22 18:04:21 +00:00
|
|
|
|
|
|
|
|
|
var diffFromTo = arrayDifference(transformation.from_tags, transformation.to_tags);
|
|
|
|
|
|
|
|
|
|
if (diffFromTo.length > 0) {
|
|
|
|
|
type = diffFromTo[0];
|
|
|
|
|
} else {
|
|
|
|
|
type = arrayDifference(transformation.to_tags, transformation.from_tags)[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((type == "plain") || (type == "polite")) {
|
|
|
|
|
type = "politeness";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
transformation.type = type;
|
2017-04-29 14:50:22 +00:00
|
|
|
|
});
|
2016-12-23 18:05:37 +00:00
|
|
|
|
|
2017-03-18 23:54:45 +00:00
|
|
|
|
// Add trick forms
|
|
|
|
|
|
|
|
|
|
var trick_forms = [];
|
|
|
|
|
|
|
|
|
|
transformations.forEach(function (transformation) {
|
2017-05-13 21:02:16 +00:00
|
|
|
|
trick_forms.push({
|
2017-03-18 23:54:45 +00:00
|
|
|
|
from: transformation.to,
|
|
|
|
|
to: transformation.to,
|
2020-07-22 18:04:21 +00:00
|
|
|
|
type: transformation.type,
|
2017-03-18 23:54:45 +00:00
|
|
|
|
phrase: transformation.phrase,
|
2017-12-25 16:48:32 +00:00
|
|
|
|
from_tags: transformation.to_tags,
|
|
|
|
|
to_tags: transformation.to_tags,
|
2017-03-18 23:54:45 +00:00
|
|
|
|
tags: transformation.tags.concat(["trick"])
|
|
|
|
|
});
|
2017-08-12 18:04:09 +00:00
|
|
|
|
});
|
2017-04-29 00:11:27 +00:00
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
transformations = transformations.concat(trick_forms);
|
|
|
|
|
}
|
2017-04-29 00:11:27 +00:00
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
function updateOptionSummary() {
|
|
|
|
|
|
|
|
|
|
// Calculate how many questions will apply
|
|
|
|
|
|
|
|
|
|
var options = getOptions();
|
|
|
|
|
var applicable = 0;
|
|
|
|
|
|
|
|
|
|
Object.keys(words).forEach(function (word) {
|
|
|
|
|
|
|
|
|
|
var forms = getVerbForms(word);
|
|
|
|
|
|
|
|
|
|
transformations.forEach(function (transformation) {
|
|
|
|
|
|
|
|
|
|
if (validQuestion(word, forms, transformation, options)) {
|
|
|
|
|
applicable++;
|
|
|
|
|
}
|
|
|
|
|
});
|
2017-03-18 23:54:45 +00:00
|
|
|
|
});
|
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
$("#questionCount").text(applicable);
|
2021-02-16 14:38:00 +00:00
|
|
|
|
|
|
|
|
|
if (!options.plain && !options.polite) {
|
|
|
|
|
document.querySelector('#politePlainError').style.display = 'block';
|
|
|
|
|
} else {
|
|
|
|
|
document.querySelector('#politePlainError').style.display = 'none';
|
|
|
|
|
}
|
2017-08-12 18:04:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-09-29 16:57:47 +00:00
|
|
|
|
function saveOptions() {
|
|
|
|
|
localStorage.setItem(localStorageOptionsKey, JSON.stringify(getOptions()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function restoreDefaults() {
|
|
|
|
|
localStorage.setItem(localStorageOptionsKey, JSON.stringify(defaultConfig));
|
|
|
|
|
loadOptions();
|
|
|
|
|
updateOptionSummary();
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-16 13:49:44 +00:00
|
|
|
|
function updateVoiceSelect() {
|
|
|
|
|
|
|
|
|
|
const options = getOptions();
|
|
|
|
|
const voice_select_options = document.querySelector("#voice_select_options");
|
|
|
|
|
|
|
|
|
|
if (options.use_voice) {
|
|
|
|
|
voice_select_options.style.display = "block";
|
|
|
|
|
} else {
|
|
|
|
|
voice_select_options.style.display = "none";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateVoiceSelection() {
|
|
|
|
|
|
|
|
|
|
const newSelection = document.querySelector("#voice_select").selectedOptions[0].text;
|
|
|
|
|
|
|
|
|
|
const voiceConfig = {
|
|
|
|
|
voice: document.querySelector("#voice_select").selectedOptions[0].text,
|
|
|
|
|
rate: 1,
|
|
|
|
|
pitch: 1
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
setVoiceConfig(voiceConfig);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-25 16:48:32 +00:00
|
|
|
|
function explain() {
|
|
|
|
|
$('#explanation').show();
|
2020-07-23 12:11:28 +00:00
|
|
|
|
$('#message').hide();
|
2017-12-25 20:29:51 +00:00
|
|
|
|
$('#explain-proceed-button').focus();
|
2017-12-25 16:48:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
function getOptions() {
|
2021-09-29 16:57:47 +00:00
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
var result = {};
|
|
|
|
|
|
2021-09-29 16:57:47 +00:00
|
|
|
|
configOptions.options.forEach(function (option) {
|
2017-08-12 18:04:09 +00:00
|
|
|
|
result[option] = $('#' + option).is(':checked') != false;
|
|
|
|
|
});
|
|
|
|
|
|
2021-09-29 16:57:47 +00:00
|
|
|
|
configOptions.selects.forEach(function (select) {
|
2020-07-22 18:04:21 +00:00
|
|
|
|
result[select] = $('#' + select).val();
|
|
|
|
|
});
|
|
|
|
|
|
2021-09-29 16:57:47 +00:00
|
|
|
|
configOptions.inputs.forEach(function (input) {
|
|
|
|
|
result[input] = $('#' + input).val();
|
|
|
|
|
});
|
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
return result;
|
2017-04-29 14:50:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-12 22:07:48 +00:00
|
|
|
|
function loadOptions() {
|
|
|
|
|
|
2021-09-29 16:57:47 +00:00
|
|
|
|
var storedOptionsText = localStorage.getItem(localStorageOptionsKey);
|
2021-06-12 22:07:48 +00:00
|
|
|
|
|
2021-09-29 16:57:47 +00:00
|
|
|
|
if (storedOptionsText) {
|
|
|
|
|
|
|
|
|
|
var storedOptions = JSON.parse(storedOptionsText);
|
|
|
|
|
|
|
|
|
|
configOptions.options.forEach(function (option) {
|
|
|
|
|
if (storedOptions[option] != undefined) {
|
|
|
|
|
$(`#${option}`).prop('checked', storedOptions[option]);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
configOptions.selects.forEach(function (select) {
|
|
|
|
|
if (storedOptions[select] != undefined) {
|
|
|
|
|
$(`#${select} [value=${storedOptions[select]}]`).attr('selected', false)
|
|
|
|
|
$(`#${select} [value=${storedOptions[select]}]`).attr('selected', 'selected')
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
configOptions.inputs.forEach(function (input) {
|
|
|
|
|
if (storedOptions[input] != undefined) {
|
|
|
|
|
$(`#${input}`).val(storedOptions[input]);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2021-06-12 22:07:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-13 21:02:16 +00:00
|
|
|
|
$('window').ready(function () {
|
2017-04-29 14:50:22 +00:00
|
|
|
|
|
2021-02-16 13:49:44 +00:00
|
|
|
|
if (window.speechSynthesis) {
|
|
|
|
|
populateVoiceList();
|
|
|
|
|
$('#useVoiceSection').show();
|
|
|
|
|
$('input#use_voice').click(updateVoiceSelect);
|
|
|
|
|
$('select#voice_select').on('change', updateVoiceSelection);
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-04 20:57:16 +00:00
|
|
|
|
calculateAllConjugations();
|
2018-03-05 02:40:09 +00:00
|
|
|
|
calculateTransitions();
|
2021-06-12 22:07:48 +00:00
|
|
|
|
loadOptions();
|
2017-03-18 23:54:45 +00:00
|
|
|
|
|
2016-12-23 18:05:37 +00:00
|
|
|
|
$('#go').click(startQuiz);
|
2021-09-29 16:57:47 +00:00
|
|
|
|
$('#defaults').click(restoreDefaults);
|
2016-12-23 18:05:37 +00:00
|
|
|
|
$('#backToStart').click(showSplash);
|
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
$('div.options input').click(updateOptionSummary);
|
2020-07-22 18:04:21 +00:00
|
|
|
|
$('select#questionFocus').on('change', updateOptionSummary);
|
2017-08-12 18:04:09 +00:00
|
|
|
|
$('input#trick').click(updateOptionSummary);
|
2020-07-22 18:00:42 +00:00
|
|
|
|
$('input#focus_mode').click(updateOptionSummary);
|
2017-08-12 18:04:09 +00:00
|
|
|
|
|
2021-09-29 16:57:47 +00:00
|
|
|
|
$('select').change(saveOptions);
|
|
|
|
|
$('input').change(saveOptions);
|
|
|
|
|
|
2017-08-12 18:04:09 +00:00
|
|
|
|
updateOptionSummary();
|
2017-03-19 00:16:23 +00:00
|
|
|
|
|
2016-12-23 18:05:37 +00:00
|
|
|
|
showSplash();
|
2016-11-22 00:22:00 +00:00
|
|
|
|
});
|