| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2012 Google Inc. All rights reserved. | 2 * Copyright (C) 2012 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| 11 * copyright notice, this list of conditions and the following disclaimer | 11 * copyright notice, this list of conditions and the following disclaimer |
| 12 * in the documentation and/or other materials provided with the | 12 * in the documentation and/or other materials provided with the |
| 13 * distribution. | 13 * distribution. |
| 14 * * Neither the name of Google Inc. nor the names of its | 14 * * Neither the name of Google Inc. nor the names of its |
| 15 * contributors may be used to endorse or promote products derived from | 15 * contributors may be used to endorse or promote products derived from |
| 16 * this software without specific prior written permission. | 16 * this software without specific prior written permission. |
| 17 * | 17 * |
| 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 */ | 29 */ |
| 30 /** |
| 31 * @implements {WebInspector.TimelineModeView} |
| 32 * @unrestricted |
| 33 */ |
| 34 WebInspector.CountersGraph = class extends WebInspector.VBox { |
| 35 /** |
| 36 * @param {!WebInspector.TimelineModeViewDelegate} delegate |
| 37 * @param {!WebInspector.TimelineModel} model |
| 38 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters |
| 39 */ |
| 40 constructor(delegate, model, filters) { |
| 41 super(); |
| 30 | 42 |
| 31 /** | 43 this.element.id = 'memory-graphs-container'; |
| 32 * @constructor | |
| 33 * @extends {WebInspector.VBox} | |
| 34 * @implements {WebInspector.TimelineModeView} | |
| 35 * @param {!WebInspector.TimelineModeViewDelegate} delegate | |
| 36 * @param {!WebInspector.TimelineModel} model | |
| 37 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters | |
| 38 */ | |
| 39 WebInspector.CountersGraph = function(delegate, model, filters) | |
| 40 { | |
| 41 WebInspector.VBox.call(this); | |
| 42 | |
| 43 this.element.id = "memory-graphs-container"; | |
| 44 | 44 |
| 45 this._delegate = delegate; | 45 this._delegate = delegate; |
| 46 this._model = model; | 46 this._model = model; |
| 47 this._filters = filters; | 47 this._filters = filters; |
| 48 this._calculator = new WebInspector.CounterGraphCalculator(this._model); | 48 this._calculator = new WebInspector.CounterGraphCalculator(this._model); |
| 49 | 49 |
| 50 // Create selectors | 50 // Create selectors |
| 51 this._infoWidget = new WebInspector.HBox(); | 51 this._infoWidget = new WebInspector.HBox(); |
| 52 this._infoWidget.element.classList.add("memory-counter-selector-swatches", "
timeline-toolbar-resizer"); | 52 this._infoWidget.element.classList.add('memory-counter-selector-swatches', '
timeline-toolbar-resizer'); |
| 53 this._infoWidget.show(this.element); | 53 this._infoWidget.show(this.element); |
| 54 | 54 |
| 55 this._graphsContainer = new WebInspector.VBox(); | 55 this._graphsContainer = new WebInspector.VBox(); |
| 56 this._graphsContainer.show(this.element); | 56 this._graphsContainer.show(this.element); |
| 57 var canvasWidget = new WebInspector.VBoxWithResizeCallback(this._resize.bind
(this)); | 57 var canvasWidget = new WebInspector.VBoxWithResizeCallback(this._resize.bind
(this)); |
| 58 canvasWidget.show(this._graphsContainer.element); | 58 canvasWidget.show(this._graphsContainer.element); |
| 59 this._createCurrentValuesBar(); | 59 this._createCurrentValuesBar(); |
| 60 this._canvasContainer = canvasWidget.element; | 60 this._canvasContainer = canvasWidget.element; |
| 61 this._canvasContainer.id = "memory-graphs-canvas-container"; | 61 this._canvasContainer.id = 'memory-graphs-canvas-container'; |
| 62 this._canvas = this._canvasContainer.createChild("canvas"); | 62 this._canvas = this._canvasContainer.createChild('canvas'); |
| 63 this._canvas.id = "memory-counters-graph"; | 63 this._canvas.id = 'memory-counters-graph'; |
| 64 | 64 |
| 65 this._canvasContainer.addEventListener("mouseover", this._onMouseMove.bind(t
his), true); | 65 this._canvasContainer.addEventListener('mouseover', this._onMouseMove.bind(t
his), true); |
| 66 this._canvasContainer.addEventListener("mousemove", this._onMouseMove.bind(t
his), true); | 66 this._canvasContainer.addEventListener('mousemove', this._onMouseMove.bind(t
his), true); |
| 67 this._canvasContainer.addEventListener("mouseleave", this._onMouseLeave.bind
(this), true); | 67 this._canvasContainer.addEventListener('mouseleave', this._onMouseLeave.bind
(this), true); |
| 68 this._canvasContainer.addEventListener("click", this._onClick.bind(this), tr
ue); | 68 this._canvasContainer.addEventListener('click', this._onClick.bind(this), tr
ue); |
| 69 // We create extra timeline grid here to reuse its event dividers. | 69 // We create extra timeline grid here to reuse its event dividers. |
| 70 this._timelineGrid = new WebInspector.TimelineGrid(); | 70 this._timelineGrid = new WebInspector.TimelineGrid(); |
| 71 this._canvasContainer.appendChild(this._timelineGrid.dividersElement); | 71 this._canvasContainer.appendChild(this._timelineGrid.dividersElement); |
| 72 | 72 |
| 73 this._counters = []; | 73 this._counters = []; |
| 74 this._counterUI = []; | 74 this._counterUI = []; |
| 75 } |
| 76 |
| 77 _createCurrentValuesBar() { |
| 78 this._currentValuesBar = this._graphsContainer.element.createChild('div'); |
| 79 this._currentValuesBar.id = 'counter-values-bar'; |
| 80 } |
| 81 |
| 82 /** |
| 83 * @param {string} uiName |
| 84 * @param {string} uiValueTemplate |
| 85 * @param {string} color |
| 86 * @param {function(number):string=} formatter |
| 87 * @return {!WebInspector.CountersGraph.Counter} |
| 88 */ |
| 89 createCounter(uiName, uiValueTemplate, color, formatter) { |
| 90 var counter = new WebInspector.CountersGraph.Counter(); |
| 91 this._counters.push(counter); |
| 92 this._counterUI.push( |
| 93 new WebInspector.CountersGraph.CounterUI(this, uiName, uiValueTemplate,
color, counter, formatter)); |
| 94 return counter; |
| 95 } |
| 96 |
| 97 /** |
| 98 * @override |
| 99 * @return {!WebInspector.Widget} |
| 100 */ |
| 101 view() { |
| 102 return this; |
| 103 } |
| 104 |
| 105 /** |
| 106 * @override |
| 107 */ |
| 108 dispose() { |
| 109 } |
| 110 |
| 111 /** |
| 112 * @override |
| 113 */ |
| 114 reset() { |
| 115 for (var i = 0; i < this._counters.length; ++i) { |
| 116 this._counters[i].reset(); |
| 117 this._counterUI[i].reset(); |
| 118 } |
| 119 this.refresh(); |
| 120 } |
| 121 |
| 122 /** |
| 123 * @override |
| 124 * @return {?Element} |
| 125 */ |
| 126 resizerElement() { |
| 127 return this._infoWidget.element; |
| 128 } |
| 129 |
| 130 _resize() { |
| 131 var parentElement = this._canvas.parentElement; |
| 132 this._canvas.width = parentElement.clientWidth * window.devicePixelRatio; |
| 133 this._canvas.height = parentElement.clientHeight * window.devicePixelRatio; |
| 134 var timelinePaddingLeft = 15; |
| 135 this._calculator.setDisplayWindow(this._canvas.width, timelinePaddingLeft); |
| 136 this.refresh(); |
| 137 } |
| 138 |
| 139 /** |
| 140 * @override |
| 141 * @param {number} startTime |
| 142 * @param {number} endTime |
| 143 */ |
| 144 setWindowTimes(startTime, endTime) { |
| 145 this._calculator.setWindow(startTime, endTime); |
| 146 this.scheduleRefresh(); |
| 147 } |
| 148 |
| 149 scheduleRefresh() { |
| 150 WebInspector.invokeOnceAfterBatchUpdate(this, this.refresh); |
| 151 } |
| 152 |
| 153 draw() { |
| 154 for (var i = 0; i < this._counters.length; ++i) { |
| 155 this._counters[i]._calculateVisibleIndexes(this._calculator); |
| 156 this._counters[i]._calculateXValues(this._canvas.width); |
| 157 } |
| 158 this._clear(); |
| 159 |
| 160 for (var i = 0; i < this._counterUI.length; i++) |
| 161 this._counterUI[i]._drawGraph(this._canvas); |
| 162 } |
| 163 |
| 164 /** |
| 165 * @param {!Event} event |
| 166 */ |
| 167 _onClick(event) { |
| 168 var x = event.x - this._canvasContainer.totalOffsetLeft(); |
| 169 var minDistance = Infinity; |
| 170 var bestTime; |
| 171 for (var i = 0; i < this._counterUI.length; ++i) { |
| 172 var counterUI = this._counterUI[i]; |
| 173 if (!counterUI.counter.times.length) |
| 174 continue; |
| 175 var index = counterUI._recordIndexAt(x); |
| 176 var distance = Math.abs(x * window.devicePixelRatio - counterUI.counter.x[
index]); |
| 177 if (distance < minDistance) { |
| 178 minDistance = distance; |
| 179 bestTime = counterUI.counter.times[index]; |
| 180 } |
| 181 } |
| 182 if (bestTime !== undefined) |
| 183 this._delegate.selectEntryAtTime(bestTime); |
| 184 } |
| 185 |
| 186 /** |
| 187 * @param {!Event} event |
| 188 */ |
| 189 _onMouseLeave(event) { |
| 190 delete this._markerXPosition; |
| 191 this._clearCurrentValueAndMarker(); |
| 192 } |
| 193 |
| 194 _clearCurrentValueAndMarker() { |
| 195 for (var i = 0; i < this._counterUI.length; i++) |
| 196 this._counterUI[i]._clearCurrentValueAndMarker(); |
| 197 } |
| 198 |
| 199 /** |
| 200 * @param {!Event} event |
| 201 */ |
| 202 _onMouseMove(event) { |
| 203 var x = event.x - this._canvasContainer.totalOffsetLeft(); |
| 204 this._markerXPosition = x; |
| 205 this._refreshCurrentValues(); |
| 206 } |
| 207 |
| 208 _refreshCurrentValues() { |
| 209 if (this._markerXPosition === undefined) |
| 210 return; |
| 211 for (var i = 0; i < this._counterUI.length; ++i) |
| 212 this._counterUI[i].updateCurrentValue(this._markerXPosition); |
| 213 } |
| 214 |
| 215 refresh() { |
| 216 this._timelineGrid.updateDividers(this._calculator); |
| 217 this.draw(); |
| 218 this._refreshCurrentValues(); |
| 219 } |
| 220 |
| 221 /** |
| 222 * @override |
| 223 */ |
| 224 refreshRecords() { |
| 225 } |
| 226 |
| 227 _clear() { |
| 228 var ctx = this._canvas.getContext('2d'); |
| 229 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); |
| 230 } |
| 231 |
| 232 /** |
| 233 * @override |
| 234 * @param {?WebInspector.TracingModel.Event} event |
| 235 * @param {string=} regex |
| 236 * @param {boolean=} select |
| 237 */ |
| 238 highlightSearchResult(event, regex, select) { |
| 239 } |
| 240 |
| 241 /** |
| 242 * @override |
| 243 * @param {?WebInspector.TracingModel.Event} event |
| 244 */ |
| 245 highlightEvent(event) { |
| 246 } |
| 247 |
| 248 /** |
| 249 * @override |
| 250 * @param {?WebInspector.TimelineSelection} selection |
| 251 */ |
| 252 setSelection(selection) { |
| 253 } |
| 75 }; | 254 }; |
| 76 | 255 |
| 77 WebInspector.CountersGraph.prototype = { | |
| 78 _createCurrentValuesBar: function() | |
| 79 { | |
| 80 this._currentValuesBar = this._graphsContainer.element.createChild("div"
); | |
| 81 this._currentValuesBar.id = "counter-values-bar"; | |
| 82 }, | |
| 83 | |
| 84 /** | |
| 85 * @param {string} uiName | |
| 86 * @param {string} uiValueTemplate | |
| 87 * @param {string} color | |
| 88 * @param {function(number):string=} formatter | |
| 89 * @return {!WebInspector.CountersGraph.Counter} | |
| 90 */ | |
| 91 createCounter: function(uiName, uiValueTemplate, color, formatter) | |
| 92 { | |
| 93 var counter = new WebInspector.CountersGraph.Counter(); | |
| 94 this._counters.push(counter); | |
| 95 this._counterUI.push(new WebInspector.CountersGraph.CounterUI(this, uiNa
me, uiValueTemplate, color, counter, formatter)); | |
| 96 return counter; | |
| 97 }, | |
| 98 | |
| 99 /** | |
| 100 * @override | |
| 101 * @return {!WebInspector.Widget} | |
| 102 */ | |
| 103 view: function() | |
| 104 { | |
| 105 return this; | |
| 106 }, | |
| 107 | |
| 108 /** | |
| 109 * @override | |
| 110 */ | |
| 111 dispose: function() | |
| 112 { | |
| 113 }, | |
| 114 | |
| 115 /** | |
| 116 * @override | |
| 117 */ | |
| 118 reset: function() | |
| 119 { | |
| 120 for (var i = 0; i < this._counters.length; ++i) { | |
| 121 this._counters[i].reset(); | |
| 122 this._counterUI[i].reset(); | |
| 123 } | |
| 124 this.refresh(); | |
| 125 }, | |
| 126 | |
| 127 /** | |
| 128 * @override | |
| 129 * @return {?Element} | |
| 130 */ | |
| 131 resizerElement: function() | |
| 132 { | |
| 133 return this._infoWidget.element; | |
| 134 }, | |
| 135 | |
| 136 _resize: function() | |
| 137 { | |
| 138 var parentElement = this._canvas.parentElement; | |
| 139 this._canvas.width = parentElement.clientWidth * window.devicePixelRati
o; | |
| 140 this._canvas.height = parentElement.clientHeight * window.devicePixelRat
io; | |
| 141 var timelinePaddingLeft = 15; | |
| 142 this._calculator.setDisplayWindow(this._canvas.width, timelinePaddingLef
t); | |
| 143 this.refresh(); | |
| 144 }, | |
| 145 | |
| 146 /** | |
| 147 * @override | |
| 148 * @param {number} startTime | |
| 149 * @param {number} endTime | |
| 150 */ | |
| 151 setWindowTimes: function(startTime, endTime) | |
| 152 { | |
| 153 this._calculator.setWindow(startTime, endTime); | |
| 154 this.scheduleRefresh(); | |
| 155 }, | |
| 156 | |
| 157 scheduleRefresh: function() | |
| 158 { | |
| 159 WebInspector.invokeOnceAfterBatchUpdate(this, this.refresh); | |
| 160 }, | |
| 161 | |
| 162 draw: function() | |
| 163 { | |
| 164 for (var i = 0; i < this._counters.length; ++i) { | |
| 165 this._counters[i]._calculateVisibleIndexes(this._calculator); | |
| 166 this._counters[i]._calculateXValues(this._canvas.width); | |
| 167 } | |
| 168 this._clear(); | |
| 169 | |
| 170 for (var i = 0; i < this._counterUI.length; i++) | |
| 171 this._counterUI[i]._drawGraph(this._canvas); | |
| 172 }, | |
| 173 | |
| 174 /** | |
| 175 * @param {!Event} event | |
| 176 */ | |
| 177 _onClick: function(event) | |
| 178 { | |
| 179 var x = event.x - this._canvasContainer.totalOffsetLeft(); | |
| 180 var minDistance = Infinity; | |
| 181 var bestTime; | |
| 182 for (var i = 0; i < this._counterUI.length; ++i) { | |
| 183 var counterUI = this._counterUI[i]; | |
| 184 if (!counterUI.counter.times.length) | |
| 185 continue; | |
| 186 var index = counterUI._recordIndexAt(x); | |
| 187 var distance = Math.abs(x * window.devicePixelRatio - counterUI.coun
ter.x[index]); | |
| 188 if (distance < minDistance) { | |
| 189 minDistance = distance; | |
| 190 bestTime = counterUI.counter.times[index]; | |
| 191 } | |
| 192 } | |
| 193 if (bestTime !== undefined) | |
| 194 this._delegate.selectEntryAtTime(bestTime); | |
| 195 }, | |
| 196 | |
| 197 /** | |
| 198 * @param {!Event} event | |
| 199 */ | |
| 200 _onMouseLeave: function(event) | |
| 201 { | |
| 202 delete this._markerXPosition; | |
| 203 this._clearCurrentValueAndMarker(); | |
| 204 }, | |
| 205 | |
| 206 _clearCurrentValueAndMarker: function() | |
| 207 { | |
| 208 for (var i = 0; i < this._counterUI.length; i++) | |
| 209 this._counterUI[i]._clearCurrentValueAndMarker(); | |
| 210 }, | |
| 211 | |
| 212 /** | |
| 213 * @param {!Event} event | |
| 214 */ | |
| 215 _onMouseMove: function(event) | |
| 216 { | |
| 217 var x = event.x - this._canvasContainer.totalOffsetLeft(); | |
| 218 this._markerXPosition = x; | |
| 219 this._refreshCurrentValues(); | |
| 220 }, | |
| 221 | |
| 222 _refreshCurrentValues: function() | |
| 223 { | |
| 224 if (this._markerXPosition === undefined) | |
| 225 return; | |
| 226 for (var i = 0; i < this._counterUI.length; ++i) | |
| 227 this._counterUI[i].updateCurrentValue(this._markerXPosition); | |
| 228 }, | |
| 229 | |
| 230 refresh: function() | |
| 231 { | |
| 232 this._timelineGrid.updateDividers(this._calculator); | |
| 233 this.draw(); | |
| 234 this._refreshCurrentValues(); | |
| 235 }, | |
| 236 | |
| 237 /** | |
| 238 * @override | |
| 239 */ | |
| 240 refreshRecords: function() { }, | |
| 241 | |
| 242 _clear: function() | |
| 243 { | |
| 244 var ctx = this._canvas.getContext("2d"); | |
| 245 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); | |
| 246 }, | |
| 247 | |
| 248 /** | |
| 249 * @override | |
| 250 * @param {?WebInspector.TracingModel.Event} event | |
| 251 * @param {string=} regex | |
| 252 * @param {boolean=} select | |
| 253 */ | |
| 254 highlightSearchResult: function(event, regex, select) | |
| 255 { | |
| 256 }, | |
| 257 | |
| 258 /** | |
| 259 * @override | |
| 260 * @param {?WebInspector.TracingModel.Event} event | |
| 261 */ | |
| 262 highlightEvent: function(event) | |
| 263 { | |
| 264 }, | |
| 265 | |
| 266 /** | |
| 267 * @override | |
| 268 * @param {?WebInspector.TimelineSelection} selection | |
| 269 */ | |
| 270 setSelection: function(selection) | |
| 271 { | |
| 272 }, | |
| 273 | |
| 274 __proto__: WebInspector.VBox.prototype | |
| 275 }; | |
| 276 | |
| 277 /** | 256 /** |
| 278 * @constructor | 257 * @unrestricted |
| 279 */ | 258 */ |
| 280 WebInspector.CountersGraph.Counter = function() | 259 WebInspector.CountersGraph.Counter = class { |
| 281 { | 260 constructor() { |
| 282 this.times = []; | 261 this.times = []; |
| 283 this.values = []; | 262 this.values = []; |
| 263 } |
| 264 |
| 265 /** |
| 266 * @param {number} time |
| 267 * @param {number} value |
| 268 */ |
| 269 appendSample(time, value) { |
| 270 if (this.values.length && this.values.peekLast() === value) |
| 271 return; |
| 272 this.times.push(time); |
| 273 this.values.push(value); |
| 274 } |
| 275 |
| 276 reset() { |
| 277 this.times = []; |
| 278 this.values = []; |
| 279 } |
| 280 |
| 281 /** |
| 282 * @param {number} value |
| 283 */ |
| 284 setLimit(value) { |
| 285 this._limitValue = value; |
| 286 } |
| 287 |
| 288 /** |
| 289 * @return {!{min: number, max: number}} |
| 290 */ |
| 291 _calculateBounds() { |
| 292 var maxValue; |
| 293 var minValue; |
| 294 for (var i = this._minimumIndex; i <= this._maximumIndex; i++) { |
| 295 var value = this.values[i]; |
| 296 if (minValue === undefined || value < minValue) |
| 297 minValue = value; |
| 298 if (maxValue === undefined || value > maxValue) |
| 299 maxValue = value; |
| 300 } |
| 301 minValue = minValue || 0; |
| 302 maxValue = maxValue || 1; |
| 303 if (this._limitValue) { |
| 304 if (maxValue > this._limitValue * 0.5) |
| 305 maxValue = Math.max(maxValue, this._limitValue); |
| 306 minValue = Math.min(minValue, this._limitValue); |
| 307 } |
| 308 return {min: minValue, max: maxValue}; |
| 309 } |
| 310 |
| 311 /** |
| 312 * @param {!WebInspector.CounterGraphCalculator} calculator |
| 313 */ |
| 314 _calculateVisibleIndexes(calculator) { |
| 315 var start = calculator.minimumBoundary(); |
| 316 var end = calculator.maximumBoundary(); |
| 317 |
| 318 // Maximum index of element whose time <= start. |
| 319 this._minimumIndex = Number.constrain(this.times.upperBound(start) - 1, 0, t
his.times.length - 1); |
| 320 |
| 321 // Minimum index of element whose time >= end. |
| 322 this._maximumIndex = Number.constrain(this.times.lowerBound(end), 0, this.ti
mes.length - 1); |
| 323 |
| 324 // Current window bounds. |
| 325 this._minTime = start; |
| 326 this._maxTime = end; |
| 327 } |
| 328 |
| 329 /** |
| 330 * @param {number} width |
| 331 */ |
| 332 _calculateXValues(width) { |
| 333 if (!this.values.length) |
| 334 return; |
| 335 |
| 336 var xFactor = width / (this._maxTime - this._minTime); |
| 337 |
| 338 this.x = new Array(this.values.length); |
| 339 for (var i = this._minimumIndex + 1; i <= this._maximumIndex; i++) |
| 340 this.x[i] = xFactor * (this.times[i] - this._minTime); |
| 341 } |
| 284 }; | 342 }; |
| 285 | 343 |
| 286 WebInspector.CountersGraph.Counter.prototype = { | |
| 287 /** | |
| 288 * @param {number} time | |
| 289 * @param {number} value | |
| 290 */ | |
| 291 appendSample: function(time, value) | |
| 292 { | |
| 293 if (this.values.length && this.values.peekLast() === value) | |
| 294 return; | |
| 295 this.times.push(time); | |
| 296 this.values.push(value); | |
| 297 }, | |
| 298 | |
| 299 reset: function() | |
| 300 { | |
| 301 this.times = []; | |
| 302 this.values = []; | |
| 303 }, | |
| 304 | |
| 305 /** | |
| 306 * @param {number} value | |
| 307 */ | |
| 308 setLimit: function(value) | |
| 309 { | |
| 310 this._limitValue = value; | |
| 311 }, | |
| 312 | |
| 313 /** | |
| 314 * @return {!{min: number, max: number}} | |
| 315 */ | |
| 316 _calculateBounds: function() | |
| 317 { | |
| 318 var maxValue; | |
| 319 var minValue; | |
| 320 for (var i = this._minimumIndex; i <= this._maximumIndex; i++) { | |
| 321 var value = this.values[i]; | |
| 322 if (minValue === undefined || value < minValue) | |
| 323 minValue = value; | |
| 324 if (maxValue === undefined || value > maxValue) | |
| 325 maxValue = value; | |
| 326 } | |
| 327 minValue = minValue || 0; | |
| 328 maxValue = maxValue || 1; | |
| 329 if (this._limitValue) { | |
| 330 if (maxValue > this._limitValue * 0.5) | |
| 331 maxValue = Math.max(maxValue, this._limitValue); | |
| 332 minValue = Math.min(minValue, this._limitValue); | |
| 333 } | |
| 334 return { min: minValue, max: maxValue }; | |
| 335 }, | |
| 336 | |
| 337 /** | |
| 338 * @param {!WebInspector.CounterGraphCalculator} calculator | |
| 339 */ | |
| 340 _calculateVisibleIndexes: function(calculator) | |
| 341 { | |
| 342 var start = calculator.minimumBoundary(); | |
| 343 var end = calculator.maximumBoundary(); | |
| 344 | |
| 345 // Maximum index of element whose time <= start. | |
| 346 this._minimumIndex = Number.constrain(this.times.upperBound(start) - 1,
0, this.times.length - 1); | |
| 347 | |
| 348 // Minimum index of element whose time >= end. | |
| 349 this._maximumIndex = Number.constrain(this.times.lowerBound(end), 0, thi
s.times.length - 1); | |
| 350 | |
| 351 // Current window bounds. | |
| 352 this._minTime = start; | |
| 353 this._maxTime = end; | |
| 354 }, | |
| 355 | |
| 356 /** | |
| 357 * @param {number} width | |
| 358 */ | |
| 359 _calculateXValues: function(width) | |
| 360 { | |
| 361 if (!this.values.length) | |
| 362 return; | |
| 363 | |
| 364 var xFactor = width / (this._maxTime - this._minTime); | |
| 365 | |
| 366 this.x = new Array(this.values.length); | |
| 367 for (var i = this._minimumIndex + 1; i <= this._maximumIndex; i++) | |
| 368 this.x[i] = xFactor * (this.times[i] - this._minTime); | |
| 369 } | |
| 370 }; | |
| 371 | |
| 372 /** | 344 /** |
| 373 * @constructor | 345 * @unrestricted |
| 374 * @param {!WebInspector.CountersGraph} memoryCountersPane | |
| 375 * @param {string} title | |
| 376 * @param {string} currentValueLabel | |
| 377 * @param {string} graphColor | |
| 378 * @param {!WebInspector.CountersGraph.Counter} counter | |
| 379 * @param {(function(number): string)|undefined} formatter | |
| 380 */ | 346 */ |
| 381 WebInspector.CountersGraph.CounterUI = function(memoryCountersPane, title, curre
ntValueLabel, graphColor, counter, formatter) | 347 WebInspector.CountersGraph.CounterUI = class { |
| 382 { | 348 /** |
| 349 * @param {!WebInspector.CountersGraph} memoryCountersPane |
| 350 * @param {string} title |
| 351 * @param {string} currentValueLabel |
| 352 * @param {string} graphColor |
| 353 * @param {!WebInspector.CountersGraph.Counter} counter |
| 354 * @param {(function(number): string)|undefined} formatter |
| 355 */ |
| 356 constructor(memoryCountersPane, title, currentValueLabel, graphColor, counter,
formatter) { |
| 383 this._memoryCountersPane = memoryCountersPane; | 357 this._memoryCountersPane = memoryCountersPane; |
| 384 this.counter = counter; | 358 this.counter = counter; |
| 385 this._formatter = formatter || Number.withThousandsSeparator; | 359 this._formatter = formatter || Number.withThousandsSeparator; |
| 386 var container = memoryCountersPane._infoWidget.element.createChild("div", "m
emory-counter-selector-info"); | 360 var container = memoryCountersPane._infoWidget.element.createChild('div', 'm
emory-counter-selector-info'); |
| 387 | 361 |
| 388 this._setting = WebInspector.settings.createSetting("timelineCountersGraph-"
+ title, true); | 362 this._setting = WebInspector.settings.createSetting('timelineCountersGraph-'
+ title, true); |
| 389 this._filter = new WebInspector.ToolbarCheckbox(title, title, this._setting)
; | 363 this._filter = new WebInspector.ToolbarCheckbox(title, title, this._setting)
; |
| 390 this._filter.inputElement.classList.add("-theme-preserve"); | 364 this._filter.inputElement.classList.add('-theme-preserve'); |
| 391 var color = WebInspector.Color.parse(graphColor).setAlpha(0.5).asString(WebI
nspector.Color.Format.RGBA); | 365 var color = WebInspector.Color.parse(graphColor).setAlpha(0.5).asString(WebI
nspector.Color.Format.RGBA); |
| 392 if (color) { | 366 if (color) { |
| 393 this._filter.element.backgroundColor = color; | 367 this._filter.element.backgroundColor = color; |
| 394 this._filter.element.borderColor = "transparent"; | 368 this._filter.element.borderColor = 'transparent'; |
| 395 } | 369 } |
| 396 this._filter.inputElement.addEventListener("click", this._toggleCounterGraph
.bind(this)); | 370 this._filter.inputElement.addEventListener('click', this._toggleCounterGraph
.bind(this)); |
| 397 container.appendChild(this._filter.element); | 371 container.appendChild(this._filter.element); |
| 398 this._range = this._filter.element.createChild("span", "range"); | 372 this._range = this._filter.element.createChild('span', 'range'); |
| 399 | 373 |
| 400 this._value = memoryCountersPane._currentValuesBar.createChild("span", "memo
ry-counter-value"); | 374 this._value = memoryCountersPane._currentValuesBar.createChild('span', 'memo
ry-counter-value'); |
| 401 this._value.style.color = graphColor; | 375 this._value.style.color = graphColor; |
| 402 this.graphColor = graphColor; | 376 this.graphColor = graphColor; |
| 403 this.limitColor = WebInspector.Color.parse(graphColor).setAlpha(0.3).asStrin
g(WebInspector.Color.Format.RGBA); | 377 this.limitColor = WebInspector.Color.parse(graphColor).setAlpha(0.3).asStrin
g(WebInspector.Color.Format.RGBA); |
| 404 this.graphYValues = []; | 378 this.graphYValues = []; |
| 405 this._verticalPadding = 10; | 379 this._verticalPadding = 10; |
| 406 | 380 |
| 407 this._currentValueLabel = currentValueLabel; | 381 this._currentValueLabel = currentValueLabel; |
| 408 this._marker = memoryCountersPane._canvasContainer.createChild("div", "memor
y-counter-marker"); | 382 this._marker = memoryCountersPane._canvasContainer.createChild('div', 'memor
y-counter-marker'); |
| 409 this._marker.style.backgroundColor = graphColor; | 383 this._marker.style.backgroundColor = graphColor; |
| 410 this._clearCurrentValueAndMarker(); | 384 this._clearCurrentValueAndMarker(); |
| 385 } |
| 386 |
| 387 reset() { |
| 388 this._range.textContent = ''; |
| 389 } |
| 390 |
| 391 /** |
| 392 * @param {number} minValue |
| 393 * @param {number} maxValue |
| 394 */ |
| 395 setRange(minValue, maxValue) { |
| 396 var min = this._formatter(minValue); |
| 397 var max = this._formatter(maxValue); |
| 398 this._range.textContent = WebInspector.UIString('[%s\u2009\u2013\u2009%s]',
min, max); |
| 399 } |
| 400 |
| 401 /** |
| 402 * @param {!WebInspector.Event} event |
| 403 */ |
| 404 _toggleCounterGraph(event) { |
| 405 this._value.classList.toggle('hidden', !this._filter.checked()); |
| 406 this._memoryCountersPane.refresh(); |
| 407 } |
| 408 |
| 409 /** |
| 410 * @param {number} x |
| 411 * @return {number} |
| 412 */ |
| 413 _recordIndexAt(x) { |
| 414 return this.counter.x.upperBound( |
| 415 x * window.devicePixelRatio, null, this.counter._minimumIndex + 1
, this.counter._maximumIndex + 1) - |
| 416 1; |
| 417 } |
| 418 |
| 419 /** |
| 420 * @param {number} x |
| 421 */ |
| 422 updateCurrentValue(x) { |
| 423 if (!this.visible() || !this.counter.values.length || !this.counter.x) |
| 424 return; |
| 425 var index = this._recordIndexAt(x); |
| 426 var value = Number.withThousandsSeparator(this.counter.values[index]); |
| 427 this._value.textContent = WebInspector.UIString(this._currentValueLabel, val
ue); |
| 428 var y = this.graphYValues[index] / window.devicePixelRatio; |
| 429 this._marker.style.left = x + 'px'; |
| 430 this._marker.style.top = y + 'px'; |
| 431 this._marker.classList.remove('hidden'); |
| 432 } |
| 433 |
| 434 _clearCurrentValueAndMarker() { |
| 435 this._value.textContent = ''; |
| 436 this._marker.classList.add('hidden'); |
| 437 } |
| 438 |
| 439 /** |
| 440 * @param {!HTMLCanvasElement} canvas |
| 441 */ |
| 442 _drawGraph(canvas) { |
| 443 var ctx = canvas.getContext('2d'); |
| 444 var width = canvas.width; |
| 445 var height = canvas.height - 2 * this._verticalPadding; |
| 446 if (height <= 0) { |
| 447 this.graphYValues = []; |
| 448 return; |
| 449 } |
| 450 var originY = this._verticalPadding; |
| 451 var counter = this.counter; |
| 452 var values = counter.values; |
| 453 |
| 454 if (!values.length) |
| 455 return; |
| 456 |
| 457 var bounds = counter._calculateBounds(); |
| 458 var minValue = bounds.min; |
| 459 var maxValue = bounds.max; |
| 460 this.setRange(minValue, maxValue); |
| 461 |
| 462 if (!this.visible()) |
| 463 return; |
| 464 |
| 465 var yValues = this.graphYValues; |
| 466 var maxYRange = maxValue - minValue; |
| 467 var yFactor = maxYRange ? height / (maxYRange) : 1; |
| 468 |
| 469 ctx.save(); |
| 470 ctx.lineWidth = window.devicePixelRatio; |
| 471 if (ctx.lineWidth % 2) |
| 472 ctx.translate(0.5, 0.5); |
| 473 ctx.beginPath(); |
| 474 var value = values[counter._minimumIndex]; |
| 475 var currentY = Math.round(originY + height - (value - minValue) * yFactor); |
| 476 ctx.moveTo(0, currentY); |
| 477 for (var i = counter._minimumIndex; i <= counter._maximumIndex; i++) { |
| 478 var x = Math.round(counter.x[i]); |
| 479 ctx.lineTo(x, currentY); |
| 480 var currentValue = values[i]; |
| 481 if (typeof currentValue !== 'undefined') |
| 482 value = currentValue; |
| 483 currentY = Math.round(originY + height - (value - minValue) * yFactor); |
| 484 ctx.lineTo(x, currentY); |
| 485 yValues[i] = currentY; |
| 486 } |
| 487 yValues.length = i; |
| 488 ctx.lineTo(width, currentY); |
| 489 ctx.strokeStyle = this.graphColor; |
| 490 ctx.stroke(); |
| 491 if (counter._limitValue) { |
| 492 var limitLineY = Math.round(originY + height - (counter._limitValue - minV
alue) * yFactor); |
| 493 ctx.moveTo(0, limitLineY); |
| 494 ctx.lineTo(width, limitLineY); |
| 495 ctx.strokeStyle = this.limitColor; |
| 496 ctx.stroke(); |
| 497 } |
| 498 ctx.closePath(); |
| 499 ctx.restore(); |
| 500 } |
| 501 |
| 502 /** |
| 503 * @return {boolean} |
| 504 */ |
| 505 visible() { |
| 506 return this._filter.checked(); |
| 507 } |
| 411 }; | 508 }; |
| 412 | 509 |
| 413 WebInspector.CountersGraph.CounterUI.prototype = { | 510 /** |
| 414 reset: function() | 511 * @implements {WebInspector.TimelineGrid.Calculator} |
| 415 { | 512 * @unrestricted |
| 416 this._range.textContent = ""; | 513 */ |
| 417 }, | 514 WebInspector.CounterGraphCalculator = class { |
| 418 | 515 /** |
| 419 /** | 516 * @param {!WebInspector.TimelineModel} model |
| 420 * @param {number} minValue | 517 */ |
| 421 * @param {number} maxValue | 518 constructor(model) { |
| 422 */ | 519 this._model = model; |
| 423 setRange: function(minValue, maxValue) | 520 } |
| 424 { | 521 |
| 425 var min = this._formatter(minValue); | 522 /** |
| 426 var max = this._formatter(maxValue); | 523 * @override |
| 427 this._range.textContent = WebInspector.UIString("[%s\u2009\u2013\u2009%s
]", min, max); | 524 * @return {number} |
| 428 }, | 525 */ |
| 429 | 526 paddingLeft() { |
| 430 /** | 527 return this._paddingLeft; |
| 431 * @param {!WebInspector.Event} event | 528 } |
| 432 */ | 529 |
| 433 _toggleCounterGraph: function(event) | 530 /** |
| 434 { | 531 * @override |
| 435 this._value.classList.toggle("hidden", !this._filter.checked()); | 532 * @param {number} time |
| 436 this._memoryCountersPane.refresh(); | 533 * @return {number} |
| 437 }, | 534 */ |
| 438 | 535 computePosition(time) { |
| 439 /** | 536 return (time - this._minimumBoundary) / this.boundarySpan() * this._workingA
rea + this._paddingLeft; |
| 440 * @param {number} x | 537 } |
| 441 * @return {number} | 538 |
| 442 */ | 539 setWindow(minimumBoundary, maximumBoundary) { |
| 443 _recordIndexAt: function(x) | 540 this._minimumBoundary = minimumBoundary; |
| 444 { | 541 this._maximumBoundary = maximumBoundary; |
| 445 return this.counter.x.upperBound(x * window.devicePixelRatio, null, this
.counter._minimumIndex + 1, this.counter._maximumIndex + 1) - 1; | 542 } |
| 446 }, | 543 |
| 447 | 544 /** |
| 448 /** | 545 * @param {number} clientWidth |
| 449 * @param {number} x | 546 * @param {number=} paddingLeft |
| 450 */ | 547 */ |
| 451 updateCurrentValue: function(x) | 548 setDisplayWindow(clientWidth, paddingLeft) { |
| 452 { | 549 this._paddingLeft = paddingLeft || 0; |
| 453 if (!this.visible() || !this.counter.values.length || !this.counter.x) | 550 this._workingArea = clientWidth - WebInspector.CounterGraphCalculator._minWi
dth - this._paddingLeft; |
| 454 return; | 551 } |
| 455 var index = this._recordIndexAt(x); | 552 |
| 456 var value = Number.withThousandsSeparator(this.counter.values[index]); | 553 /** |
| 457 this._value.textContent = WebInspector.UIString(this._currentValueLabel,
value); | 554 * @override |
| 458 var y = this.graphYValues[index] / window.devicePixelRatio; | 555 * @param {number} value |
| 459 this._marker.style.left = x + "px"; | 556 * @param {number=} precision |
| 460 this._marker.style.top = y + "px"; | 557 * @return {string} |
| 461 this._marker.classList.remove("hidden"); | 558 */ |
| 462 }, | 559 formatValue(value, precision) { |
| 463 | 560 return Number.preciseMillisToString(value - this.zeroTime(), precision); |
| 464 _clearCurrentValueAndMarker: function() | 561 } |
| 465 { | 562 |
| 466 this._value.textContent = ""; | 563 /** |
| 467 this._marker.classList.add("hidden"); | 564 * @override |
| 468 }, | 565 * @return {number} |
| 469 | 566 */ |
| 470 /** | 567 maximumBoundary() { |
| 471 * @param {!HTMLCanvasElement} canvas | 568 return this._maximumBoundary; |
| 472 */ | 569 } |
| 473 _drawGraph: function(canvas) | 570 |
| 474 { | 571 /** |
| 475 var ctx = canvas.getContext("2d"); | 572 * @override |
| 476 var width = canvas.width; | 573 * @return {number} |
| 477 var height = canvas.height - 2 * this._verticalPadding; | 574 */ |
| 478 if (height <= 0) { | 575 minimumBoundary() { |
| 479 this.graphYValues = []; | 576 return this._minimumBoundary; |
| 480 return; | 577 } |
| 481 } | 578 |
| 482 var originY = this._verticalPadding; | 579 /** |
| 483 var counter = this.counter; | 580 * @override |
| 484 var values = counter.values; | 581 * @return {number} |
| 485 | 582 */ |
| 486 if (!values.length) | 583 zeroTime() { |
| 487 return; | 584 return this._model.minimumRecordTime(); |
| 488 | 585 } |
| 489 var bounds = counter._calculateBounds(); | 586 |
| 490 var minValue = bounds.min; | 587 /** |
| 491 var maxValue = bounds.max; | 588 * @override |
| 492 this.setRange(minValue, maxValue); | 589 * @return {number} |
| 493 | 590 */ |
| 494 if (!this.visible()) | 591 boundarySpan() { |
| 495 return; | 592 return this._maximumBoundary - this._minimumBoundary; |
| 496 | 593 } |
| 497 var yValues = this.graphYValues; | |
| 498 var maxYRange = maxValue - minValue; | |
| 499 var yFactor = maxYRange ? height / (maxYRange) : 1; | |
| 500 | |
| 501 ctx.save(); | |
| 502 ctx.lineWidth = window.devicePixelRatio; | |
| 503 if (ctx.lineWidth % 2) | |
| 504 ctx.translate(0.5, 0.5); | |
| 505 ctx.beginPath(); | |
| 506 var value = values[counter._minimumIndex]; | |
| 507 var currentY = Math.round(originY + height - (value - minValue) * yFacto
r); | |
| 508 ctx.moveTo(0, currentY); | |
| 509 for (var i = counter._minimumIndex; i <= counter._maximumIndex; i++) { | |
| 510 var x = Math.round(counter.x[i]); | |
| 511 ctx.lineTo(x, currentY); | |
| 512 var currentValue = values[i]; | |
| 513 if (typeof currentValue !== "undefined") | |
| 514 value = currentValue; | |
| 515 currentY = Math.round(originY + height - (value - minValue) * yFacto
r); | |
| 516 ctx.lineTo(x, currentY); | |
| 517 yValues[i] = currentY; | |
| 518 } | |
| 519 yValues.length = i; | |
| 520 ctx.lineTo(width, currentY); | |
| 521 ctx.strokeStyle = this.graphColor; | |
| 522 ctx.stroke(); | |
| 523 if (counter._limitValue) { | |
| 524 var limitLineY = Math.round(originY + height - (counter._limitValue
- minValue) * yFactor); | |
| 525 ctx.moveTo(0, limitLineY); | |
| 526 ctx.lineTo(width, limitLineY); | |
| 527 ctx.strokeStyle = this.limitColor; | |
| 528 ctx.stroke(); | |
| 529 } | |
| 530 ctx.closePath(); | |
| 531 ctx.restore(); | |
| 532 }, | |
| 533 | |
| 534 /** | |
| 535 * @return {boolean} | |
| 536 */ | |
| 537 visible: function() | |
| 538 { | |
| 539 return this._filter.checked(); | |
| 540 } | |
| 541 }; | 594 }; |
| 542 | 595 |
| 543 /** | |
| 544 * @constructor | |
| 545 * @param {!WebInspector.TimelineModel} model | |
| 546 * @implements {WebInspector.TimelineGrid.Calculator} | |
| 547 */ | |
| 548 WebInspector.CounterGraphCalculator = function(model) | |
| 549 { | |
| 550 this._model = model; | |
| 551 }; | |
| 552 | |
| 553 WebInspector.CounterGraphCalculator._minWidth = 5; | 596 WebInspector.CounterGraphCalculator._minWidth = 5; |
| 554 | |
| 555 WebInspector.CounterGraphCalculator.prototype = { | |
| 556 /** | |
| 557 * @override | |
| 558 * @return {number} | |
| 559 */ | |
| 560 paddingLeft: function() | |
| 561 { | |
| 562 return this._paddingLeft; | |
| 563 }, | |
| 564 | |
| 565 /** | |
| 566 * @override | |
| 567 * @param {number} time | |
| 568 * @return {number} | |
| 569 */ | |
| 570 computePosition: function(time) | |
| 571 { | |
| 572 return (time - this._minimumBoundary) / this.boundarySpan() * this._work
ingArea + this._paddingLeft; | |
| 573 }, | |
| 574 | |
| 575 setWindow: function(minimumBoundary, maximumBoundary) | |
| 576 { | |
| 577 this._minimumBoundary = minimumBoundary; | |
| 578 this._maximumBoundary = maximumBoundary; | |
| 579 }, | |
| 580 | |
| 581 /** | |
| 582 * @param {number} clientWidth | |
| 583 * @param {number=} paddingLeft | |
| 584 */ | |
| 585 setDisplayWindow: function(clientWidth, paddingLeft) | |
| 586 { | |
| 587 this._paddingLeft = paddingLeft || 0; | |
| 588 this._workingArea = clientWidth - WebInspector.CounterGraphCalculator._m
inWidth - this._paddingLeft; | |
| 589 }, | |
| 590 | |
| 591 /** | |
| 592 * @override | |
| 593 * @param {number} value | |
| 594 * @param {number=} precision | |
| 595 * @return {string} | |
| 596 */ | |
| 597 formatValue: function(value, precision) | |
| 598 { | |
| 599 return Number.preciseMillisToString(value - this.zeroTime(), precision); | |
| 600 }, | |
| 601 | |
| 602 /** | |
| 603 * @override | |
| 604 * @return {number} | |
| 605 */ | |
| 606 maximumBoundary: function() | |
| 607 { | |
| 608 return this._maximumBoundary; | |
| 609 }, | |
| 610 | |
| 611 /** | |
| 612 * @override | |
| 613 * @return {number} | |
| 614 */ | |
| 615 minimumBoundary: function() | |
| 616 { | |
| 617 return this._minimumBoundary; | |
| 618 }, | |
| 619 | |
| 620 /** | |
| 621 * @override | |
| 622 * @return {number} | |
| 623 */ | |
| 624 zeroTime: function() | |
| 625 { | |
| 626 return this._model.minimumRecordTime(); | |
| 627 }, | |
| 628 | |
| 629 /** | |
| 630 * @override | |
| 631 * @return {number} | |
| 632 */ | |
| 633 boundarySpan: function() | |
| 634 { | |
| 635 return this._maximumBoundary - this._minimumBoundary; | |
| 636 } | |
| 637 }; | |
| OLD | NEW |