Compare commits
8 Commits
ca3ed44ff3
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e257a09f0 | |||
| b0eaaf7694 | |||
| 30872ad780 | |||
| 11d4586ea7 | |||
| 319ee44aae | |||
| 0ca6e891cc | |||
| 7790fe6dda | |||
| 8dac716c28 |
@@ -0,0 +1 @@
|
||||
*.zip
|
||||
@@ -7,7 +7,7 @@ Dieses Projekt basiert auf dem Code von https://qr.alster.space/. Er wurde um ve
|
||||
## Features
|
||||
|
||||
- Vollständig clientseitig (keine Server-Kommunikation)
|
||||
- URL-Parameter für alle Einstellungen
|
||||
- URL-Parameter für alle Einstellungen (inkl. Modus Text/WLAN/Termin)
|
||||
- Anpassbare Größen und Farben
|
||||
- Verschiedene Fehlerkorrektur-Level
|
||||
- Download-Funktion
|
||||
@@ -69,9 +69,15 @@ Die App bietet spezielle Eingabefelder für WiFi-Daten:
|
||||
|
||||
Die Anwendung unterstützt folgende URL-Parameter:
|
||||
|
||||
- `text` - Text oder URL für den QR-Code
|
||||
- `ssid` - WiFi SSID
|
||||
- `password` - WiFi Passwort
|
||||
- `mode` - Inhaltstyp: `text`, `wifi` oder `event` (Termin/Kalender). Ohne `mode` wird bei vorhandenen Termin-Feldern automatisch der Termin-Modus gewählt.
|
||||
- `text` - Text oder URL für den QR-Code (Modus Text)
|
||||
- `ssid` - WiFi SSID (Modus WLAN)
|
||||
- `password` - WiFi Passwort (Modus WLAN)
|
||||
- `eventTitle` - Titel des Termins (Modus `event`)
|
||||
- `eventStart` - Beginn im Format `YYYY-MM-DDTHH:mm` (lokale Ortszeit Europe/Berlin, z. B. `2026-04-16T14:30`)
|
||||
- `eventEnd` - Ende (optional), gleiches Format wie `eventStart`
|
||||
- `eventLocation` - Ort (optional)
|
||||
- `eventDescription` - Beschreibung (optional)
|
||||
- `size` - Größe (128, 256, 512, 1024)
|
||||
- `errorCorrection` - Fehlerkorrektur (L, M, Q, H)
|
||||
- `foreground` - Vordergrundfarbe (Hex-Code)
|
||||
@@ -92,6 +98,11 @@ http://localhost:8080/?ssid=MeinWLAN&password=MeinPasswort123
|
||||
http://localhost:8080/?ssid=OffenesWLAN
|
||||
```
|
||||
|
||||
**Termin (Kalender, iCal im QR):**
|
||||
```
|
||||
http://localhost:8080/?mode=event&eventTitle=Team-Meeting&eventStart=2026-04-16T14:30&eventEnd=2026-04-16T15:30
|
||||
```
|
||||
|
||||
**Achtung:** Das WLAN-Passwort ist im Link im Klartext sichtbar!
|
||||
|
||||
## WiFi QR-Code Format
|
||||
@@ -133,7 +144,7 @@ Das Smartphone erkennt automatisch, dass es sich um WiFi-Daten handelt und biete
|
||||
|
||||
### Teilen-Funktion
|
||||
|
||||
Mit dem Button "Teilen" kann ein Link mit allen aktuellen Einstellungen (inkl. WiFi-Daten) in die Zwischenablage kopiert werden. Dieser Link kann weitergegeben werden und öffnet die App direkt mit den gewählten Einstellungen.
|
||||
Mit dem Button "Teilen" kann ein Link mit allen aktuellen Einstellungen (inkl. WiFi- oder Termin-Daten) in die Zwischenablage kopiert werden. Dieser Link kann weitergegeben werden und öffnet die App direkt mit den gewählten Einstellungen.
|
||||
|
||||
**Achtung:** Wenn ein WLAN-Passwort eingegeben ist, wird dieses im Link im Klartext übertragen!
|
||||
|
||||
|
||||
+74
-5
@@ -3,9 +3,10 @@
|
||||
<html lang="de"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none';">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://plausible.elpatron.me; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://plausible.elpatron.me; object-src 'none';">
|
||||
<title>QR ohne Schnickschnack</title>
|
||||
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' fill='white' rx='6' ry='6'/><rect x='4' y='4' width='8' height='8' fill='black'/><rect x='20' y='4' width='8' height='8' fill='black'/><rect x='4' y='20' width='8' height='8' fill='black'/><rect x='14' y='14' width='4' height='4' fill='black'/></svg>">
|
||||
<script defer data-domain="qr.elpatron.me" src="https://plausible.elpatron.me/js/script.js"></script>
|
||||
<script src="./assets/qrious.min.js"></script>
|
||||
<script src="./main.js"></script>
|
||||
<!--
|
||||
@@ -347,6 +348,26 @@
|
||||
color: #6b7280;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.vcard-preview-label {
|
||||
margin: 4px 0 6px 0;
|
||||
color: #374151;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.vcard-preview {
|
||||
margin: 0 0 15px 0;
|
||||
padding: 10px;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 6px;
|
||||
background: #f9fafb;
|
||||
color: #374151;
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
min-height: 80px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -360,6 +381,7 @@
|
||||
<option value="text">Text oder URL</option>
|
||||
<option value="wifi">WLAN</option>
|
||||
<option value="event">Termin (Kalender)</option>
|
||||
<option value="vcard">vCard (Kontakt)</option>
|
||||
</select>
|
||||
|
||||
<div id="section-text" class="content-section">
|
||||
@@ -433,6 +455,53 @@
|
||||
<textarea id="event-description" placeholder="" rows="3"></textarea>
|
||||
</div>
|
||||
|
||||
<div id="section-vcard" class="content-section" style="display: none;">
|
||||
<div class="options-row">
|
||||
<div class="options-col">
|
||||
<label for="vcard-first-name">Vorname:</label>
|
||||
<input type="text" id="vcard-first-name" placeholder="Max" autocomplete="off">
|
||||
</div>
|
||||
<div class="options-col">
|
||||
<label for="vcard-last-name">Nachname:</label>
|
||||
<input type="text" id="vcard-last-name" placeholder="Mustermann" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="options-row">
|
||||
<div class="options-col">
|
||||
<label for="vcard-org">Organisation:</label>
|
||||
<input type="text" id="vcard-org" placeholder="Knorrlabs Inc." autocomplete="off">
|
||||
</div>
|
||||
<div class="options-col">
|
||||
<label for="vcard-title">Position (optional):</label>
|
||||
<input type="text" id="vcard-title" placeholder="Master Of Desaster" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="options-row">
|
||||
<div class="options-col">
|
||||
<label for="vcard-phone">Telefon (optional):</label>
|
||||
<input type="tel" id="vcard-phone" placeholder="+49 30 1234567" autocomplete="off">
|
||||
</div>
|
||||
<div class="options-col">
|
||||
<label for="vcard-email">E-Mail (optional):</label>
|
||||
<input type="email" id="vcard-email" placeholder="max@beispiel.de" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label for="vcard-website">Webseite (optional):</label>
|
||||
<input type="url" id="vcard-website" placeholder="https://beispiel.de" autocomplete="off">
|
||||
|
||||
<label for="vcard-address">Adresse (optional):</label>
|
||||
<textarea id="vcard-address" placeholder="Musterstraße 1, 10115 Berlin" rows="2"></textarea>
|
||||
|
||||
<label for="vcard-note">Notiz (optional):</label>
|
||||
<textarea id="vcard-note" placeholder="Erreichbar Mo-Fr 9-17 Uhr" rows="2"></textarea>
|
||||
|
||||
<p class="vcard-preview-label">vCard-Vorschau:</p>
|
||||
<pre id="vcard-preview" class="vcard-preview" aria-live="polite">Bitte mindestens Vorname, Nachname oder Organisation eingeben.</pre>
|
||||
</div>
|
||||
|
||||
<div class="options-row">
|
||||
<div class="options-col">
|
||||
<label for="size">QR Code Grösse:</label>
|
||||
@@ -496,7 +565,7 @@
|
||||
|
||||
<p>Not today.</p>
|
||||
|
||||
<p>Rather than bemoan the fact that there's no easy way to get QR ohne Schnickschnak without the low-key parasitic monetization, I found a library (qrious) and put together just enough Javascript code to generate QR codes in the browser. No ads, no trackers, no sending your data to someone else's server for who-knows-what-reason.</p>
|
||||
<p>Rather than bemoan the fact that there's no easy way to get QR ohne Schnickschnak without the low-key parasitic monetization, I found a library (qrious) and put together just enough Javascript code to generate QR codes in the browser. No ads; page views are counted with self-hosted Plausible (privacy-friendly, no cookies). Your QR content is not sent anywhere.</p>
|
||||
|
||||
<p>Go ahead and inspect the page source. Save it to your computer, copy it, remix it, whatever you want.</p>
|
||||
|
||||
@@ -507,13 +576,13 @@
|
||||
<p>Be excellent to each other.</p>
|
||||
|
||||
<h3>Modi</h3>
|
||||
<p>Unter <strong>QR-Inhalt</strong> wählst du, was kodiert wird: freier Text oder URL, WLAN-Zugangsdaten (WIFI-QR) oder einen <strong>Kalendertermin</strong>. Im Modus „Termin“ erzeugt der QR-Code einen Standard-iCalendar-Eintrag (VEVENT), den viele Smartphones beim Scannen in die Kalender-App übernehmen. Datumseingabe als <strong>TT/MM/JJJJ</strong>, Uhrzeit im <strong>24-Stunden-Format</strong>; die Zeitzone ist <strong>Europe/Berlin</strong>. Es wird nichts an einen Server gesendet.</p>
|
||||
<p>Unter <strong>QR-Inhalt</strong> wählst du, was kodiert wird: freier Text oder URL, WLAN-Zugangsdaten (WIFI-QR), ein <strong>Kalendertermin</strong> oder eine <strong>vCard</strong>. Im Modus „Termin“ erzeugt der QR-Code einen Standard-iCalendar-Eintrag (VEVENT), den viele Smartphones beim Scannen in die Kalender-App übernehmen. Datumseingabe als <strong>TT/MM/JJJJ</strong>, Uhrzeit im <strong>24-Stunden-Format</strong>; die Zeitzone ist <strong>Europe/Berlin</strong>. Im Modus „vCard“ wird ein Kontakt im vCard-Format (3.0) erzeugt, den viele Geräte direkt als Kontakt speichern. Deine Eingaben für den QR-Code werden nicht an einen Server gesendet.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
Dieser QR-Code-Generator funktioniert vollständig in deinem Browser. Es werden keine Daten an einen Server gesendet. <i>Quellcode und README bei <a href="https://gitea.elpatron.me/elpatron/QR-Code-Generator" target="_blank">Gitea</a></i>.
|
||||
<p class="credit">Made with ❤️ and 🍪 by Markus. Self hosted on <a href="https://unraid.net" target="_blank">Unraid</a> for <a href="https://medisoftware.de" target="_blank">medisoftware</a>. Credits: <a href="https://qr.alster.space/" target="_blank">alsternerd</a></p>
|
||||
Dieser QR-Code-Generator funktioniert vollständig in deinem Browser; QR-Inhalte werden nicht hochgeladen. Seitenaufrufe werden mit selbst gehostetem Plausible aggregiert (ohne Cookies). <i>Quellcode und README bei <a href="https://gitea.elpatron.me/elpatron/QR-Code-Generator" target="_blank">Gitea</a></i>.
|
||||
<p class="credit">Made with ❤️ and 🍪 by <a href="mailto:elpatron+qr@mailbox.org">Markus F.J. Busche</a>. Self hosted on <a href="https://www.proxmox.com/" target="_blank">Proxmox</a>. Credits: <a href="https://qr.alster.space/" target="_blank">alsternerd</a></p>
|
||||
</div>
|
||||
|
||||
</body></html>
|
||||
@@ -6,6 +6,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const sectionText = document.getElementById('section-text');
|
||||
const sectionWifi = document.getElementById('section-wifi');
|
||||
const sectionEvent = document.getElementById('section-event');
|
||||
const sectionVcard = document.getElementById('section-vcard');
|
||||
const wifiSsidInput = document.getElementById('wifi-ssid');
|
||||
const wifiPasswordInput = document.getElementById('wifi-password');
|
||||
const eventTitleInput = document.getElementById('event-title');
|
||||
@@ -17,6 +18,15 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const eventEndMinuteInput = document.getElementById('event-end-minute');
|
||||
const eventLocationInput = document.getElementById('event-location');
|
||||
const eventDescriptionInput = document.getElementById('event-description');
|
||||
const vcardFirstNameInput = document.getElementById('vcard-first-name');
|
||||
const vcardLastNameInput = document.getElementById('vcard-last-name');
|
||||
const vcardOrgInput = document.getElementById('vcard-org');
|
||||
const vcardTitleInput = document.getElementById('vcard-title');
|
||||
const vcardPhoneInput = document.getElementById('vcard-phone');
|
||||
const vcardEmailInput = document.getElementById('vcard-email');
|
||||
const vcardWebsiteInput = document.getElementById('vcard-website');
|
||||
const vcardAddressInput = document.getElementById('vcard-address');
|
||||
const vcardNoteInput = document.getElementById('vcard-note');
|
||||
const sizeSelect = document.getElementById('size');
|
||||
const errorCorrectionSelect = document.getElementById('errorCorrection');
|
||||
const foregroundColor = document.getElementById('foreground');
|
||||
@@ -32,10 +42,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const shareBtn = document.getElementById('share');
|
||||
const shareHint = document.getElementById('share-hint');
|
||||
const eventBerlinPreview = document.getElementById('event-berlin-preview');
|
||||
const vcardPreview = document.getElementById('vcard-preview');
|
||||
|
||||
const MAX_EVENT_TITLE = 2000;
|
||||
const MAX_EVENT_LOCATION = 1000;
|
||||
const MAX_EVENT_DESCRIPTION = 8000;
|
||||
const MAX_VCARD_FIELD = 500;
|
||||
|
||||
// Title Easter egg
|
||||
let titleClickCount = 0;
|
||||
@@ -85,6 +97,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
sectionText.style.display = mode === 'text' ? 'block' : 'none';
|
||||
sectionWifi.style.display = mode === 'wifi' ? 'block' : 'none';
|
||||
sectionEvent.style.display = mode === 'event' ? 'block' : 'none';
|
||||
sectionVcard.style.display = mode === 'vcard' ? 'block' : 'none';
|
||||
}
|
||||
|
||||
contentModeSelect.addEventListener('change', function() {
|
||||
@@ -97,6 +110,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
wifiSsidInput.focus();
|
||||
} else if (this.value === 'event') {
|
||||
eventTitleInput.focus();
|
||||
} else if (this.value === 'vcard') {
|
||||
vcardFirstNameInput.focus();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -117,7 +132,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
eventEndMinuteInput.value !== '' ||
|
||||
eventLocationInput.value.trim().length > 0 ||
|
||||
eventDescriptionInput.value.trim().length > 0;
|
||||
if (hasText || hasWifi || hasEvent) {
|
||||
const hasVCard = vcardFirstNameInput.value.trim().length > 0 ||
|
||||
vcardLastNameInput.value.trim().length > 0 ||
|
||||
vcardOrgInput.value.trim().length > 0 ||
|
||||
vcardTitleInput.value.trim().length > 0 ||
|
||||
vcardPhoneInput.value.trim().length > 0 ||
|
||||
vcardEmailInput.value.trim().length > 0 ||
|
||||
vcardWebsiteInput.value.trim().length > 0 ||
|
||||
vcardAddressInput.value.trim().length > 0 ||
|
||||
vcardNoteInput.value.trim().length > 0;
|
||||
if (hasText || hasWifi || hasEvent || hasVCard) {
|
||||
clearTextBtn.style.display = 'block';
|
||||
} else {
|
||||
clearTextBtn.style.display = 'none';
|
||||
@@ -141,6 +165,15 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
eventEndMinuteInput.value = '';
|
||||
eventLocationInput.value = '';
|
||||
eventDescriptionInput.value = '';
|
||||
vcardFirstNameInput.value = '';
|
||||
vcardLastNameInput.value = '';
|
||||
vcardOrgInput.value = '';
|
||||
vcardTitleInput.value = '';
|
||||
vcardPhoneInput.value = '';
|
||||
vcardEmailInput.value = '';
|
||||
vcardWebsiteInput.value = '';
|
||||
vcardAddressInput.value = '';
|
||||
vcardNoteInput.value = '';
|
||||
contentModeSelect.value = 'text';
|
||||
toggleContentSections();
|
||||
updateBerlinPreview();
|
||||
@@ -230,6 +263,19 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
generateQRCode();
|
||||
toggleClearButton();
|
||||
});
|
||||
function onVcardInput() {
|
||||
generateQRCode();
|
||||
toggleClearButton();
|
||||
}
|
||||
vcardFirstNameInput.addEventListener('input', onVcardInput);
|
||||
vcardLastNameInput.addEventListener('input', onVcardInput);
|
||||
vcardOrgInput.addEventListener('input', onVcardInput);
|
||||
vcardTitleInput.addEventListener('input', onVcardInput);
|
||||
vcardPhoneInput.addEventListener('input', onVcardInput);
|
||||
vcardEmailInput.addEventListener('input', onVcardInput);
|
||||
vcardWebsiteInput.addEventListener('input', onVcardInput);
|
||||
vcardAddressInput.addEventListener('input', onVcardInput);
|
||||
vcardNoteInput.addEventListener('input', onVcardInput);
|
||||
|
||||
// Download QR code as image
|
||||
downloadBtn.addEventListener('click', downloadQRCode);
|
||||
@@ -286,7 +332,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
let explicitModeFromUrl = false;
|
||||
if (urlParams.has('mode')) {
|
||||
const m = urlParams.get('mode');
|
||||
if (m === 'text' || m === 'wifi' || m === 'event') {
|
||||
if (m === 'text' || m === 'wifi' || m === 'event' || m === 'vcard') {
|
||||
contentModeSelect.value = m;
|
||||
explicitModeFromUrl = true;
|
||||
}
|
||||
@@ -336,6 +382,45 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
if (urlParams.has('eventDescription')) {
|
||||
eventDescriptionInput.value = sanitizeUrlString(urlParams.get('eventDescription'), MAX_EVENT_DESCRIPTION);
|
||||
}
|
||||
const hasVcardParams = urlParams.has('vcardFirstName') ||
|
||||
urlParams.has('vcardLastName') ||
|
||||
urlParams.has('vcardOrg') ||
|
||||
urlParams.has('vcardTitle') ||
|
||||
urlParams.has('vcardPhone') ||
|
||||
urlParams.has('vcardEmail') ||
|
||||
urlParams.has('vcardWebsite') ||
|
||||
urlParams.has('vcardAddress') ||
|
||||
urlParams.has('vcardNote');
|
||||
if (!explicitModeFromUrl && !hasEventParams && hasVcardParams) {
|
||||
contentModeSelect.value = 'vcard';
|
||||
}
|
||||
if (urlParams.has('vcardFirstName')) {
|
||||
vcardFirstNameInput.value = sanitizeUrlString(urlParams.get('vcardFirstName'), MAX_VCARD_FIELD);
|
||||
}
|
||||
if (urlParams.has('vcardLastName')) {
|
||||
vcardLastNameInput.value = sanitizeUrlString(urlParams.get('vcardLastName'), MAX_VCARD_FIELD);
|
||||
}
|
||||
if (urlParams.has('vcardOrg')) {
|
||||
vcardOrgInput.value = sanitizeUrlString(urlParams.get('vcardOrg'), MAX_VCARD_FIELD);
|
||||
}
|
||||
if (urlParams.has('vcardTitle')) {
|
||||
vcardTitleInput.value = sanitizeUrlString(urlParams.get('vcardTitle'), MAX_VCARD_FIELD);
|
||||
}
|
||||
if (urlParams.has('vcardPhone')) {
|
||||
vcardPhoneInput.value = sanitizeUrlString(urlParams.get('vcardPhone'), MAX_VCARD_FIELD);
|
||||
}
|
||||
if (urlParams.has('vcardEmail')) {
|
||||
vcardEmailInput.value = sanitizeUrlString(urlParams.get('vcardEmail'), MAX_VCARD_FIELD);
|
||||
}
|
||||
if (urlParams.has('vcardWebsite')) {
|
||||
vcardWebsiteInput.value = sanitizeUrlString(urlParams.get('vcardWebsite'), MAX_VCARD_FIELD);
|
||||
}
|
||||
if (urlParams.has('vcardAddress')) {
|
||||
vcardAddressInput.value = sanitizeUrlString(urlParams.get('vcardAddress'), MAX_VCARD_FIELD);
|
||||
}
|
||||
if (urlParams.has('vcardNote')) {
|
||||
vcardNoteInput.value = sanitizeUrlString(urlParams.get('vcardNote'), MAX_VCARD_FIELD);
|
||||
}
|
||||
|
||||
if (urlParams.has('size')) {
|
||||
const sizeValue = urlParams.get('size');
|
||||
@@ -555,6 +640,74 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
return lines.join('\r\n');
|
||||
}
|
||||
|
||||
function escapeVCardText(str) {
|
||||
return String(str)
|
||||
.replace(/\r\n/g, '\n')
|
||||
.replace(/\r/g, '')
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/;/g, '\\;')
|
||||
.replace(/,/g, '\\,')
|
||||
.replace(/\n/g, '\\n');
|
||||
}
|
||||
|
||||
function buildVCard(opts) {
|
||||
const firstName = opts.firstName.trim();
|
||||
const lastName = opts.lastName.trim();
|
||||
const org = opts.org.trim();
|
||||
const title = opts.title.trim();
|
||||
const phone = opts.phone.trim();
|
||||
const email = opts.email.trim();
|
||||
const website = opts.website.trim();
|
||||
const address = opts.address.trim();
|
||||
const note = opts.note.trim();
|
||||
const displayName = [firstName, lastName].filter(Boolean).join(' ').trim() || org;
|
||||
|
||||
const lines = [
|
||||
'BEGIN:VCARD',
|
||||
'VERSION:3.0',
|
||||
'N:' + escapeVCardText(lastName) + ';' + escapeVCardText(firstName) + ';;;',
|
||||
'FN:' + escapeVCardText(displayName)
|
||||
];
|
||||
if (org) {
|
||||
lines.push('ORG:' + escapeVCardText(org));
|
||||
}
|
||||
if (title) {
|
||||
lines.push('TITLE:' + escapeVCardText(title));
|
||||
}
|
||||
if (phone) {
|
||||
lines.push('TEL;TYPE=CELL:' + escapeVCardText(phone));
|
||||
}
|
||||
if (email) {
|
||||
lines.push('EMAIL;TYPE=INTERNET:' + escapeVCardText(email));
|
||||
}
|
||||
if (website) {
|
||||
lines.push('URL:' + escapeVCardText(website));
|
||||
}
|
||||
if (address) {
|
||||
lines.push('ADR;TYPE=WORK:;;' + escapeVCardText(address) + ';;;;');
|
||||
}
|
||||
if (note) {
|
||||
lines.push('NOTE:' + escapeVCardText(note));
|
||||
}
|
||||
lines.push('END:VCARD');
|
||||
return lines.join('\r\n');
|
||||
}
|
||||
|
||||
function updateVCardPreview(text) {
|
||||
if (!vcardPreview) {
|
||||
return;
|
||||
}
|
||||
if (contentModeSelect.value !== 'vcard') {
|
||||
vcardPreview.textContent = '';
|
||||
return;
|
||||
}
|
||||
if (text && String(text).trim()) {
|
||||
vcardPreview.textContent = String(text);
|
||||
return;
|
||||
}
|
||||
vcardPreview.textContent = 'Bitte mindestens Vorname, Nachname oder Organisation eingeben.';
|
||||
}
|
||||
|
||||
function generateQRCode() {
|
||||
const mode = contentModeSelect.value;
|
||||
const text = textInput.value.trim();
|
||||
@@ -574,6 +727,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
// Clear previous error
|
||||
errorMessage.textContent = '';
|
||||
downloadBtn.style.display = 'none';
|
||||
if (mode !== 'vcard') {
|
||||
updateVCardPreview('');
|
||||
}
|
||||
|
||||
let qrText = '';
|
||||
|
||||
@@ -663,6 +819,40 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
description: desc.slice(0, MAX_EVENT_DESCRIPTION)
|
||||
});
|
||||
|
||||
textInput.value = qrText;
|
||||
toggleClearButton();
|
||||
} else if (mode === 'vcard') {
|
||||
const firstName = vcardFirstNameInput.value.slice(0, MAX_VCARD_FIELD);
|
||||
const lastName = vcardLastNameInput.value.slice(0, MAX_VCARD_FIELD);
|
||||
const org = vcardOrgInput.value.slice(0, MAX_VCARD_FIELD);
|
||||
const title = vcardTitleInput.value.slice(0, MAX_VCARD_FIELD);
|
||||
const phone = vcardPhoneInput.value.slice(0, MAX_VCARD_FIELD);
|
||||
const email = vcardEmailInput.value.slice(0, MAX_VCARD_FIELD);
|
||||
const website = vcardWebsiteInput.value.slice(0, MAX_VCARD_FIELD);
|
||||
const address = vcardAddressInput.value.slice(0, MAX_VCARD_FIELD);
|
||||
const note = vcardNoteInput.value.slice(0, MAX_VCARD_FIELD);
|
||||
const hasIdentity = firstName.trim() || lastName.trim() || org.trim();
|
||||
|
||||
if (!hasIdentity) {
|
||||
updateVCardPreview('');
|
||||
errorMessage.textContent = 'Bitte geben Sie mindestens Vorname, Nachname oder Organisation an';
|
||||
qrcodeCanvas.style.display = 'none';
|
||||
qrcodeImg.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
qrText = buildVCard({
|
||||
firstName: firstName,
|
||||
lastName: lastName,
|
||||
org: org,
|
||||
title: title,
|
||||
phone: phone,
|
||||
email: email,
|
||||
website: website,
|
||||
address: address,
|
||||
note: note
|
||||
});
|
||||
updateVCardPreview(qrText);
|
||||
textInput.value = qrText;
|
||||
toggleClearButton();
|
||||
} else if (mode === 'wifi') {
|
||||
@@ -688,6 +878,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
errorMessage.textContent = 'Bitte geben Sie Text/URL oder WLAN-Daten ein';
|
||||
} else if (mode === 'event') {
|
||||
errorMessage.textContent = 'Termin konnte nicht erzeugt werden';
|
||||
} else if (mode === 'vcard') {
|
||||
errorMessage.textContent = 'vCard konnte nicht erzeugt werden';
|
||||
} else {
|
||||
errorMessage.textContent = 'Bitte geben Sie Text oder eine URL ein';
|
||||
}
|
||||
@@ -827,6 +1019,34 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
if (eventDescriptionInput.value.trim()) {
|
||||
params.set('eventDescription', eventDescriptionInput.value.trim());
|
||||
}
|
||||
} else if (mode === 'vcard') {
|
||||
if (vcardFirstNameInput.value.trim()) {
|
||||
params.set('vcardFirstName', vcardFirstNameInput.value.trim());
|
||||
}
|
||||
if (vcardLastNameInput.value.trim()) {
|
||||
params.set('vcardLastName', vcardLastNameInput.value.trim());
|
||||
}
|
||||
if (vcardOrgInput.value.trim()) {
|
||||
params.set('vcardOrg', vcardOrgInput.value.trim());
|
||||
}
|
||||
if (vcardTitleInput.value.trim()) {
|
||||
params.set('vcardTitle', vcardTitleInput.value.trim());
|
||||
}
|
||||
if (vcardPhoneInput.value.trim()) {
|
||||
params.set('vcardPhone', vcardPhoneInput.value.trim());
|
||||
}
|
||||
if (vcardEmailInput.value.trim()) {
|
||||
params.set('vcardEmail', vcardEmailInput.value.trim());
|
||||
}
|
||||
if (vcardWebsiteInput.value.trim()) {
|
||||
params.set('vcardWebsite', vcardWebsiteInput.value.trim());
|
||||
}
|
||||
if (vcardAddressInput.value.trim()) {
|
||||
params.set('vcardAddress', vcardAddressInput.value.trim());
|
||||
}
|
||||
if (vcardNoteInput.value.trim()) {
|
||||
params.set('vcardNote', vcardNoteInput.value.trim());
|
||||
}
|
||||
} else if (mode === 'wifi') {
|
||||
if (wifiSsid) {
|
||||
params.set('ssid', wifiSsid);
|
||||
|
||||
Reference in New Issue
Block a user