📜
files.js
Back
📝 Javascript ⚡ Executable Ctrl+S: Save • Ctrl+R: Run • Ctrl+F: Find
// Debug alert for mobile debugging if (typeof debugAlert === 'function') debugAlert('files.js loaded'); // ===== Shared state (globals so tilepicker.js can read them) ===== window.selectedImage = null; // URL of currently selected sheet window.selectedImageName = null; // Name of selected sheet window.selectedTileSize = window.selectedTileSize || 32; // default when in Local Mode // Internal UI state let selectedImageDiv = null; // Keep ALL locally chosen sheets (multiple) const localSheets = []; // [{ name, url, size }] // Revoke created blob URLs on page exit to avoid leaks window.addEventListener('beforeunload', () => { localSheets.forEach(ls => { if (ls.url && ls.url.startsWith('blob:')) try { URL.revokeObjectURL(ls.url); } catch {} }); }); // View mode: 'server' (folders/images) or 'local' (only local sheets) let viewMode = 'server'; let currentSub = ''; // track current server subfolder path // ===== DOM ===== const fileList = document.getElementById('fileList'); const breadcrumb = document.querySelector('.breadcrumb'); const preview = document.getElementById('preview'); /** Load files for a subdirectory (server) */ function loadFiles(sub) { viewMode = 'server'; currentSub = sub || ''; fetch('media.php?sub=' + encodeURIComponent(currentSub)) .then(r => r.json()) .then(data => { if (data.error) throw new Error(data.error); renderBreadcrumb(data.breadcrumb); renderServerView(data); }) .catch(err => { console.error('Error loading files:', err); if (typeof debugAlert === 'function') alert('Error loading files: ' + err.message); // Fallback: still render Local Mode option renderBreadcrumb([{ label: 'tiles', sub: '' }]); renderServerView({ folders: [], images: [] }); }); } /** Breadcrumb renderer for server paths or Local Mode */ function renderBreadcrumb(crumbs) { breadcrumb.innerHTML = ''; if (viewMode === 'local') { // Show a simple "Local" crumb const span = document.createElement('span'); span.textContent = 'Local'; breadcrumb.appendChild(span); return; } (crumbs || []).forEach((c, i) => { const span = document.createElement('span'); span.textContent = c.label; span.style.cursor = 'pointer'; span.onclick = () => loadFiles(c.sub); breadcrumb.appendChild(span); if (i < crumbs.length - 1) { const sep = document.createElement('span'); sep.textContent = ' / '; breadcrumb.appendChild(sep); } }); } /** Add the "➕ Local sheet…" tile (mobile-safe file input over the tile) */ function addLocalSheetTile() { const div = document.createElement('div'); div.className = 'folder local-sheet-tile'; div.title = 'Pick image(s) from your device (Files)'; div.textContent = '➕ Local sheet…'; div.style.position = 'relative'; fileList.appendChild(div); const input = document.createElement('input'); input.type = 'file'; input.multiple = true; input.accept = '.png,.jpg,.jpeg,.webp,.gif,.bmp,.svg'; // favors file explorer Object.assign(input.style, { position: 'absolute', inset: '0', width: '100%', height: '100%', opacity: '0', cursor: 'pointer', zIndex: '1' }); input.addEventListener('change', (e) => handleLocalFiles(e.target.files), { passive: true }); input.addEventListener('input', (e) => handleLocalFiles(e.target.files), { passive: true }); input.addEventListener('touchstart', () => {}, { passive: true }); div.appendChild(input); } /** Handle files picked or dropped locally (supports MULTIPLE) */ function handleLocalFiles(fileListLike) { if (!fileListLike || !fileListLike.length) return; Array.from(fileListLike).forEach(file => { // Avoid duplicates by name+size if user re-selects const exists = localSheets.find(ls => ls.name === file.name && ls.size === file.size); if (exists) return; const url = URL.createObjectURL(file); localSheets.push({ name: file.name, url, size: file.size }); }); // Auto-select the LAST picked file (most recent) const last = localSheets[localSheets.length - 1]; window.selectedImage = last.url; window.selectedImageName = last.name; selectedImageDiv = null; showPreview({ name: last.name, url: last.url }); // Enter Local Mode immediately enterLocalMode(); refreshTilePickerIfOpen(); } /** Enter Local Mode UI */ function enterLocalMode() { viewMode = 'local'; renderBreadcrumb(null); renderLocalView(); } /** Exit Local Mode back to folders/images */ function exitLocalMode() { viewMode = 'server'; // Restore server view at the last subpath loadFiles(currentSub); } /** Clear all local sheets */ function clearLocalSheets() { // Revoke blob URLs localSheets.forEach(ls => { if (ls.url && ls.url.startsWith('blob:')) try { URL.revokeObjectURL(ls.url); } catch {} }); localSheets.length = 0; // Clear selection & preview window.selectedImage = null; window.selectedImageName = null; preview.innerHTML = ''; renderLocalView(); } /** Toolbar for Local Mode: Back, Clear, Tile Size */ function renderLocalToolbar() { const bar = document.createElement('div'); bar.className = 'local-toolbar'; bar.style.cssText = ` display:flex;align-items:center;gap:8px;flex-wrap:wrap; padding:8px 10px;margin-bottom:10px;background:#333;border-radius:6px; `; const backBtn = document.createElement('button'); backBtn.textContent = '← Back to folders'; backBtn.style.cssText = ` background:#555;color:#fff;border:1px solid #777;border-radius:4px; padding:6px 10px;cursor:pointer;font-size:12px; `; backBtn.onclick = exitLocalMode; const clearBtn = document.createElement('button'); clearBtn.textContent = 'Clear local sheets'; clearBtn.style.cssText = ` background:#d44;color:#fff;border:none;border-radius:4px; padding:6px 10px;cursor:pointer;font-size:12px; `; clearBtn.onclick = () => { if (confirm('Remove all locally added sheets?')) clearLocalSheets(); }; // Tile size selector (since numeric folders are hidden here) const sizeLabel = document.createElement('span'); sizeLabel.textContent = 'Tile size:'; sizeLabel.style.cssText = 'color:#ccc;font-size:12px;'; const sizeSelect = document.createElement('select'); sizeSelect.style.cssText = ` background:#555;color:#fff;border:1px solid #777;border-radius:4px; padding:5px 8px;font-size:12px; `; const sizes = [8, 12, 16, 24, 32, 48, 64, 96, 128]; sizes.forEach(s => { const opt = document.createElement('option'); opt.value = s; opt.textContent = `${s}px`; if (Number(window.selectedTileSize) === s) opt.selected = true; sizeSelect.appendChild(opt); }); sizeSelect.onchange = () => { window.selectedTileSize = parseInt(sizeSelect.value, 10); // If overlay is open, rebuild grid with the new size refreshTilePickerIfOpen(); }; bar.appendChild(backBtn); bar.appendChild(clearBtn); bar.appendChild(sizeLabel); bar.appendChild(sizeSelect); fileList.appendChild(bar); } /** Render Local Mode UI (only local sheets) */ function renderLocalView() { fileList.innerHTML = ''; preview.innerHTML = preview.innerHTML; // keep current preview (if any) renderLocalToolbar(); if (!localSheets.length) { const empty = document.createElement('div'); empty.style.cssText = ` color:#888;text-align:center;padding:20px;border:1px dashed #555;border-radius:6px; `; empty.innerHTML = ` No local sheets yet.<br> Use <b>➕ Local sheet…</b> in folders view to add files, or drag & drop onto this area. `; fileList.appendChild(empty); enableLocalDragDrop(); // allow adding by drop even in Local Mode return; } const grid = document.createElement('div'); grid.style.cssText = 'display:flex;flex-wrap:wrap;gap:8px;'; fileList.appendChild(grid); localSheets.forEach(ls => { const div = document.createElement('div'); div.className = 'image'; div.title = `Local: ${ls.name}`; const img = document.createElement('img'); img.src = ls.url; img.alt = ls.name; div.appendChild(img); div.onclick = () => { if (selectedImageDiv) selectedImageDiv.classList.remove('selected'); div.classList.add('selected'); selectedImageDiv = div; window.selectedImage = ls.url; window.selectedImageName = ls.name; showPreview({ name: ls.name, url: ls.url }); refreshTilePickerIfOpen(); }; grid.appendChild(div); }); enableLocalDragDrop(); } /** Drag & Drop support (works in both modes) */ function enableLocalDragDrop() { ['dragenter','dragover'].forEach(ev => fileList.addEventListener(ev, e => { e.preventDefault(); e.dataTransfer.dropEffect = 'copy'; fileList.classList.add('dropping'); }, { passive: false }) ); ['dragleave','drop'].forEach(ev => fileList.addEventListener(ev, e => { e.preventDefault(); fileList.classList.remove('dropping'); }, { passive: false }) ); fileList.addEventListener('drop', e => { const files = e.dataTransfer.files; if (files && files.length) handleLocalFiles(files); }, { passive: false }); } /** Render Server Mode (folders + images + Local tile) */ function renderServerView(data) { fileList.innerHTML = ''; preview.innerHTML = preview.innerHTML; // keep current preview (if any) // 1) Local sheet tile so user can jump into Local Mode addLocalSheetTile(); // 2) Folders (numeric folders set tile size when clicked) (data.folders || []).forEach(f => { const div = document.createElement('div'); div.className = 'folder'; div.textContent = f.name; div.onclick = () => { if (/^\d+$/.test(f.name)) window.selectedTileSize = parseInt(f.name, 10); loadFiles(f.sub); }; fileList.appendChild(div); }); // 3) Server images (data.images || []).forEach(img => { const div = document.createElement('div'); div.className = 'image'; const el = document.createElement('img'); el.src = img.url; el.alt = img.name; div.appendChild(el); div.onclick = () => { if (selectedImageDiv) selectedImageDiv.classList.remove('selected'); div.classList.add('selected'); selectedImageDiv = div; window.selectedImage = img.url; window.selectedImageName = img.name; showPreview(img); refreshTilePickerIfOpen(); }; fileList.appendChild(div); }); } /** Preview + click-to-open tile picker */ function showPreview(img) { preview.innerHTML = ` <h3>Selected: ${img.name}</h3> <img id="previewImg" src="${img.url}" alt="${img.name}"> `; const big = document.getElementById('previewImg'); big.onclick = () => openOverlay('tiles'); } /** If the tile picker overlay is already open, refresh it to the new selectedImage/size */ function refreshTilePickerIfOpen() { const overlay = document.getElementById('overlay'); if (!overlay || overlay.style.display !== 'block') return; if (typeof openTilePickerOverlay === 'function') openTilePickerOverlay(); } // ===== Initial drag & drop binding for Server Mode as well ===== enableLocalDragDrop();