From 744f764484fde15cd078801f344d6827ffef34c1 Mon Sep 17 00:00:00 2001 From: Blackwhitebear8 Date: Sun, 6 Jul 2025 17:59:09 +0200 Subject: [PATCH] Upload files to "static/js/pages" --- static/js/pages/visual-route.js | 158 ++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 static/js/pages/visual-route.js diff --git a/static/js/pages/visual-route.js b/static/js/pages/visual-route.js new file mode 100644 index 0000000..68348ac --- /dev/null +++ b/static/js/pages/visual-route.js @@ -0,0 +1,158 @@ +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(); + }); + }); + } +}); \ No newline at end of file