// ==UserScript== // @name Metasearch // @namespace https://github.com/Jkker/metasearch-tampermonkey // @version 1.3.0 // @description Aggregated Searcher // @author Jkker // @license MIT // @match *://bing.com/search* // @match *://*.bing.com/search* // @match *://google.com/search* // @match *://*.google.com/search* // @match *://baidu.com/s* // @match *://*.baidu.com/s* // @match *://zhihu.com/search* // @match *://*.zhihu.com/search* // @match *://bilibili.com/all* // @match *://*.bilibili.com/all* // @match *://xiaohongshu.com/search_result/* // @match *://*.xiaohongshu.com/search_result/* // @match *://duckduckgo.com/* // @match *://*.duckduckgo.com/* // @match *://youtube.com/results* // @match *://*.youtube.com/results* // @match *://github.com/search* // @match *://*.github.com/search* // @match *://twitter.com/search* // @match *://*.twitter.com/search* // @match *://wolframalpha.com/input* // @match *://*.wolframalpha.com/input* // @match *://weibo.com/weibo* // @match *://*.weibo.com/weibo* // @match *://youdao.com/result* // @match *://*.youdao.com/result* // @match *://amazon.com/s* // @match *://*.amazon.com/s* // @match *://ebay.com/sch/i.html* // @match *://*.ebay.com/sch/i.html* // @match *://jd.com/bases/m/searchKeyword.htm* // @match *://*.jd.com/bases/m/searchKeyword.htm* // @match *://taobao.com/search* // @match *://*.taobao.com/search* // @match *://mozilla.org/en-US/search* // @match *://*.mozilla.org/en-US/search* // @icon https://raw.githubusercontent.com/Jkker/metasearch-tampermonkey/master/src/favicon.ico // @grant none // @updateURL https://github.com/Jkker/metasearch-tampermonkey/blob/main/dist/metasearch.iife.js // @downloadURL https://github.com/Jkker/metasearch-tampermonkey/blob/main/dist/metasearch.iife.js // @supportURL https://github.com/Jkker/metasearch-tampermonkey/issues // ==/UserScript== (function() { "use strict"; const config = { engines: [ { name: "Bing", key: "bing", url: "https://www.bing.com/search?q=%s", matcher: "https://www.bing.com/search", embeddable: false, priority: 11, disabled: false, lightness: 0.49, color: "#174AE4", background: "linear-gradient(90deg, #2870EA 10.79%, #1B4AEF 87.08%)", icon: '' }, { name: "Google", key: "google", disabled: false, url: "https://www.google.com/search?igu=1&pws=0&gl=us&gws_rd=cr&source=hp&newwindow=1&q=%s&oq=%s&safe=off", matcher: /^https?:\/\/www\.google(?:\.[A-z]{2,3}){1,2}\/[^?]+\?(?!tbm=)(?:&?q=|(?:[^#](?!&tbm=))+?&q=)(?:.(?!&tbm=))*$|(^https?:\/\/xn--flw351e\.ml\/search\?q=)/i, embeddable: true, priority: 10, lightness: 0.607843137254902, color: "#4285F4", icon: '' }, { name: "\u767E\u5EA6", key: "baidu", disabled: false, url: "https://www.baidu.com/s?ie=utf-8&word=%s", matcher: /^https?:\/\/www\.baidu\.com\/(?:s|baidu)/i, embeddable: true, priority: 9, lightness: 0.5078431372549019, color: "#556dea", icon: '', q: ["wd", "word"] }, { name: "\u77E5\u4E4E", key: "zhihu", disabled: false, url: "https://www.zhihu.com/search?type=content&q=%s", matcher: /^https?:\/\/www\.zhihu\.com\/search\?/i, embeddable: false, priority: 8, lightness: 0.5, color: "#0084FF", icon: '' }, { name: "bilibili", key: "bilibili", disabled: false, url: "https://search.bilibili.com/all?keyword=%s", matcher: /^https?:\/\/search\.bilibili\.com\/all/i, q: "keyword", embeddable: true, priority: 7, deeplink: "bilibili://search?keyword=%s", lightness: 0.6431372549019607, color: "#DF698C", icon: '' }, { name: "\u5C0F\u7EA2\u4E66", key: "xiaohongshu", disabled: false, url: "https://www.xiaohongshu.com/search_result/?source=web_search_result_notes&keyword=%s", matcher: /^https?:\/\/www\.xiaohongshu\.com\/search_result/i, q: "keyword", embeddable: false, priority: 7, lightness: 0.6431372549019607, color: "#ff2e4d", icon: '' }, { name: "DDG", key: "duckduckgo", url: "https://duckduckgo.com/?q=%s&kaj=m&k1=-1&kn=1&kp=-2", matcher: /^https?:\/\/duckduckgo\.com\/*/i, embeddable: true, priority: 6, disabled: false, lightness: 0.6039215686274509, color: "#E37151", icon: '' }, { name: "Reddit", key: "reddit", disabled: false, url: "https://www.google.com/search?q=%s+site%3Areddit.com", matcher: (url2, query) => { var _a, _b, _c; return url2.includes("google.com") && ((_c = (_b = decodeURIComponent((_a = query.get("q")) != null ? _a : "")) == null ? void 0 : _b.includes) == null ? void 0 : _c.call(_b, "site:reddit.com")); }, q: (url2, query) => { var _a, _b, _c; return (_c = (_b = decodeURIComponent((_a = query.get("q")) != null ? _a : "")).replace) == null ? void 0 : _c.call( _b, "site:reddit.com", "" ); }, embeddable: true, priority: 6, lightness: 0.5372549019607843, color: "#eb5527", icon: '' }, { name: "YouTube", key: "youtube", disabled: false, url: "https://www.youtube.com/results?search_query=%s", matcher: /^https?:\/\/www\.youtube\.com\/results/i, q: "search_query", embeddable: true, priority: 6, deeplink: "youtube://YouTube.com/results?search_query=%s", lightness: 0.5254901960784314, color: "#ea3322", icon: '' }, { name: "Github", key: "github", disabled: false, url: "https://github.com/search?q=%s", matcher: /^https?:\/\/github\.com\/search/i, embeddable: true, priority: 5, lightness: 0.09215686274509804, color: "#181717", icon: '' }, { name: "Twitter", key: "twitter", disabled: false, url: "https://twitter.com/search?q=%s", matcher: /^https?:\/\/twitter\.com\/search/i, embeddable: true, priority: 2, lightness: 0.5254901960784314, color: "#1da1f2", icon: 'Twitter' }, { name: "Wolfram", key: "wolfram", disabled: false, url: "https://www.wolframalpha.com/input?i=%s", matcher: /^https?:\/\/www\.wolframalpha\.com\/input/i, embeddable: true, priority: 4, lightness: 0.5254901960784314, color: "#e87242", icon: '', q: "i" }, { name: "\u4ECA\u65E5\u5934\u6761", key: "toutiao", disabled: true, url: "https://so.toutiao.com/search?keyword=%s", matcher: /^https?:\/\/so\.toutiao\.com\/search/i, q: "keyword", embeddable: true, priority: 4, lightness: 0.5764705882352941, color: "#dd5049", icon: '' }, { name: "\u5FAE\u535A", key: "weibo", disabled: false, url: "https://s.weibo.com/weibo?q=%s", matcher: /^https?:\/\/(s|m)\.weibo\.c(om|n)\/(weibo|search)/i, q: (url2, query) => { var _a, _b, _c; if (url2.match(/^https?:\/\/s\.weibo\.com\/weibo/i)) { return (_a = query.get("q")) != null ? _a : ""; } return (_c = new URLSearchParams( decodeURIComponent((_b = query.get("containerid")) != null ? _b : "") ).get("q")) != null ? _c : ""; }, embeddable: true, priority: 3.5, deeplink: "sinaweibo://searchall?q=%s", lightness: 0.5156862745098039, color: "#d33436", icon: '' }, { name: "Quora", key: "quora", url: "https://www.google.com/search?q=%s+site%3Aquora.com", matcher: (url2, query) => { var _a, _b, _c; return url2.includes("google.com") && ((_c = (_b = decodeURIComponent((_a = query.get("q")) != null ? _a : "")) == null ? void 0 : _b.includes) == null ? void 0 : _c.call(_b, "site:quora.com")); }, q: (url2, query) => { var _a, _b, _c; return (_c = (_b = decodeURIComponent((_a = query.get("q")) != null ? _a : "")).replace) == null ? void 0 : _c.call( _b, "site:quora.com", "" ); }, embeddable: true, priority: 3, disabled: true, lightness: 0.42549019607843136, color: "#aa382f", icon: '' }, { name: "\u8BCD\u5178", key: "bingdict", url: "https://cn.bing.com/dict/search?q=%s", matcher: "https://cn.bing.com/dict/search", embeddable: false, priority: 3, disabled: true, lightness: 0.6, color: "#09ABA0", icon: ' ' }, { name: "\u6709\u9053", key: "youdao", disabled: false, url: "https://dict.youdao.com/result?word=%s&lang=en", matcher: /^https?:\/\/dict\.youdao\.com\/(m\/)?result/i, q: "word", embeddable: true, priority: 3, lightness: 0.484313725490196, color: "#E31436", icon: 'Created by potrace 1.16, written by Peter Selinger 2001-2019' }, { name: "Amazon", key: "amazon", disabled: false, url: "https://www.amazon.com/s?k=%s", matcher: "amazon.com/s", q: "k", embeddable: true, priority: 2, lightness: 0.5, color: "#FF9900", icon: '' }, { name: "eBay", key: "ebay", url: "https://www.ebay.com/sch/i.html?_nkw=%s", matcher: "ebay.com/sch", q: "_nkw", embeddable: true, priority: 2, disabled: false, lightness: 0.4862745098039215, color: "#4164ea", icon: '' }, { name: "\u4EAC\u4E1C", key: "jing-dong", disabled: false, url: "https://sou.m.jd.com/bases/m/searchKeyword.htm?keyword=%s", matcher: "sou.m.jd.com/bases/m/searchKeyword.htm", q: "keyword", embeddable: true, priority: 1, deeplink: 'openapp.jdmobile://virtual?params={"des":"productList","keyWord":"%s","from":"search","category":"jump"}', lightness: 0.4941176470588235, color: "#E1251B", icon: '' }, { name: "\u6DD8\u5B9D", key: "taobao", disabled: false, url: "https://s.taobao.com/search?q=%s", matcher: "s.taobao.com/search", embeddable: true, priority: 1, deeplink: "taobao://s.taobao.com?q=%s", lightness: 0.5196078431372548, color: "#E94F20", icon: '' }, { name: "MDN", key: "mdnwebdocs", disabled: false, url: "https://developer.mozilla.org/en-US/search?q=%s", matcher: "developer.mozilla.org/en-US/search", embeddable: true, priority: -1, lightness: 0, color: "#000000", icon: '' }, { name: "\u5FAE\u4FE1", key: "wechat", disabled: true, url: "https://weixin.sogou.com/weixin?p=01030402&query=%s&type=2&ie=utf8", matcher: "weixin.sogou.com/weixin", q: "query", embeddable: true, priority: -2, lightness: 0.39215686274509803, color: "#07C160", icon: '' } ] }; const styles = ""; const throttle = (callback, limit = 100) => { let waiting = false; return (...args) => { if (!waiting) { callback.apply(null, args); waiting = true; setTimeout(() => { waiting = false; }, limit); } }; }; const lightnessOfHexColor = (hex) => { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); if (!result) return 0.5; const r = parseInt(result[1], 16) / 255; const g = parseInt(result[2], 16) / 255; const b = parseInt(result[3], 16) / 255; const max = Math.max(r, g, b), min = Math.min(r, g, b); const l = (max + min) / 2; return l; }; const isMobileDevice = /Mobi/i.test(window.navigator.userAgent); const engines = config.engines.filter((e) => !e.disabled).sort((a, b) => b.priority - a.priority); const hotkeys = engines.reduce((obj, engine, index) => { const key = engine.key[0].toLowerCase(); obj[key] = obj[key] ? [...obj[key], index] : [index]; return obj; }, {}); function Button({ icon, color, background, name, lightness, url: url2, deeplink, index, searchQuery = "" }) { const href = (isMobileDevice ? deeplink || url2 : url2).replaceAll("%s", searchQuery); const a = Object.assign(document.createElement("a"), { title: name, href, target: "_blank", rel: "noopener noreferrer", "aria-label": name, "data-index": String(index), innerHTML: icon + `${name}`, className: "icon-button" }); if (color) a.style.setProperty("--color", color); if (background) a.style.setProperty("--background", background); if ((lightness != null ? lightness : lightnessOfHexColor(color)) < 0.5) a.classList.add("dark-invert"); return a; } const getCurrentEngineIndex = (url2, searchParams) => { for (let i = engines.length - 1; i >= 0; i--) { const e = engines[i]; if (!e.matcher) continue; if (e.matcher instanceof RegExp) { if (e.matcher.test(url2)) { return i; } } else if (typeof e.matcher === "function") { try { if (e.matcher(url2, searchParams)) { return i; } } catch (e2) { console.error(e2); } } else if (typeof e.matcher === "string") { if (url2.includes(e.matcher)) { return i; } } } return -1; }; const getSearchQuery = (engine, url2, searchParams) => { if (typeof engine.q === "string") { return searchParams.get(engine.q); } if (Array.isArray(engine.q)) { for (const param of engine.q) { const q = searchParams.get(param); if (q) return q; } } if (engine.q instanceof RegExp) { const match = engine.q.exec(window.location.href); if (match) return match[1]; } if (typeof engine.q === "function") { return engine.q(url2, searchParams); } return searchParams.get("q") || searchParams.get("query") || void 0; }; const url = window.location.href; const params = new URLSearchParams(window.location.search); const currEngineIndex = getCurrentEngineIndex(url, params); if (currEngineIndex !== -1) { const filtered = engines.filter((_, i) => i !== currEngineIndex); const matchedEngine = engines[currEngineIndex]; let searchQuery = getSearchQuery(matchedEngine, url, params); if (searchQuery) { const body = document.querySelector("body"); const root = document.createElement("div"); const linkContainer = document.createElement("div"); linkContainer.id = "metasearch-link-container"; root.id = "metasearch-root"; let prevScrollPosition = window.scrollY; window.addEventListener( "scroll", throttle(() => { const currentScrollPos = window.scrollY; if (prevScrollPosition < currentScrollPos) { root.style.bottom = "-48px"; } else { root.style.bottom = "0"; } prevScrollPosition = currentScrollPos; }, 100), true ); const linkList = []; filtered.forEach((engine, index) => { const button = Button({ ...engine, index, searchQuery: encodeURIComponent(searchQuery.trim()) }); linkList.push(button); linkContainer.appendChild(button); }); root.appendChild(linkContainer); root.appendChild( Object.assign(document.createElement("button"), { title: "Close", "aria-label": "Close", innerHTML: ``, className: "icon-button", id: "metasearch-close", onclick: () => { root.style.bottom = "-40px"; } }) ); function styleInject(css,ref){if(ref===void 0){ref={}}var insertAt=ref.insertAt;if(!css||typeof document==="undefined"){return}var head=document.head||document.getElementsByTagName("head")[0];var style=document.createElement("style");style.type="text/css";if(insertAt==="top"){if(head.firstChild){head.insertBefore(style,head.firstChild)}else{head.appendChild(style)}}else{head.appendChild(style)}if(style.styleSheet){style.styleSheet.cssText=css}else{style.appendChild(document.createTextNode(css))}};styleInject(`#metasearch-root { box-sizing: border-box; width: 100vw; display: flex; position: fixed; bottom: 0; left: 0; transition: all 0.1s ease-in-out; height: 32px; background-color: rgba(255, 255, 255, 0.8); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); z-index: 999999999; box-shadow: 0px 0px 9px rgba(0, 0, 0, 0.07); overflow-y: hidden; } #metasearch-root .icon-button { all: unset; box-sizing: border-box; text-decoration: none; color: var(--color); display: flex; align-items: center; justify-content: center; gap: 4px; background-color: transparent; border: 0; outline: transparent; cursor: pointer; transition: all 0.15s ease-in-out; white-space: nowrap; -webkit-tap-highlight-color: transparent; flex: 1 0 auto; padding: 2px 5px; min-width: 32px; height: 100%; } #metasearch-root .icon-button > svg { width: 20px; height: 20px; color: currentColor; fill: currentColor; stroke: currentColor; } @media screen and (max-width: 768px) { #metasearch-root .icon-button > svg { height: 24px; width: 24px; } } #metasearch-root .icon-button:hover { filter: brightness(0.85); background: rgba(0, 0, 0, 0.08); } @media screen and (prefers-color-scheme: dark) { #metasearch-root .icon-button:hover.dark-invert { filter: invert(1) hue-rotate(180deg) brightness(0.85); } } #metasearch-root .icon-button:active { -webkit-tap-highlight-color: transparent; filter: brightness(0.9); color: white; background: var(--color); } @media screen and (prefers-color-scheme: dark) { #metasearch-root .icon-button:active.dark-invert { filter: invert(1) hue-rotate(180deg) brightness(0.9); } } #metasearch-root .icon-button:focus { -webkit-tap-highlight-color: transparent; color: white; background: var(--color); } @media screen and (prefers-color-scheme: dark) { #metasearch-root .icon-button:focus.dark-invert { filter: invert(1) hue-rotate(180deg) brightness(0.7); } } @media screen and (max-width: 768px) { #metasearch-root .icon-button { padding: 0; min-width: 40px; } #metasearch-root .icon-button > span { display: none; } } @media screen and (max-width: 768px) { #metasearch-root { height: 40px; } } @media screen and (prefers-color-scheme: dark) { #metasearch-root { background-color: rgba(25, 25, 25, 0.7); color: rgba(255, 255, 255, 0.8); } #metasearch-root .dark-invert { filter: invert(1) hue-rotate(180deg); } } #metasearch-root #metasearch-close { box-shadow: -1px 2px 9px rgba(0, 0, 0, 0.1); } #metasearch-root #metasearch-link-container { scrollbar-width: none; display: flex; flex-direction: row; overflow-x: auto; overflow-y: hidden; width: 100%; box-sizing: border-box; } #metasearch-root #metasearch-link-container::-webkit-scrollbar { display: none; } body { position: relative !important; }`); const getNextTabIndex = (currIndex = -1, key) => { for (let i = currIndex + 1; i < filtered.length + currIndex; i++) { const index = i % filtered.length; if (filtered[index].key[0] === key.toLowerCase()) return index; } return currIndex; }; const keydownListener = (e) => { if (e.key === "Alt") { root.style.bottom = "0"; } const active = document.activeElement; if (e.key === "Escape") { if (root.contains(active)) { e.preventDefault(); active.blur(); return; } } const key = e.key.toLowerCase(); const focusIndex = linkContainer.contains(active) ? parseInt(active.getAttribute("data-index") || "-1", 10) : -1; if (e.altKey && hotkeys[key] !== void 0) { e.preventDefault(); const next = getNextTabIndex(focusIndex, key); linkList[next].focus(); return; } const num = parseInt(e.key, 10); if (e.altKey && !isNaN(num) && num < filtered.length) { e.preventDefault(); const index = num - 1; linkList[index].focus(); return; } if (e.altKey && (e.key === "[" || e.key === "-")) { const prevIndex = focusIndex - 1 < 0 ? filtered.length - 1 : focusIndex - 1; linkList[prevIndex].focus(); return; } if (e.altKey && (e.key === "]" || e.key === "=")) { const nextIndex = (focusIndex + 1) % filtered.length; linkList[nextIndex].focus(); return; } }; const keyUpListener = (e) => { const active = document.activeElement; if (e.key === "Alt" && linkContainer.contains(active)) { active.click(); active.blur(); } }; document.addEventListener("keydown", keydownListener); document.addEventListener("keyup", keyUpListener); linkContainer.addEventListener( "wheel", throttle((event) => { if (!event.deltaY) return; linkContainer.scrollBy({ left: event.deltaY * 4, behavior: "smooth" }); event.preventDefault(); event.stopPropagation(); }, 100) ); body.appendChild(root); } } })();