Sicherheit: CSP aktiviert, JavaScript ausgelagert, XSS-Schutz und README aktualisiert
This commit is contained in:
12
README.md
12
README.md
@@ -13,6 +13,7 @@ Ein einfacher QR-Code-Generator, der vollständig im Browser läuft. Keine Daten
|
|||||||
- ✅ **WiFi QR-Code Unterstützung mit eigener UI**
|
- ✅ **WiFi QR-Code Unterstützung mit eigener UI**
|
||||||
- ✅ **Dynamische Klartext-Anzeige**
|
- ✅ **Dynamische Klartext-Anzeige**
|
||||||
- ✅ **Automatische QR-Code-Generierung**
|
- ✅ **Automatische QR-Code-Generierung**
|
||||||
|
- ✅ **Sicher gegen XSS (Content Security Policy, validierte Eingaben, kein Inline-JS)**
|
||||||
|
|
||||||
## Docker Setup
|
## Docker Setup
|
||||||
|
|
||||||
@@ -122,4 +123,13 @@ Das Smartphone erkennt automatisch, dass es sich um WiFi-Daten handelt und biete
|
|||||||
|
|
||||||
### Intelligente Validierung
|
### Intelligente Validierung
|
||||||
- WiFi-Passwort ohne SSID wird als Fehler angezeigt
|
- WiFi-Passwort ohne SSID wird als Fehler angezeigt
|
||||||
- Leere Eingaben werden entsprechend behandelt
|
- Leere Eingaben werden entsprechend behandelt
|
||||||
|
|
||||||
|
## Sicherheit & Content Security Policy (CSP)
|
||||||
|
|
||||||
|
Die Anwendung ist gegen XSS-Angriffe abgesichert:
|
||||||
|
|
||||||
|
- Es wird eine strenge Content Security Policy (CSP) verwendet (`script-src 'self'`), die Inline-Skripte blockiert.
|
||||||
|
- Das gesamte JavaScript ist in die Datei `main.js` ausgelagert und wird als externes Skript eingebunden.
|
||||||
|
- Alle Benutzereingaben und URL-Parameter werden validiert und gefiltert.
|
||||||
|
- Es wird kein `innerHTML` verwendet, sondern nur sichere Methoden wie `.textContent` und `.value`.
|
293
index.html
293
index.html
@@ -3,9 +3,11 @@
|
|||||||
<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<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';">
|
||||||
<title>Just a QR Code</title>
|
<title>Just a QR Code</title>
|
||||||
<link rel="icon" 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='%23ffffff' rx='3' ry='3'/><rect x='8' y='8' width='4' height='4' fill='%23000000'/><rect x='12' y='8' width='4' height='4' fill='%23000000'/><rect x='16' y='8' width='4' height='4' fill='%23000000'/><rect x='8' y='12' width='4' height='4' fill='%23000000'/><rect x='8' y='16' width='4' height='4' fill='%23000000'/><rect x='20' y='8' width='4' height='4' fill='%23000000'/><rect x='16' y='12' width='4' height='4' fill='%23000000'/><rect x='20' y='16' width='4' height='4' fill='%23000000'/><rect x='8' y='20' width='4' height='4' fill='%23000000'/><rect x='12' y='20' width='4' height='4' fill='%23000000'/><rect x='16' y='20' width='4' height='4' fill='%23000000'/><rect x='20' y='20' width='4' height='4' fill='%23000000'/><rect x='20' y='12' width='4' height='4' fill='%23000000'/></svg>">
|
<link rel="icon" 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='%23ffffff' rx='3' ry='3'/><rect x='8' y='8' width='4' height='4' fill='%23000000'/><rect x='12' y='8' width='4' height='4' fill='%23000000'/><rect x='16' y='8' width='4' height='4' fill='%23000000'/><rect x='8' y='12' width='4' height='4' fill='%23000000'/><rect x='8' y='16' width='4' height='4' fill='%23000000'/><rect x='20' y='8' width='4' height='4' fill='%23000000'/><rect x='16' y='12' width='4' height='4' fill='%23000000'/><rect x='20' y='16' width='4' height='4' fill='%23000000'/><rect x='8' y='20' width='4' height='4' fill='%23000000'/><rect x='12' y='20' width='4' height='4' fill='%23000000'/><rect x='16' y='20' width='4' height='4' fill='%23000000'/><rect x='20' y='20' width='4' height='4' fill='%23000000'/><rect x='20' y='12' width='4' height='4' fill='%23000000'/></svg>">
|
||||||
<script src="./assets/qrious.min.js.Download"></script>
|
<script src="./assets/qrious.min.js"></script>
|
||||||
|
<script src="./main.js"></script>
|
||||||
<!--
|
<!--
|
||||||
Licensed under the Creative Commons Attribution-NonCommercial (BY-NC) 4.0 license.
|
Licensed under the Creative Commons Attribution-NonCommercial (BY-NC) 4.0 license.
|
||||||
Feel free to use this for anything noncommercial.
|
Feel free to use this for anything noncommercial.
|
||||||
@@ -395,293 +397,4 @@
|
|||||||
<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>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
|
|
||||||
const textInput = document.getElementById('text');
|
|
||||||
const clearTextBtn = document.getElementById('clear-text');
|
|
||||||
const wifiSsidInput = document.getElementById('wifi-ssid');
|
|
||||||
const wifiPasswordInput = document.getElementById('wifi-password');
|
|
||||||
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');
|
|
||||||
|
|
||||||
// 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');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set random compliment as default text and select it
|
|
||||||
textInput.value = 'https://medisoftware.de';
|
|
||||||
textInput.select();
|
|
||||||
|
|
||||||
// Show/hide clear button based on text input content
|
|
||||||
function toggleClearButton() {
|
|
||||||
if (textInput.value.length > 0) {
|
|
||||||
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 = '';
|
|
||||||
toggleClearButton();
|
|
||||||
textInput.focus();
|
|
||||||
generateQRCode();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update clear button visibility when text changes
|
|
||||||
textInput.addEventListener('input', function() {
|
|
||||||
toggleClearButton();
|
|
||||||
// Only generate QR code if no wifi data is being entered
|
|
||||||
const wifiSsid = wifiSsidInput.value.trim();
|
|
||||||
const wifiPassword = wifiPasswordInput.value.trim();
|
|
||||||
if (!wifiSsid && !wifiPassword) {
|
|
||||||
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() {
|
|
||||||
updateTextDisplay();
|
|
||||||
generateQRCode();
|
|
||||||
});
|
|
||||||
wifiPasswordInput.addEventListener('input', function() {
|
|
||||||
updateTextDisplay();
|
|
||||||
generateQRCode();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Download QR code as image
|
|
||||||
downloadBtn.addEventListener('click', downloadQRCode);
|
|
||||||
|
|
||||||
// Or check for URL parameters if present
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
|
||||||
if (urlParams.has('text')) {
|
|
||||||
textInput.value = urlParams.get('text');
|
|
||||||
}
|
|
||||||
if (urlParams.has('ssid')) {
|
|
||||||
wifiSsidInput.value = urlParams.get('ssid');
|
|
||||||
}
|
|
||||||
if (urlParams.has('password')) {
|
|
||||||
wifiPasswordInput.value = urlParams.get('password');
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
generateQRCode();
|
|
||||||
|
|
||||||
function generateQRCode() {
|
|
||||||
const text = textInput.value.trim();
|
|
||||||
const wifiSsid = wifiSsidInput.value.trim();
|
|
||||||
const wifiPassword = wifiPasswordInput.value.trim();
|
|
||||||
const size = parseInt(sizeSelect.value);
|
|
||||||
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';
|
|
||||||
|
|
||||||
// Determine what to encode
|
|
||||||
let qrText = text;
|
|
||||||
|
|
||||||
// If wifi credentials are provided, generate wifi QR code
|
|
||||||
if (wifiSsid && wifiPassword) {
|
|
||||||
// Wifi QR code format: WIFI:S:<SSID>;T:WPA;P:<password>;;
|
|
||||||
qrText = `WIFI:S:${escapeSpecialChars(wifiSsid)};T:WPA;P:${escapeSpecialChars(wifiPassword)};;`;
|
|
||||||
} else if (wifiSsid && !wifiPassword) {
|
|
||||||
// SSID only (open network)
|
|
||||||
qrText = `WIFI:S:${escapeSpecialChars(wifiSsid)};T:nopass;;`;
|
|
||||||
} else if (!wifiSsid && wifiPassword) {
|
|
||||||
// Password without SSID is invalid
|
|
||||||
errorMessage.textContent = 'Bitte geben Sie eine SSID ein, wenn Sie ein Passwort angeben';
|
|
||||||
qrcodeCanvas.style.display = 'none';
|
|
||||||
qrcodeImg.style.display = 'none';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate input
|
|
||||||
if (qrText === '') {
|
|
||||||
errorMessage.textContent = 'Bitte geben Sie Text/URL oder Wifi-Daten ein';
|
|
||||||
// Hide both canvas and image
|
|
||||||
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(() => {
|
|
||||||
downloadBtn.textContent = originalText;
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to update the text display with current QR code content
|
|
||||||
function updateTextDisplay() {
|
|
||||||
const wifiSsid = wifiSsidInput.value.trim();
|
|
||||||
const wifiPassword = wifiPasswordInput.value.trim();
|
|
||||||
|
|
||||||
if (wifiSsid && wifiPassword) {
|
|
||||||
// Show wifi QR code format
|
|
||||||
textInput.value = `WIFI:S:${escapeSpecialChars(wifiSsid)};T:WPA;P:${escapeSpecialChars(wifiPassword)};;`;
|
|
||||||
} else if (wifiSsid && !wifiPassword) {
|
|
||||||
// Show open network format
|
|
||||||
textInput.value = `WIFI:S:${escapeSpecialChars(wifiSsid)};T:nopass;;`;
|
|
||||||
} else {
|
|
||||||
// If no wifi data, keep original text input value
|
|
||||||
// Don't change it here to avoid overwriting user input
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update clear button visibility
|
|
||||||
toggleClearButton();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
</body></html>
|
</body></html>
|
295
main.js
Normal file
295
main.js
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
|
const textInput = document.getElementById('text');
|
||||||
|
const clearTextBtn = document.getElementById('clear-text');
|
||||||
|
const wifiSsidInput = document.getElementById('wifi-ssid');
|
||||||
|
const wifiPasswordInput = document.getElementById('wifi-password');
|
||||||
|
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');
|
||||||
|
|
||||||
|
// 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');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set random compliment as default text and select it
|
||||||
|
textInput.value = 'https://medisoftware.de';
|
||||||
|
textInput.select();
|
||||||
|
|
||||||
|
// Show/hide clear button based on text input content
|
||||||
|
function toggleClearButton() {
|
||||||
|
if (textInput.value.length > 0) {
|
||||||
|
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 = '';
|
||||||
|
toggleClearButton();
|
||||||
|
textInput.focus();
|
||||||
|
generateQRCode();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update clear button visibility when text changes
|
||||||
|
textInput.addEventListener('input', function() {
|
||||||
|
toggleClearButton();
|
||||||
|
// Only generate QR code if no wifi data is being entered
|
||||||
|
const wifiSsid = wifiSsidInput.value.trim();
|
||||||
|
const wifiPassword = wifiPasswordInput.value.trim();
|
||||||
|
if (!wifiSsid && !wifiPassword) {
|
||||||
|
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() {
|
||||||
|
updateTextDisplay();
|
||||||
|
generateQRCode();
|
||||||
|
});
|
||||||
|
wifiPasswordInput.addEventListener('input', function() {
|
||||||
|
updateTextDisplay();
|
||||||
|
generateQRCode();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Download QR code as image
|
||||||
|
downloadBtn.addEventListener('click', downloadQRCode);
|
||||||
|
|
||||||
|
// Or check for URL parameters if present
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
if (urlParams.has('text')) {
|
||||||
|
const safeText = urlParams.get('text');
|
||||||
|
// Nur erlaubte Zeichen (Buchstaben, Zahlen, gängige URL-Zeichen)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
generateQRCode();
|
||||||
|
|
||||||
|
function generateQRCode() {
|
||||||
|
const text = textInput.value.trim();
|
||||||
|
const wifiSsid = wifiSsidInput.value.trim();
|
||||||
|
const wifiPassword = wifiPasswordInput.value.trim();
|
||||||
|
const size = parseInt(sizeSelect.value);
|
||||||
|
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';
|
||||||
|
|
||||||
|
// Determine what to encode
|
||||||
|
let qrText = text;
|
||||||
|
|
||||||
|
// If wifi credentials are provided, generate wifi QR code
|
||||||
|
if (wifiSsid && wifiPassword) {
|
||||||
|
// Wifi QR code format: WIFI:S:<SSID>;T:WPA;P:<password>;;
|
||||||
|
qrText = `WIFI:S:${escapeSpecialChars(wifiSsid)};T:WPA;P:${escapeSpecialChars(wifiPassword)};;`;
|
||||||
|
} else if (wifiSsid && !wifiPassword) {
|
||||||
|
// SSID only (open network)
|
||||||
|
qrText = `WIFI:S:${escapeSpecialChars(wifiSsid)};T:nopass;;`;
|
||||||
|
} else if (!wifiSsid && wifiPassword) {
|
||||||
|
// Password without SSID is invalid
|
||||||
|
errorMessage.textContent = 'Bitte geben Sie eine SSID ein, wenn Sie ein Passwort angeben';
|
||||||
|
qrcodeCanvas.style.display = 'none';
|
||||||
|
qrcodeImg.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate input
|
||||||
|
if (qrText === '') {
|
||||||
|
errorMessage.textContent = 'Bitte geben Sie Text/URL oder Wifi-Daten ein';
|
||||||
|
// Hide both canvas and image
|
||||||
|
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(() => {
|
||||||
|
downloadBtn.textContent = originalText;
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to update the text display with current QR code content
|
||||||
|
function updateTextDisplay() {
|
||||||
|
const wifiSsid = wifiSsidInput.value.trim();
|
||||||
|
const wifiPassword = wifiPasswordInput.value.trim();
|
||||||
|
|
||||||
|
if (wifiSsid && wifiPassword) {
|
||||||
|
// Show wifi QR code format
|
||||||
|
textInput.value = `WIFI:S:${escapeSpecialChars(wifiSsid)};T:WPA;P:${escapeSpecialChars(wifiPassword)};;`;
|
||||||
|
} else if (wifiSsid && !wifiPassword) {
|
||||||
|
// Show open network format
|
||||||
|
textInput.value = `WIFI:S:${escapeSpecialChars(wifiSsid)};T:nopass;;`;
|
||||||
|
} else {
|
||||||
|
// If no wifi data, keep original text input value
|
||||||
|
// Don't change it here to avoid overwriting user input
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update clear button visibility
|
||||||
|
toggleClearButton();
|
||||||
|
}
|
||||||
|
});
|
Reference in New Issue
Block a user