(function() { 'use strict'; let diameter = 1280; let radius = diameter / 2; let innerRadius = radius - 240; let cluster = d3.cluster(); cluster.size([ 360, innerRadius ]); let line = d3.radialLine(); line.curve(d3.curveBundle.beta(0.85)); line.radius(function(d) { return d.y; }); line.angle(function(d) { return d.x / 180 * Math.PI; }); let link; let node; let selectedNode; let selectedSubNode; function init() { let domListCol = document.createElement("div"); domListCol.id = "violate_list_column"; let domGraphCol = document.createElement("div"); domGraphCol.id = "dep_graph_column"; let domResetBtn = document.createElement("button"); domResetBtn.id = "reset_btn"; domResetBtn.innerHTML = "Reset"; domGraphCol.appendChild(domResetBtn); document.body.appendChild(domListCol); document.body.appendChild(domGraphCol); let canvas = d3.select("#dep_graph_column").append("svg"); canvas.attr("width", diameter + 200); canvas.attr("height", diameter); let svg = canvas.append("g"); svg.attr("transform", "translate(" + (radius + 100) + "," + radius + ")"); link = svg.append("g").selectAll(".link"); node = svg.append("g").selectAll(".node"); showResult(depData, violatedLibs); } function showList(depMap, violatedLibs) { function makeTitle(tagName) { let domTitle = document.createElement("div"); let domText = document.createElement("h3"); domText.innerHTML = tagName; domTitle.appendChild(domText); return domTitle; } function makeButton(libName, count) { let domButton = document.createElement("button"); domButton.className = "violate"; domButton.innerHTML = libName + " (" + count + ")"; domButton.onclick = function() { this.classList.toggle("active"); let currentList = this.nextElementSibling; if (currentList.style.display === "block") { currentList.style.display = "none"; selectedNode = undefined; if (selectedSubNode) { selectedSubNode.classList.toggle("active"); selectedSubNode.nextElementSibling.style.display = "none"; selectedSubNode = undefined; } resetclicked(); } else { currentList.style.display = "block"; for (let i = 1; i < currentList.childElementCount; i += 2) { currentList.childNodes[i].style.display = "none"; } if (selectedNode) { selectedNode.classList.toggle("active"); selectedNode.nextElementSibling.style.display = "none"; if (selectedSubNode) { selectedSubNode.classList.toggle("active"); selectedSubNode.nextElementSibling.style.display = "none"; selectedSubNode = undefined; } } selectedNode = domButton; mouseclicked(depMap[libName]); } }; return domButton; } function makeSubButton(libName, count) { let domButton = document.createElement("button"); domButton.className = "violate-list"; domButton.innerHTML = libName + " (" + count + ")"; domButton.onclick = function() { this.classList.toggle("active"); let currentSubList = this.nextElementSibling; if (currentSubList.style.display === "block") { currentSubList.style.display = "none"; selectedSubNode = undefined; } else { currentSubList.style.display = "block"; for (let i = 0; i < currentSubList.childElementCount; i++) { if (currentSubList.childNodes[i].childElementCount > 0) { currentSubList.childNodes[i].childNodes[1].style.display = "none"; } } if (selectedSubNode) { selectedSubNode.classList.toggle("active"); selectedSubNode.nextElementSibling.style.display = "none"; } selectedSubNode = domButton; } }; return domButton; } function changeFormat(symbol) { let res = ""; let i; for (i = 0; i < symbol.length; i++) { if (symbol.charAt(i) >= '0' && symbol.charAt(i) <= '9') { break; } } while (i < symbol.length) { if (symbol.charAt(i) < '0' || symbol.charAt(i) > '9') { break; } let len = parseInt(symbol.substr(i, symbol.length)); let count = 1; if (len < 10) { count = 0; } res = res + "::" + symbol.substr(i + 1 + count, len); i = i + 1 + count + len; } return res.substr(2, res.length); } function makeList(domList, list) { for (let i = 0; i < list.length; i++) { domList.appendChild(makeButton(list[i][0], list[i][1])); let domDepList = document.createElement("div"); let depItem = depMap[list[i][0]]; let violates = depItem.data.violates; for (let j = 0; j < violates.length; j++) { let domDepLib = document.createElement("div"); let tag = depMap[violates[j][0]].data.tag; let symbols = violates[j][1]; let domDepButton = makeSubButton(violates[j][0] + " [" + tag.substring(tag.lastIndexOf(".") + 1) + "]", symbols.length); for (let k = 0; k < symbols.length; k++) { let domDepSym = document.createElement("div"); domDepSym.className = "violate-list-sym"; domDepSym.innerHTML = symbols[k]; if (symbols[k].indexOf("_Z") === 0) { let cplusplusSym = document.createElement("span"); cplusplusSym.className = "cplusplus-sym"; cplusplusSym.innerHTML = changeFormat(symbols[k].substr(2, symbols[k].length)); domDepSym.appendChild(cplusplusSym); domDepSym.onmouseover = function(e) { e.currentTarget.style.position = "relative"; e.currentTarget.childNodes[1].style.display = "block"; }; domDepSym.onmouseout = function(e) { e.currentTarget.style.position = "static"; e.currentTarget.childNodes[1].style.display = "none"; }; } domDepLib.appendChild(domDepSym); } domDepList.appendChild(domDepButton); domDepList.appendChild(domDepLib); } domList.appendChild(domDepList); domDepList.style.display = "none"; } } let domViolatedList = document.getElementById("violate_list_column"); if ("vendor.private.bin" in violatedLibs) { let list = violatedLibs["vendor.private.bin"]; domViolatedList.appendChild(makeTitle("VENDOR (" + list.length + ")")); makeList(domViolatedList, list); } for (let tag in violatedLibs) { if (tag === "vendor.private.bin") continue; let list = violatedLibs[tag]; if (tag === "system.private.bin") tag = "SYSTEM"; else tag = tag.substring(tag.lastIndexOf(".") + 1).toUpperCase(); domViolatedList.appendChild(makeTitle(tag + " (" + list.length + ")")); makeList(domViolatedList, list); } } function showResult(depDumps, violatedLibs) { let root = tagHierarchy(depDumps).sum(function(d) { return 1; }); cluster(root); let libsDepData = libsDepends(root.leaves()); showList(libsDepData[1], violatedLibs); link = link.data(libsDepData[0]) .enter() .append("path") .each(function(d) { d.source = d[0], d.target = d[d.length - 1]; }) .attr("class", function(d) { return d.allow ? "link" : "link--violate" }) .attr("d", line); node = node.data(root.leaves()) .enter() .append("text") .attr("class", function(d) { return d.data.parent.parent.parent.key == "system" ? (d.data.parent.parent.key == "system.public" ? "node--sys-pub" : "node--sys-pri") : "node"; }) .attr("dy", "0.31em") .attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + (d.y + 8) + ",0)" + (d.x < 180 ? "" : "rotate(180)"); }) .attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; }) .text(function(d) { return d.data.key; }) .on("click", mouseclicked); document.getElementById("reset_btn").onclick = resetclicked; } function resetclicked() { if (selectedNode) { selectedNode.classList.toggle("active"); selectedNode.nextElementSibling.style.display = "none"; if (selectedSubNode) { selectedSubNode.classList.toggle("active"); selectedSubNode.nextElementSibling.style.display = "none"; selectedSubNode = undefined; } selectedNode = undefined; } link.classed("link--target", false) .classed("link--source", false); node.classed("node--target", false) .classed("node--source", false) .classed("node--selected", false); } function mouseclicked(d) { node.each(function(n) { n.target = n.source = false; }); link.classed("link--target", function(l) { if (l.target === d) { l.source.source = true; return true; } else { return false; } }) .classed("link--source", function(l) { if (l.source === d) { l.target.target = true; return true; } else { return false; } }) .filter(function(l) { return l.target === d || l.source === d; }) .raise(); node.classed("node--target", function(n) { return n.target; }) .classed("node--source", function(n) { return n.source; }) .classed("node--selected", function(n) { return n === d; }); } function tagHierarchy(depDumps) { let map = {}; function find(name, tag, data) { let node = map[name], i; if (!node) { node = map[name] = data || { name : name, children : [] }; if (name.length) { node.parent = find(tag, tag.substring(0, tag.lastIndexOf("."))); node.parent.children.push(node); node.key = name; } } return node; } depDumps.forEach(function(d) { find(d.name, d.tag, d); }); return d3.hierarchy(map[""]); } function libsDepends(nodes) { let map = {}, depends = []; // Compute a map from name to node. nodes.forEach(function(d) { map[d.data.name] = d; }); // For each dep, construct a link from the source to target node. nodes.forEach(function(d) { if (d.data.depends) d.data.depends.forEach(function(i) { let l = map[d.data.name].path(map[i]); l.allow = true; depends.push(l); }); if (d.data.violates.length) { map[d.data.name].not_allow = true; d.data.violates.forEach(function(i) { map[i[0]].not_allow = true; let l = map[d.data.name].path(map[i[0]]); l.allow = false; depends.push(l); }); } }); return [ depends, map ]; } window.onload = init; })();