/** * Navigation functionality - breadcrumbs and folder tree */ export class NavigationManager { /** * Build breadcrumb navigation based on current path */ buildBreadcrumb() { const currentPath = window.location.pathname; const pathParts = currentPath.split('/').filter(part => part !== '' && part !== 'index.html'); const breadcrumb = document.getElementById('breadcrumb'); if (!breadcrumb) return; if (pathParts.length === 0) { breadcrumb.innerHTML = '🏠 Root'; return; } let html = '🏠 Root'; for (let i = 0; i < pathParts.length; i++) { const part = pathParts[i]; html += '/'; if (i === pathParts.length - 1) { html += `${decodeURIComponent(part)}`; } else { const levelsUp = pathParts.length - 1 - i; const relativePath = '../'.repeat(levelsUp) + 'index.html'; html += `${decodeURIComponent(part)}`; } } breadcrumb.innerHTML = html; } /** * Extract subdirs and item count from a parsed page document. * Reads from the embedded #gallery-data JSON element. */ extractPageData(doc) { const dataEl = doc.getElementById('gallery-data'); if (dataEl) { try { const data = JSON.parse(dataEl.textContent); return { subdirs: data.subdirs || [], itemCount: data.item_count || 0 }; } catch {} } // Fallback for pages that pre-date the data element return { subdirs: [], itemCount: doc.querySelectorAll('.grid-item').length }; } /** * Build and display the folder tree structure */ async buildFolderTree() { const treeContainer = document.getElementById('folderTree'); const currentPath = window.location.pathname; if (!treeContainer) return; try { const tree = await this.buildTreeRecursive(currentPath, 0, currentPath); treeContainer.innerHTML = tree; } catch (error) { console.error('Error building folder tree:', error); treeContainer.innerHTML = '
❌ Error loading folder tree
'; } } /** * Recursively build tree structure for folders with collapsed empty directories */ async buildTreeRecursive(path, depth, currentPath, maxDepth = 5) { if (depth > maxDepth) { const indent = ' '.repeat(depth); return `
${indent}└─ ...
`; } try { const response = await fetch(path); const htmlContent = await response.text(); const parser = new DOMParser(); const doc = parser.parseFromString(htmlContent, 'text/html'); const { subdirs, itemCount } = this.extractPageData(doc); const baseUrl = path.replace(/\/[^\/]*$/, '/'); // Check if this is an empty directory (only one subdirectory, no items) if (itemCount === 0 && subdirs.length === 1) { const subPath = baseUrl + subdirs[0] + '/index.html'; const collapsedPath = await this.getCollapsedPath(path, subPath); return await this.buildCollapsedTreeItem(collapsedPath, depth, currentPath, maxDepth); } // Normal directory processing const indent = ' '.repeat(depth); const folderName = path.split('/').filter(p => p !== '' && p !== 'index.html').pop() || 'Gallery'; const totalItems = itemCount + subdirs.length; const arrow = depth === 0 ? '' : '└─ '; let html = ''; if (path === currentPath) { html += `
${indent}${arrow}📁 ${folderName} (${totalItems} items)
`; } else { html += `
${indent}${arrow}📁 ${folderName} (${totalItems} items)
`; } for (const subdir of subdirs) { const subPath = baseUrl + subdir + '/index.html'; html += await this.buildTreeRecursive(subPath, depth + 1, currentPath, maxDepth); } return html; } catch (error) { const indent = ' '.repeat(depth); const arrow = depth === 0 ? '' : '└─ '; const folderName = path.split('/').filter(p => p !== '' && p !== 'index.html').pop() || 'Gallery'; return `
${indent}${arrow}📁 ${folderName} (error loading)
`; } } /** * Get the collapsed path by following empty directories */ async getCollapsedPath(startPath, currentPath) { const pathSegments = []; let path = startPath; while (true) { try { const response = await fetch(path); const htmlContent = await response.text(); const parser = new DOMParser(); const doc = parser.parseFromString(htmlContent, 'text/html'); const { subdirs, itemCount } = this.extractPageData(doc); const folderName = path.split('/').filter(p => p !== '' && p !== 'index.html').pop() || 'Gallery'; pathSegments.push({ name: folderName, path: path }); if (itemCount > 0 || subdirs.length !== 1) { break; } const baseUrl = path.replace(/\/[^\/]*$/, '/'); path = baseUrl + subdirs[0] + '/index.html'; } catch (error) { break; } } return { segments: pathSegments, finalPath: path }; } /** * Build a collapsed tree item for empty directory chains */ async buildCollapsedTreeItem(collapsedPath, depth, currentPath, maxDepth) { const indent = ' '.repeat(depth); const arrow = depth === 0 ? '' : '└─ '; const displayName = collapsedPath.segments.map(seg => seg.name).join(' / '); const finalPath = collapsedPath.finalPath; let totalItems = 0; let subdirs = []; try { const response = await fetch(finalPath); const htmlContent = await response.text(); const parser = new DOMParser(); const doc = parser.parseFromString(htmlContent, 'text/html'); const data = this.extractPageData(doc); totalItems = data.itemCount + data.subdirs.length; subdirs = data.subdirs; } catch (error) {} let html = ''; if (finalPath === currentPath) { html += `
${indent}${arrow}📁 ${displayName} (${totalItems} items)
`; } else { html += `
${indent}${arrow}📁 ${displayName} (${totalItems} items)
`; } const baseUrl = finalPath.replace(/\/[^\/]*$/, '/'); for (const subdir of subdirs) { const subPath = baseUrl + subdir + '/index.html'; html += await this.buildTreeRecursive(subPath, depth + 1, currentPath, maxDepth); } return html; } }