document.getElementById("instruction").classList.remove("hidden"); //// OVERLAY FOR VIEWING MEDIA FILES const overlay = document.getElementById("overlay"); const overlay_content = overlay.children[1]; const overlay_label = overlay.children[2]; const g_tbody = document.getElementsByTagName('tbody')[0]; let g_first_row = g_tbody.firstChild; let g_last_row = g_tbody.lastChild; const g_back_row = document.getElementsByTagName("tr")[1]; let g_scale = 1; let g_oc_offset = [0, 0]; let g_oc_translate = [0, 0]; let g_current_row = g_back_row; if (localStorage.getItem('audio_volume') == null) localStorage['audio_volume'] = 0.5; function overlayClose() { overlay_content.firstChild.remove(); overlay.style.visibility = "hidden"; g_scale = 1; g_oc_offset = g_oc_translate = [0, 0]; } overlay.addEventListener("mouseup", e => { if (e.target.tagName === "DIV" && e.button === 0) overlayClose(); }); overlay_content.addEventListener("wheel", e => { e.preventDefault(); g_scale = Math.min(Math.max(0.25, g_scale + (e.deltaY * -0.001)), 4); e.target.style.transform = `translate(${g_oc_translate[0]}px, ${g_oc_translate[1]}px) scale(${g_scale})`; }); overlay_content.addEventListener("mousedown", e => { if (e.buttons !== 1) return; e.preventDefault(); g_oc_offset = [e.offsetX, e.offsetY]; }); overlay_content.addEventListener("mousemove", e => { if (e.buttons !== 1) return; e.preventDefault(); g_oc_translate[0] = e.clientX - e.target.x - g_oc_offset[0]; g_oc_translate[1] = e.clientY - e.target.y - g_oc_offset[1]; e.target.style.transform = `translate(${g_oc_translate[0]}px, ${g_oc_translate[1]}px) scale(${g_scale})`; }); function determineMediaElement(path) { path = path.toLowerCase(); if (["webm", "mp4", "mov"].some(ext => path.endsWith(ext))) return "video"; else if (["mp3", "flac", "opus", "ogg", "m4a"].some(ext => path.endsWith(ext))) return "audio"; else if (["jpg", "jpeg", "gif", "png", "bmp", "webp"].some(ext => path.endsWith(ext))) return "img"; return undefined; } function overlaySet(pathname, media_type_element) { if (media_type_element === undefined) return false; if (overlay_content.children.length != 0) { g_oc_offset = g_oc_translate = [0, 0]; g_scale = 1; } const media_element = document.createElement(media_type_element); media_element.src = pathname; if (media_type_element !== "img") { media_element.autoplay = media_element.controls = true; media_element.addEventListener("volumechange", e => { localStorage['audio_volume'] = e.target.volume; }); media_element.volume = localStorage["audio_volume"]; media_element.addEventListener("ended", e => { if (overlay_autoplay.checked) b_next.click(); }); } overlay_label.textContent = decodeURI(pathname.substr(pathname.lastIndexOf("/") + 1)); overlay_content.replaceChildren(media_element); overlay.style.visibility = "visible"; return true; } function getSibling(isNext = true, upDown = false) { if (upDown && g_current_row == g_back_row) g_current_row = isNext ? g_first_row : g_last_row; else g_current_row = isNext ? ( (g_current_row.nextSibling === null) ? (upDown ? g_back_row : g_first_row) : g_current_row.nextSibling ) : ( (g_current_row.previousSibling === null) ? (upDown ? g_back_row : g_last_row) : g_current_row.previousSibling ); return g_current_row; } const [b_prev, b_next] = overlay.getElementsByTagName("button"); b_prev.addEventListener("click", e => { do { getSibling(false, false); } while (g_current_row.classList.contains("hidden") || !overlaySet(g_current_row.firstChild.firstChild.pathname, determineMediaElement(g_current_row.firstChild.firstChild.pathname))); g_current_row.firstChild.firstChild.focus(); e.preventDefault(); }); b_next.addEventListener("click", e => { do { getSibling(true, false); } while (g_current_row.classList.contains("hidden") || !overlaySet(g_current_row.firstChild.firstChild.pathname, determineMediaElement(g_current_row.firstChild.firstChild.pathname))); g_current_row.firstChild.firstChild.focus(); e.preventDefault(); }); Array.from(g_tbody.children) .forEach(v => { if (v.lastChild.innerHTML === "DIR") return; v.firstChild.firstChild.addEventListener("click", e => { g_current_row = e.target.parentNode.parentNode; if (overlaySet(e.target.pathname, determineMediaElement(e.target.pathname))) e.preventDefault(); }); }); overlay_autoplay = document.getElementsByName("autoplay")[0]; if (localStorage.getItem('autoplay') == null) localStorage['autoplay'] = overlay_autoplay.checked = false; overlay_autoplay.addEventListener("change", e => { localStorage['autoplay'] = e.target.checked; const media_element = overlay_content.firstChild if (e.target.checked && media_element !== undefined) if (media_element.tagName === "AUDIO" || media_element.tagName === "VIDEO") if (media_element.ended) b_next.click(); }); //// KEYBOARD HANDLING window.addEventListener("keydown", e => { if (e.isComposing) return; if (overlay.style.visibility === "hidden" || overlay.style.visibility === "") { switch (e.code) { case "Backspace": if (e.ctrlKey) window.location = "../"; break; case "Home": g_current_row = g_back_row; g_back_row.firstChild.firstChild.focus(); break; case "End": g_current_row = g_last_row; g_last_row.firstChild.firstChild.focus(); break; case "ArrowUp": case "ArrowLeft": e.preventDefault(); getSibling(false, true); g_current_row.firstChild.firstChild.focus(); break; case "ArrowDown": case "ArrowRight": e.preventDefault(); getSibling(true, true); g_current_row.firstChild.firstChild.focus(); break; } return; } switch (e.code) { case "ArrowUp": case "ArrowLeft": e.preventDefault(); b_prev.click(); break; case "ArrowDown": case "ArrowRight": e.preventDefault(); b_next.click(); break; case "Escape": overlayClose(); break; case "Space": e.preventDefault(); const el = overlay_content.firstChild; if (el.paused !== undefined) el.paused ? el.play() : el.pause(); } }); //// FILTERING document.getElementsByName("filter")[0].classList.remove("hidden"); function filter(sub) { const table = g_tbody.children; for (let j = 0; j < table.length; ++j) table[j].classList.toggle("hidden", !(sub === "" || table[j].firstChild.firstChild.innerText.toLowerCase().indexOf(sub) != -1)); } document.getElementsByName("filter")[0].addEventListener("input", e => filter(e.target.value.toLowerCase())); //// SORT BY COLUMN const units = {"B": 0, "KiB": 1, "MiB": 2, "GiB": 3, "TiB": 4}; const [thead_name, thead_date, thead_size] = document.getElementsByTagName('thead')[0] .children[0].children; let g_sort_reverse = false; thead_name.classList.toggle("clickable"); thead_name.addEventListener('click', e => { e.preventDefault(); sortTable((a,b) => { const a_name = a.children[0].textContent.toLowerCase(); const b_name = b.children[0].textContent.toLowerCase(); return a_name < b_name ? -1 : a_name > b_name ? 1 : 0; }, null, null, thead_name, [thead_date, thead_size]); g_first_row = g_tbody.firstChild; g_last_row = g_tbody.lastChild; }); thead_date.classList.toggle("clickable"); thead_date.addEventListener('click', e => { e.preventDefault(); sortTable((a,b) => { const a_date = new Date(a.children[1].textContent.slice(0, -4)); const b_date = new Date(b.children[1].textContent.slice(0, -4)); return a_date - b_date; }, null, null, thead_date, [thead_name, thead_size]); g_first_row = g_tbody.firstChild; g_last_row = g_tbody.lastChild; }); function sizeToBytes(size, unit) { if (units[unit] == 0) return size; for (let i = 0; i <= units[unit]; ++i) size *= 1024; return size; } thead_size.classList.toggle("clickable"); thead_size.addEventListener('click', e => { e.preventDefault(); sortTable( (a,b) => { if (a.textContent == "DIR") return 1; let [a_size, a_unit] = a.children[2].textContent.split(" "); let [b_size, b_unit] = b.children[2].textContent.split(" "); return sizeToBytes(+a_size, a_unit) - sizeToBytes(+b_size, b_unit); }, e => e.children[2].textContent == "DIR", e => e.children[2].textContent != "DIR", thead_size, [thead_name, thead_date]); g_first_row = g_tbody.firstChild; g_last_row = g_tbody.lastChild; }); function sortTable(compareFn, filterFn, filterNegFn, target, other) { let records = Array.from(g_tbody.children); let dirs = []; if (filterFn != null) { dirs = records.filter(filterFn); records = records.filter(filterNegFn); } records.sort(compareFn); g_tbody.textContent = ""; other.forEach(v => { v.classList.remove("sort-up"); v.classList.remove("sort-down"); }); if (filterFn != null) g_tbody.append(...dirs); if (g_sort_reverse) { g_tbody.append(...records.reverse()); target.classList.add("sort-up"); target.classList.remove("sort-down"); } else { g_tbody.append(...records); target.classList.add("sort-down"); target.classList.remove("sort-up"); } g_sort_reverse = !g_sort_reverse; }