Procházet zdrojové kódy

Brand new timeline graph

Gaël Métais před 10 roky
rodič
revize
2d00ac81ea

+ 27 - 0
app/node_views/results.html

@@ -116,6 +116,33 @@
         </div>
         </div>
 
 
         <div ng-show="view == 'execution' && !phantomasResults.error" class="execution">
         <div ng-show="view == 'execution' && !phantomasResults.error" class="execution">
+            <h2>Javascript Spaghetti Timeline</h2>
+            <p>
+                This graph gives a quick view of when the Javascript interactions with the DOM occur during the loading of the page.
+            </p>
+            <div class="timeline">
+                <div class="chart">
+                    <div ng-repeat="duration in timeline track by $index"
+                         class="interval"
+                         ng-class="{
+                                    domCreation: $index * timelineIntervalDuration < phantomasResults.metrics.domInteractive,
+                                    domContentLoaded: $index * timelineIntervalDuration >= phantomasResults.metrics.domContentLoaded
+                                                    && $index * timelineIntervalDuration < phantomasResults.metrics.domContentLoadedEnd,
+                                    domContentLoadedEnd: $index * timelineIntervalDuration >= phantomasResults.metrics.domContentLoadedEnd
+                                                    && $index * timelineIntervalDuration < phantomasResults.metrics.domComplete,
+                                    domComplete: $index * timelineIntervalDuration >= phantomasResults.metrics.domComplete
+                         }">
+                        <div style="height: {{100 * duration / timelineMax | number: 0}}px" class="color"></div>
+                    </div>
+                </div>
+                <div class="legend">
+                    <div class="domCreation"><div class="color"></div>DOM creation</div>
+                    <div class="domContentLoaded"><div class="color"></div>DOM content loaded event</div>
+                    <div class="domContentLoadedEnd"><div class="color"></div>Page completion</div>
+                    <div class="domComplete"><div class="color"></div>Page completed</div>
+                </div>
+            </div>
+
             <h2>Javascript Spaghetti Profiler</h2>
             <h2>Javascript Spaghetti Profiler</h2>
             <p>
             <p>
                 The table below shows the interactions between Javascript and the DOM. It is usefull to understand what's going on when the page loads.
                 The table below shows the interactions between Javascript and the DOM. It is usefull to understand what's going on when the page loads.

+ 50 - 0
app/public/scripts/resultsCtrl.js

@@ -215,6 +215,56 @@ app.controller('ResultsCtrl', function ($scope) {
     function initExecutionView() {
     function initExecutionView() {
         $scope.slowRequestsOn = false;
         $scope.slowRequestsOn = false;
         $scope.slowRequestsLimit = 5;
         $scope.slowRequestsLimit = 5;
+
+
+        // Now read the tree and display it on a timeline
+        
+        // Split the timeline into 200 intervals
+        var numberOfIntervals = 200;
+        var lastEvent = $scope.javascript.children[$scope.javascript.children.length - 1];
+        var endTime =  lastEvent.data.timestamp + (lastEvent.data.time || 0);
+        $scope.timelineIntervalDuration = endTime / numberOfIntervals;
+        
+        // Pre-filled array of 100 elements
+        $scope.timeline = Array.apply(null, new Array(numberOfIntervals)).map(Number.prototype.valueOf,0);
+
+        treeRunner($scope.javascript, function(node) {
+            
+            if (node.data.time) {
+                
+                // If a node is between two intervals, split it. That's the meaning of the following dirty algorithm.
+
+                var startInterval = Math.floor(node.data.timestamp / $scope.timelineIntervalDuration);
+                var endInterval = Math.floor((node.data.timestamp + node.data.time) / $scope.timelineIntervalDuration);
+
+                if (startInterval === endInterval) {
+                    
+                    $scope.timeline[startInterval] += node.data.time;
+
+                } else {
+                    
+                    var timeToDispatch = node.data.time;
+                    
+                    var startIntervalPart = ((startInterval + 1) * $scope.timelineIntervalDuration) - node.data.timestamp;
+                    $scope.timeline[startInterval] += startIntervalPart;
+                    timeToDispatch -= startIntervalPart;
+                    
+                    var currentInterval = startInterval;
+                    while(currentInterval < endInterval && currentInterval + 1 < numberOfIntervals) {
+                        currentInterval ++;
+                        var currentIntervalPart = Math.min(timeToDispatch, $scope.timelineIntervalDuration);
+                        $scope.timeline[currentInterval] = currentIntervalPart;
+                        timeToDispatch -= currentIntervalPart;
+                    }
+                }
+            }
+            
+            if (node.data.type !== 'main') {
+                // Don't check the children
+                return false;
+            }
+        });
+        $scope.timelineMax = Math.max.apply(Math, $scope.timeline);
     }
     }
 
 
     function initMetricsView() {
     function initMetricsView() {

+ 61 - 0
app/public/styles/results.css

@@ -115,6 +115,67 @@ h4 {
     background: #CCC;
     background: #CCC;
 }
 }
 
 
+.timeline {
+    margin: 2em 0 5em;
+}
+.timeline .chart {
+    height: 100px;
+    width: 100%;
+    border-bottom: 1px solid #000;
+}
+.timeline .interval {
+    position: relative;
+    display: inline-block;
+    height: 100px;
+    width: 0.5%;
+}
+.timeline .interval > div {
+    position: absolute;
+    bottom: 0;
+    width: 100%;
+}
+.timeline .domCreation.interval {
+    background: #FFE0CC;
+}
+.timeline .domCreation .color {
+    background: #FF6600;
+}
+.timeline .domContentLoaded.interval {
+    background: #E0FFD1;
+}
+.timeline .domContentLoaded .color {
+    background: #99FF66;
+}
+.timeline .domContentLoadedEnd.interval {
+    background: #D8F0F0;
+}
+.timeline .domContentLoadedEnd .color {
+    background: #7ECCCC;
+}
+.timeline .domComplete.interval {
+    background: #EDE3FF;
+}
+.timeline .domComplete .color {
+    background: #C2A3FF;
+}
+.timeline .legend {
+    display: table;
+    width: 100%;
+}
+.timeline .legend > div {
+    display: table-cell;
+    margin-top: 1em;
+}
+.timeline .legend .color {
+    display: inline-block;
+    position: relative;
+    top: 0.4em;
+    height: 1.5em;
+    width: 1.5em;
+    border-radius: 0.5em;
+    margin: 0 0.2em 0 1em;
+}
+
 
 
 .metrics h4 {
 .metrics h4 {
     padding-left: 2em;
     padding-left: 2em;