| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. | |
| 3 * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org> | |
| 4 * Copyright (C) 2009 Google Inc. All rights reserved. | |
| 5 * | |
| 6 * Redistribution and use in source and binary forms, with or without | |
| 7 * modification, are permitted provided that the following conditions | |
| 8 * are met: | |
| 9 * | |
| 10 * 1. Redistributions of source code must retain the above copyright | |
| 11 * notice, this list of conditions and the following disclaimer. | |
| 12 * 2. Redistributions in binary form must reproduce the above copyright | |
| 13 * notice, this list of conditions and the following disclaimer in the | |
| 14 * documentation and/or other materials provided with the distribution. | |
| 15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of | |
| 16 * its contributors may be used to endorse or promote products derived | |
| 17 * from this software without specific prior written permission. | |
| 18 * | |
| 19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | |
| 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
| 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| 22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | |
| 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
| 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
| 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
| 26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
| 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 29 */ | |
| 30 | |
| 31 /** | |
| 32 * @constructor | |
| 33 */ | |
| 34 WebInspector.TimelineGrid = function() | |
| 35 { | |
| 36 this.element = createElement("div"); | |
| 37 | |
| 38 this._dividersElement = this.element.createChild("div", "resources-dividers"
); | |
| 39 | |
| 40 this._gridHeaderElement = createElement("div"); | |
| 41 this._gridHeaderElement.id = "timeline-grid-header"; | |
| 42 this._eventDividersElement = this._gridHeaderElement.createChild("div", "res
ources-event-dividers"); | |
| 43 this._dividersLabelBarElement = this._gridHeaderElement.createChild("div", "
resources-dividers-label-bar"); | |
| 44 this.element.appendChild(this._gridHeaderElement); | |
| 45 | |
| 46 this._leftCurtainElement = this.element.createChild("div", "timeline-cpu-cur
tain-left"); | |
| 47 this._rightCurtainElement = this.element.createChild("div", "timeline-cpu-cu
rtain-right"); | |
| 48 } | |
| 49 | |
| 50 /** | |
| 51 * @param {!WebInspector.TimelineGrid.Calculator} calculator | |
| 52 * @return {!{offsets: !Array.<number>, precision: number}} | |
| 53 */ | |
| 54 WebInspector.TimelineGrid.calculateDividerOffsets = function(calculator) | |
| 55 { | |
| 56 const minGridSlicePx = 64; // minimal distance between grid lines. | |
| 57 const gridFreeZoneAtLeftPx = 50; | |
| 58 | |
| 59 var clientWidth = calculator.computePosition(calculator.maximumBoundary()); | |
| 60 var dividersCount = clientWidth / minGridSlicePx; | |
| 61 var gridSliceTime = calculator.boundarySpan() / dividersCount; | |
| 62 var pixelsPerTime = clientWidth / calculator.boundarySpan(); | |
| 63 | |
| 64 // Align gridSliceTime to a nearest round value. | |
| 65 // We allow spans that fit into the formula: span = (1|2|5)x10^n, | |
| 66 // e.g.: ... .1 .2 .5 1 2 5 10 20 50 ... | |
| 67 // After a span has been chosen make grid lines at multiples of the span. | |
| 68 | |
| 69 var logGridSliceTime = Math.ceil(Math.log(gridSliceTime) / Math.LN10); | |
| 70 gridSliceTime = Math.pow(10, logGridSliceTime); | |
| 71 if (gridSliceTime * pixelsPerTime >= 5 * minGridSlicePx) | |
| 72 gridSliceTime = gridSliceTime / 5; | |
| 73 if (gridSliceTime * pixelsPerTime >= 2 * minGridSlicePx) | |
| 74 gridSliceTime = gridSliceTime / 2; | |
| 75 | |
| 76 var firstDividerTime = Math.ceil((calculator.minimumBoundary() - calculator.
zeroTime()) / gridSliceTime) * gridSliceTime + calculator.zeroTime(); | |
| 77 var lastDividerTime = calculator.maximumBoundary(); | |
| 78 // Add some extra space past the right boundary as the rightmost divider lab
el text | |
| 79 // may be partially shown rather than just pop up when a new rightmost divid
er gets into the view. | |
| 80 if (calculator.paddingLeft() > 0) | |
| 81 lastDividerTime = lastDividerTime + minGridSlicePx / pixelsPerTime; | |
| 82 dividersCount = Math.ceil((lastDividerTime - firstDividerTime) / gridSliceTi
me); | |
| 83 | |
| 84 var skipLeftmostDividers = calculator.paddingLeft() === 0; | |
| 85 | |
| 86 if (!gridSliceTime) | |
| 87 dividersCount = 0; | |
| 88 | |
| 89 var offsets = []; | |
| 90 for (var i = 0; i < dividersCount; ++i) { | |
| 91 var left = calculator.computePosition(firstDividerTime + gridSliceTime *
i); | |
| 92 if (skipLeftmostDividers && left < gridFreeZoneAtLeftPx) | |
| 93 continue; | |
| 94 offsets.push(firstDividerTime + gridSliceTime * i); | |
| 95 } | |
| 96 | |
| 97 return {offsets: offsets, precision: Math.max(0, -Math.floor(Math.log(gridSl
iceTime * 1.01) / Math.LN10))}; | |
| 98 } | |
| 99 | |
| 100 /** | |
| 101 * @param {!Object} canvas | |
| 102 * @param {!WebInspector.TimelineGrid.Calculator} calculator | |
| 103 * @param {?Array.<number>=} dividerOffsets | |
| 104 */ | |
| 105 WebInspector.TimelineGrid.drawCanvasGrid = function(canvas, calculator, dividerO
ffsets) | |
| 106 { | |
| 107 var context = canvas.getContext("2d"); | |
| 108 context.save(); | |
| 109 var ratio = window.devicePixelRatio; | |
| 110 context.scale(ratio, ratio); | |
| 111 var printDeltas = !!dividerOffsets; | |
| 112 var width = canvas.width / window.devicePixelRatio; | |
| 113 var height = canvas.height / window.devicePixelRatio; | |
| 114 var precision = 0; | |
| 115 if (!dividerOffsets) { | |
| 116 var dividersData = WebInspector.TimelineGrid.calculateDividerOffsets(cal
culator); | |
| 117 dividerOffsets = dividersData.offsets; | |
| 118 precision = dividersData.precision; | |
| 119 } | |
| 120 | |
| 121 context.fillStyle = "rgba(255, 255, 255, 0.5)"; | |
| 122 context.fillRect(0, 0, width, 15); | |
| 123 | |
| 124 context.fillStyle = "#333"; | |
| 125 context.strokeStyle = "rgba(0, 0, 0, 0.1)"; | |
| 126 context.textBaseline = "hanging"; | |
| 127 context.font = (printDeltas ? "italic bold 11px " : " 11px ") + WebInspector
.fontFamily(); | |
| 128 context.lineWidth = 1; | |
| 129 | |
| 130 context.translate(0.5, 0.5); | |
| 131 const minWidthForTitle = 60; | |
| 132 var lastPosition = 0; | |
| 133 var time = 0; | |
| 134 var lastTime = 0; | |
| 135 var paddingRight = 4; | |
| 136 var paddingTop = 3; | |
| 137 for (var i = 0; i < dividerOffsets.length; ++i) { | |
| 138 time = dividerOffsets[i]; | |
| 139 var position = calculator.computePosition(time); | |
| 140 context.beginPath(); | |
| 141 if (position - lastPosition > minWidthForTitle) { | |
| 142 if (!printDeltas || i !== 0) { | |
| 143 var text = printDeltas ? calculator.formatTime(calculator.zeroTi
me() + time - lastTime) : calculator.formatTime(time, precision); | |
| 144 var textWidth = context.measureText(text).width; | |
| 145 var textPosition = printDeltas ? (position + lastPosition - text
Width) / 2 : position - textWidth - paddingRight; | |
| 146 context.fillText(text, textPosition, paddingTop); | |
| 147 } | |
| 148 } | |
| 149 context.moveTo(position, 0); | |
| 150 context.lineTo(position, height); | |
| 151 context.stroke(); | |
| 152 lastTime = time; | |
| 153 lastPosition = position; | |
| 154 } | |
| 155 context.restore(); | |
| 156 }, | |
| 157 | |
| 158 WebInspector.TimelineGrid.prototype = { | |
| 159 get dividersElement() | |
| 160 { | |
| 161 return this._dividersElement; | |
| 162 }, | |
| 163 | |
| 164 get dividersLabelBarElement() | |
| 165 { | |
| 166 return this._dividersLabelBarElement; | |
| 167 }, | |
| 168 | |
| 169 removeDividers: function() | |
| 170 { | |
| 171 this._dividersElement.removeChildren(); | |
| 172 this._dividersLabelBarElement.removeChildren(); | |
| 173 }, | |
| 174 | |
| 175 /** | |
| 176 * @param {!WebInspector.TimelineGrid.Calculator} calculator | |
| 177 * @param {?Array.<number>=} dividerOffsets | |
| 178 * @param {boolean=} printDeltas | |
| 179 * @return {boolean} | |
| 180 */ | |
| 181 updateDividers: function(calculator, dividerOffsets, printDeltas) | |
| 182 { | |
| 183 var precision = 0; | |
| 184 if (!dividerOffsets) { | |
| 185 var dividersData = WebInspector.TimelineGrid.calculateDividerOffsets
(calculator); | |
| 186 dividerOffsets = dividersData.offsets; | |
| 187 precision = dividersData.precision; | |
| 188 printDeltas = false; | |
| 189 } | |
| 190 | |
| 191 var dividersElementClientWidth = this._dividersElement.clientWidth; | |
| 192 | |
| 193 // Reuse divider elements and labels. | |
| 194 var divider = /** @type {?Element} */ (this._dividersElement.firstChild)
; | |
| 195 var dividerLabelBar = /** @type {?Element} */ (this._dividersLabelBarEle
ment.firstChild); | |
| 196 | |
| 197 const minWidthForTitle = 60; | |
| 198 var lastPosition = 0; | |
| 199 var lastTime = 0; | |
| 200 for (var i = 0; i < dividerOffsets.length; ++i) { | |
| 201 if (!divider) { | |
| 202 divider = createElement("div"); | |
| 203 divider.className = "resources-divider"; | |
| 204 this._dividersElement.appendChild(divider); | |
| 205 | |
| 206 dividerLabelBar = createElement("div"); | |
| 207 dividerLabelBar.className = "resources-divider"; | |
| 208 var label = createElement("div"); | |
| 209 label.className = "resources-divider-label"; | |
| 210 dividerLabelBar._labelElement = label; | |
| 211 dividerLabelBar.appendChild(label); | |
| 212 this._dividersLabelBarElement.appendChild(dividerLabelBar); | |
| 213 } | |
| 214 | |
| 215 var time = dividerOffsets[i]; | |
| 216 var position = calculator.computePosition(time); | |
| 217 if (position - lastPosition > minWidthForTitle) | |
| 218 dividerLabelBar._labelElement.textContent = printDeltas ? calcul
ator.formatTime(time - lastTime) : calculator.formatTime(time, precision); | |
| 219 else | |
| 220 dividerLabelBar._labelElement.textContent = ""; | |
| 221 | |
| 222 if (printDeltas) | |
| 223 dividerLabelBar._labelElement.style.width = Math.ceil(position -
lastPosition) + "px"; | |
| 224 else | |
| 225 dividerLabelBar._labelElement.style.removeProperty("width"); | |
| 226 | |
| 227 lastPosition = position; | |
| 228 lastTime = time; | |
| 229 var percentLeft = 100 * position / dividersElementClientWidth; | |
| 230 divider.style.left = percentLeft + "%"; | |
| 231 dividerLabelBar.style.left = percentLeft + "%"; | |
| 232 | |
| 233 divider = /** @type {?Element} */ (divider.nextSibling); | |
| 234 dividerLabelBar = /** @type {?Element} */ (dividerLabelBar.nextSibli
ng); | |
| 235 } | |
| 236 | |
| 237 // Remove extras. | |
| 238 while (divider) { | |
| 239 var nextDivider = divider.nextSibling; | |
| 240 this._dividersElement.removeChild(divider); | |
| 241 divider = nextDivider; | |
| 242 } | |
| 243 while (dividerLabelBar) { | |
| 244 var nextDivider = dividerLabelBar.nextSibling; | |
| 245 this._dividersLabelBarElement.removeChild(dividerLabelBar); | |
| 246 dividerLabelBar = nextDivider; | |
| 247 } | |
| 248 return true; | |
| 249 }, | |
| 250 | |
| 251 addEventDivider: function(divider) | |
| 252 { | |
| 253 this._eventDividersElement.appendChild(divider); | |
| 254 }, | |
| 255 | |
| 256 addEventDividers: function(dividers) | |
| 257 { | |
| 258 this._gridHeaderElement.removeChild(this._eventDividersElement); | |
| 259 for (var i = 0; i < dividers.length; ++i) { | |
| 260 if (dividers[i]) | |
| 261 this._eventDividersElement.appendChild(dividers[i]); | |
| 262 } | |
| 263 this._gridHeaderElement.appendChild(this._eventDividersElement); | |
| 264 }, | |
| 265 | |
| 266 removeEventDividers: function() | |
| 267 { | |
| 268 this._eventDividersElement.removeChildren(); | |
| 269 }, | |
| 270 | |
| 271 hideEventDividers: function() | |
| 272 { | |
| 273 this._eventDividersElement.classList.add("hidden"); | |
| 274 }, | |
| 275 | |
| 276 showEventDividers: function() | |
| 277 { | |
| 278 this._eventDividersElement.classList.remove("hidden"); | |
| 279 }, | |
| 280 | |
| 281 hideDividers: function() | |
| 282 { | |
| 283 this._dividersElement.classList.add("hidden"); | |
| 284 }, | |
| 285 | |
| 286 showDividers: function() | |
| 287 { | |
| 288 this._dividersElement.classList.remove("hidden"); | |
| 289 }, | |
| 290 | |
| 291 hideCurtains: function() | |
| 292 { | |
| 293 this._leftCurtainElement.classList.add("hidden"); | |
| 294 this._rightCurtainElement.classList.add("hidden"); | |
| 295 }, | |
| 296 | |
| 297 /** | |
| 298 * @param {number} gapOffset | |
| 299 * @param {number} gapWidth | |
| 300 */ | |
| 301 showCurtains: function(gapOffset, gapWidth) | |
| 302 { | |
| 303 this._leftCurtainElement.style.width = gapOffset + "px"; | |
| 304 this._leftCurtainElement.classList.remove("hidden"); | |
| 305 this._rightCurtainElement.style.left = (gapOffset + gapWidth) + "px"; | |
| 306 this._rightCurtainElement.classList.remove("hidden"); | |
| 307 }, | |
| 308 | |
| 309 setScrollAndDividerTop: function(scrollTop, dividersTop) | |
| 310 { | |
| 311 this._dividersLabelBarElement.style.top = scrollTop + "px"; | |
| 312 this._eventDividersElement.style.top = scrollTop + "px"; | |
| 313 this._leftCurtainElement.style.top = scrollTop + "px"; | |
| 314 this._rightCurtainElement.style.top = scrollTop + "px"; | |
| 315 } | |
| 316 } | |
| 317 | |
| 318 /** | |
| 319 * @interface | |
| 320 */ | |
| 321 WebInspector.TimelineGrid.Calculator = function() { } | |
| 322 | |
| 323 WebInspector.TimelineGrid.Calculator.prototype = { | |
| 324 /** | |
| 325 * @return {number} | |
| 326 */ | |
| 327 paddingLeft: function() { }, | |
| 328 | |
| 329 /** | |
| 330 * @param {number} time | |
| 331 * @return {number} | |
| 332 */ | |
| 333 computePosition: function(time) { }, | |
| 334 | |
| 335 /** | |
| 336 * @param {number} time | |
| 337 * @param {number=} precision | |
| 338 * @return {string} | |
| 339 */ | |
| 340 formatTime: function(time, precision) { }, | |
| 341 | |
| 342 /** @return {number} */ | |
| 343 minimumBoundary: function() { }, | |
| 344 | |
| 345 /** @return {number} */ | |
| 346 zeroTime: function() { }, | |
| 347 | |
| 348 /** @return {number} */ | |
| 349 maximumBoundary: function() { }, | |
| 350 | |
| 351 /** @return {number} */ | |
| 352 boundarySpan: function() { } | |
| 353 } | |
| OLD | NEW |