PageSourceSearch

https://transparencia.saojosedopiaui.pi.gov.br/novo/js/filtro_busca.js

js saojosedopiaui.pi.gov.br collected 2026-10-01 01:21:01 UTC 16,636 bytes, 504 lines download raw bytes

1document.addEventListener('DOMContentLoaded', function () {
2    //bloqueando input de consulta
3    // const inputBusca = document.getElementById('descricao');
4    // const placeholderOriginal = inputBusca.placeholder;
5    
6    // inputBusca.disabled = true;
7    // inputBusca.placeholder = 'Carregando informações...';
8
9    // delay para garantir que o menu seja numerado antes da busca 
10    // const tempoEspera = 3000
11    // const tempoEspera = 0
12    // setTimeout(() => {
13    //     busca_menu();
14    //     inputBusca.disabled = false; 
15    //     inputBusca.placeholder = placeholderOriginal;
16    // }, tempoEspera);
17
18    busca_menu();
19
20});
21
22
23function busca_menu() {
24
25    var PAGINAS = [
26        { caminho: '/transparencia',      cor: 'amarelo' },
27        { caminho: '/acessoinformacao',   cor: 'azul'    },
28        { caminho: '/servicosonline',     cor: 'verde'   },
29        { caminho: '/informacoesgerais',  cor: 'vermelho' }
30    ];
31    var CORES_ORIGEM = {
32        amarelo:  { fundo: '#fffbe6', hover: '#fff3b0', icone: '#d4a017', texto: '#6b4c00' },
33        azul:     { fundo: '#e8f4fd', hover: '#c5e3f7', icone: '#2980b9', texto: '#0c3558' },
34        verde:    { fundo: '#eafaf1', hover: '#c3f0d5', icone: '#27ae60', texto: '#0d4a26' },
35        vermelho: { fundo: '#fdecea', hover: '#f9c9c5', icone: '#c0392b', texto: '#5c0c08' }
36    };
37
38    var SINONIMOS = {
39        // 2. INFORMAÇÕES INSTITUCIONAIS    
40        'atribuicoes': [
41            'Secretarias (Competências e Responsáveis)'
42        ],
43        //   'contatos': [
44        //     'Pedido de Informação',
45        //     'Ouvidoria',
46        //     'Saúde - Local e horário de atendimento'
47        //   ],
48        'telefone': [
49            'Ouvidoria',
50            'Pedido de Informação'
51        ],
52        'respostas': [
53            'Dúvidas e Perguntas Frequêntes'
54        ],
55
56        // 4. DESPESAS
57        'fornecedores': [
58            'Credores'
59        ],
60
61        // 5. CONVÊNIOS
62        'convenio': [
63            'Transferências Voluntárias Recebidas',
64            'Transferências Voluntárias Realizadas'
65        ],
66        'acordo': [
67            'Convênios Recebidos',
68            'Relação de parcerias (Lei nº 13.019/2014, art. 10)'
69        ],
70        'acordos firmados': [
71            'Convênios Recebidos',
72            'Relação de parcerias (Lei nº 13.019/2014, art. 10)'
73        ],
74
75        // 6. RH
76        'funcionarios': [
77            'Servidores Ativos'
78        ],
79        'salario': [
80            'Padrão de remunerações',
81            // 'Contracheque Online 2018 aos Atuais',
82            // 'Contracheque Online 2017 e anteriores'
83        ],
84        'remuneracao': [
85            'Padrão de remunerações'
86        ],
87        'remuneracao nominal': [
88            'Padrão de remunerações'
89        ],
90
91        // 8. LICITAÇÕES
92        'contratacoes': [
93            'Contratos Administrativos',
94            'Planos de Contratações Anual'
95        ],
96        // 9. CONTRATOS
97        'termos aditivos': [
98            'Contratos Administrativos'
99        ],
100
101        // 12. SIC
102        'sic': [
103            'Pedido de Informação'
104        ],
105        'esic': [
106            'Pedido de Informação'
107        ],
108        'lai': [
109            'decretos',
110            'portarias'
111        ],
112        'relatorio anual estatistico':[
113            'Estatísticas de Atendimento'
114        ],
115        'relatorio estatistico': [
116            'Estatísticas de Atendimento'
117        ],
118        'lista de documentos classificados': [
119            'Rol de documentos classificados em cada grau de sigilo, com identificação para referência futura'
120        ],
121        'lista de informacoes desclassificadas': [
122            'Rol das informações que tenham sido desclassificadas nos últimos 12 (doze) meses'
123        ],
124        'informacoes desclassificadas': [
125            'Rol das informações que tenham sido desclassificadas nos últimos 12 (doze) meses'
126        ],
127
128        // 14. OUVIDORIA
129        'denuncia': [
130            'Ouvidoria'
131        ],
132        'reclamacao': [
133            'Ouvidoria'
134        ],
135    };
136
137    var campoBusca = document.getElementById('descricao');
138    var formulario = document.getElementById('form_pesquisa');
139
140    if (!campoBusca || !formulario) return;
141
142    formulario.parentNode.style.position = 'relative';
143
144    var dropdown = document.createElement('div');
145    dropdown.id = 'dropdown-filtro';
146    formulario.parentNode.appendChild(dropdown);
147
148    adicionarEstilos();
149
150    var todosOsItens = [];
151    var itemSelecionado = -1;
152
153    // Inicia carregando os itens das 4 páginas
154    coletarItensDe4Paginas().then(function (itens) {
155        todosOsItens = itens;
156    });
157
158    // Monta a URL base do servidor para fazer a as buscas nos 4 módulos do portal
159    function obterOrigemServidor() {
160        var origem = window.location.origin; 
161        var pathname = window.location.pathname;
162
163        //pega apenas a url + nome municipio
164        var urlComNomeMunicipio = pathname.split('/').filter(function(seg) {
165            return seg !== '';
166        })[0];
167    
168        return origem + (urlComNomeMunicipio ? '/' + urlComNomeMunicipio : '');
169    }
170    
171    // Busca o HTML de uma página e extrai os itens do menu
172    function buscarItensDePagina(urlCompleta, cor) {
173        return fetch(urlCompleta, { credentials: 'same-origin' })
174            .then(function (resposta) {
175                if (!resposta.ok) throw new Error('Falha ao carregar: ' + urlCompleta);
176                return resposta.text();
177            })
178            .then(function (html) {
179                var parser = new DOMParser();
180                var doc = parser.parseFromString(html, 'text/html');
181                return coletarItensDoMenu(doc, cor);
182            })
183            .catch(function (erro) {
184                console.warn('[Filtro Busca] Não foi possível carregar ' + urlCompleta + ':', erro);
185                return [];
186            });
187    }
188
189    // Coleta itens de todas as 4 páginas 
190    function coletarItensDe4Paginas() {
191        var baseUrl = obterOrigemServidor();
192
193        var promessas = PAGINAS.map(function (pagina) {
194            var url = baseUrl + pagina.caminho;
195            return buscarItensDePagina(url, pagina.cor);
196        });
197
198        return Promise.all(promessas).then(function (resultadosPorPagina) {
199            var itensCombinados = [];
200            resultadosPorPagina.forEach(function (itens) {
201                itensCombinados = itensCombinados.concat(itens);
202            });
203            return itensCombinados;
204        });
205    }
206
207    function buscar(textoBuscado) {
208        var termo = textoBuscado.trim();
209        dropdown.innerHTML = '';
210        itemSelecionado = -1;
211
212        if (!termo) {
213            fechar();
214            return;
215        }
216         // Mostra mensagem de carregamento
217        dropdown.innerHTML = '<div class="sem-resultado">Procurando resultados...</div>';
218        abrir();
219
220        // Delay antes de processar
221        setTimeout(function() {
222            var termoSemAcento = removerAcentos(termo); 
223            if (termoSemAcento == 'sic'){ 
224                resultados = [];
225            
226            }else{            
227                var resultados = todosOsItens.filter(function (item) {
228                    var nomeItem = removerAcentos(item.nome);
229                    var categoriaItem = removerAcentos(item.categoria);
230                    return nomeItem.includes(termoSemAcento) || c
230ategoriaItem.includes(termoSemAcento);
231                });
232            }
233            if (resultados.length === 0) {
234                var sinonimos = buscarSinonimos(termo);
235                
236                if (sinonimos.length > 0) {
237                    resultados = todosOsItens.filter(function (item) {
238                        var nomeItem = removerAcentos(item.nome);
239                        var categoriaItem = removerAcentos(item.categoria);
240                        
241                        // Verifica se algum sinônimo está presente no nome ou categoria
242                        return sinonimos.some(function (sinonimo) {
243                            var sinonimoNormalizado = removerAcentos(sinonimo);
244                            return nomeItem.includes(sinonimoNormalizado) || 
245                                categoriaItem.includes(sinonimoNormalizado);
246                        });
247                    });
248                }
249            }
250
251            dropdown.innerHTML = '';
252            if (resultados.length === 0) {
253                dropdown.innerHTML = '<div class="sem-resultado">Nenhum resultado encontrado.</div>';
254                abrir();
255                return;
256            }
257
258            mostrarResultados(resultados);
259            // abrir();
260        }, 300);
261    }
262
263    // Busca os sinônimos de um termo (palavra completa apenas)
264    function buscarSinonimos(termo) {
265        var termoNormalizado = removerAcentos(termo.toLowerCase().trim());
266        
267        // Verifica se existe uma entrada exata no dicionário de sinônimos
268        if (SINONIMOS.hasOwnProperty(termoNormalizado)) {
269            return SINONIMOS[termoNormalizado];
270        }
271        
272        return [];
273    }
274
275
276    // Organiza e exibe os resultados
277    function mostrarResultados(resultados) {
278        var porCategoria = {};
279        var ordemCategorias = [];
280
281        // Agrupa por categoria
282        resultados.forEach(function (item) {
283            if (!porCategoria[item.categoria]) {
284                porCategoria[item.categoria] = [];
285                ordemCategorias.push(item.categoria);
286            }
287            porCategoria[item.categoria].push(item);
288        });
289
290        ordemCategorias.forEach(function (categoria) {
291            var titulo = document.createElement('div');
292            titulo.className = 'grupo-categoria';
293            titulo.textContent = categoria;
294            dropdown.appendChild(titulo);             
295
296            porCategoria[categoria].forEach(function (item) {
297                var itemDiv = criarItemSugestao(item);
298                dropdown.appendChild(itemDiv);
299            });
300        });
301    }
302
303
304    function criarItemSugestao(item) {
305        var div = document.createElement('div');
306        div.className = 'item-sugestao';
307        div.setAttribute('data-href', item.link);
308        div.setAttribute('data-cor', item.cor || '');
309        div.innerHTML = protegerHTML(item.nome);
310
311        aplicarCorOrigem(div, item.cor, false);
312
313        div.addEventListener('mouseover', function () {
314            aplicarCorOrigem(div, item.cor, true);
315        });
316        div.addEventListener('mouseout', function () {
317            aplicarCorOrigem(div, item.cor, false);
318        });
319        div.addEventListener('mousedown', function (e) {
320            e.preventDefault();
321            if (item.link) window.location.href = item.link;
322        });
323
324        return div;
325    }
326
327    // Aplica cor de fundo e texto de acordo com a origem
328    function aplicarCorOrigem(elemento, cor, hover) {
329        var cores = CORES_ORIGEM[cor];
330        if (!cores) return;
331
332        elemento.style.backgroundColor = hover ? cores.hover : cores.fundo;
333        elemento.style.color = cores.texto;
334    }
335
336    function abrir() {
337        dropdown.style.display = 'block';
338    }
339
340    function fechar() {
341        dropdown.style.display = 'none';
342        itemSelecionado = -1;
343    }
344
345    // Remove acentos para busca (c encontra ç, a encontra á, etc)
346    function removerAcentos(texto) {
347        return texto
348            .toLowerCase()
349            .normalize('NFD')
350            .replace(/[\u0300-\u036f]/g, '');
351    }
352
353    // Protege contra XSS
354    function protegerHTML(texto) {
355        return texto
356            .replace(/&/g, '&amp;')
357            .replace(/</g, '&lt;')
358            .replace(/>/g, '&gt;');
359    }
360
361    function coletarItensDoMenu(doc, cor) {
362        var itens = [];
363        var menuPrincipal = doc.querySelector('ul.nav.nav-pills');
364
365        if (!menuPrincipal) return itens;
366
367        var secoes = menuPrincipal.querySelectorAll(':scope > li.nav-item.bg-warning');
368
369        secoes.forEach(function (secao) {
370            var linkPrincipal = secao.querySelector(':scope > a');
371            var nomeCategoria = linkPrincipal ? limparTexto(linkPrincipal) : '';
372            var linkCategoria = (linkPrincipal && !linkPrincipal.getAttribute('data-toggle'))
373                ? linkPrincipal.getAttribute('href') : null;
374
375            if (linkCategoria) {
376                itens.push({
377                    categoria: nomeCategoria,
378                    nome: nomeCategoria,
379                    link: linkCategoria,
380                    cor: cor
381                });
382            }
383
384            var subItens1 = secao.querySelectorAll(':scope > .collapse > ul > li.nav-item');
385
386            subItens1.forEach(function (subItem) {
387                var link = subItem.querySelector(':scope > a');
388                var nome = link ? limparTexto(link) : '';
389                var href = (link && !link.getAttribute('data-toggle')) ? link.getAttribute('href') : null;
390
391                if (href) {
392                    itens.push({
393                        categoria: nomeCategoria,
394                        nome: nome,
395                        link: href,
396                        cor: cor
397                    });
398                }
399
400                var subItens2 = subItem.querySelectorAll(':scope > .collapse > ul > li.nav-item');
401
402                subItens2.forEach(function (subItem2) {
403                    var link2 = subItem2.querySelector(':scope > a');
404                    var nome2 = link2 ? limparTexto(link2) : '';
405                    var href2 = (link2 && !link2.getAttribute('data-toggle')) ? link2.getAttribute('href') : null;
406
407                    if (href2) {
408                        itens.push({
409                            categoria: nomeCategoria + ' › ' + nome,
410                            nome: nome2,
411                            link: href2,
412                            cor: cor
413                        });
414                    }
415                });
416            });
417        });
418
419        return itens;
420    }
421
422    function limparTexto(elemento) {
423        var clone = elemento.cloneNode(true);
424        clone.querySelectorAll('i, .fa, svg').forEach(function (icone) {
425            icone.remove();
426        });
427        return clone.textContent.replace(/\s+/g, ' ').trim();
428    }
429
430    function adicionarEstilos() {
431        var estilos = document.createElement('style');
432        estilos.textContent = `
433            #dropdown-filtro {
434                position: absolute;
435                top: 100%;
436                left: 0;
437                min-width: 450px;
438                max-width: 480px;
439                max-height: 380px;
440                overflow-y: auto;
441                background: #fff;
442                border: 1px solid #d0d7de;
443                border-radius: 0 0 8px 8px;
444                box-shadow: 0 8px 24px rgba(0,0,0,0.13);
445                z-index: 9999;
446                display: none;
447                font-family: "Roboto Condensed", sans-serif;
448            }
449            #dropdown-filtro .grupo-categoria {
450                padding: 6px 14px 2px 14px;
451                font-size: 15px;
452                font-weight: 700;
453                text-transform: uppercase;
454                letter-spacing: .08em;
455                color: #888;
456                background: #f8f8f8;
457                border-top: 1px solid #eee;
458                user-select: none;
459            }
460            #dropdown-filtro .grupo-categoria:first-child {
461                border-top: none;
462            }
463            #dropdown-filtro .item-sugestao {
464                display: flex;
465                align-items: center;
466                padding: 9px 14px 9px 20px;
467                cursor: pointer;
468                font-size: 17px;
469                border-bottom: 1px solid #f3f3f3;
470                transition: background-color .15s, color .15s;
471                gap: 8px;
472            }
473
474            #dropdown-filtro .sem-resultado {
475                padding: 12px 14px;
476                color: #999;
477                font-size: 13px;
478                text-align: center;
479            }
480        `;
481        document.head.appendChild(estilos);
482    }
483
484
485    campoBusca.addEventListener('input', function () {
486        buscar(this.value);
487    });
488
489    campoBusca.addEventListener('focus', function () {
490        if (this.value.trim()) buscar(this.value);
491    });
492
493    campoBusca.addEventListener('blur', function () {
494        setTimeout(fechar, 180);
495    });
496
497    document.addEventListener('click', function (e) {
498        if (!formulario.parentNode.contains(e.target)) {
499            fechar();
500        }
501    });
502
503    console.log('busca pronta!');
504};

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.