File size: 5,136 Bytes
df53738 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | const video = document.getElementById('videoFeed');
const empIdInput = document.getElementById('empId');
const empNameInput = document.getElementById('empName');
const btnRegister = document.getElementById('btnRegister');
const statusText = document.getElementById('statusText');
const progressCircle = document.getElementById('progressCircle');
const spoofToast = document.getElementById('spoofToast');
const spoofToastMessage = document.getElementById('spoofToastMessage');
let capturedFrames = [];
let isCapturing = false;
const REQUIRED_FRAMES = 5;
let toastDismissTimer = null;
// Start Camera
navigator.mediaDevices.getUserMedia({ video: { facingMode: 'user', width: 640, height: 480 } })
.then(stream => { video.srcObject = stream; })
.catch(err => console.error(err));
// Monitor inputs to enable capture
[empIdInput, empNameInput].forEach(input => {
input.addEventListener('input', checkInputs);
});
function checkInputs() {
const valid = empIdInput.value.trim().length > 0 && empNameInput.value.trim().length > 0;
if (valid && !isCapturing && capturedFrames.length === 0) {
btnRegister.disabled = false;
btnRegister.textContent = "START CAPTURE";
btnRegister.onclick = startCaptureProcess;
} else if (capturedFrames.length === REQUIRED_FRAMES) {
btnRegister.disabled = false;
btnRegister.textContent = "Register";
btnRegister.onclick = submitRegistration;
} else {
btnRegister.disabled = true;
}
}
function startCaptureProcess() {
isCapturing = true;
btnRegister.disabled = true;
capturedFrames = [];
statusText.textContent = "Keep face steady...";
let count = 0;
const interval = setInterval(() => {
if (count >= REQUIRED_FRAMES) {
clearInterval(interval);
finishCapture();
return;
}
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0);
capturedFrames.push(canvas.toDataURL('image/jpeg', 0.8));
count++;
updateProgress(count / REQUIRED_FRAMES);
statusText.textContent = `Scanning... ${Math.round((count/REQUIRED_FRAMES)*100)}%`;
}, 600);
}
function updateProgress(percent) {
const offset = 113 - (113 * percent);
progressCircle.style.strokeDashoffset = offset;
}
function finishCapture() {
isCapturing = false;
statusText.textContent = "Face captured ✓";
statusText.style.color = "var(--color-primary)";
checkInputs(); // Re-enable button for submit
}
async function submitRegistration() {
btnRegister.disabled = true;
btnRegister.textContent = "REGISTERING...";
const payload = {
employee_id: empIdInput.value.trim(),
name: empNameInput.value.trim(),
frames: capturedFrames
};
try {
const res = await fetch('/api/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const data = await res.json();
if (data.status === 'registered') {
document.getElementById('registerForm').style.display = 'none';
document.getElementById('successState').style.display = 'block';
document.getElementById('successChip').textContent = `${payload.name} — ${payload.employee_id}`;
} else if (data.status === 'spoof') {
showSpoofToast(data);
btnRegister.disabled = false;
btnRegister.textContent = "Register";
} else {
// Duplicate user or other error → snackbar
var msg = data.message || "Unknown error";
if (msg.indexOf("already registered") !== -1 || msg.indexOf("already registered to") !== -1) {
if (typeof showSnackbar === 'function') showSnackbar("Duplicate registration — this face is already registered.", 'error');
} else if (msg.indexOf("No face detected") !== -1 || msg.indexOf("no face") !== -1) {
if (typeof showSnackbar === 'function') showSnackbar("Clear photo — ensure your face is visible and well lit.", 'info');
} else {
if (typeof showSnackbar === 'function') showSnackbar(msg, 'error');
}
btnRegister.disabled = false;
btnRegister.textContent = "Register";
}
} catch (e) {
if (typeof showSnackbar === 'function') showSnackbar("Network error. Please try again.", 'error');
btnRegister.disabled = false;
btnRegister.textContent = "Register";
}
}
function showSpoofToast(data) {
const msg = data.reason || data.message || "Use a live face, not a photo or screen.";
spoofToastMessage.textContent = msg;
spoofToast.classList.add('show');
if (toastDismissTimer) clearTimeout(toastDismissTimer);
toastDismissTimer = setTimeout(() => {
spoofToast.classList.remove('show');
toastDismissTimer = null;
}, 4500);
}
|