235 lines
9.3 KiB
JavaScript
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, '<') + '</p>' +
|
|
'<div class="joke-punch" data-punch>' + jj.p.replace(/</g, '<') + '</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, '<') + '</p>' +
|
|
'<div class="joke-punch" data-punch>' + j.p.replace(/</g, '<') + '</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;
|
|
});
|
|
});
|
|
})();
|