timeline.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /**
  2. * @file
  3. * Timeline panel app.
  4. */
  5. (function ($, Drupal, drupalSettings, d3) {
  6. 'use strict';
  7. Drupal.behaviors.webprofiler_timeline = {
  8. attach: function (context) {
  9. if (typeof d3 != 'undefined') {
  10. // data
  11. var data = drupalSettings.webprofiler.time.events;
  12. var parts = [];
  13. var dataL = data.length;
  14. var perL;
  15. var labelW = [];
  16. var rowW;
  17. var scalePadding;
  18. var endTime = parseInt(data[(dataL - 1)].endtime);
  19. var roundTime = Math.ceil(endTime / 1000) * 1000;
  20. var endScale;
  21. for (var j = 0; j < dataL; j++) {
  22. perL = data[j].periods.length;
  23. for (var k = 0; k < perL; k++) {
  24. parts.push({
  25. lane: j,
  26. category: data[j].category,
  27. memory: data[j].memory,
  28. name: data[j].name,
  29. start: data[j].periods[k].start,
  30. end: data[j].periods[k].end
  31. });
  32. }
  33. }
  34. var tooltipCtrl = function (d, i) {
  35. tooltip.html('<span class="tooltip__content">memory usage: ' + d.memory + '</span>' +
  36. '<span class="tooltip__content">' + parseInt(d.start) + 'ms ~ ' + parseInt(d.end) + 'ms</span>');
  37. tooltip
  38. .style('display', 'block')
  39. .style('left', (d3.event.layerX - 87) + 'px')
  40. .style('top', ((d.lane + 1) * 22) + 'px')
  41. .style('opacity', .9);
  42. };
  43. var xscale = d3.scale.linear().domain([0, roundTime]).range([0, 1000]);
  44. d3.select('#timeline').append('svg').attr('height', (dataL + 1) * 22 + 'px').attr('width', '100%').attr('class', 'timeline__canvas');
  45. // tooltips
  46. var tooltip = d3.select('#timeline')
  47. .append('div')
  48. .attr('class', 'tooltip');
  49. // Add a rectangle for every data element.
  50. d3.select('.timeline__canvas')
  51. .append('g')
  52. .attr('class', 'timeline__rows')
  53. .attr('x', 0)
  54. .attr('y', 0)
  55. .selectAll('g')
  56. .data(data)
  57. .enter()
  58. .append('rect')
  59. .attr('class', 'timeline__row')
  60. .attr('x', 0)
  61. .attr('y', function (d, i) {
  62. return (i * 22);
  63. })
  64. .attr('height', 22)
  65. .attr('width', '100%')
  66. .each(function () {
  67. rowW = this.getBoundingClientRect().width;
  68. });
  69. // scale
  70. var scale = d3.select('.timeline__canvas')
  71. .append('g')
  72. .attr('class', 'timeline__scale')
  73. .attr('id', 'timeline__scale')
  74. .attr('x', 0)
  75. .attr('y', 0)
  76. .selectAll('g')
  77. .data(data)
  78. .enter()
  79. .append('a')
  80. .attr('xlink:href', function (d) {
  81. return Drupal.webprofiler.helpers.ideLink(d.link);
  82. })
  83. .attr('class', function (d) {
  84. return 'timeline__label ' + d.category;
  85. })
  86. .attr('x', xscale(5))
  87. .attr('y', function (d, i) {
  88. return (((i + 1) * 22) - 5);
  89. });
  90. scale.append('title')
  91. .text(function (d) {
  92. return d.name;
  93. });
  94. scale.append('text')
  95. .attr('x', xscale(5))
  96. .attr('y', function (d, i) {
  97. return (((i + 1) * 22) - 5);
  98. })
  99. .text(function (d) {
  100. return Drupal.webprofiler.helpers.shortLink(d.name);
  101. })
  102. .each(function (d) {
  103. labelW.push(this.getBoundingClientRect().width);
  104. });
  105. scalePadding = Math.max.apply(null, labelW) + 10;
  106. scale.insert('rect', 'title')
  107. .attr('x', 0)
  108. .attr('y', function (d, i) {
  109. return (i * 22);
  110. })
  111. .attr('height', 22)
  112. .attr('stroke', 'transparent')
  113. .attr('strokw-width', 1)
  114. .attr('width', scalePadding);
  115. // times
  116. var events = d3.select('.timeline__canvas')
  117. .insert('g', '.timeline__scale')
  118. .attr('class', 'timeline__parts')
  119. .attr('x', 0)
  120. .attr('y', 0)
  121. .selectAll('g')
  122. .data(parts)
  123. .enter();
  124. events.append('rect').attr('class', function (d) {
  125. return 'timeline__period--' + d.category;
  126. })
  127. .attr('x', function (d) {
  128. return xscale(parseInt(d.start)) + scalePadding;
  129. })
  130. .attr('y', function (d) {
  131. return d.lane * 22;
  132. })
  133. .attr('height', 22)
  134. .attr('width', function (d) {
  135. return xscale(Math.max(parseInt(d.end - d.start), 1));
  136. });
  137. events.append('rect')
  138. .attr('class', function (d) {
  139. return 'timeline__period-trigger';
  140. })
  141. .attr('x', function (d) {
  142. return xscale(parseInt(d.start)) + scalePadding - 5;
  143. })
  144. .attr('y', function (d) {
  145. return d.lane * 22;
  146. })
  147. .attr('height', 22)
  148. .attr('width', function (d) {
  149. return xscale(Math.max(parseInt(d.end - d.start), 1)) + 11;
  150. })
  151. .on('mouseover', function (d, i) {
  152. tooltipCtrl(d, i);
  153. })
  154. .on('mouseout', function (d) {
  155. tooltip
  156. .style('display', 'none');
  157. });
  158. // Draw X-axis grid lines
  159. d3.select('.timeline__parts').insert('g', '.timeline__parts')
  160. .selectAll('line')
  161. .data(xscale.ticks(10))
  162. .enter()
  163. .append('line')
  164. .attr('class', 'timeline__scale--x')
  165. .attr('x1', xscale)
  166. .attr('x2', xscale)
  167. .attr('y1', 0)
  168. .attr('y2', data.length * 22)
  169. .attr('transform', 'translate( ' + scalePadding + ' , 0)');
  170. var xAxis = d3.svg.axis().scale(xscale).ticks(10).orient('bottom').tickFormat(function (d) {
  171. return d + ' ms';
  172. });
  173. d3.select('.timeline__parts').insert('g', '.timeline__parts')
  174. .attr('class', 'axis')
  175. .attr('transform', 'translate(' + scalePadding + ', ' + dataL * 22 + ')')
  176. .call(xAxis);
  177. endScale = xscale(endTime) - rowW - parseInt(scalePadding);
  178. if (parseInt(xscale(endTime)) > (rowW - parseInt(scalePadding))) {
  179. d3.select('.timeline__canvas')
  180. .call(
  181. d3.behavior.zoom()
  182. .scaleExtent([1, 1])
  183. .x(xscale)
  184. .on('zoom', function () {
  185. var t = d3.event.translate;
  186. var tx = t[0];
  187. tx = tx > 0 ? 0 : tx;
  188. tx = tx < endScale ? endScale : tx;
  189. d3.select('.timeline__parts').attr('transform', 'translate( ' + tx + ' , 0)');
  190. }));
  191. }
  192. }
  193. }
  194. };
  195. })(jQuery, Drupal, drupalSettings, d3);