// ==UserScript==
// @name GreasyFork脚本过滤
// @namespace http://tampermonkey.net/
// @version 1.0
// @description GreasyFork脚本过滤器
// @author YourName
// @match https://gf.qytechs.cn/zh-CN/scripts*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
// 配置
const CONFIG = {
keywords: GM_getValue('filterKeywords', []) || [],
caseSensitive: GM_getValue('caseSensitive', false),
panelExpanded: GM_getValue('panelExpanded', true),
debug: true
};
// 添加优化后的样式 - 修复按钮动画问题
GM_addStyle(`
/* 修复侧边栏定位 */
.gf-sidebar {
position: fixed;
left: 0;
top: 50%;
transform: translateY(-50%) translateX(-100%);
z-index: 99999;
display: flex;
transition: transform 0.3s ease;
overflow: visible;
}
/* 展开状态 */
.gf-sidebar.expanded {
transform: translateY(-50%) translateX(0);
}
/* 控制面板 */
.gf-control-panel {
background: #2c3e50;
color: white;
width: 300px;
padding: 15px;
border-radius: 0 10px 10px 0;
box-shadow: 3px 3px 10px rgba(0,0,0,0.3);
display: flex;
flex-direction: column;
gap: 15px;
}
/* 面板标题 */
.gf-panel-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 10px;
border-bottom: 1px solid #3498db;
}
.gf-panel-title {
color: #3498db;
font-weight: bold;
font-size: 18px;
display: flex;
align-items: center;
gap: 8px;
}
.gf-close-btn {
background: #e74c3c;
color: white;
border: none;
width: 28px;
height: 28px;
border-radius: 50%;
font-size: 16px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
/* 输入组 */
.gf-input-group {
display: flex;
gap: 5px;
}
.gf-input-group input {
flex: 1;
padding: 10px 12px;
border: 1px solid #3498db;
border-radius: 4px;
background: rgba(0,0,0,0.2);
color: white;
font-size: 14px;
}
.gf-button {
background: #3498db;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
white-space: nowrap;
transition: background 0.3s;
flex-shrink: 0;
}
.gf-button:hover {
background: #2980b9;
}
.gf-button-add {
background: #27ae60;
}
.gf-button-clear {
background: #e74c3c;
}
/* 关键词容器 */
.gf-keywords-container {
display: flex;
flex-direction: column;
gap: 8px;
max-height: 250px;
overflow-y: auto;
padding: 5px;
}
.gf-keyword-item {
background: #3498db;
padding: 8px 12px;
border-radius: 4px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
}
.gf-remove-keyword {
background: none;
border: none;
color: white;
cursor: pointer;
font-weight: bold;
font-size: 16px;
}
.gf-remove-keyword:hover {
color: #e74c3c;
}
/* 控制按钮组 */
.gf-control-buttons {
display: flex;
gap: 10px;
margin-top: 10px;
}
.gf-toggle-container {
display: flex;
align-items: center;
padding: 8px;
background: rgba(0,0,0,0.1);
border-radius: 4px;
}
/* 改进的展开按钮设计 - 移除宽度变化的悬停效果 */
.gf-toggle-btn {
background: #95a5a6;
color: white;
border: none;
padding: 8px 12px;
border-radius: 0 4px 4px 0;
font-weight: bold;
cursor: pointer;
box-shadow: 2px 2px 5px rgba(0,0,0,0.2);
writing-mode: vertical-rl;
text-orientation: mixed;
height: 80px;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
left: 100%;
top: 50%;
transform: translateY(-50%);
z-index: 100001;
font-size: 14px;
transition: background 0.3s ease; /* 只保留背景色过渡 */
}
/* 简化悬停效果 - 仅改变背景色 */
.gf-toggle-btn:hover {
background: #7f8c8d;
}
.gf-hidden-script {
display: none !important;
}
/* 滚动条样式 */
.gf-keywords-container::-webkit-scrollbar {
width: 6px;
}
.gf-keywords-container::-webkit-scrollbar-track {
background: rgba(0,0,0,0.1);
border-radius: 3px;
}
.gf-keywords-container::-webkit-scrollbar-thumb {
background: #3498db;
border-radius: 3px;
}
.gf-keywords-container::-webkit-scrollbar-thumb:hover {
background: #2980b9;
}
`);
// 创建侧边栏
function createSidebar() {
if (document.querySelector('.gf-sidebar')) {
if (CONFIG.debug) console.log('侧边栏已存在');
return;
}
const sidebar = document.createElement('div');
sidebar.className = 'gf-sidebar';
// 初始状态
if (CONFIG.panelExpanded) {
sidebar.classList.add('expanded');
}
const controlPanel = document.createElement('div');
controlPanel.className = 'gf-control-panel';
controlPanel.innerHTML = `
<div class="gf-panel-header">
<div class="gf-panel-title">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 6H20M7 12H17M9 18H15" stroke="#3498db" stroke-width="2" stroke-linecap="round"/>
</svg>
脚本过滤设置
</div>
<button class="gf-close-btn" id="gf-close-btn">×</button>
</div>
<div class="gf-input-group">
<input type="text" id="gf-keyword-input" placeholder="输入屏蔽词..." autocomplete="off">
<button class="gf-button gf-button-add" id="gf-add-keyword">添加</button>
</div>
<div class="gf-keywords-container" id="gf-keywords-list">
<div style="color:#bdc3c7; padding:10px; text-align:center;">无屏蔽关键词</div>
</div>
<div class="gf-control-buttons">
<button class="gf-button gf-button-clear" id="gf-clear-keywords">清除所有</button>
<div class="gf-toggle-container">
<input type="checkbox" id="gf-case-sensitive" ${CONFIG.caseSensitive ? 'checked' : ''}>
<label for="gf-case-sensitive" style="margin-left: 5px; font-size: 14px;">区分大小写</label>
</div>
</div>
`;
sidebar.appendChild(controlPanel);
const toggleBtn = document.createElement('button');
toggleBtn.className = 'gf-toggle-btn';
toggleBtn.id = 'gf-toggle-btn';
toggleBtn.textContent = '过滤设置';
sidebar.appendChild(toggleBtn);
document.body.appendChild(sidebar);
if (CONFIG.debug) console.log('侧边栏已创建');
renderKeywords();
return sidebar;
}
// 渲染关键词列表
function renderKeywords() {
const container = document.getElementById('gf-keywords-list');
if (!container) return;
container.innerHTML = '';
if (CONFIG.keywords.length === 0) {
container.innerHTML = '<div style="color:#bdc3c7; padding:10px; text-align:center;">无屏蔽关键词</div>';
return;
}
CONFIG.keywords.forEach(keyword => {
const item = document.createElement('div');
item.className = 'gf-keyword-item';
item.innerHTML = `
${keyword}
<button class="gf-remove-keyword" data-keyword="${keyword}" title="删除此关键词">×</button>
`;
container.appendChild(item);
});
document.querySelectorAll('.gf-remove-keyword').forEach(btn => {
btn.addEventListener('click', function() {
const keyword = this.getAttribute('data-keyword');
removeKeyword(keyword);
});
});
}
// 添加关键词
function addKeyword(keyword) {
keyword = keyword.trim();
if (keyword && !CONFIG.keywords.includes(keyword)) {
CONFIG.keywords.push(keyword);
GM_setValue('filterKeywords', CONFIG.keywords);
renderKeywords();
filterScripts();
}
}
// 移除关键词
function removeKeyword(keyword) {
const index = CONFIG.keywords.indexOf(keyword);
if (index !== -1) {
CONFIG.keywords.splice(index, 1);
GM_setValue('filterKeywords', CONFIG.keywords);
renderKeywords();
filterScripts();
}
}
// 切换侧边栏状态
function toggleSidebar() {
const sidebar = document.querySelector('.gf-sidebar');
if (!sidebar) {
if (CONFIG.debug) console.error('侧边栏元素未找到');
return;
}
const isExpanded = sidebar.classList.contains('expanded');
if (isExpanded) {
sidebar.classList.remove('expanded');
CONFIG.panelExpanded = false;
} else {
sidebar.classList.add('expanded');
CONFIG.panelExpanded = true;
}
GM_setValue('panelExpanded', CONFIG.panelExpanded);
if (CONFIG.debug) {
console.log('侧边栏状态:', CONFIG.panelExpanded ? '展开' : '折叠');
}
}
// 简单高效的匹配函数
function containsKeyword(text, keyword, caseSensitive) {
if (!text || !keyword) return false;
return caseSensitive ?
text.indexOf(keyword) !== -1 :
text.toLowerCase().indexOf(keyword.toLowerCase()) !== -1;
}
// 高效过滤脚本 - 仅检测标题
function filterScripts() {
const caseSensitiveCheckbox = document.getElementById('gf-case-sensitive');
if (caseSensitiveCheckbox) {
CONFIG.caseSensitive = caseSensitiveCheckbox.checked;
GM_setValue('caseSensitive', CONFIG.caseSensitive);
}
const scriptItems = document.querySelectorAll('.script-list > li');
if (!scriptItems.length) return;
if (CONFIG.keywords.length === 0) {
scriptItems.forEach(item => {
item.classList.remove('gf-hidden-script');
});
return;
}
scriptItems.forEach(item => {
const titleElement = item.querySelector('h2 a');
if (!titleElement) return;
const titleText = titleElement.textContent || titleElement.innerText;
let isMatch = false;
for (const keyword of CONFIG.keywords) {
if (containsKeyword(titleText, keyword, CONFIG.caseSensitive)) {
isMatch = true;
break;
}
}
if (isMatch) {
item.classList.add('gf-hidden-script');
} else {
item.classList.remove('gf-hidden-script');
}
});
}
// 设置事件监听器
function setupEventListeners() {
// 添加关键词按钮
document.getElementById('gf-add-keyword')?.addEventListener('click', function() {
const input = document.getElementById('gf-keyword-input');
if (input) {
addKeyword(input.value.trim());
input.value = '';
input.focus();
}
});
// 输入框回车事件
document.getElementById('gf-keyword-input')?.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
document.getElementById('gf-add-keyword')?.click();
}
});
// 清除所有关键词
document.getElementById('gf-clear-keywords')?.addEventListener('click', function() {
CONFIG.keywords = [];
GM_setValue('filterKeywords', CONFIG.keywords);
renderKeywords();
filterScripts();
});
// 区分大小写切换
const caseSensitiveCheckbox = document.getElementById('gf-case-sensitive');
if (caseSensitiveCheckbox) {
caseSensitiveCheckbox.addEventListener('change', filterScripts);
}
// 切换按钮
document.getElementById('gf-toggle-btn')?.addEventListener('click', function(e) {
e.stopPropagation();
toggleSidebar();
});
// 关闭按钮
document.getElementById('gf-close-btn')?.addEventListener('click', function(e) {
e.stopPropagation();
toggleSidebar();
});
}
// 主初始化函数
function init() {
try {
if (CONFIG.debug) {
console.groupCollapsed('[GF Filter] 初始化调试信息');
console.log('匹配URL:', window.location.href);
console.log('配置:', CONFIG);
}
// 创建侧边栏
createSidebar();
// 设置事件监听
setupEventListeners();
// 初始过滤
const filterRetry = () => {
if (document.querySelector('.script-list')) {
if (CONFIG.debug) console.log('找到脚本列表,开始过滤');
filterScripts();
} else {
if (CONFIG.debug) console.log('未找到脚本列表,等待中...');
setTimeout(filterRetry, 500);
}
};
filterRetry();
if (CONFIG.debug) {
console.log('初始化完成');
console.groupEnd();
}
} catch (error) {
console.error('[脚本过滤器] 初始化错误:', error);
}
}
// 确保页面加载完成后执行
if (document.readyState === 'complete') {
init();
} else {
window.addEventListener('load', init);
document.addEventListener('DOMContentLoaded', init);
}
// 全局对象定义
window.GF_Filter = {
version: '8.0',
reload: init,
config: CONFIG,
toggleSidebar: toggleSidebar
};
})();