1+ // ==UserScript==
2+ // @name DuckDuckGo 增强
3+ // @version 1.0.0
4+ // @author X.I.U
5+ // @description 屏蔽指定域名、修复图标加载、链接不携来源、快捷回到顶部(右键两侧空白处)
6+ // @match https://duckduckgo.com/*
7+ // @icon https://duckduckgo.com/favicon.ico
8+ // @grant GM_registerMenuCommand
9+ // @grant GM_unregisterMenuCommand
10+ // @grant GM_openInTab
11+ // @grant GM_getValue
12+ // @grant GM_setValue
13+ // @grant GM_notification
14+ // @license GPL-3.0 License
15+ // @run -at document-end
16+ // @namespace https://github.com/XIU2/UserScript
17+ // @supportURL https://github.com/XIU2/UserScript
18+ // @homepageURL https://github.com/XIU2/UserScript
19+ // ==/UserScript==
20+
21+ ( function ( ) {
22+ 'use strict' ;
23+ var menu_ALL = [
24+ [ 'menu_blockDomainBtn' , '显示屏蔽按钮' , '显示屏蔽按钮' , true ] ,
25+ [ 'menu_blockDomain' , '编辑屏蔽域名' , '编辑屏蔽域名' , [ ] ] ,
26+ [ 'menu_backToTop' , '快捷回到顶部' , '快捷回到顶部' , true ]
27+ ] , menu_ID = [ ] ;
28+ 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 ] ) } ; }
29+ registerMenuCommand ( ) ;
30+
31+ // 注册脚本菜单
32+ function registerMenuCommand ( ) {
33+ // 如果菜单ID数组多于菜单数组,说明不是首次添加菜单,需要卸载所有脚本菜单
34+ if ( menu_ID . length > menu_ALL . length ) { for ( let i = 0 ; i < menu_ID . length ; i ++ ) { GM_unregisterMenuCommand ( menu_ID [ i ] ) ; } }
35+ // 循环注册脚本菜单
36+ for ( let i = 0 ; i < menu_ALL . length ; i ++ ) {
37+ if ( menu_ALL [ i ] [ 0 ] === 'menu_blockDomain' ) {
38+ menu_ID [ i ] = GM_registerMenuCommand ( `#️⃣ ${ menu_ALL [ i ] [ 1 ] } ` , function ( ) { customBlockDomain ( ) } ) ;
39+ } else {
40+ 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 ] ) } ) ;
41+ }
42+ }
43+ menu_ID [ menu_ID . length ] = GM_registerMenuCommand ( '💬 反馈 & 建议' , function ( ) { window . GM_openInTab ( 'https://github.com/XIU2/UserScript#xiu2userscript' , { active : true , insert : true , setParent : true } ) ; window . GM_openInTab ( 'https://greasyfork.org/zh-CN/scripts/436428/feedback' , { active : true , insert : true , setParent : true } ) ; } ) ;
44+ }
45+
46+ // 菜单开关
47+ function menu_switch ( Status , Name , Tips ) {
48+ 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 ( ) ; } } ) ; }
49+ registerMenuCommand ( ) ;
50+ } ;
51+
52+
53+ 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;}' ;
54+ mutationObserver ( ) ; // 屏蔽指定域名 + 修复图标加载 + 链接不携来源
55+ backToTop ( ) ; // 快捷回到顶部
56+
57+
58+ // 自定义屏蔽指定域名
59+ function customBlockDomain ( ) {
60+ let nowBlockDomain = '' ;
61+ GM_getValue ( 'menu_blockDomain' ) . forEach ( function ( item ) { nowBlockDomain += '|' + item } )
62+ let newBlockDomain = prompt ( '编辑 [屏蔽指定域名]\n(不同域名之间使用 "|" 分隔,例如:a.com|b.com|c.com )' , nowBlockDomain . replace ( '|' , '' ) ) ;
63+ if ( newBlockDomain === '' ) {
64+ GM_setValue ( 'menu_blockDomain' , [ ] ) ;
65+ registerMenuCommand ( ) ;
66+ } else if ( newBlockDomain != null ) {
67+ GM_setValue ( 'menu_blockDomain' , newBlockDomain . split ( '|' ) ) ;
68+ registerMenuCommand ( ) ;
69+ }
70+ }
71+
72+
73+ // 屏蔽指定域名 + 修复图标加载 + 链接不携来源
74+ function mutationObserver ( ) {
75+ const callback = ( mutationsList , observer ) => {
76+ for ( const mutation of mutationsList ) {
77+ for ( const target of mutation . addedNodes ) {
78+ if ( target . nodeType != 1 ) break
79+
80+ // 屏蔽指定域名
81+ if ( target . dataset . domain && checkDomain ( target . dataset . domain ) ) { target . remove ( ) ; break ; }
82+
83+ // 修复图标加载
84+ let img = target . querySelector ( 'img.result__icon__img[data-src]' ) ; // 寻找图标元素
85+ if ( img && ! img . src ) img . src = img . dataset . src
86+
87+ // 链接不携来源
88+ addRel ( target ) ;
89+
90+ // 添加屏蔽按钮
91+ addBlockDomainBtn ( target , target . dataset . domain ) ;
92+ }
93+ }
94+ } ;
95+ const observer = new MutationObserver ( callback ) ;
96+ observer . observe ( document , { childList : true , subtree : true } ) ;
97+ }
98+
99+
100+ // 检查域名是否存在黑名单中
101+ function checkDomain ( domain ) {
102+ let blockDomain = GM_getValue ( 'menu_blockDomain' ) ;
103+ for ( let i = 0 ; i < blockDomain . length ; i ++ ) {
104+ if ( domain === blockDomain [ i ] ) return true
105+ }
106+ return false
107+ }
108+
109+
110+ // 添加 rel 属性
111+ function addRel ( doc ) {
112+ doc . querySelectorAll ( 'a' ) . forEach ( function ( one ) { one . rel = 'noreferrer noopener nofollow' } )
113+ }
114+
115+
116+ // 添加屏蔽按钮
117+ function addBlockDomainBtn ( doc , domain ) {
118+ if ( ! GM_getValue ( 'menu_blockDomainBtn' ) ) return
119+ let toElement = doc . querySelector ( 'a.result__url' ) ;
120+ if ( toElement ) {
121+ toElement . insertAdjacentHTML ( 'afterend' , `<button class="btn blockDomainBtn" data-domain="${ domain } " title="点击在搜索结果中屏蔽 [ ${ domain } ] 域名">屏蔽</button>` ) ;
122+ doc . querySelector ( 'button.blockDomainBtn' ) . addEventListener ( 'click' , function ( e ) {
123+ e . stopPropagation ( ) ;
124+ // 追加屏蔽域名
125+ let blockDomain = GM_getValue ( 'menu_blockDomain' ) ;
126+ blockDomain . push ( e . target . dataset . domain )
127+ GM_setValue ( 'menu_blockDomain' , blockDomain ) ;
128+ // 隐藏该域名的所有搜索结果
129+ document . querySelectorAll ( `#links > div[data-domain="${ e . target . dataset . domain } "]` ) . forEach ( function ( one ) { one . style . display = 'none' } )
130+ } ) ;
131+ }
132+ }
133+
134+
135+ // 快捷回到顶部(右键两侧空白处)
136+ function backToTop ( ) {
137+ if ( ! GM_getValue ( 'menu_backToTop' ) ) return
138+ document . getElementById ( 'web_content_wrapper' ) . oncontextmenu = document . getElementById ( 'links_wrapper' ) . oncontextmenu = function ( e ) {
139+ if ( e . target == this ) {
140+ e . preventDefault ( ) ;
141+ window . scrollTo ( 0 , 0 ) ;
142+ }
143+ }
144+ }
145+ } ) ( ) ;
0 commit comments