Spaces:
Running
Running
O tempo só aparece quando está a 1/4 do final e a animação do aro também! Corrija isso - Follow Up Deployment
e08db04
verified
| <html lang="pt-br"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Controle de Tempo</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> | |
| @keyframes progress { | |
| 0% { | |
| stroke-dashoffset: 440; | |
| } | |
| 100% { | |
| stroke-dashoffset: 0; | |
| } | |
| } | |
| .progress-ring { | |
| transform: rotate(-90deg); | |
| transform-origin: 50% 50%; | |
| } | |
| .beep { | |
| animation: beep 0.5s ease-out; | |
| } | |
| @keyframes beep { | |
| 0% { transform: scale(1); } | |
| 50% { transform: scale(1.1); } | |
| 100% { transform: scale(1); } | |
| } | |
| .long-beep { | |
| animation: longBeep 1s ease-out; | |
| } | |
| @keyframes longBeep { | |
| 0% { transform: scale(1); } | |
| 25% { transform: scale(1.2); } | |
| 50% { transform: scale(0.9); } | |
| 75% { transform: scale(1.1); } | |
| 100% { transform: scale(1); } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 text-white min-h-screen"> | |
| <audio id="beepSound" src="https://assets.mixkit.co/sfx/preview/mixkit-alarm-digital-clock-beep-989.mp3"></audio> | |
| <audio id="endSound" src="https://assets.mixkit.co/sfx/preview/mixkit-achievement-bell-600.mp3"></audio> | |
| <div class="container mx-auto px-4 py-8"> | |
| <!-- Main Menu --> | |
| <div id="mainMenu" class="text-center"> | |
| <h1 class="text-4xl font-bold mb-12">Controle de Tempo</h1> | |
| <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> | |
| <button onclick="showClock()" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-6 px-4 rounded-lg text-xl transition-all duration-300 transform hover:scale-105"> | |
| <i class="fas fa-clock mr-2"></i> Relógio | |
| </button> | |
| <button onclick="showTabataConfig()" class="bg-green-600 hover:bg-green-700 text-white font-bold py-6 px-4 rounded-lg text-xl transition-all duration-300 transform hover:scale-105"> | |
| <i class="fas fa-stopwatch mr-2"></i> Tabata | |
| </button> | |
| <button onclick="showForTimeConfig()" class="bg-purple-600 hover:bg-purple-700 text-white font-bold py-6 px-4 rounded-lg text-xl transition-all duration-300 transform hover:scale-105"> | |
| <i class="fas fa-hourglass-start mr-2"></i> Por Tempo | |
| </button> | |
| <button onclick="showQspConfig()" class="bg-red-600 hover:bg-red-700 text-white font-bold py-6 px-4 rounded-lg text-xl transition-all duration-300 transform hover:scale-105"> | |
| <i class="fas fa-hourglass-end mr-2"></i> QSP/QRP | |
| </button> | |
| <button onclick="showEmomConfig()" class="bg-yellow-600 hover:bg-yellow-700 text-white font-bold py-6 px-4 rounded-lg text-xl transition-all duration-300 transform hover:scale-105"> | |
| <i class="fas fa-sync-alt mr-2"></i> EMOM | |
| </button> | |
| <button onclick="showCustomConfig()" class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-6 px-4 rounded-lg text-xl transition-all duration-300 transform hover:scale-105"> | |
| <i class="fas fa-cog mr-2"></i> Custom | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Clock Screen --> | |
| <div id="clockScreen" class="hidden text-center"> | |
| <h2 class="text-3xl font-bold mb-8">Relógio</h2> | |
| <div class="text-6xl font-mono mb-8" id="currentTime"></div> | |
| <button onclick="backToMenu()" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-3 px-6 rounded-lg mt-4"> | |
| <i class="fas fa-arrow-left mr-2"></i> Voltar | |
| </button> | |
| </div> | |
| <!-- Tabata Config Screen --> | |
| <div id="tabataConfigScreen" class="hidden max-w-md mx-auto"> | |
| <h2 class="text-3xl font-bold mb-8 text-center">Configuração Tabata</h2> | |
| <div class="bg-gray-800 p-6 rounded-lg"> | |
| <div class="mb-4"> | |
| <label class="block text-gray-300 mb-2">Número de Séries</label> | |
| <input type="number" id="tabataRounds" min="1" max="20" value="8" class="w-full px-3 py-2 bg-gray-700 rounded"> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="block text-gray-300 mb-2">Tempo de Trabalho (segundos)</label> | |
| <input type="number" id="tabataWork" min="5" max="300" value="20" class="w-full px-3 py-2 bg-gray-700 rounded"> | |
| </div> | |
| <div class="mb-6"> | |
| <label class="block text-gray-300 mb-2">Tempo de Descanso (segundos)</label> | |
| <input type="number" id="tabataRest" min="5" max="300" value="10" class="w-full px-3 py-2 bg-gray-700 rounded"> | |
| </div> | |
| <div class="flex justify-between"> | |
| <button onclick="backToMenu()" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded"> | |
| <i class="fas fa-arrow-left mr-2"></i> Voltar | |
| </button> | |
| <button onclick="startTabata()" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"> | |
| <i class="fas fa-play mr-2"></i> Iniciar | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Tabata Timer Screen --> | |
| <div id="tabataTimerScreen" class="hidden text-center"> | |
| <h2 class="text-3xl font-bold mb-2">Tabata</h2> | |
| <p class="text-xl mb-8" id="tabataPhase">Preparar</p> | |
| <div class="relative w-64 h-64 mx-auto mb-8"> | |
| <svg class="w-full h-full" viewBox="0 0 100 100"> | |
| <circle cx="50" cy="50" r="45" fill="none" stroke="#333" stroke-width="8"/> | |
| <circle id="tabataProgress" cx="50" cy="50" r="45" fill="none" stroke="#10B981" stroke-width="8" stroke-dasharray="283" stroke-dashoffset="283" class="progress-ring"/> | |
| </svg> | |
| <div class="absolute inset-0 flex items-center justify-center"> | |
| <div id="tabataTime" class="text-5xl font-mono">00:20</div> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <span id="tabataRound" class="text-xl">Série 1/8</span> | |
| </div> | |
| <button onclick="pauseTabata()" id="tabataPauseBtn" class="bg-yellow-600 hover:bg-yellow-700 text-white font-bold py-2 px-6 rounded mr-2"> | |
| <i class="fas fa-pause mr-2"></i> Pausar | |
| </button> | |
| <button onclick="stopTabata()" class="bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-6 rounded"> | |
| <i class="fas fa-stop mr-2"></i> Parar | |
| </button> | |
| </div> | |
| <!-- For Time Config Screen --> | |
| <div id="forTimeConfigScreen" class="hidden max-w-md mx-auto"> | |
| <h2 class="text-3xl font-bold mb-8 text-center">Por Tempo</h2> | |
| <div class="bg-gray-800 p-6 rounded-lg"> | |
| <div class="mb-4"> | |
| <label class="block text-gray-300 mb-2">Minutos</label> | |
| <input type="number" id="forTimeMinutes" min="0" max="120" value="10" class="w-full px-3 py-2 bg-gray-700 rounded"> | |
| </div> | |
| <div class="mb-6"> | |
| <label class="block text-gray-300 mb-2">Segundos</label> | |
| <input type="number" id="forTimeSeconds" min="0" max="59" value="0" class="w-full px-3 py-2 bg-gray-700 rounded"> | |
| </div> | |
| <div class="flex justify-between"> | |
| <button onclick="backToMenu()" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded"> | |
| <i class="fas fa-arrow-left mr-2"></i> Voltar | |
| </button> | |
| <button onclick="startForTime()" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"> | |
| <i class="fas fa-play mr-2"></i> Iniciar | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- For Time Timer Screen --> | |
| <div id="forTimeTimerScreen" class="hidden text-center"> | |
| <h2 class="text-3xl font-bold mb-8">Por Tempo</h2> | |
| <div class="relative w-64 h-64 mx-auto mb-8"> | |
| <svg class="w-full h-full" viewBox="0 0 100 100"> | |
| <circle cx="50" cy="50" r="45" fill="none" stroke="#333" stroke-width="8"/> | |
| <circle id="forTimeProgress" cx="50" cy="50" r="45" fill="none" stroke="#10B981" stroke-width="8" stroke-dasharray="283" stroke-dashoffset="283" class="progress-ring"/> | |
| </svg> | |
| <div class="absolute inset-0 flex items-center justify-center"> | |
| <div id="forTimeTime" class="text-5xl font-mono">10:00</div> | |
| </div> | |
| </div> | |
| <button onclick="pauseForTime()" id="forTimePauseBtn" class="bg-yellow-600 hover:bg-yellow-700 text-white font-bold py-2 px-6 rounded mr-2"> | |
| <i class="fas fa-pause mr-2"></i> Pausar | |
| </button> | |
| <button onclick="stopForTime()" class="bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-6 rounded"> | |
| <i class="fas fa-stop mr-2"></i> Parar | |
| </button> | |
| </div> | |
| <!-- QSP Config Screen --> | |
| <div id="qspConfigScreen" class="hidden max-w-md mx-auto"> | |
| <h2 class="text-3xl font-bold mb-8 text-center">QSP/QRP</h2> | |
| <div class="bg-gray-800 p-6 rounded-lg"> | |
| <div class="mb-4"> | |
| <label class="block text-gray-300 mb-2">Minutos</label> | |
| <input type="number" id="qspMinutes" min="0" max="120" value="15" class="w-full px-3 py-2 bg-gray-700 rounded"> | |
| </div> | |
| <div class="mb-6"> | |
| <label class="block text-gray-300 mb-2">Segundos</label> | |
| <input type="number" id="qspSeconds" min="0" max="59" value="0" class="w-full px-3 py-2 bg-gray-700 rounded"> | |
| </div> | |
| <div class="flex justify-between"> | |
| <button onclick="backToMenu()" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded"> | |
| <i class="fas fa-arrow-left mr-2"></i> Voltar | |
| </button> | |
| <button onclick="startQsp()" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"> | |
| <i class="fas fa-play mr-2"></i> Iniciar | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- QSP Timer Screen --> | |
| <div id="qspTimerScreen" class="hidden text-center"> | |
| <h2 class="text-3xl font-bold mb-8">QSP/QRP</h2> | |
| <div class="relative w-64 h-64 mx-auto mb-8"> | |
| <svg class="w-full h-full" viewBox="0 0 100 100"> | |
| <circle cx="50" cy="50" r="45" fill="none" stroke="#333" stroke-width="8"/> | |
| <circle id="qspProgress" cx="50" cy="50" r="45" fill="none" stroke="#10B981" stroke-width="8" stroke-dasharray="283" stroke-dashoffset="283" class="progress-ring"/> | |
| </svg> | |
| <div class="absolute inset-0 flex items-center justify-center"> | |
| <div id="qspTime" class="text-5xl font-mono">15:00</div> | |
| </div> | |
| </div> | |
| <button onclick="pauseQsp()" id="qspPauseBtn" class="bg-yellow-600 hover:bg-yellow-700 text-white font-bold py-2 px-6 rounded mr-2"> | |
| <i class="fas fa-pause mr-2"></i> Pausar | |
| </button> | |
| <button onclick="stopQsp()" class="bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-6 rounded"> | |
| <i class="fas fa-stop mr-2"></i> Parar | |
| </button> | |
| </div> | |
| <!-- EMOM Config Screen --> | |
| <div id="emomConfigScreen" class="hidden max-w-md mx-auto"> | |
| <h2 class="text-3xl font-bold mb-8 text-center">Configuração EMOM</h2> | |
| <div class="bg-gray-800 p-6 rounded-lg"> | |
| <div class="mb-4"> | |
| <label class="block text-gray-300 mb-2">Número de Séries</label> | |
| <input type="number" id="emomRounds" min="1" max="30" value="10" class="w-full px-3 py-2 bg-gray-700 rounded"> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="block text-gray-300 mb-2">Tempo de Trabalho (minutos:segundos)</label> | |
| <div class="flex"> | |
| <input type="number" id="emomWorkMinutes" min="0" max="10" value="0" class="w-1/2 px-3 py-2 bg-gray-700 rounded mr-2"> | |
| <input type="number" id="emomWorkSeconds" min="0" max="59" value="30" class="w-1/2 px-3 py-2 bg-gray-700 rounded"> | |
| </div> | |
| </div> | |
| <div class="mb-6"> | |
| <label class="block text-gray-300 mb-2">Tempo de Descanso (minutos:segundos)</label> | |
| <div class="flex"> | |
| <input type="number" id="emomRestMinutes" min="0" max="10" value="0" class="w-1/2 px-3 py-2 bg-gray-700 rounded mr-2"> | |
| <input type="number" id="emomRestSeconds" min="0" max="59" value="30" class="w-1/2 px-3 py-2 bg-gray-700 rounded"> | |
| </div> | |
| </div> | |
| <div class="flex justify-between"> | |
| <button onclick="backToMenu()" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded"> | |
| <i class="fas fa-arrow-left mr-2"></i> Voltar | |
| </button> | |
| <button onclick="startEmom()" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"> | |
| <i class="fas fa-play mr-2"></i> Iniciar | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- EMOM Timer Screen --> | |
| <div id="emomTimerScreen" class="hidden text-center"> | |
| <h2 class="text-3xl font-bold mb-2">EMOM</h2> | |
| <p class="text-xl mb-8" id="emomPhase">Preparar</p> | |
| <div class="relative w-64 h-64 mx-auto mb-8"> | |
| <svg class="w-full h-full" viewBox="0 0 100 100"> | |
| <circle cx="50" cy="50" r="45" fill="none" stroke="#333" stroke-width="8"/> | |
| <circle id="emomProgress" cx="50" cy="50" r="45" fill="none" stroke="#10B981" stroke-width="8" stroke-dasharray="283" stroke-dashoffset="283" class="progress-ring"/> | |
| </svg> | |
| <div class="absolute inset-0 flex items-center justify-center"> | |
| <div id="emomTime" class="text-5xl font-mono">00:30</div> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <span id="emomRound" class="text-xl">Série 1/10</span> | |
| </div> | |
| <button onclick="pauseEmom()" id="emomPauseBtn" class="bg-yellow-600 hover:bg-yellow-700 text-white font-bold py-2 px-6 rounded mr-2"> | |
| <i class="fas fa-pause mr-2"></i> Pausar | |
| </button> | |
| <button onclick="stopEmom()" class="bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-6 rounded"> | |
| <i class="fas fa-stop mr-2"></i> Parar | |
| </button> | |
| </div> | |
| <!-- Custom Config Screen --> | |
| <div id="customConfigScreen" class="hidden max-w-md mx-auto"> | |
| <h2 class="text-3xl font-bold mb-8 text-center">Configuração Customizada</h2> | |
| <div class="bg-gray-800 p-6 rounded-lg"> | |
| <div class="mb-4"> | |
| <label class="block text-gray-300 mb-2">Contagem Regressiva Inicial (segundos)</label> | |
| <input type="number" id="customCountdown" min="0" max="60" value="10" class="w-full px-3 py-2 bg-gray-700 rounded"> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="block text-gray-300 mb-2">Número de Séries</label> | |
| <input type="number" id="customRounds" min="1" max="20" value="3" class="w-full px-3 py-2 bg-gray-700 rounded"> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="block text-gray-300 mb-2">Tempo Igual para Todas as Séries?</label> | |
| <select id="customEqualTime" class="w-full px-3 py-2 bg-gray-700 rounded"> | |
| <option value="yes">Sim</option> | |
| <option value="no">Não</option> | |
| </select> | |
| </div> | |
| <div id="customEqualTimeConfig"> | |
| <div class="mb-4"> | |
| <label class="block text-gray-300 mb-2">Tempo da Série (minutos:segundos)</label> | |
| <div class="flex"> | |
| <input type="number" id="customWorkMinutes" min="0" max="30" value="1" class="w-1/2 px-3 py-2 bg-gray-700 rounded mr-2"> | |
| <input type="number" id="customWorkSeconds" min="0" max="59" value="0" class="w-1/2 px-3 py-2 bg-gray-700 rounded"> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="block text-gray-300 mb-2">Tempo de Descanso (minutos:segundos)</label> | |
| <div class="flex"> | |
| <input type="number" id="customRestMinutes" min="0" max="30" value="0" class="w-1/2 px-3 py-2 bg-gray-700 rounded mr-2"> | |
| <input type="number" id="customRestSeconds" min="0" max="59" value="30" class="w-1/2 px-3 py-2 bg-gray-700 rounded"> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="customDifferentTimeConfig" class="hidden"> | |
| <div class="mb-4"> | |
| <label class="block text-gray-300 mb-2">Configuração por Série</label> | |
| <div id="customSeriesConfig" class="space-y-4"> | |
| <!-- Will be populated by JavaScript --> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="block text-gray-300 mb-2">Tipo de Contagem</label> | |
| <select id="customCountType" class="w-full px-3 py-2 bg-gray-700 rounded"> | |
| <option value="progress">Progressiva</option> | |
| <option value="regress">Regressiva</option> | |
| </select> | |
| </div> | |
| <div class="flex justify-between"> | |
| <button onclick="backToMenu()" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded"> | |
| <i class="fas fa-arrow-left mr-2"></i> Voltar | |
| </button> | |
| <button onclick="startCustom()" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"> | |
| <i class="fas fa-play mr-2"></i> Iniciar | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Custom Timer Screen --> | |
| <div id="customTimerScreen" class="hidden text-center"> | |
| <h2 class="text-3xl font-bold mb-2">Customizado</h2> | |
| <p class="text-xl mb-8" id="customPhase">Preparar</p> | |
| <div class="relative w-64 h-64 mx-auto mb-8"> | |
| <svg class="w-full h-full" viewBox="0 0 100 100"> | |
| <circle cx="50" cy="50" r="45" fill="none" stroke="#333" stroke-width="8"/> | |
| <circle id="customProgress" cx="50" cy="50" r="45" fill="none" stroke="#10B981" stroke-width="8" stroke-dasharray="283" stroke-dashoffset="283" class="progress-ring"/> | |
| </svg> | |
| <div class="absolute inset-0 flex items-center justify-center"> | |
| <div id="customTime" class="text-5xl font-mono">00:10</div> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <span id="customRound" class="text-xl">Série 1/3</span> | |
| </div> | |
| <button onclick="pauseCustom()" id="customPauseBtn" class="bg-yellow-600 hover:bg-yellow-700 text-white font-bold py-2 px-6 rounded mr-2"> | |
| <i class="fas fa-pause mr-2"></i> Pausar | |
| </button> | |
| <button onclick="stopCustom()" class="bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-6 rounded"> | |
| <i class="fas fa-stop mr-2"></i> Parar | |
| </button> | |
| </div> | |
| </div> | |
| <script> | |
| // Global variables | |
| let currentScreen = 'mainMenu'; | |
| let clockInterval; | |
| let tabataInterval; | |
| let forTimeInterval; | |
| let qspInterval; | |
| let emomInterval; | |
| let customInterval; | |
| // Audio elements | |
| const beepSound = document.getElementById('beepSound'); | |
| const endSound = document.getElementById('endSound'); | |
| // Initialize the app | |
| document.addEventListener('DOMContentLoaded', function() { | |
| updateClock(); | |
| clockInterval = setInterval(updateClock, 1000); | |
| // Setup event listeners for custom config | |
| document.getElementById('customEqualTime').addEventListener('change', function() { | |
| const equalTime = this.value === 'yes'; | |
| document.getElementById('customEqualTimeConfig').style.display = equalTime ? 'block' : 'none'; | |
| document.getElementById('customDifferentTimeConfig').style.display = equalTime ? 'none' : 'block'; | |
| if (!equalTime) { | |
| setupCustomSeriesConfig(); | |
| } | |
| }); | |
| }); | |
| // Navigation functions | |
| function backToMenu() { | |
| hideAllScreens(); | |
| document.getElementById('mainMenu').classList.remove('hidden'); | |
| currentScreen = 'mainMenu'; | |
| // Clear any running timers | |
| clearAllIntervals(); | |
| } | |
| function showClock() { | |
| hideAllScreens(); | |
| document.getElementById('clockScreen').classList.remove('hidden'); | |
| currentScreen = 'clock'; | |
| } | |
| function showTabataConfig() { | |
| hideAllScreens(); | |
| document.getElementById('tabataConfigScreen').classList.remove('hidden'); | |
| currentScreen = 'tabataConfig'; | |
| } | |
| function showForTimeConfig() { | |
| hideAllScreens(); | |
| document.getElementById('forTimeConfigScreen').classList.remove('hidden'); | |
| currentScreen = 'forTimeConfig'; | |
| } | |
| function showQspConfig() { | |
| hideAllScreens(); | |
| document.getElementById('qspConfigScreen').classList.remove('hidden'); | |
| currentScreen = 'qspConfig'; | |
| } | |
| function showEmomConfig() { | |
| hideAllScreens(); | |
| document.getElementById('emomConfigScreen').classList.remove('hidden'); | |
| currentScreen = 'emomConfig'; | |
| } | |
| function showCustomConfig() { | |
| hideAllScreens(); | |
| document.getElementById('customConfigScreen').classList.remove('hidden'); | |
| currentScreen = 'customConfig'; | |
| setupCustomSeriesConfig(); | |
| } | |
| function hideAllScreens() { | |
| const screens = [ | |
| 'mainMenu', 'clockScreen', | |
| 'tabataConfigScreen', 'tabataTimerScreen', | |
| 'forTimeConfigScreen', 'forTimeTimerScreen', | |
| 'qspConfigScreen', 'qspTimerScreen', | |
| 'emomConfigScreen', 'emomTimerScreen', | |
| 'customConfigScreen', 'customTimerScreen' | |
| ]; | |
| screens.forEach(screen => { | |
| document.getElementById(screen).classList.add('hidden'); | |
| }); | |
| } | |
| function clearAllIntervals() { | |
| clearInterval(clockInterval); | |
| clearInterval(tabataInterval); | |
| clearInterval(forTimeInterval); | |
| clearInterval(qspInterval); | |
| clearInterval(emomInterval); | |
| clearInterval(customInterval); | |
| } | |
| // Clock functions | |
| function updateClock() { | |
| const now = new Date(); | |
| const timeString = now.toLocaleTimeString(); | |
| document.getElementById('currentTime').textContent = timeString; | |
| } | |
| // Tabata functions | |
| function startTabata() { | |
| const rounds = parseInt(document.getElementById('tabataRounds').value); | |
| const workTime = parseInt(document.getElementById('tabataWork').value); | |
| const restTime = parseInt(document.getElementById('tabataRest').value); | |
| // Initialize tabata state | |
| const tabataState = { | |
| rounds: rounds, | |
| currentRound: 0, | |
| workTime: workTime, | |
| restTime: restTime, | |
| remainingTime: 3, // 3 second countdown | |
| phase: 'prepare', // prepare, work, rest | |
| isRunning: true, | |
| isPaused: false | |
| }; | |
| // Update UI | |
| document.getElementById('tabataRound').textContent = `Série 0/${rounds}`; | |
| document.getElementById('tabataPhase').textContent = 'Preparar'; | |
| document.getElementById('tabataTime').textContent = formatTime(tabataState.remainingTime); | |
| // Setup progress ring | |
| const progressRing = document.getElementById('tabataProgress'); | |
| progressRing.style.stroke = '#10B981'; // Green | |
| progressRing.style.strokeDashoffset = calculateDashOffset(tabataState.remainingTime, tabataState.remainingTime); | |
| // Show timer screen | |
| hideAllScreens(); | |
| document.getElementById('tabataTimerScreen').classList.remove('hidden'); | |
| currentScreen = 'tabataTimer'; | |
| // Start timer | |
| clearInterval(tabataInterval); | |
| tabataInterval = setInterval(() => updateTabataTimer(tabataState), 1000); | |
| } | |
| function updateTabataTimer(state) { | |
| if (state.isPaused) return; | |
| state.remainingTime--; | |
| // Update progress ring | |
| const progressRing = document.getElementById('tabataProgress'); | |
| let totalTime; | |
| switch (state.phase) { | |
| case 'prepare': | |
| totalTime = 3; | |
| break; | |
| case 'work': | |
| totalTime = state.workTime; | |
| break; | |
| case 'rest': | |
| totalTime = state.restTime; | |
| break; | |
| } | |
| const dashOffset = calculateDashOffset(state.remainingTime, totalTime); | |
| progressRing.style.strokeDashoffset = dashOffset; | |
| // Change color based on time remaining | |
| if (state.remainingTime <= totalTime / 4 && state.phase !== 'prepare') { | |
| progressRing.style.stroke = '#EF4444'; // Red | |
| } else if (state.remainingTime <= totalTime / 2 && state.phase !== 'prepare') { | |
| progressRing.style.stroke = '#F59E0B'; // Yellow | |
| } | |
| // Play sounds | |
| if (state.remainingTime <= 3 && state.remainingTime > 0) { | |
| playBeep(); | |
| document.getElementById('tabataTime').classList.add('beep'); | |
| setTimeout(() => { | |
| document.getElementById('tabataTime').classList.remove('beep'); | |
| }, 500); | |
| } | |
| if (state.remainingTime === 0) { | |
| playEndBeep(); | |
| document.getElementById('tabataTime').classList.add('long-beep'); | |
| setTimeout(() => { | |
| document.getElementById('tabataTime').classList.remove('long-beep'); | |
| }, 1000); | |
| } | |
| // Update display | |
| document.getElementById('tabataTime').textContent = formatTime(state.remainingTime); | |
| // Handle phase transitions | |
| if (state.remainingTime <= 0) { | |
| if (state.phase === 'prepare') { | |
| state.phase = 'work'; | |
| state.currentRound++; | |
| state.remainingTime = state.workTime; | |
| document.getElementById('tabataPhase').textContent = 'Trabalhar'; | |
| document.getElementById('tabataRound').textContent = `Série ${state.currentRound}/${state.rounds}`; | |
| progressRing.style.stroke = '#10B981'; // Reset to green | |
| } | |
| else if (state.phase === 'work') { | |
| if (state.currentRound < state.rounds) { | |
| state.phase = 'rest'; | |
| state.remainingTime = state.restTime; | |
| document.getElementById('tabataPhase').textContent = 'Descansar'; | |
| progressRing.style.stroke = '#10B981'; // Reset to green | |
| } else { | |
| // Tabata complete | |
| clearInterval(tabataInterval); | |
| document.getElementById('tabataPhase').textContent = 'Concluído!'; | |
| return; | |
| } | |
| } | |
| else if (state.phase === 'rest') { | |
| state.phase = 'work'; | |
| state.currentRound++; | |
| state.remainingTime = state.workTime; | |
| document.getElementById('tabataPhase').textContent = 'Trabalhar'; | |
| document.getElementById('tabataRound').textContent = `Série ${state.currentRound}/${state.rounds}`; | |
| progressRing.style.stroke = '#10B981'; // Reset to green | |
| } | |
| } | |
| } | |
| function pauseTabata() { | |
| const pauseBtn = document.getElementById('tabataPauseBtn'); | |
| if (pauseBtn.innerHTML.includes('Pausar')) { | |
| pauseBtn.innerHTML = '<i class="fas fa-play mr-2"></i> Continuar'; | |
| // Pause logic would be handled in the updateTabataTimer function | |
| } else { | |
| pauseBtn.innerHTML = '<i class="fas fa-pause mr-2"></i> Pausar'; | |
| // Resume logic would be handled in the updateTabataTimer function | |
| } | |
| } | |
| function stopTabata() { | |
| clearInterval(tabataInterval); | |
| backToMenu(); | |
| } | |
| // For Time functions | |
| function startForTime() { | |
| const minutes = parseInt(document.getElementById('forTimeMinutes').value); | |
| const seconds = parseInt(document.getElementById('forTimeSeconds').value); | |
| const totalSeconds = minutes * 60 + seconds; | |
| // Initialize state | |
| const forTimeState = { | |
| totalTime: totalSeconds, | |
| elapsedTime: 0, | |
| isRunning: true, | |
| isPaused: false | |
| }; | |
| // Update UI - show 00:00 initially for progressive timer | |
| document.getElementById('forTimeTime').textContent = formatTime(0); | |
| // Setup progress ring | |
| const progressRing = document.getElementById('forTimeProgress'); | |
| progressRing.style.stroke = '#10B981'; // Green | |
| progressRing.style.strokeDashoffset = calculateDashOffset(0, totalSeconds); | |
| // Show timer screen | |
| hideAllScreens(); | |
| document.getElementById('forTimeTimerScreen').classList.remove('hidden'); | |
| currentScreen = 'forTimeTimer'; | |
| // Start timer | |
| clearInterval(forTimeInterval); | |
| forTimeInterval = setInterval(() => updateForTimeTimer(forTimeState), 1000); | |
| } | |
| function updateForTimeTimer(state) { | |
| if (state.isPaused) return; | |
| state.elapsedTime++; | |
| const elapsedTime = state.elapsedTime; | |
| // Update progress ring | |
| const progressRing = document.getElementById('forTimeProgress'); | |
| const dashOffset = calculateDashOffset(state.totalTime - elapsedTime, state.totalTime); | |
| progressRing.style.strokeDashoffset = dashOffset; | |
| // Change color based on time elapsed | |
| const remainingTime = state.totalTime - elapsedTime; | |
| if (remainingTime <= state.totalTime * 0.25) { | |
| progressRing.style.stroke = '#EF4444'; // Red | |
| } else if (remainingTime <= state.totalTime * 0.5) { | |
| progressRing.style.stroke = '#F59E0B'; // Yellow | |
| } | |
| // Play sounds | |
| if (elapsedTime >= state.totalTime - 3 && elapsedTime < state.totalTime) { | |
| playBeep(); | |
| document.getElementById('forTimeTime').classList.add('beep'); | |
| setTimeout(() => { | |
| document.getElementById('forTimeTime').classList.remove('beep'); | |
| }, 500); | |
| } | |
| if (elapsedTime >= state.totalTime) { | |
| playEndBeep(); | |
| document.getElementById('forTimeTime').classList.add('long-beep'); | |
| setTimeout(() => { | |
| document.getElementById('forTimeTime').classList.remove('long-beep'); | |
| }, 1000); | |
| } | |
| // Update display | |
| document.getElementById('forTimeTime').textContent = formatTime(elapsedTime); | |
| // Check if timer is complete | |
| if (elapsedTime >= state.totalTime) { | |
| clearInterval(forTimeInterval); | |
| } | |
| } | |
| function pauseForTime() { | |
| const pauseBtn = document.getElementById('forTimePauseBtn'); | |
| if (pauseBtn.innerHTML.includes('Pausar')) { | |
| pauseBtn.innerHTML = '<i class="fas fa-play mr-2"></i> Continuar'; | |
| // Pause logic would be handled in the updateForTimeTimer function | |
| } else { | |
| pauseBtn.innerHTML = '<i class="fas fa-pause mr-2"></i> Pausar'; | |
| // Resume logic would be handled in the updateForTimeTimer function | |
| } | |
| } | |
| function stopForTime() { | |
| clearInterval(forTimeInterval); | |
| backToMenu(); | |
| } | |
| // QSP functions | |
| function startQsp() { | |
| const minutes = parseInt(document.getElementById('qspMinutes').value); | |
| const seconds = parseInt(document.getElementById('qspSeconds').value); | |
| const totalSeconds = minutes * 60 + seconds; | |
| // Initialize state | |
| const qspState = { | |
| remainingTime: totalSeconds, | |
| isRunning: true, | |
| isPaused: false | |
| }; | |
| // Update UI | |
| document.getElementById('qspTime').textContent = formatTime(totalSeconds); | |
| // Setup progress ring | |
| const progressRing = document.getElementById('qspProgress'); | |
| progressRing.style.stroke = '#10B981'; // Green | |
| progressRing.style.strokeDashoffset = calculateDashOffset(totalSeconds, totalSeconds); | |
| // Show timer screen | |
| hideAllScreens(); | |
| document.getElementById('qspTimerScreen').classList.remove('hidden'); | |
| currentScreen = 'qspTimer'; | |
| // Start timer | |
| clearInterval(qspInterval); | |
| qspInterval = setInterval(() => updateQspTimer(qspState), 1000); | |
| } | |
| function updateQspTimer(state) { | |
| if (state.isPaused) return; | |
| state.remainingTime--; | |
| // Update progress ring | |
| const progressRing = document.getElementById('qspProgress'); | |
| const totalTime = state.remainingTime + state.elapsedTime; // Need to track initial total time | |
| const dashOffset = calculateDashOffset(state.remainingTime, totalTime); | |
| progressRing.style.strokeDashoffset = dashOffset; | |
| // Change color based on time remaining | |
| if (state.remainingTime <= totalTime / 4) { | |
| progressRing.style.stroke = '#EF4444'; // Red | |
| } else if (state.remainingTime <= totalTime / 2) { | |
| progressRing.style.stroke = '#F59E0B'; // Yellow | |
| } | |
| // Play sounds | |
| if (state.remainingTime <= 3 && state.remainingTime > 0) { | |
| playBeep(); | |
| document.getElementById('qspTime').classList.add('beep'); | |
| setTimeout(() => { | |
| document.getElementById('qspTime').classList.remove('beep'); | |
| }, 500); | |
| } | |
| if (state.remainingTime === 0) { | |
| playEndBeep(); | |
| document.getElementById('qspTime').classList.add('long-beep'); | |
| setTimeout(() => { | |
| document.getElementById('qspTime').classList.remove('long-beep'); | |
| }, 1000); | |
| } | |
| // Update display | |
| document.getElementById('qspTime').textContent = formatTime(state.remainingTime); | |
| // Check if timer is complete | |
| if (state.remainingTime <= 0) { | |
| clearInterval(qspInterval); | |
| } | |
| } | |
| function pauseQsp() { | |
| const pauseBtn = document.getElementById('qspPauseBtn'); | |
| if (pauseBtn.innerHTML.includes('Pausar')) { | |
| pauseBtn.innerHTML = '<i class="fas fa-play mr-2"></i> Continuar'; | |
| // Pause logic would be handled in the updateQspTimer function | |
| } else { | |
| pauseBtn.innerHTML = '<i class="fas fa-pause mr-2"></i> Pausar'; | |
| // Resume logic would be handled in the updateQspTimer function | |
| } | |
| } | |
| function stopQsp() { | |
| clearInterval(qspInterval); | |
| backToMenu(); | |
| } | |
| // EMOM functions | |
| function startEmom() { | |
| const rounds = parseInt(document.getElementById('emomRounds').value); | |
| const workMinutes = parseInt(document.getElementById('emomWorkMinutes').value); | |
| const workSeconds = parseInt(document.getElementById('emomWorkSeconds').value); | |
| const restMinutes = parseInt(document.getElementById('emomRestMinutes').value); | |
| const restSeconds = parseInt(document.getElementById('emomRestSeconds').value); | |
| const workTime = workMinutes * 60 + workSeconds; | |
| const restTime = restMinutes * 60 + restSeconds; | |
| // Initialize emom state | |
| const emomState = { | |
| rounds: rounds, | |
| currentRound: 0, | |
| workTime: workTime, | |
| restTime: restTime, | |
| remainingTime: 3, // 3 second countdown | |
| phase: 'prepare', // prepare, work, rest | |
| isRunning: true, | |
| isPaused: false | |
| }; | |
| // Update UI | |
| document.getElementById('emomRound').textContent = `Série 0/${rounds}`; | |
| document.getElementById('emomPhase').textContent = 'Preparar'; | |
| document.getElementById('emomTime').textContent = formatTime(emomState.remainingTime); | |
| // Setup progress ring | |
| const progressRing = document.getElementById('emomProgress'); | |
| progressRing.style.stroke = '#10B981'; // Green | |
| progressRing.style.strokeDashoffset = calculateDashOffset(emomState.remainingTime, emomState.remainingTime); | |
| // Show timer screen | |
| hideAllScreens(); | |
| document.getElementById('emomTimerScreen').classList.remove('hidden'); | |
| currentScreen = 'emomTimer'; | |
| // Start timer | |
| clearInterval(emomInterval); | |
| emomInterval = setInterval(() => updateEmomTimer(emomState), 1000); | |
| } | |
| function updateEmomTimer(state) { | |
| if (state.isPaused) return; | |
| state.remainingTime--; | |
| // Update progress ring | |
| const progressRing = document.getElementById('emomProgress'); | |
| let totalTime; | |
| switch (state.phase) { | |
| case 'prepare': | |
| totalTime = 3; | |
| break; | |
| case 'work': | |
| totalTime = state.workTime; | |
| break; | |
| case 'rest': | |
| totalTime = state.restTime; | |
| break; | |
| } | |
| const dashOffset = calculateDashOffset(state.remainingTime, totalTime); | |
| progressRing.style.strokeDashoffset = dashOffset; | |
| // Change color based on time remaining | |
| if (state.remainingTime <= totalTime / 4 && state.phase !== 'prepare') { | |
| progressRing.style.stroke = '#EF4444'; // Red | |
| } else if (state.remainingTime <= totalTime / 2 && state.phase !== 'prepare') { | |
| progressRing.style.stroke = '#F59E0B'; // Yellow | |
| } | |
| // Play sounds | |
| if (state.remainingTime <= 3 && state.remainingTime > 0) { | |
| playBeep(); | |
| document.getElementById('emomTime').classList.add('beep'); | |
| setTimeout(() => { | |
| document.getElementById('emomTime').classList.remove('beep'); | |
| }, 500); | |
| } | |
| if (state.remainingTime === 0) { | |
| playEndBeep(); | |
| document.getElementById('emomTime').classList.add('long-beep'); | |
| setTimeout(() => { | |
| document.getElementById('emomTime').classList.remove('long-beep'); | |
| }, 1000); | |
| } | |
| // Update display | |
| document.getElementById('emomTime').textContent = formatTime(state.remainingTime); | |
| // Handle phase transitions | |
| if (state.remainingTime <= 0) { | |
| if (state.phase === 'prepare') { | |
| state.phase = 'work'; | |
| state.currentRound++; | |
| state.remainingTime = state.workTime; | |
| document.getElementById('emomPhase').textContent = 'Trabalhar'; | |
| document.getElementById('emomRound').textContent = `Série ${state.currentRound}/${state.rounds}`; | |
| progressRing.style.stroke = '#10B981'; // Reset to green | |
| } | |
| else if (state.phase === 'work') { | |
| if (state.currentRound < state.rounds) { | |
| state.phase = 'rest'; | |
| state.remainingTime = state.restTime; | |
| document.getElementById('emomPhase').textContent = 'Descansar'; | |
| progressRing.style.stroke = '#10B981'; // Reset to green | |
| } else { | |
| // EMOM complete | |
| clearInterval(emomInterval); | |
| document.getElementById('emomPhase').textContent = 'Concluído!'; | |
| return; | |
| } | |
| } | |
| else if (state.phase === 'rest') { | |
| state.phase = 'work'; | |
| state.currentRound++; | |
| state.remainingTime = state.workTime; | |
| document.getElementById('emomPhase').textContent = 'Trabalhar'; | |
| document.getElementById('emomRound').textContent = `Série ${state.currentRound}/${state.rounds}`; | |
| progressRing.style.stroke = '#10B981'; // Reset to green | |
| } | |
| } | |
| } | |
| function pauseEmom() { | |
| const pauseBtn = document.getElementById('emomPauseBtn'); | |
| if (pauseBtn.innerHTML.includes('Pausar')) { | |
| pauseBtn.innerHTML = '<i class="fas fa-play mr-2"></i> Continuar'; | |
| // Pause logic would be handled in the updateEmomTimer function | |
| } else { | |
| pauseBtn.innerHTML = '<i class="fas fa-pause mr-2"></i> Pausar'; | |
| // Resume logic would be handled in the updateEmomTimer function | |
| } | |
| } | |
| function stopEmom() { | |
| clearInterval(emomInterval); | |
| backToMenu(); | |
| } | |
| // Custom functions | |
| function setupCustomSeriesConfig() { | |
| const rounds = parseInt(document.getElementById('customRounds').value); | |
| const container = document.getElementById('customSeriesConfig'); | |
| container.innerHTML = ''; | |
| for (let i = 0; i < rounds; i++) { | |
| const seriesDiv = document.createElement('div'); | |
| seriesDiv.className = 'bg-gray-700 p-4 rounded'; | |
| seriesDiv.innerHTML = ` | |
| <h3 class="text-lg font-semibold mb-2">Série ${i + 1}</h3> | |
| <div class="mb-2"> | |
| <label class="block text-gray-300 mb-1">Tempo (minutos:segundos)</label> | |
| <div class="flex"> | |
| <input type="number" id="customWorkMinutes${i}" min="0" max="30" value="1" class="w-1/2 px-2 py-1 bg-gray-600 rounded mr-2"> | |
| <input type="number" id="customWorkSeconds${i}" min="0" max="59" value="0" class="w-1/2 px-2 py-1 bg-gray-600 rounded"> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="block text-gray-300 mb-1">Descanso (minutos:segundos)</label> | |
| <div class="flex"> | |
| <input type="number" id="customRestMinutes${i}" min="0" max="30" value="0" class="w-1/2 px-2 py-1 bg-gray-600 rounded mr-2"> | |
| <input type="number" id="customRestSeconds${i}" min="0" max="59" value="30" class="w-1/2 px-2 py-1 bg-gray-600 rounded"> | |
| </div> | |
| </div> | |
| `; | |
| container.appendChild(seriesDiv); | |
| } | |
| } | |
| function startCustom() { | |
| const countdown = parseInt(document.getElementById('customCountdown').value); | |
| const rounds = parseInt(document.getElementById('customRounds').value); | |
| const equalTime = document.getElementById('customEqualTime').value === 'yes'; | |
| const countType = document.getElementById('customCountType').value; | |
| let seriesTimes = []; | |
| let restTimes = []; | |
| if (equalTime) { | |
| const workMinutes = parseInt(document.getElementById('customWorkMinutes').value); | |
| const workSeconds = parseInt(document.getElementById('customWorkSeconds').value); | |
| const restMinutes = parseInt(document.getElementById('customRestMinutes').value); | |
| const restSeconds = parseInt(document.getElementById('customRestSeconds').value); | |
| const workTime = workMinutes * 60 + workSeconds; | |
| const restTime = restMinutes * 60 + restSeconds; | |
| for (let i = 0; i < rounds; i++) { | |
| seriesTimes.push(workTime); | |
| restTimes.push(restTime); | |
| } | |
| } else { | |
| for (let i = 0; i < rounds; i++) { | |
| const workMinutes = parseInt(document.getElementById(`customWorkMinutes${i}`).value); | |
| const workSeconds = parseInt(document.getElementById(`customWorkSeconds${i}`).value); | |
| const restMinutes = parseInt(document.getElementById(`customRestMinutes${i}`).value); | |
| const restSeconds = parseInt(document.getElementById(`customRestSeconds${i}`).value); | |
| seriesTimes.push(workMinutes * 60 + workSeconds); | |
| restTimes.push(restMinutes * 60 + restSeconds); | |
| } | |
| } | |
| // Initialize custom state | |
| const customState = { | |
| rounds: rounds, | |
| currentRound: 0, | |
| seriesTimes: seriesTimes, | |
| restTimes: restTimes, | |
| remainingTime: countdown, | |
| phase: 'prepare', // prepare, work, rest | |
| countType: countType, | |
| isRunning: true, | |
| isPaused: false | |
| }; | |
| // Update UI | |
| document.getElementById('customRound').textContent = `Série 0/${rounds}`; | |
| document.getElementById('customPhase').textContent = 'Preparar'; | |
| document.getElementById('customTime').textContent = formatTime(customState.remainingTime); | |
| // Setup progress ring | |
| const progressRing = document.getElementById('customProgress'); | |
| progressRing.style.stroke = '#10B981'; // Green | |
| progressRing.style.strokeDashoffset = calculateDashOffset(customState.remainingTime, customState.remainingTime); | |
| // Show timer screen | |
| hideAllScreens(); | |
| document.getElementById('customTimerScreen').classList.remove('hidden'); | |
| currentScreen = 'customTimer'; | |
| // Start timer | |
| clearInterval(customInterval); | |
| customInterval = setInterval(() => updateCustomTimer(customState), 1000); | |
| } | |
| function updateCustomTimer(state) { | |
| if (state.isPaused) return; | |
| state.remainingTime--; | |
| // Update progress ring | |
| const progressRing = document.getElementById('customProgress'); | |
| let totalTime; | |
| switch (state.phase) { | |
| case 'prepare': | |
| totalTime = state.remainingTime + 1; // Initial countdown | |
| break; | |
| case 'work': | |
| totalTime = state.seriesTimes[state.currentRound - 1]; | |
| break; | |
| case 'rest': | |
| totalTime = state.restTimes[state.currentRound - 1]; | |
| break; | |
| } | |
| const dashOffset = calculateDashOffset(state.remainingTime, totalTime); | |
| progressRing.style.strokeDashoffset = dashOffset; | |
| // Change color based on time remaining | |
| if (state.remainingTime <= totalTime / 4 && state.phase !== 'prepare') { | |
| progressRing.style.stroke = '#EF4444'; // Red | |
| } else if (state.remainingTime <= totalTime / 2 && state.phase !== 'prepare') { | |
| progressRing.style.stroke = '#F59E0B'; // Yellow | |
| } | |
| // Play sounds | |
| if (state.remainingTime <= 3 && state.remainingTime > 0) { | |
| playBeep(); | |
| document.getElementById('customTime').classList.add('beep'); | |
| setTimeout(() => { | |
| document.getElementById('customTime').classList.remove('beep'); | |
| }, 500); | |
| } | |
| if (state.remainingTime === 0) { | |
| playEndBeep(); | |
| document.getElementById('customTime').classList.add('long-beep'); | |
| setTimeout(() => { | |
| document.getElementById('customTime').classList.remove('long-beep'); | |
| }, 1000); | |
| } | |
| // Update display | |
| document.getElementById('customTime').textContent = formatTime(state.remainingTime); | |
| // Handle phase transitions | |
| if (state.remainingTime <= 0) { | |
| if (state.phase === 'prepare') { | |
| state.phase = 'work'; | |
| state.currentRound++; | |
| state.remainingTime = state.seriesTimes[state.currentRound - 1]; | |
| document.getElementById('customPhase').textContent = 'Trabalhar'; | |
| document.getElementById('customRound').textContent = `Série ${state.currentRound}/${state.rounds}`; | |
| progressRing.style.stroke = '#10B981'; // Reset to green | |
| } | |
| else if (state.phase === 'work') { | |
| if (state.currentRound < state.rounds) { | |
| state.phase = 'rest'; | |
| state.remainingTime = state.restTimes[state.currentRound - 1]; | |
| document.getElementById('customPhase').textContent = 'Descansar'; | |
| progressRing.style.stroke = '#10B981'; // Reset to green | |
| } else { | |
| // Custom complete | |
| clearInterval(customInterval); | |
| document.getElementById('customPhase').textContent = 'Concluído!'; | |
| return; | |
| } | |
| } | |
| else if (state.phase === 'rest') { | |
| state.phase = 'work'; | |
| state.currentRound++; | |
| state.remainingTime = state.seriesTimes[state.currentRound - 1]; | |
| document.getElementById('customPhase').textContent = 'Trabalhar'; | |
| document.getElementById('customRound').textContent = `Série ${state.currentRound}/${state.rounds}`; | |
| progressRing.style.stroke = '#10B981'; // Reset to green | |
| } | |
| } | |
| } | |
| function pauseCustom() { | |
| const pauseBtn = document.getElementById('customPauseBtn'); | |
| if (pauseBtn.innerHTML.includes('Pausar')) { | |
| pauseBtn.innerHTML = '<i class="fas fa-play mr-2"></i> Continuar'; | |
| // Pause logic would be handled in the updateCustomTimer function | |
| } else { | |
| pauseBtn.innerHTML = '<i class="fas fa-pause mr-2"></i> Pausar'; | |
| // Resume logic would be handled in the updateCustomTimer function | |
| } | |
| } | |
| function stopCustom() { | |
| clearInterval(customInterval); | |
| backToMenu(); | |
| } | |
| // Helper functions | |
| function formatTime(seconds) { | |
| const mins = Math.floor(seconds / 60); | |
| const secs = seconds % 60; | |
| return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; | |
| } | |
| function calculateDashOffset(remaining, total) { | |
| const circumference = 283; // 2 * π * r (where r is 45) | |
| const progress = remaining / total; | |
| return circumference * progress; | |
| } | |
| function playBeep() { | |
| beepSound.currentTime = 0; | |
| beepSound.play(); | |
| } | |
| function playEndBeep() { | |
| endSound.currentTime = 0; | |
| endSound.play(); | |
| } | |
| </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=XiaoxiaoBR/timerwod" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |