Gmail & Outlook Image n Javascript Disabler

Disables tracking in Gmail and Outlook by blocking images and JavaScript in email content areas

  1. // ==UserScript==
  2. // @name Gmail & Outlook Image n Javascript Disabler
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description Disables tracking in Gmail and Outlook by blocking images and JavaScript in email content areas
  6. // @author Minoa
  7. // @match https://mail.google.com/*
  8. // @match https://outlook.office365.com/*
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. // Configuration for different email clients
  17. const CONFIG = {
  18. gmail: {
  19. container: '.AO .Tm.aeJ',
  20. observer: true
  21. },
  22. outlook: {
  23. container: '#ConversationReadingPaneContainer.MtujV',
  24. observer: true
  25. }
  26. };
  27.  
  28. // CSS to block images and iframes
  29. const style = document.createElement('style');
  30. style.textContent = `
  31. /* Gmail - only target elements within email content */
  32. .AO .Tm.aeJ .a3s img,
  33. .AO .Tm.aeJ .a3s iframe,
  34. .AO .Tm.aeJ .a3s script,
  35. .AO .Tm.aeJ .a3s [style*="background-image"] {
  36. display: none !important;
  37. }
  38. .AO .Tm.aeJ .a3s [style*="background"] {
  39. background-image: none !important;
  40. }
  41.  
  42. /* Outlook - only target elements within email content */
  43. #ConversationReadingPaneContainer.MtujV .allowTextSelection img,
  44. #ConversationReadingPaneContainer.MtujV .allowTextSelection iframe,
  45. #ConversationReadingPaneContainer.MtujV .allowTextSelection script,
  46. #ConversationReadingPaneContainer.MtujV .allowTextSelection [style*="background-image"] {
  47. display: none !important;
  48. }
  49. #ConversationReadingPaneContainer.MtujV .allowTextSelection [style*="background"] {
  50. background-image: none !important;
  51. }
  52.  
  53. /* Placeholder for blocked content */
  54. .blocked-content-placeholder {
  55. display: inline-block;
  56. padding: 5px 10px;
  57. background: #f1f1f1;
  58. border: 1px solid #ddd;
  59. border-radius: 3px;
  60. color: #666;
  61. font-size: 12px;
  62. margin: 5px 0;
  63. cursor: pointer;
  64. transition: background-color 0.2s ease;
  65. }
  66. .blocked-content-placeholder:hover {
  67. background: #e5e5e5;
  68. }
  69. `;
  70. document.head.appendChild(style);
  71.  
  72. // Function to process elements and block content
  73. function processElement(element) {
  74. if (!element) return;
  75.  
  76. // Remove all script tags
  77. element.querySelectorAll('script').forEach(script => script.remove());
  78.  
  79. // Replace images with placeholders
  80. element.querySelectorAll('img').forEach(img => {
  81. const placeholder = document.createElement('div');
  82. placeholder.className = 'blocked-content-placeholder';
  83. placeholder.textContent = '🖼️';
  84. placeholder.dataset.originalSrc = img.src;
  85. placeholder.addEventListener('click', function() {
  86. if (this.classList.contains('unblocked')) {
  87. this.textContent = '🖼️';
  88. this.classList.remove('unblocked');
  89. } else {
  90. const img = document.createElement('img');
  91. img.src = this.dataset.originalSrc;
  92. this.textContent = '';
  93. this.appendChild(img);
  94. this.classList.add('unblocked');
  95. }
  96. });
  97. img.parentNode.replaceChild(placeholder, img);
  98. });
  99.  
  100. // Remove background images from elements
  101. element.querySelectorAll('[style*="background"]').forEach(el => {
  102. if (!el.dataset.originalBackground) {
  103. el.dataset.originalBackground = el.style.backgroundImage;
  104. }
  105. el.style.backgroundImage = 'none';
  106. if (!el.classList.contains('blocked-content-placeholder')) {
  107. el.addEventListener('click', function() {
  108. if (this.classList.contains('unblocked')) {
  109. this.style.backgroundImage = 'none';
  110. this.classList.remove('unblocked');
  111. } else {
  112. this.style.backgroundImage = this.dataset.originalBackground;
  113. this.classList.add('unblocked');
  114. }
  115. });
  116. }
  117. });
  118.  
  119. // Handle iframes
  120. element.querySelectorAll('iframe').forEach(iframe => {
  121. const placeholder = document.createElement('div');
  122. placeholder.className = 'blocked-content-placeholder';
  123. placeholder.textContent = '🔲 Frame';
  124. placeholder.dataset.originalSrc = iframe.src;
  125. placeholder.addEventListener('click', function() {
  126. if (this.classList.contains('unblocked')) {
  127. this.textContent = '🔲 Frame';
  128. this.classList.remove('unblocked');
  129. } else {
  130. const iframe = document.createElement('iframe');
  131. iframe.src = this.dataset.originalSrc;
  132. this.textContent = '';
  133. this.appendChild(iframe);
  134. this.classList.add('unblocked');
  135. }
  136. });
  137. iframe.parentNode.replaceChild(placeholder, iframe);
  138. });
  139. }
  140.  
  141. // Function to initialize observers
  142. function initializeObserver(config) {
  143. const observer = new MutationObserver((mutations) => {
  144. mutations.forEach(mutation => {
  145. if (mutation.type === 'childList') {
  146. processElement(mutation.target);
  147. }
  148. });
  149. });
  150.  
  151. // Start observing the container
  152. const container = document.querySelector(config.container);
  153. if (container) {
  154. processElement(container);
  155. observer.observe(container, {
  156. childList: true,
  157. subtree: true
  158. });
  159. }
  160.  
  161. return observer;
  162. }
  163.  
  164. // Initialize based on current page
  165. let currentConfig;
  166. if (window.location.hostname.includes('mail.google.com')) {
  167. currentConfig = CONFIG.gmail;
  168. } else if (window.location.hostname.includes('outlook.office365.com')) {
  169. currentConfig = CONFIG.outlook;
  170. }
  171.  
  172. if (currentConfig) {
  173. // Initial processing
  174. const container = document.querySelector(currentConfig.container);
  175. if (container) {
  176. processElement(container);
  177. }
  178.  
  179. // Set up observer for dynamic content
  180. if (currentConfig.observer) {
  181. // Wait for the container to be available
  182. const checkContainer = setInterval(() => {
  183. const container = document.querySelector(currentConfig.container);
  184. if (container) {
  185. clearInterval(checkContainer);
  186. initializeObserver(currentConfig);
  187. }
  188. }, 1000);
  189.  
  190. // Clear interval after 30 seconds to prevent infinite checking
  191. setTimeout(() => clearInterval(checkContainer), 30000);
  192. }
  193. }
  194. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址