let input = document.getElementById('select-json'); let textInput = document.getElementById('text-json'); let text = document.getElementById('text'); let simulation; let links; let nodes; let context; textInput.onchange = function change() { try { json = JSON.parse(this.value); nodes = buildNodes(json); links = buildLinks(json); } catch (e) { alert(e.message); return; } render(json); } input.onchange = function change() { var reader = new FileReader(); reader.onload = () => { let text = reader.result; try { json = JSON.parse(text); nodes = buildNodes(json); links = buildLinks(json); } catch (e) { alert(e.message); return; } render(json); } reader.readAsText(this.files[0]); }; function buildNodes(json) { let keys = Object.keys(json); return keys.map(key => ({ id: key, group: 1 })); } function buildLinks(json) { let keys = Object.keys(json); let lengths = {}; keys.forEach(key => lengths[key] = json[key].length); return keys.reduce((acc, key) => { let list = []; json[key].forEach(t => { if (keys.indexOf(t) > -1) { list.push({ source: key, target: t }); } }); return acc.concat(list); }, []); } function render(json) { let n = nodes.length; text.innerText = `Total of ${n} nodes`; let canvas = document.querySelector("canvas"); let width = window.innerWidth, height = window.innerHeight; context = canvas.getContext("2d"); d3.select(canvas) .attr('width', width * window.devicePixelRatio) .attr('height', height * window.devicePixelRatio) .style('width', width + 'px') .style('height', height + 'px'); context.scale(window.devicePixelRatio, window.devicePixelRatio); simulation = d3.forceSimulation() .force('link', d3.forceLink().id(d => d.id).distance(300)) .force('charge', d3.forceManyBody()) .force('center', d3.forceCenter(width / 2, height / 2)); simulation.nodes(nodes) .on('tick', ticked); simulation.force('link') .links(links); d3.select(canvas) .call(d3.drag() .container(canvas) .subject(dragsubject) .on('start', dragstarted) .on('drag', dragged) .on('end', dragended)); function ticked() { context.clearRect(0, 0, width, height); links.forEach(drawLink); nodes.forEach(drawNode); } function dragsubject() { return simulation.find(d3.event.x, d3.event.y); } } function dragstarted() { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d3.event.subject.fx = d3.event.subject.x; d3.event.subject.fy = d3.event.subject.y; } function dragged() { d3.event.subject.fx = d3.event.x; d3.event.subject.fy = d3.event.y; } function dragended() { if (!d3.event.active) simulation.alphaTarget(0); d3.event.subject.fx = null; d3.event.subject.fy = null; } function drawLink(d) { context.beginPath(); context.moveTo(d.source.x, d.source.y); context.lineTo(d.target.x, d.target.y); context.lineWidth = 3; if (d.target.id === "127.0.0.1" || d.source.id === "127.0.0.1") { context.strokeStyle = "red"; } else if (d.target.id === "127.0.0.2" || d.source.id === "127.0.0.2") { context.strokeStyle = "green"; } else { context.strokeStyle = "#aaa"; } context.stroke(); } function drawNode(d) { context.beginPath(); context.moveTo(d.x + 10, d.y); context.arc(d.x, d.y, 10, 0, 2 * Math.PI); context.lineWidth = 1; if (d.id === "127.0.0.1") { context.fillStyle = "red"; } else if (d.id === "127.0.0.2" ) { context.fillStyle = "green"; } else { context.fillStyle = "black"; } context.fill(); context.strokeStyle = "#fff"; context.stroke(); }