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와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==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.
    }
})();