Latest build deployed.
This commit is contained in:
File diff suppressed because one or more lines are too long
Vendored
+282
@@ -0,0 +1,282 @@
|
||||
window.addEventListener("load", (event2) => {
|
||||
addKnowls(document);
|
||||
});
|
||||
function addKnowls(target) {
|
||||
const xrefs = target.querySelectorAll("[data-knowl]");
|
||||
for (const xref of xrefs) {
|
||||
LinkKnowl.initializeXrefKnowl(xref);
|
||||
}
|
||||
const bornHiddens = target.querySelectorAll(".born-hidden-knowl");
|
||||
for (const bhk of bornHiddens) {
|
||||
const summary = bhk.querySelector(":scope > summary");
|
||||
const contents = bhk.querySelector(":scope > summary + *");
|
||||
new SlideRevealer(summary, contents, bhk);
|
||||
}
|
||||
}
|
||||
class SlideRevealer {
|
||||
static STATE = Object.freeze({
|
||||
INACTIVE: 0,
|
||||
CLOSING: 1,
|
||||
EXPANDING: 2
|
||||
});
|
||||
// triggerElement is the element clicked to open/close
|
||||
// contentElement is the element that will hide/reveal
|
||||
// animatedElement is the element that will grow/shrink as contentElement is modified
|
||||
// may be the same as contentElement or a parent of it
|
||||
constructor(triggerElement, contentElement, animatedElement) {
|
||||
this.triggerElement = triggerElement;
|
||||
this.contentElement = contentElement;
|
||||
this.animatedElement = animatedElement;
|
||||
this.animation = null;
|
||||
this.animationState = SlideRevealer.STATE.INACTIVE;
|
||||
this.animatedElementInlineStyle = null;
|
||||
this.triggerElement.addEventListener("click", (e) => this.onClick(e));
|
||||
}
|
||||
isBusy() {
|
||||
return this.animationState !== SlideRevealer.STATE.INACTIVE || this.animatedElementInlineStyle !== null;
|
||||
}
|
||||
storeAnimatedElementInlineStyle() {
|
||||
if (this.animatedElementInlineStyle !== null) return;
|
||||
this.animatedElementInlineStyle = {
|
||||
overflow: this.animatedElement.style.overflow,
|
||||
height: this.animatedElement.style.height,
|
||||
paddingTop: this.animatedElement.style.paddingTop,
|
||||
paddingBottom: this.animatedElement.style.paddingBottom
|
||||
};
|
||||
}
|
||||
restoreAnimatedElementInlineStyle() {
|
||||
if (this.animatedElementInlineStyle === null) return;
|
||||
this.animatedElement.style.overflow = this.animatedElementInlineStyle.overflow;
|
||||
this.animatedElement.style.height = this.animatedElementInlineStyle.height;
|
||||
this.animatedElement.style.paddingTop = this.animatedElementInlineStyle.paddingTop;
|
||||
this.animatedElement.style.paddingBottom = this.animatedElementInlineStyle.paddingBottom;
|
||||
this.animatedElementInlineStyle = null;
|
||||
}
|
||||
onClick(e) {
|
||||
if (e) e.preventDefault();
|
||||
if (this.isBusy()) return;
|
||||
this.storeAnimatedElementInlineStyle();
|
||||
this.animatedElement.style.overflow = "hidden";
|
||||
if (this.animationState === SlideRevealer.STATE.CLOSING || !this.animatedElement.hasAttribute("open")) {
|
||||
this.animatedElement.setAttribute("open", "");
|
||||
this.triggerElement.setAttribute("open", "");
|
||||
this.contentElement.style.display = "";
|
||||
this.contentElement.style.visibility = "hidden";
|
||||
let closedHeight = 0;
|
||||
if (this.animatedElement.contains(this.triggerElement))
|
||||
closedHeight = this.triggerElement.offsetHeight;
|
||||
const naturalStyle = window.getComputedStyle(this.animatedElement);
|
||||
const naturalPaddingTop = naturalStyle.paddingTop;
|
||||
const naturalPaddingBottom = naturalStyle.paddingBottom;
|
||||
this.animatedElement.style.height = `${closedHeight}px`;
|
||||
this.animatedElement.style.paddingTop = "0px";
|
||||
this.animatedElement.style.paddingBottom = "0px";
|
||||
const expandingMeasurements = {
|
||||
fullHeight: this.contentElement === this.animatedElement ? this.contentElement.scrollHeight : closedHeight + this.contentElement.offsetHeight,
|
||||
paddingTop: naturalPaddingTop,
|
||||
paddingBottom: naturalPaddingBottom
|
||||
};
|
||||
this.contentElement.style.visibility = "";
|
||||
this.toggle(true, expandingMeasurements);
|
||||
} else if (this.animationState === SlideRevealer.STATE.EXPANDING || this.animatedElement.hasAttribute("open")) {
|
||||
this.toggle(false);
|
||||
}
|
||||
}
|
||||
toggle(expanding, expandingMeasurements = null) {
|
||||
let closedHeight = 0;
|
||||
if (this.animatedElement.contains(this.triggerElement))
|
||||
closedHeight = this.triggerElement.offsetHeight;
|
||||
const computedStyle = window.getComputedStyle(this.animatedElement);
|
||||
const fullHeight = expandingMeasurements?.fullHeight ?? closedHeight + this.contentElement.offsetHeight;
|
||||
const startHeight = `${expanding ? closedHeight : this.animatedElement.offsetHeight}px`;
|
||||
const endHeight = `${expanding ? fullHeight : closedHeight}px`;
|
||||
const currentPaddingTop = computedStyle.paddingTop;
|
||||
const currentPaddingBottom = computedStyle.paddingBottom;
|
||||
const endPaddingTop = expandingMeasurements?.paddingTop ?? currentPaddingTop;
|
||||
const endPaddingBottom = expandingMeasurements?.paddingBottom ?? currentPaddingBottom;
|
||||
const startPadTop = expanding ? "0px" : currentPaddingTop;
|
||||
const endPadTop = expanding ? endPaddingTop : "0px";
|
||||
const startPadBottom = expanding ? "0px" : currentPaddingBottom;
|
||||
const endPadBottom = expanding ? endPaddingBottom : "0px";
|
||||
if (this.animation) {
|
||||
this.animation.cancel();
|
||||
}
|
||||
const animDuration = Math.max(Math.min(Math.abs(closedHeight - fullHeight) / 400 * 1e3, 750), 250);
|
||||
this.animationState = expanding ? SlideRevealer.STATE.EXPANDING : SlideRevealer.STATE.CLOSING;
|
||||
this.animation = this.animatedElement.animate({
|
||||
height: [startHeight, endHeight],
|
||||
paddingTop: [startPadTop, endPadTop],
|
||||
paddingBottom: [startPadBottom, endPadBottom]
|
||||
}, {
|
||||
duration: animDuration,
|
||||
easing: "ease-out"
|
||||
});
|
||||
this.animation.onfinish = () => {
|
||||
this.onAnimationFinish(expanding);
|
||||
};
|
||||
this.animation.oncancel = () => {
|
||||
this.animationState = SlideRevealer.STATE.INACTIVE;
|
||||
this.restoreAnimatedElementInlineStyle();
|
||||
};
|
||||
}
|
||||
onAnimationFinish(isOpen) {
|
||||
this.animation = null;
|
||||
this.animationState = SlideRevealer.STATE.INACTIVE;
|
||||
if (!isOpen) {
|
||||
this.animatedElement.removeAttribute("open");
|
||||
this.triggerElement.removeAttribute("open");
|
||||
}
|
||||
this.restoreAnimatedElementInlineStyle();
|
||||
if (!isOpen)
|
||||
this.contentElement.style.display = "none";
|
||||
this.contentElement.style.visibility = "";
|
||||
if (isOpen) {
|
||||
let hasCallback = this.contentElement.querySelectorAll("[data-knowl-callback]");
|
||||
hasCallback.forEach((el) => {
|
||||
window[el.getAttribute("data-knowl-callback")](el, open);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
class LinkKnowl {
|
||||
// Used to uniquely identify XrefKnowls
|
||||
static xrefCount = 0;
|
||||
// Factory to create an XrefKnowl from a knowl link
|
||||
// Will avoid duplicate initialization
|
||||
// This should be used by outside code to create XrefKnowls
|
||||
static initializeXrefKnowl(knowlLinkElement) {
|
||||
if (knowlLinkElement.getAttribute("data-knowl-uid") === null) {
|
||||
return new LinkKnowl(knowlLinkElement);
|
||||
}
|
||||
}
|
||||
// "Private" constructor - should only be called by initializeXrefKnowl
|
||||
constructor(knowlLinkElement) {
|
||||
this.linkElement = knowlLinkElement;
|
||||
this.outputElement = null;
|
||||
this.slideHandler = null;
|
||||
this.uid = LinkKnowl.xrefCount++;
|
||||
knowlLinkElement.setAttribute("data-knowl-uid", this.uid);
|
||||
knowlLinkElement.setAttribute("role", "button");
|
||||
knowlLinkElement.setAttribute("data-base-title", knowlLinkElement.getAttribute("title") || this.linkElement.textContent);
|
||||
knowlLinkElement.classList.add("knowl__link");
|
||||
this.updateLabels(false);
|
||||
knowlLinkElement.addEventListener("click", this.handleLinkClick.bind(this));
|
||||
}
|
||||
// Set aria-label and title based on visibility of knowl
|
||||
updateLabels(isVisible) {
|
||||
const verb = isVisible ? this.linkElement.getAttribute("data-close-label") || "Close" : this.linkElement.getAttribute("data-reveal-label") || "Reveal";
|
||||
const targetDescript = this.linkElement.getAttribute("data-base-title");
|
||||
const helpText = verb + " " + targetDescript;
|
||||
this.linkElement.setAttribute("aria-label", helpText);
|
||||
this.linkElement.setAttribute("title", helpText);
|
||||
}
|
||||
// Toggle the state of the knowl link and output elements
|
||||
// Assumes output is already created
|
||||
toggle() {
|
||||
this.linkElement.classList.toggle("active");
|
||||
const isActive = this.linkElement.classList.contains("active");
|
||||
this.updateLabels(isActive);
|
||||
if (isActive) {
|
||||
const h = this.outputElement.getBoundingClientRect().height;
|
||||
if (h > window.innerHeight) {
|
||||
this.outputElement.scrollIntoView(true);
|
||||
} else {
|
||||
if (this.outputElement.getBoundingClientRect().bottom > window.innerHeight)
|
||||
this.outputElement.scrollIntoView(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Returns element the knowl output should be inserted after
|
||||
findOutputLocation() {
|
||||
const invalidParents = "table, mjx-container, div.tabular-box, .runestone > .parsons";
|
||||
let el = this.linkElement.parentElement;
|
||||
let problemAncestor = el.closest(invalidParents);
|
||||
while (problemAncestor && problemAncestor !== el) {
|
||||
el = problemAncestor;
|
||||
problemAncestor = el.closest(invalidParents);
|
||||
}
|
||||
return el;
|
||||
}
|
||||
// Create the knowl output element
|
||||
createOutputElement() {
|
||||
const outputId = "knowl-uid-" + this.uid;
|
||||
const outputContentsId = "knowl-output-" + this.uid;
|
||||
const linkTarget = this.linkElement.getAttribute("data-knowl");
|
||||
const placeholderText = `<div class='knowl__content' style='display:none;' id='${outputId}' aria-live='polite' id='${outputContentsId}'>Loading '${linkTarget}'</div>`;
|
||||
const temp = document.createElement("template");
|
||||
temp.innerHTML = placeholderText;
|
||||
this.outputElement = temp.content.children[0];
|
||||
const insertLoc = this.findOutputLocation(this.linkElement);
|
||||
insertLoc.after(this.outputElement);
|
||||
}
|
||||
// Get content for knowl as dom element. Returns promise that resolves to knowl content
|
||||
async getContent() {
|
||||
const contentURL = this.linkElement.getAttribute("data-knowl");
|
||||
const knowlContent = await fetch(contentURL).then((response) => response.text()).then((data) => {
|
||||
let knowlDoc = new DOMParser().parseFromString(data, "text/html");
|
||||
let tempContainer2 = knowlDoc.body;
|
||||
let scripts = knowlDoc.querySelectorAll("head script");
|
||||
tempContainer2.append(...scripts);
|
||||
return tempContainer2;
|
||||
}).catch((error) => {
|
||||
const destination = this.linkElement.getAttribute("href");
|
||||
const text = this.linkElement.textContent;
|
||||
const err_message = `<div class='knowl-output__error'><div class='para'>Error fetching content. (<em>${error}</em>)</div><div class='para'><a href='${destination}'>Navigate to ${text}</a> instead.</div><div class='para'>If you are viewing this book from your local filesystem, this is expected behavior. To view the book with all features, you must serve the book from a web server. See the <a href="https://pretextbook.org/doc/guide/html/author-faq.html#how-do-i-view-my-book-locally">PreTeXt FAQ</a> for more information.</div></div>`;
|
||||
return err_message;
|
||||
});
|
||||
return knowlContent;
|
||||
}
|
||||
// Handle a click on the knowl link
|
||||
handleLinkClick(event) {
|
||||
event.preventDefault();
|
||||
if (this.slideHandler?.isBusy()) {
|
||||
return;
|
||||
}
|
||||
if (this.outputElement !== null) {
|
||||
this.toggle();
|
||||
} else {
|
||||
this.createOutputElement();
|
||||
this.slideHandler = new SlideRevealer(this.linkElement, this.outputElement, this.outputElement);
|
||||
this.linkElement.addEventListener("click", this.slideHandler);
|
||||
let loadingTimeout = setTimeout(() => {
|
||||
loadingTimeout = null;
|
||||
this.slideHandler.onClick();
|
||||
this.toggle();
|
||||
}, 500);
|
||||
const content = this.getContent();
|
||||
content.then((tempContainer) => {
|
||||
if (loadingTimeout !== null) {
|
||||
clearTimeout(loadingTimeout);
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.slideHandler.onClick();
|
||||
this.toggle();
|
||||
}, 100);
|
||||
const runestoneElements = tempContainer.querySelectorAll(".ptx-runestone-container");
|
||||
[...runestoneElements].forEach((e) => {
|
||||
const rsId = e.querySelector("[data-component]")?.id;
|
||||
const onPage = document.getElementById(rsId);
|
||||
if (onPage) {
|
||||
e.innerHTML = `<div class="para">The interactive that belongs here is already on the page and cannot appear multiple times. <a href="#${rsId}">Scroll to interactive.</a>`;
|
||||
} else {
|
||||
window.runestoneComponents.renderOneComponent(e);
|
||||
}
|
||||
});
|
||||
const children = [...tempContainer.children];
|
||||
this.outputElement.innerHTML = "";
|
||||
this.outputElement.append(...children);
|
||||
addKnowls(this.outputElement);
|
||||
MathJax.typesetPromise([this.outputElement]);
|
||||
Prism.highlightAllUnder(this.outputElement);
|
||||
[...this.outputElement.getElementsByTagName("script")].forEach((s) => {
|
||||
if (s.getAttribute("type") === null || s.getAttribute("type") === "text/javascript") {
|
||||
eval(s.innerHTML);
|
||||
}
|
||||
});
|
||||
}).catch((data) => {
|
||||
console.log("Error fetching knowl content: " + data);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=knowl.js.map
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
window.addEventListener("message", function(event) {
|
||||
let edata = event.data;
|
||||
if (typeof event.data == "string" && event.data.match(/lti\.frameResize/)) {
|
||||
edata = JSON.parse(event.data);
|
||||
}
|
||||
if (edata.subject === "lti.frameResize") {
|
||||
if ("frame_id" in edata) {
|
||||
let el = document.getElementById(edata["frame_id"]);
|
||||
document.getElementById(edata["frame_id"]).style.height = edata.height + "px";
|
||||
if (edata.wrapheight && document.getElementById(edata["frame_id"] + "wrap")) {
|
||||
document.getElementById(edata["frame_id"] + "wrap").style.height = edata.wrapheight + "px";
|
||||
}
|
||||
} else if ("iframe_resize_id" in edata) {
|
||||
document.getElementById(edata["iframe_resize_id"]).style.height = edata.height + "px";
|
||||
} else {
|
||||
const iFrames = document.getElementsByTagName("iframe");
|
||||
for (const iFrame of iFrames) {
|
||||
if (iFrame.contentWindow === event.source) {
|
||||
if (edata.height) {
|
||||
iFrame.height = edata.height;
|
||||
iFrame.style.height = edata.height + "px";
|
||||
}
|
||||
if (edata.width) {
|
||||
iFrame.width = edata.width;
|
||||
iFrame.style.width = edata.width + "px";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
function sendResizeRequest(el) {
|
||||
el.contentWindow.postMessage("requestResize", "*");
|
||||
}
|
||||
//# sourceMappingURL=lti_iframe_resizer.js.map
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
let mathJaxOpts = {
|
||||
"tex": {
|
||||
"inlineMath": [
|
||||
[
|
||||
"\\(",
|
||||
"\\)"
|
||||
]
|
||||
],
|
||||
"tags": "none",
|
||||
"tagSide": "right",
|
||||
"tagIndent": ".8em",
|
||||
"packages": {
|
||||
"[+]": [
|
||||
"amscd",
|
||||
"color",
|
||||
"knowl"
|
||||
]
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"ignoreHtmlClass": "tex2jax_ignore|ignore-math",
|
||||
"processHtmlClass": "process-math"
|
||||
},
|
||||
"chtml": {
|
||||
"scale": 0.98,
|
||||
"mtextInheritFont": true
|
||||
},
|
||||
"loader": {
|
||||
"load": [
|
||||
"input/asciimath",
|
||||
"[tex]/amscd",
|
||||
"[tex]/color"
|
||||
]
|
||||
}
|
||||
};
|
||||
function startMathJax(opts) {
|
||||
if (opts.hasWebworkReps || opts.hasSage) {
|
||||
mathJaxOpts["renderActions"] = {
|
||||
"findScript": [
|
||||
10,
|
||||
function(doc) {
|
||||
document.querySelectorAll('script[type^="math/tex"]').forEach(function(node) {
|
||||
var display = !!node.type.match(/; *mode=display/);
|
||||
var math = new doc.options.MathItem(node.textContent, doc.inputJax[0], display);
|
||||
var text = document.createTextNode("");
|
||||
node.parentNode.replaceChild(text, node);
|
||||
math.start = { node: text, delim: "", n: 0 };
|
||||
math.end = { node: text, delim: "", n: 0 };
|
||||
doc.math.push(math);
|
||||
});
|
||||
},
|
||||
""
|
||||
]
|
||||
};
|
||||
}
|
||||
if (opts.isReact) {
|
||||
mathJaxOpts["startup"] = {
|
||||
typeset: false
|
||||
};
|
||||
} else {
|
||||
mathJaxOpts["startup"] = {
|
||||
ready() {
|
||||
const { Configuration } = MathJax._.input.tex.Configuration;
|
||||
const configuration = Configuration.create("knowl", {
|
||||
handler: {
|
||||
macro: ["knowl"]
|
||||
}
|
||||
});
|
||||
const NodeUtil = MathJax._.input.tex.NodeUtil.default;
|
||||
function GetArgumentMML(parser, name) {
|
||||
const arg = parser.ParseArg(name);
|
||||
if (!NodeUtil.isInferred(arg)) {
|
||||
return arg;
|
||||
}
|
||||
const children = NodeUtil.getChildren(arg);
|
||||
if (children.length === 1) {
|
||||
return children[0];
|
||||
}
|
||||
const mrow = parser.create("node", "mrow");
|
||||
NodeUtil.copyChildren(arg, mrow);
|
||||
NodeUtil.copyAttributes(arg, mrow);
|
||||
return mrow;
|
||||
}
|
||||
;
|
||||
const CommandMap = MathJax._.input.tex.TokenMap.CommandMap;
|
||||
new CommandMap(
|
||||
"knowl",
|
||||
{
|
||||
knowl(parser, name) {
|
||||
const url = parser.GetArgument(name);
|
||||
const arg = GetArgumentMML(parser, name);
|
||||
const mrow = parser.create("node", "mrow", [arg], { tabindex: "0", "data-knowl": url });
|
||||
parser.Push(mrow);
|
||||
}
|
||||
}
|
||||
);
|
||||
MathJax.startup.defaultReady();
|
||||
},
|
||||
pageReady() {
|
||||
return MathJax.startup.defaultPageReady().then(rsMathReady);
|
||||
}
|
||||
};
|
||||
}
|
||||
if (opts.htmlPresentation) {
|
||||
mathJaxOpts["options"]["menuOptions"] = {
|
||||
"settings": {
|
||||
"zoom": "Click",
|
||||
"zscale": "300%"
|
||||
}
|
||||
};
|
||||
}
|
||||
window.MathJax = mathJaxOpts;
|
||||
const runestoneMathReady = new Promise((resolve) => window.rsMathReady = resolve);
|
||||
window.runestoneMathReady = runestoneMathReady;
|
||||
}
|
||||
export {
|
||||
startMathJax
|
||||
};
|
||||
//# sourceMappingURL=mathjax_startup.js.map
|
||||
+2106
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,440 @@
|
||||
const timeOutHandler = new Object();
|
||||
const inputPrefix = "stackapi_input_";
|
||||
const feedbackPrefix = "stackapi_fb_";
|
||||
const validationPrefix = "stackapi_val_";
|
||||
const stackstring = {
|
||||
"teacheranswershow_mcq": "A correct answer is: {$a->display}",
|
||||
"api_which_typed": "which can be typed as follows",
|
||||
"api_valid_all_parts": "Please enter valid answers for all parts of the question.",
|
||||
"api_out_of": "out of",
|
||||
"api_marks_sub": "Marks for this submission",
|
||||
"api_submit": "Submit Answers",
|
||||
"generalfeedback": "General feedback",
|
||||
"score": "Score",
|
||||
"api_response": "Response summary",
|
||||
"api_correct": "Correct answers"
|
||||
};
|
||||
function wrap_math(content) {
|
||||
content = content.replace(/(?<!\\)(\\\(.*?(?<!\\)\\\))/gs, '<span class="process-math">$1</span>');
|
||||
return content.replace(/(?<!\\)(\\\[.*?(?<!\\)\\\])/gs, '<span class="process-math">$1</span>');
|
||||
}
|
||||
async function collectData(qfile, qname, qprefix) {
|
||||
let res = "";
|
||||
await getQuestionFile(qfile, qname).then((response) => {
|
||||
if (response.questionxml != "<quiz>\nnull\n</quiz>") {
|
||||
res = {
|
||||
questionDefinition: response.questionxml,
|
||||
answers: collectAnswer(qprefix),
|
||||
seed: response.seed,
|
||||
renderInputs: qprefix + inputPrefix,
|
||||
readOnly: false
|
||||
};
|
||||
}
|
||||
;
|
||||
});
|
||||
return res;
|
||||
}
|
||||
function collectAnswer(qprefix) {
|
||||
const inputs = document.getElementsByTagName("input");
|
||||
const textareas = document.getElementsByTagName("textarea");
|
||||
const selects = document.getElementsByTagName("select");
|
||||
let res = {};
|
||||
res = processNodes(res, inputs, qprefix);
|
||||
res = processNodes(res, textareas, qprefix);
|
||||
res = processNodes(res, selects, qprefix);
|
||||
return res;
|
||||
}
|
||||
function processNodes(res, nodes, qprefix) {
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const element = nodes[i];
|
||||
if (element.name.indexOf(qprefix + inputPrefix) === 0 && element.name.indexOf("_val") === -1) {
|
||||
if (element.type === "checkbox" || element.type === "radio") {
|
||||
if (element.checked) {
|
||||
res[element.name.slice((qprefix + inputPrefix).length)] = element.value;
|
||||
}
|
||||
} else {
|
||||
res[element.name.slice((qprefix + inputPrefix).length)] = element.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
function send(qfile, qname, qprefix) {
|
||||
const http = new XMLHttpRequest();
|
||||
const url = stack_api_url + "/render";
|
||||
http.open("POST", url, true);
|
||||
http.setRequestHeader("Content-Type", "application/json");
|
||||
http.onreadystatechange = function() {
|
||||
if (http.readyState == 4) {
|
||||
try {
|
||||
const json = JSON.parse(http.responseText);
|
||||
if (json.message) {
|
||||
console.log(json);
|
||||
document.getElementById(`${qprefix + "errors"}`).innerText = json.message;
|
||||
return;
|
||||
} else {
|
||||
document.getElementById(`${qprefix + "errors"}`).innerText = "";
|
||||
}
|
||||
renameIframeHolders();
|
||||
let question = json.questionrender;
|
||||
const inputs = json.questioninputs;
|
||||
const seed = json.questionseed;
|
||||
let correctAnswers = "";
|
||||
for (const [name, input] of Object.entries(inputs)) {
|
||||
question = question.replace(`[[input:${name}]]`, input.render);
|
||||
question = question.replace(`[[validation:${name}]]`, `<span name='${qprefix + validationPrefix + name}'></span>`);
|
||||
question = question.replace(/javascript:download\(([^,]+?),([^,]+?)\)/, `javascript:download($1,$2, '${qfile}', '${qname}', '${qprefix}', ${seed})`);
|
||||
question = wrap_math(question);
|
||||
if (input.samplesolutionrender && name !== "remember") {
|
||||
correctAnswers += `<p>
|
||||
${stackstring["teacheranswershow_mcq"]} \\[{${input.samplesolutionrender}}\\],
|
||||
${stackstring["api_which_typed"]}: `;
|
||||
for (const [name2, solution] of Object.entries(input.samplesolution)) {
|
||||
if (name2.indexOf("_val") === -1) {
|
||||
correctAnswers += `<span class='correct-answer'>${wrap_math(solution)}</span>`;
|
||||
}
|
||||
}
|
||||
correctAnswers += ".</p>";
|
||||
} else if (name !== "remember") {
|
||||
for (const solution of Object.values(input.samplesolution)) {
|
||||
if (input.configuration.options) {
|
||||
correctAnswers += `<p class='correct-answer'>${input.configuration.options[solution]}</p>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const [name, file] of Object.entries(json.questionassets)) {
|
||||
const plotUrl = getPlotUrl(file);
|
||||
question = question.replace(name, plotUrl);
|
||||
json.questionsamplesolutiontext = json.questionsamplesolutiontext.replace(name, plotUrl);
|
||||
correctAnswers = correctAnswers.replace(name, plotUrl);
|
||||
}
|
||||
question = replaceFeedbackTags(question, qprefix);
|
||||
qoutput = document.getElementById(`${qprefix + "output"}`);
|
||||
qoutput.innerHTML = question;
|
||||
document.getElementById(`${qprefix + "stackapi_qtext"}`).style.display = "block";
|
||||
document.getElementById(`${qprefix + "stackapi_correct"}`).style.display = "block";
|
||||
for (const inputName of Object.keys(inputs)) {
|
||||
const inputElements = document.querySelectorAll(`[name^=${qprefix + inputPrefix + inputName}]`);
|
||||
for (const inputElement of Object.values(inputElements)) {
|
||||
inputElement.oninput = (event) => {
|
||||
const currentTimeout = timeOutHandler[event.target.id];
|
||||
if (currentTimeout) {
|
||||
window.clearTimeout(currentTimeout);
|
||||
}
|
||||
timeOutHandler[event.target.id] = window.setTimeout(validate.bind(null, event.target, qfile, qname, qprefix), 1e3);
|
||||
};
|
||||
}
|
||||
}
|
||||
let sampleText = json.questionsamplesolutiontext;
|
||||
if (sampleText) {
|
||||
sampleText = replaceFeedbackTags(sampleText, qprefix);
|
||||
document.getElementById(`${qprefix + "generalfeedback"}`).innerHTML = wrap_math(sampleText);
|
||||
document.getElementById(`${qprefix + "stackapi_generalfeedback"}`).style.display = "block";
|
||||
} else {
|
||||
document.getElementById(`${qprefix + "stackapi_generalfeedback"}`).style.display = "none";
|
||||
}
|
||||
document.getElementById(`${qprefix + "stackapi_score"}`).style.display = "none";
|
||||
document.getElementById(`${qprefix + "stackapi_validity"}`).innerText = "";
|
||||
const innerFeedback = document.getElementById(`${qprefix + "specificfeedback"}`);
|
||||
innerFeedback.innerHTML = "";
|
||||
innerFeedback.classList.remove("feedback");
|
||||
document.getElementById(`${qprefix + "formatcorrectresponse"}`).innerHTML = correctAnswers;
|
||||
document.getElementById(`${qprefix + "stackapi_generalfeedback"}`).style.display = "none";
|
||||
document.getElementById(`${qprefix + "stackapi_correct"}`).style.display = "none";
|
||||
createIframes(json.iframes);
|
||||
MathJax.typeset();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
document.getElementById(`${qprefix + "errors"}`).innerText = http.responseText;
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
collectData(qfile, qname, qprefix).then((data) => {
|
||||
let submitbutton = document.getElementById(`${qprefix + "stackapi_qtext"}`).querySelector('input[type="button"]');
|
||||
submitbutton.addEventListener("click", function() {
|
||||
answer(qfile, qname, qprefix, data.seed);
|
||||
});
|
||||
http.send(JSON.stringify(data));
|
||||
let questioncontainer = document.getElementById(`${qprefix + "stack"}`).parentElement;
|
||||
if (questioncontainer.getBoundingClientRect().top < 0) {
|
||||
questioncontainer.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}
|
||||
;
|
||||
});
|
||||
}
|
||||
function validate(element, qfile, qname, qprefix) {
|
||||
const http = new XMLHttpRequest();
|
||||
const url = stack_api_url + "/validate";
|
||||
http.open("POST", url, true);
|
||||
const answerNamePrefixTrim = (qprefix + inputPrefix).length;
|
||||
const answerName = element.name.slice(answerNamePrefixTrim).split("_", 1)[0];
|
||||
http.setRequestHeader("Content-Type", "application/json");
|
||||
http.onreadystatechange = function() {
|
||||
if (http.readyState == 4) {
|
||||
try {
|
||||
const json = JSON.parse(http.responseText);
|
||||
if (json.message) {
|
||||
document.getElementById(`${qprefix + "errors"}`).innerText = json.message;
|
||||
return;
|
||||
} else {
|
||||
document.getElementById(`${qprefix + "errors"}`).innerText = "";
|
||||
}
|
||||
renameIframeHolders();
|
||||
const validationHTML = json.validation;
|
||||
const element2 = document.getElementsByName(`${qprefix + validationPrefix + answerName}`)[0];
|
||||
element2.innerHTML = wrap_math(validationHTML);
|
||||
if (validationHTML) {
|
||||
element2.classList.add("validation");
|
||||
} else {
|
||||
element2.classList.remove("validation");
|
||||
}
|
||||
createIframes(json.iframes);
|
||||
MathJax.typeset();
|
||||
} catch (e) {
|
||||
document.getElementById(`${qprefix + "errors"}`).innerText = http.responseText;
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
collectData(qfile, qname, qprefix).then((data) => {
|
||||
data.inputName = answerName;
|
||||
http.send(JSON.stringify(data));
|
||||
});
|
||||
}
|
||||
function answer(qfile, qname, qprefix, seed) {
|
||||
const http = new XMLHttpRequest();
|
||||
const url = stack_api_url + "/grade";
|
||||
http.open("POST", url, true);
|
||||
if (!document.getElementById(`${qprefix + "output"}`).innerText) {
|
||||
return;
|
||||
}
|
||||
http.setRequestHeader("Content-Type", "application/json");
|
||||
http.onreadystatechange = function() {
|
||||
if (http.readyState == 4) {
|
||||
try {
|
||||
const json = JSON.parse(http.responseText);
|
||||
if (json.message) {
|
||||
document.getElementById(`${qprefix + "errors"}`).innerText = json.message;
|
||||
return;
|
||||
} else {
|
||||
document.getElementById(`${qprefix + "errors"}`).innerText = "";
|
||||
}
|
||||
if (!json.isgradable) {
|
||||
document.getElementById(`${qprefix + "stackapi_validity"}`).innerText = " " + stackstring["api_valid_all_parts"];
|
||||
return;
|
||||
}
|
||||
renameIframeHolders();
|
||||
document.getElementById(`${qprefix + "score"}`).innerText = (json.score * json.scoreweights.total).toFixed(2) + " " + stackstring["api_out_of"] + " " + json.scoreweights.total;
|
||||
document.getElementById(`${qprefix + "stackapi_score"}`).style.display = "block";
|
||||
document.getElementById(`${qprefix + "response_summary"}`).innerText = json.responsesummary;
|
||||
document.getElementById(`${qprefix + "stackapi_generalfeedback"}`).style.display = "block";
|
||||
document.getElementById(`${qprefix + "stackapi_summary"}`).style.display = "none";
|
||||
const feedback = json.prts;
|
||||
const specificFeedbackElement2 = document.getElementById(`${qprefix + "specificfeedback"}`);
|
||||
if (json.specificfeedback) {
|
||||
for (const [name, file] of Object.entries(json.gradingassets)) {
|
||||
json.specificfeedback = json.specificfeedback.replace(name, getPlotUrl(file));
|
||||
}
|
||||
json.specificfeedback = replaceFeedbackTags(json.specificfeedback, qprefix);
|
||||
specificFeedbackElement2.innerHTML = wrap_math(json.specificfeedback);
|
||||
specificFeedbackElement2.classList.add("feedback");
|
||||
} else {
|
||||
specificFeedbackElement2.classList.remove("feedback");
|
||||
}
|
||||
for (let [name, fb] of Object.entries(feedback)) {
|
||||
for (const [name2, file] of Object.entries(json.gradingassets)) {
|
||||
fb = fb.replace(name2, getPlotUrl(file));
|
||||
}
|
||||
const elements = document.getElementsByName(`${qprefix + feedbackPrefix + name}`);
|
||||
if (elements.length > 0) {
|
||||
const element = elements[0];
|
||||
if (json.scores[name] !== void 0) {
|
||||
fb = fb + `<div>${stackstring["api_marks_sub"]}:
|
||||
${(json.scores[name] * json.scoreweights[name] * json.scoreweights.total).toFixed(2)}
|
||||
/ ${(json.scoreweights[name] * json.scoreweights.total).toFixed(2)}.</div>`;
|
||||
}
|
||||
element.innerHTML = wrap_math(fb);
|
||||
}
|
||||
}
|
||||
createIframes(json.iframes);
|
||||
MathJax.typeset();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
document.getElementById(`${qprefix + "errors"}`).innerText = http.responseText;
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
const specificFeedbackElement = document.getElementById(`${qprefix + "specificfeedback"}`);
|
||||
specificFeedbackElement.innerHTML = "";
|
||||
specificFeedbackElement.classList.remove("feedback");
|
||||
document.getElementById(`${qprefix + "response_summary"}`).innerText = "";
|
||||
document.getElementById(`${qprefix + "stackapi_summary"}`).style.display = "none";
|
||||
const inputElements = document.querySelectorAll(`[name^=${qprefix + feedbackPrefix}]`);
|
||||
for (const inputElement of Object.values(inputElements)) {
|
||||
inputElement.innerHTML = "";
|
||||
inputElement.classList.remove("feedback");
|
||||
}
|
||||
document.getElementById(`${qprefix + "stackapi_score"}`).style.display = "none";
|
||||
document.getElementById(`${qprefix + "stackapi_validity"}`).innerText = "";
|
||||
collectData(qfile, qname, qprefix).then((data) => {
|
||||
data.seed = seed;
|
||||
http.send(JSON.stringify(data));
|
||||
});
|
||||
}
|
||||
function download(filename, fileid, qfile, qname, qprefix, seed) {
|
||||
const http = new XMLHttpRequest();
|
||||
const url = stack_api_url + "/download";
|
||||
http.open("POST", url, true);
|
||||
http.setRequestHeader("Content-Type", "application/json");
|
||||
http.filename = filename;
|
||||
http.fileid = fileid;
|
||||
http.onreadystatechange = function() {
|
||||
if (http.readyState == 4) {
|
||||
try {
|
||||
const blob = new Blob([http.responseText], { type: "application/octet-binary", endings: "native" });
|
||||
const selector = CSS.escape(`javascript:download('${http.filename}', ${http.fileid}, '${qfile}', '${qname}', '${qprefix}', ${seed})`);
|
||||
const linkElements = document.querySelectorAll(`a[href^=${selector}]`);
|
||||
const link = linkElements[0];
|
||||
link.setAttribute("href", URL.createObjectURL(blob));
|
||||
link.setAttribute("download", filename);
|
||||
link.click();
|
||||
} catch (e) {
|
||||
document.getElementById("errors").innerText = http.responseText;
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
collectData(qfile, qname, qprefix).then((data) => {
|
||||
data.filename = filename;
|
||||
data.fileid = fileid;
|
||||
data.seed = seed;
|
||||
http.send(JSON.stringify(data));
|
||||
});
|
||||
}
|
||||
function saveState(key, value) {
|
||||
if (typeof Storage !== "undefined") {
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
}
|
||||
function loadState(key) {
|
||||
if (typeof Storage !== "undefined") {
|
||||
return localStorage.getItem(key) || "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
function renameIframeHolders() {
|
||||
for (const iframe of document.querySelectorAll(`[id^=stack-iframe-holder]:not([id$=old]`)) {
|
||||
iframe.id = iframe.id + "_old";
|
||||
}
|
||||
}
|
||||
function createIframes(iframes) {
|
||||
const corsFragment = "/cors.php?name=";
|
||||
for (const iframe of iframes) {
|
||||
create_iframe(
|
||||
iframe[0],
|
||||
iframe[1].replaceAll(corsFragment, stack_api_url + corsFragment),
|
||||
...iframe.slice(2)
|
||||
);
|
||||
}
|
||||
}
|
||||
function replaceFeedbackTags(text, qprefix) {
|
||||
let result = text;
|
||||
const feedbackTags = text.match(/\[\[feedback:.*?\]\]/g);
|
||||
if (feedbackTags) {
|
||||
for (const tag of feedbackTags) {
|
||||
result = result.replace(tag, `<div name='${qprefix + feedbackPrefix + tag.slice(11, -2)}'></div>`);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
async function getQuestionFile(questionURL, questionName) {
|
||||
let res = "";
|
||||
if (questionURL) {
|
||||
await fetch(questionURL).then((result) => result.text()).then((result) => {
|
||||
res = loadQuestionFromFile(result, questionName);
|
||||
});
|
||||
}
|
||||
return res;
|
||||
}
|
||||
function loadQuestionFromFile(fileContents, questionName) {
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(fileContents, "text/xml");
|
||||
let thequestion = null;
|
||||
let randSeed = "";
|
||||
for (const question of xmlDoc.getElementsByTagName("question")) {
|
||||
if (question.getAttribute("type").toLowerCase() === "stack" && (!questionName || question.querySelectorAll("name text")[0].textContent === questionName)) {
|
||||
thequestion = question.outerHTML;
|
||||
let seeds = question.querySelectorAll("deployedseed");
|
||||
if (seeds.length) {
|
||||
randSeed = parseInt(seeds[Math.floor(Math.random() * seeds.length)].textContent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return { questionxml: setQuestion(thequestion), seed: randSeed };
|
||||
}
|
||||
function setQuestion(question) {
|
||||
return "<quiz>\n" + question + "\n</quiz>";
|
||||
}
|
||||
function createQuestionBlocks() {
|
||||
questionBlocks = document.getElementsByClassName("que stack");
|
||||
let i = 0;
|
||||
for (questionblock of questionBlocks) {
|
||||
i++;
|
||||
let questionPrefix = "q" + i.toString() + "_";
|
||||
var qfile = questionblock.dataset.qfile;
|
||||
var qname = questionblock.dataset.qname || "";
|
||||
questionblock.innerHTML = `
|
||||
<div class="collapsiblecontent" id=${questionPrefix + "stack"}>
|
||||
<div class="vstack gap-3 ms-3 col-lg-8">
|
||||
<div id=${questionPrefix + "errors"}></div>
|
||||
<div id=${questionPrefix + "stackapi_qtext"} class="col-lg-8" style="display: none">
|
||||
<!--<h2>${stackstring["questiontext"]}:</h2>-->
|
||||
<div id=${questionPrefix + "output"} class="formulation"></div>
|
||||
<div id=${questionPrefix + "specificfeedback"}></div>
|
||||
<br>
|
||||
<!-- <input type="button" onclick="answer('${qfile}', '${qname}', '${questionPrefix}')" class="btn btn-primary" value=${stackstring["api_submit"]}/>-->
|
||||
<input type="button" class="btn btn-primary" value=${stackstring["api_submit"]}/>
|
||||
<span id=${questionPrefix + "stackapi_validity"} style="color:darkred"></span>
|
||||
</div>
|
||||
<div id=${questionPrefix + "stackapi_generalfeedback"} class="col-lg-8" style="display: none">
|
||||
<h2>${stackstring["generalfeedback"]}:</h2>
|
||||
<div id=${questionPrefix + "generalfeedback"} class="feedback"></div>
|
||||
</div>
|
||||
<h2 id=${questionPrefix + "stackapi_score"} style="display: none">${stackstring["score"]}: <span id=${questionPrefix + "score"}></span></h2>
|
||||
<div id=${questionPrefix + "stackapi_summary"} class="col-lg-10" style="display: none">
|
||||
<h2>${stackstring["api_response"]}:</h2>
|
||||
<div id=${questionPrefix + "response_summary"} class="feedback"></div>
|
||||
</div>
|
||||
<div id=${questionPrefix + "stackapi_correct"} class="col-lg-10" style="display: none">
|
||||
<h2>${stackstring["api_correct"]}:</h2>
|
||||
<div id=${questionPrefix + "formatcorrectresponse"} class="feedback"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id=${questionPrefix + "newquestionbutton"}>
|
||||
<input type="button" onclick="send('${qfile}', '${qname}', '${questionPrefix}')" class="btn btn-primary" value="Show new example question"/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
function addCollapsibles() {
|
||||
var collapsibles = document.querySelectorAll(".level2>h2, .stack>h2");
|
||||
for (let i = 0; i < collapsibles.length; i++) {
|
||||
collapsibles[i].addEventListener("click", () => collapseFunc(this));
|
||||
}
|
||||
}
|
||||
function collapseFunc(e) {
|
||||
e.classList.toggle("collapsed");
|
||||
}
|
||||
function stackSetup() {
|
||||
createQuestionBlocks();
|
||||
addCollapsibles();
|
||||
}
|
||||
function getPlotUrl(file) {
|
||||
return `${stack_api_url}/plots/${file}`;
|
||||
}
|
||||
//# sourceMappingURL=stackapicalls.js.map
|
||||
+390
@@ -0,0 +1,390 @@
|
||||
"use strict";
|
||||
/**
|
||||
* A javascript module to handle separation of author sourced scripts into
|
||||
* IFRAMES. All such scripts will have limited access to the actual document
|
||||
* on the VLE side and this script represents the VLE side endpoint for
|
||||
* message handling needed to give that access. When porting STACK onto VLEs
|
||||
* one needs to map this script to do the following:
|
||||
*
|
||||
* 1. Ensure that searches for target elements/inputs are limited to questions
|
||||
* and do not return any elements outside them.
|
||||
*
|
||||
* 2. Map any identifiers needed to identify inputs by name.
|
||||
*
|
||||
* 3. Any change handling related to input value modifications through this
|
||||
* logic gets connected to any such handling on the VLE side.
|
||||
*
|
||||
*
|
||||
* This script is intenttionally ordered so that the VLE specific bits should
|
||||
* be at the top.
|
||||
*
|
||||
*
|
||||
* This script assumes the following:
|
||||
*
|
||||
* 1. Each relevant IFRAME has an `id`-attribute that will be told to this
|
||||
* script.
|
||||
*
|
||||
* 2. Each such IFRAME exists within the question itself, so that one can
|
||||
* traverse up the DOM tree from that IFRAME to find the border of
|
||||
* the question.
|
||||
*
|
||||
* @module qtype_stack/stackjsvle
|
||||
* @copyright 2023 Aalto University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
let IFRAMES = {};
|
||||
let INPUTS = {};
|
||||
let INPUTS_INPUT_EVENT = {};
|
||||
let DISABLE_CHANGES = false;
|
||||
function vle_get_element(id) {
|
||||
let candidate = document.getElementById(id);
|
||||
let iter = candidate;
|
||||
while (iter && !iter.classList.contains("formulation")) {
|
||||
iter = iter.parentElement;
|
||||
}
|
||||
if (iter && iter.classList.contains("formulation")) {
|
||||
return candidate;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function vle_get_input_element(name, srciframe) {
|
||||
let initialcandidate = document.getElementById(srciframe);
|
||||
let iter = initialcandidate;
|
||||
while (iter && !iter.classList.contains("formulation")) {
|
||||
iter = iter.parentElement;
|
||||
}
|
||||
if (iter && iter.classList.contains("formulation")) {
|
||||
let possible2 = iter.querySelector('input[id$="_' + name + '"]');
|
||||
if (possible2 !== null) {
|
||||
return possible2;
|
||||
}
|
||||
possible2 = iter.querySelector('input[id$="_' + name + '_1"][type=radio]');
|
||||
if (possible2 !== null) {
|
||||
return possible2;
|
||||
}
|
||||
possible2 = iter.querySelector('select[id$="_' + name + '"]');
|
||||
if (possible2 !== null) {
|
||||
return possible2;
|
||||
}
|
||||
}
|
||||
let possible = document.querySelector('.formulation input[id$="_' + name + '"]');
|
||||
if (possible !== null) {
|
||||
return possible;
|
||||
}
|
||||
possible = document.querySelector('.formulation input[id$="_' + name + '_1"][type=radio]');
|
||||
if (possible !== null) {
|
||||
return possible;
|
||||
}
|
||||
possible = document.querySelector('.formulation select[id$="_' + name + '"]');
|
||||
return possible;
|
||||
}
|
||||
function vle_update_input(inputelement) {
|
||||
const c = new Event("change");
|
||||
inputelement.dispatchEvent(c);
|
||||
const i = new Event("input");
|
||||
inputelement.dispatchEvent(i);
|
||||
}
|
||||
function vle_update_dom(modifiedsubtreerootelement) {
|
||||
CustomEvents.notifyFilterContentUpdated(modifiedsubtreerootelement);
|
||||
}
|
||||
function vle_html_sanitize(src) {
|
||||
let parser = new DOMParser();
|
||||
let doc = parser.parseFromString(src, "text/html");
|
||||
for (let el of doc.querySelectorAll("script, style")) {
|
||||
el.remove();
|
||||
}
|
||||
for (let el of doc.querySelectorAll("*")) {
|
||||
for (let { name, value } of el.attributes) {
|
||||
if (is_evil_attribute(name, value)) {
|
||||
el.removeAttribute(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return doc.body;
|
||||
}
|
||||
function is_evil_attribute(name, value) {
|
||||
const lcname = name.toLowerCase();
|
||||
if (lcname.startsWith("on")) {
|
||||
return true;
|
||||
}
|
||||
if (lcname === "src" || lcname.endsWith("href")) {
|
||||
const lcvalue = value.replace(/\s+/g, "").toLowerCase();
|
||||
if (lcvalue.includes("javascript:") || lcvalue.includes("data:text")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
window.addEventListener("message", (e) => {
|
||||
if (!(typeof e.data === "string" || e.data instanceof String)) {
|
||||
return;
|
||||
}
|
||||
let msg = null;
|
||||
try {
|
||||
msg = JSON.parse(e.data);
|
||||
} catch (e2) {
|
||||
return;
|
||||
}
|
||||
if (!("version" in msg && msg.version.startsWith("STACK-JS"))) {
|
||||
return;
|
||||
}
|
||||
if (!("src" in msg && "type" in msg && msg.src in IFRAMES)) {
|
||||
return;
|
||||
}
|
||||
let element = null;
|
||||
let input = null;
|
||||
let response = {
|
||||
version: "STACK-JS:1.1.0"
|
||||
};
|
||||
switch (msg.type) {
|
||||
case "register-input-listener":
|
||||
input = vle_get_input_element(msg.name, msg.src);
|
||||
if (input === null) {
|
||||
response.type = "error";
|
||||
response.msg = 'Failed to connect to input: "' + msg.name + '"';
|
||||
response.tgt = msg.src;
|
||||
IFRAMES[msg.src].contentWindow.postMessage(JSON.stringify(response), "*");
|
||||
return;
|
||||
}
|
||||
response.type = "initial-input";
|
||||
response.name = msg.name;
|
||||
response.tgt = msg.src;
|
||||
if (input.nodeName.toLowerCase() === "select") {
|
||||
response.value = input.value;
|
||||
response["input-type"] = "select";
|
||||
response["input-readonly"] = input.hasAttribute("disabled");
|
||||
} else if (input.type === "checkbox") {
|
||||
response.value = input.checked;
|
||||
response["input-type"] = "checkbox";
|
||||
response["input-readonly"] = input.hasAttribute("disabled");
|
||||
} else {
|
||||
response.value = input.value;
|
||||
response["input-type"] = input.type;
|
||||
response["input-readonly"] = input.hasAttribute("readonly");
|
||||
}
|
||||
if (input.type === "radio") {
|
||||
response["input-readonly"] = input.hasAttribute("disabled");
|
||||
response.value = "";
|
||||
for (let inp of document.querySelectorAll("input[type=radio][name=" + CSS.escape(input.name) + "]")) {
|
||||
if (inp.checked) {
|
||||
response.value = inp.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (input.id in INPUTS) {
|
||||
if (msg.src in INPUTS[input.id]) {
|
||||
return;
|
||||
}
|
||||
if (input.type !== "radio") {
|
||||
INPUTS[input.id].push(msg.src);
|
||||
} else {
|
||||
let radgroup = document.querySelectorAll("input[type=radio][name=" + CSS.escape(input.name) + "]");
|
||||
for (let inp of radgroup) {
|
||||
INPUTS[inp.id].push(msg.src);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (input.type !== "radio") {
|
||||
INPUTS[input.id] = [msg.src];
|
||||
} else {
|
||||
let radgroup = document.querySelectorAll("input[type=radio][name=" + CSS.escape(input.name) + "]");
|
||||
for (let inp of radgroup) {
|
||||
INPUTS[inp.id] = [msg.src];
|
||||
}
|
||||
}
|
||||
if (input.type !== "radio") {
|
||||
input.addEventListener("change", () => {
|
||||
if (DISABLE_CHANGES) {
|
||||
return;
|
||||
}
|
||||
let resp = {
|
||||
version: "STACK-JS:1.0.0",
|
||||
type: "changed-input",
|
||||
name: msg.name
|
||||
};
|
||||
if (input.type === "checkbox") {
|
||||
resp["value"] = input.checked;
|
||||
} else {
|
||||
resp["value"] = input.value;
|
||||
}
|
||||
for (let tgt of INPUTS[input.id]) {
|
||||
resp["tgt"] = tgt;
|
||||
IFRAMES[tgt].contentWindow.postMessage(JSON.stringify(resp), "*");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let radgroup = document.querySelectorAll("input[type=radio][name=" + CSS.escape(input.name) + "]");
|
||||
radgroup.forEach((inp) => {
|
||||
inp.addEventListener("change", () => {
|
||||
if (DISABLE_CHANGES) {
|
||||
return;
|
||||
}
|
||||
let resp = {
|
||||
version: "STACK-JS:1.0.0",
|
||||
type: "changed-input",
|
||||
name: msg.name
|
||||
};
|
||||
if (inp.checked) {
|
||||
resp.value = inp.value;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
for (let tgt of INPUTS[inp.id]) {
|
||||
resp["tgt"] = tgt;
|
||||
IFRAMES[tgt].contentWindow.postMessage(JSON.stringify(resp), "*");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
if ("track-input" in msg && msg["track-input"] && input.type !== "radio") {
|
||||
if (input.id in INPUTS_INPUT_EVENT) {
|
||||
if (msg.src in INPUTS_INPUT_EVENT[input.id]) {
|
||||
return;
|
||||
}
|
||||
INPUTS_INPUT_EVENT[input.id].push(msg.src);
|
||||
} else {
|
||||
INPUTS_INPUT_EVENT[input.id] = [msg.src];
|
||||
input.addEventListener("input", () => {
|
||||
if (DISABLE_CHANGES) {
|
||||
return;
|
||||
}
|
||||
let resp = {
|
||||
version: "STACK-JS:1.0.0",
|
||||
type: "changed-input",
|
||||
name: msg.name
|
||||
};
|
||||
if (input.type === "checkbox") {
|
||||
resp["value"] = input.checked;
|
||||
} else {
|
||||
resp["value"] = input.value;
|
||||
}
|
||||
for (let tgt of INPUTS_INPUT_EVENT[input.id]) {
|
||||
resp["tgt"] = tgt;
|
||||
IFRAMES[tgt].contentWindow.postMessage(JSON.stringify(resp), "*");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!(msg.src in INPUTS[input.id])) {
|
||||
IFRAMES[msg.src].contentWindow.postMessage(JSON.stringify(response), "*");
|
||||
}
|
||||
break;
|
||||
case "changed-input":
|
||||
input = vle_get_input_element(msg.name, msg.src);
|
||||
if (input === null) {
|
||||
const ret = {
|
||||
version: "STACK-JS:1.0.0",
|
||||
type: "error",
|
||||
msg: 'Failed to modify input: "' + msg.name + '"',
|
||||
tgt: msg.src
|
||||
};
|
||||
IFRAMES[msg.src].contentWindow.postMessage(JSON.stringify(ret), "*");
|
||||
return;
|
||||
}
|
||||
DISABLE_CHANGES = true;
|
||||
if (input.type === "checkbox") {
|
||||
input.checked = msg.value;
|
||||
} else {
|
||||
input.value = msg.value;
|
||||
}
|
||||
vle_update_input(input);
|
||||
DISABLE_CHANGES = false;
|
||||
response.type = "changed-input";
|
||||
response.name = msg.name;
|
||||
response.value = msg.value;
|
||||
for (let tgt of INPUTS[input.id]) {
|
||||
if (tgt !== msg.src) {
|
||||
response.tgt = tgt;
|
||||
IFRAMES[tgt].contentWindow.postMessage(JSON.stringify(response), "*");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "toggle-visibility":
|
||||
element = vle_get_element(msg.target);
|
||||
if (element === null) {
|
||||
const ret = {
|
||||
version: "STACK-JS:1.0.0",
|
||||
type: "error",
|
||||
msg: 'Failed to find element: "' + msg.target + '"',
|
||||
tgt: msg.src
|
||||
};
|
||||
IFRAMES[msg.src].contentWindow.postMessage(JSON.stringify(ret), "*");
|
||||
return;
|
||||
}
|
||||
if (msg.set === "show") {
|
||||
element.style.display = "block";
|
||||
vle_update_dom(element);
|
||||
} else if (msg.set === "hide") {
|
||||
element.style.display = "none";
|
||||
}
|
||||
break;
|
||||
case "change-content":
|
||||
element = vle_get_element(msg.target);
|
||||
if (element === null) {
|
||||
response.type = "error";
|
||||
response.msg = 'Failed to find element: "' + msg.target + '"';
|
||||
response.tgt = msg.src;
|
||||
IFRAMES[msg.src].contentWindow.postMessage(JSON.stringify(response), "*");
|
||||
return;
|
||||
}
|
||||
element.replaceChildren(vle_html_sanitize(msg.content));
|
||||
vle_update_dom(element);
|
||||
break;
|
||||
case "get-content":
|
||||
element = vle_get_element(msg.target);
|
||||
response.type = "xfer-content";
|
||||
response.tgt = msg.src;
|
||||
response.target = msg.target;
|
||||
response.content = null;
|
||||
if (element !== null) {
|
||||
response.content = element.innerHTML;
|
||||
}
|
||||
IFRAMES[msg.src].contentWindow.postMessage(JSON.stringify(response), "*");
|
||||
break;
|
||||
case "resize-frame":
|
||||
element = IFRAMES[msg.src].parentElement;
|
||||
element.style.width = msg.width;
|
||||
element.style.height = msg.height;
|
||||
IFRAMES[msg.src].style.width = "100%";
|
||||
IFRAMES[msg.src].style.height = "100%";
|
||||
vle_update_dom(element);
|
||||
break;
|
||||
case "ping":
|
||||
response.type = "ping";
|
||||
response.tgt = msg.src;
|
||||
IFRAMES[msg.src].contentWindow.postMessage(JSON.stringify(response), "*");
|
||||
return;
|
||||
case "initial-input":
|
||||
case "error":
|
||||
break;
|
||||
default:
|
||||
response.type = "error";
|
||||
response.msg = 'Unknown message-type: "' + msg.type + '"';
|
||||
response.tgt = msg.src;
|
||||
IFRAMES[msg.src].contentWindow.postMessage(JSON.stringify(response), "*");
|
||||
}
|
||||
});
|
||||
function create_iframe(iframeid, content, targetdivid, title, scrolling, evil) {
|
||||
const frm = document.createElement("iframe");
|
||||
frm.id = iframeid;
|
||||
frm.style.width = "100%";
|
||||
frm.style.height = "100%";
|
||||
frm.style.border = 0;
|
||||
if (scrolling === false) {
|
||||
frm.scrolling = "no";
|
||||
frm.style.overflow = "hidden";
|
||||
} else {
|
||||
frm.scrolling = "yes";
|
||||
}
|
||||
frm.title = title;
|
||||
frm.referrerpolicy = "no-referrer";
|
||||
if (!evil) {
|
||||
frm.sandbox = "allow-scripts allow-downloads";
|
||||
}
|
||||
frm.srcdoc = content;
|
||||
document.getElementById(targetdivid).replaceChildren(frm);
|
||||
IFRAMES[iframeid] = frm;
|
||||
}
|
||||
;
|
||||
//# sourceMappingURL=stackjsvle.js.map
|
||||
@@ -0,0 +1,605 @@
|
||||
function handleWW(ww_id, action) {
|
||||
const ww_container = document.getElementById(ww_id);
|
||||
const ww_domain = ww_container.dataset.domain;
|
||||
const ww_problemSource = ww_container.dataset.problemsource;
|
||||
const ww_sourceFilePath = ww_container.dataset.sourcefilepath;
|
||||
const ww_course_id = ww_container.dataset.courseid;
|
||||
const ww_user_id = ww_container.dataset.userid;
|
||||
const ww_course_password = ww_container.dataset.coursepassword;
|
||||
const localize_correct = ww_container.dataset.localizeCorrect ? ww_container.dataset.localizeCorrect : "Correct";
|
||||
const localize_incorrect = ww_container.dataset.localizeIncorrect ? ww_container.dataset.localizeIncorrect : "Incorrect";
|
||||
const localize_check_responses = ww_container.dataset.localizeCheckResponses ? ww_container.dataset.localizeCheckResponses : "Check Responses";
|
||||
const localize_randomize = ww_container.dataset.localizeRandomize ? ww_container.dataset.localizeRandomize : "Randomize";
|
||||
const localize_reset = ww_container.dataset.localizeReset ? ww_container.dataset.localizeReset : "Reset";
|
||||
const activate_button = document.getElementById(ww_id + "-button");
|
||||
if (!action) ww_container.dataset.current_seed = ww_container.dataset.seed;
|
||||
else if (action == "randomize") ww_container.dataset.current_seed = Number(ww_container.dataset.current_seed) + 100;
|
||||
let loader = document.createElement("div");
|
||||
loader.style.position = "absolute";
|
||||
loader.style.left = 0;
|
||||
loader.style.top = 0;
|
||||
loader.style.backgroundColor = "rgba(0.2, 0.2, 0.2, 0.4)";
|
||||
loader.style.color = "white";
|
||||
loader.style.width = "100%";
|
||||
loader.style.height = "100%";
|
||||
loader.style.display = "flex";
|
||||
loader.style.alignItems = "center";
|
||||
loader.style.justifyContent = "center";
|
||||
loader.style.marginTop = "0";
|
||||
loader.tabIndex = -1;
|
||||
const loaderText = document.createElement("span");
|
||||
loaderText.textContent = "Loading";
|
||||
loaderText.style.fontSize = "2rem";
|
||||
loader.appendChild(loaderText);
|
||||
ww_container.appendChild(loader);
|
||||
loader.focus();
|
||||
if (!action) {
|
||||
ww_container.dataset.hasHint = ww_container.getElementsByClassName("hint").length > 0;
|
||||
ww_container.dataset.hasSolution = ww_container.getElementsByClassName("solution").length > 0;
|
||||
ww_container.dataset.hasAnswer = ww_container.getElementsByClassName("answer").length > 0;
|
||||
ww_container.dataset.hintLabelText = ww_container.dataset.hasHint == "true" ? ww_container.querySelectorAll(".hint-knowl span.type")[0].textContent : "Hint";
|
||||
ww_container.dataset.solutionLabelText = ww_container.dataset.hasSolution == "true" ? ww_container.querySelectorAll(".solution-knowl span.type")[0].textContent : "Solution";
|
||||
ww_container.tabIndex = -1;
|
||||
}
|
||||
let url;
|
||||
if (action == "check") {
|
||||
const iframe2 = ww_container.querySelector(".problem-iframe");
|
||||
const formData = new FormData(iframe2.contentDocument.getElementById(ww_id + "-form"));
|
||||
const params = new URLSearchParams(formData);
|
||||
url = new URL(ww_domain + "/webwork2/html2xml?" + params.toString());
|
||||
url.searchParams.append("answersSubmitted", "1");
|
||||
url.searchParams.append("WWsubmit", "1");
|
||||
} else {
|
||||
url = new URL(ww_domain + "/webwork2/html2xml");
|
||||
url.searchParams.append("problemSeed", ww_container.dataset.current_seed);
|
||||
if (ww_problemSource) url.searchParams.append("problemSource", ww_problemSource);
|
||||
else if (ww_sourceFilePath) url.searchParams.append("sourceFilePath", ww_sourceFilePath);
|
||||
url.searchParams.append("answersSubmitted", "0");
|
||||
url.searchParams.append("displayMode", "MathJax");
|
||||
url.searchParams.append("courseID", ww_course_id);
|
||||
url.searchParams.append("userID", ww_user_id);
|
||||
url.searchParams.append("course_password", ww_course_password);
|
||||
url.searchParams.append("outputformat", "raw");
|
||||
url.searchParams.append("showSolutions", ww_container.dataset.hasSolution == "true" ? "1" : "0");
|
||||
url.searchParams.append("showHints", ww_container.dataset.hasHint == "true" ? "1" : "0");
|
||||
url.searchParams.append("problemUUID", ww_id);
|
||||
}
|
||||
$.getJSON(url.toString(), (data) => {
|
||||
const form = document.createElement("form");
|
||||
form.id = ww_id + "-form";
|
||||
const body_div = document.createElement("div");
|
||||
body_div.id = ww_id + "-body";
|
||||
body_div.classList.add("exercise", "exercise-like");
|
||||
body_div.lang = data.lang;
|
||||
body_div.dir = data.dir;
|
||||
body_div.innerHTML = data.rh_result.text;
|
||||
for (const tag_name of ["h6", "h5", "h4", "h3", "h2", "h1"]) {
|
||||
const headings = body_div.getElementsByTagName(tag_name);
|
||||
for (heading of headings) {
|
||||
const new_heading = document.createElement("h6");
|
||||
new_heading.innerHTML = heading.innerHTML;
|
||||
cloneAttributes(new_heading, heading);
|
||||
new_heading.classList.add("webwork-part");
|
||||
heading.replaceWith(new_heading);
|
||||
}
|
||||
}
|
||||
adjustSrcHrefs(body_div, ww_domain);
|
||||
translateHintSol(
|
||||
ww_id,
|
||||
body_div,
|
||||
ww_domain,
|
||||
ww_container.dataset.hasHint == "true",
|
||||
ww_container.dataset.hasSolution == "true",
|
||||
ww_container.dataset.hintLabelText,
|
||||
ww_container.dataset.solutionLabelText
|
||||
);
|
||||
form.appendChild(body_div);
|
||||
const wwInputs = {
|
||||
problemSeed: data.inputs_ref.problemSeed,
|
||||
problemUUID: data.inputs_ref.problemUUID,
|
||||
psvn: data.inputs_ref.psvn,
|
||||
courseName: ww_course_id,
|
||||
courseID: ww_course_id,
|
||||
userID: ww_user_id,
|
||||
course_password: ww_course_password,
|
||||
displayMode: "MathJax",
|
||||
session_key: data.rh_result.session_key,
|
||||
outputformat: "raw",
|
||||
language: data.formLanguage,
|
||||
showSummary: data.showSummary,
|
||||
// note ww_container.dataset.hasSolution is a string, possibly 'false' which is true
|
||||
showSolutions: ww_container.dataset.hasSolution == "true" ? "1" : "0",
|
||||
showHints: ww_container.dataset.hasHint == "true" ? "1" : "0",
|
||||
forcePortNumber: data.forcePortNumber
|
||||
};
|
||||
if (ww_sourceFilePath) wwInputs.sourceFilePath = ww_sourceFilePath;
|
||||
else if (ww_problemSource) wwInputs.problemSource = ww_problemSource;
|
||||
for (const wwInputName of Object.keys(wwInputs)) {
|
||||
const input = document.createElement("input");
|
||||
input.type = "hidden";
|
||||
input.name = wwInputName;
|
||||
input.value = wwInputs[wwInputName];
|
||||
form.appendChild(input);
|
||||
}
|
||||
const answers = {};
|
||||
Object.keys(data.rh_result.answers).forEach(function(id) {
|
||||
answers[id] = {};
|
||||
}, data.rh_result.answers);
|
||||
if (ww_container.dataset.hasAnswer == "true") {
|
||||
Object.keys(data.rh_result.answers).forEach(function(id) {
|
||||
answers[id] = {
|
||||
correct_ans: this[id].correct_ans,
|
||||
correct_ans_latex_string: this[id].correct_ans_latex_string,
|
||||
correct_choice: this[id].correct_choice
|
||||
};
|
||||
}, data.rh_result.answers);
|
||||
}
|
||||
let buttonContainer = ww_container.querySelector(".problem-buttons.webwork");
|
||||
if (!buttonContainer) {
|
||||
ww_container.querySelector(".problem-buttons").classList.add("hidden-content");
|
||||
if (activate_button != null) {
|
||||
activate_button.classList.add("hidden-content");
|
||||
}
|
||||
;
|
||||
buttonContainer = document.createElement("div");
|
||||
buttonContainer.classList.add("problem-buttons", "webwork");
|
||||
if (activate_button != null) {
|
||||
activate_button.after(buttonContainer);
|
||||
} else {
|
||||
ww_container.prepend(buttonContainer);
|
||||
}
|
||||
const check = document.createElement("button");
|
||||
check.type = "button";
|
||||
check.id = ww_id + "-check";
|
||||
check.style.marginRight = "0.25rem";
|
||||
check.classList.add("webwork-button");
|
||||
const answerCount = body_div.querySelectorAll("input:not([type=hidden])").length + body_div.querySelectorAll("select:not([type=hidden])").length;
|
||||
check.textContent = localize_check_responses;
|
||||
check.addEventListener("click", () => handleWW(ww_id, "check"));
|
||||
buttonContainer.appendChild(check);
|
||||
if (ww_container.dataset.hasAnswer == "true") {
|
||||
const correct = document.createElement("button");
|
||||
correct.classList.add("show-correct", "webwork-button");
|
||||
correct.type = "button";
|
||||
correct.style.marginRight = "0.25rem";
|
||||
correct.textContent = answerCount > 1 ? "Show Correct Answers" : "Show Correct Answer";
|
||||
correct.addEventListener("click", () => WWshowCorrect(ww_id, answers));
|
||||
buttonContainer.appendChild(correct);
|
||||
}
|
||||
const randomize = document.createElement("button");
|
||||
randomize.type = "button";
|
||||
randomize.classList.add("webwork-button");
|
||||
randomize.style.marginRight = "0.25rem";
|
||||
randomize.textContent = localize_randomize;
|
||||
randomize.addEventListener("click", () => handleWW(ww_id, "randomize"));
|
||||
buttonContainer.appendChild(randomize);
|
||||
const reset = document.createElement("button");
|
||||
reset.type = "button";
|
||||
reset.classList.add("webwork-button");
|
||||
reset.textContent = localize_reset;
|
||||
reset.addEventListener("click", () => resetWW(ww_id));
|
||||
buttonContainer.appendChild(reset);
|
||||
} else {
|
||||
if (ww_container.dataset.hasAnswer == "true") {
|
||||
const correct = buttonContainer.querySelector(".show-correct");
|
||||
const correctNew = correct.cloneNode(true);
|
||||
correctNew.addEventListener("click", () => WWshowCorrect(ww_id, answers));
|
||||
correct.replaceWith(correctNew);
|
||||
}
|
||||
}
|
||||
if (action == "check") {
|
||||
$("body").trigger("runestone_ww_check", data);
|
||||
const inputs = body_div.querySelectorAll("input:not([type=hidden])");
|
||||
for (const input of inputs) {
|
||||
const name = input.name;
|
||||
if (input.type == "text" && answers[name]) {
|
||||
const score = data.rh_result.answers[name].score;
|
||||
let title = "";
|
||||
if (score == 1) {
|
||||
title = `<span class="correct">${localize_correct}!</span>`;
|
||||
} else if (score > 0 && score < 1) {
|
||||
title = `<span class="partly-correct">${Math.round(score * 100)}% ${localize_correct}.</span>`;
|
||||
} else if (data.rh_result.answers[name].student_ans == "") {
|
||||
continue;
|
||||
} else if (score == 0) {
|
||||
title = `<span class="incorrect">${localize_incorrect}.</span>`;
|
||||
}
|
||||
input.after(createFeedbackButton(`${ww_id}-${name}`, title, data.rh_result.answers[name].ans_message));
|
||||
}
|
||||
if (input.type == "radio" && answers[name]) {
|
||||
if (input.value == data.rh_result.answers[name].student_value) {
|
||||
const feedbackButton = createFeedbackButton(
|
||||
`${ww_id}-${name}`,
|
||||
data.rh_result.answers[name].student_value == data.rh_result.answers[name].correct_choice ? `<span class="correct">${localize_correct}!</span>` : `<span class="incorrect">${localize_incorrect}.</span>`
|
||||
);
|
||||
feedbackButton.style.marginRight = "0.25rem";
|
||||
input.after(feedbackButton);
|
||||
}
|
||||
}
|
||||
}
|
||||
const hiddenInputs = body_div.querySelectorAll("input[type=hidden]");
|
||||
for (const input of hiddenInputs) {
|
||||
const name = input.name;
|
||||
if (!input.nextElementSibling) continue;
|
||||
const graphtoolContainer = input.nextElementSibling.nextElementSibling;
|
||||
if (graphtoolContainer && answers[name] && graphtoolContainer.classList.contains("graphtool-container")) {
|
||||
graphtoolContainer.style.position = "relative";
|
||||
const score = data.rh_result.answers[name].score;
|
||||
let title = "";
|
||||
if (score == 1) {
|
||||
title = `<span class="correct">${localize_correct}!</span>`;
|
||||
} else if (score > 0 && score < 1) {
|
||||
title = `<span class="partly-correct">${Math.round(score * 100)}% ${localize_correct}.</span>`;
|
||||
} else if (data.rh_result.answers[name].student_ans == "") {
|
||||
continue;
|
||||
} else if (score == 0) {
|
||||
title = `<span class="incorrect">${localize_incorrect}.</span>`;
|
||||
}
|
||||
const feedbackButton = createFeedbackButton(`${ww_id}-${name}`, title, data.rh_result.answers[name].ans_message);
|
||||
feedbackButton.style.position = "absolute";
|
||||
feedbackButton.style.left = "100%";
|
||||
feedbackButton.style.top = 0;
|
||||
feedbackButton.style.marginLeft = "0.5rem";
|
||||
feedbackButton.dataset.container = "body";
|
||||
graphtoolContainer.appendChild(feedbackButton);
|
||||
}
|
||||
}
|
||||
const selects = body_div.querySelectorAll("select:not([type=hidden])");
|
||||
for (const select of selects) {
|
||||
const name = select.name;
|
||||
const feedbackButton = createFeedbackButton(
|
||||
`${ww_id}-${name}`,
|
||||
data.rh_result.answers[name].score == 1 ? `<span class="correct">${localize_correct}!</span>` : `<span class="incorrect">${localize_incorrect}.</span>`
|
||||
);
|
||||
feedbackButton.style.marginRight = "0.25rem";
|
||||
feedbackButton.style.marginLeft = "0.5rem";
|
||||
select.after(feedbackButton);
|
||||
}
|
||||
}
|
||||
let iframeContents = '<!DOCTYPE html><head><script src="' + ww_domain + `/webwork2_files/node_modules/jquery/dist/jquery.min.js"><\/script><script>window.MathJax = {
|
||||
tex: {
|
||||
inlineMath: [['\\\\(','\\\\)']],
|
||||
tags: "none",
|
||||
useLabelIds: true,
|
||||
tagSide: "right",
|
||||
tagIndent: ".8em",
|
||||
packages: {'[+]': ['base', 'extpfeil', 'ams', 'amscd', 'newcommand', 'knowl']}
|
||||
},
|
||||
options: {
|
||||
ignoreHtmlClass: "tex2jax_ignore",
|
||||
processHtmlClass: "has_am",
|
||||
renderActions: {
|
||||
findScript: [10, function (doc) {
|
||||
document.querySelectorAll('script[type^="math/tex"]').forEach(function(node) {
|
||||
const display = !!node.type.match(/; *mode=display/);
|
||||
const math = new doc.options.MathItem(node.textContent, doc.inputJax[0], display);
|
||||
const text = document.createTextNode('');
|
||||
node.parentNode.replaceChild(text, node);
|
||||
math.start = {node: text, delim: '', n: 0};
|
||||
math.end = {node: text, delim: '', n: 0};
|
||||
doc.math.push(math);
|
||||
});
|
||||
}, '']
|
||||
},
|
||||
},
|
||||
chtml: {
|
||||
scale: 0.88,
|
||||
mtextInheritFont: true
|
||||
},
|
||||
loader: {
|
||||
load: ['input/asciimath', '[tex]/extpfeil', '[tex]/amscd', '[tex]/newcommand', '[pretext]/mathjaxknowl3.js'],
|
||||
paths: {pretext: "https://pretextbook.org/js/lib"},
|
||||
},
|
||||
};
|
||||
<\/script><script src="` + ww_domain + '/webwork2_files/mathjax/es5/tex-chtml.js" id="MathJax-script" defer><\/script><script src="https://pretextbook.org/js/lib/knowl.js" defer><\/script><link rel="stylesheet" href="' + ww_domain + '/webwork2_files/js/vendor/bootstrap/css/bootstrap.css"/><link rel="stylesheet" href="' + ww_domain + '/webwork2_files/themes/math4/math4.css"/><script src="' + ww_domain + '/webwork2_files/js/vendor/bootstrap/js/bootstrap.js" id="MathJax-script" defer><\/script>';
|
||||
const extra_css_files = [];
|
||||
const extra_js_files = [];
|
||||
if (data.rh_result.flags.extra_css_files) data.rh_result.flags.extra_css_files.unshift(...extra_css_files);
|
||||
else data.rh_result.flags.extra_css_files = extra_css_files;
|
||||
for (const cssFile of data.rh_result.flags.extra_css_files) {
|
||||
iframeContents += '<link rel="stylesheet" href="' + (cssFile.external !== "1" ? ww_domain + "/webwork2_files/" : "") + cssFile.file + '"/>';
|
||||
}
|
||||
if (data.rh_result.flags.extra_js_files) data.rh_result.flags.extra_js_files.unshift(...extra_js_files);
|
||||
else data.rh_result.flags.extra_js_files = extra_js_files;
|
||||
for (const jsFile of data.rh_result.flags.extra_js_files) {
|
||||
iframeContents += '<script src="' + (jsFile.external !== "1" ? ww_domain + "/webwork2_files/" : "") + jsFile.file + '" ' + Object.keys(jsFile.attributes || {}).reduce((ret, key) => {
|
||||
ret += key + '="' + jsFile.attributes[key] + '" ';
|
||||
return ret;
|
||||
}, "") + "><\/script>";
|
||||
}
|
||||
iframeContents += '<link rel="stylesheet" href="https://pretextbook.org/css/0.31/pretext_add_on.css"/><link rel="stylesheet" href="https://pretextbook.org/css/0.31/knowls_default.css"/><script src="' + ww_domain + `/webwork2_files/node_modules/iframe-resizer/js/iframeResizer.contentWindow.min.js"><\/script><style>
|
||||
html { overflow-y: hidden; }
|
||||
html body { background:unset; margin: 0; }
|
||||
body { font-size: initial; line-height: initial; }
|
||||
.hidden-content { display: none; }
|
||||
input[type="text"], input[type="radio"], label, select {
|
||||
height: auto;
|
||||
width: auto;
|
||||
max-width: unset;
|
||||
margin: 0;
|
||||
font-size: initial;
|
||||
font-family: sans-serif;
|
||||
line-height: initial;
|
||||
}
|
||||
input[type="text"] {
|
||||
padding: 1px;
|
||||
padding-inline: 2px;
|
||||
border-width: 1px;
|
||||
border-style: inset;
|
||||
border-color: rgb(133, 133, 133);
|
||||
border-radius: 0;
|
||||
box-shadow: unset;
|
||||
transition: none;
|
||||
}
|
||||
input[type="text"]:focus, input[type="text"]:active {
|
||||
box-shadow: unset;
|
||||
border-width: 1px;
|
||||
border-style: inset;
|
||||
border-radius: 4px;
|
||||
border-color: rgb(133, 133, 133);
|
||||
outline: auto;
|
||||
}
|
||||
input[type="radio"] {
|
||||
box-sizing: border-box;
|
||||
margin-block: 3px 0;
|
||||
margin-inline: 5px 3px;
|
||||
vertical-align: unset;
|
||||
}
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
select {
|
||||
margin: 0;
|
||||
border-width: 1px;
|
||||
border-style: inset;
|
||||
display: inline-block;
|
||||
padding-block: 1px;
|
||||
vertical-align: baseline;
|
||||
background-color: unset;
|
||||
padding-inline: 0;
|
||||
}
|
||||
.popover-title, .popover-content {
|
||||
text-align: center;
|
||||
}
|
||||
.popover-title.correct {
|
||||
background-color: #8F8;
|
||||
}
|
||||
.accordion-body.expanded {
|
||||
overflow-y: visible;
|
||||
overflow-x: clip;
|
||||
}
|
||||
.graphtool-answer-container .graphtool-graph {
|
||||
margin: 0;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
div.PGML img.image-view-elt {
|
||||
max-width:100%;
|
||||
}
|
||||
</style></head><body><main class="pretext-content">` + form.outerHTML + "</main></body></html>";
|
||||
let iframe2;
|
||||
if (!action) {
|
||||
iframe2 = document.createElement("iframe");
|
||||
iframe2.style.width = "1px";
|
||||
iframe2.style.minWidth = "100%";
|
||||
iframe2.classList.add("problem-iframe");
|
||||
ww_container.querySelector(".problem-contents").classList.add("hidden-content");
|
||||
if (activate_button != null) {
|
||||
activate_button.after(iframe2);
|
||||
} else {
|
||||
ww_container.prepend(iframe2);
|
||||
}
|
||||
iFrameResize({ checkOrigin: false, scrolling: "omit", heightCalculationMethod: "min" }, iframe2);
|
||||
iframe2.addEventListener("load", () => {
|
||||
const iframeForm = iframe2.contentDocument.getElementById(ww_id + "-form");
|
||||
iframeForm.addEventListener("submit", (e) => {
|
||||
handleWW(ww_id, "check");
|
||||
e.preventDefault();
|
||||
});
|
||||
iframe2.contentDocument.querySelectorAll(".collapse.in").forEach((collapse) => collapse.classList.add("expanded"));
|
||||
iframe2.contentWindow.jQuery(".collapse").on("shown", function(e) {
|
||||
if (e.target != this) return;
|
||||
this.classList.add("expanded");
|
||||
});
|
||||
iframe2.contentWindow.jQuery(".collapse").on("hide", function(e) {
|
||||
if (e.target != this) return;
|
||||
this.classList.remove("expanded");
|
||||
});
|
||||
iframe2.contentDocument.querySelectorAll("button.ww-feedback[data-content]").forEach((button) => {
|
||||
iframe2.contentWindow.jQuery(button).popover("show");
|
||||
const content = iframe2.contentDocument.getElementById(button.id.replace("-feedback-button", "-content"));
|
||||
const popover = content.parentNode.parentNode;
|
||||
popover.id = button.id.replace("-feedback-button", "-feedback");
|
||||
button.setAttribute("aria-describedby", popover.id);
|
||||
if (button.previousElementSibling)
|
||||
button.previousElementSibling.setAttribute("aria-describedby", popover.id);
|
||||
popover.querySelector(".arrow").remove();
|
||||
const title = popover.querySelector(".popover-title");
|
||||
if (button.dataset.emptyContent) {
|
||||
title.style.borderBottomWidth = 0;
|
||||
content.parentNode.remove();
|
||||
}
|
||||
if (title.textContent == localize_correct + "!") title.classList.add("correct");
|
||||
});
|
||||
iframe2.contentWindow.MathJax.startup.promise.then(() => iframe2.contentWindow.MathJax.typesetPromise([".popover", ".popover-content"]));
|
||||
});
|
||||
} else {
|
||||
iframe2 = ww_container.querySelector(".problem-iframe");
|
||||
}
|
||||
iframe2.srcdoc = iframeContents;
|
||||
iframe2.addEventListener("load", () => {
|
||||
loader.remove();
|
||||
}, { once: true });
|
||||
ww_container.focus();
|
||||
});
|
||||
}
|
||||
function WWshowCorrect(ww_id, answers) {
|
||||
const ww_container = document.getElementById(ww_id);
|
||||
const iframe2 = ww_container.querySelector(".problem-iframe");
|
||||
const body = iframe2.contentDocument.getElementById(ww_id + "-body");
|
||||
$("body").trigger("runestone_show_correct", {
|
||||
"ww_id": ww_id,
|
||||
"answers": answers
|
||||
});
|
||||
let inputs = body.querySelectorAll("input:not([type=hidden])");
|
||||
for (const input of inputs) {
|
||||
const name = input.name;
|
||||
const span_id = `${ww_id}-${name}-correct`;
|
||||
if (input.type == "text" && answers[name] && !iframe2.contentDocument.getElementById(span_id)) {
|
||||
const feedbackButton = iframe2.contentDocument.getElementById(`${ww_id}-${name}-feedback-button`);
|
||||
if (feedbackButton) {
|
||||
feedbackButton.remove();
|
||||
iframe2.contentWindow.jQuery(feedbackButton).popover("hide");
|
||||
}
|
||||
label = iframe2.contentDocument.getElementById(`${span_id}-label`);
|
||||
if (label) {
|
||||
label.parentElement.insertBefore(input, label);
|
||||
label.remove();
|
||||
}
|
||||
input.type = "hidden";
|
||||
const correct_ans_text = iframe2.contentDocument.createElement("div");
|
||||
correct_ans_text.innerHTML = answers[name].correct_ans;
|
||||
input.value = correct_ans_text.textContent;
|
||||
const show_span = iframe2.contentDocument.createElement("span");
|
||||
show_span.id = span_id;
|
||||
show_span.appendChild(answers[name].correct_ans_latex_string ? iframe2.contentDocument.createTextNode("\\(" + answers[name].correct_ans_latex_string + "\\)") : iframe2.contentDocument.createTextNode(answers[name].correct_ans));
|
||||
input.parentElement.insertBefore(show_span, input);
|
||||
}
|
||||
if (input.type == "radio" && answers[name]) {
|
||||
const feedbackButton = iframe2.contentDocument.getElementById(`${ww_id}-${name}-feedback-button`);
|
||||
if (feedbackButton) {
|
||||
feedbackButton.remove();
|
||||
iframe2.contentWindow.jQuery(feedbackButton).popover("hide");
|
||||
}
|
||||
correct_value = answers[name].correct_choice;
|
||||
if (input.value == correct_value) input.checked = true;
|
||||
}
|
||||
}
|
||||
const hiddenInputs = body.querySelectorAll("input[type=hidden]");
|
||||
for (const input of hiddenInputs) {
|
||||
const name = input.name;
|
||||
if (!input.nextElementSibling) continue;
|
||||
const graphtoolContainer = input.nextElementSibling.nextElementSibling;
|
||||
if (graphtoolContainer && answers[name] && graphtoolContainer.classList.contains("graphtool-container")) {
|
||||
const feedbackButton = iframe2.contentDocument.getElementById(`${ww_id}-${name}-feedback-button`);
|
||||
if (feedbackButton) {
|
||||
feedbackButton.remove();
|
||||
iframe2.contentWindow.jQuery(feedbackButton).popover("hide");
|
||||
}
|
||||
const correct_ans_div = iframe2.contentDocument.createElement("div");
|
||||
input.parentElement.insertBefore(correct_ans_div, graphtoolContainer);
|
||||
graphtoolContainer.style.display = "none";
|
||||
input.value = answers[name].correct_ans;
|
||||
iframe2.contentWindow.jQuery(correct_ans_div).html(answers[name].correct_ans_latex_string);
|
||||
const script = iframe2.contentDocument.createElement("script");
|
||||
script.textContent = correct_ans_div.querySelector("script").textContent.replace('\nwindow.addEventListener("DOMContentLoaded",', "(").replace(/;\n$/, "();");
|
||||
iframe2.contentDocument.body.appendChild(script);
|
||||
}
|
||||
}
|
||||
let selects = body.querySelectorAll("select:not([type=hidden])");
|
||||
for (const select of selects) {
|
||||
const name = select.name;
|
||||
const span_id = `${ww_id}-${name}-correct`;
|
||||
if (answers[name] && !iframe2.contentDocument.getElementById(span_id)) {
|
||||
const feedbackButton = iframe2.contentDocument.getElementById(`${ww_id}-${name}-feedback-button`);
|
||||
if (feedbackButton) {
|
||||
feedbackButton.remove();
|
||||
iframe2.contentWindow.jQuery(feedbackButton).popover("hide");
|
||||
}
|
||||
select.style.display = "none";
|
||||
select.value = answers[name].correct_ans;
|
||||
const show_span = iframe2.contentDocument.createElement("span");
|
||||
show_span.id = span_id;
|
||||
show_span.appendChild(answers[name].correct_ans_latex_string ? iframe2.contentDocument.createTextNode("\\(" + answers[name].correct_ans_latex_string + "\\)") : iframe2.contentDocument.createTextNode(answers[name].correct_ans));
|
||||
select.parentElement.insertBefore(show_span, select);
|
||||
}
|
||||
}
|
||||
const mathjaxTypesetScript = iframe2.contentDocument.createElement("script");
|
||||
mathjaxTypesetScript.textContent = "MathJax.startup.promise.then(() => MathJax.typesetPromise([document.body]));";
|
||||
iframe2.contentDocument.body.appendChild(mathjaxTypesetScript);
|
||||
}
|
||||
function resetWW(ww_id) {
|
||||
const ww_container = document.getElementById(ww_id);
|
||||
const activate_button = document.getElementById(ww_id + "-button");
|
||||
ww_container.dataset.current_seed = ww_container.dataset.seed;
|
||||
iframe = ww_container.querySelector(".problem-iframe");
|
||||
iframe.remove();
|
||||
ww_container.querySelector(".problem-contents").classList.remove("hidden-content");
|
||||
ww_container.querySelector(".problem-buttons.webwork").remove();
|
||||
ww_container.querySelector(".problem-buttons").classList.remove("hidden-content");
|
||||
if (activate_button != null) {
|
||||
activate_button.classList.remove("hidden-content");
|
||||
}
|
||||
;
|
||||
}
|
||||
function adjustSrcHrefs(container, ww_domain) {
|
||||
container.querySelectorAll("[href]").forEach((node) => {
|
||||
const href = node.attributes.href.value;
|
||||
if (href !== "#" && !href.match(/^[a-z]+:\/\//i)) node.href = ww_domain + "/" + href;
|
||||
});
|
||||
container.querySelectorAll("[src]").forEach((node) => {
|
||||
node.src = ww_domain + "/" + node.attributes.src.value;
|
||||
});
|
||||
}
|
||||
function translateHintSol(ww_id, body_div, ww_domain, b_ptx_has_hint, b_ptx_has_solution, hint_label_text, solution_label_text) {
|
||||
const ww_container = document.getElementById(ww_id);
|
||||
const hintsolnodes = document.evaluate("//p[a/b]", body_div, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
|
||||
if (hintsolnodes) {
|
||||
let solutionlikewrapper;
|
||||
for (let i = 0; i < hintsolnodes.snapshotLength; i++) {
|
||||
const hintsolp = hintsolnodes.snapshotItem(i);
|
||||
if (!hintsolp) continue;
|
||||
const hintSolType = hintsolp.textContent.trim().toLowerCase().replace(":", "");
|
||||
if (hintsolp.previousElementSibling.textContent.trim() != "Hint:" && hintsolp.previousElementSibling.textContent.trim() != "Solution:") {
|
||||
solutionlikewrapper = document.createElement("div");
|
||||
solutionlikewrapper.classList.add("webwork", "solutions");
|
||||
hintsolp.parentNode.insertBefore(solutionlikewrapper, hintsolp);
|
||||
}
|
||||
if (hintSolType == "solution" && !b_ptx_has_solution || hintSolType == "hint" && !b_ptx_has_hint) continue;
|
||||
const knowlDetails = document.createElement("details");
|
||||
knowlDetails.classList.add(hintSolType);
|
||||
knowlDetails.classList.add("solution-like");
|
||||
knowlDetails.classList.add("born-hidden-knowl");
|
||||
const knowlSummary = document.createElement("summary");
|
||||
const summaryLabel = document.createElement("span");
|
||||
summaryLabel.classList.add("type");
|
||||
summaryLabel.innerHTML = hintSolType == "hint" ? hint_label_text : solution_label_text;
|
||||
knowlSummary.appendChild(summaryLabel);
|
||||
knowlDetails.appendChild(knowlSummary);
|
||||
const knowlContents = document.createElement("div");
|
||||
knowlContents.classList.add(hintSolType);
|
||||
knowlContents.classList.add("solution-like");
|
||||
knowlContents.innerHTML = hintsolp.firstElementChild.dataset.knowlContents;
|
||||
knowlDetails.appendChild(knowlContents);
|
||||
adjustSrcHrefs(knowlContents, ww_domain);
|
||||
solutionlikewrapper.appendChild(knowlDetails);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < hintsolnodes.snapshotLength; i++) {
|
||||
hintsolnodes.snapshotItem(i).remove();
|
||||
}
|
||||
}
|
||||
function cloneAttributes(target, source) {
|
||||
[...source.attributes].forEach((attr) => {
|
||||
target.setAttribute(attr.nodeName, attr.nodeValue);
|
||||
});
|
||||
}
|
||||
function createFeedbackButton(id, title, content) {
|
||||
const feedbackButton = document.createElement("button");
|
||||
feedbackButton.dataset.title = title;
|
||||
feedbackButton.dataset.content = `<div id="${id}-content">${content || ""}</div>`;
|
||||
if (!content) feedbackButton.dataset.emptyContent = "1";
|
||||
const contentSpan = document.createElement("span");
|
||||
contentSpan.style.fontWeight = 1e3;
|
||||
contentSpan.textContent = "\u{1F5E9}";
|
||||
feedbackButton.appendChild(contentSpan);
|
||||
feedbackButton.type = "button";
|
||||
feedbackButton.classList.add("ww-feedback");
|
||||
feedbackButton.style.borderRadius = 0;
|
||||
feedbackButton.id = `${id}-feedback-button`;
|
||||
feedbackButton.dataset.html = true;
|
||||
feedbackButton.dataset.placement = "bottom";
|
||||
feedbackButton.dataset.trigger = "click";
|
||||
return feedbackButton;
|
||||
}
|
||||
//# sourceMappingURL=pretext-webwork.js.map
|
||||
@@ -0,0 +1,885 @@
|
||||
function handleWW(ww_id, action) {
|
||||
const ww_container = document.getElementById(ww_id);
|
||||
const ww_domain = ww_container.dataset.domain;
|
||||
const ww_problemSource = ww_container.dataset.problemsource;
|
||||
const ww_sourceFilePath = ww_container.dataset.sourcefilepath;
|
||||
const ww_course_id = ww_container.dataset.courseid;
|
||||
const ww_user_id = ww_container.dataset.userid;
|
||||
const ww_course_password = ww_container.dataset.coursepassword || ww_container.dataset.passwd;
|
||||
const localize_correct = ww_container.dataset.localizeCorrect || "Correct";
|
||||
const localize_incorrect = ww_container.dataset.localizeIncorrect || "Incorrect";
|
||||
const localize_blank = ww_container.dataset.localizeBlank || "Blank";
|
||||
const localize_submit = ww_container.dataset.localizeSubmit || "Submit";
|
||||
const localize_check_responses = ww_container.dataset.localizeCheckResponses || "Check Responses";
|
||||
const localize_reveal = ww_container.dataset.localizeReveal || "Reveal";
|
||||
const localize_randomize = ww_container.dataset.localizeRandomize || "Randomize";
|
||||
const localize_reset = ww_container.dataset.localizeReset || "Reset";
|
||||
const runestone_logged_in = typeof eBookConfig !== "undefined" && eBookConfig.username !== "";
|
||||
const activate_button = document.getElementById(ww_id + "-button");
|
||||
if (!action) {
|
||||
ww_container.dataset.current_seed = ww_container.dataset.seed;
|
||||
if (runestone_logged_in) {
|
||||
ww_container.dataset.current_seed = webworkSeedHash(eBookConfig.username + ww_container.dataset.current_seed);
|
||||
}
|
||||
} else if (action == "randomize") ww_container.dataset.current_seed = Number(ww_container.dataset.current_seed) + 100;
|
||||
let loader = document.createElement("div");
|
||||
loader.style.position = "absolute";
|
||||
loader.style.left = 0;
|
||||
loader.style.top = 0;
|
||||
loader.style.backgroundColor = "rgba(0.2, 0.2, 0.2, 0.4)";
|
||||
loader.style.color = "white";
|
||||
loader.style.width = "100%";
|
||||
loader.style.height = "100%";
|
||||
loader.style.display = "flex";
|
||||
loader.style.alignItems = "center";
|
||||
loader.style.justifyContent = "center";
|
||||
loader.style.marginTop = "0";
|
||||
loader.tabIndex = -1;
|
||||
const loaderText = document.createElement("span");
|
||||
loaderText.textContent = "Loading";
|
||||
loaderText.style.fontSize = "2rem";
|
||||
loader.appendChild(loaderText);
|
||||
ww_container.appendChild(loader);
|
||||
loader.focus();
|
||||
if (!action) {
|
||||
ww_container.dataset.hasHint = ww_container.getElementsByClassName("hint").length > 0;
|
||||
ww_container.dataset.hasSolution = ww_container.getElementsByClassName("solution").length > 0;
|
||||
ww_container.dataset.hasAnswer = ww_container.getElementsByClassName("answer").length > 0;
|
||||
ww_container.dataset.hintLabelText = ww_container.dataset.hasHint == "true" ? ww_container.querySelectorAll(".hint-knowl span.type, details.hint span.type")[0].textContent : "Hint";
|
||||
ww_container.dataset.solutionLabelText = ww_container.dataset.hasSolution == "true" ? ww_container.querySelectorAll(".solution-knowl span.type, details.solution span.type")[0].textContent : "Solution";
|
||||
ww_container.tabIndex = -1;
|
||||
}
|
||||
let url;
|
||||
if (action == "check") {
|
||||
const iframe2 = ww_container.querySelector(".problem-iframe");
|
||||
const formData = new FormData(iframe2.contentDocument.getElementById(ww_id + "-form"));
|
||||
const params = new URLSearchParams(formData);
|
||||
url = new URL(ww_domain + "/webwork2/html2xml?" + params.toString());
|
||||
url.searchParams.append("answersSubmitted", "1");
|
||||
url.searchParams.append("WWsubmit", "1");
|
||||
} else {
|
||||
url = new URL(ww_domain + "/webwork2/html2xml");
|
||||
url.searchParams.append("problemSeed", ww_container.dataset.current_seed);
|
||||
if (ww_problemSource) url.searchParams.append("problemSource", ww_problemSource);
|
||||
else if (ww_sourceFilePath) url.searchParams.append("sourceFilePath", ww_sourceFilePath);
|
||||
url.searchParams.append("answersSubmitted", "0");
|
||||
url.searchParams.append("displayMode", "MathJax");
|
||||
url.searchParams.append("courseID", ww_course_id);
|
||||
url.searchParams.append("userID", ww_user_id);
|
||||
url.searchParams.append("course_password", ww_course_password);
|
||||
url.searchParams.append("outputformat", "raw");
|
||||
url.searchParams.append("showSolutions", ww_container.dataset.hasSolution == "true" ? "1" : "0");
|
||||
url.searchParams.append("showHints", ww_container.dataset.hasHint == "true" ? "1" : "0");
|
||||
url.searchParams.append("problemUUID", ww_id);
|
||||
}
|
||||
$.getJSON(url.toString(), (data) => {
|
||||
const form = document.createElement("form");
|
||||
form.id = ww_id + "-form";
|
||||
const body_div = document.createElement("div");
|
||||
body_div.id = ww_id + "-body";
|
||||
body_div.classList.add("exercise", "exercise-like");
|
||||
body_div.lang = data.lang;
|
||||
body_div.dir = data.dir;
|
||||
body_div.innerHTML = data.rh_result.text;
|
||||
for (const tag_name of ["h6", "h5", "h4", "h3", "h2", "h1"]) {
|
||||
const headings = body_div.getElementsByTagName(tag_name);
|
||||
for (heading of headings) {
|
||||
const new_heading = document.createElement("h6");
|
||||
new_heading.innerHTML = heading.innerHTML;
|
||||
cloneAttributes(new_heading, heading);
|
||||
new_heading.classList.add("webwork-part");
|
||||
heading.replaceWith(new_heading);
|
||||
}
|
||||
}
|
||||
adjustSrcHrefs(body_div, ww_domain);
|
||||
translateHintSol(
|
||||
ww_id,
|
||||
body_div,
|
||||
ww_domain,
|
||||
ww_container.dataset.hasHint == "true",
|
||||
ww_container.dataset.hasSolution == "true",
|
||||
ww_container.dataset.hintLabelText,
|
||||
ww_container.dataset.solutionLabelText
|
||||
);
|
||||
if (runestone_logged_in) {
|
||||
const answersObject = wwList[ww_id.replace(/-ww-rs$/, "")].answers ? wwList[ww_id.replace(/-ww-rs$/, "")].answers : { "answers": [], "mqAnswers": [] };
|
||||
const mqAnswers = answersObject.mqAnswers;
|
||||
for (const mqAnswer in mqAnswers) {
|
||||
const mqInput = body_div.querySelector("input[id=" + mqAnswer + "]");
|
||||
if (mqInput && mqInput.value == "") {
|
||||
mqInput.setAttribute("value", mqAnswers[mqAnswer]);
|
||||
}
|
||||
}
|
||||
const answers2 = answersObject.answers;
|
||||
for (const answer in answers2) {
|
||||
const input = body_div.querySelector("input[id=" + answer + "]");
|
||||
if (input && input.value == "") {
|
||||
input.setAttribute("value", answers2[answer]);
|
||||
}
|
||||
if (input && input.type.toUpperCase() == "RADIO") {
|
||||
const buttons = body_div.querySelectorAll("input[name=" + answer + "]");
|
||||
for (const button of buttons) {
|
||||
if (button.value == answers2[answer]) {
|
||||
button.setAttribute("checked", "checked");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (input && input.type.toUpperCase() == "CHECKBOX") {
|
||||
const checkboxes = body_div.querySelectorAll("input[name=" + answer + "]");
|
||||
for (const checkbox of checkboxes) {
|
||||
let checkbox_regex = new RegExp("(\\[|, )" + checkbox.value + "(, |\\])");
|
||||
if (answers2[answer].match(checkbox_regex)) {
|
||||
checkbox.setAttribute("checked", "checked");
|
||||
}
|
||||
}
|
||||
}
|
||||
var select = body_div.querySelector("select[id=" + answer + "]");
|
||||
if (select && answers2[answer]) {
|
||||
let this_answer = answers2[answer];
|
||||
if (/^\\text\{.*\}$/.test(this_answer)) {
|
||||
this_answer = this_answer.match(/^\\text\{(.*)\}$/)[1];
|
||||
}
|
||||
;
|
||||
let quote_escaped_answer = this_answer.replace(/"/g, '\\"');
|
||||
const option = body_div.querySelector(`select[id="${answer}"] option[value="${quote_escaped_answer}"]`);
|
||||
if (option) {
|
||||
option.setAttribute("selected", "selected");
|
||||
}
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
form.appendChild(body_div);
|
||||
const wwInputs = {
|
||||
problemSeed: data.inputs_ref.problemSeed,
|
||||
problemUUID: data.inputs_ref.problemUUID,
|
||||
psvn: data.inputs_ref.psvn,
|
||||
courseName: ww_course_id,
|
||||
courseID: ww_course_id,
|
||||
userID: ww_user_id,
|
||||
course_password: ww_course_password,
|
||||
displayMode: "MathJax",
|
||||
session_key: data.rh_result.session_key,
|
||||
outputformat: "raw",
|
||||
language: data.formLanguage,
|
||||
showSummary: data.showSummary,
|
||||
// note ww_container.dataset.hasSolution is a string, possibly 'false' which is true
|
||||
showSolutions: ww_container.dataset.hasSolution == "true" ? "1" : "0",
|
||||
showHints: ww_container.dataset.hasHint == "true" ? "1" : "0",
|
||||
forcePortNumber: data.forcePortNumber
|
||||
};
|
||||
if (ww_sourceFilePath) wwInputs.sourceFilePath = ww_sourceFilePath;
|
||||
else if (ww_problemSource) wwInputs.problemSource = ww_problemSource;
|
||||
for (const wwInputName of Object.keys(wwInputs)) {
|
||||
const input = document.createElement("input");
|
||||
input.type = "hidden";
|
||||
input.name = wwInputName;
|
||||
input.value = wwInputs[wwInputName];
|
||||
form.appendChild(input);
|
||||
}
|
||||
const answers = {};
|
||||
Object.keys(data.rh_result.answers).forEach(function(id) {
|
||||
answers[id] = {};
|
||||
}, data.rh_result.answers);
|
||||
if (ww_container.dataset.hasAnswer == "true") {
|
||||
Object.keys(data.rh_result.answers).forEach(function(id) {
|
||||
answers[id] = {
|
||||
correct_ans: this[id].correct_ans,
|
||||
correct_ans_latex_string: this[id].correct_ans_latex_string,
|
||||
correct_choice: this[id].correct_choice,
|
||||
correct_choices: this[id].correct_choices
|
||||
};
|
||||
}, data.rh_result.answers);
|
||||
}
|
||||
let buttonContainer = ww_container.querySelector(".problem-buttons.webwork");
|
||||
if (!buttonContainer) {
|
||||
ww_container.querySelector(".problem-buttons").classList.add("hidden-content");
|
||||
if (activate_button != null) {
|
||||
activate_button.classList.add("hidden-content");
|
||||
}
|
||||
;
|
||||
buttonContainer = document.createElement("div");
|
||||
buttonContainer.classList.add("problem-buttons", "webwork");
|
||||
if (activate_button != null) {
|
||||
activate_button.after(buttonContainer);
|
||||
} else {
|
||||
ww_container.prepend(buttonContainer);
|
||||
}
|
||||
const check = document.createElement("button");
|
||||
check.type = "button";
|
||||
check.id = ww_id + "-check";
|
||||
check.style.marginRight = "0.25rem";
|
||||
check.classList.add("webwork-button");
|
||||
const answerCount = body_div.querySelectorAll("input:not([type=hidden])").length + body_div.querySelectorAll("select:not([type=hidden])").length;
|
||||
check.textContent = runestone_logged_in ? localize_submit : localize_check_responses;
|
||||
check.addEventListener("click", () => handleWW(ww_id, "check"));
|
||||
buttonContainer.appendChild(check);
|
||||
if (ww_container.dataset.hasAnswer == "true") {
|
||||
const correct = document.createElement("button");
|
||||
correct.classList.add("show-correct", "webwork-button");
|
||||
correct.type = "button";
|
||||
correct.style.marginRight = "0.25rem";
|
||||
correct.textContent = localize_reveal;
|
||||
correct.addEventListener("click", () => WWshowCorrect(ww_id, answers));
|
||||
buttonContainer.appendChild(correct);
|
||||
}
|
||||
const randomize = document.createElement("button");
|
||||
randomize.type = "button";
|
||||
randomize.classList.add("webwork-button");
|
||||
randomize.style.marginRight = "0.25rem";
|
||||
randomize.textContent = localize_randomize;
|
||||
randomize.addEventListener("click", () => handleWW(ww_id, "randomize"));
|
||||
buttonContainer.appendChild(randomize);
|
||||
const reset = document.createElement("button");
|
||||
reset.type = "button";
|
||||
reset.classList.add("webwork-button");
|
||||
reset.textContent = localize_reset;
|
||||
reset.addEventListener("click", () => resetWW(ww_id));
|
||||
buttonContainer.appendChild(reset);
|
||||
} else {
|
||||
if (ww_container.dataset.hasAnswer == "true") {
|
||||
const correct = buttonContainer.querySelector(".show-correct");
|
||||
const correctNew = correct.cloneNode(true);
|
||||
correctNew.addEventListener("click", () => WWshowCorrect(ww_id, answers));
|
||||
correct.replaceWith(correctNew);
|
||||
}
|
||||
}
|
||||
if (action == "check") {
|
||||
$("body").trigger("runestone_ww_check", data);
|
||||
const inputs = body_div.querySelectorAll("input:not([type=hidden])");
|
||||
for (const input of inputs) {
|
||||
const name = input.name;
|
||||
if (input.type == "text" && answers[name]) {
|
||||
const score = data.rh_result.answers[name].score;
|
||||
let headline2 = "";
|
||||
let correctClass2 = "";
|
||||
if (score >= 1) {
|
||||
headline2 = localize_correct + "!";
|
||||
correctClass2 = "correct";
|
||||
} else if (score > 0 && score < 1) {
|
||||
headline2 = `${Math.round(score * 100)}% ${localize_correct}.`;
|
||||
correctClass2 = "partly-correct";
|
||||
} else if (data.rh_result.answers[name].student_ans == "") {
|
||||
headline2 = localize_blank + ".";
|
||||
correctClass2 = "blank";
|
||||
} else if (score <= 0) {
|
||||
headline2 = localize_incorrect + ".";
|
||||
correctClass2 = "incorrect";
|
||||
}
|
||||
let title = `<span class="${correctClass2}">${headline2}</span>`;
|
||||
let message = data.rh_result.answers[name].ans_message ? data.rh_result.answers[name].ans_message : "";
|
||||
input.classList.add(correctClass2);
|
||||
feedbackSpan = document.createElement("span");
|
||||
feedbackSpan.id = `${ww_id}-${name}-feedback`;
|
||||
feedbackHeadline = document.createElement("span");
|
||||
feedbackHeadline.id = `${ww_id}-${name}-headline`;
|
||||
feedbackHeadline.textContent = headline2;
|
||||
feedbackSpan.appendChild(feedbackHeadline);
|
||||
feedbackMessage = document.createElement("span");
|
||||
feedbackMessage.id = `${ww_id}-${name}-message`;
|
||||
feedbackMessage.textContent = message;
|
||||
feedbackSpan.appendChild(feedbackMessage);
|
||||
let nbSpan = document.createElement("span");
|
||||
nbSpan.classList.add("nobreak");
|
||||
input.parentNode.insertBefore(nbSpan, input);
|
||||
nbSpan.appendChild(input);
|
||||
let srSpan = document.createElement("span");
|
||||
srSpan.classList.add("visually-hidden");
|
||||
srSpan.appendChild(feedbackSpan);
|
||||
nbSpan.appendChild(srSpan);
|
||||
input.setAttribute("aria-describedby", `${ww_id}-${name}-feedback`);
|
||||
srSpan.after(createFeedbackButton(`${ww_id}-${name}`, title, message));
|
||||
}
|
||||
if (input.type.toUpperCase() == "RADIO" && answers[name]) {
|
||||
const score = data.rh_result.answers[name].score;
|
||||
const student_ans = data.rh_result.answers[name].student_value || data.rh_result.answers[name].student_ans;
|
||||
const correct_ans = data.rh_result.answers[name].correct_choice || data.rh_result.answers[name].correct_ans;
|
||||
if (input.value == student_ans) {
|
||||
if (score == 1) {
|
||||
input.parentNode.classList.add("correct");
|
||||
} else {
|
||||
input.parentNode.classList.add("incorrect");
|
||||
}
|
||||
const feedbackButton = createFeedbackButton(
|
||||
`${ww_id}-${name}`,
|
||||
student_ans == correct_ans ? `<span class="correct">${localize_correct}</span>` : `<span class="incorrect">${localize_incorrect}.</span>`
|
||||
);
|
||||
feedbackButton.style.marginRight = "0.25rem";
|
||||
input.after(feedbackButton);
|
||||
}
|
||||
}
|
||||
if (input.type.toUpperCase() == "CHECKBOX" && answers[name]) {
|
||||
const score = data.rh_result.answers[name].score;
|
||||
const student_ans = data.rh_result.answers[name].student_ans;
|
||||
const correct_ans = data.rh_result.answers[name].correct_ans;
|
||||
if (input.value == data.rh_result.answers[name].firstElement) {
|
||||
const checkbox_div = input.parentNode.parentNode;
|
||||
if (score == 1) {
|
||||
checkbox_div.classList.add("correct");
|
||||
} else {
|
||||
checkbox_div.classList.add("incorrect");
|
||||
}
|
||||
const feedbackButton = createFeedbackButton(
|
||||
`${ww_id}-${name}`,
|
||||
student_ans == correct_ans ? `<span class="correct">${localize_correct}</span>` : `<span class="incorrect">${localize_incorrect}.</span>`
|
||||
);
|
||||
checkbox_div.insertBefore(feedbackButton, checkbox_div.firstChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
const hiddenInputs = body_div.querySelectorAll("input[type=hidden]");
|
||||
for (const input of hiddenInputs) {
|
||||
const name = input.name;
|
||||
if (!input.nextElementSibling) continue;
|
||||
const graphtoolContainer = input.nextElementSibling.nextElementSibling;
|
||||
if (graphtoolContainer && answers[name] && graphtoolContainer.classList.contains("graphtool-container")) {
|
||||
graphtoolContainer.style.position = "relative";
|
||||
const score = data.rh_result.answers[name].score;
|
||||
let title = "";
|
||||
if (score == 1) {
|
||||
title = `<span class="correct">${localize_correct}</span>`;
|
||||
} else if (score > 0 && score < 1) {
|
||||
title = `<span class="partly-correct">${Math.round(score * 100)}% ${localize_correct}.</span>`;
|
||||
} else if (data.rh_result.answers[name].student_ans == "") {
|
||||
continue;
|
||||
} else if (score == 0) {
|
||||
title = `<span class="incorrect">${localize_incorrect}.</span>`;
|
||||
}
|
||||
const feedbackButton = createFeedbackButton(`${ww_id}-${name}`, title, data.rh_result.answers[name].ans_message);
|
||||
feedbackButton.style.position = "absolute";
|
||||
feedbackButton.style.left = "100%";
|
||||
feedbackButton.style.top = 0;
|
||||
feedbackButton.style.marginLeft = "0.5rem";
|
||||
feedbackButton.dataset.bsContainer = "body";
|
||||
graphtoolContainer.appendChild(feedbackButton);
|
||||
if (score == 1) {
|
||||
graphtoolContainer.classList.add("correct");
|
||||
} else {
|
||||
graphtoolContainer.classList.add("incorrect");
|
||||
}
|
||||
}
|
||||
}
|
||||
const selects = body_div.querySelectorAll("select:not([type=hidden])");
|
||||
for (const select2 of selects) {
|
||||
const name = select2.name;
|
||||
const score = data.rh_result.answers[name].score;
|
||||
let title = "";
|
||||
if (score == 1) {
|
||||
headline = localize_correct + "!";
|
||||
correctClass = "correct";
|
||||
} else {
|
||||
headline = localize_incorrect + ".";
|
||||
correctClass = "incorrect";
|
||||
}
|
||||
select2.classList.add(correctClass);
|
||||
title = `<span class="${correctClass}">${headline}</span>`;
|
||||
feedbackSpan = document.createElement("span");
|
||||
feedbackSpan.id = `${ww_id}-${name}-feedback`;
|
||||
feedbackHeadline = document.createElement("span");
|
||||
feedbackHeadline.id = `${ww_id}-${name}-headline`;
|
||||
feedbackHeadline.textContent = headline;
|
||||
feedbackSpan.appendChild(feedbackHeadline);
|
||||
let nbSpan = document.createElement("span");
|
||||
nbSpan.classList.add("nobreak");
|
||||
select2.parentNode.insertBefore(nbSpan, select2);
|
||||
nbSpan.appendChild(select2);
|
||||
let psSpan = document.createElement("span");
|
||||
psSpan.classList.add("select-wrapper", correctClass);
|
||||
select2.parentNode.insertBefore(psSpan, select2);
|
||||
psSpan.appendChild(select2);
|
||||
let srSpan = document.createElement("span");
|
||||
srSpan.classList.add("visually-hidden");
|
||||
srSpan.appendChild(feedbackSpan);
|
||||
nbSpan.appendChild(srSpan);
|
||||
select2.setAttribute("aria-describedby", `${ww_id}-${name}-feedback`);
|
||||
srSpan.after(createFeedbackButton(`${ww_id}-${name}`, title));
|
||||
}
|
||||
}
|
||||
let iframeContents = '<!DOCTYPE html><head><script src="' + ww_domain + `/webwork2_files/node_modules/jquery/dist/jquery.min.js"><\/script><script>window.MathJax = {
|
||||
tex: {
|
||||
inlineMath: [['\\\\(','\\\\)']],
|
||||
tags: "none",
|
||||
useLabelIds: true,
|
||||
tagSide: "right",
|
||||
tagIndent: ".8em",
|
||||
packages: {'[+]': ['base', 'extpfeil', 'ams', 'amscd', 'newcommand', 'knowl']}
|
||||
},
|
||||
options: {
|
||||
ignoreHtmlClass: "tex2jax_ignore",
|
||||
processHtmlClass: "has_am",
|
||||
renderActions: {
|
||||
findScript: [10, function (doc) {
|
||||
document.querySelectorAll('script[type^="math/tex"]').forEach(function(node) {
|
||||
const display = !!node.type.match(/; *mode=display/);
|
||||
const math = new doc.options.MathItem(node.textContent, doc.inputJax[0], display);
|
||||
const text = document.createTextNode('');
|
||||
node.parentNode.replaceChild(text, node);
|
||||
math.start = {node: text, delim: '', n: 0};
|
||||
math.end = {node: text, delim: '', n: 0};
|
||||
doc.math.push(math);
|
||||
});
|
||||
}, '']
|
||||
},
|
||||
},
|
||||
chtml: {
|
||||
scale: 0.88,
|
||||
mtextInheritFont: true
|
||||
},
|
||||
loader: {
|
||||
load: ['input/asciimath', '[tex]/extpfeil', '[tex]/amscd', '[tex]/newcommand', '[pretext]/mathjaxknowl3.js'],
|
||||
paths: {pretext: "https://pretextbook.org/js/lib"},
|
||||
},
|
||||
};
|
||||
<\/script><script src="` + ww_domain + '/webwork2_files/node_modules/mathjax/es5/tex-chtml.js" id="MathJax-script" defer><\/script><script src="https://pretextbook.org/js/lib/knowl.js" defer><\/script><link rel="stylesheet" href="' + ww_domain + '/webwork2_files/node_modules/bootstrap/dist/css/bootstrap.min.css"/><script src="' + ww_domain + '/webwork2_files/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js" defer><\/script>';
|
||||
const extra_css_files = [];
|
||||
const extra_js_files = [];
|
||||
if (data.extra_css_files) data.extra_css_files.unshift(...extra_css_files);
|
||||
else data.extra_css_files = extra_css_files;
|
||||
for (const cssFile of data.extra_css_files) {
|
||||
if (/knowl/.test(cssFile.file)) continue;
|
||||
iframeContents += '<link rel="stylesheet" href="' + (cssFile.external !== "1" ? ww_domain : "") + cssFile.file + '"/>';
|
||||
}
|
||||
if (data.extra_js_files) data.extra_js_files.unshift(...extra_js_files);
|
||||
else data.extra_js_files = extra_js_files;
|
||||
for (const jsFile of data.extra_js_files) {
|
||||
if (/knowl/.test(jsFile.file)) continue;
|
||||
iframeContents += '<script src="' + (jsFile.external !== "1" ? ww_domain : "") + jsFile.file + '" ' + Object.keys(jsFile.attributes || {}).reduce((ret, key) => {
|
||||
ret += key + '="' + jsFile.attributes[key] + '" ';
|
||||
return ret;
|
||||
}, "") + "><\/script>";
|
||||
}
|
||||
iframeContents += '<link rel="stylesheet" href="https://pretextbook.org/css/0.31/pretext_add_on.css"/><link rel="stylesheet" href="https://pretextbook.org/css/0.31/knowls_default.css"/><script src="' + ww_domain + `/webwork2_files/node_modules/iframe-resizer/js/iframeResizer.contentWindow.min.js"><\/script><style>
|
||||
html { overflow-y: hidden; }
|
||||
html body { background:unset; margin: 0; }
|
||||
body { font-size: initial; line-height: initial; padding:2px; }
|
||||
.hidden-content { display: none; }
|
||||
input[type="text"], input[type="radio"], label, select {
|
||||
height: auto;
|
||||
width: auto;
|
||||
max-width: unset;
|
||||
margin: 0;
|
||||
font-size: initial;
|
||||
/*font-family: sans-serif;*/
|
||||
line-height: initial;
|
||||
}
|
||||
input[type="text"] {
|
||||
padding: 1px;
|
||||
padding-inline: 2px;
|
||||
border-width: 1px;
|
||||
border-style: inset;
|
||||
border-color: rgb(133, 133, 133);
|
||||
border-radius: 0;
|
||||
box-shadow: unset;
|
||||
transition: none;
|
||||
}
|
||||
input[type="text"]:focus, input[type="text"]:active {
|
||||
box-shadow: unset;
|
||||
border-width: 1px;
|
||||
border-style: inset;
|
||||
border-radius: 4px;
|
||||
border-color: rgb(133, 133, 133);
|
||||
outline: auto;
|
||||
}
|
||||
input[type="radio"] {
|
||||
box-sizing: border-box;
|
||||
margin-block: 3px 0;
|
||||
margin-inline: 5px 3px;
|
||||
vertical-align: unset;
|
||||
}
|
||||
span.nobreak {
|
||||
white-space: nowrap;
|
||||
}
|
||||
input[type="text"].blank, input[type="text"].correct, input[type="text"].partly-correct, input[type="text"].incorrect, select.correct, select.incorrect {
|
||||
background-size: auto 100%;
|
||||
background-position: right;
|
||||
background-repeat: no-repeat;
|
||||
padding-right: 29px;
|
||||
}
|
||||
input[type="text"].blank {
|
||||
background-color: #CDF;
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='20px' width='20px'><text x='19' y='16' fill='%230049DB' text-anchor='end'>\u26A0</text></svg>");
|
||||
}
|
||||
input[type="text"].correct, select.correct, input[type="text"].correct + span.mq-editable-field {
|
||||
background-color: #8F8;
|
||||
}
|
||||
input[type="text"].correct {
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='20px' width='20px'><text x='19' y='16' fill='%23060' text-anchor='end'>\u2713</text></svg>");
|
||||
}
|
||||
padding-left: 2rem;
|
||||
background-repeat: no-repeat;
|
||||
background-position-y: center;
|
||||
}
|
||||
input[type="text"].partly-correct, input[type="text"].partly-correct + span.mq-editable-field {
|
||||
background-color: #CDF;
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='22px' width='30px'><text x='28' y='18' fill='%235C5C00' text-anchor='end'>\u26A0</text></svg>");
|
||||
}
|
||||
input[type="text"].incorrect, select.incorrect, input[type="text"].incorrect + span.mq-editable-field {
|
||||
background-color: #DAA;
|
||||
}
|
||||
input[type="text"].incorrect {
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='22px' width='30px'><text x='28' y='18' fill='%23943D3D' text-anchor='end'>\u26A0</text></svg>");
|
||||
}
|
||||
input[type="text"].partly-correct, input[type="text"].incorrect {
|
||||
background-size: auto 70%;
|
||||
}
|
||||
label {
|
||||
padding-left: 1.8em;
|
||||
}
|
||||
label.correct::before {
|
||||
color: #060;
|
||||
content: '\u2713';
|
||||
}
|
||||
label.incorrect::before {
|
||||
color: #943D3D;
|
||||
content: '\u26A0';
|
||||
font-size: small;
|
||||
}
|
||||
label.correct::before, label.incorrect::before {
|
||||
display:inline-block;
|
||||
width: 0;
|
||||
direction: rtl;
|
||||
}
|
||||
.select-wrapper.correct::before {
|
||||
color: #060;
|
||||
content: '\u2713';
|
||||
margin-right: 2pt;
|
||||
}
|
||||
.select-wrapper.incorrect::before {
|
||||
color: #943D3D;
|
||||
content: '\u26A0';
|
||||
margin-right: 2pt;
|
||||
font-size: small;
|
||||
}
|
||||
.checkboxes-container.correct label input, .checkboxes-container.incorrect label input {
|
||||
border-radius: 2px;
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.checkboxes-container.correct label input {
|
||||
background-color: #8F8;
|
||||
border: #060 solid;
|
||||
}
|
||||
.checkboxes-container.correct label input:checked {
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='14px' width='14px'><text x='13' y='13' text-anchor='end'>\u2713</text></svg>");
|
||||
}
|
||||
.checkboxes-container.incorrect label input {
|
||||
background-color: #DAA;
|
||||
border: #943D3D solid;
|
||||
}
|
||||
.checkboxes-container.incorrect label input:checked {
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='14px' width='14px'><text x='13' y='13' text-anchor='end'>\u2718</text></svg>");
|
||||
}
|
||||
div.PGML img.image-view-elt {
|
||||
max-width:100%;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
select {
|
||||
margin: 0;
|
||||
border-width: 1px;
|
||||
border-style: inset;
|
||||
display: inline-block;
|
||||
padding-block: 1px;
|
||||
vertical-align: baseline;
|
||||
background-color: unset;
|
||||
padding-inline: 0;
|
||||
}
|
||||
.result-popover .popover-header, .result-popover .popover-content {
|
||||
text-align: center;
|
||||
}
|
||||
.result-popover .popover-header.correct {
|
||||
background-color: #8F8;
|
||||
}
|
||||
.result-popover.nocontent .popover-header {
|
||||
border-radius: calc(.3rem - 1px);
|
||||
}
|
||||
.result-popover.nocontent .popover-header::before {
|
||||
border: none;
|
||||
}
|
||||
.accordion-body.expanded {
|
||||
overflow-y: visible;
|
||||
overflow-x: clip;
|
||||
}
|
||||
.graphtool-answer-container .graphtool-graph {
|
||||
margin: 0;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
.graphtool-answer-container .graphtool-number-line {
|
||||
height: 57px;
|
||||
}
|
||||
.checkboxes-container .ww-feedback {
|
||||
position: absolute;
|
||||
left: 2px;
|
||||
}
|
||||
.graphtool-container.correct .graphtool-graph {
|
||||
background-color: #8F8;
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='20px' width='20px'><text x='19' y='16' fill='%23060' text-anchor='end'>\u2713</text></svg>");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.graphtool-container.incorrect .graphtool-graph {
|
||||
background-color: #DAA;
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='22px' width='30px'><text x='28' y='18' fill='%235C5C00' text-anchor='end'>\u26A0</text></svg>");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
</style></head><body><main class="pretext-content">` + form.outerHTML + "</main></body></html>";
|
||||
let iframe2;
|
||||
if (!action) {
|
||||
iframe2 = document.createElement("iframe");
|
||||
iframe2.style.width = "1px";
|
||||
iframe2.style.minWidth = "100%";
|
||||
iframe2.classList.add("problem-iframe");
|
||||
ww_container.querySelector(".problem-contents").classList.add("hidden-content");
|
||||
if (activate_button != null) {
|
||||
activate_button.after(iframe2);
|
||||
} else {
|
||||
ww_container.prepend(iframe2);
|
||||
}
|
||||
iFrameResize({ checkOrigin: false, scrolling: "omit", heightCalculationMethod: "min" }, iframe2);
|
||||
iframe2.addEventListener("load", () => {
|
||||
const iframeForm = iframe2.contentDocument.getElementById(ww_id + "-form");
|
||||
iframeForm.addEventListener("submit", (e) => {
|
||||
handleWW(ww_id, "check");
|
||||
e.preventDefault();
|
||||
});
|
||||
iframe2.contentDocument.querySelectorAll(".collapse.in").forEach((collapse) => collapse.classList.add("expanded"));
|
||||
iframe2.contentWindow.jQuery(".collapse").on("shown", function(e) {
|
||||
if (e.target != this) return;
|
||||
this.classList.add("expanded");
|
||||
});
|
||||
iframe2.contentWindow.jQuery(".collapse").on("hide", function(e) {
|
||||
if (e.target != this) return;
|
||||
this.classList.remove("expanded");
|
||||
});
|
||||
iframe2.contentDocument.querySelectorAll("button.ww-feedback[data-bs-content]").forEach((button) => {
|
||||
const bsPopover = new iframe2.contentWindow.bootstrap.Popover(button, {
|
||||
html: true,
|
||||
placement: "bottom",
|
||||
fallbackPlacements: [],
|
||||
trigger: "click",
|
||||
customClass: "result-popover" + (button.dataset.emptyContent ? " nocontent" : "")
|
||||
});
|
||||
bsPopover.show();
|
||||
const content = iframe2.contentDocument.getElementById(button.id.replace("-feedback-button", "-content"));
|
||||
const popover = content.parentNode.parentNode;
|
||||
bsPopover.hide();
|
||||
button.addEventListener("click", () => bsPopover.show(), { once: true });
|
||||
popover.querySelector(".popover-arrow").remove();
|
||||
const title = popover.querySelector(".popover-header");
|
||||
if (button.dataset.emptyContent) content.parentNode.remove();
|
||||
if (title.textContent == localize_correct + "!") title.classList.add("correct");
|
||||
});
|
||||
iframe2.contentWindow.MathJax.startup.promise.then(() => iframe2.contentWindow.MathJax.typesetPromise([".popover", ".popover-content"]));
|
||||
});
|
||||
} else {
|
||||
iframe2 = ww_container.querySelector(".problem-iframe");
|
||||
}
|
||||
iframe2.srcdoc = iframeContents;
|
||||
iframe2.addEventListener("load", () => {
|
||||
loader.remove();
|
||||
}, { once: true });
|
||||
ww_container.focus();
|
||||
});
|
||||
}
|
||||
function WWshowCorrect(ww_id, answers) {
|
||||
const ww_container = document.getElementById(ww_id);
|
||||
const iframe2 = ww_container.querySelector(".problem-iframe");
|
||||
const body = iframe2.contentDocument.getElementById(ww_id + "-body");
|
||||
$("body").trigger("runestone_show_correct", {
|
||||
"ww_id": ww_id,
|
||||
"answers": answers
|
||||
});
|
||||
let inputs = body.querySelectorAll("input:not([type=hidden])");
|
||||
for (const input of inputs) {
|
||||
const name = input.name;
|
||||
const span_id = `${ww_id}-${name}-correct`;
|
||||
if (input.type == "text" && answers[name] && !iframe2.contentDocument.getElementById(span_id)) {
|
||||
const input_id = input.id;
|
||||
const mq_span = iframe2.contentDocument.getElementById(`mq-answer-${input_id}`);
|
||||
if (mq_span) {
|
||||
mq_span.style.display = "none";
|
||||
}
|
||||
const feedbackButton = iframe2.contentDocument.getElementById(`${ww_id}-${name}-feedback-button`);
|
||||
if (feedbackButton) {
|
||||
iframe2.contentWindow.bootstrap.Popover.getInstance(feedbackButton)?.hide();
|
||||
feedbackButton.remove();
|
||||
}
|
||||
label = iframe2.contentDocument.getElementById(`${span_id}-label`);
|
||||
if (label) {
|
||||
label.parentElement.insertBefore(input, label);
|
||||
label.remove();
|
||||
}
|
||||
input.type = "hidden";
|
||||
const correct_ans_text = iframe2.contentDocument.createElement("div");
|
||||
correct_ans_text.innerHTML = answers[name].correct_ans;
|
||||
input.value = correct_ans_text.textContent;
|
||||
const show_span = iframe2.contentDocument.createElement("span");
|
||||
show_span.id = span_id;
|
||||
show_span.appendChild(answers[name].correct_ans_latex_string ? iframe2.contentDocument.createTextNode("\\(" + answers[name].correct_ans_latex_string + "\\)") : iframe2.contentDocument.createTextNode(answers[name].correct_ans));
|
||||
input.parentElement.insertBefore(show_span, input);
|
||||
}
|
||||
if (input.type.toUpperCase() == "RADIO" && answers[name]) {
|
||||
const feedbackButton = iframe2.contentDocument.getElementById(`${ww_id}-${name}-feedback-button`);
|
||||
if (feedbackButton) {
|
||||
iframe2.contentWindow.bootstrap.Popover.getInstance(feedbackButton)?.hide();
|
||||
feedbackButton.remove();
|
||||
}
|
||||
const correct_ans = answers[name].correct_choice || answers[name].correct_ans;
|
||||
if (input.value == correct_ans) {
|
||||
input.checked = true;
|
||||
} else {
|
||||
input.checked = false;
|
||||
}
|
||||
}
|
||||
if (input.type.toUpperCase() == "CHECKBOX" && answers[name]) {
|
||||
const correct_choices = answers[name].correct_choices;
|
||||
if (correct_choices.includes(input.value)) {
|
||||
input.checked = true;
|
||||
} else {
|
||||
input.checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
const hiddenInputs = body.querySelectorAll("input[type=hidden]");
|
||||
for (const input of hiddenInputs) {
|
||||
const name = input.name;
|
||||
if (!input.nextElementSibling) continue;
|
||||
const graphtoolContainer = input.nextElementSibling.nextElementSibling;
|
||||
if (graphtoolContainer && answers[name] && graphtoolContainer.classList.contains("graphtool-container")) {
|
||||
const feedbackButton = iframe2.contentDocument.getElementById(`${ww_id}-${name}-feedback-button`);
|
||||
if (feedbackButton) {
|
||||
iframe2.contentWindow.bootstrap.Popover.getInstance(feedbackButton)?.hide();
|
||||
feedbackButton.remove();
|
||||
}
|
||||
const correct_ans_div = iframe2.contentDocument.createElement("div");
|
||||
input.parentElement.insertBefore(correct_ans_div, graphtoolContainer);
|
||||
graphtoolContainer.style.display = "none";
|
||||
input.value = answers[name].correct_ans;
|
||||
iframe2.contentWindow.jQuery(correct_ans_div).html(answers[name].correct_ans_latex_string);
|
||||
const script = iframe2.contentDocument.createElement("script");
|
||||
script.textContent = correct_ans_div.querySelector("script").textContent.replace('\nwindow.addEventListener("DOMContentLoaded",', "(").replace(/;\n$/, "();");
|
||||
iframe2.contentDocument.body.appendChild(script);
|
||||
}
|
||||
}
|
||||
let selects = body.querySelectorAll("select:not([type=hidden])");
|
||||
for (const select of selects) {
|
||||
const name = select.name;
|
||||
const span_id = `${ww_id}-${name}-correct`;
|
||||
if (answers[name] && !iframe2.contentDocument.getElementById(span_id)) {
|
||||
const feedbackButton = iframe2.contentDocument.getElementById(`${ww_id}-${name}-feedback-button`);
|
||||
if (feedbackButton) {
|
||||
iframe2.contentWindow.bootstrap.Popover.getInstance(feedbackButton)?.hide();
|
||||
feedbackButton.remove();
|
||||
}
|
||||
select.style.display = "none";
|
||||
select.value = answers[name].correct_ans;
|
||||
const show_span = iframe2.contentDocument.createElement("span");
|
||||
show_span.id = span_id;
|
||||
show_span.appendChild(answers[name].correct_ans_latex_string ? iframe2.contentDocument.createTextNode("\\(" + answers[name].correct_ans_latex_string + "\\)") : iframe2.contentDocument.createTextNode(answers[name].correct_ans));
|
||||
select.parentElement.insertBefore(show_span, select);
|
||||
}
|
||||
}
|
||||
const mathjaxTypesetScript = iframe2.contentDocument.createElement("script");
|
||||
mathjaxTypesetScript.textContent = "MathJax.startup.promise.then(() => MathJax.typesetPromise([document.body]));";
|
||||
iframe2.contentDocument.body.appendChild(mathjaxTypesetScript);
|
||||
}
|
||||
function resetWW(ww_id) {
|
||||
const ww_container = document.getElementById(ww_id);
|
||||
const activate_button = document.getElementById(ww_id + "-button");
|
||||
ww_container.dataset.current_seed = ww_container.dataset.seed;
|
||||
iframe = ww_container.querySelector(".problem-iframe");
|
||||
iframe.remove();
|
||||
ww_container.querySelector(".problem-contents").classList.remove("hidden-content");
|
||||
ww_container.querySelector(".problem-buttons.webwork").remove();
|
||||
ww_container.querySelector(".problem-buttons").classList.remove("hidden-content");
|
||||
if (activate_button != null) {
|
||||
activate_button.classList.remove("hidden-content");
|
||||
}
|
||||
;
|
||||
}
|
||||
function adjustSrcHrefs(container, ww_domain) {
|
||||
container.querySelectorAll("[href]").forEach((node) => {
|
||||
const href = node.attributes.href.value;
|
||||
if (href !== "#" && !href.match(/^[a-z]+:\/\//i)) node.href = ww_domain + "/" + href;
|
||||
});
|
||||
container.querySelectorAll("[src]").forEach((node) => {
|
||||
node.src = new URL(node.attributes.src.value, ww_domain).href;
|
||||
});
|
||||
}
|
||||
function translateHintSol(ww_id, body_div, ww_domain, b_ptx_has_hint, b_ptx_has_solution, hint_label_text, solution_label_text) {
|
||||
const hintSols = body_div.querySelectorAll('.knowl[data-type="hint"],.knowl[data-type="solution"]');
|
||||
let solutionlikewrapper;
|
||||
for (const hintSol of hintSols) {
|
||||
const hintsolp = hintSol.parentNode;
|
||||
if (!hintsolp) continue;
|
||||
const hintsolpp = hintsolp.parentNode;
|
||||
const hintSolType = hintSol.dataset.type;
|
||||
if (hintsolpp.querySelectorAll(".webwork.solutions").length == 0) {
|
||||
solutionlikewrapper = document.createElement("div");
|
||||
solutionlikewrapper.classList.add("webwork", "solutions");
|
||||
hintsolpp.insertBefore(solutionlikewrapper, hintsolp);
|
||||
}
|
||||
if (hintSolType == "solution" && !b_ptx_has_solution || hintSolType == "hint" && !b_ptx_has_hint)
|
||||
continue;
|
||||
const knowlDetails = document.createElement("details");
|
||||
knowlDetails.classList.add(hintSolType);
|
||||
knowlDetails.classList.add("solution-like");
|
||||
knowlDetails.classList.add("born-hidden-knowl");
|
||||
const knowlSummary = document.createElement("summary");
|
||||
const summaryLabel = document.createElement("span");
|
||||
summaryLabel.classList.add("type");
|
||||
summaryLabel.innerHTML = hintSolType == "hint" ? hint_label_text : solution_label_text;
|
||||
knowlSummary.appendChild(summaryLabel);
|
||||
knowlDetails.appendChild(knowlSummary);
|
||||
const knowlContents = document.createElement("div");
|
||||
knowlContents.classList.add(hintSolType);
|
||||
knowlContents.classList.add("solution-like");
|
||||
knowlContents.innerHTML = hintsolp.firstElementChild.dataset.knowlContents;
|
||||
knowlDetails.appendChild(knowlContents);
|
||||
adjustSrcHrefs(knowlContents, ww_domain);
|
||||
solutionlikewrapper.appendChild(knowlDetails);
|
||||
}
|
||||
for (const hintSol of hintSols) {
|
||||
hintSol.parentNode?.remove();
|
||||
}
|
||||
}
|
||||
function cloneAttributes(target, source) {
|
||||
[...source.attributes].forEach((attr) => {
|
||||
target.setAttribute(attr.nodeName, attr.nodeValue);
|
||||
});
|
||||
}
|
||||
function createFeedbackButton(id, title, content) {
|
||||
const feedbackButton = document.createElement("button");
|
||||
feedbackButton.dataset.bsTitle = title;
|
||||
feedbackButton.dataset.bsContent = `<div id="${id}-content">${content || ""}</div>`;
|
||||
if (!content) feedbackButton.dataset.emptyContent = "1";
|
||||
const contentSpan = document.createElement("span");
|
||||
contentSpan.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" fill="none" stroke-width="2" viewBox="0 0 24 24" color="#000000"><path stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M12 11.5v5M12 7.51l.01-.011M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10Z"></path></svg>`;
|
||||
feedbackButton.appendChild(contentSpan);
|
||||
feedbackButton.type = "button";
|
||||
feedbackButton.classList.add("ww-feedback");
|
||||
feedbackButton.id = `${id}-feedback-button`;
|
||||
feedbackButton.dataset.bsToggle = "popover";
|
||||
return feedbackButton;
|
||||
}
|
||||
function webworkSeedHash(string) {
|
||||
var hash = 0, i, chr;
|
||||
if (string.length === 0) return hash;
|
||||
for (i = 0; i < string.length; i++) {
|
||||
chr = string.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + chr;
|
||||
hash |= 0;
|
||||
}
|
||||
return Math.abs(hash);
|
||||
}
|
||||
;
|
||||
//# sourceMappingURL=pretext-webwork.js.map
|
||||
@@ -0,0 +1,885 @@
|
||||
function handleWW(ww_id, action) {
|
||||
const ww_container = document.getElementById(ww_id);
|
||||
const ww_domain = ww_container.dataset.domain;
|
||||
const ww_problemSource = ww_container.dataset.problemsource;
|
||||
const ww_sourceFilePath = ww_container.dataset.sourcefilepath;
|
||||
const ww_course_id = ww_container.dataset.courseid;
|
||||
const ww_user_id = ww_container.dataset.userid;
|
||||
const ww_course_password = ww_container.dataset.coursepassword;
|
||||
const localize_correct = ww_container.dataset.localizeCorrect || "Correct";
|
||||
const localize_incorrect = ww_container.dataset.localizeIncorrect || "Incorrect";
|
||||
const localize_blank = ww_container.dataset.localizeBlank || "Blank";
|
||||
const localize_submit = ww_container.dataset.localizeSubmit || "Submit";
|
||||
const localize_check_responses = ww_container.dataset.localizeCheckResponses || "Check Responses";
|
||||
const localize_reveal = ww_container.dataset.localizeReveal || "Reveal";
|
||||
const localize_randomize = ww_container.dataset.localizeRandomize || "Randomize";
|
||||
const localize_reset = ww_container.dataset.localizeReset || "Reset";
|
||||
const runestone_logged_in = typeof eBookConfig !== "undefined" && eBookConfig.username !== "";
|
||||
const activate_button = document.getElementById(ww_id + "-button");
|
||||
if (!action) {
|
||||
ww_container.dataset.current_seed = ww_container.dataset.seed;
|
||||
if (runestone_logged_in) {
|
||||
ww_container.dataset.current_seed = webworkSeedHash(eBookConfig.username + ww_container.dataset.current_seed);
|
||||
}
|
||||
} else if (action == "randomize") ww_container.dataset.current_seed = Number(ww_container.dataset.current_seed) + 100;
|
||||
let loader = document.createElement("div");
|
||||
loader.style.position = "absolute";
|
||||
loader.style.left = 0;
|
||||
loader.style.top = 0;
|
||||
loader.style.backgroundColor = "rgba(0.2, 0.2, 0.2, 0.4)";
|
||||
loader.style.color = "white";
|
||||
loader.style.width = "100%";
|
||||
loader.style.height = "100%";
|
||||
loader.style.display = "flex";
|
||||
loader.style.alignItems = "center";
|
||||
loader.style.justifyContent = "center";
|
||||
loader.style.marginTop = "0";
|
||||
loader.tabIndex = -1;
|
||||
const loaderText = document.createElement("span");
|
||||
loaderText.textContent = "Loading";
|
||||
loaderText.style.fontSize = "2rem";
|
||||
loader.appendChild(loaderText);
|
||||
ww_container.appendChild(loader);
|
||||
loader.focus();
|
||||
if (!action) {
|
||||
ww_container.dataset.hasHint = ww_container.getElementsByClassName("hint").length > 0;
|
||||
ww_container.dataset.hasSolution = ww_container.getElementsByClassName("solution").length > 0;
|
||||
ww_container.dataset.hasAnswer = ww_container.getElementsByClassName("answer").length > 0;
|
||||
ww_container.dataset.hintLabelText = ww_container.dataset.hasHint == "true" ? ww_container.querySelectorAll(".hint-knowl span.type, details.hint span.type")[0].textContent : "Hint";
|
||||
ww_container.dataset.solutionLabelText = ww_container.dataset.hasSolution == "true" ? ww_container.querySelectorAll(".solution-knowl span.type, details.solution span.type")[0].textContent : "Solution";
|
||||
ww_container.tabIndex = -1;
|
||||
}
|
||||
let url;
|
||||
if (action == "check") {
|
||||
const iframe2 = ww_container.querySelector(".problem-iframe");
|
||||
const formData = new FormData(iframe2.contentDocument.getElementById(ww_id + "-form"));
|
||||
const params = new URLSearchParams(formData);
|
||||
url = new URL(ww_domain + "/webwork2/html2xml?" + params.toString());
|
||||
url.searchParams.append("answersSubmitted", "1");
|
||||
url.searchParams.append("WWsubmit", "1");
|
||||
} else {
|
||||
url = new URL(ww_domain + "/webwork2/html2xml");
|
||||
url.searchParams.append("problemSeed", ww_container.dataset.current_seed);
|
||||
if (ww_problemSource) url.searchParams.append("problemSource", ww_problemSource);
|
||||
else if (ww_sourceFilePath) url.searchParams.append("sourceFilePath", ww_sourceFilePath);
|
||||
url.searchParams.append("answersSubmitted", "0");
|
||||
url.searchParams.append("displayMode", "MathJax");
|
||||
url.searchParams.append("courseID", ww_course_id);
|
||||
url.searchParams.append("userID", ww_user_id);
|
||||
url.searchParams.append("course_password", ww_course_password);
|
||||
url.searchParams.append("outputformat", "raw");
|
||||
url.searchParams.append("showSolutions", ww_container.dataset.hasSolution == "true" ? "1" : "0");
|
||||
url.searchParams.append("showHints", ww_container.dataset.hasHint == "true" ? "1" : "0");
|
||||
url.searchParams.append("problemUUID", ww_id);
|
||||
}
|
||||
$.getJSON(url.toString(), (data) => {
|
||||
const form = document.createElement("form");
|
||||
form.id = ww_id + "-form";
|
||||
const body_div = document.createElement("div");
|
||||
body_div.id = ww_id + "-body";
|
||||
body_div.classList.add("exercise", "exercise-like");
|
||||
body_div.lang = data.lang;
|
||||
body_div.dir = data.dir;
|
||||
body_div.innerHTML = data.rh_result.text;
|
||||
for (const tag_name of ["h6", "h5", "h4", "h3", "h2", "h1"]) {
|
||||
const headings = body_div.getElementsByTagName(tag_name);
|
||||
for (heading of headings) {
|
||||
const new_heading = document.createElement("h6");
|
||||
new_heading.innerHTML = heading.innerHTML;
|
||||
cloneAttributes(new_heading, heading);
|
||||
new_heading.classList.add("webwork-part");
|
||||
heading.replaceWith(new_heading);
|
||||
}
|
||||
}
|
||||
adjustSrcHrefs(body_div, ww_domain);
|
||||
translateHintSol(
|
||||
ww_id,
|
||||
body_div,
|
||||
ww_domain,
|
||||
ww_container.dataset.hasHint == "true",
|
||||
ww_container.dataset.hasSolution == "true",
|
||||
ww_container.dataset.hintLabelText,
|
||||
ww_container.dataset.solutionLabelText
|
||||
);
|
||||
if (runestone_logged_in) {
|
||||
const answersObject = wwList[ww_id.replace(/-ww-rs$/, "")].answers ? wwList[ww_id.replace(/-ww-rs$/, "")].answers : { "answers": [], "mqAnswers": [] };
|
||||
const mqAnswers = answersObject.mqAnswers;
|
||||
for (const mqAnswer in mqAnswers) {
|
||||
const mqInput = body_div.querySelector("input[id=" + mqAnswer + "]");
|
||||
if (mqInput && mqInput.value == "") {
|
||||
mqInput.setAttribute("value", mqAnswers[mqAnswer]);
|
||||
}
|
||||
}
|
||||
const answers2 = answersObject.answers;
|
||||
for (const answer in answers2) {
|
||||
const input = body_div.querySelector("input[id=" + answer + "]");
|
||||
if (input && input.value == "") {
|
||||
input.setAttribute("value", answers2[answer]);
|
||||
}
|
||||
if (input && input.type.toUpperCase() == "RADIO") {
|
||||
const buttons = body_div.querySelectorAll("input[name=" + answer + "]");
|
||||
for (const button of buttons) {
|
||||
if (button.value == answers2[answer]) {
|
||||
button.setAttribute("checked", "checked");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (input && input.type.toUpperCase() == "CHECKBOX") {
|
||||
const checkboxes = body_div.querySelectorAll("input[name=" + answer + "]");
|
||||
for (const checkbox of checkboxes) {
|
||||
let checkbox_regex = new RegExp("(\\[|, )" + checkbox.value + "(, |\\])");
|
||||
if (answers2[answer].match(checkbox_regex)) {
|
||||
checkbox.setAttribute("checked", "checked");
|
||||
}
|
||||
}
|
||||
}
|
||||
var select = body_div.querySelector("select[id=" + answer + "]");
|
||||
if (select && answers2[answer]) {
|
||||
let this_answer = answers2[answer];
|
||||
if (/^\\text\{.*\}$/.test(this_answer)) {
|
||||
this_answer = this_answer.match(/^\\text\{(.*)\}$/)[1];
|
||||
}
|
||||
;
|
||||
let quote_escaped_answer = this_answer.replace(/"/g, '\\"');
|
||||
const option = body_div.querySelector(`select[id="${answer}"] option[value="${quote_escaped_answer}"]`);
|
||||
if (option) {
|
||||
option.setAttribute("selected", "selected");
|
||||
}
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
form.appendChild(body_div);
|
||||
const wwInputs = {
|
||||
problemSeed: data.inputs_ref.problemSeed,
|
||||
problemUUID: data.inputs_ref.problemUUID,
|
||||
psvn: data.inputs_ref.psvn,
|
||||
courseName: ww_course_id,
|
||||
courseID: ww_course_id,
|
||||
userID: ww_user_id,
|
||||
course_password: ww_course_password,
|
||||
displayMode: "MathJax",
|
||||
session_key: data.rh_result.session_key,
|
||||
outputformat: "raw",
|
||||
language: data.formLanguage,
|
||||
showSummary: data.showSummary,
|
||||
// note ww_container.dataset.hasSolution is a string, possibly 'false' which is true
|
||||
showSolutions: ww_container.dataset.hasSolution == "true" ? "1" : "0",
|
||||
showHints: ww_container.dataset.hasHint == "true" ? "1" : "0",
|
||||
forcePortNumber: data.forcePortNumber
|
||||
};
|
||||
if (ww_sourceFilePath) wwInputs.sourceFilePath = ww_sourceFilePath;
|
||||
else if (ww_problemSource) wwInputs.problemSource = ww_problemSource;
|
||||
for (const wwInputName of Object.keys(wwInputs)) {
|
||||
const input = document.createElement("input");
|
||||
input.type = "hidden";
|
||||
input.name = wwInputName;
|
||||
input.value = wwInputs[wwInputName];
|
||||
form.appendChild(input);
|
||||
}
|
||||
const answers = {};
|
||||
Object.keys(data.rh_result.answers).forEach(function(id) {
|
||||
answers[id] = {};
|
||||
}, data.rh_result.answers);
|
||||
if (ww_container.dataset.hasAnswer == "true") {
|
||||
Object.keys(data.rh_result.answers).forEach(function(id) {
|
||||
answers[id] = {
|
||||
correct_ans: this[id].correct_ans,
|
||||
correct_ans_latex_string: this[id].correct_ans_latex_string,
|
||||
correct_choice: this[id].correct_choice,
|
||||
correct_choices: this[id].correct_choices
|
||||
};
|
||||
}, data.rh_result.answers);
|
||||
}
|
||||
let buttonContainer = ww_container.querySelector(".problem-buttons.webwork");
|
||||
if (!buttonContainer) {
|
||||
ww_container.querySelector(".problem-buttons").classList.add("hidden-content");
|
||||
if (activate_button != null) {
|
||||
activate_button.classList.add("hidden-content");
|
||||
}
|
||||
;
|
||||
buttonContainer = document.createElement("div");
|
||||
buttonContainer.classList.add("problem-buttons", "webwork");
|
||||
if (activate_button != null) {
|
||||
activate_button.after(buttonContainer);
|
||||
} else {
|
||||
ww_container.prepend(buttonContainer);
|
||||
}
|
||||
const check = document.createElement("button");
|
||||
check.type = "button";
|
||||
check.id = ww_id + "-check";
|
||||
check.style.marginRight = "0.25rem";
|
||||
check.classList.add("webwork-button");
|
||||
const answerCount = body_div.querySelectorAll("input:not([type=hidden])").length + body_div.querySelectorAll("select:not([type=hidden])").length;
|
||||
check.textContent = runestone_logged_in ? localize_submit : localize_check_responses;
|
||||
check.addEventListener("click", () => handleWW(ww_id, "check"));
|
||||
buttonContainer.appendChild(check);
|
||||
if (ww_container.dataset.hasAnswer == "true") {
|
||||
const correct = document.createElement("button");
|
||||
correct.classList.add("show-correct", "webwork-button");
|
||||
correct.type = "button";
|
||||
correct.style.marginRight = "0.25rem";
|
||||
correct.textContent = localize_reveal;
|
||||
correct.addEventListener("click", () => WWshowCorrect(ww_id, answers));
|
||||
buttonContainer.appendChild(correct);
|
||||
}
|
||||
const randomize = document.createElement("button");
|
||||
randomize.type = "button";
|
||||
randomize.classList.add("webwork-button");
|
||||
randomize.style.marginRight = "0.25rem";
|
||||
randomize.textContent = localize_randomize;
|
||||
randomize.addEventListener("click", () => handleWW(ww_id, "randomize"));
|
||||
buttonContainer.appendChild(randomize);
|
||||
const reset = document.createElement("button");
|
||||
reset.type = "button";
|
||||
reset.classList.add("webwork-button");
|
||||
reset.textContent = localize_reset;
|
||||
reset.addEventListener("click", () => resetWW(ww_id));
|
||||
buttonContainer.appendChild(reset);
|
||||
} else {
|
||||
if (ww_container.dataset.hasAnswer == "true") {
|
||||
const correct = buttonContainer.querySelector(".show-correct");
|
||||
const correctNew = correct.cloneNode(true);
|
||||
correctNew.addEventListener("click", () => WWshowCorrect(ww_id, answers));
|
||||
correct.replaceWith(correctNew);
|
||||
}
|
||||
}
|
||||
if (action == "check") {
|
||||
$("body").trigger("runestone_ww_check", data);
|
||||
const inputs = body_div.querySelectorAll("input:not([type=hidden])");
|
||||
for (const input of inputs) {
|
||||
const name = input.name;
|
||||
if (input.type == "text" && answers[name]) {
|
||||
const score = data.rh_result.answers[name].score;
|
||||
let headline2 = "";
|
||||
let correctClass2 = "";
|
||||
if (score >= 1) {
|
||||
headline2 = localize_correct + "!";
|
||||
correctClass2 = "correct";
|
||||
} else if (score > 0 && score < 1) {
|
||||
headline2 = `${Math.round(score * 100)}% ${localize_correct}.`;
|
||||
correctClass2 = "partly-correct";
|
||||
} else if (data.rh_result.answers[name].student_ans == "") {
|
||||
headline2 = localize_blank + ".";
|
||||
correctClass2 = "blank";
|
||||
} else if (score <= 0) {
|
||||
headline2 = localize_incorrect + ".";
|
||||
correctClass2 = "incorrect";
|
||||
}
|
||||
let title = `<span class="${correctClass2}">${headline2}</span>`;
|
||||
let message = data.rh_result.answers[name].ans_message ? data.rh_result.answers[name].ans_message : "";
|
||||
input.classList.add(correctClass2);
|
||||
feedbackSpan = document.createElement("span");
|
||||
feedbackSpan.id = `${ww_id}-${name}-feedback`;
|
||||
feedbackHeadline = document.createElement("span");
|
||||
feedbackHeadline.id = `${ww_id}-${name}-headline`;
|
||||
feedbackHeadline.textContent = headline2;
|
||||
feedbackSpan.appendChild(feedbackHeadline);
|
||||
feedbackMessage = document.createElement("span");
|
||||
feedbackMessage.id = `${ww_id}-${name}-message`;
|
||||
feedbackMessage.textContent = message;
|
||||
feedbackSpan.appendChild(feedbackMessage);
|
||||
let nbSpan = document.createElement("span");
|
||||
nbSpan.classList.add("nobreak");
|
||||
input.parentNode.insertBefore(nbSpan, input);
|
||||
nbSpan.appendChild(input);
|
||||
let srSpan = document.createElement("span");
|
||||
srSpan.classList.add("visually-hidden");
|
||||
srSpan.appendChild(feedbackSpan);
|
||||
nbSpan.appendChild(srSpan);
|
||||
input.setAttribute("aria-describedby", `${ww_id}-${name}-feedback`);
|
||||
srSpan.after(createFeedbackButton(`${ww_id}-${name}`, title, message));
|
||||
}
|
||||
if (input.type.toUpperCase() == "RADIO" && answers[name]) {
|
||||
const score = data.rh_result.answers[name].score;
|
||||
const student_ans = data.rh_result.answers[name].student_value || data.rh_result.answers[name].student_ans;
|
||||
const correct_ans = data.rh_result.answers[name].correct_choice || data.rh_result.answers[name].correct_ans;
|
||||
if (input.value == student_ans) {
|
||||
if (score == 1) {
|
||||
input.parentNode.classList.add("correct");
|
||||
} else {
|
||||
input.parentNode.classList.add("incorrect");
|
||||
}
|
||||
const feedbackButton = createFeedbackButton(
|
||||
`${ww_id}-${name}`,
|
||||
student_ans == correct_ans ? `<span class="correct">${localize_correct}</span>` : `<span class="incorrect">${localize_incorrect}.</span>`
|
||||
);
|
||||
feedbackButton.style.marginRight = "0.25rem";
|
||||
input.after(feedbackButton);
|
||||
}
|
||||
}
|
||||
if (input.type.toUpperCase() == "CHECKBOX" && answers[name]) {
|
||||
const score = data.rh_result.answers[name].score;
|
||||
const student_ans = data.rh_result.answers[name].student_ans;
|
||||
const correct_ans = data.rh_result.answers[name].correct_ans;
|
||||
if (input.value == data.rh_result.answers[name].firstElement) {
|
||||
const checkbox_div = input.parentNode.parentNode;
|
||||
if (score == 1) {
|
||||
checkbox_div.classList.add("correct");
|
||||
} else {
|
||||
checkbox_div.classList.add("incorrect");
|
||||
}
|
||||
const feedbackButton = createFeedbackButton(
|
||||
`${ww_id}-${name}`,
|
||||
student_ans == correct_ans ? `<span class="correct">${localize_correct}</span>` : `<span class="incorrect">${localize_incorrect}.</span>`
|
||||
);
|
||||
checkbox_div.insertBefore(feedbackButton, checkbox_div.firstChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
const hiddenInputs = body_div.querySelectorAll("input[type=hidden]");
|
||||
for (const input of hiddenInputs) {
|
||||
const name = input.name;
|
||||
if (!input.nextElementSibling) continue;
|
||||
const graphtoolContainer = input.nextElementSibling.nextElementSibling;
|
||||
if (graphtoolContainer && answers[name] && graphtoolContainer.classList.contains("graphtool-container")) {
|
||||
graphtoolContainer.style.position = "relative";
|
||||
const score = data.rh_result.answers[name].score;
|
||||
let title = "";
|
||||
if (score == 1) {
|
||||
title = `<span class="correct">${localize_correct}</span>`;
|
||||
} else if (score > 0 && score < 1) {
|
||||
title = `<span class="partly-correct">${Math.round(score * 100)}% ${localize_correct}.</span>`;
|
||||
} else if (data.rh_result.answers[name].student_ans == "") {
|
||||
continue;
|
||||
} else if (score == 0) {
|
||||
title = `<span class="incorrect">${localize_incorrect}.</span>`;
|
||||
}
|
||||
const feedbackButton = createFeedbackButton(`${ww_id}-${name}`, title, data.rh_result.answers[name].ans_message);
|
||||
feedbackButton.style.position = "absolute";
|
||||
feedbackButton.style.left = "100%";
|
||||
feedbackButton.style.top = 0;
|
||||
feedbackButton.style.marginLeft = "0.5rem";
|
||||
feedbackButton.dataset.bsContainer = "body";
|
||||
graphtoolContainer.appendChild(feedbackButton);
|
||||
if (score == 1) {
|
||||
graphtoolContainer.classList.add("correct");
|
||||
} else {
|
||||
graphtoolContainer.classList.add("incorrect");
|
||||
}
|
||||
}
|
||||
}
|
||||
const selects = body_div.querySelectorAll("select:not([type=hidden])");
|
||||
for (const select2 of selects) {
|
||||
const name = select2.name;
|
||||
const score = data.rh_result.answers[name].score;
|
||||
let title = "";
|
||||
if (score == 1) {
|
||||
headline = localize_correct + "!";
|
||||
correctClass = "correct";
|
||||
} else {
|
||||
headline = localize_incorrect + ".";
|
||||
correctClass = "incorrect";
|
||||
}
|
||||
select2.classList.add(correctClass);
|
||||
title = `<span class="${correctClass}">${headline}</span>`;
|
||||
feedbackSpan = document.createElement("span");
|
||||
feedbackSpan.id = `${ww_id}-${name}-feedback`;
|
||||
feedbackHeadline = document.createElement("span");
|
||||
feedbackHeadline.id = `${ww_id}-${name}-headline`;
|
||||
feedbackHeadline.textContent = headline;
|
||||
feedbackSpan.appendChild(feedbackHeadline);
|
||||
let nbSpan = document.createElement("span");
|
||||
nbSpan.classList.add("nobreak");
|
||||
select2.parentNode.insertBefore(nbSpan, select2);
|
||||
nbSpan.appendChild(select2);
|
||||
let psSpan = document.createElement("span");
|
||||
psSpan.classList.add("select-wrapper", correctClass);
|
||||
select2.parentNode.insertBefore(psSpan, select2);
|
||||
psSpan.appendChild(select2);
|
||||
let srSpan = document.createElement("span");
|
||||
srSpan.classList.add("visually-hidden");
|
||||
srSpan.appendChild(feedbackSpan);
|
||||
nbSpan.appendChild(srSpan);
|
||||
select2.setAttribute("aria-describedby", `${ww_id}-${name}-feedback`);
|
||||
srSpan.after(createFeedbackButton(`${ww_id}-${name}`, title));
|
||||
}
|
||||
}
|
||||
let iframeContents = '<!DOCTYPE html><head><script src="' + ww_domain + `/webwork2_files/node_modules/jquery/dist/jquery.min.js"><\/script><script>window.MathJax = {
|
||||
tex: {
|
||||
inlineMath: [['\\\\(','\\\\)']],
|
||||
tags: "none",
|
||||
useLabelIds: true,
|
||||
tagSide: "right",
|
||||
tagIndent: ".8em",
|
||||
packages: {'[+]': ['base', 'extpfeil', 'ams', 'amscd', 'newcommand', 'knowl']}
|
||||
},
|
||||
options: {
|
||||
ignoreHtmlClass: "tex2jax_ignore",
|
||||
processHtmlClass: "has_am",
|
||||
renderActions: {
|
||||
findScript: [10, function (doc) {
|
||||
document.querySelectorAll('script[type^="math/tex"]').forEach(function(node) {
|
||||
const display = !!node.type.match(/; *mode=display/);
|
||||
const math = new doc.options.MathItem(node.textContent, doc.inputJax[0], display);
|
||||
const text = document.createTextNode('');
|
||||
node.parentNode.replaceChild(text, node);
|
||||
math.start = {node: text, delim: '', n: 0};
|
||||
math.end = {node: text, delim: '', n: 0};
|
||||
doc.math.push(math);
|
||||
});
|
||||
}, '']
|
||||
},
|
||||
},
|
||||
chtml: {
|
||||
scale: 0.88,
|
||||
mtextInheritFont: true
|
||||
},
|
||||
loader: {
|
||||
load: ['input/asciimath', '[tex]/extpfeil', '[tex]/amscd', '[tex]/newcommand', '[pretext]/mathjaxknowl3.js'],
|
||||
paths: {pretext: "https://pretextbook.org/js/lib"},
|
||||
},
|
||||
};
|
||||
<\/script><script src="` + ww_domain + '/webwork2_files/node_modules/mathjax/es5/tex-chtml.js" id="MathJax-script" defer><\/script><script src="https://pretextbook.org/js/lib/knowl.js" defer><\/script><link rel="stylesheet" href="' + ww_domain + '/webwork2_files/node_modules/bootstrap/dist/css/bootstrap.min.css"/><script src="' + ww_domain + '/webwork2_files/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js" defer><\/script>';
|
||||
const extra_css_files = [];
|
||||
const extra_js_files = [];
|
||||
if (data.extra_css_files) data.extra_css_files.unshift(...extra_css_files);
|
||||
else data.extra_css_files = extra_css_files;
|
||||
for (const cssFile of data.extra_css_files) {
|
||||
if (/knowl/.test(cssFile.file)) continue;
|
||||
iframeContents += '<link rel="stylesheet" href="' + (cssFile.external !== "1" ? ww_domain : "") + cssFile.file + '"/>';
|
||||
}
|
||||
if (data.extra_js_files) data.extra_js_files.unshift(...extra_js_files);
|
||||
else data.extra_js_files = extra_js_files;
|
||||
for (const jsFile of data.extra_js_files) {
|
||||
if (/knowl/.test(jsFile.file)) continue;
|
||||
iframeContents += '<script src="' + (jsFile.external !== "1" ? ww_domain : "") + jsFile.file + '" ' + Object.keys(jsFile.attributes || {}).reduce((ret, key) => {
|
||||
ret += key + '="' + jsFile.attributes[key] + '" ';
|
||||
return ret;
|
||||
}, "") + "><\/script>";
|
||||
}
|
||||
iframeContents += '<link rel="stylesheet" href="https://pretextbook.org/css/0.31/pretext_add_on.css"/><link rel="stylesheet" href="https://pretextbook.org/css/0.31/knowls_default.css"/><script src="' + ww_domain + `/webwork2_files/node_modules/iframe-resizer/js/iframeResizer.contentWindow.min.js"><\/script><style>
|
||||
html { overflow-y: hidden; }
|
||||
html body { background:unset; margin: 0; }
|
||||
body { font-size: initial; line-height: initial; padding:2px; }
|
||||
.hidden-content { display: none; }
|
||||
input[type="text"], input[type="radio"], label, select {
|
||||
height: auto;
|
||||
width: auto;
|
||||
max-width: unset;
|
||||
margin: 0;
|
||||
font-size: initial;
|
||||
/*font-family: sans-serif;*/
|
||||
line-height: initial;
|
||||
}
|
||||
input[type="text"] {
|
||||
padding: 1px;
|
||||
padding-inline: 2px;
|
||||
border-width: 1px;
|
||||
border-style: inset;
|
||||
border-color: rgb(133, 133, 133);
|
||||
border-radius: 0;
|
||||
box-shadow: unset;
|
||||
transition: none;
|
||||
}
|
||||
input[type="text"]:focus, input[type="text"]:active {
|
||||
box-shadow: unset;
|
||||
border-width: 1px;
|
||||
border-style: inset;
|
||||
border-radius: 4px;
|
||||
border-color: rgb(133, 133, 133);
|
||||
outline: auto;
|
||||
}
|
||||
input[type="radio"] {
|
||||
box-sizing: border-box;
|
||||
margin-block: 3px 0;
|
||||
margin-inline: 5px 3px;
|
||||
vertical-align: unset;
|
||||
}
|
||||
span.nobreak {
|
||||
white-space: nowrap;
|
||||
}
|
||||
input[type="text"].blank, input[type="text"].correct, input[type="text"].partly-correct, input[type="text"].incorrect, select.correct, select.incorrect {
|
||||
background-size: auto 100%;
|
||||
background-position: right;
|
||||
background-repeat: no-repeat;
|
||||
padding-right: 29px;
|
||||
}
|
||||
input[type="text"].blank {
|
||||
background-color: #CDF;
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='20px' width='20px'><text x='19' y='16' fill='%230049DB' text-anchor='end'>\u26A0</text></svg>");
|
||||
}
|
||||
input[type="text"].correct, select.correct, input[type="text"].correct + span.mq-editable-field {
|
||||
background-color: #8F8;
|
||||
}
|
||||
input[type="text"].correct {
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='20px' width='20px'><text x='19' y='16' fill='%23060' text-anchor='end'>\u2713</text></svg>");
|
||||
}
|
||||
padding-left: 2rem;
|
||||
background-repeat: no-repeat;
|
||||
background-position-y: center;
|
||||
}
|
||||
input[type="text"].partly-correct, input[type="text"].partly-correct + span.mq-editable-field {
|
||||
background-color: #CDF;
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='22px' width='30px'><text x='28' y='18' fill='%235C5C00' text-anchor='end'>\u26A0</text></svg>");
|
||||
}
|
||||
input[type="text"].incorrect, select.incorrect, input[type="text"].incorrect + span.mq-editable-field {
|
||||
background-color: #DAA;
|
||||
}
|
||||
input[type="text"].incorrect {
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='22px' width='30px'><text x='28' y='18' fill='%23943D3D' text-anchor='end'>\u26A0</text></svg>");
|
||||
}
|
||||
input[type="text"].partly-correct, input[type="text"].incorrect {
|
||||
background-size: auto 70%;
|
||||
}
|
||||
label {
|
||||
padding-left: 1.8em;
|
||||
}
|
||||
label.correct::before {
|
||||
color: #060;
|
||||
content: '\u2713';
|
||||
}
|
||||
label.incorrect::before {
|
||||
color: #943D3D;
|
||||
content: '\u26A0';
|
||||
font-size: small;
|
||||
}
|
||||
label.correct::before, label.incorrect::before {
|
||||
display:inline-block;
|
||||
width: 0;
|
||||
direction: rtl;
|
||||
}
|
||||
.select-wrapper.correct::before {
|
||||
color: #060;
|
||||
content: '\u2713';
|
||||
margin-right: 2pt;
|
||||
}
|
||||
.select-wrapper.incorrect::before {
|
||||
color: #943D3D;
|
||||
content: '\u26A0';
|
||||
margin-right: 2pt;
|
||||
font-size: small;
|
||||
}
|
||||
.checkboxes-container.correct label input, .checkboxes-container.incorrect label input {
|
||||
border-radius: 2px;
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.checkboxes-container.correct label input {
|
||||
background-color: #8F8;
|
||||
border: #060 solid;
|
||||
}
|
||||
.checkboxes-container.correct label input:checked {
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='14px' width='14px'><text x='13' y='13' text-anchor='end'>\u2713</text></svg>");
|
||||
}
|
||||
.checkboxes-container.incorrect label input {
|
||||
background-color: #DAA;
|
||||
border: #943D3D solid;
|
||||
}
|
||||
.checkboxes-container.incorrect label input:checked {
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='14px' width='14px'><text x='13' y='13' text-anchor='end'>\u2718</text></svg>");
|
||||
}
|
||||
div.PGML img.image-view-elt {
|
||||
max-width:100%;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
select {
|
||||
margin: 0;
|
||||
border-width: 1px;
|
||||
border-style: inset;
|
||||
display: inline-block;
|
||||
padding-block: 1px;
|
||||
vertical-align: baseline;
|
||||
background-color: unset;
|
||||
padding-inline: 0;
|
||||
}
|
||||
.result-popover .popover-header, .result-popover .popover-content {
|
||||
text-align: center;
|
||||
}
|
||||
.result-popover .popover-header.correct {
|
||||
background-color: #8F8;
|
||||
}
|
||||
.result-popover.nocontent .popover-header {
|
||||
border-radius: calc(.3rem - 1px);
|
||||
}
|
||||
.result-popover.nocontent .popover-header::before {
|
||||
border: none;
|
||||
}
|
||||
.accordion-body.expanded {
|
||||
overflow-y: visible;
|
||||
overflow-x: clip;
|
||||
}
|
||||
.graphtool-answer-container .graphtool-graph {
|
||||
margin: 0;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
.graphtool-answer-container .graphtool-number-line {
|
||||
height: 57px;
|
||||
}
|
||||
.checkboxes-container .ww-feedback {
|
||||
position: absolute;
|
||||
left: 2px;
|
||||
}
|
||||
.graphtool-container.correct .graphtool-graph {
|
||||
background-color: #8F8;
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='20px' width='20px'><text x='19' y='16' fill='%23060' text-anchor='end'>\u2713</text></svg>");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.graphtool-container.incorrect .graphtool-graph {
|
||||
background-color: #DAA;
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='22px' width='30px'><text x='28' y='18' fill='%235C5C00' text-anchor='end'>\u26A0</text></svg>");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
</style></head><body><main class="pretext-content">` + form.outerHTML + "</main></body></html>";
|
||||
let iframe2;
|
||||
if (!action) {
|
||||
iframe2 = document.createElement("iframe");
|
||||
iframe2.style.width = "1px";
|
||||
iframe2.style.minWidth = "100%";
|
||||
iframe2.classList.add("problem-iframe");
|
||||
ww_container.querySelector(".problem-contents").classList.add("hidden-content");
|
||||
if (activate_button != null) {
|
||||
activate_button.after(iframe2);
|
||||
} else {
|
||||
ww_container.prepend(iframe2);
|
||||
}
|
||||
iFrameResize({ checkOrigin: false, scrolling: "omit", heightCalculationMethod: "min" }, iframe2);
|
||||
iframe2.addEventListener("load", () => {
|
||||
const iframeForm = iframe2.contentDocument.getElementById(ww_id + "-form");
|
||||
iframeForm.addEventListener("submit", (e) => {
|
||||
handleWW(ww_id, "check");
|
||||
e.preventDefault();
|
||||
});
|
||||
iframe2.contentDocument.querySelectorAll(".collapse.in").forEach((collapse) => collapse.classList.add("expanded"));
|
||||
iframe2.contentWindow.jQuery(".collapse").on("shown", function(e) {
|
||||
if (e.target != this) return;
|
||||
this.classList.add("expanded");
|
||||
});
|
||||
iframe2.contentWindow.jQuery(".collapse").on("hide", function(e) {
|
||||
if (e.target != this) return;
|
||||
this.classList.remove("expanded");
|
||||
});
|
||||
iframe2.contentDocument.querySelectorAll("button.ww-feedback[data-bs-content]").forEach((button) => {
|
||||
const bsPopover = new iframe2.contentWindow.bootstrap.Popover(button, {
|
||||
html: true,
|
||||
placement: "bottom",
|
||||
fallbackPlacements: [],
|
||||
trigger: "click",
|
||||
customClass: "result-popover" + (button.dataset.emptyContent ? " nocontent" : "")
|
||||
});
|
||||
bsPopover.show();
|
||||
const content = iframe2.contentDocument.getElementById(button.id.replace("-feedback-button", "-content"));
|
||||
const popover = content.parentNode.parentNode;
|
||||
bsPopover.hide();
|
||||
button.addEventListener("click", () => bsPopover.show(), { once: true });
|
||||
popover.querySelector(".popover-arrow").remove();
|
||||
const title = popover.querySelector(".popover-header");
|
||||
if (button.dataset.emptyContent) content.parentNode.remove();
|
||||
if (title.textContent == localize_correct + "!") title.classList.add("correct");
|
||||
});
|
||||
iframe2.contentWindow.MathJax.startup.promise.then(() => iframe2.contentWindow.MathJax.typesetPromise([".popover", ".popover-content"]));
|
||||
});
|
||||
} else {
|
||||
iframe2 = ww_container.querySelector(".problem-iframe");
|
||||
}
|
||||
iframe2.srcdoc = iframeContents;
|
||||
iframe2.addEventListener("load", () => {
|
||||
loader.remove();
|
||||
}, { once: true });
|
||||
ww_container.focus();
|
||||
});
|
||||
}
|
||||
function WWshowCorrect(ww_id, answers) {
|
||||
const ww_container = document.getElementById(ww_id);
|
||||
const iframe2 = ww_container.querySelector(".problem-iframe");
|
||||
const body = iframe2.contentDocument.getElementById(ww_id + "-body");
|
||||
$("body").trigger("runestone_show_correct", {
|
||||
"ww_id": ww_id,
|
||||
"answers": answers
|
||||
});
|
||||
let inputs = body.querySelectorAll("input:not([type=hidden])");
|
||||
for (const input of inputs) {
|
||||
const name = input.name;
|
||||
const span_id = `${ww_id}-${name}-correct`;
|
||||
if (input.type == "text" && answers[name] && !iframe2.contentDocument.getElementById(span_id)) {
|
||||
const input_id = input.id;
|
||||
const mq_span = iframe2.contentDocument.getElementById(`mq-answer-${input_id}`);
|
||||
if (mq_span) {
|
||||
mq_span.style.display = "none";
|
||||
}
|
||||
const feedbackButton = iframe2.contentDocument.getElementById(`${ww_id}-${name}-feedback-button`);
|
||||
if (feedbackButton) {
|
||||
iframe2.contentWindow.bootstrap.Popover.getInstance(feedbackButton)?.hide();
|
||||
feedbackButton.remove();
|
||||
}
|
||||
label = iframe2.contentDocument.getElementById(`${span_id}-label`);
|
||||
if (label) {
|
||||
label.parentElement.insertBefore(input, label);
|
||||
label.remove();
|
||||
}
|
||||
input.type = "hidden";
|
||||
const correct_ans_text = iframe2.contentDocument.createElement("div");
|
||||
correct_ans_text.innerHTML = answers[name].correct_ans;
|
||||
input.value = correct_ans_text.textContent;
|
||||
const show_span = iframe2.contentDocument.createElement("span");
|
||||
show_span.id = span_id;
|
||||
show_span.appendChild(answers[name].correct_ans_latex_string ? iframe2.contentDocument.createTextNode("\\(" + answers[name].correct_ans_latex_string + "\\)") : iframe2.contentDocument.createTextNode(answers[name].correct_ans));
|
||||
input.parentElement.insertBefore(show_span, input);
|
||||
}
|
||||
if (input.type.toUpperCase() == "RADIO" && answers[name]) {
|
||||
const feedbackButton = iframe2.contentDocument.getElementById(`${ww_id}-${name}-feedback-button`);
|
||||
if (feedbackButton) {
|
||||
iframe2.contentWindow.bootstrap.Popover.getInstance(feedbackButton)?.hide();
|
||||
feedbackButton.remove();
|
||||
}
|
||||
const correct_ans = answers[name].correct_choice || answers[name].correct_ans;
|
||||
if (input.value == correct_ans) {
|
||||
input.checked = true;
|
||||
} else {
|
||||
input.checked = false;
|
||||
}
|
||||
}
|
||||
if (input.type.toUpperCase() == "CHECKBOX" && answers[name]) {
|
||||
const correct_choices = answers[name].correct_choices;
|
||||
if (correct_choices.includes(input.value)) {
|
||||
input.checked = true;
|
||||
} else {
|
||||
input.checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
const hiddenInputs = body.querySelectorAll("input[type=hidden]");
|
||||
for (const input of hiddenInputs) {
|
||||
const name = input.name;
|
||||
if (!input.nextElementSibling) continue;
|
||||
const graphtoolContainer = input.nextElementSibling.nextElementSibling;
|
||||
if (graphtoolContainer && answers[name] && graphtoolContainer.classList.contains("graphtool-container")) {
|
||||
const feedbackButton = iframe2.contentDocument.getElementById(`${ww_id}-${name}-feedback-button`);
|
||||
if (feedbackButton) {
|
||||
iframe2.contentWindow.bootstrap.Popover.getInstance(feedbackButton)?.hide();
|
||||
feedbackButton.remove();
|
||||
}
|
||||
const correct_ans_div = iframe2.contentDocument.createElement("div");
|
||||
input.parentElement.insertBefore(correct_ans_div, graphtoolContainer);
|
||||
graphtoolContainer.style.display = "none";
|
||||
input.value = answers[name].correct_ans;
|
||||
iframe2.contentWindow.jQuery(correct_ans_div).html(answers[name].correct_ans_latex_string);
|
||||
const script = iframe2.contentDocument.createElement("script");
|
||||
script.textContent = correct_ans_div.querySelector("script").textContent.replace('\nwindow.addEventListener("DOMContentLoaded",', "(").replace(/;\n$/, "();");
|
||||
iframe2.contentDocument.body.appendChild(script);
|
||||
}
|
||||
}
|
||||
let selects = body.querySelectorAll("select:not([type=hidden])");
|
||||
for (const select of selects) {
|
||||
const name = select.name;
|
||||
const span_id = `${ww_id}-${name}-correct`;
|
||||
if (answers[name] && !iframe2.contentDocument.getElementById(span_id)) {
|
||||
const feedbackButton = iframe2.contentDocument.getElementById(`${ww_id}-${name}-feedback-button`);
|
||||
if (feedbackButton) {
|
||||
iframe2.contentWindow.bootstrap.Popover.getInstance(feedbackButton)?.hide();
|
||||
feedbackButton.remove();
|
||||
}
|
||||
select.style.display = "none";
|
||||
select.value = answers[name].correct_ans;
|
||||
const show_span = iframe2.contentDocument.createElement("span");
|
||||
show_span.id = span_id;
|
||||
show_span.appendChild(answers[name].correct_ans_latex_string ? iframe2.contentDocument.createTextNode("\\(" + answers[name].correct_ans_latex_string + "\\)") : iframe2.contentDocument.createTextNode(answers[name].correct_ans));
|
||||
select.parentElement.insertBefore(show_span, select);
|
||||
}
|
||||
}
|
||||
const mathjaxTypesetScript = iframe2.contentDocument.createElement("script");
|
||||
mathjaxTypesetScript.textContent = "MathJax.startup.promise.then(() => MathJax.typesetPromise([document.body]));";
|
||||
iframe2.contentDocument.body.appendChild(mathjaxTypesetScript);
|
||||
}
|
||||
function resetWW(ww_id) {
|
||||
const ww_container = document.getElementById(ww_id);
|
||||
const activate_button = document.getElementById(ww_id + "-button");
|
||||
ww_container.dataset.current_seed = ww_container.dataset.seed;
|
||||
iframe = ww_container.querySelector(".problem-iframe");
|
||||
iframe.remove();
|
||||
ww_container.querySelector(".problem-contents").classList.remove("hidden-content");
|
||||
ww_container.querySelector(".problem-buttons.webwork").remove();
|
||||
ww_container.querySelector(".problem-buttons").classList.remove("hidden-content");
|
||||
if (activate_button != null) {
|
||||
activate_button.classList.remove("hidden-content");
|
||||
}
|
||||
;
|
||||
}
|
||||
function adjustSrcHrefs(container, ww_domain) {
|
||||
container.querySelectorAll("[href]").forEach((node) => {
|
||||
const href = node.attributes.href.value;
|
||||
if (href !== "#" && !href.match(/^[a-z]+:\/\//i)) node.href = ww_domain + "/" + href;
|
||||
});
|
||||
container.querySelectorAll("[src]").forEach((node) => {
|
||||
node.src = new URL(node.attributes.src.value, ww_domain).href;
|
||||
});
|
||||
}
|
||||
function translateHintSol(ww_id, body_div, ww_domain, b_ptx_has_hint, b_ptx_has_solution, hint_label_text, solution_label_text) {
|
||||
const hintSols = body_div.querySelectorAll('.knowl[data-type="hint"],.knowl[data-type="solution"]');
|
||||
let solutionlikewrapper;
|
||||
for (const hintSol of hintSols) {
|
||||
const hintsolp = hintSol.parentNode;
|
||||
if (!hintsolp) continue;
|
||||
const hintsolpp = hintsolp.parentNode;
|
||||
const hintSolType = hintSol.dataset.type;
|
||||
if (hintsolpp.querySelectorAll(".webwork.solutions").length == 0) {
|
||||
solutionlikewrapper = document.createElement("div");
|
||||
solutionlikewrapper.classList.add("webwork", "solutions");
|
||||
hintsolpp.insertBefore(solutionlikewrapper, hintsolp);
|
||||
}
|
||||
if (hintSolType == "solution" && !b_ptx_has_solution || hintSolType == "hint" && !b_ptx_has_hint)
|
||||
continue;
|
||||
const knowlDetails = document.createElement("details");
|
||||
knowlDetails.classList.add(hintSolType);
|
||||
knowlDetails.classList.add("solution-like");
|
||||
knowlDetails.classList.add("born-hidden-knowl");
|
||||
const knowlSummary = document.createElement("summary");
|
||||
const summaryLabel = document.createElement("span");
|
||||
summaryLabel.classList.add("type");
|
||||
summaryLabel.innerHTML = hintSolType == "hint" ? hint_label_text : solution_label_text;
|
||||
knowlSummary.appendChild(summaryLabel);
|
||||
knowlDetails.appendChild(knowlSummary);
|
||||
const knowlContents = document.createElement("div");
|
||||
knowlContents.classList.add(hintSolType);
|
||||
knowlContents.classList.add("solution-like");
|
||||
knowlContents.innerHTML = hintsolp.firstElementChild.dataset.knowlContents;
|
||||
knowlDetails.appendChild(knowlContents);
|
||||
adjustSrcHrefs(knowlContents, ww_domain);
|
||||
solutionlikewrapper.appendChild(knowlDetails);
|
||||
}
|
||||
for (const hintSol of hintSols) {
|
||||
hintSol.parentNode?.remove();
|
||||
}
|
||||
}
|
||||
function cloneAttributes(target, source) {
|
||||
[...source.attributes].forEach((attr) => {
|
||||
target.setAttribute(attr.nodeName, attr.nodeValue);
|
||||
});
|
||||
}
|
||||
function createFeedbackButton(id, title, content) {
|
||||
const feedbackButton = document.createElement("button");
|
||||
feedbackButton.dataset.bsTitle = title;
|
||||
feedbackButton.dataset.bsContent = `<div id="${id}-content">${content || ""}</div>`;
|
||||
if (!content) feedbackButton.dataset.emptyContent = "1";
|
||||
const contentSpan = document.createElement("span");
|
||||
contentSpan.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" fill="none" stroke-width="2" viewBox="0 0 24 24" color="#000000"><path stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M12 11.5v5M12 7.51l.01-.011M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10Z"></path></svg>`;
|
||||
feedbackButton.appendChild(contentSpan);
|
||||
feedbackButton.type = "button";
|
||||
feedbackButton.classList.add("ww-feedback");
|
||||
feedbackButton.id = `${id}-feedback-button`;
|
||||
feedbackButton.dataset.bsToggle = "popover";
|
||||
return feedbackButton;
|
||||
}
|
||||
function webworkSeedHash(string) {
|
||||
var hash = 0, i, chr;
|
||||
if (string.length === 0) return hash;
|
||||
for (i = 0; i < string.length; i++) {
|
||||
chr = string.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + chr;
|
||||
hash |= 0;
|
||||
}
|
||||
return Math.abs(hash);
|
||||
}
|
||||
;
|
||||
//# sourceMappingURL=pretext-webwork.js.map
|
||||
@@ -0,0 +1,545 @@
|
||||
async function handleWW(ww_id, action) {
|
||||
const ww_container = document.getElementById(ww_id);
|
||||
const ww_domain = ww_container.dataset.domain;
|
||||
const ww_processing = "webwork2";
|
||||
const ww_origin = ww_container.dataset.origin;
|
||||
const ww_problemSource = ww_container.dataset.problemsource;
|
||||
const ww_sourceFilePath = ww_container.dataset.sourcefilepath;
|
||||
const ww_course_id = ww_container.dataset.courseid;
|
||||
const ww_user_id = ww_container.dataset.userid;
|
||||
const ww_passwd = ww_container.dataset.coursepassword;
|
||||
const localize_submit = ww_container.dataset.localizeSubmit || "Submit";
|
||||
const localize_check_responses = ww_container.dataset.localizeCheckResponses || "Check Responses";
|
||||
const localize_reveal = ww_container.dataset.localizeReveal || "Reveal";
|
||||
const localize_randomize = ww_container.dataset.localizeRandomize || "Randomize";
|
||||
const localize_reset = ww_container.dataset.localizeReset || "Reset";
|
||||
const runestone_logged_in = typeof eBookConfig !== "undefined" && eBookConfig.username !== "";
|
||||
const activate_button = document.getElementById(ww_id + "-button");
|
||||
if (!action) {
|
||||
ww_container.dataset.current_seed = ww_container.dataset.seed;
|
||||
if (runestone_logged_in) {
|
||||
ww_container.dataset.current_seed = webworkSeedHash(eBookConfig.username + ww_container.dataset.current_seed);
|
||||
}
|
||||
} else if (action == "randomize") ww_container.dataset.current_seed = Number(ww_container.dataset.current_seed) + 100;
|
||||
let loader = document.createElement("div");
|
||||
loader.style.position = "absolute";
|
||||
loader.style.left = 0;
|
||||
loader.style.top = 0;
|
||||
loader.style.backgroundColor = "rgba(0.2, 0.2, 0.2, 0.4)";
|
||||
loader.style.color = "white";
|
||||
loader.style.width = "100%";
|
||||
loader.style.height = "100%";
|
||||
loader.style.display = "flex";
|
||||
loader.style.alignItems = "center";
|
||||
loader.style.justifyContent = "center";
|
||||
loader.style.marginTop = "0";
|
||||
loader.tabIndex = -1;
|
||||
const loaderText = document.createElement("span");
|
||||
loaderText.textContent = "Loading";
|
||||
loaderText.style.fontSize = "2rem";
|
||||
loader.appendChild(loaderText);
|
||||
ww_container.appendChild(loader);
|
||||
loader.focus();
|
||||
if (!action) {
|
||||
ww_container.dataset.hasHint = ww_container.getElementsByClassName("hint").length > 0;
|
||||
ww_container.dataset.hasSolution = ww_container.getElementsByClassName("solution").length > 0;
|
||||
ww_container.dataset.hasAnswer = ww_container.getElementsByClassName("answer").length > 0;
|
||||
ww_container.dataset.hintLabelText = ww_container.dataset.hasHint == "true" ? ww_container.querySelectorAll(".hint-knowl span.type, details.hint span.type")[0].textContent : "Hint";
|
||||
ww_container.dataset.solutionLabelText = ww_container.dataset.hasSolution == "true" ? ww_container.querySelectorAll(".solution-knowl span.type, details.solution span.type")[0].textContent : "Solution";
|
||||
ww_container.tabIndex = -1;
|
||||
}
|
||||
let url;
|
||||
if (ww_processing == "webwork2") {
|
||||
url = new URL(ww_domain + "/webwork2/render_rpc");
|
||||
}
|
||||
let formData = new FormData();
|
||||
let generatedPG = "generated/webwork/pg/";
|
||||
if (runestone_logged_in) {
|
||||
generatedPG = `/ns/books/published/${eBookConfig.basecourse}/${generatedPG}`;
|
||||
}
|
||||
if (action == "check" || action == "reveal") {
|
||||
const iframe2 = ww_container.querySelector(".problem-iframe");
|
||||
formData = new FormData(iframe2.contentDocument.getElementById(ww_id + "-form"));
|
||||
formData.set("answersSubmitted", "1");
|
||||
formData.set("WWsubmit", "1");
|
||||
if (action == "reveal" && ww_container.dataset.hasAnswer == "true") {
|
||||
formData.set("WWcorrectAnsOnly", "1");
|
||||
}
|
||||
if (ww_origin == "generated") {
|
||||
const rawProblemSource = await fetch(generatedPG + ww_problemSource).then((r) => r.text());
|
||||
formData.set("rawProblemSource", rawProblemSource);
|
||||
} else if (ww_origin == "webwork2") formData.set("sourceFilePath", ww_sourceFilePath);
|
||||
} else {
|
||||
formData.set("problemSeed", ww_container.dataset.current_seed);
|
||||
if (ww_origin == "generated") {
|
||||
const rawProblemSource = await fetch(generatedPG + ww_problemSource).then((r) => r.text());
|
||||
formData.set("rawProblemSource", rawProblemSource);
|
||||
} else if (ww_origin == "webwork2") formData.set("sourceFilePath", ww_sourceFilePath);
|
||||
formData.set("answersSubmitted", "0");
|
||||
formData.set("displayMode", "MathJax");
|
||||
formData.set("courseID", ww_course_id);
|
||||
formData.set("user", ww_user_id);
|
||||
formData.set("userID", ww_user_id);
|
||||
formData.set("passwd", ww_passwd);
|
||||
formData.set("disableCookies", "1");
|
||||
formData.set("outputformat", "raw");
|
||||
formData.set("showSolutions", ww_container.dataset.hasSolution == "true" ? "1" : "0");
|
||||
formData.set("showHints", ww_container.dataset.hasHint == "true" ? "1" : "0");
|
||||
formData.set("problemUUID", ww_id);
|
||||
}
|
||||
let checkboxesString = "";
|
||||
if (runestone_logged_in && !action) {
|
||||
const answersObject = wwList[ww_id.replace(/-ww-rs$/, "")].answers ? wwList[ww_id.replace(/-ww-rs$/, "")].answers : { "answers": [], "mqAnswers": [] };
|
||||
const previousAnswers = answersObject.answers;
|
||||
if (previousAnswers !== null) {
|
||||
formData.set("WWsubmit", 1);
|
||||
}
|
||||
for (const answer in previousAnswers) {
|
||||
if (previousAnswers[answer].constructor === Array) {
|
||||
for (const k in previousAnswers[answer]) {
|
||||
checkboxesString += "&";
|
||||
checkboxesString += answer;
|
||||
checkboxesString += "=";
|
||||
checkboxesString += previousAnswers[answer][k];
|
||||
}
|
||||
} else {
|
||||
formData.set(answer, previousAnswers[answer]);
|
||||
}
|
||||
}
|
||||
}
|
||||
const formString = new URLSearchParams(formData).toString();
|
||||
$.post(url, formString + checkboxesString, (data) => {
|
||||
const form = document.createElement("form");
|
||||
form.id = ww_id + "-form";
|
||||
form.dataset.iframeHeight = 1;
|
||||
const body_div = document.createElement("div");
|
||||
body_div.id = ww_id + "-body";
|
||||
body_div.classList.add("exercise", "exercise-like");
|
||||
body_div.lang = data.lang;
|
||||
body_div.dir = data.dir;
|
||||
body_div.innerHTML = data.rh_result.text;
|
||||
if ("showPartialCorrectAnswers" in data.rh_result.flags && data.rh_result.flags.showPartialCorrectAnswers == 0) {
|
||||
if ("score" in data.rh_result.problem_result && data.rh_result.problem_result.score >= 1) {
|
||||
body_div.querySelectorAll("button.ww-feedback-btn").forEach(
|
||||
function(button) {
|
||||
button.classList.remove("btn-info");
|
||||
button.classList.add("btn-success");
|
||||
button.setAttribute("aria-label", "Correct");
|
||||
button.dataset.bsCustomClass = button.dataset.bsCustomClass + " correct";
|
||||
button.dataset.bsTitle = button.dataset.bsTitle.replace("Answer Preview", "Correct");
|
||||
button.firstChild.classList.add("correct");
|
||||
}
|
||||
);
|
||||
} else {
|
||||
body_div.querySelectorAll("button.ww-feedback-btn").forEach(
|
||||
function(button) {
|
||||
button.setAttribute("aria-label", "One or more answers incorrect");
|
||||
button.dataset.bsTitle = button.dataset.bsTitle.replace("Answer Preview", "One or more answers incorrect");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
for (const tag_name of ["h6", "h5", "h4", "h3", "h2", "h1"]) {
|
||||
const headings = body_div.getElementsByTagName(tag_name);
|
||||
for (heading of headings) {
|
||||
const new_heading = document.createElement("h6");
|
||||
new_heading.innerHTML = heading.innerHTML;
|
||||
cloneAttributes(new_heading, heading);
|
||||
new_heading.classList.add("webwork-part");
|
||||
heading.replaceWith(new_heading);
|
||||
}
|
||||
}
|
||||
var textareas = body_div.getElementsByTagName("textarea");
|
||||
for (var i = 0, max = textareas.length; i < max; i++) {
|
||||
textareas[i].style.display = "none";
|
||||
textareas[i].className = "";
|
||||
}
|
||||
var textareabuttons = body_div.querySelectorAll(".latexentry-preview");
|
||||
for (var i = 0, max = textareabuttons.length; i < max; i++) {
|
||||
textareabuttons[i].remove();
|
||||
}
|
||||
adjustSrcHrefs(body_div, ww_domain);
|
||||
translateHintSol(
|
||||
ww_id,
|
||||
body_div,
|
||||
ww_domain,
|
||||
ww_container.dataset.hasHint == "true",
|
||||
ww_container.dataset.hasSolution == "true",
|
||||
ww_container.dataset.hintLabelText,
|
||||
ww_container.dataset.solutionLabelText
|
||||
);
|
||||
if (runestone_logged_in) {
|
||||
const answersObject = wwList[ww_id.replace(/-ww-rs$/, "")].answers ? wwList[ww_id.replace(/-ww-rs$/, "")].answers : { "answers": [], "mqAnswers": [] };
|
||||
const mqAnswers = answersObject.mqAnswers;
|
||||
for (const mqAnswer in mqAnswers) {
|
||||
const mqInput = body_div.querySelector("input[id=" + mqAnswer + "]");
|
||||
if (mqInput && mqInput.value == "") {
|
||||
mqInput.setAttribute("value", mqAnswers[mqAnswer]);
|
||||
}
|
||||
}
|
||||
const answers = answersObject.answers;
|
||||
for (const answer in answers) {
|
||||
const input = body_div.querySelector("input[id=" + answer + "]");
|
||||
if (input && input.value == "") {
|
||||
input.setAttribute("value", answers[answer]);
|
||||
}
|
||||
}
|
||||
}
|
||||
form.appendChild(body_div);
|
||||
const wwInputs = {
|
||||
problemSeed: data.inputs_ref.problemSeed,
|
||||
problemUUID: data.inputs_ref.problemUUID,
|
||||
psvn: data.inputs_ref.psvn,
|
||||
courseName: ww_course_id,
|
||||
courseID: ww_course_id,
|
||||
user: ww_user_id,
|
||||
passwd: ww_passwd,
|
||||
displayMode: "MathJax",
|
||||
session_key: data.rh_result.session_key,
|
||||
outputformat: "raw",
|
||||
language: data.formLanguage,
|
||||
showSummary: data.showSummary,
|
||||
disableCookies: "1",
|
||||
// note ww_container.dataset.hasSolution is a string, possibly 'false' which is true
|
||||
showSolutions: ww_container.dataset.hasSolution == "true" ? "1" : "0",
|
||||
showHints: ww_container.dataset.hasHint == "true" ? "1" : "0",
|
||||
forcePortNumber: data.forcePortNumber
|
||||
};
|
||||
if (ww_sourceFilePath) wwInputs.sourceFilePath = ww_sourceFilePath;
|
||||
else if (ww_problemSource && ww_origin == "webwork2") wwInputs.problemSource = ww_problemSource;
|
||||
for (const wwInputName of Object.keys(wwInputs)) {
|
||||
const input = document.createElement("input");
|
||||
input.type = "hidden";
|
||||
input.name = wwInputName;
|
||||
input.value = wwInputs[wwInputName];
|
||||
form.appendChild(input);
|
||||
}
|
||||
let buttonContainer = ww_container.querySelector(".problem-buttons.webwork");
|
||||
if (!buttonContainer) {
|
||||
ww_container.querySelector(".problem-buttons").classList.add("hidden-content", "hidden");
|
||||
buttonContainer = document.createElement("div");
|
||||
buttonContainer.classList.add("problem-buttons", "webwork");
|
||||
if (activate_button != null) {
|
||||
activate_button.after(buttonContainer);
|
||||
} else {
|
||||
ww_container.prepend(buttonContainer);
|
||||
}
|
||||
const check = document.createElement("button");
|
||||
check.type = "button";
|
||||
check.id = ww_id + "-check";
|
||||
check.style.marginRight = "0.25rem";
|
||||
check.classList.add("webwork-button");
|
||||
check.textContent = runestone_logged_in ? localize_submit : localize_check_responses;
|
||||
check.addEventListener("click", () => handleWW(ww_id, "check"));
|
||||
buttonContainer.appendChild(check);
|
||||
if (ww_container.dataset.hasAnswer == "true") {
|
||||
const correct = document.createElement("button");
|
||||
correct.classList.add("show-correct", "webwork-button");
|
||||
correct.type = "button";
|
||||
correct.style.marginRight = "0.25rem";
|
||||
correct.textContent = localize_reveal;
|
||||
correct.addEventListener("click", () => handleWW(ww_id, "reveal"));
|
||||
buttonContainer.appendChild(correct);
|
||||
}
|
||||
const randomize = document.createElement("button");
|
||||
randomize.type = "button";
|
||||
randomize.classList.add("webwork-button");
|
||||
randomize.style.marginRight = "0.25rem";
|
||||
randomize.textContent = localize_randomize;
|
||||
randomize.addEventListener("click", () => handleWW(ww_id, "randomize"));
|
||||
buttonContainer.appendChild(randomize);
|
||||
const reset = document.createElement("button");
|
||||
reset.type = "button";
|
||||
reset.classList.add("webwork-button");
|
||||
reset.textContent = localize_reset;
|
||||
reset.addEventListener("click", () => resetWW(ww_id));
|
||||
buttonContainer.appendChild(reset);
|
||||
}
|
||||
if (runestone_logged_in && action == "check") {
|
||||
$("body").trigger("runestone_ww_check", data);
|
||||
}
|
||||
let courseUrlBase = "";
|
||||
if (runestone_logged_in) {
|
||||
courseUrlBase = "/ns/books/published/" + eBookConfig.basecourse + "/";
|
||||
}
|
||||
let iframeContents = '<!DOCTYPE html><head><script src="' + ww_domain + `/webwork2_files/node_modules/jquery/dist/jquery.min.js"><\/script><script>
|
||||
window.MathJax = {
|
||||
tex: { packages: { '[+]': ['noerrors'] } },
|
||||
loader: { load: ['input/asciimath', '[tex]/noerrors'] },
|
||||
startup: {
|
||||
ready() {
|
||||
const AM = MathJax.InputJax.AsciiMath.AM;
|
||||
|
||||
// Modify existing AsciiMath triggers.
|
||||
AM.symbols[AM.names.indexOf('**')] = {
|
||||
input: '**',
|
||||
tag: 'msup',
|
||||
output: '^',
|
||||
tex: null,
|
||||
ttype: AM.TOKEN.INFIX
|
||||
};
|
||||
|
||||
const i = AM.names.indexOf('infty');
|
||||
AM.names[i] = 'infinity';
|
||||
AM.symbols[i] = { input: 'infinity', tag: 'mo', output: '\u221E', tex: 'infty', ttype: AM.TOKEN.CONST };
|
||||
|
||||
return MathJax.startup.defaultReady();
|
||||
}
|
||||
},
|
||||
options: {
|
||||
processHtmlClass: "process-math",
|
||||
renderActions: {
|
||||
findScript: [
|
||||
10,
|
||||
(doc) => {
|
||||
for (const node of document.querySelectorAll('script[type^="math/tex"]')) {
|
||||
const math = new doc.options.MathItem(
|
||||
node.textContent,
|
||||
doc.inputJax[0],
|
||||
!!node.type.match(/; *mode=display/)
|
||||
);
|
||||
const text = document.createTextNode('');
|
||||
node.parentNode.replaceChild(text, node);
|
||||
math.start = { node: text, delim: '', n: 0 };
|
||||
math.end = { node: text, delim: '', n: 0 };
|
||||
doc.math.push(math);
|
||||
}
|
||||
},
|
||||
''
|
||||
]
|
||||
},
|
||||
ignoreHtmlClass: 'tex2jax_ignore'
|
||||
}
|
||||
};
|
||||
<\/script><script src="` + ww_domain + `/webwork2_files/node_modules/mathjax/es5/tex-chtml.js" id="MathJax-script" defer><\/script><script src="${courseUrlBase}_static/pretext/js/dist/knowl.js" defer><\/script><link rel="stylesheet" href="` + ww_domain + '/webwork2_files/node_modules/bootstrap/dist/css/bootstrap.min.css"/><script src="' + ww_domain + '/webwork2_files/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js" defer><\/script>';
|
||||
const extra_css_files = [];
|
||||
const extra_js_files = [];
|
||||
if (data.extra_css_files) data.extra_css_files.unshift(...extra_css_files);
|
||||
else data.extra_css_files = extra_css_files;
|
||||
for (const cssFile of data.extra_css_files) {
|
||||
iframeContents += '<link rel="stylesheet" href="' + (cssFile.external !== "1" ? ww_domain : "") + cssFile.file + '"/>';
|
||||
}
|
||||
if (data.extra_js_files) data.extra_js_files.unshift(...extra_js_files);
|
||||
else data.extra_js_files = extra_js_files;
|
||||
for (const jsFile of data.extra_js_files) {
|
||||
iframeContents += '<script src="' + (jsFile.external !== "1" ? ww_domain : "") + jsFile.file + '" ' + Object.keys(jsFile.attributes || {}).reduce((ret, key) => {
|
||||
ret += key + '="' + jsFile.attributes[key] + '" ';
|
||||
return ret;
|
||||
}, "") + "><\/script>";
|
||||
}
|
||||
iframeContents += `<link rel="stylesheet" href="${courseUrlBase}_static/pretext/css/theme.css"/><script src="` + ww_domain + `/webwork2_files/node_modules/iframe-resizer/js/iframeResizer.contentWindow.min.js"><\/script><style>
|
||||
html { overflow-y: hidden; }
|
||||
html body { background:unset; margin: 0; }
|
||||
body { font-size: initial; line-height: initial; padding:2px; }
|
||||
.hidden-content { display: none; }
|
||||
span.nobreak { white-space: nowrap; }
|
||||
div.PGML img.image-view-elt { max-width:100%; }
|
||||
.graphtool-answer-container .graphtool-graph { margin: 0; width: 300px; height: 300px; }
|
||||
.graphtool-answer-container .graphtool-number-line { height: 57px; }
|
||||
.quill-toolbar { scrollbar-width: thin; overflow-x: hidden; }
|
||||
</style></head><body><main class="pretext-content problem-content" data-iframe-height="1">` + form.outerHTML + "</main></body></html>";
|
||||
let iframe2;
|
||||
if (!action) {
|
||||
iframe2 = document.createElement("iframe");
|
||||
iframe2.style.width = "1px";
|
||||
iframe2.style.minWidth = "100%";
|
||||
iframe2.classList.add("problem-iframe");
|
||||
ww_container.querySelector(".problem-contents").classList.add("hidden-content", "hidden");
|
||||
if (activate_button != null) {
|
||||
activate_button.after(iframe2);
|
||||
} else {
|
||||
ww_container.prepend(iframe2);
|
||||
}
|
||||
iFrameResize({ checkOrigin: false, scrolling: "omit", heightCalculationMethod: "taggedElement" }, iframe2);
|
||||
iframe2.addEventListener("load", () => {
|
||||
const iframeForm = iframe2.contentDocument.getElementById(ww_id + "-form");
|
||||
iframeForm.addEventListener("submit", (e) => {
|
||||
handleWW(ww_id, "check");
|
||||
e.preventDefault();
|
||||
});
|
||||
iframe2.contentDocument.querySelectorAll(".collapse.in").forEach((collapse) => collapse.classList.add("expanded"));
|
||||
iframe2.contentWindow.jQuery(".collapse").on("shown", function(e) {
|
||||
if (e.target != this) return;
|
||||
this.classList.add("expanded");
|
||||
});
|
||||
iframe2.contentWindow.jQuery(".collapse").on("hide", function(e) {
|
||||
if (e.target != this) return;
|
||||
this.classList.remove("expanded");
|
||||
});
|
||||
iframe2.contentWindow.MathJax.startup.promise.then(() => iframe2.contentWindow.MathJax.typesetPromise([".popover", ".popover-content"]));
|
||||
});
|
||||
} else {
|
||||
iframe2 = ww_container.querySelector(".problem-iframe");
|
||||
}
|
||||
iframe2.srcdoc = iframeContents;
|
||||
iframe2.addEventListener("load", () => {
|
||||
loader.remove();
|
||||
}, { once: true });
|
||||
ww_container.focus();
|
||||
}, "json");
|
||||
}
|
||||
function WWshowCorrect(ww_id, answers) {
|
||||
const ww_container = document.getElementById(ww_id);
|
||||
const iframe2 = ww_container.querySelector(".problem-iframe");
|
||||
const body = iframe2.contentDocument.getElementById(ww_id + "-body");
|
||||
$("body").trigger("runestone_show_correct", {
|
||||
"ww_id": ww_id,
|
||||
"answers": answers
|
||||
});
|
||||
let inputs = body.querySelectorAll("input:not([type=hidden])");
|
||||
for (const input of inputs) {
|
||||
const name = input.name;
|
||||
const span_id = `${ww_id}-${name}-correct`;
|
||||
if (input.type == "text" && answers[name] && !iframe2.contentDocument.getElementById(span_id)) {
|
||||
const input_id = input.id;
|
||||
const mq_span = iframe2.contentDocument.getElementById(`mq-answer-${input_id}`);
|
||||
if (mq_span) {
|
||||
mq_span.style.display = "none";
|
||||
}
|
||||
label = iframe2.contentDocument.getElementById(`${span_id}-label`);
|
||||
if (label) {
|
||||
label.parentElement.insertBefore(input, label);
|
||||
label.remove();
|
||||
}
|
||||
input.type = "hidden";
|
||||
const correct_ans_text = iframe2.contentDocument.createElement("div");
|
||||
correct_ans_text.innerHTML = answers[name].correct_ans;
|
||||
input.value = correct_ans_text.textContent;
|
||||
const show_span = iframe2.contentDocument.createElement("span");
|
||||
show_span.id = span_id;
|
||||
show_span.appendChild(answers[name].correct_ans_latex_string ? iframe2.contentDocument.createTextNode("\\(" + answers[name].correct_ans_latex_string + "\\)") : iframe2.contentDocument.createTextNode(answers[name].correct_ans));
|
||||
input.parentElement.insertBefore(show_span, input);
|
||||
}
|
||||
if (input.type.toUpperCase() == "RADIO" && answers[name]) {
|
||||
const correct_ans = answers[name].correct_choice || answers[name].correct_ans;
|
||||
if (input.value == correct_ans) {
|
||||
input.checked = true;
|
||||
} else {
|
||||
input.checked = false;
|
||||
}
|
||||
}
|
||||
if (input.type.toUpperCase() == "CHECKBOX" && answers[name]) {
|
||||
const correct_choices = answers[name].correct_choices;
|
||||
if (correct_choices.includes(input.value)) {
|
||||
input.checked = true;
|
||||
} else {
|
||||
input.checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
const hiddenInputs = body.querySelectorAll("input[type=hidden]");
|
||||
for (const input of hiddenInputs) {
|
||||
const name = input.name;
|
||||
if (!input.nextElementSibling) continue;
|
||||
const graphtoolContainer = input.nextElementSibling.nextElementSibling;
|
||||
if (graphtoolContainer && answers[name] && graphtoolContainer.classList.contains("graphtool-container")) {
|
||||
const correct_ans_div = iframe2.contentDocument.createElement("div");
|
||||
input.parentElement.insertBefore(correct_ans_div, graphtoolContainer);
|
||||
graphtoolContainer.style.display = "none";
|
||||
input.value = answers[name].correct_ans;
|
||||
iframe2.contentWindow.jQuery(correct_ans_div).html(answers[name].correct_ans_latex_string);
|
||||
const script = iframe2.contentDocument.createElement("script");
|
||||
script.textContent = correct_ans_div.querySelector("script").textContent.replace('\nwindow.addEventListener("DOMContentLoaded",', "(").replace(/;\n$/, "();");
|
||||
iframe2.contentDocument.body.appendChild(script);
|
||||
}
|
||||
}
|
||||
let selects = body.querySelectorAll("select:not([type=hidden])");
|
||||
for (const select of selects) {
|
||||
const name = select.name;
|
||||
const span_id = `${ww_id}-${name}-correct`;
|
||||
if (answers[name] && !iframe2.contentDocument.getElementById(span_id)) {
|
||||
select.style.display = "none";
|
||||
select.value = answers[name].correct_ans;
|
||||
const show_span = iframe2.contentDocument.createElement("span");
|
||||
show_span.id = span_id;
|
||||
show_span.appendChild(answers[name].correct_ans_latex_string ? iframe2.contentDocument.createTextNode("\\(" + answers[name].correct_ans_latex_string + "\\)") : iframe2.contentDocument.createTextNode(answers[name].correct_ans));
|
||||
select.parentElement.insertBefore(show_span, select);
|
||||
}
|
||||
}
|
||||
const mathjaxTypesetScript = iframe2.contentDocument.createElement("script");
|
||||
mathjaxTypesetScript.textContent = "MathJax.startup.promise.then(() => MathJax.typesetPromise([document.body]));";
|
||||
iframe2.contentDocument.body.appendChild(mathjaxTypesetScript);
|
||||
}
|
||||
function resetWW(ww_id) {
|
||||
const ww_container = document.getElementById(ww_id);
|
||||
const activate_button = document.getElementById(ww_id + "-button");
|
||||
ww_container.dataset.current_seed = ww_container.dataset.seed;
|
||||
iframe = ww_container.querySelector(".problem-iframe");
|
||||
iframe.remove();
|
||||
ww_container.querySelector(".problem-contents").classList.remove("hidden-content", "hidden");
|
||||
ww_container.querySelector(".problem-buttons.webwork").remove();
|
||||
ww_container.querySelector(".problem-buttons").classList.remove("hidden-content", "hidden");
|
||||
if (activate_button != null) {
|
||||
activate_button.classList.remove("hidden-content", "hidden");
|
||||
}
|
||||
;
|
||||
}
|
||||
function adjustSrcHrefs(container, ww_domain) {
|
||||
container.querySelectorAll("[href]").forEach((node) => {
|
||||
const href = node.attributes.href.value;
|
||||
if (href !== "#" && !href.match(/^[a-z]+:\/\//i)) node.href = ww_domain + "/" + href;
|
||||
});
|
||||
container.querySelectorAll("[data-knowl-url]").forEach((node) => {
|
||||
const dku = node.dataset.knowlUrl;
|
||||
if (dku !== "#" && !dku.match(/^[a-z]+:\/\//i)) node.dataset.knowlUrl = ww_domain + dku;
|
||||
});
|
||||
container.querySelectorAll("[src]").forEach((node) => {
|
||||
node.src = new URL(node.attributes.src.value, ww_domain).href;
|
||||
});
|
||||
}
|
||||
function translateHintSol(ww_id, body_div, ww_domain, b_ptx_has_hint, b_ptx_has_solution, hint_label_text, solution_label_text) {
|
||||
const hintSols = body_div.querySelectorAll(".accordion.hint,.accordion.solution");
|
||||
if (hintSols.length == 0) {
|
||||
return;
|
||||
}
|
||||
;
|
||||
for (const hintSol of hintSols) {
|
||||
const parent = hintSol.parentNode;
|
||||
solutionlikewrapper = document.createElement("div");
|
||||
solutionlikewrapper.classList.add("solutions");
|
||||
parent.insertBefore(solutionlikewrapper, hintSol);
|
||||
const hintSolType = hintSol.classList.contains("hint") ? "hint" : "solution";
|
||||
if (hintSolType == "solution" && !b_ptx_has_solution || hintSolType == "hint" && !b_ptx_has_hint)
|
||||
continue;
|
||||
const knowlDetails = hintSol.getElementsByTagName("details")[0];
|
||||
knowlDetails.className = "";
|
||||
knowlDetails.classList.add(hintSolType, "solution-like", "born-hidden-knowl");
|
||||
solutionlikewrapper.appendChild(knowlDetails);
|
||||
const knowlSummary = knowlDetails.getElementsByTagName("summary")[0];
|
||||
knowlSummary.className = "";
|
||||
knowlSummary.classList.add("knowl__link");
|
||||
const summaryLabel = knowlSummary.children[0];
|
||||
summaryLabel.remove();
|
||||
const newLabelSpan = document.createElement("span");
|
||||
newLabelSpan.innerHTML = hintSolType == "hint" ? hint_label_text : solution_label_text;
|
||||
knowlSummary.appendChild(newLabelSpan);
|
||||
const newLabelPeriod = document.createElement("span");
|
||||
newLabelPeriod.innerHTML = ".";
|
||||
knowlSummary.appendChild(newLabelPeriod);
|
||||
adjustSrcHrefs(knowlDetails, ww_domain);
|
||||
hintSol.remove();
|
||||
const originalDetailsContent = knowlDetails.getElementsByTagName("div")[0];
|
||||
const newDetailsContent = originalDetailsContent.getElementsByTagName("div")[0];
|
||||
newDetailsContent.className = "";
|
||||
newDetailsContent.classList.add(hintSolType, "solution-like", "knowl__content");
|
||||
knowlDetails.appendChild(newDetailsContent);
|
||||
originalDetailsContent.remove();
|
||||
}
|
||||
}
|
||||
function cloneAttributes(target, source) {
|
||||
[...source.attributes].forEach((attr) => {
|
||||
target.setAttribute(attr.nodeName, attr.nodeValue);
|
||||
});
|
||||
}
|
||||
function webworkSeedHash(string) {
|
||||
var hash = 0, i, chr;
|
||||
if (string.length === 0) return hash;
|
||||
for (i = 0; i < string.length; i++) {
|
||||
chr = string.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + chr;
|
||||
hash |= 0;
|
||||
}
|
||||
return Math.abs(hash);
|
||||
}
|
||||
;
|
||||
//# sourceMappingURL=pretext-webwork.js.map
|
||||
@@ -0,0 +1,551 @@
|
||||
async function handleWW(ww_id, action) {
|
||||
const ww_container = document.getElementById(ww_id);
|
||||
const ww_domain = ww_container.dataset.domain;
|
||||
const ww_processing = "webwork2";
|
||||
const ww_origin = ww_container.dataset.origin;
|
||||
const ww_problemSource = ww_container.dataset.problemsource;
|
||||
let ww_baseCourse = ww_container.dataset.documentid;
|
||||
const ww_sourceFilePath = ww_container.dataset.sourcefilepath;
|
||||
const ww_course_id = ww_container.dataset.courseid;
|
||||
const ww_user_id = ww_container.dataset.userid;
|
||||
const ww_passwd = ww_container.dataset.coursepassword;
|
||||
const localize_submit = ww_container.dataset.localizeSubmit || "Submit";
|
||||
const localize_check_responses = ww_container.dataset.localizeCheckResponses || "Check Responses";
|
||||
const localize_reveal = ww_container.dataset.localizeReveal || "Reveal";
|
||||
const localize_randomize = ww_container.dataset.localizeRandomize || "Randomize";
|
||||
const localize_reset = ww_container.dataset.localizeReset || "Reset";
|
||||
const runestone_logged_in = typeof eBookConfig !== "undefined" && eBookConfig.username !== "";
|
||||
const activate_button = document.getElementById(ww_id + "-button");
|
||||
if (!action) {
|
||||
ww_container.dataset.current_seed = ww_container.dataset.seed;
|
||||
if (runestone_logged_in) {
|
||||
ww_container.dataset.current_seed = webworkSeedHash(eBookConfig.username + ww_container.dataset.current_seed);
|
||||
}
|
||||
} else if (action == "randomize") ww_container.dataset.current_seed = Number(ww_container.dataset.current_seed) + 100;
|
||||
let loader = document.createElement("div");
|
||||
loader.style.position = "absolute";
|
||||
loader.style.left = 0;
|
||||
loader.style.top = 0;
|
||||
loader.style.backgroundColor = "rgba(0.2, 0.2, 0.2, 0.4)";
|
||||
loader.style.color = "white";
|
||||
loader.style.width = "100%";
|
||||
loader.style.height = "100%";
|
||||
loader.style.display = "flex";
|
||||
loader.style.alignItems = "center";
|
||||
loader.style.justifyContent = "center";
|
||||
loader.style.marginTop = "0";
|
||||
loader.tabIndex = -1;
|
||||
const loaderText = document.createElement("span");
|
||||
loaderText.textContent = "Loading";
|
||||
loaderText.style.fontSize = "2rem";
|
||||
loader.appendChild(loaderText);
|
||||
ww_container.appendChild(loader);
|
||||
loader.focus();
|
||||
if (!action) {
|
||||
ww_container.dataset.hasHint = ww_container.getElementsByClassName("hint").length > 0;
|
||||
ww_container.dataset.hasSolution = ww_container.getElementsByClassName("solution").length > 0;
|
||||
ww_container.dataset.hasAnswer = ww_container.getElementsByClassName("answer").length > 0;
|
||||
ww_container.dataset.hintLabelText = ww_container.dataset.hasHint == "true" ? ww_container.querySelectorAll(".hint-knowl span.type, details.hint span.type")[0].textContent : "Hint";
|
||||
ww_container.dataset.solutionLabelText = ww_container.dataset.hasSolution == "true" ? ww_container.querySelectorAll(".solution-knowl span.type, details.solution span.type")[0].textContent : "Solution";
|
||||
ww_container.tabIndex = -1;
|
||||
}
|
||||
let url;
|
||||
if (ww_processing == "webwork2") {
|
||||
url = new URL(ww_domain + "/webwork2/render_rpc");
|
||||
}
|
||||
let formData = new FormData();
|
||||
let generatedPG = "generated/webwork/pg/";
|
||||
if (runestone_logged_in) {
|
||||
if (ww_problemSource && !ww_baseCourse) {
|
||||
const parts = ww_id.split("_");
|
||||
ww_baseCourse = parts.length > 1 ? parts[0] : eBookConfig.basecourse;
|
||||
console.log("using the base course " + ww_baseCourse);
|
||||
}
|
||||
generatedPG = `/ns/books/published/${ww_baseCourse}/${generatedPG}`;
|
||||
}
|
||||
if (action == "check" || action == "reveal") {
|
||||
const iframe2 = ww_container.querySelector(".problem-iframe");
|
||||
formData = new FormData(iframe2.contentDocument.getElementById(ww_id + "-form"));
|
||||
formData.set("answersSubmitted", "1");
|
||||
formData.set("WWsubmit", "1");
|
||||
if (action == "reveal" && ww_container.dataset.hasAnswer == "true") {
|
||||
formData.set("WWcorrectAnsOnly", "1");
|
||||
}
|
||||
if (ww_origin == "generated") {
|
||||
const rawProblemSource = await fetch(generatedPG + ww_problemSource).then((r) => r.text());
|
||||
formData.set("rawProblemSource", rawProblemSource);
|
||||
} else if (ww_origin == "webwork2") formData.set("sourceFilePath", ww_sourceFilePath);
|
||||
} else {
|
||||
formData.set("problemSeed", ww_container.dataset.current_seed);
|
||||
if (ww_origin == "generated") {
|
||||
const rawProblemSource = await fetch(generatedPG + ww_problemSource).then((r) => r.text());
|
||||
formData.set("rawProblemSource", rawProblemSource);
|
||||
} else if (ww_origin == "webwork2") formData.set("sourceFilePath", ww_sourceFilePath);
|
||||
formData.set("answersSubmitted", "0");
|
||||
formData.set("displayMode", "MathJax");
|
||||
formData.set("courseID", ww_course_id);
|
||||
formData.set("user", ww_user_id);
|
||||
formData.set("userID", ww_user_id);
|
||||
formData.set("passwd", ww_passwd);
|
||||
formData.set("disableCookies", "1");
|
||||
formData.set("outputformat", "raw");
|
||||
formData.set("showSolutions", ww_container.dataset.hasSolution == "true" ? "1" : "0");
|
||||
formData.set("showHints", ww_container.dataset.hasHint == "true" ? "1" : "0");
|
||||
formData.set("problemUUID", ww_id);
|
||||
}
|
||||
let checkboxesString = "";
|
||||
if (runestone_logged_in && !action) {
|
||||
const answersObject = wwList[ww_id.replace(/-ww-rs$/, "")].answers ? wwList[ww_id.replace(/-ww-rs$/, "")].answers : { "answers": [], "mqAnswers": [] };
|
||||
const previousAnswers = answersObject.answers;
|
||||
if (previousAnswers !== null) {
|
||||
formData.set("WWsubmit", 1);
|
||||
}
|
||||
for (const answer in previousAnswers) {
|
||||
if (previousAnswers[answer].constructor === Array) {
|
||||
for (const k in previousAnswers[answer]) {
|
||||
checkboxesString += "&";
|
||||
checkboxesString += answer;
|
||||
checkboxesString += "=";
|
||||
checkboxesString += previousAnswers[answer][k];
|
||||
}
|
||||
} else {
|
||||
formData.set(answer, previousAnswers[answer]);
|
||||
}
|
||||
}
|
||||
}
|
||||
const formString = new URLSearchParams(formData).toString();
|
||||
$.post(url, formString + checkboxesString, (data) => {
|
||||
const form = document.createElement("form");
|
||||
form.id = ww_id + "-form";
|
||||
form.dataset.iframeHeight = 1;
|
||||
const body_div = document.createElement("div");
|
||||
body_div.id = ww_id + "-body";
|
||||
body_div.classList.add("exercise", "exercise-like");
|
||||
body_div.lang = data.lang;
|
||||
body_div.dir = data.dir;
|
||||
body_div.innerHTML = data.rh_result.text;
|
||||
if ("showPartialCorrectAnswers" in data.rh_result.flags && data.rh_result.flags.showPartialCorrectAnswers == 0) {
|
||||
if ("score" in data.rh_result.problem_result && data.rh_result.problem_result.score >= 1) {
|
||||
body_div.querySelectorAll("button.ww-feedback-btn").forEach(
|
||||
function(button) {
|
||||
button.classList.remove("btn-info");
|
||||
button.classList.add("btn-success");
|
||||
button.setAttribute("aria-label", "Correct");
|
||||
button.dataset.bsCustomClass = button.dataset.bsCustomClass + " correct";
|
||||
button.dataset.bsTitle = button.dataset.bsTitle.replace("Answer Preview", "Correct");
|
||||
button.firstChild.classList.add("correct");
|
||||
}
|
||||
);
|
||||
} else {
|
||||
body_div.querySelectorAll("button.ww-feedback-btn").forEach(
|
||||
function(button) {
|
||||
button.setAttribute("aria-label", "One or more answers incorrect");
|
||||
button.dataset.bsTitle = button.dataset.bsTitle.replace("Answer Preview", "One or more answers incorrect");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
for (const tag_name of ["h6", "h5", "h4", "h3", "h2", "h1"]) {
|
||||
const headings = body_div.getElementsByTagName(tag_name);
|
||||
for (heading of headings) {
|
||||
const new_heading = document.createElement("h6");
|
||||
new_heading.innerHTML = heading.innerHTML;
|
||||
cloneAttributes(new_heading, heading);
|
||||
new_heading.classList.add("webwork-part");
|
||||
heading.replaceWith(new_heading);
|
||||
}
|
||||
}
|
||||
var textareas = body_div.getElementsByTagName("textarea");
|
||||
for (var i = 0, max = textareas.length; i < max; i++) {
|
||||
textareas[i].style.display = "none";
|
||||
textareas[i].className = "";
|
||||
}
|
||||
var textareabuttons = body_div.querySelectorAll(".latexentry-preview");
|
||||
for (var i = 0, max = textareabuttons.length; i < max; i++) {
|
||||
textareabuttons[i].remove();
|
||||
}
|
||||
adjustSrcHrefs(body_div, ww_domain);
|
||||
translateHintSol(
|
||||
ww_id,
|
||||
body_div,
|
||||
ww_domain,
|
||||
ww_container.dataset.hasHint == "true",
|
||||
ww_container.dataset.hasSolution == "true",
|
||||
ww_container.dataset.hintLabelText,
|
||||
ww_container.dataset.solutionLabelText
|
||||
);
|
||||
if (runestone_logged_in) {
|
||||
const answersObject = wwList[ww_id.replace(/-ww-rs$/, "")].answers ? wwList[ww_id.replace(/-ww-rs$/, "")].answers : { "answers": [], "mqAnswers": [] };
|
||||
const mqAnswers = answersObject.mqAnswers;
|
||||
for (const mqAnswer in mqAnswers) {
|
||||
const mqInput = body_div.querySelector("input[id=" + mqAnswer + "]");
|
||||
if (mqInput && mqInput.value == "") {
|
||||
mqInput.setAttribute("value", mqAnswers[mqAnswer]);
|
||||
}
|
||||
}
|
||||
const answers = answersObject.answers;
|
||||
for (const answer in answers) {
|
||||
const input = body_div.querySelector("input[id=" + answer + "]");
|
||||
if (input && input.value == "") {
|
||||
input.setAttribute("value", answers[answer]);
|
||||
}
|
||||
}
|
||||
}
|
||||
form.appendChild(body_div);
|
||||
const wwInputs = {
|
||||
problemSeed: data.inputs_ref.problemSeed,
|
||||
problemUUID: data.inputs_ref.problemUUID,
|
||||
psvn: data.inputs_ref.psvn,
|
||||
courseName: ww_course_id,
|
||||
courseID: ww_course_id,
|
||||
user: ww_user_id,
|
||||
passwd: ww_passwd,
|
||||
displayMode: "MathJax",
|
||||
session_key: data.rh_result.session_key,
|
||||
outputformat: "raw",
|
||||
language: data.formLanguage,
|
||||
showSummary: data.showSummary,
|
||||
disableCookies: "1",
|
||||
// note ww_container.dataset.hasSolution is a string, possibly 'false' which is true
|
||||
showSolutions: ww_container.dataset.hasSolution == "true" ? "1" : "0",
|
||||
showHints: ww_container.dataset.hasHint == "true" ? "1" : "0",
|
||||
forcePortNumber: data.forcePortNumber
|
||||
};
|
||||
if (ww_sourceFilePath) wwInputs.sourceFilePath = ww_sourceFilePath;
|
||||
else if (ww_problemSource && ww_origin == "webwork2") wwInputs.problemSource = ww_problemSource;
|
||||
for (const wwInputName of Object.keys(wwInputs)) {
|
||||
const input = document.createElement("input");
|
||||
input.type = "hidden";
|
||||
input.name = wwInputName;
|
||||
input.value = wwInputs[wwInputName];
|
||||
form.appendChild(input);
|
||||
}
|
||||
let buttonContainer = ww_container.querySelector(".problem-buttons.webwork");
|
||||
if (!buttonContainer) {
|
||||
ww_container.querySelector(".problem-buttons").classList.add("hidden-content", "hidden");
|
||||
buttonContainer = document.createElement("div");
|
||||
buttonContainer.classList.add("problem-buttons", "webwork");
|
||||
if (activate_button != null) {
|
||||
activate_button.after(buttonContainer);
|
||||
} else {
|
||||
ww_container.prepend(buttonContainer);
|
||||
}
|
||||
const check = document.createElement("button");
|
||||
check.type = "button";
|
||||
check.id = ww_id + "-check";
|
||||
check.style.marginRight = "0.25rem";
|
||||
check.classList.add("webwork-button");
|
||||
check.textContent = runestone_logged_in ? localize_submit : localize_check_responses;
|
||||
check.addEventListener("click", () => handleWW(ww_id, "check"));
|
||||
buttonContainer.appendChild(check);
|
||||
if (ww_container.dataset.hasAnswer == "true") {
|
||||
const correct = document.createElement("button");
|
||||
correct.classList.add("show-correct", "webwork-button");
|
||||
correct.type = "button";
|
||||
correct.style.marginRight = "0.25rem";
|
||||
correct.textContent = localize_reveal;
|
||||
correct.addEventListener("click", () => handleWW(ww_id, "reveal"));
|
||||
buttonContainer.appendChild(correct);
|
||||
}
|
||||
const randomize = document.createElement("button");
|
||||
randomize.type = "button";
|
||||
randomize.classList.add("webwork-button");
|
||||
randomize.style.marginRight = "0.25rem";
|
||||
randomize.textContent = localize_randomize;
|
||||
randomize.addEventListener("click", () => handleWW(ww_id, "randomize"));
|
||||
buttonContainer.appendChild(randomize);
|
||||
const reset = document.createElement("button");
|
||||
reset.type = "button";
|
||||
reset.classList.add("webwork-button");
|
||||
reset.textContent = localize_reset;
|
||||
reset.addEventListener("click", () => resetWW(ww_id));
|
||||
buttonContainer.appendChild(reset);
|
||||
}
|
||||
if (runestone_logged_in && action == "check") {
|
||||
$("body").trigger("runestone_ww_check", data);
|
||||
}
|
||||
let courseUrlBase = "";
|
||||
if (runestone_logged_in) {
|
||||
courseUrlBase = "/ns/books/published/" + eBookConfig.basecourse + "/";
|
||||
}
|
||||
let iframeContents = '<!DOCTYPE html><head><script src="' + ww_domain + `/webwork2_files/node_modules/jquery/dist/jquery.min.js"><\/script><script>
|
||||
window.MathJax = {
|
||||
tex: { packages: { '[+]': ['noerrors'] } },
|
||||
loader: { load: ['input/asciimath', '[tex]/noerrors'] },
|
||||
startup: {
|
||||
ready() {
|
||||
const AM = MathJax.InputJax.AsciiMath.AM;
|
||||
|
||||
// Modify existing AsciiMath triggers.
|
||||
AM.symbols[AM.names.indexOf('**')] = {
|
||||
input: '**',
|
||||
tag: 'msup',
|
||||
output: '^',
|
||||
tex: null,
|
||||
ttype: AM.TOKEN.INFIX
|
||||
};
|
||||
|
||||
const i = AM.names.indexOf('infty');
|
||||
AM.names[i] = 'infinity';
|
||||
AM.symbols[i] = { input: 'infinity', tag: 'mo', output: '\u221E', tex: 'infty', ttype: AM.TOKEN.CONST };
|
||||
|
||||
return MathJax.startup.defaultReady();
|
||||
}
|
||||
},
|
||||
options: {
|
||||
processHtmlClass: "process-math",
|
||||
renderActions: {
|
||||
findScript: [
|
||||
10,
|
||||
(doc) => {
|
||||
for (const node of document.querySelectorAll('script[type^="math/tex"]')) {
|
||||
const math = new doc.options.MathItem(
|
||||
node.textContent,
|
||||
doc.inputJax[0],
|
||||
!!node.type.match(/; *mode=display/)
|
||||
);
|
||||
const text = document.createTextNode('');
|
||||
node.parentNode.replaceChild(text, node);
|
||||
math.start = { node: text, delim: '', n: 0 };
|
||||
math.end = { node: text, delim: '', n: 0 };
|
||||
doc.math.push(math);
|
||||
}
|
||||
},
|
||||
''
|
||||
]
|
||||
},
|
||||
ignoreHtmlClass: 'tex2jax_ignore'
|
||||
}
|
||||
};
|
||||
<\/script><script src="` + ww_domain + `/webwork2_files/node_modules/mathjax/es5/tex-chtml.js" id="MathJax-script" defer><\/script><script src="${courseUrlBase}_static/pretext/js/dist/knowl.js" defer><\/script><link rel="stylesheet" href="` + ww_domain + '/webwork2_files/node_modules/bootstrap/dist/css/bootstrap.min.css"/><script src="' + ww_domain + '/webwork2_files/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js" defer><\/script>';
|
||||
const extra_css_files = [];
|
||||
const extra_js_files = [];
|
||||
if (data.extra_css_files) data.extra_css_files.unshift(...extra_css_files);
|
||||
else data.extra_css_files = extra_css_files;
|
||||
for (const cssFile of data.extra_css_files) {
|
||||
iframeContents += '<link rel="stylesheet" href="' + (cssFile.external !== "1" ? ww_domain : "") + cssFile.file + '"/>';
|
||||
}
|
||||
if (data.extra_js_files) data.extra_js_files.unshift(...extra_js_files);
|
||||
else data.extra_js_files = extra_js_files;
|
||||
for (const jsFile of data.extra_js_files) {
|
||||
iframeContents += '<script src="' + (jsFile.external !== "1" ? ww_domain : "") + jsFile.file + '" ' + Object.keys(jsFile.attributes || {}).reduce((ret, key) => {
|
||||
ret += key + '="' + jsFile.attributes[key] + '" ';
|
||||
return ret;
|
||||
}, "") + "><\/script>";
|
||||
}
|
||||
iframeContents += `<link rel="stylesheet" href="${courseUrlBase}_static/pretext/css/theme.css"/><script src="` + ww_domain + `/webwork2_files/node_modules/iframe-resizer/js/iframeResizer.contentWindow.min.js"><\/script><style>
|
||||
html { overflow-y: hidden; }
|
||||
html body { background:unset; margin: 0; }
|
||||
body { font-size: initial; line-height: initial; padding:2px; }
|
||||
.hidden-content { display: none; }
|
||||
span.nobreak { white-space: nowrap; }
|
||||
div.PGML img.image-view-elt { max-width:100%; }
|
||||
.graphtool-answer-container .graphtool-graph { margin: 0; width: 300px; height: 300px; }
|
||||
.graphtool-answer-container .graphtool-number-line { height: 57px; }
|
||||
.quill-toolbar { scrollbar-width: thin; overflow-x: hidden; }
|
||||
</style></head><body><main class="pretext-content problem-content" data-iframe-height="1">` + form.outerHTML + "</main></body></html>";
|
||||
let iframe2;
|
||||
if (!action) {
|
||||
iframe2 = document.createElement("iframe");
|
||||
iframe2.style.width = "1px";
|
||||
iframe2.style.minWidth = "100%";
|
||||
iframe2.classList.add("problem-iframe");
|
||||
ww_container.querySelector(".problem-contents").classList.add("hidden-content", "hidden");
|
||||
if (activate_button != null) {
|
||||
activate_button.after(iframe2);
|
||||
} else {
|
||||
ww_container.prepend(iframe2);
|
||||
}
|
||||
iFrameResize({ checkOrigin: false, scrolling: "omit", heightCalculationMethod: "taggedElement" }, iframe2);
|
||||
iframe2.addEventListener("load", () => {
|
||||
const iframeForm = iframe2.contentDocument.getElementById(ww_id + "-form");
|
||||
iframeForm.addEventListener("submit", (e) => {
|
||||
handleWW(ww_id, "check");
|
||||
e.preventDefault();
|
||||
});
|
||||
iframe2.contentDocument.querySelectorAll(".collapse.in").forEach((collapse) => collapse.classList.add("expanded"));
|
||||
iframe2.contentWindow.jQuery(".collapse").on("shown", function(e) {
|
||||
if (e.target != this) return;
|
||||
this.classList.add("expanded");
|
||||
});
|
||||
iframe2.contentWindow.jQuery(".collapse").on("hide", function(e) {
|
||||
if (e.target != this) return;
|
||||
this.classList.remove("expanded");
|
||||
});
|
||||
iframe2.contentWindow.MathJax.startup.promise.then(() => iframe2.contentWindow.MathJax.typesetPromise([".popover", ".popover-content"]));
|
||||
});
|
||||
} else {
|
||||
iframe2 = ww_container.querySelector(".problem-iframe");
|
||||
}
|
||||
iframe2.srcdoc = iframeContents;
|
||||
iframe2.addEventListener("load", () => {
|
||||
loader.remove();
|
||||
}, { once: true });
|
||||
ww_container.focus();
|
||||
}, "json");
|
||||
}
|
||||
function WWshowCorrect(ww_id, answers) {
|
||||
const ww_container = document.getElementById(ww_id);
|
||||
const iframe2 = ww_container.querySelector(".problem-iframe");
|
||||
const body = iframe2.contentDocument.getElementById(ww_id + "-body");
|
||||
$("body").trigger("runestone_show_correct", {
|
||||
"ww_id": ww_id,
|
||||
"answers": answers
|
||||
});
|
||||
let inputs = body.querySelectorAll("input:not([type=hidden])");
|
||||
for (const input of inputs) {
|
||||
const name = input.name;
|
||||
const span_id = `${ww_id}-${name}-correct`;
|
||||
if (input.type == "text" && answers[name] && !iframe2.contentDocument.getElementById(span_id)) {
|
||||
const input_id = input.id;
|
||||
const mq_span = iframe2.contentDocument.getElementById(`mq-answer-${input_id}`);
|
||||
if (mq_span) {
|
||||
mq_span.style.display = "none";
|
||||
}
|
||||
label = iframe2.contentDocument.getElementById(`${span_id}-label`);
|
||||
if (label) {
|
||||
label.parentElement.insertBefore(input, label);
|
||||
label.remove();
|
||||
}
|
||||
input.type = "hidden";
|
||||
const correct_ans_text = iframe2.contentDocument.createElement("div");
|
||||
correct_ans_text.innerHTML = answers[name].correct_ans;
|
||||
input.value = correct_ans_text.textContent;
|
||||
const show_span = iframe2.contentDocument.createElement("span");
|
||||
show_span.id = span_id;
|
||||
show_span.appendChild(answers[name].correct_ans_latex_string ? iframe2.contentDocument.createTextNode("\\(" + answers[name].correct_ans_latex_string + "\\)") : iframe2.contentDocument.createTextNode(answers[name].correct_ans));
|
||||
input.parentElement.insertBefore(show_span, input);
|
||||
}
|
||||
if (input.type.toUpperCase() == "RADIO" && answers[name]) {
|
||||
const correct_ans = answers[name].correct_choice || answers[name].correct_ans;
|
||||
if (input.value == correct_ans) {
|
||||
input.checked = true;
|
||||
} else {
|
||||
input.checked = false;
|
||||
}
|
||||
}
|
||||
if (input.type.toUpperCase() == "CHECKBOX" && answers[name]) {
|
||||
const correct_choices = answers[name].correct_choices;
|
||||
if (correct_choices.includes(input.value)) {
|
||||
input.checked = true;
|
||||
} else {
|
||||
input.checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
const hiddenInputs = body.querySelectorAll("input[type=hidden]");
|
||||
for (const input of hiddenInputs) {
|
||||
const name = input.name;
|
||||
if (!input.nextElementSibling) continue;
|
||||
const graphtoolContainer = input.nextElementSibling.nextElementSibling;
|
||||
if (graphtoolContainer && answers[name] && graphtoolContainer.classList.contains("graphtool-container")) {
|
||||
const correct_ans_div = iframe2.contentDocument.createElement("div");
|
||||
input.parentElement.insertBefore(correct_ans_div, graphtoolContainer);
|
||||
graphtoolContainer.style.display = "none";
|
||||
input.value = answers[name].correct_ans;
|
||||
iframe2.contentWindow.jQuery(correct_ans_div).html(answers[name].correct_ans_latex_string);
|
||||
const script = iframe2.contentDocument.createElement("script");
|
||||
script.textContent = correct_ans_div.querySelector("script").textContent.replace('\nwindow.addEventListener("DOMContentLoaded",', "(").replace(/;\n$/, "();");
|
||||
iframe2.contentDocument.body.appendChild(script);
|
||||
}
|
||||
}
|
||||
let selects = body.querySelectorAll("select:not([type=hidden])");
|
||||
for (const select of selects) {
|
||||
const name = select.name;
|
||||
const span_id = `${ww_id}-${name}-correct`;
|
||||
if (answers[name] && !iframe2.contentDocument.getElementById(span_id)) {
|
||||
select.style.display = "none";
|
||||
select.value = answers[name].correct_ans;
|
||||
const show_span = iframe2.contentDocument.createElement("span");
|
||||
show_span.id = span_id;
|
||||
show_span.appendChild(answers[name].correct_ans_latex_string ? iframe2.contentDocument.createTextNode("\\(" + answers[name].correct_ans_latex_string + "\\)") : iframe2.contentDocument.createTextNode(answers[name].correct_ans));
|
||||
select.parentElement.insertBefore(show_span, select);
|
||||
}
|
||||
}
|
||||
const mathjaxTypesetScript = iframe2.contentDocument.createElement("script");
|
||||
mathjaxTypesetScript.textContent = "MathJax.startup.promise.then(() => MathJax.typesetPromise([document.body]));";
|
||||
iframe2.contentDocument.body.appendChild(mathjaxTypesetScript);
|
||||
}
|
||||
function resetWW(ww_id) {
|
||||
const ww_container = document.getElementById(ww_id);
|
||||
const activate_button = document.getElementById(ww_id + "-button");
|
||||
ww_container.dataset.current_seed = ww_container.dataset.seed;
|
||||
iframe = ww_container.querySelector(".problem-iframe");
|
||||
iframe.remove();
|
||||
ww_container.querySelector(".problem-contents").classList.remove("hidden-content", "hidden");
|
||||
ww_container.querySelector(".problem-buttons.webwork").remove();
|
||||
ww_container.querySelector(".problem-buttons").classList.remove("hidden-content", "hidden");
|
||||
if (activate_button != null) {
|
||||
activate_button.classList.remove("hidden-content", "hidden");
|
||||
}
|
||||
;
|
||||
}
|
||||
function adjustSrcHrefs(container, ww_domain) {
|
||||
container.querySelectorAll("[href]").forEach((node) => {
|
||||
const href = node.attributes.href.value;
|
||||
if (href !== "#" && !href.match(/^[a-z]+:\/\//i)) node.href = ww_domain + "/" + href;
|
||||
});
|
||||
container.querySelectorAll("[data-knowl-url]").forEach((node) => {
|
||||
const dku = node.dataset.knowlUrl;
|
||||
if (dku !== "#" && !dku.match(/^[a-z]+:\/\//i)) node.dataset.knowlUrl = ww_domain + dku;
|
||||
});
|
||||
container.querySelectorAll("[src]").forEach((node) => {
|
||||
node.src = new URL(node.attributes.src.value, ww_domain).href;
|
||||
});
|
||||
}
|
||||
function translateHintSol(ww_id, body_div, ww_domain, b_ptx_has_hint, b_ptx_has_solution, hint_label_text, solution_label_text) {
|
||||
const hintSols = body_div.querySelectorAll(".accordion.hint,.accordion.solution");
|
||||
if (hintSols.length == 0) {
|
||||
return;
|
||||
}
|
||||
;
|
||||
for (const hintSol of hintSols) {
|
||||
const parent = hintSol.parentNode;
|
||||
solutionlikewrapper = document.createElement("div");
|
||||
solutionlikewrapper.classList.add("solutions");
|
||||
parent.insertBefore(solutionlikewrapper, hintSol);
|
||||
const hintSolType = hintSol.classList.contains("hint") ? "hint" : "solution";
|
||||
if (hintSolType == "solution" && !b_ptx_has_solution || hintSolType == "hint" && !b_ptx_has_hint)
|
||||
continue;
|
||||
const knowlDetails = hintSol.getElementsByTagName("details")[0];
|
||||
knowlDetails.className = "";
|
||||
knowlDetails.classList.add(hintSolType, "solution-like", "born-hidden-knowl");
|
||||
solutionlikewrapper.appendChild(knowlDetails);
|
||||
const knowlSummary = knowlDetails.getElementsByTagName("summary")[0];
|
||||
knowlSummary.className = "";
|
||||
knowlSummary.classList.add("knowl__link");
|
||||
const summaryLabel = knowlSummary.children[0];
|
||||
summaryLabel.remove();
|
||||
const newLabelSpan = document.createElement("span");
|
||||
newLabelSpan.innerHTML = hintSolType == "hint" ? hint_label_text : solution_label_text;
|
||||
knowlSummary.appendChild(newLabelSpan);
|
||||
const newLabelPeriod = document.createElement("span");
|
||||
newLabelPeriod.innerHTML = ".";
|
||||
knowlSummary.appendChild(newLabelPeriod);
|
||||
adjustSrcHrefs(knowlDetails, ww_domain);
|
||||
hintSol.remove();
|
||||
const originalDetailsContent = knowlDetails.getElementsByTagName("div")[0];
|
||||
const newDetailsContent = originalDetailsContent.getElementsByTagName("div")[0];
|
||||
newDetailsContent.className = "";
|
||||
newDetailsContent.classList.add(hintSolType, "solution-like", "knowl__content");
|
||||
knowlDetails.appendChild(newDetailsContent);
|
||||
originalDetailsContent.remove();
|
||||
}
|
||||
}
|
||||
function cloneAttributes(target, source) {
|
||||
[...source.attributes].forEach((attr) => {
|
||||
target.setAttribute(attr.nodeName, attr.nodeValue);
|
||||
});
|
||||
}
|
||||
function webworkSeedHash(string) {
|
||||
var hash = 0, i, chr;
|
||||
if (string.length === 0) return hash;
|
||||
for (i = 0; i < string.length; i++) {
|
||||
chr = string.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + chr;
|
||||
hash |= 0;
|
||||
}
|
||||
return Math.abs(hash);
|
||||
}
|
||||
;
|
||||
//# sourceMappingURL=pretext-webwork.js.map
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
window.i18next = window.i18next || {
|
||||
t(key, params = {}) {
|
||||
for (const param in params) {
|
||||
key = key.replace(`{{${param}}}`, params[param]);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
};
|
||||
function doSearch() {
|
||||
const terms = document.getElementById("ptx-search-terms").value;
|
||||
localStorage.setItem("last-search-terms", JSON.stringify({ terms, time: Date.now() }));
|
||||
let resultArea = document.getElementById("ptx-search-results");
|
||||
resultArea.innerHTML = "";
|
||||
const searchTerms = terms.toLowerCase().trim();
|
||||
let pageResult = [];
|
||||
if (searchTerms != "") {
|
||||
pageResult = ptx_lunr_idx.query((q) => {
|
||||
for (let term of searchTerms.split(" ")) {
|
||||
q.term(term, { fields: ["title"], boost: 20 });
|
||||
q.term(term, { wildcard: lunr.Query.wildcard.TRAILING, fields: ["title"], boost: 10 });
|
||||
q.term(term, { fields: ["body"], boost: 5 });
|
||||
q.term(term, { wildcard: lunr.Query.wildcard.TRAILING, fields: ["body"] });
|
||||
}
|
||||
});
|
||||
}
|
||||
snum = 0;
|
||||
for (let doc of ptx_lunr_docs) {
|
||||
doc.snum = snum;
|
||||
snum += 1;
|
||||
}
|
||||
const MAX_RESULTS = 100;
|
||||
let numUnshown = pageResult.length > MAX_RESULTS ? pageResult.length - MAX_RESULTS : 0;
|
||||
pageResult.slice(0, MAX_RESULTS);
|
||||
augmentResults(pageResult, ptx_lunr_docs);
|
||||
pageResult.sort(comparePosition);
|
||||
addResultToPage(terms, pageResult, ptx_lunr_docs, numUnshown, resultArea);
|
||||
MathJax.typeset();
|
||||
}
|
||||
function findEntry(resultId, db) {
|
||||
for (const page of db) {
|
||||
if (page.id === resultId) {
|
||||
return page;
|
||||
}
|
||||
}
|
||||
return resultId;
|
||||
}
|
||||
function augmentResults(result, docs) {
|
||||
for (let res of result) {
|
||||
let info = findEntry(res.ref, docs);
|
||||
res.number = info.number;
|
||||
res.type = info.type;
|
||||
res.title = info.title;
|
||||
res.url = info.url;
|
||||
res.level = info.level;
|
||||
res.snum = info.snum;
|
||||
res.score = parseFloat(res.score);
|
||||
const LEVEL_WEIGHTS = [3, 2, 1.5];
|
||||
if (res.level < 2)
|
||||
res.score *= LEVEL_WEIGHTS[res.level];
|
||||
res.body = "";
|
||||
const REVEAL_WINDOW = 30;
|
||||
let titleMarked = false;
|
||||
for (const hit in res.matchData.metadata) {
|
||||
if (res.matchData.metadata[hit].title) {
|
||||
if (!titleMarked) {
|
||||
if (!res.matchData.metadata[hit].title.position)
|
||||
continue;
|
||||
let positionData = res.matchData.metadata[hit].title.position[0];
|
||||
const startClipInd = positionData[0];
|
||||
const endClipInd = positionData[0] + positionData[1];
|
||||
res.title = res.title.substring(0, endClipInd) + "</span>" + res.title.substring(endClipInd);
|
||||
res.title = res.title.substring(0, startClipInd) + '<span class="ptx-search-result-clip-highlight">' + res.title.substring(startClipInd);
|
||||
titleMarked = true;
|
||||
}
|
||||
} else if (res.matchData.metadata[hit].body) {
|
||||
if (!res.matchData.metadata[hit].body.position)
|
||||
continue;
|
||||
const bodyContent = info.body;
|
||||
let positionData = res.matchData.metadata[hit].body.position[0];
|
||||
const startInd = positionData[0] - REVEAL_WINDOW;
|
||||
const endInd = positionData[0] + positionData[1] + REVEAL_WINDOW;
|
||||
const startClipInd = positionData[0];
|
||||
const endClipInd = positionData[0] + positionData[1];
|
||||
let resultSnippet = (startInd > 0 ? "..." : "") + bodyContent.substring(startInd, startClipInd);
|
||||
resultSnippet += '<span class="ptx-search-result-clip-highlight">' + bodyContent.substring(startClipInd, endClipInd) + "</span>";
|
||||
resultSnippet += bodyContent.substring(endClipInd, endInd) + (endInd < bodyContent.length ? "..." : "") + "<br/>";
|
||||
res.body += resultSnippet;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function rearrangedArray(arry) {
|
||||
let newarry = [];
|
||||
let startind = 0;
|
||||
let numtograb = 0;
|
||||
let ct = 1;
|
||||
while (arry.length > 0 && ct < 500) {
|
||||
++ct;
|
||||
const locofmax = maxLocation(arry);
|
||||
let segmentstart = locofmax;
|
||||
let segmentlength = 1;
|
||||
while (arry[segmentstart].level == "2") {
|
||||
--segmentstart;
|
||||
}
|
||||
while (segmentstart + segmentlength < arry.length && arry[segmentstart + segmentlength].level == "2") {
|
||||
++segmentlength;
|
||||
}
|
||||
newarry.push(...arry.splice(segmentstart, segmentlength));
|
||||
}
|
||||
return newarry;
|
||||
}
|
||||
function maxLocation(arry) {
|
||||
let maxloc = 0;
|
||||
let maxvalsofar = -1;
|
||||
for (let index = 0; index < arry.length; ++index) {
|
||||
if (arry[index].score > maxvalsofar) {
|
||||
maxloc = index;
|
||||
maxvalsofar = arry[index].score;
|
||||
}
|
||||
}
|
||||
return maxloc;
|
||||
}
|
||||
function comparePosition(a, b) {
|
||||
if (a.snum < b.snum) {
|
||||
return -1;
|
||||
}
|
||||
if (a.snum > b.snum) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
function compareScoreDesc(a, b) {
|
||||
if (a.score < b.score) {
|
||||
return 1;
|
||||
}
|
||||
if (a.score > b.score) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
function addResultToPage(searchterms, result, docs, numUnshown, resultArea) {
|
||||
let len = result.length;
|
||||
const searchStatus = document.getElementById("ptx-search-status");
|
||||
if (len == 0) {
|
||||
document.getElementById("ptx-search-empty").style.display = "block";
|
||||
document.getElementById("ptx-search-dialog").style.display = null;
|
||||
searchStatus.innerHTML = window.i18next.t('No results found for "{{terms}}".', { terms: searchterms });
|
||||
return;
|
||||
}
|
||||
document.getElementById("ptx-search-empty").style.display = "none";
|
||||
searchStatus.innerHTML = window.i18next.t("{{count}} results found.", { count: len });
|
||||
let allScores = result.map(function(r) {
|
||||
return r.score;
|
||||
});
|
||||
allScores.sort((a, b) => a - b);
|
||||
allScores.reverse();
|
||||
let high = allScores[Math.floor(len * 0.2)];
|
||||
let med = allScores[Math.floor(len * 0.4)];
|
||||
let low = allScores[Math.floor(len * 0.75)];
|
||||
if (ptx_lunr_search_style == "reference") {
|
||||
result = rearrangedArray(result);
|
||||
}
|
||||
let indent = "1";
|
||||
let currIndent = indent;
|
||||
let origResult = resultArea;
|
||||
for (const res of result) {
|
||||
let link = document.createElement("a");
|
||||
if (res.score >= high) {
|
||||
link.classList.add("ptx-search-result-high");
|
||||
} else if (res.score >= med) {
|
||||
link.classList.add("ptx-search-result-medium");
|
||||
} else if (res.score >= low) {
|
||||
link.classList.add("ptx-search-result-low");
|
||||
} else {
|
||||
link.classList.add("ptx-search-result-none");
|
||||
}
|
||||
currIndent = res.level;
|
||||
if (currIndent > indent) {
|
||||
indent = currIndent;
|
||||
let ilist = document.createElement("ul");
|
||||
ilist.classList.add("ptx-search-detailed-result");
|
||||
resultArea.appendChild(ilist);
|
||||
resultArea = ilist;
|
||||
} else if (currIndent < indent) {
|
||||
resultArea = origResult;
|
||||
indent = currIndent;
|
||||
}
|
||||
link.href = `${res.url}`;
|
||||
link.innerHTML = `${res.type} ${res.number} ${res.title}`;
|
||||
let clip = document.createElement("div");
|
||||
clip.classList.add("ptx-search-result-clip");
|
||||
clip.innerHTML = `${res.body}`;
|
||||
let bullet = document.createElement("li");
|
||||
bullet.classList.add("ptx-search-result-bullet");
|
||||
bullet.appendChild(link);
|
||||
bullet.appendChild(clip);
|
||||
let p = document.createElement("text");
|
||||
p.classList.add("ptx-search-result-score");
|
||||
p.innerHTML = ` (${res.score.toFixed(2)})`;
|
||||
bullet.appendChild(p);
|
||||
resultArea.appendChild(bullet);
|
||||
}
|
||||
const resultsDialog = document.getElementById("ptx-search-dialog");
|
||||
resultArea.querySelectorAll("a").forEach((link) => {
|
||||
link.addEventListener("click", (e) => {
|
||||
resultsDialog.close();
|
||||
});
|
||||
});
|
||||
document.getElementById("ptx-search-dialog").style.display = null;
|
||||
MathJax.typesetPromise();
|
||||
}
|
||||
window.addEventListener("load", function(event) {
|
||||
const searchDialogElement = document.getElementById("ptx-search-dialog");
|
||||
const searchButtonElement = document.getElementById("ptx-search-button");
|
||||
const closeBtn = document.getElementById("ptx-search-close");
|
||||
const searchDialog = new PTXDialog(searchDialogElement, searchButtonElement, {
|
||||
closeButton: closeBtn
|
||||
});
|
||||
searchButtonElement.addEventListener("click", (e) => {
|
||||
const lastSearch = localStorage.getItem("last-search-terms");
|
||||
let searchInput = document.getElementById("ptx-search-terms");
|
||||
searchInput.value = lastSearch ? JSON.parse(lastSearch)?.terms || "" : "";
|
||||
searchInput.select();
|
||||
if (searchInput.value) {
|
||||
doSearch();
|
||||
}
|
||||
});
|
||||
document.getElementById("ptx-search-terms").addEventListener("input", (e) => {
|
||||
doSearch();
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=pretext_search.js.map
|
||||
+1122
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user