Home | History | Annotate | Download | only in visual
      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 })();