Profanity Filter

Simple filtering for profanity from website text. Not limited to static text, while avoiding performance impact.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

You will need to install an extension such as Tampermonkey to install this script.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Zateb bir user-style yöneticim var, yükleyeyim!)

  1. // ==UserScript==
  2. // @name Profanity Filter
  3. // @author adisib
  4. // @namespace namespace_adisib
  5. // @description Simple filtering for profanity from website text. Not limited to static text, while avoiding performance impact.
  6. // @version 2018.10.10
  7. // @include http://*
  8. // @include https://*
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13.  
  14. "use strict";
  15.  
  16.  
  17. // --- SETTINGS --------
  18.  
  19.  
  20. // The string that replaces offending words.
  21. const replaceString = "*bleep*";
  22.  
  23. // 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.
  24. // The words list does not have to include endings like plurals or "ing", as they will always be handled.
  25. // 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']
  26. // This should be ordered by most common first for performance, and must only contain alpha-numeric (unless you sanitize for regex)
  27. const useCustomWords = false;
  28. const customWords = [];
  29.  
  30. // Display performance and debugging information to the console.
  31. const DEBUG = false;
  32.  
  33.  
  34. // --------------------
  35.  
  36.  
  37. 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)";
  38. const wordsFilter = new RegExp(wordString, "gi");
  39. wordString = null;
  40.  
  41. const findText = document.createExpression(".//text()[string-length() > 2 and not(parent::script or parent::code)]", null);
  42.  
  43.  
  44. // Initial slow filter pass that handles static text
  45. function filterStaticText()
  46. {
  47. let startTime, endTime;
  48. if (DEBUG)
  49. {
  50. startTime = performance.now();
  51. }
  52.  
  53. // Do title first because it is always visible
  54. if (wordsFilter.test(document.title))
  55. {
  56. document.title = document.title.replace(wordsFilter, replaceString);
  57. }
  58.  
  59. filterNodeTree(document.body);
  60.  
  61. if (DEBUG)
  62. {
  63. endTime = performance.now();
  64. console.log("PF | Static Text Run-Time (ms): " + (endTime - startTime).toString());
  65. }
  66. }
  67.  
  68.  
  69. // --------------------
  70.  
  71.  
  72. // filters dynamic text, and handles things such as AJAX Youtube comments
  73. function filterDynamicText()
  74. {
  75. let textMutationObserver = new MutationObserver(filterMutations);
  76. let TxMOInitOps = { characterData: true, childList: true, subtree: true };
  77. textMutationObserver.observe(document.body, TxMOInitOps);
  78.  
  79. let title = document.getElementsByTagName("title")[0];
  80. if (title)
  81. {
  82. let titleMutationObserver = new MutationObserver( function(mutations) { filterNode(title); } );
  83. let TiMOInitOps = { characterData: true, subtree: true };
  84. titleMutationObserver.observe(title, TiMOInitOps);
  85. }
  86. }
  87.  
  88.  
  89. // --------------------
  90.  
  91.  
  92. // Handler for mutation observer from filterDynamicText()
  93. function filterMutations(mutations)
  94. {
  95. let startTime, endTime;
  96. if (DEBUG)
  97. {
  98. startTime = performance.now();
  99. }
  100.  
  101. for (let i = 0; i < mutations.length; ++i)
  102. {
  103. let mutation = mutations[i];
  104.  
  105. if (mutation.type === "childList")
  106. {
  107. let nodes = mutation.addedNodes;
  108. for (let j = 0; j < nodes.length; ++j)
  109. {
  110. filterNodeTree(nodes[j]);
  111. }
  112. }
  113. else if (mutation.type === "characterData" && !mutation.target.parentNode.isContentEditable)
  114. {
  115. filterNode(mutation.target);
  116. }
  117. }
  118.  
  119. if (DEBUG)
  120. {
  121. endTime = performance.now();
  122. console.log("PF | Dynamic Text Run-Time (ms): " + (endTime - startTime).toString());
  123. }
  124. }
  125.  
  126.  
  127. // --------------------
  128.  
  129.  
  130. // Filters a textNode
  131. function filterNode(node)
  132. {
  133. if (wordsFilter.test(node.data))
  134. {
  135. node.data = node.data.replace(wordsFilter, replaceString);
  136. }
  137. }
  138.  
  139.  
  140. // --------------------
  141.  
  142.  
  143. // Filters all of the text from a node and its decendants
  144. function filterNodeTree(node)
  145. {
  146. if (!node || (node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.TEXT_NODE))
  147. {
  148. return;
  149. }
  150.  
  151. if (node.nodeType === Node.TEXT_NODE)
  152. {
  153. filterNode(node);
  154. return; // text nodes don't have children
  155. }
  156.  
  157. let textNodes = findText.evaluate(node, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  158.  
  159. const l = textNodes.snapshotLength;
  160. for (let i = 0; i < l; ++i)
  161. {
  162. filterNode(textNodes.snapshotItem(i));
  163. }
  164. }
  165.  
  166.  
  167. // --------------------
  168.  
  169.  
  170. // Runs the different filter types
  171. function filterPage()
  172. {
  173. filterStaticText();
  174. filterDynamicText();
  175. }
  176.  
  177.  
  178. // --- MAIN -----------
  179.  
  180. filterPage();
  181.  
  182. })();