// Data processing // =============== d3.json("network.json", function (error, originalData) { if (error) throw error; var state = { filter: "", filteredLinks: originalData.links.slice(), filteredNodes: originalData.nodes.slice() }; // Prepare all data structures var linksUpdate, linksEnter, linksMerge, linksExit, nodesUpdate, nodesEnter, nodesMerge, nodesExit; // Define color palet to use for nodes var color = d3.scaleOrdinal(d3.schemeCategory10); // The SVG element already exists, get a reference to it var svg = d3 .select("svg") .classed("svg-content", true) var g_links = svg.append("g").attr("class", "links") var g_nodes = svg.append("g").attr("class", "nodes") // Describe zoom behavior var zoom = d3.zoom() .scaleExtent([1 / 10, 4]) .on("zoom", zoomed); // Describe forces to be used for the simulation var simulation = d3.forceSimulation() .force("link", d3.forceLink().id(function (d) { return d.id; })) .force("center", d3.forceCenter(svg.attr("width") / 2, svg.attr("height") / 2)) .force("charge", d3.forceManyBody().strength(function (d) { return Math.log(d.value) * -4 })); svg .style("fill", "none") .style("pointer-events", "all") .call(zoom); // Search var optArray = svg.nodes() .map(function (node) { return node.id; }) .sort(); $(function () { $("#search").autocomplete({ source: optArray }); }); // Bind UI event handlers $('#searchbutton').click(function () { console.log("search clicked") }); $('[name="filter"]').change(function (ev) { state.filter = ev.target.value; render(); }); // Tooltips var tool_tip = d3 .tip() .attr("class", "d3-tip") .offset([0, 0]) .html(function (d) { return "IP: " + d.id + "
" + "Links: " + d.value; }); svg.call(tool_tip); // Draw the graph render(); function zoomed() { var transform = d3.event.transform; g_nodes.attr("transform", d3.event.transform); g_links.attr("transform", d3.event.transform); } function dragsubject() { searchRadius = 40; return simulation.find(d3.event.x - svg.attr("height"), d3.event.y - svg.attr("height") / 2, searchRadius); } function dragstarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } function dragged(d) { d.fx = d3.event.x; d.fy = d3.event.y; } function dragended(d) { if (!d3.event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; } function filterLinks(filter) { var filteredLinks = originalData.links; if (filter) { filteredLinks = originalData.links.filter(function (i, n) { return i.proto == filter; }) } return filteredLinks; } function filterNodes() { var ipMap = {}; // Iterate all IPs state.filteredLinks.forEach(function (link) { ipMap[ link.source.id ] = true; ipMap[ link.target.id ] = true; }); var filteredNodes = originalData.nodes.filter(function (node) { return ipMap[node.id] }); return filteredNodes; } function render() { // Update links state.filteredLinks = state.filter ? filterLinks(state.filter) : originalData.links; // Remove all nodes without links state.filteredNodes = state.filter ? filterNodes() : originalData.nodes; simulation .nodes(state.filteredNodes) .on("tick", ticked) .force("link") .links(state.filteredLinks); linksUpdate = g_links .selectAll("line") .data(state.filteredLinks, function (d) { return d.id; }); linksEnter = linksUpdate.enter() .append("line") .style("opacity", 1) .attr("stroke-width", function (d) { return 0.3; }); linksMerge = linksUpdate.merge(linksEnter); linksExit = linksUpdate.exit().transition() .style("opacity", 0) .duration(500) .remove(); nodesUpdate = g_nodes .selectAll("circle") .data(state.filteredNodes, function (d) { return d.id; }); nodesEnter = nodesUpdate.enter() .append("circle") .style("opacity", 1) .attr("r", function (d) { return Math.log(d.value); }) .attr("fill", function (d) { return color(d.group); }) .attr("stroke-opacity", 0.4) .attr("stroke-width", 1) .call(d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended)) .on('mouseover', tool_tip.show) //Added .on('mouseout', tool_tip.hide); //Added nodesMerge = nodesUpdate.merge(nodesEnter); nodesExit = nodesUpdate.exit().transition() .style("opacity", 0) .duration(500) .remove(); nodesMerge.append("title") .attr("dx", 12) .attr("dy", ".35em") .text(function (d) { return d.has_ip_layer; }); ticked(); } function ticked() { linksMerge .attr("x1", function (d) { return d.source.x; }) .attr("y1", function (d) { return d.source.y; }) .attr("x2", function (d) { return d.target.x; }) .attr("y2", function (d) { return d.target.y; }); nodesMerge .attr("cx", function (d) { return d.x; }) .attr("cy", function (d) { return d.y; }); } });