document.addEventListener('DOMContentLoaded', function() { let network = null; let allNodes = new vis.DataSet(); let allEdges = new vis.DataSet(); const form = document.getElementById('bgprtv-form'); if (!form) return; const ipAddressInput = document.getElementById('bgprtv-ip-address'); const loader = document.getElementById('bgprtv-loader'); const errorMessageContainer = document.getElementById('bgprtv-error-message'); const networkContainer = document.getElementById('bgprtv-mynetwork'); const graphUrl = networkContainer.dataset.graphUrl; const filterControls = document.querySelectorAll('input[name="bgprtv-filter"]'); const allCommunitiesCheckbox = document.querySelector('input[value="all"]'); const activeCheckbox = document.querySelector('input[value="active"]'); if (!graphUrl) { errorMessageContainer.textContent = 'Configuration Error: The graph URL is missing.'; errorMessageContainer.style.display = 'block'; form.querySelector('input[type="submit"]').disabled = true; return; } form.addEventListener('submit', function(event) { event.preventDefault(); const ipAddress = ipAddressInput.value; loader.style.display = 'block'; errorMessageContainer.style.display = 'none'; errorMessageContainer.textContent = ''; if (network) { network.destroy(); network = null; } fetch(graphUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ip_address: ipAddress }) }) .then(response => { return response.json().then(data => { if (!response.ok) { throw new Error(data.error || `HTTP error! Status: ${response.status}`); } return data; }); }) .then(data => { loader.style.display = 'none'; if (data.error) { errorMessageContainer.textContent = data.error; errorMessageContainer.style.display = 'block'; } else if (data.nodes && data.nodes.length > 0) { drawGraph(data); } else { errorMessageContainer.textContent = 'Could not parse any valid AS paths from the API response.'; errorMessageContainer.style.display = 'block'; } }) .catch(error => { loader.style.display = 'none'; errorMessageContainer.textContent = error.message; errorMessageContainer.style.display = 'block'; console.error('Error:', error); }); }); function drawGraph(data) { const pathCount = data.path_count || 1; const container = document.getElementById('bgprtv-mynetwork'); const baseHeight = 400; const heightPerLane = 120; const lanes = Math.ceil((pathCount - 1) / 2); const newHeight = baseHeight + (lanes * heightPerLane); container.style.height = `${Math.max(400, newHeight)}px`; allNodes = new vis.DataSet(data.nodes); allEdges = new vis.DataSet(data.edges); const options = { layout: { hierarchical: false }, edges: { arrows: { to: { enabled: true, scaleFactor: 0.7 } }, smooth: { enabled: true, type: "cubicBezier", forceDirection: "horizontal", roundness: 0.85 } }, nodes: { shape: 'box', margin: 15, font: { size: 14, color: '#343a40', multi: 'html', align: 'left' }, borderWidth: 2 }, physics: { enabled: false }, interaction: { dragNodes: true, dragView: true, zoomView: true } }; network = new vis.Network(networkContainer, {}, options); if(activeCheckbox) activeCheckbox.checked = false; if(allCommunitiesCheckbox) allCommunitiesCheckbox.checked = true; filterControls.forEach(cb => { if (cb.value !== 'all' && cb.value !== 'active') cb.checked = false; }); filterGraph(); } function filterGraph() { if (!network) return; const showOnlyActive = activeCheckbox ? activeCheckbox.checked : false; const showAllCommunities = allCommunitiesCheckbox ? allCommunitiesCheckbox.checked : true; const selectedCategories = Array.from(filterControls) .filter(cb => cb.checked && cb.value !== 'active' && cb.value !== 'all') .map(cb => cb.value); const itemFilter = (item) => { const activeFilterPassed = !showOnlyActive || item.is_active; const categoryFilterPassed = showAllCommunities || selectedCategories.includes(item.path_category); return item.path_category === 'global' || (activeFilterPassed && categoryFilterPassed); }; const edgeFilter = (item) => { const activeFilterPassed = !showOnlyActive || item.is_active; const categoryFilterPassed = showAllCommunities || selectedCategories.includes(item.path_category); return activeFilterPassed && categoryFilterPassed; }; const filteredNodes = allNodes.get({ filter: itemFilter }); const filteredEdges = allEdges.get({ filter: edgeFilter }); network.setData({ nodes: new vis.DataSet(filteredNodes), edges: new vis.DataSet(filteredEdges) }); } if(filterControls.length > 0) { filterControls.forEach(checkbox => { checkbox.addEventListener('change', function(e) { const changedValue = e.target.value; if (changedValue === 'all' && e.target.checked) { filterControls.forEach(cb => { if (cb.value !== 'all' && cb.value !== 'active') { cb.checked = false; } }); } else if (changedValue !== 'all' && changedValue !== 'active' && e.target.checked) { allCommunitiesCheckbox.checked = false; } const specificCommunityChecked = Array.from(filterControls).some(cb => cb.checked && cb.value !== 'all' && cb.value !== 'active'); if (!specificCommunityChecked) { allCommunitiesCheckbox.checked = true; } filterGraph(); }); }); } });