Search Cross

不同搜索引擎间的切换,自用

  1. // ==UserScript==
  2. // @name Search Cross
  3. // @namespace https://github.com/saplf/search-cross
  4. // @version 0.8
  5. // @description 不同搜索引擎间的切换,自用
  6. // @author saplf
  7. // @license GPL-3.0
  8. // @supportURL https://github.com/saplf/search-cross
  9. // @home-url https://gf.qytechs.cn/zh-CN/scripts/389989-search-cross
  10. // @match *://www.baidu.com/s?*
  11. // @match *://www.google.com/search?*
  12. // @match *://www.bing.com/search?*
  13. // @match *://www.so.com/s?*
  14. // @match *://github.com/search?*
  15. // @match *://www.zhihu.com/search?*
  16. // @match *://search.bilibili.com/*
  17. // @match *://zh.wikipedia.org/wiki/*
  18. // @match *://www.sogou.com/web?*
  19. // @match *://www.douban.com/search?*
  20. // @match *://mijisou.com/?*
  21. // @match *://duckduckgo.com/?*
  22. // @match *://s.taobao.com/search?*
  23. // @note 2020.01.10-v0.3 修复github下样式问题
  24. // @note 2020.06.29-v0.4 切换图标源,减小源码体积;添加中文维基
  25. // @note 2020.06.29-v0.5 由于 Github 的安全策略,外部样式代码改由代码下载
  26. // @note 2020.06.29-v0.6 添加部分搜索引擎
  27. // @note 2020.07.27-v0.7 添加部分搜索引擎;添加设置面板
  28. // @note 2020.09.28-v0.8 修复 Safari 浏览器不支持反向预查正则的 bug
  29. // @grant GM_addStyle
  30. // @grant GM_setValue
  31. // @grant GM_getValue
  32. // @grant GM_info
  33. // @grant GM_xmlhttpRequest
  34. // @connect at.alicdn.com
  35. // @run-at document-end
  36. // ==/UserScript==
  37.  
  38. var config = {
  39. default: {
  40. position: 'left', // 'left' or 'right'
  41. height: 54,
  42. top: '120px',
  43. peekSize: 30,
  44. delayEnter: 120,
  45. delayLeave: 400,
  46. zIndex: 9999,
  47. triggleVer: '10px',
  48. triggleHor: '20px',
  49. },
  50. 'www.google.com': {
  51. top: '140px',
  52. },
  53. };
  54.  
  55. var engines = {
  56. 'www.baidu.com': {
  57. name: '百度',
  58. icon: 'sc-baidu',
  59. url: 'https://www.baidu.com/s?wd={q}',
  60. match: /(?:wd=)([^&]+)(?=&|$)/,
  61. },
  62. 'www.google.com': {
  63. name: 'Google',
  64. icon: 'sc-google',
  65. url: 'https://www.google.com/search?q={q}',
  66. match: /(?:q=)([^&]+)(?=&|$)/,
  67. },
  68. 'www.bing.com': {
  69. name: 'Bing',
  70. icon: 'sc-bing',
  71. url: 'https://cn.bing.com/search?q={q}',
  72. match: /(?:q=)([^&]+)(?=&|$)/,
  73. },
  74. 'github.com': {
  75. name: 'GitHub',
  76. icon: 'sc-github',
  77. url: 'https://github.com/search?q={q}',
  78. match: /(?:q=)([^&]+)(?=&|$)/,
  79. },
  80. 'www.zhihu.com': {
  81. name: '知乎',
  82. icon: 'sc-zhihu',
  83. url: 'https://www.zhihu.com/search?q={q}',
  84. match: /(?:q=)([^&]+)(?=&|$)/,
  85. },
  86. 'search.bilibili.com': {
  87. name: 'bilibili',
  88. icon: 'sc-bilibili',
  89. url: 'https://search.bilibili.com/all?keyword={q}',
  90. match: /(?:keyword=)([^&]+)(?=&|$)/,
  91. },
  92. 'zh.wikipedia.org': {
  93. name: '维基中文',
  94. icon: 'sc-wiki',
  95. url: 'https://zh.wikipedia.org/wiki/{q}',
  96. match: /(?:wiki\/)([^?&]+)(?=&|$|\?)/,
  97. },
  98. 'www.so.com': {
  99. name: '360',
  100. icon: 'sc-360',
  101. url: 'https://www.so.com/s?q={q}',
  102. match: /(?:q=)([^&]+)(?=&|$)/,
  103. },
  104. 'www.sogou.com': {
  105. name: '搜狗',
  106. icon: 'sc-sougou',
  107. url: 'https://www.sogou.com/web?query={q}',
  108. match: /(?:query=)([^&]+)(?=&|$)/,
  109. },
  110. 'www.douban.com': {
  111. name: '豆瓣',
  112. icon: 'sc-douban',
  113. url: 'https://www.douban.com/search?q={q}',
  114. match: /(?:q=)([^&]+)(?=&|$)/,
  115. },
  116. 'mijisou.com': {
  117. name: '秘迹',
  118. icon: 'sc-mj',
  119. url: 'https://mijisou.com/?q={q}',
  120. match: /(?:q=)([^&]+)(?=&|$)/,
  121. },
  122. 'duckduckgo.com': {
  123. name: 'Duck',
  124. icon: 'sc-ddg',
  125. url: 'https://duckduckgo.com/?q={q}',
  126. match: /(?:q=)([^&]+)(?=&|$)/,
  127. },
  128. 's.taobao.com': {
  129. name: '淘宝',
  130. icon: 'sc-taobao',
  131. url: 'https://s.taobao.com/search?q={q}',
  132. match: /(?:q=)([^&]+)(?=&|$)/,
  133. },
  134. };
  135. var enginesArray = Object.entries(engines);
  136.  
  137. // var configCached = GM_getValue('config', config);
  138. // GM_setValue('config', configCached);
  139. // var setting = Object.assign(config.default, configCached.default, configCached[location.host]);
  140. // engines = GM_getValue('sites', engines);
  141. // GM_setValue('sites', engines);
  142. var setting = config.default;
  143.  
  144. // 标记用于重置存储数据的计数量
  145. var appClearCounter = 1;
  146. var cacheClearCounter = GM_getValue('cacheClearCounter', 0);
  147. var enabledSites;
  148. if (cacheClearCounter < appClearCounter) {
  149. GM_setValue('cacheClearCounter', appClearCounter);
  150. enabledSites = enginesArray.map(function (it) { return it[0] });
  151. } else {
  152. enabledSites = GM_getValue('enabledSites', enginesArray.map(function (it) { return it[0] }));
  153. }
  154.  
  155. function appendStyles() {
  156. var isLeft = setting.position === 'left';
  157. var offsetSignal = isLeft ? '-' : '';
  158. GM_addStyle(`
  159. #sc-panel {
  160. position: fixed;
  161. ${setting.position}: ${setting.peekSize}px;
  162. top: ${setting.top};
  163. padding: 0 20px 0 80px;
  164. transform: translate(${offsetSignal}100%, -50%);
  165. transition: all .2s;
  166. height: ${setting.height}px;
  167. border-radius: ${setting.height / 2}px;
  168. opacity: .6;
  169. background: red;
  170. z-index: ${setting.zIndex};
  171.  
  172. display: flex;
  173. flex-direction: row;
  174. align-items: stretch;
  175. }
  176.  
  177. #sc-panel.active {
  178. transform: translate(${offsetSignal}${setting.peekSize * 2}px, -50%);
  179. box-shadow: 0 0 10px rgba(255, 0, 0, .4);
  180. opacity: 1;
  181. }
  182.  
  183. #sc-panel-triggle {
  184. position: absolute;
  185. left: -${isLeft ? 0 : setting.triggleHor};
  186. right: -${isLeft ? setting.triggleHor : 0};
  187. top: -${setting.triggleVer};
  188. bottom: -${setting.triggleVer};
  189. z-index: ${setting.zIndex - 1};
  190. }
  191.  
  192. #sc-panel .sc-panel-item {
  193. position: relative;
  194. z-index: ${setting.zIndex + 1};
  195. color: white;
  196. font-size: 12px;
  197. box-sizing: content-box;
  198.  
  199. display: flex;
  200. flex-direction: column;
  201. align-items: center;
  202. justify-content: center;
  203. padding: 0 10px;
  204. transition: background .3s;
  205. }
  206.  
  207. #sc-panel .sc-panel-item:hover {
  208. background: rgba(255, 255, 255, .2);
  209. }
  210.  
  211. #sc-panel .scf {
  212. font-size: 2em;
  213. margin-bottom: 2px;
  214. }
  215.  
  216. #sc-panel .scf-setting {
  217. position: absolute;
  218. top: 50%;
  219. left: 48px;
  220. transform: translateY(-50%);
  221. color: rgba(255, 255, 255, .6);
  222. transition: all .2s;
  223. cursor: pointer;
  224. z-index: ${setting.zIndex + 1};
  225. padding: 4px;
  226. border-radius: 50%;
  227. }
  228.  
  229. #sc-panel .scf-setting:hover {
  230. color: rgba(255, 255, 255, 1);
  231. background: rgba(255, 255, 255, .2);
  232. }
  233.  
  234. #sc-panel .sc-setting {
  235. font-size: 1.4em;
  236. }
  237.  
  238. #sc-panel-setting {
  239. z-index: ${setting.zIndex + 2};
  240. position: fixed;
  241. top: 0;
  242. right: 0;
  243. bottom: 0;
  244. left: 0;
  245. background-color: rgba(0, 0, 0, .6);
  246. opacity: 0;
  247. transition: opacity .2s;
  248. }
  249.  
  250. #sc-panel-setting.active {
  251. opacity: 1;
  252. }
  253.  
  254. #sc-panel-setting.none {
  255. display: none;
  256. }
  257.  
  258. #sc-setting-box {
  259. position: absolute;
  260. top: 120px;
  261. left: 50%;
  262. transform: translateX(-50%);
  263.  
  264. width: 500px;
  265. height: 420px;
  266. background: white;
  267. border-radius: 4px;
  268. box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
  269.  
  270. display: flex;
  271. flex-direction: column;
  272. align-items: stretch;
  273. }
  274.  
  275. #sc-setting-box h3 {
  276. flex: 0 0 auto;
  277.  
  278. box-sizing: border-box;
  279. font-weight: normal;
  280. font-size: x-large;
  281. padding: 16px 16px 12px;
  282. border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  283. }
  284.  
  285. #sc-tab {
  286. flex-grow: 1;
  287. overflow: auto;
  288. }
  289.  
  290. #sc-tab .sc-list {
  291. padding: 16px;
  292. }
  293.  
  294. #sc-tab .sc-list li {
  295. padding: 8px 4px;
  296. display: flex;
  297. align-items: baseline;
  298. transition: background-color .2s;
  299. }
  300.  
  301. #sc-tab .sc-list li:hover {
  302. background-color: rgba(0, 0, 0, .1);
  303. }
  304.  
  305. #sc-tab .sc-list label {
  306. flex-grow: 1;
  307. display: inline-block;
  308. margin: 0 4px;
  309. }
  310.  
  311. #sc-tab .sc-list .sc-name {
  312. margin-left: 2px;
  313. }
  314.  
  315. #sc-setting-box .btn-panel {
  316. border-top: 1px solid rgba(0, 0, 0, 0.1);
  317. padding: 10px 16px;
  318. display: flex;
  319. justify-content: flex-end;
  320. align-items: baseline;
  321. }
  322.  
  323. #sc-setting-box .btn-panel > * {
  324. margin: 0 4px;
  325. }
  326. `);
  327. }
  328.  
  329. function appendPanel() {
  330. var body = document.body;
  331. if (!body) return;
  332.  
  333. var panel = generateEle('div', {
  334. id: 'sc-panel',
  335. // className: 'active',
  336. });
  337.  
  338. // panel triggle
  339. var triggle = generateEle('div', { id: 'sc-panel-triggle' });
  340. var timerEnter = null;
  341. var timerLeave = null;
  342. var funcEnter = function() { addClassName(panel, 'active'); };
  343. var funcLeave = function() { removeClassName(panel, 'active'); };
  344. panel.onmouseenter = function() {
  345. clearTimeout(timerLeave);
  346. timerEnter = setTimeout(funcEnter, setting.delayEnter);
  347. }
  348. panel.onmouseleave = function() {
  349. clearTimeout(timerEnter);
  350. timerLeave = setTimeout(funcLeave, setting.delayLeave);
  351. }
  352. panel.appendChild(triggle);
  353.  
  354. // engines
  355. enginesArray.forEach(function(entry) {
  356. var key = entry[0];
  357. if (key === location.host || !enabledSites.includes(key)) return;
  358. var engine = entry[1];
  359. var ele = generateEle('a', {
  360. className: 'sc-panel-item',
  361. href: engine.url.replace(/\{q\}/, queryParam()),
  362. });
  363. var iconI = generateEle('i', { className: 'scf ' + engine.icon });
  364. var name = generateEle('span', { innerText: engine.name });
  365. ele.appendChild(iconI);
  366. ele.appendChild(name);
  367. panel.appendChild(ele);
  368. });
  369.  
  370. // setting trigger
  371. var settingBox = generateEle('div', {
  372. className: 'scf-setting',
  373. onclick: showPanelSetting,
  374. });
  375. var settingIcon = generateEle('i', { className: 'scf sc-setting' });
  376. settingBox.appendChild(settingIcon);
  377. panel.appendChild(settingBox);
  378.  
  379. body.appendChild(panel);
  380. }
  381.  
  382. function appendPanelSetting() {
  383. var body = document.body;
  384. if (!body) return;
  385.  
  386. var panel = document.getElementById('sc-panel-setting');
  387. if (panel) return;
  388.  
  389. panel = generateEle('div', {
  390. id: 'sc-panel-setting',
  391. // className: 'active',
  392. onclick: hidePanelSetting,
  393. });
  394.  
  395. panel.addEventListener('transitionend', function () {
  396. if (panel.getAttribute('hide')) {
  397. addClassName(panel, 'none');
  398. }
  399. });
  400.  
  401. var box = generateEle('div', {
  402. id: 'sc-setting-box',
  403. onclick: function (e) { e.stopPropagation() },
  404. });
  405.  
  406. var title = generateEle('h3', {
  407. innerText: '搜索引擎配置',
  408. });
  409. var settingTab = generateEle('div', { id: 'sc-tab' });
  410.  
  411. var listBox = generateEle('ul', {
  412. className: 'sc-list',
  413. });
  414. enginesArray.forEach(function(entry, index) {
  415. var key = entry[0];
  416. var item = entry[1];
  417. var cusId = 'sc-check-' + index;
  418. var listItem = generateEle('li', {});
  419.  
  420. var checkbox = generateEle('input', {
  421. className: 'sc-site-enabled',
  422. type: 'checkbox',
  423. name: key,
  424. id: cusId,
  425. });
  426. var label = generateEle('label', {});
  427. label.setAttribute('for', cusId);
  428. var icon = generateEle('i', { className: 'scf ' + item.icon });
  429. var name = generateEle('span', {
  430. className: 'sc-name',
  431. innerText: item.name,
  432. });
  433. var suffix = generateEle('a', {
  434. className: 'sc-href',
  435. target: '_blank',
  436. href: 'https://' + key,
  437. innerText: key,
  438. });
  439.  
  440. label.appendChild(icon);
  441. label.appendChild(name);
  442. listItem.appendChild(checkbox);
  443. listItem.appendChild(label);
  444. listItem.appendChild(suffix);
  445. listBox.appendChild(listItem);
  446. });
  447. settingTab.appendChild(listBox);
  448.  
  449. var buttonPanel = generateEle('div', { className: 'btn-panel' });
  450. var hint = generateEle('span', {
  451. innerText: '保存后刷新生效',
  452. });
  453. var saveBtn = generateEle('button', {
  454. innerText: '仅保存',
  455. onclick: function () {
  456. storeEnabledSites(panel);
  457. hidePanelSetting();
  458. },
  459. });
  460. var refreshBtn = generateEle('button', {
  461. innerText: '保存并刷新',
  462. onclick: function () {
  463. storeEnabledSites(panel);
  464. hidePanelSetting();
  465. location.reload();
  466. },
  467. });
  468. buttonPanel.appendChild(hint);
  469. buttonPanel.appendChild(saveBtn);
  470. buttonPanel.appendChild(refreshBtn);
  471.  
  472. box.appendChild(title);
  473. box.appendChild(settingTab);
  474. box.appendChild(buttonPanel);
  475. panel.appendChild(box);
  476. body.appendChild(panel);
  477. return panel;
  478. }
  479.  
  480. function assignEnabledSites(panel) {
  481. var list = panel.getElementsByClassName('sc-site-enabled');
  482. for (var i = 0; i < list.length; i++) {
  483. var input = list[i];
  484. input.checked = enabledSites.includes(input.name);
  485. }
  486. }
  487.  
  488. function storeEnabledSites(panel) {
  489. var list = panel.getElementsByClassName('sc-site-enabled');
  490. var results = [];
  491. for (var i = 0; i < list.length; i++) {
  492. var input = list[i];
  493. if (input.checked) {
  494. results.push(input.name);
  495. }
  496. }
  497. GM_setValue('enabledSites', results);
  498. }
  499.  
  500. function showPanelSetting() {
  501. var panel = document.getElementById('sc-panel-setting');
  502. if (!panel) {
  503. panel = appendPanelSetting();
  504. }
  505. assignEnabledSites(panel);
  506. panel.setAttribute('hide', '');
  507. removeClassName(panel, 'none');
  508. setTimeout(function () {
  509. addClassName(panel, 'active');
  510. }, 0);
  511. }
  512.  
  513. function hidePanelSetting() {
  514. var panel = document.getElementById('sc-panel-setting');
  515. if (!panel) {
  516. panel = appendPanelSetting();
  517. }
  518. panel.setAttribute('hide', '1');
  519. removeClassName(panel, 'active');
  520. }
  521.  
  522. function generateEle(name, properties) {
  523. var ele = document.createElement(name);
  524. Object.entries(properties).forEach(function(it) {
  525. ele[it[0]] = it[1];
  526. });
  527. return ele;
  528. }
  529.  
  530. function addClassName(ele, name) {
  531. var classes = (ele.className || '').split(' ').filter(function (it) { return it });
  532. if (!classes.includes(name)) {
  533. classes.push(name);
  534. }
  535. ele.className = classes.join(' ');
  536. }
  537.  
  538. function removeClassName(ele, name) {
  539. var classes = (ele.className || '')
  540. .split(' ')
  541. .filter(function (it) { return it && it !== name });
  542. ele.className = classes.join(' ');
  543. }
  544.  
  545. function toggleClassName(ele, name) {
  546. var classes = (ele.className || '')
  547. .split(' ')
  548. .filter(function (it) { return it });
  549. if (classes.includes(name)) {
  550. classes = classes.filter(function (it) { return it && it !== name });
  551. } else {
  552. classes.push(name);
  553. }
  554. ele.className = classes.join(' ');
  555. }
  556.  
  557. function queryParam() {
  558. var current = engines[location.host];
  559. if (!current) return '';
  560. var result = location.href.match(current.match);
  561. return result && (result[1] || result[0]);
  562. }
  563.  
  564. function appendExtraCss(url) {
  565. GM_xmlhttpRequest({
  566. method: 'GET',
  567. url: url,
  568. onload(args) {
  569. GM_addStyle(args.responseText);
  570. },
  571. });
  572. }
  573.  
  574. (function() {
  575. 'use strict';
  576. if (!enabledSites.includes(location.host)) return;
  577.  
  578. appendStyles();
  579. appendPanel();
  580. appendExtraCss('//at.alicdn.com/t/font_1911184_1ib7wqdqydk.css');
  581. })();

QingJ © 2025

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