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');
})();
!doctype>
Comentários
Postar um comentário