• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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