Simple Search Engines

简洁的适配国内地区的搜索切换脚本.

  1. // ==UserScript==
  2. // @name Simple Search Engines
  3. // @namespace https://www.iklfy.com
  4. // @version 0.2.5
  5. // @description 简洁的适配国内地区的搜索切换脚本.
  6. // @author Ancient
  7. // @match *://cn.bing.com/search*
  8. // @match *://www.baidu.com/s*
  9. // @match *://www.yandex.com/search*
  10. // @match *://www.sogou.com/web*
  11. // @match *://www.zhihu.com/search*
  12. // @match *://so.csdn.net/so/search*
  13. // @grant none
  14. // @license MIT
  15. // ==/UserScript==
  16. /**
  17. * 搜索引擎配置管理器类,用于根据浏览器类型动态调整搜索引擎配置,
  18. * 并在页面上创建一个搜索引擎切换容器,提升用户体验。
  19. */
  20. class SearchEngineManager
  21. {
  22. urlMapsConfig = {};
  23.  
  24. /**
  25. * 构造函数,初始化搜索引擎配置。
  26. * @param urlMapsConfig
  27. */
  28. constructor(urlMapsConfig)
  29. {
  30. this.urlMapsConfig = urlMapsConfig;
  31. }
  32.  
  33. /**
  34. * 从URL查询字符串中提取指定变量的值。
  35. *
  36. * @param {string} variable - 要提取的查询参数名。
  37. * @return {string|null} - 查询参数的值,若不存在则返回null。
  38. */
  39. getQueryVariable(variable)
  40. {
  41. let query = window.location.search.substring(1);
  42. if (!query) {
  43. return null;
  44. }
  45. const pairs = query.split('&');
  46. for (const pair of pairs) {
  47. const [key, value] = pair.split('=');
  48. // 对键和值都进行解码,保持一致性。
  49. const decodedKey = decodeURIComponent(key);
  50. const decodedValue = decodeURIComponent(value);
  51. if (decodedKey === variable) {
  52. return decodedValue;
  53. }
  54. }
  55. return null;
  56. }
  57.  
  58. /**
  59. * 根据当前URL获取关键词。
  60. *
  61. * @return {string} - 当前搜索的关键词。
  62. */
  63. getKeywords()
  64. {
  65. for (const item of this.urlMapsConfig) {
  66. if (item.testUrl.test(window.location.href)) {
  67. return this.getQueryVariable(item.keyName);
  68. }
  69. }
  70. return '';
  71. }
  72.  
  73. /**
  74. * 检测是否为Firefox浏览器并相应调整配置。
  75. */
  76. checkAndAdjustForFirefox()
  77. {
  78. // 使用功能检测代替User-Agent检测
  79. if ('MozWebSocket' in window) { // 假设Firefox特有的API是MozWebSocket
  80. console.info('[ Firefox ] 🚀');
  81. if (this.urlMapsConfig.length > 0) {
  82. this.urlMapsConfig[0].searchUrl = 'https://www.baidu.com/baidu?wd=';
  83. this.urlMapsConfig[0].testUrl = /https:\/\/www\.baidu\.com\/baidu.*/;
  84. }
  85. }
  86. }
  87.  
  88. /**
  89. * 添加样式
  90. */
  91. addStyleToHead()
  92. {
  93. // 检查是否已存在该样式,如果不存在再进行添加
  94. if (!document.getElementById('search-container-style')) {
  95. const style = document.createElement('style');
  96. style.id = 'search-container-style';
  97. // 将样式内容赋值给style节点的textContent,代替innerHTML,提高安全性
  98. style.textContent = `
  99. #search-container{width:80px;background-color:#f1f6f9d9;z-index:99999;position:fixed;display:flex;align-items:center;justify-content:center;padding:10px 0;top:150px;left:50px;border-radius:10px}
  100. #search-container ul{padding:initial;margin:initial}
  101. #search-container li.title{font-weight:700;user-select:none}
  102. #search-container li{display:block;margin:8px 0;text-align:center}
  103. #search-container a{color:#24578f;display:block}
  104. `;
  105. // 将style节点添加到head中
  106. document.getElementsByTagName('head')[0].appendChild(style);
  107. }
  108. }
  109.  
  110. /**
  111. * 添加容器
  112. */
  113. createSearchContainer()
  114. {
  115. this.checkAndAdjustForFirefox();
  116. // div#search-container
  117. const container = document.createElement('div');
  118. container.id = 'search-container';
  119. document.body.insertBefore(container, document.body.firstChild);
  120. //document.body.insertAdjacentElement('afterbegin', container);
  121. // ul
  122. const ul = document.createElement('ul');
  123. container.appendChild(ul);
  124. // li.title
  125. let titleLi = document.createElement('li');
  126. titleLi.textContent = 'Engine';
  127. titleLi.className = 'title';
  128. ul.appendChild(titleLi);
  129. // 优化DOM操作
  130. const fragment = document.createDocumentFragment();
  131. // 搜索列表
  132. this.urlMapsConfig.forEach(item =>
  133. {
  134. // li > a
  135. const li = document.createElement('li');
  136. const a = document.createElement('a');
  137. a.textContent = item.name;
  138. a.className = 'search-engine-a';
  139. a.href = `${item.searchUrl}${this.getKeywords()}`;
  140. // ul > li > a
  141. li.appendChild(a);
  142. fragment.appendChild(li);
  143. });
  144. ul.appendChild(fragment);
  145. }
  146.  
  147. /**
  148. * 初始化并运行搜索容器的创建流程。
  149. */
  150. initialize()
  151. {
  152. this.addStyleToHead();
  153. this.createSearchContainer();
  154. }
  155. }
  156.  
  157. (function ()
  158. {
  159. 'use strict';
  160. /**
  161. * 用于配置URL映射的对象。每个映射包含名称、键名、搜索URL字符串和测试URL的正则表达式。
  162. *
  163. * @typedef {Object} urlMapsConfig
  164. * @property {string} name - 映射的名称。不能为空。
  165. * @property {string} keyName - 映射的键名。不能为空。
  166. * @property {string} searchUrl - 用于搜索的URL字符串。必须是合法的URL格式。
  167. * @property {RegExp} testUrl - 用于测试URL是否匹配的正则表达式对象。必须是有效的正则表达式。
  168. */
  169. const urlMapsConfig = [
  170. {
  171. name: 'Bing', searchUrl: 'https://cn.bing.com/search?q=', keyName: 'q', testUrl: /https:\/\/cn.bing.com\/search.*/
  172. }, {
  173. name: '百度', searchUrl: 'https://www.baidu.com/s?wd=', keyName: 'wd', testUrl: /https:\/\/www.baidu.com\/s.*/
  174. }, {
  175. name: 'Yandex', searchUrl: 'https://www.yandex.com/search/?text=', keyName: 'text', testUrl: /https:\/\/www.yandex.com\/search.*/
  176. }, {
  177. name: '搜狗', searchUrl: 'https://www.sogou.com/web?query=', keyName: 'query', testUrl: /https:\/\/www.sogou.com\/web.*/
  178. }, {
  179. name: '知乎', searchUrl: 'https://www.zhihu.com/search?q=', keyName: 'q', testUrl: /https:\/\/www.zhihu.com\/search.*/
  180. }, {
  181. name: 'CSDN', searchUrl: 'https://so.csdn.net/so/search?q=', keyName: 'q', testUrl: /https:\/\/so.csdn.net\/so\/search.*/
  182. }
  183. ];
  184.  
  185. /**
  186. * 初始化管理器
  187. * @returns {Promise<void>}
  188. */
  189. async function initializeManager()
  190. {
  191. const manager = new SearchEngineManager(urlMapsConfig);
  192. try {
  193. // 使用async-await优化异步逻辑
  194. await manager.initialize();
  195. console.log('Manager initialized successfully.');
  196. } catch (error) {
  197. console.error('Error initializing manager:', error);
  198. }
  199. }
  200.  
  201. /**
  202. * 确保只添加一个事件监听器,避免内存泄露
  203. */
  204. window.addEventListener('load', () =>
  205. {
  206. initializeManager().catch(error =>
  207. {
  208. console.error('Error during manager initialization:', error);
  209. });
  210. }, {once: true});
  211. })();

QingJ © 2025

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