Block unwanted scripts on GreasyFork. Non-Latin filter, custom keywords, and per-script hiding. Compatible with most Greasyfork enhancement scripts.
// ==UserScript==
// @name Simple GreasyFork Script Filter
// @name:zh-CN 简单 GreasyFork 脚本过滤器
// @name:zh-TW 簡單 GreasyFork 腳本過濾器
// @name:ja シンプル GreasyFork スクリプトフィルター
// @description Block unwanted scripts on GreasyFork. Non-Latin filter, custom keywords, and per-script hiding. Compatible with most Greasyfork enhancement scripts.
// @description:zh-CN 在 GreasyFork 上屏蔽不需要的脚本。非拉丁字符过滤、自定义关键词、按脚本隐藏。兼容大部分 Greasyfork 美化与增强脚本。
// @description:zh-TW 在 GreasyFork 上封鎖不需要的腳本。非拉丁字元過濾、自訂關鍵詞、按腳本隱藏。兼容大部分 Greasyfork 美化与增强脚本。
// @description:ja GreasyFork で不要なスクリプトをブロック。非ラテン文字フィルター、カスタムキーワード、スクリプトごとの非表示。大部分 Greasyfork ページをサポート。
// @namespace simple_greasyfork_script_filter
// @version 1.1.0
// @license Apache-2.0
// @author Ckrvxr
// @homepage https://github.com/Ckrvxr/simple_greasyfork_script_filter_js
// @match https://greatest.deepsurf.us/*
// @run-at document-end
// @grant none
// ==/UserScript==
(function () {
'use strict';
/* ---- i18n ---- */
var LOCALES = {
'en': {
groupTitle: 'Script Filter:',
blacklisted: 'Filter by Regex',
hidden: 'Filter by Script ID',
dateFiltered: 'Filter by Update Date',
filterSettings: 'Script Filter Settings',
nonLatin: 'Non-Latin Filter',
nonLatinNote: 'Block scripts with non-Latin characters in title or description',
dateFilter: 'Filter by Update Date',
dateFilterNote: 'Hide scripts updated before the specified date',
datePlaceholder: 'YYYY-MM-DD',
regexMatch: 'Filter by Regex',
regexMatchPlace: 're/^test/i\nLearning Tool\nOnline Course',
regexMatchNote: 'Each line is a regex or keyword. Matching scripts will be filtered.',
hideScripts: 'Filter by Script ID',
hideAddPlace: 'Add Script ID',
hideSearchPlace: 'Search script name or ID ...',
cancel: 'Cancel',
save: 'Save',
hide: 'Filter',
unhide: 'Unfilter'
},
'zh-Hans': {
groupTitle: '脚本过滤器:',
blacklisted: '按正则表达式过滤',
hidden: '按脚本ID过滤',
dateFiltered: '按更新日期过滤',
filterSettings: '脚本过滤器设置',
nonLatin: '过滤非拉丁字符脚本',
nonLatinNote: '屏蔽标题或描述含非拉丁字符的脚本',
dateFilter: '按更新日期过滤',
dateFilterNote: '隐藏指定日期之前最后更新的脚本',
datePlaceholder: '年-月-日',
regexMatch: '按正则表达式匹配过滤',
regexMatchPlace: 're/^test/i\n学习通\n网课',
regexMatchNote: '每行一个正则或关键词。匹配中脚本名称或描述的会被过滤。',
hideScripts: '按脚本ID过滤',
hideAddPlace: '添加脚本ID',
hideSearchPlace: '搜索脚本名称或 ID ...',
cancel: '取消',
save: '保存',
hide: '过滤',
unhide: '取消过滤'
},
'zh-Hant': {
groupTitle: '腳本過濾器:',
blacklisted: '按正則表達式過濾',
hidden: '按腳本ID過濾',
dateFiltered: '按更新日期過濾',
filterSettings: '腳本過濾器設定',
nonLatin: '過濾非拉丁字元腳本',
nonLatinNote: '封鎖標題或描述含非拉丁字元的腳本',
dateFilter: '按更新日期過濾',
dateFilterNote: '隱藏指定日期之前最後更新的腳本',
datePlaceholder: '年-月-日',
regexMatch: '按正則表達式匹配過濾',
regexMatchPlace: 're/^test/i\n學習通\n網課',
regexMatchNote: '每行一個正則或關鍵詞。匹配腳本名稱或描述的將被封鎖。',
hideScripts: '按腳本ID過濾',
hideAddPlace: '添加腳本ID',
hideSearchPlace: '搜索腳本名稱或 ID ...',
cancel: '取消',
save: '儲存',
hide: '過濾',
unhide: '取消過濾'
},
'ja': {
groupTitle: 'スクリプトフィルター:',
blacklisted: '正規表現でフィルター',
hidden: 'スクリプトIDでフィルター',
dateFiltered: '更新日でフィルター',
filterSettings: 'スクリプトフィルター設定',
nonLatin: '非ラテン文字をフィルター',
nonLatinNote: 'タイトルまたは説明に非ラテン文字を含むスクリプトをブロック',
dateFilter: '更新日でフィルター',
dateFilterNote: '指定日以前に更新されたスクリプトを非表示',
datePlaceholder: 'YYYY-MM-DD',
regexMatch: '正規表現でフィルター',
regexMatchPlace: 're/^test/i\n学習通\nネット授業',
regexMatchNote: '各行に正規表現またはキーワード。一致するスクリプトはフィルターされます。',
hideScripts: 'スクリプトIDでフィルター',
hideAddPlace: 'スクリプトIDを追加',
hideSearchPlace: 'スクリプト名またはIDを検索...',
cancel: 'キャンセル',
save: '保存',
hide: 'フィルター',
unhide: 'フィルター解除'
}
};
var langAlias = {
'zh-CN': 'zh-Hans',
'zh-MO': 'zh-Hans',
'zh-SG': 'zh-Hans',
'zh-MY': 'zh-Hans',
'zh-TW': 'zh-Hant',
'zh-HK': 'zh-Hant'
};
var lang = (document.documentElement.lang || 'en').replace(/_/g, '-');
lang = langAlias[lang] || lang;
if (!LOCALES[lang]) { var l2 = lang.substring(0, 2); lang = LOCALES[l2] ? l2 : 'en'; }
function T(k) { return (LOCALES[lang] || LOCALES.en)[k] || k; }
/* ---- filter definitions ---- */
var FILTERS = {
nonLatin: /[^\p{Script=Latin}\p{Script=Common}\p{Script=Inherited}]/gu,
};
/* ---- persistence ---- */
var CFG = {
nonLatins: false,
customBlacklist: '',
hiddenList: [],
cutoffDate: ''
};
function hiddenIndex(id) {
for (var i = 0; i < CFG.hiddenList.length; i++) {
if (CFG.hiddenList[i].id === id) return i;
}
return -1;
}
function loadCfg() {
try {
CFG.nonLatins = JSON.parse(localStorage.getItem('gf-blocker-nonLatins') || 'false');
CFG.customBlacklist = localStorage.getItem('gf-blocker-customBlacklist') || 're/VIP视频|全网VIP|视频解析|vip视频|会员视频解析|学习通|网课助手|[刷网]课|自动[答刷]题|超星|知到|智慧树|自动刷课/i';
CFG.cutoffDate = localStorage.getItem('gf-blocker-cutoffDate') || '';
var hl = JSON.parse(localStorage.getItem('gf-blocker-hiddenList') || '[]');
if (Array.isArray(hl)) {
CFG.hiddenList = hl.map(function (e) {
if (typeof e === 'number' && !isNaN(e)) return { id: e, name: '' };
if (e && typeof e.id === 'number' && !isNaN(e.id)) return { id: e.id, name: e.name || '' };
return null;
}).filter(function (e) { return e; });
} else {
CFG.hiddenList = [];
}
} catch (e) { CFG.hiddenList = []; }
}
function saveCfg() {
localStorage.setItem('gf-blocker-nonLatins', JSON.stringify(CFG.nonLatins));
localStorage.setItem('gf-blocker-customBlacklist', CFG.customBlacklist);
localStorage.setItem('gf-blocker-cutoffDate', CFG.cutoffDate);
localStorage.setItem('gf-blocker-hiddenList', JSON.stringify(CFG.hiddenList));
}
loadCfg();
/* ---- custom blacklist parser ---- */
var customBlacklistFn = null;
function buildCustomBlacklist(txt) {
if (!txt) { customBlacklistFn = null; return; }
var reArr = [];
txt = txt.replace(/\uE084/g, '\uE084x');
var c = 800;
while (c--) {
var i = txt.search(/\bre\//);
if (i < 0) break;
var s = txt.substring(i + 3);
var j = -1;
var g = /(.?)\//g, m;
while (m = g.exec(s)) { if (m[1] !== '\\') { j = g.lastIndex + i + 3; break; } }
if (j < 0) break;
var o = (/^[a-z]+/.exec(txt.substring(j)) || [''])[0];
var rc = txt.substring(i + 3, j - 1);
txt = txt.substring(0, i) + '\uE084' + reArr.length + 'r' + txt.substring(j + o.length);
reArr.push(new RegExp(rc, o));
}
var rules = txt.replace(/[,、]/g, ',').split(/,|\n/).map(function (e) { return e.trim(); }).filter(function (e) { return e; });
customBlacklistFn = function (text) {
var t0 = text.replace(/\uE084/g, '\uE084x');
for (var k = 0; k < rules.length; k++) {
var r = rules[k];
var match = false;
if (!r.includes('\uE084')) {
match = text.toLowerCase('en-US').includes(r.toLowerCase('en-US'));
} else {
var parts = r.split(/\uE084(\d+)r/);
match = parts.every(function (p, idx) {
if (!p || !p.length) return true;
if (idx % 2) return reArr[+p].test(t0);
return t0.includes(p.trim());
});
}
if (match) return k;
}
};
}
buildCustomBlacklist(CFG.customBlacklist);
/* ---- CSS ---- */
var CSS = '\
.script-list li.bl-blacklisted,\
.script-list li.bl-hidden { display:none }\
.script-list li.bl-blacklisted { background:rgba(180,40,40,0.06); border-left:3px solid rgba(180,40,40,0.35) }\
.script-list li.bl-hidden { background:rgba(70,70,200,0.06); border-left:3px solid rgba(70,70,200,0.35) }\
.script-list li.bl-dt { background:rgba(40,180,40,0.06); border-left:3px solid rgba(40,180,40,0.35) }\
.bl-btn {\
cursor:pointer; font-size:0.75rem; white-space:nowrap; display:inline-flex; align-items:center;\
padding:0.2em 0.65em; border-radius:4px; border:1px solid #ccc;\
background:linear-gradient(180deg,#f0f0ff,#e0e0f0); color:#222;\
margin-left:0.6em; user-select:none\
}\
.bl-btn:hover { border-color:#66f; background:linear-gradient(180deg,#e0e0ff,#d0d0f0) }\
html { --bl-bl-display:none; --bl-hl-display:none; --bl-dt-display:none }\
[bl-bl-shown] { --bl-bl-display:list-item }\
[bl-hl-shown] { --bl-hl-display:list-item }\
[bl-dt-shown] { --bl-dt-display:list-item }\
.script-list li.bl-blacklisted { display:var(--bl-bl-display,none) !important }\
.script-list li.bl-hidden { display:var(--bl-hl-display,none) !important }\
.script-list li.bl-dt { display:var(--bl-dt-display,none) !important }\
#bl-bl-opt, #bl-hl-opt, #bl-dt-opt { border:none!important; outline:none!important; box-shadow:none!important; background:none!important; color:inherit!important; opacity:0.5 }\
[bl-bl-shown] #bl-bl-opt { opacity:1 }\
[bl-hl-shown] #bl-hl-opt { opacity:1 }\
[bl-dt-shown] #bl-dt-opt { opacity:1 }\
';
function injectCSS() {
var s = document.createElement('style');
s.textContent = CSS;
(document.head || document.documentElement).appendChild(s);
}
/* ---- filtering ---- */
function hideBL(element, list) {
if (!element) return;
var name = (element.querySelector('.script-link') || {}).textContent || '';
var desc = (element.querySelector('.script-description') || {}).textContent || '';
if (!name) return;
switch (list) {
case 'nl':
if (FILTERS.nonLatin.test(name) || FILTERS.nonLatin.test(desc))
element.classList.add('bl-blacklisted', 'bl-nl');
break;
case 'cbl':
if (customBlacklistFn && (customBlacklistFn(name) >= 0 || customBlacklistFn(desc) >= 0))
element.classList.add('bl-blacklisted', 'bl-cbl');
break;
case 'dt':
var updated = element.getAttribute('data-script-updated-date');
if (updated && CFG.cutoffDate && updated < CFG.cutoffDate)
element.classList.add('bl-dt');
break;
}
}
function hideOne(element, id) {
id = +id;
if (!(id >= 0)) return;
var idx = hiddenIndex(id);
var hidden = idx !== -1;
if (hidden) element.classList.add('bl-hidden');
var name = (element.querySelector('.script-link') || {}).textContent || '';
var btn = document.createElement('span');
btn.className = 'bl-btn';
btn.textContent = hidden ? T('unhide') : T('hide');
btn.addEventListener('click', function (e) {
e.stopPropagation();
e.stopImmediatePropagation();
var i = hiddenIndex(id);
if (i === -1) { CFG.hiddenList.push({ id: id, name: name }); element.classList.add('bl-hidden'); btn.textContent = T('unhide'); }
else { CFG.hiddenList.splice(i, 1); element.classList.remove('bl-hidden'); btn.textContent = T('hide'); }
saveCfg();
});
var badge = element.querySelector('.badge-js, .badge-css');
if (badge) badge.parentNode.insertBefore(btn, badge.nextSibling);
else { var h2 = element.querySelector('h2'); if (h2) h2.appendChild(btn); }
}
function processList(list) {
if (!list || !list.isConnected) return;
var items = list.querySelectorAll('li[data-script-id]:not([bl-done])');
for (var i = 0; i < items.length; i++) {
var el = items[i];
el.setAttribute('bl-done', '1');
var sid = +el.getAttribute('data-script-id');
if (!(sid > 0)) continue;
if (CFG.nonLatins) hideBL(el, 'nl');
if (customBlacklistFn) hideBL(el, 'cbl');
if (CFG.cutoffDate) hideBL(el, 'dt');
hideOne(el, sid);
}
updateSidebar();
}
function processDiscussions(list) {
if (!list || !list.isConnected) return;
var items = list.querySelectorAll('.discussion-list-item:not([bl-done])');
for (var i = 0; i < items.length; i++) {
var item = items[i];
item.setAttribute('bl-done', '1');
var link = item.querySelector('a.script-link');
if (!link) continue;
var m = /\/scripts\/(\d+)/.exec(link.getAttribute('href') || '');
var id = m ? +m[1] : 0;
if (id > 0 && hiddenIndex(id) !== -1) {
(item.closest('.discussion-list-container') || item).classList.add('bl-hidden');
}
}
}
/* ---- sidebar ---- */
function addSidebar() {
var g = document.querySelector('.list-option-groups');
if (!g || document.getElementById('bl-group')) return;
var d = document.createElement('div');
d.className = 'list-option-group';
d.id = 'bl-group';
d.innerHTML = T('groupTitle') + '<ul>\
<li class="list-option"><a href="#" id="bl-bl-opt">' + T('blacklisted') + ' (0)</a></li>\
<li class="list-option"><a href="#" id="bl-dt-opt">' + T('dateFiltered') + ' (0)</a></li>\
<li class="list-option"><a href="#" id="bl-hl-opt">' + T('hidden') + ' (0)</a></li>\
<li class="list-option"><a href="#" id="bl-set">' + T('filterSettings') + '</a></li></ul>';
var f = g.querySelector('div');
(f || g).parentNode.insertBefore(d, f || null);
var toggle = function (t) {
var a = t === 'bl' ? 'bl-bl-shown' : t === 'hl' ? 'bl-hl-shown' : 'bl-dt-shown';
if (document.documentElement.hasAttribute(a)) document.documentElement.removeAttribute(a);
else document.documentElement.setAttribute(a, '');
};
(document.getElementById('bl-bl-opt') || {}).addEventListener('click', function (e) { e.preventDefault(); toggle('bl'); });
(document.getElementById('bl-hl-opt') || {}).addEventListener('click', function (e) { e.preventDefault(); toggle('hl'); });
(document.getElementById('bl-dt-opt') || {}).addEventListener('click', function (e) { e.preventDefault(); toggle('dt'); });
(document.getElementById('bl-set') || {}).addEventListener('click', function (e) { e.preventDefault(); openSettings(); });
}
function updateSidebar() {
var bl = document.getElementById('bl-bl-opt');
var hl = document.getElementById('bl-hl-opt');
var dt = document.getElementById('bl-dt-opt');
if (bl) bl.textContent = T('blacklisted') + ' (' + document.querySelectorAll('.script-list li.bl-blacklisted').length + ')';
if (hl) hl.textContent = T('hidden') + ' (' + document.querySelectorAll('.script-list li.bl-hidden').length + ')';
if (dt) dt.textContent = T('dateFiltered') + ' (' + document.querySelectorAll('.script-list li.bl-dt').length + ')';
}
/* ---- settings modal ---- */
function openSettings() {
if (document.getElementById('bl-modal')) return;
var c = document.createElement('div');
c.id = 'bl-modal';
c.innerHTML = '\
<div style="position:fixed;inset:0;background:rgba(0,0,0,0.5);z-index:9999;display:flex;align-items:center;justify-content:center">\
<div style="background:#fff;max-width:460px;width:90%;border-radius:8px;padding:24px;box-shadow:0 20px 40px rgba(0,0,0,0.3);max-height:90vh;overflow-y:auto;color:#222;font-family:sans-serif;font-size:14px;line-height:1.5">\
<h2 style="margin:0 0 16px;font-size:18px">' + T('filterSettings') + '</h2>\
<div style="margin-bottom:12px"><label style="display:flex;align-items:center;gap:6px"><input type="checkbox" id="bl-f-nl"' + (CFG.nonLatins ? ' checked' : '') + '> ' + T('nonLatin') + '</label><span style="font-size:12px;color:#888">' + T('nonLatinNote') + '</span></div>\
<div style="margin-bottom:12px"><label style="display:block;margin-bottom:4px;font-weight:600">' + T('dateFilter') + '</label><input type="date" id="bl-f-dt" value="' + (CFG.cutoffDate || '') + '" style="width:100%;box-sizing:border-box;padding:10px 12px;border:1px solid #ccc;border-radius:4px;font:inherit;font-size:14px"><span style="font-size:12px;color:#888">' + T('dateFilterNote') + '</span></div>\
<div style="margin-bottom:12px"><label style="display:block;margin-bottom:4px;font-weight:600">' + T('regexMatch') + '</label><textarea id="bl-f-cbl" rows="3" placeholder="' + T('regexMatchPlace') + '" style="width:100%;box-sizing:border-box;padding:10px 12px;border:1px solid #ccc;border-radius:4px;font:inherit;font-size:14px;font-family:monospace">' + (CFG.customBlacklist || '') + '</textarea><span style="font-size:12px;color:#888">' + T('regexMatchNote') + '</span></div>\
<div style="margin-bottom:12px"><label style="display:block;margin-bottom:4px;font-weight:600">' + T('hideScripts') + '</label>\
<div style="display:flex;gap:4px;margin-bottom:6px">\
<input type="text" id="bl-hl-add" placeholder="' + T('hideAddPlace') + '" style="flex:1;padding:6px 8px;border:1px solid #ccc;border-radius:4px;font-family:monospace;font-size:13px">\
<button id="bl-hl-add-btn" style="padding:6px 12px;border:1px solid #ccc;background:#f5f5f5;border-radius:4px;cursor:pointer;font-size:13px">+</button></div>\
<input type="text" id="bl-hl-search" placeholder="' + T('hideSearchPlace') + '" style="width:100%;box-sizing:border-box;padding:6px 8px;border:1px solid #ccc;border-radius:4px;font-size:13px;margin-bottom:6px">\
<div id="bl-hl-items" style="max-height:300px;overflow-y:auto;border:1px solid #eee;border-radius:4px;font-family:monospace;font-size:13px"></div></div>\
<div style="text-align:right;margin-top:16px">\
<button id="bl-cancel" style="padding:8px 16px;border:1px solid #ccc;background:#f5f5f5;border-radius:4px;cursor:pointer;margin-right:8px">' + T('cancel') + '</button>\
<button id="bl-save" style="padding:8px 16px;border:none;background:#4f46e5;color:#fff;border-radius:4px;cursor:pointer;font-weight:600">' + T('save') + '</button></div></div></div>';
document.body.appendChild(c);
var _hl = CFG.hiddenList.map(function (e) { return { id: e.id, name: e.name }; });
var _search = '';
function renderHL() {
var container = document.getElementById('bl-hl-items');
if (!container) return;
var q = _search.toLowerCase();
var filtered = _hl.filter(function (e) {
return !q || ('' + e.id).indexOf(q) !== -1 || (e.name || '').toLowerCase().indexOf(q) !== -1;
});
container.innerHTML = filtered.map(function (e) {
var text = e.id + (e.name ? ' ' + e.name : '');
return '<div style="display:flex;align-items:center;padding:4px 8px;border-bottom:1px solid #f0f0f0">\
<span style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + text + '</span>\
<span class="bl-hl-del" data-id="' + e.id + '" style="cursor:pointer;color:#999;padding:0 4px;font-family:sans-serif">✕</span></div>';
}).join('');
Array.prototype.forEach.call(container.querySelectorAll('.bl-hl-del'), function (el) {
el.addEventListener('click', function () {
var id = +this.getAttribute('data-id');
for (var k = 0; k < _hl.length; k++) {
if (_hl[k].id === id) { _hl.splice(k, 1); break; }
}
renderHL();
});
});
}
renderHL();
document.getElementById('bl-hl-search').addEventListener('input', function () {
_search = this.value;
renderHL();
});
document.getElementById('bl-hl-add-btn').addEventListener('click', function () {
var input = document.getElementById('bl-hl-add');
var val = input.value.trim();
if (!val) return;
var n = parseInt(val);
if (!isNaN(n)) {
if (_hl.some(function (e) { return e.id === n; })) { input.value = ''; return; }
_hl.push({ id: n, name: '' });
input.value = '';
renderHL();
}
});
document.getElementById('bl-hl-add').addEventListener('keydown', function (e) {
if (e.key === 'Enter') document.getElementById('bl-hl-add-btn').click();
});
var close = function () { c.remove(); };
c.querySelector('div').addEventListener('click', function (e) { if (e.target === e.currentTarget) close(); });
document.getElementById('bl-cancel').addEventListener('click', close);
document.getElementById('bl-save').addEventListener('click', function () {
CFG.nonLatins = document.getElementById('bl-f-nl').checked;
CFG.customBlacklist = document.getElementById('bl-f-cbl').value || '';
CFG.cutoffDate = document.getElementById('bl-f-dt').value || '';
CFG.hiddenList = _hl;
saveCfg();
buildCustomBlacklist(CFG.customBlacklist);
close();
window.location.reload();
});
}
/* ---- init ---- */
function init() {
injectCSS();
var sl = document.querySelector('.script-list');
if (sl) {
addSidebar();
processList(sl);
new MutationObserver(function () { processList(sl); }).observe(sl, { childList: true, subtree: true });
}
var dl = document.querySelector('.discussion-list');
if (dl) {
processDiscussions(dl);
new MutationObserver(function () { processDiscussions(dl); }).observe(dl, { childList: true, subtree: true });
}
}
if (document.readyState === 'loading') window.addEventListener('DOMContentLoaded', init, { once: true });
else init();
})();