{ const ta = document.getElementById("babycode-content"); for (let button of document.querySelectorAll(".reply-button")) { button.addEventListener("click", (e) => { ta.value += button.value; ta.scrollIntoView() ta.focus(); }) } function supportsPopover() { return Object.hasOwn(HTMLElement.prototype, "popover"); } if (supportsPopover()){ let quotedPostContainer = null; function isQuoteSelectionValid() { const selection = document.getSelection(); if (!selection || selection.rangeCount === 0 || selection.isCollapsed) { return false; } const range = selection.getRangeAt(0); const commonAncestor = range.commonAncestorContainer; const ancestorElement = commonAncestor.nodeType === Node.TEXT_NODE ? commonAncestor.parentNode : commonAncestor; const container = ancestorElement.closest(".post-inner"); if (!container) { return false; } const success = container.contains(ancestorElement); if (success) { quotedPostContainer = container; } return success; } let quotePopover = null; let isSelecting = false; document.addEventListener("mousedown", () => { isSelecting = true; }) document.addEventListener("mouseup", () => { isSelecting = false; handlePossibleSelection(); }) document.addEventListener("keyup", (e) => { if (e.shiftKey && (e.key.startsWith('Arrow') || e.key === 'Home' || e.key === 'End')) { handlePossibleSelection(); } }) function handlePossibleSelection() { setTimeout(() => { const valid = isQuoteSelectionValid(); if (isSelecting || !valid) { removePopover(); return; } const selection = document.getSelection(); const selectionStr = selection.toString().trim(); if (selection.isCollapsed || selectionStr === "") { removePopover(); return; } showPopover(); }, 50) } function removePopover() { quotePopover?.hidePopover(); } function createPopover() { quotePopover = document.createElement("div"); quotePopover.popover = "auto"; quotePopover.className = "quote-popover"; const quoteButton = document.createElement("button"); quoteButton.textContent = "Quote fragment" quoteButton.className = "reduced" quotePopover.appendChild(quoteButton); document.body.appendChild(quotePopover); return quoteButton; } function showPopover() { if (!quotePopover) { const quoteButton = createPopover(); quoteButton.addEventListener("click", () => { console.log("Quoting:", document.getSelection().toString()); const postPermalink = quotedPostContainer.dataset.postPermalink; const authorUsername = quotedPostContainer.dataset.authorUsername; console.log(postPermalink, authorUsername); if (ta.value.trim() !== "") { ta.value += "\n" } ta.value += `[url=${postPermalink}]${authorUsername} said:[/url]\n[quote]${document.getSelection().toString()}[/quote]\n`; ta.scrollIntoView() ta.focus(); document.getSelection().empty(); removePopover(); }) } const range = document.getSelection().getRangeAt(0); const rect = range.getBoundingClientRect(); const scrollY = window.scrollY || window.pageYOffset; quotePopover.style.setProperty("top", `${rect.top + scrollY - 55}px`) quotePopover.style.setProperty("left", `${rect.left + rect.width/2}px`) if (!quotePopover.matches(':popover-open')) { quotePopover.showPopover(); } } } const deleteDialog = document.getElementById("delete-dialog"); const deleteDialogCloseButton = document.getElementById("post-delete-dialog-close"); let deletionTargetPostContainer; function closeDeleteDialog() { deletionTargetPostContainer.style.removeProperty("background-color"); deleteDialog.close(); } deleteDialogCloseButton.addEventListener("click", (e) => { closeDeleteDialog(); }) deleteDialog.addEventListener("click", (e) => { if (e.target === deleteDialog) { closeDeleteDialog(); } }) for (let button of document.querySelectorAll(".post-delete-button")) { button.addEventListener("click", (e) => { deleteDialog.showModal(); const postId = button.value; deletionTargetPostContainer = document.getElementById("post-" + postId).querySelector(".post-content-container"); deletionTargetPostContainer.style.setProperty("background-color", "#fff"); const form = document.getElementById("post-delete-form"); form.action = `/post/${postId}/delete` }) } const threadEndpoint = document.getElementById("thread-subscribe-endpoint").value; let now = Math.floor(new Date() / 1000); function hideNotification() { const notification = document.getElementById('new-post-notification'); notification.classList.add('hidden'); } function showNewPostNotification(url) { const notification = document.getElementById("new-post-notification"); notification.classList.remove("hidden"); document.getElementById("dismiss-new-post-button").onclick = () => { now = Math.floor(new Date() / 1000); hideNotification(); tryFetchUpdate(); } document.getElementById("go-to-new-post-button").href = url; document.getElementById("unsub-new-post-button").onclick = () => { hideNotification(); } } function tryFetchUpdate() { if (!threadEndpoint) return; const body = JSON.stringify({'since': now}); fetch(threadEndpoint, {method: "POST", headers: {"Content-Type": "application/json"}, body: body}) .then(res => res.json()) .then(json => { if (json.status === "none") { setTimeout(tryFetchUpdate, 5000); } else if (json.status === "new_post") { showNewPostNotification(json.url); } }) .catch(error => console.log(error)) } tryFetchUpdate(); }