// Mobile menu toggle document.addEventListener('DOMContentLoaded', function() { const toggle = document.querySelector('.menu-toggle'); const navList = document.querySelector('.nav-list'); if (toggle) { toggle.addEventListener('click', function() { navList.classList.toggle('open'); }); } // Close menu on link click const navLinks = document.querySelectorAll('.nav-list a'); navLinks.forEach(function(link) { link.addEventListener('click', function() { if (navList) navList.classList.remove('open'); }); }); // Highlight active page in nav const currentPage = window.location.pathname.split('/').pop() || 'index.html'; navLinks.forEach(function(link) { const href = link.getAttribute('href'); if (href === currentPage) { link.classList.add('active'); } }); }); // --- Exercises --- function checkExercise(btn) { const exercise = btn.closest('.exercise'); const selected = exercise.querySelector('input[type="radio"]:checked'); const feedback = exercise.querySelector('.feedback'); if (!selected) { feedback.textContent = 'Please select an answer!'; feedback.className = 'feedback wrong'; feedback.style.display = 'block'; return; } const options = exercise.querySelectorAll('.option'); options.forEach(function(opt) { opt.classList.remove('correct', 'wrong'); }); const parent = selected.closest('.option'); const isCorrect = selected.value === 'true'; if (isCorrect) { parent.classList.add('correct'); feedback.textContent = 'Correct! +10 XP'; feedback.className = 'feedback correct'; var exId = exercise.getAttribute('data-ex-id') || exercise.querySelector('h3').textContent.trim(); markExerciseDone(exId); } else { parent.classList.add('wrong'); feedback.textContent = 'Try again!'; feedback.className = 'feedback wrong'; options.forEach(function(opt) { const radio = opt.querySelector('input[type="radio"]'); if (radio && radio.value === 'true') { opt.classList.add('correct'); } }); } feedback.style.display = 'block'; } function checkFill(input) { const exercise = input.closest('.exercise'); const answer = input.getAttribute('data-answer'); const feedback = exercise.querySelector('.feedback'); input.classList.remove('correct', 'wrong'); if (input.value.trim().toLowerCase() === answer.toLowerCase()) { input.classList.add('correct'); feedback.textContent = 'Correct! +10 XP'; feedback.className = 'feedback correct'; var exId = input.closest('.exercise').getAttribute('data-ex-id') || input.closest('.exercise').querySelector('h3').textContent.trim(); markExerciseDone(exId); } else { input.classList.add('wrong'); feedback.textContent = 'Try again! Correct answer: ' + answer; feedback.className = 'feedback wrong'; } feedback.style.display = 'block'; } function checkTranslate(btn) { const exercise = btn.closest('.exercise'); const input = exercise.querySelector('input[type="text"]'); const feedback = exercise.querySelector('.feedback'); const answer = input.getAttribute('data-answer'); input.classList.remove('correct', 'wrong'); if (input.value.trim().toLowerCase() === answer.toLowerCase()) { input.classList.add('correct'); feedback.textContent = 'Correct! +10 XP'; feedback.className = 'feedback correct'; var exId = exercise.getAttribute('data-ex-id') || exercise.querySelector('h3').textContent.trim(); markExerciseDone(exId); } else { input.classList.add('wrong'); feedback.textContent = 'Try again! Correct answer: ' + answer; feedback.className = 'feedback wrong'; } feedback.style.display = 'block'; } function checkMatch(btn) { const exercise = btn.closest('.exercise'); const inputs = exercise.querySelectorAll('input[type="text"]'); const feedback = exercise.querySelector('.feedback'); let allCorrect = true; inputs.forEach(function(input) { const answer = input.getAttribute('data-answer'); input.classList.remove('correct', 'wrong'); if (input.value.trim().toLowerCase() === answer.toLowerCase()) { input.classList.add('correct'); } else { input.classList.add('wrong'); allCorrect = false; } }); if (allCorrect) { feedback.textContent = 'All correct! Great job! +10 XP'; feedback.className = 'feedback correct'; var exId = exercise.getAttribute('data-ex-id') || exercise.querySelector('h3').textContent.trim(); markExerciseDone(exId); } else { feedback.textContent = 'Some answers are wrong. Try again!'; feedback.className = 'feedback wrong'; } feedback.style.display = 'block'; } // --- Tests --- function checkTest() { let score = 0; const total = document.querySelectorAll('.test-question').length; const questions = document.querySelectorAll('.test-question'); questions.forEach(function(q) { const selected = q.querySelector('input[type="radio"]:checked'); if (selected && selected.value === 'true') { score++; } }); markTestDone(score, total); var data = getProgress(); var info = getLevelProgress(data.xp); const resultBox = document.getElementById('test-result'); const scoreDisplay = document.getElementById('score'); scoreDisplay.textContent = score + '/' + total; resultBox.classList.add('show'); var detail = document.getElementById('test-detail'); if (detail) { detail.innerHTML = 'Level: ' + info.current.label + ' | XP: ' + data.xp + ' | Best: ' + data.bestTestScore + '/' + total + ''; } resultBox.scrollIntoView({ behavior: 'smooth' }); } // --- Contact form --- function submitForm(event) { event.preventDefault(); const message = document.getElementById('form-message'); message.classList.add('show'); document.getElementById('contact-form').reset(); setTimeout(function() { message.classList.remove('show'); }, 5000); } // --- Pronunciation --- function speakWord(word, rate) { if (!window.speechSynthesis) { alert('Speech synthesis is not supported in your browser.'); return; } window.speechSynthesis.cancel(); var utterance = new SpeechSynthesisUtterance(word); utterance.lang = 'en-US'; utterance.rate = rate || 0.9; utterance.pitch = 1; window.speechSynthesis.speak(utterance); } function repeatWord(btn) { var word = btn.getAttribute('data-word'); speakWord(word, 0.8); btn.textContent = '🔊 Playing...'; markWordListened(); setTimeout(function() { btn.textContent = '🔊 Listen'; }, 1500); } function speakSlow(btn) { var word = btn.getAttribute('data-word'); speakWord(word, 0.4); btn.textContent = '🐢 Slow...'; setTimeout(function() { btn.innerHTML = '🐢 Slow'; }, 2000); } function checkDictation(input) { var exercise = input.closest('.exercise'); var answer = input.getAttribute('data-answer'); var feedback = exercise.querySelector('.feedback'); input.classList.remove('correct', 'wrong'); if (input.value.trim().toLowerCase() === answer.toLowerCase()) { input.classList.add('correct'); feedback.textContent = 'Correct! Great listening!'; feedback.className = 'feedback correct'; } else { input.classList.add('wrong'); feedback.textContent = 'Not quite. The correct word is: ' + answer; feedback.className = 'feedback wrong'; } feedback.style.display = 'block'; } function speakPhrase(btn) { var phrase = btn.getAttribute('data-phrase'); speakWord(phrase, 0.8); btn.textContent = '🔊 Playing...'; setTimeout(function() { btn.innerHTML = '🔊 Hear Phrase'; }, 1500); } // --- Progress Tracking --- var PROGRESS_KEY = 'english_easy_progress'; var LEVELS = [ { min: 0, label: 'A0 Beginner', desc: 'You are just starting your English journey!' }, { min: 50, label: 'A1 Starter', desc: 'You know basic words and phrases.' }, { min: 150, label: 'A2 Elementary', desc: 'You can understand simple sentences.' }, { min: 300, label: 'B1 Intermediate', desc: 'You can talk about familiar topics.' }, { min: 500, label: 'B2 Upper Intermediate', desc: 'You can communicate fluently.' }, { min: 800, label: 'C1 Advanced', desc: 'You understand complex texts and ideas.' } ]; function getProgress() { var data = JSON.parse(localStorage.getItem(PROGRESS_KEY)); if (!data) { data = { xp: 0, exercisesDone: [], testsDone: 0, bestTestScore: 0, lastTestScore: 0, wordsListened: 0, readingDone: [], joined: new Date().toISOString().split('T')[0] }; localStorage.setItem(PROGRESS_KEY, JSON.stringify(data)); } return data; } function saveProgress(data) { localStorage.setItem(PROGRESS_KEY, JSON.stringify(data)); } function addXP(amount, reason) { var data = getProgress(); data.xp += amount; saveProgress(data); updateLevelBadge(); return data; } function getLevel(xp) { for (var i = LEVELS.length - 1; i >= 0; i--) { if (xp >= LEVELS[i].min) return LEVELS[i]; } return LEVELS[0]; } function getLevelProgress(xp) { var current = getLevel(xp); var nextMin = 9999; for (var i = 0; i < LEVELS.length; i++) { if (LEVELS[i].min > current.min) { nextMin = LEVELS[i].min; break; } } var range = nextMin - current.min; var progress = xp - current.min; return { current: current, nextMin: nextMin, progress: progress, range: range, percent: range > 0 ? Math.min(100, Math.round(progress / range * 100)) : 100 }; } function updateLevelBadge() { var data = getProgress(); var info = getLevelProgress(data.xp); var badge = document.querySelector('.level-badge'); if (badge) { badge.innerHTML = '' + info.current.label + '' + data.xp + ' XP'; } var bar = document.querySelector('.level-bar-fill'); if (bar) bar.style.width = info.percent + '%'; } function markExerciseDone(id) { var data = getProgress(); if (data.exercisesDone.indexOf(id) === -1) { data.exercisesDone.push(id); data.xp += 10; saveProgress(data); } } function markTestDone(score, total) { var data = getProgress(); data.testsDone++; data.lastTestScore = score; if (score > data.bestTestScore) data.bestTestScore = score; data.xp += 15 + score * 2; saveProgress(data); } function markWordListened() { var data = getProgress(); data.wordsListened++; if (data.wordsListened % 5 === 0) { data.xp += 5; saveProgress(data); } } // --- Daily Reminder at 20:00 --- (function() { var LS_KEY = 'english_easy_daily'; var TOAST_SHOWN = false; function getToday() { return new Date().toDateString(); } function getStreak() { var data = JSON.parse(localStorage.getItem(LS_KEY) || '{"streak":0,"lastDate":""}'); var yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); var yesterdayStr = yesterday.toDateString(); var today = getToday(); if (data.lastDate === today) { // already done today } else if (data.lastDate === yesterdayStr) { // consecutive day } else if (data.lastDate && data.lastDate !== today) { data.streak = 0; } return data; } function saveStreak(data) { data.lastDate = getToday(); localStorage.setItem(LS_KEY, JSON.stringify(data)); } function showDailyToast() { if (TOAST_SHOWN) return; TOAST_SHOWN = true; var data = getStreak(); var existing = document.querySelector('.daily-toast'); if (existing) existing.remove(); var toast = document.createElement('div'); toast.className = 'daily-toast'; var streakHTML = ''; if (data.streak > 0) { streakHTML = '
It\'s time for your daily English practice! Spend 5 minutes on an exercise or test.
' + '