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(tbody, 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)); tbody.appendChild(row); } const tbody = document.getElementById("djDebugBrowserTimingTableBody"); // This is a reasonably complete and ordered set of timing periods (2 params) and events (1 param) addRow(tbody, "domainLookupStart", "domainLookupEnd"); addRow(tbody, "connectStart", "connectEnd"); addRow(tbody, "requestStart", "responseEnd"); // There is no requestEnd addRow(tbody, "responseStart", "responseEnd"); addRow(tbody, "domLoading", "domComplete"); // Spans the events below addRow(tbody, "domInteractive"); addRow(tbody, "domContentLoadedEventStart", "domContentLoadedEventEnd"); addRow(tbody, "loadEventStart", "loadEventEnd"); document.getElementById("djDebugBrowserTiming").classList.remove("djdt-hidden");