158 lines
5.6 KiB
JavaScript
158 lines
5.6 KiB
JavaScript
document.addEventListener('DOMContentLoaded', () => {
|
|
const dataElement = document.getElementById('calendar-events-data');
|
|
const modalElement = document.getElementById('dayEventsModal');
|
|
let calendarEvents = {};
|
|
|
|
const escapeHtml = (value) => String(value || '')
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''');
|
|
|
|
const safeExternalUrl = (value) => {
|
|
if (!value) return '';
|
|
try {
|
|
const url = new URL(value, window.location.origin);
|
|
return ['http:', 'https:'].includes(url.protocol) ? url.href : '';
|
|
} catch (error) {
|
|
return '';
|
|
}
|
|
};
|
|
|
|
if (dataElement) {
|
|
try {
|
|
calendarEvents = JSON.parse(dataElement.textContent);
|
|
} catch (error) {
|
|
calendarEvents = {};
|
|
}
|
|
}
|
|
|
|
if (modalElement && window.bootstrap) {
|
|
if (modalElement.parentElement !== document.body) {
|
|
document.body.appendChild(modalElement);
|
|
}
|
|
|
|
const modal = new window.bootstrap.Modal(modalElement);
|
|
const titleNode = modalElement.querySelector('[data-calendar-title]');
|
|
const bodyNode = modalElement.querySelector('[data-calendar-body]');
|
|
|
|
const renderEvents = (isoDate) => {
|
|
const selectedEvents = calendarEvents[isoDate] || [];
|
|
const titleDate = new Date(`${isoDate}T12:00:00`);
|
|
titleNode.textContent = titleDate.toLocaleDateString(undefined, {
|
|
weekday: 'long',
|
|
month: 'long',
|
|
day: 'numeric',
|
|
year: 'numeric',
|
|
});
|
|
|
|
if (!selectedEvents.length) {
|
|
bodyNode.innerHTML = `
|
|
<div class="modal-event-card">
|
|
<h4>No event scheduled</h4>
|
|
<p class="mb-0 text-secondary">This date is currently open. Check another day or revisit after the next update.</p>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
bodyNode.innerHTML = selectedEvents
|
|
.map((event) => {
|
|
const name = escapeHtml(event.name);
|
|
const time = escapeHtml(event.time);
|
|
const location = escapeHtml(event.location || 'Location announced soon');
|
|
const summary = event.summary ? `<p>${escapeHtml(event.summary)}</p>` : '';
|
|
const detailUrl = escapeHtml(event.detail_url || '#');
|
|
const eventUrl = safeExternalUrl(event.event_url);
|
|
return `
|
|
<article class="modal-event-card">
|
|
<h4>${name}</h4>
|
|
<div class="modal-event-meta">
|
|
<span>${time}</span>
|
|
<span>${location}</span>
|
|
</div>
|
|
${summary}
|
|
<div class="d-flex gap-2 flex-wrap mt-3">
|
|
<a class="btn btn-sm btn-primary-brand" href="${detailUrl}">View details</a>
|
|
${eventUrl ? `<a class="btn btn-sm btn-ghost" href="${escapeHtml(eventUrl)}" target="_blank" rel="noopener">Event page</a>` : ''}
|
|
</div>
|
|
</article>
|
|
`;
|
|
})
|
|
.join('');
|
|
};
|
|
|
|
document.querySelectorAll('[data-calendar-date]').forEach((button) => {
|
|
button.addEventListener('click', () => {
|
|
renderEvents(button.dataset.calendarDate);
|
|
modal.show();
|
|
});
|
|
});
|
|
}
|
|
|
|
document.querySelectorAll('[data-copy-snippet]').forEach((button) => {
|
|
button.addEventListener('click', async () => {
|
|
const target = document.querySelector(button.dataset.copyTarget);
|
|
if (!target) return;
|
|
try {
|
|
await navigator.clipboard.writeText(target.textContent.trim());
|
|
const original = button.textContent;
|
|
button.textContent = 'Copied';
|
|
window.setTimeout(() => {
|
|
button.textContent = original;
|
|
}, 1600);
|
|
} catch (error) {
|
|
button.textContent = 'Copy manually';
|
|
}
|
|
});
|
|
});
|
|
|
|
const escapeAttribute = (value) => String(value || '')
|
|
.replace(/&/g, '&')
|
|
.replace(/"/g, '"')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>');
|
|
|
|
document.querySelectorAll('[data-embed-settings]').forEach((form) => {
|
|
const baseUrl = form.dataset.embedBaseUrl || '';
|
|
const urlOutput = document.getElementById('dashboardEmbedUrl');
|
|
const iframeOutput = document.getElementById('dashboardIframeSnippet');
|
|
const previewLink = document.querySelector('[data-embed-preview]');
|
|
if (!baseUrl || !urlOutput || !iframeOutput) return;
|
|
|
|
const updateEmbedOutput = () => {
|
|
const formData = new FormData(form);
|
|
const params = new URLSearchParams();
|
|
const month = String(formData.get('month') || '').trim();
|
|
const header = String(formData.get('header') || '1');
|
|
const title = String(formData.get('title') || 'Where to find us calendar').trim() || 'Where to find us calendar';
|
|
const width = String(formData.get('width') || '100%').trim() || '100%';
|
|
const height = Math.max(parseInt(String(formData.get('height') || '760'), 10) || 760, 360);
|
|
const radius = Math.max(parseInt(String(formData.get('radius') || '24'), 10) || 24, 0);
|
|
|
|
if (/^\d{4}-\d{2}$/.test(month)) {
|
|
params.set('month', month);
|
|
}
|
|
if (header === '0') {
|
|
params.set('header', '0');
|
|
}
|
|
|
|
const widgetUrl = params.toString() ? `${baseUrl}?${params.toString()}` : baseUrl;
|
|
const iframeSnippet = `<iframe src="${escapeAttribute(widgetUrl)}" title="${escapeAttribute(title)}" width="${escapeAttribute(width)}" height="${height}" style="border:0;border-radius:${radius}px;overflow:hidden;" loading="lazy"></iframe>`;
|
|
|
|
urlOutput.textContent = widgetUrl;
|
|
iframeOutput.textContent = iframeSnippet;
|
|
if (previewLink) {
|
|
previewLink.href = widgetUrl;
|
|
}
|
|
};
|
|
|
|
['input', 'change'].forEach((eventName) => {
|
|
form.addEventListener(eventName, updateEmbedOutput);
|
|
});
|
|
updateEmbedOutput();
|
|
});
|
|
|
|
});
|