Latest build deployed.

This commit is contained in:
2026-02-10 07:59:07 -05:00
parent b48c520d4b
commit 1be515cd2b
84 changed files with 2855 additions and 659 deletions
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3
View File
File diff suppressed because one or more lines are too long
+5
View File
@@ -0,0 +1,5 @@
/*! Hammer.JS - v2.0.8 - 2016-04-23
* http://hammerjs.github.io/
*
* Copyright (c) 2016 Jorik Tangelder;
* Licensed under the MIT license */
BIN
View File
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
+3
View File
File diff suppressed because one or more lines are too long
+5
View File
@@ -0,0 +1,5 @@
/*! Hammer.JS - v2.0.8 - 2016-04-23
* http://hammerjs.github.io/
*
* Copyright (c) 2016 Jorik Tangelder;
* Licensed under the MIT license */
BIN
View File
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
+2 -2
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" ?>
<all>
<js type="list">
<item type="str">prefix-runtime.fc0e080e84d54281.bundle.js</item>
<item type="str">prefix-runtime.0d5f811896ddeac1.bundle.js</item>
<item type="str">prefix-723.3e6434f80549315a.bundle.js</item>
<item type="str">prefix-runestone.e2abc79debce15fd.bundle.js</item>
</js>
@@ -10,5 +10,5 @@
<item type="str">prefix-runestone.6ec5960970d8b364.css</item>
</css>
<cdn-url type="str">https://runestone.academy/cdn/runestone/</cdn-url>
<version type="str">7.11.11</version>
<version type="str">7.11.13</version>
</all>
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
View File
File diff suppressed because one or more lines are too long
+338
View File
@@ -0,0 +1,338 @@
// Code controlling behavior of xref knowls and born hidden knowls
// Assumes this file is loaded as part of initial page
window.addEventListener("load", (event) => {
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);
}
}
// Used to animate both types of knowls
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;
// mid animation state tracking
this.animation = null;
this.animationState = SlideRevealer.STATE.INACTIVE;
this.triggerElement.addEventListener('click', (e) => this.onClick(e));
}
onClick(e) {
// Stop default behavior from the browser
if (e) e.preventDefault();
// Add an overflow on the <details> to avoid content overflowing
this.animatedElement.style.overflow = 'hidden';
// Check if the element is being closed or is already closed
if (this.animationState === SlideRevealer.STATE.CLOSING || !this.animatedElement.hasAttribute("open")) {
// Force the [open] attributes - allow for similar targetting of xref and born-hidden knowls
this.animatedElement.setAttribute("open","");
this.triggerElement.setAttribute("open","");
this.contentElement.style.display = '';
// Trigger the animation to expand or collapse the knowl.
// Delay the MathJax typesetting until the knowl is visible to ensure proper measurements
// are taken, but before the unrolling begins. This helps avoid layout shifts and ensures
// smooth animation with correctly sized content.
MathJax.typesetPromise().then(() => window.requestAnimationFrame(() => this.toggle(true)));
} else if (this.animationState === SlideRevealer.STATE.EXPANDING || this.animatedElement.hasAttribute("open")) {
this.toggle(false);
}
}
toggle(expanding) {
let closedHeight = 0;
if (this.animatedElement.contains(this.triggerElement))
closedHeight = this.triggerElement.offsetHeight;
const fullHeight = closedHeight + this.contentElement.offsetHeight;
const startHeight = `${expanding ? closedHeight : fullHeight}px`;
const endHeight = `${expanding ? fullHeight : closedHeight}px`;
// Need to animate padding to avoid extra height for xref knowls
const padding = this.animatedElement.offsetHeight - this.animatedElement.clientHeight;
const startPad = `${expanding ? 0 : padding}px`;
const endPad = `${expanding ? padding : 0}px`;
// Cancel any existing animation
if (this.animation) {
this.animation.cancel();
}
// Animate ~400 pixels per second with max of 0.75 second and min of 0.25
const animDuration = Math.max( Math.min( (Math.abs(closedHeight - fullHeight) / 400 * 1000), 750), 250);
// Start animation
this.animationState = expanding ? SlideRevealer.STATE.EXPANDING : SlideRevealer.STATE.CLOSING;
this.animation = this.animatedElement.animate({
height: [startHeight, endHeight],
paddingTop: [startPad, endPad],
paddingBottom: [startPad, endPad]
}, {
duration: animDuration,
easing: 'ease'
});
this.animation.onfinish = () => { this.onAnimationFinish(expanding); };
this.animation.oncancel = () => { this.animationState = SlideRevealer.STATE.INACTIVE; };
}
onAnimationFinish(isOpen) {
// Clear animation state
this.animation = null;
this.animationState = SlideRevealer.STATE.INACTIVE;
// Make sure animated element has open (needed for details)
if(!isOpen) {
this.animatedElement.removeAttribute("open");
this.triggerElement.removeAttribute("open");
}
// Clear styles used in animation
this.animatedElement.style.overflow = '';
if (!isOpen)
this.contentElement.style.display = 'none';
if (isOpen) {
let hasCallback = this.contentElement.querySelectorAll("[data-knowl-callback]");
hasCallback.forEach((el) => {
window[el.getAttribute("data-knowl-callback")](el, open);
});
}
}
}
// A LinkKnowl manages a single link based knowl
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.uid = LinkKnowl.xrefCount++;
knowlLinkElement.setAttribute("data-knowl-uid", this.uid);
// Xref's behavior is that of a button
knowlLinkElement.setAttribute("role", "button");
// Stash a copy of the original title for use in aria-label
// If no title, use textContent
knowlLinkElement.setAttribute("data-base-title", knowlLinkElement.getAttribute("title") || this.linkElement.textContent);
knowlLinkElement.classList.add("knowl__link");
this.updateLabels(false);
// Bind required to force "this" of event handler to be this object
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);
// Scroll to reveal if needed
if (isActive) {
const h = this.outputElement.getBoundingClientRect().height;
if (h > window.innerHeight) {
// knowl is taller than window, scroll to top of knowl
this.outputElement.scrollIntoView(true);
} else {
// reveal full knowl
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";
// Start with the link's parent, move up as long as there are invalid parents
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) => {
// knowls are full HTML pages, need to just extract body
let knowlDoc = (new DOMParser()).parseFromString(data, "text/html");
let tempContainer = knowlDoc.body;
// grab any scripts from head of knowl doc and add them to the output
let scripts = knowlDoc.querySelectorAll("head script");
tempContainer.append(...scripts);
return tempContainer;
})
.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) {
// prevent navigation
event.preventDefault();
if (this.outputElement !== null) {
// output already created, toggle visibility
this.toggle();
} else {
this.createOutputElement();
const slideHandler = new SlideRevealer(this.linkElement, this.outputElement, this.outputElement);
//slideHandler is now responsible for handling clicks to this element
this.linkElement.addEventListener('click', slideHandler);
// Wait up to a half second in hopes of avoiding double content change
// then render to show loading message
let loadingTimeout = setTimeout(() => {
loadingTimeout = null;
slideHandler.onClick(); //fake initial click
this.toggle();
}, 500);
const content = this.getContent();
// Content is a promise at this point, insert when resolved
content
.then((tempContainer) => {
// if timeout still active, cancel it and render
if (loadingTimeout !== null) {
clearTimeout(loadingTimeout);
}
// Now give code that follows .1 seconds to render before making visible
setTimeout(() => {
slideHandler.onClick(); //fake initial click
this.toggle();
}, 100);
// check embedded runestone interactives by loading content into a temp container
// we want to not render any that already are on page. Dupe IDs probably bad
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 {
// let runestone start rendering it
window.runestoneComponents.renderOneComponent(e);
}
});
// now move all contents to the real output element
const children = [...tempContainer.children];
this.outputElement.innerHTML = "";
this.outputElement.append(...children);
// render any knowls and mathjax in the knowl
addKnowls(this.outputElement);
// try prism highlighting
Prism.highlightAllUnder(this.outputElement);
// force any scripts (e.g. sagecell) to execute by evaling them
[...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);
});
}
}
}
+48
View File
@@ -0,0 +1,48 @@
const { Configuration } = MathJax._.input.tex.Configuration;
const { CommandMap } = MathJax._.input.tex.SymbolMap;
const NodeUtil = MathJax._.input.tex.NodeUtil.default;
var GetArgumentMML = function (parser, name) {
var arg = parser.ParseArg(name);
if (!NodeUtil.isInferred(arg)) {
return arg;
}
var children = NodeUtil.getChildren(arg);
if (children.length === 1) {
return children[0];
}
var mrow = parser.create("node", "mrow");
NodeUtil.copyChildren(arg, mrow);
NodeUtil.copyAttributes(arg, mrow);
return mrow;
};
let mathjaxKnowl = {};
/**
* Implements \knowl{url}{math}
* @param {TexParser} parser The calling parser.
* @param {string} name The TeX string
*/
mathjaxKnowl.Knowl = function (parser, name) {
var url = parser.GetArgument(name);
var arg = GetArgumentMML(parser, name);
var mrow = parser.create("node", "mrow", [arg], {
tabindex: '0',
"data-knowl": url
});
parser.Push(mrow);
};
new CommandMap(
"knowl",
{
knowl: ["Knowl"]
},
mathjaxKnowl
);
const configuration = Configuration.create("knowl", {
handler: {
macro: ["knowl"]
}
});
@@ -17,6 +17,14 @@ const stackstring = {
"api_correct":"Correct answers"
};
function wrap_math(content) {
// Wrap instances of \[ ... \] and \( ... \) into the tags configured to be processed by MathJax
// Here we make sure that the backslashes are not escaped like \\[
content = content.replace(/(?<!\\)(\\\(.*?(?<!\\)\\\))/g, "<span class=\"process-math\">$1</span>");
return content.replace(/(?<!\\)(\\\[.*?(?<!\\)\\\])/g, "<span class=\"process-math\">$1</span>");
}
// Create data for call to API.
async function collectData(qfile, qname, qprefix) {
let res = "";
@@ -95,6 +103,7 @@ function send(qfile, qname, qprefix) {
// This is a bit of a hack. The question render returns an <a href="..."> calling the download function with
// two arguments. We add the additional arguments that we need for context (question definition) here.
question = question.replace(/javascript:download\(([^,]+?),([^,]+?)\)/, `javascript:download($1,$2, '${qfile}', '${qname}', '${qprefix}', ${seed})`);
question = wrap_math(question);
if (input.samplesolutionrender && name !== 'remember') {
// Display render of answer and matching user input to produce the answer.
correctAnswers += `<p>
@@ -102,7 +111,7 @@ function send(qfile, qname, qprefix) {
${stackstring['api_which_typed']}: `;
for (const [name, solution] of Object.entries(input.samplesolution)) {
if (name.indexOf('_val') === -1) {
correctAnswers += `<span class='correct-answer'>${solution}</span>`;
correctAnswers += `<span class='correct-answer'>${wrap_math(solution)}</span>`;
}
}
correctAnswers += '.</p>';
@@ -147,7 +156,7 @@ function send(qfile, qname, qprefix) {
let sampleText = json.questionsamplesolutiontext;
if (sampleText) {
sampleText = replaceFeedbackTags(sampleText,qprefix);
document.getElementById(`${qprefix+'generalfeedback'}`).innerHTML = sampleText;
document.getElementById(`${qprefix+'generalfeedback'}`).innerHTML = wrap_math(sampleText);
document.getElementById(`${qprefix+'stackapi_generalfeedback'}`).style.display = 'block';
} else {
// If the question is updated, there may no longer be general feedback.
@@ -165,7 +174,7 @@ function send(qfile, qname, qprefix) {
document.getElementById(`${qprefix+'stackapi_correct'}`).style.display = 'none';
createIframes(json.iframes);
MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
MathJax.typeset();
}
catch(e) {
console.log(e);
@@ -207,14 +216,14 @@ function validate(element, qfile, qname, qprefix) {
renameIframeHolders();
const validationHTML = json.validation;
const element = document.getElementsByName(`${qprefix+validationPrefix + answerName}`)[0];
element.innerHTML = validationHTML;
element.innerHTML = wrap_math(validationHTML);
if (validationHTML) {
element.classList.add('validation');
} else {
element.classList.remove('validation');
}
createIframes(json.iframes);
MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
MathJax.typeset();
}
catch(e) {
document.getElementById(`${qprefix+'errors'}`).innerText = http.responseText;
@@ -274,7 +283,7 @@ function answer(qfile, qname, qprefix, seed) {
json.specificfeedback = json.specificfeedback.replace(name, getPlotUrl(file));
}
json.specificfeedback = replaceFeedbackTags(json.specificfeedback,qprefix);
specificFeedbackElement.innerHTML = json.specificfeedback;
specificFeedbackElement.innerHTML = wrap_math(json.specificfeedback);
specificFeedbackElement.classList.add('feedback');
} else {
specificFeedbackElement.classList.remove('feedback');
@@ -292,7 +301,7 @@ function answer(qfile, qname, qprefix, seed) {
${(json.scores[name] * json.scoreweights[name] * json.scoreweights.total).toFixed(2)}
/ ${(json.scoreweights[name] * json.scoreweights.total).toFixed(2)}.</div>`;
}
element.innerHTML = fb;
element.innerHTML = wrap_math(fb);
// if (fb) {
// element.classList.add('feedback');
// } else {
@@ -301,7 +310,7 @@ function answer(qfile, qname, qprefix, seed) {
}
}
createIframes(json.iframes);
MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
MathJax.typeset();
}
catch(e) {
console.log(e);
+24 -20
View File
@@ -133,25 +133,6 @@ window.addEventListener("DOMContentLoaded",function(event) {
}
});
/* jump to next page if reader tries to scroll past the bottom */
// var hitbottom = false;
// window.onscroll = function(ev) {
// if ((window.innerHeight + window.scrollY) >= document.body.scrollHeight) {
// // you're at the bottom of the page
// console.log("Bottom of page");
// if (hitbottom) {
// console.log("hit bottom again");
// thenextbutton = document.getElementsByClassName("next-button")[0];
// thenextbutton.click();
// } else {
// hitbottom = true;
// /* only jump to next page if hard scroll in quick succession */
// window.scrollBy(0, -20);
// setTimeout(function (){ hitbottom = false }, 1000);
// }
// }
// };
//-----------------------------------------------------------------------------
// Dynamic TOC logic
@@ -246,7 +227,30 @@ window.addEventListener("DOMContentLoaded", function(event) {
expander.title = "Expand" + (itemType !== "" ? " " + itemType : "");
}
}
}
}
//Do we have a hash in the URL? If so, we need to identify up to make sure
// all parents of that item are expanded
if(window.location.hash) {
let hash = window.location.hash;
// find the link in the TOC that has an href ending in this hash
let hashLink = document.querySelector(`.ptx-toc a[href$="${hash}"]`);
if(hashLink) {
let parentTocItem = hashLink.closest(".toc-item");
while(parentTocItem && !parentTocItem.classList.contains("contains-active")) {
parentTocItem.classList.add("contains-active");
let expander = parentTocItem.querySelector(".toc-expander");
if(expander) {
//make sure it is expanded
if(!parentTocItem.classList.contains("expanded")) {
toggleTOCItem(expander);
}
}
parentTocItem = parentTocItem.parentElement.closest(".toc-item");
}
}
}
});
// This needs to be after the TOC's geometry is settled
+321 -304
View File
@@ -15,13 +15,6 @@
console.log("thisbrowser.userAgent", window.navigator.userAgent);
*/
var minivers = "0";
if (typeof miniversion !== 'undefined') {
console.log("typeof miniversion", typeof miniversion, "dddd", typeof miniversion == 'undefined');
minivers = miniversion.toString();
}
console.log(" minivers", minivers);
/* scrollbar width from https://stackoverflow.com/questions/13382516/getting-scroll-bar-width-using-javascript */
function getScrollbarWidth() {
var outer = document.createElement("div");
@@ -289,22 +282,8 @@ function updateURLParameter(url, param, paramVal){
return baseURL + "?" + newAdditionalURL + rows_txt;
}
function WWiframeReseed(iframe, seed) {
var this_problem = document.getElementsByName(iframe)[0];
var this_problem_url = this_problem.src;
if (seed === undefined){seed = Number(this_problem.getAttribute('data-seed')) + 80 + 84 + 88;}
this_problem.setAttribute('data-seed', seed);
this_problem_url = updateURLParameter(this_problem_url, "problemSeed", seed);
this_problem.src = this_problem_url;
}
function process_workspace() {
console.log("processing workspace");
// next does not work, because the cursor does back to the beginning
// so: need to handle the cursor
// the_text = document.activeElement.innerHTML;
// the_text = the_text.replace(/(^|\s)\$([^\$]+)\$(\s|$|[.,!?;:])/g, "\1\\(\2\\)\3")
// document.activeElement.innerHTML = the_text
MathJax.typesetPromise();
}
/* for the GeoGebra calculator */
@@ -352,17 +331,8 @@ window.addEventListener("load",function(event) {
ggbscript.id = "create_ggb_calc";
ggbscript.innerHTML = "ggbApp.inject('geogebra-calculator')";
document.body.appendChild(ggbscript);
// setTimeout( function() {
// $("#calculator-toggle").focus();
// var inputfield = $("input.gwt-SuggestBox.TextField")[0];
// console.log("inputfield", inputfield);
// inputfield.focus();
// }, 4000);
} else {
pretext_geogebra_calculator_onload();
// var inputfield = $("input.gwt-SuggestBox.TextField")[0];
// console.log("inputfield", inputfield);
// inputfield.focus();
}
} else {
$('#calculator-container').css('display', 'none');
@@ -374,16 +344,6 @@ window.addEventListener("load",function(event) {
});
/*
window.addEventListener("load",function(event) {
// setTimeout( function() {
console.log("changein play color");
$('figure > div.onclick > svg > path').attr('fill', '#0000aa');
$('path').attr('fill', '#0000aa')
// }, 5000)
});
*/
window.addEventListener("load",function(event) {
document.onkeyup = function(event)
{
@@ -402,14 +362,6 @@ window.addEventListener("load",function(event) {
console.log("staying in the sage cell", parent_sage_cell, document.activeElement)
just_hit_escape = true;
setTimeout(function(){ just_hit_escape = false }, 1000);
// console.log("parent_sage_cell", parent_sage_cell);
// if ($(parent_sage_cell).hasClass('sagecell_editor')) {
// console.log("I am trapped in a sage cell", $(document.activeElement).closest(".sagecell_editor"));
// console.log($(document.activeElement));
// var this_sage_cell = $(document.activeElement).closest(".sagecell_editor");
// this_sage_cell.next().focus;
// }
// else
} else
if(knowl_focus_stack.length > 0 ) {
most_recently_opened = knowl_focus_stack.pop();
@@ -426,144 +378,6 @@ window.addEventListener("load",function(event) {
},
false);
// a hack for hosted tracking
window.addEventListener("load",function(event) {
if($('body').attr('id') == "judson-AATA") {
console.log(" found AATA");
console.log(" looking for id");
if (typeof eBookConfig !== 'undefined') {
if(eBookConfig['username']) {
aa_id = "run" + eBookConfig['username'];
ut_id = eBookConfig['username'];
console.log(" done looking for id", ut_id);
var newscript = document.createElement('script');
newscript.type = 'text/javascript';
newscript.async = true;
newscript.src = 'https://pretextbook.org/js/' + '0.13' + '/' + 'trails' + '.js';
var allscripts = document.getElementsByTagName('script');
var s = allscripts[allscripts.length - 1];
console.log('s',s);
console.log("adding a script", newscript);
s.parentNode.insertBefore(newscript, s.nextSibling);
trail = true;
console.log(" done adding script");
} else {
console.log(" did not find username");
}
} else {
console.log(" did not find eBookConfig")
}
}
});
function loadResource(type, file) {
/* type should be js or css */
if (typeof js_version === 'undefined') { js_version = '0.2' }
if (typeof css_version === 'undefined') { css_version = '0.6' }
var newresource, allresources, s;
var linktype = "script";
if (type == "css") { linktype = "link" }
newresource = document.createElement(linktype);
if (type == "css") {
newresource.type = 'text/css';
newresource.rel = 'stylesheet';
newresource.href = 'https://pretextbook.org/css/' + css_version + '/' + file + '.css';
newresource.href += '?minivers=' + minivers;
} else if (type == "js") {
newresource.type = 'text/javascript';
// newscript.async = true;
newresource.src = 'https://pretextbook.org/js/' + js_version + '/' + file + '.js';
newresource.src += '?minivers=' + minivers;
} else {
console.log("unknown resource type", type, "for", file);
return
}
allresources = document.getElementsByTagName(linktype);
s = allresources[allresources.length - 1];
console.log('s',s);
console.log("adding a resource", newresource);
s.parentNode.insertBefore(newresource, s.nextSibling);
}
window.addEventListener("load",function(event) {
if(false && $('body').attr('id') == "pretext-SA") {
console.log(" found DMOI");
if (typeof uname === "undefined") { uname = "" }
console.log("aaaa", uname, " uname");
if(uname == "editor") {
loadResource('js', 'edit');
} else {
console.log("not enabling editing")
}
/* } else if ($('body').attr('id') == "pugetsound-SW") { */
} else if (false && window.location.href.includes("soundwriting.pugetsound")) {
/* a bunch of temporary exploration for a Sound Writing survey */
console.log("please take our survey");
console.log(window.location.href);
console.log(window.location.href.includes("soundwriting.pugetsound"));
loadResource("js", "login");
loadResource("css", "features");
setTimeout( loadResource("js", "survey"), 1000); /* I know: sloppy */
// } else if ((typeof online_editable !== 'undefined') && online_editable) {
} else if (false && $('body').attr('id') == "pretext-SA") {
loadResource('css', 'features');
loadResource('js', 'login')
loadResource('js', 'edit');
} else {
var this_source_txt;
var source_url = window.location.href;
source_url = source_url.replace(/(#|\?).*/, "");
source_url = source_url.replace(/html$/, "ptx");
if (typeof sourceeditable !== 'undefined') {
fetch(source_url).then(
function(u){ return u.text();}
).then(
function(text){
this_source_txt = text;
if (this_source_txt.includes("404 Not")) {
console.log("Editing not enabled: source unavailable")
} else {
loadResource('css', 'features');
loadResource('css', 'edit');
loadResource('js', 'login')
loadResource('js', 'edit');
}
}
);
} else {
console.log("Source file unavailable: editing not possible")
}
}
});
// this is to open every knowl on a page
// (this code is not actually used anywhere)
window.addEventListener("load",function(event) {
if($('body').hasClass("braillesample")) {
var knowl_id_counterX = 0;
console.log(" found braillesample");
var all_knowls = $('[data-knowl]');
console.log("found", all_knowls.length, "knowls");
console.log("which are", all_knowls);
for (var j=1; j < all_knowls.length; ++j) {
console.log(j, "un-knowling", all_knowls[j]);
console.log("attr", $(all_knowls[j]).attr("data-knowl"));
$knowl = $(all_knowls[j]);
if(!$knowl.attr("data-knowl-uid")) {
$knowl.attr("data-knowl-uid", knowl_id_counterX);
knowl_id_counterX++;
}
knowl_click_handler($knowl);
// knowl_click_handler($(all_knowls[j]))
}
}});
// when the anchor is a knowl, open it
window.addEventListener("load",function(event) {
@@ -594,13 +408,6 @@ window.addEventListener("load",function(event) {
});
// What purpose does this serve?
function urlattribute() {
var this_urlstub = window.location.hostname;
document.body.setAttribute("data-urlstub", this_urlstub);
}
// The new method for creating pages and adjusting workspace //
// This is used multiple places to set height of workspace divs to their author-provided heights
@@ -614,6 +421,7 @@ function setInitialWorkspaceHeights() {
// If a printout (worksheet or handout) includes authored pages, we only need to put content before the first page and after the last page into the first and last pages, respectively.
function adjustPrintoutPages() {
console.log("*** Adjusting printout pages.");
const printout = document.querySelector('section.worksheet, section.handout');
if (!printout) {
console.warn("No printout found, exiting adjustPrintoutPages.");
@@ -647,6 +455,7 @@ function adjustPrintoutPages() {
// This is the main function we will call then a printout does not come from the XSL with pages already defined (for now, the XSL will keep the <page> behavior as an option).
function createPrintoutPages(margins) {
console.log("*** Creating printout pages with margins:", margins);
// Assumptions: needs to work for both letter (8.5in x 11in) and a4 (210mm x 297mm) paper sizes. We will work in pixels (96/in): those are 816px x 1056px and 794px x 1122.5px respectively (1 inch = 96 px, 1 cm = 37.8 px). We assume that the printing interface of the browser will do the right thing with these.
@@ -679,16 +488,6 @@ function createPrintoutPages(margins) {
printout.insertBefore(tasks[i], child.nextSibling);
}
// Skipping separate treatment of exercisegroups for now.
//} else if (child.classList.contains('exercisegroup')) {
// for (const subChild of child.children) {
// if (subChild.classList.contains('exercisegroup-exercises')){
// for (const row of subChild.children){
// rows.push(row);
// }
// } else {
// rows.push(child);
// }
// }
} else {
rows.push(child);
}
@@ -743,10 +542,90 @@ function createPrintoutPages(margins) {
}
}
// Add headers and footers to all pages in a printout. Start with this set to be hidden by default; a toggle later will show/hide them.
function addHeadersAndFootersToPrintout() {
const printout = document.querySelector('section.worksheet, section.handout');
if (!printout) {
console.warn("No printout found, exiting addHeadersAndFootersToPrintout.");
return;
}
const pages = printout.querySelectorAll('.onepage');
// Loop through pages and add header and footer divs
pages.forEach((page, index) => {
const isFirstPage = index === 0;
// Add header
const headerDiv = document.createElement('div');
headerDiv.classList.add(isFirstPage ? 'first-page-header' : 'running-header', 'hidden');
headerDiv.innerHTML = `<div class="header-left" contenteditable="true"></div><div class="header-center" contenteditable="true"></div><div class="header-right" contenteditable="true"></div>`;
page.insertBefore(headerDiv, page.firstChild);
// Add footer
const footerDiv = document.createElement('div');
footerDiv.classList.add(isFirstPage ? 'first-page-footer' : 'running-footer', 'hidden');
footerDiv.innerHTML = `<div class="footer-left" contenteditable="true"></div><div class="footer-center" contenteditable="true"></div><div class="footer-right" contenteditable="true"></div>`;
page.appendChild(footerDiv);
});
// Add content based on local storage if available, otherwise from data-attributes on the printout
const headerFooterKeys = ['header-first-left', 'header-first-center', 'header-first-right', 'footer-first-left', 'footer-first-center', 'footer-first-right', 'header-running-left', 'header-running-center', 'header-running-right', 'footer-running-left', 'footer-running-center', 'footer-running-right'];
const headerFooterContent = {};
headerFooterKeys.forEach(key => {
headerFooterContent[key] = localStorage.getItem(key) || printout.getAttribute(`data-${key}`) || '';
});
// First page header and footer
document.querySelector('.first-page-header').querySelector('.header-left').innerHTML = headerFooterContent['header-first-left'];
document.querySelector('.first-page-header').querySelector('.header-center').innerHTML = headerFooterContent['header-first-center'];
document.querySelector('.first-page-header').querySelector('.header-right').innerHTML = headerFooterContent['header-first-right'];
document.querySelector('.first-page-footer').querySelector('.footer-left').innerHTML = headerFooterContent['footer-first-left'];
document.querySelector('.first-page-footer').querySelector('.footer-center').innerHTML = headerFooterContent['footer-first-center'];
document.querySelector('.first-page-footer').querySelector('.footer-right').innerHTML = headerFooterContent['footer-first-right'];
// Running headers and footers
document.querySelectorAll('.running-header').forEach(headerDiv => {
headerDiv.querySelector('.header-left').innerHTML = headerFooterContent['header-running-left'];
headerDiv.querySelector('.header-center').innerHTML = headerFooterContent['header-running-center'];
headerDiv.querySelector('.header-right').innerHTML = headerFooterContent['header-running-right'];
});
document.querySelectorAll('.running-footer').forEach(footerDiv => {
footerDiv.querySelector('.footer-left').innerHTML = headerFooterContent['footer-running-left'];
footerDiv.querySelector('.footer-center').innerHTML = headerFooterContent['footer-running-center'];
footerDiv.querySelector('.footer-right').innerHTML = headerFooterContent['footer-running-right'];
});
// Add event listeners to update local storage when content is edited
headerFooterKeys.forEach(key => {
const selectorMap = {
'header-first-left': '.first-page-header .header-left',
'header-first-center': '.first-page-header .header-center',
'header-first-right': '.first-page-header .header-right',
'footer-first-left': '.first-page-footer .footer-left',
'footer-first-center': '.first-page-footer .footer-center',
'footer-first-right': '.first-page-footer .footer-right',
'header-running-left': '.running-header .header-left',
'header-running-center': '.running-header .header-center',
'header-running-right': '.running-header .header-right',
'footer-running-left': '.running-footer .footer-left',
'footer-running-center': '.running-footer .footer-center',
'footer-running-right': '.running-footer .footer-right'
};
const elements = document.querySelectorAll(selectorMap[key]);
elements.forEach(elem => {
elem.addEventListener('input', () => {
localStorage.setItem(key, elem.innerHTML);
});
});
});
}
// We look at each page and adjust the heights of the workspaces to fit it nicely into the page.
// The width and height of the page will now depend on the letter or a4 setting.
// We look at each page and adjust the heights of the workspaces to fit it nicely into the page.
// The width and height of the page will now depend on the letter or a4 setting.
function adjustWorkspaceToFitPage({paperSize, margins}) {
console.log("*** Adjusting workspace to fit page size:", paperSize, "with margins:", margins);
// Toggle off workspace highlight if it is on, so it doesn't interfere with resizing
const highlightWorkspaceCheckbox = document.getElementById("highlight-workspace-checkbox");
const wasHighlighted = highlightWorkspaceCheckbox && highlightWorkspaceCheckbox.checked;
if (wasHighlighted) {
toggleWorkspaceHighlight(false);
}
let paperWidth, paperHeight;
if (paperSize === 'a4' || document.body.classList.contains('a4')) {
console.log("Setting page size to A4");
@@ -796,6 +675,11 @@ function adjustWorkspaceToFitPage({paperSize, margins}) {
page.style.width = "";
});
console.log("Set page sizes to content area of paper size.");
// Reset the highlight workspace checkbox state
if (wasHighlighted) {
toggleWorkspaceHighlight(true);
}
}
// Helper functions for calculating heights and workspace sizes
@@ -855,6 +739,7 @@ function getElemWorkspaceHeight(elem) {
// Functions for finding the optimal page breaks
function findPageBreaks(rows, pageHeight) {
console.log("*** Finding page breaks for", rows.length, "rows with page height:", pageHeight);
// An array for the page breaks. The nth element will be the index of the last row on page n.
let pageBreaks = [];
// An array for the minimum cost possible for rows i to the end.
@@ -901,7 +786,9 @@ function findPageBreaks(rows, pageHeight) {
return pageBreaks;
}
// Function to set CSS variables and @page rules for page geometry. This will be called whenever the paper size or margins change (in practice, only when page size changes, since margins are fixed for now).
function setPageGeometryCSS({paperSize, margins}) {
console.log("*** Setting page geometry CSS for paper size:", paperSize, "with margins:", margins);
// Remove any existing geometry CSS to avoid duplicates
const existingStyle = document.getElementById("page-geometry-css");
if (existingStyle) {
@@ -948,7 +835,6 @@ function toggleWorkspaceHighlight(isChecked) {
original.classList.add('original-workspace');
const originalHeight = workspace.getAttribute('data-space') || '0px';
original.setAttribute('title', 'Author-specified workspace height (' + originalHeight + ')');
console.log("setting original workspace height for", workspace);
// Use the data-space attribute for height of original workspace
original.style.height = originalHeight;
// insert original div before the workspace content
@@ -964,55 +850,24 @@ function toggleWorkspaceHighlight(isChecked) {
}
} else {
document.body.classList.remove("highlight-workspace");
// Remove the original workspace divs. We don't want to keep these in, as they interfere with changing page sizes and workspace heights.
document.querySelectorAll('.workspace-container').forEach(container => {
const workspace = container.querySelector('.workspace');
// Move the workspace out of the container
container.parentNode.insertBefore(workspace, container);
// Remove the container
container.remove();
});
}
}
// Printout print preview and page setup
window.addEventListener("load",function(event) {
// We condition on the existence of the papersize radio buttons, which only appear in the printout print preview.
if (document.querySelector('input[name="papersize"]')) {
// First, get the margins for pages to be passed around as needed.
const marginList = document.querySelector('section.worksheet, section.handout').getAttribute('data-margins').split(' ');
// Convert margin values to pixels if they are not already numbers
function toPixels(value) {
if (typeof value === "number") return value;
if (typeof value !== "string") return 0;
value = value.trim();
if (value.endsWith("px")) {
return parseFloat(value);
} else if (value.endsWith("in")) {
return Math.floor(parseFloat(value) * 96);
} else if (value.endsWith("cm")) {
return Math.floor(parseFloat(value) * 37.8);
} else if (value.endsWith("mm")) {
return Math.floor(parseFloat(value) * 3.78);
} else if (value.endsWith("pt")) {
return Math.floor(parseFloat(value) * (96 / 72));
} else {
// fallback: try to parse as px
return parseFloat(value) || 0;
}
}
const margins = {
top: toPixels(marginList[0] || "0.75in"), // Default to 0.75in if not specified
right: toPixels(marginList[1] || "0.75in"),
bottom: toPixels(marginList[2] || "0.75in"),
left: toPixels(marginList[3] || "0.75in")
}
// Get the papersize from localStorage or set it based on user's geographic region
function getPaperSize() {
let paperSize = localStorage.getItem("papersize");
if (paperSize) {
const radio = document.querySelector(`input[name="papersize"][value="${paperSize}"]`);
if (radio) {
radio.checked = true;
}
// Set the papersize class on body
document.body.classList.remove("a4", "letter");
document.body.classList.add(paperSize);
setPageGeometryCSS({paperSize: paperSize, margins: margins});
return paperSize;
} else {
// Try to set papersize based on user's geographic region
// Default to 'letter' for North and South America, 'a4' elsewhere
// Try to set papersize based on user's geographic region
// Default to 'letter' for North and South America, 'a4' elsewhere
try {
fetch('https://ipapi.co/json/')
.then(response => response.json())
@@ -1037,69 +892,231 @@ window.addEventListener("load",function(event) {
const radio = document.querySelector(`input[name="papersize"][value="letter"]`);
if (radio) radio.checked = true;
}
//NB: the default papersize is set to 'letter' in the body class list.
}
const papersizeRadios = document.querySelectorAll('input[name="papersize"]');
papersizeRadios.forEach(radio => {
radio.addEventListener('change', function() {
if (this.checked) {
document.body.classList.remove("a4", "letter");
document.body.classList.add(this.value);
localStorage.setItem("papersize", this.value);
console.log("Setting papersize to", this.value);
return paperSize || "letter";
}
// If the "highlight workspace" checkbox was already checked, then we should restart the process by reloading the page. Specifically, we run into issues when there are .workspace-container divs already present.
if (document.querySelector(".workspace-container")) {
console.log("Reloading page to apply new papersize with workspace highlight enabled.");
window.location.reload();
return;
} else {
// Otherwise, we can just adjust the workspace heights to fit the new paper size.
console.log("Adjusting workspace heights to fit new papersize.");
adjustWorkspaceToFitPage({paperSize: this.value, margins: margins});
setPageGeometryCSS({paperSize: this.value, margins: margins});
}
}
});
});
// Function to load the printout section and switch to print stylesheet. This will run whenever a user clicks on a print preview link (which adds ?printpreview=sectionID to the URL).
async function loadPrintout(printableSectionID) {
// Open all details elements (knowls) on the page
var born_hidden_knowls = document.querySelectorAll('details');
console.log("born_hidden_knowls", born_hidden_knowls);
born_hidden_knowls.forEach(function(detail) {
detail.open = true;
});
// If the printout has authored pages, there will be at least one .onepage element.
if (document.querySelector('.onepage')) {
adjustPrintoutPages();
/* not the right way: need to figure out what this needs to wait for */
//window.setTimeout(adjustPrintoutPages, 1000);
} else {
createPrintoutPages(margins);
}
// After pages are set up, we adjust the workspace heights to fit the page (based on the paper size).
adjustWorkspaceToFitPage({paperSize: paperSize, margins: margins});
console.log("finished adjusting workspace");
// Get the 'highlight workspace' checkbox state from localStorage or set it to false by default
const highlightWorkspaceCheckbox = document.getElementById("highlight-workspace-checkbox");
if (highlightWorkspaceCheckbox) {
highlightWorkspaceCheckbox.checked = localStorage.getItem("highlightWorkspace") === "true";
highlightWorkspaceCheckbox.addEventListener("change", function() {
localStorage.setItem("highlightWorkspace", this.checked);
toggleWorkspaceHighlight(this.checked);
// Switch to print-worksheet.css for print preview
const themeStylesheetLink = document.querySelector('link[rel="stylesheet"][href*="theme"]');
// get the href of the theme stylesheet link
const themeStylesheetHref = themeStylesheetLink ? themeStylesheetLink.getAttribute('href') : null;
if (themeStylesheetHref) {
// replace 'theme.css' with 'print-worksheet.css' in the href
const printStylesheetHref = themeStylesheetHref.replace(/theme.*\.css/, 'print-worksheet.css');
// update the href of the theme stylesheet link
themeStylesheetLink.setAttribute('href', printStylesheetHref);
// Wait for the new stylesheet to load. This is important to ensure the styles are applied before the calling function tries to compute workspace sizes.
await new Promise((resolve) => {
themeStylesheetLink.addEventListener('load', resolve, { once: true });
});
// Initial toggle to apply the highlight class if checked
toggleWorkspaceHighlight(highlightWorkspaceCheckbox.checked);
}
// Find the section with this ID
const printableSection = document.getElementById(printableSectionID);
if (!printableSection) {
console.error("No section found with ID:", printableSectionID);
return;
}
// Remove any existing sections from .ptx-content and add only the printable section
const ptxContent = document.querySelector('.ptx-content');
const existingSections = ptxContent.querySelectorAll(':scope > section');
existingSections.forEach(sec => ptxContent.removeChild(sec));
ptxContent.appendChild(printableSection);
}
// Function to redo solutions details to divs with summary as title
function rewriteSolutions() {
var born_hidden_knowls = document.querySelectorAll('.worksheet details, .handout details');
born_hidden_knowls.forEach(function(detail) {
const summary = detail.querySelector('summary');
const content = detail.innerHTML.replace(summary.outerHTML, '');
const div = document.createElement('div');
div.classList = detail.classList;
if (summary) {
const title = document.createElement('h5');
title.innerHTML = summary.innerHTML;
div.appendChild(title);
}
const body = document.createElement('div');
body.innerHTML = content;
div.appendChild(body);
detail.parentNode.replaceChild(div, detail);
});
}
// Not sure why this is here:
window.setTimeout(urlattribute, 1500);
}
// Utility to convert various CSS length units to pixels
function toPixels(value) {
if (typeof value === "number") return value;
if (typeof value !== "string") return 0;
value = value.trim();
if (value.endsWith("px")) {
return parseFloat(value);
} else if (value.endsWith("in")) {
return Math.floor(parseFloat(value) * 96);
} else if (value.endsWith("cm")) {
return Math.floor(parseFloat(value) * 37.8);
} else if (value.endsWith("mm")) {
return Math.floor(parseFloat(value) * 3.78);
} else if (value.endsWith("pt")) {
return Math.floor(parseFloat(value) * (96 / 72));
} else {
// fallback: try to parse as px
return parseFloat(value) || 0;
}
}
// Event listener for page load to handle print preview setup
window.addEventListener("DOMContentLoaded", async function(event) {
const urlParams = new URLSearchParams(window.location.search);
// We condition on the existence of the papersize radio buttons, which only appear in the printout print preview.
if (urlParams.has("printpreview")) {
const printableSectionID = urlParams.get("printpreview");
await loadPrintout(printableSectionID);
// First, get the margins for pages to be passed around as needed.
const marginList = document.querySelector('section.worksheet, section.handout').getAttribute('data-margins').split(' ');
// Convert margin values to pixels if they are not already numbers
const margins = {
top: toPixels(marginList[0] || "0.75in"), // Default to 0.75in if not specified
right: toPixels(marginList[1] || "0.75in"),
bottom: toPixels(marginList[2] || "0.75in"),
left: toPixels(marginList[3] || "0.75in")
}
// Transform all solutions details elements to divs with the summary as a title
rewriteSolutions();
// Get the papersize from localStorage or set it based on user's geographic region. This will always return a value (defaulting to 'letter' if all else fails).
let paperSize = getPaperSize();
if (paperSize) {
const radio = document.querySelector(`input[name="papersize"][value="${paperSize}"]`);
if (radio) {
radio.checked = true;
}
// Set the papersize class on body
document.body.classList.remove("a4", "letter");
document.body.classList.add(paperSize);
setPageGeometryCSS({paperSize: paperSize, margins: margins});
} else {
console.warning("Bug: paperSize should always have a value here.");
}
// Add event listeners to the papersize radio buttons to handle changes
const papersizeRadios = document.querySelectorAll('input[name="papersize"]');
papersizeRadios.forEach(radio => {
radio.addEventListener('change', function() {
if (this.checked) {
document.body.classList.remove("a4", "letter");
document.body.classList.add(this.value);
localStorage.setItem("papersize", this.value);
setPageGeometryCSS({paperSize: this.value, margins: margins});
adjustWorkspaceToFitPage({paperSize: this.value, margins: margins});
}
});
});
// Add event listeners to the hide hints/answers/solutions checkboxes
for (const solutionType of ["hint", "answer", "solution"]) {
const checkbox = document.getElementById(`hide-${solutionType}-checkbox`);
if (checkbox) {
const storageKey = `hide-${solutionType}`;
// by default, hide answer and solution divs
if (solutionType === "answer" || solutionType === "solution") {
if (!localStorage.getItem(storageKey)) {
checkbox.checked = true;
localStorage.setItem(storageKey, "true");
}
}
// Now adjust based on local storage
// set visibility based on current checkbox state
checkbox.checked = localStorage.getItem(storageKey) === "true";
document.querySelectorAll(`div.${solutionType}`).forEach(elem => {
// add hidden to class list
if (checkbox.checked) {
elem.classList.add("hidden");
} else {
elem.classList.remove("hidden");
}
});
// Add event listener to toggle visibility
checkbox.addEventListener("change", function() {
localStorage.setItem(storageKey, this.checked);
// toggle visibility of solution divs
document.querySelectorAll(`div.${solutionType}`).forEach(elem => {
if (checkbox.checked) {
elem.classList.add("hidden");
} else {
elem.classList.remove("hidden");
}
//adjustPrintoutPages();
adjustWorkspaceToFitPage({paperSize: paperSize, margins: margins});
});
});
}
}
// Finally, with everything set up, we create or adjust the printout pages as needed.
// If the printout has authored pages, there will be at least one .onepage element.
if (document.querySelector('.onepage')) {
adjustPrintoutPages();
} else {
createPrintoutPages(margins);
}
// Add headers and footers to all pages in the printout
addHeadersAndFootersToPrintout();
// Add event listeners to the print header/footer checkboxes
for (const hf of ["first-page-header", "running-header", "first-page-footer", "running-footer"]) {
const checkbox = document.getElementById(`print-${hf}-checkbox`);
if (checkbox) {
// set visibility based on current checkbox state
checkbox.checked = localStorage.getItem(`print-${hf}`) === "true";
document.querySelectorAll(`.${hf}`).forEach(elem => {
// add hidden to class list
if (checkbox.checked) {
elem.classList.remove("hidden");
} else {
elem.classList.add("hidden");
}
});
// Add event listener to toggle visibility
checkbox.addEventListener("change", function() {
localStorage.setItem(`print-${hf}`, this.checked);
// toggle visibility of header/footer divs
document.querySelectorAll(`.${hf}`).forEach(elem => {
if (checkbox.checked) {
elem.classList.remove("hidden");
} else {
elem.classList.add("hidden");
}
adjustWorkspaceToFitPage({paperSize: paperSize, margins: margins});
});
});
}
}
// After pages are set up, we adjust the workspace heights to fit the page (based on the paper size).
adjustWorkspaceToFitPage({paperSize: paperSize, margins: margins});
// Get the 'highlight workspace' checkbox state from localStorage or set it to false by default
// NB we need to do this after the adjustment of workspace heights so that the additional original workspace divs don't throw off the calculations when the page is reloaded.
const highlightWorkspaceCheckbox = document.getElementById("highlight-workspace-checkbox");
if (highlightWorkspaceCheckbox) {
highlightWorkspaceCheckbox.checked = localStorage.getItem("highlightWorkspace") === "true";
highlightWorkspaceCheckbox.addEventListener("change", function() {
localStorage.setItem("highlightWorkspace", this.checked);
toggleWorkspaceHighlight(this.checked);
});
// Initial toggle to apply the highlight class if checked
toggleWorkspaceHighlight(highlightWorkspaceCheckbox.checked);
}
console.log("finished adjusting workspace");
}
});
+1 -1
View File
@@ -1 +1 @@
{"js":["prefix-runtime.fc0e080e84d54281.bundle.js","prefix-723.3e6434f80549315a.bundle.js","prefix-runestone.e2abc79debce15fd.bundle.js"],"css":["prefix-723.3bccd435914aa0ff.css","prefix-runestone.6ec5960970d8b364.css"]}
{"js":["prefix-runtime.0d5f811896ddeac1.bundle.js","prefix-723.3e6434f80549315a.bundle.js","prefix-runestone.e2abc79debce15fd.bundle.js"],"css":["prefix-723.3bccd435914aa0ff.css","prefix-runestone.6ec5960970d8b364.css"]}
Binary file not shown.
+2 -2
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" ?>
<all>
<js type="list">
<item type="str">prefix-runtime.fc0e080e84d54281.bundle.js</item>
<item type="str">prefix-runtime.0d5f811896ddeac1.bundle.js</item>
<item type="str">prefix-723.3e6434f80549315a.bundle.js</item>
<item type="str">prefix-runestone.e2abc79debce15fd.bundle.js</item>
</js>
@@ -10,5 +10,5 @@
<item type="str">prefix-runestone.6ec5960970d8b364.css</item>
</css>
<cdn-url type="str">https://runestone.academy/cdn/runestone/</cdn-url>
<version type="str">7.11.11</version>
<version type="str">7.11.13</version>
</all>