1 (function() { 2 'use strict'; 3 4 let diameter = 1280; 5 let radius = diameter / 2; 6 let innerRadius = radius - 240; 7 8 let cluster = d3.cluster(); 9 cluster.size([ 360, innerRadius ]); 10 11 let line = d3.radialLine(); 12 line.curve(d3.curveBundle.beta(0.85)); 13 line.radius(function(d) { return d.y; }); 14 line.angle(function(d) { return d.x / 180 * Math.PI; }); 15 16 let link; 17 let node; 18 let selectedNode; 19 20 function init() { 21 let domListCol = document.createElement("div"); 22 domListCol.id = "violate_list_column"; 23 let domGraphCol = document.createElement("div"); 24 domGraphCol.id = "dep_graph_column"; 25 let domResetBtn = document.createElement("button"); 26 domResetBtn.id = "reset_btn"; 27 domResetBtn.innerHTML = "Reset"; 28 domGraphCol.appendChild(domResetBtn); 29 30 document.body.appendChild(domListCol); 31 document.body.appendChild(domGraphCol); 32 33 let canvas = d3.select("#dep_graph_column").append("svg"); 34 canvas.attr("width", diameter + 200); 35 canvas.attr("height", diameter); 36 37 let svg = canvas.append("g"); 38 svg.attr("transform", "translate(" + (radius + 100) + "," + radius + ")"); 39 40 link = svg.append("g").selectAll(".link"); 41 node = svg.append("g").selectAll(".node"); 42 43 showResult(depData, violatedLibs); 44 } 45 46 function showList(depMap, violatedLibs) { 47 function makeTitle(tagName) { 48 let domTitle = document.createElement("div"); 49 let domText = document.createElement("h3"); 50 domText.innerHTML = tagName; 51 domTitle.appendChild(domText); 52 return domTitle; 53 } 54 function makeButton(libName, count) { 55 let domButton = document.createElement("button"); 56 domButton.className = "violate"; 57 domButton.innerHTML = libName + " (" + count + ")"; 58 domButton.onclick = function() { 59 this.classList.toggle("active"); 60 let currentList = this.nextElementSibling; 61 if (currentList.style.display === "block") { 62 currentList.style.display = "none"; 63 selectedNode = undefined; 64 resetclicked(); 65 } else { 66 currentList.style.display = "block"; 67 if (selectedNode) { 68 selectedNode.classList.toggle("active"); 69 selectedNode.nextElementSibling.style.display = "none"; 70 } 71 selectedNode = domButton; 72 mouseclicked(depMap[libName]); 73 } 74 }; 75 return domButton; 76 } 77 function makeList(domList, list) 78 { 79 for (let i = 0; i < list.length; i++) { 80 domList.appendChild(makeButton(list[i][0], list[i][1])); 81 let domDepList = document.createElement("div"); 82 let depItem = depMap[list[i][0]]; 83 let violates = depItem.data.violates; 84 for (let j = 0; j < violates.length; j++) { 85 let domDepLib = document.createElement("div"); 86 let tag = depMap[violates[j]].data.tag; 87 domDepLib.className = "violate-list"; 88 domDepLib.innerHTML = violates[j] + " [" 89 + tag.substring(tag.lastIndexOf(".") + 1) + "]"; 90 domDepList.appendChild(domDepLib); 91 } 92 domList.appendChild(domDepList); 93 domDepList.style.display = "none"; 94 } 95 } 96 97 let domViolatedList = document.getElementById("violate_list_column"); 98 if ("vendor.private.bin" in violatedLibs) { 99 let list = violatedLibs["vendor.private.bin"]; 100 domViolatedList.appendChild(makeTitle("VENDOR (" + list.length + ")")); 101 makeList(domViolatedList, list); 102 } 103 for (let tag in violatedLibs) { 104 if (tag === "vendor.private.bin") 105 continue; 106 let list = violatedLibs[tag]; 107 if (tag === "system.private.bin") 108 tag = "SYSTEM"; 109 else 110 tag = tag.substring(tag.lastIndexOf(".") + 1).toUpperCase(); 111 domViolatedList.appendChild(makeTitle(tag + " (" + list.length + ")")); 112 makeList(domViolatedList, list); 113 } 114 } 115 116 function showResult(depDumps, violatedLibs) { 117 let root = tagHierarchy(depDumps).sum(function(d) { return 1; }); 118 cluster(root); 119 120 let libsDepData = libsDepends(root.leaves()); 121 showList(libsDepData[1], violatedLibs); 122 link = link.data(libsDepData[0]) 123 .enter() 124 .append("path") 125 .each(function(d) { d.source = d[0], d.target = d[d.length - 1]; }) 126 .attr("class", function(d) { return d.allow ? "link" : "link--violate" }) 127 .attr("d", line); 128 129 node = node.data(root.leaves()) 130 .enter() 131 .append("text") 132 .attr("class", 133 function(d) { 134 return d.data.parent.parent.parent.key == "system" ? 135 (d.data.parent.parent.key == "system.public" ? 136 "node--sys-pub" : 137 "node--sys-pri") : 138 "node"; 139 }) 140 .attr("dy", "0.31em") 141 .attr("transform", 142 function(d) { 143 return "rotate(" + (d.x - 90) + ")translate(" + (d.y + 8) + ",0)" + 144 (d.x < 180 ? "" : "rotate(180)"); 145 }) 146 .attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; }) 147 .text(function(d) { return d.data.key; }) 148 .on("click", mouseclicked); 149 document.getElementById("reset_btn").onclick = resetclicked; 150 } 151 152 function resetclicked() { 153 if (selectedNode) { 154 selectedNode.classList.toggle("active"); 155 selectedNode.nextElementSibling.style.display = "none"; 156 selectedNode = undefined; 157 } 158 link.classed("link--target", false) 159 .classed("link--source", false); 160 node.classed("node--target", false) 161 .classed("node--source", false) 162 .classed("node--selected", false); 163 } 164 165 function mouseclicked(d) { 166 node.each(function(n) { n.target = n.source = false; }); 167 168 link.classed("link--target", 169 function(l) { 170 if (l.target === d) { 171 l.source.source = true; 172 return true; 173 } else { 174 return false; 175 } 176 }) 177 .classed("link--source", 178 function(l) { 179 if (l.source === d) { 180 l.target.target = true; 181 return true; 182 } else { 183 return false; 184 } 185 }) 186 .filter(function(l) { return l.target === d || l.source === d; }) 187 .raise(); 188 189 node.classed("node--target", 190 function(n) { 191 return n.target; 192 }) 193 .classed("node--source", 194 function(n) { return n.source; }) 195 .classed("node--selected", 196 function(n) { 197 return n === d; 198 }); 199 } 200 201 function tagHierarchy(depDumps) { 202 let map = {}; 203 204 function find(name, tag, data) { 205 let node = map[name], i; 206 if (!node) { 207 node = map[name] = data || { name : name, children : [] }; 208 if (name.length) { 209 node.parent = find(tag, tag.substring(0, tag.lastIndexOf("."))); 210 node.parent.children.push(node); 211 node.key = name; 212 } 213 } 214 return node; 215 } 216 217 depDumps.forEach(function(d) { find(d.name, d.tag, d); }); 218 219 return d3.hierarchy(map[""]); 220 } 221 222 function libsDepends(nodes) { 223 let map = {}, depends = []; 224 225 // Compute a map from name to node. 226 nodes.forEach(function(d) { map[d.data.name] = d; }); 227 228 // For each dep, construct a link from the source to target node. 229 nodes.forEach(function(d) { 230 if (d.data.depends) 231 d.data.depends.forEach(function(i) { 232 let l = map[d.data.name].path(map[i]); 233 l.allow = true; 234 depends.push(l); 235 }); 236 if (d.data.violates.length) { 237 map[d.data.name].not_allow = true; 238 d.data.violates.forEach(function(i) { 239 map[i].not_allow = true; 240 let l = map[d.data.name].path(map[i]); 241 l.allow = false; 242 depends.push(l); 243 }); 244 } 245 }); 246 247 return [ depends, map ]; 248 } 249 250 window.onload = init; 251 })();