Plain is headed towards 1.0! Subscribe for development updates →

 1<form
 2    data-querystats
 3    action="."
 4    method="get"
 5    target="querystats"
 6    class="relative group/querystats"
 7    style="display: none;">
 8    <input type="hidden" name="querystats" value="store">
 9    <button type="submit" class="px-2 py-px text-xs rounded-full bg-stone-700 text-stone-300 whitespace-nowrap" data-querystats-summary></button>
10    <div data-querystats-list style="display: none;" class="absolute right-0 z-50 hidden translate-y-full -bottom-1 group/querystats-hover:block">
11        <div class="p-2 text-xs border rounded shadow-md bg-zinc-900 border-zinc-700"><table><tbody></tbody></table></div>
12    </div>
13    <script async defer>
14    // Catch errors since some browsers throw when using the new `type` option.
15    // https://bugs.webkit.org/show_bug.cgi?id=209216
16    var querystatsTimings = [];
17    function renderQuerystats() {
18        // Render the most recent timing call
19        const latestTiming = querystatsTimings[querystatsTimings.length - 1];
20        let summary = latestTiming.description;
21        if (querystatsTimings.length > 1) {
22            summary += ` *`;
23        }
24        document.querySelector('[data-querystats-summary]').innerText = summary;
25
26        // Make sure the elements are visible
27        document.querySelector('[data-querystats]').style.display = 'inline';
28
29        // Render the table rows for all timings
30        const list = document.querySelector('[data-querystats-list]');
31        if (querystatsTimings.length > 1) {
32            const tableRows = querystatsTimings.map(timing => {
33                let url = timing.url;
34                if (url.startsWith(window.location.origin)) {
35                    // Make the url relative if possible (usually is)
36                    url = url.slice(window.location.origin.length);
37                }
38                return `<tr>
39                    <td class="pr-2 font-medium whitespace-nowrap">${url}</td>
40                    <td class="whitespace-nowrap">${timing.description}</td>
41                </tr>`;
42            }).join('');
43            list.querySelector("tbody").innerHTML = tableRows;
44            list.style.display = '';
45        } else {
46            list.style.display = 'none';
47        }
48    }
49    try {
50        // Create the performance observer.
51        const po = new PerformanceObserver((list) => {
52            for (const entry of list.getEntries()) {
53                if (!entry.serverTiming) {
54                    console.warn("Server timing not available for querystats.")
55                    return;
56                }
57                for (const timing of entry.serverTiming) {
58                    if (querystatsTimings.length > 0) {
59                        if (querystatsTimings[querystatsTimings.length - 1] === timing) {
60                            // Skip duplicate timings (happens on initial load...)
61                            continue;
62                        }
63                    }
64                    if (timing.name === "querystats") {
65                        console.log("Querystats timing", entry)
66                        timing.url = entry.name;  // Store this for reference later
67                        querystatsTimings.push(timing);
68                        renderQuerystats();
69                    }
70                }
71            }
72        });
73        po.observe({type: 'navigation', buffered: true});  // Catch the regular page loads
74        po.observe({type: 'resource', buffered: true});  // Catch future ajax requests
75    } catch (e) {
76        // Do nothing if the browser doesn't support this API.
77    }
78    </script>
79</form>