text: function sunburstDraw(scope, element) {
  
    /**
     * Angular variables
     *
     */
    // watch for changes on scope.data
    scope.$watch("data", function() {
      var data = scope.data;
      render(data);
    });
  
  
    /**
     * Dimensions of svg, sunburst, legend, breadcrumbs
     *
     */
    // svg dimensions
    var width = 500;
    var height = 300;
    var radius = Math.min(width, height) / 2;
  
    // Breadcrumb dimensions: width, height, spacing, width of tip/tail.
    var b = {
      w: 60,
      h: 30,
      s: 3,
      t: 10
    };
  
    // Legend dimensions: width, height, spacing, radius of rounded rect.
    var li = {
      w: 75,
      h: 30,
      s: 3,
      r: 3
    };
  
    // margins
    var margin = {
      top: radius,
      bottom: 50,
      left: radius,
      right: 0
    };
  
    // sunburst margins
    var sunburstMargin = {
      top: 2 * radius + b.h,
      bottom: 0,
      left: 0,
      right: radius / 2
    };
  
  
    /**
     * Drawing variables:
     *
     * e.g. colors, totalSize, partitions, arcs
     */
    // Mapping of nodes to colorscale.
    var colors = d3.scale.category10();
  
    // Total size of all nodes, to be used later when data is loaded
    var totalSize = 0;
  
    // create d3.layout.partition
    var partition = d3.layout.partition()
      .size([2 * Math.PI, radius * radius])
      .value(function(d) {
        return d.size;
      });
  
    // create arcs for drawing D3 paths
    var arc = d3.svg.arc()
      .startAngle(function(d) {
        return d.x;
      })
      .endAngle(function(d) {
        return d.x + d.dx;
      })
      .innerRadius(function(d) {
        return Math.sqrt(d.y);
      })
      .outerRadius(function(d) {
        return Math.sqrt(d.y + d.dy);
      });
  
  
  
  
    /**
     * Define and initialize D3 select references and div-containers
     *
     * e.g. vis, breadcrumbs, lastCrumb, summary, sunburst, legend
     */
    // create main vis selection
    var vis = d3.select(element[0])
      .append("div").classed("vis-continer", true)
      .style("position", "relative")
      .style("margin-top", "20px")
      .style("margin-bottom", "20px")
      .style("left", "50px")
      .style("height", height + 2 * b.h + "px");
  
    // create and position SVG
    var sunburst = vis
      .append("div").classed("sunburst-container", true)
      .style("position", "absolute")
      .style("left", sunburstMargin.left + "px")
      .append("svg")
      .attr("width", width)
      .attr("height", height)
      .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  
    // create and position legend
    var legend = vis
      .append("div").classed("legend-container", true)
      .style("position", "absolute")
      .style("top", b.h + "px")
      .style("left", 2 * radius + sunburstMargin.right + "px")
      .style("width", 50 + "px")
      .style("height", 50 + "px")
      .append("svg")
      .attr("width", li.w)
      .attr("height", height);
  
    // create and position breadcrumbs container and svg
    var breadcrumbs = vis
      .append("div").classed("breadcrumbs-container", true)
      .style("position", "absolute")
      .style("top", sunburstMargin.top + "px")
      .append("svg")
      .attr("width", width)
      .attr("height", b.h)
      .attr("fill", "white")
      .attr("font-weight", 600);
  
    // create last breadcrumb element
    var lastCrumb = breadcrumbs
      .append("text").classed("lastCrumb", true);
  
    // create and position summary container
    var summary = vis
      .append("div").classed("summary-container", true)
      .style("position", "absolute")
      .style("top", radius * 0.80 + "px")
      .style("left", sunburstMargin.left + radius / 2 + "px")
      .style("width", radius + "px")
      .style("height", radius + "px")
      .style("text-align", "center")
      .style("font-size", "11px")
      .style("color", "#666")
      .style("z-index", "-1");
  
  
  
    /**
     * Render process:
     *
     * 1) Load data
     * 2) Build Tree
     * 3) Draw visualization
     */
    // render visualization
    function render(data) {
      var parsedData = d3.csv.parseRows(data); // load data
      var json = buildHierarchy(parsedData); // build json tree
      removeVisualization(); // remove existing visualization if any
      createVisualization(json); // visualize json tree
    }
  
  
  
    /**
     * Helper functions:
     *
     * @function removeVisualization(): removes existing SVG components
     * @function createVisualization(json): create visualization from json tree structure
     * @function colorMap(d): color nodes with colors mapping
     * @function mouseover(d): mouseover function
     * @function mouseleave(d): mouseleave function
     * @function getAncestors(node): get ancestors of a specified node
     * @function buildHierarchy(data): generate json nested structure from csv data input
     */
    // removes existing SVG components
    function removeVisualization() {
      sunburst.selectAll(".nodePath").remove();
      legend.selectAll("g").remove();
    }
  
  
    // visualize json tree structure
    function createVisualization(json) {
      drawSunburst(json); // draw sunburst
      drawLegend(); // draw legend
    };
  
  
    // helper function colorMap - color gray if "end" is detected
    function colorMap(d) {
      return colors(d.name);
    }
  
  
    // helper function to draw the sunburst and breadcrumbs
    function drawSunburst(json) {
      // Build only nodes of a threshold "visible" sizes to improve efficiency
      var nodes = partition.nodes(json)
        .filter(function(d) {
          return (d.dx > 0.005); // 0.005 radians = 0.29 degrees
        });
  
      // this section is required to update the colors.domain() every time the data updates
      var uniqueNames = (function(a) {
        var output = [];
        a.forEach(function(d) {
          if (output.indexOf(d.name) === -1) output.push(d.name);
        });
        return output;
      })(nodes);
      colors.domain(uniqueNames); // update domain colors
  
      // create path based on nodes
      var path = sunburst.data([json]).selectAll("path")
        .data(nodes).enter()
        .append("path").classed("nodePath", true)
        .attr("display", function(d) {
          return d.depth ? null : "none";
        })
        .attr("d", arc)
        .attr("fill", colorMap)
        .attr("opacity", 1)
        .attr("stroke", "white")
        .on("mouseover", mouseover);
  
  
      // // trigger mouse click over sunburst to reset visualization summary
      vis.on("click", click);
  
      // Update totalSize of the tree = value of root node from partition.
      totalSize = path.node().__data__.value;
    }
  
  
    // helper function to draw legend
    function drawLegend() {
      // remove "root" label from legend
      var labels = colors.domain().splice(1, colors.domain().length);
  
      // create legend "pills"
      var g = legend.selectAll("g")
        .data(labels).enter()
        .append("g")
        .attr("transform", function(d, i) {
          return "translate(0," + i * (li.h + li.s) + ")";
        });
  
      g.append("rect").classed("legend-pills", true)
        .attr("rx", li.r)
        .attr("ry", li.r)
        .attr("width", li.w)
        .attr("height", li.h)
        .style("fill", function(d) {
          return colors(d);
        });
  
      g.append("text").classed("legend-text", true)
        .attr("x", li.w / 2)
        .attr("y", li.h / 2)
        .attr("dy", "0.35em")
        .attr("text-anchor", "middle")
        .attr("fill", "white")
        .attr("font-size", "10px")
        .attr("font-weight", 600)
        .text(function(d) {
          return d;
        });
    }
  
  
    // helper function mouseover to handle mouseover events/animations and calculation of ancestor nodes etc
    function mouseover(d) {
      // build percentage string
      var percentage = (100 * d.value / totalSize).toPrecision(3);
      var percentageString = percentage + "%";
      if (percentage < 1) {
        percentageString = "< 1.0%";
      }
  
      // update breadcrumbs (get all ancestors)
      var ancestors = getAncestors(d);
      updateBreadcrumbs(ancestors, percentageString);
  
      // update sunburst (Fade all the segments and highlight only ancestors of current segment)
      sunburst.selectAll("path")
        .attr("opacity", 0.3);
      sunburst.selectAll("path")
        .filter(function(node) {
          return (ancestors.indexOf(node) >= 0);
        })
        .attr("opacity", 1);
  
      // update summary
      summary.html(
        "Stage: " + d.depth + "<br />" +
        "<span class='percentage'>" + percentageString + "</span><br />" +
        d.value + " of " + totalSize + "<br />"
      );
  
      // display summary and breadcrumbs if hidden
      summary.style("visibility", "");
      breadcrumbs.style("visibility", "");
    }
  
  
    // helper function click to handle mouseleave events/animations
    function click(d) {
      // Deactivate all segments then retransition each segment to full opacity.
      sunburst.selectAll("path").on("mouseover", null);
      sunburst.selectAll("path")
        .transition()
        .duration(1000)
        .attr("opacity", 1)
        .each("end", function() {
          d3.select(this).on("mouseover", mouseover);
        });
  
      // hide summary and breadcrumbs if visible
      breadcrumbs.style("visibility", "hidden");
      summary.style("visibility", "hidden");
    }
  
  
    // Return array of ancestors of nodes, highest first, but excluding the root.
    function getAncestors(node) {
      var path = [];
      var current = node;
  
      while (current.parent) {
        path.unshift(current);
        current = current.parent;
      }
      return path;
    }
  
  
    // Generate a string representation for drawing a breadcrumb polygon.
    function breadcrumbPoints(d, i) {
      var points = [];
      points.push("0,0");
      points.push(b.w + ",0");
      points.push(b.w + b.t + "," + (b.h / 2));
      points.push(b.w + "," + b.h);
      points.push("0," + b.h);
  
      if (i > 0) { // Leftmost breadcrumb; don't include 6th vertex.
        points.push(b.t + "," + (b.h / 2));
      }
      return points.join(" ");
    }
  
  
    // Update the breadcrumb breadcrumbs to show the current sequence and percentage.
    function updateBreadcrumbs(ancestors, percentageString) {
      // Data join, where primary key = name + depth.
      var g = breadcrumbs.selectAll("g")
        .data(ancestors, function(d) {
          return d.name + d.depth;
        });
  
      // Add breadcrumb and label for entering nodes.
      var breadcrumb = g.enter().append("g");
  
      breadcrumb
        .append("polygon").classed("breadcrumbs-shape", true)
        .attr("points", breadcrumbPoints)
        .attr("fill", colorMap);
  
      breadcrumb
        .append("text").classed("breadcrumbs-text", true)
        .attr("x", (b.w + b.t) / 2)
        .attr("y", b.h / 2)
        .attr("dy", "0.35em")
        .attr("font-size", "10px")
        .attr("text-anchor", "middle")
        .text(function(d) {
          return d.name;
        });
  
      // Set position for entering and updating nodes.
      g.attr("transform", function(d, i) {
        return "translate(" + i * (b.w + b.s) + ", 0)";
      });
  
      // Remove exiting nodes.
      g.exit().remove();
  
      // Update percentage at the lastCrumb.
      lastCrumb
        .attr("x", (ancestors.length + 0.5) * (b.w + b.s))
        .attr("y", b.h / 2)
        .attr("dy", "0.35em")
        .attr("text-anchor", "middle")
        .attr("fill", "black")
        .attr("font-weight", 600)
        .text(percentageString);
    }
  
  
  
    // Take a 4-column CSV of ["sequence", "stage", "node", "value"] and
    // transform it into a hierarchical structure suitable for a partition layout.
    function buildHierarchy(csv) {
      var data = csv2json(csv); // build JSON dataframe from csv using helper function
  
      // build tree
      var root = {
        name: "root",
        children: []
      };
  
      data.forEach(function(d) {
        var nodes = d.nodes;
        var size = parseInt(d.size);
  
        // build graph, nodes, and child nodes
        var currentNode = root;
        for (var j = 0; j < nodes.length; j++) {
          var children = currentNode.children;
          var nodeName = nodes[j];
          var childNode;
  
          if (j + 1 < nodes.length) {
            // Not yet at the end of the sequence; move down the tree.
            var foundChild = false;
            for (var k = 0; k < children.length; k++) {
              if (children[k].name == nodeName) {
                childNode = children[k];
                foundChild = true;
                break;
              }
            }
            if (!foundChild) { // If we don't already have a child node for this branch, create it.
              childNode = {
                name: nodeName,
                children: []
              };
              children.push(childNode);
            }
            currentNode = childNode;
          } else { // Reached the end of the sequence; create a leaf node.
            childNode = {
              name: nodeName,
              size: size
            };
            children.push(childNode);
          }
        }
      });
      return root;
    }
  
  
  
    // helper function to buildHierarchy to transform 4-column CSV into a JSON dataframe.
    function csv2json(csv) {
      var data = [];
      var sequences = [];
  
      // sort the dataframe ascending by sequence (d[0]) then by stage (d[1])
      csv.sort(function(a, b) {
        if (a[2] === b[2]) {
          return d3.ascending(a[0], b[0]);
        }
        return d3.ascending(a[1], b[1]);
      });
      csv.forEach(function(record) {
        var sequence = record[0];
        if (sequences.indexOf(sequence) < 0) sequences.push(sequence);
      });
  
      sequences.forEach(function(sequence) {
        var d = {
          nodes: [],
          size: 0
        };
        csv.forEach(function(record) {
          var node = record[2];
          var size = record[3];
          if (sequence === record[0]) {
            d.nodes.push(node);
            d.size = size;
          }
        });
        data.push(d);
      });
      return data;
    }
  }