venv
This commit is contained in:
@ -0,0 +1,344 @@
|
||||
docReady(() => {
|
||||
if (!EVALEX_TRUSTED) {
|
||||
initPinBox();
|
||||
}
|
||||
// if we are in console mode, show the console.
|
||||
if (CONSOLE_MODE && EVALEX) {
|
||||
createInteractiveConsole();
|
||||
}
|
||||
|
||||
const frames = document.querySelectorAll("div.traceback div.frame");
|
||||
if (EVALEX) {
|
||||
addConsoleIconToFrames(frames);
|
||||
}
|
||||
addEventListenersToElements(document.querySelectorAll("div.detail"), "click", () =>
|
||||
document.querySelector("div.traceback").scrollIntoView(false)
|
||||
);
|
||||
addToggleFrameTraceback(frames);
|
||||
addToggleTraceTypesOnClick(document.querySelectorAll("h2.traceback"));
|
||||
addInfoPrompt(document.querySelectorAll("span.nojavascript"));
|
||||
wrapPlainTraceback();
|
||||
});
|
||||
|
||||
function addToggleFrameTraceback(frames) {
|
||||
frames.forEach((frame) => {
|
||||
frame.addEventListener("click", () => {
|
||||
frame.getElementsByTagName("pre")[0].parentElement.classList.toggle("expanded");
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function wrapPlainTraceback() {
|
||||
const plainTraceback = document.querySelector("div.plain textarea");
|
||||
const wrapper = document.createElement("pre");
|
||||
const textNode = document.createTextNode(plainTraceback.textContent);
|
||||
wrapper.appendChild(textNode);
|
||||
plainTraceback.replaceWith(wrapper);
|
||||
}
|
||||
|
||||
function makeDebugURL(args) {
|
||||
const params = new URLSearchParams(args)
|
||||
params.set("s", SECRET)
|
||||
return `?__debugger__=yes&${params}`
|
||||
}
|
||||
|
||||
function initPinBox() {
|
||||
document.querySelector(".pin-prompt form").addEventListener(
|
||||
"submit",
|
||||
function (event) {
|
||||
event.preventDefault();
|
||||
const btn = this.btn;
|
||||
btn.disabled = true;
|
||||
|
||||
fetch(
|
||||
makeDebugURL({cmd: "pinauth", pin: this.pin.value})
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then(({auth, exhausted}) => {
|
||||
if (auth) {
|
||||
EVALEX_TRUSTED = true;
|
||||
fadeOut(document.getElementsByClassName("pin-prompt")[0]);
|
||||
} else {
|
||||
alert(
|
||||
`Error: ${
|
||||
exhausted
|
||||
? "too many attempts. Restart server to retry."
|
||||
: "incorrect pin"
|
||||
}`
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
alert("Error: Could not verify PIN. Network error?");
|
||||
console.error(err);
|
||||
})
|
||||
.finally(() => (btn.disabled = false));
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
function promptForPin() {
|
||||
if (!EVALEX_TRUSTED) {
|
||||
fetch(makeDebugURL({cmd: "printpin"}));
|
||||
const pinPrompt = document.getElementsByClassName("pin-prompt")[0];
|
||||
fadeIn(pinPrompt);
|
||||
document.querySelector('.pin-prompt input[name="pin"]').focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for shell initialization
|
||||
*/
|
||||
function openShell(consoleNode, target, frameID) {
|
||||
promptForPin();
|
||||
if (consoleNode) {
|
||||
slideToggle(consoleNode);
|
||||
return consoleNode;
|
||||
}
|
||||
let historyPos = 0;
|
||||
const history = [""];
|
||||
const consoleElement = createConsole();
|
||||
const output = createConsoleOutput();
|
||||
const form = createConsoleInputForm();
|
||||
const command = createConsoleInput();
|
||||
|
||||
target.parentNode.appendChild(consoleElement);
|
||||
consoleElement.append(output);
|
||||
consoleElement.append(form);
|
||||
form.append(command);
|
||||
command.focus();
|
||||
slideToggle(consoleElement);
|
||||
|
||||
form.addEventListener("submit", (e) => {
|
||||
handleConsoleSubmit(e, command, frameID).then((consoleOutput) => {
|
||||
output.append(consoleOutput);
|
||||
command.focus();
|
||||
consoleElement.scrollTo(0, consoleElement.scrollHeight);
|
||||
const old = history.pop();
|
||||
history.push(command.value);
|
||||
if (typeof old !== "undefined") {
|
||||
history.push(old);
|
||||
}
|
||||
historyPos = history.length - 1;
|
||||
command.value = "";
|
||||
});
|
||||
});
|
||||
|
||||
command.addEventListener("keydown", (e) => {
|
||||
if (e.key === "l" && e.ctrlKey) {
|
||||
output.innerText = "--- screen cleared ---";
|
||||
} else if (e.key === "ArrowUp" || e.key === "ArrowDown") {
|
||||
// Handle up arrow and down arrow.
|
||||
if (e.key === "ArrowUp" && historyPos > 0) {
|
||||
e.preventDefault();
|
||||
historyPos--;
|
||||
} else if (e.key === "ArrowDown" && historyPos < history.length - 1) {
|
||||
historyPos++;
|
||||
}
|
||||
command.value = history[historyPos];
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
return consoleElement;
|
||||
}
|
||||
|
||||
function addEventListenersToElements(elements, event, listener) {
|
||||
elements.forEach((el) => el.addEventListener(event, listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add extra info
|
||||
*/
|
||||
function addInfoPrompt(elements) {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
elements[i].innerHTML =
|
||||
"<p>To switch between the interactive traceback and the plaintext " +
|
||||
'one, you can click on the "Traceback" headline. From the text ' +
|
||||
"traceback you can also create a paste of it. " +
|
||||
(!EVALEX
|
||||
? ""
|
||||
: "For code execution mouse-over the frame you want to debug and " +
|
||||
"click on the console icon on the right side." +
|
||||
"<p>You can execute arbitrary Python code in the stack frames and " +
|
||||
"there are some extra helpers available for introspection:" +
|
||||
"<ul><li><code>dump()</code> shows all variables in the frame" +
|
||||
"<li><code>dump(obj)</code> dumps all that's known about the object</ul>");
|
||||
elements[i].classList.remove("nojavascript");
|
||||
}
|
||||
}
|
||||
|
||||
function addConsoleIconToFrames(frames) {
|
||||
for (let i = 0; i < frames.length; i++) {
|
||||
let consoleNode = null;
|
||||
const target = frames[i];
|
||||
const frameID = frames[i].id.substring(6);
|
||||
|
||||
for (let j = 0; j < target.getElementsByTagName("pre").length; j++) {
|
||||
const img = createIconForConsole();
|
||||
img.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
consoleNode = openShell(consoleNode, target, frameID);
|
||||
return false;
|
||||
});
|
||||
target.getElementsByTagName("pre")[j].append(img);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function slideToggle(target) {
|
||||
target.classList.toggle("active");
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle traceback types on click.
|
||||
*/
|
||||
function addToggleTraceTypesOnClick(elements) {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
elements[i].addEventListener("click", () => {
|
||||
document.querySelector("div.traceback").classList.toggle("hidden");
|
||||
document.querySelector("div.plain").classList.toggle("hidden");
|
||||
});
|
||||
elements[i].style.cursor = "pointer";
|
||||
document.querySelector("div.plain").classList.toggle("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function createConsole() {
|
||||
const consoleNode = document.createElement("pre");
|
||||
consoleNode.classList.add("console");
|
||||
consoleNode.classList.add("active");
|
||||
return consoleNode;
|
||||
}
|
||||
|
||||
function createConsoleOutput() {
|
||||
const output = document.createElement("div");
|
||||
output.classList.add("output");
|
||||
output.innerHTML = "[console ready]";
|
||||
return output;
|
||||
}
|
||||
|
||||
function createConsoleInputForm() {
|
||||
const form = document.createElement("form");
|
||||
form.innerHTML = ">>> ";
|
||||
return form;
|
||||
}
|
||||
|
||||
function createConsoleInput() {
|
||||
const command = document.createElement("input");
|
||||
command.type = "text";
|
||||
command.setAttribute("autocomplete", "off");
|
||||
command.setAttribute("spellcheck", false);
|
||||
command.setAttribute("autocapitalize", "off");
|
||||
command.setAttribute("autocorrect", "off");
|
||||
return command;
|
||||
}
|
||||
|
||||
function createIconForConsole() {
|
||||
const img = document.createElement("img");
|
||||
img.setAttribute("src", makeDebugURL({cmd: "resource", f: "console.png"}));
|
||||
img.setAttribute("title", "Open an interactive python shell in this frame");
|
||||
return img;
|
||||
}
|
||||
|
||||
function createExpansionButtonForConsole() {
|
||||
const expansionButton = document.createElement("a");
|
||||
expansionButton.setAttribute("href", "#");
|
||||
expansionButton.setAttribute("class", "toggle");
|
||||
expansionButton.innerHTML = " ";
|
||||
return expansionButton;
|
||||
}
|
||||
|
||||
function createInteractiveConsole() {
|
||||
const target = document.querySelector("div.console div.inner");
|
||||
while (target.firstChild) {
|
||||
target.removeChild(target.firstChild);
|
||||
}
|
||||
openShell(null, target, 0);
|
||||
}
|
||||
|
||||
function handleConsoleSubmit(e, command, frameID) {
|
||||
// Prevent page from refreshing.
|
||||
e.preventDefault();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
fetch(makeDebugURL({cmd: command.value, frm: frameID}))
|
||||
.then((res) => {
|
||||
return res.text();
|
||||
})
|
||||
.then((data) => {
|
||||
const tmp = document.createElement("div");
|
||||
tmp.innerHTML = data;
|
||||
resolve(tmp);
|
||||
|
||||
// Handle expandable span for long list outputs.
|
||||
// Example to test: list(range(13))
|
||||
let wrapperAdded = false;
|
||||
const wrapperSpan = document.createElement("span");
|
||||
const expansionButton = createExpansionButtonForConsole();
|
||||
|
||||
tmp.querySelectorAll("span.extended").forEach((spanToWrap) => {
|
||||
const parentDiv = spanToWrap.parentNode;
|
||||
if (!wrapperAdded) {
|
||||
parentDiv.insertBefore(wrapperSpan, spanToWrap);
|
||||
wrapperAdded = true;
|
||||
}
|
||||
parentDiv.removeChild(spanToWrap);
|
||||
wrapperSpan.append(spanToWrap);
|
||||
spanToWrap.hidden = true;
|
||||
|
||||
expansionButton.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
spanToWrap.hidden = !spanToWrap.hidden;
|
||||
expansionButton.classList.toggle("open");
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
// Add expansion button at end of wrapper.
|
||||
if (wrapperAdded) {
|
||||
wrapperSpan.append(expansionButton);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function fadeOut(element) {
|
||||
element.style.opacity = 1;
|
||||
|
||||
(function fade() {
|
||||
element.style.opacity -= 0.1;
|
||||
if (element.style.opacity < 0) {
|
||||
element.style.display = "none";
|
||||
} else {
|
||||
requestAnimationFrame(fade);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
function fadeIn(element, display) {
|
||||
element.style.opacity = 0;
|
||||
element.style.display = display || "block";
|
||||
|
||||
(function fade() {
|
||||
let val = parseFloat(element.style.opacity) + 0.1;
|
||||
if (val <= 1) {
|
||||
element.style.opacity = val;
|
||||
requestAnimationFrame(fade);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
function docReady(fn) {
|
||||
if (document.readyState === "complete" || document.readyState === "interactive") {
|
||||
setTimeout(fn, 1);
|
||||
} else {
|
||||
document.addEventListener("DOMContentLoaded", fn);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user