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