Expandable Nodes

Adapted from work by G. Hobbelt's http://bl.ocks.org/GerHobbelt/3071239

by Nivaldo

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<!--
http://bl.ocks.org/GerHobbelt/3071239
...-->

CSS

svg {
  border: 1px solid #ccc;
}
body {
  font: 10px sans-serif;
}
circle.node {
  fill: lightsteelblue;
  stroke: #555;
  stroke-width: 3px;
}
circle.leaf {
  stroke: #fff;
  stroke-width: 1.5px;
}
path.hull {
  fill: lightsteelblue;
  fill-opacity: 0.3;
}
line.link {
  stroke: #333;
  stroke-opacity: 0.5;
  pointer-events: none;
}

JavaScript

var width = 960,     // svg width
    height = 600,     // svg height
    dr = 4,      // default point radius
    off = 15,    // cluster hull offset
    expand = {}, // expanded clusters
    data, net, force, hullg, hull, linkg, link, nodeg, node;

var curve = d3.svg.line()
    .interpolate("cardinal-closed")
    .tension(.85);

var fill = d3.scale.category20();

function noop() { return false; }

function nodeid(n) {
  return n.size ? "_g_"+n.group : n.name;
}

function linkid(l) {
  var u = nodeid(l.source),
      v = nodeid(l.target);
  return u<v ? u+"|"+v : v+"|"+u;
}

function getGroup(n) { return n.group; }

// Constructs the network to visualize
//  Regenerates he nodes and links from the original data, based on the expand[]
//  info, i.e. which group(s) should be shown in expanded form and which shouldn't.
function network(data, prev, index, expand) {
  expand = expand || {};
  var gm = {},    // group map
      nm = {},    // node map
      lm = {},    // link map
      gn = {},    // previous group nodes
      gc = {},    // previous group centroids
      nodes = [], // output nodes
      links = []; // output links

  // process previous nodes for reuse or centroid calculation
  if (prev) {
    prev.nodes.forEach(function(n) {
      var i = index(n), o;
      if (n.size > 0) {
        gn[i] = n;
        n.size = 0;
      } else {
        o = gc[i] || (gc[i] = {x:0,y:0,count:0});
        o.x += n.x;
        o.y += n.y;
        o.count += 1;
      }
    });
  }

  // determine nodes
  for (var k=0; k<data.nodes.length; ++k) {
    var n = data.nodes[k],
        i = index(n),
        l = gm[i] || (gm[i]=gn[i]) || (gm[i]={group:i, size:0, nodes:[]});

    if (expand[i]) {
      // the node should be directly visible
      nm[n.name] = nodes.length;
      nodes.push(n);
      if (gn[i]) {
        // place new nodes at cluster location (plus jitter)
        n.x = gn[i].x + Math.random();
        n.y = gn[i].y + Math.random();
      }
    } else {
      //...