Index: chrome/browser/resources/chromeos/power.js |
diff --git a/chrome/browser/resources/chromeos/power.js b/chrome/browser/resources/chromeos/power.js |
index 2d2f2df2b2a3978823fc4dd1894188e81f0e663d..a61bd966c9e25efa5fc8b7ea4b66b44de8b9d5a3 100644 |
--- a/chrome/browser/resources/chromeos/power.js |
+++ b/chrome/browser/resources/chromeos/power.js |
@@ -5,8 +5,10 @@ |
/** |
* Plot a line graph of data versus time on a HTML canvas element. |
* |
- * @param {HTMLCanvasElement} canvas The canvas on which the line graph is |
+ * @param {HTMLCanvasElement} plotCanvas The canvas on which the line graph is |
* drawn. |
+ * @param {HTMLCanvasElement} legendCanvas The canvas on which the legend for |
+ * the line graph is drawn. |
* @param {Array.<number>} tData The time (in seconds) in the past when the |
* corresponding data in plots was sampled. |
* @param {Array.<{data: Array.<number>, color: string}>} plots An |
@@ -21,28 +23,30 @@ |
* @param {integer} yPrecision An integer value representing the number of |
* digits of precision the y-axis data should be printed with. |
*/ |
-function plotLineGraph(canvas, tData, plots, yMin, yMax, yPrecision) { |
+function plotLineGraph( |
+ plotCanvas, legendCanvas, tData, plots, yMin, yMax, yPrecision) { |
var textFont = '12px Arial'; |
var textHeight = 12; |
var padding = 5; // Pixels |
var errorOffsetPixels = 15; |
var gridColor = '#ccc'; |
- var ctx = canvas.getContext('2d'); |
+ var plotCtx = plotCanvas.getContext('2d'); |
var size = tData.length; |
- function drawText(text, x, y) { |
+ function drawText(ctx, text, x, y) { |
ctx.font = textFont; |
ctx.fillStyle = '#000'; |
ctx.fillText(text, x, y); |
} |
- function printErrorText(text) { |
- ctx.clearRect(0, 0, canvas.width, canvas.height); |
- drawText(text, errorOffsetPixels, errorOffsetPixels); |
+ function printErrorText(ctx, text) { |
+ ctx.clearRect(0, 0, plotCanvas.width, plotCanvas.height); |
+ drawText(ctx, text, errorOffsetPixels, errorOffsetPixels); |
} |
if (size < 2) { |
- printErrorText(loadTimeData.getString('notEnoughDataAvailableYet')); |
+ printErrorText(plotCtx, |
+ loadTimeData.getString('notEnoughDataAvailableYet')); |
return; |
} |
@@ -53,10 +57,14 @@ function plotLineGraph(canvas, tData, plots, yMin, yMax, yPrecision) { |
} |
function valueToString(value) { |
- return Number(value).toPrecision(yPrecision); |
+ if (Math.abs(value) < 1) { |
+ return Number(value).toFixed(yPrecision - 1); |
+ } else { |
+ return Number(value).toPrecision(yPrecision); |
+ } |
} |
- function getTextWidth(text) { |
+ function getTextWidth(ctx, text) { |
ctx.font = textFont; |
// For now, all text is drawn to the left of vertical lines, or centered. |
// Add a 2 pixel padding so that there is some spacing between the text |
@@ -64,16 +72,16 @@ function plotLineGraph(canvas, tData, plots, yMin, yMax, yPrecision) { |
return Math.round(ctx.measureText(text).width) + 2; |
} |
- function drawHighlightText(text, x, y, color) { |
+ function drawHighlightText(ctx, text, x, y, color) { |
ctx.strokeStyle = '#000'; |
- ctx.strokeRect(x, y - textHeight, getTextWidth(text), textHeight); |
+ ctx.strokeRect(x, y - textHeight, getTextWidth(ctx, text), textHeight); |
ctx.fillStyle = color; |
- ctx.fillRect(x, y - textHeight, getTextWidth(text), textHeight); |
+ ctx.fillRect(x, y - textHeight, getTextWidth(ctx, text), textHeight); |
ctx.fillStyle = '#fff'; |
ctx.fillText(text, x, y); |
} |
- function drawLine(x1, y1, x2, y2, color) { |
+ function drawLine(ctx, x1, y1, x2, y2, color) { |
ctx.save(); |
ctx.beginPath(); |
ctx.moveTo(x1, y1); |
@@ -83,66 +91,138 @@ function plotLineGraph(canvas, tData, plots, yMin, yMax, yPrecision) { |
ctx.restore(); |
} |
- // The strokeRect method of the 2d context of a canvas draws a bounding |
+ // The strokeRect method of the 2d context of a plotCanvas draws a bounding |
// rectangle with an offset origin and greater dimensions. Hence, use this |
// function to draw a rect at the desired location with desired dimensions. |
- function drawRect(x, y, width, height, color) { |
- drawLine(x, y, x + width - 1, y, color); |
- drawLine(x, y, x, y + height - 1, color); |
- drawLine(x, y + height - 1, x + width - 1, y + height - 1, color); |
- drawLine(x + width - 1, y, x + width - 1, y + height - 1, color); |
+ function drawRect(ctx, x, y, width, height, color) { |
+ drawLine(ctx, x, y, x + width - 1, y, color); |
+ drawLine(ctx, x, y, x, y + height - 1, color); |
+ drawLine(ctx, x, y + height - 1, x + width - 1, y + height - 1, color); |
+ drawLine(ctx, x + width - 1, y, x + width - 1, y + height - 1, color); |
+ } |
+ |
+ function drawLegend() { |
+ // Show a legend only if at least one individual plot has a name. |
+ var valid = false; |
+ for (var i = 0; i < plots.length; i++) { |
+ if (plots[i].name != null) { |
+ valid = true; |
+ break; |
+ } |
+ } |
+ if (!valid) { |
+ legendCanvas.hidden = true; |
+ return; |
+ } |
+ |
+ var padding = 2; |
+ var legendSquareSide = 12; |
+ var legendCtx = legendCanvas.getContext('2d'); |
+ var xLoc = padding; |
+ var yLoc = padding; |
+ // Adjust the height of the canvas before drawing on it. |
+ for (var i = 0; i < plots.length; i++) { |
+ if (plots[i].name == null) { |
+ continue; |
+ } |
+ var legendText = ' - ' + plots[i].name; |
+ xLoc += legendSquareSide + getTextWidth(legendCtx, legendText) + |
+ 2 * padding; |
+ if (i < plots.length - 1) { |
+ var xLocNext = xLoc + |
+ getTextWidth(legendCtx, ' - ' + plots[i + 1].name) + |
+ legendSquareSide; |
+ if (xLocNext >= legendCanvas.width) { |
+ xLoc = padding; |
+ yLoc = yLoc + 2 * padding + textHeight; |
+ } |
+ } |
+ } |
+ |
+ legendCanvas.height = yLoc + textHeight + padding; |
+ |
+ xLoc = padding; |
+ yLoc = padding; |
+ // Go over the plots again, this time drawing the legends. |
+ for (var i = 0; i < plots.length; i++) { |
+ legendCtx.fillStyle = plots[i].color; |
+ legendCtx.fillRect(xLoc, yLoc, legendSquareSide, legendSquareSide); |
+ xLoc += legendSquareSide; |
+ |
+ var legendText = ' - ' + plots[i].name; |
+ drawText(legendCtx, legendText, xLoc, yLoc + textHeight - 1); |
+ xLoc += getTextWidth(legendCtx, legendText) + 2 * padding; |
+ |
+ if (i < plots.length - 1) { |
+ var xLocNext = xLoc + |
+ getTextWidth(legendCtx, ' - ' + plots[i + 1].name) + |
+ legendSquareSide; |
+ if (xLocNext >= legendCanvas.width) { |
+ xLoc = padding; |
+ yLoc = yLoc + 2 * padding + textHeight; |
+ } |
+ } |
+ } |
} |
var yMinStr = valueToString(yMin); |
var yMaxStr = valueToString(yMax); |
var yHalfStr = valueToString((yMax + yMin) / 2); |
- var yMinWidth = getTextWidth(yMinStr); |
- var yMaxWidth = getTextWidth(yMaxStr); |
- var yHalfWidth = getTextWidth(yHalfStr); |
+ var yMinWidth = getTextWidth(plotCtx, yMinStr); |
+ var yMaxWidth = getTextWidth(plotCtx, yMaxStr); |
+ var yHalfWidth = getTextWidth(plotCtx, yHalfStr); |
var xMinStr = tData[0]; |
var xMaxStr = tData[size - 1]; |
- var xMinWidth = getTextWidth(xMinStr); |
- var xMaxWidth = getTextWidth(xMaxStr); |
+ var xMinWidth = getTextWidth(plotCtx, xMinStr); |
+ var xMaxWidth = getTextWidth(plotCtx, xMaxStr); |
var xOrigin = padding + Math.max(yMinWidth, |
yMaxWidth, |
Math.round(xMinWidth / 2)); |
var yOrigin = padding + textHeight; |
- var width = canvas.width - xOrigin - Math.floor(xMaxWidth / 2) - padding; |
+ var width = plotCanvas.width - xOrigin - Math.floor(xMaxWidth / 2) - padding; |
if (width < size) { |
- canvas.width += size - width; |
+ plotCanvas.width += size - width; |
width = size; |
} |
- var height = canvas.height - yOrigin - textHeight - padding; |
+ var height = plotCanvas.height - yOrigin - textHeight - padding; |
function drawPlots() { |
// Start fresh. |
- ctx.clearRect(0, 0, canvas.width, canvas.height); |
+ plotCtx.clearRect(0, 0, plotCanvas.width, plotCanvas.height); |
// Draw the bounding rectangle. |
- drawRect(xOrigin, yOrigin, width, height, gridColor); |
+ drawRect(plotCtx, xOrigin, yOrigin, width, height, gridColor); |
// Draw the x and y bound values. |
- drawText(yMaxStr, xOrigin - yMaxWidth, yOrigin + textHeight); |
- drawText(yMinStr, xOrigin - yMinWidth, yOrigin + height); |
- drawText(xMinStr, xOrigin - xMinWidth / 2, yOrigin + height + textHeight); |
- drawText(xMaxStr, |
+ drawText(plotCtx, yMaxStr, xOrigin - yMaxWidth, yOrigin + textHeight); |
+ drawText(plotCtx, yMinStr, xOrigin - yMinWidth, yOrigin + height); |
+ drawText(plotCtx, |
+ xMinStr, |
+ xOrigin - xMinWidth / 2, |
+ yOrigin + height + textHeight); |
+ drawText(plotCtx, |
+ xMaxStr, |
xOrigin + width - xMaxWidth / 2, |
yOrigin + height + textHeight); |
// Draw y-level (horizontal) lines. |
- drawLine(xOrigin + 1, yOrigin + height / 4, |
+ drawLine(plotCtx, |
+ xOrigin + 1, yOrigin + height / 4, |
xOrigin + width - 2, yOrigin + height / 4, |
gridColor); |
- drawLine(xOrigin + 1, yOrigin + height / 2, |
+ drawLine(plotCtx, |
+ xOrigin + 1, yOrigin + height / 2, |
xOrigin + width - 2, yOrigin + height / 2, gridColor); |
- drawLine(xOrigin + 1, yOrigin + 3 * height / 4, |
+ drawLine(plotCtx, |
+ xOrigin + 1, yOrigin + 3 * height / 4, |
xOrigin + width - 2, yOrigin + 3 * height / 4, |
gridColor); |
// Draw half-level value. |
- drawText(yHalfStr, |
+ drawText(plotCtx, |
+ yHalfStr, |
xOrigin - yHalfWidth, |
yOrigin + height / 2 + textHeight / 2); |
@@ -151,15 +231,15 @@ function plotLineGraph(canvas, tData, plots, yMin, yMax, yPrecision) { |
for (var count = 0; count < plots.length; count++) { |
var plot = plots[count]; |
var yData = plot.data; |
- ctx.strokeStyle = plot.color; |
- ctx.beginPath(); |
+ plotCtx.strokeStyle = plot.color; |
+ plotCtx.beginPath(); |
var beginPath = true; |
for (var i = 0; i < size; i++) { |
var val = yData[i]; |
if (typeof val === 'string') { |
// Stroke the plot drawn so far and begin a fresh plot. |
- ctx.stroke(); |
- ctx.beginPath(); |
+ plotCtx.stroke(); |
+ plotCtx.beginPath(); |
beginPath = true; |
continue; |
} |
@@ -167,27 +247,35 @@ function plotLineGraph(canvas, tData, plots, yMin, yMax, yPrecision) { |
var yPos = yOrigin + height - 1 - |
Math.round((val - yMin) / yValRange * (height - 1)); |
if (beginPath) { |
- ctx.moveTo(xPos, yPos); |
+ plotCtx.moveTo(xPos, yPos); |
// A simple move to does not print anything. Hence, draw a little |
// square here to mark a beginning. |
- ctx.fillStyle = '#000'; |
- ctx.fillRect(xPos - 1, yPos - 1, 2, 2); |
+ plotCtx.fillStyle = '#000'; |
+ plotCtx.fillRect(xPos - 1, yPos - 1, 2, 2); |
beginPath = false; |
} else { |
- ctx.lineTo(xPos, yPos); |
+ plotCtx.lineTo(xPos, yPos); |
if (i === size - 1 || typeof yData[i + 1] === 'string') { |
// Draw a little square to mark an end to go with the start |
// markers from above. |
- ctx.fillStyle = '#000'; |
- ctx.fillRect(xPos - 1, yPos - 1, 2, 2); |
+ plotCtx.fillStyle = '#000'; |
+ plotCtx.fillRect(xPos - 1, yPos - 1, 2, 2); |
} |
} |
} |
- ctx.stroke(); |
+ plotCtx.stroke(); |
} |
// Paint the missing time intervals with |gridColor|. |
// Pick one of the plots to look for missing time intervals. |
+ function drawMissingRect(start, end) { |
+ var xLeft = xOrigin + Math.floor(start / (size - 1) * (width - 1)); |
+ var xRight = xOrigin + Math.floor(end / (size - 1) * (width - 1)); |
+ plotCtx.fillStyle = gridColor; |
+ // The x offsets below are present so that the blank space starts |
+ // and ends between two valid samples. |
+ plotCtx.fillRect(xLeft + 1, yOrigin, xRight - xLeft - 2, height - 1); |
+ } |
var inMissingInterval = false; |
var intervalStart; |
for (var i = 0; i < size; i++) { |
@@ -198,24 +286,24 @@ function plotLineGraph(canvas, tData, plots, yMin, yMax, yPrecision) { |
// sample. |
intervalStart = Math.max(i - 1, 0); |
} |
+ |
+ if (i == size - 1) { |
+ // If this is the last sample, just draw missing rect. |
+ drawMissingRect(intervalStart, i); |
+ } |
} else if (inMissingInterval) { |
inMissingInterval = false; |
- var xLeft = xOrigin + |
- Math.floor(intervalStart / (size - 1) * (width - 1)); |
- var xRight = xOrigin + Math.floor(i / (size - 1) * (width - 1)); |
- ctx.fillStyle = gridColor; |
- // The x offsets below are present so that the blank space starts |
- // and ends between two valid samples. |
- ctx.fillRect(xLeft + 1, yOrigin, xRight - xLeft - 2, height - 1); |
+ drawMissingRect(intervalStart, i); |
} |
} |
} |
function drawTimeGuide(tDataIndex) { |
var x = xOrigin + tDataIndex / (size - 1) * (width - 1); |
- drawLine(x, yOrigin, x, yOrigin + height - 1, '#000'); |
- drawText(tData[tDataIndex], |
- x - getTextWidth(tData[tDataIndex]) / 2, |
+ drawLine(plotCtx, x, yOrigin, x, yOrigin + height - 1, '#000'); |
+ drawText(plotCtx, |
+ tData[tDataIndex], |
+ x - getTextWidth(plotCtx, tData[tDataIndex]) / 2, |
yOrigin - 2); |
for (var count = 0; count < plots.length; count++) { |
@@ -233,8 +321,8 @@ function plotLineGraph(canvas, tData, plots, yMin, yMax, yPrecision) { |
Math.round((val - yMin) / (yMax - yMin) * (height - 1)); |
valStr = valueToString(val); |
} |
- ctx.fillStyle = '#000'; |
- ctx.fillRect(x - 2, yPos - 2, 4, 4); |
+ plotCtx.fillStyle = '#000'; |
+ plotCtx.fillRect(x - 2, yPos - 2, 4, 4); |
// Draw the val to right of the intersection. |
var yLoc; |
@@ -245,16 +333,16 @@ function plotLineGraph(canvas, tData, plots, yMin, yMax, yPrecision) { |
} else { |
yLoc = yPos + textHeight / 2; |
} |
- drawHighlightText(valStr, x + 5, yLoc, plots[count].color); |
+ drawHighlightText(plotCtx, valStr, x + 5, yLoc, plots[count].color); |
} |
} |
function onMouseOverOrMove(event) { |
drawPlots(); |
- var boundingRect = canvas.getBoundingClientRect(); |
- var x = event.clientX - boundingRect.left; |
- var y = event.clientY - boundingRect.top; |
+ var boundingRect = plotCanvas.getBoundingClientRect(); |
+ var x = Math.round(event.clientX - boundingRect.left); |
+ var y = Math.round(event.clientY - boundingRect.top); |
if (x < xOrigin || x >= xOrigin + width || |
y < yOrigin || y >= yOrigin + height) { |
return; |
@@ -271,14 +359,65 @@ function plotLineGraph(canvas, tData, plots, yMin, yMax, yPrecision) { |
drawPlots(); |
} |
+ drawLegend(); |
drawPlots(); |
- canvas.addEventListener('mouseover', onMouseOverOrMove); |
- canvas.addEventListener('mousemove', onMouseOverOrMove); |
- canvas.addEventListener('mouseout', onMouseOut); |
+ plotCanvas.addEventListener('mouseover', onMouseOverOrMove); |
+ plotCanvas.addEventListener('mousemove', onMouseOverOrMove); |
+ plotCanvas.addEventListener('mouseout', onMouseOut); |
} |
var sleepSampleInterval = 30 * 1000; // in milliseconds. |
var sleepText = loadTimeData.getString('systemSuspended'); |
+var invalidDataText = loadTimeData.getString('invalidData'); |
+var offlineText = loadTimeData.getString('offlineText'); |
+ |
+var plotColors = ['Red', 'Blue', 'Green', 'Gold', 'CadetBlue', 'LightCoral', |
+ 'LightSlateGray', 'Peru', 'DarkRed', 'LawnGreen', 'Tan']; |
+ |
+/** |
+ * Add canvases for plotting to |plotsDiv|. For every header in |headerArray|, |
+ * one canvas for the plot and one for its legend are added. |
+ * |
+ * @param {Array.<string>} headerArray Headers for the different plots to be |
+ * added to |plotsDiv|. |
+ * @param {HTMLDivElement} plotsDiv The div element into which the canvases |
+ * are added. |
+ * @return {<string>: {plotCanvas: <HTMLCanvasElement>, |
+ * legendCanvas: <HTMLCanvasElement>} Returns an object |
+ * with the headers as 'keys'. Each element is an object containing the |
+ * legend canvas and the plot canvas that have been added to |plotsDiv|. |
+ */ |
+function addCanvases(headerArray, plotsDiv) { |
+ // Remove the contents before adding new ones. |
+ while (plotsDiv.firstChild != null) { |
+ plotsDiv.removeChild(plotsDiv.firstChild); |
+ } |
+ var width = Math.floor(plotsDiv.getBoundingClientRect().width); |
+ var canvases = {}; |
+ for (var i = 0; i < headerArray.length; i++) { |
+ var header = document.createElement('h4'); |
+ header.textContent = headerArray[i]; |
+ plotsDiv.appendChild(header); |
+ |
+ var legendCanvas = document.createElement('canvas'); |
+ legendCanvas.width = width; |
+ legendCanvas.height = 5; |
+ plotsDiv.appendChild(legendCanvas); |
+ |
+ var plotCanvasDiv = document.createElement('div'); |
+ plotCanvasDiv.style.overflow = 'auto'; |
+ plotCanvasDiv.style.maxWidth = String(width) + 'px'; |
+ plotsDiv.appendChild(plotCanvasDiv); |
+ |
+ plotCanvas = document.createElement('canvas'); |
+ plotCanvas.width = width; |
+ plotCanvas.height = 200; |
+ plotCanvasDiv.appendChild(plotCanvas); |
+ |
+ canvases[headerArray[i]] = {plot: plotCanvas, legend: legendCanvas}; |
+ } |
+ return canvases; |
+} |
/** |
* Add samples in |sampleArray| to individual plots in |plots|. If the system |
@@ -306,7 +445,7 @@ var sleepText = loadTimeData.getString('systemSuspended'); |
* 'sleepDuration' field is for the time in milliseconds the system spent |
* in sleep/suspend state. |
*/ |
-function addTimeDataSample(plots, tData, sampleArray, |
+function addTimeDataSample(plots, tData, absTime, sampleArray, |
sampleTime, previousSampleTime, |
systemResumedArray) { |
for (var i = 0; i < plots.length; i++) { |
@@ -318,6 +457,7 @@ function addTimeDataSample(plots, tData, sampleArray, |
var time; |
if (tData.length == 0) { |
time = new Date(sampleTime); |
+ absTime[0] = sampleTime; |
tData[0] = time.toLocaleTimeString(); |
for (var i = 0; i < plots.length; i++) { |
plots[i].data[0] = sampleArray[i]; |
@@ -329,11 +469,17 @@ function addTimeDataSample(plots, tData, sampleArray, |
var resumeTime = systemResumedArray[i].time; |
var sleepDuration = systemResumedArray[i].sleepDuration; |
var sleepStartTime = resumeTime - sleepDuration; |
- if (resumeTime < sampleTime && sleepStartTime > previousSampleTime) { |
+ if (resumeTime < sampleTime) { |
+ if (sleepStartTime < previousSampleTime) { |
+ // This can happen if pending callbacks were handled before actually |
+ // suspending. |
+ sleepStartTime = previousSampleTime + 1000; |
+ } |
// Add sleep samples for every |sleepSampleInterval|. |
var sleepSampleTime = sleepStartTime; |
while (sleepSampleTime < resumeTime) { |
time = new Date(sleepSampleTime); |
+ absTime.push(sleepSampleTime); |
tData.push(time.toLocaleTimeString()); |
for (var j = 0; j < plots.length; j++) { |
plots[j].data.push(sleepText); |
@@ -344,6 +490,7 @@ function addTimeDataSample(plots, tData, sampleArray, |
} |
time = new Date(sampleTime); |
+ absTime.push(sampleTime); |
tData.push(time.toLocaleTimeString()); |
for (var i = 0; i < plots.length; i++) { |
plots[i].data.push(sampleArray[i]); |
@@ -369,15 +516,19 @@ function addTimeDataSample(plots, tData, sampleArray, |
*/ |
function showBatteryChargeData(powerSupplyArray, systemResumedArray) { |
var chargeTimeData = []; |
+ var chargeAbsTime = []; |
var chargePlot = [ |
{ |
+ name: loadTimeData.getString('batteryChargePercentageHeader'), |
color: '#0000FF', |
data: [] |
} |
]; |
var dischargeRateTimeData = []; |
+ var dischargeRateAbsTime = []; |
var dischargeRatePlot = [ |
{ |
+ name: loadTimeData.getString('dischargeRateLegendText'), |
color: '#FF0000', |
data: [] |
} |
@@ -387,7 +538,9 @@ function showBatteryChargeData(powerSupplyArray, systemResumedArray) { |
for (var i = 0; i < powerSupplyArray.length; i++) { |
var j = Math.max(i - 1, 0); |
- addTimeDataSample(chargePlot, chargeTimeData, |
+ addTimeDataSample(chargePlot, |
+ chargeTimeData, |
+ chargeAbsTime, |
[powerSupplyArray[i].batteryPercent], |
powerSupplyArray[i].time, |
powerSupplyArray[j].time, |
@@ -398,6 +551,7 @@ function showBatteryChargeData(powerSupplyArray, systemResumedArray) { |
maxDischargeRate = Math.max(dischargeRate, maxDischargeRate); |
addTimeDataSample(dischargeRatePlot, |
dischargeRateTimeData, |
+ dischargeRateAbsTime, |
[dischargeRate], |
powerSupplyArray[i].time, |
powerSupplyArray[j].time, |
@@ -410,26 +564,250 @@ function showBatteryChargeData(powerSupplyArray, systemResumedArray) { |
maxDischargeRate += 1; |
} |
- var chargeCanvas = $('battery-charge-percentage-canvas'); |
- var dischargeRateCanvas = $('battery-discharge-rate-canvas'); |
- plotLineGraph(chargeCanvas, chargeTimeData, chargePlot, 0.00, 100.00, 3); |
- plotLineGraph(dischargeRateCanvas, |
- dischargeRateTimeData, |
- dischargeRatePlot, |
- minDischargeRate, |
- maxDischargeRate, |
- 3); |
+ plotsDiv = $('battery-charge-plots-div'); |
+ |
+ canvases = addCanvases( |
+ [loadTimeData.getString('batteryChargePercentageHeader'), |
+ loadTimeData.getString('batteryDischargeRateHeader')], |
+ plotsDiv); |
+ |
+ batteryChargeCanvases = canvases[ |
+ loadTimeData.getString('batteryChargePercentageHeader')]; |
+ plotLineGraph( |
+ batteryChargeCanvases['plot'], |
+ batteryChargeCanvases['legend'], |
+ chargeTimeData, |
+ chargePlot, |
+ 0.00, |
+ 100.00, |
+ 3); |
+ |
+ dischargeRateCanvases = canvases[ |
+ loadTimeData.getString('batteryDischargeRateHeader')]; |
+ plotLineGraph( |
+ dischargeRateCanvases['plot'], |
+ dischargeRateCanvases['legend'], |
+ dischargeRateTimeData, |
+ dischargeRatePlot, |
+ minDischargeRate, |
+ maxDischargeRate, |
+ 3); |
+} |
+ |
+/** |
+ * Shows state occupancy data (CPU idle or CPU freq state occupancy) on a set of |
+ * plots on the about:power UI. |
+ * |
+ * @param {Array.<{Array.<{ |
+ * time: number, |
+ * cpuOnline:boolean, |
+ * timeInState: {<string>: number}>}>} timeInStateData Array of arrays |
+ * where each array corresponds to a CPU on the system. The elements of the |
+ * individual arrays contain state occupancy samples. |
+ * @param {Array.<{time: ?, sleepDuration: ?}>} systemResumedArray An array |
+ * objects with fields 'time' and 'sleepDuration'. Each object corresponds |
+ * to a system resume event. The 'time' field is for the time in |
+ * milliseconds since the epoch when the system resumed. The 'sleepDuration' |
+ * field is for the time in milliseconds the system spent in sleep/suspend |
+ * state. |
+ * @param {string} i18nHeaderString The header string to be displayed with each |
+ * plot. For example, CPU idle data will have its own header format, and CPU |
+ * freq data will have its header format. |
+ * @param {string} unitString This is the string capturing the unit, if any, |
+ * for the different states. Note that this is not the unit of the data |
+ * being plotted. |
+ * @param {HTMLDivElement} plotsDivId The div element in which the plots should |
+ * be added. |
+ */ |
+function showStateOccupancyData(timeInStateData, |
+ systemResumedArray, |
+ i18nHeaderString, |
+ unitString, |
+ plotsDivId) { |
+ var cpuPlots = []; |
+ for (var cpu = 0; cpu < timeInStateData.length; cpu++) { |
+ var cpuData = timeInStateData[cpu]; |
+ if (cpuData.length == 0) { |
+ cpuPlots[cpu] = {plots: [], tData: []}; |
+ continue; |
+ } |
+ tData = []; |
+ absTime = []; |
+ // Each element of |plots| is an array of samples, one for each of the CPU |
+ // states. The number of states is dicovered by looking at the first |
+ // sample for which the CPU is online. |
+ var plots = []; |
+ var stateIndexMap = []; |
+ var stateCount = 0; |
+ for (var i = 0; i < cpuData.length; i++) { |
+ if (cpuData[i].cpuOnline) { |
+ for (var state in cpuData[i].timeInState) { |
+ var stateName = state; |
+ if (unitString != null) { |
+ stateName += ' ' + unitString; |
+ } |
+ plots.push({ |
+ name: stateName, |
+ data: [], |
+ color: plotColors[stateCount] |
+ }); |
+ stateIndexMap.push(state); |
+ stateCount += 1; |
+ } |
+ break; |
+ } |
+ } |
+ // If stateCount is 0, then it means the CPU has been offline |
+ // throughout. Just add a single plot for such a case. |
+ if (stateCount == 0) { |
+ plots.push({ |
+ name: null, |
+ data: [], |
+ color: null |
+ }); |
+ stateCount = 1; // Some invalid state! |
+ } |
+ |
+ // Pass the samples through the function addTimeDataSample to add 'sleep' |
+ // samples. |
+ for (var i = 0; i < cpuData.length; i++) { |
+ var sample = cpuData[i]; |
+ var valArray = []; |
+ for (var j = 0; j < stateCount; j++) { |
+ if (sample.cpuOnline) { |
+ valArray[j] = sample.timeInState[stateIndexMap[j]]; |
+ } else { |
+ valArray[j] = offlineText; |
+ } |
+ } |
+ |
+ var k = Math.max(i - 1, 0); |
+ addTimeDataSample(plots, |
+ tData, |
+ absTime, |
+ valArray, |
+ sample.time, |
+ cpuData[k].time, |
+ systemResumedArray); |
+ } |
+ |
+ // Calculate the percentage occupancy of each state. A valid number is |
+ // possible only if two consecutive samples are valid/numbers. |
+ for (var k = 0; k < stateCount; k++) { |
+ var stateData = plots[k].data; |
+ // Skip the first sample as there is no previous sample. |
+ for (var i = stateData.length - 1; i > 0; i--) { |
+ if (typeof stateData[i] === 'number') { |
+ if (typeof stateData[i - 1] === 'number') { |
+ stateData[i] = (stateData[i] - stateData[i - 1]) / |
+ (absTime[i] - absTime[i - 1]) * 100; |
+ } else { |
+ stateData[i] = invalidDataText; |
+ } |
+ } |
+ } |
+ } |
+ |
+ // Remove the first sample from the time and data arrays. |
+ tData.shift(); |
+ for (var k = 0; k < stateCount; k++) { |
+ plots[k].data.shift(); |
+ } |
+ cpuPlots[cpu] = {plots: plots, tData: tData}; |
+ } |
+ |
+ headers = []; |
+ for (var cpu = 0; cpu < timeInStateData.length; cpu++) { |
+ headers[cpu] = |
+ 'CPU ' + cpu + ' ' + loadTimeData.getString(i18nHeaderString); |
+ } |
+ |
+ canvases = addCanvases(headers, $(plotsDivId)); |
+ for (var cpu = 0; cpu < timeInStateData.length; cpu++) { |
+ cpuCanvases = canvases[headers[cpu]]; |
+ plotLineGraph(cpuCanvases['plot'], |
+ cpuCanvases['legend'], |
+ cpuPlots[cpu]['tData'], |
+ cpuPlots[cpu]['plots'], |
+ 0, |
+ 100, |
+ 3); |
+ } |
+} |
+ |
+function showCpuIdleData(idleStateData, systemResumedArray) { |
+ showStateOccupancyData(idleStateData, |
+ systemResumedArray, |
+ 'idleStateOccupancyPercentageHeader', |
+ null, |
+ 'cpu-idle-plots-div'); |
+} |
+ |
+function showCpuFreqData(freqStateData, systemResumedArray) { |
+ showStateOccupancyData(freqStateData, |
+ systemResumedArray, |
+ 'frequencyStateOccupancyPercentageHeader', |
+ 'MHz', |
+ 'cpu-freq-plots-div'); |
} |
function requestBatteryChargeData() { |
chrome.send('requestBatteryChargeData'); |
} |
+function requestCpuIdleData() { |
+ chrome.send('requestCpuIdleData'); |
+} |
+ |
+function requestCpuFreqData() { |
+ chrome.send('requestCpuFreqData'); |
+} |
+ |
+/** |
+ * Return a callback for the 'Show'/'Hide' buttons for each section of the |
+ * about:power page. |
+ * |
+ * @param {string} sectionId The ID of the section which is to be shown or |
+ * hidden. |
+ * @param {string} buttonId The ID of the 'Show'/'Hide' button. |
+ * @param {function} requestFunction The function which should be invoked on |
+ * 'Show' to request for data from chrome. |
+ * @return {function} The button callback function. |
+ */ |
+function showHideCallback(sectionId, buttonId, requestFunction) { |
+ return function() { |
+ if ($(sectionId).hidden) { |
+ $(sectionId).hidden = false; |
+ $(buttonId).textContent = loadTimeData.getString('hideButton'); |
+ requestFunction(); |
+ } else { |
+ $(sectionId).hidden = true; |
+ $(buttonId).textContent = loadTimeData.getString('showButton'); |
+ } |
+ } |
+} |
+ |
var powerUI = { |
- showBatteryChargeData: showBatteryChargeData |
+ showBatteryChargeData: showBatteryChargeData, |
+ showCpuIdleData: showCpuIdleData, |
+ showCpuFreqData: showCpuFreqData |
}; |
document.addEventListener('DOMContentLoaded', function() { |
- requestBatteryChargeData(); |
+ $('battery-charge-section').hidden = true; |
+ $('battery-charge-show-button').onclick = showHideCallback( |
+ 'battery-charge-section', |
+ 'battery-charge-show-button', |
+ requestBatteryChargeData); |
$('battery-charge-reload-button').onclick = requestBatteryChargeData; |
+ |
+ $('cpu-idle-section').hidden = true; |
+ $('cpu-idle-show-button').onclick = showHideCallback( |
+ 'cpu-idle-section', 'cpu-idle-show-button', requestCpuIdleData); |
+ $('cpu-idle-reload-button').onclick = requestCpuIdleData; |
+ |
+ $('cpu-freq-section').hidden = true; |
+ $('cpu-freq-show-button').onclick = showHideCallback( |
+ 'cpu-freq-section', 'cpu-freq-show-button', requestCpuFreqData); |
+ $('cpu-freq-reload-button').onclick = requestCpuFreqData; |
}); |