devops/bdj/static/main.js

235 lines
9.3 KiB
JavaScript

/* BestDadJokes.lol client-side behavior */
(function () {
'use strict';
document.body.classList.add('js-ux');
var JOKES = (window.BDJ && window.BDJ.jokes) || [];
function byId(id) { return JOKES[id]; }
function $(sel, root) { return (root || document).querySelector(sel); }
function $$(sel, root) { return Array.prototype.slice.call((root || document).querySelectorAll(sel)); }
/* ---- punchline reveal ---- */
document.addEventListener('click', function (e) {
var punch = e.target.closest && e.target.closest('[data-punch]');
if (punch && !punch.classList.contains('revealed')) {
punch.classList.add('revealed');
}
});
/* ---- random ---- */
var lastId = -1;
function randomJoke() {
if (!JOKES.length) return null;
var j, guard = 0;
do { j = JOKES[Math.floor(Math.random() * JOKES.length)]; guard++; }
while (j.id === lastId && guard < 20 && JOKES.length > 1);
lastId = j.id;
return j;
}
function fillRandom(setupEl, punchEl) {
var j = randomJoke();
if (!j) return null;
setupEl.textContent = j.s;
punchEl.textContent = j.p;
punchEl.classList.add('revealed');
punchEl.setAttribute('data-no-blur', '');
return j;
}
/* ---- clipboard ---- */
function copyText(text, btn) {
function ok() { if (btn) { var o = btn.textContent; btn.textContent = '✅ Copied'; setTimeout(function () { btn.textContent = o; }, 1400); } }
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(ok, function () { legacy(); });
} else { legacy(); }
function legacy() {
var ta = document.createElement('textarea');
ta.value = text; document.body.appendChild(ta); ta.select();
try { document.execCommand('copy'); ok(); } catch (err) {}
document.body.removeChild(ta);
}
}
/* ---- share card (1080x1350) ---- */
function wrapText(ctx, text, x, y, maxW, lh) {
var words = text.split(' '), line = '', lines = [];
words.forEach(function (w) {
var test = line + w + ' ';
if (ctx.measureText(test).width > maxW && line) { lines.push(line.trim()); line = w + ' '; }
else { line = test; }
});
lines.push(line.trim());
lines.forEach(function (l, i) { ctx.fillText(l, x, y + i * lh); });
return y + lines.length * lh;
}
function makeCard(setup, punch, cb) {
var c = document.createElement('canvas');
c.width = 1080; c.height = 1350;
var ctx = c.getContext('2d');
ctx.fillStyle = '#fff8ec'; ctx.fillRect(0, 0, 1080, 1350);
// border frame
ctx.strokeStyle = '#2b2118'; ctx.lineWidth = 8;
ctx.strokeRect(36, 36, 1008, 1278);
// emoji
ctx.font = '110px serif'; ctx.textAlign = 'center';
ctx.fillText('😂', 540, 240);
// setup
ctx.fillStyle = '#2b2118'; ctx.textAlign = 'center';
ctx.font = '700 54px ui-rounded, -apple-system, "Segoe UI", Arial, sans-serif';
var y = wrapText(ctx, setup, 540, 380, 880, 72);
// punchline
ctx.fillStyle = '#e4572e';
ctx.font = '800 66px ui-rounded, -apple-system, "Segoe UI", Arial, sans-serif';
y = wrapText(ctx, punch, 540, y + 150, 880, 86);
// footer
ctx.fillStyle = '#5c4f43'; ctx.font = '700 34px -apple-system, "Segoe UI", Arial, sans-serif';
ctx.fillText('BestDadJokes.lol', 540, 1240);
cb(c);
}
function shareCard(setup, punch) {
var name = 'dad-joke.png';
makeCard(setup, punch, function (canvas) {
if (navigator.canShare && navigator.share) {
canvas.toBlob(function (blob) {
if (!blob) return;
var file = new File([blob], name, { type: 'image/png' });
if (navigator.canShare({ files: [file] })) {
navigator.share({ files: [file], title: 'A dad joke for you' }).catch(function () {});
}
}, 'image/png');
} else {
var a = document.createElement('a');
a.download = name; a.href = canvas.toDataURL('image/png');
document.body.appendChild(a); a.click(); document.body.removeChild(a);
}
});
}
function wireCard(opts) {
var setupEl = $(opts.setup), punchEl = $(opts.punch);
if (!setupEl || !punchEl) return;
var current = opts.initial || null;
if (opts.nextBtn) {
$(opts.nextBtn).addEventListener('click', function () { current = fillRandom(setupEl, punchEl); });
}
if (opts.copyBtn) {
$(opts.copyBtn).addEventListener('click', function (e) {
if (current) copyText(current.s + ' ' + current.p, e.target);
});
}
if (opts.shareBtn) {
$(opts.shareBtn).addEventListener('click', function () {
if (current) shareCard(current.s, current.p);
});
}
return {
get: function () { return current; },
set: function (j) { current = j; setupEl.textContent = j.s; punchEl.textContent = j.p; punchEl.classList.add('revealed'); punchEl.setAttribute('data-no-blur', ''); }
};
}
/* ---- page-specific wiring ---- */
// hero
var hero = wireCard({ setup: '[data-hero-setup]', punch: '[data-hero-punch]', nextBtn: '[data-hero-next]', copyBtn: '[data-copy-hero]', shareBtn: '[data-share-hero]' });
if (hero) hero.set(randomJoke());
// generator
wireCard({ setup: '[data-gen-setup]', punch: '[data-gen-punch]', nextBtn: '[data-gen-next]', copyBtn: '[data-gen-copy]', shareBtn: '[data-gen-share]' });
// joke of the day (+ ?d= override for archive permalinks)
var jodSetup = $('[data-jod-setup]');
if (jodSetup) {
var m = /[?&]d=(\d{4}-\d{2}-\d{2})/.exec(location.search);
var base = new Date(Date.UTC(2026, 8, 15)); // launch epoch for the rotation
var jod = null;
if (m) {
var parts = m[1].split('-'), d = new Date(Date.UTC(+parts[0], +parts[1] - 1, +parts[2]));
if (!isNaN(d.getTime())) {
var idx = Math.floor((d - new Date(d.getUTCFullYear(), 0, 1)) / 86400000) + d.getUTCMonth() * 31 + d.getUTCDate();
jod = JOKES[(d.getTime() / 86400000 | 0) % JOKES.length] || JOKES[0];
$('[data-jod-date]').textContent = d.toUTCString().slice(0, 16);
var ctl = { setup: '[data-jod-setup]', punch: '[data-jod-punch]' };
jodSetup.textContent = jod.s;
$('[data-jod-punch]').textContent = jod.p;
$('[data-jod-punch]').classList.add('revealed');
$('[data-jod-punch]').setAttribute('data-no-blur', '');
}
}
var jodCopy = $('[data-jod-copy]');
if (jodCopy) {
var punchEl = $('[data-jod-punch]');
jodCopy.addEventListener('click', function (e) { copyText(jodSetup.textContent + ' ' + punchEl.textContent, e.target); });
}
var jodShare = $('[data-jod-share]');
if (jodShare) {
jodShare.addEventListener('click', function () { shareCard(jodSetup.textContent, $('[data-jod-punch]').textContent); });
}
// archive: last 30 days
var arch = $('[data-jod-archive]');
if (arch && JOKES.length) {
var today = new Date();
var html = '';
for (var i = 1; i <= 30; i++) {
var dd = new Date(today.getTime() - i * 86400000);
var iso = dd.toISOString().slice(0, 10);
var jj = JOKES[Math.floor(dd.getTime() / 86400000) % JOKES.length];
html += '<figure class="joke-card"><p class="jod-date">' + iso + '</p>' +
'<p class="joke-setup">' + jj.s.replace(/</g, '&lt;') + '</p>' +
'<div class="joke-punch" data-punch>' + jj.p.replace(/</g, '&lt;') + '</div></figure>';
}
arch.innerHTML = html;
}
}
/* ---- per-card copy/share buttons ---- */
document.addEventListener('click', function (e) {
var t = e.target;
if (t.hasAttribute && t.hasAttribute('data-copy')) {
var j = byId(+t.getAttribute('data-copy'));
if (j) copyText(j.s + ' ' + j.p, t);
}
if (t.hasAttribute && t.hasAttribute('data-share')) {
var jj = byId(+t.getAttribute('data-share'));
if (jj) shareCard(jj.s, jj.p);
}
});
/* ---- search ---- */
var box = $('#search-box');
if (box) {
var results = $('[data-search-results]');
var countEl = $('[data-search-count]');
var emptyEl = $('[data-search-empty]');
function renderResults(q) {
q = q.trim().toLowerCase();
var hits = [];
if (q.length > 1) {
hits = JOKES.filter(function (j) {
return (j.s + ' ' + j.p).toLowerCase().indexOf(q) !== -1;
}).slice(0, 60);
}
countEl.textContent = q.length > 1 ? hits.length + ' joke' + (hits.length === 1 ? '' : 's') + ' matching "' + q + '"' : '';
emptyEl.style.display = (q.length > 1 && !hits.length) ? 'block' : 'none';
results.innerHTML = hits.map(function (j) {
return '<figure class="joke-card"><p class="joke-setup">' + j.s.replace(/</g, '&lt;') + '</p>' +
'<div class="joke-punch" data-punch>' + j.p.replace(/</g, '&lt;') + '</div>' +
'<figcaption class="joke-actions"><button class="btn-tiny" data-copy="' + j.id + '">📋 Copy</button>' +
'<a class="btn-tiny" style="text-decoration:none" href="/category/' + j.cat + '/">Category →</a></figcaption></figure>';
}).join('');
}
box.addEventListener('input', function () { renderResults(box.value); });
var q = /[?&]q=([^&]*)/.exec(location.search);
if (q) { box.value = decodeURIComponent(q[1].replace(/\+/g, ' ')); }
renderResults(box.value);
box.focus();
}
/* ---- close mobile nav on selection ---- */
$$('.site-nav a').forEach(function (a) {
a.addEventListener('click', function () {
var t = $('#navtoggle');
if (t) t.checked = false;
});
});
})();