Boosty Image URL Cleaner

Удаление параметров mw и mh из ссылок на изображения на Boosty, если не указано "&croped", позволяет загружать изображения-превью в оригинальном разрешении

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Boosty Image URL Cleaner
// @version      1.11
// @description  Удаление параметров mw и mh из ссылок на изображения на Boosty, если не указано "&croped",  позволяет загружать изображения-превью в оригинальном разрешении
// @match        *://*.boosty.to/*
// @grant        none
// @namespace http://tampermonkey.net/
// ==/UserScript==

(function() {
    'use strict';
    // --- (Этот скрипт оптимизирован для эффективности) ---
    // --- (Использует MutationObserver для обработки только НОВЫХ img) ---
    // --- (Использует Map для кэширования уже обработанных URL) ---

    // --- КАТЕГОРИЯ: КЭШИРОВАНИЕ И СОСТОЯНИЕ ---
    // Этот раздел содержит переменные, используемые для оптимизации и отслеживания состояния.
    // - urlCleanCache: Map-объект для кэширования.
    //   - Ключ: Оригинальный URL изображения.
    //   - Значение: Уже очищенный URL.
    //   - Это предотвращает повторную обработку одного и того же URL (например,
    //     одинаковых аватарок в комментариях) и значительно ускоряет скрипт.
    const urlCleanCache = new Map();

    // --- КАТЕГОРИЯ: ОСНОВНАЯ ЛОГИКА ---
    // Функции, выполняющие основную работу скрипта.

    /**
     * Принимает URL, очищает его от параметров 'mw' и 'mh',
     * если в нем нет параметра "&croped".
     * Использует кэширование для быстрого ответа.
     *
     * @param {string} url - Оригинальный URL изображения.
     * @returns {string} - Очищенный URL.
     */
    function cleanURL(url) {
        // 1. Сначала проверяем, нет ли уже готового URL в кэше
        if (urlCleanCache.has(url)) {
            return urlCleanCache.get(url);
        }

        // 2. Если в кэше нет, проводим анализ и очистку
        let urlParts = url.split('?');
        
        // Если URL вообще не содержит параметров (нет '?'),
        // он уже "чистый". Кэшируем его как есть.
        if (urlParts.length < 2) {
            urlCleanCache.set(url, url); // Кэшируем "чистый" URL (он же = оригиналу)
            return url;
        }

        let baseUrl = urlParts[0];
        // Собираем обратно строку запроса, на случай если в ней был '?'
        let queryString = urlParts.slice(1).join('?'); 
        let searchParams = new URLSearchParams(queryString);

        // 3. Главное условие:
        // Если в строке запроса есть "&croped" (или "croped" как первый параметр),
        // мы НЕ трогаем 'mw' и 'mh', т.к. они нужны для кадрирования.
        // Мы проверяем именно `queryString`, а не `searchParams.has`
        // для точного соответствия оригинальной логике (поиск подстроки).
        if (!queryString.includes('&croped') && !queryString.startsWith('croped=')) {
            // Удаляем параметры, отвечающие за ограничение размера
            searchParams.delete('mw');
            searchParams.delete('mh');
        }

        // 4. Собираем URL обратно
        const newQueryString = searchParams.toString();
        const finalUrl = newQueryString ? `${baseUrl}?${newQueryString}` : baseUrl;

        // 5. Сохраняем результат в кэш
        urlCleanCache.set(url, finalUrl);
        return finalUrl;
    }

    // --- КАТЕГОРИЯ: ОБРАБОТКА DOM ---
    // Функции, которые взаимодействуют с элементами на странице.

    /**
     * Обрабатывает один DOM-узел <img>.
     * Проверяет, является ли он целевым изображением Boosty,
     * и применяет к нему `cleanURL` для `src` и `srcset`.
     *
     * @param {Node} node - DOM-узел для проверки.
     */
    function processImage(node) {
        // Убеждаемся, что это:
        // 1. Элемент (nodeType === 1)
        // 2. Тег <img> с `src`, содержащим "boosty.to/image"
        // 3. Элемент, который мы еще не обрабатывали (нет `data-cleaned="true"`)
        // Селектор :not([data-cleaned="true"]) - ключевой для производительности.
        if (node.nodeType === 1 &&
            node.matches('img[src*="boosty.to/image"]:not([data-cleaned="true"])')) {

            // Помечаем сразу, чтобы избежать повторной обработки
            // этим же скриптом или другим колбэком MutationObserver.
            node.dataset.cleaned = 'true';

            // Чистим основной `src`
            // (cleanURL быстро возьмет из кэша, если URL уже встречался)
            if (node.src) {
                node.src = cleanURL(node.src);
            }

            // Чистим `srcset`, который используется для адаптивных изображений
            if (node.srcset) {
                // `srcset` - это строка вида "url1 100w, url2 200w, ..."
                node.srcset = node.srcset.split(',') // -> ["url1 100w", " url2 200w"]
                    .map(srcEntry => {
                        const parts = srcEntry.trim().split(' '); // -> ["url1", "100w"] или ["url2"]
                        const url = parts[0];
                        const size = parts[1] || ''; // '100w' или пустая строка
                        
                        // Чистим только URL, а дескриптор размера (size) оставляем
                        return `${cleanURL(url)} ${size}`;
                    })
                    .join(', '); // Собираем обратно в строку
            }
        }
    }

    // --- КАТЕГОРИЯ: НАБЛЮДЕНИЕ ЗА DOM И ИНИЦИАЛИЗАЦИЯ ---
    // Код, который запускает скрипт и отслеживает будущие изменения.

    // 1. Создание Наблюдателя (MutationObserver)
    // Это современный и очень эффективный способ реагировать
    // на добавление новых элементов на страницу (например, при
    // бесконечной прокрутке или открытии комментариев).
    const observer = new MutationObserver((mutations) => {
        // Перебираем все зафиксированные мутации
        for (const mutation of mutations) {
            // Нас интересуют только *добавленные* узлы
            for (const node of mutation.addedNodes) {
                // А. Если добавлен сам <img>
                // (nodeType === 1 нужен, чтобы отсеять текст, комменты и т.д.)
                if (node.nodeType === 1) {
                    processImage(node);
                }

                // Б. Если добавлен контейнер (например, <div> с постом),
                // ищем <img> *внутри* него.
                // `node.querySelectorAll` существует только у элементов.
                if (node.querySelectorAll) {
                    node.querySelectorAll('img[src*="boosty.to/image"]:not([data-cleaned="true"])')
                        .forEach(processImage);
                }
            }
        }
    });

    // 2. Запуск наблюдателя
    // Мы "прикрепляем" его к document.body и говорим:
    // - childList: true -> следи за добавлением/удалением дочерних элементов
    // - subtree: true   -> следи за всеми потомками, а не только прямыми
    observer.observe(document.body, { childList: true, subtree: true });

    // 3. Первоначальная обработка
    // Наблюдатель `observer` сработает только на *новые* элементы.
    // Эта строка находит и обрабатывает все картинки, которые
    // уже были на странице в момент запуска скрипта.
    document.querySelectorAll('img[src*="boosty.to/image"]:not([data-cleaned="true"])')
        .forEach(processImage);
})();