15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Copyright (c) 2012 The Chromium Authors. All rights reserved. 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Use of this source code is governed by a BSD-style license that can be 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) found in the LICENSE file. 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)*/ 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @fileoverview Collection of functions and classes used to plot data in a 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * <canvas>. Create a Plotter() to generate a plot. 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Adds commas to a given number. 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Examples: 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1234.56 => "1,234.56" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 99999 => "99,999" 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string|number} number The number to format. 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} String representation of |number| with commas for every 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * three digits to the left of a decimal point. 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function addCommas(number) { 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) number += ''; // Convert number to string if not already a string. 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var numberParts = number.split('.'); 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var integralPart = numberParts[0]; 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var fractionalPart = numberParts.length > 1 ? '.' + numberParts[1] : ''; 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var reThreeDigits = /(\d+)(\d{3})/; 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (reThreeDigits.test(integralPart)) 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) integralPart = integralPart.replace(reThreeDigits, '$1' + ',' + '$2'); 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return integralPart + fractionalPart; 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Vertical marker to highlight data points that are being hovered over by the 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * mouse. 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} color The color to make the marker, e.g., 'rgb(100,80,240)'. 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Element} A div Element object representing the vertical marker. 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function VerticalMarker(color) { 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var m = document.createElement('div'); 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m.style.backgroundColor = color; 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m.style.opacity = '0.3'; 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m.style.position = 'absolute'; 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m.style.left = '-2px'; 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m.style.top = '-2px'; 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m.style.width = '0px'; 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m.style.height = '0px'; 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return m; 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Class representing a horizontal marker at the indicated mouse location. 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Element} canvasElement The canvas bounds. 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Number} yValue The data value corresponding to the vertical click 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * location. 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Number} yOtherValue If the plot is overlaying two coordinate systems, 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * this is the data value corresponding to the vertical click location in 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the second coordinate system. Can be null. 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function HorizontalMarker(canvasElement, yValue, yOtherValue) { 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var m = document.createElement('div'); 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m.style.backgroundColor = HorizontalMarker.COLOR; 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m.style.opacity = '0.3'; 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m.style.position = 'absolute'; 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m.style.width = canvasElement.offsetWidth + 'px'; 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) m.style.height = HorizontalMarker.HEIGHT + 'px'; 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.markerDiv = m; 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.value = yValue; 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.otherValue = yOtherValue; 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HorizontalMarker.HEIGHT = 5; 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HorizontalMarker.COLOR = 'rgb(0,100,100)'; 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Locates this element at a specified position. 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Element} canvasElement The canvas element at which this element is 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * to be placed. 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} y Y position relative to the canvas element. 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HorizontalMarker.prototype.locateAt = function(canvasElement, y) { 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var div = this.markerDiv; 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) div.style.left = domUtils.pageXY(canvasElement).x - 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) domUtils.pageXY(div.offsetParent) + 'px'; 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) div.style.top = (y + domUtils.pageXY(canvasElement).y 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) - domUtils.pageXY(div.offsetParent).y 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) - (HorizontalMarker.HEIGHT / 2)) + 'px'; 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Removes the horizontal marker from the graph. 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HorizontalMarker.prototype.remove = function() { 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.markerDiv.parentNode.removeChild(this.markerDiv); 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * An information indicator hovering around the mouse cursor on the graph. 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * This class is used to show a legend near the mouse cursor. 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * A set of legends under the graph is managed separately in 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * {@code Plotter.createLegendsSummaryElement_}. 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function HoveringInfo() { 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.containerDiv_ = document.createElement('div'); 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.containerDiv_.style.display = 'none'; 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.containerDiv_.style.position = 'absolute'; 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.containerDiv_.style.border = '1px solid #000'; 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.containerDiv_.style.padding = '0.12em'; 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.containerDiv_.style.backgroundColor = '#ddd'; 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.colorIndicator_ = document.createElement('div'); 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.colorIndicator_.style.display = 'inline-block'; 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.colorIndicator_.style.width = '1em'; 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.colorIndicator_.style.height = '1em'; 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.colorIndicator_.style.verticalAlign = 'text-bottom'; 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.colorIndicator_.style.margin = '0 0.24em 0 0'; 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.colorIndicator_.style.border = '1px solid #000'; 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.legendText_ = document.createElement('span'); 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.itemValueText_ = document.createElement('span'); 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.containerDiv_.appendChild(this.colorIndicator_); 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.containerDiv_.appendChild(this.legendText_); 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var div = document.createElement('div'); 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) div.appendChild(this.itemValueText_); 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.containerDiv_.appendChild(div); 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Returns the container element; 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Element} The container element. 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HoveringInfo.prototype.getElement = function() { 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return this.containerDiv_; 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Shows or hides the element. 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} show Shows the element if true, or hides it. 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HoveringInfo.prototype.show = function(show) { 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.containerDiv_.style.display = show ? 'block' : 'none'; 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Returns the position of the container element in the page coordinate. 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Object} A point object which has {@code x} and {@code y} fields. 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HoveringInfo.prototype.pageXY = function() { 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return domUtils.pageXY(this.containerDiv_); 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Locates the element at the specified position. 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} x X position in the page coordinate. 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} y Y position in the page coordinate. 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HoveringInfo.prototype.locateAtPageXY = function(x, y) { 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var parentXY = domUtils.pageXY(this.containerDiv_.offsetParent); 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.containerDiv_.style.left = x - parentXY.x + 'px'; 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.containerDiv_.style.top = y - parentXY.y + 'px'; 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Returns the legend text. 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {?string} The legend text. 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HoveringInfo.prototype.getLegendText = function() { 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return this.legendText_.textContent; 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Changes the legend text. 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} text The new text to be set. 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HoveringInfo.prototype.setLegendText = function(text) { 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.legendText_.textContent = text; 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Changes the item value. 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} value The new value to be shown. 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HoveringInfo.prototype.setItemValue = function(value) { 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.itemValueText_.textContent = 'Item value = ' + addCommas(value); 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Changes the color of the color indicator. 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} color The new color to be set. 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HoveringInfo.prototype.setColorIndicator = function(color) { 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.colorIndicator_.style.backgroundColor = color; 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Main class that does the actual plotting. 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Draws a chart using a canvas element. Takes an array of lines to draw. 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Array} plotData list of arrays that represent individual lines. The 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * line itself is an Array of points. 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Array} dataDescriptions list of data descriptions for each line in 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * |plotData|. 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} eventName The string name of an event to overlay on the 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * graph. Should be 'null' if there are no events to overlay. 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} eventInfo If |eventName| is specified, an array of event 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * points to overlay on the graph. Each event point in the array is itself 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * a 2-element array, where the first element is the x-axis value at which 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the event occurred during the test, and the second element is a 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * dictionary of kay/value pairs representing metadata associated with the 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * event. 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} unitsX The x-axis units of the data being plotted. 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} unitsY The y-axis units of the data being plotted. 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} unitsYOther If another graph (with different y-axis units) is 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * being overlayed over the first graph, this represents the units of the 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * other graph. Otherwise, this should be 'null'. 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {?number} graphsOtherStartIndex Specifies the starting index of 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the second set of lines. {@code plotData} in the range of 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * [0, {@code graphsOtherStartIndex}) are treated as the first set of lines, 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * and ones in the range of 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * [{@code graphsOtherStartIndex}, {@code plotData.length}) are as 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the second set. 0, {@code plotData.length} and {@code null} mean 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * no second set, i.e. all the data in {@code plotData} represent the single 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * set of lines. 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Element} resultNode A DOM Element object representing the DOM node to 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * which the plot should be attached. 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} is_lookout Whether or not the graph should be drawn 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * in 'lookout' mode, which is a summarized view that is made for overview 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * pages when the graph is drawn in a more confined space. 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} stackedGraph Whether or not the first set of lines is 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * a stacked graph. 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} stackedGraphOther Whether or not the second set of lines is 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * a stacked graph. 2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Example of the |plotData|: 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * [ 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * [line 1 data], 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * [line 2 data] 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * ]. 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Line data looks like [[point one], [point two]]. 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * And individual points are [x value, y value] 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function Plotter(plotData, dataDescriptions, eventName, eventInfo, unitsX, 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) unitsY, unitsYOther, graphsOtherStartIndex, resultNode, 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_lookout, stackedGraph, stackedGraphOther) { 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.plotData_ = plotData; 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.dataDescriptions_ = dataDescriptions; 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.eventName_ = eventName; 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.eventInfo_ = eventInfo; 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.unitsX_ = unitsX; 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.unitsY_ = unitsY; 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.unitsYOther_ = unitsYOther; 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.graphsOtherStartIndex_ = 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (0 < graphsOtherStartIndex && graphsOtherStartIndex < plotData.length) ? 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graphsOtherStartIndex : null; 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.resultNode_ = resultNode; 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.is_lookout_ = is_lookout; 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.stackedGraph_ = stackedGraph; 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.stackedGraphOther_ = stackedGraphOther; 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.dataColors_ = []; 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.coordinates = null; 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.coordinatesOther = null; 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.unitsYOther_ && this.graphsOtherStartIndex_) { 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Need two different coordinate systems to overlay on the same graph. 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.coordinates = new Coordinates( 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.plotData_.slice(0, this.graphsOtherStartIndex_)); 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.coordinatesOther = new Coordinates( 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.plotData_.slice(this.graphsOtherStartIndex_)); 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.coordinates = new Coordinates(this.plotData_); 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // A color palette that's unambigous for normal and color-deficient viewers. 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Values are (red, green, blue) on a scale of 255. 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Taken from http://jfly.iam.u-tokyo.ac.jp/html/manuals/pdf/color_blind.pdf. 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.colors = [[0, 114, 178], // Blue. 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [230, 159, 0], // Orange. 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [0, 158, 115], // Green. 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [204, 121, 167], // Purplish pink. 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [86, 180, 233], // Sky blue. 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [213, 94, 0], // Dark orange. 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [0, 0, 0], // Black. 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [240, 228, 66] // Yellow. 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ]; 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 0, colorIndex = 0; i < this.dataDescriptions_.length; ++i) 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.dataColors_[i] = this.makeColor(colorIndex++); 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Generates a string representing a color corresponding to the given index 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * in a color array. Handles wrapping around the color array if necessary. 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} i An index into the |this.colors| array. 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} A string representing a color in 'rgb(X,Y,Z)' format. 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.makeColor = function(i) { 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var index = i % this.colors.length; 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 'rgb(' + this.colors[index][0] + ',' + 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.colors[index][1] + ',' + 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.colors[index][2] + ')'; 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Same as function makeColor above, but also takes a transparency value 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * indicating how transparent to make the color appear. 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} i An index into the |this.colors| array. 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} transparencyPercent Percentage transparency to make the 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * color, e.g., 0.75. 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} A string representing a color in 'rgb(X,Y,Z,A)' format, 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * where A is the percentage transparency. 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.makeColorTransparent = function(i, transparencyPercent) { 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var index = i % this.colors.length; 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 'rgba(' + this.colors[index][0] + ',' + 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.colors[index][1] + ',' + 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.colors[index][2] + ',' + transparencyPercent + ')'; 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Gets the data color value associated with a specified color index. 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} i An index into the |this.colors| array. 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} A string representing a color in 'rgb(X,Y,Z,A)' format, 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * where A is the percentage transparency. 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.getDataColor = function(i) { 3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.dataColors_[i]) 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return this.dataColors_[i]; 3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return this.makeColor(i); 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Gets the fill color value associated with a specified color index. 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} i An index into the |this.colors| array. 3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} A string representing a color in 'rgba(R,G,B,A)' format, 3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * where A is the percentage transparency. 3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.getFillColor = function(i) { 3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return this.makeColorTransparent(i, 0.4); 3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Does the actual plotting. 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.plot = function() { 3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var self = this; 3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.canvasElement_ = this.canvas_(); 3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.rulerDiv_ = this.ruler_(); 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Markers for the result point(s)/events that the mouse is currently 3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // hovering over. 3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.cursorDiv_ = new VerticalMarker('rgb(100,80,240)'); 3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.cursorDivOther_ = new VerticalMarker('rgb(50,50,50)'); 3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.eventDiv_ = new VerticalMarker('rgb(255, 0, 0)'); 3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.hoveringInfo_ = new HoveringInfo(); 3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.resultNode_.appendChild(this.canvasElement_); 3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.resultNode_.appendChild(this.coordinates_()); 3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.resultNode_.appendChild(this.rulerDiv_); 3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.resultNode_.appendChild(this.cursorDiv_); 3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.resultNode_.appendChild(this.cursorDivOther_); 3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.resultNode_.appendChild(this.eventDiv_); 3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.resultNode_.appendChild(this.hoveringInfo_.getElement()); 3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.attachEventListeners_(); 3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Now draw the canvas. 3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var ctx = this.canvasElement_.getContext('2d'); 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Clear it with white: otherwise canvas will draw on top of existing data. 3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.clearRect(0, 0, this.canvasElement_.width, this.canvasElement_.height); 3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Draw all data lines in the reverse order so the last graph appears on 3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the backmost and the first graph appears on the frontmost. 3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) function draw(plotData, coordinates, colorOffset, stack) { 3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = plotData.length - 1; i >= 0; --i) { 4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (stack) { 4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.plotAreaUnderLine_(ctx, self.getFillColor(colorOffset + i), 4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) plotData[i], coordinates); 4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.plotLine_(ctx, self.getDataColor(colorOffset + i), 4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) plotData[i], coordinates); 4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) draw(this.plotData_.slice(0, 4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.graphsOtherStartIndex_ ? 4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.graphsOtherStartIndex_ : 4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.plotData_.length), 4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.coordinates, 0, this.stackedGraph_); 4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.graphsOtherStartIndex_) { 4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) draw(this.plotData_.slice(this.graphsOtherStartIndex_), 4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.unitsYOther_ ? this.coordinatesOther : this.coordinates, 4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.graphsOtherStartIndex_, this.stackedGraphOther_); 4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Draw events overlayed on graph if needed. 4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.eventName_ && this.eventInfo_) 4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.plotEvents_(ctx, 'rgb(255, 150, 150)', this.coordinates); 4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.graduation_divs_ = this.graduations_(this.coordinates, 0, false); 4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.unitsYOther_) { 4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.graduation_divs_ = this.graduation_divs_.concat( 4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.graduations_(this.coordinatesOther, 1, true)); 4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 0; i < this.graduation_divs_.length; ++i) 4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.resultNode_.appendChild(this.graduation_divs_[i]); 4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Draws events overlayed on top of an existing graph. 4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} ctx A canvas element object for drawing. 4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} strokeStyles A string representing the drawing style. 4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} coordinateSystem A Coordinates object representing the 4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * coordinate system of the graph. 4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.plotEvents_ = function(ctx, strokeStyles, coordinateSystem) { 4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.strokeStyle = strokeStyles; 4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.fillStyle = strokeStyles; 4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.lineWidth = 1.0; 4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.beginPath(); 4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var data = this.eventInfo_; 4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var index = 0; index < data.length; ++index) { 4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var event_time = data[index][0]; 4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var x = coordinateSystem.xPixel(event_time); 4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.moveTo(x, 0); 4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.lineTo(x, this.canvasElement_.offsetHeight); 4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.closePath(); 4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.stroke(); 4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Draws a line on the graph. 4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} ctx A canvas element object for drawing. 4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} strokeStyles A string representing the drawing style. 4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Array} data A list of [x, y] values representing the line to plot. 4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} coordinateSystem A Coordinates object representing the 4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * coordinate system of the graph. 4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.plotLine_ = function(ctx, strokeStyles, data, 4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) coordinateSystem) { 4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.strokeStyle = strokeStyles; 4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.fillStyle = strokeStyles; 4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.lineWidth = 2.0; 4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.beginPath(); 4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var initial = true; 4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var allPoints = []; 4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 0; i < data.length; ++i) { 4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var pointX = parseFloat(data[i][0]); 4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var pointY = parseFloat(data[i][1]); 4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var x = coordinateSystem.xPixel(pointX); 4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var y = coordinateSystem.yPixel(0); 4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (isNaN(pointY)) { 4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Re-set 'initial' if we're at a gap in the data. 4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) initial = true; 4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) y = coordinateSystem.yPixel(pointY); 4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (initial) 4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) initial = false; 4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.lineTo(x, y); 4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.moveTo(x, y); 4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!data[i].interpolated) { 4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) allPoints.push([x, y]); 4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.closePath(); 4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.stroke(); 4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!this.is_lookout_) { 5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Draw a small dot at each point. 5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 0; i < allPoints.length; ++i) { 5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.beginPath(); 5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.arc(allPoints[i][0], allPoints[i][1], 3, 0, Math.PI*2, true); 5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.fill(); 5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Fills an area under the given line on the graph. 5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} ctx A canvas element object for drawing. 5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} fillStyle A string representing the drawing style. 5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Array} data A list of [x, y] values representing the line to plot. 5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} coordinateSystem A Coordinates object representing the 5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * coordinate system of the graph. 5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.plotAreaUnderLine_ = function(ctx, fillStyle, data, 5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) coordinateSystem) { 5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!data[0]) { 5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; // nothing to draw 5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.beginPath(); 5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var x = coordinateSystem.xPixel(parseFloat(data[0][0]) || 0); 5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var y = coordinateSystem.yPixel(parseFloat(data[0][1]) || 0); 5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var y0 = coordinateSystem.yPixel(coordinateSystem.yMinValue()); 5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.moveTo(x, y0); 5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var point, i = 0; point = data[i]; ++i) { 5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var pointX = parseFloat(point[0]); 5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var pointY = parseFloat(point[1]); 5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (isNaN(pointX)) { continue; } // Skip an invalid point. 5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (isNaN(pointY)) { 5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.lineTo(x, y0); 5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var yWasNaN = true; 5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) x = coordinateSystem.xPixel(pointX); 5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) y = coordinateSystem.yPixel(pointY); 5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (yWasNaN) { 5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.lineTo(x, y0); 5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) yWasNaN = false; 5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.lineTo(x, y); 5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.lineTo(x, y0); 5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.lineWidth = 0; 5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Clear the area with white color first. 5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var COLOR_WHITE = 'rgb(255,255,255)'; 5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.strokeStyle = COLOR_WHITE; 5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.fillStyle = COLOR_WHITE; 5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.fill(); 5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Then, fill the area with the specified color. 5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.strokeStyle = fillStyle; 5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.fillStyle = fillStyle; 5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ctx.fill(); 5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Attaches event listeners to DOM nodes. 5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.attachEventListeners_ = function() { 5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var self = this; 5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.canvasElement_.parentNode.addEventListener( 5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'mousemove', function(evt) { self.onMouseMove_(evt); }, false); 5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.canvasElement_.parentNode.addEventListener( 5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'mouseover', function(evt) { self.onMouseOver_(evt); }, false); 5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.canvasElement_.parentNode.addEventListener( 5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'mouseout', function(evt) { self.onMouseOut_(evt); }, false); 5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.cursorDiv_.addEventListener( 5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'click', function(evt) { self.onMouseClick_(evt); }, false); 5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.cursorDivOther_.addEventListener( 5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'click', function(evt) { self.onMouseClick_(evt); }, false); 5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.eventDiv_.addEventListener( 5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'click', function(evt) { self.onMouseClick_(evt); }, false); 5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Update the horizontal line that is following where the mouse is hovering. 5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} evt A mouse event object representing a mouse move event. 5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.updateRuler_ = function(evt) { 5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var r = this.rulerDiv_; 5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) r.style.left = this.canvasElement_.offsetLeft + 'px'; 5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) r.style.top = this.canvasElement_.offsetTop + 'px'; 5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) r.style.width = this.canvasElement_.offsetWidth + 'px'; 5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var h = domUtils.pageXYOfEvent(evt).y - 5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) domUtils.pageXY(this.canvasElement_).y; 5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (h > this.canvasElement_.offsetHeight) 5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) h = this.canvasElement_.offsetHeight; 5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) r.style.height = h + 'px'; 5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Update the highlighted data point at the x value that the mouse is hovering 5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * over. 5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} coordinateSystem A Coordinates object representing the 6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * coordinate system of the graph. 6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} currentIndex The index into the |this.plotData| array of the 6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * data point being hovered over, for a given line. 6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} cursorDiv A DOM element div object representing the highlight 6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * itself. 6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} dataIndex The index into the |this.plotData| array of the 6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * line being hovered over. 6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.updateCursor_ = function(coordinateSystem, currentIndex, 6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cursorDiv, dataIndex) { 6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var c = cursorDiv; 6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) c.style.top = this.canvasElement_.offsetTop + 'px'; 6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) c.style.height = this.canvasElement_.offsetHeight + 'px'; 6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Left point is half-way to the previous x value, unless it's the first 6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // point, in which case it's the x value of the current point. 6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var leftPoint = null; 6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (currentIndex == 0) { 6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) leftPoint = this.canvasElement_.offsetLeft + 6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) coordinateSystem.xPixel(this.plotData_[dataIndex][0][0]); 6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else { 6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var left_x = this.canvasElement_.offsetLeft + 6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex - 1][0]); 6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var curr_x = this.canvasElement_.offsetLeft + 6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex][0]); 6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) leftPoint = (left_x + curr_x) / 2; 6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) c.style.left = leftPoint; 6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Width is half-way to the next x value minus the left point, unless it's 6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the last point, in which case it's the x value of the current point minus 6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the left point. 6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (currentIndex == this.plotData_[dataIndex].length - 1) { 6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var curr_x = this.canvasElement_.offsetLeft + 6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex][0]); 6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) c.style.width = curr_x - leftPoint; 6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else { 6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var next_x = this.canvasElement_.offsetLeft + 6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex + 1][0]); 6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var curr_x = this.canvasElement_.offsetLeft + 6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) coordinateSystem.xPixel(this.plotData_[dataIndex][currentIndex][0]); 6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) c.style.width = ((next_x + curr_x) / 2) - leftPoint; 6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Update the highlighted event at the x value that the mouse is hovering over. 6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} x The x-value (pixel) at which to draw the event highlight 6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * div. 6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} show Whether or not to show the highlight div. 6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.updateEventDiv_ = function(x, show) { 6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var c = this.eventDiv_; 6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) c.style.top = this.canvasElement_.offsetTop + 'px'; 6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) c.style.height = this.canvasElement_.offsetHeight + 'px'; 6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (show) { 6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) c.style.left = this.canvasElement_.offsetLeft + (x - 2); 6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) c.style.width = 8; 6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) c.style.width = 0; 6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Updates the hovering information. 6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Event} evt An event object, which specifies the position of the mouse 6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * cursor. 6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} show Whether or not to show the hovering info. Even if it's 6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * true, if the cursor position is out of the appropriate area, nothing will 6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * be shown. 6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.updateHoveringInfo_ = function(evt, show) { 6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var evtPageXY = domUtils.pageXYOfEvent(evt); 6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var hoveringInfoPageXY = this.hoveringInfo_.pageXY(); 6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var canvasPageXY = domUtils.pageXY(this.canvasElement_); 6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var coord = this.coordinates; 6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // p = the mouse cursor position in value coordinates. 6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var p = {'x': coord.xValue(evtPageXY.x - canvasPageXY.x), 6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'y': coord.yValue(evtPageXY.y - canvasPageXY.y)}; 6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!show || 6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) !(this.stackedGraph_ || this.stackedGraphOther_) || 6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) p.x < coord.xMinValue() || coord.xMaxValue() < p.x || 6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) p.y < coord.yMinValue() || coord.yMaxValue() < p.y) { 6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.hoveringInfo_.show(false); 6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.hoveringInfo_.show(true); 6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** 6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Finds the closest lines (upside and downside of the cursor position). 6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Returns a set of upside/downside line indices and point index on success 6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * or null. 7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) function findClosestLines(lines, opt_startIndex, opt_endIndex) { 7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var offsetIndex = opt_startIndex || 0; 7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines = 7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opt_endIndex != null ? lines.slice(offsetIndex, opt_endIndex) : 7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) opt_startIndex != null ? lines.slice(offsetIndex) : 7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lines; 7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var upsideClosestLineIndex = null; 7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var upsideClosestYDistance = coord.yValueRange(); 7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var downsideClosestLineIndex = null; 7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var downsideClosestYDistance = coord.yValueRange(); 7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var upsideClosestPointIndex = null; 7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var lineIndex = 0, line; line = lines[lineIndex]; ++lineIndex) { 7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 1; line[i]; ++i) { 7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var p0 = line[i - 1], p1 = line[i]; 7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (p0[0] <= p.x && p.x < p1[0]) { 7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Calculate y-value of the line at p.x, which is the cursor point. 7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var y = (p.x - p0[0]) / (p1[0] - p0[0]) * (p1[1] - p0[1]) + p0[1]; 7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (p.y < y && y - p.y < upsideClosestYDistance) { 7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upsideClosestLineIndex = lineIndex; 7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upsideClosestYDistance = y - p.y; 7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (p.x - p0[0] < p1[0] - p.x) { 7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upsideClosestPointIndex = i - 1; 7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upsideClosestPointIndex = i; 7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (y <= p.y && p.y - y < downsideClosestYDistance) { 7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) downsideClosestLineIndex = lineIndex; 7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) downsideClosestYDistance = p.y - y; 7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (upsideClosestLineIndex != null && 7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) upsideClosestPointIndex != null) ? 7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) {'upsideLineIndex': offsetIndex + upsideClosestLineIndex, 7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'downsideLineIndex': downsideClosestYDistance == null ? null : 7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) offsetIndex + downsideClosestLineIndex, 7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'upsidePointIndex': offsetIndex + upsideClosestPointIndex} : 7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) null; 7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Find the closest lines above and below the mouse cursor. 7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var closest = null; 7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Since the other set of graphs are drawn over the first set, try to find 7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the closest lines from the other set of graphs first. 7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.graphsOtherStartIndex_ && this.stackedGraphOther_) { 7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) closest = findClosestLines(this.plotData_, this.graphsOtherStartIndex_); 7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!closest && this.stackedGraph_) { 7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) closest = this.graphsOtherStartIndex_ ? 7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) findClosestLines(this.plotData_, 0, this.graphsOtherStartIndex_) : 7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) findClosestLines(this.plotData_); 7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!closest) { 7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.hoveringInfo_.show(false); 7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Update the contents of the hovering info box. 7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Color indicator, description and the value of the item. 7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.hoveringInfo_.setColorIndicator( 7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.getDataColor(closest.upsideLineIndex)); 7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.hoveringInfo_.setLegendText( 7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.dataDescriptions_[closest.upsideLineIndex]); 7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var y1 = this.plotData_[closest.upsideLineIndex][closest.upsidePointIndex][1]; 7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var y0 = closest.downsideLineIndex == null ? 7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 0 : 7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.plotData_[closest.downsideLineIndex][closest.upsidePointIndex][1]; 7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.hoveringInfo_.setItemValue(y1 - y0); 7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Locate the hovering info box near the mouse cursor. 7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var DIV_X_OFFSET = 10, DIV_Y_OFFSET = -20; 7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (evtPageXY.x + this.hoveringInfo_.getElement().offsetWidth < 7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) canvasPageXY.x + this.canvasElement_.offsetWidth) { 7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.hoveringInfo_.locateAtPageXY(evtPageXY.x + DIV_X_OFFSET, 7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) evtPageXY.y + DIV_Y_OFFSET); 7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { // If lacking space at the right side, locate it at the left side. 7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.hoveringInfo_.locateAtPageXY( 7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) evtPageXY.x - this.hoveringInfo_.getElement().offsetWidth - DIV_X_OFFSET, 7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) evtPageXY.y + DIV_Y_OFFSET); 7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Handle a mouse move event. 7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} evt A mouse event object representing a mouse move event. 7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.onMouseMove_ = function(evt) { 7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var self = this; 7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var canvas = evt.currentTarget.firstChild; 7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var evtPageXY = domUtils.pageXYOfEvent(evt); 7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var canvasPageXY = domUtils.pageXY(this.canvasElement_); 8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var positionX = evtPageXY.x - canvasPageXY.x; 8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var positionY = evtPageXY.y - canvasPageXY.y; 8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Identify the index of the x value that is closest to the mouse x value. 8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var xValue = this.coordinates.xValue(positionX); 8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var lineIndex = !this.stackedGraph_ ? 0 : 8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.graphsOtherStartIndex_ ? this.graphsOtherStartIndex_ - 1 : 8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.plotData_.length - 1; 8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var line = this.plotData_[lineIndex]; 8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var min_diff = Math.abs(line[0][0] - xValue); 8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) indexValueX = 0; 8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 1; i < line.length; ++i) { 8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var diff = Math.abs(line[i][0] - xValue); 8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (diff < min_diff) { 8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) min_diff = diff; 8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) indexValueX = i; 8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Identify the index of the x value closest to the mouse x value for the 8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // other graph being overlayed on top of the original graph, if one exists. 8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.unitsYOther_) { 8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var xValue = this.coordinatesOther.xValue(positionX); 8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var lineIndexOther = !this.stackedGraphOther_ ? 8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.graphsOtherStartIndex_ : this.plotData_.length - 1; 8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var lineOther = this.plotData_[lineIndexOther]; 8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var min_diff = Math.abs(lineOther[0][0] - xValue); 8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var indexValueXOther = 0; 8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 1; i < lineOther.length; ++i) { 8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var diff = Math.abs(lineOther[i][0] - xValue); 8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (diff < min_diff) { 8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) min_diff = diff; 8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) indexValueXOther = i; 8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Update coordinate information displayed directly underneath the graph. 8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) function legendLabel(lineIndex, opt_labelText) { 8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return '<span style="color:' + self.getDataColor(lineIndex) + '">' + 8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (opt_labelText || self.dataDescriptions_[lineIndex]) + 8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) '</span>: '; 8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) function valuesAtCursor(lineIndex, pointIndex, unitsY, yValue) { 8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return '<span style="color:' + self.getDataColor(lineIndex) + '">' + 8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.plotData_[lineIndex][pointIndex][0] + ' ' + self.unitsX_ + ': ' + 8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) addCommas(self.plotData_[lineIndex][pointIndex][1].toFixed(2)) + ' ' + 8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) unitsY + '</span> [hovering at ' + addCommas(yValue.toFixed(2)) + 8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ' ' + unitsY + ']'; 8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.infoBox_.rows[0].label.innerHTML = legendLabel(lineIndex); 8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.infoBox_.rows[0].content.innerHTML = valuesAtCursor( 8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lineIndex, indexValueX, this.unitsY_, this.coordinates.yValue(positionY)); 8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var row = this.infoBox_.rows[1]; 8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.unitsYOther_) { 8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) row.label.innerHTML = legendLabel(lineIndexOther); 8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) row.content.innerHTML = valuesAtCursor( 8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lineIndexOther, indexValueXOther, this.unitsYOther_, 8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.coordinatesOther.yValue(positionY)); 8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (this.graphsOtherStartIndex_) { 8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) row.label.innerHTML = legendLabel( 8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.stackedGraphOther_ ? 8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.plotData_.length - 1 : this.graphsOtherStartIndex_); 8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) row.content.innerHTML = valuesAtCursor( 8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.stackedGraphOther_ ? 8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.plotData_.length - 1 : this.graphsOtherStartIndex_, 8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) indexValueX, this.unitsY_, this.coordinates.yValue(positionY)); 8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (!this.stackedGraph_ && this.dataDescriptions_.length > 1) { 8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) row.label.innerHTML = legendLabel(1); 8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) row.content.innerHTML = valuesAtCursor( 8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1, indexValueX, this.unitsY_, this.coordinates.yValue(positionY)); 8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (row) { 8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) row.label.innerHTML = ''; 8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) row.content.innerHTML = ''; 8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If there is a horizontal marker, also display deltas relative to it. 8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.horizontal_marker_) { 8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var baseline = this.horizontal_marker_.value; 8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var delta = this.coordinates.yValue(positionY) - baseline; 8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var fraction = delta / baseline; // Allow division by 0. 8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var deltaStr = (delta >= 0 ? '+' : '') + delta.toFixed(0) + ' ' + 8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.unitsY_; 8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var percentStr = (fraction >= 0 ? '+' : '') + (fraction * 100).toFixed(3) + 8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) '%'; 8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.baselineDeltasTd_.innerHTML = deltaStr + ': ' + percentStr; 8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.unitsYOther_) { 8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var baseline = this.horizontal_marker_.otherValue; 8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var yValue2 = this.coordinatesOther.yValue(positionY); 8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var delta = yValue2 - baseline; 8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var fraction = delta / baseline; // Allow division by 0. 8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var deltaStr = (delta >= 0 ? '+' : '') + delta.toFixed(0) + ' ' + 8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.unitsYOther_; 8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var percentStr = (fraction >= 0 ? '+' : '') + 8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (fraction * 100).toFixed(3) + '%'; 9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.baselineDeltasTd_.innerHTML += '<br>' + deltaStr + ': ' + percentStr; 9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.updateRuler_(evt); 9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.updateCursor_(this.coordinates, indexValueX, this.cursorDiv_, 0); 9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.unitsYOther_ && this.graphsOtherStartIndex_) { 9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.updateCursor_(this.coordinatesOther, indexValueXOther, 9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.cursorDivOther_, this.graphsOtherStartIndex_); 9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If there are events displayed, see if we're hovering close to an existing 9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // event on the graph, and if so, display the metadata associated with it. 9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.eventName_ != null && this.eventInfo_ != null) { 9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.infoBox_.rows[1].label.innerHTML = 'Event "' + this.eventName_ + 9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) '": '; 9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var data = this.eventInfo_; 9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var showed_event = false; 9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var x = 0; 9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var index = 0; index < data.length; ++index) { 9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var event_time = data[index][0]; 9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) x = this.coordinates.xPixel(event_time); 9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (positionX >= x - 10 && positionX <= x + 10) { 9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var metadata = data[index][1]; 9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var metadata_str = ""; 9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var meta_key in metadata) 9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) metadata_str += meta_key + ': ' + metadata[meta_key] + ', '; 9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) metadata_str = metadata_str.substring(0, metadata_str.length - 2); 9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.infoBox_.rows[1].content.innerHTML = event_time + ' ' + 9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.unitsX_ + ': {' + metadata_str + '}'; 9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) showed_event = true; 9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.updateEventDiv_(x, true); 9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!showed_event) { 9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.coordinatesTdOther_.innerHTML = 9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'move mouse close to vertical event marker'; 9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.updateEventDiv_(x, false); 9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.updateHoveringInfo_(evt, true); 9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Handle a mouse over event. 9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} evt A mouse event object representing a mouse move event. 9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.onMouseOver_ = function(evt) { 9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.updateHoveringInfo_(evt, true); 9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Handle a mouse out event. 9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} evt A mouse event object representing a mouse move event. 9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.onMouseOut_ = function(evt) { 9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.updateHoveringInfo_(evt, false); 9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Handle a mouse click event. 9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} evt A mouse event object representing a mouse click event. 9675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.onMouseClick_ = function(evt) { 9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Shift-click controls the horizontal reference line. 9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (evt.shiftKey) { 9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.horizontal_marker_) 9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.horizontal_marker_.remove(); 9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var canvasY = domUtils.pageXYOfEvent(evt).y - 9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) domUtils.pageXY(this.canvasElement_).y; 9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.horizontal_marker_ = new HorizontalMarker( 9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.canvasElement_, 9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.coordinates.yValue(canvasY), 9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (this.coordinatesOther ? this.coordinatesOther.yValue(canvasY) : null)); 9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Insert before cursor node, otherwise it catches clicks. 9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.cursorDiv_.parentNode.insertBefore( 9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.horizontal_marker_.markerDiv, this.cursorDiv_); 9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.horizontal_marker_.locateAt(this.canvasElement_, canvasY); 9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 9855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 9885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Generates and returns a list of div objects representing horizontal lines in 9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the graph that indicate y-axis values at a computed interval. 9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} coordinateSystem a Coordinates object representing the 9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * coordinate system for which the graduations should be created. 9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} colorIndex An index into the |this.colors| array representing 9945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the color to make the graduations in the event that two graphs with 9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * different coordinate systems are being overlayed on the same plot. 9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} isRightSide Whether or not the graduations should have 9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * right-aligned text (used when the graduations are for a second graph 9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * that is being overlayed on top of another graph). 9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Array} An array of DOM Element objects representing the divs. 10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.graduations_ = function(coordinateSystem, colorIndex, 10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) isRightSide) { 10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Don't allow a graduation in the bottom 5% of the chart or the number label 10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // would overflow the chart bounds. 10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var yMin = coordinateSystem.yLowerLimitValue() + 10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) .05 * coordinateSystem.yValueRange(); 10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var yRange = coordinateSystem.yUpperLimitValue() - yMin; 10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Use the largest scale that fits 3 or more graduations. 10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We allow scales of [...,500, 250, 100, 50, 25, 10,...]. 10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var scale = 5000000000; 10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (scale) { 10135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (Math.floor(yRange / scale) > 2) break; // 5s. 10145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scale /= 2; 10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (Math.floor(yRange / scale) > 2) break; // 2.5s. 10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scale /= 2.5; 10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (Math.floor(yRange / scale) > 2) break; // 1s. 10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scale /= 2; 10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 10205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var graduationPosition = yMin + (scale - yMin % scale); 10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var graduationDivs = []; 10235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (graduationPosition < coordinateSystem.yUpperLimitValue() || 10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) yRange == 0) { 10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var graduation = document.createElement('div'); 10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var canvasPosition; 10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (yRange == 0) { 10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Center the graduation vertically. 10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) canvasPosition = this.canvasElement_.offsetHeight / 2; 10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 10315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) canvasPosition = coordinateSystem.yPixel(graduationPosition); 10325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.unitsYOther_) { 10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graduation.style.borderTop = '1px dashed ' + 10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.makeColorTransparent(colorIndex, 0.4) 10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graduation.style.borderTop = '1px dashed rgba(0,0,0,.08)'; 10385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graduation.style.position = 'absolute'; 10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graduation.style.left = this.canvasElement_.offsetLeft + 'px'; 10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graduation.style.top = canvasPosition + this.canvasElement_.offsetTop + 10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'px'; 10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graduation.style.width = this.canvasElement_.offsetWidth - 10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.canvasElement_.offsetLeft + 'px'; 10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graduation.style.paddingLeft = '4px'; 10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.unitsYOther_) 10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graduation.style.color = this.makeColorTransparent(colorIndex, 0.9) 10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graduation.style.color = 'rgba(0,0,0,.4)'; 10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graduation.style.fontSize = '9px'; 10515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graduation.style.paddingTop = '0'; 10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graduation.style.zIndex = '-1'; 10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (isRightSide) 10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graduation.style.textAlign = 'right'; 10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (yRange == 0) 10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graduation.innerHTML = addCommas(yMin); 10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 10585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graduation.innerHTML = addCommas(graduationPosition); 10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graduationDivs.push(graduation); 10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (yRange == 0) 10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) graduationPosition += scale; 10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return graduationDivs; 10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 10685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Generates and returns a div object representing the horizontal line that 10695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * follows the mouse pointer around the plot. 10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 10715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Object} A DOM Element object representing the div. 10725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.ruler_ = function() { 10745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var ruler = document.createElement('div'); 10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ruler.setAttribute('class', 'plot-ruler'); 10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ruler.style.borderBottom = '1px dotted black'; 10775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ruler.style.position = 'absolute'; 10785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ruler.style.left = '-2px'; 10795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ruler.style.top = '-2px'; 10805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ruler.style.width = '0px'; 10815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ruler.style.height = '0px'; 10825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ruler; 10835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 10845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 10855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 10865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Generates and returns a canvas object representing the plot itself. 10875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 10885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Object} A DOM Element object representing the canvas. 10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 10905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.canvas_ = function() { 10915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var canvas = document.createElement('canvas'); 10925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) canvas.setAttribute('id', '_canvas'); 10935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) canvas.setAttribute('class', 'plot'); 10945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) canvas.setAttribute('width', this.coordinates.widthMax); 10955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) canvas.setAttribute('height', this.coordinates.heightMax); 10965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) canvas.plotter = this; 10975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return canvas; 10985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 10995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Generates and returns a div object representing the coordinate information 11025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * displayed directly underneath a graph. 11035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Object} A DOM Element object representing the div. 11055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 11065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.coordinates_ = function() { 11075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var coordinatesDiv = document.createElement('div'); 11085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var table_html = '<table border=0 width="100%"'; 11095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.is_lookout_) { 11105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table_html += ' style="font-size:0.8em"'; 11115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 11125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table_html += '><tbody><tr>'; 11135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table_html += '<td><span class="legend_item"></span>' + 11145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) '<span class="plot-coordinates"><i>move mouse over graph</i></span></td>'; 11155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table_html += '<td align="right">x-axis is ' + this.unitsX_ + '</td>'; 11165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table_html += '</tr><tr>'; 11175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table_html += '<td><span class="legend_item"></span>' + 11185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) '<span class="plot-coordinates"></span></td>'; 11195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!this.is_lookout_) { 11215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table_html += '<td align="right" style="color: ' + HorizontalMarker.COLOR + 11225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) '"><i>Shift-click to place baseline.</i></td>'; 11235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 11245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) table_html += '</tr></tbody></table>'; 11255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) coordinatesDiv.innerHTML = table_html; 11265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var trs = coordinatesDiv.querySelectorAll('tr'); 11285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.infoBox_ = {rows: []}; 11295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.infoBox_.rows.push({ 11305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) label: trs[0].querySelector('span.legend_item'), 11315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) content: trs[0].querySelector('span.plot-coordinates')}); 11325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.dataDescriptions_.length > 1 || this.eventName_) { 11335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.infoBox_.rows.push({ 11345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) label: trs[1].querySelector('span.legend_item'), 11355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) content: trs[1].querySelector('span.plot-coordinates')}); 11365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 11375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.baselineDeltasTd_ = trs[1].childNodes[1]; 11395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Add a summary of legends in case of stacked graphs. 11415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.stackedGraph_ || this.stackedGraphOther_) { 11425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var legendPane = document.createElement('div'); 11435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) legendPane.style.fontSize = '80%'; 11445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) coordinatesDiv.appendChild(legendPane); 11455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.graphsOtherStartIndex_) { 11475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) legendPane.appendChild( 11485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.createLegendsSummaryElement_( 11495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.dataDescriptions_.slice(0, this.graphsOtherStartIndex_), 11505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 0)); 11515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) legendPane.appendChild( 11525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.createLegendsSummaryElement_( 11535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.dataDescriptions_.slice(this.graphsOtherStartIndex_), 11545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.graphsOtherStartIndex_)); 11555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 11565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) legendPane.appendChild( 11575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.createLegendsSummaryElement_(this.dataDescriptions_, 0)); 11585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 11595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 11605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return coordinatesDiv; 11625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 11635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 11655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Creates and returns a DOM element which shows a summary of legends. 11665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 11675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {!Array.<string>} legendTexts An array of legend texts. 11685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} colorIndexOffset Offset index for color. i-th legend text 11695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * has an indicator in {@code (colorIndexOffset + i)}-th color 11705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {!Element} An element which shows a summary of legends. 11715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 11725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Plotter.prototype.createLegendsSummaryElement_ = function(legendTexts, 11735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) colorIndexOffset) { 11745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var containerElem = document.createElement('div'); 11755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 0, text; text = legendTexts[i]; ++i) { 11775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var colorIndicatorElem = document.createElement('div'); 11785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) colorIndicatorElem.style.display = 'inline-block'; 11795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) colorIndicatorElem.style.width = '1em'; 11805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) colorIndicatorElem.style.height = '1em'; 11815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) colorIndicatorElem.style.verticalAlign = 'text-bottom'; 11825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) colorIndicatorElem.style.margin = '0 0.24em 0 0'; 11835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) colorIndicatorElem.style.border = '1px solid #000'; 11845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) colorIndicatorElem.style.backgroundColor = 11855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.getDataColor(colorIndexOffset + i); 11865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var legendTextElem = document.createElement('span'); 11875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) legendTextElem.textContent = text; 11885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var legendElem = document.createElement('span'); 11895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) legendElem.style.whiteSpace = 'nowrap'; 11905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) legendElem.appendChild(colorIndicatorElem); 11915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) legendElem.appendChild(legendTextElem); 11925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) legendElem.style.margin = '0 0.8em 0 0'; 11935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) containerElem.appendChild(legendElem); 11945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Add a space to break lines if necessary. 11955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) containerElem.appendChild(document.createTextNode(' ')); 11965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 11975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 11985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return containerElem; 11995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1200