const hiragana = { "あ": { romaji: "a", english: "hiragana_a", level: 1 }, "い": { romaji: "i", english: "hiragana_i", level: 1 }, "う": { romaji: "u", english: "hiragana_u", level: 1 }, "え": { romaji: "e", english: "hiragana_e", level: 1 }, "お": { romaji: "o", english: "hiragana_o", level: 1 }, "か": { romaji: "ka", english: "hiragana_ka", level: 1 }, "き": { romaji: "ki", english: "hiragana_ki", level: 1 }, "く": { romaji: "ku", english: "hiragana_ku", level: 1 }, "け": { romaji: "ke", english: "hiragana_ke", level: 1 }, "こ": { romaji: "ko", english: "hiragana_ko", level: 1 }, "さ": { romaji: "sa", english: "hiragana_sa", level: 1 }, "し": { romaji: "shi", english: "hiragana_shi", level: 1 }, "す": { romaji: "su", english: "hiragana_su", level: 1 }, "せ": { romaji: "se", english: "hiragana_se", level: 1 }, "そ": { romaji: "so", english: "hiragana_so", level: 1 }, "た": { romaji: "ta", english: "hiragana_ta", level: 1 }, "ち": { romaji: "chi", english: "hiragana_chi", level: 1 }, "つ": { romaji: "tsu", english: "hiragana_tsu", level: 1 }, "て": { romaji: "te", english: "hiragana_te", level: 1 }, "と": { romaji: "to", english: "hiragana_to", level: 1 }, "な": { romaji: "na", english: "hiragana_na", level: 1 }, "に": { romaji: "ni", english: "hiragana_ni", level: 1 }, "ぬ": { romaji: "nu", english: "hiragana_nu", level: 1 }, "ね": { romaji: "ne", english: "hiragana_ne", level: 1 }, "の": { romaji: "no", english: "hiragana_no", level: 1 }, "は": { romaji: "ha", english: "hiragana_ha", level: 1 }, "ひ": { romaji: "hi", english: "hiragana_hi", level: 1 }, "ふ": { romaji: "fu", english: "hiragana_fu", level: 1 }, "へ": { romaji: "he", english: "hiragana_he", level: 1 }, "ほ": { romaji: "ho", english: "hiragana_ho", level: 1 }, "ま": { romaji: "ma", english: "hiragana_ma", level: 1 }, "み": { romaji: "mi", english: "hiragana_mi", level: 1 }, "む": { romaji: "mu", english: "hiragana_mu", level: 1 }, "め": { romaji: "me", english: "hiragana_me", level: 1 }, "も": { romaji: "mo", english: "hiragana_mo", level: 1 }, "や": { romaji: "ya", english: "hiragana_ya", level: 1 }, "ゆ": { romaji: "yu", english: "hiragana_yu", level: 1 }, "よ": { romaji: "yo", english: "hiragana_yo", level: 1 }, "ら": { romaji: "ra", english: "hiragana_ra", level: 1 }, "り": { romaji: "ri", english: "hiragana_ri", level: 1 }, "る": { romaji: "ru", english: "hiragana_ru", level: 1 }, "れ": { romaji: "re", english: "hiragana_re", level: 1 }, "ろ": { romaji: "ro", english: "hiragana_ro", level: 1 }, "わ": { romaji: "wa", english: "hiragana_wa", level: 1 }, "を": { romaji: "wo", english: "hiragana_wo", level: 1 }, "ん": { romaji: "n", english: "hiragana_n", level: 1 }, "が": { romaji: "ga", english: "hiragana_ga", level: 1 }, "ぎ": { romaji: "gi", english: "hiragana_gi", level: 1 }, "ぐ": { romaji: "gu", english: "hiragana_gu", level: 1 }, "げ": { romaji: "ge", english: "hiragana_ge", level: 1 }, "ご": { romaji: "go", english: "hiragana_go", level: 1 }, "ざ": { romaji: "za", english: "hiragana_za", level: 1 }, "じ": { romaji: "ji", english: "hiragana_ji", level: 1 }, "ず": { romaji: "zu", english: "hiragana_zu", level: 1 }, "ぜ": { romaji: "ze", english: "hiragana_ze", level: 1 }, "ぞ": { romaji: "zo", english: "hiragana_zo", level: 1 }, "だ": { romaji: "da", english: "hiragana_da", level: 1 }, "ぢ": { romaji: "ji", english: "hiragana_di", level: 1 }, "づ": { romaji: "zu", english: "hiragana_du", level: 1 }, "で": { romaji: "de", english: "hiragana_de", level: 1 }, "ど": { romaji: "do", english: "hiragana_do", level: 1 }, "ば": { romaji: "ba", english: "hiragana_ba", level: 1 }, "び": { romaji: "bi", english: "hiragana_bi", level: 1 }, "ぶ": { romaji: "bu", english: "hiragana_bu", level: 1 }, "べ": { romaji: "be", english: "hiragana_be", level: 1 }, "ぼ": { romaji: "bo", english: "hiragana_bo", level: 1 }, "ぱ": { romaji: "pa", english: "hiragana_pa", level: 1 }, "ぴ": { romaji: "pi", english: "hiragana_pi", level: 1 }, "ぷ": { romaji: "pu", english: "hiragana_pu", level: 1 }, "ぺ": { romaji: "pe", english: "hiragana_pe", level: 1 }, "ぽ": { romaji: "po", english: "hiragana_po", level: 1 }, "きゃ": { romaji: "kya", english: "hiragana_kya", level: 1 }, "きゅ": { romaji: "kyu", english: "hiragana_kyu", level: 1 }, "きょ": { romaji: "kyo", english: "hiragana_kyo", level: 1 }, "しゃ": { romaji: "sha", english: "hiragana_sha", level: 1 }, "しゅ": { romaji: "shu", english: "hiragana_shu", level: 1 }, "しょ": { romaji: "sho", english: "hiragana_sho", level: 1 }, "ちゃ": { romaji: "cha", english: "hiragana_cha", level: 1 }, "ちゅ": { romaji: "chu", english: "hiragana_chu", level: 1 }, "ちょ": { romaji: "cho", english: "hiragana_cho", level: 1 }, "にゃ": { romaji: "nya", english: "hiragana_nya", level: 1 }, "にゅ": { romaji: "nyu", english: "hiragana_nyu", level: 1 }, "にょ": { romaji: "nyo", english: "hiragana_nyo", level: 1 }, "ひゃ": { romaji: "hya", english: "hiragana_hya", level: 1 }, "ひゅ": { romaji: "hyu", english: "hiragana_hyu", level: 1 }, "ひょ": { romaji: "hyo", english: "hiragana_hyo", level: 1 }, "みゃ": { romaji: "mya", english: "hiragana_mya", level: 1 }, "みゅ": { romaji: "myu", english: "hiragana_myu", level: 1 }, "みょ": { romaji: "myo", english: "hiragana_myo", level: 1 }, "りゃ": { romaji: "rya", english: "hiragana_rya", level: 1 }, "りゅ": { romaji: "ryu", english: "hiragana_ryu", level: 1 }, "りょ": { romaji: "ryo", english: "hiragana_ryo", level: 1 }, "ぎゃ": { romaji: "gya", english: "hiragana_gya", level: 1 }, "ぎゅ": { romaji: "gyu", english: "hiragana_gyu", level: 1 }, "ぎょ": { romaji: "gyo", english: "hiragana_gyo", level: 1 }, "じゃ": { romaji: "ja", english: "hiragana_ja", level: 1 }, "じゅ": { romaji: "ju", english: "hiragana_ju", level: 1 }, "じょ": { romaji: "jo", english: "hiragana_jo", level: 1 }, "びゃ": { romaji: "bya", english: "hiragana_bya", level: 1 }, "びゅ": { romaji: "byu", english: "hiragana_byu", level: 1 }, "びょ": { romaji: "byo", english: "hiragana_byo", level: 1 }, "ぴゃ": { romaji: "pya", english: "hiragana_pya", level: 1 }, "ぴゅ": { romaji: "pyu", english: "hiragana_pyu", level: 1 }, "ぴょ": { romaji: "pyo", english: "hiragana_pyo", level: 1 } }; const katakana = { "ア": { romaji: "a", english: "katakana_a", level: 1 }, "イ": { romaji: "i", english: "katakana_i", level: 1 }, "ウ": { romaji: "u", english: "katakana_u", level: 1 }, "エ": { romaji: "e", english: "katakana_e", level: 1 }, "オ": { romaji: "o", english: "katakana_o", level: 1 }, "カ": { romaji: "ka", english: "katakana_ka", level: 1 }, "キ": { romaji: "ki", english: "katakana_ki", level: 1 }, "ク": { romaji: "ku", english: "katakana_ku", level: 1 }, "ケ": { romaji: "ke", english: "katakana_ke", level: 1 }, "コ": { romaji: "ko", english: "katakana_ko", level: 1 }, "サ": { romaji: "sa", english: "katakana_sa", level: 1 }, "シ": { romaji: "shi", english: "katakana_shi", level: 1 }, "ス": { romaji: "su", english: "katakana_su", level: 1 }, "セ": { romaji: "se", english: "katakana_se", level: 1 }, "ソ": { romaji: "so", english: "katakana_so", level: 1 }, "タ": { romaji: "ta", english: "katakana_ta", level: 1 }, "チ": { romaji: "chi", english: "katakana_chi", level: 1 }, "ツ": { romaji: "tsu", english: "katakana_tsu", level: 1 }, "テ": { romaji: "te", english: "katakana_te", level: 1 }, "ト": { romaji: "to", english: "katakana_to", level: 1 }, "ナ": { romaji: "na", english: "katakana_na", level: 1 }, "ニ": { romaji: "ni", english: "katakana_ni", level: 1 }, "ヌ": { romaji: "nu", english: "katakana_nu", level: 1 }, "ネ": { romaji: "ne", english: "katakana_ne", level: 1 }, "ノ": { romaji: "no", english: "katakana_no", level: 1 }, "ハ": { romaji: "ha", english: "katakana_ha", level: 1 }, "ヒ": { romaji: "hi", english: "katakana_hi", level: 1 }, "フ": { romaji: "fu", english: "katakana_fu", level: 1 }, "ヘ": { romaji: "he", english: "katakana_he", level: 1 }, "ホ": { romaji: "ho", english: "katakana_ho", level: 1 }, "マ": { romaji: "ma", english: "katakana_ma", level: 1 }, "ミ": { romaji: "mi", english: "katakana_mi", level: 1 }, "ム": { romaji: "mu", english: "katakana_mu", level: 1 }, "メ": { romaji: "me", english: "katakana_me", level: 1 }, "モ": { romaji: "mo", english: "katakana_mo", level: 1 }, "ヤ": { romaji: "ya", english: "katakana_ya", level: 1 }, "ユ": { romaji: "yu", english: "katakana_yu", level: 1 }, "ヨ": { romaji: "yo", english: "katakana_yo", level: 1 }, "ラ": { romaji: "ra", english: "katakana_ra", level: 1 }, "リ": { romaji: "ri", english: "katakana_ri", level: 1 }, "ル": { romaji: "ru", english: "katakana_ru", level: 1 }, "レ": { romaji: "re", english: "katakana_re", level: 1 }, "ロ": { romaji: "ro", english: "katakana_ro", level: 1 }, "ワ": { romaji: "wa", english: "katakana_wa", level: 1 }, "ヲ": { romaji: "wo", english: "katakana_wo", level: 1 }, "ン": { romaji: "n", english: "katakana_n", level: 1 }, "ガ": { romaji: "ga", english: "katakana_ga", level: 1 }, "ギ": { romaji: "gi", english: "katakana_gi", level: 1 }, "グ": { romaji: "gu", english: "katakana_gu", level: 1 }, "ゲ": { romaji: "ge", english: "katakana_ge", level: 1 }, "ゴ": { romaji: "go", english: "katakana_go", level: 1 }, "ザ": { romaji: "za", english: "katakana_za", level: 1 }, "ジ": { romaji: "ji", english: "katakana_ji", level: 1 }, "ズ": { romaji: "zu", english: "katakana_zu", level: 1 }, "ゼ": { romaji: "ze", english: "katakana_ze", level: 1 }, "ゾ": { romaji: "zo", english: "katakana_zo", level: 1 }, "ダ": { romaji: "da", english: "katakana_da", level: 1 }, "ヂ": { romaji: "ji", english: "katakana_di", level: 1 }, "ヅ": { romaji: "zu", english: "katakana_du", level: 1 }, "デ": { romaji: "de", english: "katakana_de", level: 1 }, "ド": { romaji: "do", english: "katakana_do", level: 1 }, "バ": { romaji: "ba", english: "katakana_ba", level: 1 }, "ビ": { romaji: "bi", english: "katakana_bi", level: 1 }, "ブ": { romaji: "bu", english: "katakana_bu", level: 1 }, "ベ": { romaji: "be", english: "katakana_be", level: 1 }, "ボ": { romaji: "bo", english: "katakana_bo", level: 1 }, "パ": { romaji: "pa", english: "katakana_pa", level: 1 }, "ピ": { romaji: "pi", english: "katakana_pi", level: 1 }, "プ": { romaji: "pu", english: "katakana_pu", level: 1 }, "ペ": { romaji: "pe", english: "katakana_pe", level: 1 }, "ポ": { romaji: "po", english: "katakana_po", level: 1 }, "キャ": { romaji: "kya", english: "katakana_kya", level: 1 }, "キュ": { romaji: "kyu", english: "katakana_kyu", level: 1 }, "キョ": { romaji: "kyo", english: "katakana_kyo", level: 1 }, "シャ": { romaji: "sha", english: "katakana_sha", level: 1 }, "シュ": { romaji: "shu", english: "katakana_shu", level: 1 }, "ショ": { romaji: "sho", english: "katakana_sho", level: 1 }, "チャ": { romaji: "cha", english: "katakana_cha", level: 1 }, "チュ": { romaji: "chu", english: "katakana_chu", level: 1 }, "チョ": { romaji: "cho", english: "katakana_cho", level: 1 }, "ニャ": { romaji: "nya", english: "katakana_nya", level: 1 }, "ニュ": { romaji: "nyu", english: "katakana_nyu", level: 1 }, "ニョ": { romaji: "nyo", english: "katakana_nyo", level: 1 }, "ヒャ": { romaji: "hya", english: "katakana_hya", level: 1 }, "ヒュ": { romaji: "hyu", english: "katakana_hyu", level: 1 }, "ヒョ": { romaji: "hyo", english: "katakana_hyo", level: 1 }, "ミャ": { romaji: "mya", english: "katakana_mya", level: 1 }, "ミュ": { romaji: "myu", english: "katakana_myu", level: 1 }, "ミョ": { romaji: "myo", english: "katakana_myo", level: 1 }, "リャ": { romaji: "rya", english: "katakana_rya", level: 1 }, "リュ": { romaji: "ryu", english: "katakana_ryu", level: 1 }, "リョ": { romaji: "ryo", english: "katakana_ryo", level: 1 }, "ギャ": { romaji: "gya", english: "katakana_gya", level: 1 }, "ギュ": { romaji: "gyu", english: "katakana_gyu", level: 1 }, "ギョ": { romaji: "gyo", english: "katakana_gyo", level: 1 }, "ジャ": { romaji: "ja", english: "katakana_ja", level: 1 }, "ジュ": { romaji: "ju", english: "katakana_ju", level: 1 }, "ジョ": { romaji: "jo", english: "katakana_jo", level: 1 }, "ビャ": { romaji: "bya", english: "katakana_bya", level: 1 }, "ビュ": { romaji: "byu", english: "katakana_byu", level: 1 }, "ビョ": { romaji: "byo", english: "katakana_byo", level: 1 }, "ピャ": { romaji: "pya", english: "katakana_pya", level: 1 }, "ピュ": { romaji: "pyu", english: "katakana_pyu", level: 1 }, "ピョ": { romaji: "pyo", english: "katakana_pyo", level: 1 } }; const hiraganaRows = { "あ-row": ["あ", "い", "う", "え", "お"], "か-row": ["か", "き", "く", "け", "こ"], "さ-row": ["さ", "し", "す", "せ", "そ"], "た-row": ["た", "ち", "つ", "て", "と"], "な-row": ["な", "に", "ぬ", "ね", "の"], "は-row": ["は", "ひ", "ふ", "へ", "ほ"], "ま-row": ["ま", "み", "む", "め", "も"], "や-row": ["や", "ゆ", "よ"], "ら-row": ["ら", "り", "る", "れ", "ろ"], "わ-row": ["わ", "を"], "ん-row": ["ん"], "が-row": ["が", "ぎ", "ぐ", "げ", "ご"], "ざ-row": ["ざ", "じ", "ず", "ぜ", "ぞ"], "だ-row": ["だ", "ぢ", "づ", "で", "ど"], "ば-row": ["ば", "び", "ぶ", "べ", "ぼ"], "ぱ-row": ["ぱ", "ぴ", "ぷ", "ぺ", "ぽ"], "digraphs": [ "きゃ", "きゅ", "きょ", "しゃ", "しゅ", "しょ", "ちゃ", "ちゅ", "ちょ", "にゃ", "にゅ", "にょ", "ひゃ", "ひゅ", "ひょ", "みゃ", "みゅ", "みょ", "りゃ", "りゅ", "りょ", "ぎゃ", "ぎゅ", "ぎょ", "じゃ", "じゅ", "じょ", "びゃ", "びゅ", "びょ", "ぴゃ", "ぴゅ", "ぴょ" ] }; const katakanaRows = { "ア-row": ["ア", "イ", "ウ", "エ", "オ"], "カ-row": ["カ", "キ", "ク", "ケ", "コ"], "サ-row": ["サ", "シ", "ス", "セ", "ソ"], "タ-row": ["タ", "チ", "ツ", "テ", "ト"], "ナ-row": ["ナ", "ニ", "ヌ", "ネ", "ノ"], "ハ-row": ["ハ", "ヒ", "フ", "ヘ", "ホ"], "マ-row": ["マ", "ミ", "ム", "メ", "モ"], "ヤ-row": ["ヤ", "ユ", "ヨ"], "ラ-row": ["ラ", "リ", "ル", "レ", "ロ"], "ワ-row": ["ワ", "ヲ"], "ン-row": ["ン"], "ガ-row": ["ガ", "ギ", "グ", "ゲ", "ゴ"], "ザ-row": ["ザ", "ジ", "ズ", "ゼ", "ゾ"], "ダ-row": ["ダ", "ヂ", "ヅ", "デ", "ド"], "バ-row": ["バ", "ビ", "ブ", "ベ", "ボ"], "パ-row": ["パ", "ピ", "プ", "ペ", "ポ"], "digraphs": [ "キャ", "キュ", "キョ", "シャ", "シュ", "ショ", "チャ", "チュ", "チョ", "ニャ", "ニュ", "ニョ", "ヒャ", "ヒュ", "ヒョ", "ミャ", "ミュ", "ミョ", "リャ", "リュ", "リョ", "ギャ", "ギュ", "ギョ", "ジャ", "ジュ", "ジョ", "ビャ", "ビュ", "ビョ", "ピャ", "ピュ", "ピョ" ] }; const hiraganaTwoLetterWords = { "こんにちは": { romaji: "konnichiwa", english: "hello", level: 2 }, "さようなら": { romaji: "sayonara", english: "goodbye", level: 2 }, "ありがとう": { romaji: "arigatou", english: "thank you", level: 2 }, "おはよう": { romaji: "ohayou", english: "good morning", level: 2 }, "こんばんは": { romaji: "konbanwa", english: "good evening", level: 2 }, "はい": { romaji: "hai", english: "yes", level: 2 }, "いいえ": { romaji: "iie", english: "no", level: 2 }, "すみません": { romaji: "sumimasen", english: "excuse me / I’m sorry", level: 2 }, "おねがいします": { romaji: "onegaishimasu", english: "please", level: 2 }, "わかりました": { romaji: "wakarimashita", english: "I understand", level: 2 }, "どういたしまして": { romaji: "dou itashimashite", english: "you’re welcome", level: 2 }, "ごめんなさい": { romaji: "gomennasai", english: "I’m sorry", level: 2 }, "たすけて": { romaji: "tasukete", english: "help", level: 2 }, "おやすみ": { romaji: "oyasumi", english: "good night", level: 2 }, "いただきます": { romaji: "itadakimasu", english: "let’s eat (before a meal)", level: 2 }, "ごちそうさま": { romaji: "gochisousama", english: "thank you for the meal (after eating)", level: 2 }, "いってきます": { romaji: "ittekimasu", english: "I’m leaving (home)", level: 2 }, "ただいま": { romaji: "tadaima", english: "I’m home", level: 2 }, "おかえり": { romaji: "okaeri", english: "welcome back", level: 2 }, "あめ": { romaji: "ame", english: "rain", level: 2 }, "ゆき": { romaji: "yuki", english: "snow", level: 2 }, "くるま": { romaji: "kuruma", english: "car", level: 2 }, "ひこうき": { romaji: "hikouki", english: "airplane", level: 2 }, "じてんしゃ": { romaji: "jitensha", english: "bicycle", level: 2 }, "てがみ": { romaji: "tegami", english: "letter", level: 2 }, "ほん": { romaji: "hon", english: "book", level: 2 }, "えんぴつ": { romaji: "enpitsu", english: "pencil", level: 2 }, "かさ": { romaji: "kasa", english: "umbrella", level: 2 }, "つくえ": { romaji: "tsukue", english: "desk", level: 2 }, "いす": { romaji: "isu", english: "chair", level: 2 }, "たんす": { romaji: "tansu", english: "drawer", level: 2 }, "くつ": { romaji: "kutsu", english: "shoes", level: 2 }, "ふく": { romaji: "fuku", english: "clothes", level: 2 }, "たてもの": { romaji: "tatemono", english: "building", level: 2 }, "き": { romaji: "ki", english: "tree", level: 2 }, "はな": { romaji: "hana", english: "flower", level: 2 }, "やま": { romaji: "yama", english: "mountain", level: 2 }, "かわ": { romaji: "kawa", english: "river", level: 2 }, "うみ": { romaji: "umi", english: "sea", level: 2 }, "そら": { romaji: "sora", english: "sky", level: 2 }, "とり": { romaji: "tori", english: "bird", level: 2 }, "いぬ": { romaji: "inu", english: "dog", level: 2 }, "ねこ": { romaji: "neko", english: "cat", level: 2 }, "りんご": { romaji: "ringo", english: "apple", level: 2 }, "みかん": { romaji: "mikan", english: "orange", level: 2 }, "ぶどう": { romaji: "budou", english: "grapes", level: 2 }, "にんじん": { romaji: "ninjin", english: "carrot", level: 2 }, "おかし": { romaji: "okashi", english: "snack", level: 2 }, "ごはん": { romaji: "gohan", english: "rice/meal", level: 2 }, "さかな": { romaji: "sakana", english: "fish", level: 2 }, "にく": { romaji: "niku", english: "meat", level: 2 }, "やさい": { romaji: "yasai", english: "vegetable", level: 2 }, "すいか": { romaji: "suika", english: "watermelon", level: 2 }, "やすい": { romaji: "yasui", english: "cheap", level: 2 }, "たかい": { romaji: "takai", english: "expensive/high", level: 2 }, "あたらしい": { romaji: "atarashii", english: "new", level: 2 }, "ふるい": { romaji: "furui", english: "old", level: 2 }, "あつい": { romaji: "atsui", english: "hot", level: 2 }, "さむい": { romaji: "samui", english: "cold", level: 2 }, "ながい": { romaji: "nagai", english: "long", level: 2 }, "みじかい": { romaji: "mijikai", english: "short", level: 2 }, "すき": { romaji: "suki", english: "like", level: 2 }, "きらい": { romaji: "kirai", english: "dislike", level: 2 }, "おいしい": { romaji: "oishii", english: "delicious", level: 2 }, "まずい": { romaji: "mazui", english: "bad-tasting", level: 2 } }; const katakanaTwoLetterWords = { "アイス": { romaji: "aisu", english: "ice cream", level: 2 }, "コーヒー": { romaji: "koohii", english: "coffee", level: 2 }, "パン": { romaji: "pan", english: "bread", level: 2 }, "テレビ": { romaji: "terebi", english: "television", level: 2 }, "コンピュータ": { romaji: "konpyuuta", english: "computer", level: 2 }, "タクシー": { romaji: "takushii", english: "taxi", level: 2 }, "スポーツ": { romaji: "supootsu", english: "sports", level: 2 }, "レストラン": { romaji: "resutoran", english: "restaurant", level: 2 }, "サッカー": { romaji: "sakkaa", english: "soccer", level: 2 }, "バス": { romaji: "basu", english: "bus", level: 2 }, "ミュージック": { romaji: "myuujikku", english: "music", level: 2 }, "メール": { romaji: "meeru", english: "email", level: 2 }, "データ": { romaji: "deeta", english: "data", level: 2 }, "トレーニング": { romaji: "toreeningu", english: "training", level: 2 }, "ファッション": { romaji: "fasshon", english: "fashion", level: 2 }, "ゲーム": { romaji: "geemu", english: "game", level: 2 }, "スマートフォン": { romaji: "sumaatofon", english: "smartphone", level: 2 }, "ハンバーガー": { romaji: "hanbaagaa", english: "hamburger", level: 2 }, "ビール": { romaji: "biiru", english: "beer", level: 2 }, "メニュー": { romaji: "menyuu", english: "menu", level: 2 }, "シャツ": { romaji: "shatsu", english: "shirt", level: 2 }, "スーツ": { romaji: "suutsu", english: "suit", level: 2 }, "カメラ": { romaji: "kamera", english: "camera", level: 2 }, "セーター": { romaji: "seetaa", english: "sweater", level: 2 }, "タブレット": { romaji: "taburetto", english: "tablet", level: 2 }, "ドリンク": { romaji: "dorinku", english: "drink", level: 2 }, "クレジットカード": { romaji: "kurejitto kaado", english: "credit card", level: 2 }, "チョコレート": { romaji: "chokoreeto", english: "chocolate", level: 2 }, "アニメ": { romaji: "anime", english: "anime", level: 2 }, "マンガ": { romaji: "manga", english: "manga", level: 2 }, "ファイル": { romaji: "fairu", english: "file", level: 2 }, "キャンディ": { romaji: "kyandi", english: "candy", level: 2 }, "ネクタイ": { romaji: "nekutai", english: "tie", level: 2 }, "エネルギー": { romaji: "enerugii", english: "energy", level: 2 }, "チーズ": { romaji: "chiizu", english: "cheese", level: 2 }, "ビデオ": { romaji: "bideo", english: "video", level: 2 } }; let letterDisplay; let choicesContainer; let startButton; let buttonSingleSounds; let buttonWords; let currentScriptRows = {}; let selectedRows = new Set(); let correctLetter; let currentLevel = "Level 1: Single Sounds"; // Default level let initialTime = 5000; // 5 seconds let correctCount = 0; let incorrectCount = 0; let livesLeft = 5; // Start with 5 lives // Counter and level UI elements let correctCounterTextView; let incorrectCounterTextView; let hiraganaButton; let katakanaButton; let selectedScript = null; let correctAnswerCount = 0; let currentUserLevel = 1; let levelTextView; let countDownTimer; let timeLeftInMillis = initialTime; document.addEventListener("DOMContentLoaded", function() { // Initialize UI components letterDisplay = document.getElementById('letter_display'); choicesContainer = document.getElementById('choices_container'); startButton = document.getElementById('start_button'); buttonSingleSounds = document.getElementById('button_single_sounds'); buttonWords = document.getElementById('button_words'); correctCounterTextView = document.getElementById('correct_counter_text'); incorrectCounterTextView = document.getElementById('incorrect_counter_text'); hiraganaButton = document.getElementById('hiragana_button'); katakanaButton = document.getElementById('katakana_button'); levelTextView = document.getElementById('level_text_view'); englishTranslationTextView = document.getElementById('english_translation'); setupButtonListeners(); const selectAllCheckbox = document.getElementById('select-all-checkbox'); const confirmButton = document.getElementById('confirm-button'); selectAllCheckbox.addEventListener("change", (event) => { const isChecked = event.target.checked; checkedItems.fill(isChecked); // Set all items to the same checked state }); // Handle dialog confirmation (saving selected rows) confirmButton.addEventListener('click', () => { alert(`${selectedRows.length} row(s) selected`); saveSelectedRows(); closeDialog(); }); startButton.addEventListener('click', function() { if (startButton.textContent === "Reset Game") { window.location.reload(); } else { startQuizWithSelections(); collapseSections(); } }); // Listen for script selection (Hiragana/Katakana) hiraganaButton.addEventListener("click", function() { updateStartButtonState(); updateLevelButtonsState(); }); katakanaButton.addEventListener("click", function() { updateStartButtonState(); updateLevelButtonsState(); }); // Listen for level selection (Single Sounds/Words) buttonSingleSounds.addEventListener("click", function() { updateStartButtonState(); }); buttonWords.addEventListener("click", function() { updateStartButtonState(); }); // Function to check if start button should be enabled or disabled function updateStartButtonState() { if ( (buttonSingleSounds.classList.contains("active") || buttonWords.classList.contains("active")) ) { startButton.disabled = false; // Enable start button } else { startButton.disabled = true; // Disable start button } } // Function to update the state of the level buttons (disabled if no script selected) function updateLevelButtonsState() { if (hiraganaButton.classList.contains("active") || katakanaButton.classList.contains("active")) { buttonSingleSounds.disabled = false; // Enable level buttons buttonWords.disabled = false; } else { buttonSingleSounds.disabled = true; // Disable level buttons buttonWords.disabled = true; } } // Initial state: Disable start button and level buttons updateStartButtonState(); updateLevelButtonsState(); }); function collapseSections() { // Hide only the selection container const selectionContainer = document.getElementById('selection-container'); if (selectionContainer) { selectionContainer.style.display = 'none'; } } function updateTimerText() { const timerTextView = document.getElementById("timer_text"); const seconds = Math.floor(timeLeftInMillis / 1000); const tenths = Math.floor((timeLeftInMillis % 1000) / 100); timerTextView.textContent = `Time: ${seconds}.${tenths} s`; } function handleIncorrectAnswer(question, correctAnswer, onRetry) { stopCountdownTimer(); // Stop the timer livesLeft = Math.max(livesLeft - 1, 0); // Decrease lives console.log(`Incorrect answer! Lives left: ${livesLeft}`); showIncorrectAnswerDialog(question, correctAnswer, onRetry); if (livesLeft === 0) { handleGameOver(); // Only handle game over when lives are 0 } } function getLevel(key) { return parseInt(localStorage.getItem(key)) || 0; // Return the level as an integer, default to 0 } function saveLevel(key, value) { localStorage.setItem(key, value); } function getCorrectAnswer() { // Check the current level and return the correct answer based on that if (currentLevel === "Level 1: Single Sounds") { return getCorrectAnswerFromKana(correctLetter) || "Unknown"; } else { return currentCorrectAnswer || "Unknown"; // Use stored correct answer if available } } // Function to reset and start the timer function setupQuizTimer() { resetTimer(); startCountdownTimer(); } function resetTimer() { timeLeftInMillis = updateStartingTimeBasedOnLevel(); } // Update the state of the "Select All" checkbox function updateSelectAllCheckboxState() { selectAllCheckbox.checked = checkedItems.every(Boolean); selectAllCheckbox.indeterminate = !checkedItems.every(Boolean) && checkedItems.some(Boolean); } function updateRowSelections() { selectedRows = rowKeys.filter((_, index) => checkedItems[index]); confirmButton.disabled = selectedRows.length === 0; console.log(selectedRows); // Debug: Log selected rows } function populateRows(rowKeys, checkedItems) { const rowListContainer = document.getElementById("row-list"); rowListContainer.innerHTML = ''; // Clear existing rows rowKeys.forEach((rowKey, index) => { const listItem = document.createElement("li"); listItem.classList.add("row-item"); const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.checked = checkedItems[index]; checkbox.addEventListener("change", (event) => { checkedItems[index] = event.target.checked; }); listItem.appendChild(checkbox); listItem.appendChild(document.createTextNode(`Row ${index + 1}: ${rowKey}`)); rowListContainer.appendChild(listItem); }); } // Save the selected rows (in localStorage for web version) function saveSelectedRows() { localStorage.setItem('selectedRows', JSON.stringify(selectedRows)); } function loadRowsForScript(scriptName, level) { let rowData = []; let targetLevel = null; // Determine the target level number if (level) { if (level.includes("Level 1")) { targetLevel = 1; } else if (level.includes("Level 2")) { targetLevel = 2; } } if (scriptName === "Hiragana") { if (targetLevel === 1) { // Level 1: Load rows from hiraganaRows rowData = Object.entries(hiraganaRows).map(([rowName, characters]) => ({ type: "row", rowName, characters, })); } else if (targetLevel === 2) { // Level 2: Load words from hiraganaTwoLetterWords rowData = Object.entries(hiraganaTwoLetterWords).map(([word, data]) => ({ type: "word", word, ...data, })); } } else if (scriptName === "Katakana") { if (targetLevel === 1) { // Level 1: Load rows from katakanaRows rowData = Object.entries(katakanaRows).map(([rowName, characters]) => ({ type: "row", rowName, characters, })); } else if (targetLevel === 2) { // Level 2: Load words from katakanaTwoLetterWords rowData = Object.entries(katakanaTwoLetterWords).map(([word, data]) => ({ type: "word", word, ...data, })); } } const checkedItems = new Array(rowData.length).fill(true); // Default to all checked return { rows: rowData, checkedItems }; } function initializeUIComponents() { letterDisplay = document.getElementById("letter_display"); choicesContainer = document.getElementById("choices_container"); startButton = document.getElementById("start_button"); hiraganaButton = document.getElementById("hiragana_button"); katakanaButton = document.getElementById("katakana_button"); correctCounterTextView = document.getElementById("correct_counter_text"); incorrectCounterTextView = document.getElementById("incorrect_counter_text"); buttonSingleSounds = document.getElementById("button_single_sounds"); buttonWords = document.getElementById("button_words"); levelTextView = document.getElementById("level_text_view"); timerTextView = document.getElementById("timer_text"); if (!levelTextView || !timerTextView) { console.error("One or more UI elements are missing!"); return; } updateLevelDisplay(); } function updateLevelDisplay() { // Define the scale-up animation const scaleUp = new KeyframeEffect(levelTextView, [ { transform: 'scale(1)' }, { transform: 'scale(1.5)' } ], { duration: 200 }); // Define the scale-down animation const scaleDown = new KeyframeEffect(levelTextView, [ { transform: 'scale(1.5)' }, { transform: 'scale(1)' } ], { duration: 200 }); // Create an animator (Animation object) const animator = new Animation(scaleUp, levelTextView); // Add the onfinish event handler to the animator animator.onfinish = () => { // After the scale-up animation finishes, play the scale-down animation const scaleDownAnimation = new Animation(scaleDown, levelTextView); scaleDownAnimation.play(); }; // Play the animation animator.play(); // Update the level text content levelTextView.textContent = `Level: ${currentUserLevel}`; } // Event Listeners for Buttons // Add function to handle button activation state function activateButton(button) { // Remove "active" class from all buttons const buttons = document.querySelectorAll(".btn"); buttons.forEach(btn => btn.classList.remove("active")); // Add "active" class to the clicked button button.classList.add("active"); } function setupButtonListeners() { buttonSingleSounds.addEventListener("click", () => { onLevelSelected("Level 1: Single Sounds"); activateButton(buttonSingleSounds); }); buttonWords.addEventListener("click", () => { onLevelSelected("Level 2: Words"); activateButton(buttonWords); }); hiraganaButton.addEventListener("click", () => { onScriptSelected("Hiragana"); activateButton(hiraganaButton); }); katakanaButton.addEventListener("click", () => { onScriptSelected("Katakana"); activateButton(katakanaButton); }); } function onLevelSelected(level) { currentLevel = level; checkSelections(); } // Function to handle script selection function onScriptSelected(scriptName) { selectedScript = scriptName; // Update the selected script } // Function to check selections and show the dialog if both script and level are selected function checkSelections() { if (selectedScript && currentLevel) { showRowsDialog(); // Both script and level are selected, show the dialog } } function showRowsDialog() { const { rows, checkedItems } = loadRowsForScript(selectedScript, currentLevel); const modal = document.createElement("div"); modal.classList.add("modal", "fade"); modal.tabIndex = -1; modal.innerHTML = ` `; document.body.appendChild(modal); const rowListContainer = modal.querySelector("#row-list"); const selectAllCheckbox = modal.querySelector("#select-all-checkbox"); const okButton = modal.querySelector("#ok-button"); const cancelButton = modal.querySelector("#cancel-button"); rows.forEach((row, index) => { const listItem = document.createElement("li"); listItem.classList.add("list-group-item", "d-flex", "align-items-center"); const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.checked = checkedItems[index]; checkbox.addEventListener("change", () => (checkedItems[index] = checkbox.checked)); const label = document.createElement("span"); label.style.marginLeft = "10px"; let labelText = ""; if (row.type === "row") { // Level 1: It's a row of characters (single sounds) labelText = `${row.rowName}: ${row.characters.join(", ")}`; } else if (row.type === "word") { // Level 2: It's a word labelText = `${row.word} (${row.romaji}) - ${row.english}`; } else { // Fallback for other types labelText = "Unknown item"; } label.textContent = labelText; listItem.appendChild(checkbox); listItem.appendChild(label); rowListContainer.appendChild(listItem); }); selectAllCheckbox.checked = checkedItems.every((item) => item); selectAllCheckbox.addEventListener("change", () => { const isChecked = selectAllCheckbox.checked; checkedItems.fill(isChecked); rowListContainer .querySelectorAll("input[type='checkbox']") .forEach((cb) => (cb.checked = isChecked)); }); okButton.onclick = () => { console.log("Selections saved:", checkedItems); selectedRows = new Set(rows.filter((row, index) => checkedItems[index])); // Hide the Bootstrap modal const bsModal = bootstrap.Modal.getInstance(modal); bsModal.hide(); // Remove the modal from the DOM modal.addEventListener('hidden.bs.modal', () => { modal.remove(); }); // Start the quiz with the selected rows }; cancelButton.onclick = () => { const bsModal = bootstrap.Modal.getInstance(modal); bsModal.hide(); modal.addEventListener('hidden.bs.modal', () => { modal.remove(); }); }; // Initialize and show the Bootstrap modal const bsModal = new bootstrap.Modal(modal); bsModal.show(); } function startQuizWithSelections() { if (currentLevel === "Level 1: Single Sounds" && selectedRows.size === 0) { showToast("Please select at least one row."); } else { saveStartTime(); if (currentLevel === "Level 1: Single Sounds") { startQuizLevel1(); } else { startQuizLevel2(); } startCountdownTimer(); // Start the quiz timer } } // Generate and display choices for Level 2 (Words) function generateAndDisplayChoices(availableWords, correctWord) { choicesContainer.innerHTML = ""; // Clear previous choices const correctAnswer = getRomajiForWord(correctWord); if (!correctAnswer) return; const choices = generateChoices(availableWords, correctWord, correctAnswer); choices.forEach((choice) => { createChoiceButton(choice, correctAnswer, (isCorrect) => { handleChoiceSelection(isCorrect); }); }); } // Generate and display choices for Level 1 (Kana) function generateChoicesForKana(availableChars, correctLetter) { choicesContainer.innerHTML = ""; // Clear previous buttons // Create an array to hold the choices const choices = []; // Add the correct letter to the choices array choices.push(correctLetter); // Add random letters from availableChars to the choices array, excluding the correct letter while (choices.length < 4) { // Assuming we want 4 choices const randomChar = availableChars[Math.floor(Math.random() * availableChars.length)]; if (!choices.includes(randomChar)) { choices.push(randomChar); } } // Shuffle the choices array to ensure random order choices.sort(() => Math.random() - 0.5); if (!choices.length) return; // Abort if no choices generated // Create buttons for each choice choices.forEach((choice) => { const romajiChoice = getRomaji(choice); createChoiceButton(romajiChoice, getRomaji(correctLetter), (isCorrect) => { if (isCorrect) { startQuizLevel1(); } else { handleIncorrectAnswer(correctLetter, getRomaji(correctLetter), () => startQuizLevel1() ); } }); }); } function getRomaji(kana) { return hiragana[kana]?.romaji || katakana[kana]?.romaji || kana; } function generateKanaChoices(availableChars, correctLetter) { // Create an array to hold the choices const choices = []; // Add the correct letter to the choices array choices.push(correctLetter); // Add random letters from availableChars to the choices array, excluding the correct letter while (choices.length < 4) { // Assuming we want 4 choices const randomChar = availableChars[Math.floor(Math.random() * availableChars.length)]; if (!choices.includes(randomChar)) { choices.push(randomChar); } } return choices.sort(() => Math.random() - 0.5); } function createChoiceButton(choice, correctLetter, onClick) { // Ensure the choices container is visible const choicesContainer = document.getElementById("choices_container"); choicesContainer.classList.remove("d-none"); // Create a new button for each choice const button = document.createElement("button"); button.textContent = choice; // Add Bootstrap classes for styling button.classList.add("btn", "btn-outline-dark", "w-100", "mb-2", "fs-4"); // Define button behavior button.onclick = () => { const isCorrect = choice === correctLetter; handleAnswer(isCorrect); // Handle the correctness of the answer clearInterval(countDownTimer); // Stop the countdown timer onClick(isCorrect); // Trigger the callback function }; // Append the button to the container choicesContainer.appendChild(button); } function handleChoiceSelection(isCorrect, correctWord, correctAnswer) { if (isCorrect) { handleCorrectAnswer(); } else { handleIncorrectAnswer(correctWord, correctAnswer, () => { if (currentLevel === "Level 1: Single Sounds") { startQuizLevel1(); } else { startQuizLevel2(); } }); } } function handleAnswer(isCorrect) { if (isCorrect) { correctCount++; correctCounterTextView.textContent = `Correct: ${correctCount}`; playSuccessSound(); // Play success sound updateLevelIfNeeded(); // Update level only on correct answers } else { incorrectCount++; incorrectCounterTextView.textContent = `Incorrect: ${incorrectCount}`; } } // Function to handle correct answers function handleCorrectAnswer() { setupQuizTimer(); startQuizLevel2(); } function showIncorrectAnswerDialog(question, correctAnswer, onRetry) { const dialogView = document.getElementById("dialog_incorrect_answer"); if (!dialogView) { console.error("Dialog element not found!"); return; } const questionTextView = dialogView.querySelector("#tv_question"); const correctAnswerTextView = dialogView.querySelector("#tv_correct_answer"); const okButton = dialogView.querySelector("#btn_ok"); const heartContainer = dialogView.querySelector("#heart_container"); if (!questionTextView || !correctAnswerTextView || !okButton || !heartContainer) { console.error("One or more dialog elements are missing!"); return; } // Set text for question and correct answer questionTextView.textContent = question; correctAnswerTextView.textContent = `The correct answer is: ${correctAnswer}`; // Update hearts based on lives left const hearts = heartContainer.querySelectorAll(".heart"); hearts.forEach((heart, index) => { heart.src = index < livesLeft ? "assets/images/ic_heart_red.svg" : "assets/images/ic_heart_grey.svg"; }); // Show dialog dialogView.style.display = "block"; okButton.onclick = () => { dialogView.style.display = "none"; // Only retry if the game is not over if (!isGameOver) { onRetry(); // Retry the quiz } }; } let isGameOver = false; // Tracks whether the game is over function updateLevelIfNeeded() { if (correctCount % 5 === 0) { currentUserLevel++; // Increase level by 1 updateLevelDisplay(); // Update the level on the UI updateStartingTimeBasedOnLevel(); // Adjust time based on the level } } function updateStartingTimeBasedOnLevel() { let adjustedTimeInSeconds; if (typeof currentUserLevel !== 'number' || currentUserLevel <= 0) { console.error('Invalid user level'); return; } if (currentUserLevel === 1) { adjustedTimeInSeconds = initialTime; console.log(`Base Time at Level 1: ${adjustedTimeInSeconds} seconds`); } else { adjustedTimeInSeconds = Math.round(initialTime * Math.pow(0.9, currentUserLevel - 1)); console.log(`Base Time at Level ${currentUserLevel}: ${initialTime} seconds`); console.log(`Adjusted Time at Level ${currentUserLevel}: ${adjustedTimeInSeconds} seconds`); } return adjustedTimeInSeconds; // Return time in milliseconds } function updateLevelDisplay() { // Define the scale-up animation const scaleUp = new KeyframeEffect(levelTextView, [ { transform: 'scale(1)' }, { transform: 'scale(1.5)' } ], { duration: 200 }); // Define the scale-down animation const scaleDown = new KeyframeEffect(levelTextView, [ { transform: 'scale(1.5)' }, { transform: 'scale(1)' } ], { duration: 200 }); // Create an animator (Animation object) const animator = new Animation(scaleUp); // Add the onfinish event handler to the animator animator.onfinish = () => { // After the scale-up animation finishes, play the scale-down animation const scaleDownAnimation = new Animation(scaleDown); scaleDownAnimation.play(); }; // Play the animation animator.play(); // Update the level text content levelTextView.textContent = `Level: ${currentUserLevel}`; } let currentCorrectAnswer = null; // Declare the variable globally let mediaPlayer = new Audio(); // Assuming this is where your audio is being initialized function resetMediaPlayer() { if (mediaPlayer) { mediaPlayer.pause(); // Stop playback mediaPlayer.currentTime = 0; // Reset to the start // mediaPlayer = null; // You don't need to nullify it unless you plan to fully discard it. } } function startQuizLevel1() { resetMediaPlayer(); const nonNullKana = getSelectedKana(); if (nonNullKana.length === 0) { showToast("No characters to quiz"); return; } const selectedKanaObject = nonNullKana[Math.floor(Math.random() * nonNullKana.length)]; correctLetter = selectedKanaObject.character; letterDisplay.textContent = correctLetter; const audioFile = getAudioFileForKana(selectedKanaObject); console.log(`Audio File: ${audioFile}`); generateChoicesForKana(nonNullKana.map(kana => kana.character), correctLetter); // Call the merged function setupAudioIconClickListener(audioFile); prepareQuizForNextKana(correctLetter); } function saveStartTime() { const sharedPref = JSON.parse(localStorage.getItem("Settings")) || { starting_time: initialTime }; sharedPref.adjusted_starting_time = initialTime; localStorage.setItem("Settings", JSON.stringify(sharedPref)); localStorage.setItem("saved_start_time", initialTime); } function startQuizLevel2() { resetMediaPlayer(); const selectedWords = getSelectedWords(); if (selectedWords.length === 0) { showToast("No words to quiz"); return; } const correctWord = selectRandomWord(selectedWords); displayQuizWord(correctWord); setupAudioPlayback(correctWord); generateAndDisplayChoices(selectedWords, correctWord); prepareQuizForNextWord(correctWord); } function displayQuizWord(word) { letterDisplay.textContent = word; correctLetter = word[0]; // First character const translation = getEnglishTranslation(word) || "Translation not available"; englishTranslationTextView.textContent = translation; } function getEnglishTranslation(correctWord) { if (getSelectedScript() === "Hiragana") { return hiraganaTwoLetterWords[correctWord]?.english; } else { return katakanaTwoLetterWords[correctWord]?.english; } } function getRomajiForWord(word) { if (getSelectedScript() === "Hiragana") { return hiraganaTwoLetterWords[word]?.romaji; } else { return katakanaTwoLetterWords[word]?.romaji; } } // Function to get selected script function getSelectedScript() { return selectedScript; } // Function to get selected words function getSelectedWords() { const wordsMap = getSelectedScript() === "Hiragana" ? hiraganaTwoLetterWords : katakanaTwoLetterWords; return Object.keys(wordsMap); } // Function to select a random word function selectRandomWord(words) { return words[Math.floor(Math.random() * words.length)]; } // Function to set up audio playback for the word function setupAudioPlayback(word) { const audioFile = getAudioFileForWord(word); if (audioFile) { setupAudioIconClickListener(audioFile); } else { showToast("No audio file available for this word."); } } function getAudioFileForWord(correctWord) { const romajiWord = getSelectedScript() === "Hiragana" ? hiraganaTwoLetterWords[correctWord]?.romaji : katakanaTwoLetterWords[correctWord]?.romaji; if (romajiWord) { const selectedVoice = getVoicePreference(); // Assuming this function is already defined and works const audioFileName = selectedVoice === "b" ? `${getSelectedScript().toLowerCase()}_${romajiWord}_b.mp3` : `${getSelectedScript().toLowerCase()}_${romajiWord}_c.mp3`; // Return the full path to the audio file return `assets/images/raw/${audioFileName}`; } return null; // Return null if no audio file is found } function startCountdownTimer() { if (countDownTimer) { clearInterval(countDownTimer); } countDownTimer = setInterval(function() { if (timeLeftInMillis > 0) { timeLeftInMillis -= 100; // Decrement by 100ms updateTimerText(); } else { clearInterval(countDownTimer); handleTimerFinish(); } }, 100); // Adjust the interval time as needed } function handleTimerFinish() { const timerTextView = document.getElementById("timer_text"); timerTextView.textContent = "Time's up!"; incorrectCount++; const incorrectCounterTextView = document.getElementById("incorrect_counter_text"); incorrectCounterTextView.textContent = `Incorrect: ${incorrectCount}`; const question = currentLevel === "Level 1: Single Sounds" ? correctLetter || "Unknown" : letterDisplay.textContent; const correctAnswer = getCorrectAnswer(); handleIncorrectAnswer(question, correctAnswer, () => { if (currentLevel === "Level 1: Single Sounds") { startQuizLevel1(); } else { startQuizLevel2(); } }); } function stopCountdownTimer() { clearInterval(countDownTimer); // Stop the timer when needed } function prepareQuizForNextKana(correctLetter) { startButton.textContent = "Reset Game"; stopCountdownTimer(); // Stop the timer before starting a new one setupQuizTimer(); // Start a new countdown timer currentCorrectAnswer = getRomaji(correctLetter); // Update the current correct answer } function prepareQuizForNextWord(correctWord) { startButton.textContent = "Reset Game"; stopCountdownTimer(); // Stop the timer before starting a new one setupQuizTimer(); // Start a new countdown timer currentCorrectAnswer = getRomaji(correctWord); // Update the current correct answer } // Function to get selected script function getSelectedScript() { return selectedScript; } // Function to get selected words function getSelectedWords() { const wordsMap = getSelectedScript() === "Hiragana" ? hiraganaTwoLetterWords : katakanaTwoLetterWords; return Object.keys(wordsMap); } // Function to select a random word function selectRandomWord(words) { return words[Math.floor(Math.random() * words.length)]; } function generateChoices(availableWords, correctWord, correctAnswer) { const wrongAnswers = availableWords.filter(word => word !== correctWord) .map(word => getRomajiForWord(word)) .filter(answer => answer !== null) .sort(() => Math.random() - 0.5) // Shuffle .slice(0, 3); // Take only 3 wrong answers return [...wrongAnswers, correctAnswer].sort(() => Math.random() - 0.5); // Shuffle choices } function getSelectedKana() { const selectedKana = []; selectedRows.forEach(row => { if (row.type === "row") { selectedKana.push(...row.characters.map(character => ({ character, audio: row.audio // or other relevant data }))); } }); return selectedKana; } function showToast(message) { // Basic toast implementation (for web) const toast = document.createElement('div'); toast.textContent = message; toast.style.position = 'fixed'; toast.style.bottom = '20px'; toast.style.left = '50%'; toast.style.transform = 'translateX(-50%)'; toast.style.padding = '10px 20px'; toast.style.backgroundColor = '#333'; toast.style.color = '#fff'; toast.style.borderRadius = '5px'; toast.style.fontSize = '16px'; toast.style.zIndex = '1000'; toast.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.2)'; document.body.appendChild(toast); // Remove the toast after 3 seconds setTimeout(() => { toast.remove(); }, 3000); } function getAudioFileForKana(selectedKanaObject) { return isVoiceFemale() ? selectedKanaObject.audioFileDefault : selectedKanaObject.audioFileDefault.replace("_b", "_c"); } function isVoiceFemale() { return getVoicePreference() === "b"; } function getVoicePreference() { // Retrieve the voice preference from localStorage (or use a default value) const preference = localStorage.getItem("audio_voice"); return preference || "b"; // Default to "b" if not set } function setupAudioIconClickListener(audioFile) { const audioIcon = document.getElementById("ic_audio"); audioIcon.addEventListener('click', () => { if (audioFile) { playAudio(audioFile); } else { showToast("No audio file available for this character."); } }); } function playAudio(audioFile) { resetMediaPlayer(); const audioUri = `android.resource://${packageName}/raw/${audioFile}`; mediaPlayer = new MediaPlayer(); mediaPlayer.setDataSource(audioUri); mediaPlayer.prepare(); mediaPlayer.start(); mediaPlayer.setOnCompletionListener(() => { mediaPlayer.release(); mediaPlayer = null; }); } function playSuccessSound() { const audio = new Audio('success_sound.mp3'); audio.volume = 0.1; audio.addEventListener('canplaythrough', () => { audio.play().catch(error => { console.error('Playback failed:', error); }); }); audio.addEventListener('ended', () => { // Audio has finished playing console.log('Success sound finished playing.'); }); } let audio; window.addEventListener('load', () => { audio = new Audio('audio/success_sound.mp3'); // Updated path audio.volume = 0.1; audio.load(); audio.addEventListener('error', (e) => { console.error('Error loading audio:', e); }); }); function playSuccessSound() { if (audio) { audio.currentTime = 0; // Reset to start audio.play().catch(error => console.error('Playback failed:', error)); } } function getCorrectAnswerFromKana(character) { // Check if character exists in Hiragana object and return its romaji const hiraganaAnswer = hiragana[character]?.romaji; // If not found in Hiragana, check in Katakana const katakanaAnswer = katakana[character]?.romaji; // Return the first valid answer found, defaulting to "Unknown" if none found return hiraganaAnswer || katakanaAnswer || "Unknown"; } function saveLevel(key, level) { const sharedPrefs = getSharedPreferences("Settings", MODE_PRIVATE); const editor = sharedPrefs.edit(); editor.putInt(key, level); const isSuccess = editor.commit(); if (isSuccess) { console.log(`${key} saved: ${level}`); } else { console.error(`Failed to save ${key}`); } } function handleGameOver() { isGameOver = true; // Mark the game as over const highestLevel = parseInt(localStorage.getItem("highest_level")) || 0; localStorage.setItem("current_level", currentUserLevel); if (currentUserLevel > highestLevel) { localStorage.setItem("highest_level", currentUserLevel); } // Set the dialog message dynamically const messageTextView = document.getElementById("dialog_message"); messageTextView.innerHTML = `

Final Level: ${currentUserLevel}

Previous Highest Level: ${highestLevel}

${currentUserLevel > highestLevel ? '

Congratulations! You\'ve set a new record!

' : '

Keep going! Aim for a new record next time!

'} `; // Show the Bootstrap modal const gameOverDialog = new bootstrap.Modal(document.getElementById("game-over-dialog")); gameOverDialog.show(); // Handle Exit Button const exitButton = document.getElementById("btn_exit"); exitButton.addEventListener('click', () => { window.location.reload(); // Reload the page to restart the game }); } function logEvent(eventName) { const params = { }; } function getColor(colorName) { const colors = { "teal_200": "#80CBC4", // Example teal color "white": "#FFFFFF" }; return colors[colorName] || "#000000"; // Default to black if color is not found }