function setMsg(target, text, ok) { const el = document.getElementById('msg-' + target); el.textContent = text; el.className = 'msg ' + (ok ? 'ok' : 'err'); } async function upload(target) { const file = document.getElementById('file-' + target).files[0]; const btn = document.getElementById('btn-' + target); const origText = btn.textContent; const msg = t => setMsg(target, t, false); if (!file) { msg('Először válassz egy új PNG fájlt a mentéshez.'); return; } if (file.type !== 'image/png') { msg('Csak PNG fájl tölthető fel.'); return; } if (file.size > 1024 * 1024) { msg('A fájl nagyobb, mint 1 MB.'); return; } btn.disabled = true; btn.textContent = '⏳ Mentés folyamatban...'; try { const bytes = new Uint8Array(await file.arrayBuffer()); await sendLogoBinary(target, bytes); } catch (e) { msg('❌ Hálózati hiba mentés közben'); } btn.disabled = false; btn.textContent = origText; } async function sendLogoBinary(target, bytes) { const res = await fetch('/logo?target=' + target, { method: 'POST', headers: { 'Content-Type': 'image/png', 'X-CSRF-Token': CSRF_TOKEN }, body: bytes }); if (res.status === 401) { location.href = '/login'; return; } const json = await res.json(); if (json.ok) { setMsg(target, '✅ Logó sikeresen elmentve! (A weboldalon a Publikálás után jelenik meg.)', true); const variantParam = target === 'header' ? 'variant=header&' : ''; document.getElementById('prev-' + target).src = '/logo.png?' + variantParam + 't=' + Date.now(); const meta = document.getElementById('meta-' + target); if (meta) meta.textContent = 'Módosítva (' + (bytes.length / 1024).toFixed(1) + ' KB) — elmentve'; } else setMsg(target, '❌ ' + json.error, false); } document.querySelectorAll('input[type=file]').forEach(inp => { inp.addEventListener('change', () => { const target = inp.id.replace('file-', ''); const f = inp.files[0]; const meta = document.getElementById('meta-' + target); const prev = document.getElementById('prev-' + target); if (!f) return; if (f.type !== 'image/png') { setMsg(target, 'Csak PNG formátumú kép választható ki.', false); if (meta) meta.textContent = ''; return; } if (f.size > 1024 * 1024) { setMsg(target, 'A fájl nagyobb 1 MB-nál.', false); if (meta) meta.textContent = ''; return; } setMsg(target, 'Új fájl kiválasztva. Kattints a Mentés vagy a ✏️ Szerkesztés gombra.', true); if (meta) meta.textContent = f.name + ' — ' + (f.size / 1024).toFixed(1) + ' KB (még nincs mentve)'; prev.src = URL.createObjectURL(f); }); }); /* ── Interactive Canvas Editor Logic ────────────────────────────── */ let currentEditTarget = 'header'; let editImg = new Image(); let editState = { zoom: 1, panX: 0, panY: 0, rotation: 0, flipH: 1, flipV: 1, padding: 0, aspect: 0, brightness: 100, contrast: 100, invert: false }; const canvas = document.getElementById('edit-canvas'); const ctx = canvas.getContext('2d'); const wrap = document.getElementById('canvas-wrap'); let isDragging = false, startX = 0, startY = 0; function openEditor(target) { currentEditTarget = target; document.getElementById('modal-title').textContent = '🎨 Logó szerkesztése — ' + (target === 'header' ? 'Weboldal fejléc' : 'CMS ikon'); editState.aspect = (target === 'icon' ? 1 : 0); updateAspectBtns(); resetFilters(); resetPan(); const fileInput = document.getElementById('file-' + target); if (fileInput.files && fileInput.files[0]) { const reader = new FileReader(); reader.onload = e => { loadImg(e.target.result); }; reader.readAsDataURL(fileInput.files[0]); } else { const previewSrc = document.getElementById('prev-' + target).src; loadImg(previewSrc); } } function loadImg(src) { editImg = new Image(); editImg.crossOrigin = 'anonymous'; editImg.onload = () => { document.getElementById('editor-modal').classList.add('open'); fitToCrop(); render(); }; editImg.src = src; } function closeEditor() { document.getElementById('editor-modal').classList.remove('open'); } function setAspect(ratio) { editState.aspect = ratio; updateAspectBtns(); render(); } function updateAspectBtns() { document.querySelectorAll('#aspect-btns button').forEach(b => { const a = parseFloat(b.dataset.aspect); b.classList.toggle('active', (editState.aspect === 0 && a === 0) || (Math.abs(editState.aspect - a) < 0.01)); }); } function setZoom(val) { editState.zoom = parseFloat(val); document.getElementById('zoom-val').textContent = Math.round(editState.zoom * 100) + '%'; render(); } function setPadding(val) { editState.padding = parseInt(val, 10); document.getElementById('pad-val').textContent = editState.padding + 'px'; render(); } function setFilter(name, val) { editState[name] = parseInt(val, 10); document.getElementById(name.slice(0, 6) + '-val').textContent = val + '%'; render(); } function toggleInvert() { editState.invert = !editState.invert; document.getElementById('btn-invert').classList.toggle('active', editState.invert); render(); } function resetFilters() { editState.brightness = 100; editState.contrast = 100; editState.invert = false; editState.padding = 0; document.getElementById('bright-range').value = 100; document.getElementById('bright-val').textContent = '100%'; document.getElementById('contrast-range').value = 100; document.getElementById('contrast-val').textContent = '100%'; document.getElementById('pad-range').value = 0; document.getElementById('pad-val').textContent = '0px'; document.getElementById('btn-invert').classList.remove('active'); render(); } function rotate(deg) { editState.rotation = (editState.rotation + deg) % 360; render(); } function toggleFlip(dir) { if (dir === 'h') editState.flipH *= -1; if (dir === 'v') editState.flipV *= -1; render(); } function resetPan() { editState.panX = 0; editState.panY = 0; render(); } function getCropRect() { const cw = canvas.width, ch = canvas.height; let rw = cw * 0.85, rh = ch * 0.85; if (editState.aspect > 0) { if (rw / rh > editState.aspect) rw = rh * editState.aspect; else rh = rw / editState.aspect; } return { x: (cw - rw) / 2, y: (ch - rh) / 2, w: rw, h: rh }; } function fitToCrop() { if (!editImg.width || !editImg.height) return; const crop = getCropRect(); const isRotated = Math.abs(editState.rotation) === 90 || Math.abs(editState.rotation) === 270; const iw = isRotated ? editImg.height : editImg.width; const ih = isRotated ? editImg.width : editImg.height; const scale = Math.min(crop.w / iw, crop.h / ih); editState.zoom = Math.max(0.3, Math.min(3, scale)); document.getElementById('zoom-range').value = editState.zoom; document.getElementById('zoom-val').textContent = Math.round(editState.zoom * 100) + '%'; editState.panX = 0; editState.panY = 0; render(); } function render() { if (!editImg.width) return; ctx.clearRect(0, 0, canvas.width, canvas.height); const crop = getCropRect(); // Draw image ctx.save(); ctx.filter = 'brightness(' + editState.brightness + '%) contrast(' + editState.contrast + '%)' + (editState.invert ? ' invert(100%)' : ''); ctx.translate(canvas.width / 2 + editState.panX, canvas.height / 2 + editState.panY); ctx.rotate((editState.rotation * Math.PI) / 180); ctx.scale(editState.zoom * editState.flipH, editState.zoom * editState.flipV); const pad = editState.padding / (editState.zoom || 1); const dw = Math.max(10, editImg.width - pad * 2); const dh = Math.max(10, editImg.height - pad * 2); ctx.drawImage(editImg, -dw / 2, -dh / 2, dw, dh); ctx.restore(); // Dark overlay outside crop rect ctx.save(); ctx.fillStyle = 'rgba(15, 17, 23, 0.75)'; ctx.fillRect(0, 0, canvas.width, crop.y); ctx.fillRect(0, crop.y + crop.h, canvas.width, canvas.height - (crop.y + crop.h)); ctx.fillRect(0, crop.y, crop.x, crop.h); ctx.fillRect(crop.x + crop.w, crop.y, canvas.width - (crop.x + crop.w), crop.h); // Crop border ctx.strokeStyle = '#3b82f6'; ctx.lineWidth = 2; ctx.setLineDash([6, 4]); ctx.strokeRect(crop.x, crop.y, crop.w, crop.h); ctx.restore(); } // Drag & Pan handlers wrap.addEventListener('mousedown', e => { isDragging = true; startX = e.clientX - editState.panX; startY = e.clientY - editState.panY; wrap.classList.add('grabbing'); }); window.addEventListener('mousemove', e => { if (!isDragging) return; editState.panX = e.clientX - startX; editState.panY = e.clientY - startY; render(); }); window.addEventListener('mouseup', () => { isDragging = false; wrap.classList.remove('grabbing'); }); wrap.addEventListener('wheel', e => { e.preventDefault(); const delta = e.deltaY < 0 ? 0.05 : -0.05; setZoom(Math.max(0.3, Math.min(3, editState.zoom + delta))); document.getElementById('zoom-range').value = editState.zoom; }, { passive: false }); async function saveEditedLogo() { const crop = getCropRect(); const outCanvas = document.createElement('canvas'); outCanvas.width = Math.round(crop.w * 2); // 2x for retina sharpness outCanvas.height = Math.round(crop.h * 2); const octx = outCanvas.getContext('2d'); octx.save(); octx.scale(2, 2); octx.translate(-crop.x, -crop.y); octx.filter = 'brightness(' + editState.brightness + '%) contrast(' + editState.contrast + '%)' + (editState.invert ? ' invert(100%)' : ''); octx.translate(canvas.width / 2 + editState.panX, canvas.height / 2 + editState.panY); octx.rotate((editState.rotation * Math.PI) / 180); octx.scale(editState.zoom * editState.flipH, editState.zoom * editState.flipV); const pad = editState.padding / (editState.zoom || 1); const dw = Math.max(10, editImg.width - pad * 2); const dh = Math.max(10, editImg.height - pad * 2); octx.drawImage(editImg, -dw / 2, -dh / 2, dw, dh); octx.restore(); const saveBtn = document.getElementById('modal-save-btn'); saveBtn.disabled = true; saveBtn.textContent = '⏳ Mentés folyamatban...'; outCanvas.toBlob(async blob => { if (!blob) { alert('Hiba a kép exportálásakor'); saveBtn.disabled = false; return; } try { const bytes = new Uint8Array(await blob.arrayBuffer()); await sendLogoBinary(currentEditTarget, bytes); closeEditor(); } catch (e) { alert('Hiba történt a mentés során.'); } saveBtn.disabled = false; saveBtn.textContent = '💾 Szerkesztett logó mentése'; }, 'image/png'); } async function uploadPartner() { const name = document.getElementById('partner-name').value.trim(); const file = document.getElementById('file-partner').files[0]; const msg = document.getElementById('msg-partner'); const pathOut = document.getElementById('path-partner'); const btn = document.getElementById('btn-partner'); pathOut.textContent = ''; if (!name) { msg.textContent = '❌ Adj meg egy fájlnevet.'; msg.className = 'msg err'; return; } if (!file) { msg.textContent = '❌ Válassz PNG fájlt.'; msg.className = 'msg err'; return; } if (file.type !== 'image/png') { msg.textContent = '❌ Csak PNG tölthető fel.'; msg.className = 'msg err'; return; } btn.disabled = true; try { const bytes = new Uint8Array(await file.arrayBuffer()); const res = await fetch('/partner-logo?name=' + encodeURIComponent(name), { method: 'POST', headers: { 'Content-Type': 'image/png', 'X-CSRF-Token': CSRF_TOKEN }, body: bytes }); if (res.status === 401) { location.href = '/login'; return; } const json = await res.json(); if (json.ok) { msg.textContent = '✅ Feltöltve.'; msg.className = 'msg ok'; pathOut.textContent = 'Elérési út: ' + json.path; } else { msg.textContent = '❌ ' + json.error; msg.className = 'msg err'; } } catch (e) { msg.textContent = '❌ Hálózati hiba'; msg.className = 'msg err'; } btn.disabled = false; }