Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 /* Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 * Use of this source code is governed by a BSD-style license that can be | 2 * Use of this source code is governed by a BSD-style license that can be |
| 3 * found in the LICENSE file. */ | 3 * found in the LICENSE file. */ |
| 4 | 4 |
| 5 'use strict'; | 5 'use strict'; |
|
Dan Beam
2012/08/15 20:53:53
I think you want to put this 'use strict'; inside
clintstaley
2012/08/16 00:20:25
Done.
| |
| 6 | 6 |
| 7 /* convert to cr.define('PerformanceMonitor', function() {... | 7 cr.define('performance_monitor', function() { |
| 8 * when integrating into webui */ | |
| 9 | |
| 10 var Installer = function() { | |
| 11 /** | 8 /** |
| 12 * Enum for time ranges, giving a descriptive name, time span prior to |now|, | 9 * Enum for time ranges, giving a descriptive name, time span prior to |now|, |
| 13 * data point resolution, and time-label frequency and format for each. | 10 * data point resolution, and time-label frequency and format for each. |
| 14 * @enum {{ | 11 * @enum {{ |
| 15 * value: !number, | 12 * value: !number, |
| 16 * name: !string, | 13 * name: !string, |
| 17 * timeSpan: !number, | 14 * timeSpan: !number, |
| 18 * resolution: !number, | 15 * resolution: !number, |
| 19 * labelEvery: !number, | 16 * labelEvery: !number, |
| 20 * format: !string | 17 * format: !string |
| 21 * }} | 18 * }} |
| 22 * @private | 19 * @private |
| 23 */ | 20 */ |
| 24 var TimeRange_ = { | 21 var TimeRange_ = { |
| 22 // Prior 12 min, resolution of 1s, at most 720 points. | |
| 23 // Labels at 60 point (1 min) intervals. | |
| 24 minutes: {value: 0, name: 'Last 12 min', timeSpan: 720 * 1000, | |
| 25 resolution: 1000, labelEvery: 60, format: 'MM-dd'}, | |
| 26 | |
| 27 // Prior hour, resolution of 5s, at most 720 points. | |
| 28 // Labels at 60 point (5 min) intervals. | |
| 29 hour: {value: 1, name: 'Last Hour', timeSpan: 3600 * 1000, | |
| 30 resolution: 1000 * 5, labelEvery: 60, format: 'MM-dd'}, | |
| 31 | |
| 25 // Prior day, resolution of 2 min, at most 720 points. | 32 // Prior day, resolution of 2 min, at most 720 points. |
| 26 // Labels at 90 point (3 hour) intervals. | 33 // Labels at 90 point (3 hour) intervals. |
| 27 day: {value: 0, name: 'Last Day', timeSpan: 24 * 3600 * 1000, | 34 day: {value: 2, name: 'Last Day', timeSpan: 24 * 3600 * 1000, |
| 28 resolution: 1000 * 60 * 2, labelEvery: 90, format: 'MM-dd'}, | 35 resolution: 1000 * 60 * 2, labelEvery: 90, format: 'MM-dd'}, |
| 29 | 36 |
| 30 // Prior week, resolution of 15 min -- at most 672 data points. | 37 // Prior week, resolution of 15 min -- at most 672 data points. |
| 31 // Labels at 96 point (daily) intervals. | 38 // Labels at 96 point (daily) intervals. |
| 32 week: {value: 1, name: 'Last Week', timeSpan: 7 * 24 * 3600 * 1000, | 39 week: {value: 3, name: 'Last Week', timeSpan: 7 * 24 * 3600 * 1000, |
| 33 resolution: 1000 * 60 * 15, labelEvery: 96, format: 'M/d'}, | 40 resolution: 1000 * 60 * 15, labelEvery: 96, format: 'M/d'}, |
| 34 | 41 |
| 35 // Prior month (30 days), resolution of 1 hr -- at most 720 data points. | 42 // Prior month (30 days), resolution of 1 hr -- at most 720 data points. |
| 36 // Labels at 168 point (weekly) intervals. | 43 // Labels at 168 point (weekly) intervals. |
| 37 month: {value: 2, name: 'Last Month', timeSpan: 30 * 24 * 3600 * 1000, | 44 month: {value: 4, name: 'Last Month', timeSpan: 30 * 24 * 3600 * 1000, |
| 38 resolution: 1000 * 3600, labelEvery: 168, format: 'M/d'}, | 45 resolution: 1000 * 3600, labelEvery: 168, format: 'M/d'}, |
| 39 | 46 |
| 40 // Prior quarter (90 days), resolution of 3 hr -- at most 720 data points. | 47 // Prior quarter (90 days), resolution of 3 hr -- at most 720 data points. |
| 41 // Labels at 112 point (fortnightly) intervals. | 48 // Labels at 112 point (fortnightly) intervals. |
| 42 quarter: {value: 3, name: 'Last Quarter', timeSpan: 90 * 24 * 3600 * 1000, | 49 quarter: {value: 5, name: 'Last Quarter', timeSpan: 90 * 24 * 3600 * 1000, |
| 43 resolution: 1000 * 3600 * 3, labelEvery: 112, format: 'M/yy'}, | 50 resolution: 1000 * 3600 * 3, labelEvery: 112, format: 'M/yy'}, |
| 44 }; | 51 }; |
| 45 | 52 |
| 53 /* | |
| 54 * Offset, in ms, by which to subtract to convert GMT to local time | |
| 55 * @type {!number} | |
| 56 */ | |
| 57 var timezoneOffset = new Date().getTimezoneOffset() * 60000; | |
| 58 | |
| 46 /** @constructor */ | 59 /** @constructor */ |
| 47 function PerformanceMonitor() { | 60 function PerformanceMonitor() { |
| 48 this.__proto__ = PerformanceMonitor.prototype; | 61 this.__proto__ = PerformanceMonitor.prototype; |
| 49 /** | 62 /** |
| 50 * All metrics have entries, but those not displayed have an empty div list. | 63 * All metrics have entries, but those not displayed have an empty div list. |
| 51 * If a div list is not empty, the associated data will be non-null, or | 64 * If a div list is not empty, the associated data will be non-null, or |
| 52 * null but about to be filled by webui response. Thus, any metric with | 65 * null but about to be filled by webui handler response. Thus, any metric |
| 53 * non-empty div list but null data is awaiting a data response from the | 66 * with non-empty div list but null data is awaiting a data response |
| 54 * webui. | 67 * from the webui handler. The webui handler uses numbers to uniquely |
| 68 * identify metric and event types, so we use the same numbers (in | |
| 69 * string form) for the map key, repeating the id number in the |id| | |
| 70 * field, as a number. | |
| 55 * @type {Object.<string, { | 71 * @type {Object.<string, { |
| 72 * id: !number, | |
| 73 * description: !string, | |
| 74 * units: !string, | |
| 75 * yAxis: !{max: !number, color: !string}, | |
| 56 * divs: !Array.<HTMLDivElement>, | 76 * divs: !Array.<HTMLDivElement>, |
| 57 * yAxis: !{max: !number, color: !string}, | 77 * data: ?Array.<{time: !number, value: !number}> |
| 58 * data: ?Array.<{time: !number, value: !number}>, | |
| 59 * description: !string, | |
| 60 * units: !string | |
| 61 * }>} | 78 * }>} |
| 62 * @private | 79 * @private |
| 63 */ | 80 */ |
| 64 this.metricMap_ = { | 81 this.metricMap_ = {}; |
| 65 jankiness: { | |
| 66 divs: [], | |
| 67 yAxis: {max: 100, color: 'rgb(255, 128, 128)'}, | |
| 68 data: null, | |
| 69 description: 'Jankiness', | |
| 70 units: 'milliJanks' | |
| 71 }, | |
| 72 oddness: { | |
| 73 divs: [], | |
| 74 yAxis: {max: 20, color: 'rgb(0, 192, 0)'}, | |
| 75 data: null, | |
| 76 description: 'Oddness', | |
| 77 units: 'kOdds' | |
| 78 } | |
| 79 }; | |
| 80 | 82 |
| 81 /* | 83 /* |
| 82 * Similar data for events, though no yAxis info is needed since events | 84 * Similar data for events, though no yAxis info is needed since events |
| 83 * are simply labelled markers at X locations. Rules regarding null data | 85 * are simply labelled markers at X locations. Rules regarding null data |
| 84 * with non-empty div list apply here as for metricMap_ above. | 86 * with non-empty div list apply here as for metricMap_ above. |
| 85 * @type {Object.<string, { | 87 * @type {Object.<number, { |
| 86 * divs: !Array.<HTMLDivElement>, | 88 * id: !number, |
| 87 * description: !string, | 89 * description: !string, |
| 88 * color: !string, | 90 * color: !string, |
| 91 * divs: !Array.<HTMLDivElement>, | |
| 89 * data: ?Array.<{time: !number, longDescription: !string}> | 92 * data: ?Array.<{time: !number, longDescription: !string}> |
|
Dan Beam
2012/08/15 20:53:53
nit: ! in front of number|string|boolean|function
clintstaley
2012/08/16 00:20:25
I guess I need to understand this a bit better. n
Dan Beam
2012/08/16 00:32:42
! in front of number|boolean|string|function isn't
clintstaley
2012/08/17 16:37:56
Done.
| |
| 90 * }>} | 93 * }>} |
| 91 * @private | 94 * @private |
| 92 */ | 95 */ |
| 93 this.eventMap_ = { | 96 this.eventMap_ = {}; |
| 94 wampusAttacks: { | |
| 95 divs: [], | |
| 96 description: 'Wampus Attack', | |
| 97 color: 'rgb(0, 0, 255)', | |
| 98 data: null | |
| 99 }, | |
| 100 solarEclipses: { | |
| 101 divs: [], | |
| 102 description: 'Solar Eclipse', | |
| 103 color: 'rgb(255, 0, 255)', | |
| 104 data: null | |
| 105 } | |
| 106 }; | |
| 107 | 97 |
| 108 /** | 98 /** |
| 109 * Time periods in which the browser was active and collecting metrics. | 99 * Time periods in which the browser was active and collecting metrics |
| 110 * and events. | 100 * and events. |
| 111 * @type {!Array.<{start: !number, end: !number}>} | 101 * @type {!Array.<{start: !number, end: !number}>} |
| 112 * @private | 102 * @private |
| 113 */ | 103 */ |
| 114 this.intervals_ = []; | 104 this.intervals_ = []; |
| 115 | 105 |
| 116 this.setupCheckboxes_( | |
| 117 '#chooseMetrics', this.metricMap_, this.addMetric, this.dropMetric); | |
| 118 this.setupCheckboxes_( | |
| 119 '#chooseEvents', this.eventMap_, this.addEventType, this.dropEventType); | |
| 120 this.setupTimeRangeChooser_(); | 106 this.setupTimeRangeChooser_(); |
| 107 chrome.send('getAllEventTypes'); | |
| 108 chrome.send('getAllMetricTypes'); | |
| 121 this.setupMainChart_(); | 109 this.setupMainChart_(); |
| 122 TimeRange_.day.element.click().call(this); | 110 TimeRange_.day.element.click(); |
| 123 } | 111 } |
| 124 | 112 |
| 125 PerformanceMonitor.prototype = { | 113 PerformanceMonitor.prototype = { |
| 126 /** | 114 /** |
| 127 * Set up the radio button set to choose time range. Use div#radioTemplate | 115 * Receive a list of all metrics, and populate |this.metricMap_| to |
| 116 * reflect said list. Reconfigure the checkbox set for metric selection. | |
| 117 * @param {Array.<{metricType: !String, shortDescription: !String}>} | |
|
Dan Beam
2012/08/15 20:53:53
s/String/string/g (String = new String("blah"), st
clintstaley
2012/08/16 00:20:25
Sorry. Too much Java. :) Fixed here and elsewher
clintstaley
2012/08/17 16:37:56
Done.
| |
| 118 * allMetrics All metrics from which to select. | |
| 119 */ | |
| 120 getAllMetricTypesCallback: function(allMetrics) { | |
| 121 for (var i = 0; i < allMetrics.length; i++) { | |
| 122 var metric = allMetrics[i]; | |
| 123 | |
| 124 this.metricMap_[metric.metricType] = { | |
| 125 id: metric.metricType, | |
| 126 description: metric.shortDescription, | |
| 127 units: metric.units, | |
| 128 yAxis: {min: 0, max: metric.maxValue, color: 'rgb(0, 0, 255'}, | |
|
Dan Beam
2012/08/15 20:53:53
color: 'rgba(0,0,255)'
^--- ri
clintstaley
2012/08/16 00:20:25
That's a Flot property, not HTML. Flot wants rgb n
Dan Beam
2012/08/16 00:32:42
a) you're missing an end ')', which is mainly what
clintstaley
2012/08/17 16:37:56
Doh. Sorry to miss that. And thanks for the JQue
| |
| 129 divs: [], | |
| 130 data: null | |
| 131 }; | |
| 132 } | |
| 133 | |
| 134 this.setupCheckboxes_($('#choose-metrics')[0], | |
| 135 this.metricMap_, this.addMetric, this.dropMetric); | |
| 136 }, | |
| 137 | |
| 138 /** | |
| 139 * Receive a list of all event types, and populate |this.eventMap_| to | |
| 140 * reflect said list. Reconfigure the checkbox set for event selection. | |
| 141 * @param {Array.<{eventType: !String, shortDescription: !String}>} | |
| 142 * allEvents All events from which to select. | |
| 143 */ | |
| 144 getAllEventTypesCallback: function(allEvents) { | |
| 145 for (var i = 0; i < allEvents.length; i++) { | |
| 146 var eventInfo = allEvents[i]; | |
| 147 | |
| 148 this.eventMap_[eventInfo.eventType] = { | |
| 149 id: eventInfo.eventType, | |
| 150 color: 'rgb(255, 0, 0)', | |
|
Dan Beam
2012/08/15 20:53:53
this could be 'red' and it'd probably be easier to
clintstaley
2012/08/16 00:20:25
Same answer, sadly. Flot requirement.
clintstaley
2012/08/17 16:37:56
Done.
| |
| 151 data: null, | |
| 152 description: eventInfo.shortDescription, | |
| 153 divs: [] | |
| 154 }; | |
| 155 } | |
| 156 | |
| 157 this.setupCheckboxes_($('#choose-events')[0], | |
| 158 this.eventMap_, this.addEventType, this.dropEventType); | |
| 159 }, | |
| 160 | |
| 161 /** | |
| 162 * Set up the radio button set to choose time range. Use div#radio-template | |
| 128 * as a template. | 163 * as a template. |
| 129 * @private | 164 * @private |
| 130 */ | 165 */ |
| 131 setupTimeRangeChooser_: function() { | 166 setupTimeRangeChooser_: function() { |
| 132 var timeDiv = $('#chooseTimeRange')[0]; | 167 var timeDiv = $('#choose-time-range')[0]; |
| 133 var radioTemplate = $('#radioTemplate')[0]; | 168 var radioTemplate = $('#radio-template')[0]; |
| 134 | 169 |
| 135 for (var time in TimeRange_) { | 170 for (var time in TimeRange_) { |
| 136 var timeRange = TimeRange_[time]; | 171 var timeRange = TimeRange_[time]; |
| 137 var radio = radioTemplate.cloneNode(true); | 172 var radio = radioTemplate.cloneNode(true); |
| 138 var input = radio.querySelector('input'); | 173 var input = radio.querySelector('input'); |
| 139 | 174 |
| 140 input.value = timeRange.value; | 175 input.value = timeRange.value; |
| 141 input.timeRange = timeRange; | 176 input.timeRange = timeRange; |
| 142 radio.querySelector('span').innerText = timeRange.name; | 177 radio.querySelector('span').innerText = timeRange.name; |
| 143 timeDiv.appendChild(radio); | 178 timeDiv.appendChild(radio); |
| 144 timeRange.element = input; | 179 timeRange.element = input; |
| 145 } | 180 } |
| 146 | 181 |
| 147 timeDiv.addEventListener('click', function(e) { | 182 timeDiv.addEventListener('click', function(e) { |
| 148 if (!e.target.webkitMatchesSelector('input[type="radio"]')) | 183 if (!e.target.webkitMatchesSelector('input[type="radio"]')) |
| 149 return; | 184 return; |
| 150 | 185 |
| 151 this.setTimeRange(e.target.timeRange); | 186 this.setTimeRange(e.target.timeRange); |
| 152 }.bind(this)); | 187 }.bind(this)); |
| 153 }, | 188 }, |
| 154 | 189 |
| 155 /** | 190 /** |
| 156 * Generalized function for setting up checkbox blocks for either events | 191 * Generalized function for setting up checkbox blocks for either events |
| 157 * or metrics. Take a div ID |divId| into which to place the checkboxes, | 192 * or metrics. Take a div |div| into which to place the checkboxes, |
| 158 * and a map |optionMap| with values that each include a property | 193 * and a map |optionMap| with values that each include a property |
| 159 * |description|. Set up one checkbox for each entry in |optionMap| | 194 * |description|. Set up one checkbox for each entry in |optionMap| |
| 160 * labelled with that description. Arrange callbacks to function |check| | 195 * labelled with that description. Arrange callbacks to function |check| |
| 161 * or |uncheck|, passing them the key of the checked or unchecked option, | 196 * or |uncheck|, passing them the key of the checked or unchecked option, |
| 162 * when the relevant checkbox state changes. | 197 * when the relevant checkbox state changes. |
| 163 * @param {!string} divId Id of division into which to put checkboxes | 198 * @param {!HTMLDivElement} div A <div> into which to put checkboxes. |
| 164 * @param {!Object} optionMap map of metric/event entries | 199 * @param {!Object} optionMap A map of metric/event entries. |
| 165 * @param {!function(this:Controller, Object)} check | 200 * @param {!function(this:Controller, Object)} check |
| 166 * function to select an entry (metric or event) | 201 * The function to select an entry (metric or event). |
| 167 * @param {!function(this:Controller, Object)} uncheck | 202 * @param {!function(this:Controller, Object)} uncheck |
| 168 * function to deselect an entry (metric or event) | 203 * The function to deselect an entry (metric or event). |
| 169 * @private | 204 * @private |
| 170 */ | 205 */ |
| 171 setupCheckboxes_: function(divId, optionMap, check, uncheck) { | 206 setupCheckboxes_: function(div, optionMap, check, uncheck) { |
| 172 var checkboxTemplate = $('#checkboxTemplate')[0]; | 207 var checkboxTemplate = $('#checkbox-template')[0]; |
| 173 var chooseMetricsDiv = $(divId)[0]; | |
| 174 | 208 |
| 175 for (var option in optionMap) { | 209 for (var option in optionMap) { |
| 176 var checkbox = checkboxTemplate.cloneNode(true); | 210 var checkbox = checkboxTemplate.cloneNode(true); |
| 177 checkbox.querySelector('span').innerText = 'Show ' + | 211 checkbox.querySelector('span').innerText = 'Show ' + |
| 178 optionMap[option].description; | 212 optionMap[option].description; |
| 179 chooseMetricsDiv.appendChild(checkbox); | 213 div.appendChild(checkbox); |
| 180 | 214 |
| 181 var input = checkbox.querySelector('input'); | 215 var input = checkbox.querySelector('input'); |
| 182 input.option = option; | 216 input.option = optionMap[option].id; |
| 183 input.addEventListener('change', function(e) { | 217 input.addEventListener('change', function(e) { |
| 184 if (e.target.checked) | 218 (e.target.checked ? check : uncheck).call(this, e.target.option); |
| 185 check.call(this, e.target.option); | |
| 186 else | |
| 187 uncheck.call(this, e.target.option); | |
| 188 }.bind(this)); | 219 }.bind(this)); |
| 189 } | 220 } |
| 190 }, | 221 }, |
| 191 | 222 |
| 192 /** | 223 /** |
| 193 * Set up just one chart in which all metrics will be displayed | 224 * Set up just one chart in which all metrics will be displayed |
| 194 * initially. But, the design readily accommodates addition of | 225 * initially. But, the design readily accommodates addition of |
| 195 * new charts, and movement of metrics into those other charts. | 226 * new charts, and movement of metrics into those other charts. |
| 196 * @private | 227 * @private |
| 197 */ | 228 */ |
| 198 setupMainChart_: function() { | 229 setupMainChart_: function() { |
| 199 this.chartParent = $('#charts')[0]; | 230 this.chartParent = $('#charts')[0]; |
| 200 this.charts = [document.createElement('div')]; | 231 this.charts = [document.createElement('div')]; |
| 201 this.charts[0].className = 'chart'; | 232 this.charts[0].className = 'chart'; |
| 202 this.chartParent.appendChild(this.charts[0]); | 233 this.chartParent.appendChild(this.charts[0]); |
| 203 }, | 234 }, |
| 204 | 235 |
| 205 /** | 236 /** |
| 206 * Set the time range for which to display metrics and events. For | 237 * Set the time range for which to display metrics and events. For |
| 207 * now, the time range always ends at "now", but future implementations | 238 * now, the time range always ends at "now", but future implementations |
| 208 * may allow time ranges not so anchored. | 239 * may allow time ranges not so anchored. |
| 209 * @param {!{start: !number, end: !number, resolution: !number}} range | 240 * @param {!{start: !number, end: !number, resolution: !number}} range |
| 241 * The time range for which to get display data. | |
| 210 */ | 242 */ |
| 211 setTimeRange: function(range) { | 243 setTimeRange: function(range) { |
| 212 this.range = range; | 244 this.range = range; |
| 213 this.end = Math.floor(Date.now() / range.resolution) * | 245 this.end = Math.floor(Date.now() / range.resolution) * |
| 214 range.resolution; | 246 range.resolution; |
| 215 | 247 |
| 216 // Take the GMT value of this.end ("now") and subtract from it the | |
| 217 // number of minutes by which we lag GMT in the present timezone, | |
| 218 // X mS/minute. This will show time in the present timezone. | |
| 219 this.end -= new Date().getTimezoneOffset() * 60000; | |
| 220 this.start = this.end - range.timeSpan; | 248 this.start = this.end - range.timeSpan; |
| 221 this.requestIntervals(); | 249 this.requestIntervals(); |
| 222 }, | 250 }, |
| 223 | 251 |
| 224 /** | 252 /** |
| 225 * Return mock interval set for testing. | |
| 226 * @return {!Array.<{start: !number, end: !number}>} intervals | |
| 227 */ | |
| 228 getMockIntervals: function() { | |
| 229 var interval = this.end - this.start; | |
| 230 | |
| 231 return [ | |
| 232 {start: this.start + interval * .1, | |
| 233 end: this.start + interval * .2}, | |
| 234 {start: this.start + interval * .7, | |
| 235 end: this.start + interval} | |
| 236 ]; | |
| 237 }, | |
| 238 | |
| 239 /** | |
| 240 * Request activity intervals in the current time range. | 253 * Request activity intervals in the current time range. |
| 241 */ | 254 */ |
| 242 requestIntervals: function() { | 255 requestIntervals: function() { |
| 243 this.onReceiveIntervals(this.getMockIntervals()); | 256 chrome.send('getActiveIntervals', [this.start, this.end]); |
| 244 // Replace with: chrome.send('getIntervals', this.start, this.end, | |
| 245 // this.onReceiveIntervals); | |
| 246 }, | 257 }, |
| 247 | 258 |
| 248 /** | 259 /** |
| 249 * Webui callback delivering response from requestIntervals call. Assumes | 260 * Webui callback delivering response from requestIntervals call. Assumes |
| 250 * this is a new time range choice, which results in complete refresh of | 261 * this is a new time range choice, which results in complete refresh of |
| 251 * all metrics and event types that are currently selected. | 262 * all metrics and event types that are currently selected. |
| 252 * @param {!Array.<{start: !number, end: !number}>} intervals new intervals | 263 * @param {!Array.<{start: !number, end: !number}>} intervals |
| 264 * The new intervals. | |
| 253 */ | 265 */ |
| 254 onReceiveIntervals: function(intervals) { | 266 getActiveIntervalsCallback: function(intervals) { |
| 255 this.intervals_ = intervals; | 267 this.intervals_ = intervals; |
| 256 | 268 |
| 257 for (var metric in this.metricMap_) { | 269 for (var metric in this.metricMap_) { |
| 258 var metricValue = this.metricMap_[metric]; | 270 var metricValue = this.metricMap_[metric]; |
| 259 if (metricValue.divs.length > 0) // if we're displaying this metric. | 271 if (metricValue.divs.length > 0) // if we're displaying this metric. |
| 260 this.refreshMetric(metric); | 272 this.refreshMetric(metric); |
| 261 } | 273 } |
| 262 | 274 |
| 263 for (var eventType in this.eventMap_) { | 275 for (var eventType in this.eventMap_) { |
| 264 var eventValue = this.eventMap_[eventType]; | 276 var eventValue = this.eventMap_[eventType]; |
| 265 if (eventValue.divs.length > 0) | 277 if (eventValue.divs.length > 0) |
| 266 this.refreshEventType(eventType); | 278 this.refreshEventType(eventValue.id); |
| 267 } | 279 } |
| 268 }, | 280 }, |
| 269 | 281 |
| 270 /** | 282 /** |
| 271 * Add a new metric to the main (currently only) chart. | 283 * Add a new metric to the main (currently only) chart. |
| 272 * @param {!string} metric Metric to start displaying | 284 * @param {!string} metric The metric to start displaying. |
| 273 */ | 285 */ |
| 274 addMetric: function(metric) { | 286 addMetric: function(metric) { |
| 275 this.metricMap_[metric].divs.push(this.charts[0]); | 287 this.metricMap_[metric].divs.push(this.charts[0]); |
| 276 this.refreshMetric(metric); | 288 this.refreshMetric(metric); |
| 277 }, | 289 }, |
| 278 | 290 |
| 279 /** | 291 /** |
| 280 * Remove a metric from the chart(s). | 292 * Remove a metric from the chart(s). |
| 281 * @param {!string} metric Metric to stop displaying | 293 * @param {!string} metric The metric to stop displaying. |
| 282 */ | 294 */ |
| 283 dropMetric: function(metric) { | 295 dropMetric: function(metric) { |
| 284 var metricValue = this.metricMap_[metric]; | 296 var metricValue = this.metricMap_[metric]; |
| 285 var affectedCharts = metricValue.divs; | 297 var affectedCharts = metricValue.divs; |
| 286 metricValue.divs = []; | 298 metricValue.divs = []; |
| 287 | 299 |
| 288 affectedCharts.forEach(this.drawChart, this); | 300 affectedCharts.forEach(this.drawChart, this); |
| 289 }, | 301 }, |
| 290 | 302 |
| 291 /** | 303 /** |
| 292 * Return mock metric data points for testing. Give values ranging from | |
| 293 * offset to max-offset. (This lets us avoid direct overlap of | |
| 294 * different mock data sets in an ugly way that will die in the next | |
| 295 * version anyway.) | |
| 296 * @param {!number} max Max data value to return, less offset | |
| 297 * @param {!number} offset Adjustment factor | |
| 298 * @return {!Array.<{time: !number, value: !number}>} | |
| 299 */ | |
| 300 getMockDataPoints: function(max, offset) { | |
| 301 var dataPoints = []; | |
| 302 | |
| 303 for (var i = 0; i < this.intervals_.length; i++) { | |
| 304 // Rise from low offset to high max-offset in 100 point steps. | |
| 305 for (var time = this.intervals_[i].start; | |
| 306 time <= this.intervals_[i].end; time += this.range.resolution) { | |
| 307 var numPoints = time / this.range.resolution; | |
| 308 dataPoints.push({time: time, value: offset + (numPoints % 100) * | |
| 309 (max - 2 * offset) / 100}); | |
| 310 } | |
| 311 } | |
| 312 return dataPoints; | |
| 313 }, | |
| 314 | |
| 315 /** | |
| 316 * Request new metric data, assuming the metric table and chart already | 304 * Request new metric data, assuming the metric table and chart already |
| 317 * exist. | 305 * exist. |
| 318 * @param {!string} metric Metric for which to get data | 306 * @param {!string} metric The metric for which to get data. |
| 319 */ | 307 */ |
| 320 refreshMetric: function(metric) { | 308 refreshMetric: function(metric) { |
| 321 var metricValue = this.metricMap_[metric]; | 309 var metricValue = this.metricMap_[metric]; |
| 322 | 310 |
| 323 metricValue.data = null; // Mark metric as awaiting response. | 311 metricValue.data = null; // Mark metric as awaiting response. |
| 324 this.onReceiveMetric(metric, | 312 chrome.send('getMetric', [metricValue.id, |
| 325 this.getMockDataPoints(metricValue.yAxis.max, 5)); | 313 this.start, this.end, this.range.resolution]); |
| 326 // Replace with: | |
| 327 // chrome.send("getMetric", this.range.start, this.range.end, | |
| 328 // this.range.resolution, this.onReceiveMetric); | |
| 329 }, | 314 }, |
| 330 | 315 |
| 331 /** | 316 /** |
| 332 * Receive new datapoints for |metric|, convert the data to Flot-usable | 317 * Receive new datapoints for a metric, convert the data to Flot-usable |
| 333 * form, and redraw all affected charts. | 318 * form, and redraw all affected charts. |
| 334 * @param {!string} metric Metric to which |points| applies | 319 * @param {!{ |
| 335 * @param {!Array.<{time: !number, value: !number}>} points | 320 * type: !number, |
| 336 * new data points | 321 * points: !Array.<{time: !number, value: !number}> |
| 322 * }} result An object giving metric ID, and time/value point pairs for | |
| 323 * that id. | |
| 337 */ | 324 */ |
| 338 onReceiveMetric: function(metric, points) { | 325 getMetricCallback: function(result) { |
| 339 var metricValue = this.metricMap_[metric]; | 326 var metricValue = this.metricMap_[result.metricType]; |
| 340 | |
| 341 // Might have been dropped while waiting for data. | 327 // Might have been dropped while waiting for data. |
| 342 if (metricValue.divs.length == 0) | 328 if (metricValue.divs.length == 0) |
| 343 return; | 329 return; |
| 344 | 330 |
| 345 var series = []; | 331 var series = []; |
| 346 metricValue.data = [series]; | 332 metricValue.data = [series]; // Data ends with current open series. |
| 347 | 333 |
| 348 // Traverse the points, and the intervals, in parallel. Both are in | 334 // Traverse the points, and the intervals, in parallel. Both are in |
| 349 // ascending time order. Create a sequence of data "series" (per Flot) | 335 // ascending time order. Create a sequence of data "series" (per Flot) |
| 350 // arrays, with each series comprising all points within a given interval. | 336 // arrays, with each series comprising all points within a given interval. |
| 351 var interval = this.intervals_[0]; | 337 var interval = this.intervals_[0]; |
| 352 var intervalIndex = 0; | 338 var intervalIndex = 0; |
| 353 var pointIndex = 0; | 339 var pointIndex = 0; |
| 354 while (pointIndex < points.length && | 340 while (pointIndex < result.points.length && |
| 355 intervalIndex < this.intervals_.length) { | 341 intervalIndex < this.intervals_.length) { |
| 356 var point = points[pointIndex++]; | 342 var point = result.points[pointIndex++]; |
| 357 while (intervalIndex < this.intervals_.length && | 343 while (intervalIndex < this.intervals_.length && |
| 358 point.time > interval.end) { | 344 point.time > interval.end) { |
| 359 interval = this.intervals_[++intervalIndex]; // Jump to new interval. | 345 interval = this.intervals_[++intervalIndex]; // Jump to new interval. |
| 360 if (series.length > 0) { | 346 if (series.length > 0) { |
| 361 series = []; // Start a new series. | 347 series = []; // Start a new series. |
| 362 metricValue.data.push(series); // Put it on the end of the data. | 348 metricValue.data.push(series); // Put it on the end of the data. |
| 363 } | 349 } |
| 364 } | 350 } |
| 365 if (intervalIndex < this.intervals_.length && | 351 if (intervalIndex < this.intervals_.length && |
| 366 point.time > interval.start) | 352 point.time > interval.start) { |
| 367 series.push([point.time, point.value]); | 353 series.push([point.time - timezoneOffset, point.value]); |
| 354 } | |
| 368 } | 355 } |
| 369 | |
| 370 metricValue.divs.forEach(this.drawChart, this); | 356 metricValue.divs.forEach(this.drawChart, this); |
| 371 }, | 357 }, |
| 372 | 358 |
| 373 /** | 359 /** |
| 374 * Add a new event to the chart(s). | 360 * Add a new event to the chart(s). |
| 375 * @param {!string} eventType type of event to start displaying | 361 * @param {!string} eventType The type of event to start displaying. |
| 376 */ | 362 */ |
| 377 addEventType: function(eventType) { | 363 addEventType: function(eventType) { |
| 378 // Events show on all charts. | 364 // Events show on all charts. |
| 379 this.eventMap_[eventType].divs = this.charts; | 365 this.eventMap_[eventType].divs = this.charts; |
| 380 this.refreshEventType(eventType); | 366 this.refreshEventType(eventType); |
| 381 }, | 367 }, |
| 382 | 368 |
| 383 /* | 369 /* |
| 384 * Remove an event from the chart(s). | 370 * Remove an event from the chart(s). |
| 385 * @param {!string} eventType type of event to stop displaying | 371 * @param {!string} eventType The type of event to stop displaying. |
| 386 */ | 372 */ |
| 387 dropEventType: function(eventType) { | 373 dropEventType: function(eventType) { |
| 388 var eventValue = this.eventMap_[eventType]; | 374 var eventValue = this.eventMap_[eventType]; |
| 389 var affectedCharts = eventValue.divs; | 375 var affectedCharts = eventValue.divs; |
| 390 eventValue.divs = []; | 376 eventValue.divs = []; |
| 391 | 377 |
| 392 affectedCharts.forEach(this.drawChart, this); | 378 affectedCharts.forEach(this.drawChart, this); |
| 393 }, | 379 }, |
| 394 | 380 |
| 395 /** | 381 /** |
| 396 * Return mock event points for testing. | |
| 397 * @param {!string} eventType type of event to generate mock data for | |
| 398 * @return {!Array.<{time: !number, longDescription: !string}>} | |
| 399 */ | |
| 400 getMockEventValues: function(eventType) { | |
| 401 var mockValues = []; | |
| 402 for (var i = 0; i < this.intervals_.length; i++) { | |
| 403 var interval = this.intervals_[i]; | |
| 404 | |
| 405 mockValues.push({ | |
| 406 time: interval.start, | |
| 407 longDescription: eventType + ' at ' + | |
| 408 new Date(interval.start) + ' blah, blah blah'}); | |
| 409 mockValues.push({ | |
| 410 time: (interval.start + interval.end) / 2, | |
| 411 longDescription: eventType + ' at ' + | |
| 412 new Date((interval.start + interval.end) / 2) + ' blah, blah'}); | |
| 413 mockValues.push({ | |
| 414 time: interval.end, | |
| 415 longDescription: eventType + ' at ' + new Date(interval.end) + | |
| 416 ' blah, blah blah'}); | |
| 417 } | |
| 418 return mockValues; | |
| 419 }, | |
| 420 | |
| 421 /** | |
| 422 * Request new data for |eventType|, for times in the current range. | 382 * Request new data for |eventType|, for times in the current range. |
| 423 * @param {!string} eventType type of event to get new data for | 383 * @param {!string} eventType The type of event to get new data for. |
| 424 */ | 384 */ |
| 425 refreshEventType: function(eventType) { | 385 refreshEventType: function(eventType) { |
| 426 // Mark eventType as awaiting response. | 386 // Mark eventType as awaiting response. |
| 427 this.eventMap_[eventType].data = null; | 387 this.eventMap_[eventType].data = null; |
| 428 this.onReceiveEventType(eventType, this.getMockEventValues(eventType)); | 388 |
| 429 // Replace with: | 389 chrome.send('getEvents', [eventType, this.start, this.end]); |
| 430 // chrome.send("getEvents", eventType, this.range.start, this.range.end); | |
| 431 }, | 390 }, |
| 432 | 391 |
| 433 /** | 392 /** |
| 434 * Receive new data for |eventType|. If the event has been deselected while | 393 * Receive new events for |eventType|. If |eventType| has been deselected |
| 435 * awaiting webui response, do nothing. Otherwise, save the data directly, | 394 * while awaiting webui handler response, do nothing. Otherwise, save the |
| 436 * since events are handled differently than metrics when drawing | 395 * data directly, since events are handled differently than metrics |
| 437 * (no "series"), and redraw all the affected charts. | 396 * when drawing (no "series"), and redraw all the affected charts. |
| 438 * @param {!string} eventType type of event the new data applies to | 397 * @param {!{ |
| 439 * @param {!Array.<{time: !number, longDescription: !string}>} values | 398 * type: !number, |
| 440 * new event values | 399 * points: !Array.<{time: !number, longDescription: string}> |
| 400 * }} result An object giving eventType ID, and time/description pairs for | |
| 401 * each event of that type in the range requested. | |
| 441 */ | 402 */ |
| 442 onReceiveEventType: function(eventType, values) { | 403 getEventsCallback: function(result) { |
| 443 var eventValue = this.eventMap_[eventType]; | 404 var eventValue = this.eventMap_[result.eventType]; |
| 444 | 405 |
| 445 if (eventValue.divs.length == 0) | 406 if (eventValue.divs.length == 0) |
| 446 return; | 407 return; |
| 447 | 408 |
| 448 eventValue.data = values; | 409 result.points.forEach(function(element) { |
| 410 console.log('Got event at time ' + element.time); | |
|
Dan Beam
2012/08/15 20:53:53
remove
clintstaley
2012/08/16 00:20:25
We're still building this thing, so some log messa
| |
| 411 element.time -= timezoneOffset; | |
| 412 }); | |
| 413 | |
| 414 eventValue.data = result.points; | |
| 449 eventValue.divs.forEach(this.drawChart, this); | 415 eventValue.divs.forEach(this.drawChart, this); |
| 450 }, | 416 }, |
| 451 | 417 |
| 452 /** | 418 /** |
| 453 * Return an object containing an array of metrics and another of events | 419 * Return an object containing an array of metrics and another of events |
| 454 * that include |chart| as one of the divs into which they display. | 420 * that include |chart| as one of the divs into which they display. |
| 455 * @param {HTMLDivElement} chart div for which to get relevant items | 421 * @param {HTMLDivElement} chart The <div> for which to get relevant items. |
| 456 * @return {!{metrics: !Array,<Object>, events: !Array.<Object>}} | 422 * @return {!{metrics: !Array,<Object>, events: !Array.<Object>}} |
| 457 * @private | 423 * @private |
| 458 */ | 424 */ |
| 459 getChartData_: function(chart) { | 425 getChartData_: function(chart) { |
| 460 var result = {metrics: [], events: []}; | 426 var result = {metrics: [], events: []}; |
| 461 | 427 |
| 462 for (var metric in this.metricMap_) { | 428 for (var metric in this.metricMap_) { |
| 463 var metricValue = this.metricMap_[metric]; | 429 var metricValue = this.metricMap_[metric]; |
| 464 | 430 |
| 465 if (metricValue.divs.indexOf(chart) != -1) | 431 if (metricValue.divs.indexOf(chart) != -1) |
| 466 result.metrics.push(metricValue); | 432 result.metrics.push(metricValue); |
| 467 } | 433 } |
| 468 | 434 |
| 469 for (var eventType in this.eventMap_) { | 435 for (var eventType in this.eventMap_) { |
| 470 var eventValue = this.eventMap_[eventType]; | 436 var eventValue = this.eventMap_[eventType]; |
| 471 | 437 |
| 472 // Events post to all divs, if they post to any. | 438 // Events post to all divs, if they post to any. |
| 473 if (eventValue.divs.length > 0) | 439 if (eventValue.divs.length > 0) |
| 474 result.events.push(eventValue); | 440 result.events.push(eventValue); |
| 475 } | 441 } |
| 476 | 442 |
| 477 return result; | 443 return result; |
| 478 }, | 444 }, |
| 479 | 445 |
| 480 /** | 446 /** |
| 481 * Check all entries in an object of the type returned from getChartData, | 447 * Check all entries in an object of the type returned from getChartData, |
| 482 * above, to see if all events and metrics have completed data (none is | 448 * above, to see if all events and metrics have completed data (none is |
| 483 * awaiting an asynchronous webui response to get their current data). | 449 * awaiting an asynchronous webui handler response to get their current |
| 450 * data). | |
| 484 * @param {!{metrics: !Array,<Object>, events: !Array.<Object>}} chartData | 451 * @param {!{metrics: !Array,<Object>, events: !Array.<Object>}} chartData |
| 485 * event/metric data to check for readiness | 452 * The event/metric data to check for readiness. |
| 486 * @return {boolean} is data ready? | 453 * @return {boolean} Is data ready? |
|
Dan Beam
2012/08/15 20:53:53
nit: Whether the data is ready.
clintstaley
2012/08/16 00:20:25
Done.
| |
| 487 * @private | 454 * @private |
| 488 */ | 455 */ |
| 489 isDataReady_: function(chartData) { | 456 isDataReady_: function(chartData) { |
| 490 for (var i = 0; i < chartData.metrics.length; i++) { | 457 for (var i = 0; i < chartData.metrics.length; i++) { |
| 491 if (!chartData.metrics[i].data) | 458 if (!chartData.metrics[i].data) |
| 492 return false; | 459 return false; |
| 493 } | 460 } |
| 494 | 461 |
| 495 for (var i = 0; i < chartData.events.length; i++) { | 462 for (var i = 0; i < chartData.events.length; i++) { |
| 496 if (!chartData.events[i].data) | 463 if (!chartData.events[i].data) |
| 497 return false; | 464 return false; |
| 498 } | 465 } |
| 499 | 466 |
| 500 return true; | 467 return true; |
| 501 }, | 468 }, |
| 502 | 469 |
| 503 /** | 470 /** |
| 504 * Create and return an array of "markings" (per Flot), representing | 471 * Create and return an array of "markings" (per Flot), representing |
| 505 * vertical lines at the event time, in the event's color. Also add | 472 * vertical lines at the event time, in the event's color. Also add |
| 506 * (not per Flot) a |description| property to each, to be used for hand | 473 * (not per Flot) a |description| property to each, to be used for hand |
| 507 * creating description boxes. | 474 * creating description boxes. |
| 508 * @param {!Array.<{ | 475 * @param {!Array.<{ |
| 509 * description: !string, | 476 * description: !string, |
| 510 * color: !string, | 477 * color: !string, |
| 511 * data: !Array.<{time: !number}> | 478 * data: !Array.<{time: !number}> |
| 512 * }>} eventValues events to make markings for | 479 * }>} eventValues The events to make markings for. |
| 513 * @return {!Array.<{ | 480 * @return {!Array.<{ |
| 514 * color: !string, | 481 * color: !string, |
| 515 * description: !string, | 482 * description: !string, |
| 516 * xaxis: {from: !number, to: !number} | 483 * xaxis: {from: !number, to: !number} |
| 517 * }>} mark data structure for Flot to use | 484 * }>} A marks data structure for Flot to use. |
| 518 * @private | 485 * @private |
| 519 */ | 486 */ |
| 520 getEventMarks_: function(eventValues) { | 487 getEventMarks_: function(eventValues) { |
| 521 var markings = []; | 488 var markings = []; |
| 522 | 489 |
| 523 for (var i = 0; i < eventValues.length; i++) { | 490 for (var i = 0; i < eventValues.length; i++) { |
| 524 var eventValue = eventValues[i]; | 491 var eventValue = eventValues[i]; |
| 525 for (var d = 0; d < eventValue.data.length; d++) { | 492 for (var d = 0; d < eventValue.data.length; d++) { |
| 526 var point = eventValue.data[d]; | 493 var point = eventValue.data[d]; |
| 527 markings.push({ | 494 if (point.time >= this.start - timezoneOffset && |
| 528 color: eventValue.color, | 495 point.time <= this.end - timezoneOffset) { |
| 529 description: eventValue.description, | 496 markings.push({ |
| 530 xaxis: {from: point.time, to: point.time} | 497 color: eventValue.color, |
| 531 }); | 498 description: eventValue.description, |
| 499 xaxis: {from: point.time, to: point.time} | |
| 500 }); | |
| 501 } else { | |
| 502 console.log('Event out of time range ' + this.start + ' -> ' + | |
|
Dan Beam
2012/08/15 20:53:53
remove or make error or warning?
clintstaley
2012/08/16 00:20:25
Agreed, but would like to do this in the next iter
| |
| 503 this.end + ' at: ' + point.time); | |
| 504 } | |
| 532 } | 505 } |
| 533 } | 506 } |
| 534 | 507 |
| 535 return markings; | 508 return markings; |
| 536 }, | 509 }, |
| 537 | 510 |
| 538 /** | 511 /** |
| 539 * Redraw the chart in div |chart|, *if* all its dependent data is present. | 512 * Redraw the chart in div |chart|, *if* all its dependent data is present. |
| 540 * Otherwise simply return, and await another call when all data is | 513 * Otherwise simply return, and await another call when all data is |
| 541 * available. | 514 * available. |
| 542 * @param {HTMLDivElement} chart div to redraw | 515 * @param {HTMLDivElement} chart The <div> to redraw. |
| 543 */ | 516 */ |
| 544 drawChart: function(chart) { | 517 drawChart: function(chart) { |
| 545 var chartData = this.getChartData_(chart); | 518 var chartData = this.getChartData_(chart); |
| 546 | 519 |
| 547 if (!this.isDataReady_(chartData)) | 520 if (!this.isDataReady_(chartData)) |
| 548 return; | 521 return; |
| 549 | 522 |
| 550 var seriesSeq = []; | 523 var seriesSeq = []; |
| 551 var yAxes = []; | 524 var yAxes = []; |
| 552 chartData.metrics.forEach(function(value) { | 525 chartData.metrics.forEach(function(value) { |
| 553 yAxes.push(value.yAxis); | 526 yAxes.push(value.yAxis); |
| 554 for (var i = 0; i < value.data.length; i++) { | 527 for (var i = 0; i < value.data.length; i++) { |
| 555 seriesSeq.push({ | 528 seriesSeq.push({ |
| 556 color: value.yAxis.color, | 529 color: value.yAxis.color, |
| 557 data: value.data[i], | 530 data: value.data[i], |
| 558 label: i == 0 ? value.description + ' (' + value.units + ')' : null, | 531 label: i == 0 ? value.description + ' (' + value.units + ')' : null, |
| 559 yaxis: yAxes.length, // Use just-added Y axis. | 532 yaxis: yAxes.length, // Use just-added Y axis. |
| 560 }); | 533 }); |
| 561 } | 534 } |
| 562 }); | 535 }); |
| 563 | 536 |
| 537 // Ensure at least one y axis, as a reference for event placement. | |
| 538 if (yAxes.length == 0) | |
| 539 yAxes.push({max: 1.0}); | |
| 540 | |
| 564 var markings = this.getEventMarks_(chartData.events); | 541 var markings = this.getEventMarks_(chartData.events); |
| 565 var chart = this.charts[0]; | 542 var chart = this.charts[0]; |
| 566 var plot = $.plot(chart, seriesSeq, { | 543 var plot = $.plot(chart, seriesSeq, { |
| 567 yaxes: yAxes, | 544 yaxes: yAxes, |
| 568 xaxis: {mode: 'time'}, | 545 xaxis: { |
| 546 mode: 'time', | |
| 547 min: this.start - timezoneOffset, | |
| 548 max: this.end - timezoneOffset | |
| 549 }, | |
| 550 points: {show: true, radius: 1}, | |
| 551 lines: {show: true}, | |
| 569 grid: {markings: markings} | 552 grid: {markings: markings} |
| 570 }); | 553 }); |
| 571 | 554 |
| 572 // For each event in |markings|, create also a label div, with left | 555 // For each event in |markings|, create also a label div, with left |
| 573 // edge colinear with the event vertical-line. Top of label is | 556 // edge colinear with the event vertical line. Top of label is |
| 574 // presently a hack-in, putting labels in three tiers of 25px height | 557 // presently a hack-in, putting labels in three tiers of 25px height |
| 575 // each to avoid overlap. Will need something better. | 558 // each to avoid overlap. Will need something better. |
| 576 var labelTemplate = $('#labelTemplate')[0]; | 559 var labelTemplate = $('#label-template')[0]; |
| 577 for (var i = 0; i < markings.length; i++) { | 560 for (var i = 0; i < markings.length; i++) { |
| 578 var mark = markings[i]; | 561 var mark = markings[i]; |
| 579 var point = | 562 var point = |
| 580 plot.pointOffset({x: mark.xaxis.to, y: yAxes[0].max, yaxis: 1}); | 563 plot.pointOffset({x: mark.xaxis.to, y: yAxes[0].max, yaxis: 1}); |
| 581 var labelDiv = labelTemplate.cloneNode(true); | 564 var labelDiv = labelTemplate.cloneNode(true); |
| 582 labelDiv.innerText = mark.description; | 565 labelDiv.innerText = mark.description; |
| 583 labelDiv.style.left = point.left + 'px'; | 566 labelDiv.style.left = point.left + 'px'; |
| 584 labelDiv.style.top = (point.top + 25 * (i % 3)) + 'px'; | 567 labelDiv.style.top = (point.top + 25 * (i % 3)) + 'px'; |
| 568 | |
| 585 chart.appendChild(labelDiv); | 569 chart.appendChild(labelDiv); |
| 586 } | 570 } |
| 587 } | 571 } |
| 588 }; | 572 }; |
| 589 return { | 573 return { |
| 590 PerformanceMonitor: PerformanceMonitor | 574 PerformanceMonitor: PerformanceMonitor |
| 591 }; | 575 }; |
| 592 }(); | 576 }); |
| 593 | 577 |
| 594 var performanceMonitor = new Installer.PerformanceMonitor(); | 578 var PerformanceMonitor = new performance_monitor.PerformanceMonitor(); |
| OLD | NEW |