Old Reddit AutoTools

Consolidated: Auto-Scroll, Auto-Collapse, Unblur, Over18 Continue, Thumbnail Auto-Expand/Collapse, Moderator Comment Collapse, Infinite Scroll with Page Numbers + Image Embedder

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

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

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

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

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

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.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==UserScript==
// @name         Old Reddit AutoTools
// @namespace    http://tampermonkey.net/
// @version      1.3.6
// @description  Consolidated: Auto-Scroll, Auto-Collapse, Unblur, Over18 Continue, Thumbnail Auto-Expand/Collapse, Moderator Comment Collapse, Infinite Scroll with Page Numbers + Image Embedder
// @author       Crates
// @license      MIT
// @match        https://old.reddit.com/*
// @match        https://www.reddit.com/*
// @match        http://old.reddit.com/*
// @grant        none
// @run-at       document-start
// ==/UserScript==
(function() {
    'use strict';

    // Track state for infinite scroll
    let isLoading = false;
    let currentPage = 1;
    let postCounter = 0;
    let prefetchedPage = null;
    let prefetchedDoc = null;

    // ========================================
    // UTILITY: Throttle function
    // ========================================
    function throttle(func, limit) {
        let inThrottle;
        return function(...args) {
            if (!inThrottle) {
                func.apply(this, args);
                inThrottle = true;
                setTimeout(() => inThrottle = false, limit);
            }
        };
    }

    // ========================================
    // 1. OVER18 AUTO-CONTINUE
    // ========================================
    function clickContinue() {
        // Method 1: By name attribute (original)
        const buttonsByName = document.getElementsByName('over18');
        for (let button of buttonsByName) {
            if (button.value === "yes") {
                button.click();
                return true;
            }
        }

        // Method 2: Direct button selector with value
        const continueBtn = document.querySelector('button[name="over18"][value="yes"]');
        if (continueBtn) {
            continueBtn.click();
            return true;
        }

        // Method 3: By class (for new Reddit style)
        const primaryBtn = document.querySelector('.c-btn-primary[name="over18"]');
        if (primaryBtn) {
            primaryBtn.click();
            return true;
        }

        // Method 4: Input element (old style)
        const inputBtn = document.querySelector('input[name="over18"][value="yes"]');
        if (inputBtn) {
            inputBtn.click();
            return true;
        }

        // Method 5: Any button/input with "continue" text on over18 pages
        if (window.location.href.includes('over18')) {
            const allButtons = document.querySelectorAll('button[type="submit"], input[type="submit"]');
            for (let btn of allButtons) {
                const text = (btn.textContent || btn.value || '').toLowerCase();
                if (text.includes('continue') || text.includes('yes')) {
                    btn.click();
                    return true;
                }
            }
        }

        return false;
    }

    // ========================================
    // 2. AUTO-COLLAPSE MODERATOR COMMENTS
    // ========================================
    function collapseModeratorComments() {
        document.querySelectorAll('.thing.comment.stickied:not(.collapsed):not([data-auto-collapsed]), .thing.comment:has(.tagline .moderator):not(.collapsed):not([data-auto-collapsed]), .thing.comment:has(.tagline .admin):not(.collapsed):not([data-auto-collapsed])').forEach(comment => {
            const expandBtn = comment.querySelector(':scope > .entry .expand');
            if (expandBtn) {
                comment.setAttribute('data-auto-collapsed', 'true');
                expandBtn.click();
            }
        });
    }

    // ========================================
    // 3. AUTO-EXPAND POST ON COMMENT PAGES
    // ========================================
    function autoExpandPost() {
        if (!document.querySelector('.commentarea')) return;
        const expando = document.querySelector('.thing.link .expando-button.collapsed');
        if (expando) expando.click();
    }

    // ========================================
    // 4. CONSTRAIN OVERSIZED EMBEDS (portrait videos, etc.)
    // ========================================
    function constrainEmbeds() {
        document.querySelectorAll('.expando iframe:not([data-resized])').forEach(iframe => {
            const w = parseInt(iframe.getAttribute('width'));
            const h = parseInt(iframe.getAttribute('height'));
            if (!w || !h) return;

            iframe.setAttribute('data-resized', 'true');

            const maxH = window.innerHeight * 0.65;
            if (h > maxH) {
                const scale = maxH / h;
                iframe.setAttribute('width', Math.round(w * scale));
                iframe.setAttribute('height', Math.round(maxH));
            }
        });
    }

    // ========================================
    // 4.1. NEW: EMBED PLACEHOLDER IMAGES (<image>)
    // ========================================
    function embedPlaceholderImages() {
        const links = document.querySelectorAll('a');
        links.forEach(link => {
            if ((link.textContent.trim() === '<image>' || link.innerText.trim() === '<image>') && !link.dataset.processed) {
                link.dataset.processed = "true";
                const imageUrl = link.href;
                const img = document.createElement('img');
                img.src = imageUrl;

                // Reasonable Size Styling
                img.style.maxWidth = '600px';
                img.style.maxHeight = '500px';
                img.style.width = 'auto';
                img.style.height = 'auto';
                img.style.display = 'block';
                img.style.marginTop = '8px';
                img.style.marginBottom = '8px';
                img.style.borderRadius = '3px';
                img.style.border = '1px solid #ccc';
                img.style.cursor = 'zoom-in';

                // Click to toggle full size
                img.onclick = (e) => {
                    e.preventDefault();
                    if (img.style.maxWidth === '600px') {
                        img.style.maxWidth = '100%';
                        img.style.maxHeight = 'none';
                        img.style.cursor = 'zoom-out';
                    } else {
                        img.style.maxWidth = '600px';
                        img.style.maxHeight = '500px';
                        img.style.cursor = 'zoom-in';
                    }
                };

                link.textContent = '';
                link.appendChild(img);
            }
        });
    }

    // ========================================
    // 5. NUMBER POSTS AND ADD PAGE BREAKS
    // ========================================
    function numberPosts() {
        const posts = document.querySelectorAll('.thing.link:not([data-numbered])');
        if (posts.length === 0) return;

        posts.forEach(post => {
            postCounter++;
            post.setAttribute('data-numbered', 'true');
            post.setAttribute('data-post-number', postCounter);

            const rank = post.querySelector('.rank');
            if (rank) {
                rank.textContent = postCounter;
            }
        });
    }

    // ========================================
    // 6. ADD PAGE SEPARATOR
    // ========================================
    function addPageSeparator(pageNum) {
        const siteTable = document.querySelector('.sitetable.linklisting');
        if (!siteTable) return;

        const separator = document.createElement('div');
        separator.className = 'page-separator';
        separator.setAttribute('data-page', pageNum);
        separator.innerHTML = `
            <div class="page-separator-line"></div>
            <div class="page-separator-label">Page ${pageNum}</div>
            <div class="page-separator-line"></div>
        `;
        siteTable.appendChild(separator);
    }

    // ========================================
    // 7. INFINITE SCROLL (with prefetching)
    // ========================================
    function prefetchNextPage() {
        const nextButton = document.querySelector('.next-button a');
        if (!nextButton || prefetchedDoc) return;

        const url = nextButton.href;
        if (prefetchedPage === url) return;

        prefetchedPage = url;
        fetch(url)
            .then(response => response.text())
            .then(html => {
                const parser = new DOMParser();
                prefetchedDoc = parser.parseFromString(html, 'text/html');
            })
            .catch(() => {
                prefetchedPage = null;
            });
    }

    function loadNextPage() {
        if (isLoading) return;

        const nextButton = document.querySelector('.next-button a');
        if (!nextButton) return;

        isLoading = true;

        const siteTable = document.querySelector('.sitetable.linklisting');
        if (!siteTable) {
            isLoading = false;
            return;
        }

        function appendPosts(doc) {
            currentPage++;
            addPageSeparator(currentPage);

            const newPosts = doc.querySelectorAll('.thing.link');
            newPosts.forEach(post => {
                const clonedPost = post.cloneNode(true);
                clonedPost.removeAttribute('data-numbered');
                siteTable.appendChild(clonedPost);
            });

            const newNextButton = doc.querySelector('.next-button a');
            if (newNextButton) {
                nextButton.href = newNextButton.href;
                prefetchedDoc = null;
                prefetchedPage = null;
                prefetchNextPage();
            } else {
                const navButtons = document.querySelector('.nav-buttons');
                if (navButtons) {
                    navButtons.innerHTML = '<span style="color: #888; padding: 10px;">No more posts</span>';
                }
            }

            numberPosts();
            embedPlaceholderImages(); // Ensure images embed on new pages
            isLoading = false;
        }

        if (prefetchedDoc && prefetchedPage === nextButton.href) {
            appendPosts(prefetchedDoc);
            prefetchedDoc = null;
            prefetchedPage = null;
            return;
        }

        fetch(nextButton.href)
            .then(response => response.text())
            .then(html => {
                const parser = new DOMParser();
                const doc = parser.parseFromString(html, 'text/html');
                appendPosts(doc);
            })
            .catch(err => {
                console.error('Infinite scroll error:', err);
                isLoading = false;
            });
    }

    function setupInfiniteScroll() {
        if (document.querySelector('.commentarea')) return;

        prefetchNextPage();

        const handleScroll = throttle(() => {
            const scrollPosition = window.innerHeight + window.scrollY;
            const pageHeight = document.documentElement.scrollHeight;

            if (scrollPosition >= pageHeight - 1500) {
                prefetchNextPage();
            }

            if (scrollPosition >= pageHeight - 800) {
                loadNextPage();
            }
        }, 150);

        window.addEventListener('scroll', handleScroll);

        numberPosts();
    }

    // ========================================
    // 8. CLICK-BASED LOGIC
    // ========================================
    let internalClick = false;

    document.addEventListener('click', function(e) {
        const thumbnail = e.target.closest('a.thumbnail');
        if (thumbnail) {
            e.preventDefault();
            const thing = thumbnail.closest('.thing');
            if (thing) {
                const expandoButton = thing.querySelector('.expando-button');
                if (expandoButton) {
                    internalClick = true;
                    expandoButton.click();
                    internalClick = false;
                }
            }
            return;
        }

        const expandoButton = e.target.closest('.expando-button');
        if (expandoButton && expandoButton.classList.contains('collapsed')) {
            if (e.isTrusted || internalClick) {
                document.querySelectorAll('.expando-button.expanded').forEach(btn => {
                    if (btn !== expandoButton) btn.click();
                });

                const thing = expandoButton.closest('.thing');
                if (thing) {
                    setTimeout(() => {
                        thing.scrollIntoView({ behavior: 'smooth', block: 'start' });
                    }, 50);
                }
            }

            setTimeout(() => {
                const nsfwBtn = document.querySelector('button.expando-gate__show-once');
                if (nsfwBtn) nsfwBtn.click();
            }, 100);
        }
    }, true);

    // ========================================
    // 9. MUTATION OBSERVER (Optimized)
    // ========================================
    let observerTimeout = null;

    const observer = new MutationObserver((mutations) => {
        clickContinue();
        const nsfwBtn = document.querySelector('button.expando-gate__show-once');
        if (nsfwBtn) nsfwBtn.click();

        if (observerTimeout) return;

        observerTimeout = setTimeout(() => {
            collapseModeratorComments();
            constrainEmbeds();
            embedPlaceholderImages(); // Catch dynamic image links
            numberPosts();
            observerTimeout = null;
        }, 100);
    });

    // ========================================
    // 10. INITIALIZATION
    // ========================================
    function init() {
        observer.observe(document.documentElement, { childList: true, subtree: true });

        clickContinue();
        setTimeout(clickContinue, 100);
        setTimeout(clickContinue, 300);
        setTimeout(clickContinue, 500);
        setTimeout(clickContinue, 1000);

        collapseModeratorComments();
        autoExpandPost();
        constrainEmbeds();
        embedPlaceholderImages();
        setupInfiniteScroll();

        const style = document.createElement('style');
        style.textContent = `
            .page-separator {
                clear: both;
                width: 100%;
                display: flex;
                align-items: center;
                gap: 14px;
                margin: 20px 0;
                padding: 0 10px;
            }
            .page-separator-line {
                flex: 1;
                height: 1px;
                background: linear-gradient(90deg, transparent, #c0d0e0 15%, #c0d0e0 85%, transparent);
            }
            .page-separator-label {
                font-size: 11px;
                font-weight: 600;
                letter-spacing: 1.5px;
                text-transform: uppercase;
                color: #8aa8c7;
                white-space: nowrap;
            }
            .thing.link {
                border-bottom: 1px solid #e5e5e5 !important;
                padding-bottom: 10px !important;
                margin-bottom: 10px !important;
            }
            .infinite-scroll-loading {
                clear: both;
                width: 100%;
            }
            .rank {
                min-width: 30px !important;
            }
        `;
        document.head.appendChild(style);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

    window.addEventListener('load', () => {
        clickContinue();
        setTimeout(clickContinue, 500);
    });
})();