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.
6 * Plot a line graph of data versus time on a HTML canvas element.
8 * @param {HTMLCanvasElement} canvas The canvas on which the line graph is
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.
23 function plotLineGraph(canvas, tData, plots, yMin, yMax, yPrecision) {
24 var textFont = '12px Arial';
26 var padding = 5; // Pixels
27 var errorOffsetPixels = 15;
28 var gridColor = '#ccc';
29 var ctx = canvas.getContext('2d');
30 var size = tData.length;
32 function drawText(text, x, y) {
34 ctx.fillStyle = '#000';
35 ctx.fillText(text, x, y);
38 function printErrorText(text) {
39 ctx.clearRect(0, 0, canvas.width, canvas.height);
40 drawText(text, errorOffsetPixels, errorOffsetPixels);
44 printErrorText(loadTimeData.getString('notEnoughDataAvailableYet'));
48 for (var count = 0; count < plots.length; count++) {
49 if (plots[count].data.length != size) {
50 printErrorText(loadTimeData.getString('timeAndPlotDataMismatch'));
55 function valueToString(value) {
56 return Number(value).toPrecision(yPrecision);
59 function getTextWidth(text) {
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;
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);
76 function drawLine(x1, y1, x2, y2, color) {
81 ctx.strokeStyle = color;
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);
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);
103 var xMinStr = tData[0];
104 var xMaxStr = tData[size - 1];
105 var xMinWidth = getTextWidth(xMinStr);
106 var xMaxWidth = getTextWidth(xMaxStr);
108 var xOrigin = padding + Math.max(yMinWidth,
110 Math.round(xMinWidth / 2));
111 var yOrigin = padding + textHeight;
112 var width = canvas.width - xOrigin - Math.floor(xMaxWidth / 2) - padding;
114 canvas.width += size - width;
117 var height = canvas.height - yOrigin - textHeight - padding;
119 function drawPlots() {
121 ctx.clearRect(0, 0, canvas.width, canvas.height);
123 // Draw the bounding rectangle.
124 drawRect(xOrigin, yOrigin, width, height, gridColor);
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);
131 xOrigin + width - xMaxWidth / 2,
132 yOrigin + height + textHeight);
134 // Draw y-level (horizontal) lines.
135 drawLine(xOrigin, yOrigin + height / 4,
136 xOrigin + width - 1, yOrigin + height / 4,
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,
144 // Draw half-level value.
146 xOrigin - yHalfWidth,
147 yOrigin + height / 2 + textHeight / 2);
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;
156 for (var i = 0; i < size; i++) {
157 var xPos = xOrigin + Math.floor(i / (size - 1) * (width - 1));
159 var yPos = yOrigin + height - 1 -
160 Math.round((val - yMin) / yValRange * (height - 1));
162 ctx.moveTo(xPos, yPos);
164 ctx.lineTo(xPos, yPos);
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,
178 for (var count = 0; count < plots.length; count++) {
179 var yData = plots[count].data;
181 // Draw small black square on the plot where the time guide intersects
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);
189 // Draw the val to right of the intersection.
190 var valStr = valueToString(val);
192 if (yPos - textHeight / 2 < yOrigin) {
193 yLoc = yOrigin + textHeight;
194 } else if (yPos + textHeight / 2 >= yPos + height) {
195 yLoc = yOrigin + height - 1;
197 yLoc = yPos + textHeight / 2;
199 drawHighlightText(valStr, x + 5, yLoc, plots[count].color);
203 function onMouseOverOrMove(event) {
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) {
215 drawTimeGuide(x - xOrigin);
217 drawTimeGuide(Math.round((x - xOrigin) / (width - 1) * (size - 1)));
221 function onMouseOut(event) {
226 canvas.addEventListener('mouseover', onMouseOverOrMove);
227 canvas.addEventListener('mousemove', onMouseOverOrMove);
228 canvas.addEventListener('mouseout', onMouseOut);
232 * Display the battery charge vs time on a line graph.
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.
238 function showBatteryChargeData(powerSupplyArray) {
246 var dischargeRatePlot = [
252 var minDischargeRate = 1000; // A high unrealistic number to begin with.
253 var maxDischargeRate = -1000; // A low unrealistic number to begin with.
254 for (var i = 0; i < powerSupplyArray.length; i++) {
255 var time = new Date(powerSupplyArray[i].time);
256 tData[i] = time.toLocaleTimeString();
258 chargePlot[0].data[i] = powerSupplyArray[i].battery_percent;
260 var dischargeRate = powerSupplyArray[i].battery_discharge_rate;
261 dischargeRatePlot[0].data[i] = dischargeRate;
262 minDischargeRate = Math.min(dischargeRate, minDischargeRate);
263 maxDischargeRate = Math.max(dischargeRate, maxDischargeRate);
265 if (minDischargeRate == maxDischargeRate) {
266 // This means that all the samples had the same value. Hence, offset the
267 // extremes by a bit so that the plot looks good.
268 minDischargeRate -= 1;
269 maxDischargeRate += 1;
272 var chargeCanvas = $('battery-charge-percentage-canvas');
273 var dischargeRateCanvas = $('battery-discharge-rate-canvas');
274 plotLineGraph(chargeCanvas, tData, chargePlot, 0.00, 100.00, 3);
275 plotLineGraph(dischargeRateCanvas,
283 function requestBatteryChargeData() {
284 chrome.send('requestBatteryChargeData');
288 showBatteryChargeData: showBatteryChargeData
291 document.addEventListener('DOMContentLoaded', function() {
292 requestBatteryChargeData();
293 $('battery-charge-reload-button').onclick = requestBatteryChargeData;