diff options
Diffstat (limited to 'index.html')
| -rw-r--r-- | index.html | 362 |
1 files changed, 25 insertions, 337 deletions
@@ -1,345 +1,33 @@ -<!DOCTYPE html> +<!doctype html> <html lang="en"> - -<head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Learn Morse Code the Fun Way - Morse Hero</title> <meta name="description" - content="Morse Hero is a fun, interactive game to learn Morse code. Practice your skills with audio playback and visual hints to master Morse code quickly."> - <meta name="keywords" content="morse code, morse code game, learn morse code, morse code practice, morse hero"> - <meta name="robots" content="index, follow"> - <script src="https://cdn.usefathom.com/script.js" data-site="NEBPAKSA" defer></script> - <script src="https://cdn.jsdelivr.net/npm/cw@latest/dist/cw.min.js"></script> - <meta name="canonical" href="https://morsehero.com"> + content="Morse Hero is a fun, interactive game to learn Morse code. Practice your skills with audio playback and visual hints to master Morse code quickly." /> + <meta name="keywords" content="morse code, morse code game, learn morse code, morse code practice, morse hero" /> + <meta name="robots" content="index, follow" /> + <meta name="canonical" href="https://morsehero.com" /> - <link rel="apple-touch-icon" sizes="180x180" href="/ico/apple-touch-icon.png"> - <link rel="icon" type="image/png" sizes="32x32" href="/ico/icon-192.png"> - <link rel="icon" type="image/png" sizes="16x16" href="/ico/icon-192.png"> - <link rel="manifest" href="/site.webmanifest"> - <meta name="theme-color" content="#2c3e50"> + <link rel="apple-touch-icon" sizes="180x180" href="/ico/apple-touch-icon.png" /> + <link rel="icon" type="image/png" sizes="32x32" href="/ico/icon-192.png" /> + <link rel="icon" type="image/png" sizes="16x16" href="/ico/icon-192.png" /> + <link rel="manifest" href="/site.webmanifest" /> + <meta name="theme-color" content="#2c3e50" /> <!-- Google Fonts --> - <link rel="preconnect" href="https://fonts.googleapis.com"> - <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> + <link rel="preconnect" href="https://fonts.googleapis.com" /> + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link href="https://fonts.googleapis.com/css2?family=Fredoka:wght@400;600&family=Poppins:wght@400;600;700&display=swap" - rel="stylesheet"> - - <!-- Main Stylesheet --> - <link rel="stylesheet" href="base.css"> - <link rel="stylesheet" href="styles.css"> -</head> - -<body> - <div class="container"> - <h1><a href="/" class="title-link">Morse Hero</a></h1> - - <div id="startScreen"> - <p>Learn Morse code the fun way!</p> - <button id="startButton" class="start-button">Start</button> - <div class="chart-link"> - <a href="/chart/">View Morse Code Chart</a> - </div> - </div> - - <div id="gameArea"> - <div class="score-container"> - <div class="score-widget"> - <div class="score-info"> - <div class="score-label">Score:</div> - <div class="score-value"> - <span id="score">0</span> / <span id="total">0</span> - </div> - </div> - <div class="score-tracker" id="scoreTracker"></div> - </div> - </div> - - <div class="game-container"> - <div class="options"> - <button class="option-button" id="option1"></button> - <button class="option-button" id="option2"></button> - <button class="option-button" id="option3"></button> - <button class="option-button" id="option4"></button> - </div> - - <div class="feedback" id="feedback"></div> - </div> - - <div class="settings"> - <label for="wpmSelect">Speed:</label> - <select id="wpmSelect"> - <option value="10">10 WPM</option> - <option value="15">15 WPM</option> - <option value="20" selected>20 WPM</option> - <option value="25">25 WPM</option> - <option value="30">30 WPM</option> - </select> - <div class="hint-setting"> - <input type="checkbox" id="hintMode" name="hintMode"> - <label for="hintMode">Show Hints</label> - </div> - </div> - </div> - - <footer class="footer"> - <p>Need help? View the <a href="/chart/">Morse Code Chart</a>. </p> - <p>Created with <a href="https://www.mastercw.com/cw.js/">CW.js</a>. For a complete and professional - Morse Code training solution visit <a href="https://www.mastercw.com">Master CW</a>.</p> - </footer> - </div> - - <script> - // Game variables - let actx = null; - - // Define available characters - const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - const numbers = '0123456789'; - const specialChars = '.,?/='; - const allChars = letters + numbers + specialChars; - - // Game state - let currentChar = ''; - let score = 0; - let totalPlayed = 0; - - // DOM elements - const startButton = document.getElementById('startButton'); - const startScreen = document.getElementById('startScreen'); - const gameArea = document.getElementById('gameArea'); - - // Start button event listener - startButton.addEventListener('click', function () { - // Initialize audio context only when the start button is clicked - try { - actx = cw.initAudioContext({ tone: 600 }); - - // Hide start screen and show game area - startScreen.style.display = 'none'; - gameArea.style.display = 'block'; - document.body.classList.add('game-active'); - - // Initialize the game - init(); - } catch (error) { - console.error('Failed to initialize audio context:', error); - alert('Failed to initialize audio. Please try again.'); - } - }); - - // Initialize the game - function init() { - // DOM elements for the game - const optionButtons = [ - document.getElementById('option1'), - document.getElementById('option2'), - document.getElementById('option3'), - document.getElementById('option4') - ]; - const feedbackElement = document.getElementById('feedback'); - const scoreElement = document.getElementById('score'); - const totalElement = document.getElementById('total'); - const wpmSelect = document.getElementById('wpmSelect'); - const hintModeCheckbox = document.getElementById('hintMode'); - - optionButtons.forEach((button, index) => { - button.addEventListener('click', function () { - checkAnswer(button); - }); - // Remove number labels - we'll add character labels dynamically in newRound - const existingLabel = button.querySelector('.key-label'); - if (existingLabel) { - button.removeChild(existingLabel); - } - }); - - // Add keyboard listener for character keys - document.addEventListener('keydown', (event) => { - // Only process if we're in the game area (not on start screen) - if (gameArea.style.display === 'block') { - const key = event.key.toUpperCase(); - - // Find if any button has this character - for (let i = 0; i < optionButtons.length; i++) { - const charSpan = optionButtons[i].querySelector('.char-display'); - if (charSpan && charSpan.textContent === key && !optionButtons[i].disabled) { - // Simulate button press with visual feedback - optionButtons[i].classList.add('key-pressed'); - setTimeout(() => { - optionButtons[i].classList.remove('key-pressed'); - checkAnswer(optionButtons[i]); - }, 100); - break; - } - } - } - }); - - hintModeCheckbox.addEventListener('change', updateButtonsWithHints); - - // Start the game - newRound(); - - // Generate a new round - function newRound() { - // Clear feedback - feedbackElement.textContent = ''; - - // Reset all buttons to default state - optionButtons.forEach(button => { - button.disabled = false; - button.classList.remove('correct', 'incorrect'); - // Clear button content - button.innerHTML = ''; - }); - - // Generate a random character - const randomIndex = Math.floor(Math.random() * allChars.length); - currentChar = allChars[randomIndex]; - - // Generate 3 wrong options - let options = [currentChar]; - while (options.length < 4) { - const randomIndex = Math.floor(Math.random() * allChars.length); - const randomChar = allChars[randomIndex]; - if (!options.includes(randomChar)) { - options.push(randomChar); - } - } - - // Shuffle options - options = shuffleArray(options); - - // Set button text - for (let i = 0; i < 4; i++) { - // Create a span for the character - const charSpan = document.createElement('span'); - charSpan.className = 'char-display'; - charSpan.textContent = options[i]; - optionButtons[i].appendChild(charSpan); - } - - // Update buttons with hints if hint mode is enabled - updateButtonsWithHints(); - - // immediately play sound - playMorse(); - } - - // Update buttons with Morse code hints if hint mode is enabled - function updateButtonsWithHints() { - if (hintModeCheckbox.checked) { - optionButtons.forEach(button => { - // Remove any existing hint spans - const existingHint = button.querySelector('.morse-hint'); - if (existingHint) { - button.removeChild(existingHint); - } - - // Get the character from the button's char-display span - const charSpan = button.querySelector('.char-display'); - if (!charSpan) return; - - const char = charSpan.textContent; - - // Get the Morse code for the character - const morseCode = cw.codes[char]; - - if (morseCode) { - // Create a span for the hint - const hintSpan = document.createElement('span'); - hintSpan.className = 'morse-hint'; - hintSpan.textContent = morseCode.replace(/\./g, '·').replace(/\-/g, '−'); - - // Add the hint to the button - button.appendChild(hintSpan); - } - }); - } else { - // Remove hints if hint mode is disabled - optionButtons.forEach(button => { - const existingHint = button.querySelector('.morse-hint'); - if (existingHint) { - button.removeChild(existingHint); - } - }); - } - } - - // Play the morse code - function playMorse() { - const wpm = parseInt(wpmSelect.value); - - // Play the character with the existing audio context - cw.play(currentChar, { - actx: actx, - wpm: wpm - }); - } - - // Check the user's answer - function checkAnswer(selectedButton) { - // Disable all buttons - optionButtons.forEach(button => { - button.disabled = true; - }); - - totalPlayed++; - totalElement.textContent = totalPlayed; - - // Get the character from the button's char-display span - const charSpan = selectedButton.querySelector('.char-display'); - const selectedChar = charSpan ? charSpan.textContent : ''; - - // Create a new score dot - const scoreDot = document.createElement('div'); - scoreDot.className = 'score-dot'; - - if (selectedChar === currentChar) { - // Correct answer - score++; - scoreElement.textContent = score; - selectedButton.classList.add('correct'); - scoreDot.classList.add('correct'); - } else { - // Wrong answer - mark selected button as incorrect - selectedButton.classList.add('incorrect'); - scoreDot.classList.add('incorrect'); - - // Find and highlight the correct button - optionButtons.forEach(button => { - const buttonCharSpan = button.querySelector('.char-display'); - if (buttonCharSpan && buttonCharSpan.textContent === currentChar) { - button.classList.add('correct'); - } - }); - } - - // Add the score dot to the tracker - const scoreTracker = document.getElementById('scoreTracker'); - scoreTracker.appendChild(scoreDot); - - // Limit the number of dots shown (keep last 20) - if (scoreTracker.children.length > 20) { - scoreTracker.removeChild(scoreTracker.children[0]); - } - - // Start a new round after a delay - setTimeout(newRound, 1000); - } - } - - // Utility function to shuffle an array - function shuffleArray(array) { - for (let i = array.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [array[i], array[j]] = [array[j], array[i]]; - } - return array; - } - </script> -</body> - -</html>
\ No newline at end of file + rel="stylesheet" /> + + <!-- Analytics --> + <script src="https://cdn.usefathom.com/script.js" data-site="NEBPAKSA" defer></script> + </head> + <body> + <div id="root"></div> + <script type="module" src="/src/main.tsx"></script> + </body> +</html> |
