1/** 2 * IBD MEMBERS SYSTEM - JAVASCRIPT OTIMIZADO PARA CACHE 3 * Versão: 2.2.0 - Cache habilitado e performance otimizada 4 */ 5 6class IBDMembersSystemOptimized { 7 constructor() { 8 this.apiUrl = ibdAjax.apiUrl; 9 // CACHE INTERNO HABILITADO: Versão estática 10 this.version = ibdAjax.version || '2.2.0'; 11 // CACHE INTERNO: 30 minutos de cache para requisições 12 this.membersCache = null; 13 this.cacheExpiry = 30 * 60 * 1000; // 30 minutos de cache 14 this.cacheTimestamp = null; 15 16 // DEBUG DESABILITADO: Sem logs para produção 17 this.debugMode = false; 18 19 this.init(); 20 } 21 22 async init() { 23 this.createHeader(); 24 await this.loadMembros(); 25 } 26 27 createHeader() { 28 const system = document.getElementById('ibd-members-system'); 29 if (!system) { 30 return; 31 } 32 33 const header = document.createElement('div'); 34 header.className = 'ibd-page-header'; 35 header.innerHTML = ` 36 <h1>Membros IBD Trânsito</h1> 37 <p>Conheça os membros que compõem a diretoria e conselho do Instituto Brasileiro de Direito do Trânsito</p> 38 `; 39 40 system.insertBefore(header, system.firstChild); 41 } 42 43 async loadMembros() { 44 const container = document.getElementById('ibd-members-grid'); 45 if (!container) return; 46 47 // Verificar cache interno 48 if (this.membersCache && this.cacheTimestamp && 49 (Date.now() - this.cacheTimestamp) < this.cacheExpiry) { 50 // CACHE INTERNO: Usando dados em cache 51 this.renderMembros(this.membersCache); 52 return; 53 } 54 55 try { 56 // Loading state 57 container.innerHTML = '<div class="ibd-loading">â³ Carregando membros...</div>'; 58 59 // URL da API LIMPA: Sem cache busting ou timestamps 60 const apiUrl = this.apiUrl; 61 62 // HEADERS NORMAIS: Removido no-cache para permitir cache 63 const response = await fetch(apiUrl, { 64 method: 'GET', 65 headers: { 66 'Content-Type': 'application/json', 67 'X-WP-Nonce': ibdAjax.nonce 68 } 69 }); 70 71 if (!response.ok) { 72 throw new Error(`HTTP error! status: ${response.status}`); 73 } 74 75 const data = await response.json(); 76 77 // Armazenar em cache interno 78 this.membersCache = data.posts; 79 this.cacheTimestamp = Date.now(); 80 81 // CACHE INTERNO: Dados armazenados em cache por 30 minutos 82 83 this.renderMembros(data.posts); 84 85 } catch (error) { 86 // DEBUG DESABILITADO: Sem logs de erro em produção 87 this.showError(); 88 } 89 } 90 91 showError() { 92 const container = document.getElementById('ibd-members-grid'); 93 if (!container) return; 94 95 container.innerHTML = ` 96 <div class="ibd-no-results"> 97 <h3>â Erro ao carregar membros da diretoria</h3> 98 <p>Tente recarregar a página ou entre em contato com o suporte.</p> 99 <button onclick="location.reload()" class="ibd-btn-more" style="display: inline-block; margin-top: 15px;"> 100 ð Recarregar Página 101 </button> 102 </div> 103 `; 104 } 105 106 renderMembros(membros) { 107 const container = document.getElementById('ibd-members-grid'); 108 if (!container) { 109 return; 110 } 111 112 if (membros.length === 0) { 113 container.innerHTML = ` 114 <div class="ibd-no-results"> 115 <h3>ð Nenhum membro cadastrado</h3> 116 <p>Não há membros da diretoria cadastrados no momento.</p> 117 </div> 118 `; 119 return; 120 } 121 122 container.innerHTML = membros.map((membro, index) => 123 this.createMemberCard(membro, index) 124 ).join(''); 125 126 // PERFORMANCE: Lazy loading de imagens após renderização 127 this.setupLazyLoading(); 128 } 129 130 // PERFORMANCE: Implementar lazy loading para imagens 131 setupLazyLoading() { 132 const images = document.querySelectorAll('.ibd-member-photo img'); 133 134 if ('IntersectionObserver' in window) { 135 const imageObserver = new IntersectionObserver((entries, observer) => {
136 entries.forEach(entry => { 137 if (entry.isIntersecting) { 138 const img = entry.target; 139 img.src = img.dataset.src || img.src; 140 img.classList.remove('lazy'); 141 observer.unobserve(img); 142 } 143 }); 144 }); 145 146 images.forEach(img => imageObserver.observe(img)); 147 } 148 } 149 150 createMemberCard(membro, index) { 151 // PERFORMANCE: Processar links sociais de forma otimizada 152 const socialLinks = Object.entries(membro.social_links) 153 .filter(([key, value]) => value) 154 .map(([platform, url]) => { 155 // PERFORMANCE: Formatação otimizada para WhatsApp 156 let finalUrl = url; 157 if (platform === 'whatsapp') { 158 finalUrl = this.formatWhatsAppUrl(url); 159 } 160 161 // PERFORMANCE: Ãcones SVG inline para melhor performance 162 const icons = { 163 linkedin: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"> 164 <path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/> 165 </svg>`, 166 facebook: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"> 167 <path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/> 168 </svg>`, 169 instagram: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"> 170 <path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/> 171 </svg>`, 172 whatsapp: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"> 173 <path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893A11.821 11.821 0 0020.885 3.488"/> 174 </svg>`, 175 youtube: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"> 176 <path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/> 177 </svg>` 178 }; 179 return `<a href="${finalUrl}" target="_blank" rel="noopener noreferrer" class="ibd-social-link ibd-social-${platform}" title="${platform.charAt(0).toUpperCase() + platform.slice(1)} de ${membro.name}" aria-label="${platform}">${icons[platform] || 'ð'}</a>`; 180 }) 181 .join(''); 182 183 return ` 184 <div class="ibd-member-card" style="animation-delay: ${index * 0.1}s;"> 185 <div class="ibd-member-photo"> 186 <img src="${membro.photo}" 187 alt="Foto de ${membro.name}" 188 loading="lazy" 189 onerror="this.src='https://images.pexels.com/photos/2182970/pexels-photo-2182970.jpeg?auto=compress&cs=tinysrgb&w=400&h=400&fit=crop'"> 190 </div> 191 <div class="ibd-member-info"> 192 <h3 class="ibd-member-name">${membro.name}</h3>
193 <span class="ibd-member-specialty ${!membro.cargo ? 'ibd-cargo-empty' : ''}">${membro.cargo || 'Cargo não definido'}</span> 194 ${membro.regiao ? `<div class="ibd-member-region">${membro.regiao}</div>` : ''} 195 ${membro.bio ? `<div class="ibd-member-bio">${this.formatMemberBio(membro.bio)}</div>` : ''} 196 ${socialLinks ? `<div class="ibd-social-links">${socialLinks}</div>` : ''} 197 <a href="${membro.saiba_mais_url || membro.permalink}" target="_blank" rel="noopener noreferrer" class="ibd-btn-more" aria-label="Saiba mais sobre ${membro.name}"> 198 Saiba mais â 199 </a> 200 </div> 201 </div> 202 `; 203 } 204 205 /** 206 * PERFORMANCE: Formatar URL do WhatsApp de forma otimizada 207 * @param {string} phoneNumber - Número de telefone ou URL 208 * @returns {string} URL formatada para WhatsApp Web 209 */ 210 formatWhatsAppUrl(phoneNumber) { 211 // PERFORMANCE: Verificação rápida se já é URL válida 212 if (phoneNumber.includes('wa.me') || phoneNumber.includes('whatsapp.com')) { 213 return phoneNumber; 214 } 215 216 // PERFORMANCE: Regex otimizada para limpeza 217 let cleanNumber = phoneNumber.replace(/\D/g, ''); 218 219 // PERFORMANCE: Lógica simplificada para código do paÃs 220 if (cleanNumber.length === 11 && cleanNumber.startsWith('11')) { 221 cleanNumber = '55' + cleanNumber; 222 } else if (cleanNumber.length === 10) { 223 cleanNumber = '5511' + cleanNumber; 224 } else if (cleanNumber.length === 11 && !cleanNumber.startsWith('55')) { 225 cleanNumber = '55' + cleanNumber; 226 } 227 228 return `https://wa.me/${cleanNumber}`; 229 } 230 231 formatMemberBio(bio) { if (!bio) return ''; 232 233 const cleanBio = bio.trim(); 234 235 // A PARTIR DE AGORA: nada de tÃtulo, nada de regex, nada de heurÃstica. 236 return `<div class="ibd-bio-text">${cleanBio}</div>`;} 237 238 // PERFORMANCE: Cache de regex para verificação de maiúsculas 239 isUppercaseWord(word) { 240 if (!this.uppercaseRegex) { 241 this.uppercaseRegex = /^[A-ZÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃ\-&]+$/; 242 } 243 return word.match(/^[A-ZÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃ\-&]+$/) || 244 word === '&' || 245 word === '&'; 246 } 247 248 // Método para limpar cache manualmente 249 clearCache() { 250 this.membersCache = null; 251 this.cacheTimestamp = null; 252 // CACHE LIMPO: Cache interno removido 253 } 254 255 // Método para forçar reload 256 async forceReload() { 257 this.clearCache(); 258 await this.loadMembros(); 259 } 260} 261 262// PERFORMANCE: Inicialização otimizada com verificações mÃnimas 263function initIBDMembers() { 264 if (document.getElementById('ibd-members-grid')) { 265 window.ibdMembersInstance = new IBDMembersSystemOptimized(); 266 } 267} 268 269// PERFORMANCE: Event listeners otimizados 270document.addEventListener('DOMContentLoaded', initIBDMembers); 271 272// PERFORMANCE: Compatibilidade com Elementor 273jQuery(document).ready(function($) { 274 // PERFORMANCE: Timeout otimizado 275 setTimeout(initIBDMembers, 500); 276}); 277 278// PERFORMANCE: Verificação de estado do DOM 279if (document.readyState === 'loading') { 280 document.addEventListener('DOMContentLoaded', initIBDMembers); 281} else { 282 initIBDMembers(); 283} 284 285// Função global para limpar cache 286window.ibdClearCache = function() { 287 if (window.ibdMembersInstance) { 288 window.ibdMembersInstance.clearCache(); 289 } 290}; 291 292// Função global para forçar reload 293window.ibdForceReload = function() { 294 if (window.ibdMembersInstance) { 295 window.ibdMembersInstance.forceReload(); 296 } 297};
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.