Home | History | Annotate | Download | only in front_end
      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