Plain is headed towards 1.0! Subscribe for development updates →

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