const timingOffset = performance.timing.navigationStart, timingEnd = performance.timing.loadEventEnd, totalTime = timingEnd - timingOffset; function getLeft(stat) { return ((performance.timing[stat] - timingOffset) / (totalTime)) * 100.0; } function getCSSWidth(stat, endStat) { let width = ((performance.timing[endStat] - performance.timing[stat]) / (totalTime)) * 100.0; // Calculate relative percent (same as sql panel logic) width = 100.0 * width / (100.0 - getLeft(stat)); return (width < 1) ? "2px" : width + "%"; } function addRow(stat, endStat) { const row = document.createElement('tr'); if (endStat) { // Render a start through end bar row.innerHTML = '' + stat.replace('Start', '') + '' + '' + '' + (performance.timing[stat] - timingOffset) + ' (+' + (performance.timing[endStat] - performance.timing[stat]) + ')'; row.querySelector('rect').setAttribute('width', getCSSWidth(stat, endStat)); } else { // Render a point in time row.innerHTML = '' + stat + '' + '' + '' + (performance.timing[stat] - timingOffset) + ''; row.querySelector('rect').setAttribute('width', 2); } row.querySelector('rect').setAttribute('x', getLeft(stat)); document.querySelector('#djDebugBrowserTimingTableBody').appendChild(row); } // This is a reasonably complete and ordered set of timing periods (2 params) and events (1 param) addRow('domainLookupStart', 'domainLookupEnd'); addRow('connectStart', 'connectEnd'); addRow('requestStart', 'responseEnd'); // There is no requestEnd addRow('responseStart', 'responseEnd'); addRow('domLoading', 'domComplete'); // Spans the events below addRow('domInteractive'); addRow('domContentLoadedEventStart', 'domContentLoadedEventEnd'); addRow('loadEventStart', 'loadEventEnd'); document.querySelector('#djDebugBrowserTiming').classList.remove('djdt-hidden');