您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Easy image filtering and post marking + hide your own posts
当前为
// ==UserScript== // @name Better Sturdy // @namespace dunkydonut // @version 1.1 // @description Easy image filtering and post marking + hide your own posts // @author ILoveS10 // @license MIT // @match https://sturdychan.help/* // @grant none // ==/UserScript== (function () { 'use strict'; let hiddenCount = 0; const hiddenDisplay = document.querySelector('#hidden'); const hiddenPosts = new Map(); function updateHiddenDisplay() { if (hiddenDisplay) hiddenDisplay.textContent = `Hidden: ${hiddenPosts.size}`; } function hashFiltering(postElement) { const image = postElement.querySelector('figure img'); const filterArea = document.querySelector('#filterArea'); if (!image || !filterArea) return null; const imageURL = image.src; const filenameWithExt = imageURL.split('/').pop(); const filenameWithoutExt = filenameWithExt.split('.')[0]; const hashLine = `hash:${filenameWithoutExt}`; filterArea.value += `\n${hashLine}`; return hashLine; } function showMessage(text, undoCallback) { const message = document.createElement('div'); message.id = 'sturdy-message'; message.style.position = 'fixed'; message.style.top = '30px'; message.style.left = '50%'; message.style.transform = 'translateX(-50%)'; message.style.backgroundColor = '#000'; message.style.color = '#fff'; message.style.padding = '10px 20px'; message.style.borderRadius = '8px'; message.style.zIndex = '10000'; message.style.fontSize = '16px'; message.style.boxShadow = '0 2px 10px rgba(0,0,0,0.5)'; message.style.display = 'flex'; message.style.alignItems = 'center'; message.style.gap = '10px'; const textNode = document.createElement('span'); textNode.textContent = text; const showButton = document.createElement('button'); showButton.textContent = '[Show]'; showButton.style.background = 'none'; showButton.style.color = '#0af'; showButton.style.border = 'none'; showButton.style.cursor = 'pointer'; showButton.style.fontSize = '16px'; const undoButton = document.createElement('button'); undoButton.textContent = '[Undo]'; undoButton.style.background = 'none'; undoButton.style.color = '#0af'; undoButton.style.border = 'none'; undoButton.style.cursor = 'pointer'; undoButton.style.fontSize = '16px'; showButton.addEventListener('click', () => { const filterBox = document.getElementById('megukascript-options'); const filterTabs = filterBox?.querySelectorAll('.tab-link'); const filtersTab = Array.from(filterTabs || []).find(tab => tab.dataset.id === '1'); const filterArea = document.getElementById('filterArea'); if (filterBox) filterBox.style.display = 'block'; if (filtersTab) filtersTab.click(); if (filterArea) filterArea.scrollIntoView({ behavior: 'smooth', block: 'center' }); }); undoButton.addEventListener('click', () => { undoCallback(); message.remove(); }); message.appendChild(textNode); message.appendChild(showButton); message.appendChild(undoButton); document.body.appendChild(message); const removeMessage = () => { message.remove(); document.removeEventListener('keydown', onKeyDown); }; const onKeyDown = (e) => { if (e.key === 'Escape') removeMessage(); }; document.addEventListener('keydown', onKeyDown); setTimeout(removeMessage, 10000); } function hideReplies(post) { const backlinks = post.querySelectorAll('.backlinks a.post-link[data-id]'); backlinks.forEach(link => { const replyId = link.getAttribute('data-id'); const replyPost = document.getElementById(`p${replyId}`); if (replyPost && !hiddenPosts.has(replyPost)) { const originalClass = replyPost.className; replyPost.classList.add('hidden'); hiddenPosts.set(replyPost, originalClass); } }); } function addButtons() { const dropdowns = document.querySelectorAll('ul.popup-menu.glass'); dropdowns.forEach(dropdown => { const post = dropdown.closest('article'); if (!post) return; if (!dropdown.querySelector('li[data-id="hide"]')) { const hideItem = document.createElement('li'); hideItem.setAttribute('data-id', 'hide'); hideItem.textContent = 'Hide'; hideItem.addEventListener('click', e => { e.stopPropagation(); const originalClass = post.classList.contains('postMine') ? 'glass postMine' : 'glass'; post.classList.remove('postMine'); post.classList.add('hidden'); hiddenPosts.set(post, originalClass); hideReplies(post); updateHiddenDisplay(); }); dropdown.insertBefore(hideItem, dropdown.firstChild); } if (!dropdown.querySelector('li[data-id="addHashToFilter"]')) { const hasImage = post.querySelector('figcaption a[download]'); if (hasImage) { const hashFilter = document.createElement('li'); hashFilter.setAttribute('data-id', 'addHashToFilter'); hashFilter.textContent = 'Filter Hash'; hashFilter.addEventListener('click', e => { e.stopPropagation(); const hashLine = hashFiltering(post); if (!hashLine) return; const saveButton = document.getElementById('filterArea_button'); if (saveButton) saveButton.click(); showMessage('Image Filtered. Refresh page.', () => { const filterArea = document.querySelector('#filterArea'); if (!filterArea) return; const lines = filterArea.value.split('\n').filter(line => line.trim() !== hashLine); filterArea.value = lines.join('\n'); if (saveButton) saveButton.click(); }); }); dropdown.appendChild(hashFilter); } } }); } addButtons(); const observer = new MutationObserver(addButtons); observer.observe(document.body, { childList: true, subtree: true }); function addToggleMarkButtons() { document.querySelectorAll('article.glass .popup-menu.glass').forEach(menu => { const article = menu.closest('article'); if (!article) return; let existingToggle = menu.querySelector('li[data-id="toggle-own"]'); if (!existingToggle) { const toggleItem = document.createElement('li'); toggleItem.setAttribute('data-id', 'toggle-own'); function updateToggleText() { toggleItem.textContent = article.classList.contains('postMine') ? 'Unmark post as your own' : 'Mark post as your own'; } toggleItem.addEventListener('click', () => { const postId = article.id.replace('p', ''); const replySelectors = `a.post-link[data-id="${postId}"]`; if (article.classList.contains('postMine')) { article.classList.remove('postMine'); const author = article.querySelector('b.name i'); if (author && author.textContent.trim() === '(You)') author.remove(); document.querySelectorAll(replySelectors).forEach(link => { const replyArticle = link.closest('article'); if (replyArticle) { link.innerHTML = link.innerHTML.replace(' (You)', ''); replyArticle.classList.remove('postReply'); } }); } else { article.classList.add('postMine'); const nameSpan = article.querySelector('b.name span'); if (nameSpan && !article.querySelector('b.name i')) { const youMarker = document.createElement('i'); youMarker.textContent = ' (You)'; nameSpan.insertAdjacentElement('afterend', youMarker); } document.querySelectorAll(replySelectors).forEach(link => { const replyArticle = link.closest('article'); if (replyArticle && !link.innerHTML.includes(' (You)')) { link.innerHTML = link.innerHTML.replace(/(>>\d+)/, '$1 (You)'); replyArticle.classList.add('postReply'); } }); } updateToggleText(); }); updateToggleText(); menu.appendChild(toggleItem); } }); } addToggleMarkButtons(); const toggleObserver = new MutationObserver(addToggleMarkButtons); toggleObserver.observe(document.body, { childList: true, subtree: true }); if (hiddenDisplay) { hiddenDisplay.addEventListener('click', () => { hiddenPosts.forEach((originalClass, post) => { post.className = originalClass; }); hiddenPosts.clear(); updateHiddenDisplay(); }); } })();