PageSourceSearch

https://www.ibdtransito.org.br/wp-content/plugins/ibd-members-system/ibd-members.js?ver=3.0.1

js ibdtransito.org.br collected 2026-10-10 02:35:33 UTC 14,798 bytes, 297 lines download raw bytes

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.