butter-kanglam-ui/assets/js/butter-dir-listing.js

209 lines
7.7 KiB
JavaScript
Raw Normal View History

2024-06-05 13:20:56 -05:00
/**
* This script redraws the default lighttpd directory listing to our custom styles.
*
* Since the person inserting the files has physical access to the box and we serve
* their static sites directly from the USB, ***THIS SCRIPT DOES NOT ATTEMPT TO PREVENT
* INJECTION ATTACKS***. We just pop filenames and the like right into innerHTML.
*/
2024-06-05 12:39:00 -05:00
2024-06-12 21:02:43 -05:00
const supported_extensions = ["apk", "deb", "dmg", "pdf", "exe", "jpg", "png", "pptx", "ppt", "mp3"];
2024-06-05 14:35:54 -05:00
const usbRoot = "usb-butter/";
const inferredBaseURL = window.location.pathname.split("/" + usbRoot)[0] + "/";
2024-06-05 13:20:56 -05:00
// Don't display these in the dirlisting; they get first-class treatment
// on the homepage.
const foldersToHide = ["appstore", "osm-map-files"];
2024-06-05 12:39:00 -05:00
const getFolderDivHTML = (directory_name, number_of_items, href) => {
return `
2024-06-07 14:38:54 -05:00
<a class="filerow" href="${href}">
<div class="logo-filerow">
2024-06-05 14:26:24 -05:00
<img src="${inferredBaseURL}assets/images/directory.svg" alt="directory">
2024-06-05 12:39:00 -05:00
</div>
2024-06-07 14:38:54 -05:00
<div class="text-filerow">
2024-06-05 12:39:00 -05:00
<div class="upper-text">${directory_name}</div>
2024-06-12 21:33:48 -05:00
<div class="lower-text"><span id="${href}">${number_of_items}</span> items</div>
2024-06-05 12:39:00 -05:00
</div>
<div class="dl-block"></div>
2024-06-05 12:39:00 -05:00
</a>`;
2024-06-05 13:20:56 -05:00
};
const getFileDivHTML = (file_name, size, date, href) => {
icon = "ext-unknown.svg";
extension = file_name.split('.').pop();
if (supported_extensions.includes(extension)) {
2024-06-05 14:58:48 -05:00
icon = "ext-" + extension + ".svg";
2024-06-05 13:20:56 -05:00
}
2024-06-07 18:32:47 -05:00
// turn date from "2021-09-01 12:34:56" to "Sep 1, 2021"
date = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
2024-06-05 13:20:56 -05:00
return `
<div class="filerow">
<a href="${href}" class="logo-filerow">
2024-06-05 14:26:24 -05:00
<img src="${inferredBaseURL}assets/images/${icon}" alt="directory">
</a>
<a href="${href}" class="text-filerow">
2024-06-05 13:20:56 -05:00
<div class="upper-text">${file_name}</div>
<div class="lower-text">${size} | ${date}</div>
</a>
<a href="${href}" download="${file_name}">
<div class="dl-block">
<img src="${inferredBaseURL}assets/images/download-icon.svg" alt="download">
</div>
</a>
</div>
`;
2024-06-05 13:20:56 -05:00
};
2024-06-05 12:39:00 -05:00
2024-06-12 21:33:48 -05:00
const getFolderItemCount = async (folder_href) => {
async function populateSpan(response) {
if (!response.ok) {
console.error("Failed to fetch " + folder_href);
return;
}
const text = await response.text();
const doc = new DOMParser().parseFromString(text, 'text/html');
const { files, folders } = extractDirectoryListing(doc);
const items = files.length + folders.length;
const span = document.getElementById(folder_href);
span.textContent = items;
}
const response = fetch(folder_href).then(populateSpan);
}
2024-06-05 12:39:00 -05:00
// Function to extract directory listing information
2024-06-12 21:33:48 -05:00
function extractDirectoryListing(doc) {
2024-06-05 12:39:00 -05:00
// Get all rows in the table body
2024-06-12 21:33:48 -05:00
const rows = doc.querySelectorAll('tbody tr');
2024-06-05 12:39:00 -05:00
// Initialize arrays to hold file and folder information
const files = [];
const folders = [];
// Iterate over each row
rows.forEach(row => {
const nameCell = row.querySelector('td.n a');
const modifiedCell = row.querySelector('td.m');
const sizeCell = row.querySelector('td.s');
const typeCell = row.querySelector('td.t');
const name = nameCell.textContent;
const lastModified = modifiedCell.textContent.trim();
const size = sizeCell.textContent.trim();
const type = typeCell.textContent.trim();
const href = nameCell.getAttribute('href');
// Determine if it's a file or folder based on the class of the row or type
2024-06-05 13:20:56 -05:00
if (type === 'Directory') {
if (name !== '..' && !foldersToHide.includes(name)) {
2024-06-05 13:20:56 -05:00
folders.push({
name: name.replace('/', ''), // Remove the trailing slash
lastModified: lastModified,
size: size,
type: type,
href: href
});
}
2024-06-05 12:39:00 -05:00
} else {
2024-06-25 11:42:01 -05:00
if (!name.startsWith('.')) {
files.push({
name: name,
lastModified: lastModified,
size: size,
type: type,
href: href
});
}
2024-06-05 12:39:00 -05:00
}
});
return { files, folders };
}
2025-02-18 15:14:36 -06:00
function renderDirectory() {
2024-06-12 21:33:48 -05:00
const { files, folders } = extractDirectoryListing(window.document);
2024-06-05 14:16:00 -05:00
const listDiv = document.querySelector('div.list');
// Header with breadcrumbs
const h2Path = document.querySelector('h2').textContent;
const path = h2Path.replace('Index of ', '').replace(usbRoot, '');
const breadcrumbs = document.createElement("div");
breadcrumbs.classList.add('breadcrumbs');
let breadcrumbHTML = `
2024-06-12 21:50:55 -05:00
<a href="${inferredBaseURL}" class="crumb"><img class="home-icon" src="${inferredBaseURL}assets/images/home.svg" alt="home"></a>
2024-06-05 14:29:10 -05:00
<a href="${inferredBaseURL}${usbRoot}" class="crumb"><img class="path-next" src="${inferredBaseURL}assets/images/next.svg">Explore</a>
2024-06-05 14:16:00 -05:00
`;
2024-06-05 14:58:48 -05:00
const pathSteps = path.split('/');
let thisDirsName = "Explore";
2024-06-05 14:16:00 -05:00
let pathToHere = usbRoot;
pathSteps.forEach((step, index) => {
if (step !== "") {
2024-06-05 14:58:48 -05:00
pathToHere += step + '/';
thisDirsName = step;
2024-06-05 14:16:00 -05:00
breadcrumbHTML += `
2024-06-05 14:29:10 -05:00
<a href="${inferredBaseURL}${pathToHere}" class="crumb"><img class="path-next" src="${inferredBaseURL}assets/images/next.svg">${step}</a>`;
2024-06-05 14:16:00 -05:00
}
});
breadcrumbs.innerHTML = breadcrumbHTML;
listDiv.parentNode.insertBefore(breadcrumbs, listDiv);
// hr
const fullWithHR = document.createElement('hr');
fullWithHR.classList.add('full-width');
listDiv.parentNode.insertBefore(fullWithHR, listDiv);
2024-06-05 12:39:00 -05:00
2024-06-05 14:58:48 -05:00
// Secondary header
const secondaryHeader = document.createElement('div');
2024-06-07 16:07:53 -05:00
secondaryHeader.classList.add('breadcrumb-name');
2024-06-05 14:58:48 -05:00
secondaryHeader.innerHTML = `<a href="../"><img class="back" src="${inferredBaseURL}assets/images/next.svg"></a>`;
secondaryHeader.innerHTML += `${thisDirsName}`;
listDiv.parentNode.insertBefore(secondaryHeader, listDiv);
2024-06-05 14:16:00 -05:00
// Create a folder listing
2024-06-05 12:39:00 -05:00
const folderListing = document.createElement("div");
folderListing.classList.add('folder-list');
if (folders.length > 0) {
folderListing.innerHTML = '<h2>Folders</h2>';
}
2024-06-05 12:39:00 -05:00
// for each folder, add a div to folderListing
folders.forEach(folder => {
const folderDiv = document.createElement('div');
folderDiv.classList.add('folder-row');
2024-06-12 21:33:48 -05:00
folderDiv.innerHTML = getFolderDivHTML(folder.name, "?", folder.href);
2024-06-05 12:39:00 -05:00
folderListing.appendChild(folderDiv);
2024-06-12 21:33:48 -05:00
getFolderItemCount(folder.href);
2024-06-05 12:39:00 -05:00
});
// do the insertion
listDiv.parentNode.insertBefore(folderListing, listDiv);
2024-06-05 13:20:56 -05:00
if (folders.length > 0 && files.length > 0) {
listDiv.parentNode.insertBefore(document.createElement('hr'), listDiv);
}
// then for files
const fileListing = document.createElement("div");
fileListing.classList.add('file-list');
if (files.length > 0) {
fileListing.innerHTML = '<h2>Files</h2>';
}
2024-06-05 13:20:56 -05:00
// for each file, add a div to fileListing
files.forEach(file => {
const fileDiv = document.createElement('div');
fileDiv.classList.add('file-row');
fileDiv.innerHTML = getFileDivHTML(file.name, file.size, file.lastModified, file.href);
fileListing.appendChild(fileDiv);
});
// do the insertion
listDiv.parentNode.insertBefore(fileListing, listDiv);
2025-02-18 15:14:36 -06:00
}
window.onload = function () {
if (window.location.pathname.includes(usbRoot)) {
renderDirectory();
}
2024-06-05 12:39:00 -05:00
}