Add paper/print alternatives for off-screen learning
Build and Push Docker Image / build-and-push (push) Successful in 3m35s
Build and Push Docker Image / build-and-push (push) Successful in 3m35s
- Spelling: "Blank test" print button per word list (numbered lines, no words shown) for dictated paper tests - Spelling: "Record Paper Test" form in admin to log a paper test score to the gradebook - Gradebook: "Record Paper Score" form to log a paper quiz/assignment score for any course item - Flashcards: "Print" button per deck generates a cut-and-fold card sheet (fronts page + backs page) - Crossword: print now includes a second page with a filled answer key - Lessons + Assignments: 🖨 Print button in student view generates a clean printable page - Word lists: new "CSV file" import tab — upload a .csv with word, sentence, definition columns Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
6baea15986
commit
a92db291c7
5 files changed
+392
-3
No files matched your search
+43
-2
@@ -1222,6 +1222,7 @@ async function openLesson(itemId) {
|
||||
$('#lesson-kicker').textContent = `${item.course_name} · ${item.unit_name}`;
|
||||
$('#lesson-title').textContent = item.title;
|
||||
$('#lesson-body').innerHTML = item.body;
|
||||
$('#lesson-print-btn').onclick = () => printItemPage('📖 ' + item.title, item.course_name + ' · ' + item.unit_name, item.body);
|
||||
show('lesson');
|
||||
|
||||
const btn = $('#lesson-done-btn');
|
||||
@@ -1252,6 +1253,7 @@ async function openAssignment(itemId) {
|
||||
$('#assignment-title').textContent = item.title;
|
||||
$('#assignment-points').textContent = item.points ? `Worth ${item.points} points` : '';
|
||||
$('#assignment-body').innerHTML = item.body || '';
|
||||
$('#assignment-print-btn').onclick = () => printItemPage('📝 ' + item.title, item.course_name + ' · ' + item.unit_name, item.body || '');
|
||||
|
||||
const btn = $('#assignment-done-btn');
|
||||
const status = $('#assignment-status');
|
||||
@@ -2053,7 +2055,27 @@ async function openCrossword(itemId) {
|
||||
show('crossword');
|
||||
}
|
||||
|
||||
function printCrosswordWorksheet(title, cw) {
|
||||
function printItemPage(title, kicker, bodyHtml) {
|
||||
const win = window.open('', '_blank');
|
||||
win.document.write(`<!DOCTYPE html><html><head><title>${esc(title)}</title>
|
||||
<style>
|
||||
body{font-family:Georgia,serif;max-width:680px;margin:2rem auto;color:#222}
|
||||
.kicker{color:#888;font-size:.85rem;margin-bottom:.25rem}
|
||||
h1{font-size:1.4rem;margin:0 0 1rem;border-bottom:2px solid #222;padding-bottom:.4rem}
|
||||
.body{line-height:1.65}
|
||||
.body h2,.body h3{margin:1rem 0 .4rem}
|
||||
.body ul,.body ol{padding-left:1.5rem}
|
||||
@media print{body{margin:.75in;max-width:none}}
|
||||
</style></head><body>
|
||||
<p class="kicker">${esc(kicker)}</p>
|
||||
<h1>${esc(title)}</h1>
|
||||
<div class="body">${bodyHtml}</div>
|
||||
<script>window.print()<\/script>
|
||||
</body></html>`);
|
||||
win.document.close();
|
||||
}
|
||||
|
||||
function buildCrosswordSvg(cw, filled) {
|
||||
const CELL = 28;
|
||||
const numMap = {};
|
||||
for (const w of [...(cw.across||[]), ...(cw.down||[])]) if (w.num) numMap[`${w.row},${w.col}`] = w.num;
|
||||
@@ -2067,21 +2089,40 @@ function printCrosswordWorksheet(title, cw) {
|
||||
svg += `<rect x="${x}" y="${y}" width="${CELL}" height="${CELL}" fill="#fff" stroke="#999" stroke-width="0.5"/>`;
|
||||
const key = `${r},${c}`;
|
||||
if (numMap[key]) svg += `<text x="${x+2}" y="${y+9}" font-size="6" font-family="sans-serif">${numMap[key]}</text>`;
|
||||
if (filled && cw.grid[r][c]) {
|
||||
const cx = x + CELL / 2, cy = y + CELL / 2 + 5;
|
||||
svg += `<text x="${cx}" y="${cy}" font-size="13" font-family="sans-serif" text-anchor="middle" fill="#1a1a1a">${esc(cw.grid[r][c])}</text>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
svg += '</svg>';
|
||||
return svg;
|
||||
}
|
||||
|
||||
function printCrosswordWorksheet(title, cw) {
|
||||
const svg = buildCrosswordSvg(cw, false);
|
||||
const svgKey = buildCrosswordSvg(cw, true);
|
||||
const acrossHtml = (cw.across||[]).map((w) => `<li><strong>${w.num}.</strong> ${esc(w.clue)}</li>`).join('');
|
||||
const downHtml = (cw.down||[]).map((w) => `<li><strong>${w.num}.</strong> ${esc(w.clue)}</li>`).join('');
|
||||
const win = window.open('', '_blank');
|
||||
win.document.write(`<!DOCTYPE html><html><head><title>${esc(title)}</title>
|
||||
<style>body{font-family:sans-serif;padding:1.5rem}h1{margin-bottom:1rem}.cols{display:flex;gap:2rem}ol{list-style:none;padding:0}li{margin:.2rem 0;font-size:.9rem}@media print{body{padding:.5rem}}</style>
|
||||
<style>
|
||||
body{font-family:sans-serif;padding:1.5rem}h1{margin-bottom:1rem}
|
||||
.cols{display:flex;gap:2rem}ol{list-style:none;padding:0}li{margin:.2rem 0;font-size:.9rem}
|
||||
.page-break{page-break-before:always;margin-top:2rem}
|
||||
@media print{body{padding:.5rem}}
|
||||
</style>
|
||||
</head><body>
|
||||
<h1>${esc(title)}</h1>${svg}
|
||||
<div class="cols">
|
||||
<div><h3>Across</h3><ol>${acrossHtml}</ol></div>
|
||||
<div><h3>Down</h3><ol>${downHtml}</ol></div>
|
||||
</div>
|
||||
<div class="page-break">
|
||||
<h2 style="margin-bottom:.75rem">Answer Key — ${esc(title)}</h2>
|
||||
${svgKey}
|
||||
</div>
|
||||
<script>window.print();<\/script>
|
||||
</body></html>`);
|
||||
win.document.close();
|
||||
|
||||
Reference in new issue
Block a user