bpmf-tw / index.html
MarcusCorvus's picture
Add 3 files
eec29a4 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Zhuyin Bopomofo Learning App</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@400;700&display=swap');
body {
font-family: 'Noto Sans TC', sans-serif;
background-color: #f8f9fa;
}
.zhuyin-char {
font-size: 4rem;
line-height: 1;
color: #3b82f6;
}
.lesson-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
}
.character-card {
transition: all 0.3s ease;
}
.character-card:hover {
transform: scale(1.05);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.progress-bar {
height: 8px;
border-radius: 4px;
background-color: #e5e7eb;
}
.progress-fill {
height: 100%;
border-radius: 4px;
background-color: #10b981;
transition: width 0.3s ease;
}
.correct-answer {
animation: correctPulse 0.5s;
}
.wrong-answer {
animation: wrongShake 0.5s;
}
@keyframes correctPulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
@keyframes wrongShake {
0%, 100% { transform: translateX(0); }
20%, 60% { transform: translateX(-5px); }
40%, 80% { transform: translateX(5px); }
}
.tab-active {
border-bottom: 3px solid #3b82f6;
color: #3b82f6;
font-weight: bold;
}
</style>
</head>
<body class="min-h-screen">
<div class="container mx-auto px-4 py-8 max-w-6xl">
<!-- Header -->
<header class="text-center mb-12">
<h1 class="text-4xl font-bold text-gray-800 mb-2">Zhuyin Bopomofo Learning App</h1>
<p class="text-lg text-gray-600">Master the phonetic system for learning Mandarin Chinese</p>
</header>
<!-- Main Content -->
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
<!-- Tabs Navigation -->
<div class="flex border-b border-gray-200">
<button id="lessons-tab" class="flex-1 py-4 px-6 text-center font-medium text-gray-700 hover:text-blue-500 focus:outline-none tab-active">
<i class="fas fa-book-open mr-2"></i> Lessons
</button>
<button id="practice-tab" class="flex-1 py-4 px-6 text-center font-medium text-gray-700 hover:text-blue-500 focus:outline-none">
<i class="fas fa-pencil-alt mr-2"></i> Practice
</button>
<button id="progress-tab" class="flex-1 py-4 px-6 text-center font-medium text-gray-700 hover:text-blue-500 focus:outline-none">
<i class="fas fa-chart-line mr-2"></i> Progress
</button>
</div>
<!-- Tab Content -->
<div class="p-6">
<!-- Lessons Tab Content -->
<div id="lessons-content">
<h2 class="text-2xl font-bold text-gray-800 mb-6">Zhuyin Lessons</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Lesson 1: Initials -->
<div class="lesson-card bg-white border border-gray-200 rounded-lg p-6 shadow-sm transition duration-300 cursor-pointer" onclick="loadLesson('initials')">
<div class="flex items-center mb-4">
<div class="bg-blue-100 p-3 rounded-full mr-4">
<i class="fas fa-volume-up text-blue-500 text-xl"></i>
</div>
<h3 class="text-xl font-semibold text-gray-800">Initial Consonants</h3>
</div>
<p class="text-gray-600 mb-4">Learn the 21 initial consonant sounds in Zhuyin.</p>
<div class="flex justify-between items-center">
<span class="text-sm text-gray-500">21 characters</span>
<span class="text-sm font-medium text-blue-500">Start Lesson →</span>
</div>
</div>
<!-- Lesson 2: Medials -->
<div class="lesson-card bg-white border border-gray-200 rounded-lg p-6 shadow-sm transition duration-300 cursor-pointer" onclick="loadLesson('medials')">
<div class="flex items-center mb-4">
<div class="bg-green-100 p-3 rounded-full mr-4">
<i class="fas fa-comment text-green-500 text-xl"></i>
</div>
<h3 class="text-xl font-semibold text-gray-800">Medial Vowels</h3>
</div>
<p class="text-gray-600 mb-4">Master the 3 medial vowel sounds that combine with initials.</p>
<div class="flex justify-between items-center">
<span class="text-sm text-gray-500">3 characters</span>
<span class="text-sm font-medium text-blue-500">Start Lesson →</span>
</div>
</div>
<!-- Lesson 3: Finals -->
<div class="lesson-card bg-white border border-gray-200 rounded-lg p-6 shadow-sm transition duration-300 cursor-pointer" onclick="loadLesson('finals')">
<div class="flex items-center mb-4">
<div class="bg-purple-100 p-3 rounded-full mr-4">
<i class="fas fa-spell-check text-purple-500 text-xl"></i>
</div>
<h3 class="text-xl font-semibold text-gray-800">Final Sounds</h3>
</div>
<p class="text-gray-600 mb-4">Learn the 13 final sounds that complete Zhuyin syllables.</p>
<div class="flex justify-between items-center">
<span class="text-sm text-gray-500">13 characters</span>
<span class="text-sm font-medium text-blue-500">Start Lesson →</span>
</div>
</div>
<!-- Lesson 4: Tones -->
<div class="lesson-card bg-white border border-gray-200 rounded-lg p-6 shadow-sm transition duration-300 cursor-pointer" onclick="loadLesson('tones')">
<div class="flex items-center mb-4">
<div class="bg-yellow-100 p-3 rounded-full mr-4">
<i class="fas fa-music text-yellow-500 text-xl"></i>
</div>
<h3 class="text-xl font-semibold text-gray-800">Tones</h3>
</div>
<p class="text-gray-600 mb-4">Understand the 4 tones and neutral tone in Mandarin.</p>
<div class="flex justify-between items-center">
<span class="text-sm text-gray-500">5 tone marks</span>
<span class="text-sm font-medium text-blue-500">Start Lesson →</span>
</div>
</div>
<!-- Lesson 5: Combined -->
<div class="lesson-card bg-white border border-gray-200 rounded-lg p-6 shadow-sm transition duration-300 cursor-pointer" onclick="loadLesson('combined')">
<div class="flex items-center mb-4">
<div class="bg-red-100 p-3 rounded-full mr-4">
<i class="fas fa-random text-red-500 text-xl"></i>
</div>
<h3 class="text-xl font-semibold text-gray-800">Combined Sounds</h3>
</div>
<p class="text-gray-600 mb-4">Practice combining initials, medials and finals.</p>
<div class="flex justify-between items-center">
<span class="text-sm text-gray-500">All characters</span>
<span class="text-sm font-medium text-blue-500">Start Lesson →</span>
</div>
</div>
</div>
<!-- Lesson Detail View (hidden by default) -->
<div id="lesson-detail" class="hidden mt-8">
<div class="flex justify-between items-center mb-6">
<h2 id="lesson-title" class="text-2xl font-bold text-gray-800"></h2>
<button onclick="backToLessons()" class="flex items-center text-blue-500 hover:text-blue-700">
<i class="fas fa-arrow-left mr-2"></i> Back to Lessons
</button>
</div>
<div id="lesson-description" class="mb-6 text-gray-700"></div>
<div id="lesson-characters" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4"></div>
</div>
</div>
<!-- Practice Tab Content -->
<div id="practice-content" class="hidden">
<h2 class="text-2xl font-bold text-gray-800 mb-6">Practice Zhuyin Characters</h2>
<div class="bg-blue-50 rounded-lg p-6 mb-8">
<div class="flex flex-col md:flex-row items-center">
<div class="md:w-1/2 mb-4 md:mb-0">
<h3 class="text-xl font-semibold text-gray-800 mb-2">Test Your Knowledge</h3>
<p class="text-gray-600">Listen to the pronunciation and select the correct Zhuyin character.</p>
</div>
<div class="md:w-1/2 flex justify-end">
<button id="start-practice" class="bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-6 rounded-lg transition duration-300 flex items-center">
<i class="fas fa-play mr-2"></i> Start Practice
</button>
</div>
</div>
</div>
<div id="practice-session" class="hidden">
<div class="bg-white border border-gray-200 rounded-lg p-6 shadow-sm mb-6">
<div class="flex justify-between items-center mb-4">
<span id="progress-text" class="text-sm font-medium text-gray-500">Question 1 of 10</span>
<div class="w-32 bg-gray-200 rounded-full h-2.5">
<div id="progress-bar" class="bg-blue-600 h-2.5 rounded-full" style="width: 10%"></div>
</div>
</div>
<div class="text-center py-8">
<button id="play-sound" class="bg-green-500 hover:bg-green-600 text-white font-medium py-3 px-8 rounded-full transition duration-300 flex items-center mx-auto">
<i class="fas fa-volume-up mr-2"></i> Play Sound
</button>
<p class="text-gray-500 mt-4">Listen to the pronunciation and select the correct character below.</p>
</div>
<div id="answer-options" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-4 mt-6"></div>
</div>
<div id="feedback" class="hidden">
<div id="correct-feedback" class="bg-green-50 border border-green-200 rounded-lg p-4 mb-4 hidden">
<div class="flex items-center">
<div class="bg-green-100 p-2 rounded-full mr-3">
<i class="fas fa-check text-green-500"></i>
</div>
<div>
<h4 class="font-medium text-green-800">Correct!</h4>
<p class="text-green-600 text-sm">Good job! That was the right answer.</p>
</div>
</div>
</div>
<div id="wrong-feedback" class="bg-red-50 border border-red-200 rounded-lg p-4 mb-4 hidden">
<div class="flex items-center">
<div class="bg-red-100 p-2 rounded-full mr-3">
<i class="fas fa-times text-red-500"></i>
</div>
<div>
<h4 class="font-medium text-red-800">Incorrect</h4>
<p id="correct-answer-text" class="text-red-600 text-sm">The correct answer was: ㄅ (b)</p>
</div>
</div>
</div>
<button id="next-question" class="w-full bg-blue-500 hover:bg-blue-600 text-white font-medium py-3 px-6 rounded-lg transition duration-300">
Next Question <i class="fas fa-arrow-right ml-2"></i>
</button>
</div>
</div>
<div id="practice-results" class="hidden">
<div class="bg-white border border-gray-200 rounded-lg p-6 shadow-sm">
<div class="text-center mb-6">
<div class="w-24 h-24 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
<i class="fas fa-trophy text-green-500 text-4xl"></i>
</div>
<h3 class="text-2xl font-bold text-gray-800 mb-2">Practice Complete!</h3>
<p id="results-score" class="text-xl text-gray-600">You scored 8 out of 10</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
<div class="bg-blue-50 rounded-lg p-4">
<h4 class="font-medium text-gray-800 mb-2">Correct Answers</h4>
<div class="flex items-center">
<div class="text-3xl font-bold text-blue-500 mr-3" id="correct-count">8</div>
<div class="progress-bar w-full">
<div class="progress-fill" style="width: 80%"></div>
</div>
</div>
</div>
<div class="bg-red-50 rounded-lg p-4">
<h4 class="font-medium text-gray-800 mb-2">Incorrect Answers</h4>
<div class="flex items-center">
<div class="text-3xl font-bold text-red-500 mr-3" id="incorrect-count">2</div>
<div class="progress-bar w-full">
<div class="progress-fill bg-red-500" style="width: 20%"></div>
</div>
</div>
</div>
</div>
<div class="flex flex-col sm:flex-row gap-4">
<button onclick="restartPractice()" class="flex-1 bg-blue-500 hover:bg-blue-600 text-white font-medium py-3 px-6 rounded-lg transition duration-300">
<i class="fas fa-redo mr-2"></i> Try Again
</button>
<button onclick="backToLessons()" class="flex-1 bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-3 px-6 rounded-lg transition duration-300">
<i class="fas fa-book-open mr-2"></i> Back to Lessons
</button>
</div>
</div>
</div>
</div>
<!-- Progress Tab Content -->
<div id="progress-content" class="hidden">
<h2 class="text-2xl font-bold text-gray-800 mb-6">Your Learning Progress</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
<div class="bg-white border border-gray-200 rounded-lg p-6 shadow-sm">
<h3 class="text-lg font-semibold text-gray-800 mb-4">Overall Progress</h3>
<div class="flex items-center mb-2">
<div class="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center mr-4">
<i class="fas fa-chart-pie text-blue-500"></i>
</div>
<div class="flex-1">
<div class="flex justify-between mb-1">
<span class="text-sm font-medium text-gray-600">Characters Learned</span>
<span id="learned-percent" class="text-sm font-medium text-gray-600">25%</span>
</div>
<div class="progress-bar">
<div id="overall-progress" class="progress-fill" style="width: 25%"></div>
</div>
</div>
</div>
</div>
<div class="bg-white border border-gray-200 rounded-lg p-6 shadow-sm">
<h3 class="text-lg font-semibold text-gray-800 mb-4">Practice Accuracy</h3>
<div class="flex items-center mb-2">
<div class="w-12 h-12 bg-green-100 rounded-full flex items-center justify-center mr-4">
<i class="fas fa-check-circle text-green-500"></i>
</div>
<div class="flex-1">
<div class="flex justify-between mb-1">
<span class="text-sm font-medium text-gray-600">Correct Answers</span>
<span id="accuracy-percent" class="text-sm font-medium text-gray-600">75%</span>
</div>
<div class="progress-bar">
<div id="accuracy-progress" class="progress-fill bg-green-500" style="width: 75%"></div>
</div>
</div>
</div>
</div>
</div>
<div class="bg-white border border-gray-200 rounded-lg p-6 shadow-sm">
<h3 class="text-lg font-semibold text-gray-800 mb-4">Lesson Completion</h3>
<div class="space-y-4">
<!-- Initials Progress -->
<div>
<div class="flex justify-between mb-1">
<span class="font-medium text-gray-700">Initial Consonants</span>
<span class="text-sm font-medium text-gray-600">8/21</span>
</div>
<div class="progress-bar">
<div class="progress-fill" style="width: 38%"></div>
</div>
</div>
<!-- Medials Progress -->
<div>
<div class="flex justify-between mb-1">
<span class="font-medium text-gray-700">Medial Vowels</span>
<span class="text-sm font-medium text-gray-600">1/3</span>
</div>
<div class="progress-bar">
<div class="progress-fill" style="width: 33%"></div>
</div>
</div>
<!-- Finals Progress -->
<div>
<div class="flex justify-between mb-1">
<span class="font-medium text-gray-700">Final Sounds</span>
<span class="text-sm font-medium text-gray-600">4/13</span>
</div>
<div class="progress-bar">
<div class="progress-fill" style="width: 31%"></div>
</div>
</div>
<!-- Tones Progress -->
<div>
<div class="flex justify-between mb-1">
<span class="font-medium text-gray-700">Tones</span>
<span class="text-sm font-medium text-gray-600">2/5</span>
</div>
<div class="progress-bar">
<div class="progress-fill" style="width: 40%"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// Zhuyin character data
const zhuyinData = {
initials: {
title: "Initial Consonants",
description: "Initial consonants are the starting sounds of Zhuyin syllables. There are 21 initial consonants in total.",
characters: [
{ char: "ㄅ", pinyin: "b", example: "八 (bā)" },
{ char: "ㄆ", pinyin: "p", example: "怕 (pà)" },
{ char: "ㄇ", pinyin: "m", example: "媽 (mā)" },
{ char: "ㄈ", pinyin: "f", example: "發 (fā)" },
{ char: "ㄉ", pinyin: "d", example: "大 (dà)" },
{ char: "ㄊ", pinyin: "t", example: "他 (tā)" },
{ char: "ㄋ", pinyin: "n", example: "你 (nǐ)" },
{ char: "ㄌ", pinyin: "l", example: "來 (lái)" },
{ char: "ㄍ", pinyin: "g", example: "哥 (gē)" },
{ char: "ㄎ", pinyin: "k", example: "看 (kàn)" },
{ char: "ㄏ", pinyin: "h", example: "好 (hǎo)" },
{ char: "ㄐ", pinyin: "j", example: "家 (jiā)" },
{ char: "ㄑ", pinyin: "q", example: "七 (qī)" },
{ char: "ㄒ", pinyin: "x", example: "西 (xī)" },
{ char: "ㄓ", pinyin: "zh", example: "這 (zhè)" },
{ char: "ㄔ", pinyin: "ch", example: "吃 (chī)" },
{ char: "ㄕ", pinyin: "sh", example: "是 (shì)" },
{ char: "ㄖ", pinyin: "r", example: "日 (rì)" },
{ char: "ㄗ", pinyin: "z", example: "在 (zài)" },
{ char: "ㄘ", pinyin: "c", example: "菜 (cài)" },
{ char: "ㄙ", pinyin: "s", example: "三 (sān)" }
]
},
medials: {
title: "Medial Vowels",
description: "Medial vowels are the middle sounds that combine with initial consonants. There are 3 medial vowels.",
characters: [
{ char: "ㄧ", pinyin: "i/y", example: "一 (yī)" },
{ char: "ㄨ", pinyin: "u/w", example: "五 (wǔ)" },
{ char: "ㄩ", pinyin: "ü/yu", example: "魚 (yú)" }
]
},
finals: {
title: "Final Sounds",
description: "Final sounds complete the Zhuyin syllables. There are 13 final sounds.",
characters: [
{ char: "ㄚ", pinyin: "a", example: "阿 (ā)" },
{ char: "ㄛ", pinyin: "o", example: "喔 (ō)" },
{ char: "ㄜ", pinyin: "e", example: "餓 (è)" },
{ char: "ㄝ", pinyin: "ê", example: "誒 (ê̄)" },
{ char: "ㄞ", pinyin: "ai", example: "愛 (ài)" },
{ char: "ㄟ", pinyin: "ei", example: "飛 (fēi)" },
{ char: "ㄠ", pinyin: "ao", example: "好 (hǎo)" },
{ char: "ㄡ", pinyin: "ou", example: "歐 (ōu)" },
{ char: "ㄢ", pinyin: "an", example: "安 (ān)" },
{ char: "ㄣ", pinyin: "en", example: "恩 (ēn)" },
{ char: "ㄤ", pinyin: "ang", example: "昂 (áng)" },
{ char: "ㄥ", pinyin: "eng", example: "冷 (lěng)" },
{ char: "ㄦ", pinyin: "er", example: "兒 (ér)" }
]
},
tones: {
title: "Tones",
description: "Mandarin Chinese has 4 main tones and 1 neutral tone. These are marked with tone marks in Zhuyin.",
characters: [
{ char: "ˉ", pinyin: "1st tone", example: "高平調 (high level)" },
{ char: "ˊ", pinyin: "2nd tone", example: "上升調 (rising)" },
{ char: "ˇ", pinyin: "3rd tone", example: "降升調 (falling-rising)" },
{ char: "ˋ", pinyin: "4th tone", example: "下降調 (falling)" },
{ char: "˙", pinyin: "neutral", example: "輕聲 (light)" }
]
},
combined: {
title: "Combined Sounds",
description: "Practice combining initials, medials and finals to form complete syllables.",
characters: [
{ char: "ㄅㄚ", pinyin: "ba", example: "八 (bā)" },
{ char: "ㄆㄠ", pinyin: "pao", example: "跑 (pǎo)" },
{ char: "ㄇㄧ", pinyin: "mi", example: "米 (mǐ)" },
{ char: "ㄈㄨ", pinyin: "fu", example: "服 (fú)" },
{ char: "ㄉㄧㄠ", pinyin: "diao", example: "掉 (diào)" },
{ char: "ㄊㄧㄢ", pinyin: "tian", example: "天 (tiān)" },
{ char: "ㄋㄩ", pinyin: "nü", example: "女 (nǚ)" },
{ char: "ㄌㄧㄤ", pinyin: "liang", example: "兩 (liǎng)" },
{ char: "ㄍㄨㄛ", pinyin: "guo", example: "國 (guó)" },
{ char: "ㄎㄨㄞ", pinyin: "kuai", example: "快 (kuài)" },
{ char: "ㄏㄜ", pinyin: "he", example: "和 (hé)" },
{ char: "ㄐㄧㄝ", pinyin: "jie", example: "姐 (jiě)" },
{ char: "ㄑㄩㄥ", pinyin: "qiong", example: "窮 (qióng)" },
{ char: "ㄒㄩㄝ", pinyin: "xue", example: "學 (xué)" },
{ char: "ㄓㄨㄤ", pinyin: "zhuang", example: "裝 (zhuāng)" },
{ char: "ㄔㄨㄣ", pinyin: "chun", example: "春 (chūn)" },
{ char: "ㄕㄨㄟ", pinyin: "shui", example: "水 (shuǐ)" },
{ char: "ㄖㄣ", pinyin: "ren", example: "人 (rén)" },
{ char: "ㄗㄞ", pinyin: "zai", example: "在 (zài)" },
{ char: "ㄘㄨㄣ", pinyin: "cun", example: "村 (cūn)" },
{ char: "ㄙㄨㄥ", pinyin: "song", example: "送 (sòng)" }
]
}
};
// Practice questions
const practiceQuestions = [
{ char: "ㄅ", pinyin: "b", audio: "b" },
{ char: "ㄆ", pinyin: "p", audio: "p" },
{ char: "ㄇ", pinyin: "m", audio: "m" },
{ char: "ㄈ", pinyin: "f", audio: "f" },
{ char: "ㄉ", pinyin: "d", audio: "d" },
{ char: "ㄊ", pinyin: "t", audio: "t" },
{ char: "ㄋ", pinyin: "n", audio: "n" },
{ char: "ㄌ", pinyin: "l", audio: "l" },
{ char: "ㄍ", pinyin: "g", audio: "g" },
{ char: "ㄎ", pinyin: "k", audio: "k" },
{ char: "ㄏ", pinyin: "h", audio: "h" },
{ char: "ㄐ", pinyin: "j", audio: "j" },
{ char: "ㄑ", pinyin: "q", audio: "q" },
{ char: "ㄒ", pinyin: "x", audio: "x" },
{ char: "ㄓ", pinyin: "zh", audio: "zh" },
{ char: "ㄔ", pinyin: "ch", audio: "ch" },
{ char: "ㄕ", pinyin: "sh", audio: "sh" },
{ char: "ㄖ", pinyin: "r", audio: "r" },
{ char: "ㄗ", pinyin: "z", audio: "z" },
{ char: "ㄘ", pinyin: "c", audio: "c" },
{ char: "ㄙ", pinyin: "s", audio: "s" },
{ char: "ㄧ", pinyin: "i/y", audio: "i" },
{ char: "ㄨ", pinyin: "u/w", audio: "u" },
{ char: "ㄩ", pinyin: "ü/yu", audio: "ü" },
{ char: "ㄚ", pinyin: "a", audio: "a" },
{ char: "ㄛ", pinyin: "o", audio: "o" },
{ char: "ㄜ", pinyin: "e", audio: "e" },
{ char: "ㄝ", pinyin: "ê", audio: "ê" },
{ char: "ㄞ", pinyin: "ai", audio: "ai" },
{ char: "ㄟ", pinyin: "ei", audio: "ei" },
{ char: "ㄠ", pinyin: "ao", audio: "ao" },
{ char: "ㄡ", pinyin: "ou", audio: "ou" },
{ char: "ㄢ", pinyin: "an", audio: "an" },
{ char: "ㄣ", pinyin: "en", audio: "en" },
{ char: "ㄤ", pinyin: "ang", audio: "ang" },
{ char: "ㄥ", pinyin: "eng", audio: "eng" },
{ char: "ㄦ", pinyin: "er", audio: "er" }
];
// Tab switching functionality
document.getElementById('lessons-tab').addEventListener('click', () => {
switchTab('lessons');
});
document.getElementById('practice-tab').addEventListener('click', () => {
switchTab('practice');
});
document.getElementById('progress-tab').addEventListener('click', () => {
switchTab('progress');
});
function switchTab(tabName) {
// Hide all tab contents
document.getElementById('lessons-content').classList.add('hidden');
document.getElementById('practice-content').classList.add('hidden');
document.getElementById('progress-content').classList.add('hidden');
// Remove active class from all tabs
document.getElementById('lessons-tab').classList.remove('tab-active');
document.getElementById('practice-tab').classList.remove('tab-active');
document.getElementById('progress-tab').classList.remove('tab-active');
// Show selected tab content and add active class
document.getElementById(`${tabName}-content`).classList.remove('hidden');
document.getElementById(`${tabName}-tab`).classList.add('tab-active');
}
// Lesson loading functionality
function loadLesson(lessonType) {
const lesson = zhuyinData[lessonType];
// Hide lessons list and show lesson detail
document.getElementById('lessons-content').querySelector('.grid').classList.add('hidden');
document.getElementById('lesson-detail').classList.remove('hidden');
// Set lesson title and description
document.getElementById('lesson-title').textContent = lesson.title;
document.getElementById('lesson-description').textContent = lesson.description;
// Clear previous characters
const charactersContainer = document.getElementById('lesson-characters');
charactersContainer.innerHTML = '';
// Add characters to the lesson
lesson.characters.forEach(charData => {
const charElement = document.createElement('div');
charElement.className = 'character-card bg-white border border-gray-200 rounded-lg p-4 text-center cursor-pointer hover:shadow-md';
charElement.innerHTML = `
<div class="zhuyin-char mb-2">${charData.char}</div>
<div class="text-gray-700 font-medium">${charData.pinyin}</div>
<div class="text-sm text-gray-500 mt-1">${charData.example}</div>
`;
charElement.addEventListener('click', () => {
// In a real app, this would play the sound
console.log(`Playing sound for ${charData.char}`);
});
charactersContainer.appendChild(charElement);
});
}
function backToLessons() {
// Show lessons list and hide lesson detail
document.getElementById('lessons-content').querySelector('.grid').classList.remove('hidden');
document.getElementById('lesson-detail').classList.add('hidden');
}
// Practice session functionality
let currentQuestion = 0;
let correctAnswers = 0;
let questions = [];
let currentCorrectAnswer = '';
document.getElementById('start-practice').addEventListener('click', startPractice);
function startPractice() {
// Hide start button and show practice session
document.getElementById('start-practice').classList.add('hidden');
document.getElementById('practice-session').classList.remove('hidden');
// Reset counters
currentQuestion = 0;
correctAnswers = 0;
// Create a shuffled list of 10 questions
questions = [...practiceQuestions].sort(() => 0.5 - Math.random()).slice(0, 10);
// Load first question
loadQuestion();
}
function loadQuestion() {
// Update progress
document.getElementById('progress-text').textContent = `Question ${currentQuestion + 1} of ${questions.length}`;
document.getElementById('progress-bar').style.width = `${(currentQuestion / questions.length) * 100}%`;
// Get current question
const question = questions[currentQuestion];
currentCorrectAnswer = question.char;
// Clear previous options and feedback
document.getElementById('answer-options').innerHTML = '';
document.getElementById('feedback').classList.add('hidden');
document.getElementById('correct-feedback').classList.add('hidden');
document.getElementById('wrong-feedback').classList.add('hidden');
// Create answer options (current question + 3 random others)
const options = [question];
const otherOptions = practiceQuestions.filter(q => q.char !== question.char);
// Shuffle and pick 3 random options
const shuffledOthers = [...otherOptions].sort(() => 0.5 - Math.random()).slice(0, 3);
options.push(...shuffledOthers);
// Shuffle the options again
const shuffledOptions = options.sort(() => 0.5 - Math.random());
// Create option buttons
shuffledOptions.forEach(option => {
const optionButton = document.createElement('button');
optionButton.className = 'bg-white border border-gray-200 rounded-lg p-4 text-center hover:bg-gray-50 focus:outline-none';
optionButton.innerHTML = `
<div class="zhuyin-char">${option.char}</div>
<div class="text-sm text-gray-500">${option.pinyin}</div>
`;
optionButton.addEventListener('click', () => checkAnswer(option.char));
document.getElementById('answer-options').appendChild(optionButton);
});
// Set up play sound button
document.getElementById('play-sound').addEventListener('click', () => {
// In a real app, this would play the sound for the current question
console.log(`Playing sound for ${question.char}`);
});
}
function checkAnswer(selectedChar) {
// Disable all answer options
const options = document.getElementById('answer-options').querySelectorAll('button');
options.forEach(option => {
option.disabled = true;
if (option.textContent.includes(currentCorrectAnswer)) {
option.classList.add('bg-green-100', 'border-green-300');
}
if (option.textContent.includes(selectedChar) && selectedChar !== currentCorrectAnswer) {
option.classList.add('bg-red-100', 'border-red-300');
}
});
// Show feedback
document.getElementById('feedback').classList.remove('hidden');
if (selectedChar === currentCorrectAnswer) {
correctAnswers++;
document.getElementById('correct-feedback').classList.remove('hidden');
} else {
document.getElementById('correct-answer-text').textContent = `The correct answer was: ${currentCorrectAnswer}`;
document.getElementById('wrong-feedback').classList.remove('hidden');
}
}
document.getElementById('next-question').addEventListener('click', () => {
currentQuestion++;
if (currentQuestion < questions.length) {
loadQuestion();
} else {
// Practice session complete, show results
showResults();
}
});
function showResults() {
document.getElementById('practice-session').classList.add('hidden');
document.getElementById('practice-results').classList.remove('hidden');
document.getElementById('results-score').textContent = `You scored ${correctAnswers} out of ${questions.length}`;
document.getElementById('correct-count').textContent = correctAnswers;
document.getElementById('incorrect-count').textContent = questions.length - correctAnswers;
// Update progress bars
document.querySelector('#practice-results .progress-fill').style.width = `${(correctAnswers / questions.length) * 100}%`;
document.querySelector('#practice-results .bg-red-50 .progress-fill').style.width = `${((questions.length - correctAnswers) / questions.length) * 100}%`;
}
function restartPractice() {
document.getElementById('practice-results').classList.add('hidden');
startPractice();
}
// Initialize the app with lessons tab
switchTab('lessons');
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=MarcusCorvus/bpmf-tw" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>