Index of /kali/pool/main/g/gspiceui/
/* Nginx Fancyindex JavaScript - Seti UI Style */
(function() {
'use strict';
// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
init();
});
function init() {
addSearchBox();
addTitle();
addKeyboardShortcuts();
addSortingFeature();
fixTime();
enhanceAccessibility();
}
// Add search box
function addSearchBox() {
const h1 = document.querySelector('h1');
if (!h1) return;
const searchInput = document.createElement('input');
searchInput.type = 'search';
searchInput.id = 'search';
searchInput.placeholder = 'Search files...';
// Insert search box after h1
h1.parentNode.insertBefore(searchInput, h1.nextSibling);
// Get all file rows
const table = document.querySelector('table');
if (!table) return;
const tbody = table.querySelector('tbody');
if (!tbody) return;
const rows = Array.from(tbody.querySelectorAll('tr'));
// Find parent directory row
const parentRow = rows.find(row => {
const link = row.querySelector('a');
return link && link.getAttribute('href') === '../';
});
const fileRows = rows.filter(row => row !== parentRow);
// Search functionality
searchInput.addEventListener('input', function() {
const query = this.value.toLowerCase();
let visibleCount = 0;
fileRows.forEach(row => {
const link = row.querySelector('a');
if (!link) return;
const filename = link.textContent.toLowerCase();
const shouldShow = query === '' || filename.includes(query);
row.style.display = shouldShow ? '' : 'none';
if (shouldShow) visibleCount++;
});
// Show empty state if no results
updateEmptyState(query, visibleCount);
});
}
// Update empty state
function updateEmptyState(query, visibleCount) {
const existingEmpty = document.querySelector('.empty-state');
if (existingEmpty) {
existingEmpty.remove();
}
if (query === '' || visibleCount > 0) return;
const emptyState = document.createElement('div');
emptyState.className = 'empty-state';
emptyState.innerHTML = 'No files found matching "' + escapeHtml(query) + '"
Try a different search term
';
const table = document.querySelector('table');
if (table) {
table.parentNode.insertBefore(emptyState, table.nextSibling);
}
}
// Escape HTML
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Add dynamic title based on path
function addTitle() {
let path = window.location.pathname.replace(/\/$/g, '');
let titleText;
if (path) {
const parts = path.split('/');
path = parts[parts.length - 1];
titleText = titleize(path).replace(/-|_/g, ' ');
} else {
titleText = window.location.host;
}
titleText = 'Index of ' + titleText;
const h1 = document.querySelector('h1');
if (h1) {
h1.textContent = titleText;
}
document.title = titleText;
}
// Titleize helper function
function titleize(str) {
return decodeURI(str).toLowerCase().replace(/(?:^|\s|-)\S/g, function(c) { return c.toUpperCase(); });
}
// Add keyboard shortcuts
function addKeyboardShortcuts() {
document.addEventListener('keydown', function(e) {
// Focus search with '/' key
if (e.key === '/' && e.target !== document.getElementById('search')) {
e.preventDefault();
const search = document.getElementById('search');
if (search) {
search.focus();
}
return;
}
// Clear search with Escape
if (e.key === 'Escape') {
const search = document.getElementById('search');
if (search && search === document.activeElement) {
search.value = '';
search.dispatchEvent(new Event('input'));
search.blur();
}
return;
}
// Navigate with arrow keys
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
e.preventDefault();
navigateWithKeyboard(e.key === 'ArrowDown');
}
});
}
// Navigate files with keyboard
function navigateWithKeyboard(down) {
const tbody = document.querySelector('tbody');
if (!tbody) return;
const visibleRows = Array.from(tbody.querySelectorAll('tr'))
.filter(function(row) { return row.style.display !== 'none'; });
const currentFocused = document.querySelector('tbody tr a:focus');
let currentIndex = -1;
if (currentFocused) {
const currentRow = currentFocused.closest('tr');
currentIndex = visibleRows.indexOf(currentRow);
}
let nextIndex;
if (down) {
nextIndex = currentIndex + 1;
if (nextIndex >= visibleRows.length) nextIndex = 0;
} else {
nextIndex = currentIndex - 1;
if (nextIndex < 0) nextIndex = visibleRows.length - 1;
}
if (nextIndex >= 0 && nextIndex < visibleRows.length) {
const nextLink = visibleRows[nextIndex].querySelector('a');
if (nextLink) {
nextLink.focus();
}
}
}
// Add sorting feature to table headers
function addSortingFeature() {
const thead = document.querySelector('thead');
if (!thead) return;
const headers = thead.querySelectorAll('th');
headers.forEach(function(header, index) {
header.style.cursor = 'pointer';
header.addEventListener('click', function() { sortTable(index); });
});
}
// Sort table by column
function sortTable(columnIndex) {
const table = document.querySelector('table');
const tbody = table.querySelector('tbody');
if (!tbody) return;
const rows = Array.from(tbody.querySelectorAll('tr'));
// Separate parent directory row
const parentRow = rows.find(function(row) {
const link = row.querySelector('a');
return link && link.getAttribute('href') === '../';
});
const fileRows = rows.filter(function(row) { return row !== parentRow; });
// Determine sort direction
const header = document.querySelectorAll('thead th')[columnIndex];
const isAscending = !header.classList.contains('sort-desc');
// Clear previous sort indicators
document.querySelectorAll('thead th').forEach(function(th) {
th.classList.remove('sort-asc', 'sort-desc');
});
header.classList.add(isAscending ? 'sort-asc' : 'sort-desc');
// Sort rows
fileRows.sort(function(a, b) {
let aValue, bValue;
if (columnIndex === 0) {
// Sort by name (first column)
const aLink = a.querySelector('a');
const bLink = b.querySelector('a');
aValue = aLink ? aLink.textContent.trim() : '';
bValue = bLink ? bLink.textContent.trim() : '';
// Directories first
const aIsDir = aLink && aLink.getAttribute('href').endsWith('/');
const bIsDir = bLink && bLink.getAttribute('href').endsWith('/');
if (aIsDir && !bIsDir) return isAscending ? -1 : 1;
if (!aIsDir && bIsDir) return isAscending ? 1 : -1;
} else {
// Sort by other columns
const aCell = a.children[columnIndex];
const bCell = b.children[columnIndex];
aValue = aCell ? aCell.textContent.trim() : '';
bValue = bCell ? bCell.textContent.trim() : '';
// Handle empty values
if (aValue === '-' || aValue === '') aValue = isAscending ? 'zzz' : '';
if (bValue === '-' || bValue === '') bValue = isAscending ? 'zzz' : '';
// Special handling for size column (usually column 2)
if (columnIndex === 2) {
aValue = parseSizeToBytes(aValue);
bValue = parseSizeToBytes(bValue);
}
// Special handling for date column (usually column 1)
if (columnIndex === 1) {
const aDate = new Date(aValue);
const bDate = new Date(bValue);
aValue = isNaN(aDate.getTime()) ? 0 : aDate.getTime();
bValue = isNaN(bDate.getTime()) ? 0 : bDate.getTime();
}
}
if (typeof aValue === 'string' && typeof bValue === 'string') {
return isAscending
? aValue.localeCompare(bValue)
: bValue.localeCompare(aValue);
}
if (aValue < bValue) return isAscending ? -1 : 1;
if (aValue > bValue) return isAscending ? 1 : -1;
return 0;
});
// Rebuild tbody
tbody.innerHTML = '';
if (parentRow) tbody.appendChild(parentRow);
fileRows.forEach(function(row) { tbody.appendChild(row); });
}
// Parse size string to bytes for sorting
function parseSizeToBytes(sizeStr) {
if (!sizeStr || sizeStr === '-' || sizeStr === '') return 0;
const matches = sizeStr.match(/^(\d+(?:\.\d+)?)\s*([KMGT]?)B?$/i);
if (!matches) return 0;
const size = parseFloat(matches[1]);
const unit = matches[2].toUpperCase();
const multipliers = {
'': 1,
'K': 1024,
'M': 1024 * 1024,
'G': 1024 * 1024 * 1024,
'T': 1024 * 1024 * 1024 * 1024
};
return size * (multipliers[unit] || 1);
}
})();