OLD | NEW |
| (Empty) |
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. | |
4 | |
5 /** | |
6 * This is a view class showing flot graph. | |
7 * @param {Object} profiler Must have addListener method. | |
8 * @construct | |
9 */ | |
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)); | |
15 }; | |
16 | |
17 /** | |
18 * Generate lines for flot plotting. | |
19 * @param {Array.<Object>} models | |
20 * @return {Array.<Object>} | |
21 * @private | |
22 */ | |
23 GraphView.prototype.generateLines_ = function(models) { | |
24 function getLeaves(node, categories) { | |
25 if ('children' in node) { | |
26 node.children.forEach(function(child) { | |
27 getLeaves(child, categories); | |
28 }); | |
29 } else { | |
30 categories.push(node); | |
31 } | |
32 } | |
33 | |
34 var lines = {}; | |
35 var categoryMap = {}; | |
36 var snapshotNum = models.length; | |
37 // Initialize lines with all zero. | |
38 models.forEach(function(model) { | |
39 var categories = []; | |
40 getLeaves(model, categories); | |
41 categories.forEach(function(category) { | |
42 var id = category.id; | |
43 if (lines[id]) | |
44 return; | |
45 lines[id] = []; | |
46 for (var i = 0; i < snapshotNum; ++i) | |
47 lines[id].push([i, 0]); | |
48 categoryMap[id] = category; | |
49 }); | |
50 }); | |
51 | |
52 // Assignment lines with values of models. | |
53 models.forEach(function(model, index) { | |
54 var categories = []; | |
55 getLeaves(model, categories); | |
56 categories.forEach(function(category) { | |
57 var id = category.id; | |
58 var size = category.size; | |
59 lines[id][index] = [index, size]; | |
60 }); | |
61 }); | |
62 | |
63 return Object.keys(lines).map(function(id) { | |
64 var name = categoryMap[id].name; | |
65 return { | |
66 id: id, | |
67 label: name, | |
68 data: lines[id] | |
69 }; | |
70 }); | |
71 }; | |
72 | |
73 /** | |
74 * Update graph view when model updated. | |
75 * TODO(junjianx): use redraw function to improve perfomance. | |
76 * @param {Array.<Object>} models | |
77 * @private | |
78 */ | |
79 GraphView.prototype.redraw_ = function(models) { | |
80 var self = this; | |
81 var data = this.generateLines_(models); | |
82 if (!this.graph_) { | |
83 var $graph = $(this.placeholder_); | |
84 this.graph_ = $.plot($graph, data, { | |
85 series: { | |
86 stack: true, | |
87 lines: { show: true, fill: true } | |
88 }, | |
89 grid: { | |
90 hoverable: true, | |
91 clickable: true | |
92 } | |
93 }); | |
94 | |
95 // Bind click event so that user can select category by clicking stack | |
96 // area. It firstly checks x range which clicked point is in, and all lines | |
97 // share same x values, so it is checked only once at first. Secondly, it | |
98 // checked y range by accumulated y values because this is a stack graph. | |
99 $graph.bind('plotclick', function(event, pos, item) { | |
100 // Get newest lines data from graph. | |
101 var lines = self.graph_.getData(); | |
102 // If only <=1 line exists or axis area clicked, return. | |
103 var right = binarySearch.call(lines[0].data.map(function(point) { | |
104 return point[0]; | |
105 }), pos.x); | |
106 if (lines.length <= 1 || right === lines.length || right === 0) | |
107 return; | |
108 | |
109 // Calculate interpolate y value of every line. | |
110 for (var i = 0; i < lines.length; ++i) { | |
111 var line = lines[i].data; | |
112 // [left, right] is the range including clicked point. | |
113 var left = right - 1; | |
114 var leftPoint = { | |
115 x: line[left][0], | |
116 y: (leftPoint ? leftPoint.y : 0) + line[left][1] | |
117 }; | |
118 var rightPoint = { | |
119 x: line[right][0], | |
120 y: (rightPoint ? rightPoint.y : 0) + line[right][1] | |
121 }; | |
122 | |
123 // Calculate slope of the linear equation. | |
124 var slope = (rightPoint.y - leftPoint.y) / (rightPoint.x - leftPoint.x); | |
125 var interpolateY = slope * (pos.x - rightPoint.x) + rightPoint.y; | |
126 if (interpolateY >= pos.y) | |
127 break; | |
128 } | |
129 | |
130 // If pos.y is higher than all lines, return. | |
131 if (i === lines.length) | |
132 return; | |
133 | |
134 self.profiler_.setSelected(lines[i].id); | |
135 }); | |
136 } else { | |
137 this.graph_.setData(data); | |
138 this.graph_.setupGrid(); | |
139 this.graph_.draw(); | |
140 } | |
141 }; | |
OLD | NEW |