// @author rubenwardy // @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3-or-Later "use strict"; function updateOrder() { const elements = [...document.querySelector("#package_list").children]; const ids = elements .filter(x => !x.classList.contains("d-none")) .map(x => x.dataset.id?.trim()) .filter(x => x); document.querySelector("input[name='order']").value = ids.join(","); } function removePackage(card) { const message = document.getElementById("confirm_delete").innerText.trim(); const title = card.querySelector("h5 a").innerText.trim(); if (!confirm(message.replace("{title}", title))) { return; } card.querySelector("input[name^=package_removed]").value = "1"; card.classList.add("d-none"); onPackageQueryUpdate(); updateOrder(); } function restorePackage(id) { const idElement = document.querySelector(`[value='${id}']`); if (!idElement) { return false; } const card = idElement.parentNode.parentNode.parentNode.parentNode; console.assert(card.classList.contains("card")); card.classList.remove("d-none"); card.querySelector("input[name^=package_removed]").value = "0"; card.scrollIntoView(); onPackageQueryUpdate(); updateOrder(); return true; } function getAddedPackages() { const ids = document.querySelectorAll("#package_list > article:not(.d-none) input[name^=package_ids]"); return [...ids].map(x => x.value); } function escapeHtml(unsafe) { return unsafe .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function addPackage(pkg) { document.getElementById("add_package").value = ""; document.getElementById("add_package_results").innerHTML = ""; const id = `${pkg.author}/${pkg.name}`; if (restorePackage(id)) { return; } const nextId = document.querySelectorAll("input[name^=package_ids-]").length; const url = `/packages/${id}/`; const temp = document.createElement("div"); temp.innerHTML = `
${escapeHtml(pkg.title)} by ${escapeHtml(pkg.author)}

${escapeHtml(pkg.short_description)}

You can replace the description with your own
`; const card = temp.children[0]; document.getElementById("package_list").appendChild(card); card.scrollIntoView(); const button = card.querySelector(".btn-danger"); button.addEventListener("click", () => removePackage(card)); updateOrder(); } function updateResults(packages) { const results = document.getElementById("add_package_results"); results.innerHTML = ""; document.getElementById("add_package_empty").style.display = packages.length === 0 ? "block" : "none"; const alreadyAdded = getAddedPackages(); packages.slice(0, 5).forEach(pkg => { const result = document.createElement("a"); result.classList.add("list-group-item"); result.classList.add("list-group-item-action"); result.innerText = `${pkg.title} by ${pkg.author}`; if (alreadyAdded.includes(`${pkg.author}/${pkg.name}`)) { result.classList.add("active"); result.innerHTML = "" + result.innerHTML; } result.addEventListener("click", () => addPackage(pkg)); results.appendChild(result); }); } let currentRequestId; async function fetchPackagesAndUpdateResults(query) { const requestId = Math.random() * 1000000; currentRequestId = requestId; if (query === "") { updateResults([]); return; } const url = new URL("/api/packages/", window.location.origin); url.searchParams.set("q", query); const resp = await fetch(url.toString()); if (!resp.ok) { return; } const packages = await resp.json(); if (currentRequestId !== requestId) { return; } updateResults(packages); } let timeoutHandle; function onPackageQueryUpdate() { const query = document.getElementById("add_package").value.trim(); if (timeoutHandle) { clearTimeout(timeoutHandle); } timeoutHandle = setTimeout( () => fetchPackagesAndUpdateResults(query).catch(console.error), 200); } window.addEventListener("load", () => { document.querySelectorAll(".remove-package").forEach(button => { const card = button.parentNode.parentNode.parentNode.parentNode; console.assert(card.classList.contains("card")); const field = card.querySelector("input[name^=package_removed]"); // Reloading/validation errors will cause this to be 1 at load if (field && field.value === "1") { card.classList.add("d-none"); } else { button.addEventListener("click", () => removePackage(card)); } }); const addPackageQuery = document.getElementById("add_package"); addPackageQuery.value = ""; addPackageQuery.classList.remove("d-none"); addPackageQuery.addEventListener("input", onPackageQueryUpdate); addPackageQuery.addEventListener('keydown',(e)=>{ if (e.key === "Enter") { onPackageQueryUpdate(); e.preventDefault(); } }) updateOrder(); $(".sortable").sortable({ update: updateOrder, }); });