| const webroot = document.querySelector("meta[name='webroot']").content; |
| const fileInput = document.querySelector('input[type="file"]'); |
| const dropZone = document.getElementById("dropzone"); |
| const convertButton = document.querySelector("input[type='submit']"); |
| const fileNames = []; |
| let fileType; |
| let pendingFiles = 0; |
| let formatSelected = false; |
|
|
| dropZone.addEventListener("dragover", (e) => { |
| e.preventDefault(); |
| dropZone.classList.add("dragover"); |
| }); |
|
|
| dropZone.addEventListener("dragleave", () => { |
| dropZone.classList.remove("dragover"); |
| }); |
|
|
| dropZone.addEventListener("drop", (e) => { |
| e.preventDefault(); |
| dropZone.classList.remove("dragover"); |
|
|
| const files = e.dataTransfer.files; |
|
|
| if (files.length === 0) { |
| console.warn("No files dropped — likely a URL or unsupported source."); |
| return; |
| } |
|
|
| for (const file of files) { |
| console.log("Handling dropped file:", file.name); |
| handleFile(file); |
| } |
| }); |
|
|
| |
| function handleFile(file) { |
| const fileList = document.querySelector("#file-list"); |
|
|
| const row = document.createElement("tr"); |
| row.innerHTML = ` |
| <td>${file.name}</td> |
| <td><progress max="100" class="inline-block h-2 appearance-none overflow-hidden rounded-full border-0 bg-neutral-700 bg-none text-accent-500 accent-accent-500 [&::-moz-progress-bar]:bg-accent-500 [&::-webkit-progress-value]:rounded-full [&::-webkit-progress-value]:[background:none] [&[value]::-webkit-progress-value]:bg-accent-500 [&[value]::-webkit-progress-value]:transition-[inline-size]"></progress></td> |
| <td>${(file.size / 1024).toFixed(2)} kB</td> |
| <td><a onclick="deleteRow(this)">Remove</a></td> |
| `; |
|
|
| if (!fileType) { |
| fileType = file.name.split(".").pop(); |
| fileInput.setAttribute("accept", `.${fileType}`); |
| setTitle(); |
|
|
| fetch(`${webroot}/conversions`, { |
| method: "POST", |
| body: JSON.stringify({ fileType }), |
| headers: { "Content-Type": "application/json" }, |
| }) |
| .then((res) => res.text()) |
| .then((html) => { |
| selectContainer.innerHTML = html; |
| updateSearchBar(); |
| }) |
| .catch(console.error); |
| } |
|
|
| fileList.appendChild(row); |
| file.htmlRow = row; |
| fileNames.push(file.name); |
| uploadFile(file); |
| } |
|
|
| const selectContainer = document.querySelector("form .select_container"); |
|
|
| const updateSearchBar = () => { |
| const convertToInput = document.querySelector("input[name='convert_to_search']"); |
| const convertToPopup = document.querySelector(".convert_to_popup"); |
| const convertToGroupElements = document.querySelectorAll(".convert_to_group"); |
| const convertToGroups = {}; |
| const convertToElement = document.querySelector("select[name='convert_to']"); |
|
|
| const showMatching = (search) => { |
| for (const [targets, groupElement] of Object.values(convertToGroups)) { |
| let matchingTargetsFound = 0; |
| for (const target of targets) { |
| if (target.dataset.target.includes(search)) { |
| matchingTargetsFound++; |
| target.classList.remove("hidden"); |
| target.classList.add("flex"); |
| } else { |
| target.classList.add("hidden"); |
| target.classList.remove("flex"); |
| } |
| } |
|
|
| if (matchingTargetsFound === 0) { |
| groupElement.classList.add("hidden"); |
| groupElement.classList.remove("flex"); |
| } else { |
| groupElement.classList.remove("hidden"); |
| groupElement.classList.add("flex"); |
| } |
| } |
| }; |
|
|
| for (const groupElement of convertToGroupElements) { |
| const groupName = groupElement.dataset.converter; |
|
|
| const targetElements = groupElement.querySelectorAll(".target"); |
| const targets = Array.from(targetElements); |
|
|
| for (const target of targets) { |
| target.onmousedown = () => { |
| convertToElement.value = target.dataset.value; |
| convertToInput.value = `${target.dataset.target} using ${target.dataset.converter}`; |
| formatSelected = true; |
| if (pendingFiles === 0 && fileNames.length > 0) { |
| convertButton.disabled = false; |
| } |
| showMatching(""); |
| }; |
| } |
|
|
| convertToGroups[groupName] = [targets, groupElement]; |
| } |
|
|
| convertToInput.addEventListener("input", (e) => { |
| showMatching(e.target.value.toLowerCase()); |
| }); |
|
|
| convertToInput.addEventListener("search", () => { |
| |
| convertButton.disabled = true; |
| formatSelected = false; |
| }); |
|
|
| convertToInput.addEventListener("blur", (e) => { |
| |
| |
| if (e?.relatedTarget?.classList?.contains("target")) { |
| convertToPopup.classList.add("hidden"); |
| convertToPopup.classList.remove("flex"); |
| return; |
| } |
|
|
| convertToPopup.classList.add("hidden"); |
| convertToPopup.classList.remove("flex"); |
| }); |
|
|
| convertToInput.addEventListener("focus", () => { |
| convertToPopup.classList.remove("hidden"); |
| convertToPopup.classList.add("flex"); |
| }); |
| }; |
|
|
| |
| fileInput.addEventListener("change", (e) => { |
| const files = e.target.files; |
| for (const file of files) { |
| handleFile(file); |
| } |
| }); |
|
|
| const setTitle = () => { |
| const title = document.querySelector("h1"); |
| title.textContent = `Convert ${fileType ? `.${fileType}` : ""}`; |
| }; |
|
|
| |
| |
| const deleteRow = (target) => { |
| const filename = target.parentElement.parentElement.children[0].textContent; |
| const row = target.parentElement.parentElement; |
| row.remove(); |
|
|
| |
| const index = fileNames.indexOf(filename); |
| fileNames.splice(index, 1); |
|
|
| |
| fileInput.value = ""; |
|
|
| |
| if (fileNames.length === 0) { |
| fileType = null; |
| fileInput.removeAttribute("accept"); |
| convertButton.disabled = true; |
| setTitle(); |
| } |
|
|
| fetch(`${webroot}/delete`, { |
| method: "POST", |
| body: JSON.stringify({ filename: filename }), |
| headers: { |
| "Content-Type": "application/json", |
| }, |
| }).catch((err) => console.log(err)); |
| }; |
|
|
| const uploadFile = (file) => { |
| convertButton.disabled = true; |
| convertButton.textContent = "Uploading..."; |
| pendingFiles += 1; |
|
|
| const formData = new FormData(); |
| formData.append("file", file, file.name); |
|
|
| let xhr = new XMLHttpRequest(); |
|
|
| xhr.open("POST", `${webroot}/upload`, true); |
|
|
| xhr.onload = () => { |
| let data = JSON.parse(xhr.responseText); |
|
|
| pendingFiles -= 1; |
| if (pendingFiles === 0) { |
| if (formatSelected) { |
| convertButton.disabled = false; |
| } |
| convertButton.textContent = "Convert"; |
| } |
|
|
| |
| let progressbar = file.htmlRow.getElementsByTagName("progress"); |
| progressbar[0].parentElement.remove(); |
| console.log(data); |
| }; |
|
|
| xhr.upload.onprogress = (e) => { |
| let sent = e.loaded; |
| let total = e.total; |
| console.log(`upload progress (${file.name}):`, (100 * sent) / total); |
|
|
| let progressbar = file.htmlRow.getElementsByTagName("progress"); |
| progressbar[0].value = (100 * sent) / total; |
| }; |
|
|
| xhr.onerror = (e) => { |
| console.log(e); |
| }; |
|
|
| xhr.send(formData); |
| }; |
|
|
| const formConvert = document.querySelector(`form[action='${webroot}/convert']`); |
|
|
| formConvert.addEventListener("submit", () => { |
| const hiddenInput = document.querySelector("input[name='file_names']"); |
| hiddenInput.value = JSON.stringify(fileNames); |
| }); |
|
|
| updateSearchBar(); |
|
|