616 lines
22 KiB
JavaScript
616 lines
22 KiB
JavaScript
/*
|
|
* ANIMATION STORYBOARD
|
|
*
|
|
* 0ms old course run starts, all weeks reset
|
|
* 260ms next week activates, student log receives one consequence
|
|
* 1200ms active week reaches exam signal, learner state updates
|
|
* 2400ms run ends with diagnosis visible in course file
|
|
*/
|
|
const TIMING = {
|
|
oldCourseStep: 260,
|
|
feedbackDelay: 900
|
|
};
|
|
|
|
const $ = (selector, root = document) => root.querySelector(selector);
|
|
const $$ = (selector, root = document) => Array.from(root.querySelectorAll(selector));
|
|
|
|
const course = {
|
|
outcome: null,
|
|
practice: null,
|
|
assessment: null,
|
|
feedback: null,
|
|
opening: null,
|
|
classMoves: {},
|
|
memory: new Set()
|
|
};
|
|
|
|
const labels = {
|
|
outcome: {
|
|
vague: "understand trees, graphs, and hashing",
|
|
usable: "choose and justify a data structure for a stated constraint",
|
|
tooBig: "design industrial-scale storage systems from scratch"
|
|
},
|
|
practice: {
|
|
lecture: "lecture examples only",
|
|
compare: "compare structures for a constraint",
|
|
debug: "debug a bad choice in pairs"
|
|
},
|
|
assessment: {
|
|
definitions: "definition-heavy exam",
|
|
justify: "scenario justification task",
|
|
project: "mini-project with design memo"
|
|
},
|
|
feedback: {
|
|
late: "after the midterm",
|
|
quick: "same-class comparison feedback",
|
|
revision: "revision attempt after feedback"
|
|
},
|
|
opening: {
|
|
rules: "grading rules first",
|
|
mystery: "real design failure first",
|
|
support: "outcome, practice, and help route first"
|
|
},
|
|
move: {
|
|
hook: "opening problem",
|
|
predict: "prediction",
|
|
pair: "pair talk",
|
|
quiz: "quick quiz",
|
|
paper: "minute paper"
|
|
}
|
|
};
|
|
|
|
function clamp(value, min, max) {
|
|
return Math.max(min, Math.min(max, value));
|
|
}
|
|
|
|
function setText(selector, text) {
|
|
const node = typeof selector === "string" ? $(selector) : selector;
|
|
if (node) node.textContent = text;
|
|
}
|
|
|
|
function initProgress() {
|
|
const bar = $(".progress-line span");
|
|
const update = () => {
|
|
const max = document.documentElement.scrollHeight - window.innerHeight;
|
|
const percent = max <= 0 ? 0 : (window.scrollY / max) * 100;
|
|
bar.style.width = `${clamp(percent, 0, 100)}%`;
|
|
};
|
|
update();
|
|
window.addEventListener("scroll", update, { passive: true });
|
|
}
|
|
|
|
function initNav() {
|
|
const links = $$(".topbar nav a");
|
|
const byId = new Map(links.map((link) => [link.getAttribute("href").slice(1), link]));
|
|
const observer = new IntersectionObserver(
|
|
(entries) => {
|
|
entries.forEach((entry) => {
|
|
if (!entry.isIntersecting) return;
|
|
links.forEach((link) => link.classList.remove("active"));
|
|
const link = byId.get(entry.target.id);
|
|
if (link) link.classList.add("active");
|
|
});
|
|
},
|
|
{ rootMargin: "-36% 0px -54% 0px", threshold: 0.01 }
|
|
);
|
|
links.forEach((link) => {
|
|
const section = $(link.getAttribute("href"));
|
|
if (section) observer.observe(section);
|
|
});
|
|
}
|
|
|
|
function initWeeks() {
|
|
const track = $("#weekTrack");
|
|
if (!track) return;
|
|
track.replaceChildren();
|
|
for (let i = 1; i <= 12; i += 1) {
|
|
const week = document.createElement("div");
|
|
week.className = "week";
|
|
week.textContent = `W${i}`;
|
|
track.append(week);
|
|
}
|
|
}
|
|
|
|
function runOldCourse() {
|
|
const weeks = $$(".week");
|
|
const log = $("#studentLog");
|
|
const entries = [
|
|
"Week 2: Maya copies the list implementation. It works, but she cannot explain the design choice.",
|
|
"Week 5: Trees arrive. The course has moved on before her earlier misconception is visible.",
|
|
"Week 8: The midterm asks definitions. Her effort shifts toward remembering names.",
|
|
"Week 12: She passes many recall items. Transfer is still fragile."
|
|
];
|
|
weeks.forEach((week) => week.classList.remove("active"));
|
|
log.replaceChildren();
|
|
setText(log.appendChild(document.createElement("p")), "The run starts from the topic list.");
|
|
|
|
let i = 0;
|
|
const timer = window.setInterval(() => {
|
|
if (i > 0) weeks[i - 1]?.classList.remove("active");
|
|
if (i >= weeks.length) {
|
|
window.clearInterval(timer);
|
|
updateLearnerNotes("diagnosed");
|
|
updateCourseFile();
|
|
return;
|
|
}
|
|
weeks[i].classList.add("active");
|
|
if ([1, 4, 7, 11].includes(i)) {
|
|
const p = document.createElement("p");
|
|
p.textContent = entries.shift();
|
|
log.append(p);
|
|
}
|
|
i += 1;
|
|
}, TIMING.oldCourseStep);
|
|
}
|
|
|
|
function outcomeFeedback(kind) {
|
|
const node = $("#outcomeFeedback");
|
|
const messages = {
|
|
vague: [
|
|
"This is a topic in disguise.",
|
|
"It does not tell you what students must produce, so assessment will drift."
|
|
],
|
|
usable: [
|
|
"This can design the course.",
|
|
"It names a student action, a judgment, and a realistic course-level capability."
|
|
],
|
|
tooBig: [
|
|
"This overpromises.",
|
|
"Ambition helps only when students can practice the component skills inside the course."
|
|
]
|
|
};
|
|
const [title, detail] = messages[kind];
|
|
node.innerHTML = `<strong>${title}</strong><span>${detail}</span>`;
|
|
}
|
|
|
|
function initOutcomes() {
|
|
$$("#outcomeChoices .choice-card").forEach((button) => {
|
|
button.addEventListener("click", () => {
|
|
$$("#outcomeChoices .choice-card").forEach((item) => item.classList.toggle("selected", item === button));
|
|
course.outcome = button.dataset.outcome;
|
|
outcomeFeedback(course.outcome);
|
|
updateLearnerNotes("outcome");
|
|
updateCourseFile();
|
|
updateAlignmentMap();
|
|
updatePlan();
|
|
});
|
|
});
|
|
}
|
|
|
|
function initAlignmentControls() {
|
|
$$("#practiceChoices button").forEach((button) => {
|
|
button.addEventListener("click", () => {
|
|
$$("#practiceChoices button").forEach((item) => item.classList.toggle("selected", item === button));
|
|
course.practice = button.dataset.practice;
|
|
updateAlignmentMap();
|
|
updateLearnerNotes("alignment");
|
|
updateCourseFile();
|
|
updatePlan();
|
|
});
|
|
});
|
|
$$("#assessmentChoices button").forEach((button) => {
|
|
button.addEventListener("click", () => {
|
|
$$("#assessmentChoices button").forEach((item) => item.classList.toggle("selected", item === button));
|
|
course.assessment = button.dataset.assessment;
|
|
updateAlignmentMap();
|
|
updateLearnerNotes("alignment");
|
|
updateCourseFile();
|
|
updatePlan();
|
|
});
|
|
});
|
|
}
|
|
|
|
function updateAlignmentMap() {
|
|
const outcome = course.outcome ? labels.outcome[course.outcome] : "not written";
|
|
const practice = course.practice ? labels.practice[course.practice] : "listening";
|
|
const assessment = course.assessment ? labels.assessment[course.assessment] : "recall exam";
|
|
const feedback = course.feedback ? labels.feedback[course.feedback] : "after exam";
|
|
|
|
setText("#mapOutcome", shorten(outcome, 24));
|
|
setText("#mapPractice", shorten(practice, 24));
|
|
setText("#mapAssessment", shorten(assessment, 24));
|
|
setText("#mapFeedback", shorten(feedback, 24));
|
|
|
|
const goodOutcome = course.outcome === "usable";
|
|
const goodPractice = course.practice === "compare" || course.practice === "debug";
|
|
const goodAssessment = course.assessment === "justify" || course.assessment === "project";
|
|
const goodFeedback = course.feedback === "quick" || course.feedback === "revision";
|
|
|
|
$(".wire-a")?.classList.toggle("strong", goodOutcome);
|
|
$(".wire-b")?.classList.toggle("strong", goodPractice);
|
|
$(".wire-c")?.classList.toggle("strong", goodAssessment);
|
|
$(".wire-d")?.classList.toggle("strong", goodFeedback);
|
|
$(".wire-c")?.classList.toggle("warn", course.assessment === "definitions");
|
|
|
|
const hidden = $("#hiddenCourse");
|
|
if (!course.assessment || course.assessment === "definitions") {
|
|
hidden.classList.remove("good");
|
|
hidden.innerHTML = "<strong>The hidden course is still recall.</strong><span>Students will protect marks by memorizing definitions.</span>";
|
|
} else if (goodOutcome && goodPractice && goodAssessment) {
|
|
hidden.classList.add("good");
|
|
hidden.innerHTML = "<strong>The hidden course now matches the stated course.</strong><span>Students can see that practice and marks both point toward justified choice.</span>";
|
|
} else {
|
|
hidden.classList.remove("good");
|
|
hidden.innerHTML = "<strong>The signals are mixed.</strong><span>One corner has improved, but students still receive competing messages about effort.</span>";
|
|
}
|
|
}
|
|
|
|
function shorten(text, limit) {
|
|
return text.length > limit ? `${text.slice(0, limit - 3)}...` : text;
|
|
}
|
|
|
|
function initClassBuilder() {
|
|
const palette = $$("#movePalette button");
|
|
const slotRow = $("#slotRow");
|
|
let selectedMove = null;
|
|
|
|
palette.forEach((button) => {
|
|
button.addEventListener("click", () => {
|
|
selectedMove = button.dataset.move;
|
|
palette.forEach((item) => item.classList.toggle("selected", item === button));
|
|
});
|
|
});
|
|
|
|
slotRow.replaceChildren();
|
|
[0, 10, 20, 30, 40, 50].forEach((minute) => {
|
|
const slot = document.createElement("button");
|
|
slot.type = "button";
|
|
slot.className = "slot";
|
|
slot.dataset.minute = String(minute);
|
|
slot.textContent = `${minute} min`;
|
|
slot.addEventListener("click", () => {
|
|
if (!selectedMove) {
|
|
setText("#classFeedback", "Choose a move first, then place it in the hour.");
|
|
return;
|
|
}
|
|
course.classMoves[minute] = selectedMove;
|
|
slot.classList.add("filled");
|
|
slot.textContent = `${minute}: ${labels.move[selectedMove]}`;
|
|
updateAttention();
|
|
updateLearnerNotes("class");
|
|
updateCourseFile();
|
|
updatePlan();
|
|
});
|
|
slotRow.append(slot);
|
|
});
|
|
|
|
updateAttention();
|
|
}
|
|
|
|
function attentionAt(minute) {
|
|
const placed = Object.entries(course.classMoves)
|
|
.map(([key, value]) => ({ minute: Number(key), move: value }))
|
|
.sort((a, b) => a.minute - b.minute);
|
|
const previous = placed.filter((item) => item.minute <= minute).pop();
|
|
const since = previous ? minute - previous.minute : minute;
|
|
const reset = previous && since < 5 ? 11 : 0;
|
|
const endLift = minute > 54 ? (minute - 54) * 2 : 0;
|
|
return clamp(86 - since * 2.15 + reset + endLift, 26, 94);
|
|
}
|
|
|
|
function updateAttention() {
|
|
const path = $("#attentionCurve");
|
|
const markers = $("#attentionMarkers");
|
|
if (!path || !markers) return;
|
|
const points = [];
|
|
for (let minute = 0; minute <= 60; minute += 2) {
|
|
const x = 42 + (minute / 60) * 700;
|
|
const y = 232 - (attentionAt(minute) / 100) * 190;
|
|
points.push(`${x},${y}`);
|
|
}
|
|
path.setAttribute("d", `M ${points.join(" L ")}`);
|
|
markers.replaceChildren();
|
|
Object.entries(course.classMoves).forEach(([minute, move]) => {
|
|
const x = 42 + (Number(minute) / 60) * 700;
|
|
const y = 232 - (attentionAt(Number(minute)) / 100) * 190;
|
|
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
circle.setAttribute("cx", x);
|
|
circle.setAttribute("cy", y);
|
|
circle.setAttribute("r", 8);
|
|
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
text.setAttribute("x", x);
|
|
text.setAttribute("y", y - 15);
|
|
text.setAttribute("text-anchor", "middle");
|
|
text.textContent = labels.move[move];
|
|
markers.append(circle, text);
|
|
});
|
|
const moves = Object.values(course.classMoves);
|
|
const hasHook = moves.includes("hook");
|
|
const thinkingMoves = moves.filter((move) => ["predict", "pair", "quiz", "paper"].includes(move)).length;
|
|
if (hasHook && thinkingMoves >= 2) {
|
|
setText("#classFeedback", "This hour now asks students to predict, explain, or retrieve before the idea goes cold.");
|
|
} else if (thinkingMoves >= 1) {
|
|
setText("#classFeedback", "Good start. Add one more thinking move so the middle of the hour does not sag.");
|
|
} else {
|
|
setText("#classFeedback", "Place an opening problem and two short thinking moves.");
|
|
}
|
|
}
|
|
|
|
function initFeedbackLab() {
|
|
const bars = $("#persistenceBars");
|
|
bars.replaceChildren();
|
|
["same day", "one week", "midterm"].forEach((label) => {
|
|
const row = document.createElement("div");
|
|
row.className = "persistence-row";
|
|
row.innerHTML = `<span>${label}</span><div class="bar-shell"><span></span></div>`;
|
|
bars.append(row);
|
|
});
|
|
updatePersistence("late");
|
|
|
|
$$(".feedback-choices button").forEach((button) => {
|
|
button.addEventListener("click", () => {
|
|
$$(".feedback-choices button").forEach((item) => item.classList.toggle("selected", item === button));
|
|
course.feedback = button.dataset.feedback;
|
|
updatePersistence(course.feedback);
|
|
updateAlignmentMap();
|
|
updateLearnerNotes("feedback");
|
|
updateCourseFile();
|
|
updatePlan();
|
|
});
|
|
});
|
|
}
|
|
|
|
function updatePersistence(kind) {
|
|
const levels = {
|
|
late: [80, 72, 58],
|
|
quick: [58, 30, 18],
|
|
revision: [62, 24, 10]
|
|
};
|
|
$$(".bar-shell span").forEach((bar, index) => {
|
|
bar.style.setProperty("--amount", `${levels[kind][index]}%`);
|
|
bar.style.background = kind === "late" ? "var(--coral)" : "var(--teal)";
|
|
});
|
|
}
|
|
|
|
function initOpening() {
|
|
$$("#openingChoices button").forEach((button) => {
|
|
button.addEventListener("click", () => {
|
|
$$("#openingChoices button").forEach((item) => item.classList.toggle("selected", item === button));
|
|
course.opening = button.dataset.opening;
|
|
const response = $("#motivationResponse");
|
|
if (course.opening === "rules") {
|
|
response.classList.remove("good");
|
|
response.innerHTML = "<strong>Maya hears that marks matter.</strong><span>Value, expectancy, and support are still mostly implicit.</span>";
|
|
} else if (course.opening === "mystery") {
|
|
response.classList.add("good");
|
|
response.innerHTML = "<strong>Maya sees value first.</strong><span>The opening gives the outcome a reason to exist before naming the machinery.</span>";
|
|
} else {
|
|
response.classList.add("good");
|
|
response.innerHTML = "<strong>Maya sees the route.</strong><span>The opening joins value with expectancy: here is the goal, the practice path, and where help lives.</span>";
|
|
}
|
|
updateLearnerNotes("motivation");
|
|
updateCourseFile();
|
|
updatePlan();
|
|
});
|
|
});
|
|
}
|
|
|
|
function initMemoryChecks() {
|
|
$$(".memory-check").forEach((check) => {
|
|
$$("button", check).forEach((button) => {
|
|
button.addEventListener("click", () => {
|
|
$$("button", check).forEach((item) => item.classList.remove("correct", "incorrect"));
|
|
const isRight = button.dataset.answer === "right";
|
|
button.classList.add(isRight ? "correct" : "incorrect");
|
|
const feedback = $(".check-feedback", check);
|
|
if (isRight) {
|
|
course.memory.add(check.dataset.check);
|
|
feedback.textContent = "Settled. This will show up in the course file.";
|
|
} else {
|
|
feedback.textContent = "Not yet. Try the option that changes what the teacher designs next.";
|
|
}
|
|
updateCourseFile();
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
function updateLearnerNotes(stage) {
|
|
const notes = {
|
|
diagnosed: [
|
|
"She can repeat definitions after lecture.",
|
|
"She waits until the exam to find out what counts.",
|
|
"She cannot yet transfer the ideas to a new problem."
|
|
],
|
|
outcome: [
|
|
course.outcome === "usable" ? "She can see the course is about justified choices, not names alone." : "The destination is still hard to recognize from a student's seat.",
|
|
"She needs practice that matches the promise.",
|
|
"The exam will decide what she actually studies."
|
|
],
|
|
alignment: [
|
|
course.practice === "compare" || course.practice === "debug" ? "She has a place to practice choosing, not just hearing." : "She is still mostly watching someone else reason.",
|
|
course.assessment === "justify" || course.assessment === "project" ? "Marks now reward the capability the outcome names." : "The visible reward still points toward recall.",
|
|
"Alignment is becoming a message she can read."
|
|
],
|
|
class: [
|
|
Object.values(course.classMoves).length >= 3 ? "She has to predict, explain, and retrieve during class." : "The class has started to ask for thinking, but the rhythm is still thin.",
|
|
"Short tasks make confusion visible before the exam.",
|
|
"Attention is being used as a design material."
|
|
],
|
|
feedback: [
|
|
course.feedback === "late" ? "Her misconception survives until the midterm." : "Her misconception gets interrupted while it is still fixable.",
|
|
"Feedback now changes the next attempt, not just the grade record.",
|
|
"Formative work has become part of teaching."
|
|
],
|
|
motivation: [
|
|
course.opening === "rules" ? "She hears the grading system first." : "She sees why the outcome matters before the syllabus details.",
|
|
course.opening === "support" ? "She also sees how effort can succeed." : "Support still needs to be named clearly.",
|
|
"Motivation is being designed, not wished for."
|
|
]
|
|
};
|
|
const list = $("#learnerNotes");
|
|
list.replaceChildren();
|
|
notes[stage].forEach((note) => {
|
|
const item = document.createElement("li");
|
|
item.textContent = note;
|
|
list.append(item);
|
|
});
|
|
}
|
|
|
|
function designLevels() {
|
|
let use = 22;
|
|
let confidence = 18;
|
|
let transfer = 14;
|
|
if (course.outcome === "usable") {
|
|
use += 18;
|
|
transfer += 16;
|
|
} else if (course.outcome) {
|
|
use += 6;
|
|
}
|
|
if (course.practice === "compare" || course.practice === "debug") {
|
|
use += 16;
|
|
transfer += 14;
|
|
}
|
|
if (course.assessment === "justify" || course.assessment === "project") {
|
|
use += 16;
|
|
confidence += 14;
|
|
transfer += 12;
|
|
}
|
|
if (Object.values(course.classMoves).length >= 3) {
|
|
confidence += 14;
|
|
use += 10;
|
|
}
|
|
if (course.feedback === "quick" || course.feedback === "revision") {
|
|
confidence += 18;
|
|
transfer += 10;
|
|
}
|
|
if (course.opening === "mystery" || course.opening === "support") {
|
|
confidence += 10;
|
|
use += 8;
|
|
}
|
|
return {
|
|
use: clamp(use, 8, 96),
|
|
confidence: clamp(confidence, 8, 96),
|
|
transfer: clamp(transfer, 8, 96)
|
|
};
|
|
}
|
|
|
|
function updateCourseFile() {
|
|
setText("#spineOutcome", course.outcome ? labels.outcome[course.outcome] : "not yet written");
|
|
setText("#spinePractice", course.practice ? labels.practice[course.practice] : "mostly listening");
|
|
setText("#spineAssessment", course.assessment ? labels.assessment[course.assessment] : "midterm plus final");
|
|
setText("#spineFeedback", course.feedback ? labels.feedback[course.feedback] : "late and scarce");
|
|
setText("#spineMotivation", course.opening ? labels.opening[course.opening] : "grades carry the message");
|
|
|
|
const levels = designLevels();
|
|
$("#meterUse").style.setProperty("--fill", `${levels.use}%`);
|
|
$("#meterConfidence").style.setProperty("--fill", `${levels.confidence}%`);
|
|
$("#meterTransfer").style.setProperty("--fill", `${levels.transfer}%`);
|
|
|
|
const memory = $("#memoryTrace");
|
|
memory.replaceChildren();
|
|
if (course.memory.size === 0) {
|
|
const span = document.createElement("span");
|
|
span.textContent = "0 checks settled";
|
|
memory.append(span);
|
|
} else {
|
|
course.memory.forEach((check) => {
|
|
const span = document.createElement("span");
|
|
span.className = "settled";
|
|
span.textContent = check;
|
|
memory.append(span);
|
|
});
|
|
}
|
|
|
|
const aligned =
|
|
course.outcome === "usable" &&
|
|
(course.practice === "compare" || course.practice === "debug") &&
|
|
(course.assessment === "justify" || course.assessment === "project") &&
|
|
(course.feedback === "quick" || course.feedback === "revision");
|
|
if (aligned) {
|
|
setText("#fileStatus", "The course now gives students one coherent message.");
|
|
} else if (course.outcome || course.practice || course.assessment || course.feedback || course.opening) {
|
|
setText("#fileStatus", "The course is improving, but at least one signal still needs work.");
|
|
} else {
|
|
setText("#fileStatus", "Topic coverage is still driving the course.");
|
|
}
|
|
}
|
|
|
|
function updatePlan() {
|
|
const moves = Object.entries(course.classMoves)
|
|
.sort((a, b) => Number(a[0]) - Number(b[0]))
|
|
.map(([minute, move]) => `${minute} min: ${labels.move[move]}`);
|
|
const plan = `Data Structures I - first class redesign
|
|
|
|
Learning outcome
|
|
${course.outcome ? labels.outcome[course.outcome] : "Write one observable course outcome before class."}
|
|
|
|
Opening
|
|
${course.opening ? labels.opening[course.opening] : "Open by showing why the outcome matters to a real design problem."}
|
|
|
|
In-class practice
|
|
${moves.length ? moves.join("\n") : "0 min: opening problem\n15 min: prediction\n30 min: think-pair-share\n45 min: one-minute quiz"}
|
|
|
|
Assessment signal
|
|
${course.assessment ? labels.assessment[course.assessment] : "Use a scenario task that asks students to justify a structure."}
|
|
|
|
Feedback route
|
|
${course.feedback ? labels.feedback[course.feedback] : "Give quick formative feedback before the first high-stakes exam."}
|
|
|
|
Teacher check
|
|
Students should leave knowing what they are trying to learn, how they will practice it, how it will be assessed, and where to get help.`;
|
|
setText("#finalPlan", plan);
|
|
}
|
|
|
|
function initCopy() {
|
|
$("#copyPlan")?.addEventListener("click", async () => {
|
|
try {
|
|
await navigator.clipboard.writeText($("#finalPlan").textContent);
|
|
setText("#copyStatus", "Copied.");
|
|
} catch {
|
|
const range = document.createRange();
|
|
range.selectNodeContents($("#finalPlan"));
|
|
const selection = window.getSelection();
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
setText("#copyStatus", "Selected the plan text.");
|
|
}
|
|
});
|
|
}
|
|
|
|
function initReset() {
|
|
$$("[data-action='reset']").forEach((button) => {
|
|
button.addEventListener("click", () => {
|
|
course.outcome = null;
|
|
course.practice = null;
|
|
course.assessment = null;
|
|
course.feedback = null;
|
|
course.opening = null;
|
|
course.classMoves = {};
|
|
course.memory.clear();
|
|
$$(".selected, .correct, .incorrect, .filled").forEach((node) => node.classList.remove("selected", "correct", "incorrect", "filled"));
|
|
$$(".slot").forEach((slot) => {
|
|
slot.textContent = `${slot.dataset.minute} min`;
|
|
});
|
|
updateLearnerNotes("diagnosed");
|
|
updateAlignmentMap();
|
|
updateAttention();
|
|
updatePersistence("late");
|
|
updateCourseFile();
|
|
updatePlan();
|
|
setText("#classFeedback", "Place an opening problem and two short thinking moves.");
|
|
$("#outcomeFeedback").innerHTML = "<strong>No outcome chosen yet.</strong><span>The course still has topics, not a destination.</span>";
|
|
$("#motivationResponse").innerHTML = "<strong>Maya is waiting to see what this course values.</strong><span>The opening has not yet told her why effort will be worth it.</span>";
|
|
$("#hiddenCourse").innerHTML = "<strong>The hidden course is still recall.</strong><span>Students will protect marks by memorizing definitions.</span>";
|
|
});
|
|
});
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
initProgress();
|
|
initNav();
|
|
initWeeks();
|
|
initOutcomes();
|
|
initAlignmentControls();
|
|
initClassBuilder();
|
|
initFeedbackLab();
|
|
initOpening();
|
|
initMemoryChecks();
|
|
initCopy();
|
|
initReset();
|
|
$("#runOldCourse")?.addEventListener("click", runOldCourse);
|
|
updateLearnerNotes("diagnosed");
|
|
updateAlignmentMap();
|
|
updateCourseFile();
|
|
updatePlan();
|
|
});
|