-
Notifications
You must be signed in to change notification settings - Fork 1
/
profanity_filter.user.js
executable file
·182 lines (133 loc) · 5.79 KB
/
profanity_filter.user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// ==UserScript==
// @name Profanity Filter
// @author adisib
// @namespace namespace_adisib
// @description Simple filtering for profanity from website text. Not limited to static text, while avoiding performance impact.
// @version 2018.10.10
// @include http://*
// @include https://*
// @grant none
// ==/UserScript==
(function() {
"use strict";
// --- SETTINGS --------
// The string that replaces offending words.
const replaceString = "*bleep*";
// If useCustomWords is true, then customWords is used as the word list and the default list will not be used. Otherwise, it uses a pre-compiled version of the default list for performance.
// The words list does not have to include endings like plurals or "ing", as they will always be handled.
// The default list is: ['fuck','shit','ass','damn','asshole','bullshit','bitch','piss','goddamn','crap','sh!t','bastard','dumbass','fag','motherfuck','nigger','cunt','douche','douchebag','jackass','mothafuck','pissoff','shitfull','fuk','fuckme','fucktard','fvck','fcuk','b!tch','phuq','phuk','phuck','fatass','faggot','dipshit','fagot','faggit','fagget','assfuck','buttfuck','asswipe','asskiss','assclown']
// This should be ordered by most common first for performance, and must only contain alpha-numeric (unless you sanitize for regex)
const useCustomWords = false;
const customWords = [];
// Display performance and debugging information to the console.
const DEBUG = false;
// --------------------
let wordString = useCustomWords ? "\\b(?:" + customWords.join("|") + ")[tgkp]??(?=(?:ing?(?:ess)??|ed|i??er|a)??(?:e??[syz])??\\b)" : "\\b(?:(?:f(?:u(?:ck(?:me|tard)??|k)|a(?:g(?:(?:g[eio]|o)t)??|tass)|(?:cu|vc)k)|b(?:u(?:llshit|ttfuck)|[!i]tch|astard)|ass(?:(?:hol|wip)e|clown|fuck|kiss)??|d(?:amn|umbass|ouche(?:bag)??|ipshit)|p(?:hu(?:c?k|q)|iss(?:off)??)|sh(?:it(?:full)??|!t)|moth(?:er|a)fuck|c(?:rap|unt)|goddamn|jackass|nigg))[tgkp]??(?=(?:ing?(?:ess)??|ed|i??er|a)??(?:e??[syz])??\\b)";
const wordsFilter = new RegExp(wordString, "gi");
wordString = null;
const findText = document.createExpression(".//text()[string-length() > 2 and not(parent::script or parent::code)]", null);
// Initial slow filter pass that handles static text
function filterStaticText()
{
let startTime, endTime;
if (DEBUG)
{
startTime = performance.now();
}
// Do title first because it is always visible
if (wordsFilter.test(document.title))
{
document.title = document.title.replace(wordsFilter, replaceString);
}
filterNodeTree(document.body);
if (DEBUG)
{
endTime = performance.now();
console.log("PF | Static Text Run-Time (ms): " + (endTime - startTime).toString());
}
}
// --------------------
// filters dynamic text, and handles things such as AJAX Youtube comments
function filterDynamicText()
{
let textMutationObserver = new MutationObserver(filterMutations);
let TxMOInitOps = { characterData: true, childList: true, subtree: true };
textMutationObserver.observe(document.body, TxMOInitOps);
let title = document.getElementsByTagName("title")[0];
if (title)
{
let titleMutationObserver = new MutationObserver( function(mutations) { filterNode(title); } );
let TiMOInitOps = { characterData: true, subtree: true };
titleMutationObserver.observe(title, TiMOInitOps);
}
}
// --------------------
// Handler for mutation observer from filterDynamicText()
function filterMutations(mutations)
{
let startTime, endTime;
if (DEBUG)
{
startTime = performance.now();
}
for (let i = 0; i < mutations.length; ++i)
{
let mutation = mutations[i];
if (mutation.type === "childList")
{
let nodes = mutation.addedNodes;
for (let j = 0; j < nodes.length; ++j)
{
filterNodeTree(nodes[j]);
}
}
else if (mutation.type === "characterData" && !mutation.target.parentNode.isContentEditable)
{
filterNode(mutation.target);
}
}
if (DEBUG)
{
endTime = performance.now();
console.log("PF | Dynamic Text Run-Time (ms): " + (endTime - startTime).toString());
}
}
// --------------------
// Filters a textNode
function filterNode(node)
{
if (wordsFilter.test(node.data))
{
node.data = node.data.replace(wordsFilter, replaceString);
}
}
// --------------------
// Filters all of the text from a node and its decendants
function filterNodeTree(node)
{
if (!node || (node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.TEXT_NODE))
{
return;
}
if (node.nodeType === Node.TEXT_NODE)
{
filterNode(node);
return; // text nodes don't have children
}
let textNodes = findText.evaluate(node, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
const l = textNodes.snapshotLength;
for (let i = 0; i < l; ++i)
{
filterNode(textNodes.snapshotItem(i));
}
}
// --------------------
// Runs the different filter types
function filterPage()
{
filterStaticText();
filterDynamicText();
}
// --- MAIN -----------
filterPage();
})();