1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 * This is a view class showing flot graph.
7 * @param {Object} profiler Must have addListener method.
10 var GraphView = function(profiler
) {
11 this.profiler_
= profiler
;
12 this.placeholder_
= '#graph-div';
13 // Update graph view and menu view when profiler model changed.
14 profiler
.addListener('changed', this.redraw_
.bind(this));
18 * Generate lines for flot plotting.
19 * @param {Array.<Object>} models
20 * @return {Array.<Object>}
23 GraphView
.prototype.generateLines_ = function(models
) {
24 function mergeCategoryTree(snapNode
, treeNode
) {
25 if ('children' in snapNode
) {
26 // If |snapNode| is not a leaf node, we should go deeper.
27 if (!('children' in treeNode
))
28 treeNode
.children
= {};
29 snapNode
.children
.forEach(function(child
) {
30 if (!(child
.id
in treeNode
.children
))
31 treeNode
.children
[child
.id
] = {};
32 mergeCategoryTree(child
, treeNode
.children
[child
.id
]);
35 treeNode
.name
= snapNode
.name
;
39 function getCategoriesMap(node
, id
, categories
) {
40 if ('children' in node
) {
41 Object
.keys(node
.children
).forEach(function(id
) {
42 getCategoriesMap(node
.children
[id
], id
, categories
);
45 if (!(id
in categories
)) {
50 for (var i
= 0; i
< models
.length
; ++i
)
51 categories
[id
].data
.push([models
[i
].time
- models
[0].time
, 0]);
56 function getLineValues(snapNode
, index
, categories
) {
57 if ('children' in snapNode
) {
58 snapNode
.children
.forEach(function(child
) {
59 getLineValues(child
, index
, categories
);
62 categories
[snapNode
.id
].data
[index
][1] = snapNode
.size
;
66 // TODO(dmikurube): Remove this function after adding "color" attribute
68 function getHashColorCode(id
) {
70 for (var i
= 0; i
< id
.length
; ++i
)
71 color
= (color
* 0x57 + id
.charCodeAt(i
)) & 0xffffff;
72 color
= color
.toString(16);
73 while (color
.length
< 6)
78 var categoryTree
= {};
79 models
.forEach(function(model
) {
80 mergeCategoryTree(model
, categoryTree
);
82 // Convert layout of categories from tree style to hash map style.
84 getCategoriesMap(categoryTree
, '', categoryMap
);
85 // Get size of each category.
86 models
.forEach(function(model
, index
) {
87 getLineValues(model
, index
, categoryMap
);
90 return Object
.keys(categoryMap
).map(function(id
) {
92 color
: getHashColorCode(id
),
93 data
: categoryMap
[id
].data
,
95 label
: categoryMap
[id
].name
101 * Update graph view when model updated.
102 * TODO(junjianx): use redraw function to improve perfomance.
103 * @param {Array.<Object>} models
106 GraphView
.prototype.redraw_ = function(models
) {
108 var data
= this.generateLines_(models
);
110 var $graph
= $(this.placeholder_
);
111 this.graph_
= $.plot($graph
, data
, {
114 lines
: { show
: true, fill
: true }
122 // Bind click event so that user can select category by clicking stack
123 // area. It firstly checks x range which clicked point is in, and all lines
124 // share same x values, so it is checked only once at first. Secondly, it
125 // checked y range by accumulated y values because this is a stack graph.
126 $graph
.bind('plotclick', function(event
, pos
, item
) {
127 // Get newest lines data from graph.
128 var lines
= self
.graph_
.getData();
129 // If only <=1 line exists or axis area clicked, return.
130 var right
= binarySearch
.call(lines
[0].data
.map(function(point
) {
133 if (lines
.length
<= 1 || right
=== lines
.length
|| right
=== 0)
136 // Calculate interpolate y value of every line.
137 for (var i
= 0; i
< lines
.length
; ++i
) {
138 var line
= lines
[i
].data
;
139 // [left, right] is the range including clicked point.
140 var left
= right
- 1;
143 y
: (leftPoint
? leftPoint
.y
: 0) + line
[left
][1]
147 y
: (rightPoint
? rightPoint
.y
: 0) + line
[right
][1]
150 // Calculate slope of the linear equation.
151 var slope
= (rightPoint
.y
- leftPoint
.y
) / (rightPoint
.x
- leftPoint
.x
);
152 var interpolateY
= slope
* (pos
.x
- rightPoint
.x
) + rightPoint
.y
;
153 if (interpolateY
>= pos
.y
)
157 // If pos.y is higher than all lines, return.
158 if (i
=== lines
.length
) {
159 self
.profiler_
.setSelected(null);
163 self
.profiler_
.setSelected(lines
[i
].id
, pos
);
166 this.graph_
.setData(data
);
167 this.graph_
.setupGrid();