You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1010 lines
24 KiB

  1. // A D3.js plugin that produces flame graphs from hierarchical data.
  2. // https://github.com/spiermar/d3-flame-graph
  3. // Version 2.0.0-alpha4
  4. // See LICENSE file for license details
  5. package d3flamegraph
  6. // JSSource returns the d3-flamegraph.js file
  7. const JSSource = `
  8. (function (global, factory) {
  9. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3')) :
  10. typeof define === 'function' && define.amd ? define(['exports', 'd3'], factory) :
  11. (factory((global.d3 = global.d3 || {}),global.d3));
  12. }(this, (function (exports,d3) { 'use strict';
  13. var d3__default = 'default' in d3 ? d3['default'] : d3;
  14. var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
  15. function createCommonjsModule(fn, module) {
  16. return module = { exports: {} }, fn(module, module.exports), module.exports;
  17. }
  18. var d3Tip = createCommonjsModule(function (module) {
  19. // d3.tip
  20. // Copyright (c) 2013 Justin Palmer
  21. //
  22. // Tooltips for d3.js SVG visualizations
  23. (function (root, factory) {
  24. if (typeof undefined === 'function' && undefined.amd) {
  25. // AMD. Register as an anonymous module with d3 as a dependency.
  26. undefined(['d3'], factory);
  27. } else if ('object' === 'object' && module.exports) {
  28. // CommonJS
  29. var d3$$1 = d3__default;
  30. module.exports = factory(d3$$1);
  31. } else {
  32. // Browser global.
  33. root.d3.tip = factory(root.d3);
  34. }
  35. }(commonjsGlobal, function (d3$$1) {
  36. // Public - contructs a new tooltip
  37. //
  38. // Returns a tip
  39. return function() {
  40. var direction = d3_tip_direction,
  41. offset = d3_tip_offset,
  42. html = d3_tip_html,
  43. node = initNode(),
  44. svg = null,
  45. point = null,
  46. target = null;
  47. function tip(vis) {
  48. svg = getSVGNode(vis);
  49. point = svg.createSVGPoint();
  50. document.body.appendChild(node);
  51. }
  52. // Public - show the tooltip on the screen
  53. //
  54. // Returns a tip
  55. tip.show = function() {
  56. var args = Array.prototype.slice.call(arguments);
  57. if(args[args.length - 1] instanceof SVGElement) target = args.pop();
  58. var content = html.apply(this, args),
  59. poffset = offset.apply(this, args),
  60. dir = direction.apply(this, args),
  61. nodel = getNodeEl(),
  62. i = directions.length,
  63. coords,
  64. scrollTop = document.documentElement.scrollTop || document.body.scrollTop,
  65. scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
  66. nodel.html(content)
  67. .style('opacity', 1).style('pointer-events', 'all');
  68. while(i--) nodel.classed(directions[i], false);
  69. coords = direction_callbacks.get(dir).apply(this);
  70. nodel.classed(dir, true)
  71. .style('top', (coords.top + poffset[0]) + scrollTop + 'px')
  72. .style('left', (coords.left + poffset[1]) + scrollLeft + 'px');
  73. return tip;
  74. };
  75. // Public - hide the tooltip
  76. //
  77. // Returns a tip
  78. tip.hide = function() {
  79. var nodel = getNodeEl();
  80. nodel.style('opacity', 0).style('pointer-events', 'none');
  81. return tip
  82. };
  83. // Public: Proxy attr calls to the d3 tip container. Sets or gets attribute value.
  84. //
  85. // n - name of the attribute
  86. // v - value of the attribute
  87. //
  88. // Returns tip or attribute value
  89. tip.attr = function(n, v) {
  90. if (arguments.length < 2 && typeof n === 'string') {
  91. return getNodeEl().attr(n)
  92. } else {
  93. var args = Array.prototype.slice.call(arguments);
  94. d3$$1.selection.prototype.attr.apply(getNodeEl(), args);
  95. }
  96. return tip
  97. };
  98. // Public: Proxy style calls to the d3 tip container. Sets or gets a style value.
  99. //
  100. // n - name of the property
  101. // v - value of the property
  102. //
  103. // Returns tip or style property value
  104. tip.style = function(n, v) {
  105. if (arguments.length < 2 && typeof n === 'string') {
  106. return getNodeEl().style(n)
  107. } else {
  108. var args = Array.prototype.slice.call(arguments);
  109. d3$$1.selection.prototype.style.apply(getNodeEl(), args);
  110. }
  111. return tip
  112. };
  113. // Public: Set or get the direction of the tooltip
  114. //
  115. // v - One of n(north), s(south), e(east), or w(west), nw(northwest),
  116. // sw(southwest), ne(northeast) or se(southeast)
  117. //
  118. // Returns tip or direction
  119. tip.direction = function(v) {
  120. if (!arguments.length) return direction
  121. direction = v == null ? v : functor(v);
  122. return tip
  123. };
  124. // Public: Sets or gets the offset of the tip
  125. //
  126. // v - Array of [x, y] offset
  127. //
  128. // Returns offset or
  129. tip.offset = function(v) {
  130. if (!arguments.length) return offset
  131. offset = v == null ? v : functor(v);
  132. return tip
  133. };
  134. // Public: sets or gets the html value of the tooltip
  135. //
  136. // v - String value of the tip
  137. //
  138. // Returns html value or tip
  139. tip.html = function(v) {
  140. if (!arguments.length) return html
  141. html = v == null ? v : functor(v);
  142. return tip
  143. };
  144. // Public: destroys the tooltip and removes it from the DOM
  145. //
  146. // Returns a tip
  147. tip.destroy = function() {
  148. if(node) {
  149. getNodeEl().remove();
  150. node = null;
  151. }
  152. return tip;
  153. };
  154. function d3_tip_direction() { return 'n' }
  155. function d3_tip_offset() { return [0, 0] }
  156. function d3_tip_html() { return ' ' }
  157. var direction_callbacks = d3$$1.map({
  158. n: direction_n,
  159. s: direction_s,
  160. e: direction_e,
  161. w: direction_w,
  162. nw: direction_nw,
  163. ne: direction_ne,
  164. sw: direction_sw,
  165. se: direction_se
  166. }),
  167. directions = direction_callbacks.keys();
  168. function direction_n() {
  169. var bbox = getScreenBBox();
  170. return {
  171. top: bbox.n.y - node.offsetHeight,
  172. left: bbox.n.x - node.offsetWidth / 2
  173. }
  174. }
  175. function direction_s() {
  176. var bbox = getScreenBBox();
  177. return {
  178. top: bbox.s.y,
  179. left: bbox.s.x - node.offsetWidth / 2
  180. }
  181. }
  182. function direction_e() {
  183. var bbox = getScreenBBox();
  184. return {
  185. top: bbox.e.y - node.offsetHeight / 2,
  186. left: bbox.e.x
  187. }
  188. }
  189. function direction_w() {
  190. var bbox = getScreenBBox();
  191. return {
  192. top: bbox.w.y - node.offsetHeight / 2,
  193. left: bbox.w.x - node.offsetWidth
  194. }
  195. }
  196. function direction_nw() {
  197. var bbox = getScreenBBox();
  198. return {
  199. top: bbox.nw.y - node.offsetHeight,
  200. left: bbox.nw.x - node.offsetWidth
  201. }
  202. }
  203. function direction_ne() {
  204. var bbox = getScreenBBox();
  205. return {
  206. top: bbox.ne.y - node.offsetHeight,
  207. left: bbox.ne.x
  208. }
  209. }
  210. function direction_sw() {
  211. var bbox = getScreenBBox();
  212. return {
  213. top: bbox.sw.y,
  214. left: bbox.sw.x - node.offsetWidth
  215. }
  216. }
  217. function direction_se() {
  218. var bbox = getScreenBBox();
  219. return {
  220. top: bbox.se.y,
  221. left: bbox.e.x
  222. }
  223. }
  224. function initNode() {
  225. var node = d3$$1.select(document.createElement('div'));
  226. node.style('position', 'absolute').style('top', 0).style('opacity', 0)
  227. .style('pointer-events', 'none').style('box-sizing', 'border-box');
  228. return node.node()
  229. }
  230. function getSVGNode(el) {
  231. el = el.node();
  232. if(el.tagName.toLowerCase() === 'svg')
  233. return el
  234. return el.ownerSVGElement
  235. }
  236. function getNodeEl() {
  237. if(node === null) {
  238. node = initNode();
  239. // re-add node to DOM
  240. document.body.appendChild(node);
  241. }
  242. return d3$$1.select(node);
  243. }
  244. // Private - gets the screen coordinates of a shape
  245. //
  246. // Given a shape on the screen, will return an SVGPoint for the directions
  247. // n(north), s(south), e(east), w(west), ne(northeast), se(southeast), nw(northwest),
  248. // sw(southwest).
  249. //
  250. // +-+-+
  251. // | |
  252. // + +
  253. // | |
  254. // +-+-+
  255. //
  256. // Returns an Object {n, s, e, w, nw, sw, ne, se}
  257. function getScreenBBox() {
  258. var targetel = target || d3$$1.event.target;
  259. while ('undefined' === typeof targetel.getScreenCTM && 'undefined' === targetel.parentNode) {
  260. targetel = targetel.parentNode;
  261. }
  262. var bbox = {},
  263. matrix = targetel.getScreenCTM(),
  264. tbbox = targetel.getBBox(),
  265. width = tbbox.width,
  266. height = tbbox.height,
  267. x = tbbox.x,
  268. y = tbbox.y;
  269. point.x = x;
  270. point.y = y;
  271. bbox.nw = point.matrixTransform(matrix);
  272. point.x += width;
  273. bbox.ne = point.matrixTransform(matrix);
  274. point.y += height;
  275. bbox.se = point.matrixTransform(matrix);
  276. point.x -= width;
  277. bbox.sw = point.matrixTransform(matrix);
  278. point.y -= height / 2;
  279. bbox.w = point.matrixTransform(matrix);
  280. point.x += width;
  281. bbox.e = point.matrixTransform(matrix);
  282. point.x -= width / 2;
  283. point.y -= height / 2;
  284. bbox.n = point.matrixTransform(matrix);
  285. point.y += height;
  286. bbox.s = point.matrixTransform(matrix);
  287. return bbox
  288. }
  289. // Private - replace D3JS 3.X d3.functor() function
  290. function functor(v) {
  291. return typeof v === "function" ? v : function() {
  292. return v
  293. }
  294. }
  295. return tip
  296. };
  297. }));
  298. });
  299. var flamegraph = function () {
  300. var w = 960; // graph width
  301. var h = null; // graph height
  302. var c = 18; // cell height
  303. var selection = null; // selection
  304. var tooltip = true; // enable tooltip
  305. var title = ''; // graph title
  306. var transitionDuration = 750;
  307. var transitionEase = d3.easeCubic; // tooltip offset
  308. var sort = false;
  309. var inverted = false; // invert the graph direction
  310. var clickHandler = null;
  311. var minFrameSize = 0;
  312. var details = null;
  313. var tip = d3Tip()
  314. .direction('s')
  315. .offset([8, 0])
  316. .attr('class', 'd3-flame-graph-tip')
  317. .html(function (d) { return label(d) });
  318. var svg;
  319. function name (d) {
  320. return d.data.n || d.data.name
  321. }
  322. function libtype (d) {
  323. return d.data.l || d.data.libtype
  324. }
  325. function children (d) {
  326. return d.c || d.children
  327. }
  328. function value (d) {
  329. return d.v || d.value
  330. }
  331. var label = function (d) {
  332. return name(d) + ' (' + d3.format('.3f')(100 * (d.x1 - d.x0), 3) + '%, ' + value(d) + ' samples)'
  333. };
  334. function setDetails (t) {
  335. if (details) { details.innerHTML = t; }
  336. }
  337. var colorMapper = function (d) {
  338. return d.highlight ? '#E600E6' : colorHash(name(d), libtype(d))
  339. };
  340. function generateHash (name) {
  341. // Return a vector (0.0->1.0) that is a hash of the input string.
  342. // The hash is computed to favor early characters over later ones, so
  343. // that strings with similar starts have similar vectors. Only the first
  344. // 6 characters are considered.
  345. const MAX_CHAR = 6;
  346. var hash = 0;
  347. var maxHash = 0;
  348. var weight = 1;
  349. var mod = 10;
  350. if (name) {
  351. for (var i = 0; i < name.length; i++) {
  352. if (i > MAX_CHAR) { break }
  353. hash += weight * (name.charCodeAt(i) % mod);
  354. maxHash += weight * (mod - 1);
  355. weight *= 0.70;
  356. }
  357. if (maxHash > 0) { hash = hash / maxHash; }
  358. }
  359. return hash
  360. }
  361. function colorHash (name, libtype) {
  362. // Return a color for the given name and library type. The library type
  363. // selects the hue, and the name is hashed to a color in that hue.
  364. var r;
  365. var g;
  366. var b;
  367. // Select hue. Order is important.
  368. var hue;
  369. if (typeof libtype === 'undefined' || libtype === '') {
  370. // default when libtype is not in use
  371. hue = 'warm';
  372. } else {
  373. hue = 'red';
  374. if (name.match(/::/)) {
  375. hue = 'yellow';
  376. }
  377. if (libtype === 'kernel') {
  378. hue = 'orange';
  379. } else if (libtype === 'jit') {
  380. hue = 'green';
  381. } else if (libtype === 'inlined') {
  382. hue = 'aqua';
  383. }
  384. }
  385. // calculate hash
  386. var vector = 0;
  387. if (name) {
  388. var nameArr = name.split('` + "`" + `');
  389. if (nameArr.length > 1) {
  390. name = nameArr[nameArr.length - 1]; // drop module name if present
  391. }
  392. name = name.split('(')[0]; // drop extra info
  393. vector = generateHash(name);
  394. }
  395. // calculate color
  396. if (hue === 'red') {
  397. r = 200 + Math.round(55 * vector);
  398. g = 50 + Math.round(80 * vector);
  399. b = g;
  400. } else if (hue === 'orange') {
  401. r = 190 + Math.round(65 * vector);
  402. g = 90 + Math.round(65 * vector);
  403. b = 0;
  404. } else if (hue === 'yellow') {
  405. r = 175 + Math.round(55 * vector);
  406. g = r;
  407. b = 50 + Math.round(20 * vector);
  408. } else if (hue === 'green') {
  409. r = 50 + Math.round(60 * vector);
  410. g = 200 + Math.round(55 * vector);
  411. b = r;
  412. } else if (hue === 'aqua') {
  413. r = 50 + Math.round(60 * vector);
  414. g = 165 + Math.round(55 * vector);
  415. b = g;
  416. } else {
  417. // original warm palette
  418. r = 200 + Math.round(55 * vector);
  419. g = 0 + Math.round(230 * (1 - vector));
  420. b = 0 + Math.round(55 * (1 - vector));
  421. }
  422. return 'rgb(' + r + ',' + g + ',' + b + ')'
  423. }
  424. function hide (d) {
  425. d.data.hide = true;
  426. if (children(d)) {
  427. children(d).forEach(hide);
  428. }
  429. }
  430. function show (d) {
  431. d.data.fade = false;
  432. d.data.hide = false;
  433. if (children(d)) {
  434. children(d).forEach(show);
  435. }
  436. }
  437. function getSiblings (d) {
  438. var siblings = [];
  439. if (d.parent) {
  440. var me = d.parent.children.indexOf(d);
  441. siblings = d.parent.children.slice(0);
  442. siblings.splice(me, 1);
  443. }
  444. return siblings
  445. }
  446. function hideSiblings (d) {
  447. var siblings = getSiblings(d);
  448. siblings.forEach(function (s) {
  449. hide(s);
  450. });
  451. if (d.parent) {
  452. hideSiblings(d.parent);
  453. }
  454. }
  455. function fadeAncestors (d) {
  456. if (d.parent) {
  457. d.parent.data.fade = true;
  458. fadeAncestors(d.parent);
  459. }
  460. }
  461. // function getRoot (d) {
  462. // if (d.parent) {
  463. // return getRoot(d.parent)
  464. // }
  465. // return d
  466. // }
  467. function zoom (d) {
  468. tip.hide(d);
  469. hideSiblings(d);
  470. show(d);
  471. fadeAncestors(d);
  472. update();
  473. if (typeof clickHandler === 'function') {
  474. clickHandler(d);
  475. }
  476. }
  477. function searchTree (d, term) {
  478. var re = new RegExp(term);
  479. var searchResults = [];
  480. function searchInner (d) {
  481. var label = name(d);
  482. if (children(d)) {
  483. children(d).forEach(function (child) {
  484. searchInner(child);
  485. });
  486. }
  487. if (label.match(re)) {
  488. d.highlight = true;
  489. searchResults.push(d);
  490. } else {
  491. d.highlight = false;
  492. }
  493. }
  494. searchInner(d);
  495. return searchResults
  496. }
  497. function clear (d) {
  498. d.highlight = false;
  499. if (children(d)) {
  500. children(d).forEach(function (child) {
  501. clear(child);
  502. });
  503. }
  504. }
  505. function doSort (a, b) {
  506. if (typeof sort === 'function') {
  507. return sort(a, b)
  508. } else if (sort) {
  509. return d3.ascending(name(a), name(b))
  510. }
  511. }
  512. var p = d3.partition();
  513. function filterNodes (root) {
  514. var nodeList = root.descendants();
  515. if (minFrameSize > 0) {
  516. var kx = w / (root.x1 - root.x0);
  517. nodeList = nodeList.filter(function (el) {
  518. return ((el.x1 - el.x0) * kx) > minFrameSize
  519. });
  520. }
  521. return nodeList
  522. }
  523. function update () {
  524. selection.each(function (root) {
  525. var x = d3.scaleLinear().range([0, w]);
  526. var y = d3.scaleLinear().range([0, c]);
  527. if (sort) root.sort(doSort);
  528. root.sum(function (d) {
  529. if (d.fade || d.hide) {
  530. return 0
  531. }
  532. // The node's self value is its total value minus all children.
  533. var v = value(d);
  534. if (children(d)) {
  535. var c = children(d);
  536. for (var i = 0; i < c.length; i++) {
  537. v -= value(c[i]);
  538. }
  539. }
  540. return v
  541. });
  542. p(root);
  543. var kx = w / (root.x1 - root.x0);
  544. function width (d) { return (d.x1 - d.x0) * kx }
  545. var descendants = filterNodes(root);
  546. var g = d3.select(this).select('svg').selectAll('g').data(descendants, function (d) { return d.id });
  547. g.transition()
  548. .duration(transitionDuration)
  549. .ease(transitionEase)
  550. .attr('transform', function (d) { return 'translate(' + x(d.x0) + ',' + (inverted ? y(d.depth) : (h - y(d.depth) - c)) + ')' });
  551. g.select('rect')
  552. .attr('width', width);
  553. var node = g.enter()
  554. .append('svg:g')
  555. .attr('transform', function (d) { return 'translate(' + x(d.x0) + ',' + (inverted ? y(d.depth) : (h - y(d.depth) - c)) + ')' });
  556. node.append('svg:rect')
  557. .transition()
  558. .delay(transitionDuration / 2)
  559. .attr('width', width);
  560. if (!tooltip) { node.append('svg:title'); }
  561. node.append('foreignObject')
  562. .append('xhtml:div');
  563. // Now we have to re-select to see the new elements (why?).
  564. g = d3.select(this).select('svg').selectAll('g').data(descendants, function (d) { return d.id });
  565. g.attr('width', width)
  566. .attr('height', function (d) { return c })
  567. .attr('name', function (d) { return name(d) })
  568. .attr('class', function (d) { return d.data.fade ? 'frame fade' : 'frame' });
  569. g.select('rect')
  570. .attr('height', function (d) { return c })
  571. .attr('fill', function (d) { return colorMapper(d) });
  572. if (!tooltip) {
  573. g.select('title')
  574. .text(label);
  575. }
  576. g.select('foreignObject')
  577. .attr('width', width)
  578. .attr('height', function (d) { return c })
  579. .select('div')
  580. .attr('class', 'd3-flame-graph-label')
  581. .style('display', function (d) { return (width(d) < 35) ? 'none' : 'block' })
  582. .transition()
  583. .delay(transitionDuration)
  584. .text(name);
  585. g.on('click', zoom);
  586. g.exit()
  587. .remove();
  588. g.on('mouseover', function (d) {
  589. if (tooltip) tip.show(d, this);
  590. setDetails(label(d));
  591. }).on('mouseout', function (d) {
  592. if (tooltip) tip.hide(d);
  593. setDetails('');
  594. });
  595. });
  596. }
  597. function merge (data, samples) {
  598. samples.forEach(function (sample) {
  599. var node = data.find(function (element) {
  600. return (element.name === sample.name)
  601. });
  602. if (node) {
  603. if (node.original) {
  604. node.original += sample.value;
  605. } else {
  606. node.value += sample.value;
  607. }
  608. if (sample.children) {
  609. if (!node.children) {
  610. node.children = [];
  611. }
  612. merge(node.children, sample.children);
  613. }
  614. } else {
  615. data.push(sample);
  616. }
  617. });
  618. }
  619. function s4 () {
  620. return Math.floor((1 + Math.random()) * 0x10000)
  621. .toString(16)
  622. .substring(1)
  623. }
  624. function injectIds (node) {
  625. node.id = s4() + '-' + s4() + '-' + '-' + s4() + '-' + s4();
  626. var children = node.c || node.children || [];
  627. for (var i = 0; i < children.length; i++) {
  628. injectIds(children[i]);
  629. }
  630. }
  631. function chart (s) {
  632. var root = d3.hierarchy(
  633. s.datum(), function (d) { return children(d) }
  634. );
  635. injectIds(root);
  636. selection = s.datum(root);
  637. if (!arguments.length) return chart
  638. if (!h) {
  639. h = (root.height + 2) * c;
  640. }
  641. selection.each(function (data) {
  642. if (!svg) {
  643. svg = d3.select(this)
  644. .append('svg:svg')
  645. .attr('width', w)
  646. .attr('height', h)
  647. .attr('class', 'partition d3-flame-graph')
  648. .call(tip);
  649. svg.append('svg:text')
  650. .attr('class', 'title')
  651. .attr('text-anchor', 'middle')
  652. .attr('y', '25')
  653. .attr('x', w / 2)
  654. .attr('fill', '#808080')
  655. .text(title);
  656. }
  657. });
  658. // first draw
  659. update();
  660. }
  661. chart.height = function (_) {
  662. if (!arguments.length) { return h }
  663. h = _;
  664. return chart
  665. };
  666. chart.width = function (_) {
  667. if (!arguments.length) { return w }
  668. w = _;
  669. return chart
  670. };
  671. chart.cellHeight = function (_) {
  672. if (!arguments.length) { return c }
  673. c = _;
  674. return chart
  675. };
  676. chart.tooltip = function (_) {
  677. if (!arguments.length) { return tooltip }
  678. if (typeof _ === 'function') {
  679. tip = _;
  680. }
  681. tooltip = !!_;
  682. return chart
  683. };
  684. chart.title = function (_) {
  685. if (!arguments.length) { return title }
  686. title = _;
  687. return chart
  688. };
  689. chart.transitionDuration = function (_) {
  690. if (!arguments.length) { return transitionDuration }
  691. transitionDuration = _;
  692. return chart
  693. };
  694. chart.transitionEase = function (_) {
  695. if (!arguments.length) { return transitionEase }
  696. transitionEase = _;
  697. return chart
  698. };
  699. chart.sort = function (_) {
  700. if (!arguments.length) { return sort }
  701. sort = _;
  702. return chart
  703. };
  704. chart.inverted = function (_) {
  705. if (!arguments.length) { return inverted }
  706. inverted = _;
  707. return chart
  708. };
  709. chart.label = function (_) {
  710. if (!arguments.length) { return label }
  711. label = _;
  712. return chart
  713. };
  714. chart.search = function (term) {
  715. var searchResults = [];
  716. selection.each(function (data) {
  717. searchResults = searchTree(data, term);
  718. update();
  719. });
  720. return searchResults
  721. };
  722. chart.clear = function () {
  723. selection.each(function (data) {
  724. clear(data);
  725. update();
  726. });
  727. };
  728. chart.zoomTo = function (d) {
  729. zoom(d);
  730. };
  731. chart.resetZoom = function () {
  732. selection.each(function (data) {
  733. zoom(data); // zoom to root
  734. });
  735. };
  736. chart.onClick = function (_) {
  737. if (!arguments.length) {
  738. return clickHandler
  739. }
  740. clickHandler = _;
  741. return chart
  742. };
  743. chart.merge = function (samples) {
  744. var newRoot; // Need to re-create hierarchy after data changes.
  745. selection.each(function (root) {
  746. merge([root.data], [samples]);
  747. newRoot = d3.hierarchy(root.data, function (d) { return children(d) });
  748. injectIds(newRoot);
  749. });
  750. selection = selection.datum(newRoot);
  751. update();
  752. };
  753. chart.color = function (_) {
  754. if (!arguments.length) { return colorMapper }
  755. colorMapper = _;
  756. return chart
  757. };
  758. chart.minFrameSize = function (_) {
  759. if (!arguments.length) { return minFrameSize }
  760. minFrameSize = _;
  761. return chart
  762. };
  763. chart.details = function (_) {
  764. if (!arguments.length) { return details }
  765. details = _;
  766. return chart
  767. };
  768. return chart
  769. };
  770. exports.flamegraph = flamegraph;
  771. Object.defineProperty(exports, '__esModule', { value: true });
  772. })));
  773. `
  774. // CSSSource returns the d3-flamegraph.css file
  775. const CSSSource = `
  776. .d3-flame-graph rect {
  777. stroke: #EEEEEE;
  778. fill-opacity: .8;
  779. }
  780. .d3-flame-graph rect:hover {
  781. stroke: #474747;
  782. stroke-width: 0.5;
  783. cursor: pointer;
  784. }
  785. .d3-flame-graph-label {
  786. pointer-events: none;
  787. white-space: nowrap;
  788. text-overflow: ellipsis;
  789. overflow: hidden;
  790. font-size: 12px;
  791. font-family: Verdana;
  792. margin-left: 4px;
  793. margin-right: 4px;
  794. line-height: 1.5;
  795. padding: 0 0 0;
  796. font-weight: 400;
  797. color: black;
  798. text-align: left;
  799. }
  800. .d3-flame-graph .fade {
  801. opacity: 0.6 !important;
  802. }
  803. .d3-flame-graph .title {
  804. font-size: 20px;
  805. font-family: Verdana;
  806. }
  807. .d3-flame-graph-tip {
  808. line-height: 1;
  809. font-family: Verdana;
  810. font-size: 12px;
  811. padding: 12px;
  812. background: rgba(0, 0, 0, 0.8);
  813. color: #fff;
  814. border-radius: 2px;
  815. pointer-events: none;
  816. }
  817. /* Creates a small triangle extender for the tooltip */
  818. .d3-flame-graph-tip:after {
  819. box-sizing: border-box;
  820. display: inline;
  821. font-size: 10px;
  822. width: 100%;
  823. line-height: 1;
  824. color: rgba(0, 0, 0, 0.8);
  825. position: absolute;
  826. pointer-events: none;
  827. }
  828. /* Northward tooltips */
  829. .d3-flame-graph-tip.n:after {
  830. content: "\25BC";
  831. margin: -1px 0 0 0;
  832. top: 100%;
  833. left: 0;
  834. text-align: center;
  835. }
  836. /* Eastward tooltips */
  837. .d3-flame-graph-tip.e:after {
  838. content: "\25C0";
  839. margin: -4px 0 0 0;
  840. top: 50%;
  841. left: -8px;
  842. }
  843. /* Southward tooltips */
  844. .d3-flame-graph-tip.s:after {
  845. content: "\25B2";
  846. margin: 0 0 1px 0;
  847. top: -8px;
  848. left: 0;
  849. text-align: center;
  850. }
  851. /* Westward tooltips */
  852. .d3-flame-graph-tip.w:after {
  853. content: "\25B6";
  854. margin: -4px 0 0 -1px;
  855. top: 50%;
  856. left: 100%;
  857. }
  858. `