195 lines
6.6 KiB
JavaScript
195 lines
6.6 KiB
JavaScript
/**
|
|
* 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 = '<span>🏠 Root</span>';
|
|
return;
|
|
}
|
|
|
|
let html = '<a href="/">🏠 Root</a>';
|
|
|
|
for (let i = 0; i < pathParts.length; i++) {
|
|
const part = pathParts[i];
|
|
html += '<span class="separator">/</span>';
|
|
|
|
if (i === pathParts.length - 1) {
|
|
html += `<span>${decodeURIComponent(part)}</span>`;
|
|
} else {
|
|
const levelsUp = pathParts.length - 1 - i;
|
|
const relativePath = '../'.repeat(levelsUp) + 'index.html';
|
|
html += `<a href="${relativePath}">${decodeURIComponent(part)}</a>`;
|
|
}
|
|
}
|
|
|
|
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 = '<div class="tree-item">❌ Error loading folder tree</div>';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 `<div class="tree-item">${indent}└─ ...</div>`;
|
|
}
|
|
|
|
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 += `<div class="tree-item">${indent}${arrow}📁 <span class="tree-current">${folderName}</span> (${totalItems} items)</div>`;
|
|
} else {
|
|
html += `<div class="tree-item">${indent}${arrow}📁 <a href="${path}" class="tree-link">${folderName}</a> (${totalItems} items)</div>`;
|
|
}
|
|
|
|
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 `<div class="tree-item">${indent}${arrow}📁 ${folderName} (error loading)</div>`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 += `<div class="tree-item">${indent}${arrow}📁 <span class="tree-current">${displayName}</span> (${totalItems} items)</div>`;
|
|
} else {
|
|
html += `<div class="tree-item">${indent}${arrow}📁 <a href="${finalPath}" class="tree-link">${displayName}</a> (${totalItems} items)</div>`;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|