Skip to content

Commit e022543

Browse files
revised to accommodate math in questions
1 parent 9a90ecf commit e022543

3 files changed

Lines changed: 161 additions & 49 deletions

File tree

_layouts/cards-template.html

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@
66
<a href="{{ '/' | relative_url }}" class="back-link">← Back to Home</a>
77
<h1>{{ page.title }}</h1>
88

9-
10-
119
<div class="flashcard-container">
12-
<div class="flashcard-wrapper"> <!-- <-- NEW wrapper -->
10+
<div class="flashcard-wrapper">
1311
<div class="flashcard" id="flashcard">
1412
<div class="flashcard-inner">
1513
<div class="flashcard-front"></div>
@@ -24,6 +22,20 @@ <h1>{{ page.title }}</h1>
2422
</div>
2523
</div>
2624

25+
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
26+
27+
<!-- MathJax config -->
28+
<script>
29+
window.MathJax = {
30+
tex: {
31+
inlineMath: [['$', '$'], ['\\(', '\\)']], // allow $...$ and \(...\)
32+
displayMath: [['$$', '$$'], ['\\[', '\\]']] // allow $$...$$ and \[...\]
33+
},
34+
svg: { fontCache: 'global' }
35+
};
36+
</script>
37+
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>
38+
2739
<script>
2840
async function loadCards() {
2941
const res = await fetch("{{ page.jsonfile | relative_url }}");
@@ -35,16 +47,24 @@ <h1>{{ page.title }}</h1>
3547
const back = flashcard.querySelector(".flashcard-back");
3648

3749
function renderCard() {
38-
front.innerHTML = marked.parse(cards[current].front); // markdown support
50+
front.innerHTML = marked.parse(cards[current].front);
3951
back.innerHTML = marked.parse(cards[current].back);
4052
flashcard.classList.remove("is-flipped");
53+
54+
if (window.MathJax) {
55+
MathJax.typesetClear();
56+
MathJax.typesetPromise([flashcard]);
57+
}
4158
}
4259

4360
renderCard();
4461

4562
// flip on click
4663
flashcard.addEventListener("click", () => {
4764
flashcard.classList.toggle("is-flipped");
65+
if (window.MathJax) {
66+
MathJax.typesetPromise([flashcard]);
67+
}
4868
});
4969

5070
document.getElementById("prevBtn").addEventListener("click", () => {
@@ -64,7 +84,3 @@ <h1>{{ page.title }}</h1>
6484

6585
loadCards();
6686
</script>
67-
68-
<!-- Add Markdown renderer (same one you used before) -->
69-
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
70-

assets/data/cards_04.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[
22
{
33
"front": "What does Linear dependence and Independence of vectors imply? ",
4-
"back": "**Discuss**"
4+
"back": "**Discuss** ${\\bf v} = \\sum_i a_i {\\bf v}_i$"
55
},
66
{
77
"front": "How is the Computational complexity of Quantum system's simulation on a classical system connected to the size of the quantum system.",

assets/js/flashcards.js

Lines changed: 136 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,156 @@
1+
// assets/js/flashcards.js
12
let flashcards = [];
23
let currentIndex = 0;
34

45
async function loadFlashcards(jsonUrl) {
5-
const res = await fetch(jsonUrl);
6-
flashcards = await res.json();
7-
currentIndex = 0;
8-
renderFlashcard();
6+
try {
7+
const r = await fetch(jsonUrl, { cache: "no-store" });
8+
if (!r.ok) throw new Error(`HTTP ${r.status} ${r.statusText}`);
9+
flashcards = await r.json();
10+
if (!Array.isArray(flashcards) || flashcards.length === 0) {
11+
throw new Error("Cards JSON is empty or not an array");
12+
}
13+
currentIndex = 0;
14+
initUI();
15+
renderCard();
16+
} catch (err) {
17+
console.error("Failed to load flashcards:", err);
18+
const container = document.getElementById("flashcard-container") || document.getElementById("flashcard");
19+
if (container) container.innerHTML = `<p style="color:red">Error loading cards: ${err.message}</p>`;
20+
}
921
}
1022

11-
function renderFlashcard() {
12-
const container = document.getElementById("flashcard-container");
13-
container.innerHTML = "";
14-
15-
const cardData = flashcards[currentIndex];
16-
17-
const card = document.createElement("div");
18-
card.className = "flashcard";
19-
card.innerHTML = `
20-
<div class="flashcard-inner">
21-
<div class="flashcard-front">${marked.parse(cardData.front)}</div>
22-
<div class="flashcard-back">${marked.parse(cardData.back)}</div>
23-
</div>
24-
`;
23+
/* safe typeset helper - waits for MathJax to be ready */
24+
function safeTypeset(element) {
25+
return new Promise((resolve) => {
26+
const tryTypeset = () => {
27+
if (window.MathJax && MathJax.startup && MathJax.startup.promise) {
28+
MathJax.startup.promise.then(() => {
29+
if (MathJax.typesetPromise) {
30+
MathJax.typesetPromise([element]).then(resolve).catch((e) => {
31+
console.warn("MathJax typeset failed:", e);
32+
resolve();
33+
});
34+
} else {
35+
// older MathJax fallback
36+
try { MathJax.Hub && MathJax.Hub.Queue(["Typeset", MathJax.Hub, element]); } catch (e) {}
37+
resolve();
38+
}
39+
}).catch((e) => {
40+
console.warn("MathJax startup promise failed:", e);
41+
resolve();
42+
});
43+
} else {
44+
// MathJax not yet defined — retry a few times then give up
45+
setTimeout(() => {
46+
tryTypeset();
47+
}, 150);
48+
}
49+
};
50+
tryTypeset();
51+
});
52+
}
2553

26-
container.appendChild(card);
54+
function initUI() {
55+
const flashcard = document.getElementById("flashcard");
56+
const front = flashcard.querySelector(".flashcard-front");
57+
const back = flashcard.querySelector(".flashcard-back");
58+
const prevBtn = document.getElementById("prevBtn");
59+
const nextBtn = document.getElementById("nextBtn");
2760

28-
// Flip on click
29-
card.addEventListener("click", () => {
30-
card.classList.toggle("is-flipped"); // match CSS
31-
if (window.MathJax) {
32-
MathJax.typesetPromise();
33-
}
61+
// Prevent nav clicks from bubbling to the card (so they don't flip)
62+
[prevBtn, nextBtn].forEach(btn => {
63+
btn.addEventListener("click", (ev) => ev.stopPropagation());
3464
});
3565

36-
// Always reset to front before showing a new card
37-
card.classList.remove("is-flipped");
38-
void card.offsetWidth; // force reflow (prevents ghost transforms)
39-
40-
// Typeset math after rendering
41-
if (window.MathJax) {
42-
MathJax.typesetPromise();
43-
}
44-
}
66+
// Flip on click (toggle class on outer .flashcard)
67+
flashcard.addEventListener("click", () => {
68+
flashcard.classList.toggle("is-flipped");
69+
// typeset visible side after flip
70+
safeTypeset(flashcard).catch(()=>{});
71+
});
4572

46-
// Navigation
47-
document.addEventListener("DOMContentLoaded", () => {
48-
document.getElementById("prevBtn").addEventListener("click", () => {
73+
prevBtn.addEventListener("click", (ev) => {
74+
ev.preventDefault();
4975
if (currentIndex > 0) {
5076
currentIndex--;
51-
renderFlashcard();
77+
renderCard();
5278
}
5379
});
54-
document.getElementById("nextBtn").addEventListener("click", () => {
80+
81+
nextBtn.addEventListener("click", (ev) => {
82+
ev.preventDefault();
5583
if (currentIndex < flashcards.length - 1) {
5684
currentIndex++;
57-
renderFlashcard();
85+
renderCard();
86+
}
87+
});
88+
89+
// optional keyboard navigation
90+
document.addEventListener("keydown", (ev) => {
91+
if (ev.key === "ArrowLeft") {
92+
if (currentIndex > 0) { currentIndex--; renderCard(); }
93+
} else if (ev.key === "ArrowRight") {
94+
if (currentIndex < flashcards.length - 1) { currentIndex++; renderCard(); }
95+
} else if (ev.key === " " || ev.key === "Spacebar") {
96+
// space toggles flip (avoid flipping when focused on button)
97+
const active = document.activeElement;
98+
if (active && (active.tagName === "BUTTON" || active.tagName === "INPUT" || active.tagName === "TEXTAREA")) return;
99+
ev.preventDefault();
100+
document.getElementById("flashcard").classList.toggle("is-flipped");
101+
safeTypeset(document.getElementById("flashcard")).catch(()=>{});
58102
}
59103
});
104+
}
105+
106+
async function renderCard() {
107+
const flashcard = document.getElementById("flashcard");
108+
const front = flashcard.querySelector(".flashcard-front");
109+
const back = flashcard.querySelector(".flashcard-back");
110+
const prevBtn = document.getElementById("prevBtn");
111+
const nextBtn = document.getElementById("nextBtn");
112+
113+
// Defensive checks
114+
if (!flashcard || !front || !back) {
115+
console.error("Flashcard DOM nodes missing");
116+
return;
117+
}
118+
if (!flashcards.length) {
119+
front.innerHTML = "<p>No cards available</p>";
120+
back.innerHTML = "";
121+
return;
122+
}
123+
124+
// Reset flip and force reflow to avoid animation glitches
125+
flashcard.classList.remove("is-flipped");
126+
void flashcard.offsetWidth; // force reflow
127+
128+
// Render content (try/catch so a broken card won't break the UI)
129+
try {
130+
const item = flashcards[currentIndex] || { front: "", back: "" };
131+
front.innerHTML = marked.parse(item.front || "");
132+
back.innerHTML = marked.parse(item.back || "");
133+
} catch (err) {
134+
console.error("Error rendering markdown:", err);
135+
front.textContent = flashcards[currentIndex].front || "";
136+
back.textContent = flashcards[currentIndex].back || "";
137+
}
138+
139+
// update nav disabled state
140+
if (prevBtn) prevBtn.disabled = currentIndex === 0;
141+
if (nextBtn) nextBtn.disabled = currentIndex === (flashcards.length - 1);
142+
143+
// typeset math on visible content first, then invisible side
144+
try {
145+
await safeTypeset(front);
146+
// we also typeset back in background so when flipped it appears quickly
147+
safeTypeset(back).catch(()=>{});
148+
} catch (e) {
149+
console.warn("Typeset error (ignored):", e);
150+
}
151+
}
152+
153+
/* run (from template) */
154+
document.addEventListener("DOMContentLoaded", () => {
155+
// nothing here – loadCards(jsonUrl) should be invoked by the template inline script
60156
});

0 commit comments

Comments
 (0)