Update web/templates/search.html
This commit is contained in:
parent
1311bd25fd
commit
2651b65a70
@ -247,7 +247,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
/* ===============================
|
||||
/* ===============================
|
||||
Illustration of the Day (client-only, deterministic)
|
||||
=============================== */
|
||||
(function illustrationOfTheDay(){
|
||||
@ -266,50 +266,47 @@
|
||||
const today = new Date();
|
||||
const ymd = today.getFullYear()*10000 + (today.getMonth()+1)*100 + today.getDate();
|
||||
|
||||
// Simple string hash -> 32-bit -> xorshift for decent spread
|
||||
// 32‑bit xorshift PRNG for a good daily seed
|
||||
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;
|
||||
const seed = xorshift32(ymd ^ 0x9E3779B9);
|
||||
|
||||
// We don’t know ID gaps, so try a deterministic pseudo‑random
|
||||
// permutation over a *generous* range and stop at the first 200.
|
||||
const maxGuess = Math.max(100, Math.floor(total * 4)); // cast a wide net
|
||||
const maxAttempts = Math.min(600, maxGuess); // cap network work
|
||||
|
||||
// Linear congruential generator style “index -> id” mapping (deterministic)
|
||||
function idAt(i){
|
||||
// multiplier is odd; modulus is implicit 2^32, then map to [1..maxGuess]
|
||||
const v = (Math.imul(i + 1, 1103515245) + 12345 + seed) >>> 0;
|
||||
return 1 + (v % maxGuess);
|
||||
}
|
||||
|
||||
// We don’t 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){
|
||||
async function fetchEntryHtml(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 };
|
||||
}
|
||||
const r = await fetch(url, { credentials: 'same-origin' });
|
||||
if (r.ok) return await r.text();
|
||||
}catch(_){}
|
||||
return { ok:false };
|
||||
return null;
|
||||
}
|
||||
|
||||
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"
|
||||
// Look for label “Illustration” / “Application” commonly used in your templates
|
||||
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') ||
|
||||
@ -319,7 +316,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fallback: first .lead-text / .section-body on page if label search failed
|
||||
// fallback
|
||||
const alt = doc.querySelector('.lead-text, .section-body');
|
||||
return alt ? (alt.textContent || '').trim() : '';
|
||||
}
|
||||
@ -328,22 +325,23 @@
|
||||
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 today’s illustration.';
|
||||
iotdOpenEl.href = entryUrlFor(id);
|
||||
iotdOpenEl.style.display = 'inline-block';
|
||||
}
|
||||
|
||||
(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; }
|
||||
for (let i = 0; i < maxAttempts; i++){
|
||||
const id = idAt(i);
|
||||
const html = await fetchEntryHtml(id);
|
||||
if (html){
|
||||
renderIotd(id, html);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Give up—show a graceful fallback
|
||||
// If nothing responded 200 within our attempts, fail gracefully
|
||||
iotdTextEl.textContent = 'Unable to load today’s illustration.';
|
||||
iotdOpenEl.style.display = 'none';
|
||||
})();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user