diff --git a/public/s/js/admin.js b/public/s/js/admin.js index 347f41e..6548181 100644 --- a/public/s/js/admin.js +++ b/public/s/js/admin.js @@ -52,7 +52,7 @@ const delbutton = document.createElement("a"); delbutton.innerHTML = " ×"; - delbutton.href = "#"; + delbutton.href = "javascript:void(0)"; // Class for delegation delbutton.classList.add("admin-deltag"); @@ -70,18 +70,62 @@ if (!ctx) return; const { postid } = ctx; - if (!confirm("Do you really want to delete this tag?")) - return; - const tagname = e.target.parentElement.querySelector('a:first-child').innerText; + // Use normalized target logic if possible, or assume e.target (wrapped in listener) + // In delegation, we passed 'e'. e.target might be text node if not normalized in the handler call? + // In listener I normalized 'target', but passed 'e' to deleteEvent. + // So 'e.target' is still the raw event target. + // I should check nodeType here or use closest properly. - const res = await (await fetch("/api/v2/admin/" + postid + "/tags/" + encodeURIComponent(tagname), { - method: 'DELETE' - })).json(); + let target = e.target; + if (target.nodeType === 3) target = target.parentElement; - if (!res.success) - return alert("uff"); + const badge = target.closest('.badge'); + const tagLink = badge.querySelector('a:first-child'); + const tagname = tagLink.innerText; - renderTags(res.tags); + const modal = document.getElementById('delete-tag-modal'); + const nameEl = document.getElementById('delete-tag-name'); + const confirmBtn = document.getElementById('delete-tag-confirm'); + const cancelBtn = document.getElementById('delete-tag-cancel'); + + if (modal) { + nameEl.textContent = tagname; + confirmBtn.textContent = 'Delete'; + confirmBtn.disabled = false; + modal.style.display = 'flex'; + + const closeModal = () => { + modal.style.display = 'none'; + confirmBtn.onclick = null; + cancelBtn.onclick = null; + }; + + cancelBtn.onclick = closeModal; + + confirmBtn.onclick = async () => { + confirmBtn.textContent = 'Deleting...'; + confirmBtn.disabled = true; + try { + const res = await (await fetch("/api/v2/admin/" + postid + "/tags/" + encodeURIComponent(tagname), { + method: 'DELETE' + })).json(); + + if (!res.success) { + alert(res.msg || "uff"); + confirmBtn.textContent = 'Delete'; + confirmBtn.disabled = false; + } else { + renderTags(res.tags); + closeModal(); + } + } catch (err) { + console.error(err); + alert("Error deleting tag"); + confirmBtn.textContent = 'Delete'; + confirmBtn.disabled = false; + } + }; + } }; const addtagClick = (e) => { @@ -314,29 +358,24 @@ return false; }; + // Event Delegation // Event Delegation document.addEventListener("click", e => { - if (e.target.matches("a#a_addtag")) { + const target = e.target.nodeType === 3 ? e.target.parentElement : e.target; + + if (target.matches("a#a_addtag")) { addtagClick(e); - } else if (e.target.matches("a#a_toggle")) { + } else if (target.matches("a#a_toggle")) { toggleEvent(e); - } else if (e.target.closest("svg#a_favo")) { + } else if (target.closest("svg#a_favo")) { toggleFavEvent(e); - } else if (e.target.closest("svg#a_delete")) { + } else if (target.closest("svg#a_delete")) { deleteButtonEvent(e); - } else if (e.target.matches("#tags > .badge > a:first-child")) { + } else if (target.matches("#tags > .badge > a:first-child")) { editTagEvent(e); - } else if (e.target.innerText === " \u00d7" && e.target.closest(".badge")) { // check text " x" or similar for delete? - // Original was " ×" which is × (\u00d7). - // Logic in deleteEvent expects match. - // Let's rely on class or structure. - // In renderTags I added class 'admin-deltag'. - // Existing tags in HTML might NOT have this class unless rendered by JS? - // But existing tags are just HTML. We should match structure. - // selector: "#tags > .badge > a:last-child" - if (e.target.matches("#tags > .badge > a:last-child")) { - deleteEvent(e); - } + } else if (target.matches("#tags > .badge > a:last-child") || target.closest('.admin-deltag')) { + // Match by structure OR class (added in renderTags) + deleteEvent(e); } }); diff --git a/public/s/js/f0ck.js b/public/s/js/f0ck.js index 08c4cf1..2836b67 100644 --- a/public/s/js/f0ck.js +++ b/public/s/js/f0ck.js @@ -518,9 +518,10 @@ window.requestAnimFrame = (function () { // Intercept clicks document.addEventListener('click', (e) => { + const target = e.target.nodeType === 3 ? e.target.parentElement : e.target; // Check for thumbnail links on index page - const thumbnail = e.target.closest('.posts > a'); + const thumbnail = target.closest('.posts > a'); if (thumbnail && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey) { e.preventDefault(); // Thumbnails inherit context (e.g. from Tag Index) @@ -528,7 +529,7 @@ window.requestAnimFrame = (function () { return; } - const link = e.target.closest('#next, #prev, #random, #nav-random, .id-link, .nav-next, .nav-prev'); + const link = target.closest('#next, #prev, #random, #nav-random, .id-link, .nav-next, .nav-prev'); if (link && link.href && link.hostname === window.location.hostname && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey) { // Special check for random if (link.id === 'random' || link.id === 'nav-random') { @@ -579,7 +580,7 @@ window.requestAnimFrame = (function () { } else { loadItemAjax(link.href, true); } - } else if (e.target.closest('#togglebg')) { + } else if (target.closest('#togglebg')) { e.preventDefault(); background = !background; localStorage.setItem('background', background.toString()); @@ -604,9 +605,9 @@ window.requestAnimFrame = (function () { canvas.classList.add('fader-out'); } } - } else if (e.target.closest('.removetag')) { + } else if (target.closest('.removetag')) { e.preventDefault(); - const removeBtn = e.target.closest('.removetag'); + const removeBtn = target.closest('.removetag'); const tagLink = removeBtn.previousElementSibling; if (tagLink) { diff --git a/public/s/js/user.js b/public/s/js/user.js index 9ebd742..593c5fd 100644 --- a/public/s/js/user.js +++ b/public/s/js/user.js @@ -49,6 +49,17 @@ span.insertAdjacentElement("beforeend", a); + if (document.querySelector('a[href^="/admin"]')) { + const space = document.createTextNode('\u00A0'); //   + span.appendChild(space); + + const del = document.createElement("a"); + del.className = "removetag"; + del.href = "javascript:void(0)"; + del.innerHTML = "×"; + span.insertAdjacentElement("beforeend", del); + } + document.querySelector("#tags").insertAdjacentElement("afterbegin", span); }); }; @@ -209,11 +220,12 @@ // Event Delegation document.addEventListener("click", e => { - if (e.target.matches("a#a_addtag")) { + const target = e.target.nodeType === 3 ? e.target.parentElement : e.target; + if (target.matches("a#a_addtag")) { addtagClick(e); - } else if (e.target.matches("a#a_toggle")) { + } else if (target.matches("a#a_toggle")) { toggleEvent(e); - } else if (e.target.closest("svg#a_favo")) { + } else if (target.closest("svg#a_favo")) { toggleFavEvent(e); } });