OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 /** |
| 6 * Plot a line graph of data versus time on a HTML canvas element. |
| 7 * |
| 8 * @param {HTMLCanvasElement} canvas The canvas on which the line graph is |
| 9 * drawn. |
| 10 * @param {Array.<number>} tData The time (in seconds) in the past when the |
| 11 * corresponding data in plots was sampled. |
| 12 * @param {Array.<Object>} plots An array of objects with fields 'data' and |
| 13 * 'color'. The field 'data' is an array of samples to be plotted as a |
| 14 * line graph with 'color'. The elements in the 'data' array are ordered |
| 15 * corresponding to their sampling time in the argument 'tData'. Also, |
| 16 * the number of elements in the data array should be the same as in the |
| 17 * time array 'tData' above. |
| 18 * @param {number} yMin Minimum bound of y-axis |
| 19 * @param {number} yMax Maximum bound of y-axis. |
| 20 * @param {integer} yPrecision An integer value representing the number of |
| 21 * digits of precision the y-axis data should be printed with. |
| 22 */ |
| 23 function plotLineGraph(canvas, tData, plots, yMin, yMax, yPrecision) { |
| 24 var textFont = '12px Arial'; |
| 25 var textHeight = 12; |
| 26 var padding = 5; // Pixels |
| 27 var errorOffsetPixels = 15; |
| 28 var gridColor = '#ccc'; |
| 29 var ctx = canvas.getContext('2d'); |
| 30 var size = tData.length; |
| 31 |
| 32 function drawText(text, x, y) { |
| 33 ctx.font = textFont; |
| 34 ctx.fillStyle = '#000'; |
| 35 ctx.fillText(text, x, y); |
| 36 } |
| 37 |
| 38 function printErrorText(text) { |
| 39 ctx.clearRect(0, 0, canvas.width, canvas.height); |
| 40 drawText(text, errorOffsetPixels, errorOffsetPixels); |
| 41 } |
| 42 |
| 43 if (size < 2) { |
| 44 printErrorText(loadTimeData.getString('notEnoughDataAvailableYet')); |
| 45 return; |
| 46 } |
| 47 |
| 48 for (var count = 0; count < plots.length; count++) { |
| 49 if (plots[count].data.length != size) { |
| 50 printErrorText(loadTimeData.getString('timeAndPlotDataMismatch')); |
| 51 return; |
| 52 } |
| 53 } |
| 54 |
| 55 function valueToString(value) { |
| 56 return Number(value).toPrecision(yPrecision); |
| 57 } |
| 58 |
| 59 function getTextWidth(text) { |
| 60 ctx.font = textFont; |
| 61 // 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 |
| 63 // and the vertical line. |
| 64 return Math.round(ctx.measureText(text).width) + 2; |
| 65 } |
| 66 |
| 67 function drawHighlightText(text, x, y, color) { |
| 68 ctx.strokeStyle = '#000'; |
| 69 ctx.strokeRect(x, y - textHeight, getTextWidth(text), textHeight); |
| 70 ctx.fillStyle = color; |
| 71 ctx.fillRect(x, y - textHeight, getTextWidth(text), textHeight); |
| 72 ctx.fillStyle = '#fff'; |
| 73 ctx.fillText(text, x, y); |
| 74 } |
| 75 |
| 76 function drawLine(x1, y1, x2, y2, color) { |
| 77 ctx.save(); |
| 78 ctx.beginPath(); |
| 79 ctx.moveTo(x1, y1); |
| 80 ctx.lineTo(x2, y2); |
| 81 ctx.strokeStyle = color; |
| 82 ctx.stroke(); |
| 83 ctx.restore(); |
| 84 } |
| 85 |
| 86 // The strokeRect method of the 2d context of a canvas draws a bounding |
| 87 // rectangle with an offset origin and greater dimensions. Hence, use this |
| 88 // function to draw a rect at the desired location with desired dimensions. |
| 89 function drawRect(x, y, width, height, color) { |
| 90 drawLine(x, y, x + width - 1, y, color); |
| 91 drawLine(x, y, x, y + height - 1, color); |
| 92 drawLine(x, y + height - 1, x + width - 1, y + height - 1, color); |
| 93 drawLine(x + width - 1, y, x + width - 1, y + height - 1, color); |
| 94 } |
| 95 |
| 96 var yMinStr = valueToString(yMin); |
| 97 var yMaxStr = valueToString(yMax); |
| 98 var yHalfStr = valueToString((yMax + yMin) / 2); |
| 99 var yMinWidth = getTextWidth(yMinStr); |
| 100 var yMaxWidth = getTextWidth(yMaxStr); |
| 101 var yHalfWidth = getTextWidth(yHalfStr); |
| 102 |
| 103 var xMinStr = tData[0]; |
| 104 var xMaxStr = tData[size - 1]; |
| 105 var xMinWidth = getTextWidth(xMinStr); |
| 106 var xMaxWidth = getTextWidth(xMaxStr); |
| 107 |
| 108 var xOrigin = padding + Math.max(yMinWidth, |
| 109 yMaxWidth, |
| 110 Math.round(xMinWidth / 2)); |
| 111 var yOrigin = padding + textHeight; |
| 112 var width = canvas.width - xOrigin - Math.floor(xMaxWidth / 2) - padding; |
| 113 if (width < size) { |
| 114 canvas.width += size - width; |
| 115 width = size; |
| 116 } |
| 117 var height = canvas.height - yOrigin - textHeight - padding; |
| 118 |
| 119 function drawPlots() { |
| 120 // Start fresh. |
| 121 ctx.clearRect(0, 0, canvas.width, canvas.height); |
| 122 |
| 123 // Draw the bounding rectangle. |
| 124 drawRect(xOrigin, yOrigin, width, height, gridColor); |
| 125 |
| 126 // Draw the x and y bound values. |
| 127 drawText(yMaxStr, xOrigin - yMaxWidth, yOrigin + textHeight); |
| 128 drawText(yMinStr, xOrigin - yMinWidth, yOrigin + height); |
| 129 drawText(xMinStr, xOrigin - xMinWidth / 2, yOrigin + height + textHeight); |
| 130 drawText(xMaxStr, |
| 131 xOrigin + width - xMaxWidth / 2, |
| 132 yOrigin + height + textHeight); |
| 133 |
| 134 // Draw y-level (horizontal) lines. |
| 135 drawLine(xOrigin, yOrigin + height / 4, |
| 136 xOrigin + width - 1, yOrigin + height / 4, |
| 137 gridColor); |
| 138 drawLine(xOrigin, yOrigin + height / 2, |
| 139 xOrigin + width - 1, yOrigin + height / 2, gridColor); |
| 140 drawLine(xOrigin, yOrigin + 3 * height / 4, |
| 141 xOrigin + width - 1, yOrigin + 3 * height / 4, |
| 142 gridColor); |
| 143 |
| 144 // Draw half-level value. |
| 145 drawText(yHalfStr, |
| 146 xOrigin - yHalfWidth, |
| 147 yOrigin + height / 2 + textHeight / 2); |
| 148 |
| 149 // Draw the plots. |
| 150 var yValRange = yMax - yMin; |
| 151 for (var count = 0; count < plots.length; count++) { |
| 152 var plot = plots[count]; |
| 153 var yData = plot.data; |
| 154 ctx.strokeStyle = plot.color; |
| 155 ctx.beginPath(); |
| 156 for (var i = 0; i < size; i++) { |
| 157 var xPos = xOrigin + Math.floor(i / (size - 1) * (width - 1)); |
| 158 var val = yData[i]; |
| 159 var yPos = yOrigin + height - 1 - |
| 160 Math.round((val - yMin) / yValRange * (height - 1)); |
| 161 if (i == 0) { |
| 162 ctx.moveTo(xPos, yPos); |
| 163 } else { |
| 164 ctx.lineTo(xPos, yPos); |
| 165 } |
| 166 } |
| 167 ctx.stroke(); |
| 168 } |
| 169 } |
| 170 |
| 171 function drawTimeGuide(tDataIndex) { |
| 172 var x = xOrigin + tDataIndex / (size - 1) * (width - 1); |
| 173 drawLine(x, yOrigin, x, yOrigin + height - 1, '#000'); |
| 174 drawText(tData[tDataIndex], |
| 175 x - getTextWidth(tData[tDataIndex]) / 2, |
| 176 yOrigin - 2); |
| 177 |
| 178 for (var count = 0; count < plots.length; count++) { |
| 179 var yData = plots[count].data; |
| 180 |
| 181 // Draw small black square on the plot where the time guide intersects |
| 182 // it. |
| 183 var val = yData[tDataIndex]; |
| 184 var yPos = yOrigin + height - 1 - |
| 185 Math.round((val - yMin) / (yMax - yMin) * (height - 1)); |
| 186 ctx.fillStyle = '#000'; |
| 187 ctx.fillRect(x - 2, yPos - 2, 4, 4); |
| 188 |
| 189 // Draw the val to right of the intersection. |
| 190 var valStr = valueToString(val); |
| 191 var yLoc; |
| 192 if (yPos - textHeight / 2 < yOrigin) { |
| 193 yLoc = yOrigin + textHeight; |
| 194 } else if (yPos + textHeight / 2 >= yPos + height) { |
| 195 yLoc = yOrigin + height - 1; |
| 196 } else { |
| 197 yLoc = yPos + textHeight / 2; |
| 198 } |
| 199 drawHighlightText(valStr, x + 5, yLoc, plots[count].color); |
| 200 } |
| 201 } |
| 202 |
| 203 function onMouseOverOrMove(event) { |
| 204 drawPlots(); |
| 205 |
| 206 var boundingRect = canvas.getBoundingClientRect(); |
| 207 var x = event.clientX - boundingRect.left; |
| 208 var y = event.clientY - boundingRect.top; |
| 209 if (x < xOrigin || x >= xOrigin + width || |
| 210 y < yOrigin || y >= yOrigin + height) { |
| 211 return; |
| 212 } |
| 213 |
| 214 if (width == size) { |
| 215 drawTimeGuide(x - xOrigin); |
| 216 } else { |
| 217 drawTimeGuide(Math.round((x - xOrigin) / (width - 1) * (size - 1))); |
| 218 } |
| 219 } |
| 220 |
| 221 function onMouseOut(event) { |
| 222 drawPlots(); |
| 223 } |
| 224 |
| 225 drawPlots(); |
| 226 canvas.addEventListener('mouseover', onMouseOverOrMove); |
| 227 canvas.addEventListener('mousemove', onMouseOverOrMove); |
| 228 canvas.addEventListener('mouseout', onMouseOut); |
| 229 } |
| 230 |
| 231 /** |
| 232 * Display the battery charge vs time on a line graph. |
| 233 * |
| 234 * @param {Array.<Object>} powerSupplyArray An array of objects with fields |
| 235 * representing the battery charge, time when the charge measurement was |
| 236 * taken, and whether there was external power connected at that time. |
| 237 */ |
| 238 function showBatteryChargeData(powerSupplyArray) { |
| 239 var canvas = $('battery-charge-canvas'); |
| 240 var tData = []; |
| 241 var plot = []; |
| 242 for (var i = 0; i < powerSupplyArray.length; i++) { |
| 243 var time = new Date(powerSupplyArray[i].time); |
| 244 tData[i] = time.toLocaleTimeString(); |
| 245 plot[i] = powerSupplyArray[i].battery_percent; |
| 246 } |
| 247 |
| 248 var plots = [ |
| 249 { |
| 250 color: '#0000FF', |
| 251 data: plot |
| 252 } |
| 253 ]; |
| 254 plotLineGraph(canvas, tData, plots, 0.00, 100.00, 3); |
| 255 } |
| 256 |
| 257 function requestBatteryChargeData() { |
| 258 chrome.send('requestBatteryChargeData'); |
| 259 } |
| 260 |
| 261 var powerUI = { |
| 262 showBatteryChargeData: showBatteryChargeData |
| 263 }; |
| 264 |
| 265 document.addEventListener('DOMContentLoaded', function() { |
| 266 requestBatteryChargeData(); |
| 267 $('battery-charge-reload-button').onclick = requestBatteryChargeData; |
| 268 }); |
OLD | NEW |