Reddit Subreddit Top All Time Button (XPath Fixed)

Adds a button on subreddit pages to quickly navigate to the "Top - All Time" posts. Uses MutationObserver to wait for the target element.

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

You will need to install an extension such as Tampermonkey to install this script.

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Reddit Subreddit Top All Time Button (XPath Fixed)
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  Adds a button on subreddit pages to quickly navigate to the "Top - All Time" posts. Uses MutationObserver to wait for the target element.
// @author       Gemini (via Google)
// @match        https://www.reddit.com/r/*
// @match        https://new.reddit.com/r/*
// @match        https://old.reddit.com/r/*
// @grant        none
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Target XPath provided by the user (the container for the sort/view options)
    const TARGET_XPATH = '/html/body/shreddit-app/div[3]/div/div[2]/main/div[1]/shreddit-async-loader/div';
    const BUTTON_ID = 'top-all-time-button';

    /**
     * Finds an element using an XPath expression.
     * @param {string} path - The XPath expression.
     * @returns {Element | null} The found element or null.
     */
    function getElementByXpath(path) {
        return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
    }

    /**
     * Extracts the subreddit name from the current URL.
     * @returns {string | null} The subreddit name or null if not on a subreddit page.
     */
    function getSubredditName() {
        // Regex to capture the subreddit name, ensuring we are not on a /comments/ page.
        const path = window.location.pathname;
        if (path.includes('/comments/')) {
            return null;
        }
        const match = path.match(/\/r\/([^/]+)/);
        return match ? match[1] : null;
    }

    /**
     * Creates and injects the "Top All Time" button into the target container.
     * @param {Element} targetElement - The container where the button should be placed.
     */
    function createAndInjectButton(targetElement) {
        const subredditName = getSubredditName();
        if (!subredditName) return;

        // Check if the button already exists to prevent duplicates
        if (document.getElementById(BUTTON_ID)) {
             document.getElementById(BUTTON_ID).querySelector('a').href = `${window.location.origin}/r/${subredditName}/top/?t=all`;
             return;
        }

        const topAllTimeUrl = `${window.location.origin}/r/${subredditName}/top/?t=all`;

        // --- Create Button Elements ---
        const buttonWrapper = document.createElement('div');
        buttonWrapper.id = BUTTON_ID;
        buttonWrapper.className = 'subreddit-top-all-time-wrapper'; // Use a class for easier styling if needed

        const buttonLink = document.createElement('a');
        buttonLink.href = topAllTimeUrl;
        buttonLink.textContent = '🏆 Top All Time';

        // --- Styling for the New UI ---
        // This styling tries to mimic the look of the 'Best'/'New'/'Top' filter buttons
        buttonLink.style.cssText = `
            display: flex;
            align-items: center;
            height: 32px; /* Match filter button height */
            padding: 0 12px;
            background-color: #FF4500; /* Reddit Orange/Red */
            color: white !important;
            border-radius: 9999px; /* Pill shape */
            font-size: 14px;
            font-weight: 700;
            text-decoration: none !important;
            margin-left: 8px; /* Spacing from adjacent elements */
            transition: background-color 0.1s ease;
        `;

        buttonLink.onmouseover = function() {
            this.style.backgroundColor = '#E53E00'; // Darker red on hover
        };
        buttonLink.onmouseout = function() {
            this.style.backgroundColor = '#FF4500';
        };

        buttonWrapper.appendChild(buttonLink);

        // --- Injection ---
        // Insert the new button *before* the first element in the container (often the sort dropdown)
        if (targetElement.firstChild) {
            targetElement.insertBefore(buttonWrapper, targetElement.firstChild);
        } else {
            targetElement.appendChild(buttonWrapper);
        }
    }

    /**
     * Core function to find the target and run the injection.
     */
    function initializeButtonLogic() {
        const targetElement = getElementByXpath(TARGET_XPATH);

        if (targetElement) {
            // Found the element, now inject the button
            createAndInjectButton(targetElement);
            return true; // Indicate success
        }
        return false; // Indicate failure
    }

    // --- Main Execution Logic (The Watcher) ---

    // We need to continuously observe the body because:
    // 1. The script runs before <shreddit-app> is rendered.
    // 2. Reddit is an SPA (Single-Page Application) and navigating to a different subreddit won't cause a full page reload.

    let observer;

    const observerCallback = (mutations, obs) => {
        // Run the injection logic. If it succeeds, the element has been found.
        if (initializeButtonLogic()) {
            // Successfully injected the button on the initial load.
            // We can stop this specific observer, but need a new one for SPA navigation.
            if (observer) {
                observer.disconnect();
                // Start the URL watcher for SPA navigation
                startUrlWatcher();
            }
        }
    };

    // Use a robust observer to wait for the target element to be created.
    observer = new MutationObserver(observerCallback);

    // Start observing the main body for any changes (subtree is important for dynamic loading)
    observer.observe(document.body, { childList: true, subtree: true });

    // --- SPA Navigation Watcher (Handles subreddit-to-subreddit changes) ---
    function startUrlWatcher() {
        let lastUrl = window.location.href;
        const urlObserver = new MutationObserver(() => {
            const currentUrl = window.location.href;
            const subredditName = getSubredditName();
            // Only trigger if the URL actually changed AND we are on a subreddit page
            if (currentUrl !== lastUrl && subredditName) {
                lastUrl = currentUrl;
                // Give the elements a brief moment to load before re-injecting
                setTimeout(() => {
                    const targetElement = getElementByXpath(TARGET_XPATH);
                    if (targetElement) {
                        createAndInjectButton(targetElement);
                    }
                }, 300);
            }
        });
        // Watch a stable element (like the body) for changes indicative of a page update
        urlObserver.observe(document.body, { childList: true, subtree: true });
    }

    // Attempt the initial run immediately in case the elements were fast to load
    if (!initializeButtonLogic()) {
        // If the element wasn't immediately found, the MutationObserver will handle it.
    }
})();