// ==UserScript== // @name 🔍聚合快搜 // @namespace https://ez118.github.io/ // @version 0.4.1 // @description 为Via设计的第三方聚合搜索栏 // @author ZZY_WISU // @match *://*/* // @license GNU GPLv3 // @icon data:image/svg+xml;charset=utf-8;base64,PHN2ZyB2ZXJzaW9uPSIxLjIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDEyOCAxMjgiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCI+PHBhdGggZD0iTTY0IDEyOEMyOC42IDEyOCAwIDk5LjQgMCA2NFMyOC42IDAgNjQgMHM2NCAyOC42IDY0IDY0LTI4LjYgNjQtNjQgNjR6IiBzdHlsZT0iZmlsbDojZjJmN2ZmIi8+PHBhdGggZD0iTTM0LjMgNzkuNUMyMy4xIDc5LjUgMTQgNzAuNCAxNCA1OS4yIDE0IDQ4LjEgMjMuMSAzOSAzNC4zIDM5YzExLjEgMCAyMC4yIDkuMSAyMC4yIDIwLjMgMCAxMS4xLTkuMSAyMC4yLTIwLjIgMjAuMnoiIHN0eWxlPSJmaWxsOiMwMGE1ZjYiLz48cGF0aCBkPSJNNTkgMTA0Yy0xMy44IDAtMjUtMTEuMi0yNS0yNXMxMS4yLTI1IDI1LTI1IDI1IDExLjIgMjUgMjUtMTEuMiAyNS0yNSAyNXoiIHN0eWxlPSJmaWxsOiNmZjNlMDAiLz48cGF0aCBkPSJNODguMiA4MC41QzcxLjUgODAuNSA1OCA2NyA1OCA1MC4yIDU4IDMzLjUgNzEuNSAyMCA4OC4yIDIwYzE2LjggMCAzMC4zIDEzLjUgMzAuMyAzMC4yIDAgMTYuOC0xMy41IDMwLjMtMzAuMyAzMC4zeiIgc3R5bGU9ImZpbGw6I2ZmYjcwMCIvPjwvc3ZnPg== // @run-at document-start // @grant GM_registerMenuCommand // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_addElement // @grant window.onurlchange // @require https://unpkg.com/jquery@3.7.1/dist/jquery.min.js // ==/UserScript== /* =====[ 用户自定义 ]===== */ var searchEngine = [ { "id":"google", "name":"Google", "link":"https://www.google.com/search?q=%s" }, { "id":"bing", "name":"Bing", "link":"https://www.bing.com/search?q=%s" }, { "id":"cnbing", "name":"必应", "link":"https://cn.bing.com/search?q=%s" }, { "id":"duckduckgo", "name":"DuckDuckGo", "link":"https://duckduckgo.com/?t=h_&q=%s" }, { "id":"github", "name":"Github", "link":"https://github.com/search?q=%s&type=repositories" }, { "id":"fdroid", "name":"F-Droid", "link":"https://search.f-droid.org/?q=%s&lang=zh_Hans" }, { "id":"baidu", "name":"百度", "link":"https://www.baidu.com/s?wd=%s" } ]; /* =====[ 变量存储 ]===== */ const ICONS = { 'settings': '', 'up': '', 'del': '' }; var currentEngine = null; /* 存储当前正在使用的搜索引擎 */ var currentQuery = null; /* 存储当前检索词 */ /* =====[ 菜单注册 ]===== */ /* =====[ 计算反色 ]===== */ function invertHex(hex) { hex = hex.replace("#", ""); // 补全短的 hex 颜色代码 if (hex.length === 3) { hex = hex.split('').map(c => c + c).join(''); } // 确保 hex 长度为 6 if (hex.length !== 6) { throw new Error("Invalid HEX color."); } // 计算反色 let inverted = (Number(`0x${hex}`) ^ 0xFFFFFF).toString(16).toUpperCase(); // 补全不足位数的 0 inverted = ("000000" + inverted).slice(-6); return `#${inverted}`; } function invertRgb(rgb) { // 匹配 rgb(r, g, b) 格式 const match = rgb.match(/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/); if (!match) { throw new Error("Invalid RGB color."); } const r = 255 - parseInt(match[1], 10); const g = 255 - parseInt(match[2], 10); const b = 255 - parseInt(match[3], 10); return `rgb(${r}, ${g}, ${b})`; } function invertRgba(rgba) { // 匹配 rgba(r, g, b, a) 格式 const match = rgba.match(/^rgba\((\d{1,3}), (\d{1,3}), (\d{1,3}), (0|0?\.\d+|1(\.0)?)\)$/); if (!match) { throw new Error("Invalid RGBA color."); } const r = 255 - parseInt(match[1], 10); const g = 255 - parseInt(match[2], 10); const b = 255 - parseInt(match[3], 10); return `rgba(${r}, ${g}, ${b}, 1)`; } function invertColor(color) { if (color.startsWith("#")) { return invertHex(color); } else if (color.startsWith("rgb(")) { return invertRgb(color); } else if (color.startsWith("rgba(")) { return invertRgba(color); } else { throw new Error("Invalid color format."); } } /* ================ */ function hash(str) { let hash = 5381; for (let i = 0; i < str.length; i++) { hash = (hash * 33) ^ str.charCodeAt(i); } return hash >>> 0; } function findByKeyValue(array, key, value) { /* 在JSON中,以键值匹配项 */ return array.findIndex(item => item[key] === value); } function isSearchEngine() { /* 判断是否是被记录的搜索页 */ const currentUrl = new URL(window.location.href); return searchEngine.some(engine => { const prefix = engine.link.split('%s')[0]; const engineUrl = new URL(prefix); return currentUrl.hostname === engineUrl.hostname && currentUrl.pathname.startsWith(engineUrl.pathname); }); } function showOption() { if($("#userscript-optDlg").length > 0) { return; } let newEngineList = searchEngine; let origEngineList = searchEngine.slice(); var $optDlg = $('
', { class: 'userscript-optDlg', id: 'userscript-optDlg', style: 'display:none;' }).appendTo('body'); $optDlg.hide(); $optDlg.fadeIn(100); var listHtml = ''; $.each(newEngineList, (index, item) => { listHtml += `

  ${item.name}

${ICONS.up}

${ICONS.del}

`; }); $optDlg.html(`

设置

搜索引擎:

` + listHtml + `
`); $(document).on('click', '.list-item>.item-movebtn', function(e) { let seid = $(e.target).parent().attr("seid"); const index = findByKeyValue(newEngineList, 'id', seid); if (index > 0) { [newEngineList[index - 1], newEngineList[index]] = [newEngineList[index], newEngineList[index - 1]]; // 交换DOM元素位置 const currentItem = $(`.list-item[seid="${seid}"]`); const previousItem = currentItem.prev(); if (previousItem.length) { currentItem.insertBefore(previousItem); } } }); $(document).on('click', '.list-item>.item-delbtn', function(e) { let seid = $(e.target).parent().attr("seid"); const index = findByKeyValue(newEngineList, 'id', seid); if (index !== -1) { newEngineList.splice(index, 1); $(`.list-item[seid="${seid}"]`).remove(); } }); $(document).on('click', '#userscript-cancelBtn', function(e) { /* 取消按钮 */ newEngineList = origEngineList; $(document).off('click', '#userscript-addBtn'); $(document).off('click', '#userscript-saveBtn'); $(document).off('click', '.list-item>.item-movebtn'); $(document).off('click', '.list-item>.item-delbtn'); let $optDlg = $("#userscript-optDlg"); $optDlg.fadeOut(100); setTimeout(() => { $optDlg.remove(); $(document).off('click', '#userscript-cancelBtn'); location.reload(); }, 110); }); $(document).on('click', '#userscript-addBtn', function(e) { let url = prompt("【添加搜索引擎】\n请在此处填写搜索引擎的链接。(用“%s”代替搜索字词)", "https://"); if(!url){ return; } if(!url.includes("%s") || !url.includes("://")) { alert("【未添加】不合法的链接。"); return; } let name = prompt("【添加搜索引擎】\n请为该条目命名。", "") if(!name){ return; } if(name.length > 100) { alert("【未添加】过长的名称。"); return; } newEngineList.push({ "id": hash(url + name).toString(), "name": name, "link": url }); GM_setValue('search_engine', newEngineList); alert("【已添加】页面即将刷新"); location.reload(); }); $(document).on('click', '#userscript-saveBtn', function(e) { /* 保存按钮 */ GM_setValue('search_engine', newEngineList); alert("【已保存】请刷新页面以应用更改"); $(document).off('click', '#userscript-addBtn'); $(document).off('click', '#userscript-cancelBtn'); $(document).off('click', '.list-item>.item-movebtn'); $(document).off('click', '.list-item>.item-delbtn'); let $optDlg = $("#userscript-optDlg"); $optDlg.fadeOut(100); setTimeout(() => { $optDlg.remove(); $(document).off('click', '#userscript-saveBtn'); }, 110); }); } function initEle() { // 创建搜索栏元素并添加到页面 var $quickSearchBar = $('
', { class: 'userscript-quickSearchBar', id: 'userscript-quickSearchBar' }).appendTo('body'); let html = ''; const currentURL = window.location.href; $.each(searchEngine, (index, item) => { const link = item.link; const splitLink = link.split('%s'); let prefix = null; let suffix = null; if (splitLink.length === 2) { prefix = splitLink[0]; suffix = splitLink[1]; } else if (splitLink.length > 2) { prefix = splitLink[0]; suffix = splitLink.slice(1).join('%s'); } if (currentURL.startsWith(prefix) && currentURL.endsWith(suffix)) { currentEngine = item; currentQuery = currentURL.slice(prefix.length, currentURL.length - suffix.length); html += `
${item.name}
`; } else { html += `
${item.name}
`; } }); // 清理 HTML 内容并插入到搜索栏 $quickSearchBar.append(html + `
` + ICONS.settings + `
`); // 添加点击事件监听器 $(document).on('click', '#userscript-quickSearchBar>.item', function(e) { var seid = $(e.target).attr("seid"); var seIndex = findByKeyValue(searchEngine, "id", seid); location.href = searchEngine[seIndex].link.replace("%s", currentQuery); }); $("#userscript-optBtn").click(() => { showOption(); }); } (function () { 'use strict'; var menu_ = GM_registerMenuCommand('⚙️ 脚本设置', function () { showOption(); }, 'o'); if(GM_getValue('search_engine') == null || GM_getValue('search_engine') == "" || GM_getValue('search_engine') == undefined){ GM_setValue('search_engine', searchEngine); } else { searchEngine = GM_getValue('search_engine'); } var websiteThemeColor = $('body').css('background-color') || "#FFF"; var websiteFontColor = invertColor(websiteThemeColor); websiteThemeColor = invertColor(websiteFontColor); GM_addStyle(` body{ -webkit-appearance: none !important; } .userscript-quickSearchBar{ user-select:none; background-color:` + websiteThemeColor + `; color:` + websiteFontColor + `; padding:6px 10px; font-size:12px; line-height:20px; width:100vw; height:30px; border-top:1px solid #99999999; position:fixed; bottom:-1px; left:0; right:0; display:flex; flex-direction:row; overflow-x:auto; overflow-y:hidden; box-sizing:initial; z-index:100000; font-family:"Hiragino Sans GB","Microsoft YaHei","WenQuanYi Micro Hei",sans-serif; } .userscript-quickSearchBar>.item{ margin:0px 5px; border-radius:20px; border:1px solid ` + websiteFontColor + `; padding:5px 9px; width:fit-content; flex-basis:fit-content; flex-shrink:0; cursor:pointer; background-color:transparent; } .userscript-quickSearchBar>.active{ border:1px solid #6d7fb4; color:#6d7fb4; } .userscript-quickSearchBar>.blank{ flex-basis:25px; flex-shrink:0; width:25px; } #userscript-optBtn{ margin:0px 5px; flex-shrink:0; flex-basis:fit-content; width:fit-content; border-radius:20px; border:1px solid ` + websiteFontColor + `; padding:4px 6px; cursor:pointer; background-color:transparent; } #userscript-optBtn svg{ fill:` + websiteFontColor + `; } .userscript-optDlg{ user-select:none; background-color:` + websiteThemeColor + `; color:` + websiteFontColor + `; border:1px solid #99999999; position:fixed; top:50%; height:fit-content; left:50%; transform:translateX(-50%) translateY(-50%); width:92vw; max-width:300px; padding:15px; border-radius:15px; z-index:100000; box-shadow:0 1px 10px #00000088; font-family:"Hiragino Sans GB","Microsoft YaHei","WenQuanYi Micro Hei",sans-serif; } .userscript-optDlg .ctrlbtn{ border:none; background-color:transparent; padding:8px; margin:0; color:#6d7fb4; cursor:pointer; overflow:hidden; } .userscript-optDlg h3{ margin:5px; margin-bottom:15px; font-size:24px; } .userscript-optDlg .subtitle{ margin:5px 1px; font-size:16px; font-weight:400; } .userscript-optDlg .list-item{ width:100%; border-top:1px solid #99999999; padding:10px 0; margin:0; display:flex; flex-direction:row; vertical-align:middle; } .userscript-optDlg .list-item:hover{ background-color:#55555555; } .userscript-optDlg .list-item>p{ padding:0; margin:0; font-size:16px; } .userscript-optDlg .list-item>.item-title{ width:calc(100% - 30px); } .userscript-optDlg .list-item>.item-movebtn{ cursor:pointer; width:25px; } .userscript-optDlg .list-item>.item-movebtn svg{ fill:` + websiteFontColor + `; height:16px; } .userscript-optDlg .list-item>.item-delbtn{ cursor:pointer; width:25px; } .userscript-optDlg .list-item>.item-delbtn svg{ fill:` + websiteFontColor + `; height:16px; } `); if(isSearchEngine()){ /* 存储初始化 */ console.log("【提示】匹配到已保存的搜素引擎"); try{ initEle(); }catch(e){ console.log(e) } setTimeout(() => { if($("#userscript-quickSearchBar").length == 0){ /* 如果在页面加载时未完成初始化 */ initEle(); } }, 600); setTimeout(()=>{ if($("#userscript-quickSearchBar").length == 0){ /* 如果在页面加载时未完成初始化 */ initEle(); } }, 1200) } })();