d3-gantt.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. d3.gantt = function() {
  2. function gantt(config, logs, autoscale) {
  3. parseLogs(config, logs);
  4. if (autoscale) {
  5. gantt.timeDomain([minT, maxT]);
  6. }
  7. initAxis();
  8. // create svg element
  9. svg = d3.select(selector)
  10. .append("svg")
  11. .attr("class", "chart")
  12. .attr("width", width + margin.left + margin.right)
  13. .attr("height", height + margin.top + margin.bottom)
  14. ;
  15. // create arrowhead marker
  16. defs = svg.append("defs");
  17. defs.append("marker")
  18. .attr("id", "arrow")
  19. .attr("viewBox", "0 -5 10 10")
  20. .attr("refX", 5)
  21. .attr("refY", 0)
  22. .attr("markerWidth", 4)
  23. .attr("markerHeight", 4)
  24. .attr("orient", "auto")
  25. .append("path")
  26. .attr("d", "M0,-5L10,0L0,5")
  27. .attr("class","arrowHead")
  28. ;
  29. zoom = d3.zoom()
  30. .scaleExtent([0.1, 1000])
  31. //.translateExtent([0, 0], [1000,0])
  32. .on("zoom", function() {
  33. if (tipShown != null) {
  34. tip.hide(tipShown);
  35. }
  36. var tr = d3.event.transform;
  37. xZoomed = tr.rescaleX(x);
  38. svg.select("g.x.axis").call(xAxis.scale(xZoomed));
  39. var dy = d3.event.sourceEvent.screenY - zoom.startScreenY;
  40. var newScrollTop = documentBodyScrollTop() - dy;
  41. window.scrollTo(documentBodyScrollLeft(), newScrollTop);
  42. documentBodyScrollTop(newScrollTop);
  43. zoom.startScreenY = d3.event.sourceEvent.screenY;
  44. zoomContainer1.attr("transform", "translate(" + tr.x + ",0) scale(" + tr.k + ",1)");
  45. zoomContainer2.attr("transform", "translate(" + tr.x + ",0) scale(" + tr.k + ",1)");
  46. render();
  47. })
  48. .on("start", function() {
  49. zoom.startScreenY = d3.event.sourceEvent.screenY;
  50. })
  51. .on("end", function() {
  52. })
  53. ;
  54. svgChartContainer = svg.append('g')
  55. .attr("transform", "translate(" + margin.left + ", " + margin.top + ")")
  56. ;
  57. svgChart = svgChartContainer.append("svg")
  58. .attr("top", 0)
  59. .attr("left", 0)
  60. .attr("width", width)
  61. .attr("height", height)
  62. .attr("viewBox", "0 0 " + width + " " + height)
  63. ;
  64. zoomContainer1 = svgChart.append("g");
  65. zoomPanel = svgChart.append("rect")
  66. .attr("class", "zoom-panel")
  67. .attr("width", width)
  68. .attr("height", height)
  69. .call(zoom)
  70. ;
  71. zoomContainer2 = svgChart.append("g");
  72. bandsSvg = zoomContainer2.append("g");
  73. // tooltips for bands
  74. var maxTipHeight = 130;
  75. const tipDirection = d => y(d.band) - maxTipHeight < documentBodyScrollTop()? 's': 'n';
  76. tip = d3.tip()
  77. .attr("class", "d3-tip")
  78. .offset(function(d) {
  79. // compute x to return tip in chart region
  80. var t0 = (d.t1 + d.t2) / 2;
  81. var t1 = Math.min(Math.max(t0, xZoomed.invert(0)), xZoomed.invert(width));
  82. var dir = tipDirection(d);
  83. return [dir === 'n'? -10 : 10, xZoomed(t1) - xZoomed(t0)];
  84. })
  85. .direction(tipDirection)
  86. .html(function(d) {
  87. let text = '';
  88. for (let item of d.record) {
  89. text += probes[item[PROBEID]].provider + "." + probes[item[PROBEID]].name + "(";
  90. let first = true;
  91. for (let [param, value] of Object.entries(item[PARAMS])) {
  92. text += (first? "": ", ") + param + "='" + value + "'";
  93. first = false;
  94. }
  95. text += ")\n";
  96. }
  97. return "<pre>" + text + "</pre>";
  98. })
  99. ;
  100. bandsSvg.call(tip);
  101. render();
  102. // container for non-zoomable elements
  103. fixedContainer = svg.append("g")
  104. .attr("transform", "translate(" + margin.left + ", " + margin.top + ")")
  105. ;
  106. // create x axis
  107. fixedContainer.append("g")
  108. .attr("class", "x axis")
  109. .attr("transform", "translate(0, " + (height - margin.top - margin.bottom) + ")")
  110. .transition()
  111. .call(xAxis)
  112. ;
  113. // create y axis
  114. fixedContainer.append("g")
  115. .attr("class", "y axis")
  116. .transition()
  117. .call(yAxis)
  118. ;
  119. // make y axis ticks draggable
  120. var ytickdrag = d3.drag()
  121. .on("drag", function(d) {
  122. var ypos = d3.event.y - margin.top;
  123. var index = Math.floor((ypos / y.step()));
  124. index = Math.min(Math.max(index, 0), this.initDomain.length - 1);
  125. if (index != this.curIndex) {
  126. var newDomain = [];
  127. for (var i = 0; i < this.initDomain.length; ++i) {
  128. newDomain.push(this.initDomain[i]);
  129. }
  130. newDomain.splice(this.initIndex, 1);
  131. newDomain.splice(index, 0, this.initDomain[this.initIndex]);
  132. this.curIndex = index;
  133. this.curDomain = newDomain;
  134. y.domain(newDomain);
  135. // rearange y scale and axis
  136. svg.select("g.y.axis").transition().call(yAxis);
  137. // rearange other stuff
  138. render(-1, true);
  139. }
  140. })
  141. .on("start", function(d) {
  142. var ypos = d3.event.y - margin.top;
  143. this.initIndex = Math.floor((ypos / y.step()));
  144. this.initDomain = y.domain();
  145. })
  146. .on("end", function(d) {
  147. svg.select("g.y.axis").call(yAxis);
  148. })
  149. ;
  150. svg.selectAll("g.y.axis .tick")
  151. .call(ytickdrag)
  152. ;
  153. // right margin
  154. var rmargin = fixedContainer.append("g")
  155. .attr("id", "right-margin")
  156. .attr("transform", "translate(" + width + ", 0)")
  157. ;
  158. rmargin.append("rect")
  159. .attr("x", 0)
  160. .attr("y", 0)
  161. .attr("width", 1)
  162. .attr("height", height - margin.top - margin.bottom)
  163. ;
  164. // top margin
  165. var tmargin = fixedContainer.append("g")
  166. .attr("id", "top-margin")
  167. .attr("transform", "translate(0, 0)")
  168. ;
  169. tmargin.append("rect")
  170. .attr("x", 0)
  171. .attr("y", 0)
  172. .attr("width", width)
  173. .attr("height", 1)
  174. ;
  175. // ruler
  176. ruler = fixedContainer.append("g")
  177. .attr("id", "ruler")
  178. .attr("transform", "translate(0, 0)")
  179. ;
  180. ruler.append("rect")
  181. .attr("id", "ruler-line")
  182. .attr("x", 0)
  183. .attr("y", 0)
  184. .attr("width", "1")
  185. .attr("height", height - margin.top - margin.bottom + 8)
  186. ;
  187. ruler.append("rect")
  188. .attr("id", "bgrect")
  189. .attr("x", 0)
  190. .attr("y", 0)
  191. .attr("width", 0)
  192. .attr("height", 0)
  193. .style("fill", "white")
  194. ;
  195. ruler.append("text")
  196. .attr("x", 0)
  197. .attr("y", height - margin.top - margin.bottom + 16)
  198. .attr("dy", "0.71em")
  199. .text("0")
  200. ;
  201. svg.on('mousemove', function() {
  202. positionRuler(d3.event.pageX);
  203. });
  204. // scroll handling
  205. window.onscroll = function myFunction() {
  206. documentBodyScrollLeft(document.body.scrollLeft);
  207. documentBodyScrollTop(document.body.scrollTop);
  208. var scroll = scrollParams();
  209. svgChartContainer
  210. .attr("transform", "translate(" + margin.left
  211. + ", " + (margin.top + scroll.y1) + ")");
  212. svgChart
  213. .attr("viewBox", "0 " + scroll.y1 + " " + width + " " + scroll.h)
  214. .attr("height", scroll.h);
  215. tmargin
  216. .attr("transform", "translate(0," + scroll.y1 + ")");
  217. fixedContainer.select(".x.axis")
  218. .attr("transform", "translate(0," + scroll.y2 + ")");
  219. rmargin.select("rect")
  220. .attr("y", scroll.y1)
  221. .attr("height", scroll.h);
  222. ruler.select("#ruler-line")
  223. .attr("y", scroll.y1)
  224. .attr("height", scroll.h);
  225. positionRuler();
  226. }
  227. // render axis
  228. svg.select("g.x.axis").call(xAxis);
  229. svg.select("g.y.axis").call(yAxis);
  230. // update to initiale state
  231. window.onscroll(0);
  232. return gantt;
  233. }
  234. // private:
  235. var keyFunction = function(d) {
  236. return d.t1.toString() + d.t2.toString() + d.band.toString();
  237. }
  238. var bandTransform = function(d) {
  239. return "translate(" + x(d.t1) + "," + y(d.band) + ")";
  240. }
  241. var xPixel = function(d) {
  242. return xZoomed.invert(1) - xZoomed.invert(0);
  243. }
  244. var render = function(t0, smooth) {
  245. // Save/restore last t0 value
  246. if (!arguments.length || t0 == -1) {
  247. t0 = render.t0;
  248. }
  249. render.t0 = t0;
  250. smooth = smooth || false;
  251. // Create rectangles for bands
  252. bands = bandsSvg.selectAll("rect.bar")
  253. .data(data, keyFunction);
  254. bands.exit().remove();
  255. bands.enter().append("rect")
  256. .attr("class", "bar")
  257. .attr("vector-effect", "non-scaling-stroke")
  258. .style("fill", d => d.color)
  259. .on('click', function(d) {
  260. if (tipShown != d) {
  261. tipShown = d;
  262. tip.show(d);
  263. } else {
  264. tipShown = null;
  265. tip.hide(d);
  266. }
  267. })
  268. .merge(bands)
  269. .transition().duration(smooth? 250: 0)
  270. .attr("y", 0)
  271. .attr("transform", bandTransform)
  272. .attr("height", y.bandwidth())
  273. .attr("width", d => Math.max(1*xPixel(), x(d.t2) - x(d.t1)))
  274. ;
  275. var emptyMarker = bandsSvg.selectAll("text")
  276. .data(data.length == 0? ["no data to show"]: []);
  277. emptyMarker.exit().remove();
  278. emptyMarker.enter().append("text")
  279. .text(d => d)
  280. ;
  281. }
  282. function initAxis() {
  283. x = d3.scaleLinear()
  284. .domain([timeDomainStart, timeDomainEnd])
  285. .range([0, width])
  286. //.clamp(true); // dosn't work with zoom/pan
  287. xZoomed = x;
  288. y = d3.scaleBand()
  289. .domain(Object.values(data).map(d => d.band).sort())
  290. .rangeRound([0, height - margin.top - margin.bottom])
  291. .padding(0.5);
  292. xAxis = d3.axisBottom()
  293. .scale(x)
  294. //.tickSubdivide(true)
  295. .tickSize(8)
  296. .tickPadding(8);
  297. yAxis = d3.axisLeft()
  298. .scale(y)
  299. .tickSize(0);
  300. }
  301. // slow function wrapper
  302. var documentBodyScrollLeft = function(value) {
  303. if (!arguments.length) {
  304. if (documentBodyScrollLeft.value === undefined) {
  305. documentBodyScrollLeft.value = document.body.scrollLeft;
  306. }
  307. return documentBodyScrollLeft.value;
  308. } else {
  309. documentBodyScrollLeft.value = value;
  310. }
  311. }
  312. // slow function wrapper
  313. var documentBodyScrollTop = function(value) {
  314. if (!arguments.length) {
  315. if (!documentBodyScrollTop.value === undefined) {
  316. documentBodyScrollTop.value = document.body.scrollTop;
  317. }
  318. return documentBodyScrollTop.value;
  319. } else {
  320. documentBodyScrollTop.value = value;
  321. }
  322. }
  323. var scrollParams = function() {
  324. var y1 = documentBodyScrollTop();
  325. var y2 = y1 + window.innerHeight - margin.footer;
  326. y2 = Math.min(y2, height - margin.top - margin.bottom);
  327. var h = y2 - y1;
  328. return {
  329. y1: y1,
  330. y2: y2,
  331. h: h
  332. };
  333. }
  334. var posTextFormat = d3.format(".1f");
  335. var positionRuler = function(pageX) {
  336. if (!arguments.length) {
  337. pageX = positionRuler.pageX || 0;
  338. } else {
  339. positionRuler.pageX = pageX;
  340. }
  341. // x-coordinate
  342. if (!positionRuler.svgLeft) {
  343. positionRuler.svgLeft = svg.node().getBoundingClientRect().x;
  344. }
  345. var xpos = pageX - margin.left + 1 - positionRuler.svgLeft;
  346. var tpos = xZoomed.invert(xpos);
  347. tpos = Math.min(Math.max(tpos, xZoomed.invert(0)), xZoomed.invert(width));
  348. ruler.attr("transform", "translate(" + xZoomed(tpos) + ", 0)");
  349. var posText = posTextFormat(tpos);
  350. // scroll-related
  351. var scroll = scrollParams();
  352. var text = ruler.select("text")
  353. .attr("y", scroll.y2 + 16)
  354. ;
  355. // getBBox() is very slow, so compute symbol width once
  356. var xpadding = 5;
  357. var ypadding = 5;
  358. if (!positionRuler.bbox) {
  359. positionRuler.bbox = text.node().getBBox();
  360. }
  361. text.text(posText);
  362. var textWidth = 10 * posText.length;
  363. ruler.select("#bgrect")
  364. .attr("x", -textWidth/2 - xpadding)
  365. .attr("y", positionRuler.bbox.y - ypadding)
  366. .attr("width", textWidth + (xpadding*2))
  367. .attr("height", positionRuler.bbox.height + (ypadding*2))
  368. ;
  369. render(tpos);
  370. }
  371. /*
  372. * Log Query Language:
  373. * Data expressions:
  374. * 1) myparam[0], myparam[-1] // the first and the last myparam in any probe/provider
  375. * 2) myparam // the first (the same as [0])
  376. * 3) PROVIDER..myparam // any probe with myparam in PROVIDER
  377. * 4) MyProbe._elapsedMs // Ms since track begin for the first MyProbe event
  378. * 5) PROVIDER.MyProbe._sliceUs // Us since previous event in track for the first PROVIDER.MyProbe event
  379. */
  380. function compile(query) {
  381. query = query.replace(/\s/g, "");
  382. let [compiled, rest] = sum(query);
  383. if (rest.length != 0) {
  384. throw "parse error: unexpected expression starting at: '" + query + "'";
  385. }
  386. return record => {
  387. try {
  388. return compiled(record);
  389. } catch (e) {
  390. return null;
  391. }
  392. };
  393. function sum(query) {
  394. let term0;
  395. if (query[0] == '-') {
  396. let [term, rest] = product(query.substr(1));
  397. query = rest;
  398. term0 = x => -term(x);
  399. } else {
  400. let [term, rest] = product(query);
  401. query = rest;
  402. term0 = term;
  403. }
  404. let terms = [];
  405. while (query.length > 0) {
  406. let negate;
  407. if (query[0] == '+') {
  408. negate = false;
  409. } else if (query[0] == '-') {
  410. negate = true;
  411. } else {
  412. break;
  413. }
  414. let [term, rest] = product(query.substr(1));
  415. query = rest;
  416. terms.push(negate? x => -term(x): term);
  417. }
  418. const cast = x => (isNaN(+x)? x: +x);
  419. return [terms.reduce((a, f) => x => cast(a(x)) + cast(f(x)), term0), query];
  420. }
  421. function product(query) {
  422. let [factor0, rest] = parentheses(query);
  423. query = rest;
  424. let factors = [];
  425. while (query.length > 0) {
  426. let invert;
  427. if (query[0] == '*') {
  428. invert = false;
  429. } else if (query[0] == '/') {
  430. invert = true;
  431. } else {
  432. break;
  433. }
  434. let [factor, rest] = parentheses(query.substr(1));
  435. query = rest;
  436. factors.push(invert? x => 1 / factor(x): factor);
  437. }
  438. return [factors.reduce((a, f) => x => a(x) * f(x), factor0), query];
  439. }
  440. function parentheses(query) {
  441. if (query[0] == "(") {
  442. let [expr, rest] = sum(query.substr(1));
  443. if (rest[0] != ")") {
  444. throw "parse error: missing ')' before '" + rest + "'";
  445. }
  446. return [expr, rest.substr(1)];
  447. } else {
  448. return atom(query);
  449. }
  450. }
  451. function atom(query) {
  452. specialParam = {
  453. _thrNTime: item => (item[US] - thrNTimeZero) * 1e-6,
  454. _thrRTime: item => (item[US] - thrRTimeZero) * 1e-6,
  455. _thrTime: item => item[US] * 1e-6,
  456. _thrNTimeMs: item => (item[US] - thrNTimeZero) * 1e-3,
  457. _thrRTimeMs: item => (item[US] - thrRTimeZero) * 1e-3,
  458. _thrTimeMs: item => item[US] * 1e-3,
  459. _thrNTimeUs: item => item[US] - thrNTimeZero,
  460. _thrRTimeUs: item => item[US] - thrRTimeZero,
  461. _thrTimeUs: item => item[US],
  462. _thrNTimeNs: item => (item[US] - thrNTimeZero) * 1e+3,
  463. _thrRTimeNs: item => (item[US] - thrRTimeZero) * 1e+3,
  464. _thrTimeNs: item => item[US] * 1e+3,
  465. _thread: item => item[THREAD],
  466. };
  467. var match;
  468. if (match = query.match(/^\d+(\.\d+)?/)) { // number literals
  469. let literal = match[0];
  470. return [record => literal, query.substr(match[0].length)];
  471. } else if (match = query.match(/^#[0-9a-fA-F]+/)) { // color literals
  472. let literal = match[0];
  473. return [record => literal, query.substr(match[0].length)];
  474. } else if (match = query.match(/^'(.*)'/)) { // string literal
  475. let literal = match[1].replace(/\\'/, "'").replace(/\\\\/, "\\");
  476. return [record => literal, query.substr(match[0].length)];
  477. } else if (match = query.match(/^(?:(?:(\w+)\.)?(\w*)\.)?(\w+)(?:\[(-?\d+)\])?/)) {
  478. let provider = match[1] || "";
  479. let probe = match[2] || "";
  480. let param = match[3];
  481. let index = +(match[4] || 0);
  482. let probeId = new Set();
  483. for (let id = 0; id < probes.length; id++) {
  484. if ((!probe || probes[id].name == probe) &&
  485. (!provider || probes[id].provider == provider))
  486. {
  487. probeId.add(id);
  488. }
  489. }
  490. let isSpecial = specialParam.hasOwnProperty(param);
  491. return [record => {
  492. let end = index >= 0? record.length: -1;
  493. let step = index >= 0? 1: -1;
  494. let skip = index >= 0? index: -index - 1;
  495. for (let i = index >= 0? 0: record.length - 1; i != end; i += step) {
  496. let item = record[i];
  497. if (probeId.has(item[PROBEID]) && (isSpecial || item[PARAMS].hasOwnProperty(param))) {
  498. if (skip == 0) {
  499. return isSpecial? specialParam[param](item): item[PARAMS][param];
  500. } else {
  501. skip--;
  502. }
  503. }
  504. }
  505. throw "no data";
  506. }, query.substr(match[0].length)];
  507. } else {
  508. throw "parse error: invalid expression starting at '" + query + "'";
  509. }
  510. }
  511. }
  512. // ex1: "linear().domain([-1, 0, 1]).range(['red', 'white', 'green'])",
  513. // ex2: "ordinal().domain(['a1', 'a2']).range(['blue', 'yellow'])"
  514. function parseScale(query) {
  515. query = query.replaceAll("'","\"");
  516. var match, scale;
  517. if (match = query.match(/^linear\(\)/)) {
  518. scale = d3.scaleLinear();
  519. } else if (match = query.match(/^pow\(\)/)) {
  520. scale = d3.scalePow();
  521. } else if (match = query.match(/^log\(\)/)) {
  522. scale = d3.scaleLog();
  523. } else if (match = query.match(/^identity\(\)/)) {
  524. scale = d3.scaleIdentity();
  525. } else if (match = query.match(/^time\(\)/)) {
  526. scale = d3.scaleTime();
  527. } else if (match = query.match(/^threshold\(\)/)) {
  528. scale = d3.scaleThreshold();
  529. } else if (match = query.match(/^ordinal\(\)/)) {
  530. scale = d3.scaleOrdinal();
  531. } else {
  532. throw "Unable to parse scale: " + query;
  533. }
  534. if (match = query.match(/\.domain\(([^\)]+)\)/)) {
  535. scale.domain(JSON.parse(match[1]));
  536. }
  537. if (match = query.match(/\.range\(([^\)]+)\)/)) {
  538. scale.range(JSON.parse(match[1]));
  539. }
  540. if (match = query.match(/\.unknown\(([^\)]+)\)/)) {
  541. scale.unknown(JSON.parse(match[1]));
  542. }
  543. return scale;
  544. }
  545. function parseLogs(config, logs) {
  546. data = [];
  547. probes = logs.probes;
  548. if (config.hasOwnProperty('scales'))
  549. for (let [name, text] of Object.entries(config.scales)) {
  550. scales[name] = parseScale(text);
  551. }
  552. // Compute aggregates
  553. let minUs = new Map();
  554. for (record of logs.depot) {
  555. if (record.length > 0) {
  556. let item = record[0];
  557. let us = item[US];
  558. let thread = item[US];
  559. if (!minUs.has(thread)) {
  560. minUs.set(thread, us);
  561. } else {
  562. minUs.set(thread, Math.min(us, minUs.get(thread)));
  563. }
  564. }
  565. }
  566. thrNTimeZero = Number.MAX_VALUE;
  567. thrRTimeZero = 0;
  568. for (let [thread, us] of minUs) {
  569. thrNTimeZero = Math.min(thrNTimeZero, us);
  570. thrRTimeZero = Math.max(thrRTimeZero, us);
  571. }
  572. // Comple data for bands
  573. for (let bandCfg of config.bands) {
  574. function applyScale(func, scaleName) {
  575. if (scaleName) {
  576. let scale = scales[scaleName];
  577. return (record) => {
  578. let value = func(record);
  579. if (value != null) {
  580. value = scale(value);
  581. }
  582. return value;
  583. };
  584. } else {
  585. return func;
  586. }
  587. }
  588. let t1f = applyScale(compile(bandCfg.t1), bandCfg.t1Scale);
  589. let t2f = applyScale(compile(bandCfg.t2), bandCfg.t2Scale);
  590. let bandf = applyScale(compile(bandCfg.band), bandCfg.bandScale);
  591. let colorf = applyScale(compile(bandCfg.color), bandCfg.colorScale);
  592. let minTime = Number.MAX_VALUE;
  593. let maxTime = -Number.MAX_VALUE;
  594. for (record of logs.depot) {
  595. let t1 = t1f(record),
  596. t2 = t2f(record),
  597. band = bandf(record),
  598. color = colorf(record)
  599. ;
  600. if (t1 != null && t2 != null && band != null && color != null) {
  601. data.push({t1, t2, band, color, record});
  602. minTime = Math.min(minTime, t1);
  603. maxTime = Math.max(maxTime, t2);
  604. }
  605. }
  606. if (minTime != Number.MAX_VALUE) {
  607. minT = minTime;
  608. maxT = maxTime;
  609. }
  610. }
  611. }
  612. // public:
  613. gantt.width = function(value) {
  614. if (!arguments.length)
  615. return width;
  616. width = +value;
  617. return gantt;
  618. }
  619. gantt.height = function(value) {
  620. if (!arguments.length)
  621. return height;
  622. height = +value;
  623. return gantt;
  624. }
  625. gantt.selector = function(value) {
  626. if (!arguments.length)
  627. return selector;
  628. selector = value;
  629. return gantt;
  630. }
  631. gantt.timeDomain = function(value) {
  632. if (!arguments.length)
  633. return [timeDomainStart, timeDomainEnd];
  634. timeDomainStart = value[0];
  635. timeDomainEnd = value[1];
  636. return gantt;
  637. }
  638. gantt.data = function() {
  639. return data;
  640. }
  641. // constructor
  642. // Log Format
  643. const
  644. THREAD = 0,
  645. US = 1,
  646. PROBEID = 2,
  647. PARAMS = 3
  648. ;
  649. // Config
  650. var margin = { top: 20, right: 40, bottom: 20, left: 100, footer: 100 },
  651. height = document.body.clientHeight - margin.top - margin.bottom - 5,
  652. width = document.body.clientWidth - margin.right - margin.left - 5,
  653. selector = 'body',
  654. timeDomainStart = 0,
  655. timeDomainEnd = 1000,
  656. scales = {};
  657. ;
  658. // View
  659. var x = null,
  660. xZoomed = null,
  661. y = null,
  662. xAxis = null,
  663. yAxis = null,
  664. svg = null,
  665. defs = null,
  666. svgChartContainer = null,
  667. svgChart = null,
  668. zoomPanel = null,
  669. zoomContainer1 = null,
  670. zoomContainer2 = null,
  671. fixedContainer = null,
  672. zoom = null,
  673. bandsSvg = null,
  674. bands = null,
  675. tip = null,
  676. tipShown = null,
  677. ruler = null
  678. ;
  679. // Model
  680. var data = null,
  681. probes = null,
  682. thrRTimeZero = 0,
  683. thrNTimeZero = 0,
  684. minT,
  685. maxT
  686. ;
  687. return gantt;
  688. }