Announcement card doubles as a notice
Today's webinar was cancelled and the card had no way to say so: it required a flyer to render at all, and it always drew a Join Google Meet button and the calendar buttons, so the only shape it could take was an invitation. It now renders without an image, and with no meetUrl it draws no Join button and no calendar row. Never invite someone to a meeting that is not happening. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+208
-202
@@ -1,202 +1,208 @@
|
|||||||
/* announce.js — dismissible event pop-up overlay (RM Circle).
|
/* announce.js — dismissible event pop-up overlay (RM Circle).
|
||||||
Content is admin-controlled: fetched from /api/announce (edit it under
|
Content is admin-controlled: fetched from /api/announce (edit it under
|
||||||
Admin → Settings → Event pop-up; no deploy needed). Self-gating: shows once
|
Admin → Settings → Event pop-up; no deploy needed). Self-gating: shows once
|
||||||
per browser until dismissed, and never after `expiresUTC`. */
|
per browser until dismissed, and never after `expiresUTC`. */
|
||||||
(function () {
|
(function () {
|
||||||
function parseTimes(s) {
|
function parseTimes(s) {
|
||||||
return String(s || '').split(/\r?\n+/).map(function (l) { return l.trim(); }).filter(Boolean)
|
return String(s || '').split(/\r?\n+/).map(function (l) { return l.trim(); }).filter(Boolean)
|
||||||
.map(function (l) { var i = l.indexOf('|'); return i < 0 ? [l, ''] : [l.slice(0, i).trim(), l.slice(i + 1).trim()]; });
|
.map(function (l) { var i = l.indexOf('|'); return i < 0 ? [l, ''] : [l.slice(0, i).trim(), l.slice(i + 1).trim()]; });
|
||||||
}
|
}
|
||||||
|
|
||||||
function start(EV) {
|
function start(EV) {
|
||||||
if (!EV || !EV.enabled || !EV.id || !EV.img) return false;
|
// A cancellation has no flyer and no meet link, so neither is required any more: the
|
||||||
if (EV.expiresUTC) { var exp = Date.parse(EV.expiresUTC); if (exp && Date.now() > exp) return false; }
|
// same card doubles as a plain notice (Marty, 2026-09-24).
|
||||||
var KEY = 'rmc-announce-' + EV.id;
|
if (!EV || !EV.enabled || !EV.id) return false;
|
||||||
try { if (localStorage.getItem(KEY) === 'done') return false; } catch (e) {}
|
if (EV.expiresUTC) { var exp = Date.parse(EV.expiresUTC); if (exp && Date.now() > exp) return false; }
|
||||||
EV.timesArr = parseTimes(EV.times);
|
var KEY = 'rmc-announce-' + EV.id;
|
||||||
|
try { if (localStorage.getItem(KEY) === 'done') return false; } catch (e) {}
|
||||||
var back, card;
|
EV.timesArr = parseTimes(EV.times);
|
||||||
function done() { try { localStorage.setItem(KEY, 'done'); } catch (e) {} }
|
|
||||||
function close() {
|
var back, card;
|
||||||
if (!back) return;
|
function done() { try { localStorage.setItem(KEY, 'done'); } catch (e) {} }
|
||||||
back.style.opacity = '0';
|
function close() {
|
||||||
setTimeout(function () { if (back && back.parentNode) back.parentNode.removeChild(back); back = null; }, 260);
|
if (!back) return;
|
||||||
document.removeEventListener('keydown', onKey);
|
back.style.opacity = '0';
|
||||||
}
|
setTimeout(function () { if (back && back.parentNode) back.parentNode.removeChild(back); back = null; }, 260);
|
||||||
function dismiss() { done(); close(); }
|
document.removeEventListener('keydown', onKey);
|
||||||
function onKey(e) { if (e.key === 'Escape') dismiss(); }
|
}
|
||||||
|
function dismiss() { done(); close(); }
|
||||||
function build() {
|
function onKey(e) { if (e.key === 'Escape') dismiss(); }
|
||||||
back = document.createElement('div');
|
|
||||||
back.setAttribute('role', 'dialog');
|
function build() {
|
||||||
back.setAttribute('aria-label', (EV.eyebrow || 'Event') + ' invitation');
|
back = document.createElement('div');
|
||||||
back.style.cssText = 'position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;' +
|
back.setAttribute('role', 'dialog');
|
||||||
'padding:20px;background:rgba(3,7,14,.82);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);' +
|
back.setAttribute('aria-label', (EV.eyebrow || 'Event') + ' invitation');
|
||||||
'opacity:0;transition:opacity .28s ease;overflow:auto;';
|
back.style.cssText = 'position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;' +
|
||||||
back.addEventListener('click', function (e) { if (e.target === back) dismiss(); });
|
'padding:20px;background:rgba(3,7,14,.82);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);' +
|
||||||
|
'opacity:0;transition:opacity .28s ease;overflow:auto;';
|
||||||
card = document.createElement('div');
|
back.addEventListener('click', function (e) { if (e.target === back) dismiss(); });
|
||||||
card.style.cssText = 'position:relative;width:100%;max-width:428px;max-height:94vh;overflow:auto;border-radius:20px;' +
|
|
||||||
'background:#0a1119;border:1px solid rgba(212,175,55,.55);box-shadow:0 24px 70px rgba(0,0,0,.7),0 0 0 1px rgba(212,175,55,.15);' +
|
card = document.createElement('div');
|
||||||
'font-family:system-ui,Segoe UI,Arial,sans-serif;';
|
card.style.cssText = 'position:relative;width:100%;max-width:428px;max-height:94vh;overflow:auto;border-radius:20px;' +
|
||||||
|
'background:#0a1119;border:1px solid rgba(212,175,55,.55);box-shadow:0 24px 70px rgba(0,0,0,.7),0 0 0 1px rgba(212,175,55,.15);' +
|
||||||
var x = document.createElement('button');
|
'font-family:system-ui,Segoe UI,Arial,sans-serif;';
|
||||||
x.setAttribute('aria-label', 'Close');
|
|
||||||
x.innerHTML = '×';
|
var x = document.createElement('button');
|
||||||
x.style.cssText = 'position:absolute;top:10px;right:12px;z-index:2;width:38px;height:38px;border-radius:50%;border:none;cursor:pointer;' +
|
x.setAttribute('aria-label', 'Close');
|
||||||
'background:rgba(6,10,16,.72);color:#fff;font-size:24px;line-height:1;font-weight:700;display:grid;place-items:center;box-shadow:0 2px 10px rgba(0,0,0,.5);';
|
x.innerHTML = '×';
|
||||||
x.addEventListener('click', dismiss);
|
x.style.cssText = 'position:absolute;top:10px;right:12px;z-index:2;width:38px;height:38px;border-radius:50%;border:none;cursor:pointer;' +
|
||||||
|
'background:rgba(6,10,16,.72);color:#fff;font-size:24px;line-height:1;font-weight:700;display:grid;place-items:center;box-shadow:0 2px 10px rgba(0,0,0,.5);';
|
||||||
var eyebrow = document.createElement('div');
|
x.addEventListener('click', dismiss);
|
||||||
eyebrow.textContent = EV.eyebrow || '';
|
|
||||||
eyebrow.style.cssText = 'text-align:center;letter-spacing:2px;text-transform:uppercase;font-size:11px;font-weight:700;color:#d4af37;padding:14px 44px 10px;';
|
var eyebrow = document.createElement('div');
|
||||||
|
eyebrow.textContent = EV.eyebrow || '';
|
||||||
var img = document.createElement('img');
|
eyebrow.style.cssText = 'text-align:center;letter-spacing:2px;text-transform:uppercase;font-size:11px;font-weight:700;color:#d4af37;padding:14px 44px 10px;';
|
||||||
img.src = EV.img;
|
|
||||||
img.alt = EV.eyebrow || 'Event flyer';
|
var img = EV.img ? document.createElement('img') : null;
|
||||||
img.style.cssText = 'display:block;width:100%;height:auto;';
|
if (img) {
|
||||||
|
img.src = EV.img;
|
||||||
var foot = document.createElement('div');
|
img.alt = EV.eyebrow || 'Event flyer';
|
||||||
foot.style.cssText = 'padding:16px 20px 20px;';
|
img.style.cssText = 'display:block;width:100%;height:auto;';
|
||||||
|
}
|
||||||
if (EV.dateLabel) {
|
|
||||||
var dateRow = document.createElement('div');
|
var foot = document.createElement('div');
|
||||||
dateRow.innerHTML = '🗓 <b></b>';
|
foot.style.cssText = 'padding:16px 20px 20px;';
|
||||||
dateRow.querySelector('b').textContent = EV.dateLabel;
|
|
||||||
dateRow.style.cssText = 'text-align:center;color:#eef2f7;font-size:15px;margin-bottom:12px;';
|
if (EV.dateLabel) {
|
||||||
foot.appendChild(dateRow);
|
var dateRow = document.createElement('div');
|
||||||
}
|
dateRow.innerHTML = '🗓 <b></b>';
|
||||||
|
dateRow.querySelector('b').textContent = EV.dateLabel;
|
||||||
if (EV.timesArr.length) {
|
dateRow.style.cssText = 'text-align:center;color:#eef2f7;font-size:15px;margin-bottom:12px;';
|
||||||
var grid = document.createElement('div');
|
foot.appendChild(dateRow);
|
||||||
grid.style.cssText = 'display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:16px;';
|
}
|
||||||
EV.timesArr.forEach(function (t) {
|
|
||||||
var cell = document.createElement('div');
|
if (EV.timesArr.length) {
|
||||||
cell.style.cssText = 'background:rgba(212,175,55,.08);border:1px solid rgba(212,175,55,.22);border-radius:10px;padding:8px 10px;';
|
var grid = document.createElement('div');
|
||||||
var time = document.createElement('div');
|
grid.style.cssText = 'display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:16px;';
|
||||||
time.style.cssText = 'font-size:15px;font-weight:800;color:#f3c34a;'; time.textContent = t[0];
|
EV.timesArr.forEach(function (t) {
|
||||||
var region = document.createElement('div');
|
var cell = document.createElement('div');
|
||||||
region.style.cssText = 'font-size:11px;color:#9fb0c2;margin-top:1px;'; region.textContent = t[1] || ' ';
|
cell.style.cssText = 'background:rgba(212,175,55,.08);border:1px solid rgba(212,175,55,.22);border-radius:10px;padding:8px 10px;';
|
||||||
cell.appendChild(time); cell.appendChild(region); grid.appendChild(cell);
|
var time = document.createElement('div');
|
||||||
});
|
time.style.cssText = 'font-size:15px;font-weight:800;color:#f3c34a;'; time.textContent = t[0];
|
||||||
foot.appendChild(grid);
|
var region = document.createElement('div');
|
||||||
}
|
region.style.cssText = 'font-size:11px;color:#9fb0c2;margin-top:1px;'; region.textContent = t[1] || ' ';
|
||||||
|
cell.appendChild(time); cell.appendChild(region); grid.appendChild(cell);
|
||||||
var join = document.createElement('a');
|
});
|
||||||
join.href = EV.meetUrl || '#'; join.target = '_blank'; join.rel = 'noopener';
|
foot.appendChild(grid);
|
||||||
join.textContent = 'Join Google Meet →';
|
}
|
||||||
join.style.cssText = 'display:block;text-align:center;text-decoration:none;background:linear-gradient(180deg,#e9c249,#d4af37);' +
|
|
||||||
'color:#0a1119;font-size:18px;font-weight:800;padding:15px;border-radius:12px;box-shadow:0 8px 22px rgba(212,175,55,.35);';
|
var join = EV.meetUrl ? document.createElement('a') : null;
|
||||||
join.addEventListener('click', done);
|
if (join) {
|
||||||
foot.appendChild(join);
|
join.href = EV.meetUrl; join.target = '_blank'; join.rel = 'noopener';
|
||||||
|
join.textContent = 'Join Google Meet →';
|
||||||
var st = EV.startUTC ? Date.parse(EV.startUTC) : 0;
|
join.style.cssText = 'display:block;text-align:center;text-decoration:none;background:linear-gradient(180deg,#e9c249,#d4af37);' +
|
||||||
if (st) {
|
'color:#0a1119;font-size:18px;font-weight:800;padding:15px;border-radius:12px;box-shadow:0 8px 22px rgba(212,175,55,.35);';
|
||||||
var en = st + (Number(EV.durationMin) || 60) * 60000;
|
join.addEventListener('click', done);
|
||||||
var f = function (ms) { return new Date(ms).toISOString().replace(/[-:]|\.\d{3}/g, ''); };
|
foot.appendChild(join);
|
||||||
var title = EV.eyebrow || 'Team RM Circle · Weekly Huddle';
|
}
|
||||||
var details = (EV.meetUrl ? 'Join: ' + EV.meetUrl + '\n' : '') + String(EV.times || '').replace(/\|/g, ' ');
|
|
||||||
var gcal = 'https://calendar.google.com/calendar/render?action=TEMPLATE&text=' + encodeURIComponent(title) +
|
var st = (EV.meetUrl && EV.startUTC) ? Date.parse(EV.startUTC) : 0;
|
||||||
'&dates=' + f(st) + '/' + f(en) + '&details=' + encodeURIComponent(details) + '&location=' + encodeURIComponent(EV.meetUrl || '');
|
if (st) {
|
||||||
// On a phone a calendar.google.com link is claimed by the Google Calendar app, which opens its own
|
var en = st + (Number(EV.durationMin) || 60) * 60000;
|
||||||
// home screen and throws the event details away. Phones get the calendar file instead: iOS, Android
|
var f = function (ms) { return new Date(ms).toISOString().replace(/[-:]|\.\d{3}/g, ''); };
|
||||||
// and the Google Calendar app all import it straight into the default calendar.
|
var title = EV.eyebrow || 'Team RM Circle · Weekly Huddle';
|
||||||
var phone = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent || '');
|
var details = (EV.meetUrl ? 'Join: ' + EV.meetUrl + '\n' : '') + String(EV.times || '').replace(/\|/g, ' ');
|
||||||
var row = document.createElement('div');
|
var gcal = 'https://calendar.google.com/calendar/render?action=TEMPLATE&text=' + encodeURIComponent(title) +
|
||||||
row.style.cssText = 'display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:10px;';
|
'&dates=' + f(st) + '/' + f(en) + '&details=' + encodeURIComponent(details) + '&location=' + encodeURIComponent(EV.meetUrl || '');
|
||||||
var btnCss = 'display:block;text-align:center;text-decoration:none;background:rgba(212,175,55,.08);border:1px solid rgba(212,175,55,.35);' +
|
// On a phone a calendar.google.com link is claimed by the Google Calendar app, which opens its own
|
||||||
'color:#f3c34a;font-size:13px;font-weight:700;padding:10px 8px;border-radius:10px;';
|
// home screen and throws the event details away. Phones get the calendar file instead: iOS, Android
|
||||||
var g = document.createElement('a');
|
// and the Google Calendar app all import it straight into the default calendar.
|
||||||
g.textContent = '📅 Add to Calendar'; g.style.cssText = btnCss;
|
var phone = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent || '');
|
||||||
if (phone) g.href = '/announce.ics?open=1';
|
var row = document.createElement('div');
|
||||||
else { g.href = gcal; g.target = '_blank'; g.rel = 'noopener'; }
|
row.style.cssText = 'display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:10px;';
|
||||||
g.addEventListener('click', done);
|
var btnCss = 'display:block;text-align:center;text-decoration:none;background:rgba(212,175,55,.08);border:1px solid rgba(212,175,55,.35);' +
|
||||||
var a = document.createElement('a');
|
'color:#f3c34a;font-size:13px;font-weight:700;padding:10px 8px;border-radius:10px;';
|
||||||
a.style.cssText = btnCss;
|
var g = document.createElement('a');
|
||||||
if (phone) { a.href = gcal; a.target = '_blank'; a.rel = 'noopener'; a.textContent = 'Google Calendar'; }
|
g.textContent = '📅 Add to Calendar'; g.style.cssText = btnCss;
|
||||||
else { a.href = '/announce.ics'; a.setAttribute('download', 'rm-circle-huddle.ics'); a.textContent = 'Apple / Outlook'; }
|
if (phone) g.href = '/announce.ics?open=1';
|
||||||
a.addEventListener('click', done);
|
else { g.href = gcal; g.target = '_blank'; g.rel = 'noopener'; }
|
||||||
row.appendChild(g); row.appendChild(a); foot.appendChild(row);
|
g.addEventListener('click', done);
|
||||||
}
|
var a = document.createElement('a');
|
||||||
|
a.style.cssText = btnCss;
|
||||||
if (EV.meetUrl) {
|
if (phone) { a.href = gcal; a.target = '_blank'; a.rel = 'noopener'; a.textContent = 'Google Calendar'; }
|
||||||
var link = document.createElement('div');
|
else { a.href = '/announce.ics'; a.setAttribute('download', 'rm-circle-huddle.ics'); a.textContent = 'Apple / Outlook'; }
|
||||||
link.textContent = EV.meetUrl.replace(/^https?:\/\//, '');
|
a.addEventListener('click', done);
|
||||||
link.style.cssText = 'text-align:center;color:#9fb0c2;font-size:13px;margin-top:10px;font-family:ui-monospace,Menlo,Consolas,monospace;word-break:break-all;';
|
row.appendChild(g); row.appendChild(a); foot.appendChild(row);
|
||||||
foot.appendChild(link);
|
}
|
||||||
}
|
|
||||||
|
if (EV.meetUrl) {
|
||||||
var later = document.createElement('button');
|
var link = document.createElement('div');
|
||||||
later.textContent = 'Maybe later';
|
link.textContent = EV.meetUrl.replace(/^https?:\/\//, '');
|
||||||
later.style.cssText = 'display:block;margin:12px auto 0;background:none;border:none;color:#7d8ea0;font-size:13px;cursor:pointer;text-decoration:underline;';
|
link.style.cssText = 'text-align:center;color:#9fb0c2;font-size:13px;margin-top:10px;font-family:ui-monospace,Menlo,Consolas,monospace;word-break:break-all;';
|
||||||
later.addEventListener('click', dismiss);
|
foot.appendChild(link);
|
||||||
foot.appendChild(later);
|
}
|
||||||
|
|
||||||
card.appendChild(x); card.appendChild(eyebrow); card.appendChild(img); card.appendChild(foot);
|
var later = document.createElement('button');
|
||||||
back.appendChild(card); document.body.appendChild(back);
|
later.textContent = 'Maybe later';
|
||||||
document.addEventListener('keydown', onKey);
|
later.style.cssText = 'display:block;margin:12px auto 0;background:none;border:none;color:#7d8ea0;font-size:13px;cursor:pointer;text-decoration:underline;';
|
||||||
requestAnimationFrame(function () { back.style.opacity = '1'; });
|
later.addEventListener('click', dismiss);
|
||||||
}
|
foot.appendChild(later);
|
||||||
|
|
||||||
setTimeout(build, 1100);
|
card.appendChild(x); card.appendChild(eyebrow); if (img) card.appendChild(img); card.appendChild(foot);
|
||||||
return true;
|
back.appendChild(card); document.body.appendChild(back);
|
||||||
}
|
document.addEventListener('keydown', onKey);
|
||||||
|
requestAnimationFrame(function () { back.style.opacity = '1'; });
|
||||||
/* Promo overlay: one flyer, one button, once per browser SESSION (sessionStorage), only on the
|
}
|
||||||
pages listed in promo.pages (path segment after the first slash: 'my', 'app', or 'all'). */
|
|
||||||
function startPromo(P) {
|
setTimeout(build, 1100);
|
||||||
if (!P || !P.enabled || !P.id || !P.img) return;
|
return true;
|
||||||
if (P.expiresUTC) { var exp = Date.parse(P.expiresUTC); if (exp && Date.now() > exp) return; }
|
}
|
||||||
var seg = (location.pathname.split('/')[1] || 'home').toLowerCase();
|
|
||||||
var pages = String(P.pages || 'all').split(',').map(function (x) { return x.trim().toLowerCase(); }).filter(Boolean);
|
/* Promo overlay: one flyer, one button, once per browser SESSION (sessionStorage), only on the
|
||||||
if (pages.indexOf('all') < 0 && pages.indexOf(seg) < 0) return;
|
pages listed in promo.pages (path segment after the first slash: 'my', 'app', or 'all'). */
|
||||||
var KEY = 'rmc-promo-' + P.id;
|
function startPromo(P) {
|
||||||
try { if (sessionStorage.getItem(KEY) === 'seen') return; } catch (e) {}
|
if (!P || !P.enabled || !P.id || !P.img) return;
|
||||||
var back;
|
if (P.expiresUTC) { var exp = Date.parse(P.expiresUTC); if (exp && Date.now() > exp) return; }
|
||||||
function mark() { try { sessionStorage.setItem(KEY, 'seen'); } catch (e) {} }
|
var seg = (location.pathname.split('/')[1] || 'home').toLowerCase();
|
||||||
function close() { if (!back) return; mark(); back.style.opacity = '0'; setTimeout(function () { if (back && back.parentNode) back.parentNode.removeChild(back); back = null; }, 260); document.removeEventListener('keydown', onKey); }
|
var pages = String(P.pages || 'all').split(',').map(function (x) { return x.trim().toLowerCase(); }).filter(Boolean);
|
||||||
function onKey(e) { if (e.key === 'Escape') close(); }
|
if (pages.indexOf('all') < 0 && pages.indexOf(seg) < 0) return;
|
||||||
function build() {
|
var KEY = 'rmc-promo-' + P.id;
|
||||||
back = document.createElement('div');
|
try { if (sessionStorage.getItem(KEY) === 'seen') return; } catch (e) {}
|
||||||
back.setAttribute('role', 'dialog'); back.setAttribute('aria-label', P.eyebrow || 'Upgrade');
|
var back;
|
||||||
back.style.cssText = 'position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;padding:16px;background:rgba(3,7,14,.84);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);opacity:0;transition:opacity .28s ease;overflow:auto;';
|
function mark() { try { sessionStorage.setItem(KEY, 'seen'); } catch (e) {} }
|
||||||
back.addEventListener('click', function (e) { if (e.target === back) close(); });
|
function close() { if (!back) return; mark(); back.style.opacity = '0'; setTimeout(function () { if (back && back.parentNode) back.parentNode.removeChild(back); back = null; }, 260); document.removeEventListener('keydown', onKey); }
|
||||||
var card = document.createElement('div');
|
function onKey(e) { if (e.key === 'Escape') close(); }
|
||||||
card.style.cssText = 'position:relative;width:100%;max-width:420px;max-height:96vh;overflow:auto;border-radius:20px;background:#0a1119;border:1px solid rgba(212,175,55,.55);box-shadow:0 24px 70px rgba(0,0,0,.7);font-family:system-ui,Segoe UI,Arial,sans-serif;';
|
function build() {
|
||||||
var x = document.createElement('button');
|
back = document.createElement('div');
|
||||||
x.setAttribute('aria-label', 'Close'); x.innerHTML = '×';
|
back.setAttribute('role', 'dialog'); back.setAttribute('aria-label', P.eyebrow || 'Upgrade');
|
||||||
x.style.cssText = 'position:absolute;top:10px;right:12px;z-index:2;width:38px;height:38px;border-radius:50%;border:none;cursor:pointer;background:rgba(6,10,16,.72);color:#fff;font-size:24px;line-height:1;font-weight:700;display:grid;place-items:center;box-shadow:0 2px 10px rgba(0,0,0,.5);';
|
back.style.cssText = 'position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;padding:16px;background:rgba(3,7,14,.84);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);opacity:0;transition:opacity .28s ease;overflow:auto;';
|
||||||
x.addEventListener('click', close);
|
back.addEventListener('click', function (e) { if (e.target === back) close(); });
|
||||||
var img = document.createElement('img');
|
var card = document.createElement('div');
|
||||||
img.src = P.img; img.alt = P.eyebrow || 'Upgrade'; img.style.cssText = 'display:block;width:100%;height:auto;border-radius:20px 20px 0 0;';
|
card.style.cssText = 'position:relative;width:100%;max-width:420px;max-height:96vh;overflow:auto;border-radius:20px;background:#0a1119;border:1px solid rgba(212,175,55,.55);box-shadow:0 24px 70px rgba(0,0,0,.7);font-family:system-ui,Segoe UI,Arial,sans-serif;';
|
||||||
var foot = document.createElement('div'); foot.style.cssText = 'padding:14px 18px 18px;';
|
var x = document.createElement('button');
|
||||||
var cta = document.createElement('a');
|
x.setAttribute('aria-label', 'Close'); x.innerHTML = '×';
|
||||||
cta.href = P.ctaUrl || '/my'; cta.textContent = P.ctaText || 'See my next step \u2192';
|
x.style.cssText = 'position:absolute;top:10px;right:12px;z-index:2;width:38px;height:38px;border-radius:50%;border:none;cursor:pointer;background:rgba(6,10,16,.72);color:#fff;font-size:24px;line-height:1;font-weight:700;display:grid;place-items:center;box-shadow:0 2px 10px rgba(0,0,0,.5);';
|
||||||
cta.style.cssText = 'display:block;text-align:center;text-decoration:none;background:linear-gradient(180deg,#e9c249,#d4af37);color:#0a1119;font-size:18px;font-weight:800;padding:15px;border-radius:12px;box-shadow:0 8px 22px rgba(212,175,55,.35);';
|
x.addEventListener('click', close);
|
||||||
cta.addEventListener('click', function (e) {
|
var img = document.createElement('img');
|
||||||
// already on the target page: close and scroll to the plan card instead of reloading
|
img.src = P.img; img.alt = P.eyebrow || 'Upgrade'; img.style.cssText = 'display:block;width:100%;height:auto;border-radius:20px 20px 0 0;';
|
||||||
var here = (P.ctaUrl || '/my').split('#')[0].replace(/\/$/, '') === location.pathname.replace(/\/$/, '');
|
var foot = document.createElement('div'); foot.style.cssText = 'padding:14px 18px 18px;';
|
||||||
if (here) { e.preventDefault(); close(); var t = document.querySelector('.ns-card, #nextStep, #plan'); if (t) setTimeout(function () { t.scrollIntoView({ behavior: 'smooth', block: 'center' }); }, 300); }
|
var cta = document.createElement('a');
|
||||||
else mark();
|
cta.href = P.ctaUrl || '/my'; cta.textContent = P.ctaText || 'See my next step \u2192';
|
||||||
});
|
cta.style.cssText = 'display:block;text-align:center;text-decoration:none;background:linear-gradient(180deg,#e9c249,#d4af37);color:#0a1119;font-size:18px;font-weight:800;padding:15px;border-radius:12px;box-shadow:0 8px 22px rgba(212,175,55,.35);';
|
||||||
var later = document.createElement('button');
|
cta.addEventListener('click', function (e) {
|
||||||
later.textContent = 'Not now'; later.style.cssText = 'display:block;margin:10px auto 0;background:none;border:none;color:#7d8ea0;font-size:13px;cursor:pointer;text-decoration:underline;';
|
// already on the target page: close and scroll to the plan card instead of reloading
|
||||||
later.addEventListener('click', close);
|
var here = (P.ctaUrl || '/my').split('#')[0].replace(/\/$/, '') === location.pathname.replace(/\/$/, '');
|
||||||
foot.appendChild(cta); foot.appendChild(later);
|
if (here) { e.preventDefault(); close(); var t = document.querySelector('.ns-card, #nextStep, #plan'); if (t) setTimeout(function () { t.scrollIntoView({ behavior: 'smooth', block: 'center' }); }, 300); }
|
||||||
card.appendChild(x); card.appendChild(img); card.appendChild(foot);
|
else mark();
|
||||||
back.appendChild(card); document.body.appendChild(back);
|
});
|
||||||
document.addEventListener('keydown', onKey);
|
var later = document.createElement('button');
|
||||||
requestAnimationFrame(function () { back.style.opacity = '1'; });
|
later.textContent = 'Not now'; later.style.cssText = 'display:block;margin:10px auto 0;background:none;border:none;color:#7d8ea0;font-size:13px;cursor:pointer;text-decoration:underline;';
|
||||||
}
|
later.addEventListener('click', close);
|
||||||
setTimeout(build, 900);
|
foot.appendChild(cta); foot.appendChild(later);
|
||||||
}
|
card.appendChild(x); if (img) card.appendChild(img); card.appendChild(foot);
|
||||||
|
back.appendChild(card); document.body.appendChild(back);
|
||||||
function fetchAndGo() {
|
document.addEventListener('keydown', onKey);
|
||||||
fetch('/api/announce', { headers: { 'Accept': 'application/json' } })
|
requestAnimationFrame(function () { back.style.opacity = '1'; });
|
||||||
.then(function (r) { return r.ok ? r.json() : null; })
|
}
|
||||||
.then(function (EV) { if (!EV) return; var shown = start(EV); if (!shown) startPromo(EV.promo); })
|
setTimeout(build, 900);
|
||||||
.catch(function () {});
|
}
|
||||||
}
|
|
||||||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', fetchAndGo);
|
function fetchAndGo() {
|
||||||
else fetchAndGo();
|
fetch('/api/announce', { headers: { 'Accept': 'application/json' } })
|
||||||
})();
|
.then(function (r) { return r.ok ? r.json() : null; })
|
||||||
|
.then(function (EV) { if (!EV) return; var shown = start(EV); if (!shown) startPromo(EV.promo); })
|
||||||
|
.catch(function () {});
|
||||||
|
}
|
||||||
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', fetchAndGo);
|
||||||
|
else fetchAndGo();
|
||||||
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user