1/** 2 * Fast WebX - Scripts Principais (Vanilla JS) 3 * Focado em performance e interatividade zero-bloat. 4 */ 5 6document.addEventListener('DOMContentLoaded', function() { 7 8 // 1. Botão de Copiar Código 9 const preBlocks = document.querySelectorAll('pre'); 10 11 preBlocks.forEach((pre) => { 12 // Evita duplicar o botão se o script rodar mais de uma vez 13 if (pre.querySelector('.fwx-copy-btn')) return; 14 15 const copyBtn = document.createElement('button'); 16 copyBtn.className = 'fwx-copy-btn'; 17 copyBtn.type = 'button'; 18 copyBtn.innerText = 'Copiar'; 19 20 pre.appendChild(copyBtn); 21 22 copyBtn.addEventListener('click', function() { 23 // Seleciona o elemento de código (prioriza a tag <code> dentro do <pre>) 24 const codeElement = pre.querySelector('code') || pre; 25 26 // Captura o texto limpando possÃveis espaços extras e ignorando o próprio botão 27 let codeText = ''; 28 if (pre.querySelector('code')) { 29 codeText = pre.querySelector('code').innerText; 30 } else { 31 // Se não houver <code>, clona o <pre> para remover o botão antes de pegar o texto 32 const clone = pre.cloneNode(true); 33 const btnInClone = clone.querySelector('.fwx-copy-btn'); 34 if (btnInClone) btnInClone.remove(); 35 codeText = clone.innerText; 36 } 37 38 // Função para dar feedback visual de sucesso 39 const showSuccess = () => { 40 copyBtn.innerText = 'Copiado!'; 41 copyBtn.classList.add('copied'); 42 setTimeout(() => { 43 copyBtn.innerText = 'Copiar'; 44 copyBtn.classList.remove('copied'); 45 }, 2000); 46 }; 47 48 // Tenta usar a API moderna de Clipboard 49 if (navigator.clipboard && window.isSecureContext) { 50 navigator.clipboard.writeText(codeText.trim()).then(showSuccess).catch(err => { 51 console.error('Falha ao copiar: ', err); 52 }); 53 } else { 54 // Fallback: Método antigo para navegadores sem HTTPS ou suporte limitado 55 const textArea = document.createElement("textarea"); 56 textArea.value = codeText.trim(); 57 textArea.style.position = "fixed"; 58 textArea.style.left = "-9999px"; 59 textArea.style.top = "0"; 60 document.body.appendChild(textArea); 61 textArea.focus(); 62 textArea.select(); 63 try { 64 document.execCommand('copy'); 65 showSuccess(); 66 } catch (err) { 67 console.warn('Erro no fallback de cópia:', err); 68 } 69 document.body.removeChild(textArea); 70 } 71 }); 72 }); 73 74 // 2. Botão Voltar ao Topo (Preparação para Fase B, Item 3.1) 75 const backToTop = document.getElementById('fwx-back-to-top'); 76 if (backToTop) { 77 window.addEventListener('scroll', function() { 78 if (window.pageYOffset > 300) { 79 backToTop.classList.add('visible'); 80 } else { 81 backToTop.classList.remove('visible'); 82 } 83 }); 84 85 backToTop.addEventListener('click', function(e) { 86 e.preventDefault(); 87 window.scrollTo({ top: 0, behavior: 'smooth' }); 88 }); 89 } 90 91 // 3. Exit Intent Popup 92 const exitPopup = document.getElementById('fwx-exit-popup'); 93 if (exitPopup) { 94 const frequency = exitPopup.getAttribute('data-frequency') || 'always'; 95 96 const shouldShow = () => { 97 if (frequency === 'always') { 98 return true; 99 } 100 if (frequency === 'session') { 101 return !sessionStorage.getItem('fwx_exit_popup_session_showed'); 102 } 103 if (frequency === 'permanent') { 104 return !localStorage.getItem('fwx_exit_popup_showed'); 105 } 106 107 // Frequências baseadas em tempo (1m, 1, 7, 30) 108 const lastTime = localStorage.getItem('fwx_exit_popup_last_time'); 109 if (!lastTime) { 110 return true; 111 } 112 113 const diff = Date.now() - parseInt(lastTime, 10); 114 let limit = 0; 115 116 if (frequency === '1m') { 117 limit = 60 * 1000; // 1 minuto 118 } else if (frequency === '1') { 119 limit = 24 * 60 * 60 * 1000; // 1 dia 120 } else if (frequency === '7') { 121 limit = 7 * 24 * 60 * 60 * 1000; // 1 semana 122 } else if (frequency === '30') { 123 limit = 30 * 24 * 60 * 60 * 1000; // 1 mês 124 } 125 126 return diff > limit; 127 }; 128 129 const showPopup = () => { 130 if (shouldShow()) { 131 exitPopup.classList.add('active'); 132 133 // Grava o estado conforme a recorrência 134 if (frequency === 'session') { 135 sessionStorage.setItem('fwx_exit_popup_session_showed', 'true'); 136 } else if (frequency === 'permanent') { 137 localStorage.setItem('fwx_exit_popup_showed', 'true'); 138 } else if (['1m', '1', '7', '30'].includes(frequency)) { 139 localStorage.setItem('fwx_exit_popup_last_time', Date.now().toString()); 140 } 141 } 142 }; 143 144 // Detecta mouse saindo pelo topo (Intenção de fechar aba ou mudar URL) 145 document.addEventListener('mouseleave', (e) => { 146 if (e.clientY < 0) { 147 showPopup(); 148 } 149 }); 150 151 // Fechar Popup 152 const closeBtn = exitPopup.querySelector('.fwx-exit-popup-close');
153 const overlay = exitPopup.querySelector('.fwx-exit-popup-overlay'); 154 155 if (closeBtn) { 156 closeBtn.addEventListener('click', () => { 157 exitPopup.classList.remove('active'); 158 }); 159 } 160 161 if (overlay) { 162 overlay.addEventListener('click', () => { 163 exitPopup.classList.remove('active'); 164 }); 165 } 166 } 167 168});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.