/* global zip, document, URL, MouseEvent, AbortController, alert */ (() => { if (typeof TransformStream == "undefined") { const script = document.createElement("script"); script.src = "lib/web-streams-polyfill.min.js"; document.body.appendChild(script); } const model = (() => { return { getEntries(file, options) { return (new zip.ZipReader(new zip.BlobReader(file))).getEntries(options); }, async getURL(entry, options) { return URL.createObjectURL(await entry.getData(new zip.BlobWriter(), options)); } }; })(); (() => { const appContainer = document.getElementById("container"); const fileInput = document.getElementById("file-input"); const encodingInput = document.getElementById("encoding-input"); const fileInputButton = document.getElementById("file-input-button"); const passwordInput = document.getElementById("password-input"); let fileList = document.getElementById("file-list"); let entries; let selectedFile; passwordInput.onchange = async () => fileList.querySelectorAll("a[download]").forEach(anchor => anchor.download = ""); fileInput.onchange = selectFile; encodingInput.onchange = selectEncoding; appContainer.onclick = downloadFile; fileInputButton.onclick = () => fileInput.dispatchEvent(new MouseEvent("click")); async function downloadFile(event) { const target = event.target; let href = target.getAttribute("href"); if (target.dataset.entryIndex !== undefined && !target.download && !href) { target.removeAttribute("href"); event.preventDefault(); try { await download(entries[Number(target.dataset.entryIndex)], target.parentElement.parentElement, target); href = target.getAttribute("href"); } catch (error) { alert(error); } target.setAttribute("href", href); } } async function selectFile() { try { fileInputButton.disabled = true; encodingInput.disabled = true; selectedFile = fileInput.files[0]; await loadFiles(); } catch (error) { alert(error); } finally { fileInputButton.disabled = false; fileInput.value = ""; } } async function selectEncoding() { try { encodingInput.disabled = true; fileInputButton.disabled = true; await loadFiles(encodingInput.value); } catch (error) { alert(error); } finally { fileInputButton.disabled = false; } } async function loadFiles(filenameEncoding) { entries = await model.getEntries(selectedFile, { filenameEncoding }); if (entries && entries.length) { fileList.classList.remove("empty"); const filenamesUTF8 = Boolean(!entries.find(entry => !entry.filenameUTF8)); const encrypted = Boolean(entries.find(entry => entry.encrypted)); encodingInput.value = filenamesUTF8 ? "utf-8" : filenameEncoding || "cp437"; encodingInput.disabled = filenamesUTF8; passwordInput.value = ""; passwordInput.disabled = !encrypted; refreshList(); } } function refreshList() { const newFileList = fileList.cloneNode(); entries.forEach((entry, entryIndex) => { const li = document.createElement("li"); const filenameContainer = document.createElement("span"); const filename = document.createElement("a"); filenameContainer.classList.add("filename-container"); li.appendChild(filenameContainer); filename.classList.add("filename"); filename.dataset.entryIndex = entryIndex; filename.textContent = filename.title = entry.filename; filename.title = `${entry.filename}\n Last modification date: ${entry.lastModDate.toLocaleString()}`; if (!entry.directory) { filename.href = ""; filename.title += `\n Uncompressed size: ${entry.uncompressedSize.toLocaleString()} bytes`; } filenameContainer.appendChild(filename); newFileList.appendChild(li); }); fileList.replaceWith(newFileList); fileList = newFileList; } async function download(entry, li, a) { if (!li.classList.contains("busy")) { const unzipProgress = document.createElement("progress"); li.appendChild(unzipProgress); const controller = new AbortController(); const signal = controller.signal; const abortButton = document.createElement("button"); abortButton.onclick = () => controller.abort(); abortButton.textContent = "✖"; abortButton.title = "Abort"; li.querySelector(".filename-container").appendChild(abortButton); li.classList.add("busy"); li.onclick = event => event.preventDefault(); try { const blobURL = await model.getURL(entry, { password: passwordInput.value, onprogress: (index, max) => { unzipProgress.value = index; unzipProgress.max = max; }, signal }); a.href = blobURL; a.download = entry.filename; const clickEvent = new MouseEvent("click"); a.dispatchEvent(clickEvent); } catch (error) { if (!signal.reason || signal.reason.code != error.code) { throw error; } } finally { li.classList.remove("busy"); unzipProgress.remove(); abortButton.remove(); } } } })(); })();