{#include main fluid=true} {#script} // Bean dependency graph built with d3.js // Based on https://observablehq.com/@d3/mobile-patent-suits const nodes = [ {#each info:devBeanInfos.getDependencyGraph(currentRequest.getParam('beanId')).nodes} { id:"{it.id}", kind:"{it.kind}", description:"{#if info:devBeanInfos.beanDescription ne "simple"}{it.description}{#else}{it.simpleDescription}{/if}", root:{#if it.id == currentRequest.getParam('beanId')}true{#else}false{/if} }, {/each} ]; const links = [ {#each info:devBeanInfos.getDependencyGraph(currentRequest.getParam('beanId')).links} { source:"{it.source}", target:"{it.target}", type:"{it.type}" }, {/each} ]; const beanId = "{currentRequest.getParam('beanId')}"; const chartContainer = document.getElementById('beanDepGraph_container'); {| const types = ['directDependency','directDependent','dependency','lookup','producer']; const width = chartContainer.clientWidth; const height = chartContainer.clientHeight; const color = d3.scaleOrdinal(types, d3.schemeCategory10); // Legend colors const legendDirectDependency = document.querySelector(".legend-direct-dependency"); legendDirectDependency.style.color = color('directDependency'); const legendDirectDependent = document.querySelector(".legend-direct-dependent"); legendDirectDependent.style.color = color('directDependent'); const legendDependency = document.querySelector(".legend-dependency"); legendDependency.style.color = color('dependency'); const legendLookup = document.querySelector(".legend-lookup"); legendLookup.style.color = color('lookup'); const legendProducer = document.querySelector(".legend-producer"); legendProducer.style.color = color('producer'); function linkArc(d) { const r = Math.hypot(d.target.x - d.source.x, d.target.y - d.source.y); return ` M${d.source.x},${d.source.y} A${r},${r} 0 0,1 ${d.target.x},${d.target.y} `; } const simulation = d3.forceSimulation(nodes) .force("link", d3.forceLink(links).id(d => d.id).distance(function(d) { return d.source.id === beanId || d.target.id === beanId ? 150 : 75; })) .force("charge", d3.forceManyBody().strength(-400)) .force("center", d3.forceCenter(width / 3, height / 2)) .force("x", d3.forceX()) .force("y", d3.forceY()); function dragstart(event, d){ // this line is needed, otherwise the simulation stops after few seconds if (!event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; }; function dragged(event, d) { d.fx = event.x; d.fy = event.y; } function dragended(event, d) { d.fx = event.x; d.fy = event.y; } let onZoom = function (e) { d3.select('svg g').attr('transform', e.transform); } const d3Zoom = d3.zoom() .scaleExtent([0, 1]) .on("zoom", onZoom); const svg = d3.select("#beanDepGraph_area") .attr("preserveAspectRatio", "xMinYMin meet") .attr("viewBox", [0, 0, width, height]) .style("font", "1rem sans-serif") .call(d3Zoom) .append("g"); d3.select("#zoom_in").on("click", function() { d3Zoom.scaleBy(svg.transition().duration(750), 1.2); }); d3.select("#zoom_out").on("click", function() { d3Zoom.scaleBy(svg.transition().duration(750), 0.8); }); svg.append("defs").selectAll("marker") .data(types) .join("marker") .attr("id", d => `arrow-${d}`) .attr("viewBox", "0 -5 10 10") .attr("refX", 15) .attr("refY", -0.5) .attr("markerWidth", 6) .attr("markerHeight", 6) .attr("orient", "auto") .append("path") .attr("fill", color) .attr("d", "M0,-5L10,0L0,5"); const link = svg.append("g") .attr("fill", "none") .attr("stroke-width", 1.5) .selectAll("path") .data(links) .join("path") .attr("stroke", d => color(d.type)) .attr("marker-end", d => `url(${new URL(`#arrow-${d.type}`, location)})`); const node = svg.append("g") .attr("fill", "currentColor") .attr("stroke-linecap", "round") .attr("stroke-linejoin", "round") .selectAll("g") .data(nodes) .join("g") .call(d3.drag().on("drag", dragged).on("end", dragended).on("start", dragstart)); node.append("circle") .attr("stroke", "white") .attr("stroke-width", 1) .attr("r", 5) .style("fill", d => d.root ? "red" : "black"); node.append("a") .attr("xlink:href", d => "beanDependencyGraph?beanId=" + d.id) .append("svg:text") .attr("x", 8) .attr("y", "0.31em") .style("fill", "#1f77b4") .text(d => d.description); simulation.on("tick", () => { link.attr("d", linkArc); node.attr("transform", d => `translate(${d.x},${d.y})`); }); |} {/script} {#style} #beanDepGraph_container { min-height: 800px; } {/style} {#breadcrumbs} Beans{/breadcrumbs} {#title}Bean Dependency Graph{/title} {#body} {#let bean=info:devBeanInfos.getBean(currentRequest.getParam('beanId'))}
{bean.description}
{/let} {/body} {#scriptref} {/scriptref} {/include}