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} plotCanvas The canvas on which the line graph is | 8 * @param {HTMLCanvasElement} canvas 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. | |
12 * @param {Array.<number>} tData The time (in seconds) in the past when the | 10 * @param {Array.<number>} tData The time (in seconds) in the past when the |
13 * corresponding data in plots was sampled. | 11 * corresponding data in plots was sampled. |
14 * @param {Array.<{data: Array.<number>, color: string}>} plots An | 12 * @param {Array.<{data: Array.<number>, color: string}>} plots An |
15 * array of plots to plot on the canvas. The field 'data' of a plot is an | 13 * array of plots to plot on the canvas. The field 'data' of a plot is an |
16 * array of samples to be plotted as a line graph with color speficied by | 14 * array of samples to be plotted as a line graph with color speficied by |
17 * the field 'color'. The elements in the 'data' array are ordered | 15 * the field 'color'. The elements in the 'data' array are ordered |
18 * corresponding to their sampling time in the argument 'tData'. Also, the | 16 * corresponding to their sampling time in the argument 'tData'. Also, the |
19 * number of elements in the 'data' array should be the same as in the time | 17 * number of elements in the 'data' array should be the same as in the time |
20 * array 'tData' above. | 18 * array 'tData' above. |
21 * @param {number} yMin Minimum bound of y-axis | 19 * @param {number} yMin Minimum bound of y-axis |
22 * @param {number} yMax Maximum bound of y-axis. | 20 * @param {number} yMax Maximum bound of y-axis. |
23 * @param {integer} yPrecision An integer value representing the number of | 21 * @param {integer} yPrecision An integer value representing the number of |
24 * digits of precision the y-axis data should be printed with. | 22 * digits of precision the y-axis data should be printed with. |
25 */ | 23 */ |
26 function plotLineGraph( | 24 function plotLineGraph(canvas, tData, plots, yMin, yMax, yPrecision) { |
27 plotCanvas, legendCanvas, tData, plots, yMin, yMax, yPrecision) { | |
28 var textFont = '12px Arial'; | 25 var textFont = '12px Arial'; |
29 var textHeight = 12; | 26 var textHeight = 12; |
30 var padding = 5; // Pixels | 27 var padding = 5; // Pixels |
31 var errorOffsetPixels = 15; | 28 var errorOffsetPixels = 15; |
32 var gridColor = '#ccc'; | 29 var gridColor = '#ccc'; |
33 var plotCtx = plotCanvas.getContext('2d'); | 30 var ctx = canvas.getContext('2d'); |
34 var size = tData.length; | 31 var size = tData.length; |
35 | 32 |
36 function drawText(ctx, text, x, y) { | 33 function drawText(text, x, y) { |
37 ctx.font = textFont; | 34 ctx.font = textFont; |
38 ctx.fillStyle = '#000'; | 35 ctx.fillStyle = '#000'; |
39 ctx.fillText(text, x, y); | 36 ctx.fillText(text, x, y); |
40 } | 37 } |
41 | 38 |
42 function printErrorText(ctx, text) { | 39 function printErrorText(text) { |
43 ctx.clearRect(0, 0, plotCanvas.width, plotCanvas.height); | 40 ctx.clearRect(0, 0, canvas.width, canvas.height); |
44 drawText(ctx, text, errorOffsetPixels, errorOffsetPixels); | 41 drawText(text, errorOffsetPixels, errorOffsetPixels); |
45 } | 42 } |
46 | 43 |
47 if (size < 2) { | 44 if (size < 2) { |
48 printErrorText(plotCtx, | 45 printErrorText(loadTimeData.getString('notEnoughDataAvailableYet')); |
49 loadTimeData.getString('notEnoughDataAvailableYet')); | |
50 return; | 46 return; |
51 } | 47 } |
52 | 48 |
53 for (var count = 0; count < plots.length; count++) { | 49 for (var count = 0; count < plots.length; count++) { |
54 if (plots[count].data.length != size) { | 50 if (plots[count].data.length != size) { |
55 throw new Error('Mismatch in time and plot data.'); | 51 throw new Error('Mismatch in time and plot data.'); |
56 } | 52 } |
57 } | 53 } |
58 | 54 |
59 function valueToString(value) { | 55 function valueToString(value) { |
60 if (Math.abs(value) < 1) { | 56 return Number(value).toPrecision(yPrecision); |
61 return Number(value).toFixed(yPrecision - 1); | |
62 } else { | |
63 return Number(value).toPrecision(yPrecision); | |
64 } | |
65 } | 57 } |
66 | 58 |
67 function getTextWidth(ctx, text) { | 59 function getTextWidth(text) { |
68 ctx.font = textFont; | 60 ctx.font = textFont; |
69 // For now, all text is drawn to the left of vertical lines, or centered. | 61 // For now, all text is drawn to the left of vertical lines, or centered. |
70 // Add a 2 pixel padding so that there is some spacing between the text | 62 // Add a 2 pixel padding so that there is some spacing between the text |
71 // and the vertical line. | 63 // and the vertical line. |
72 return Math.round(ctx.measureText(text).width) + 2; | 64 return Math.round(ctx.measureText(text).width) + 2; |
73 } | 65 } |
74 | 66 |
75 function drawHighlightText(ctx, text, x, y, color) { | 67 function drawHighlightText(text, x, y, color) { |
76 ctx.strokeStyle = '#000'; | 68 ctx.strokeStyle = '#000'; |
77 ctx.strokeRect(x, y - textHeight, getTextWidth(ctx, text), textHeight); | 69 ctx.strokeRect(x, y - textHeight, getTextWidth(text), textHeight); |
78 ctx.fillStyle = color; | 70 ctx.fillStyle = color; |
79 ctx.fillRect(x, y - textHeight, getTextWidth(ctx, text), textHeight); | 71 ctx.fillRect(x, y - textHeight, getTextWidth(text), textHeight); |
80 ctx.fillStyle = '#fff'; | 72 ctx.fillStyle = '#fff'; |
81 ctx.fillText(text, x, y); | 73 ctx.fillText(text, x, y); |
82 } | 74 } |
83 | 75 |
84 function drawLine(ctx, x1, y1, x2, y2, color) { | 76 function drawLine(x1, y1, x2, y2, color) { |
85 ctx.save(); | 77 ctx.save(); |
86 ctx.beginPath(); | 78 ctx.beginPath(); |
87 ctx.moveTo(x1, y1); | 79 ctx.moveTo(x1, y1); |
88 ctx.lineTo(x2, y2); | 80 ctx.lineTo(x2, y2); |
89 ctx.strokeStyle = color; | 81 ctx.strokeStyle = color; |
90 ctx.stroke(); | 82 ctx.stroke(); |
91 ctx.restore(); | 83 ctx.restore(); |
92 } | 84 } |
93 | 85 |
94 // The strokeRect method of the 2d context of a plotCanvas draws a bounding | 86 // The strokeRect method of the 2d context of a canvas draws a bounding |
95 // rectangle with an offset origin and greater dimensions. Hence, use this | 87 // rectangle with an offset origin and greater dimensions. Hence, use this |
96 // function to draw a rect at the desired location with desired dimensions. | 88 // function to draw a rect at the desired location with desired dimensions. |
97 function drawRect(ctx, x, y, width, height, color) { | 89 function drawRect(x, y, width, height, color) { |
98 drawLine(ctx, x, y, x + width - 1, y, color); | 90 drawLine(x, y, x + width - 1, y, color); |
99 drawLine(ctx, x, y, x, y + height - 1, color); | 91 drawLine(x, y, x, y + height - 1, color); |
100 drawLine(ctx, x, y + height - 1, x + width - 1, y + height - 1, color); | 92 drawLine(x, y + height - 1, x + width - 1, y + height - 1, color); |
101 drawLine(ctx, x + width - 1, y, x + width - 1, y + height - 1, color); | 93 drawLine(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 } | |
166 } | 94 } |
167 | 95 |
168 var yMinStr = valueToString(yMin); | 96 var yMinStr = valueToString(yMin); |
169 var yMaxStr = valueToString(yMax); | 97 var yMaxStr = valueToString(yMax); |
170 var yHalfStr = valueToString((yMax + yMin) / 2); | 98 var yHalfStr = valueToString((yMax + yMin) / 2); |
171 var yMinWidth = getTextWidth(plotCtx, yMinStr); | 99 var yMinWidth = getTextWidth(yMinStr); |
172 var yMaxWidth = getTextWidth(plotCtx, yMaxStr); | 100 var yMaxWidth = getTextWidth(yMaxStr); |
173 var yHalfWidth = getTextWidth(plotCtx, yHalfStr); | 101 var yHalfWidth = getTextWidth(yHalfStr); |
174 | 102 |
175 var xMinStr = tData[0]; | 103 var xMinStr = tData[0]; |
176 var xMaxStr = tData[size - 1]; | 104 var xMaxStr = tData[size - 1]; |
177 var xMinWidth = getTextWidth(plotCtx, xMinStr); | 105 var xMinWidth = getTextWidth(xMinStr); |
178 var xMaxWidth = getTextWidth(plotCtx, xMaxStr); | 106 var xMaxWidth = getTextWidth(xMaxStr); |
179 | 107 |
180 var xOrigin = padding + Math.max(yMinWidth, | 108 var xOrigin = padding + Math.max(yMinWidth, |
181 yMaxWidth, | 109 yMaxWidth, |
182 Math.round(xMinWidth / 2)); | 110 Math.round(xMinWidth / 2)); |
183 var yOrigin = padding + textHeight; | 111 var yOrigin = padding + textHeight; |
184 var width = plotCanvas.width - xOrigin - Math.floor(xMaxWidth / 2) - padding; | 112 var width = canvas.width - xOrigin - Math.floor(xMaxWidth / 2) - padding; |
185 if (width < size) { | 113 if (width < size) { |
186 plotCanvas.width += size - width; | 114 canvas.width += size - width; |
187 width = size; | 115 width = size; |
188 } | 116 } |
189 var height = plotCanvas.height - yOrigin - textHeight - padding; | 117 var height = canvas.height - yOrigin - textHeight - padding; |
190 | 118 |
191 function drawPlots() { | 119 function drawPlots() { |
192 // Start fresh. | 120 // Start fresh. |
193 plotCtx.clearRect(0, 0, plotCanvas.width, plotCanvas.height); | 121 ctx.clearRect(0, 0, canvas.width, canvas.height); |
194 | 122 |
195 // Draw the bounding rectangle. | 123 // Draw the bounding rectangle. |
196 drawRect(plotCtx, xOrigin, yOrigin, width, height, gridColor); | 124 drawRect(xOrigin, yOrigin, width, height, gridColor); |
197 | 125 |
198 // Draw the x and y bound values. | 126 // Draw the x and y bound values. |
199 drawText(plotCtx, yMaxStr, xOrigin - yMaxWidth, yOrigin + textHeight); | 127 drawText(yMaxStr, xOrigin - yMaxWidth, yOrigin + textHeight); |
200 drawText(plotCtx, yMinStr, xOrigin - yMinWidth, yOrigin + height); | 128 drawText(yMinStr, xOrigin - yMinWidth, yOrigin + height); |
201 drawText(plotCtx, | 129 drawText(xMinStr, xOrigin - xMinWidth / 2, yOrigin + height + textHeight); |
202 xMinStr, | 130 drawText(xMaxStr, |
203 xOrigin - xMinWidth / 2, | |
204 yOrigin + height + textHeight); | |
205 drawText(plotCtx, | |
206 xMaxStr, | |
207 xOrigin + width - xMaxWidth / 2, | 131 xOrigin + width - xMaxWidth / 2, |
208 yOrigin + height + textHeight); | 132 yOrigin + height + textHeight); |
209 | 133 |
210 // Draw y-level (horizontal) lines. | 134 // Draw y-level (horizontal) lines. |
211 drawLine(plotCtx, | 135 drawLine(xOrigin + 1, yOrigin + height / 4, |
212 xOrigin + 1, yOrigin + height / 4, | |
213 xOrigin + width - 2, yOrigin + height / 4, | 136 xOrigin + width - 2, yOrigin + height / 4, |
214 gridColor); | 137 gridColor); |
215 drawLine(plotCtx, | 138 drawLine(xOrigin + 1, yOrigin + height / 2, |
216 xOrigin + 1, yOrigin + height / 2, | |
217 xOrigin + width - 2, yOrigin + height / 2, gridColor); | 139 xOrigin + width - 2, yOrigin + height / 2, gridColor); |
218 drawLine(plotCtx, | 140 drawLine(xOrigin + 1, yOrigin + 3 * height / 4, |
219 xOrigin + 1, yOrigin + 3 * height / 4, | |
220 xOrigin + width - 2, yOrigin + 3 * height / 4, | 141 xOrigin + width - 2, yOrigin + 3 * height / 4, |
221 gridColor); | 142 gridColor); |
222 | 143 |
223 // Draw half-level value. | 144 // Draw half-level value. |
224 drawText(plotCtx, | 145 drawText(yHalfStr, |
225 yHalfStr, | |
226 xOrigin - yHalfWidth, | 146 xOrigin - yHalfWidth, |
227 yOrigin + height / 2 + textHeight / 2); | 147 yOrigin + height / 2 + textHeight / 2); |
228 | 148 |
229 // Draw the plots. | 149 // Draw the plots. |
230 var yValRange = yMax - yMin; | 150 var yValRange = yMax - yMin; |
231 for (var count = 0; count < plots.length; count++) { | 151 for (var count = 0; count < plots.length; count++) { |
232 var plot = plots[count]; | 152 var plot = plots[count]; |
233 var yData = plot.data; | 153 var yData = plot.data; |
234 plotCtx.strokeStyle = plot.color; | 154 ctx.strokeStyle = plot.color; |
235 plotCtx.beginPath(); | 155 ctx.beginPath(); |
236 var beginPath = true; | 156 var beginPath = true; |
237 for (var i = 0; i < size; i++) { | 157 for (var i = 0; i < size; i++) { |
238 var val = yData[i]; | 158 var val = yData[i]; |
239 if (typeof val === 'string') { | 159 if (typeof val === 'string') { |
240 // Stroke the plot drawn so far and begin a fresh plot. | 160 // Stroke the plot drawn so far and begin a fresh plot. |
241 plotCtx.stroke(); | 161 ctx.stroke(); |
242 plotCtx.beginPath(); | 162 ctx.beginPath(); |
243 beginPath = true; | 163 beginPath = true; |
244 continue; | 164 continue; |
245 } | 165 } |
246 var xPos = xOrigin + Math.floor(i / (size - 1) * (width - 1)); | 166 var xPos = xOrigin + Math.floor(i / (size - 1) * (width - 1)); |
247 var yPos = yOrigin + height - 1 - | 167 var yPos = yOrigin + height - 1 - |
248 Math.round((val - yMin) / yValRange * (height - 1)); | 168 Math.round((val - yMin) / yValRange * (height - 1)); |
249 if (beginPath) { | 169 if (beginPath) { |
250 plotCtx.moveTo(xPos, yPos); | 170 ctx.moveTo(xPos, yPos); |
251 // A simple move to does not print anything. Hence, draw a little | 171 // A simple move to does not print anything. Hence, draw a little |
252 // square here to mark a beginning. | 172 // square here to mark a beginning. |
253 plotCtx.fillStyle = '#000'; | 173 ctx.fillStyle = '#000'; |
254 plotCtx.fillRect(xPos - 1, yPos - 1, 2, 2); | 174 ctx.fillRect(xPos - 1, yPos - 1, 2, 2); |
255 beginPath = false; | 175 beginPath = false; |
256 } else { | 176 } else { |
257 plotCtx.lineTo(xPos, yPos); | 177 ctx.lineTo(xPos, yPos); |
258 if (i === size - 1 || typeof yData[i + 1] === 'string') { | 178 if (i === size - 1 || typeof yData[i + 1] === 'string') { |
259 // Draw a little square to mark an end to go with the start | 179 // Draw a little square to mark an end to go with the start |
260 // markers from above. | 180 // markers from above. |
261 plotCtx.fillStyle = '#000'; | 181 ctx.fillStyle = '#000'; |
262 plotCtx.fillRect(xPos - 1, yPos - 1, 2, 2); | 182 ctx.fillRect(xPos - 1, yPos - 1, 2, 2); |
263 } | 183 } |
264 } | 184 } |
265 } | 185 } |
266 plotCtx.stroke(); | 186 ctx.stroke(); |
267 } | 187 } |
268 | 188 |
269 // Paint the missing time intervals with |gridColor|. | 189 // Paint the missing time intervals with |gridColor|. |
270 // Pick one of the plots to look for missing time intervals. | 190 // 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 } | |
279 var inMissingInterval = false; | 191 var inMissingInterval = false; |
280 var intervalStart; | 192 var intervalStart; |
281 for (var i = 0; i < size; i++) { | 193 for (var i = 0; i < size; i++) { |
282 if (typeof plots[0].data[i] === 'string') { | 194 if (typeof plots[0].data[i] === 'string') { |
283 if (!inMissingInterval) { | 195 if (!inMissingInterval) { |
284 inMissingInterval = true; | 196 inMissingInterval = true; |
285 // The missing interval should actually start from the previous | 197 // The missing interval should actually start from the previous |
286 // sample. | 198 // sample. |
287 intervalStart = Math.max(i - 1, 0); | 199 intervalStart = Math.max(i - 1, 0); |
288 } | 200 } |
289 | |
290 if (i == size - 1) { | |
291 // If this is the last sample, just draw missing rect. | |
292 drawMissingRect(intervalStart, i); | |
293 } | |
294 } else if (inMissingInterval) { | 201 } else if (inMissingInterval) { |
295 inMissingInterval = false; | 202 inMissingInterval = false; |
296 drawMissingRect(intervalStart, i); | 203 var xLeft = xOrigin + |
| 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); |
297 } | 210 } |
298 } | 211 } |
299 } | 212 } |
300 | 213 |
301 function drawTimeGuide(tDataIndex) { | 214 function drawTimeGuide(tDataIndex) { |
302 var x = xOrigin + tDataIndex / (size - 1) * (width - 1); | 215 var x = xOrigin + tDataIndex / (size - 1) * (width - 1); |
303 drawLine(plotCtx, x, yOrigin, x, yOrigin + height - 1, '#000'); | 216 drawLine(x, yOrigin, x, yOrigin + height - 1, '#000'); |
304 drawText(plotCtx, | 217 drawText(tData[tDataIndex], |
305 tData[tDataIndex], | 218 x - getTextWidth(tData[tDataIndex]) / 2, |
306 x - getTextWidth(plotCtx, tData[tDataIndex]) / 2, | |
307 yOrigin - 2); | 219 yOrigin - 2); |
308 | 220 |
309 for (var count = 0; count < plots.length; count++) { | 221 for (var count = 0; count < plots.length; count++) { |
310 var yData = plots[count].data; | 222 var yData = plots[count].data; |
311 | 223 |
312 // Draw small black square on the plot where the time guide intersects | 224 // Draw small black square on the plot where the time guide intersects |
313 // it. | 225 // it. |
314 var val = yData[tDataIndex]; | 226 var val = yData[tDataIndex]; |
315 var yPos, valStr; | 227 var yPos, valStr; |
316 if (typeof val === 'string') { | 228 if (typeof val === 'string') { |
317 yPos = yOrigin + Math.round(height / 2); | 229 yPos = yOrigin + Math.round(height / 2); |
318 valStr = val; | 230 valStr = val; |
319 } else { | 231 } else { |
320 yPos = yOrigin + height - 1 - | 232 yPos = yOrigin + height - 1 - |
321 Math.round((val - yMin) / (yMax - yMin) * (height - 1)); | 233 Math.round((val - yMin) / (yMax - yMin) * (height - 1)); |
322 valStr = valueToString(val); | 234 valStr = valueToString(val); |
323 } | 235 } |
324 plotCtx.fillStyle = '#000'; | 236 ctx.fillStyle = '#000'; |
325 plotCtx.fillRect(x - 2, yPos - 2, 4, 4); | 237 ctx.fillRect(x - 2, yPos - 2, 4, 4); |
326 | 238 |
327 // Draw the val to right of the intersection. | 239 // Draw the val to right of the intersection. |
328 var yLoc; | 240 var yLoc; |
329 if (yPos - textHeight / 2 < yOrigin) { | 241 if (yPos - textHeight / 2 < yOrigin) { |
330 yLoc = yOrigin + textHeight; | 242 yLoc = yOrigin + textHeight; |
331 } else if (yPos + textHeight / 2 >= yPos + height) { | 243 } else if (yPos + textHeight / 2 >= yPos + height) { |
332 yLoc = yOrigin + height - 1; | 244 yLoc = yOrigin + height - 1; |
333 } else { | 245 } else { |
334 yLoc = yPos + textHeight / 2; | 246 yLoc = yPos + textHeight / 2; |
335 } | 247 } |
336 drawHighlightText(plotCtx, valStr, x + 5, yLoc, plots[count].color); | 248 drawHighlightText(valStr, x + 5, yLoc, plots[count].color); |
337 } | 249 } |
338 } | 250 } |
339 | 251 |
340 function onMouseOverOrMove(event) { | 252 function onMouseOverOrMove(event) { |
341 drawPlots(); | 253 drawPlots(); |
342 | 254 |
343 var boundingRect = plotCanvas.getBoundingClientRect(); | 255 var boundingRect = canvas.getBoundingClientRect(); |
344 var x = Math.round(event.clientX - boundingRect.left); | 256 var x = event.clientX - boundingRect.left; |
345 var y = Math.round(event.clientY - boundingRect.top); | 257 var y = event.clientY - boundingRect.top; |
346 if (x < xOrigin || x >= xOrigin + width || | 258 if (x < xOrigin || x >= xOrigin + width || |
347 y < yOrigin || y >= yOrigin + height) { | 259 y < yOrigin || y >= yOrigin + height) { |
348 return; | 260 return; |
349 } | 261 } |
350 | 262 |
351 if (width == size) { | 263 if (width == size) { |
352 drawTimeGuide(x - xOrigin); | 264 drawTimeGuide(x - xOrigin); |
353 } else { | 265 } else { |
354 drawTimeGuide(Math.round((x - xOrigin) / (width - 1) * (size - 1))); | 266 drawTimeGuide(Math.round((x - xOrigin) / (width - 1) * (size - 1))); |
355 } | 267 } |
356 } | 268 } |
357 | 269 |
358 function onMouseOut(event) { | 270 function onMouseOut(event) { |
359 drawPlots(); | 271 drawPlots(); |
360 } | 272 } |
361 | 273 |
362 drawLegend(); | |
363 drawPlots(); | 274 drawPlots(); |
364 plotCanvas.addEventListener('mouseover', onMouseOverOrMove); | 275 canvas.addEventListener('mouseover', onMouseOverOrMove); |
365 plotCanvas.addEventListener('mousemove', onMouseOverOrMove); | 276 canvas.addEventListener('mousemove', onMouseOverOrMove); |
366 plotCanvas.addEventListener('mouseout', onMouseOut); | 277 canvas.addEventListener('mouseout', onMouseOut); |
367 } | 278 } |
368 | 279 |
369 var sleepSampleInterval = 30 * 1000; // in milliseconds. | 280 var sleepSampleInterval = 30 * 1000; // in milliseconds. |
370 var sleepText = loadTimeData.getString('systemSuspended'); | 281 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 } | |
421 | 282 |
422 /** | 283 /** |
423 * Add samples in |sampleArray| to individual plots in |plots|. If the system | 284 * Add samples in |sampleArray| to individual plots in |plots|. If the system |
424 * resumed from a sleep/suspend, then "suspended" sleep samples are added to | 285 * resumed from a sleep/suspend, then "suspended" sleep samples are added to |
425 * the plot for the sleep duration. | 286 * the plot for the sleep duration. |
426 * | 287 * |
427 * @param {Array.<{data: Array.<number>, color: string}>} plots An | 288 * @param {Array.<{data: Array.<number>, color: string}>} plots An |
428 * array of plots to plot on the canvas. The field 'data' of a plot is an | 289 * array of plots to plot on the canvas. The field 'data' of a plot is an |
429 * array of samples to be plotted as a line graph with color speficied by | 290 * array of samples to be plotted as a line graph with color speficied by |
430 * the field 'color'. The elements in the 'data' array are ordered | 291 * the field 'color'. The elements in the 'data' array are ordered |
431 * corresponding to their sampling time in the argument 'tData'. Also, the | 292 * corresponding to their sampling time in the argument 'tData'. Also, the |
432 * number of elements in the 'data' array should be the same as in the time | 293 * number of elements in the 'data' array should be the same as in the time |
433 * array 'tData' below. | 294 * array 'tData' below. |
434 * @param {Array.<number>} tData The time (in seconds) in the past when the | 295 * @param {Array.<number>} tData The time (in seconds) in the past when the |
435 * corresponding data in plots was sampled. | 296 * corresponding data in plots was sampled. |
436 * @param {Array.<number>} sampleArray The array of samples wherein each | 297 * @param {Array.<number>} sampleArray The array of samples wherein each |
437 * element corresponds to the individual plot in |plots|. | 298 * element corresponds to the individual plot in |plots|. |
438 * @param {number} sampleTime Time in milliseconds since the epoch when the | 299 * @param {number} sampleTime Time in milliseconds since the epoch when the |
439 * samples in |sampleArray| were captured. | 300 * samples in |sampleArray| were captured. |
440 * @param {number} previousSampleTime Time in milliseconds since the epoch | 301 * @param {number} previousSampleTime Time in milliseconds since the epoch |
441 * when the sample prior to the current sample was captured. | 302 * when the sample prior to the current sample was captured. |
442 * @param {Array.<{time: number, sleepDuration: number}>} systemResumedArray An | 303 * @param {Array.<{time: number, sleepDuration: number}>} systemResumedArray An |
443 * array objects corresponding to system resume events. The 'time' field is | 304 * array objects corresponding to system resume events. The 'time' field is |
444 * for the time in milliseconds since the epoch when the system resumed. The | 305 * for the time in milliseconds since the epoch when the system resumed. The |
445 * 'sleepDuration' field is for the time in milliseconds the system spent | 306 * 'sleepDuration' field is for the time in milliseconds the system spent |
446 * in sleep/suspend state. | 307 * in sleep/suspend state. |
447 */ | 308 */ |
448 function addTimeDataSample(plots, tData, absTime, sampleArray, | 309 function addTimeDataSample(plots, tData, sampleArray, |
449 sampleTime, previousSampleTime, | 310 sampleTime, previousSampleTime, |
450 systemResumedArray) { | 311 systemResumedArray) { |
451 for (var i = 0; i < plots.length; i++) { | 312 for (var i = 0; i < plots.length; i++) { |
452 if (plots[i].data.length != tData.length) { | 313 if (plots[i].data.length != tData.length) { |
453 throw new Error('Mismatch in time and plot data.'); | 314 throw new Error('Mismatch in time and plot data.'); |
454 } | 315 } |
455 } | 316 } |
456 | 317 |
457 var time; | 318 var time; |
458 if (tData.length == 0) { | 319 if (tData.length == 0) { |
459 time = new Date(sampleTime); | 320 time = new Date(sampleTime); |
460 absTime[0] = sampleTime; | |
461 tData[0] = time.toLocaleTimeString(); | 321 tData[0] = time.toLocaleTimeString(); |
462 for (var i = 0; i < plots.length; i++) { | 322 for (var i = 0; i < plots.length; i++) { |
463 plots[i].data[0] = sampleArray[i]; | 323 plots[i].data[0] = sampleArray[i]; |
464 } | 324 } |
465 return; | 325 return; |
466 } | 326 } |
467 | 327 |
468 for (var i = 0; i < systemResumedArray.length; i++) { | 328 for (var i = 0; i < systemResumedArray.length; i++) { |
469 var resumeTime = systemResumedArray[i].time; | 329 var resumeTime = systemResumedArray[i].time; |
470 var sleepDuration = systemResumedArray[i].sleepDuration; | 330 var sleepDuration = systemResumedArray[i].sleepDuration; |
471 var sleepStartTime = resumeTime - sleepDuration; | 331 var sleepStartTime = resumeTime - sleepDuration; |
472 if (resumeTime < sampleTime) { | 332 if (resumeTime < sampleTime && sleepStartTime > previousSampleTime) { |
473 if (sleepStartTime < previousSampleTime) { | |
474 // This can happen if pending callbacks were handled before actually | |
475 // suspending. | |
476 sleepStartTime = previousSampleTime + 1000; | |
477 } | |
478 // Add sleep samples for every |sleepSampleInterval|. | 333 // Add sleep samples for every |sleepSampleInterval|. |
479 var sleepSampleTime = sleepStartTime; | 334 var sleepSampleTime = sleepStartTime; |
480 while (sleepSampleTime < resumeTime) { | 335 while (sleepSampleTime < resumeTime) { |
481 time = new Date(sleepSampleTime); | 336 time = new Date(sleepSampleTime); |
482 absTime.push(sleepSampleTime); | |
483 tData.push(time.toLocaleTimeString()); | 337 tData.push(time.toLocaleTimeString()); |
484 for (var j = 0; j < plots.length; j++) { | 338 for (var j = 0; j < plots.length; j++) { |
485 plots[j].data.push(sleepText); | 339 plots[j].data.push(sleepText); |
486 } | 340 } |
487 sleepSampleTime += sleepSampleInterval; | 341 sleepSampleTime += sleepSampleInterval; |
488 } | 342 } |
489 } | 343 } |
490 } | 344 } |
491 | 345 |
492 time = new Date(sampleTime); | 346 time = new Date(sampleTime); |
493 absTime.push(sampleTime); | |
494 tData.push(time.toLocaleTimeString()); | 347 tData.push(time.toLocaleTimeString()); |
495 for (var i = 0; i < plots.length; i++) { | 348 for (var i = 0; i < plots.length; i++) { |
496 plots[i].data.push(sampleArray[i]); | 349 plots[i].data.push(sampleArray[i]); |
497 } | 350 } |
498 } | 351 } |
499 | 352 |
500 /** | 353 /** |
501 * Display the battery charge vs time on a line graph. | 354 * Display the battery charge vs time on a line graph. |
502 * | 355 * |
503 * @param {Array.<{time: number, | 356 * @param {Array.<{time: number, |
504 * batteryPercent: number, | 357 * batteryPercent: number, |
505 * batteryDischargeRate: number, | 358 * batteryDischargeRate: number, |
506 * externalPower: number}>} powerSupplyArray An array of objects | 359 * externalPower: number}>} powerSupplyArray An array of objects |
507 * with fields representing the battery charge, time when the charge | 360 * with fields representing the battery charge, time when the charge |
508 * measurement was taken, and whether there was external power connected at | 361 * measurement was taken, and whether there was external power connected at |
509 * that time. | 362 * that time. |
510 * @param {Array.<{time: ?, sleepDuration: ?}>} systemResumedArray An array | 363 * @param {Array.<{time: ?, sleepDuration: ?}>} systemResumedArray An array |
511 * objects with fields 'time' and 'sleepDuration'. Each object corresponds | 364 * objects with fields 'time' and 'sleepDuration'. Each object corresponds |
512 * to a system resume event. The 'time' field is for the time in | 365 * to a system resume event. The 'time' field is for the time in |
513 * milliseconds since the epoch when the system resumed. The 'sleepDuration' | 366 * milliseconds since the epoch when the system resumed. The 'sleepDuration' |
514 * field is for the time in milliseconds the system spent in sleep/suspend | 367 * field is for the time in milliseconds the system spent in sleep/suspend |
515 * state. | 368 * state. |
516 */ | 369 */ |
517 function showBatteryChargeData(powerSupplyArray, systemResumedArray) { | 370 function showBatteryChargeData(powerSupplyArray, systemResumedArray) { |
518 var chargeTimeData = []; | 371 var chargeTimeData = []; |
519 var chargeAbsTime = []; | |
520 var chargePlot = [ | 372 var chargePlot = [ |
521 { | 373 { |
522 name: loadTimeData.getString('batteryChargePercentageHeader'), | |
523 color: '#0000FF', | 374 color: '#0000FF', |
524 data: [] | 375 data: [] |
525 } | 376 } |
526 ]; | 377 ]; |
527 var dischargeRateTimeData = []; | 378 var dischargeRateTimeData = []; |
528 var dischargeRateAbsTime = []; | |
529 var dischargeRatePlot = [ | 379 var dischargeRatePlot = [ |
530 { | 380 { |
531 name: loadTimeData.getString('dischargeRateLegendText'), | |
532 color: '#FF0000', | 381 color: '#FF0000', |
533 data: [] | 382 data: [] |
534 } | 383 } |
535 ]; | 384 ]; |
536 var minDischargeRate = 1000; // A high unrealistic number to begin with. | 385 var minDischargeRate = 1000; // A high unrealistic number to begin with. |
537 var maxDischargeRate = -1000; // A low unrealistic number to begin with. | 386 var maxDischargeRate = -1000; // A low unrealistic number to begin with. |
538 for (var i = 0; i < powerSupplyArray.length; i++) { | 387 for (var i = 0; i < powerSupplyArray.length; i++) { |
539 var j = Math.max(i - 1, 0); | 388 var j = Math.max(i - 1, 0); |
540 | 389 |
541 addTimeDataSample(chargePlot, | 390 addTimeDataSample(chargePlot, chargeTimeData, |
542 chargeTimeData, | |
543 chargeAbsTime, | |
544 [powerSupplyArray[i].batteryPercent], | 391 [powerSupplyArray[i].batteryPercent], |
545 powerSupplyArray[i].time, | 392 powerSupplyArray[i].time, |
546 powerSupplyArray[j].time, | 393 powerSupplyArray[j].time, |
547 systemResumedArray); | 394 systemResumedArray); |
548 | 395 |
549 var dischargeRate = powerSupplyArray[i].batteryDischargeRate; | 396 var dischargeRate = powerSupplyArray[i].batteryDischargeRate; |
550 minDischargeRate = Math.min(dischargeRate, minDischargeRate); | 397 minDischargeRate = Math.min(dischargeRate, minDischargeRate); |
551 maxDischargeRate = Math.max(dischargeRate, maxDischargeRate); | 398 maxDischargeRate = Math.max(dischargeRate, maxDischargeRate); |
552 addTimeDataSample(dischargeRatePlot, | 399 addTimeDataSample(dischargeRatePlot, |
553 dischargeRateTimeData, | 400 dischargeRateTimeData, |
554 dischargeRateAbsTime, | |
555 [dischargeRate], | 401 [dischargeRate], |
556 powerSupplyArray[i].time, | 402 powerSupplyArray[i].time, |
557 powerSupplyArray[j].time, | 403 powerSupplyArray[j].time, |
558 systemResumedArray); | 404 systemResumedArray); |
559 } | 405 } |
560 if (minDischargeRate == maxDischargeRate) { | 406 if (minDischargeRate == maxDischargeRate) { |
561 // This means that all the samples had the same value. Hence, offset the | 407 // This means that all the samples had the same value. Hence, offset the |
562 // extremes by a bit so that the plot looks good. | 408 // extremes by a bit so that the plot looks good. |
563 minDischargeRate -= 1; | 409 minDischargeRate -= 1; |
564 maxDischargeRate += 1; | 410 maxDischargeRate += 1; |
565 } | 411 } |
566 | 412 |
567 plotsDiv = $('battery-charge-plots-div'); | 413 var chargeCanvas = $('battery-charge-percentage-canvas'); |
568 | 414 var dischargeRateCanvas = $('battery-discharge-rate-canvas'); |
569 canvases = addCanvases( | 415 plotLineGraph(chargeCanvas, chargeTimeData, chargePlot, 0.00, 100.00, 3); |
570 [loadTimeData.getString('batteryChargePercentageHeader'), | 416 plotLineGraph(dischargeRateCanvas, |
571 loadTimeData.getString('batteryDischargeRateHeader')], | 417 dischargeRateTimeData, |
572 plotsDiv); | 418 dischargeRatePlot, |
573 | 419 minDischargeRate, |
574 batteryChargeCanvases = canvases[ | 420 maxDischargeRate, |
575 loadTimeData.getString('batteryChargePercentageHeader')]; | 421 3); |
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'); | |
752 } | 422 } |
753 | 423 |
754 function requestBatteryChargeData() { | 424 function requestBatteryChargeData() { |
755 chrome.send('requestBatteryChargeData'); | 425 chrome.send('requestBatteryChargeData'); |
756 } | 426 } |
757 | 427 |
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 | |
790 var powerUI = { | 428 var powerUI = { |
791 showBatteryChargeData: showBatteryChargeData, | 429 showBatteryChargeData: showBatteryChargeData |
792 showCpuIdleData: showCpuIdleData, | |
793 showCpuFreqData: showCpuFreqData | |
794 }; | 430 }; |
795 | 431 |
796 document.addEventListener('DOMContentLoaded', function() { | 432 document.addEventListener('DOMContentLoaded', function() { |
797 $('battery-charge-section').hidden = true; | 433 requestBatteryChargeData(); |
798 $('battery-charge-show-button').onclick = showHideCallback( | |
799 'battery-charge-section', | |
800 'battery-charge-show-button', | |
801 requestBatteryChargeData); | |
802 $('battery-charge-reload-button').onclick = requestBatteryChargeData; | 434 $('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; | |
813 }); | 435 }); |
OLD | NEW |