aboutsummaryrefslogtreecommitdiffstats
path: root/src/public/js
diff options
context:
space:
mode:
Diffstat (limited to 'src/public/js')
-rw-r--r--src/public/js/app.js200
1 files changed, 199 insertions, 1 deletions
diff --git a/src/public/js/app.js b/src/public/js/app.js
index 86b3c33..a6bb9c5 100644
--- a/src/public/js/app.js
+++ b/src/public/js/app.js
@@ -1,3 +1,201 @@
document.addEventListener('DOMContentLoaded', function () {
- // App initialization
+ var canvas = document.getElementById('preview-canvas');
+ if (!canvas) return;
+
+ var ctx = canvas.getContext('2d');
+ var video = document.getElementById('webcam-video');
+ var btnCapture = document.getElementById('btn-capture');
+ var btnSave = document.getElementById('btn-save');
+ var fileInput = document.getElementById('file-input');
+ var scaleSlider = document.getElementById('overlay-scale');
+ var tabWebcam = document.getElementById('tab-webcam');
+ var tabUpload = document.getElementById('tab-upload');
+ var webcamPanel = document.getElementById('webcam-panel');
+ var uploadPanel = document.getElementById('upload-panel');
+ var csrfToken = document.getElementById('csrf-token').value;
+ var overlayThumbs = document.querySelectorAll('.overlay-thumb');
+
+ // State
+ var baseImageData = null; // base64 data URL of the captured/uploaded image
+ var selectedOverlay = null; // filename of the selected overlay
+ var overlayImg = null; // loaded Image object for the selected overlay
+ var webcamStream = null;
+
+ // -- Tabs --
+
+ tabWebcam.addEventListener('click', function () {
+ tabWebcam.classList.add('active');
+ tabUpload.classList.remove('active');
+ webcamPanel.hidden = false;
+ uploadPanel.hidden = true;
+ startWebcam();
+ });
+
+ tabUpload.addEventListener('click', function () {
+ tabUpload.classList.add('active');
+ tabWebcam.classList.remove('active');
+ uploadPanel.hidden = false;
+ webcamPanel.hidden = true;
+ stopWebcam();
+ });
+
+ // -- Webcam --
+
+ function startWebcam() {
+ if (webcamStream) return;
+ navigator.mediaDevices.getUserMedia({ video: { width: 640, height: 640, facingMode: 'user' } })
+ .then(function (stream) {
+ webcamStream = stream;
+ video.srcObject = stream;
+ })
+ .catch(function () {
+ // Camera not available — switch to upload tab
+ tabUpload.click();
+ });
+ }
+
+ function stopWebcam() {
+ if (webcamStream) {
+ webcamStream.getTracks().forEach(function (t) { t.stop(); });
+ webcamStream = null;
+ video.srcObject = null;
+ }
+ }
+
+ btnCapture.addEventListener('click', function () {
+ // Draw current video frame onto an offscreen canvas to get a data URL
+ var offscreen = document.createElement('canvas');
+ offscreen.width = 640;
+ offscreen.height = 640;
+ var offCtx = offscreen.getContext('2d');
+ // Center-crop the video frame into the square canvas
+ var vw = video.videoWidth;
+ var vh = video.videoHeight;
+ var crop = Math.min(vw, vh);
+ var sx = (vw - crop) / 2;
+ var sy = (vh - crop) / 2;
+ offCtx.drawImage(video, sx, sy, crop, crop, 0, 0, 640, 640);
+ baseImageData = offscreen.toDataURL('image/jpeg', 0.9);
+ renderPreview();
+ });
+
+ // -- Upload --
+
+ fileInput.addEventListener('change', function () {
+ var file = fileInput.files[0];
+ if (!file) return;
+ var reader = new FileReader();
+ reader.onload = function (e) {
+ baseImageData = e.target.result;
+ renderPreview();
+ };
+ reader.readAsDataURL(file);
+ });
+
+ // -- Overlay selection --
+
+ overlayThumbs.forEach(function (thumb) {
+ thumb.addEventListener('click', function () {
+ // Deselect previous
+ overlayThumbs.forEach(function (t) { t.classList.remove('selected'); });
+ thumb.classList.add('selected');
+ selectedOverlay = thumb.dataset.filename;
+
+ // Preload the full overlay image for canvas rendering
+ overlayImg = new Image();
+ overlayImg.onload = renderPreview;
+ overlayImg.src = thumb.src;
+ });
+ });
+
+ // -- Scale slider --
+
+ scaleSlider.addEventListener('input', renderPreview);
+
+ // -- Preview --
+
+ function renderPreview() {
+ ctx.clearRect(0, 0, 640, 640);
+ // Fill with light gray so the canvas isn't transparent
+ ctx.fillStyle = '#eee';
+ ctx.fillRect(0, 0, 640, 640);
+
+ if (!baseImageData) {
+ ctx.fillStyle = '#999';
+ ctx.font = '18px sans-serif';
+ ctx.textAlign = 'center';
+ ctx.fillText('Take a photo or upload an image', 320, 320);
+ updateSaveButton();
+ return;
+ }
+
+ var img = new Image();
+ img.onload = function () {
+ // Center-crop into the 640x640 canvas
+ var sw = img.width;
+ var sh = img.height;
+ var crop = Math.min(sw, sh);
+ var sx = (sw - crop) / 2;
+ var sy = (sh - crop) / 2;
+ ctx.drawImage(img, sx, sy, crop, crop, 0, 0, 640, 640);
+
+ // Draw overlay on top if one is selected
+ if (overlayImg && overlayImg.complete) {
+ var scale = scaleSlider.value / 100;
+ var ow = overlayImg.width * scale;
+ var oh = overlayImg.height * scale;
+ var ox = (640 - ow) / 2;
+ var oy = (640 - oh) / 2;
+ ctx.drawImage(overlayImg, ox, oy, ow, oh);
+ }
+
+ updateSaveButton();
+ };
+ img.src = baseImageData;
+ }
+
+ function updateSaveButton() {
+ // Both a base image and an overlay must be selected
+ btnSave.disabled = !(baseImageData && selectedOverlay);
+ }
+
+ // -- Save --
+
+ btnSave.addEventListener('click', function () {
+ if (btnSave.disabled) return;
+ btnSave.disabled = true;
+ btnSave.textContent = 'Saving...';
+
+ var scale = scaleSlider.value / 100;
+
+ fetch('/editor', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ image_data: baseImageData,
+ overlay: selectedOverlay,
+ overlay_scale: scale,
+ csrf_token: csrfToken
+ })
+ })
+ .then(function (res) { return res.json(); })
+ .then(function (data) {
+ if (data.success) {
+ window.location.href = data.redirect;
+ } else {
+ alert(data.error || 'Something went wrong.');
+ btnSave.disabled = false;
+ btnSave.textContent = 'Save post';
+ }
+ })
+ .catch(function () {
+ alert('Network error. Please try again.');
+ btnSave.disabled = false;
+ btnSave.textContent = 'Save post';
+ });
+ });
+
+ // Start webcam by default
+ startWebcam();
+ renderPreview();
});