| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 | 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 initPinBox() {  document.querySelector(".pin-prompt form").addEventListener(    "submit",    function (event) {      event.preventDefault();      const pin = encodeURIComponent(this.pin.value);      const encodedSecret = encodeURIComponent(SECRET);      const btn = this.btn;      btn.disabled = true;      fetch(        `${document.location.pathname}?__debugger__=yes&cmd=pinauth&pin=${pin}&s=${encodedSecret}`      )        .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) {    const encodedSecret = encodeURIComponent(SECRET);    fetch(      `${document.location.pathname}?__debugger__=yes&cmd=printpin&s=${encodedSecret}`    );    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", "?__debugger__=yes&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) => {    // Get input command.    const cmd = command.value;    // Setup GET request.    const urlPath = "";    const params = {      __debugger__: "yes",      cmd: cmd,      frm: frameID,      s: SECRET,    };    const paramString = Object.keys(params)      .map((key) => {        return "&" + encodeURIComponent(key) + "=" + encodeURIComponent(params[key]);      })      .join("");    fetch(urlPath + "?" + paramString)      .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);  }}
 |