Files
QR-Code-Generator/main.js
T
elpatron d5cd4427d8 feat: Kalender-Termine als QR (iCal Europe/Berlin) und UX für Datum/Zeit
- Modus Text/WLAN/Termin; VEVENT mit TZID Europe/Berlin und VTIMEZONE
- Eingabe: separates Datum und 24h-Uhrzeit; Vorschau TT/MM/JJJJ
- URL-Parameter und Teilen für Terminfelder
- Docker Compose: Healthcheck für Caddy-Container

Made-with: Cursor
2026-04-16 08:15:29 +02:00

793 lines
28 KiB
JavaScript

document.addEventListener('DOMContentLoaded', function() {
const textInput = document.getElementById('text');
const clearTextBtn = document.getElementById('clear-text');
const contentModeSelect = document.getElementById('content-mode');
const sectionText = document.getElementById('section-text');
const sectionWifi = document.getElementById('section-wifi');
const sectionEvent = document.getElementById('section-event');
const wifiSsidInput = document.getElementById('wifi-ssid');
const wifiPasswordInput = document.getElementById('wifi-password');
const eventTitleInput = document.getElementById('event-title');
const eventStartDateInput = document.getElementById('event-start-date');
const eventStartTimeInput = document.getElementById('event-start-time');
const eventEndDateInput = document.getElementById('event-end-date');
const eventEndTimeInput = document.getElementById('event-end-time');
const eventLocationInput = document.getElementById('event-location');
const eventDescriptionInput = document.getElementById('event-description');
const sizeSelect = document.getElementById('size');
const errorCorrectionSelect = document.getElementById('errorCorrection');
const foregroundColor = document.getElementById('foreground');
const backgroundColor = document.getElementById('background');
const downloadBtn = document.getElementById('download');
const qrcodeCanvas = document.getElementById('qrcode');
const qrcodeImg = document.getElementById('qrcode-img');
const errorMessage = document.getElementById('error-message');
const infoButton = document.getElementById('info-button');
const infoPanel = document.getElementById('info-panel');
const infoClose = document.getElementById('info-close');
const titleElement = document.getElementById('title');
const shareBtn = document.getElementById('share');
const shareHint = document.getElementById('share-hint');
const eventBerlinPreview = document.getElementById('event-berlin-preview');
const MAX_EVENT_TITLE = 2000;
const MAX_EVENT_LOCATION = 1000;
const MAX_EVENT_DESCRIPTION = 8000;
// Title Easter egg
let titleClickCount = 0;
let titleTimeoutId = null;
titleElement.addEventListener('click', function() {
titleClickCount++;
clearTimeout(titleTimeoutId);
if (titleClickCount === 1) {
this.textContent = "Just a [bleep]ing QR Code";
titleTimeoutId = setTimeout(() => {
this.textContent = "Just a QR Code";
titleClickCount = 0;
}, 500);
}
});
// Info panel toggle
infoButton.addEventListener('click', function() {
infoPanel.classList.add('open');
});
infoClose.addEventListener('click', function() {
infoPanel.classList.remove('open');
});
// Close info panel when clicking outside
document.addEventListener('click', function(event) {
if (infoPanel.classList.contains('open') &&
!infoPanel.contains(event.target) &&
event.target !== infoButton) {
infoPanel.classList.remove('open');
}
});
// Close info panel with Escape key
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape' && infoPanel.classList.contains('open')) {
infoPanel.classList.remove('open');
}
});
function toggleContentSections() {
const mode = contentModeSelect.value;
sectionText.style.display = mode === 'text' ? 'block' : 'none';
sectionWifi.style.display = mode === 'wifi' ? 'block' : 'none';
sectionEvent.style.display = mode === 'event' ? 'block' : 'none';
}
contentModeSelect.addEventListener('change', function() {
toggleContentSections();
updateBerlinPreview();
generateQRCode();
if (this.value === 'text') {
textInput.focus();
} else if (this.value === 'wifi') {
wifiSsidInput.focus();
} else if (this.value === 'event') {
eventTitleInput.focus();
}
});
// Set random compliment as default text and select it
textInput.value = 'https://medisoftware.de';
textInput.select();
// Show/hide clear button based on input content
function toggleClearButton() {
const hasText = textInput.value.length > 0;
const hasWifi = wifiSsidInput.value.trim().length > 0 || wifiPasswordInput.value.trim().length > 0;
const hasEvent = eventTitleInput.value.trim().length > 0 ||
eventStartDateInput.value.length > 0 ||
eventStartTimeInput.value.length > 0 ||
eventEndDateInput.value.length > 0 ||
eventEndTimeInput.value.length > 0 ||
eventLocationInput.value.trim().length > 0 ||
eventDescriptionInput.value.trim().length > 0;
if (hasText || hasWifi || hasEvent) {
clearTextBtn.style.display = 'block';
} else {
clearTextBtn.style.display = 'none';
}
}
// Initialize clear button visibility
toggleClearButton();
// Clear text when clear button is clicked
clearTextBtn.addEventListener('click', function() {
textInput.value = '';
wifiSsidInput.value = '';
wifiPasswordInput.value = '';
eventTitleInput.value = '';
eventStartDateInput.value = '';
eventStartTimeInput.value = '';
eventEndDateInput.value = '';
eventEndTimeInput.value = '';
eventLocationInput.value = '';
eventDescriptionInput.value = '';
contentModeSelect.value = 'text';
toggleContentSections();
updateBerlinPreview();
toggleClearButton();
textInput.focus();
generateQRCode();
});
// Update clear button visibility when text changes
textInput.addEventListener('input', function() {
toggleClearButton();
if (contentModeSelect.value === 'text') {
generateQRCode();
}
});
let qr = null;
// Generate QR code when settings change
sizeSelect.addEventListener('change', generateQRCode);
errorCorrectionSelect.addEventListener('change', generateQRCode);
foregroundColor.addEventListener('input', generateQRCode);
backgroundColor.addEventListener('input', generateQRCode);
// Generate QR code when wifi settings change
wifiSsidInput.addEventListener('input', function() {
if (contentModeSelect.value === 'wifi') {
updateTextDisplay();
}
generateQRCode();
toggleClearButton();
});
wifiPasswordInput.addEventListener('input', function() {
if (contentModeSelect.value === 'wifi') {
updateTextDisplay();
}
generateQRCode();
toggleClearButton();
});
eventTitleInput.addEventListener('input', function() {
updateBerlinPreview();
generateQRCode();
toggleClearButton();
});
function onEventDateTimeInput() {
updateBerlinPreview();
generateQRCode();
toggleClearButton();
}
eventStartDateInput.addEventListener('input', onEventDateTimeInput);
eventStartDateInput.addEventListener('change', onEventDateTimeInput);
eventStartTimeInput.addEventListener('input', onEventDateTimeInput);
eventStartTimeInput.addEventListener('change', onEventDateTimeInput);
eventEndDateInput.addEventListener('input', onEventDateTimeInput);
eventEndDateInput.addEventListener('change', onEventDateTimeInput);
eventEndTimeInput.addEventListener('input', onEventDateTimeInput);
eventEndTimeInput.addEventListener('change', onEventDateTimeInput);
eventLocationInput.addEventListener('input', function() {
generateQRCode();
toggleClearButton();
});
eventDescriptionInput.addEventListener('input', function() {
generateQRCode();
toggleClearButton();
});
// Download QR code as image
downloadBtn.addEventListener('click', downloadQRCode);
function sanitizeUrlString(s, maxLen) {
if (typeof s !== 'string') {
return '';
}
return s.replace(/\0/g, '').slice(0, maxLen);
}
function isValidDatetimeLocal(s) {
return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/.test(s);
}
function combineDateTime(dateStr, timeStr) {
if (!dateStr || !timeStr) {
return '';
}
if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
return '';
}
if (!/^\d{2}:\d{2}$/.test(timeStr)) {
return '';
}
return dateStr + 'T' + timeStr;
}
function applyCombinedToDateTimeInputs(combined, dateInput, timeInput) {
if (!isValidDatetimeLocal(combined)) {
return;
}
const parts = combined.split('T');
dateInput.value = parts[0];
timeInput.value = parts[1];
}
// Or check for URL parameters if present
const urlParams = new URLSearchParams(window.location.search);
let explicitModeFromUrl = false;
if (urlParams.has('mode')) {
const m = urlParams.get('mode');
if (m === 'text' || m === 'wifi' || m === 'event') {
contentModeSelect.value = m;
explicitModeFromUrl = true;
}
}
if (urlParams.has('text')) {
const safeText = urlParams.get('text');
if (/^[\w\-.:/?&=#%+@!;,]*$/i.test(safeText)) {
textInput.value = safeText;
}
}
if (urlParams.has('ssid')) {
const safeSsid = urlParams.get('ssid');
if (/^[^<>"'`\\]*$/.test(safeSsid)) {
wifiSsidInput.value = safeSsid;
}
}
if (urlParams.has('password')) {
const safePassword = urlParams.get('password');
if (/^[^<>"'`\\]*$/.test(safePassword)) {
wifiPasswordInput.value = safePassword;
}
}
const hasEventParams = urlParams.has('eventTitle') || urlParams.has('eventStart');
if (!explicitModeFromUrl && hasEventParams) {
contentModeSelect.value = 'event';
}
if (urlParams.has('eventTitle')) {
eventTitleInput.value = sanitizeUrlString(urlParams.get('eventTitle'), MAX_EVENT_TITLE);
}
if (urlParams.has('eventStart')) {
const es = urlParams.get('eventStart');
if (isValidDatetimeLocal(es)) {
applyCombinedToDateTimeInputs(es, eventStartDateInput, eventStartTimeInput);
}
}
if (urlParams.has('eventEnd')) {
const ee = urlParams.get('eventEnd');
if (isValidDatetimeLocal(ee)) {
applyCombinedToDateTimeInputs(ee, eventEndDateInput, eventEndTimeInput);
}
}
if (urlParams.has('eventLocation')) {
eventLocationInput.value = sanitizeUrlString(urlParams.get('eventLocation'), MAX_EVENT_LOCATION);
}
if (urlParams.has('eventDescription')) {
eventDescriptionInput.value = sanitizeUrlString(urlParams.get('eventDescription'), MAX_EVENT_DESCRIPTION);
}
if (urlParams.has('size')) {
const sizeValue = urlParams.get('size');
if (["128", "256", "512", "1024"].includes(sizeValue)) {
sizeSelect.value = sizeValue;
}
}
if (urlParams.has('errorCorrection')) {
const ecValue = urlParams.get('errorCorrection').toUpperCase();
if (["L", "M", "Q", "H"].includes(ecValue)) {
errorCorrectionSelect.value = ecValue;
}
}
if (urlParams.has('foreground')) {
const fgValue = urlParams.get('foreground');
if (/^#[0-9A-F]{6}$/i.test(fgValue)) {
foregroundColor.value = fgValue;
}
}
if (urlParams.has('background')) {
const bgValue = urlParams.get('background');
if (/^#[0-9A-F]{6}$/i.test(bgValue)) {
backgroundColor.value = bgValue;
}
}
toggleContentSections();
updateBerlinPreview();
generateQRCode();
function daysInMonth(y, mo) {
return new Date(y, mo, 0).getDate();
}
function parseDatetimeLocalValue(s) {
const m = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})$/.exec(s);
if (!m) {
return null;
}
return {
y: parseInt(m[1], 10),
mo: parseInt(m[2], 10),
d: parseInt(m[3], 10),
h: parseInt(m[4], 10),
mi: parseInt(m[5], 10)
};
}
function addHoursWall(parts, deltaH) {
let y = parts.y;
let mo = parts.mo;
let d = parts.d;
let h = parts.h + deltaH;
let mi = parts.mi;
while (h >= 24) {
h -= 24;
d += 1;
const dim = daysInMonth(y, mo);
if (d > dim) {
d = 1;
mo += 1;
if (mo > 12) {
mo = 1;
y += 1;
}
}
}
return { y: y, mo: mo, d: d, h: h, mi: mi };
}
function wallTimeToICal(p) {
const pad = function(n) { return String(n).padStart(2, '0'); };
return String(p.y) + pad(p.mo) + pad(p.d) + 'T' + pad(p.h) + pad(p.mi) + '00';
}
function compareWallTime(a, b) {
return (a.y * 1e8 + a.mo * 1e6 + a.d * 1e4 + a.h * 100 + a.mi) -
(b.y * 1e8 + b.mo * 1e6 + b.d * 1e4 + b.h * 100 + b.mi);
}
function formatSlashDateTime24hBerlin(p) {
const pad = function(n) { return String(n).padStart(2, '0'); };
return pad(p.d) + '/' + pad(p.mo) + '/' + p.y + ', ' + pad(p.h) + ':' + pad(p.mi);
}
function updateBerlinPreview() {
if (!eventBerlinPreview) {
return;
}
if (contentModeSelect.value !== 'event') {
eventBerlinPreview.textContent = '';
return;
}
const startRaw = combineDateTime(eventStartDateInput.value, eventStartTimeInput.value);
const endD = eventEndDateInput.value;
const endT = eventEndTimeInput.value;
let endRaw = '';
if (endD && endT) {
endRaw = combineDateTime(endD, endT);
} else if (endD || endT) {
eventBerlinPreview.textContent =
'Bitte geben Sie für das Ende entweder beides (Datum und Uhrzeit, 24h) ein oder lassen Sie beides leer.';
return;
}
const sp = parseDatetimeLocalValue(startRaw);
if (!sp) {
eventBerlinPreview.textContent = '';
return;
}
let endDisplay;
if (endRaw) {
const ep = parseDatetimeLocalValue(endRaw);
if (!ep) {
eventBerlinPreview.textContent = 'Beginn: ' + formatSlashDateTime24hBerlin(sp) + ' (Europe/Berlin)';
return;
}
endDisplay = ep;
} else {
endDisplay = addHoursWall(sp, 1);
}
eventBerlinPreview.textContent =
'Beginn: ' + formatSlashDateTime24hBerlin(sp) +
' · Ende: ' + formatSlashDateTime24hBerlin(endDisplay) +
' (Europe/Berlin)';
}
function escapeICalText(str) {
return String(str)
.replace(/\r\n/g, '\n')
.replace(/\r/g, '')
.replace(/\\/g, '\\\\')
.replace(/;/g, '\\;')
.replace(/,/g, '\\,')
.replace(/\n/g, '\\n');
}
function formatICalUTCStamp() {
const d = new Date();
const pad = function(n) { return String(n).padStart(2, '0'); };
return (
d.getUTCFullYear() +
pad(d.getUTCMonth() + 1) +
pad(d.getUTCDate()) +
'T' +
pad(d.getUTCHours()) +
pad(d.getUTCMinutes()) +
pad(d.getUTCSeconds()) +
'Z'
);
}
function newEventUid() {
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
return crypto.randomUUID() + '@qr-generator.local';
}
return 'evt-' + String(Date.now()) + '-' + String(Math.random()).slice(2) + '@qr-generator.local';
}
function buildICalEvent(opts) {
const summary = opts.summary;
const dtStart = opts.dtStart;
const dtEnd = opts.dtEnd;
const location = opts.location;
const description = opts.description;
const vtimezoneBerlin = [
'BEGIN:VTIMEZONE',
'TZID:Europe/Berlin',
'BEGIN:DAYLIGHT',
'DTSTART:19810329T020000',
'RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU',
'TZNAME:CEST',
'TZOFFSETFROM:+0100',
'TZOFFSETTO:+0200',
'END:DAYLIGHT',
'BEGIN:STANDARD',
'DTSTART:19961027T030000',
'RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU',
'TZNAME:CET',
'TZOFFSETFROM:+0200',
'TZOFFSETTO:+0100',
'END:STANDARD',
'END:VTIMEZONE'
].join('\r\n');
const lines = [
'BEGIN:VCALENDAR',
'VERSION:2.0',
'PRODID:-//qr-generator//DE',
'CALSCALE:GREGORIAN',
vtimezoneBerlin,
'BEGIN:VEVENT',
'UID:' + opts.uid,
'DTSTAMP:' + opts.dtStamp,
'DTSTART;TZID=Europe/Berlin:' + dtStart,
'DTEND;TZID=Europe/Berlin:' + dtEnd,
'SUMMARY:' + escapeICalText(summary)
];
if (location && location.trim()) {
lines.push('LOCATION:' + escapeICalText(location.trim()));
}
if (description && description.trim()) {
lines.push('DESCRIPTION:' + escapeICalText(description.trim()));
}
lines.push('END:VEVENT', 'END:VCALENDAR');
return lines.join('\r\n');
}
function generateQRCode() {
const mode = contentModeSelect.value;
const text = textInput.value.trim();
const wifiSsid = wifiSsidInput.value.trim();
const wifiPassword = wifiPasswordInput.value.trim();
const size = parseInt(sizeSelect.value, 10);
const errorCorrection = errorCorrectionSelect.value.toLowerCase();
const fgColor = foregroundColor.value;
const bgColor = backgroundColor.value;
// Update the gabe link color to match foreground color
const gabeLink = document.querySelector('.credit a');
if (gabeLink) {
gabeLink.style.color = fgColor;
}
// Clear previous error
errorMessage.textContent = '';
downloadBtn.style.display = 'none';
let qrText = '';
if (mode === 'event') {
const title = eventTitleInput.value.trim();
const startRaw = combineDateTime(eventStartDateInput.value, eventStartTimeInput.value);
const endD = eventEndDateInput.value;
const endT = eventEndTimeInput.value;
const loc = eventLocationInput.value;
const desc = eventDescriptionInput.value;
if (!title) {
errorMessage.textContent = 'Bitte geben Sie einen Titel für den Termin ein';
qrcodeCanvas.style.display = 'none';
qrcodeImg.style.display = 'none';
return;
}
if (!startRaw) {
errorMessage.textContent = 'Bitte geben Sie Datum und Uhrzeit (24h) für den Beginn ein (TT/MM/JJJJ)';
qrcodeCanvas.style.display = 'none';
qrcodeImg.style.display = 'none';
return;
}
if (endD || endT) {
if (!endD || !endT) {
errorMessage.textContent =
'Bitte geben Sie für das Ende sowohl Datum (TT/MM/JJJJ) als auch Uhrzeit (24h) an, oder lassen Sie beides leer';
qrcodeCanvas.style.display = 'none';
qrcodeImg.style.display = 'none';
return;
}
}
const startParts = parseDatetimeLocalValue(startRaw);
if (!startParts) {
errorMessage.textContent = 'Ungültige Startzeit (Datum und 24h-Uhrzeit prüfen)';
qrcodeCanvas.style.display = 'none';
qrcodeImg.style.display = 'none';
return;
}
const dtStart = wallTimeToICal(startParts);
let endParts;
const endRaw = endD && endT ? combineDateTime(endD, endT) : '';
if (endRaw) {
endParts = parseDatetimeLocalValue(endRaw);
if (!endParts) {
errorMessage.textContent = 'Ungültige Endzeit';
qrcodeCanvas.style.display = 'none';
qrcodeImg.style.display = 'none';
return;
}
} else {
endParts = addHoursWall(startParts, 1);
}
if (compareWallTime(endParts, startParts) <= 0) {
errorMessage.textContent = 'Die Endzeit muss nach dem Beginn liegen (Ortszeit Europe/Berlin)';
qrcodeCanvas.style.display = 'none';
qrcodeImg.style.display = 'none';
return;
}
const dtEndStr = wallTimeToICal(endParts);
qrText = buildICalEvent({
uid: newEventUid(),
dtStamp: formatICalUTCStamp(),
summary: title.slice(0, MAX_EVENT_TITLE),
dtStart: dtStart,
dtEnd: dtEndStr,
location: loc.slice(0, MAX_EVENT_LOCATION),
description: desc.slice(0, MAX_EVENT_DESCRIPTION)
});
textInput.value = qrText;
toggleClearButton();
} else if (mode === 'wifi') {
if (wifiSsid && wifiPassword) {
qrText = 'WIFI:S:' + escapeSpecialChars(wifiSsid) + ';T:WPA;P:' + escapeSpecialChars(wifiPassword) + ';;';
} else if (wifiSsid && !wifiPassword) {
qrText = 'WIFI:S:' + escapeSpecialChars(wifiSsid) + ';T:nopass;;';
} else if (!wifiSsid && wifiPassword) {
errorMessage.textContent = 'Bitte geben Sie eine SSID ein, wenn Sie ein Passwort angeben';
qrcodeCanvas.style.display = 'none';
qrcodeImg.style.display = 'none';
return;
} else {
qrText = text;
}
} else {
qrText = text;
}
// Validate input
if (qrText === '') {
if (mode === 'wifi') {
errorMessage.textContent = 'Bitte geben Sie Text/URL oder WLAN-Daten ein';
} else if (mode === 'event') {
errorMessage.textContent = 'Termin konnte nicht erzeugt werden';
} else {
errorMessage.textContent = 'Bitte geben Sie Text oder eine URL ein';
}
qrcodeCanvas.style.display = 'none';
qrcodeImg.style.display = 'none';
return;
}
// Create new QR code on canvas
try {
if (!qr) {
qr = new QRious({
element: qrcodeCanvas,
size: size,
value: qrText,
foreground: fgColor,
background: bgColor,
level: errorCorrection
});
} else {
qr.set({
size: size,
value: qrText,
foreground: fgColor,
background: bgColor,
level: errorCorrection
});
}
// Convert canvas to image
const dataURL = qrcodeCanvas.toDataURL('image/png');
qrcodeImg.src = dataURL;
// Show image (hide canvas) and download button
qrcodeCanvas.style.display = 'none';
qrcodeImg.style.display = 'block';
downloadBtn.style.display = 'inline-block';
} catch (err) {
errorMessage.textContent = 'Error generating QR code: ' + err.message;
qrcodeCanvas.style.display = 'none';
qrcodeImg.style.display = 'none';
}
}
// Helper function to escape special characters in wifi strings
function escapeSpecialChars(str) {
return str.replace(/[\\;:,]/g, '\\$&');
}
function downloadQRCode() {
// Use the image source for download
const link = document.createElement('a');
link.download = 'qrcode.png';
link.href = qrcodeImg.src;
// Simulate click to trigger download
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// Show success feedback
const originalText = downloadBtn.textContent;
downloadBtn.textContent = 'Downloaded!';
// Reset button after a short delay
setTimeout(function() {
downloadBtn.textContent = originalText;
}, 2000);
}
// Function to update the text display with current QR code content (WLAN)
function updateTextDisplay() {
const wifiSsid = wifiSsidInput.value.trim();
const wifiPassword = wifiPasswordInput.value.trim();
if (wifiSsid && wifiPassword) {
textInput.value = 'WIFI:S:' + escapeSpecialChars(wifiSsid) + ';T:WPA;P:' + escapeSpecialChars(wifiPassword) + ';;';
} else if (wifiSsid && !wifiPassword) {
textInput.value = 'WIFI:S:' + escapeSpecialChars(wifiSsid) + ';T:nopass;;';
}
toggleClearButton();
}
function updateShareHint() {
const mode = contentModeSelect.value;
const wifiPassword = wifiPasswordInput.value.trim();
if (mode === 'wifi' && wifiPassword) {
shareHint.style.display = 'block';
} else {
shareHint.style.display = 'none';
}
}
wifiPasswordInput.addEventListener('input', updateShareHint);
wifiSsidInput.addEventListener('input', updateShareHint);
textInput.addEventListener('input', updateShareHint);
contentModeSelect.addEventListener('change', updateShareHint);
updateShareHint();
shareBtn.addEventListener('click', function() {
const params = new URLSearchParams();
const mode = contentModeSelect.value;
const text = textInput.value.trim();
const wifiSsid = wifiSsidInput.value.trim();
const wifiPassword = wifiPasswordInput.value.trim();
const size = sizeSelect.value;
const errorCorrection = errorCorrectionSelect.value;
const fgColor = foregroundColor.value;
const bgColor = backgroundColor.value;
params.set('mode', mode);
if (mode === 'event') {
if (eventTitleInput.value.trim()) {
params.set('eventTitle', eventTitleInput.value.trim());
}
const shareStart = combineDateTime(eventStartDateInput.value, eventStartTimeInput.value);
const shareEnd = combineDateTime(eventEndDateInput.value, eventEndTimeInput.value);
if (shareStart) {
params.set('eventStart', shareStart);
}
if (shareEnd) {
params.set('eventEnd', shareEnd);
}
if (eventLocationInput.value.trim()) {
params.set('eventLocation', eventLocationInput.value.trim());
}
if (eventDescriptionInput.value.trim()) {
params.set('eventDescription', eventDescriptionInput.value.trim());
}
} else if (mode === 'wifi') {
if (wifiSsid) {
params.set('ssid', wifiSsid);
}
if (wifiPassword) {
params.set('password', wifiPassword);
}
} else {
if (text) {
params.set('text', text);
}
}
if (size !== '256') {
params.set('size', size);
}
if (errorCorrection !== 'M') {
params.set('errorCorrection', errorCorrection);
}
if (fgColor !== '#000000') {
params.set('foreground', fgColor);
}
if (bgColor !== '#ffffff') {
params.set('background', bgColor);
}
const url = window.location.origin + window.location.pathname + '?' + params.toString();
navigator.clipboard.writeText(url).then(function() {
const original = shareBtn.textContent;
shareBtn.textContent = 'Link kopiert!';
setTimeout(function() { shareBtn.textContent = original; }, 2000);
}, function() {
alert('Konnte Link nicht kopieren.');
});
});
});