summaryrefslogtreecommitdiff
path: root/index.html
diff options
context:
space:
mode:
Diffstat (limited to 'index.html')
-rw-r--r--index.html362
1 files changed, 25 insertions, 337 deletions
diff --git a/index.html b/index.html
index e89ef13..4254c2b 100644
--- a/index.html
+++ b/index.html
@@ -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>