Greasy Fork is available in English.

Little Doll Script

Автоматическая смена и установка часового пояса, создание элемента с иконкой и количеством удаленных элементов, удаление стикеров, спойлеров, раскрытие постов

// ==UserScript==
// @name         Little Doll Script
// @namespace    Little Doll Script
// @version      1.1
// @description  Автоматическая смена и установка часового пояса, создание элемента с иконкой и количеством удаленных элементов, удаление стикеров, спойлеров, раскрытие постов
// @author       Maesta_Nequitia
// @match        *://2ch.hk/*
// @grant        GM_addStyle
// @icon         https://2ch.hk/favicon.ico
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    let deletedStickerCount = 0;
    let deletedCountElementPart2;

    // Получаем текущее время пользователя
    const userTime = new Date();

    // Получаем московское время
    const moscowTime = new Date().toLocaleString('en-US', {timeZone: 'Europe/Moscow'});
    const moscowTimeObj = new Date(moscowTime);

    // Вычисляем разницу во времени между московским временем и временем пользователя в миллисекундах
    const timeDifference = userTime - moscowTimeObj;

    const daysOfWeek = ['Вск', 'Пнд', 'Втр', 'Срд', 'Чтв', 'Птн', 'Суб'];

    const formatDate = (date) => {
        const dayOfWeek = daysOfWeek[date.getDay()];
        return `${date.getDate().toString().padStart(2, '0')}/${(date.getMonth() + 1).toString().padStart(2, '0')}/${date.getFullYear().toString().slice(-2)} ${dayOfWeek} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}`;
    };

    const modifyTime = (element) => {
        const [datePart, , timePart] = element.textContent.split(' ');
        const [day, month, year] = datePart.split('/');
        const [hours, minutes, seconds] = timePart.split(':');

        const originalDateObject = new Date(`20${year}`, month - 1, day, hours, minutes, seconds);

        // Применяем разницу во времени к времени на форуме
        const newDateObject = new Date(originalDateObject.getTime() + timeDifference);

        element.textContent = formatDate(newDateObject);
    };

    const handleMutations = (mutationsList) => {
        mutationsList.forEach((mutation) => {
            if (mutation.type === 'childList') {
                Array.from(mutation.addedNodes)
                    .filter(node => node instanceof Element)
                    .forEach(node => {
                        Array.from(node.getElementsByClassName('post__time')).forEach(modifyTime);
                    });
            }
        });
    };

    const timeElements = document.querySelectorAll('.post__time');
    timeElements.forEach(modifyTime);

    const observer = new MutationObserver(handleMutations);
    observer.observe(document.body, { childList: true, subtree: true });

    // Часть 2: Создание элемента с иконкой и количеством удаленных элементов
    function createDeletedCountElement(count) {
        const deletedCountElement = document.createElement('span');
        deletedCountElement.setAttribute('title', 'Удалено');
        deletedCountElement.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" class="icon"><use xlink:href="#icon__delete"></use></svg> ${count}`;
        return deletedCountElement;
    }

    function updateDeletedCountPart2(count) {
        if (!deletedCountElementPart2) {
            deletedCountElementPart2 = createDeletedCountElement(count);
        } else {
            deletedCountElementPart2.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" class="icon"><use xlink:href="#icon__delete"></use></svg> ${count}`;
        }

        // Находим все элементы .tn__item.desktop на странице
        const threadItems = document.querySelectorAll('.tn__item.desktop');

        // Выбираем последний элемент .tn__item.desktop
        const lastThreadItem = threadItems[threadItems.length - 1];

        // Вставляем созданный элемент после последнего .tn__item.desktop
        lastThreadItem.insertAdjacentElement('afterend', deletedCountElementPart2);
    }

    // Часть 3: Удаление стикеров, спойлеров, раскрытие постов
    const spoilerSelector = '.post__message .spoiler';
    GM_addStyle(`${spoilerSelector} { color: inherit !important; background-color: inherit !important; }`);

    const stickerSelector = '.post__images.post__images_type_single';

    function removeStickerPosts() {
        document.querySelectorAll(stickerSelector).forEach((element) => {
            const dataTitleElement = element.querySelector('[data-title^="Стикер"]');
            if (dataTitleElement) {
                element.remove();
                deletedStickerCount++;
                updateDeletedCountPart2(deletedStickerCount);
            }
        });
    }

    function expandPosts() {
        const customStyles = `
            .post > article.post__message {
                max-height: unset !important;
            }
        `;

        document.head.insertAdjacentHTML('beforeend', `<style type="text/css">${customStyles}</style>`);
    }

    function handleMutationsPart2(mutationsList) {
        mutationsList.forEach((mutation) => {
            if (mutation.type === 'childList') {
                removeStickerPosts();
            }
        });
    }

    // Инициализация
    removeStickerPosts();
    expandPosts();

    // Обсервер мутаций для второй части
    if (typeof MutationObserver !== 'undefined') {
        const observerPart2 = new MutationObserver(handleMutationsPart2);
        observerPart2.observe(document.body, { childList: true, subtree: true });
    }
})();