Update web/templates/search.html

This commit is contained in:
Joshua Laymon 2025-08-18 22:14:19 +00:00
parent 69943c2717
commit 1311bd25fd

View File

@ -35,7 +35,7 @@
<li><strong>Wildcard search</strong> — use <code>*</code>.<br> <li><strong>Wildcard search</strong> — use <code>*</code>.<br>
<em>Example:</em> <code>lov*</code></li> <em>Example:</em> <code>lov*</code></li>
<li><strong>Scripture search</strong> — type a Bible book.<br> <li><strong>Scripture search</strong> — type a Bible book.<br>
<em>Example:</em> <code>John 3:16</code></li> <em>Example:</em> <code>John 3:16</code></li>
</ul> </ul>
</div> </div>
@ -84,12 +84,22 @@
</div> </div>
</div> </div>
</div> </div>
<!-- ===== Illustration of the Day ===== -->
<div class="container"> <div class="container">
<form method="get" class="search-form"> <form method="get" class="search-form">
<h1 class="page-title">Illustration of the Day</h1> <h1 class="page-title">Illustration of the Day</h1>
<!-- insert illustration of the day here --> <!-- insert illustration of the day here -->
</div> <div id="iotd-card" class="card" style="padding:16px; margin:12px 0;">
</form> <div id="iotd-text" class="lead-text" style="white-space:pre-wrap; margin-bottom:12px;">
Loading…
</div>
<div id="iotd-actions">
<a id="iotd-open" href="#" class="btn btn-primary">Open Illustration</a>
</div>
</div>
</form>
</div>
</div> </div>
<script> <script>
@ -192,7 +202,7 @@
} }
refetchRecent(); refetchRecent();
// ✅ Log searches with Beacon on submit (this is what was missing) // ✅ Log searches with Beacon on submit
const searchForm = document.querySelector('form.search-form'); const searchForm = document.querySelector('form.search-form');
if (searchForm){ if (searchForm){
searchForm.addEventListener('submit', ()=>{ searchForm.addEventListener('submit', ()=>{
@ -200,7 +210,6 @@
const fd = new FormData(searchForm); const fd = new FormData(searchForm);
const data = new URLSearchParams(); const data = new URLSearchParams();
data.append('q', (fd.get('q') || '').trim()); data.append('q', (fd.get('q') || '').trim());
// selected checkboxes (match your field names)
['subject','illustration','application','scripture_raw','source','talk_title','talk_number','entry_code'] ['subject','illustration','application','scripture_raw','source','talk_title','talk_number','entry_code']
.forEach(k=>{ .forEach(k=>{
if (fd.get(k)) data.append(`sel[${k}]`, 'on'); if (fd.get(k)) data.append(`sel[${k}]`, 'on');
@ -237,6 +246,109 @@
if (!clickedInside) panel.classList.remove('open'); if (!clickedInside) panel.classList.remove('open');
} }
}); });
/* ===============================
Illustration of the Day (client-only, deterministic)
=============================== */
(function illustrationOfTheDay(){
const total = {{ total|default:0 }};
const iotdTextEl = document.getElementById('iotd-text');
const iotdOpenEl = document.getElementById('iotd-open');
if (!iotdTextEl || !iotdOpenEl) return;
if (!total || total < 1){
iotdTextEl.textContent = 'No illustrations available yet.';
iotdOpenEl.style.display = 'none';
return;
}
// Seed from local date (YYYYMMDD) so its the same for everyone that day
const today = new Date();
const ymd = today.getFullYear()*10000 + (today.getMonth()+1)*100 + today.getDate();
// Simple string hash -> 32-bit -> xorshift for decent spread
function xorshift32(seed){
let x = seed | 0;
x ^= x << 13; x ^= x >>> 17; x ^= x << 5;
return (x >>> 0);
}
function pickIndex(max, seed){
// (0..max-1)
return xorshift32(seed) % max;
}
// We dont know if IDs are contiguous, so:
// 1) choose a *candidate* ID in [1..maxIdGuess]
// 2) try up to N nearby IDs until one 200s
// Best guess for max id is "total * 1.5" (cheap heuristic).
const maxIdGuess = Math.max(total, Math.floor(total * 1.5));
let candidate = 1 + pickIndex(maxIdGuess, ymd ^ 0x9E3779B9);
async function tryFetch(id){
const url = entryUrlFor(id);
try{
const r = await fetch(url, { credentials:'same-origin' });
if (r.ok) {
const html = await r.text();
return { ok:true, id, html };
}
}catch(_){}
return { ok:false };
}
function ensurePunct(str){
const s = (str || '').trim();
if (!s) return '';
// If ends with punctuation, keep; else add period.
return /[.!?…]$/.test(s) ? s : (s + '.');
}
function extractSectionText(doc, label){
// Look for a section with a label like "Illustration" / "Application"
const labels = doc.querySelectorAll('.section-label, .meta-label, h3, h4, strong');
for (const el of labels){
const t = (el.textContent || '').trim().toLowerCase();
if (t === label){
// usual structure: label inside a .section; body nearby
const section = el.closest('.section') || el.parentElement;
if (section){
const body = section.querySelector('.lead-text') ||
section.querySelector('.section-body') ||
section.querySelector('p');
if (body) return (body.textContent || '').trim();
}
}
}
// Fallback: first .lead-text / .section-body on page if label search failed
const alt = doc.querySelector('.lead-text, .section-body');
return alt ? (alt.textContent || '').trim() : '';
}
function renderIotd(id, html){
const dom = new DOMParser().parseFromString(html, 'text/html');
const illustration = extractSectionText(dom, 'illustration');
const application = extractSectionText(dom, 'application');
const merged = (ensurePunct(illustration) + (application ? ' ' + application : '')).trim();
iotdTextEl.textContent = merged || 'Open to view todays illustration.';
iotdOpenEl.href = entryUrlFor(id);
}
(async function findAndRender(){
// Try candidate, then probe up to +/- 10 around it
const offsets = [0,1,-1,2,-2,3,-3,4,-4,5,-5,6,-6,7,-7,8,-8,9,-9,10,-10];
for (const off of offsets){
const id = Math.max(1, candidate + off);
const res = await tryFetch(id);
if (res.ok) { renderIotd(id, res.html); return; }
}
// Give up—show a graceful fallback
iotdTextEl.textContent = 'Unable to load todays illustration.';
iotdOpenEl.style.display = 'none';
})();
})();
})(); })();
</script> </script>