您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a sidebar to manage and display blacklisted tags and hides or shows images based on the blacklist on rule34.xxx.
当前为
// ==UserScript== // @name Remove Blacklisted Images with Sidebar // @namespace http://tampermonkey.net/ // @version 1.3 // @description Adds a sidebar to manage and display blacklisted tags and hides or shows images based on the blacklist on rule34.xxx. // @author Dramorian // @match https://rule34.xxx/index.php?page=post* // @match https://rule34.xxx/index.php?page=favorites* // @match https://rule34.xxx/index.php?page=comment* // @exclude https://rule34.xxx/index.php?page=post&s=view* // @icon https://www.google.com/s2/favicons?sz=64&domain=rule34.xxx // @grant none // ==/UserScript== (function () { 'use strict'; const debug = true; // Set to false to disable logging function getTagBlacklist() { const cookieValue = getCookieValue('tag_blacklist'); const blacklist = cookieValue ? decodeBlacklist(cookieValue) : []; if (debug) { console.log('Retrieved blacklist:', blacklist); } return blacklist; } function getCookieValue(name) { const cookie = document.cookie.split('; ').find(row => row.startsWith(`${name}=`)); return cookie ? cookie.split('=')[1] : null; } function decodeBlacklist(encodedString) { return decodeURIComponent(encodedString).split('%20'); } // Function to create and inject the sidebar function createSidebar() { const sidebarHTML = ` <div id="blacklist-box"> <div id="sidebar-header"> <h2>Blacklisted</h2> <button id="toggle-header" aria-label="Toggle Sidebar"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-right-fill" viewBox="0 0 16 16" class="toggle-icon"> <path d="m12.14 8.753-5.482 4.796c-.646.566-1.658.106-1.658-.753V3.204a1 1 0 0 1 1.659-.753l5.48 4.796a1 1 0 0 1 0 1.506z"/> </svg> </button> </div> <div id="sidebar-content" style="display: none;"> <ul id="blacklist-list" style="list-style: none;"></ul> </div> <div id="sidebar-footer"> <button id="disable-all-blacklists">Disable All</button> <button id="re-enable-all-blacklists" style="display: none;">Re-enable All</button> </div> </div> `; const targetElement = findSidebarInsertionTarget(); if (!targetElement) { console.error('Suitable element for sidebar insertion not found.'); return; } insertSidebar(targetElement, sidebarHTML); updateSidebar(); addSidebarEventListeners(); addCollapsibleFunctionality(); } function findSidebarInsertionTarget() { return document.querySelector('div.tag-search') || document.querySelector('#content > h1'); } function insertSidebar(targetElement, sidebarHTML) { const sidebarContainer = document.createElement('div'); sidebarContainer.innerHTML = sidebarHTML; targetElement.insertAdjacentElement('afterend', sidebarContainer); if (debug) { console.log(`Sidebar inserted after ${targetElement.tagName.toLowerCase()} element.`); } } function addCollapsibleFunctionality() { const toggleButton = document.getElementById('toggle-header'); const icon = toggleButton.querySelector('svg'); toggleButton.addEventListener('click', () => { const content = document.getElementById('sidebar-content'); const isCollapsed = content.style.display === 'none'; content.style.display = isCollapsed ? 'block' : 'none'; // Apply rotation transform icon.style.transform = isCollapsed ? 'rotate(90deg)' : 'rotate(0deg)'; icon.style.transition = 'transform 0.25s ease'; if (debug) { console.log('Sidebar toggle button clicked. New display state:', content.style.display); } }); } // Function to update the sidebar with current blacklist items function updateSidebar() { const blacklist = getTagBlacklist(); const blacklistList = document.getElementById('blacklist-list'); const header = document.querySelector('#blacklist-box h2'); blacklistList.innerHTML = ''; const detectedTags = blacklist.filter(isTagDetectedOnPage); const totalHiddenPosts = updateBlacklistList(detectedTags, blacklistList); updateSidebarVisibility(detectedTags.length, header, totalHiddenPosts); } function updateBlacklistList(detectedTags, blacklistList) { let totalHiddenPosts = 0; detectedTags.forEach(tag => { const isDisabled = isTagDisabled(tag); const hiddenCount = getInitialHiddenPostCount(tag); totalHiddenPosts += hiddenCount; const listItem = createBlacklistListItem(tag, isDisabled, hiddenCount); blacklistList.appendChild(listItem); if (debug) console.log(`Added tag to sidebar: ${tag}, Hidden count: ${hiddenCount}`); }); if (debug) console.log('Sidebar updated. Total hidden posts:', totalHiddenPosts); // Re-attach event listeners after updating the sidebar addCheckboxEventListeners(); return totalHiddenPosts; } function createBlacklistListItem(tag, isDisabled, hiddenCount) { const listItem = document.createElement('li'); listItem.innerHTML = ` <label> <input type="checkbox" class="blacklist-checkbox" data-tag="${encodeURIComponent(tag)}" ${isDisabled ? '' : 'checked'}> ${tag} <span class="count">${hiddenCount}</span> </label> `; return listItem; } function updateSidebarVisibility(tagCount, header, totalHiddenPosts) { const sidebar = document.getElementById('blacklist-box'); if (tagCount === 0) { sidebar.style.display = 'none'; header.textContent = 'Blacklisted (0)'; if (debug) console.log('No blacklisted tags detected. Sidebar hidden.'); } else { sidebar.style.display = ''; header.textContent = `Blacklisted (${totalHiddenPosts})`; } } // Function to check if a tag is currently detected on the page function isTagDetectedOnPage(tag) { const elements = document.querySelectorAll('img[title], div[id^="p"] > div.col1.thumb > a > img'); return Array.from(elements).some(el => { const title = el.getAttribute('title'); const isDetected = title && containsExactTag(title, [tag]); if (debug && isDetected) console.log(`Tag detected on page: ${tag}`); return isDetected; }); } // Function to check if a tag is currently disabled function isTagDisabled(tag) { const disabledTags = JSON.parse(localStorage.getItem('disabled_tags') || '[]'); const isDisabled = disabledTags.includes(tag); if (debug) console.log(`Tag ${tag} is ${isDisabled ? 'disabled' : 'enabled'}`); return isDisabled; } // Function to toggle the filtering state of a tag function toggleTag(tag, isEnabled) { let disabledTags = getDisabledTags(); disabledTags = isEnabled ? removeTag(disabledTags, tag) : addTag(disabledTags, tag); if (debug) logTagState(tag, isEnabled); localStorage.setItem('disabled_tags', JSON.stringify(disabledTags)); applyTagFiltering(); } function getDisabledTags() { return JSON.parse(localStorage.getItem('disabled_tags') || '[]'); } function removeTag(tags, tag) { return tags.filter(t => t !== tag); } function addTag(tags, tag) { if (!tags.includes(tag)) { tags.push(tag); } return tags; } function logTagState(tag, isEnabled) { const action = isEnabled ? 'enabled' : 'disabled'; console.log(`Tag ${tag} ${action}`); } // Function to get the initial count of hidden posts for a given tag function getInitialHiddenPostCount(tag) { const favoriteCount = countHiddenPosts('img[title]', tag); const commentCount = countHiddenPosts('div[id^="p"] > div.col1.thumb > a > img', tag); const totalCount = favoriteCount + commentCount; if (debug) console.log(`Initial hidden post count for tag ${tag}: ${totalCount}`); return totalCount; } function countHiddenPosts(selector, tag) { let count = 0; const images = document.querySelectorAll(selector); images.forEach(img => { const imgTitle = img.getAttribute('title'); if (imgTitle && containsExactTag(imgTitle, [tag])) { count++; } }); return count; } // Function to check if the title contains any exact blacklist tag function containsExactTag(title, blacklist) { return blacklist.some(tag => { const regex = new RegExp(`\\b${tag}\\b`, 'i'); const contains = regex.test(title); if (debug && contains) console.log(`Title "${title}" contains tag "${tag}"`); return contains; }); } function addSidebarEventListeners() { const disableAllButton = document.getElementById('disable-all-blacklists'); const reEnableAllButton = document.getElementById('re-enable-all-blacklists'); disableAllButton.addEventListener('click', (e) => handleBlacklistToggle(e, true)); reEnableAllButton.addEventListener('click', (e) => handleBlacklistToggle(e, false)); // Attach checkbox event listeners addCheckboxEventListeners(); } function handleBlacklistToggle(event, disable) { event.preventDefault(); if (disable) { const allTags = getTagBlacklist(); localStorage.setItem('disabled_tags', JSON.stringify(allTags)); if (debug) console.log('Disabled all blacklisted tags'); toggleButtonVisibility('disable-all-blacklists', 're-enable-all-blacklists'); } else { localStorage.removeItem('disabled_tags'); if (debug) console.log('Re-enabled all blacklisted tags'); toggleButtonVisibility('re-enable-all-blacklists', 'disable-all-blacklists'); } updateSidebar(); // Update the sidebar to reflect the changes applyTagFiltering(); // Apply filtering immediately } function toggleButtonVisibility(hiddenButtonId, visibleButtonId) { document.getElementById(hiddenButtonId).style.display = 'none'; document.getElementById(visibleButtonId).style.display = 'inline'; } // Function to attach event listeners to checkboxes function addCheckboxEventListeners() { document.querySelectorAll('#blacklist-list .blacklist-checkbox').forEach(checkbox => { checkbox.addEventListener('change', function () { const tag = decodeURIComponent(this.getAttribute('data-tag')); const isEnabled = this.checked; toggleTag(tag, isEnabled); if (debug) console.log(`Checkbox for tag "${tag}" changed to ${isEnabled ? 'enabled' : 'disabled'}`); }); }); } // Function to apply tag filtering to the page function applyTagFiltering() { const disabledTags = JSON.parse(localStorage.getItem('disabled_tags') || '[]'); const allTags = getTagBlacklist(); if (debug) console.log('Applying tag filtering. Disabled tags:', disabledTags, 'All tags:', allTags); // Hide posts for tags that are checked (enabled) hidePosts(allTags.filter(tag => !disabledTags.includes(tag))); // Show posts for tags that are unchecked (disabled) showPosts(disabledTags); } // Function to show posts for tags that should be visible function showPosts(tags) { tags.forEach(tag => updatePostsVisibility(tag, '', '')); } // Function to hide posts for tags that should be hidden function hidePosts(tags) { if (tags.length === 0) return; // If no tags to hide, exit the function. tags.forEach(tag => updatePostsVisibility(tag, 'none', 'important')); } // Helper function to update post visibility function updatePostsVisibility(tag, displayValue, importantValue) { const elements = document.querySelectorAll(`img[title*="${tag}"], div[id^="p"] > div.col1.thumb > a > img`); elements.forEach(el => { const title = el.getAttribute('title'); if (title && (displayValue === '' || containsExactTag(title, [tag]))) { const parent = el.closest('span') || el.closest('div[id^="p"]'); if (parent) { parent.style.display = displayValue; parent.style.setProperty('display', displayValue, importantValue); if (debug) { const action = displayValue === 'none' ? 'Hiding' : 'Showing'; console.log(`${action} post for tag "${tag}"`); } } } }); } // Function to remove the effect of the blacklisted-sidebar // Native "Hidden" button that hides/reveals images //TODO: think of the better logic function removeBlacklistedSidebarEffect() { handleBlacklistCount(); removeSidebarElement(); } function handleBlacklistCount() { const blacklistCountElement = document.getElementById('blacklist-count'); if (!blacklistCountElement) return; const postCount = parseInt(blacklistCountElement.textContent, 10); if (postCount > 0) { const hiddenLink = blacklistCountElement.closest('h5').querySelector('a'); if (hiddenLink) { hiddenLink.click(); if (debug) { console.log('Clicked on the "Hidden" to remove the blacklisted effect'); } } } } function removeSidebarElement() { const blacklistSidebar = document.getElementById('blacklisted-sidebar'); if (blacklistSidebar) { blacklistSidebar.remove(); if (debug) { console.log('Removed the native blacklisted-sidebar element from the page'); } } } removeBlacklistedSidebarEffect(); createSidebar(); applyTagFiltering(); })();