diff --git a/extension/content.js b/extension/content.js index f351d9b..aacfd94 100644 --- a/extension/content.js +++ b/extension/content.js @@ -299,6 +299,80 @@ function getDirectActionAnchorInPost(postElement, buttonBar) { return getTrackerInsertionAnchorInPost(postElement, buttonBar); } +function isNodeAfter(node, referenceNode) { + if (!node || !referenceNode || node === referenceNode) { + return false; + } + return Boolean(referenceNode.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_FOLLOWING); +} + +function getTopLevelAnchorWithinPost(postElement, element) { + if (!postElement || !element || !postElement.contains(element)) { + return null; + } + + let anchor = element; + while (anchor.parentElement && anchor.parentElement !== postElement) { + anchor = anchor.parentElement; + } + + return anchor && anchor.parentElement === postElement ? anchor : null; +} + +function findCommentSortAnchorInPost(postElement, actionAnchor = null) { + if (!postElement) { + return null; + } + + const keywords = ['relevantes zuerst', 'most relevant', 'neueste', 'newest']; + const selectorCandidates = postElement.querySelectorAll([ + '[aria-label*="relevantes zuerst" i]', + '[aria-label*="most relevant" i]', + '[aria-label*="neueste" i]', + '[aria-label*="newest" i]' + ].join(', ')); + + const candidates = new Set(selectorCandidates); + postElement.querySelectorAll('[role="button"], button').forEach((node) => { + const text = (node.textContent || '').trim().toLowerCase(); + if (text && text.length <= 80 && keywords.some((keyword) => text.includes(keyword))) { + candidates.add(node); + } + }); + + const anchors = []; + candidates.forEach((candidate) => { + if (!candidate || !isElementVisible(candidate) || candidate.closest('.fb-tracker-ui')) { + return; + } + + let anchor = getTopLevelAnchorWithinPost(postElement, candidate) || candidate; + if (anchor === actionAnchor) { + anchor = candidate; + } + if (!anchor || !anchor.parentElement) { + return; + } + if (actionAnchor && anchor !== actionAnchor && !isNodeAfter(anchor, actionAnchor)) { + return; + } + + anchors.push(anchor); + }); + + if (!anchors.length) { + return null; + } + + anchors.sort((a, b) => { + if (a === b) { + return 0; + } + return a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1; + }); + return anchors[0]; +} + const AI_CREDENTIAL_CACHE_TTL = 60 * 1000; // 1 minute cache const aiCredentialCache = { data: null, @@ -3233,8 +3307,6 @@ async function createTrackerUI(postElement, buttonBar, postNum = '?', options = row-gap: 6px; width: 100%; box-sizing: border-box; - position: relative; - z-index: 2; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-size: 13px; `; @@ -3848,11 +3920,18 @@ async function createTrackerUI(postElement, buttonBar, postNum = '?', options = } const directActionAnchor = getDirectActionAnchorInPost(postElement, buttonBar); + const inPostActionAnchor = getTopLevelAnchorWithinPost(postElement, directActionAnchor) || directActionAnchor; + const commentSortAnchor = findCommentSortAnchorInPost(postElement, inPostActionAnchor); + const preferredAnchor = commentSortAnchor || inPostActionAnchor; - // Strategy 1: Insert directly below the actions row - if (!inserted && directActionAnchor && directActionAnchor.parentElement) { - directActionAnchor.parentElement.insertBefore(container, directActionAnchor.nextSibling); - console.log('[FB Tracker] Post #' + postNum + ' - UI inserted directly below actions. ID: #' + container.id); + // Strategy 1: Insert below actions; if present, place below the sort row + if (!inserted && preferredAnchor && preferredAnchor.parentElement) { + preferredAnchor.parentElement.insertBefore(container, preferredAnchor.nextSibling); + if (commentSortAnchor) { + console.log('[FB Tracker] Post #' + postNum + ' - UI inserted below comment sort row. ID: #' + container.id); + } else { + console.log('[FB Tracker] Post #' + postNum + ' - UI inserted directly below actions. ID: #' + container.id); + } inserted = true; } // Strategy 2: Insert directly after the detected button bar if still inside the post container @@ -3900,8 +3979,11 @@ async function createTrackerUI(postElement, buttonBar, postNum = '?', options = observer.disconnect(); // Try to re-insert const currentDirectActionAnchor = getDirectActionAnchorInPost(postElement, buttonBar); - if (currentDirectActionAnchor && currentDirectActionAnchor.parentElement) { - currentDirectActionAnchor.parentElement.insertBefore(container, currentDirectActionAnchor.nextSibling); + const currentInPostActionAnchor = getTopLevelAnchorWithinPost(postElement, currentDirectActionAnchor) || currentDirectActionAnchor; + const currentCommentSortAnchor = findCommentSortAnchorInPost(postElement, currentInPostActionAnchor); + const currentPreferredAnchor = currentCommentSortAnchor || currentInPostActionAnchor; + if (currentPreferredAnchor && currentPreferredAnchor.parentElement) { + currentPreferredAnchor.parentElement.insertBefore(container, currentPreferredAnchor.nextSibling); } else if (buttonBar && buttonBar.parentElement && postElement.contains(buttonBar.parentElement)) { buttonBar.parentElement.insertBefore(container, buttonBar.nextSibling); } else if (buttonBar && buttonBar.parentElement && buttonBar.parentElement.parentElement && !postElement.contains(buttonBar.parentElement.parentElement)) {