Files
Nginx-Fancyindex-Theme/theme/header.html
T
condor b46270264b init
2026-07-24 19:05:07 +08:00

232 lines
9.7 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<link rel="stylesheet" href="https://repo.52or.com/fancyindex/styles.css?v=3">
<div class="wrapper">
<header class="site-header">
<form class="search-form" role="search" onsubmit="return false" style="display:flex;justify-content:center;align-items:center;gap:10px;">
<a class="up-btn" id="up-btn" href="#" aria-label="返回上一级目录" title="返回上一级目录">
<svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path fill="currentColor" d="M13.414 5.414 7.828 11l5.586 5.586a1 1 0 0 1-1.414 1.414l-6.293-6.293a1 1 0 0 1 0-1.414l6.293-6.293a1 1 0 0 1 1.414 1.414Z"/></svg>
<span>上一级</span>
</a>
<input name="filter" id="s" type="search" placeholder="输入关键字筛选文件…" aria-label="搜索文件">
<a class="home-btn" href="/" aria-label="返回根目录" title="返回根目录">
<svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path fill="currentColor" d="M12 3.172 2.808 11.02a1 1 0 0 0 .65 1.758H5v7.25a1 1 0 0 0 1 1h3.5a1 1 0 0 0 1-1v-4.25h3v4.25a1 1 0 0 0 1 1H18a1 1 0 0 0 1-1v-7.25h1.542a1 1 0 0 0 .65-1.758L12 3.172Z"/></svg>
<span>根目录</span>
</a>
</form>
</header>
<h2 class="dir-title">
<span class="dir-label">Directory</span>
<nav class="breadcrumb" id="dir-path" aria-label="当前路径">/</nav>
</h2>
<script>
(function () {
// 把当前 URL 路径渲染成可点击的面包屑,并与 "Directory" 显示在同一行
function renderBreadcrumb() {
var host = document.getElementById('dir-path');
if (!host) return;
// 本地 file:// 预览时无法拿到真实站点路径,直接显示根目录 "/"
var path;
if (location.protocol === 'file:') {
path = '/';
} else {
path = decodeURIComponent(location.pathname);
if (path.charAt(path.length - 1) !== '/') path += '/';
}
var parts = path.split('/').filter(function (p) { return p !== ''; });
host.innerHTML = '';
var root = document.createElement('a');
root.href = '/';
root.textContent = '/';
host.appendChild(root);
var acc = '';
parts.forEach(function (p, i) {
acc += '/' + p;
var sep = document.createElement('span');
sep.className = 'sep';
sep.textContent = (i === 0) ? ' ' : ' / ';
host.appendChild(sep);
var a = document.createElement('a');
a.href = acc + '/';
a.textContent = p;
host.appendChild(a);
});
}
renderBreadcrumb();
// 递归搜索:抓取整棵目录树(当前目录 + 子目录),构建扁平索引后按关键字过滤
function initSearch() {
var list = document.getElementById('list');
var input = document.getElementById('s');
if (!list || !input) return;
var baseDir = location.pathname;
if (baseDir.charAt(baseDir.length - 1) !== '/') baseDir += '/';
var MAX_DEPTH = 8;
var indexPromise = null;
var resultsEl = null;
function ensureResults() {
if (resultsEl) return resultsEl;
resultsEl = document.createElement('div');
resultsEl.id = 'search-results';
resultsEl.hidden = true;
list.parentNode.insertBefore(resultsEl, list.nextSibling);
return resultsEl;
}
// 从抓取到的目录页 HTML 中提取行,递归收集;dir 必须是带结尾 "/" 的绝对路径
function crawl(dir, depth, results, seen) {
if (depth > MAX_DEPTH || seen[dir]) return Promise.resolve();
seen[dir] = true;
return fetch(dir, { credentials: 'same-origin' })
.then(function (r) { return r.ok ? r.text() : ''; })
.then(function (html) {
if (!html) return;
var doc = new DOMParser().parseFromString(html, 'text/html');
var trs = doc.querySelectorAll('table#list tbody tr');
var tasks = [];
Array.prototype.forEach.call(trs, function (tr) {
var a = tr.querySelector('td a');
if (!a) return;
var name = (a.textContent || '').trim();
var href = a.getAttribute('href');
if (!href || href === '../' || name === 'Parent Directory') return;
var isDir = /\/$/.test(href);
var full = href.charAt(0) === '/' ? href : dir + href;
// 提取 Size / Last modified(与表头列保持一致)
var cells = tr.querySelectorAll('td');
var size = cells.length > 1 ? (cells[1].textContent || '').trim() : '-';
var date = cells.length > 2 ? (cells[2].textContent || '').trim() : '-';
results.push({ name: name, full: full, isDir: isDir, size: size, date: date });
if (isDir) tasks.push(crawl(full, depth + 1, results, seen));
});
return Promise.all(tasks);
})
.catch(function () { /* 忽略不可访问的目录 */ });
}
// 本地 file:// 预览用的模拟目录树(含子目录里的文件)
function demoIndex() {
return Promise.resolve([
{ name: 'ubuntu-24.04.iso', full: '/ubuntu-24.04.iso', isDir: false, size: '5.1 GiB', date: '2026-01-12 09:24' },
{ name: 'docs', full: '/docs/', isDir: true, size: '-', date: '2026-03-04 18:02' },
{ name: 'readme.txt', full: '/docs/readme.txt', isDir: false, size: '12 KiB', date: '2026-03-10 10:00' },
{ name: 'guide.pdf', full: '/docs/guide.pdf', isDir: false, size: '1.4 MiB', date: '2026-03-12 14:30' },
{ name: 'backup.tar.gz', full: '/backup.tar.gz', isDir: false, size: '842 MiB', date: '2026-05-21 22:47' },
{ name: 'README.md', full: '/README.md', isDir: false, size: '3.3 KiB', date: '2026-06-30 11:15' },
{ name: 'scripts', full: '/scripts/', isDir: true, size: '-', date: '2026-07-01 07:53' },
{ name: 'deploy.sh', full: '/scripts/deploy.sh', isDir: false, size: '3 KiB', date: '2026-07-02 08:11' },
{ name: 'setup.sh', full: '/scripts/setup.sh', isDir: false, size: '5 KiB', date: '2026-07-03 09:40' }
]);
}
// 构建索引(只抓一次,之后复用);file:// 预览用模拟数据
function buildIndex() {
if (indexPromise) return indexPromise;
if (location.protocol === 'file:') {
indexPromise = demoIndex();
} else {
var results = [];
var seen = {};
indexPromise = crawl(baseDir, 0, results, seen).then(function () { return results; });
}
return indexPromise;
}
function render(entries, q) {
var el = ensureResults();
if (!entries.length) {
el.innerHTML = '<p class="search-status">未找到匹配 “' + q + '” 的文件(已包含子目录)</p>';
return;
}
var rows = entries.map(function (e) {
var displayName = e.name + (e.isDir && !/[\/]$/.test(e.name) ? '/' : '');
return '<tr>' +
'<td><a href="' + escapeHtml(e.full) + '">' + escapeHtml(displayName) + '</a></td>' +
'<td>' + escapeHtml(e.size) + '</td>' +
'<td>' + escapeHtml(e.date) + '</td>' +
'<td> </td>' +
'</tr>';
}).join('');
el.innerHTML =
'<p class="search-status">找到 ' + entries.length + ' 个匹配项(含子目录)</p>' +
'<table class="results"><thead><tr><th>Name</th><th>Size</th><th>Last modified</th><th> </th></tr></thead>' +
'<tbody>' + rows + '</tbody></table>';
}
function escapeHtml(str) {
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
input.addEventListener('input', function () {
var q = input.value.trim().toLowerCase();
if (!q) {
list.style.display = '';
if (resultsEl) resultsEl.hidden = true;
return;
}
list.style.display = 'none';
var el = ensureResults();
el.hidden = false;
if (!indexPromise) el.innerHTML = '<p class="search-status">正在索引子目录…</p>';
buildIndex().then(function (entries) {
var matched = entries.filter(function (e) {
return e.name.toLowerCase().indexOf(q) !== -1;
});
render(matched, q);
});
});
}
// 移除 ngx-fancyindex 在表格前输出的裸文本路径(如 /bitwarden/chrome-plugins/
// CSS 无法隐藏没有标签包裹的文本节点,故用 JS 在 DOM 就绪后删除
function cleanupStrayText() {
var roots = [document.querySelector('.wrapper'), document.body];
roots.forEach(function (root) {
if (!root) return;
var nodes = root.childNodes;
for (var i = nodes.length - 1; i >= 0; i--) {
var n = nodes[i];
if (n.nodeType === 3 && n.textContent.trim() !== '') {
n.parentNode.removeChild(n);
}
}
});
}
function initUpBtn() {
var btn = document.getElementById('up-btn');
if (!btn) return;
var parent;
if (location.protocol === 'file:') {
parent = '#'; // 本地预览无真实目录层级,仅展示按钮
} else {
// 当前路径去掉末尾斜杠后求父目录
var path = location.pathname.replace(/\/+$/, '');
var idx = path.lastIndexOf('/');
parent = (idx <= 0) ? '/' : path.slice(0, idx + 1); // 如 /a/b/ -> /a/
}
btn.setAttribute('href', parent);
}
function onReady() {
initSearch();
initUpBtn();
cleanupStrayText();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', onReady);
} else {
onReady();
}
})();
</script>