| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 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 /** | 30 /** |
| 32 * @constructor | 31 * @unrestricted |
| 33 * @extends {WebInspector.VBox} | |
| 34 * @param {string} prefix | |
| 35 */ | 32 */ |
| 36 WebInspector.TimelineOverviewPane = function(prefix) | 33 WebInspector.TimelineOverviewPane = class extends WebInspector.VBox { |
| 37 { | 34 /** |
| 38 WebInspector.VBox.call(this); | 35 * @param {string} prefix |
| 39 this.element.id = prefix + "-overview-pane"; | 36 */ |
| 37 constructor(prefix) { |
| 38 super(); |
| 39 this.element.id = prefix + '-overview-pane'; |
| 40 | 40 |
| 41 this._overviewCalculator = new WebInspector.TimelineOverviewCalculator(); | 41 this._overviewCalculator = new WebInspector.TimelineOverviewCalculator(); |
| 42 this._overviewGrid = new WebInspector.OverviewGrid(prefix); | 42 this._overviewGrid = new WebInspector.OverviewGrid(prefix); |
| 43 this.element.appendChild(this._overviewGrid.element); | 43 this.element.appendChild(this._overviewGrid.element); |
| 44 this._cursorArea = this._overviewGrid.element.createChild("div", "overview-g
rid-cursor-area"); | 44 this._cursorArea = this._overviewGrid.element.createChild('div', 'overview-g
rid-cursor-area'); |
| 45 this._cursorElement = this._overviewGrid.element.createChild("div", "overvie
w-grid-cursor-position"); | 45 this._cursorElement = this._overviewGrid.element.createChild('div', 'overvie
w-grid-cursor-position'); |
| 46 this._cursorArea.addEventListener("mousemove", this._onMouseMove.bind(this),
true); | 46 this._cursorArea.addEventListener('mousemove', this._onMouseMove.bind(this),
true); |
| 47 this._cursorArea.addEventListener("mouseleave", this._hideCursor.bind(this),
true); | 47 this._cursorArea.addEventListener('mouseleave', this._hideCursor.bind(this),
true); |
| 48 | 48 |
| 49 this._overviewGrid.setResizeEnabled(false); | 49 this._overviewGrid.setResizeEnabled(false); |
| 50 this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowC
hanged, this._onWindowChanged, this); | 50 this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowC
hanged, this._onWindowChanged, this); |
| 51 this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.Click,
this._onClick, this); | 51 this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.Click,
this._onClick, this); |
| 52 this._overviewControls = []; | 52 this._overviewControls = []; |
| 53 this._markers = new Map(); | 53 this._markers = new Map(); |
| 54 | 54 |
| 55 this._popoverHelper = new WebInspector.PopoverHelper(this._cursorArea); | 55 this._popoverHelper = new WebInspector.PopoverHelper(this._cursorArea); |
| 56 this._popoverHelper.initializeCallbacks(this._getPopoverAnchor.bind(this), t
his._showPopover.bind(this), this._onHidePopover.bind(this)); | 56 this._popoverHelper.initializeCallbacks( |
| 57 this._getPopoverAnchor.bind(this), this._showPopover.bind(this), this._o
nHidePopover.bind(this)); |
| 57 this._popoverHelper.setTimeout(0); | 58 this._popoverHelper.setTimeout(0); |
| 58 | 59 |
| 59 this._updateThrottler = new WebInspector.Throttler(100); | 60 this._updateThrottler = new WebInspector.Throttler(100); |
| 60 | 61 |
| 61 this._cursorEnabled = false; | 62 this._cursorEnabled = false; |
| 62 this._cursorPosition = 0; | 63 this._cursorPosition = 0; |
| 63 this._lastWidth = 0; | 64 this._lastWidth = 0; |
| 65 } |
| 66 |
| 67 /** |
| 68 * @param {!Element} element |
| 69 * @param {!Event} event |
| 70 * @return {!Element|!AnchorBox|undefined} |
| 71 */ |
| 72 _getPopoverAnchor(element, event) { |
| 73 return this._cursorArea; |
| 74 } |
| 75 |
| 76 /** |
| 77 * @param {!Element} anchor |
| 78 * @param {!WebInspector.Popover} popover |
| 79 */ |
| 80 _showPopover(anchor, popover) { |
| 81 this._buildPopoverContents().then(maybeShowPopover.bind(this)); |
| 82 /** |
| 83 * @this {WebInspector.TimelineOverviewPane} |
| 84 * @param {!DocumentFragment} fragment |
| 85 */ |
| 86 function maybeShowPopover(fragment) { |
| 87 if (!fragment.firstChild) |
| 88 return; |
| 89 var content = new WebInspector.TimelineOverviewPane.PopoverContents(); |
| 90 this._popoverContents = content.contentElement.createChild('div'); |
| 91 this._popoverContents.appendChild(fragment); |
| 92 this._popover = popover; |
| 93 popover.showView(content, this._cursorElement); |
| 94 } |
| 95 } |
| 96 |
| 97 _onHidePopover() { |
| 98 this._popover = null; |
| 99 this._popoverContents = null; |
| 100 } |
| 101 |
| 102 /** |
| 103 * @param {!Event} event |
| 104 */ |
| 105 _onMouseMove(event) { |
| 106 if (!this._cursorEnabled) |
| 107 return; |
| 108 this._cursorPosition = event.offsetX + event.target.offsetLeft; |
| 109 this._cursorElement.style.left = this._cursorPosition + 'px'; |
| 110 this._cursorElement.style.visibility = 'visible'; |
| 111 if (!this._popover) |
| 112 return; |
| 113 this._buildPopoverContents().then(updatePopover.bind(this)); |
| 114 this._popover.positionElement(this._cursorElement); |
| 115 |
| 116 /** |
| 117 * @param {!DocumentFragment} fragment |
| 118 * @this {WebInspector.TimelineOverviewPane} |
| 119 */ |
| 120 function updatePopover(fragment) { |
| 121 if (!this._popoverContents) |
| 122 return; |
| 123 this._popoverContents.removeChildren(); |
| 124 this._popoverContents.appendChild(fragment); |
| 125 } |
| 126 } |
| 127 |
| 128 /** |
| 129 * @return {!Promise<!DocumentFragment>} |
| 130 */ |
| 131 _buildPopoverContents() { |
| 132 var document = this.element.ownerDocument; |
| 133 var x = this._cursorPosition; |
| 134 var promises = this._overviewControls.map(control => control.popoverElementP
romise(x)); |
| 135 return Promise.all(promises).then(buildFragment); |
| 136 |
| 137 /** |
| 138 * @param {!Array<?Element>} elements |
| 139 * @return {!DocumentFragment} |
| 140 */ |
| 141 function buildFragment(elements) { |
| 142 var fragment = document.createDocumentFragment(); |
| 143 elements.remove(null); |
| 144 fragment.appendChildren.apply(fragment, elements); |
| 145 return fragment; |
| 146 } |
| 147 } |
| 148 |
| 149 _hideCursor() { |
| 150 this._cursorElement.style.visibility = 'hidden'; |
| 151 } |
| 152 |
| 153 /** |
| 154 * @override |
| 155 */ |
| 156 wasShown() { |
| 157 this._update(); |
| 158 } |
| 159 |
| 160 /** |
| 161 * @override |
| 162 */ |
| 163 willHide() { |
| 164 this._popoverHelper.hidePopover(); |
| 165 } |
| 166 |
| 167 /** |
| 168 * @override |
| 169 */ |
| 170 onResize() { |
| 171 var width = this.element.offsetWidth; |
| 172 if (width === this._lastWidth) |
| 173 return; |
| 174 this._lastWidth = width; |
| 175 this.scheduleUpdate(); |
| 176 } |
| 177 |
| 178 /** |
| 179 * @param {!Array.<!WebInspector.TimelineOverview>} overviewControls |
| 180 */ |
| 181 setOverviewControls(overviewControls) { |
| 182 for (var i = 0; i < this._overviewControls.length; ++i) |
| 183 this._overviewControls[i].dispose(); |
| 184 |
| 185 for (var i = 0; i < overviewControls.length; ++i) { |
| 186 overviewControls[i].setCalculator(this._overviewCalculator); |
| 187 overviewControls[i].show(this._overviewGrid.element); |
| 188 } |
| 189 this._overviewControls = overviewControls; |
| 190 this._update(); |
| 191 } |
| 192 |
| 193 /** |
| 194 * @param {number} minimumBoundary |
| 195 * @param {number} maximumBoundary |
| 196 */ |
| 197 setBounds(minimumBoundary, maximumBoundary) { |
| 198 this._overviewCalculator.setBounds(minimumBoundary, maximumBoundary); |
| 199 this._overviewGrid.setResizeEnabled(true); |
| 200 this._cursorEnabled = true; |
| 201 } |
| 202 |
| 203 scheduleUpdate() { |
| 204 this._updateThrottler.schedule(process.bind(this)); |
| 205 /** |
| 206 * @this {WebInspector.TimelineOverviewPane} |
| 207 * @return {!Promise.<undefined>} |
| 208 */ |
| 209 function process() { |
| 210 this._update(); |
| 211 return Promise.resolve(); |
| 212 } |
| 213 } |
| 214 |
| 215 _update() { |
| 216 if (!this.isShowing()) |
| 217 return; |
| 218 this._overviewCalculator.setDisplayWindow(this._overviewGrid.clientWidth()); |
| 219 for (var i = 0; i < this._overviewControls.length; ++i) |
| 220 this._overviewControls[i].update(); |
| 221 this._overviewGrid.updateDividers(this._overviewCalculator); |
| 222 this._updateMarkers(); |
| 223 this._updateWindow(); |
| 224 } |
| 225 |
| 226 /** |
| 227 * @param {!Map<number, !Element>} markers |
| 228 */ |
| 229 setMarkers(markers) { |
| 230 this._markers = markers; |
| 231 this._updateMarkers(); |
| 232 } |
| 233 |
| 234 _updateMarkers() { |
| 235 var filteredMarkers = new Map(); |
| 236 for (var time of this._markers.keys()) { |
| 237 var marker = this._markers.get(time); |
| 238 var position = Math.round(this._overviewCalculator.computePosition(time)); |
| 239 // Limit the number of markers to one per pixel. |
| 240 if (filteredMarkers.has(position)) |
| 241 continue; |
| 242 filteredMarkers.set(position, marker); |
| 243 marker.style.left = position + 'px'; |
| 244 } |
| 245 this._overviewGrid.removeEventDividers(); |
| 246 this._overviewGrid.addEventDividers(filteredMarkers.valuesArray()); |
| 247 } |
| 248 |
| 249 reset() { |
| 250 this._windowStartTime = 0; |
| 251 this._windowEndTime = Infinity; |
| 252 this._overviewCalculator.reset(); |
| 253 this._overviewGrid.reset(); |
| 254 this._overviewGrid.setResizeEnabled(false); |
| 255 this._overviewGrid.updateDividers(this._overviewCalculator); |
| 256 this._cursorEnabled = false; |
| 257 this._hideCursor(); |
| 258 this._markers = new Map(); |
| 259 for (var i = 0; i < this._overviewControls.length; ++i) |
| 260 this._overviewControls[i].reset(); |
| 261 this._popoverHelper.hidePopover(); |
| 262 this._update(); |
| 263 } |
| 264 |
| 265 /** |
| 266 * @param {!WebInspector.Event} event |
| 267 */ |
| 268 _onClick(event) { |
| 269 var domEvent = /** @type {!Event} */ (event.data); |
| 270 for (var overviewControl of this._overviewControls) { |
| 271 if (overviewControl.onClick(domEvent)) { |
| 272 event.preventDefault(); |
| 273 return; |
| 274 } |
| 275 } |
| 276 } |
| 277 |
| 278 /** |
| 279 * @param {!WebInspector.Event} event |
| 280 */ |
| 281 _onWindowChanged(event) { |
| 282 if (this._muteOnWindowChanged) |
| 283 return; |
| 284 // Always use first control as a time converter. |
| 285 if (!this._overviewControls.length) |
| 286 return; |
| 287 var windowTimes = |
| 288 this._overviewControls[0].windowTimes(this._overviewGrid.windowLeft(), t
his._overviewGrid.windowRight()); |
| 289 this._windowStartTime = windowTimes.startTime; |
| 290 this._windowEndTime = windowTimes.endTime; |
| 291 this.dispatchEventToListeners(WebInspector.TimelineOverviewPane.Events.Windo
wChanged, windowTimes); |
| 292 } |
| 293 |
| 294 /** |
| 295 * @param {number} startTime |
| 296 * @param {number} endTime |
| 297 */ |
| 298 requestWindowTimes(startTime, endTime) { |
| 299 if (startTime === this._windowStartTime && endTime === this._windowEndTime) |
| 300 return; |
| 301 this._windowStartTime = startTime; |
| 302 this._windowEndTime = endTime; |
| 303 this._updateWindow(); |
| 304 this.dispatchEventToListeners( |
| 305 WebInspector.TimelineOverviewPane.Events.WindowChanged, {startTime: star
tTime, endTime: endTime}); |
| 306 } |
| 307 |
| 308 _updateWindow() { |
| 309 if (!this._overviewControls.length) |
| 310 return; |
| 311 var windowBoundaries = this._overviewControls[0].windowBoundaries(this._wind
owStartTime, this._windowEndTime); |
| 312 this._muteOnWindowChanged = true; |
| 313 this._overviewGrid.setWindow(windowBoundaries.left, windowBoundaries.right); |
| 314 this._muteOnWindowChanged = false; |
| 315 } |
| 64 }; | 316 }; |
| 65 | 317 |
| 66 /** @enum {symbol} */ | 318 /** @enum {symbol} */ |
| 67 WebInspector.TimelineOverviewPane.Events = { | 319 WebInspector.TimelineOverviewPane.Events = { |
| 68 WindowChanged: Symbol("WindowChanged") | 320 WindowChanged: Symbol('WindowChanged') |
| 69 }; | 321 }; |
| 70 | 322 |
| 71 WebInspector.TimelineOverviewPane.prototype = { | 323 /** |
| 72 /** | 324 * @unrestricted |
| 73 * @param {!Element} element | 325 */ |
| 74 * @param {!Event} event | 326 WebInspector.TimelineOverviewPane.PopoverContents = class extends WebInspector.V
Box { |
| 75 * @return {!Element|!AnchorBox|undefined} | 327 constructor() { |
| 76 */ | 328 super(true); |
| 77 _getPopoverAnchor: function(element, event) | 329 this.contentElement.classList.add('timeline-overview-popover'); |
| 78 { | 330 } |
| 79 return this._cursorArea; | |
| 80 }, | |
| 81 | |
| 82 /** | |
| 83 * @param {!Element} anchor | |
| 84 * @param {!WebInspector.Popover} popover | |
| 85 */ | |
| 86 _showPopover: function(anchor, popover) | |
| 87 { | |
| 88 this._buildPopoverContents().then(maybeShowPopover.bind(this)); | |
| 89 /** | |
| 90 * @this {WebInspector.TimelineOverviewPane} | |
| 91 * @param {!DocumentFragment} fragment | |
| 92 */ | |
| 93 function maybeShowPopover(fragment) | |
| 94 { | |
| 95 if (!fragment.firstChild) | |
| 96 return; | |
| 97 var content = new WebInspector.TimelineOverviewPane.PopoverContents(
); | |
| 98 this._popoverContents = content.contentElement.createChild("div"); | |
| 99 this._popoverContents.appendChild(fragment); | |
| 100 this._popover = popover; | |
| 101 popover.showView(content, this._cursorElement); | |
| 102 } | |
| 103 }, | |
| 104 | |
| 105 _onHidePopover: function() | |
| 106 { | |
| 107 this._popover = null; | |
| 108 this._popoverContents = null; | |
| 109 }, | |
| 110 | |
| 111 /** | |
| 112 * @param {!Event} event | |
| 113 */ | |
| 114 _onMouseMove: function(event) | |
| 115 { | |
| 116 if (!this._cursorEnabled) | |
| 117 return; | |
| 118 this._cursorPosition = event.offsetX + event.target.offsetLeft; | |
| 119 this._cursorElement.style.left = this._cursorPosition + "px"; | |
| 120 this._cursorElement.style.visibility = "visible"; | |
| 121 if (!this._popover) | |
| 122 return; | |
| 123 this._buildPopoverContents().then(updatePopover.bind(this)); | |
| 124 this._popover.positionElement(this._cursorElement); | |
| 125 | |
| 126 /** | |
| 127 * @param {!DocumentFragment} fragment | |
| 128 * @this {WebInspector.TimelineOverviewPane} | |
| 129 */ | |
| 130 function updatePopover(fragment) | |
| 131 { | |
| 132 if (!this._popoverContents) | |
| 133 return; | |
| 134 this._popoverContents.removeChildren(); | |
| 135 this._popoverContents.appendChild(fragment); | |
| 136 } | |
| 137 }, | |
| 138 | |
| 139 /** | |
| 140 * @return {!Promise<!DocumentFragment>} | |
| 141 */ | |
| 142 _buildPopoverContents: function() | |
| 143 { | |
| 144 var document = this.element.ownerDocument; | |
| 145 var x = this._cursorPosition; | |
| 146 var promises = this._overviewControls.map(control => control.popoverElem
entPromise(x)); | |
| 147 return Promise.all(promises).then(buildFragment); | |
| 148 | |
| 149 /** | |
| 150 * @param {!Array<?Element>} elements | |
| 151 * @return {!DocumentFragment} | |
| 152 */ | |
| 153 function buildFragment(elements) | |
| 154 { | |
| 155 var fragment = document.createDocumentFragment(); | |
| 156 elements.remove(null); | |
| 157 fragment.appendChildren.apply(fragment, elements); | |
| 158 return fragment; | |
| 159 } | |
| 160 }, | |
| 161 | |
| 162 _hideCursor: function() | |
| 163 { | |
| 164 this._cursorElement.style.visibility = "hidden"; | |
| 165 }, | |
| 166 | |
| 167 /** | |
| 168 * @override | |
| 169 */ | |
| 170 wasShown: function() | |
| 171 { | |
| 172 this._update(); | |
| 173 }, | |
| 174 | |
| 175 /** | |
| 176 * @override | |
| 177 */ | |
| 178 willHide: function() | |
| 179 { | |
| 180 this._popoverHelper.hidePopover(); | |
| 181 }, | |
| 182 | |
| 183 /** | |
| 184 * @override | |
| 185 */ | |
| 186 onResize: function() | |
| 187 { | |
| 188 var width = this.element.offsetWidth; | |
| 189 if (width === this._lastWidth) | |
| 190 return; | |
| 191 this._lastWidth = width; | |
| 192 this.scheduleUpdate(); | |
| 193 }, | |
| 194 | |
| 195 /** | |
| 196 * @param {!Array.<!WebInspector.TimelineOverview>} overviewControls | |
| 197 */ | |
| 198 setOverviewControls: function(overviewControls) | |
| 199 { | |
| 200 for (var i = 0; i < this._overviewControls.length; ++i) | |
| 201 this._overviewControls[i].dispose(); | |
| 202 | |
| 203 for (var i = 0; i < overviewControls.length; ++i) { | |
| 204 overviewControls[i].setCalculator(this._overviewCalculator); | |
| 205 overviewControls[i].show(this._overviewGrid.element); | |
| 206 } | |
| 207 this._overviewControls = overviewControls; | |
| 208 this._update(); | |
| 209 }, | |
| 210 | |
| 211 /** | |
| 212 * @param {number} minimumBoundary | |
| 213 * @param {number} maximumBoundary | |
| 214 */ | |
| 215 setBounds: function(minimumBoundary, maximumBoundary) | |
| 216 { | |
| 217 this._overviewCalculator.setBounds(minimumBoundary, maximumBoundary); | |
| 218 this._overviewGrid.setResizeEnabled(true); | |
| 219 this._cursorEnabled = true; | |
| 220 }, | |
| 221 | |
| 222 scheduleUpdate: function() | |
| 223 { | |
| 224 this._updateThrottler.schedule(process.bind(this)); | |
| 225 /** | |
| 226 * @this {WebInspector.TimelineOverviewPane} | |
| 227 * @return {!Promise.<undefined>} | |
| 228 */ | |
| 229 function process() | |
| 230 { | |
| 231 this._update(); | |
| 232 return Promise.resolve(); | |
| 233 } | |
| 234 }, | |
| 235 | |
| 236 _update: function() | |
| 237 { | |
| 238 if (!this.isShowing()) | |
| 239 return; | |
| 240 this._overviewCalculator.setDisplayWindow(this._overviewGrid.clientWidth
()); | |
| 241 for (var i = 0; i < this._overviewControls.length; ++i) | |
| 242 this._overviewControls[i].update(); | |
| 243 this._overviewGrid.updateDividers(this._overviewCalculator); | |
| 244 this._updateMarkers(); | |
| 245 this._updateWindow(); | |
| 246 }, | |
| 247 | |
| 248 /** | |
| 249 * @param {!Map<number, !Element>} markers | |
| 250 */ | |
| 251 setMarkers: function(markers) | |
| 252 { | |
| 253 this._markers = markers; | |
| 254 this._updateMarkers(); | |
| 255 }, | |
| 256 | |
| 257 _updateMarkers: function() | |
| 258 { | |
| 259 var filteredMarkers = new Map(); | |
| 260 for (var time of this._markers.keys()) { | |
| 261 var marker = this._markers.get(time); | |
| 262 var position = Math.round(this._overviewCalculator.computePosition(t
ime)); | |
| 263 // Limit the number of markers to one per pixel. | |
| 264 if (filteredMarkers.has(position)) | |
| 265 continue; | |
| 266 filteredMarkers.set(position, marker); | |
| 267 marker.style.left = position + "px"; | |
| 268 } | |
| 269 this._overviewGrid.removeEventDividers(); | |
| 270 this._overviewGrid.addEventDividers(filteredMarkers.valuesArray()); | |
| 271 }, | |
| 272 | |
| 273 reset: function() | |
| 274 { | |
| 275 this._windowStartTime = 0; | |
| 276 this._windowEndTime = Infinity; | |
| 277 this._overviewCalculator.reset(); | |
| 278 this._overviewGrid.reset(); | |
| 279 this._overviewGrid.setResizeEnabled(false); | |
| 280 this._overviewGrid.updateDividers(this._overviewCalculator); | |
| 281 this._cursorEnabled = false; | |
| 282 this._hideCursor(); | |
| 283 this._markers = new Map(); | |
| 284 for (var i = 0; i < this._overviewControls.length; ++i) | |
| 285 this._overviewControls[i].reset(); | |
| 286 this._popoverHelper.hidePopover(); | |
| 287 this._update(); | |
| 288 }, | |
| 289 | |
| 290 /** | |
| 291 * @param {!WebInspector.Event} event | |
| 292 */ | |
| 293 _onClick: function(event) | |
| 294 { | |
| 295 var domEvent = /** @type {!Event} */ (event.data); | |
| 296 for (var overviewControl of this._overviewControls) { | |
| 297 if (overviewControl.onClick(domEvent)) { | |
| 298 event.preventDefault(); | |
| 299 return; | |
| 300 } | |
| 301 } | |
| 302 }, | |
| 303 | |
| 304 /** | |
| 305 * @param {!WebInspector.Event} event | |
| 306 */ | |
| 307 _onWindowChanged: function(event) | |
| 308 { | |
| 309 if (this._muteOnWindowChanged) | |
| 310 return; | |
| 311 // Always use first control as a time converter. | |
| 312 if (!this._overviewControls.length) | |
| 313 return; | |
| 314 var windowTimes = this._overviewControls[0].windowTimes(this._overviewGr
id.windowLeft(), this._overviewGrid.windowRight()); | |
| 315 this._windowStartTime = windowTimes.startTime; | |
| 316 this._windowEndTime = windowTimes.endTime; | |
| 317 this.dispatchEventToListeners(WebInspector.TimelineOverviewPane.Events.W
indowChanged, windowTimes); | |
| 318 }, | |
| 319 | |
| 320 /** | |
| 321 * @param {number} startTime | |
| 322 * @param {number} endTime | |
| 323 */ | |
| 324 requestWindowTimes: function(startTime, endTime) | |
| 325 { | |
| 326 if (startTime === this._windowStartTime && endTime === this._windowEndTi
me) | |
| 327 return; | |
| 328 this._windowStartTime = startTime; | |
| 329 this._windowEndTime = endTime; | |
| 330 this._updateWindow(); | |
| 331 this.dispatchEventToListeners(WebInspector.TimelineOverviewPane.Events.W
indowChanged, { startTime: startTime, endTime: endTime }); | |
| 332 }, | |
| 333 | |
| 334 _updateWindow: function() | |
| 335 { | |
| 336 if (!this._overviewControls.length) | |
| 337 return; | |
| 338 var windowBoundaries = this._overviewControls[0].windowBoundaries(this._
windowStartTime, this._windowEndTime); | |
| 339 this._muteOnWindowChanged = true; | |
| 340 this._overviewGrid.setWindow(windowBoundaries.left, windowBoundaries.rig
ht); | |
| 341 this._muteOnWindowChanged = false; | |
| 342 }, | |
| 343 | |
| 344 __proto__: WebInspector.VBox.prototype | |
| 345 }; | 331 }; |
| 346 | 332 |
| 347 /** | 333 /** |
| 348 * @constructor | 334 * @implements {WebInspector.TimelineGrid.Calculator} |
| 349 * @extends {WebInspector.VBox} | 335 * @unrestricted |
| 350 */ | 336 */ |
| 351 WebInspector.TimelineOverviewPane.PopoverContents = function() | 337 WebInspector.TimelineOverviewCalculator = class { |
| 352 { | 338 constructor() { |
| 353 WebInspector.VBox.call(this, true); | |
| 354 this.contentElement.classList.add("timeline-overview-popover"); | |
| 355 }; | |
| 356 | |
| 357 WebInspector.TimelineOverviewPane.PopoverContents.prototype = { | |
| 358 __proto__: WebInspector.VBox.prototype | |
| 359 }; | |
| 360 | |
| 361 /** | |
| 362 * @constructor | |
| 363 * @implements {WebInspector.TimelineGrid.Calculator} | |
| 364 */ | |
| 365 WebInspector.TimelineOverviewCalculator = function() | |
| 366 { | |
| 367 this.reset(); | 339 this.reset(); |
| 368 }; | 340 } |
| 369 | 341 |
| 370 WebInspector.TimelineOverviewCalculator.prototype = { | 342 /** |
| 371 /** | 343 * @override |
| 372 * @override | 344 * @return {number} |
| 373 * @return {number} | 345 */ |
| 374 */ | 346 paddingLeft() { |
| 375 paddingLeft: function() | 347 return this._paddingLeft; |
| 376 { | 348 } |
| 377 return this._paddingLeft; | 349 |
| 378 }, | 350 /** |
| 379 | 351 * @override |
| 380 /** | 352 * @param {number} time |
| 381 * @override | 353 * @return {number} |
| 382 * @param {number} time | 354 */ |
| 383 * @return {number} | 355 computePosition(time) { |
| 384 */ | 356 return (time - this._minimumBoundary) / this.boundarySpan() * this._workingA
rea + this._paddingLeft; |
| 385 computePosition: function(time) | 357 } |
| 386 { | 358 |
| 387 return (time - this._minimumBoundary) / this.boundarySpan() * this._work
ingArea + this._paddingLeft; | 359 /** |
| 388 }, | 360 * @param {number} position |
| 389 | 361 * @return {number} |
| 390 /** | 362 */ |
| 391 * @param {number} position | 363 positionToTime(position) { |
| 392 * @return {number} | 364 return (position - this._paddingLeft) / this._workingArea * this.boundarySpa
n() + this._minimumBoundary; |
| 393 */ | 365 } |
| 394 positionToTime: function(position) | 366 |
| 395 { | 367 /** |
| 396 return (position - this._paddingLeft) / this._workingArea * this.boundar
ySpan() + this._minimumBoundary; | 368 * @param {number} minimumBoundary |
| 397 }, | 369 * @param {number} maximumBoundary |
| 398 | 370 */ |
| 399 /** | 371 setBounds(minimumBoundary, maximumBoundary) { |
| 400 * @param {number} minimumBoundary | 372 this._minimumBoundary = minimumBoundary; |
| 401 * @param {number} maximumBoundary | 373 this._maximumBoundary = maximumBoundary; |
| 402 */ | 374 } |
| 403 setBounds: function(minimumBoundary, maximumBoundary) | 375 |
| 404 { | 376 /** |
| 405 this._minimumBoundary = minimumBoundary; | 377 * @param {number} clientWidth |
| 406 this._maximumBoundary = maximumBoundary; | 378 * @param {number=} paddingLeft |
| 407 }, | 379 */ |
| 408 | 380 setDisplayWindow(clientWidth, paddingLeft) { |
| 409 /** | 381 this._paddingLeft = paddingLeft || 0; |
| 410 * @param {number} clientWidth | 382 this._workingArea = clientWidth - this._paddingLeft; |
| 411 * @param {number=} paddingLeft | 383 } |
| 412 */ | 384 |
| 413 setDisplayWindow: function(clientWidth, paddingLeft) | 385 reset() { |
| 414 { | 386 this.setBounds(0, 1000); |
| 415 this._paddingLeft = paddingLeft || 0; | 387 } |
| 416 this._workingArea = clientWidth - this._paddingLeft; | 388 |
| 417 }, | 389 /** |
| 418 | 390 * @override |
| 419 reset: function() | 391 * @param {number} value |
| 420 { | 392 * @param {number=} precision |
| 421 this.setBounds(0, 1000); | 393 * @return {string} |
| 422 }, | 394 */ |
| 423 | 395 formatValue(value, precision) { |
| 424 /** | 396 return Number.preciseMillisToString(value - this.zeroTime(), precision); |
| 425 * @override | 397 } |
| 426 * @param {number} value | 398 |
| 427 * @param {number=} precision | 399 /** |
| 428 * @return {string} | 400 * @override |
| 429 */ | 401 * @return {number} |
| 430 formatValue: function(value, precision) | 402 */ |
| 431 { | 403 maximumBoundary() { |
| 432 return Number.preciseMillisToString(value - this.zeroTime(), precision); | 404 return this._maximumBoundary; |
| 433 }, | 405 } |
| 434 | 406 |
| 435 /** | 407 /** |
| 436 * @override | 408 * @override |
| 437 * @return {number} | 409 * @return {number} |
| 438 */ | 410 */ |
| 439 maximumBoundary: function() | 411 minimumBoundary() { |
| 440 { | 412 return this._minimumBoundary; |
| 441 return this._maximumBoundary; | 413 } |
| 442 }, | 414 |
| 443 | 415 /** |
| 444 /** | 416 * @override |
| 445 * @override | 417 * @return {number} |
| 446 * @return {number} | 418 */ |
| 447 */ | 419 zeroTime() { |
| 448 minimumBoundary: function() | 420 return this._minimumBoundary; |
| 449 { | 421 } |
| 450 return this._minimumBoundary; | 422 |
| 451 }, | 423 /** |
| 452 | 424 * @override |
| 453 /** | 425 * @return {number} |
| 454 * @override | 426 */ |
| 455 * @return {number} | 427 boundarySpan() { |
| 456 */ | 428 return this._maximumBoundary - this._minimumBoundary; |
| 457 zeroTime: function() | 429 } |
| 458 { | |
| 459 return this._minimumBoundary; | |
| 460 }, | |
| 461 | |
| 462 /** | |
| 463 * @override | |
| 464 * @return {number} | |
| 465 */ | |
| 466 boundarySpan: function() | |
| 467 { | |
| 468 return this._maximumBoundary - this._minimumBoundary; | |
| 469 } | |
| 470 }; | 430 }; |
| 471 | 431 |
| 472 /** | 432 /** |
| 473 * @interface | 433 * @interface |
| 474 */ | 434 */ |
| 475 WebInspector.TimelineOverview = function() | 435 WebInspector.TimelineOverview = function() {}; |
| 476 { | 436 |
| 437 WebInspector.TimelineOverview.prototype = { |
| 438 /** |
| 439 * @param {!Element} parentElement |
| 440 * @param {?Element=} insertBefore |
| 441 */ |
| 442 show: function(parentElement, insertBefore) {}, |
| 443 |
| 444 update: function() {}, |
| 445 |
| 446 dispose: function() {}, |
| 447 |
| 448 reset: function() {}, |
| 449 |
| 450 /** |
| 451 * @param {number} x |
| 452 * @return {!Promise<?Element>} |
| 453 */ |
| 454 popoverElementPromise: function(x) {}, |
| 455 |
| 456 /** |
| 457 * @param {!Event} event |
| 458 * @return {boolean} |
| 459 */ |
| 460 onClick: function(event) {}, |
| 461 |
| 462 /** |
| 463 * @param {number} windowLeft |
| 464 * @param {number} windowRight |
| 465 * @return {!{startTime: number, endTime: number}} |
| 466 */ |
| 467 windowTimes: function(windowLeft, windowRight) {}, |
| 468 |
| 469 /** |
| 470 * @param {number} startTime |
| 471 * @param {number} endTime |
| 472 * @return {!{left: number, right: number}} |
| 473 */ |
| 474 windowBoundaries: function(startTime, endTime) {}, |
| 475 |
| 476 timelineStarted: function() {}, |
| 477 |
| 478 timelineStopped: function() {}, |
| 477 }; | 479 }; |
| 478 | 480 |
| 479 WebInspector.TimelineOverview.prototype = { | |
| 480 /** | |
| 481 * @param {!Element} parentElement | |
| 482 * @param {?Element=} insertBefore | |
| 483 */ | |
| 484 show: function(parentElement, insertBefore) { }, | |
| 485 | |
| 486 update: function() { }, | |
| 487 | |
| 488 dispose: function() { }, | |
| 489 | |
| 490 reset: function() { }, | |
| 491 | |
| 492 /** | |
| 493 * @param {number} x | |
| 494 * @return {!Promise<?Element>} | |
| 495 */ | |
| 496 popoverElementPromise: function(x) { }, | |
| 497 | |
| 498 /** | |
| 499 * @param {!Event} event | |
| 500 * @return {boolean} | |
| 501 */ | |
| 502 onClick: function(event) { }, | |
| 503 | |
| 504 /** | |
| 505 * @param {number} windowLeft | |
| 506 * @param {number} windowRight | |
| 507 * @return {!{startTime: number, endTime: number}} | |
| 508 */ | |
| 509 windowTimes: function(windowLeft, windowRight) { }, | |
| 510 | |
| 511 /** | |
| 512 * @param {number} startTime | |
| 513 * @param {number} endTime | |
| 514 * @return {!{left: number, right: number}} | |
| 515 */ | |
| 516 windowBoundaries: function(startTime, endTime) { }, | |
| 517 | |
| 518 timelineStarted: function() { }, | |
| 519 | |
| 520 timelineStopped: function() { }, | |
| 521 }; | |
| 522 | |
| 523 /** | 481 /** |
| 524 * @constructor | |
| 525 * @extends {WebInspector.VBox} | |
| 526 * @implements {WebInspector.TimelineOverview} | 482 * @implements {WebInspector.TimelineOverview} |
| 483 * @unrestricted |
| 527 */ | 484 */ |
| 528 WebInspector.TimelineOverviewBase = function() | 485 WebInspector.TimelineOverviewBase = class extends WebInspector.VBox { |
| 529 { | 486 constructor() { |
| 530 WebInspector.VBox.call(this); | 487 super(); |
| 531 /** @type {?WebInspector.TimelineOverviewCalculator} */ | 488 /** @type {?WebInspector.TimelineOverviewCalculator} */ |
| 532 this._calculator = null; | 489 this._calculator = null; |
| 533 this._canvas = this.element.createChild("canvas", "fill"); | 490 this._canvas = this.element.createChild('canvas', 'fill'); |
| 534 this._context = this._canvas.getContext("2d"); | 491 this._context = this._canvas.getContext('2d'); |
| 492 } |
| 493 |
| 494 /** |
| 495 * @override |
| 496 */ |
| 497 update() { |
| 498 this.resetCanvas(); |
| 499 } |
| 500 |
| 501 /** |
| 502 * @override |
| 503 */ |
| 504 dispose() { |
| 505 this.detach(); |
| 506 } |
| 507 |
| 508 /** |
| 509 * @override |
| 510 */ |
| 511 reset() { |
| 512 } |
| 513 |
| 514 /** |
| 515 * @override |
| 516 * @param {number} x |
| 517 * @return {!Promise<?Element>} |
| 518 */ |
| 519 popoverElementPromise(x) { |
| 520 return Promise.resolve(/** @type {?Element} */ (null)); |
| 521 } |
| 522 |
| 523 /** |
| 524 * @override |
| 525 */ |
| 526 timelineStarted() { |
| 527 } |
| 528 |
| 529 /** |
| 530 * @override |
| 531 */ |
| 532 timelineStopped() { |
| 533 } |
| 534 |
| 535 /** |
| 536 * @param {!WebInspector.TimelineOverviewCalculator} calculator |
| 537 */ |
| 538 setCalculator(calculator) { |
| 539 this._calculator = calculator; |
| 540 } |
| 541 |
| 542 /** |
| 543 * @override |
| 544 * @param {!Event} event |
| 545 * @return {boolean} |
| 546 */ |
| 547 onClick(event) { |
| 548 return false; |
| 549 } |
| 550 |
| 551 /** |
| 552 * @override |
| 553 * @param {number} windowLeft |
| 554 * @param {number} windowRight |
| 555 * @return {!{startTime: number, endTime: number}} |
| 556 */ |
| 557 windowTimes(windowLeft, windowRight) { |
| 558 var absoluteMin = this._calculator.minimumBoundary(); |
| 559 var timeSpan = this._calculator.maximumBoundary() - absoluteMin; |
| 560 return {startTime: absoluteMin + timeSpan * windowLeft, endTime: absoluteMin
+ timeSpan * windowRight}; |
| 561 } |
| 562 |
| 563 /** |
| 564 * @override |
| 565 * @param {number} startTime |
| 566 * @param {number} endTime |
| 567 * @return {!{left: number, right: number}} |
| 568 */ |
| 569 windowBoundaries(startTime, endTime) { |
| 570 var absoluteMin = this._calculator.minimumBoundary(); |
| 571 var timeSpan = this._calculator.maximumBoundary() - absoluteMin; |
| 572 var haveRecords = absoluteMin > 0; |
| 573 return { |
| 574 left: haveRecords && startTime ? Math.min((startTime - absoluteMin) / time
Span, 1) : 0, |
| 575 right: haveRecords && endTime < Infinity ? (endTime - absoluteMin) / timeS
pan : 1 |
| 576 }; |
| 577 } |
| 578 |
| 579 resetCanvas() { |
| 580 this._canvas.width = this.element.clientWidth * window.devicePixelRatio; |
| 581 this._canvas.height = this.element.clientHeight * window.devicePixelRatio; |
| 582 } |
| 535 }; | 583 }; |
| 536 | |
| 537 WebInspector.TimelineOverviewBase.prototype = { | |
| 538 /** | |
| 539 * @override | |
| 540 */ | |
| 541 update: function() | |
| 542 { | |
| 543 this.resetCanvas(); | |
| 544 }, | |
| 545 | |
| 546 /** | |
| 547 * @override | |
| 548 */ | |
| 549 dispose: function() | |
| 550 { | |
| 551 this.detach(); | |
| 552 }, | |
| 553 | |
| 554 /** | |
| 555 * @override | |
| 556 */ | |
| 557 reset: function() | |
| 558 { | |
| 559 }, | |
| 560 | |
| 561 /** | |
| 562 * @override | |
| 563 * @param {number} x | |
| 564 * @return {!Promise<?Element>} | |
| 565 */ | |
| 566 popoverElementPromise: function(x) | |
| 567 { | |
| 568 return Promise.resolve(/** @type {?Element} */ (null)); | |
| 569 }, | |
| 570 | |
| 571 /** | |
| 572 * @override | |
| 573 */ | |
| 574 timelineStarted: function() | |
| 575 { | |
| 576 }, | |
| 577 | |
| 578 /** | |
| 579 * @override | |
| 580 */ | |
| 581 timelineStopped: function() | |
| 582 { | |
| 583 }, | |
| 584 | |
| 585 /** | |
| 586 * @param {!WebInspector.TimelineOverviewCalculator} calculator | |
| 587 */ | |
| 588 setCalculator: function(calculator) | |
| 589 { | |
| 590 this._calculator = calculator; | |
| 591 }, | |
| 592 | |
| 593 /** | |
| 594 * @override | |
| 595 * @param {!Event} event | |
| 596 * @return {boolean} | |
| 597 */ | |
| 598 onClick: function(event) | |
| 599 { | |
| 600 return false; | |
| 601 }, | |
| 602 | |
| 603 /** | |
| 604 * @override | |
| 605 * @param {number} windowLeft | |
| 606 * @param {number} windowRight | |
| 607 * @return {!{startTime: number, endTime: number}} | |
| 608 */ | |
| 609 windowTimes: function(windowLeft, windowRight) | |
| 610 { | |
| 611 var absoluteMin = this._calculator.minimumBoundary(); | |
| 612 var timeSpan = this._calculator.maximumBoundary() - absoluteMin; | |
| 613 return { | |
| 614 startTime: absoluteMin + timeSpan * windowLeft, | |
| 615 endTime: absoluteMin + timeSpan * windowRight | |
| 616 }; | |
| 617 }, | |
| 618 | |
| 619 /** | |
| 620 * @override | |
| 621 * @param {number} startTime | |
| 622 * @param {number} endTime | |
| 623 * @return {!{left: number, right: number}} | |
| 624 */ | |
| 625 windowBoundaries: function(startTime, endTime) | |
| 626 { | |
| 627 var absoluteMin = this._calculator.minimumBoundary(); | |
| 628 var timeSpan = this._calculator.maximumBoundary() - absoluteMin; | |
| 629 var haveRecords = absoluteMin > 0; | |
| 630 return { | |
| 631 left: haveRecords && startTime ? Math.min((startTime - absoluteMin)
/ timeSpan, 1) : 0, | |
| 632 right: haveRecords && endTime < Infinity ? (endTime - absoluteMin) /
timeSpan : 1 | |
| 633 }; | |
| 634 }, | |
| 635 | |
| 636 resetCanvas: function() | |
| 637 { | |
| 638 this._canvas.width = this.element.clientWidth * window.devicePixelRatio; | |
| 639 this._canvas.height = this.element.clientHeight * window.devicePixelRati
o; | |
| 640 }, | |
| 641 | |
| 642 __proto__: WebInspector.VBox.prototype | |
| 643 }; | |
| OLD | NEW |