您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
делает треды древовидными, добавляет сворачивание веток и подсветку новых
当前为
- // ==UserScript==
- // @name 2ch tree post fork
- // @namespace http://tampermonkey.net/
- // @version 1.3
- // @description делает треды древовидными, добавляет сворачивание веток и подсветку новых
- // @author You
- // @match http://2ch.hk/*/res/*
- // @match https://2ch.hk/*/res/*
- // @match http://2ch.life/*/res/*
- // @match https://2ch.life/*/res/*
- // @grant none
- // @grant GM_addStyle
- // @grant GM_getValue
- // @grant GM_setValue
- // @license MIT
- // ==/UserScript==
- (function () {
- "use strict";
- console.time("tree script");
- // Вспомогательные функции
- // Добавляет CSS стили
- function addStyle(css) {
- const style = document.createElement('style');
- style.type = 'text/css';
- style.textContent = css;
- document.head.appendChild(style);
- }
- // Получает номер поста из элемента
- function getPostNumber(postElement) {
- if (!postElement) return null;
- const id = postElement.id; // "post-123456"
- return parseInt(id.replace("post-", ""));
- }
- // Перемещает пост и применяет стили
- function postMove(linkPost, isNewPost = false) {
- const nodePostCurr = linkPost.parentNode.parentNode; // Текущий пост (обертка .post)
- const postNumber = linkPost.innerText.match(/\d+/);
- if (!postNumber) return; // Если не удалось извлечь номер, выходим
- const targetPostNumber = postNumber[0];
- // Проверяем, ссылка на OP, другой тред или несуществующий пост
- if (/OP|→/.test(linkPost.innerText)) {
- return;
- }
- const nodePostReply = document.querySelector(`#post-${targetPostNumber}`);
- if (!nodePostReply) {
- //console.warn(`Target post #${targetPostNumber} not found.`); // отладка, если пост не найден
- return;
- }
- // Добавляем класс, помечающий что в посте есть ответы (для сворачивания)
- if (!nodePostReply.classList.contains('has-replies')) {
- nodePostReply.classList.add('has-replies');
- // Добавляем кнопку сворачивания
- const collapseButton = document.createElement('span');
- collapseButton.classList.add('collapse-button');
- collapseButton.textContent = '[-]';
- collapseButton.title = "Свернуть/Развернуть ветку";
- // Добавляем обработчик сворачивания/разворачивания
- collapseButton.addEventListener('click', (event) => {
- event.stopPropagation(); // Предотвращаем всплытие, чтобы клик по кнопке не выделял пост
- const replies = nodePostReply.querySelectorAll(':scope > .post'); // :scope - только непосредственные дочерние .post
- replies.forEach(reply => {
- reply.classList.toggle('collapsed');
- });
- collapseButton.textContent = collapseButton.textContent === '[-]' ? '[+]' : '[-]'; // Меняем текст кнопки
- });
- // Вставляем кнопку сворачивания перед .post__details
- const postDetails = nodePostReply.querySelector('.post__details');
- if (postDetails) {
- postDetails.parentNode.insertBefore(collapseButton, postDetails);
- }
- }
- nodePostReply.append(nodePostCurr); // Перемещаем
- // Подсветка новых постов
- if (isNewPost) {
- nodePostCurr.classList.add('new-post'); // Добавляем класс для новых
- // Убираем подсветку при клике (однократно)
- nodePostCurr.addEventListener("click", () => {
- nodePostCurr.classList.remove('new-post');
- nodePostCurr.style["border-left"] = "2px dashed"; // Добавляем dashed border при клике
- }, { once: true });
- }
- }
- // --- Основная логика ---
- // 1. Обработка существующих постов
- const initialLinks = document.querySelectorAll(`.post__message > :nth-child(1)[data-num]`);
- initialLinks.forEach(postMove);
- // 2. Наблюдение за новыми постами
- const threadContainer = document.querySelector(".thread");
- const observer = new MutationObserver((mutations) => {
- mutations.forEach((mutation) => {
- if (mutation.addedNodes.length > 0) {
- mutation.addedNodes.forEach(addedNode => {
- // Проверяем, что добавленный узел - это пост (у него есть класс .post)
- if (addedNode.classList && addedNode.classList.contains('post')) {
- const newLink = addedNode.querySelector(`.post__message > :nth-child(1)[data-num]`);
- if (newLink) {
- postMove(newLink, true);
- }
- }
- });
- }
- });
- });
- // 3. Запускаем наблюдение
- observer.observe(threadContainer, { childList: true });
- // 4. Стили
- addStyle(`
- .post .post_type_reply {
- border-left: 2px solid white; /* Исходный цвет границы */
- margin-left: 5px; /* небольшой отступ */
- padding-left: 5px;
- }
- .new-post {
- border-left-color: yellow !important; /* Подсветка новых постов */
- }
- .post.collapsed {
- display: none;
- }
- .collapse-button{
- cursor: pointer;
- margin-right: 5px;
- color: #888; /* Серый цвет */
- }
- .has-replies{
- position: relative; /* Для позиционирования кнопки */
- }
- `);
- console.timeEnd("tree script");
- })();