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