| 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 |