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

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

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