Skip to content

Commit 38b4af2

Browse files
refac: in memorial page (#26)
* feat (update): in memoriam navigatio - removed button from meet the dogs page - added nav button * feat (add): patlu om shanti * feat (add): section for asawarpur puppies --------- Co-authored-by: Nitin S <28nitin07@gmail.com>
1 parent 911d8e0 commit 38b4af2

19 files changed

Lines changed: 297 additions & 85 deletions

File tree

index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<button class="nav-link" id="nav-home" onclick="showPage('home')">home</button>
2020
<button class="nav-link" id="nav-about" onclick="showPage('about')">about us</button>
2121
<button class="nav-link" id="nav-dogs" onclick="showPage('dogs')">meet the dogs</button>
22+
<button class="nav-link" id="nav-memoriam" onclick="showPage('memoriam')">in memoriam</button>
2223
<button class="nav-link" id="nav-departments" onclick="showPage('departments')">departments</button>
2324
<button class="nav-link" id="nav-team" onclick="showPage('team')">meet the dawgs</button>
2425
<button class="nav-link" id="nav-gallery" onclick="showPage('gallery')">gallery</button>

js/app.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,10 @@ async function loadPage(name) {
138138
window.location.hostname.includes('localhost');
139139

140140
// Skip cache check in development mode
141-
if (!isDevelopment && _loaded[name]) return;
141+
if (!isDevelopment && _loaded[name]) {
142+
if (name === 'memoriam') loadMemorial();
143+
return;
144+
}
142145

143146
const container = document.getElementById('page-' + name);
144147
if (!container) return;

js/memoriam.js

Lines changed: 166 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ function randBetween(min, max) {
55
return Math.random() * (max - min) + min;
66
}
77

8+
/** Row unit must match grid-auto-rows in CSS (px) */
9+
const MEMORIAL_GRID_ROW_UNIT = 4;
10+
811
/** Build a memorial tile card for a single dog */
912
function buildMemorialCard(meta, body) {
1013
// Do NOT recalculate age — memorial dogs show the age at time of passing,
@@ -26,7 +29,7 @@ function buildMemorialCard(meta, body) {
2629

2730
const photoArea = meta.image
2831
? `<div class="dog-tile-photo">
29-
<img src="${meta.image}" alt="${esc(rawName)}" class="dog-photo memorial-photo" loading="lazy">
32+
<img src="${meta.image}" alt="${esc(rawName)}" class="dog-photo memorial-photo" loading="lazy" onerror="_cldImgError(this)">
3033
</div>`
3134
: `<div class="dog-tile-emoji" style="background:${meta.bgLight || ''}">
3235
<span class="dog-emoji-big">${meta.emoji || '🐕'}</span>
@@ -93,18 +96,20 @@ function initMemorialPhotos() {
9396

9497
/** Watch grid for width changes and recalculate spans */
9598
function initMemorialMasonryResize() {
96-
const grid = document.getElementById('memoriam-grid');
97-
if (!grid || !window.ResizeObserver) return;
99+
const grids = Array.from(document.querySelectorAll('#memoriam-grid, #memoriam-grid-asawarpur'));
100+
if (grids.length === 0 || !window.ResizeObserver) return;
98101
const ro = new ResizeObserver(() => {
99-
grid.querySelectorAll('.dog-card').forEach(setCardSpan);
102+
grids.forEach(grid => {
103+
grid.querySelectorAll('.dog-card').forEach(setCardSpan);
104+
});
100105
});
101-
ro.observe(grid);
106+
grids.forEach(grid => ro.observe(grid));
102107
}
103108

104109
/** Open a full-colour memorial modal when a card is clicked */
105110
function initMemorialModals() {
106-
const grid = document.getElementById('memoriam-grid');
107-
if (!grid) return;
111+
const grids = Array.from(document.querySelectorAll('#memoriam-grid, #memoriam-grid-asawarpur'));
112+
if (grids.length === 0) return;
108113

109114
const isTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
110115
let lastTouchOpenAt = 0;
@@ -135,79 +140,89 @@ function initMemorialModals() {
135140
});
136141
}
137142

138-
grid.addEventListener('touchstart', e => {
139-
const card = e.target.closest('.dog-card');
140-
if (!card) return;
141-
activeCard = card;
142-
didLongPress = false;
143-
const touch = e.touches[0];
144-
startX = touch.clientX;
145-
startY = touch.clientY;
146-
147-
pressTimer = setTimeout(() => {
148-
didLongPress = true;
149-
card.classList.add('lit');
150-
if (navigator.vibrate) navigator.vibrate(50);
151-
}, LONG_PRESS_MS);
152-
}, { passive: true });
153-
154-
grid.addEventListener('touchmove', e => {
155-
if (!pressTimer) return;
156-
const touch = e.touches[0];
157-
const dx = Math.abs(touch.clientX - startX);
158-
const dy = Math.abs(touch.clientY - startY);
159-
if (dx > MOVE_THRESHOLD || dy > MOVE_THRESHOLD) {
143+
grids.forEach(grid => {
144+
if (grid.dataset.modalBound === '1') return;
145+
grid.dataset.modalBound = '1';
146+
147+
grid.addEventListener('touchstart', e => {
148+
const card = e.target.closest('.dog-card');
149+
if (!card) return;
150+
activeCard = card;
151+
didLongPress = false;
152+
const touch = e.touches[0];
153+
startX = touch.clientX;
154+
startY = touch.clientY;
155+
156+
pressTimer = setTimeout(() => {
157+
didLongPress = true;
158+
card.classList.add('lit');
159+
if (navigator.vibrate) navigator.vibrate(50);
160+
}, LONG_PRESS_MS);
161+
}, { passive: true });
162+
163+
grid.addEventListener('touchmove', e => {
164+
if (!pressTimer) return;
165+
const touch = e.touches[0];
166+
const dx = Math.abs(touch.clientX - startX);
167+
const dy = Math.abs(touch.clientY - startY);
168+
if (dx > MOVE_THRESHOLD || dy > MOVE_THRESHOLD) {
169+
clearTimeout(pressTimer);
170+
pressTimer = null;
171+
}
172+
}, { passive: true });
173+
174+
grid.addEventListener('touchend', () => {
160175
clearTimeout(pressTimer);
161176
pressTimer = null;
162-
}
163-
}, { passive: true });
177+
if (didLongPress) {
178+
didLongPress = false;
179+
if (activeCard) activeCard.classList.remove('lit');
180+
activeCard = null;
181+
return;
182+
}
183+
if (activeCard) {
184+
openMemorialModal(activeCard);
185+
lastTouchOpenAt = Date.now();
186+
}
187+
activeCard = null;
188+
});
164189

165-
grid.addEventListener('touchend', e => {
166-
clearTimeout(pressTimer);
167-
pressTimer = null;
168-
if (didLongPress) {
169-
didLongPress = false;
190+
grid.addEventListener('touchcancel', () => {
191+
clearTimeout(pressTimer);
192+
pressTimer = null;
170193
if (activeCard) activeCard.classList.remove('lit');
194+
didLongPress = false;
171195
activeCard = null;
172-
return;
173-
}
174-
if (activeCard) {
175-
openMemorialModal(activeCard);
176-
lastTouchOpenAt = Date.now();
177-
}
178-
activeCard = null;
179-
});
180-
181-
grid.addEventListener('touchcancel', () => {
182-
clearTimeout(pressTimer);
183-
pressTimer = null;
184-
if (activeCard) activeCard.classList.remove('lit');
185-
didLongPress = false;
186-
activeCard = null;
187-
});
196+
});
188197

189-
// Suppress native context menu on long press within the grid
190-
grid.addEventListener('contextmenu', e => {
191-
if (e.target.closest('.dog-card')) e.preventDefault();
198+
// Suppress native context menu on long press within the grid
199+
grid.addEventListener('contextmenu', e => {
200+
if (e.target.closest('.dog-card')) e.preventDefault();
201+
});
192202
});
193203
}
194204

195205
/* ── Desktop: click opens modal (hover handles candle/colour via CSS) ── */
196-
grid.addEventListener('click', e => {
197-
// Ignore synthetic click events that follow a touch tap.
198-
if (Date.now() - lastTouchOpenAt < 700) return;
199-
const card = e.target.closest('.dog-card');
200-
if (!card) return;
201-
openMemorialModal(card);
202-
});
206+
grids.forEach(grid => {
207+
if (grid.dataset.modalBound === '1') return;
208+
grid.dataset.modalBound = '1';
203209

204-
// Keyboard support for focused cards.
205-
grid.addEventListener('keydown', e => {
206-
if (e.key !== 'Enter' && e.key !== ' ') return;
207-
const card = e.target.closest('.dog-card');
208-
if (!card) return;
209-
e.preventDefault();
210-
openMemorialModal(card);
210+
grid.addEventListener('click', e => {
211+
// Ignore synthetic click events that follow a touch tap.
212+
if (Date.now() - lastTouchOpenAt < 700) return;
213+
const card = e.target.closest('.dog-card');
214+
if (!card) return;
215+
openMemorialModal(card);
216+
});
217+
218+
// Keyboard support for focused cards.
219+
grid.addEventListener('keydown', e => {
220+
if (e.key !== 'Enter' && e.key !== ' ') return;
221+
const card = e.target.closest('.dog-card');
222+
if (!card) return;
223+
e.preventDefault();
224+
openMemorialModal(card);
225+
});
211226
});
212227
}
213228

@@ -266,33 +281,105 @@ function openMemorialModal(card) {
266281
/** Main entry: fetch manifest, load all memorial dogs, render to grid */
267282
async function loadMemorial() {
268283
const grid = document.getElementById('memoriam-grid');
284+
let asawarpurGrid = document.getElementById('memoriam-grid-asawarpur');
285+
let asawarpurSection = document.getElementById('memoriam-asawarpur-section');
269286
if (!grid) return;
270287

288+
// Handle cached/older memoriam HTML by creating subsection nodes on the fly.
289+
if (!asawarpurSection || !asawarpurGrid) {
290+
const wrapper = grid.closest('.memoriam-page-wrap');
291+
if (wrapper) {
292+
if (!asawarpurSection) {
293+
asawarpurSection = document.createElement('div');
294+
asawarpurSection.id = 'memoriam-asawarpur-section';
295+
asawarpurSection.className = 'memoriam-subsection-block';
296+
asawarpurSection.style.display = 'none';
297+
asawarpurSection.innerHTML = `
298+
<h2 class="memoriam-subsection-title">Asawarpur Puppies</h2>
299+
<p class="memoriam-subsection-note">As an animal welfare club that operates within a limited jurisdiction, we are often called upon to assist with emergencies in neighboring areas. It was under such circumstances that we were asked to help with the burial of these puppies. Because of this, we never had the chance to truly know them. Our members have tried to recreate their likeness through illustrations drawn from memory, in the hope of keeping their memory alive. All of them were named posthumously.</p>
300+
`;
301+
wrapper.appendChild(asawarpurSection);
302+
}
303+
if (!asawarpurGrid) {
304+
asawarpurGrid = document.createElement('div');
305+
asawarpurGrid.id = 'memoriam-grid-asawarpur';
306+
asawarpurGrid.className = 'memoriam-grid';
307+
asawarpurGrid.style.display = 'none';
308+
wrapper.appendChild(asawarpurGrid);
309+
}
310+
}
311+
}
312+
271313
grid.innerHTML = '<div class="dogs-loading">🕯️ loading…</div>';
314+
if (asawarpurGrid) asawarpurGrid.innerHTML = '<div class="dogs-loading">🕯️ loading…</div>';
272315

273316
try {
274-
const manifestRes = await fetch('public/memorial/manifest.json');
317+
const bust = `?v=${Date.now()}`;
318+
const manifestRes = await fetch('public/memorial/manifest.json' + bust);
275319
if (!manifestRes.ok) throw new Error('memorial manifest not found');
276320
const { dogs: files } = await manifestRes.json();
277321

278322
const results = await Promise.allSettled(
279323
files.map(async filename => {
280-
const res = await fetch('public/memorial/content/' + filename);
324+
const res = await fetch('public/memorial/content/' + filename + bust);
281325
if (!res.ok) throw new Error('Could not load ' + filename);
282-
return parseDogMd(await res.text());
326+
return { filename, parsed: parseDogMd(await res.text()) };
283327
})
284328
);
285329

286-
const cards = results
330+
const entries = results
287331
.filter(r => r.status === 'fulfilled')
288-
.map(r => buildMemorialCard(r.value.meta, r.value.body));
332+
.map(r => r.value);
289333

290-
if (cards.length === 0) {
334+
const asawarpurPuppyFiles = new Set([
335+
'chocolate.md',
336+
'moon.md',
337+
'iris.md',
338+
'gypsy.md',
339+
'chikku.md'
340+
]);
341+
342+
const regularEntries = entries.filter(entry => !asawarpurPuppyFiles.has(entry.filename));
343+
const asawarpurEntries = entries.filter(entry => asawarpurPuppyFiles.has(entry.filename));
344+
345+
const regularCards = regularEntries.map(entry =>
346+
buildMemorialCard(entry.parsed.meta, entry.parsed.body)
347+
);
348+
349+
const asawarpurCards = asawarpurEntries.map(entry =>
350+
buildMemorialCard(entry.parsed.meta, entry.parsed.body)
351+
);
352+
353+
if (regularCards.length === 0 && asawarpurCards.length === 0) {
291354
grid.innerHTML = '<div class="dogs-loading" style="opacity:0.6">no entries yet 🕯️</div>';
355+
if (asawarpurGrid) {
356+
asawarpurGrid.style.display = 'none';
357+
}
358+
if (asawarpurSection) {
359+
asawarpurSection.style.display = 'none';
360+
}
292361
return;
293362
}
294363

295-
grid.innerHTML = cards.join('');
364+
grid.innerHTML = regularCards.length > 0
365+
? regularCards.join('')
366+
: '<div class="dogs-loading" style="opacity:0.6">no entries yet 🕯️</div>';
367+
368+
if (asawarpurGrid) {
369+
if (asawarpurCards.length > 0) {
370+
asawarpurGrid.innerHTML = asawarpurCards.join('');
371+
asawarpurGrid.style.display = '';
372+
if (asawarpurSection) asawarpurSection.style.display = '';
373+
} else {
374+
asawarpurGrid.style.display = 'none';
375+
if (asawarpurSection) asawarpurSection.style.display = 'none';
376+
}
377+
} else {
378+
// Fallback for older cached HTML: append Asawarpur cards in main grid
379+
if (asawarpurCards.length > 0) {
380+
grid.innerHTML = [grid.innerHTML, ...asawarpurCards].join('');
381+
}
382+
}
296383
initMemorialPhotos();
297384
initMemorialModals();
298385
initMemorialMasonryResize();
@@ -301,6 +388,9 @@ async function loadMemorial() {
301388
requestAnimationFrame(() => {
302389
requestAnimationFrame(() => {
303390
grid.querySelectorAll('.dog-card').forEach(setCardSpan);
391+
if (asawarpurGrid) {
392+
asawarpurGrid.querySelectorAll('.dog-card').forEach(setCardSpan);
393+
}
304394
});
305395
});
306396

pages/dogs.html

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ <h1 style="font-family:'Caveat',cursive;font-size:clamp(2.5rem,6vw,4.5rem);font-
1111
<div class="dogs-grid" id="dogs-grid">
1212
<div class="dogs-loading">🐾 loading the pack…</div>
1313
</div>
14-
<div class="dogs-memoriam-cta" style="text-align:center; margin:2rem 0;">
15-
<button class="nav-link memoriam-cta" onclick="showPage('memoriam')">in memoriam</button>
16-
</div>
1714
<footer>
1815
<span class="footer-main">made with <span class="heart">❤️</span> by pawsitive</span>
1916
<span class="footer-credit">built by <a href="https://github.com/28nitin07" target="_blank" rel="noopener noreferrer" class="footer-glow">Nitin S</a> @ Ashoka University</span>

pages/memoriam.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,14 @@ <h1 class="memoriam-heading">🕯️ in memoriam</h1>
88
<div class="memoriam-grid" id="memoriam-grid">
99
<div class="dogs-loading">🕯️ loading…</div>
1010
</div>
11+
12+
<div class="memoriam-subsection-block" id="memoriam-asawarpur-section" style="display:none;">
13+
<h2 class="memoriam-subsection-title">Asawarpur Puppies</h2>
14+
<p class="memoriam-subsection-note">As an animal welfare club that operates within a limited jurisdiction, we are often called upon to assist with emergencies in neighboring areas. It was under such circumstances that we were asked to help with the burial of these puppies. Because of this, we never had the chance to truly know them. Our members have tried to recreate their likeness through illustrations drawn from memory, in the hope of keeping their memory alive. All of them were named posthumously.</p>
15+
</div>
16+
17+
<div class="memoriam-grid" id="memoriam-grid-asawarpur" style="display:none;">
18+
<div class="dogs-loading">🕯️ loading…</div>
19+
</div>
1120
</div>
1221
<footer>made with <span class="heart">❤️</span> by pawsitive <span class="footer-sep">·</span> built by <a href="https://github.com/28nitin07" target="_blank" rel="noopener noreferrer" title="Nitin's GitHub">Nitin S</a> <span class="footer-sep">·</span> ashoka university <span class="footer-sep">·</span> 🐾</footer>

public/memorial/content/chikku.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
name: Chikku
3+
nameEmoji: 🌰
4+
emoji: 🐶
5+
breed: 📍 Asawarpur Village · Unknown · ~3 months · Brown
6+
born: 2025
7+
dates: 2025 – 2026
8+
image: public/memorial/images/chikku.jpg
9+
tags: 🌰 named posthumously, 🐾 remembered with care, 🎨 likeness recreated from memory
10+
bgLight: linear-gradient(135deg,#efe7de,#dac9b8)
11+
bgDark: linear-gradient(135deg,#241f1a,#312821)
12+
---
13+
Chikku was one of the Asawarpur puppies whose burial we were called to help with in an emergency outside our usual area of work.
14+
15+
We never had the chance to truly know Chikku, but we hold this memory with care.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
name: Chocolate
3+
nameEmoji: 🍫
4+
emoji: 🐶
5+
breed: 📍 Asawarpur Village · Unknown · ~3 months · Black with brown spots
6+
born: 2025
7+
dates: 2025 – 2026
8+
image: public/memorial/images/chocolate.jpg
9+
tags: 🍫 named posthumously, 🐾 remembered with care, 🎨 likeness recreated from memory
10+
bgLight: linear-gradient(135deg,#efeae3,#d8cdc0)
11+
bgDark: linear-gradient(135deg,#24201d,#2f2a26)
12+
---
13+
Chocolate was one of the Asawarpur puppies whose burial we were called to help with in an emergency outside our usual area of work.
14+
15+
We never had the chance to truly know Chocolate, but we hold this memory with care.

0 commit comments

Comments
 (0)