// ═══════════════════════════════════════════════════════ // 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 => `