mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-21 03:11:11 -05:00
Add JavaScript functionality for dynamic UI updates and filtering
- Implemented font color configuration based on numeric values in various sections. - Added resizing functionality for input fields to accommodate text length. - Initialized filters on document ready for improved user interaction. - Created visualization for profile data using fetched dot format. - Enhanced SQL detail page with click event handling for row navigation. - Ensured consistent highlighting for code blocks across multiple pages.
This commit is contained in:
108
staticfiles/debug_toolbar/js/history.js
Normal file
108
staticfiles/debug_toolbar/js/history.js
Normal file
@@ -0,0 +1,108 @@
|
||||
import { $$, ajaxForm, replaceToolbarState } from "./utils.js";
|
||||
|
||||
const djDebug = document.getElementById("djDebug");
|
||||
|
||||
function difference(setA, setB) {
|
||||
const _difference = new Set(setA);
|
||||
for (const elem of setB) {
|
||||
_difference.delete(elem);
|
||||
}
|
||||
return _difference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an array of dataset properties from a NodeList.
|
||||
*/
|
||||
function pluckData(nodes, key) {
|
||||
return [...nodes].map((obj) => obj.dataset[key]);
|
||||
}
|
||||
|
||||
function refreshHistory() {
|
||||
const formTarget = djDebug.querySelector(".refreshHistory");
|
||||
const container = document.getElementById("djdtHistoryRequests");
|
||||
const oldIds = new Set(
|
||||
pluckData(
|
||||
container.querySelectorAll("tr[data-request-id]"),
|
||||
"requestId"
|
||||
)
|
||||
);
|
||||
|
||||
ajaxForm(formTarget)
|
||||
.then((data) => {
|
||||
// Remove existing rows first then re-populate with new data
|
||||
for (const node of container.querySelectorAll(
|
||||
"tr[data-request-id]"
|
||||
)) {
|
||||
node.remove();
|
||||
}
|
||||
for (const request of data.requests) {
|
||||
container.innerHTML = request.content + container.innerHTML;
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
const allIds = new Set(
|
||||
pluckData(
|
||||
container.querySelectorAll("tr[data-request-id]"),
|
||||
"requestId"
|
||||
)
|
||||
);
|
||||
const newIds = difference(allIds, oldIds);
|
||||
const lastRequestId = newIds.values().next().value;
|
||||
return {
|
||||
allIds,
|
||||
newIds,
|
||||
lastRequestId,
|
||||
};
|
||||
})
|
||||
.then((refreshInfo) => {
|
||||
for (const newId of refreshInfo.newIds) {
|
||||
const row = container.querySelector(
|
||||
`tr[data-request-id="${newId}"]`
|
||||
);
|
||||
row.classList.add("flash-new");
|
||||
}
|
||||
setTimeout(() => {
|
||||
for (const row of container.querySelectorAll(
|
||||
"tr[data-request-id]"
|
||||
)) {
|
||||
row.classList.remove("flash-new");
|
||||
}
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
function switchHistory(newRequestId) {
|
||||
const formTarget = djDebug.querySelector(
|
||||
`.switchHistory[data-request-id='${newRequestId}']`
|
||||
);
|
||||
const tbody = formTarget.closest("tbody");
|
||||
|
||||
const highlighted = tbody.querySelector(".djdt-highlighted");
|
||||
if (highlighted) {
|
||||
highlighted.classList.remove("djdt-highlighted");
|
||||
}
|
||||
formTarget.closest("tr").classList.add("djdt-highlighted");
|
||||
|
||||
ajaxForm(formTarget).then((data) => {
|
||||
if (Object.keys(data).length === 0) {
|
||||
const container = document.getElementById("djdtHistoryRequests");
|
||||
container.querySelector(
|
||||
`button[data-request-id="${newRequestId}"]`
|
||||
).innerHTML = "Switch [EXPIRED]";
|
||||
}
|
||||
replaceToolbarState(newRequestId, data);
|
||||
});
|
||||
}
|
||||
|
||||
$$.on(djDebug, "click", ".switchHistory", function (event) {
|
||||
event.preventDefault();
|
||||
switchHistory(this.dataset.requestId);
|
||||
});
|
||||
|
||||
$$.on(djDebug, "click", ".refreshHistory", (event) => {
|
||||
event.preventDefault();
|
||||
refreshHistory();
|
||||
});
|
||||
// We don't refresh the whole toolbar each fetch or ajax request,
|
||||
// so we need to refresh the history when we open the panel
|
||||
$$.onPanelRender(djDebug, "HistoryPanel", refreshHistory);
|
||||
1
staticfiles/debug_toolbar/js/redirect.js
Normal file
1
staticfiles/debug_toolbar/js/redirect.js
Normal file
@@ -0,0 +1 @@
|
||||
document.getElementById("redirect_to").focus();
|
||||
82
staticfiles/debug_toolbar/js/timer.js
Normal file
82
staticfiles/debug_toolbar/js/timer.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import { $$ } from "./utils.js";
|
||||
|
||||
function insertBrowserTiming() {
|
||||
const timingOffset = performance.timing.navigationStart;
|
||||
const timingEnd = performance.timing.loadEventEnd;
|
||||
const totalTime = timingEnd - timingOffset;
|
||||
function getLeft(stat) {
|
||||
if (totalTime !== 0) {
|
||||
return (
|
||||
((performance.timing[stat] - timingOffset) / totalTime) * 100.0
|
||||
);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
function getCSSWidth(stat, endStat) {
|
||||
let width = 0;
|
||||
if (totalTime !== 0) {
|
||||
width =
|
||||
((performance.timing[endStat] - performance.timing[stat]) /
|
||||
totalTime) *
|
||||
100.0;
|
||||
}
|
||||
const denominator = 100.0 - getLeft(stat);
|
||||
if (denominator !== 0) {
|
||||
// Calculate relative percent (same as sql panel logic)
|
||||
width = (100.0 * width) / denominator;
|
||||
} else {
|
||||
width = 0;
|
||||
}
|
||||
return width < 1 ? "2px" : `${width}%`;
|
||||
}
|
||||
function addRow(tbody, stat, endStat) {
|
||||
const row = document.createElement("tr");
|
||||
const elapsed = performance.timing[stat] - timingOffset;
|
||||
if (endStat) {
|
||||
const duration =
|
||||
performance.timing[endStat] - performance.timing[stat];
|
||||
// Render a start through end bar
|
||||
row.innerHTML = `
|
||||
<td>${stat.replace("Start", "")}</td>
|
||||
<td><svg class="djDebugLineChart" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 100 5" preserveAspectRatio="none"><rect y="0" height="5" fill="#ccc" /></svg></td>
|
||||
<td>${elapsed} (+${duration})</td>
|
||||
`;
|
||||
row.querySelector("rect").setAttribute(
|
||||
"width",
|
||||
getCSSWidth(stat, endStat)
|
||||
);
|
||||
} else {
|
||||
// Render a point in time
|
||||
row.innerHTML = `
|
||||
<td>${stat}</td>
|
||||
<td><svg class="djDebugLineChart" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 100 5" preserveAspectRatio="none"><rect y="0" height="5" fill="#ccc" /></svg></td>
|
||||
<td>${elapsed}</td>
|
||||
`;
|
||||
row.querySelector("rect").setAttribute("width", 2);
|
||||
}
|
||||
row.querySelector("rect").setAttribute("x", getLeft(stat));
|
||||
tbody.appendChild(row);
|
||||
}
|
||||
|
||||
const browserTiming = document.getElementById("djDebugBrowserTiming");
|
||||
// Determine if the browser timing section has already been rendered.
|
||||
if (browserTiming.classList.contains("djdt-hidden")) {
|
||||
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");
|
||||
browserTiming.classList.remove("djdt-hidden");
|
||||
}
|
||||
}
|
||||
|
||||
const djDebug = document.getElementById("djDebug");
|
||||
// Insert the browser timing now since it's possible for this
|
||||
// script to miss the initial panel load event.
|
||||
insertBrowserTiming();
|
||||
$$.onPanelRender(djDebug, "TimerPanel", insertBrowserTiming);
|
||||
403
staticfiles/debug_toolbar/js/toolbar.js
Normal file
403
staticfiles/debug_toolbar/js/toolbar.js
Normal file
@@ -0,0 +1,403 @@
|
||||
import { $$, ajax, debounce, replaceToolbarState } from "./utils.js";
|
||||
|
||||
function onKeyDown(event) {
|
||||
if (event.keyCode === 27) {
|
||||
djdt.hideOneLevel();
|
||||
}
|
||||
}
|
||||
|
||||
function getDebugElement() {
|
||||
// Fetch the debug element from the DOM.
|
||||
// This is used to avoid writing the element's id
|
||||
// everywhere the element is being selected. A fixed reference
|
||||
// to the element should be avoided because the entire DOM could
|
||||
// be reloaded such as via HTMX boosting.
|
||||
return document.getElementById("djDebug");
|
||||
}
|
||||
|
||||
const djdt = {
|
||||
handleDragged: false,
|
||||
needUpdateOnFetch: false,
|
||||
init() {
|
||||
const djDebug = getDebugElement();
|
||||
djdt.needUpdateOnFetch = djDebug.dataset.updateOnFetch === "True";
|
||||
$$.on(djDebug, "click", "#djDebugPanelList li a", function (event) {
|
||||
event.preventDefault();
|
||||
if (!this.className) {
|
||||
return;
|
||||
}
|
||||
const panelId = this.className;
|
||||
const current = document.getElementById(panelId);
|
||||
if ($$.visible(current)) {
|
||||
djdt.hidePanels();
|
||||
} else {
|
||||
djdt.hidePanels();
|
||||
|
||||
$$.show(current);
|
||||
this.parentElement.classList.add("djdt-active");
|
||||
|
||||
const inner = current.querySelector(
|
||||
".djDebugPanelContent .djdt-scroll"
|
||||
);
|
||||
const requestId = djDebug.dataset.requestId;
|
||||
if (requestId && inner.children.length === 0) {
|
||||
const url = new URL(
|
||||
djDebug.dataset.renderPanelUrl,
|
||||
window.location
|
||||
);
|
||||
url.searchParams.append("request_id", requestId);
|
||||
url.searchParams.append("panel_id", panelId);
|
||||
ajax(url).then((data) => {
|
||||
inner.previousElementSibling.remove(); // Remove AJAX loader
|
||||
inner.innerHTML = data.content;
|
||||
$$.executeScripts(data.scripts);
|
||||
$$.applyStyles(inner);
|
||||
djDebug.dispatchEvent(
|
||||
new CustomEvent("djdt.panel.render", {
|
||||
detail: { panelId: panelId },
|
||||
})
|
||||
);
|
||||
});
|
||||
} else {
|
||||
djDebug.dispatchEvent(
|
||||
new CustomEvent("djdt.panel.render", {
|
||||
detail: { panelId: panelId },
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
$$.on(djDebug, "click", ".djDebugClose", () => {
|
||||
djdt.hideOneLevel();
|
||||
});
|
||||
$$.on(
|
||||
djDebug,
|
||||
"click",
|
||||
".djDebugPanelButton input[type=checkbox]",
|
||||
function () {
|
||||
djdt.cookie.set(
|
||||
this.dataset.cookie,
|
||||
this.checked ? "on" : "off",
|
||||
{
|
||||
path: "/",
|
||||
expires: 10,
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// Used by the SQL and template panels
|
||||
$$.on(djDebug, "click", ".remoteCall", function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
let url;
|
||||
const ajaxData = {};
|
||||
|
||||
if (this.tagName === "BUTTON") {
|
||||
const form = this.closest("form");
|
||||
url = this.formAction;
|
||||
ajaxData.method = form.method.toUpperCase();
|
||||
ajaxData.body = new FormData(form);
|
||||
} else if (this.tagName === "A") {
|
||||
url = this.href;
|
||||
}
|
||||
|
||||
ajax(url, ajaxData).then((data) => {
|
||||
const win = document.getElementById("djDebugWindow");
|
||||
win.innerHTML = data.content;
|
||||
$$.show(win);
|
||||
});
|
||||
});
|
||||
|
||||
// Used by the cache, profiling and SQL panels
|
||||
$$.on(djDebug, "click", ".djToggleSwitch", function () {
|
||||
const id = this.dataset.toggleId;
|
||||
const toggleOpen = "+";
|
||||
const toggleClose = "-";
|
||||
const openMe = this.textContent === toggleOpen;
|
||||
const name = this.dataset.toggleName;
|
||||
const container = document.getElementById(`${name}_${id}`);
|
||||
for (const el of container.querySelectorAll(".djDebugCollapsed")) {
|
||||
$$.toggle(el, openMe);
|
||||
}
|
||||
for (const el of container.querySelectorAll(
|
||||
".djDebugUncollapsed"
|
||||
)) {
|
||||
$$.toggle(el, !openMe);
|
||||
}
|
||||
for (const el of this.closest(
|
||||
".djDebugPanelContent"
|
||||
).querySelectorAll(`.djToggleDetails_${id}`)) {
|
||||
if (openMe) {
|
||||
el.classList.add("djSelected");
|
||||
el.classList.remove("djUnselected");
|
||||
this.textContent = toggleClose;
|
||||
} else {
|
||||
el.classList.remove("djSelected");
|
||||
el.classList.add("djUnselected");
|
||||
this.textContent = toggleOpen;
|
||||
}
|
||||
const switch_ = el.querySelector(".djToggleSwitch");
|
||||
if (switch_) {
|
||||
switch_.textContent = this.textContent;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$$.on(djDebug, "click", "#djHideToolBarButton", (event) => {
|
||||
event.preventDefault();
|
||||
djdt.hideToolbar();
|
||||
});
|
||||
|
||||
$$.on(djDebug, "click", "#djShowToolBarButton", () => {
|
||||
if (!djdt.handleDragged) {
|
||||
djdt.showToolbar();
|
||||
}
|
||||
});
|
||||
let startPageY;
|
||||
let baseY;
|
||||
const handle = document.getElementById("djDebugToolbarHandle");
|
||||
function onHandleMove(event) {
|
||||
// Chrome can send spurious mousemove events, so don't do anything unless the
|
||||
// cursor really moved. Otherwise, it will be impossible to expand the toolbar
|
||||
// due to djdt.handleDragged being set to true.
|
||||
if (djdt.handleDragged || event.pageY !== startPageY) {
|
||||
let top = baseY + event.pageY;
|
||||
|
||||
if (top < 0) {
|
||||
top = 0;
|
||||
} else if (top + handle.offsetHeight > window.innerHeight) {
|
||||
top = window.innerHeight - handle.offsetHeight;
|
||||
}
|
||||
|
||||
handle.style.top = `${top}px`;
|
||||
djdt.handleDragged = true;
|
||||
}
|
||||
}
|
||||
$$.on(djDebug, "mousedown", "#djShowToolBarButton", (event) => {
|
||||
event.preventDefault();
|
||||
startPageY = event.pageY;
|
||||
baseY = handle.offsetTop - startPageY;
|
||||
document.addEventListener("mousemove", onHandleMove);
|
||||
|
||||
document.addEventListener(
|
||||
"mouseup",
|
||||
(event) => {
|
||||
document.removeEventListener("mousemove", onHandleMove);
|
||||
if (djdt.handleDragged) {
|
||||
event.preventDefault();
|
||||
localStorage.setItem("djdt.top", handle.offsetTop);
|
||||
requestAnimationFrame(() => {
|
||||
djdt.handleDragged = false;
|
||||
});
|
||||
djdt.ensureHandleVisibility();
|
||||
}
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
|
||||
// Make sure the debug element is rendered at least once.
|
||||
// showToolbar will continue to show it in the future if the
|
||||
// entire DOM is reloaded.
|
||||
$$.show(djDebug);
|
||||
const show =
|
||||
localStorage.getItem("djdt.show") || djDebug.dataset.defaultShow;
|
||||
if (show === "true") {
|
||||
djdt.showToolbar();
|
||||
} else {
|
||||
djdt.hideToolbar();
|
||||
}
|
||||
if (djDebug.dataset.sidebarUrl !== undefined) {
|
||||
djdt.updateOnAjax();
|
||||
}
|
||||
|
||||
const prefersDark = window.matchMedia(
|
||||
"(prefers-color-scheme: dark)"
|
||||
).matches;
|
||||
const themeList = prefersDark
|
||||
? ["auto", "light", "dark"]
|
||||
: ["auto", "dark", "light"];
|
||||
const setTheme = (theme) => {
|
||||
djDebug.setAttribute(
|
||||
"data-theme",
|
||||
theme === "auto" ? (prefersDark ? "dark" : "light") : theme
|
||||
);
|
||||
djDebug.setAttribute("data-user-theme", theme);
|
||||
};
|
||||
|
||||
// Updates the theme using user settings
|
||||
let userTheme = localStorage.getItem("djdt.user-theme") || "auto";
|
||||
setTheme(userTheme);
|
||||
|
||||
// Adds the listener to the Theme Toggle Button
|
||||
$$.on(djDebug, "click", "#djToggleThemeButton", () => {
|
||||
const index = themeList.indexOf(userTheme);
|
||||
userTheme = themeList[(index + 1) % themeList.length];
|
||||
localStorage.setItem("djdt.user-theme", userTheme);
|
||||
setTheme(userTheme);
|
||||
});
|
||||
},
|
||||
hidePanels() {
|
||||
const djDebug = getDebugElement();
|
||||
$$.hide(document.getElementById("djDebugWindow"));
|
||||
for (const el of djDebug.querySelectorAll(".djdt-panelContent")) {
|
||||
$$.hide(el);
|
||||
}
|
||||
for (const el of document.querySelectorAll("#djDebugToolbar li")) {
|
||||
el.classList.remove("djdt-active");
|
||||
}
|
||||
},
|
||||
ensureHandleVisibility() {
|
||||
const handle = document.getElementById("djDebugToolbarHandle");
|
||||
// set handle position
|
||||
const handleTop = Math.min(
|
||||
localStorage.getItem("djdt.top") || 265,
|
||||
window.innerHeight - handle.offsetWidth
|
||||
);
|
||||
handle.style.top = `${handleTop}px`;
|
||||
},
|
||||
hideToolbar() {
|
||||
djdt.hidePanels();
|
||||
|
||||
$$.hide(document.getElementById("djDebugToolbar"));
|
||||
|
||||
const handle = document.getElementById("djDebugToolbarHandle");
|
||||
$$.show(handle);
|
||||
djdt.ensureHandleVisibility();
|
||||
window.addEventListener("resize", djdt.ensureHandleVisibility);
|
||||
document.removeEventListener("keydown", onKeyDown);
|
||||
|
||||
localStorage.setItem("djdt.show", "false");
|
||||
},
|
||||
hideOneLevel() {
|
||||
const win = document.getElementById("djDebugWindow");
|
||||
if ($$.visible(win)) {
|
||||
$$.hide(win);
|
||||
} else {
|
||||
const toolbar = document.getElementById("djDebugToolbar");
|
||||
if (toolbar.querySelector("li.djdt-active")) {
|
||||
djdt.hidePanels();
|
||||
} else {
|
||||
djdt.hideToolbar();
|
||||
}
|
||||
}
|
||||
},
|
||||
showToolbar() {
|
||||
document.addEventListener("keydown", onKeyDown);
|
||||
$$.show(document.getElementById("djDebug"));
|
||||
$$.hide(document.getElementById("djDebugToolbarHandle"));
|
||||
$$.show(document.getElementById("djDebugToolbar"));
|
||||
localStorage.setItem("djdt.show", "true");
|
||||
window.removeEventListener("resize", djdt.ensureHandleVisibility);
|
||||
},
|
||||
updateOnAjax() {
|
||||
const sidebarUrl =
|
||||
document.getElementById("djDebug").dataset.sidebarUrl;
|
||||
const slowjax = debounce(ajax, 200);
|
||||
|
||||
function handleAjaxResponse(requestId) {
|
||||
const encodedRequestId = encodeURIComponent(requestId);
|
||||
const dest = `${sidebarUrl}?request_id=${encodedRequestId}`;
|
||||
slowjax(dest).then((data) => {
|
||||
if (djdt.needUpdateOnFetch) {
|
||||
replaceToolbarState(encodedRequestId, data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Patch XHR / traditional AJAX requests
|
||||
const origOpen = XMLHttpRequest.prototype.open;
|
||||
XMLHttpRequest.prototype.open = function (...args) {
|
||||
this.addEventListener("load", function () {
|
||||
// Chromium emits a "Refused to get unsafe header" uncatchable warning
|
||||
// when the header can't be fetched. While it doesn't impede execution
|
||||
// it's worrisome to developers.
|
||||
if (
|
||||
this.getAllResponseHeaders().indexOf("djdt-request-id") >= 0
|
||||
) {
|
||||
handleAjaxResponse(
|
||||
this.getResponseHeader("djdt-request-id")
|
||||
);
|
||||
}
|
||||
});
|
||||
origOpen.apply(this, args);
|
||||
};
|
||||
|
||||
const origFetch = window.fetch;
|
||||
window.fetch = function (...args) {
|
||||
// Heads up! Before modifying this code, please be aware of the
|
||||
// possible unhandled errors that might arise from changing this.
|
||||
// For details, see
|
||||
// https://github.com/django-commons/django-debug-toolbar/pull/2100
|
||||
const promise = origFetch.apply(this, args);
|
||||
return promise.then((response) => {
|
||||
if (response.headers.get("djdt-request-id") !== null) {
|
||||
try {
|
||||
handleAjaxResponse(
|
||||
response.headers.get("djdt-request-id")
|
||||
);
|
||||
} catch (err) {
|
||||
throw new Error(
|
||||
`"${err.name}" occurred within django-debug-toolbar: ${err.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
});
|
||||
};
|
||||
},
|
||||
cookie: {
|
||||
get(key) {
|
||||
if (!document.cookie.includes(key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const cookieArray = document.cookie.split("; ");
|
||||
const cookies = {};
|
||||
|
||||
for (const e of cookieArray) {
|
||||
const parts = e.split("=");
|
||||
cookies[parts[0]] = parts[1];
|
||||
}
|
||||
|
||||
return cookies[key];
|
||||
},
|
||||
set(key, value, options = {}) {
|
||||
if (typeof options.expires === "number") {
|
||||
const days = options.expires;
|
||||
const expires = new Date();
|
||||
expires.setDate(expires.setDate() + days);
|
||||
options.expires = expires;
|
||||
}
|
||||
|
||||
document.cookie = [
|
||||
`${encodeURIComponent(key)}=${String(value)}`,
|
||||
options.expires
|
||||
? `; expires=${options.expires.toUTCString()}`
|
||||
: "",
|
||||
options.path ? `; path=${options.path}` : "",
|
||||
options.domain ? `; domain=${options.domain}` : "",
|
||||
options.secure ? "; secure" : "",
|
||||
"samesite" in options
|
||||
? `; samesite=${options.samesite}`
|
||||
: "; samesite=lax",
|
||||
].join("");
|
||||
|
||||
return value;
|
||||
},
|
||||
},
|
||||
};
|
||||
window.djdt = {
|
||||
show_toolbar: djdt.showToolbar,
|
||||
hide_toolbar: djdt.hideToolbar,
|
||||
init: djdt.init,
|
||||
close: djdt.hideOneLevel,
|
||||
cookie: djdt.cookie,
|
||||
};
|
||||
|
||||
if (document.readyState !== "loading") {
|
||||
djdt.init();
|
||||
} else {
|
||||
document.addEventListener("DOMContentLoaded", djdt.init);
|
||||
}
|
||||
144
staticfiles/debug_toolbar/js/utils.js
Normal file
144
staticfiles/debug_toolbar/js/utils.js
Normal file
@@ -0,0 +1,144 @@
|
||||
const $$ = {
|
||||
on(root, eventName, selector, fn) {
|
||||
root.removeEventListener(eventName, fn);
|
||||
root.addEventListener(eventName, (event) => {
|
||||
const target = event.target.closest(selector);
|
||||
if (root.contains(target)) {
|
||||
fn.call(target, event);
|
||||
}
|
||||
});
|
||||
},
|
||||
onPanelRender(root, panelId, fn) {
|
||||
/*
|
||||
This is a helper function to attach a handler for a `djdt.panel.render`
|
||||
event of a specific panel.
|
||||
|
||||
root: The container element that the listener should be attached to.
|
||||
panelId: The Id of the panel.
|
||||
fn: A function to execute when the event is triggered.
|
||||
*/
|
||||
root.addEventListener("djdt.panel.render", (event) => {
|
||||
if (event.detail.panelId === panelId) {
|
||||
fn.call(event);
|
||||
}
|
||||
});
|
||||
},
|
||||
show(element) {
|
||||
element.classList.remove("djdt-hidden");
|
||||
},
|
||||
hide(element) {
|
||||
element.classList.add("djdt-hidden");
|
||||
},
|
||||
toggle(element, value) {
|
||||
if (value) {
|
||||
$$.show(element);
|
||||
} else {
|
||||
$$.hide(element);
|
||||
}
|
||||
},
|
||||
visible(element) {
|
||||
return !element.classList.contains("djdt-hidden");
|
||||
},
|
||||
executeScripts(scripts) {
|
||||
for (const script of scripts) {
|
||||
const el = document.createElement("script");
|
||||
el.type = "module";
|
||||
el.src = script;
|
||||
el.async = true;
|
||||
document.head.appendChild(el);
|
||||
}
|
||||
},
|
||||
applyStyles(container) {
|
||||
/*
|
||||
* Given a container element, apply styles set via data-djdt-styles attribute.
|
||||
* The format is data-djdt-styles="styleName1:value;styleName2:value2"
|
||||
* The style names should use the CSSStyleDeclaration camel cased names.
|
||||
*/
|
||||
for (const element of container.querySelectorAll(
|
||||
"[data-djdt-styles]"
|
||||
)) {
|
||||
const styles = element.dataset.djdtStyles || "";
|
||||
for (const styleText of styles.split(";")) {
|
||||
const styleKeyPair = styleText.split(":");
|
||||
if (styleKeyPair.length === 2) {
|
||||
const name = styleKeyPair[0].trim();
|
||||
const value = styleKeyPair[1].trim();
|
||||
element.style[name] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function ajax(url, init) {
|
||||
return fetch(url, Object.assign({ credentials: "same-origin" }, init))
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
return response
|
||||
.json()
|
||||
.catch((error) =>
|
||||
Promise.reject(
|
||||
new Error(
|
||||
`The response is a invalid Json object : ${error}`
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
return Promise.reject(
|
||||
new Error(`${response.status}: ${response.statusText}`)
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
const win = document.getElementById("djDebugWindow");
|
||||
win.innerHTML = `<div class="djDebugPanelTitle"><h3>${error.message}</h3><button type="button" class="djDebugClose">»</button></div>`;
|
||||
$$.show(win);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
function ajaxForm(element) {
|
||||
const form = element.closest("form");
|
||||
const url = new URL(form.action);
|
||||
const formData = new FormData(form);
|
||||
for (const [name, value] of formData.entries()) {
|
||||
url.searchParams.append(name, value);
|
||||
}
|
||||
const ajaxData = {
|
||||
method: form.method.toUpperCase(),
|
||||
};
|
||||
return ajax(url, ajaxData);
|
||||
}
|
||||
|
||||
function replaceToolbarState(newRequestId, data) {
|
||||
const djDebug = document.getElementById("djDebug");
|
||||
djDebug.setAttribute("data-request-id", newRequestId);
|
||||
// Check if response is empty, it could be due to an expired requestId.
|
||||
for (const panelId of Object.keys(data)) {
|
||||
const panel = document.getElementById(panelId);
|
||||
if (panel) {
|
||||
panel.outerHTML = data[panelId].content;
|
||||
document.getElementById(`djdt-${panelId}`).outerHTML =
|
||||
data[panelId].button;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function debounce(func, delay) {
|
||||
let timer = null;
|
||||
let resolves = [];
|
||||
|
||||
return (...args) => {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
const result = func(...args);
|
||||
for (const r of resolves) {
|
||||
r(result);
|
||||
}
|
||||
resolves = [];
|
||||
}, delay);
|
||||
|
||||
return new Promise((r) => resolves.push(r));
|
||||
};
|
||||
}
|
||||
|
||||
export { $$, ajax, ajaxForm, replaceToolbarState, debounce };
|
||||
Reference in New Issue
Block a user