Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.
Revision as of 22:37, 27 August 2025 by QTPi (talk | contribs)

Upload Your Selfie

Click on pin to view selfie.
Right-click on pin to delete (if Delete Mode is ON).
Saved ✅
`:''}${img}`; } const img = new Image(); img.onload = () => { const w = img.naturalWidth, h = img.naturalHeight; const map = L.map('map', { crs:L.CRS.Simple, minZoom:-2, maxZoom:5, zoomControl:true }); const bounds = [[0,0],[h,w]]; L.imageOverlay(MAP_IMAGE_URL, bounds, { crossOrigin:'anonymous' }).addTo(map); map.fitBounds(bounds); const popupOpts = { className:'tv-popup', maxWidth:600, minWidth:220, autoPan:true, keepInView:true, autoPanPaddingTopLeft:[0,80], autoPanPaddingBottomRight:[0,40] }; const selfies = L.layerGroup().addTo(map); // Delete mode let deleteMode=false; const delBtn=document.getElementById('m-delmode'); delBtn.onclick=()=>{ deleteMode=!deleteMode; delBtn.textContent='Delete mode: '+(deleteMode?'ON':'OFF'); delBtn.classList.toggle('on', deleteMode); }; function addMarker(latlng, title, desc, imgUrl){ const m = L.marker(latlng,{icon:icon(), title:title||'Selfie', desc:desc||'', imgUrl:imgUrl||null}).addTo(selfies); m.bindPopup(popupHtml(title,desc,imgUrl), popupOpts); m.on('click', () => m.openPopup()); m.on('contextmenu', () => { if(deleteMode){ selfies.removeLayer(m); saveShared().catch(console.warn); } }); return m; } function collectMarkers(){ const all=[]; selfies.eachLayer(l=>{ const ll=l.getLatLng(); all.push({ type:'selfie', y:ll.lat, x:ll.lng, title:l.options.title||'Selfie', desc:l.options.desc||'', img:l.options.imgUrl||null }); }); return all; } async function saveShared(){ const payload = JSON.stringify(collectMarkers()); const res = await fetch(SAVE_URL,{ method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ data: payload }) }); if(!res.ok) throw new Error(await res.text()); toast('Saved ✅'); } function applyMarkers(arr){ arr.forEach(m => addMarker([m.y,m.x], m.title||'Selfie', m.desc||'', m.img||null)); } const tI=document.getElementById('m-title'), dI=document.getElementById('m-desc'), pI=document.getElementById('m-photo'), addBtn=document.getElementById('m-add'); function validateInputs(){ const name = tI.value.trim(); if(!name){ alert('Please enter a player username.'); tI.focus(); return false; } const f = pI.files[0]; if(f){ const ok = ['image/jpeg','image/png','image/webp']; if(!ok.includes(f.type)){ alert('Only JPG/JPEG, PNG, or WEBP allowed.'); pI.value=''; return false; } if(f.size > 10*1024*1024){ alert('Image is over 10MB.'); pI.value=''; return false; } } return true; } async function uploadPhotoIfAny(){ const f = pI.files[0]; if(!f) return null; const fd = new FormData(); fd.append('photo', f); const res = await fetch(UPLOAD_URL, { method:'POST', body: fd }); if(!res.ok) throw new Error(await res.text()); const j = await res.json(); return j.url; } let placing=null; addBtn.onclick = () => { if(!validateInputs()) return; placing = { title: tI.value, desc: dI.value }; addBtn.textContent='Click map…'; addBtn.disabled=true; map.getContainer().style.cursor='crosshair'; function cancel(){ placing=null; addBtn.textContent='Add to Map'; addBtn.disabled=false; map.getContainer().style.cursor=''; document.removeEventListener('keydown',esc); } function esc(e){ if(e.key==='Escape') cancel(); } document.addEventListener('keydown',esc); map.once('click', async e => { try{ const imgUrl = await uploadPhotoIfAny(); addMarker([e.latlng.lat,e.latlng.lng], placing.title, placing.desc, imgUrl); await saveShared(); }catch(err){ alert('Could not add selfie: ' + err.message); } finally{ cancel(); tI.value=''; dI.value=''; pI.value=''; } }); }; (async function(){ try{ const res = await fetch(DATA_URL,{cache:'no-store'}); if(!res.ok) throw new Error('HTTP '+res.status); const arr = await res.json(); selfies.clearLayers(); applyMarkers(arr); }catch(e){ console.warn('Could not load shared data:', e); } })(); }; img.src = MAP_IMAGE_URL;