0.1% Chance Global Element Deletion with Enhanced Persistence

Gives a 0.1% chance to delete anything on any page, remembers deletions globally using multiple methods, and monitors dynamically loaded elements.

当前为 2024-08-27 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name 0.1% Chance Global Element Deletion with Enhanced Persistence
  3. // @namespace Fists
  4. // @version 1.5
  5. // @description Gives a 0.1% chance to delete anything on any page, remembers deletions globally using multiple methods, and monitors dynamically loaded elements.
  6. // @author You
  7. // @license CC BY 4.0; https://creativecommons.org/licenses/by/4.0/
  8. // @match *://*/*
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. const STORAGE_KEY = 'globalDeletedNodes_v4';
  16.  
  17. // Load deleted nodes from localStorage
  18. let deletedNodes = JSON.parse(localStorage.getItem(STORAGE_KEY)) || [];
  19.  
  20. // Function to generate XPath for a node
  21. function getXPathForElement(element) {
  22. const idx = (sib, name) => sib
  23. ? idx(sib.previousElementSibling, name || sib.localName) + (sib.localName == name)
  24. : 1;
  25. const segs = elm => !elm || elm.nodeType !== 1
  26. ? ['']
  27. : elm.id && document.getElementById(elm.id) === elm
  28. ? [`id("${elm.id}")`]
  29. : [elm.localName.toLowerCase() + (elm.className ? `[@class="${elm.className}"]` : '') + `[${idx(elm)}]`, ...segs(elm.parentNode)];
  30. return segs(element).join('/');
  31. }
  32.  
  33. // Function to generate a selector for a node
  34. function getSelectorForElement(element) {
  35. if (element.id) {
  36. return `#${element.id}`;
  37. }
  38. if (element.className) {
  39. return `.${element.className.split(' ').join('.')}`;
  40. }
  41. return element.tagName.toLowerCase();
  42. }
  43.  
  44. // Function to generate a robust identifier for a node
  45. function getNodeIdentifier(element) {
  46. const xpath = getXPathForElement(element);
  47. const selector = getSelectorForElement(element);
  48. const tagInfo = `${element.tagName}_${element.className}_${element.id}_${element.name}_${element.innerText.length}`;
  49. return { xpath, selector, tagInfo };
  50. }
  51.  
  52. // Function to check if a node has been previously deleted
  53. function isNodeDeleted(identifiers) {
  54. return deletedNodes.some(deleted =>
  55. deleted.xpath === identifiers.xpath ||
  56. deleted.selector === identifiers.selector ||
  57. deleted.tagInfo === identifiers.tagInfo
  58. );
  59. }
  60.  
  61. // Function to store deleted node identifiers
  62. function storeDeletedNode(identifiers) {
  63. if (!isNodeDeleted(identifiers)) {
  64. deletedNodes.push(identifiers);
  65. localStorage.setItem(STORAGE_KEY, JSON.stringify(deletedNodes));
  66. }
  67. }
  68.  
  69. // Function to delete an element by various methods
  70. function deleteElement(element, identifiers) {
  71. if (element) {
  72. element.remove();
  73. storeDeletedNode(identifiers);
  74. console.log('Element deleted and stored:', identifiers);
  75. }
  76. }
  77.  
  78. // Function to apply deletions using stored identifiers
  79. function applyStoredDeletions() {
  80. deletedNodes.forEach(identifiers => {
  81. // Try XPath first
  82. const xpathResult = document.evaluate(identifiers.xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
  83. let node = xpathResult.singleNodeValue;
  84.  
  85. // Try CSS Selector if XPath fails
  86. if (!node) {
  87. node = document.querySelector(identifiers.selector);
  88. }
  89.  
  90. // Final fallback: Tag information search
  91. if (!node) {
  92. const allNodes = document.querySelectorAll('*');
  93. node = [...allNodes].find(el => getNodeIdentifier(el).tagInfo === identifiers.tagInfo);
  94. }
  95.  
  96. // Delete the node if found
  97. if (node) {
  98. deleteElement(node, identifiers);
  99. }
  100. });
  101. }
  102.  
  103. // Function to attempt deletion of any node
  104. function tryDeleteNode(node) {
  105. const identifiers = getNodeIdentifier(node);
  106. if (Math.random() < 0.001 && !isNodeDeleted(identifiers)) { // 0.1% chance
  107. deleteElement(node, identifiers);
  108. }
  109. }
  110.  
  111. // Observer to watch for new elements, attributes, and nodes being added or modified in the DOM
  112. const observer = new MutationObserver(mutations => {
  113. mutations.forEach(mutation => {
  114. mutation.addedNodes.forEach(node => {
  115. if (node.nodeType === Node.ELEMENT_NODE) {
  116. tryDeleteNode(node);
  117. }
  118. });
  119. });
  120. });
  121.  
  122. // Configuration to observe child nodes and text content
  123. observer.observe(document.documentElement, {
  124. childList: true,
  125. subtree: true,
  126. characterData: true
  127. });
  128.  
  129. // Initial pass to try deleting elements that are already loaded
  130. const allNodes = document.querySelectorAll('*');
  131. allNodes.forEach(element => {
  132. tryDeleteNode(element);
  133. });
  134.  
  135. // Apply deletions from stored identifiers
  136. applyStoredDeletions();
  137. })();