first commit

This commit is contained in:
Patrick
2026-05-01 20:02:13 +02:00
commit 75fb753fc0
77 changed files with 4793 additions and 0 deletions
+163
View File
@@ -0,0 +1,163 @@
// ═══════════════════════════════════════════════════════
// QR Code Reader (using jsQR library)
// ═══════════════════════════════════════════════════════
let qrCameraStream = null;
let qrScanInterval = null;
let qrScanHistoryList = [];
function startQrCamera() {
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
return setStatus('qrReaderStatus', 'error', 'Camera API not supported in this browser.');
}
navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } })
.then(stream => {
qrCameraStream = stream;
const video = document.getElementById('qrVideo');
video.srcObject = stream;
video.play();
document.getElementById('qrCameraContainer').style.display = 'block';
document.getElementById('qrCameraBtn').style.display = 'none';
document.getElementById('qrStopBtn').style.display = '';
document.getElementById('qrImagePreview').style.display = 'none';
setStatus('qrReaderStatus', 'info', 'Camera active — scanning for QR codes...');
// Start scanning frames
const canvas = document.getElementById('qrScanCanvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
qrScanInterval = setInterval(() => {
if (video.readyState === video.HAVE_ENOUGH_DATA) {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
if (typeof jsQR !== 'undefined') {
const code = jsQR(imageData.data, imageData.width, imageData.height, { inversionAttempts: 'dontInvert' });
if (code) {
showQrResult(code.data);
setStatus('qrReaderStatus', 'success', 'QR code detected ✓');
}
}
}
}, 250);
})
.catch(err => {
setStatus('qrReaderStatus', 'error', 'Camera access denied: ' + err.message);
});
}
function stopQrCamera() {
if (qrScanInterval) { clearInterval(qrScanInterval); qrScanInterval = null; }
if (qrCameraStream) {
qrCameraStream.getTracks().forEach(t => t.stop());
qrCameraStream = null;
}
const video = document.getElementById('qrVideo');
video.srcObject = null;
document.getElementById('qrCameraContainer').style.display = 'none';
document.getElementById('qrCameraBtn').style.display = '';
document.getElementById('qrStopBtn').style.display = 'none';
setStatus('qrReaderStatus', 'info', 'Camera stopped.');
}
function scanQrFromFile(event) {
const file = event.target.files[0];
if (!file) return;
// Stop camera if running
stopQrCamera();
const img = document.getElementById('qrPreviewImg');
const reader = new FileReader();
reader.onload = function (e) {
img.src = e.target.result;
document.getElementById('qrImagePreview').style.display = 'block';
const tempImg = new Image();
tempImg.onload = function () {
const canvas = document.getElementById('qrImgCanvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
canvas.width = tempImg.width;
canvas.height = tempImg.height;
ctx.drawImage(tempImg, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
if (typeof jsQR !== 'undefined') {
const code = jsQR(imageData.data, imageData.width, imageData.height, { inversionAttempts: 'attemptBoth' });
if (code) {
showQrResult(code.data);
setStatus('qrReaderStatus', 'success', 'QR code detected in image ✓');
} else {
setStatus('qrReaderStatus', 'error', 'No QR code found in this image.');
}
} else {
setStatus('qrReaderStatus', 'error', 'QR scanning library not loaded.');
}
};
tempImg.src = e.target.result;
};
reader.readAsDataURL(file);
// Reset file input so same file can be re-selected
event.target.value = '';
}
function showQrResult(data) {
document.getElementById('qrReaderPlaceholder').style.display = 'none';
document.getElementById('qrReaderResult').style.display = 'block';
document.getElementById('qrDecodedText').textContent = data;
// Detect type
let type = 'Plain Text';
const openBtn = document.getElementById('qrOpenLinkBtn');
openBtn.style.display = 'none';
if (/^https?:\/\//i.test(data)) {
type = '🔗 URL';
openBtn.style.display = '';
} else if (/^mailto:/i.test(data)) {
type = '📧 Email';
openBtn.style.display = '';
} else if (/^tel:/i.test(data)) {
type = '📞 Phone Number';
} else if (/^BEGIN:VCARD/i.test(data)) {
type = '👤 vCard Contact';
} else if (/^BEGIN:VEVENT/i.test(data)) {
type = '📅 Calendar Event';
} else if (/^WIFI:/i.test(data)) {
type = '📶 Wi-Fi Network';
} else if (/^smsto:/i.test(data)) {
type = '💬 SMS';
} else if (/^geo:/i.test(data)) {
type = '📍 Geolocation';
}
document.getElementById('qrDecodedType').textContent = type;
// Add to history (keep last 10)
const now = new Date().toLocaleTimeString();
qrScanHistoryList.unshift({ data: data.length > 80 ? data.slice(0, 80) + '...' : data, time: now, full: data });
if (qrScanHistoryList.length > 10) qrScanHistoryList.pop();
document.getElementById('qrScanHistory').innerHTML = qrScanHistoryList.map(h =>
`<div class="result-row" style="cursor:pointer;" onclick="copyText('${h.full.replace(/'/g, "\\'")}')">
<div class="label">${h.time}</div>
<div class="value" style="font-size:0.78rem;">${h.data.replace(/</g, '&lt;')}</div>
</div>`
).join('');
}
// Stop camera when navigating away
const origShowPage = window.showPage;
if (origShowPage) {
window.showPage = function (name) {
if (name !== 'qrreader' && qrCameraStream) stopQrCamera();
origShowPage(name);
};
}