From 272fb5954556973b16c89e667ad633aad765664f Mon Sep 17 00:00:00 2001 From: Skylar Grant Date: Wed, 8 Jul 2026 10:56:04 -0400 Subject: [PATCH] Initial upload --- .gitignore | 1 + css/style.css | 9 ++++++ index.html | 78 +++++++++++++++++++++++++++++++++++++++++++++++ js/app.js | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++ js/theme.js | 16 ++++++++++ 5 files changed, 187 insertions(+) create mode 100644 .gitignore create mode 100644 css/style.css create mode 100644 index.html create mode 100644 js/app.js create mode 100644 js/theme.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f66c74 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.zip \ No newline at end of file diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..83f0de7 --- /dev/null +++ b/css/style.css @@ -0,0 +1,9 @@ +/* Project-specific tweaks that go beyond Tailwind utility classes */ + +#camera-status { + pointer-events: none; +} + +video#video { + transform: scaleX(-1); /* mirror the preview so it feels like a mirror, not a rear camera */ +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..62bff7c --- /dev/null +++ b/index.html @@ -0,0 +1,78 @@ + + + + + + Student ID Photo Capture + + + + + + + + + +
+
+

Student ID Photo Capture

+

Capture a photo from your webcam and save it using the student number.

+
+ +
+ + +

Requesting camera access…

+
+ + + +
+ + + +
+ + + + +
+ + + + + diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..9ad02e5 --- /dev/null +++ b/js/app.js @@ -0,0 +1,83 @@ +const video = document.getElementById('video'); +const previewImg = document.getElementById('preview-img'); +const canvas = document.getElementById('canvas'); +const cameraStatus = document.getElementById('camera-status'); +const studentNumberInput = document.getElementById('student-number'); +const inputError = document.getElementById('input-error'); +const captureBtn = document.getElementById('capture-btn'); +const downloadLink = document.getElementById('download-link'); + +let captured = false; + +async function startCamera() { + try { + const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false }); + video.srcObject = stream; + cameraStatus.classList.add('hidden'); + captureBtn.disabled = false; + } catch (err) { + cameraStatus.textContent = 'Unable to access camera: ' + err.message; + } +} + +function sanitizeFilename(name) { + return name.trim().replace(/[^a-z0-9_-]+/gi, '_'); +} + +function showLiveView() { + previewImg.classList.add('hidden'); + video.classList.remove('hidden'); + downloadLink.classList.add('hidden'); + captureBtn.textContent = 'Capture Photo'; + captured = false; +} + +function showCapturedView() { + video.classList.add('hidden'); + previewImg.classList.remove('hidden'); + captureBtn.textContent = 'Retake Photo'; + captured = true; +} + +function capturePhoto() { + if (captured) { + showLiveView(); + return; + } + + const studentNumber = studentNumberInput.value.trim(); + if (!studentNumber) { + inputError.classList.remove('hidden'); + studentNumberInput.focus(); + return; + } + inputError.classList.add('hidden'); + + canvas.width = video.videoWidth; + canvas.height = video.videoHeight; + const ctx = canvas.getContext('2d'); + ctx.drawImage(video, 0, 0, canvas.width, canvas.height); + + const dataUrl = canvas.toDataURL('image/png'); + const filename = `${sanitizeFilename(studentNumber)}.png`; + + previewImg.src = dataUrl; + downloadLink.href = dataUrl; + downloadLink.download = filename; + downloadLink.classList.remove('hidden'); + + showCapturedView(); + + downloadLink.click(); +} + +captureBtn.addEventListener('click', capturePhoto); + +studentNumberInput.addEventListener('keydown', (event) => { + if (event.key === 'Enter') { + event.preventDefault(); + capturePhoto(); + } +}); + +startCamera(); diff --git a/js/theme.js b/js/theme.js new file mode 100644 index 0000000..604e1c4 --- /dev/null +++ b/js/theme.js @@ -0,0 +1,16 @@ +const themeToggle = document.getElementById('theme-toggle'); +const iconSun = document.getElementById('icon-sun'); +const iconMoon = document.getElementById('icon-moon'); + +function updateIcons(isDark) { + iconSun.classList.toggle('hidden', !isDark); + iconMoon.classList.toggle('hidden', isDark); +} + +updateIcons(document.documentElement.classList.contains('dark')); + +themeToggle.addEventListener('click', () => { + const isDark = document.documentElement.classList.toggle('dark'); + localStorage.setItem('theme', isDark ? 'dark' : 'light'); + updateIcons(isDark); +});