1 /* 2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. 3 * Copyright (C) 2008, 2009 Anthony Ricaud <rik (at) 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 = document.createElement("div"); 37 38 this._itemsGraphsElement = document.createElement("div"); 39 this._itemsGraphsElement.id = "resources-graphs"; 40 this.element.appendChild(this._itemsGraphsElement); 41 42 this._dividersElement = this.element.createChild("div", "resources-dividers"); 43 44 this._gridHeaderElement = document.createElement("div"); 45 this._eventDividersElement = this._gridHeaderElement.createChild("div", "resources-event-dividers"); 46 this._dividersLabelBarElement = this._gridHeaderElement.createChild("div", "resources-dividers-label-bar"); 47 this.element.appendChild(this._gridHeaderElement); 48 49 this._leftCurtainElement = this.element.createChild("div", "timeline-cpu-curtain-left"); 50 this._rightCurtainElement = this.element.createChild("div", "timeline-cpu-curtain-right"); 51 } 52 53 WebInspector.TimelineGrid.prototype = { 54 get itemsGraphsElement() 55 { 56 return this._itemsGraphsElement; 57 }, 58 59 get dividersElement() 60 { 61 return this._dividersElement; 62 }, 63 64 get dividersLabelBarElement() 65 { 66 return this._dividersLabelBarElement; 67 }, 68 69 get gridHeaderElement() 70 { 71 return this._gridHeaderElement; 72 }, 73 74 removeDividers: function() 75 { 76 this._dividersElement.removeChildren(); 77 this._dividersLabelBarElement.removeChildren(); 78 }, 79 80 updateDividers: function(calculator) 81 { 82 const minGridSlicePx = 48; // minimal distance between grid lines. 83 const gridFreeZoneAtLeftPx = 50; 84 85 var dividersElementClientWidth = this._dividersElement.clientWidth; 86 var dividersCount = dividersElementClientWidth / minGridSlicePx; 87 var gridSliceTime = calculator.boundarySpan() / dividersCount; 88 var pixelsPerTime = dividersElementClientWidth / calculator.boundarySpan(); 89 90 // Align gridSliceTime to a nearest round value. 91 // We allow spans that fit into the formula: span = (1|2|5)x10^n, 92 // e.g.: ... .1 .2 .5 1 2 5 10 20 50 ... 93 // After a span has been chosen make grid lines at multiples of the span. 94 95 var logGridSliceTime = Math.ceil(Math.log(gridSliceTime) / Math.log(10)); 96 gridSliceTime = Math.pow(10, logGridSliceTime); 97 if (gridSliceTime * pixelsPerTime >= 5 * minGridSlicePx) 98 gridSliceTime = gridSliceTime / 5; 99 if (gridSliceTime * pixelsPerTime >= 2 * minGridSlicePx) 100 gridSliceTime = gridSliceTime / 2; 101 102 var firstDividerTime = Math.ceil((calculator.minimumBoundary() - calculator.zeroTime()) / gridSliceTime) * gridSliceTime + calculator.zeroTime(); 103 var lastDividerTime = calculator.maximumBoundary(); 104 // Add some extra space past the right boundary as the rightmost divider label text 105 // may be partially shown rather than just pop up when a new rightmost divider gets into the view. 106 if (calculator.paddingLeft > 0) 107 lastDividerTime = lastDividerTime + minGridSlicePx / pixelsPerTime; 108 dividersCount = Math.ceil((lastDividerTime - firstDividerTime) / gridSliceTime); 109 110 // Reuse divider elements and labels. 111 var divider = this._dividersElement.firstChild; 112 var dividerLabelBar = this._dividersLabelBarElement.firstChild; 113 114 var skipLeftmostDividers = calculator.paddingLeft === 0; 115 116 if (!gridSliceTime) 117 dividersCount = 0; 118 119 for (var i = 0; i < dividersCount; ++i) { 120 var left = calculator.computePosition(firstDividerTime + gridSliceTime * i); 121 if (skipLeftmostDividers && left < gridFreeZoneAtLeftPx) 122 continue; 123 124 if (!divider) { 125 divider = document.createElement("div"); 126 divider.className = "resources-divider"; 127 this._dividersElement.appendChild(divider); 128 129 dividerLabelBar = document.createElement("div"); 130 dividerLabelBar.className = "resources-divider"; 131 var label = document.createElement("div"); 132 label.className = "resources-divider-label"; 133 dividerLabelBar._labelElement = label; 134 dividerLabelBar.appendChild(label); 135 this._dividersLabelBarElement.appendChild(dividerLabelBar); 136 } 137 138 dividerLabelBar._labelElement.textContent = calculator.formatTime(firstDividerTime + gridSliceTime * i - calculator.minimumBoundary()); 139 var percentLeft = 100 * left / dividersElementClientWidth; 140 divider.style.left = percentLeft + "%"; 141 dividerLabelBar.style.left = percentLeft + "%"; 142 143 divider = divider.nextSibling; 144 dividerLabelBar = dividerLabelBar.nextSibling; 145 } 146 147 // Remove extras. 148 while (divider) { 149 var nextDivider = divider.nextSibling; 150 this._dividersElement.removeChild(divider); 151 divider = nextDivider; 152 } 153 while (dividerLabelBar) { 154 var nextDivider = dividerLabelBar.nextSibling; 155 this._dividersLabelBarElement.removeChild(dividerLabelBar); 156 dividerLabelBar = nextDivider; 157 } 158 return true; 159 }, 160 161 addEventDivider: function(divider) 162 { 163 this._eventDividersElement.appendChild(divider); 164 }, 165 166 addEventDividers: function(dividers) 167 { 168 this._gridHeaderElement.removeChild(this._eventDividersElement); 169 for (var i = 0; i < dividers.length; ++i) { 170 if (dividers[i]) 171 this._eventDividersElement.appendChild(dividers[i]); 172 } 173 this._gridHeaderElement.appendChild(this._eventDividersElement); 174 }, 175 176 removeEventDividers: function() 177 { 178 this._eventDividersElement.removeChildren(); 179 }, 180 181 hideEventDividers: function() 182 { 183 this._eventDividersElement.addStyleClass("hidden"); 184 }, 185 186 showEventDividers: function() 187 { 188 this._eventDividersElement.removeStyleClass("hidden"); 189 }, 190 191 hideCurtains: function() 192 { 193 this._leftCurtainElement.addStyleClass("hidden"); 194 this._rightCurtainElement.addStyleClass("hidden"); 195 }, 196 197 /** 198 * @param {number} gapOffset 199 * @param {number} gapWidth 200 */ 201 showCurtains: function(gapOffset, gapWidth) 202 { 203 this._leftCurtainElement.style.width = gapOffset + "px"; 204 this._leftCurtainElement.removeStyleClass("hidden"); 205 this._rightCurtainElement.style.left = (gapOffset + gapWidth) + "px"; 206 this._rightCurtainElement.removeStyleClass("hidden"); 207 }, 208 209 setScrollAndDividerTop: function(scrollTop, dividersTop) 210 { 211 this._dividersElement.style.top = scrollTop + "px"; 212 this._leftCurtainElement.style.top = scrollTop + "px"; 213 this._rightCurtainElement.style.top = scrollTop + "px"; 214 } 215 } 216 217 /** 218 * @interface 219 */ 220 WebInspector.TimelineGrid.Calculator = function() { } 221 222 WebInspector.TimelineGrid.Calculator.prototype = { 223 /** @param {number} time */ 224 computePosition: function(time) { }, 225 226 /** @param {number} time */ 227 formatTime: function(time) { }, 228 229 /** @return {number} */ 230 minimumBoundary: function() { }, 231 232 /** @return {number} */ 233 zeroTime: function() { }, 234 235 /** @return {number} */ 236 maximumBoundary: function() { }, 237 238 /** @return {number} */ 239 boundarySpan: function() { } 240 } 241