Initial decks site
This commit is contained in:
commit
666218035c
25 changed files with 3111 additions and 0 deletions
616
learning-outcomes-assessment/script.js
Normal file
616
learning-outcomes-assessment/script.js
Normal file
|
|
@ -0,0 +1,616 @@
|
|||
/*
|
||||
* 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();
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue