Brand new timeline graph
This commit is contained in:
@@ -116,6 +116,33 @@
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<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.
|
||||
|
||||
@@ -215,6 +215,56 @@ app.controller('ResultsCtrl', function ($scope) {
|
||||
function initExecutionView() {
|
||||
$scope.slowRequestsOn = false;
|
||||
$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() {
|
||||
|
||||
@@ -115,6 +115,67 @@ h4 {
|
||||
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 {
|
||||
padding-left: 2em;
|
||||
|
||||
Reference in New Issue
Block a user