nnnnnn

} function updateUI() { timerVal.textContent = formatTime(elapsed); const pct = Math.min(Math.round((elapsed / REQUIRED) * 100), 100); progressFill.style.width = pct + '%'; if (pct >= 100) progressFill.classList.add('done'); // Urgência visual nos últimos 10s timerVal.classList.toggle('urgent', REQUIRED - elapsed <= 10 && elapsed > 0); } function setStatus(dotClass, msg) { statusDot.className = 'dot' + (dotClass ? ' ' + dotClass : ''); statusMsg.textContent = msg; } function showToast(msg, type, duration) { if (duration === undefined) duration = 4000; clearTimeout(toastTimer); toast.textContent = msg; toast.className = 'toast show ' + type; if (duration > 0) { toastTimer = setTimeout(function () { toast.className = 'toast'; }, duration); } } // ─── COMPLETE ────────────────────────────────────────── function complete() { if (state === 'done') return; state = 'done'; clearInterval(tickTimer); closeTab(); elapsed = REQUIRED; updateUI(); iconLock.style.display = 'none'; iconUnlock.style.display = 'block'; iconWrap.classList.add('bounce'); setTimeout(function () { iconWrap.classList.remove('bounce'); }, 400); gateTitle.textContent = 'Acesso liberado!'; gateSub.textContent = 'Verificação concluída. Desbloqueando conteúdo...'; btn.classList.add('hidden-btn'); iosManual.style.display = 'none'; setStatus('success', 'Verificado — conteúdo liberado'); showToast('✅ Verificação completa! Conteúdo liberado.', 'ok', 0); setTimeout(dismissOverlay, 1000); } function dismissOverlay() { overlay.classList.add('hiding'); setTimeout(function () { overlay.style.display = 'none'; pageContent.classList.add('unlocked'); notice.style.display = 'block'; setTimeout(function () { notice.style.display = 'none'; }, 4000); }, 420); } // ─── RESET ───────────────────────────────────────────── function reset(errorMsg) { if (state === 'done') return; state = 'idle'; elapsed = 0; startedAt = null; clearInterval(tickTimer); progressFill.classList.remove('done'); btn.classList.remove('disabled'); updateUI(); setStatus('error', 'Falhou — toque para tentar novamente'); if (errorMsg) showToast(errorMsg, 'err'); closeTab(); } // ─── INICIAR TIMER ───────────────────────────────────── function beginTimer() { state = 'verifying'; elapsed = 0; startedAt = Date.now(); updateUI(); btn.classList.add('disabled'); setStatus('active', '✅ Aba aberta! Permaneça por 50 segundos'); showToast('🔗 Aba aberta! Permaneça nela pelos 50 segundos. Não volte antes!', 'info', 5000); clearInterval(tickTimer); tickTimer = setInterval(function () { if (state !== 'verifying') { clearInterval(tickTimer); return; } // Verifica se a aba foi fechada (funciona em desktop; iOS não expõe isso) if (newTabWin && !isIOS) { try { if (newTabWin.closed) { reset('❌ Você fechou a nova aba antes do tempo. Tente novamente.'); return; } } catch(e) {} } const elapsedSec = Math.floor((Date.now() - startedAt) / 1000); if (elapsedSec >= REQUIRED) { complete(); } else { elapsed = elapsedSec; updateUI(); const remaining = REQUIRED - elapsedSec; if (!document.hidden && (remaining === 20 || remaining === 10 || remaining === 5)) { showToast('⏱️ ' + remaining + ' segundos restantes. Fique na outra aba!', 'warn', 2500); } } }, 1000); } // ─── FECHAR ABA DA REFERÊNCIA ────────────────────────── /* window.close() SÓ funciona em janelas abertas pelo próprio script via window.open(). Por isso: - NUNCA usamos 'noopener' → o browser mantém a referência e close() funciona. - O href é apenas fallback visual; o JS sempre chama preventDefault() e abre via window.open() para garantir a referência. - Em iOS Safari isso funciona desde que window.open() seja chamado de forma SÍNCRONA dentro do handler de toque (o que fazemos aqui). */ function closeTab() { if (!newTabWin) return; try { if (!newTabWin.closed) newTabWin.close(); } catch(e) {} newTabWin = null; } // ─── ABRIR LINK (iOS-SAFE) ───────────────────────────── /* ESTRATÉGIA: 1. Sempre abrimos via window.open() SÍNCRONO no evento de toque → iOS permite. 2. NÃO usamos 'noopener' → mantemos referência → close() funciona depois. 3. Se window.open() retornar null (popup blocker ativo pelo usuário), mostramos link manual e não iniciamos o timer. 4. preventDefault() no evita duplicar a abertura pelo href. */ function startVerification(e) { if (state !== 'idle') { if (state === 'verifying') { showToast('⏳ Verificação em andamento. Continue na outra aba!', 'warn', 2500); } if (e) e.preventDefault(); return; } // Sempre previne o href para controlar a abertura via JS if (e) e.preventDefault(); // Abre de forma síncrona — iOS Safari permite se estiver dentro do handler de toque try { // SEM 'noopener': mantemos a referência para poder fechar depois newTabWin = window.open(TARGET_URL, '_blank'); } catch(err) { newTabWin = null; } if (!newTabWin || newTabWin.closed) { // window.open() bloqueado pelo usuário nas configurações do browser newTabWin = null; showToast('⚠️ Pop-up bloqueado. Permita pop-ups e tente novamente.', 'err', 7000); iosManual.style.display = 'block'; return; } beginTimer(); } // ─── LINK MANUAL (iOS fallback) ──────────────────────── manualLink.addEventListener('click', function () { if (state === 'idle') { beginTimer(); iosManual.style.display = 'none'; } }); // ─── EVENTOS DO BOTÃO PRINCIPAL ──────────────────────── btn.addEventListener('click', function (e) { startVerification(e); }); // Touch event explícito para iOS (garante que o handler rode antes do default) btn.addEventListener('touchend', function (e) { // Deixa o handler de click acima tratar — apenas prevenimos o double-fire e.stopPropagation(); }, { passive: true }); // ─── DETECÇÃO DE RETORNO À ABA ───────────────────────── function handleReturn() { if (state !== 'verifying' || !startedAt) return; const elapsedSec = Math.floor((Date.now() - startedAt) / 1000); if (elapsedSec >= REQUIRED) { complete(); } else { elapsed = elapsedSec; updateUI(); const remaining = REQUIRED - elapsedSec; showToast('⚠️ Você voltou cedo! ' + remaining + 's restantes. Volte para a outra aba.', 'warn', 4500); setStatus('', 'Volte para a outra aba (' + remaining + 's restantes)'); } } // focus (desktop) window.addEventListener('focus', handleReturn); // visibilitychange (iOS Safari — evento mais confiável que focus) document.addEventListener('visibilitychange', function () { if (!document.hidden) handleReturn(); if (document.hidden && state === 'verifying') { setStatus('active', '⏱️ Timer ativo — permaneça na outra aba'); } }); // pageshow (bfcache / back-forward em iOS) window.addEventListener('pageshow', function (e) { if (state === 'verifying' && startedAt) { const elapsedSec = Math.floor((Date.now() - startedAt) / 1000); if (elapsedSec >= REQUIRED) { complete(); } else { elapsed = elapsedSec; updateUI(); } } }); // blur (saindo desta aba) window.addEventListener('blur', function () { if (state === 'verifying') { setStatus('active', '⏱️ Timer ativo — permaneça na outra aba'); } }); // ─── INIT ────────────────────────────────────────────── updateUI(); setStatus('', 'Toque no botão para começar'); })();

Comentários