DuckDuckGo 增强

简单有效的全网通用护眼模式(夜间模式、暗黑模式、深色模式)

当前为 2023-01-13 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name DuckDuckGo 增强
  3. // @name:zh-CN DuckDuckGo 增强
  4. // @name:zh-TW DuckDuckGo 增強
  5. // @name:en DuckDuckGo Enhancements
  6. // @version 1.0.2
  7. // @author X.I.U
  8. // @description 屏蔽指定域名、修复图标加载、链接不携来源、快捷回到顶部(右键两侧空白处)
  9. // @description:zh-CN 简单有效的全网通用护眼模式(夜间模式、暗黑模式、深色模式)
  10. // @description:zh-TW 屏蔽指定域名、修復圖標加載、鏈接不攜來源、快捷回到頂部(右鍵兩側空白處)
  11. // @description:en Block the specified domain name, fix icon loading, link without source, and quickly return to the top (the blank space on both sides of the right button)...
  12. // @match https://duckduckgo.com/*
  13. // @icon https://duckduckgo.com/favicon.ico
  14. // @grant GM_registerMenuCommand
  15. // @grant GM_unregisterMenuCommand
  16. // @grant GM_openInTab
  17. // @grant GM_getValue
  18. // @grant GM_setValue
  19. // @grant GM_notification
  20. // @license GPL-3.0 License
  21. // @run-at document-idle
  22. // @namespace https://github.com/XIU2/UserScript
  23. // @supportURL https://github.com/XIU2/UserScript
  24. // @homepageURL https://github.com/XIU2/UserScript
  25. // ==/UserScript==
  26.  
  27. (function() {
  28. 'use strict';
  29. var menu_ALL = [
  30. ['menu_blockDomainBtn', '显示屏蔽按钮', '显示屏蔽按钮', true],
  31. ['menu_blockDomain', '编辑屏蔽域名', '编辑屏蔽域名', []],
  32. ['menu_backToTop', '快捷回到顶部', '快捷回到顶部', true]
  33. ], menu_ID = [];
  34. for (let i=0;i<menu_ALL.length;i++){if (GM_getValue(menu_ALL[i][0]) == null){GM_setValue(menu_ALL[i][0], menu_ALL[i][3])};}
  35. registerMenuCommand();
  36.  
  37. // 注册脚本菜单
  38. function registerMenuCommand() {
  39. // 如果菜单ID数组多于菜单数组,说明不是首次添加菜单,需要卸载所有脚本菜单
  40. if (menu_ID.length > menu_ALL.length){for (let i=0;i<menu_ID.length;i++){GM_unregisterMenuCommand(menu_ID[i]);}}
  41. // 循环注册脚本菜单
  42. for (let i=0;i<menu_ALL.length;i++){
  43. if (menu_ALL[i][0] === 'menu_blockDomain') {
  44. menu_ID[i] = GM_registerMenuCommand(`#️⃣ ${menu_ALL[i][1]}`, function(){customBlockDomain()});
  45. } else {
  46. menu_ID[i] = GM_registerMenuCommand(`${GM_getValue(menu_ALL[i][0])?'✅':'❌'} ${menu_ALL[i][1]}`, function(){menu_switch(GM_getValue(menu_ALL[i][0]), menu_ALL[i][0], menu_ALL[i][2])});
  47. }
  48. }
  49. menu_ID[menu_ID.length] = GM_registerMenuCommand('💬 反馈 & 建议', function () {GM_openInTab('https://github.com/XIU2/UserScript#xiu2userscript', {active: true,insert: true,setParent: true}); GM_openInTab('https://greasyfork.org/zh-CN/scripts/436428/feedback', {active: true,insert: true,setParent: true});});
  50. }
  51.  
  52. // 菜单开关
  53. function menu_switch(Status, Name, Tips) {
  54. if (Status == true) {GM_setValue(Name, false); GM_notification({text: `已关闭 [${Tips}] 功能\n(点击刷新网页后生效)`, timeout: 3500, onclick: function(){location.reload();}});} else {GM_setValue(Name, true); GM_notification({text: `已开启 [${Tips}] 功能\n(点击刷新网页后生效)`, timeout: 3500, onclick: function(){location.reload();}});}
  55. registerMenuCommand();
  56. };
  57.  
  58.  
  59. document.documentElement.appendChild(document.createElement('style')).textContent = '.blockDomainBtn {padding: 0 8px !important; font-size: 12px !important; line-height: normal !important; margin-left: 10px !important; border-radius: 3px !important; vertical-align: top !important; opacity: 0.4 !important; top: 3px; cursor: cell;} .result.result--sep--hr {display: none;} a[data-testid="result-title-a"]{display: inline-block}';
  60. mutationObserver(); // 屏蔽指定域名 + 修复图标加载 + 链接不携来源
  61. backToTop(); // 快捷回到顶部
  62.  
  63.  
  64. // 自定义屏蔽指定域名
  65. function customBlockDomain() {
  66. let nowBlockDomain = '';
  67. GM_getValue('menu_blockDomain').forEach(function(item){nowBlockDomain += '|' + item})
  68. let newBlockDomain = prompt('编辑 [屏蔽指定域名]\n(不同域名之间使用 "|" 分隔,例如:a.com|b.com|c.com )', nowBlockDomain.replace('|',''));
  69. if (newBlockDomain === '') {
  70. GM_setValue('menu_blockDomain', []);
  71. registerMenuCommand();
  72. } else if (newBlockDomain != null) {
  73. GM_setValue('menu_blockDomain', newBlockDomain.split('|'));
  74. registerMenuCommand();
  75. }
  76. }
  77.  
  78.  
  79. // 屏蔽指定域名 + 修复图标加载 + 链接不携来源
  80. function mutationObserver() {
  81. const callback = (mutationsList, observer) => {
  82. for (const mutation of mutationsList) {
  83. for (const target of mutation.addedNodes) {
  84. if (target.nodeType != 1) break
  85.  
  86. // 屏蔽指定域名
  87. if (target.dataset.nrn === 'result') {
  88. const a=target.querySelector('h2>a,a[data-testid="result-title-a]"')
  89. if (a && checkDomain(a.href.split('/')[2])) {
  90. target.remove(); break;
  91. } else {
  92. // 链接不携来源
  93. addRel(target);
  94.  
  95. // 添加屏蔽按钮
  96. addBlockDomainBtn(target, a, a.href.split('/')[2]);
  97. }
  98. }
  99. // 修复图标加载
  100. //let img = target.querySelector('img.result__icon__img[data-src]'); // 寻找图标元素
  101. //if (img && !img.src) img.src = img.dataset.src
  102. }
  103. }
  104. };
  105. const observer = new MutationObserver(callback);
  106. observer.observe(document, { childList: true, subtree: true });
  107. }
  108.  
  109.  
  110. // 检查域名是否存在黑名单中
  111. function checkDomain(domain) {
  112. let blockDomain = GM_getValue('menu_blockDomain');
  113. for (let i=0; i<blockDomain.length; i++) {
  114. if (domain === blockDomain[i]) return true
  115. }
  116. return false
  117. }
  118.  
  119.  
  120. // 添加 rel 属性
  121. function addRel(doc) {
  122. doc.querySelectorAll('a').forEach(function(one){one.rel = 'noreferrer noopener nofollow'})
  123. }
  124.  
  125.  
  126. // 添加屏蔽按钮
  127. function addBlockDomainBtn(doc, toElement, domain) {
  128. if (!GM_getValue('menu_blockDomainBtn')) return
  129. //let toElement = doc.querySelector('a.result__url');
  130. if (toElement) {
  131. toElement.insertAdjacentHTML('afterend', `<button class="btn blockDomainBtn" data-domain="${domain}" title="点击在搜索结果中屏蔽 [ ${domain} ] 域名">屏蔽</button>`);
  132. doc.querySelector('button.blockDomainBtn').addEventListener('click', function(e) {
  133. e.stopPropagation();
  134. // 追加屏蔽域名
  135. let blockDomain = GM_getValue('menu_blockDomain');
  136. blockDomain.push(e.target.dataset.domain)
  137. GM_setValue('menu_blockDomain', blockDomain);
  138. // 隐藏该域名的所有搜索结果
  139. document.querySelectorAll(`button[data-domain="${e.target.dataset.domain}"].blockDomainBtn`).forEach(function(one){one.parentElement.parentElement.parentElement.parentElement.remove();})
  140. });
  141. }
  142. }
  143.  
  144.  
  145. // 快捷回到顶部(右键两侧空白处)
  146. function backToTop() {
  147. if (!GM_getValue('menu_backToTop')) return
  148. document.querySelectorAll('#web_content_wrapper, #web_content_wrapper > .cw, #links_wrapper').forEach(ele => {
  149. ele.oncontextmenu = function(e) {
  150. if (e.target == this) {
  151. e.preventDefault();
  152. window.scrollTo(0,0);
  153. }
  154. }
  155. })
  156. }
  157. })();