PageSourceSearch

https://cedrodesaojoao.se.gov.br/public/assets/js/scripts.utils.js

js cedrodesaojoao.se.gov.br collected 2026-10-02 21:03:36 UTC 16,518 bytes, 510 lines download raw bytes

1// Função para atualizar os links da paginação com o hash da URL
2function UpdatePaginationWithHash() {
3    // Recupera o fragmento da URL
4    const hash = window.location.hash;
5
6    // Verifica se existe um fragmento antes de continuar
7    if (hash) {
8        // Seleciona todos os links de paginação
9        $(".pagination a").each(function () {
10            const link = $(this);
11
12            // Cria um objeto URL para manipular o href
13            // Usa a URL atual como base para preservar o caminho (pathname) quando o href for relativo (ex: "?pagina=2&")
14            const url = new URL(link.attr("href"), window.location.href);
15
16            // Adiciona o fragmento ao final da URL
17            url.hash = hash;
18
19            // Atualiza o atributo href do link
20            link.attr("href", url.toString());
21        });
22    }
23}
24
25function ChangeColorAccordionMonthHeader() {
26    // Evento de clique nos headers do accordion
27    $(".accordion-month").on("click", function () {
28        // Itera sobre todos os headers do accordion filho
29        $(".accordion-month").each(function () {
30            // Verifica se o accordion está expandido (não colapsado)
31            if ($(this).hasClass("collapsed")) {
32                // Remove a classe 'bg-light-primary' caso esteja colapsado
33                $(this).removeClass("bg-light-primary");
34            } else {
35                // Adiciona a classe 'bg-light-primary' caso esteja expandido
36                $(this).addClass("bg-light-primary");
37            }
38        });
39    });
40}
41
42function initializeDataTable($table, tableName) {
43    // Seleciona o elemento correspondente com base no `tableName`
44    const $tableId = $(`#table-list-${tableName}`);
45    if ($tableId.data("datatable-initialized")) {
46        return;
47    }
48
49    const $tableOrder = $tableId.attr("tableOrder") || "desc";
50    const $tableOrderColumnRaw = $tableId.attr("tableOrderColumn");
51    const $tableOrderColumn = Number.isFinite(Number($tableOrderColumnRaw))
52        ? Number($tableOrderColumnRaw)
53        : 0;
54    const tableOrderingAttr = String(
55        $tableId.attr("data-datatable-ordering") || "true"
56    ).toLowerCase();
57    const tableOrderingEnabled = !["false", "0", "no"].includes(
58        tableOrderingAttr
59    );
60    const tablePagingAttr = String(
61        $tableId.attr("data-datatable-paging") || "false"
62    ).toLowerCase();
63    const tablePagingEnabled = ["true", "1", "yes"].includes(
64        tablePagingAttr
65    );
66    const tablePageLengthRaw = Number(
67        $tableId.attr("data-datatable-page-length")
68    );
69    const tablePageLength =
70        Number.isFinite(tablePageLengthRaw) && tablePageLengthRaw > 0
71            ? tablePageLengthRaw
72            : -1;
73
74    // Verifica se a tabela existe no DOM antes de inicializar o DataTable
75    if ($tableId.length) {
76        // Inicializa o DataTable
77        const dataTableOptions = {
78            responsive: false,
79            ordering: tableOrderingEnabled,
80            pageLength: tablePageLength,
81            paging: tablePagingEnabled,
82            lengthChange: false,
83            searching: false,
84            info: tablePagingEnabled,
85            dom: tablePagingEnabled
86                ? "t<'d-flex flex-column flex-md-row justify-content-between align-items-center gap-4 mt-4'ip>"
87                : "<'table-responsive'tr>",
88            language: {
89                sEmptyTable: "Nenhum registro informado",
90                sInfo: "Mostrando de _START_ até _END_ de _TOTAL_ registros",
91                sInfoEmpty: "Mostrando 0 até 0 de 0 registros",
92                sInfoFiltered: "(Filtrados de _MAX_ registros)",
93                sInfoThousands: ".",
94                sLengthMenu: "_MENU_ por página",
95                sLoadingRecords: "Carregando...",
96                sProcessing: "Processando...",
97                sZeroRecords: "Nenhum registro informado",
98                sSearch: "Pesquisar: ",
99                oPaginate: {
100                    sNext: ">>",
101                    sPrevious: "<<",
102                    sFirst: "Primeiro",
103                    sLast: "Último",
104                },
105                oAria: {
106                    sSortAscending: ": Ordenar colunas de forma ascendente",
107                    sSortDescending: ": Ordenar colunas de forma descendente",
108                },
109            },
110        };
111
112        if (tableOrderingEnabled) {
113            dataTableOptions.order = [[$tableOrderColumn, $tableOrder]];
114            dataTableOptions.columnDefs = [
115                {
116                    targets: "_all",
117                    orderable: true,
118                    orderSequence: ["asc", "desc"],
119                },
120            ];
121        }
122
123        new DataTable($tableId, dataTableOptions);
124        $tableId.data("datatable-initialized", true);
125    } else {
126        console.warn(`Tabela com ID #table-list-${tableName} não encontrada.`);
127    }
128}
129
130function loadScriptOnce(key, src) {
131    window.AG_PORTAL_VENDOR_LOADS = window.AG_PORTAL_VENDOR_LOADS || {};
132
133    if (!window.AG_PORTAL_VENDOR_LOADS[key]) {
134        window.AG_PORTAL_VENDOR_LOADS[key] = new Promise((resolve, reject) => {
135            const existingScript = document.querySelector(`script[data-vendor="${key}"]`);
136
137            if (existingScript) {
138                existingScript.addEventListener("load", resolve, { once: true });
139                existingScript.addEventListener("error", reject, { once: true });
140                return;
141            }
142
143            const script = document.createElement("script");
144            script.src = src;
145            script.async = false;
146            script.dataset.vendor = key;
147            script.onload = resolve;
148            script.onerror = reject;
149            document.body.appendChild(script);
150        });
151    }
152
153    return window.AG_PORTAL_VENDOR_LOADS[key];
154}
155
156function loadStyleOnce(key, href) {
157    window.AG_PORTAL_VENDOR_STYLES = window.AG_PORTAL_VENDOR_STYLES || {};
158
159    if (window.AG_PORTAL_VENDOR_STYLES[key]) {
160        return;
161    }
162
163    const existingStyle = document.querySelector(`link[data-vendor="${key}"], link[href="${href}"]`);
164    if (existingStyle) {
165        window.AG_PORTAL_VENDOR_STYLES[key] = true;
166        return;
167    }
168
169    const link = document.createElement("link");
170    link.href = href;
171    link.rel = "stylesheet";
172    link.dataset.vendor = key;
173    document.head.appendChild(link);
174    window.AG_PORTAL_VENDOR_STYLES[key] = true;
175}
176
177function ensureDataTables(callback) {
178    if (typeof DataTable !== "undefined") {
179        callback();
180        return;
181    }
182
183    const hasSortableTables = $("table[tableName], .table[tableName]").length > 0;
184    if (!hasSortableTables) {
185        return;
186    }
187
188    const dataTablesSrc =
189        (window.AG_PORTAL_ASSETS && window.AG_PORTAL_ASSETS.datatablesJs) ||
190        "/assets/plugins/custom/datatables/datatables.bundle.js";
191    const dataTablesCss =
192        (window.AG_PORTAL_ASSETS && window.AG_PORTAL_ASSETS.datatablesCss) ||
193        "/assets/plugins/custom/datatables/datatables.bundle.css";
194
195    loadStyleOnce("datatables-css", dataTablesCss);
196    loadScriptOnce("datatables", dataTablesSrc)
197        .then(callback)
198        .catch(() => {
199            console.error("Não foi possível carregar o DataTables.");
200        });
201}
202
203function SortedTable() {
204    const $tables = $("table[tableName], .table[tableName]");
205
206    // Verifica se há tabelas no DOM
207    if ($tables.length === 0) {
208        return;
209    }
210
211    if (typeof DataTable === "undefined") {
212        ensureDataTables(SortedTable);
213        return;
214    }
215
216    // Itera sobre todas as tabelas e inicializa o DataTable
217    $tables.each(function () {
218        const tableName = $(this).attr("tableName");
219        if (tableName) {
220            initializeDataTable($(this), tableName);
221        } else {
222            console.warn("Atributo 'tableName' não encontrado na tabela.");
223        }
224    });
225}
226
227function validateMinLengthInputs(event) {
228    if (!event) {
229        return;
230    }
231
232    const $inputs = $(".minlength"); // Seleciona os inputs com a classe minlength
233    const minLength = 1; // Define o mínimo de caracteres permitidos
234    $inputs.forEach((input) => {
235        const value = input.val().trim(); // Remove espaços extras
236
237        if (value.length < minLength) {
238            event.preventDefault();
239            alert(
240                `O campo deve conter pelo menos ${minLength} caractere${
241                    minLength > 1 ? "s" : ""
242                }.`
243            );
244            input.focus(); // Mantém o foco no campo
245        }
246    });
247}
248
249$("#searchForm").on("submit", validateMinLengthInputs); // Validação no submit
250
251function fontAdjustAccessibility(acao) {
252    const tamanhoMaximo = 23;
253    const tamanhoMinimo = 11;
254    let html = document.documentElement;
255    let body = document.body;
256    let tamanhoAtual = parseFloat(window.getComputedStyle(html).fontSize);
257
258    if (acao === "+" && tamanhoAtual <= tamanhoMaximo) {
259        tamanhoAtual += 2;
260    } else if (acao === "-" && tamanhoAtual >= tamanhoMinimo) {
261        tamanhoAtual -= 2;
262    } else if (acao === "default") {
263        tamanhoAtual = 13;
264    } else {
265        return;
266    }
267
268    html.style.setProperty("font-size", `${tamanhoAtual}px`, "important");
269    body.style.setProperty("font-size", `${tamanhoAtual}px`, "important");
270
271    localStorage.setItem("tamanhoFonte", tamanhoAtual);
272}
273
274function recoverFontSizeInLocalStorage() {
275    let tamanhoSalvo = localStorage.getItem("tamanhoFonte");
276    if (tamanhoSalvo) {
277        document.body.style.setProperty(
278            "font-size",
279            `${tamanhoSalvo}px`,
280            "important"
281        );
282
283        document.documentElement.style.setProperty(
284            "font-size",
285            `${tamanhoSalvo}px`,
286            "important"
287        );
288    }
289}
290
291function handleThemeToggle() {
292    const $themeToggle = $(".themeToggle");
293
294    $themeToggle.off("click").on("click", function (e) {
295        e.preventDefault();
296        console.log("Theme toggle clicked");
297
298        const currentTheme =
299            document.documentElement.getAttribute("data-bs-theme") || "light";
300        const newTheme = currentTheme === "dark" ? "light" : "dark";
301
302        document.documentElement.setAttribute("data-bs-theme", newTheme);
303        localStorage.setItem("data-bs-theme", newTheme);
304
305        // Atualiza classe visual
306        if (newTheme === "dark") {
307            $(this).addClass("active");
308        } else {
309            $(this).removeClass("active");
310        }
311
312        // Diagnóstico adicional para mobile
313        console.log("Theme set to:", newTheme);
314    });
315
316    // Inicializa o botão com base no tema atual
317    const savedTheme = localStorage.getItem("data-bs-theme") || "light";
318    document.documentElement.setAttribute("data-bs-theme", savedTheme);
319
320    if (savedTheme === "dark") {
321        $themeToggle.addClass("active");
322    } else {
323        $themeToggle.removeClass("active");
324    }
325}
326
327function validateForm(form) {
328    const $inputs = $(form).find(
329        "input[required], textarea[required], select[required]"
330    );
331    let isValid = true;
332
333    $inputs.each(function () {
334        if (!$(this).val()) {
335            isValid = false;
336            $(this).addClass("is-invalid");
337        }
338    });
339
340    return isValid;
341}
342
343const submitForm = {
344    init: () => {
345        // Vincula eventos AJAX (se necessário)
346        $(document).ajaxStart(() => {
347            loading.show();
348        });
349
350        $(document).ajaxStop(() => {
351            loading.hide();
352        });
353
354        // Associa o evento de clique a cada botão com a classe "btn-buscar"
355        $(".btn-buscar").each(function () {
356            $(this).on("click", function (event) {
357                event.preventDefault();
358
359                // Seleciona o formulário mais próximo do botão clicado
360                const $form = $(this).closest("form");
361
362                if ($form.length === 0) {
363                    console.error(
364                        "Nenhum formulário encontrado para este botão."
365                    );
366                    return;
367                }
368
369                const isValid = validateForm($form);
370
371                if (isValid) {
372                    loading.show();
373                    $form.submit(); // Envia o formulário
374                } else {
375                    alert(
376                        "Por favor, preencha todos os campos obrigatórios deste formulário."
377                    );
378                    loading.hide();
379                }
380            });
381        });
382    },
383};
384
385const loading = {
386    init: () => {
387        // Cria o elemento de carregamento e adiciona ao body
388        const loadingEl = document.createElement("div");
389        loadingEl.classList.add(
390            "page-loader",
391            "flex-column",
392            "bg-dark",
393            "bg-opacity-25",
394            "d-none"
395        );
396        loadingEl.innerHTML = `
397            <span class="spinner-border text-primary" role="status"></span>
398            <span class="text-gray-800 fs-6 fw-semibold mt-5">Carregando...</span>
399        `;
400        document.body.prepend(loadingEl);
401    },
402    show: () => {
403        // Mostra o loader
404        $(".page-loader").removeClass("d-none");
405        // Usando Metronic
406        KTApp.showPageLoading();
407    },
408    hide: () => {
409        // Esconde o loader
410        $(".page-loader").addClass("d-none");
411        // Usando Metronic
412        KTApp.hidePageLoading();
413    },
414};
415
416// script.utils.js
417
418const toastStack = {
419    initContainer: () => {
420        // Cria container de toasts se não existir
421        if (!document.querySelector("#toast-container")) {
422            const container = document.createElement("div");
423            container.id = "toast-container";
424            container.className =
425                "toast-container position-fixed bottom-0 end-0 p-3";
426            container.style.zIndex = "1055"; // acima de modal padrão
427            document.body.appendChild(container);
428        }
429    },
430
431    show: (statusCode, message) => {
432        toastStack.initContainer(); // garante que container existe
433
434        const toastContainer = document.getElementById("toast-container");
435
436        const toastWrapper = document.createElement("div");
437        toastWrapper.className = "toast align-items-center border-0";
438        toastWrapper.classList.add(
439            statusCode != 200
440                ? ("bg-danger", "text-danger")
441                : ("bg-light-success", "text-success")
442        );
443        toastWrapper.setAttribute("role", "alert");
444        toastWrapper.setAttribute("aria-live", "assertive");
445        toastWrapper.setAttribute("aria-atomic", "true");
446
447        toastWrapper.innerHTML = `
448            <div class="toast-header">
449                <strong class="me-auto ${
450                    statusCode == 200 ? "text-success" : "text-danger"
451                }">${
452            statusCode == 200 ? `Sucesso ${statusCode}!` : `Erro ${statusCode}!`
453        }</strong>
454                <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
455            </div>
456                <div class="toast-body">
457                    ${message}
458                </div>
459            </div>`;
460
461        toastContainer.appendChild(toastWrapper);
462
463        const bsToast = new bootstrap.Toast(toastWrapper);
464        bsToast.show();
465
466        // Remove do DOM após desaparecer
467        toastWrapper.addEventListener("hidden.bs.toast", () => {
468            toastWrapper.remove();
469        });
470    },
471};
472
473// Exporta se estiver usando módulo ES
474// export default toastStack;
475window.toastStack = toastStack; // torna global caso não esteja usando módulos
476
477$(document).ready(() => {
478    // Atualiza os links da paginação com o hash da URL
479    UpdatePaginationWithHash();
480    ChangeColorAccordionMonthHeader();
481    SortedTable();
482    recoverFontSizeInLocalStorage();
483    handleThemeToggle();
484    validateMinLengthInputs();
485    toastStack.initContainer();
486
487    var tooltipTriggerList = [].slice.call(
488        document.querySelectorAll('[data-bs-toggle="tooltip"]')
489    );
490    var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
491        return new bootstrap.Tooltip(tooltipTriggerEl);
492    });
493
494    // Impede que o menu feche ao clicar nos links internos
495    $(".menu-redimensionamento").on("click", ".menu-link", function (e) {
496        e.stopPropagation();
497    });
498
499    loading.init();
500    submitForm.init();
501
502    // Vincula eventos AJAX
503    $(document).ajaxStart(() => {
504        loading.show();
505    });
506
507    $(document).ajaxStop(() => {
508        loading.hide();
509    });
510});

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.