OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * Plot a line graph of data versus time on a HTML canvas element. | 6 * Plot a line graph of data versus time on a HTML canvas element. |
7 * | 7 * |
8 * @param {HTMLCanvasElement} canvas The canvas on which the line graph is | 8 * @param {HTMLCanvasElement} plotCanvas The canvas on which the line graph is |
9 * drawn. | 9 * drawn. |
| 10 * @param {HTMLCanvasElement} legendCanvas The canvas on which the legend for |
| 11 * the line graph is drawn. |
10 * @param {Array.<number>} tData The time (in seconds) in the past when the | 12 * @param {Array.<number>} tData The time (in seconds) in the past when the |
11 * corresponding data in plots was sampled. | 13 * corresponding data in plots was sampled. |
12 * @param {Array.<{data: Array.<number>, color: string}>} plots An | 14 * @param {Array.<{data: Array.<number>, color: string}>} plots An |
13 * array of plots to plot on the canvas. The field 'data' of a plot is an | 15 * array of plots to plot on the canvas. The field 'data' of a plot is an |
14 * array of samples to be plotted as a line graph with color speficied by | 16 * array of samples to be plotted as a line graph with color speficied by |
15 * the field 'color'. The elements in the 'data' array are ordered | 17 * the field 'color'. The elements in the 'data' array are ordered |
16 * corresponding to their sampling time in the argument 'tData'. Also, the | 18 * corresponding to their sampling time in the argument 'tData'. Also, the |
17 * number of elements in the 'data' array should be the same as in the time | 19 * number of elements in the 'data' array should be the same as in the time |
18 * array 'tData' above. | 20 * array 'tData' above. |
19 * @param {number} yMin Minimum bound of y-axis | 21 * @param {number} yMin Minimum bound of y-axis |
20 * @param {number} yMax Maximum bound of y-axis. | 22 * @param {number} yMax Maximum bound of y-axis. |
21 * @param {integer} yPrecision An integer value representing the number of | 23 * @param {integer} yPrecision An integer value representing the number of |
22 * digits of precision the y-axis data should be printed with. | 24 * digits of precision the y-axis data should be printed with. |
23 */ | 25 */ |
24 function plotLineGraph(canvas, tData, plots, yMin, yMax, yPrecision) { | 26 function plotLineGraph( |
| 27 plotCanvas, legendCanvas, tData, plots, yMin, yMax, yPrecision) { |
25 var textFont = '12px Arial'; | 28 var textFont = '12px Arial'; |
26 var textHeight = 12; | 29 var textHeight = 12; |
27 var padding = 5; // Pixels | 30 var padding = 5; // Pixels |
28 var errorOffsetPixels = 15; | 31 var errorOffsetPixels = 15; |
29 var gridColor = '#ccc'; | 32 var gridColor = '#ccc'; |
30 var ctx = canvas.getContext('2d'); | 33 var plotCtx = plotCanvas.getContext('2d'); |
31 var size = tData.length; | 34 var size = tData.length; |
32 | 35 |
33 function drawText(text, x, y) { | 36 function drawText(ctx, text, x, y) { |
34 ctx.font = textFont; | 37 ctx.font = textFont; |
35 ctx.fillStyle = '#000'; | 38 ctx.fillStyle = '#000'; |
36 ctx.fillText(text, x, y); | 39 ctx.fillText(text, x, y); |
37 } | 40 } |
38 | 41 |
39 function printErrorText(text) { | 42 function printErrorText(ctx, text) { |
40 ctx.clearRect(0, 0, canvas.width, canvas.height); | 43 ctx.clearRect(0, 0, plotCanvas.width, plotCanvas.height); |
41 drawText(text, errorOffsetPixels, errorOffsetPixels); | 44 drawText(ctx, text, errorOffsetPixels, errorOffsetPixels); |
42 } | 45 } |
43 | 46 |
44 if (size < 2) { | 47 if (size < 2) { |
45 printErrorText(loadTimeData.getString('notEnoughDataAvailableYet')); | 48 printErrorText(plotCtx, |
| 49 loadTimeData.getString('notEnoughDataAvailableYet')); |
46 return; | 50 return; |
47 } | 51 } |
48 | 52 |
49 for (var count = 0; count < plots.length; count++) { | 53 for (var count = 0; count < plots.length; count++) { |
50 if (plots[count].data.length != size) { | 54 if (plots[count].data.length != size) { |
51 throw new Error('Mismatch in time and plot data.'); | 55 throw new Error('Mismatch in time and plot data.'); |
52 } | 56 } |
53 } | 57 } |
54 | 58 |
55 function valueToString(value) { | 59 function valueToString(value) { |
56 return Number(value).toPrecision(yPrecision); | 60 if (Math.abs(value) < 1) { |
| 61 return Number(value).toFixed(yPrecision - 1); |
| 62 } else { |
| 63 return Number(value).toPrecision(yPrecision); |
| 64 } |
57 } | 65 } |
58 | 66 |
59 function getTextWidth(text) { | 67 function getTextWidth(ctx, text) { |
60 ctx.font = textFont; | 68 ctx.font = textFont; |
61 // For now, all text is drawn to the left of vertical lines, or centered. | 69 // For now, all text is drawn to the left of vertical lines, or centered. |
62 // Add a 2 pixel padding so that there is some spacing between the text | 70 // Add a 2 pixel padding so that there is some spacing between the text |
63 // and the vertical line. | 71 // and the vertical line. |
64 return Math.round(ctx.measureText(text).width) + 2; | 72 return Math.round(ctx.measureText(text).width) + 2; |
65 } | 73 } |
66 | 74 |
67 function drawHighlightText(text, x, y, color) { | 75 function drawHighlightText(ctx, text, x, y, color) { |
68 ctx.strokeStyle = '#000'; | 76 ctx.strokeStyle = '#000'; |
69 ctx.strokeRect(x, y - textHeight, getTextWidth(text), textHeight); | 77 ctx.strokeRect(x, y - textHeight, getTextWidth(ctx, text), textHeight); |
70 ctx.fillStyle = color; | 78 ctx.fillStyle = color; |
71 ctx.fillRect(x, y - textHeight, getTextWidth(text), textHeight); | 79 ctx.fillRect(x, y - textHeight, getTextWidth(ctx, text), textHeight); |
72 ctx.fillStyle = '#fff'; | 80 ctx.fillStyle = '#fff'; |
73 ctx.fillText(text, x, y); | 81 ctx.fillText(text, x, y); |
74 } | 82 } |
75 | 83 |
76 function drawLine(x1, y1, x2, y2, color) { | 84 function drawLine(ctx, x1, y1, x2, y2, color) { |
77 ctx.save(); | 85 ctx.save(); |
78 ctx.beginPath(); | 86 ctx.beginPath(); |
79 ctx.moveTo(x1, y1); | 87 ctx.moveTo(x1, y1); |
80 ctx.lineTo(x2, y2); | 88 ctx.lineTo(x2, y2); |
81 ctx.strokeStyle = color; | 89 ctx.strokeStyle = color; |
82 ctx.stroke(); | 90 ctx.stroke(); |
83 ctx.restore(); | 91 ctx.restore(); |
84 } | 92 } |
85 | 93 |
86 // The strokeRect method of the 2d context of a canvas draws a bounding | 94 // The strokeRect method of the 2d context of a plotCanvas draws a bounding |
87 // rectangle with an offset origin and greater dimensions. Hence, use this | 95 // rectangle with an offset origin and greater dimensions. Hence, use this |
88 // function to draw a rect at the desired location with desired dimensions. | 96 // function to draw a rect at the desired location with desired dimensions. |
89 function drawRect(x, y, width, height, color) { | 97 function drawRect(ctx, x, y, width, height, color) { |
90 drawLine(x, y, x + width - 1, y, color); | 98 drawLine(ctx, x, y, x + width - 1, y, color); |
91 drawLine(x, y, x, y + height - 1, color); | 99 drawLine(ctx, x, y, x, y + height - 1, color); |
92 drawLine(x, y + height - 1, x + width - 1, y + height - 1, color); | 100 drawLine(ctx, x, y + height - 1, x + width - 1, y + height - 1, color); |
93 drawLine(x + width - 1, y, x + width - 1, y + height - 1, color); | 101 drawLine(ctx, x + width - 1, y, x + width - 1, y + height - 1, color); |
| 102 } |
| 103 |
| 104 function drawLegend() { |
| 105 // Show a legend only if at least one individual plot has a name. |
| 106 var valid = false; |
| 107 for (var i = 0; i < plots.length; i++) { |
| 108 if (plots[i].name != null) { |
| 109 valid = true; |
| 110 break; |
| 111 } |
| 112 } |
| 113 if (!valid) { |
| 114 legendCanvas.hidden = true; |
| 115 return; |
| 116 } |
| 117 |
| 118 var padding = 2; |
| 119 var legendSquareSide = 12; |
| 120 var legendCtx = legendCanvas.getContext('2d'); |
| 121 var xLoc = padding; |
| 122 var yLoc = padding; |
| 123 // Adjust the height of the canvas before drawing on it. |
| 124 for (var i = 0; i < plots.length; i++) { |
| 125 if (plots[i].name == null) { |
| 126 continue; |
| 127 } |
| 128 var legendText = ' - ' + plots[i].name; |
| 129 xLoc += legendSquareSide + getTextWidth(legendCtx, legendText) + |
| 130 2 * padding; |
| 131 if (i < plots.length - 1) { |
| 132 var xLocNext = xLoc + |
| 133 getTextWidth(legendCtx, ' - ' + plots[i + 1].name) + |
| 134 legendSquareSide; |
| 135 if (xLocNext >= legendCanvas.width) { |
| 136 xLoc = padding; |
| 137 yLoc = yLoc + 2 * padding + textHeight; |
| 138 } |
| 139 } |
| 140 } |
| 141 |
| 142 legendCanvas.height = yLoc + textHeight + padding; |
| 143 |
| 144 xLoc = padding; |
| 145 yLoc = padding; |
| 146 // Go over the plots again, this time drawing the legends. |
| 147 for (var i = 0; i < plots.length; i++) { |
| 148 legendCtx.fillStyle = plots[i].color; |
| 149 legendCtx.fillRect(xLoc, yLoc, legendSquareSide, legendSquareSide); |
| 150 xLoc += legendSquareSide; |
| 151 |
| 152 var legendText = ' - ' + plots[i].name; |
| 153 drawText(legendCtx, legendText, xLoc, yLoc + textHeight - 1); |
| 154 xLoc += getTextWidth(legendCtx, legendText) + 2 * padding; |
| 155 |
| 156 if (i < plots.length - 1) { |
| 157 var xLocNext = xLoc + |
| 158 getTextWidth(legendCtx, ' - ' + plots[i + 1].name) + |
| 159 legendSquareSide; |
| 160 if (xLocNext >= legendCanvas.width) { |
| 161 xLoc = padding; |
| 162 yLoc = yLoc + 2 * padding + textHeight; |
| 163 } |
| 164 } |
| 165 } |
94 } | 166 } |
95 | 167 |
96 var yMinStr = valueToString(yMin); | 168 var yMinStr = valueToString(yMin); |
97 var yMaxStr = valueToString(yMax); | 169 var yMaxStr = valueToString(yMax); |
98 var yHalfStr = valueToString((yMax + yMin) / 2); | 170 var yHalfStr = valueToString((yMax + yMin) / 2); |
99 var yMinWidth = getTextWidth(yMinStr); | 171 var yMinWidth = getTextWidth(plotCtx, yMinStr); |
100 var yMaxWidth = getTextWidth(yMaxStr); | 172 var yMaxWidth = getTextWidth(plotCtx, yMaxStr); |
101 var yHalfWidth = getTextWidth(yHalfStr); | 173 var yHalfWidth = getTextWidth(plotCtx, yHalfStr); |
102 | 174 |
103 var xMinStr = tData[0]; | 175 var xMinStr = tData[0]; |
104 var xMaxStr = tData[size - 1]; | 176 var xMaxStr = tData[size - 1]; |
105 var xMinWidth = getTextWidth(xMinStr); | 177 var xMinWidth = getTextWidth(plotCtx, xMinStr); |
106 var xMaxWidth = getTextWidth(xMaxStr); | 178 var xMaxWidth = getTextWidth(plotCtx, xMaxStr); |
107 | 179 |
108 var xOrigin = padding + Math.max(yMinWidth, | 180 var xOrigin = padding + Math.max(yMinWidth, |
109 yMaxWidth, | 181 yMaxWidth, |
110 Math.round(xMinWidth / 2)); | 182 Math.round(xMinWidth / 2)); |
111 var yOrigin = padding + textHeight; | 183 var yOrigin = padding + textHeight; |
112 var width = canvas.width - xOrigin - Math.floor(xMaxWidth / 2) - padding; | 184 var width = plotCanvas.width - xOrigin - Math.floor(xMaxWidth / 2) - padding; |
113 if (width < size) { | 185 if (width < size) { |
114 canvas.width += size - width; | 186 plotCanvas.width += size - width; |
115 width = size; | 187 width = size; |
116 } | 188 } |
117 var height = canvas.height - yOrigin - textHeight - padding; | 189 var height = plotCanvas.height - yOrigin - textHeight - padding; |
118 | 190 |
119 function drawPlots() { | 191 function drawPlots() { |
120 // Start fresh. | 192 // Start fresh. |
121 ctx.clearRect(0, 0, canvas.width, canvas.height); | 193 plotCtx.clearRect(0, 0, plotCanvas.width, plotCanvas.height); |
122 | 194 |
123 // Draw the bounding rectangle. | 195 // Draw the bounding rectangle. |
124 drawRect(xOrigin, yOrigin, width, height, gridColor); | 196 drawRect(plotCtx, xOrigin, yOrigin, width, height, gridColor); |
125 | 197 |
126 // Draw the x and y bound values. | 198 // Draw the x and y bound values. |
127 drawText(yMaxStr, xOrigin - yMaxWidth, yOrigin + textHeight); | 199 drawText(plotCtx, yMaxStr, xOrigin - yMaxWidth, yOrigin + textHeight); |
128 drawText(yMinStr, xOrigin - yMinWidth, yOrigin + height); | 200 drawText(plotCtx, yMinStr, xOrigin - yMinWidth, yOrigin + height); |
129 drawText(xMinStr, xOrigin - xMinWidth / 2, yOrigin + height + textHeight); | 201 drawText(plotCtx, |
130 drawText(xMaxStr, | 202 xMinStr, |
| 203 xOrigin - xMinWidth / 2, |
| 204 yOrigin + height + textHeight); |
| 205 drawText(plotCtx, |
| 206 xMaxStr, |
131 xOrigin + width - xMaxWidth / 2, | 207 xOrigin + width - xMaxWidth / 2, |
132 yOrigin + height + textHeight); | 208 yOrigin + height + textHeight); |
133 | 209 |
134 // Draw y-level (horizontal) lines. | 210 // Draw y-level (horizontal) lines. |
135 drawLine(xOrigin + 1, yOrigin + height / 4, | 211 drawLine(plotCtx, |
| 212 xOrigin + 1, yOrigin + height / 4, |
136 xOrigin + width - 2, yOrigin + height / 4, | 213 xOrigin + width - 2, yOrigin + height / 4, |
137 gridColor); | 214 gridColor); |
138 drawLine(xOrigin + 1, yOrigin + height / 2, | 215 drawLine(plotCtx, |
| 216 xOrigin + 1, yOrigin + height / 2, |
139 xOrigin + width - 2, yOrigin + height / 2, gridColor); | 217 xOrigin + width - 2, yOrigin + height / 2, gridColor); |
140 drawLine(xOrigin + 1, yOrigin + 3 * height / 4, | 218 drawLine(plotCtx, |
| 219 xOrigin + 1, yOrigin + 3 * height / 4, |
141 xOrigin + width - 2, yOrigin + 3 * height / 4, | 220 xOrigin + width - 2, yOrigin + 3 * height / 4, |
142 gridColor); | 221 gridColor); |
143 | 222 |
144 // Draw half-level value. | 223 // Draw half-level value. |
145 drawText(yHalfStr, | 224 drawText(plotCtx, |
| 225 yHalfStr, |
146 xOrigin - yHalfWidth, | 226 xOrigin - yHalfWidth, |
147 yOrigin + height / 2 + textHeight / 2); | 227 yOrigin + height / 2 + textHeight / 2); |
148 | 228 |
149 // Draw the plots. | 229 // Draw the plots. |
150 var yValRange = yMax - yMin; | 230 var yValRange = yMax - yMin; |
151 for (var count = 0; count < plots.length; count++) { | 231 for (var count = 0; count < plots.length; count++) { |
152 var plot = plots[count]; | 232 var plot = plots[count]; |
153 var yData = plot.data; | 233 var yData = plot.data; |
154 ctx.strokeStyle = plot.color; | 234 plotCtx.strokeStyle = plot.color; |
155 ctx.beginPath(); | 235 plotCtx.beginPath(); |
156 var beginPath = true; | 236 var beginPath = true; |
157 for (var i = 0; i < size; i++) { | 237 for (var i = 0; i < size; i++) { |
158 var val = yData[i]; | 238 var val = yData[i]; |
159 if (typeof val === 'string') { | 239 if (typeof val === 'string') { |
160 // Stroke the plot drawn so far and begin a fresh plot. | 240 // Stroke the plot drawn so far and begin a fresh plot. |
161 ctx.stroke(); | 241 plotCtx.stroke(); |
162 ctx.beginPath(); | 242 plotCtx.beginPath(); |
163 beginPath = true; | 243 beginPath = true; |
164 continue; | 244 continue; |
165 } | 245 } |
166 var xPos = xOrigin + Math.floor(i / (size - 1) * (width - 1)); | 246 var xPos = xOrigin + Math.floor(i / (size - 1) * (width - 1)); |
167 var yPos = yOrigin + height - 1 - | 247 var yPos = yOrigin + height - 1 - |
168 Math.round((val - yMin) / yValRange * (height - 1)); | 248 Math.round((val - yMin) / yValRange * (height - 1)); |
169 if (beginPath) { | 249 if (beginPath) { |
170 ctx.moveTo(xPos, yPos); | 250 plotCtx.moveTo(xPos, yPos); |
171 // A simple move to does not print anything. Hence, draw a little | 251 // A simple move to does not print anything. Hence, draw a little |
172 // square here to mark a beginning. | 252 // square here to mark a beginning. |
173 ctx.fillStyle = '#000'; | 253 plotCtx.fillStyle = '#000'; |
174 ctx.fillRect(xPos - 1, yPos - 1, 2, 2); | 254 plotCtx.fillRect(xPos - 1, yPos - 1, 2, 2); |
175 beginPath = false; | 255 beginPath = false; |
176 } else { | 256 } else { |
177 ctx.lineTo(xPos, yPos); | 257 plotCtx.lineTo(xPos, yPos); |
178 if (i === size - 1 || typeof yData[i + 1] === 'string') { | 258 if (i === size - 1 || typeof yData[i + 1] === 'string') { |
179 // Draw a little square to mark an end to go with the start | 259 // Draw a little square to mark an end to go with the start |
180 // markers from above. | 260 // markers from above. |
181 ctx.fillStyle = '#000'; | 261 plotCtx.fillStyle = '#000'; |
182 ctx.fillRect(xPos - 1, yPos - 1, 2, 2); | 262 plotCtx.fillRect(xPos - 1, yPos - 1, 2, 2); |
183 } | 263 } |
184 } | 264 } |
185 } | 265 } |
186 ctx.stroke(); | 266 plotCtx.stroke(); |
187 } | 267 } |
188 | 268 |
189 // Paint the missing time intervals with |gridColor|. | 269 // Paint the missing time intervals with |gridColor|. |
190 // Pick one of the plots to look for missing time intervals. | 270 // Pick one of the plots to look for missing time intervals. |
| 271 function drawMissingRect(start, end) { |
| 272 var xLeft = xOrigin + Math.floor(start / (size - 1) * (width - 1)); |
| 273 var xRight = xOrigin + Math.floor(end / (size - 1) * (width - 1)); |
| 274 plotCtx.fillStyle = gridColor; |
| 275 // The x offsets below are present so that the blank space starts |
| 276 // and ends between two valid samples. |
| 277 plotCtx.fillRect(xLeft + 1, yOrigin, xRight - xLeft - 2, height - 1); |
| 278 } |
191 var inMissingInterval = false; | 279 var inMissingInterval = false; |
192 var intervalStart; | 280 var intervalStart; |
193 for (var i = 0; i < size; i++) { | 281 for (var i = 0; i < size; i++) { |
194 if (typeof plots[0].data[i] === 'string') { | 282 if (typeof plots[0].data[i] === 'string') { |
195 if (!inMissingInterval) { | 283 if (!inMissingInterval) { |
196 inMissingInterval = true; | 284 inMissingInterval = true; |
197 // The missing interval should actually start from the previous | 285 // The missing interval should actually start from the previous |
198 // sample. | 286 // sample. |
199 intervalStart = Math.max(i - 1, 0); | 287 intervalStart = Math.max(i - 1, 0); |
200 } | 288 } |
| 289 |
| 290 if (i == size - 1) { |
| 291 // If this is the last sample, just draw missing rect. |
| 292 drawMissingRect(intervalStart, i); |
| 293 } |
201 } else if (inMissingInterval) { | 294 } else if (inMissingInterval) { |
202 inMissingInterval = false; | 295 inMissingInterval = false; |
203 var xLeft = xOrigin + | 296 drawMissingRect(intervalStart, i); |
204 Math.floor(intervalStart / (size - 1) * (width - 1)); | |
205 var xRight = xOrigin + Math.floor(i / (size - 1) * (width - 1)); | |
206 ctx.fillStyle = gridColor; | |
207 // The x offsets below are present so that the blank space starts | |
208 // and ends between two valid samples. | |
209 ctx.fillRect(xLeft + 1, yOrigin, xRight - xLeft - 2, height - 1); | |
210 } | 297 } |
211 } | 298 } |
212 } | 299 } |
213 | 300 |
214 function drawTimeGuide(tDataIndex) { | 301 function drawTimeGuide(tDataIndex) { |
215 var x = xOrigin + tDataIndex / (size - 1) * (width - 1); | 302 var x = xOrigin + tDataIndex / (size - 1) * (width - 1); |
216 drawLine(x, yOrigin, x, yOrigin + height - 1, '#000'); | 303 drawLine(plotCtx, x, yOrigin, x, yOrigin + height - 1, '#000'); |
217 drawText(tData[tDataIndex], | 304 drawText(plotCtx, |
218 x - getTextWidth(tData[tDataIndex]) / 2, | 305 tData[tDataIndex], |
| 306 x - getTextWidth(plotCtx, tData[tDataIndex]) / 2, |
219 yOrigin - 2); | 307 yOrigin - 2); |
220 | 308 |
221 for (var count = 0; count < plots.length; count++) { | 309 for (var count = 0; count < plots.length; count++) { |
222 var yData = plots[count].data; | 310 var yData = plots[count].data; |
223 | 311 |
224 // Draw small black square on the plot where the time guide intersects | 312 // Draw small black square on the plot where the time guide intersects |
225 // it. | 313 // it. |
226 var val = yData[tDataIndex]; | 314 var val = yData[tDataIndex]; |
227 var yPos, valStr; | 315 var yPos, valStr; |
228 if (typeof val === 'string') { | 316 if (typeof val === 'string') { |
229 yPos = yOrigin + Math.round(height / 2); | 317 yPos = yOrigin + Math.round(height / 2); |
230 valStr = val; | 318 valStr = val; |
231 } else { | 319 } else { |
232 yPos = yOrigin + height - 1 - | 320 yPos = yOrigin + height - 1 - |
233 Math.round((val - yMin) / (yMax - yMin) * (height - 1)); | 321 Math.round((val - yMin) / (yMax - yMin) * (height - 1)); |
234 valStr = valueToString(val); | 322 valStr = valueToString(val); |
235 } | 323 } |
236 ctx.fillStyle = '#000'; | 324 plotCtx.fillStyle = '#000'; |
237 ctx.fillRect(x - 2, yPos - 2, 4, 4); | 325 plotCtx.fillRect(x - 2, yPos - 2, 4, 4); |
238 | 326 |
239 // Draw the val to right of the intersection. | 327 // Draw the val to right of the intersection. |
240 var yLoc; | 328 var yLoc; |
241 if (yPos - textHeight / 2 < yOrigin) { | 329 if (yPos - textHeight / 2 < yOrigin) { |
242 yLoc = yOrigin + textHeight; | 330 yLoc = yOrigin + textHeight; |
243 } else if (yPos + textHeight / 2 >= yPos + height) { | 331 } else if (yPos + textHeight / 2 >= yPos + height) { |
244 yLoc = yOrigin + height - 1; | 332 yLoc = yOrigin + height - 1; |
245 } else { | 333 } else { |
246 yLoc = yPos + textHeight / 2; | 334 yLoc = yPos + textHeight / 2; |
247 } | 335 } |
248 drawHighlightText(valStr, x + 5, yLoc, plots[count].color); | 336 drawHighlightText(plotCtx, valStr, x + 5, yLoc, plots[count].color); |
249 } | 337 } |
250 } | 338 } |
251 | 339 |
252 function onMouseOverOrMove(event) { | 340 function onMouseOverOrMove(event) { |
253 drawPlots(); | 341 drawPlots(); |
254 | 342 |
255 var boundingRect = canvas.getBoundingClientRect(); | 343 var boundingRect = plotCanvas.getBoundingClientRect(); |
256 var x = event.clientX - boundingRect.left; | 344 var x = Math.round(event.clientX - boundingRect.left); |
257 var y = event.clientY - boundingRect.top; | 345 var y = Math.round(event.clientY - boundingRect.top); |
258 if (x < xOrigin || x >= xOrigin + width || | 346 if (x < xOrigin || x >= xOrigin + width || |
259 y < yOrigin || y >= yOrigin + height) { | 347 y < yOrigin || y >= yOrigin + height) { |
260 return; | 348 return; |
261 } | 349 } |
262 | 350 |
263 if (width == size) { | 351 if (width == size) { |
264 drawTimeGuide(x - xOrigin); | 352 drawTimeGuide(x - xOrigin); |
265 } else { | 353 } else { |
266 drawTimeGuide(Math.round((x - xOrigin) / (width - 1) * (size - 1))); | 354 drawTimeGuide(Math.round((x - xOrigin) / (width - 1) * (size - 1))); |
267 } | 355 } |
268 } | 356 } |
269 | 357 |
270 function onMouseOut(event) { | 358 function onMouseOut(event) { |
271 drawPlots(); | 359 drawPlots(); |
272 } | 360 } |
273 | 361 |
| 362 drawLegend(); |
274 drawPlots(); | 363 drawPlots(); |
275 canvas.addEventListener('mouseover', onMouseOverOrMove); | 364 plotCanvas.addEventListener('mouseover', onMouseOverOrMove); |
276 canvas.addEventListener('mousemove', onMouseOverOrMove); | 365 plotCanvas.addEventListener('mousemove', onMouseOverOrMove); |
277 canvas.addEventListener('mouseout', onMouseOut); | 366 plotCanvas.addEventListener('mouseout', onMouseOut); |
278 } | 367 } |
279 | 368 |
280 var sleepSampleInterval = 30 * 1000; // in milliseconds. | 369 var sleepSampleInterval = 30 * 1000; // in milliseconds. |
281 var sleepText = loadTimeData.getString('systemSuspended'); | 370 var sleepText = loadTimeData.getString('systemSuspended'); |
| 371 var invalidDataText = loadTimeData.getString('invalidData'); |
| 372 var offlineText = loadTimeData.getString('offlineText'); |
| 373 |
| 374 var plotColors = ['Red', 'Blue', 'Green', 'Gold', 'CadetBlue', 'LightCoral', |
| 375 'LightSlateGray', 'Peru', 'DarkRed', 'LawnGreen', 'Tan']; |
| 376 |
| 377 /** |
| 378 * Add canvases for plotting to |plotsDiv|. For every header in |headerArray|, |
| 379 * one canvas for the plot and one for its legend are added. |
| 380 * |
| 381 * @param {Array.<string>} headerArray Headers for the different plots to be |
| 382 * added to |plotsDiv|. |
| 383 * @param {HTMLDivElement} plotsDiv The div element into which the canvases |
| 384 * are added. |
| 385 * @return {<string>: {plotCanvas: <HTMLCanvasElement>, |
| 386 * legendCanvas: <HTMLCanvasElement>} Returns an object |
| 387 * with the headers as 'keys'. Each element is an object containing the |
| 388 * legend canvas and the plot canvas that have been added to |plotsDiv|. |
| 389 */ |
| 390 function addCanvases(headerArray, plotsDiv) { |
| 391 // Remove the contents before adding new ones. |
| 392 while (plotsDiv.firstChild != null) { |
| 393 plotsDiv.removeChild(plotsDiv.firstChild); |
| 394 } |
| 395 var width = Math.floor(plotsDiv.getBoundingClientRect().width); |
| 396 var canvases = {}; |
| 397 for (var i = 0; i < headerArray.length; i++) { |
| 398 var header = document.createElement('h4'); |
| 399 header.textContent = headerArray[i]; |
| 400 plotsDiv.appendChild(header); |
| 401 |
| 402 var legendCanvas = document.createElement('canvas'); |
| 403 legendCanvas.width = width; |
| 404 legendCanvas.height = 5; |
| 405 plotsDiv.appendChild(legendCanvas); |
| 406 |
| 407 var plotCanvasDiv = document.createElement('div'); |
| 408 plotCanvasDiv.style.overflow = 'auto'; |
| 409 plotCanvasDiv.style.maxWidth = String(width) + 'px'; |
| 410 plotsDiv.appendChild(plotCanvasDiv); |
| 411 |
| 412 plotCanvas = document.createElement('canvas'); |
| 413 plotCanvas.width = width; |
| 414 plotCanvas.height = 200; |
| 415 plotCanvasDiv.appendChild(plotCanvas); |
| 416 |
| 417 canvases[headerArray[i]] = {plot: plotCanvas, legend: legendCanvas}; |
| 418 } |
| 419 return canvases; |
| 420 } |
282 | 421 |
283 /** | 422 /** |
284 * Add samples in |sampleArray| to individual plots in |plots|. If the system | 423 * Add samples in |sampleArray| to individual plots in |plots|. If the system |
285 * resumed from a sleep/suspend, then "suspended" sleep samples are added to | 424 * resumed from a sleep/suspend, then "suspended" sleep samples are added to |
286 * the plot for the sleep duration. | 425 * the plot for the sleep duration. |
287 * | 426 * |
288 * @param {Array.<{data: Array.<number>, color: string}>} plots An | 427 * @param {Array.<{data: Array.<number>, color: string}>} plots An |
289 * array of plots to plot on the canvas. The field 'data' of a plot is an | 428 * array of plots to plot on the canvas. The field 'data' of a plot is an |
290 * array of samples to be plotted as a line graph with color speficied by | 429 * array of samples to be plotted as a line graph with color speficied by |
291 * the field 'color'. The elements in the 'data' array are ordered | 430 * the field 'color'. The elements in the 'data' array are ordered |
292 * corresponding to their sampling time in the argument 'tData'. Also, the | 431 * corresponding to their sampling time in the argument 'tData'. Also, the |
293 * number of elements in the 'data' array should be the same as in the time | 432 * number of elements in the 'data' array should be the same as in the time |
294 * array 'tData' below. | 433 * array 'tData' below. |
295 * @param {Array.<number>} tData The time (in seconds) in the past when the | 434 * @param {Array.<number>} tData The time (in seconds) in the past when the |
296 * corresponding data in plots was sampled. | 435 * corresponding data in plots was sampled. |
297 * @param {Array.<number>} sampleArray The array of samples wherein each | 436 * @param {Array.<number>} sampleArray The array of samples wherein each |
298 * element corresponds to the individual plot in |plots|. | 437 * element corresponds to the individual plot in |plots|. |
299 * @param {number} sampleTime Time in milliseconds since the epoch when the | 438 * @param {number} sampleTime Time in milliseconds since the epoch when the |
300 * samples in |sampleArray| were captured. | 439 * samples in |sampleArray| were captured. |
301 * @param {number} previousSampleTime Time in milliseconds since the epoch | 440 * @param {number} previousSampleTime Time in milliseconds since the epoch |
302 * when the sample prior to the current sample was captured. | 441 * when the sample prior to the current sample was captured. |
303 * @param {Array.<{time: number, sleepDuration: number}>} systemResumedArray An | 442 * @param {Array.<{time: number, sleepDuration: number}>} systemResumedArray An |
304 * array objects corresponding to system resume events. The 'time' field is | 443 * array objects corresponding to system resume events. The 'time' field is |
305 * for the time in milliseconds since the epoch when the system resumed. The | 444 * for the time in milliseconds since the epoch when the system resumed. The |
306 * 'sleepDuration' field is for the time in milliseconds the system spent | 445 * 'sleepDuration' field is for the time in milliseconds the system spent |
307 * in sleep/suspend state. | 446 * in sleep/suspend state. |
308 */ | 447 */ |
309 function addTimeDataSample(plots, tData, sampleArray, | 448 function addTimeDataSample(plots, tData, absTime, sampleArray, |
310 sampleTime, previousSampleTime, | 449 sampleTime, previousSampleTime, |
311 systemResumedArray) { | 450 systemResumedArray) { |
312 for (var i = 0; i < plots.length; i++) { | 451 for (var i = 0; i < plots.length; i++) { |
313 if (plots[i].data.length != tData.length) { | 452 if (plots[i].data.length != tData.length) { |
314 throw new Error('Mismatch in time and plot data.'); | 453 throw new Error('Mismatch in time and plot data.'); |
315 } | 454 } |
316 } | 455 } |
317 | 456 |
318 var time; | 457 var time; |
319 if (tData.length == 0) { | 458 if (tData.length == 0) { |
320 time = new Date(sampleTime); | 459 time = new Date(sampleTime); |
| 460 absTime[0] = sampleTime; |
321 tData[0] = time.toLocaleTimeString(); | 461 tData[0] = time.toLocaleTimeString(); |
322 for (var i = 0; i < plots.length; i++) { | 462 for (var i = 0; i < plots.length; i++) { |
323 plots[i].data[0] = sampleArray[i]; | 463 plots[i].data[0] = sampleArray[i]; |
324 } | 464 } |
325 return; | 465 return; |
326 } | 466 } |
327 | 467 |
328 for (var i = 0; i < systemResumedArray.length; i++) { | 468 for (var i = 0; i < systemResumedArray.length; i++) { |
329 var resumeTime = systemResumedArray[i].time; | 469 var resumeTime = systemResumedArray[i].time; |
330 var sleepDuration = systemResumedArray[i].sleepDuration; | 470 var sleepDuration = systemResumedArray[i].sleepDuration; |
331 var sleepStartTime = resumeTime - sleepDuration; | 471 var sleepStartTime = resumeTime - sleepDuration; |
332 if (resumeTime < sampleTime && sleepStartTime > previousSampleTime) { | 472 if (resumeTime < sampleTime) { |
| 473 if (sleepStartTime < previousSampleTime) { |
| 474 // This can happen if pending callbacks were handled before actually |
| 475 // suspending. |
| 476 sleepStartTime = previousSampleTime + 1000; |
| 477 } |
333 // Add sleep samples for every |sleepSampleInterval|. | 478 // Add sleep samples for every |sleepSampleInterval|. |
334 var sleepSampleTime = sleepStartTime; | 479 var sleepSampleTime = sleepStartTime; |
335 while (sleepSampleTime < resumeTime) { | 480 while (sleepSampleTime < resumeTime) { |
336 time = new Date(sleepSampleTime); | 481 time = new Date(sleepSampleTime); |
| 482 absTime.push(sleepSampleTime); |
337 tData.push(time.toLocaleTimeString()); | 483 tData.push(time.toLocaleTimeString()); |
338 for (var j = 0; j < plots.length; j++) { | 484 for (var j = 0; j < plots.length; j++) { |
339 plots[j].data.push(sleepText); | 485 plots[j].data.push(sleepText); |
340 } | 486 } |
341 sleepSampleTime += sleepSampleInterval; | 487 sleepSampleTime += sleepSampleInterval; |
342 } | 488 } |
343 } | 489 } |
344 } | 490 } |
345 | 491 |
346 time = new Date(sampleTime); | 492 time = new Date(sampleTime); |
| 493 absTime.push(sampleTime); |
347 tData.push(time.toLocaleTimeString()); | 494 tData.push(time.toLocaleTimeString()); |
348 for (var i = 0; i < plots.length; i++) { | 495 for (var i = 0; i < plots.length; i++) { |
349 plots[i].data.push(sampleArray[i]); | 496 plots[i].data.push(sampleArray[i]); |
350 } | 497 } |
351 } | 498 } |
352 | 499 |
353 /** | 500 /** |
354 * Display the battery charge vs time on a line graph. | 501 * Display the battery charge vs time on a line graph. |
355 * | 502 * |
356 * @param {Array.<{time: number, | 503 * @param {Array.<{time: number, |
357 * batteryPercent: number, | 504 * batteryPercent: number, |
358 * batteryDischargeRate: number, | 505 * batteryDischargeRate: number, |
359 * externalPower: number}>} powerSupplyArray An array of objects | 506 * externalPower: number}>} powerSupplyArray An array of objects |
360 * with fields representing the battery charge, time when the charge | 507 * with fields representing the battery charge, time when the charge |
361 * measurement was taken, and whether there was external power connected at | 508 * measurement was taken, and whether there was external power connected at |
362 * that time. | 509 * that time. |
363 * @param {Array.<{time: ?, sleepDuration: ?}>} systemResumedArray An array | 510 * @param {Array.<{time: ?, sleepDuration: ?}>} systemResumedArray An array |
364 * objects with fields 'time' and 'sleepDuration'. Each object corresponds | 511 * objects with fields 'time' and 'sleepDuration'. Each object corresponds |
365 * to a system resume event. The 'time' field is for the time in | 512 * to a system resume event. The 'time' field is for the time in |
366 * milliseconds since the epoch when the system resumed. The 'sleepDuration' | 513 * milliseconds since the epoch when the system resumed. The 'sleepDuration' |
367 * field is for the time in milliseconds the system spent in sleep/suspend | 514 * field is for the time in milliseconds the system spent in sleep/suspend |
368 * state. | 515 * state. |
369 */ | 516 */ |
370 function showBatteryChargeData(powerSupplyArray, systemResumedArray) { | 517 function showBatteryChargeData(powerSupplyArray, systemResumedArray) { |
371 var chargeTimeData = []; | 518 var chargeTimeData = []; |
| 519 var chargeAbsTime = []; |
372 var chargePlot = [ | 520 var chargePlot = [ |
373 { | 521 { |
| 522 name: loadTimeData.getString('batteryChargePercentageHeader'), |
374 color: '#0000FF', | 523 color: '#0000FF', |
375 data: [] | 524 data: [] |
376 } | 525 } |
377 ]; | 526 ]; |
378 var dischargeRateTimeData = []; | 527 var dischargeRateTimeData = []; |
| 528 var dischargeRateAbsTime = []; |
379 var dischargeRatePlot = [ | 529 var dischargeRatePlot = [ |
380 { | 530 { |
| 531 name: loadTimeData.getString('dischargeRateLegendText'), |
381 color: '#FF0000', | 532 color: '#FF0000', |
382 data: [] | 533 data: [] |
383 } | 534 } |
384 ]; | 535 ]; |
385 var minDischargeRate = 1000; // A high unrealistic number to begin with. | 536 var minDischargeRate = 1000; // A high unrealistic number to begin with. |
386 var maxDischargeRate = -1000; // A low unrealistic number to begin with. | 537 var maxDischargeRate = -1000; // A low unrealistic number to begin with. |
387 for (var i = 0; i < powerSupplyArray.length; i++) { | 538 for (var i = 0; i < powerSupplyArray.length; i++) { |
388 var j = Math.max(i - 1, 0); | 539 var j = Math.max(i - 1, 0); |
389 | 540 |
390 addTimeDataSample(chargePlot, chargeTimeData, | 541 addTimeDataSample(chargePlot, |
| 542 chargeTimeData, |
| 543 chargeAbsTime, |
391 [powerSupplyArray[i].batteryPercent], | 544 [powerSupplyArray[i].batteryPercent], |
392 powerSupplyArray[i].time, | 545 powerSupplyArray[i].time, |
393 powerSupplyArray[j].time, | 546 powerSupplyArray[j].time, |
394 systemResumedArray); | 547 systemResumedArray); |
395 | 548 |
396 var dischargeRate = powerSupplyArray[i].batteryDischargeRate; | 549 var dischargeRate = powerSupplyArray[i].batteryDischargeRate; |
397 minDischargeRate = Math.min(dischargeRate, minDischargeRate); | 550 minDischargeRate = Math.min(dischargeRate, minDischargeRate); |
398 maxDischargeRate = Math.max(dischargeRate, maxDischargeRate); | 551 maxDischargeRate = Math.max(dischargeRate, maxDischargeRate); |
399 addTimeDataSample(dischargeRatePlot, | 552 addTimeDataSample(dischargeRatePlot, |
400 dischargeRateTimeData, | 553 dischargeRateTimeData, |
| 554 dischargeRateAbsTime, |
401 [dischargeRate], | 555 [dischargeRate], |
402 powerSupplyArray[i].time, | 556 powerSupplyArray[i].time, |
403 powerSupplyArray[j].time, | 557 powerSupplyArray[j].time, |
404 systemResumedArray); | 558 systemResumedArray); |
405 } | 559 } |
406 if (minDischargeRate == maxDischargeRate) { | 560 if (minDischargeRate == maxDischargeRate) { |
407 // This means that all the samples had the same value. Hence, offset the | 561 // This means that all the samples had the same value. Hence, offset the |
408 // extremes by a bit so that the plot looks good. | 562 // extremes by a bit so that the plot looks good. |
409 minDischargeRate -= 1; | 563 minDischargeRate -= 1; |
410 maxDischargeRate += 1; | 564 maxDischargeRate += 1; |
411 } | 565 } |
412 | 566 |
413 var chargeCanvas = $('battery-charge-percentage-canvas'); | 567 plotsDiv = $('battery-charge-plots-div'); |
414 var dischargeRateCanvas = $('battery-discharge-rate-canvas'); | 568 |
415 plotLineGraph(chargeCanvas, chargeTimeData, chargePlot, 0.00, 100.00, 3); | 569 canvases = addCanvases( |
416 plotLineGraph(dischargeRateCanvas, | 570 [loadTimeData.getString('batteryChargePercentageHeader'), |
417 dischargeRateTimeData, | 571 loadTimeData.getString('batteryDischargeRateHeader')], |
418 dischargeRatePlot, | 572 plotsDiv); |
419 minDischargeRate, | 573 |
420 maxDischargeRate, | 574 batteryChargeCanvases = canvases[ |
421 3); | 575 loadTimeData.getString('batteryChargePercentageHeader')]; |
| 576 plotLineGraph( |
| 577 batteryChargeCanvases['plot'], |
| 578 batteryChargeCanvases['legend'], |
| 579 chargeTimeData, |
| 580 chargePlot, |
| 581 0.00, |
| 582 100.00, |
| 583 3); |
| 584 |
| 585 dischargeRateCanvases = canvases[ |
| 586 loadTimeData.getString('batteryDischargeRateHeader')]; |
| 587 plotLineGraph( |
| 588 dischargeRateCanvases['plot'], |
| 589 dischargeRateCanvases['legend'], |
| 590 dischargeRateTimeData, |
| 591 dischargeRatePlot, |
| 592 minDischargeRate, |
| 593 maxDischargeRate, |
| 594 3); |
| 595 } |
| 596 |
| 597 /** |
| 598 * Shows state occupancy data (CPU idle or CPU freq state occupancy) on a set of |
| 599 * plots on the about:power UI. |
| 600 * |
| 601 * @param {Array.<{Array.<{ |
| 602 * time: number, |
| 603 * cpuOnline:boolean, |
| 604 * timeInState: {<string>: number}>}>} timeInStateData Array of arrays |
| 605 * where each array corresponds to a CPU on the system. The elements of the |
| 606 * individual arrays contain state occupancy samples. |
| 607 * @param {Array.<{time: ?, sleepDuration: ?}>} systemResumedArray An array |
| 608 * objects with fields 'time' and 'sleepDuration'. Each object corresponds |
| 609 * to a system resume event. The 'time' field is for the time in |
| 610 * milliseconds since the epoch when the system resumed. The 'sleepDuration' |
| 611 * field is for the time in milliseconds the system spent in sleep/suspend |
| 612 * state. |
| 613 * @param {string} i18nHeaderString The header string to be displayed with each |
| 614 * plot. For example, CPU idle data will have its own header format, and CPU |
| 615 * freq data will have its header format. |
| 616 * @param {string} unitString This is the string capturing the unit, if any, |
| 617 * for the different states. Note that this is not the unit of the data |
| 618 * being plotted. |
| 619 * @param {HTMLDivElement} plotsDivId The div element in which the plots should |
| 620 * be added. |
| 621 */ |
| 622 function showStateOccupancyData(timeInStateData, |
| 623 systemResumedArray, |
| 624 i18nHeaderString, |
| 625 unitString, |
| 626 plotsDivId) { |
| 627 var cpuPlots = []; |
| 628 for (var cpu = 0; cpu < timeInStateData.length; cpu++) { |
| 629 var cpuData = timeInStateData[cpu]; |
| 630 if (cpuData.length == 0) { |
| 631 cpuPlots[cpu] = {plots: [], tData: []}; |
| 632 continue; |
| 633 } |
| 634 tData = []; |
| 635 absTime = []; |
| 636 // Each element of |plots| is an array of samples, one for each of the CPU |
| 637 // states. The number of states is dicovered by looking at the first |
| 638 // sample for which the CPU is online. |
| 639 var plots = []; |
| 640 var stateIndexMap = []; |
| 641 var stateCount = 0; |
| 642 for (var i = 0; i < cpuData.length; i++) { |
| 643 if (cpuData[i].cpuOnline) { |
| 644 for (var state in cpuData[i].timeInState) { |
| 645 var stateName = state; |
| 646 if (unitString != null) { |
| 647 stateName += ' ' + unitString; |
| 648 } |
| 649 plots.push({ |
| 650 name: stateName, |
| 651 data: [], |
| 652 color: plotColors[stateCount] |
| 653 }); |
| 654 stateIndexMap.push(state); |
| 655 stateCount += 1; |
| 656 } |
| 657 break; |
| 658 } |
| 659 } |
| 660 // If stateCount is 0, then it means the CPU has been offline |
| 661 // throughout. Just add a single plot for such a case. |
| 662 if (stateCount == 0) { |
| 663 plots.push({ |
| 664 name: null, |
| 665 data: [], |
| 666 color: null |
| 667 }); |
| 668 stateCount = 1; // Some invalid state! |
| 669 } |
| 670 |
| 671 // Pass the samples through the function addTimeDataSample to add 'sleep' |
| 672 // samples. |
| 673 for (var i = 0; i < cpuData.length; i++) { |
| 674 var sample = cpuData[i]; |
| 675 var valArray = []; |
| 676 for (var j = 0; j < stateCount; j++) { |
| 677 if (sample.cpuOnline) { |
| 678 valArray[j] = sample.timeInState[stateIndexMap[j]]; |
| 679 } else { |
| 680 valArray[j] = offlineText; |
| 681 } |
| 682 } |
| 683 |
| 684 var k = Math.max(i - 1, 0); |
| 685 addTimeDataSample(plots, |
| 686 tData, |
| 687 absTime, |
| 688 valArray, |
| 689 sample.time, |
| 690 cpuData[k].time, |
| 691 systemResumedArray); |
| 692 } |
| 693 |
| 694 // Calculate the percentage occupancy of each state. A valid number is |
| 695 // possible only if two consecutive samples are valid/numbers. |
| 696 for (var k = 0; k < stateCount; k++) { |
| 697 var stateData = plots[k].data; |
| 698 // Skip the first sample as there is no previous sample. |
| 699 for (var i = stateData.length - 1; i > 0; i--) { |
| 700 if (typeof stateData[i] === 'number') { |
| 701 if (typeof stateData[i - 1] === 'number') { |
| 702 stateData[i] = (stateData[i] - stateData[i - 1]) / |
| 703 (absTime[i] - absTime[i - 1]) * 100; |
| 704 } else { |
| 705 stateData[i] = invalidDataText; |
| 706 } |
| 707 } |
| 708 } |
| 709 } |
| 710 |
| 711 // Remove the first sample from the time and data arrays. |
| 712 tData.shift(); |
| 713 for (var k = 0; k < stateCount; k++) { |
| 714 plots[k].data.shift(); |
| 715 } |
| 716 cpuPlots[cpu] = {plots: plots, tData: tData}; |
| 717 } |
| 718 |
| 719 headers = []; |
| 720 for (var cpu = 0; cpu < timeInStateData.length; cpu++) { |
| 721 headers[cpu] = |
| 722 'CPU ' + cpu + ' ' + loadTimeData.getString(i18nHeaderString); |
| 723 } |
| 724 |
| 725 canvases = addCanvases(headers, $(plotsDivId)); |
| 726 for (var cpu = 0; cpu < timeInStateData.length; cpu++) { |
| 727 cpuCanvases = canvases[headers[cpu]]; |
| 728 plotLineGraph(cpuCanvases['plot'], |
| 729 cpuCanvases['legend'], |
| 730 cpuPlots[cpu]['tData'], |
| 731 cpuPlots[cpu]['plots'], |
| 732 0, |
| 733 100, |
| 734 3); |
| 735 } |
| 736 } |
| 737 |
| 738 function showCpuIdleData(idleStateData, systemResumedArray) { |
| 739 showStateOccupancyData(idleStateData, |
| 740 systemResumedArray, |
| 741 'idleStateOccupancyPercentageHeader', |
| 742 null, |
| 743 'cpu-idle-plots-div'); |
| 744 } |
| 745 |
| 746 function showCpuFreqData(freqStateData, systemResumedArray) { |
| 747 showStateOccupancyData(freqStateData, |
| 748 systemResumedArray, |
| 749 'frequencyStateOccupancyPercentageHeader', |
| 750 'MHz', |
| 751 'cpu-freq-plots-div'); |
422 } | 752 } |
423 | 753 |
424 function requestBatteryChargeData() { | 754 function requestBatteryChargeData() { |
425 chrome.send('requestBatteryChargeData'); | 755 chrome.send('requestBatteryChargeData'); |
426 } | 756 } |
427 | 757 |
| 758 function requestCpuIdleData() { |
| 759 chrome.send('requestCpuIdleData'); |
| 760 } |
| 761 |
| 762 function requestCpuFreqData() { |
| 763 chrome.send('requestCpuFreqData'); |
| 764 } |
| 765 |
| 766 /** |
| 767 * Return a callback for the 'Show'/'Hide' buttons for each section of the |
| 768 * about:power page. |
| 769 * |
| 770 * @param {string} sectionId The ID of the section which is to be shown or |
| 771 * hidden. |
| 772 * @param {string} buttonId The ID of the 'Show'/'Hide' button. |
| 773 * @param {function} requestFunction The function which should be invoked on |
| 774 * 'Show' to request for data from chrome. |
| 775 * @return {function} The button callback function. |
| 776 */ |
| 777 function showHideCallback(sectionId, buttonId, requestFunction) { |
| 778 return function() { |
| 779 if ($(sectionId).hidden) { |
| 780 $(sectionId).hidden = false; |
| 781 $(buttonId).textContent = loadTimeData.getString('hideButton'); |
| 782 requestFunction(); |
| 783 } else { |
| 784 $(sectionId).hidden = true; |
| 785 $(buttonId).textContent = loadTimeData.getString('showButton'); |
| 786 } |
| 787 } |
| 788 } |
| 789 |
428 var powerUI = { | 790 var powerUI = { |
429 showBatteryChargeData: showBatteryChargeData | 791 showBatteryChargeData: showBatteryChargeData, |
| 792 showCpuIdleData: showCpuIdleData, |
| 793 showCpuFreqData: showCpuFreqData |
430 }; | 794 }; |
431 | 795 |
432 document.addEventListener('DOMContentLoaded', function() { | 796 document.addEventListener('DOMContentLoaded', function() { |
433 requestBatteryChargeData(); | 797 $('battery-charge-section').hidden = true; |
| 798 $('battery-charge-show-button').onclick = showHideCallback( |
| 799 'battery-charge-section', |
| 800 'battery-charge-show-button', |
| 801 requestBatteryChargeData); |
434 $('battery-charge-reload-button').onclick = requestBatteryChargeData; | 802 $('battery-charge-reload-button').onclick = requestBatteryChargeData; |
| 803 |
| 804 $('cpu-idle-section').hidden = true; |
| 805 $('cpu-idle-show-button').onclick = showHideCallback( |
| 806 'cpu-idle-section', 'cpu-idle-show-button', requestCpuIdleData); |
| 807 $('cpu-idle-reload-button').onclick = requestCpuIdleData; |
| 808 |
| 809 $('cpu-freq-section').hidden = true; |
| 810 $('cpu-freq-show-button').onclick = showHideCallback( |
| 811 'cpu-freq-section', 'cpu-freq-show-button', requestCpuFreqData); |
| 812 $('cpu-freq-reload-button').onclick = requestCpuFreqData; |
435 }); | 813 }); |
OLD | NEW |