document.addEventListener('DOMContentLoaded', function () { 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(); });