123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- 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);
- }
- });
- // Extracted handleFile function for reusability in drag-and-drop and file input
- function handleFile(file) {
- const fileList = document.querySelector("#file-list");
- const row = document.createElement("tr");
- row.innerHTML = `
- <td>${file.name}</td>
- <td><progress max="100"></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", () => {
- // when the user clears the search bar using the 'x' button
- convertButton.disabled = true;
- formatSelected = false;
- });
- convertToInput.addEventListener("blur", (e) => {
- // Keep the popup open even when clicking on a target button
- // for a split second to allow the click to go through
- 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");
- });
- };
- // Add a 'change' event listener to the file input element
- 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}` : ""}`;
- };
- // Add a onclick for the delete button
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const deleteRow = (target) => {
- const filename = target.parentElement.parentElement.children[0].textContent;
- const row = target.parentElement.parentElement;
- row.remove();
- // remove from fileNames
- const index = fileNames.indexOf(filename);
- fileNames.splice(index, 1);
- // reset fileInput
- fileInput.value = "";
- // if fileNames is empty, reset fileType
- 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";
- }
- //Remove the progress bar when upload is done
- 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();
|