document.addEventListener('DOMContentLoaded', function() { let html5QrCode; const btnScanBarcode = document.getElementById('btnScanBarcode'); const btnTakePhoto = document.getElementById('btnTakePhoto'); const reader = document.getElementById('reader'); const aiLoading = document.getElementById('aiLoading'); const pName = document.getElementById('p_name'); const pCategory = document.getElementById('p_category'); const pQuantity = document.getElementById('p_quantity'); const pExpiration = document.getElementById('p_expiration'); function checkHttps() { if (location.protocol !== 'https:' && location.hostname !== 'localhost' && location.hostname !== '127.0.0.1') { alert("Camera access requires HTTPS. Please ensure you are using a secure connection."); return false; } return true; } function stopScanner() { return new Promise((resolve) => { console.log("Stopping scanner..."); cleanupUI(); if (html5QrCode && html5QrCode.isScanning) { html5QrCode.stop().then(() => { console.log("Scanner stopped."); reader.style.display = 'none'; resolve(); }).catch(err => { console.error("Failed to stop scanner", err); reader.style.display = 'none'; resolve(); }); } else { console.log("Scanner was not active."); reader.style.display = 'none'; resolve(); } }); } function cleanupUI() { const capBtn = document.getElementById('btnCaptureFrame'); if (capBtn) capBtn.remove(); const stopBtn = document.getElementById('btnStopScanner'); if (stopBtn) stopBtn.remove(); const flash = document.querySelector('.shutter-flash'); if (flash) flash.classList.remove('active'); reader.classList.remove('camera-mode'); } function addStopButton() { if (!document.getElementById('btnStopScanner')) { const stopBtn = document.createElement('button'); stopBtn.id = 'btnStopScanner'; stopBtn.innerText = "Stop Camera"; stopBtn.className = "btn btn-danger btn-sm w-100 mt-2"; stopBtn.onclick = stopScanner; reader.after(stopBtn); } } function triggerFlash() { let flash = document.querySelector('.shutter-flash'); if (!flash) { flash = document.createElement('div'); flash.className = 'shutter-flash'; reader.appendChild(flash); } flash.classList.add('active'); setTimeout(() => flash.classList.remove('active'), 150); } btnScanBarcode.addEventListener('click', function() { if (!checkHttps()) return; stopScanner().then(() => { reader.style.display = 'block'; if (!html5QrCode) { html5QrCode = new Html5Qrcode("reader"); } addStopButton(); const config = { fps: 10, qrbox: (viewfinderWidth, viewfinderHeight) => { return { width: viewfinderWidth * 0.8, height: viewfinderHeight * 0.4 }; } }; html5QrCode.start( { facingMode: "environment" }, config, (decodedText) => { console.log(`Barcode detected: ${decodedText}`); triggerFlash(); stopScanner().then(() => { analyzeProduct({ barcode: decodedText }); }); } ).catch(err => { console.error("Scanner start error:", err); alert("Could not start camera. Please check permissions."); reader.style.display = 'none'; cleanupUI(); }); }); }); btnTakePhoto.addEventListener('click', function() { if (!checkHttps()) return; stopScanner().then(() => { reader.style.display = 'block'; reader.classList.add('camera-mode'); if (!html5QrCode) { html5QrCode = new Html5Qrcode("reader"); } addStopButton(); html5QrCode.start( { facingMode: "environment" }, { fps: 10 }, () => { /* ignore QR scans in photo mode unless we want both */ } ).then(() => { if (!document.getElementById('btnCaptureFrame')) { const capBtn = document.createElement('button'); capBtn.id = 'btnCaptureFrame'; capBtn.innerHTML = ' Take Photo & Identify'; capBtn.className = "btn btn-primary btn-lg w-100 mt-2 mb-1 py-3"; capBtn.onclick = capturePhoto; // Insert before stop button if exists const stopBtn = document.getElementById('btnStopScanner'); if (stopBtn) { stopBtn.before(capBtn); } else { reader.after(capBtn); } } }).catch(err => { console.error("Camera start error:", err); alert("Camera error: " + err); reader.style.display = 'none'; cleanupUI(); }); }); }); function capturePhoto() { console.log("Capturing photo..."); const video = document.querySelector('#reader video'); if (!video) { console.error("Video element not found in reader"); alert("Camera not ready. Video element missing."); return; } triggerFlash(); const canvas = document.getElementById('photoCanvas'); if (!canvas) { console.error("Canvas element #photoCanvas not found"); alert("Application error: missing canvas."); return; } const MAX_DIM = 1024; let width = video.videoWidth; let height = video.videoHeight; if (width === 0 || height === 0) { console.error("Video dimensions are 0", {width, height}); alert("Camera error: invalid video dimensions. Try moving the camera."); return; } if (width > height) { if (width > MAX_DIM) { height *= MAX_DIM / width; width = MAX_DIM; } } else { if (height > MAX_DIM) { width *= MAX_DIM / height; height = MAX_DIM; } } canvas.width = width; canvas.height = height; const context = canvas.getContext('2d'); context.drawImage(video, 0, 0, width, height); const dataUrl = canvas.toDataURL('image/jpeg', 0.8); console.log("Data URL generated, length:", dataUrl.length); if (dataUrl.length < 1000) { console.error("Data URL seems too short, capture might have failed"); } setTimeout(() => { console.log("Proceeding to identification..."); stopScanner().then(() => { analyzeProduct({ image: dataUrl }); }); }, 300); } function analyzeProduct(params) { console.log("Sending request to api/analyze.php", params.barcode ? "with barcode" : "with image"); aiLoading.style.display = 'block'; pName.placeholder = "Identifying..."; // Clear previous values to show something is happening pName.value = ''; fetch('api/analyze.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(params) }) .then(res => { console.log("Received response status:", res.status); return res.json(); }) .then(res => { console.log("Received data:", res); aiLoading.style.display = 'none'; pName.placeholder = "e.g. Milk, Eggs, Bread"; if (res.success && res.data) { const data = res.data; if (data.name) pName.value = data.name; if (data.category) pCategory.value = data.category; if (data.quantity) pQuantity.value = data.quantity; if (data.expiration_date) pExpiration.value = data.expiration_date; // Visual highlight of changed fields [pName, pCategory, pQuantity, pExpiration].forEach(el => { if (el.value) { el.classList.add('is-valid'); setTimeout(() => el.classList.remove('is-valid'), 2000); } }); } else { console.error("Identification failed:", res.error); alert("Could not identify the product: " + (res.error || "Please try again.")); } }) .catch(err => { aiLoading.style.display = 'none'; pName.placeholder = "e.g. Milk, Eggs, Bread"; console.error("Fetch error:", err); alert("Analysis failed. Connection error or server issue."); }); } const addItemModal = document.getElementById('addItemModal'); if (addItemModal) { addItemModal.addEventListener('hidden.bs.modal', function () { stopScanner(); }); } });