var operatorVisualization = function (element, fragmentId, queryPlan, graph) {
    $(element.node()).empty();

    var hierarchy = graph.nested["f"+fragmentId],
        levels = {};
    function addLevels(node, level) {
        levels[node.id] = level++;
        _.map(node.children, function(n) {
            addLevels(n, level);
        });
    }
    addLevels(hierarchy, 0);

    var idNameMapping = nameMappingFromFragments(queryPlan.physicalPlan.plan.fragments);

    var margin = {top: 0, right: 0, bottom: 0, left: 0 },
        width = parseInt(element.style('width'), 10) - margin.left - margin.right,
        height = 60 - margin.top - margin.bottom;

    var x = d3.scale.linear()
        .range([0, width]);

    var svg = element.append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
        .attr("class", "map");

    var url = templates.urls.contribution({
        myria: myriaConnection,
        query: queryPlan.queryId,
        fragment: fragmentId
    });

    var total = 0;
    d3.csv(url, function(d) {
        d.nanoTime = +d.nanoTime;
        return d;
    }, function(error, data) {
        data = _.map(data, function(d) {
            d.level = levels[d.opId];
            d.name = idNameMapping[d.opId];
            d.rawData = graph.nodes["f"+fragmentId].opNodes[d.opId].rawData;
            total += d.nanoTime;
            return d;
        });

        data = _.sortBy(data, 'level');

        var totalDomain = 0;
        data = _.map(data, function(d) {
            d.share = _.max([0.03, d.nanoTime/total]);
            d.prevEnd = totalDomain;
            totalDomain += d.share;
            return d;
        });

        x.domain([0, totalDomain]);

        var op = svg.selectAll(".op").data(data)
          .enter().append("g")
            .attr("class", "op");

        op.append("rect")
            .attr("x", function(d) { return x(d.prevEnd); })
            .attr("width", function(d) { return x(d.share); })
            .attr("height", height)
            .style("fill", function(d) { return opToColor[d.opId]; })

        op.append("text")
            .attr("dx", 5)
            .attr("dy", 20)
            .attr("x", function(d) { return x(d.prevEnd); })
            .style("fill", "black")
            .text(function(d) { return d.name.substr(0, x(d.share)/5); })
            .popover(function(d) {
                var body = templates.row({key: "Overall runtime", value: customFullTimeFormat(d.nanoTime)});
                _.each(d.rawData, function(value, key){
                    if (key == 'operators') {
                        return;
                    }
                    if (value === null) {
                        value = 'null';
                    }
                    if (value !== null && typeof value === 'object') {
                      value = templates.code({code: JSON.stringify(value)});
                    }
                    body += templates.row({key: key, value: value});
                });
                return {
                    title: templates.strong({text: d.name}),
                    content: templates.table({body: body})
                };
            });

        op.append("text")
            .attr("dx", 5)
            .attr("dy", 40)
            .attr("x", function(d) { return x(d.prevEnd); })
            .style("fill", "black")
            .text(function(d) { return Math.round(100* d.nanoTime/total) + " %"; });

        op.on('mouseover', function(d) {
            d3.select(this).select("rect")
                .transition().duration(shortDuration)
                .style("fill", function(d) { return d3.rgb(opToColor[d.opId]).brighter(0.4); });
        });

        op.on('mouseout', function(d) {
            d3.select(this).select("rect")
                .transition().duration(animationDuration)
                .style("fill", function(d) { return opToColor[d.opId]; });
        });
    });

    // return variables that are needed outside this scope
    return {};
};

@@ -170,21 +170,44 @@
 levels =
+ %7B%7D,%0A        children =
  %7B%7D;%0A
-
     func
@@ -271,16 +271,74 @@
 evel++;%0A
+        children%5Bnode.id%5D = _.pluck(node.children, 'id');%0A
         
@@ -1235,27 +1235,8 @@
 );%0A%0A
-    var total = 0;%0A
     
@@ -1340,24 +1340,168 @@
 or, data) %7B%0A
+        var indexedData = _.object(_.map(data, function(x)%7B return %5Bx.opId, x%5D; %7D));%0A        var rootTime = indexedData%5Bhierarchy.id%5D.nanoTime;%0A
         data
@@ -1704,27 +1704,192 @@
     
-total += d.nanoTime
+var sumChildren = _.reduce(_.pluck(_.pick(indexedData, children%5Bd.opId%5D), 'nanoTime'), function(a,b) %7B return a + b; %7D, 0);%0A            d.timeWithoutChildren = d.nanoTime - sumChildren
 ;%0A  
@@ -2069,30 +2069,44 @@
 0.03, d.
-nanoTime/total
+timeWithoutChildren/rootTime
 %5D);%0A    
@@ -3010,24 +3010,153 @@
 anoTime)%7D);%0A
+                body += templates.row(%7Bkey: %22Time spent in this operator%22, value: customFullTimeFormat(d.timeWithoutChildren)%7D);%0A
             
@@ -4082,22 +4082,36 @@
 * d.
-nanoTime/total
+timeWithoutChildren/rootTime
 ) + 

Exclude children in operator contribution vis