d3-flame-graph
Advanced tools
Comparing version 1.0.1 to 1.0.4
@@ -30,5 +30,4 @@ { | ||
"d3": "~4", | ||
"d3-tip": "~0.7.1", | ||
"lodash": "~4.17.4" | ||
"d3-tip": "~0.7.1" | ||
} | ||
} |
(function() { | ||
'use strict'; | ||
/*jshint eqnull:true */ | ||
// https://tc39.github.io/ecma262/#sec-array.prototype.find | ||
if (!Array.prototype.find) { | ||
Object.defineProperty(Array.prototype, 'find', { | ||
value: function(predicate) { | ||
// 1. Let O be ? ToObject(this value). | ||
if (this == null) { | ||
throw new TypeError('"this" is null or not defined'); | ||
} | ||
var o = Object(this); | ||
// 2. Let len be ? ToLength(? Get(O, "length")). | ||
var len = o.length >>> 0; | ||
// 3. If IsCallable(predicate) is false, throw a TypeError exception. | ||
if (typeof predicate !== 'function') { | ||
throw new TypeError('predicate must be a function'); | ||
} | ||
// 4. If thisArg was supplied, let T be thisArg; else let T be undefined. | ||
var thisArg = arguments[1]; | ||
// 5. Let k be 0. | ||
var k = 0; | ||
// 6. Repeat, while k < len | ||
while (k < len) { | ||
// a. Let Pk be ! ToString(k). | ||
// b. Let kValue be ? Get(O, Pk). | ||
// c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). | ||
// d. If testResult is true, return kValue. | ||
var kValue = o[k]; | ||
if (predicate.call(thisArg, kValue, k, o)) { | ||
return kValue; | ||
} | ||
// e. Increase k by 1. | ||
k++; | ||
} | ||
// 7. Return undefined. | ||
return undefined; | ||
} | ||
}); | ||
} | ||
if (!Array.prototype.filter) | ||
Array.prototype.filter = function(func, thisArg) { | ||
if ( ! ((typeof func === 'function') && this) ) | ||
throw new TypeError(); | ||
var len = this.length >>> 0, | ||
res = new Array(len), // preallocate array | ||
c = 0, i = -1; | ||
if (thisArg === undefined) | ||
while (++i !== len) | ||
// checks to see if the key was set | ||
if (i in this) | ||
if (func(t[i], i, t)) | ||
res[c++] = t[i]; | ||
else | ||
while (++i !== len) | ||
// checks to see if the key was set | ||
if (i in this) | ||
if (func.call(thisArg, t[i], i, t)) | ||
res[c++] = t[i]; | ||
res.length = c; // shrink down array to proper size | ||
return res; | ||
}; | ||
/*jshint eqnull:false */ | ||
function flameGraph() { | ||
var w = 960, // graph width | ||
h = 540, // graph height | ||
h = null, // graph height | ||
c = 18, // cell height | ||
@@ -14,5 +86,6 @@ selection = null, // selection | ||
transitionEase = d3.easeCubic, // tooltip offset | ||
sort = true, | ||
sort = false, | ||
reversed = false, // reverse the graph direction | ||
clickHandler = null; | ||
clickHandler = null, | ||
minFrameSize = 0; | ||
@@ -27,4 +100,16 @@ var tip = d3.tip() | ||
function name(d) { | ||
return d.data.n || d.data.name; | ||
} | ||
function children(d) { | ||
return d.c || d.children; | ||
} | ||
function value(d) { | ||
return d.v || d.value; | ||
} | ||
var label = function(d) { | ||
return d.data.name + " (" + d3.format(".3f")(100 * (d.x1 - d.x0), 3) + "%, " + d.data.value + " samples)"; | ||
return name(d) + " (" + d3.format(".3f")(100 * (d.x1 - d.x0), 3) + "%, " + value(d) + " samples)"; | ||
}; | ||
@@ -38,8 +123,4 @@ | ||
function name(d) { | ||
return d.data.name; | ||
} | ||
var colorMapper = function(d) { | ||
return d.highlight ? "#E600E6" : colorHash(d.data.name); | ||
return d.highlight ? "#E600E6" : colorHash(name(d)); | ||
}; | ||
@@ -85,8 +166,11 @@ | ||
if(!d.data.original) { | ||
d.data.original = d.data.value; | ||
d.data.original = value(d); | ||
} | ||
d.data.value = 0; | ||
if(d.children) { | ||
d.children.forEach(hide); | ||
d.data.v = 0; | ||
if (d.data.value) { | ||
delete d.data.value; | ||
} | ||
if(children(d)) { | ||
children(d).forEach(hide); | ||
} | ||
} | ||
@@ -97,6 +181,6 @@ | ||
if(d.data.original) { | ||
d.data.value = d.data.original; | ||
d.data.v = d.data.original; | ||
} | ||
if(d.children) { | ||
d.children.forEach(show); | ||
if(children(d)) { | ||
children(d).forEach(show); | ||
} | ||
@@ -155,6 +239,6 @@ } | ||
function searchInner(d) { | ||
var label = d.data.name; | ||
var label = name(d); | ||
if (d.children) { | ||
d.children.forEach(function (child) { | ||
if (children(d)) { | ||
children(d).forEach(function (child) { | ||
searchInner(child); | ||
@@ -178,4 +262,4 @@ }); | ||
d.highlight = false; | ||
if(d.children) { | ||
d.children.forEach(function(child) { | ||
if(children(d)) { | ||
children(d).forEach(function(child) { | ||
clear(child); | ||
@@ -190,3 +274,3 @@ }); | ||
} else if (sort) { | ||
return d3.ascending(a.data.name, b.data.name); | ||
return d3.ascending(name(a), name(b)); | ||
} | ||
@@ -197,2 +281,13 @@ } | ||
function filterNodes(root) { | ||
var nodeList = root.descendants(); | ||
if (minFrameSize > 0) { | ||
var kx = w / (root.x1 - root.x0); | ||
nodeList = nodeList.filter(function(el) { | ||
return ((el.x1 - el.x0) * kx) > minFrameSize; | ||
}); | ||
} | ||
return nodeList; | ||
} | ||
function update() { | ||
@@ -204,4 +299,3 @@ | ||
if (sort) | ||
root.sort(doSort); | ||
if (sort) root.sort(doSort); | ||
root.sum(function(d) { | ||
@@ -212,6 +306,7 @@ if (d.fade) { | ||
// The node's self value is its total value minus all children. | ||
var v = d.v || d.value || 0; | ||
if (d.children) { | ||
for (var i = 0; i < d.children.length; i++) { | ||
v -= d.children[i].value; | ||
var v = value(d) || 0; | ||
if (children(d)) { | ||
var c = children(d); | ||
for (var i = 0; i < c.length; i++) { | ||
v -= c[i].value; | ||
} | ||
@@ -226,3 +321,4 @@ } | ||
var g = d3.select(this).select("svg").selectAll("g").data(root.descendants()); | ||
var descendants = filterNodes(root); | ||
var g = d3.select(this).select("svg").selectAll("g").data(descendants, function(d) { return d.id; }); | ||
@@ -234,5 +330,3 @@ g.transition() | ||
g.select("rect").transition() | ||
.duration(transitionDuration) | ||
.ease(transitionEase) | ||
g.select("rect") | ||
.attr("width", width); | ||
@@ -243,5 +337,8 @@ | ||
.attr("transform", function(d) { return "translate(" + x(d.x0) + "," + (reversed ? y(d.depth) : (h - y(d.depth) - c)) + ")"; }); | ||
node.append("svg:rect").attr("width", width); | ||
node.append("svg:rect") | ||
.transition() | ||
.delay(transitionDuration / 2) | ||
.attr("width", width); | ||
if (!tooltip) | ||
@@ -254,7 +351,7 @@ node.append("svg:title"); | ||
// Now we have to re-select to see the new elements (why?). | ||
g = d3.select(this).select("svg").selectAll("g").data(root.descendants()); | ||
g = d3.select(this).select("svg").selectAll("g").data(descendants, function(d) { return d.id; }); | ||
g.attr("width", width) | ||
.attr("height", function(d) { return c; }) | ||
.attr("name", function(d) { return d.data.name; }) | ||
.attr("name", function(d) { return name(d); }) | ||
.attr("class", function(d) { return d.data.fade ? "frame fade" : "frame"; }); | ||
@@ -276,2 +373,4 @@ | ||
.style("display", function(d) { return (width(d) < 35) ? "none" : "block";}) | ||
.transition() | ||
.delay(transitionDuration) | ||
.text(name); | ||
@@ -281,3 +380,4 @@ | ||
g.exit().remove(); | ||
g.exit() | ||
.remove(); | ||
@@ -296,4 +396,4 @@ g.on('mouseover', function(d) { | ||
samples.forEach(function (sample) { | ||
var node = _.find(data, function (element) { | ||
return element.name === sample.name; | ||
var node = data.find(function (element) { | ||
return (name(element) === name(sample)); | ||
}); | ||
@@ -319,4 +419,19 @@ | ||
function s4() { | ||
return Math.floor((1 + Math.random()) * 0x10000) | ||
.toString(16) | ||
.substring(1); | ||
} | ||
function injectIds(node) { | ||
node.id = s4(); | ||
var children = node.c || node.children || []; | ||
for (var i = 0; i < children.length; i++) { | ||
injectIds(children[i]); | ||
} | ||
} | ||
function chart(s) { | ||
var root = d3.hierarchy(s.datum(), function(d) { return d.c || d.children; }); | ||
var root = d3.hierarchy(s.datum(), function(d) { return children(d); }); | ||
injectIds(root); | ||
selection = s.datum(root); | ||
@@ -326,2 +441,6 @@ | ||
if (!h) { | ||
h = (root.height + 2) * c; | ||
} | ||
selection.each(function(data) { | ||
@@ -452,3 +571,3 @@ | ||
merge([root.data], [samples]); | ||
newRoot = d3.hierarchy(root.data, function(d) { return d.c || d.children; }); | ||
newRoot = d3.hierarchy(root.data, function(d) { return children(d); }); | ||
}); | ||
@@ -465,2 +584,8 @@ selection = selection.datum(newRoot); | ||
chart.minFrameSize = function (_) { | ||
if (!arguments.length) { return minFrameSize; } | ||
minFrameSize = _; | ||
return chart; | ||
}; | ||
return chart; | ||
@@ -467,0 +592,0 @@ } |
@@ -1,1 +0,1 @@ | ||
!function(){"use strict";function t(){function t(t){var n=document.getElementById("details");n&&(n.innerHTML=t)}function n(t){return t.data.name}function e(t){var n=0,e=1,r=0;if(t){for(var a=0;a<t.length&&!(a>6);a++)n+=e*(t.charCodeAt(a)%10),r+=9*e,e*=.7;r>0&&(n/=r)}return n}function r(t){var n=0;if(t){var r=t.split("`");r.length>1&&(t=r[r.length-1]),n=e(t=t.split("(")[0])}return"rgb("+(200+Math.round(55*n))+","+(0+Math.round(230*(1-n)))+","+(0+Math.round(55*(1-n)))+")"}function a(t){t.data.original||(t.data.original=t.data.value),t.data.value=0,t.children&&t.children.forEach(a)}function i(t){t.data.fade=!1,t.data.original&&(t.data.value=t.data.original),t.children&&t.children.forEach(i)}function c(t){var n=[];if(t.parent){var e=t.parent.children.indexOf(t);(n=t.parent.children.slice(0)).splice(e,1)}return n}function u(t){c(t).forEach(function(t){a(t)}),t.parent&&u(t.parent)}function o(t){t.parent&&(t.parent.data.fade=!0,o(t.parent))}function l(t){O.hide(t),u(t),i(t),o(t),s(),"function"==typeof L&&L(t)}function d(t,n){function e(t){var n=t.data.name;t.children&&t.children.forEach(function(t){e(t)}),n.match(r)?(t.highlight=!0,a.push(t)):t.highlight=!1}var r=new RegExp(n),a=[];return e(t),a}function f(t){t.highlight=!1,t.children&&t.children.forEach(function(t){f(t)})}function h(t,n){return"function"==typeof A?A(t,n):A?d3.ascending(t.data.name,n.data.name):void 0}function s(){y.each(function(e){function r(t){return(t.x1-t.x0)*c}var a=d3.scaleLinear().range([0,m]),i=d3.scaleLinear().range([0,E]);A&&e.sort(h),e.sum(function(t){if(t.fade)return 0;var n=t.v||t.value||0;if(t.children)for(var e=0;e<t.children.length;e++)n-=t.children[e].value;return n}),T(e);var c=m/(e.x1-e.x0),u=d3.select(this).select("svg").selectAll("g").data(e.descendants());u.transition().duration(M).ease(k).attr("transform",function(t){return"translate("+a(t.x0)+","+(C?i(t.depth):x-i(t.depth)-E)+")"}),u.select("rect").transition().duration(M).ease(k).attr("width",r);var o=u.enter().append("svg:g").attr("transform",function(t){return"translate("+a(t.x0)+","+(C?i(t.depth):x-i(t.depth)-E)+")"});o.append("svg:rect").attr("width",r),w||o.append("svg:title"),o.append("foreignObject").append("xhtml:div"),(u=d3.select(this).select("svg").selectAll("g").data(e.descendants())).attr("width",r).attr("height",function(t){return E}).attr("name",function(t){return t.data.name}).attr("class",function(t){return t.data.fade?"frame fade":"frame"}),u.select("rect").attr("height",function(t){return E}).attr("fill",function(t){return H(t)}),w||u.select("title").text(j),u.select("foreignObject").attr("width",r).attr("height",function(t){return E}).select("div").attr("class","label").style("display",function(t){return r(t)<35?"none":"block"}).text(n),u.on("click",l),u.exit().remove(),u.on("mouseover",function(n){w&&O.show(n),t(j(n))}).on("mouseout",function(n){w&&O.hide(n),t("")})})}function g(t,n){n.forEach(function(n){var e=_.find(t,function(t){return t.name===n.name});e?(e.original?e.original+=n.value:e.value+=n.value,n.children&&(e.children||(e.children=[]),g(e.children,n.children))):t.push(n)})}function p(t){var n=d3.hierarchy(t.datum(),function(t){return t.c||t.children});if(y=t.datum(n),!arguments.length)return p;y.each(function(t){v||(v=d3.select(this).append("svg:svg").attr("width",m).attr("height",x).attr("class","partition d3-flame-graph").call(O)).append("svg:text").attr("class","title").attr("text-anchor","middle").attr("y","25").attr("x",m/2).attr("fill","#808080").text(b)}),s()}var v,m=960,x=540,E=18,y=null,w=!0,b="",M=750,k=d3.easeCubic,A=!0,C=!1,L=null,O=d3.tip().direction("s").offset([8,0]).attr("class","d3-flame-graph-tip").html(function(t){return j(t)}),j=function(t){return t.data.name+" ("+d3.format(".3f")(100*(t.x1-t.x0),3)+"%, "+t.data.value+" samples)"},H=function(t){return t.highlight?"#E600E6":r(t.data.name)},T=d3.partition();return p.height=function(t){return arguments.length?(x=t,p):x},p.width=function(t){return arguments.length?(m=t,p):m},p.cellHeight=function(t){return arguments.length?(E=t,p):E},p.tooltip=function(t){return arguments.length?("function"==typeof t&&(O=t),w=!!t,p):w},p.title=function(t){return arguments.length?(b=t,p):b},p.transitionDuration=function(t){return arguments.length?(M=t,p):M},p.transitionEase=function(t){return arguments.length?(k=t,p):k},p.sort=function(t){return arguments.length?(A=t,p):A},p.reversed=function(t){return arguments.length?(C=t,p):C},p.label=function(t){return arguments.length?(j=t,p):j},p.search=function(t){var n=[];return y.each(function(e){n=d(e,t),s()}),n},p.clear=function(){y.each(function(t){f(t),s()})},p.zoomTo=function(t){l(t)},p.resetZoom=function(){y.each(function(t){l(t)})},p.onClick=function(t){return arguments.length?(L=t,p):L},p.merge=function(t){var n;y.each(function(e){g([e.data],[t]),n=d3.hierarchy(e.data,function(t){return t.c||t.children})}),y=y.datum(n),s()},p.color=function(t){return arguments.length?(H=t,p):H},p}"undefined"!=typeof module&&module.exports?module.exports=t:d3.flameGraph=t}(); | ||
!function(){"use strict";function n(){function t(t){return t.data.n||t.data.name}function n(t){return t.c||t.children}function r(t){return t.v||t.value}function e(t){var n=document.getElementById("details");n&&(n.innerHTML=t)}function i(t){var n=0,r=1,e=0,i=10,a=6;if(t){for(var u=0;u<t.length&&!(u>a);u++)n+=r*(t.charCodeAt(u)%i),e+=r*(i-1),r*=.7;e>0&&(n/=e)}return n}function a(t){var n=0;if(t){var r=t.split("`");r.length>1&&(t=r[r.length-1]),t=t.split("(")[0],n=i(t)}var e=200+Math.round(55*n),a=0+Math.round(230*(1-n)),u=0+Math.round(55*(1-n));return"rgb("+e+","+a+","+u+")"}function u(t){t.data.original||(t.data.original=r(t)),t.data.v=0,t.data.value&&delete t.data.value,n(t)&&n(t).forEach(u)}function o(t){t.data.fade=!1,t.data.original&&(t.data.v=t.data.original),n(t)&&n(t).forEach(o)}function c(t){var n=[];if(t.parent){var r=t.parent.children.indexOf(t);n=t.parent.children.slice(0),n.splice(r,1)}return n}function f(t){var n=c(t);n.forEach(function(t){u(t)}),t.parent&&f(t.parent)}function l(t){t.parent&&(t.parent.data.fade=!0,l(t.parent))}function h(t){B.hide(t),f(t),o(t),l(t),v(),"function"==typeof H&&H(t)}function d(r,e){function i(r){var e=t(r);n(r)&&n(r).forEach(function(t){i(t)}),e.match(a)?(r.highlight=!0,u.push(r)):r.highlight=!1}var a=new RegExp(e),u=[];return i(r),u}function s(t){t.highlight=!1,n(t)&&n(t).forEach(function(t){s(t)})}function g(n,r){return"function"==typeof L?L(n,r):L?d3.ascending(t(n),t(r)):void 0}function p(t){var n=t.descendants();if(S>0){var r=b/(t.x1-t.x0);n=n.filter(function(t){return(t.x1-t.x0)*r>S})}return n}function v(){O.each(function(i){function a(t){return(t.x1-t.x0)*c}var u=d3.scaleLinear().range([0,b]),o=d3.scaleLinear().range([0,M]);L&&i.sort(g),i.sum(function(t){if(t.fade)return 0;var e=r(t)||0;if(n(t))for(var i=n(t),a=0;a<i.length;a++)e-=i[a].value;return e}),G(i);var c=b/(i.x1-i.x0),f=p(i),l=d3.select(this).select("svg").selectAll("g").data(f,function(t){return t.id});l.transition().duration(k).ease(C).attr("transform",function(t){return"translate("+u(t.x0)+","+(z?o(t.depth):A-o(t.depth)-M)+")"}),l.select("rect").attr("width",a);var d=l.enter().append("svg:g").attr("transform",function(t){return"translate("+u(t.x0)+","+(z?o(t.depth):A-o(t.depth)-M)+")"});d.append("svg:rect").transition().delay(k/2).attr("width",a),T||d.append("svg:title"),d.append("foreignObject").append("xhtml:div"),l=d3.select(this).select("svg").selectAll("g").data(f,function(t){return t.id}),l.attr("width",a).attr("height",function(t){return M}).attr("name",function(n){return t(n)}).attr("class",function(t){return t.data.fade?"frame fade":"frame"}),l.select("rect").attr("height",function(t){return M}).attr("fill",function(t){return F(t)}),T||l.select("title").text(D),l.select("foreignObject").attr("width",a).attr("height",function(t){return M}).select("div").attr("class","label").style("display",function(t){return a(t)<35?"none":"block"}).transition().delay(k).text(t),l.on("click",h),l.exit().remove(),l.on("mouseover",function(t){T&&B.show(t),e(D(t))}).on("mouseout",function(t){T&&B.hide(t),e("")})})}function m(n,r){r.forEach(function(r){var e=n.find(function(n){return t(n)===t(r)});e?(e.original?e.original+=r.value:e.value+=r.value,r.children&&(e.children||(e.children=[]),m(e.children,r.children))):n.push(r)})}function y(){return Math.floor(65536*(1+Math.random())).toString(16).substring(1)}function x(t){t.id=y();for(var n=t.c||t.children||[],r=0;r<n.length;r++)x(n[r])}function w(t){var r=d3.hierarchy(t.datum(),function(t){return n(t)});return x(r),O=t.datum(r),arguments.length?(A||(A=(r.height+2)*M),O.each(function(t){E||(E=d3.select(this).append("svg:svg").attr("width",b).attr("height",A).attr("class","partition d3-flame-graph").call(B),E.append("svg:text").attr("class","title").attr("text-anchor","middle").attr("y","25").attr("x",b/2).attr("fill","#808080").text(j))}),void v()):w}var E,b=960,A=null,M=18,O=null,T=!0,j="",k=750,C=d3.easeCubic,L=!1,z=!1,H=null,S=0,B=d3.tip().direction("s").offset([8,0]).attr("class","d3-flame-graph-tip").html(function(t){return D(t)}),D=function(n){return t(n)+" ("+d3.format(".3f")(100*(n.x1-n.x0),3)+"%, "+r(n)+" samples)"},F=function(n){return n.highlight?"#E600E6":a(t(n))},G=d3.partition();return w.height=function(t){return arguments.length?(A=t,w):A},w.width=function(t){return arguments.length?(b=t,w):b},w.cellHeight=function(t){return arguments.length?(M=t,w):M},w.tooltip=function(t){return arguments.length?("function"==typeof t&&(B=t),T=!!t,w):T},w.title=function(t){return arguments.length?(j=t,w):j},w.transitionDuration=function(t){return arguments.length?(k=t,w):k},w.transitionEase=function(t){return arguments.length?(C=t,w):C},w.sort=function(t){return arguments.length?(L=t,w):L},w.reversed=function(t){return arguments.length?(z=t,w):z},w.label=function(t){return arguments.length?(D=t,w):D},w.search=function(t){var n=[];return O.each(function(r){n=d(r,t),v()}),n},w.clear=function(){O.each(function(t){s(t),v()})},w.zoomTo=function(t){h(t)},w.resetZoom=function(){O.each(function(t){h(t)})},w.onClick=function(t){return arguments.length?(H=t,w):H},w.merge=function(t){var r;O.each(function(e){m([e.data],[t]),r=d3.hierarchy(e.data,function(t){return n(t)})}),O=O.datum(r),v()},w.color=function(t){return arguments.length?(F=t,w):F},w.minFrameSize=function(t){return arguments.length?(S=t,w):S},w}Array.prototype.find||Object.defineProperty(Array.prototype,"find",{value:function(t){if(null==this)throw new TypeError('"this" is null or not defined');var n=Object(this),r=n.length>>>0;if("function"!=typeof t)throw new TypeError("predicate must be a function");for(var e=arguments[1],i=0;r>i;){var a=n[i];if(t.call(e,a,i,n))return a;i++}return void 0}}),Array.prototype.filter||(Array.prototype.filter=function(n,r){if("function"!=typeof n||!this)throw new TypeError;var e=this.length>>>0,i=new Array(e),a=0,u=-1;if(void 0===r)for(;++u!==e;)if(u in this)if(n(t[u],u,t))i[a++]=t[u];else for(;++u!==e;)u in this&&n.call(r,t[u],u,t)&&(i[a++]=t[u]);return i.length=a,i}),"undefined"!=typeof module&&module.exports?module.exports=n:d3.flameGraph=n}(); |
{ | ||
"name": "d3-flame-graph", | ||
"version": "1.0.1", | ||
"version": "1.0.4", | ||
"description": "A d3.js library to produce flame graphs.", | ||
@@ -10,3 +10,4 @@ "main": "src/d3.flame.js", | ||
"scripts": { | ||
"test": "mocha --reporter nyan" | ||
"test": "mocha --reporter nyan", | ||
"dist": "gulp dist && git add src/*" | ||
}, | ||
@@ -48,4 +49,8 @@ "repository": { | ||
"d3": "^4.10.0", | ||
"d3-tip": "^0.7.1", | ||
"graceful-fs": "^4.1.11" | ||
} | ||
}, | ||
"pre-commit": [ | ||
"dist" | ||
] | ||
} |
101
README.md
@@ -5,3 +5,3 @@ # d3-flame-graph | ||
[![Flame Graph Example](http://giant.gfycat.com/DelectableResponsibleHart.gif)](http://spiermar.github.io/d3-flame-graph/) | ||
[![Flame Graph Example](https://media.giphy.com/media/l41JMjBaxrZw1bqpi/giphy.gif)](http://spiermar.github.io/d3-flame-graph/) | ||
@@ -14,16 +14,39 @@ If you don't know what flame graphs are, check [Brendan Gregg's post](http://www.brendangregg.com/flamegraphs.html). | ||
## Disclaimer | ||
## Examples | ||
This is the first release of this plugin. As such, expect to find bugs and issues. We count on your support to find and report them! | ||
Click [here](http://spiermar.github.io/d3-flame-graph/) to check the demo, and [source](https://github.com/spiermar/d3-flame-graph/blob/gh-pages/index.html). | ||
**At this point, the plugin provides only basic flame graph functionality. Please check the [issues](https://github.com/spiermar/d3-flame-graph/issues) page for roadmap information.** | ||
Click [here](http://spiermar.github.io/d3-flame-graph/live.html) to check the animated assembly demo, and [source](https://github.com/spiermar/d3-flame-graph/blob/gh-pages/live.html) | ||
## Demo | ||
Click [here](http://bl.ocks.org/spiermar/4509343495f8d6e214cb) to check the simplified demo on bl.ocks.org. | ||
Click [here](http://spiermar.github.io/d3-flame-graph/) to check the fully-featured demo! | ||
## Getting Started | ||
Click [here](http://bl.ocks.org/spiermar/4509343495f8d6e214cb) to check the simplified demo on bl.ocks.org! | ||
### jsdelivr CDN | ||
## Getting Started | ||
Just reference the CDN hosted CSS and JS files! | ||
```html | ||
<head> | ||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/spiermar/d3-flame-graph@1.0.4/dist/d3.flameGraph.min.css"> | ||
</head> | ||
<body> | ||
<div id="chart"></div> | ||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script> | ||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.7.1/d3-tip.min.js"></script> | ||
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/spiermar/d3-flame-graph@1.0.4/dist/d3.flameGraph.min.js"></script> | ||
<script type="text/javascript"> | ||
var flamegraph = d3.flameGraph() | ||
.width(960); | ||
d3.json("data.json", function(error, data) { | ||
if (error) return console.warn(error); | ||
d3.select("#chart") | ||
.datum(data) | ||
.call(flamegraph); | ||
}); | ||
</script> | ||
</body> | ||
``` | ||
### Bower | ||
@@ -48,20 +71,28 @@ | ||
```html | ||
<script type="text/javascript" src="bower_components/d3/d3.js"></script> | ||
<script type="text/javascript" src="bower_components/d3-flame-graph/dist/d3.layout.flame.js"></script> | ||
<script type="text/javascript"> | ||
var flamegraph = d3.flameGraph() | ||
.width(960) | ||
.height(540); | ||
<head> | ||
<link rel="stylesheet" type="text/css" href="bower_components/d3-flame-graph/dist/d3.flameGraph.css"> | ||
</head> | ||
<body> | ||
<div id="chart"></div> | ||
<script type="text/javascript" src="bower_components/d3/d3.js"></script> | ||
<script type="text/javascript" src="bower_components/d3-tip/index.js"></script> | ||
<script type="text/javascript" src="bower_components/d3-flame-graph/dist/d3.flameGraph.js"></script> | ||
<script type="text/javascript"> | ||
var flamegraph = d3.flameGraph() | ||
.width(960); | ||
d3.json("stacks.json", function(error, data) { | ||
if (error) return console.warn(error); | ||
d3.select("#chart") | ||
d3.json("data.json", function(error, data) { | ||
if (error) return console.warn(error); | ||
d3.select("#chart") | ||
.datum(data) | ||
.call(flamegraph); | ||
}); | ||
</script> | ||
}); | ||
</script> | ||
</body> | ||
``` | ||
### Input Format | ||
More detailed examples in the [/example](/example) directory. | ||
## Input Format | ||
Input stack is a simple hierarchical data structure in JSON format. | ||
@@ -79,5 +110,5 @@ | ||
JSON format can be converted from the folded stack format using the [node-stack-convert](https://github.com/spiermar/node-stack-convert) CLI tool. | ||
JSON format can be converted from the folded stack format using the [stacko](https://github.com/spiermar/stacko) CLI tool. | ||
### Interacting with entries | ||
## Interacting with entries | ||
@@ -99,6 +130,4 @@ Internally, the data is transformed into a d3 **hierarchy**. | ||
**This is a breaking change from previous versions of d3-flame-graph, which were based on version 3 of the d3 library*** | ||
This is a breaking change from previous versions of d3-flame-graph, which were based on version 3 of the d3 library. See [d3-hierarchy](https://github.com/d3/d3-hierarchy#hierarchy). | ||
See [d3-hierarchy](https://github.com/d3/d3-hierarchy#hierarchy). | ||
## API Reference | ||
@@ -112,15 +141,19 @@ | ||
Graph width in px. Defaults to 960px if not set. If <i>size</i> is specified, it will set de graph width, otherwise it will return the flameGraph object. | ||
Graph width in px. Defaults to 960px if not set. If <i>size</i> is specified, it will set the graph width, otherwise it will return the flameGraph object. | ||
<a name="height" href="#height">#</a> flameGraph.<b>height</b>(<i>[size]</i>) | ||
Graph height in px. Defaults to 540px if not set. If <i>size</i> is specified, it will set de graph height, otherwise it will return the flameGraph object. | ||
Graph height in px. Defaults to the number of cell rows times <a name="cellHeight" href="#cellHeight"><b>cellHeight</b></a> if not set. If <i>size</i> is specified, it will set the cell height, otherwise it will return the flameGraph object. | ||
<a name="cellHeight" href="#cellHeight">#</a> flameGraph.<b>cellHeight</b>(<i>[size]</i>) | ||
Cell height in px. Defaults to 18px if not set. If <i>size</i> is specified, it will set de cell height, otherwise it will return the flameGraph object. | ||
Cell height in px. Defaults to 18px if not set. If <i>size</i> is specified, it will set the cell height, otherwise it will return the flameGraph object. | ||
<a name="minFrameSize" href="#minFrameSize">#</a> flameGraph.<b>minFrameSize</b>(<i>[size]</i>) | ||
Minimum size of a frame, in px, to be displayed in the flame graph. Defaults to 0px if not set. If <i>size</i> is specified, it will set the minimum frame size, otherwise it will return the flameGraph object. | ||
<a name="title" href="#title">#</a> flameGraph.<b>title</b>(<i>[title]</i>) | ||
Title displayed on top of graph. Defaults to empty if not set. If <i>title</i> is specified, it will set de title displayed on the graph, otherwise it will return the flameGraph object. | ||
Title displayed on top of graph. Defaults to empty if not set. If <i>title</i> is specified, it will set the title displayed on the graph, otherwise it will return the flameGraph object. | ||
@@ -151,2 +184,12 @@ <a name="tooltip" href="#tooltip">#</a> flameGraph.<b>tooltip</b>(<i>[enabled]</i>) | ||
<a name="label" href="#label">#</a> flameGraph.<b>label</b>(<i>[function]</i>) | ||
Adds a function that returns a formatted label. Example: | ||
```js | ||
flameGraph.label(function(d) { | ||
return "name: " + d.name + ", value: " + d.value; | ||
}); | ||
``` | ||
<a name="sort" href="#sort">#</a> flameGraph.<b>sort</b>(<i>[enabled]</i>) | ||
@@ -153,0 +196,0 @@ |
(function() { | ||
'use strict'; | ||
/*jshint eqnull:true */ | ||
// https://tc39.github.io/ecma262/#sec-array.prototype.find | ||
if (!Array.prototype.find) { | ||
Object.defineProperty(Array.prototype, 'find', { | ||
value: function(predicate) { | ||
// 1. Let O be ? ToObject(this value). | ||
if (this == null) { | ||
throw new TypeError('"this" is null or not defined'); | ||
} | ||
var o = Object(this); | ||
// 2. Let len be ? ToLength(? Get(O, "length")). | ||
var len = o.length >>> 0; | ||
// 3. If IsCallable(predicate) is false, throw a TypeError exception. | ||
if (typeof predicate !== 'function') { | ||
throw new TypeError('predicate must be a function'); | ||
} | ||
// 4. If thisArg was supplied, let T be thisArg; else let T be undefined. | ||
var thisArg = arguments[1]; | ||
// 5. Let k be 0. | ||
var k = 0; | ||
// 6. Repeat, while k < len | ||
while (k < len) { | ||
// a. Let Pk be ! ToString(k). | ||
// b. Let kValue be ? Get(O, Pk). | ||
// c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). | ||
// d. If testResult is true, return kValue. | ||
var kValue = o[k]; | ||
if (predicate.call(thisArg, kValue, k, o)) { | ||
return kValue; | ||
} | ||
// e. Increase k by 1. | ||
k++; | ||
} | ||
// 7. Return undefined. | ||
return undefined; | ||
} | ||
}); | ||
} | ||
if (!Array.prototype.filter) | ||
Array.prototype.filter = function(func, thisArg) { | ||
if ( ! ((typeof func === 'function') && this) ) | ||
throw new TypeError(); | ||
var len = this.length >>> 0, | ||
res = new Array(len), // preallocate array | ||
c = 0, i = -1; | ||
if (thisArg === undefined) | ||
while (++i !== len) | ||
// checks to see if the key was set | ||
if (i in this) | ||
if (func(t[i], i, t)) | ||
res[c++] = t[i]; | ||
else | ||
while (++i !== len) | ||
// checks to see if the key was set | ||
if (i in this) | ||
if (func.call(thisArg, t[i], i, t)) | ||
res[c++] = t[i]; | ||
res.length = c; // shrink down array to proper size | ||
return res; | ||
}; | ||
/*jshint eqnull:false */ | ||
function flameGraph() { | ||
var w = 960, // graph width | ||
h = 540, // graph height | ||
h = null, // graph height | ||
c = 18, // cell height | ||
@@ -14,5 +86,6 @@ selection = null, // selection | ||
transitionEase = d3.easeCubic, // tooltip offset | ||
sort = true, | ||
sort = false, | ||
reversed = false, // reverse the graph direction | ||
clickHandler = null; | ||
clickHandler = null, | ||
minFrameSize = 0; | ||
@@ -27,4 +100,16 @@ var tip = d3.tip() | ||
function name(d) { | ||
return d.data.n || d.data.name; | ||
} | ||
function children(d) { | ||
return d.c || d.children; | ||
} | ||
function value(d) { | ||
return d.v || d.value; | ||
} | ||
var label = function(d) { | ||
return d.data.name + " (" + d3.format(".3f")(100 * (d.x1 - d.x0), 3) + "%, " + d.data.value + " samples)"; | ||
return name(d) + " (" + d3.format(".3f")(100 * (d.x1 - d.x0), 3) + "%, " + value(d) + " samples)"; | ||
}; | ||
@@ -38,8 +123,4 @@ | ||
function name(d) { | ||
return d.data.name; | ||
} | ||
var colorMapper = function(d) { | ||
return d.highlight ? "#E600E6" : colorHash(d.data.name); | ||
return d.highlight ? "#E600E6" : colorHash(name(d)); | ||
}; | ||
@@ -85,8 +166,11 @@ | ||
if(!d.data.original) { | ||
d.data.original = d.data.value; | ||
d.data.original = value(d); | ||
} | ||
d.data.value = 0; | ||
if(d.children) { | ||
d.children.forEach(hide); | ||
d.data.v = 0; | ||
if (d.data.value) { | ||
delete d.data.value; | ||
} | ||
if(children(d)) { | ||
children(d).forEach(hide); | ||
} | ||
} | ||
@@ -97,6 +181,6 @@ | ||
if(d.data.original) { | ||
d.data.value = d.data.original; | ||
d.data.v = d.data.original; | ||
} | ||
if(d.children) { | ||
d.children.forEach(show); | ||
if(children(d)) { | ||
children(d).forEach(show); | ||
} | ||
@@ -155,6 +239,6 @@ } | ||
function searchInner(d) { | ||
var label = d.data.name; | ||
var label = name(d); | ||
if (d.children) { | ||
d.children.forEach(function (child) { | ||
if (children(d)) { | ||
children(d).forEach(function (child) { | ||
searchInner(child); | ||
@@ -178,4 +262,4 @@ }); | ||
d.highlight = false; | ||
if(d.children) { | ||
d.children.forEach(function(child) { | ||
if(children(d)) { | ||
children(d).forEach(function(child) { | ||
clear(child); | ||
@@ -190,3 +274,3 @@ }); | ||
} else if (sort) { | ||
return d3.ascending(a.data.name, b.data.name); | ||
return d3.ascending(name(a), name(b)); | ||
} | ||
@@ -197,2 +281,13 @@ } | ||
function filterNodes(root) { | ||
var nodeList = root.descendants(); | ||
if (minFrameSize > 0) { | ||
var kx = w / (root.x1 - root.x0); | ||
nodeList = nodeList.filter(function(el) { | ||
return ((el.x1 - el.x0) * kx) > minFrameSize; | ||
}); | ||
} | ||
return nodeList; | ||
} | ||
function update() { | ||
@@ -204,4 +299,3 @@ | ||
if (sort) | ||
root.sort(doSort); | ||
if (sort) root.sort(doSort); | ||
root.sum(function(d) { | ||
@@ -212,6 +306,7 @@ if (d.fade) { | ||
// The node's self value is its total value minus all children. | ||
var v = d.v || d.value || 0; | ||
if (d.children) { | ||
for (var i = 0; i < d.children.length; i++) { | ||
v -= d.children[i].value; | ||
var v = value(d) || 0; | ||
if (children(d)) { | ||
var c = children(d); | ||
for (var i = 0; i < c.length; i++) { | ||
v -= c[i].value; | ||
} | ||
@@ -226,3 +321,4 @@ } | ||
var g = d3.select(this).select("svg").selectAll("g").data(root.descendants()); | ||
var descendants = filterNodes(root); | ||
var g = d3.select(this).select("svg").selectAll("g").data(descendants, function(d) { return d.id; }); | ||
@@ -234,5 +330,3 @@ g.transition() | ||
g.select("rect").transition() | ||
.duration(transitionDuration) | ||
.ease(transitionEase) | ||
g.select("rect") | ||
.attr("width", width); | ||
@@ -243,5 +337,8 @@ | ||
.attr("transform", function(d) { return "translate(" + x(d.x0) + "," + (reversed ? y(d.depth) : (h - y(d.depth) - c)) + ")"; }); | ||
node.append("svg:rect").attr("width", width); | ||
node.append("svg:rect") | ||
.transition() | ||
.delay(transitionDuration / 2) | ||
.attr("width", width); | ||
if (!tooltip) | ||
@@ -254,7 +351,7 @@ node.append("svg:title"); | ||
// Now we have to re-select to see the new elements (why?). | ||
g = d3.select(this).select("svg").selectAll("g").data(root.descendants()); | ||
g = d3.select(this).select("svg").selectAll("g").data(descendants, function(d) { return d.id; }); | ||
g.attr("width", width) | ||
.attr("height", function(d) { return c; }) | ||
.attr("name", function(d) { return d.data.name; }) | ||
.attr("name", function(d) { return name(d); }) | ||
.attr("class", function(d) { return d.data.fade ? "frame fade" : "frame"; }); | ||
@@ -276,2 +373,4 @@ | ||
.style("display", function(d) { return (width(d) < 35) ? "none" : "block";}) | ||
.transition() | ||
.delay(transitionDuration) | ||
.text(name); | ||
@@ -281,3 +380,4 @@ | ||
g.exit().remove(); | ||
g.exit() | ||
.remove(); | ||
@@ -296,4 +396,4 @@ g.on('mouseover', function(d) { | ||
samples.forEach(function (sample) { | ||
var node = _.find(data, function (element) { | ||
return element.name === sample.name; | ||
var node = data.find(function (element) { | ||
return (name(element) === name(sample)); | ||
}); | ||
@@ -319,4 +419,19 @@ | ||
function s4() { | ||
return Math.floor((1 + Math.random()) * 0x10000) | ||
.toString(16) | ||
.substring(1); | ||
} | ||
function injectIds(node) { | ||
node.id = s4(); | ||
var children = node.c || node.children || []; | ||
for (var i = 0; i < children.length; i++) { | ||
injectIds(children[i]); | ||
} | ||
} | ||
function chart(s) { | ||
var root = d3.hierarchy(s.datum(), function(d) { return d.c || d.children; }); | ||
var root = d3.hierarchy(s.datum(), function(d) { return children(d); }); | ||
injectIds(root); | ||
selection = s.datum(root); | ||
@@ -326,2 +441,6 @@ | ||
if (!h) { | ||
h = (root.height + 2) * c; | ||
} | ||
selection.each(function(data) { | ||
@@ -452,3 +571,3 @@ | ||
merge([root.data], [samples]); | ||
newRoot = d3.hierarchy(root.data, function(d) { return d.c || d.children; }); | ||
newRoot = d3.hierarchy(root.data, function(d) { return children(d); }); | ||
}); | ||
@@ -465,2 +584,8 @@ selection = selection.datum(newRoot); | ||
chart.minFrameSize = function (_) { | ||
if (!arguments.length) { return minFrameSize; } | ||
minFrameSize = _; | ||
return chart; | ||
}; | ||
return chart; | ||
@@ -467,0 +592,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
11397994
21
11608
243
3
+ Addedd3-tip@^0.7.1
+ Addedd3-tip@0.7.1(transitive)