function filterTable(searchInputId, tableId) { const filter = document.getElementById(searchInputId).value.toUpperCase(); const rows = document.getElementById(tableId).getElementsByTagName("tr"); for (let i = 1; i < rows.length; i++) { rows[i].style.display = "none"; for (const cell of rows[i].getElementsByTagName("td")) { if ((cell.textContent || cell.innerText).toUpperCase().includes(filter)) { rows[i].style.display = ""; break; } } } } function sortTable(tableId, columnIndex, th) { const tbody = document.getElementById(tableId).tBodies[0]; const rows = Array.from(tbody.rows); const headers = Array.from(th.parentNode.children); headers.forEach(h => h !== th && (h.className = "", h.dataset.sortState = "none")); const state = th.dataset.sortState || "none"; const newState = state === "none" ? "asc" : state === "asc" ? "desc" : "none"; th.dataset.sortState = newState; th.className = newState !== "none" ? newState : ""; if (newState === "none") { rows.sort((a, b) => a.dataset.index - b.dataset.index); } else { rows.sort((a, b) => { const aTxt = a.cells[columnIndex].textContent.trim(); const bTxt = b.cells[columnIndex].textContent.trim(); const aNum = parseFloat(aTxt.replace(/[^0-9.-]/g, "")); const bNum = parseFloat(bTxt.replace(/[^0-9.-]/g, "")); const numeric = !isNaN(aNum) && !isNaN(bNum); const cmp = numeric ? aNum - bNum : aTxt.localeCompare(bTxt, undefined, { numeric: true }); return newState === "asc" ? cmp : -cmp; }); } rows.forEach(r => tbody.appendChild(r)); } async function loadBgpTables() { const ipv4Sum = document.getElementById("ipv4Summary"); const ipv6Sum = document.getElementById("ipv6Summary"); const ipv4Body = document.getElementById("ipv4TableBody"); const ipv6Body = document.getElementById("ipv6TableBody"); try { const res = await fetch("/bgp/json"); const data = await res.json(); ipv4Sum.innerHTML = buildSummary("IPv4", data.ipv4_info); ipv6Sum.innerHTML = buildSummary("IPv6", data.ipv6_info); const bfdPeersSet = new Set(data.bfd_peers); renderPeers(ipv4Body, data.ipv4_peers, bfdPeersSet); renderPeers(ipv6Body, data.ipv6_peers, bfdPeersSet); activatePopovers(); } catch (e) { showError(ipv4Sum, ipv4Body, "IPv4"); showError(ipv6Sum, ipv6Body, "IPv6"); } } function buildSummary(label, info) { const popContent = ` BGP Router ID: ${info.router_id}
Local AS: ${info.local_as}
VRF ID: ${info.vrf_id}
RIB Entries: ${info.rib_entries} (using ${info.rib_memory})
Peers: ${info.peers} (using ${info.peers_memory})
BGP Table Version: ${info.table_version} `.trim(); return `

${label} Unicast Summary

ℹ️
Peers: ${info.peers}
`; } function renderPeers(tbody, peers, bfdPeersSet) { tbody.innerHTML = ""; if (!peers.length) { tbody.innerHTML = `No data available.`; return; } const ip_version = tbody.id.includes('ipv4') ? 'ipv4' : 'ipv6'; peers.forEach((p, i) => { const popTitle = `${p.as_number} details`; const popContent = ` Messages Received: ${p.msg_received}
Messages Sent: ${p.msg_sent}
Inbound Queue: ${p.in_queue}
Outbound Queue: ${p.out_queue} `.trim(); const hasBfd = bfdPeersSet.has(p.neighbor); const tr = document.createElement("tr"); tr.dataset.index = i; tr.innerHTML = ` ${p.neighbor}
${p.as_number} ${p.up_down} ${p.state_pfx_rcd} ${p.prefix_sent} ${p.description} ${hasBfd ? ` ` : ''} `; tbody.appendChild(tr); }); } function activatePopovers() { const popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]')); popoverTriggerList.map(function (popoverTriggerEl) { return new bootstrap.Popover(popoverTriggerEl); }); } function activateTooltips() { const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); tooltipTriggerList.map(function (tooltipTriggerEl) { return new bootstrap.Tooltip(tooltipTriggerEl); }); } function showError(sumEl, bodyEl, label) { sumEl.textContent = `Error fetching ${label} data.`; bodyEl.innerHTML = `Error retrieving data.`; } function refreshBGPTable() { ["ipv4Search", "ipv6Search"].forEach(id => (document.getElementById(id).value = "")); toggleRefresh(true); loadBgpTables().finally(() => toggleRefresh(false)); } function toggleRefresh(loading) { document.getElementById("refreshIcon").classList.toggle("d-none", loading); document.getElementById("refreshSpinner").classList.toggle("d-none", !loading); document.getElementById("refreshIcon2").classList.toggle("d-none", loading); document.getElementById("refreshSpinner2").classList.toggle("d-none", !loading); } document.addEventListener("DOMContentLoaded", loadBgpTables);