// ==UserScript== // @name SearchJumper // @name:zh-CN 搜索酱 // @name:zh-TW 搜尋醬 // @name:ja SearchJumper // @name:ru SearchJumper // @namespace hoothin // @version 1.9.33 // @description Instantly search selected text across multiple search engines. Highlight keywords and boost your research efficiency. // @description:zh-CN 一键即时搜索选定文本或在多个搜索引擎之间快速切换,支持关键词高亮、拖拽搜索、以图搜图、页内查找与自定义引擎。 // @description:zh-TW 一鍵即時搜尋選定文字或在多個搜尋引擎之間快速切換,支援關鍵字高亮、拖曳搜尋、以圖搜圖、頁內尋找與自訂引擎。 // @description:ja 選択したテキストをワンクリックで即座に検索したり、キーワードの強調表示、ドラッグアンドドロップ検索、画像検索、ページ内検索、カスタムエンジンをサポートする複数の検索エンジン間で素早く切り替えたりできます。 // @description:ru Легко проводите поиск по выбранному тексту/изображению/ссылке. Быстро переходите к любому поисковому движку. Выделяйте искомый текст. // @author hoothin // @license MPL-2.0 // @match *://*/* // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAclBMVEUAAAD+/v7////+/v7+/v7////+/v79/f3////////+/v7////////////+/v79/f3////+/v7/rP8zMzP/2f/R0dHAwMD/zf+vr69ZWVlKSkry8vL/vv/+5/7r6+uRkZGcnJx8fHxwcHD+7/7f39+kpKTMxXKjAAAAEXRSTlMA4wrL9ICvkxk56nVVI9WgZNxdEUkAAAE2SURBVDjLfdPZloMgDAZgFtHR2uU3LnWrXd//FUfIHKRT7XfhUYIkhINYqPyoM0SZTnIlPu2PEbwo2f8LqwTvIvMW/9H4oH+WeCqxQu79/xKr5N8aSmOD5gkGm3YuQYRNkU3CG+ynCYH6VsEycwW8wJXoDK8narlOIXI4Z6IKi47ucNI5A6vCOC41mBEaX8VCAuVQFEXzQODRzENDaVsRoSwYAgUrIecJI38MCAw8NkLaFCibphyDMusKox0DoJci+6615fcA2q5fikz8b/QC0HWuKTX8NnM/wbWSyL86qW01u1D3xEQ04dLSE0z6w3ILz9rWPq/hefslUN3uL+B6v/kKMiVmO2w6CSfGhqNg6oBVWvlbxTO+XAy1kiVWInTK8EZyfQFlZBDeKbEiNfFBSh2bNBj8BZ8mNsZysMSsAAAAAElFTkSuQmCC // @grant GM.getValue // @grant GM_getValue // @grant GM.setValue // @grant GM_setValue // @grant GM_addStyle // @grant GM.addStyle // @grant GM.deleteValue // @grant GM_deleteValue // @grant GM.registerMenuCommand // @grant GM_registerMenuCommand // @grant GM.xmlHttpRequest // @grant GM_xmlhttpRequest // @grant GM.notification // @grant GM_notification // @grant GM.setClipboard // @grant GM_setClipboard // @grant GM.openInTab // @grant GM_openInTab // @grant GM.info // @grant GM_info // @grant unsafeWindow // @compatible edge tested with tm // @compatible Chrome tested with tm // @compatible Firefox tested with tm // @compatible Opera untested // @compatible Safari untested // @compatible ios tested with userscript // @compatible android tested with kiwi // @supportURL https://github.com/hoothin/SearchJumper/issues // @homepage https://github.com/hoothin/SearchJumper // @require https://update.greasyfork.icu/scripts/484118/searchJumperDefaultConfig.js // @connect global.bing.com // @connect suggestqueries.google.com // @connect api.bing.com // @connect suggestion.baidu.com // @connect webdav.hoothin.com // @connect search.hoothin.com // @connect * // @run-at document-start // @downloadURL none // ==/UserScript== (async function() { 'use strict'; const ext = false; const _unsafeWindow = (typeof unsafeWindow == 'undefined') ? window : unsafeWindow; if (_unsafeWindow.searchJumperInited) return; _unsafeWindow.searchJumperInited = true; const clipboard = navigator && navigator.clipboard; const inIframe = window.top !== window.self; if (inIframe) { try { if (window.name === 'pagetual-iframe' || (window.frameElement && window.frameElement.name === 'pagetual-iframe')) { return; } else if (window.self.innerWidth === 0 && window.self.innerHeight === 0) { let ignore = await new Promise(resolve => { window.addEventListener('load', e => { setTimeout(() => { resolve(window.self.innerWidth < 300 || window.self.innerHeight < 300); }, 500); }); }); if (ignore) return; } else if (window.self.innerWidth < 300 || window.self.innerHeight < 300) { return; } } catch(e) { return; } } const importPageReg = /^https:\/\/github\.com\/hoothin\/SearchJumper(\/(issue|discussions)|\/?$|#|\?)|^https:\/\/greasyfork\.org\/.*\/scripts\/445274[\-\/].*\/discussions/i; const mobileUa = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1"; const homePage = 'https://search.hoothin.com/'; const githubPage = 'https://hoothin.github.io/SearchJumper'; const firstRunPage = homePage + "firstRun"; let configPage = homePage + 'config/'; let isAllPage = false; let searchData = {}; searchData.sitesConfig = sitesConfig; searchData.prefConfig = { position: { x: "left", y: "top" }, offset: { x: "0", y: "0" }, firstRun: true, openInNewTab: false, enableInPage: true, altKey: false, ctrlKey: true, shiftKey: false, metaKey: false, autoClose: false, autoDelay: 1000, shortcut: true, initShow: false, alwaysShow: false, customSize: 100, tilesZoom: 100, tipsZoom: 100, typeOpenTime: 250, longPressTime: 500, noIcons: false, showSiteLists: true, alwaysShowSiteLists: false, cacheSwitch: false, noAni: false, quickAddRule: true, multiline: 2, multilineGap: 1000, historyLength: 0, dragToSearch: true, hideDragHistory: false, sortType: false, sortSite: false, autoHide: false, autoHideAll: false, showCurrent: true, shortcutKey: 'Backquote', showInSearchEngine: false, showInSearchJumpPage: true, limitInPageLen: 1, limitPopupLen: 1, ignoreWords: ["a", "in", "into", "the", "to", "on", "among", "between", "and", "an", "of", "by", "with", "about", "under", "or", "at", "as"], inPageRule: {}, firstFiveWordsColor: [], inPageWordsStyles: [], altToHighlight: true, defaultPicker: false, disableInputOnWords: false, disableTypeOpen: false, callBarAlt: false, callBarCtrl: false, callBarShift: false, callBarMeta: false, defaultFindTab: true, disableAutoOpen: false, hideOnSearchEngine: false, minSizeMode: false, hidePopup: false, minPopup: 0, selectToShow: ext, expandType: false, rightMouse: true, shiftLastUsedType: true, mouseLeaveToHide: true, currentTypeFirst: true, switchSitesPreKey: 'ArrowLeft', switchSitesNextKey: 'ArrowRight', switchSitesCtrl: true, switchSitesAlt: false, switchSitesShift: true, switchSitesMeta: false }; function run() { let lang = navigator.appName === "Netscape" ? navigator.language : navigator.userLanguage; let config = {}; function setLang() { switch (lang) { case "zh-CN": case "zh-SG": config = { import: '导入', filter: '筛选', selectAll: '全选', importOrNot: '是否导入配置?', settings: '配置脚本', batchOpen: '批量打开', batchOpenConfirm: '确定要批量打开吗?', postOver: '发送成功:', postError: '发送失败:', copyOver: '复制成功', keywords: '请输入搜索词', targetUrl: '请输入搜索URL', siteName: '站名', siteDesc: '描述', siteUrl: '地址', siteIcon: '图标', siteTest: '测试', siteCancel: '取消', siteAdd: '添加', siteType: '分类', siteExist: '已存在相同规则,是否添加为克隆项?', siteAddOver: '站点添加成功', multiline: '是否以换行符分隔多行搜索?', multilineTooMuch: '行数超过10行,是否继续搜索?', inputPlaceholder: '筛选引擎', inputTitle: '筛选引擎,支持 * ? 通配符,$代表末尾,^代表开头,分组**站点 可筛选指定分组,例如 图片**baidu,tab 下一项', inputKeywords: '输入搜索关键词', inPageTips: '自定义分隔符:$c 加分隔符,例如 $c| search | jumper,默认空格作为分隔符\n原始文本不分隔:$o 加文本,例如$oopai liked by hero\n正则表达式:/re/,例如 $c, /google/i , /aPPle/\n添加提示文本:搜索文本$t{提示文本},例如 linux$t{linux is not unix}\n添加自定义样式:搜索文本$s{背景;其他},例如 google$s{#333333;color:red;}\n左键点击关键词跳转至下一个,右键点击关键词跳转至上一个', inPagePlaceholder: '输入文字,按下回车进行页内查找', pickerBtn: '抓取元素', multiPickerBtn: '抓取元素,按住 Ctrl 或 Command 连续抓取', editBtn: '编辑查找文字', emptyBtn: '清空查找文字', copyInPageBtn: '复制查找文字', wordModeBtn: '单词模式', copyEleBtn: '复制选中元素', openLinkBtn: '打开选中链接', maxEleBtn: '展开选中元素', minEleBtn: '收起选中元素', expandAll: '全部展开', collapseAll: '全部合起', rename: '重命名', recoverBtn: '恢复查找文字', pinBtn: '固定查找文字,在所有标签页中搜索', locBtn: '定位侧边栏', filterSites: '搜索引擎', searchInPage: '页内查找', removeBtn: '移除搜索词', saveRuleBtn: '保存当前站点的搜索词', wordContent: '搜索词内容', wordHide: '隐藏父级元素', wordHideTips: '元素深度,0为当前父级', wordStyle: '搜索词样式', wordTitle: '搜索词注释', re: '正则', ignoreCase: '不区分大小写', filterLink: '筛选链接', modify: '修改', cancel: '取消', modifyWord: '修改页内搜索词', addSearchEngine: '添加搜索引擎', noValidItemAsk: '未找到有效元素,是否手动编辑规则并添加?', expand: '展开剩余站点', add: '添加', addWord: '添加新词语', wordRange: '生效范围', customInputFrame: '自定义搜索参数', customSubmit: '提交搜索', finalSearch: '目标搜索字串', search: '搜索此项', siteKeywords: '关键词(多个关键词以|分隔)', siteMatch: '站点 URL 匹配正则', openSelect: '打开选项', openInDefault: '默认', openInNewTab: '新标签页打开', openInCurrent: '当前页打开', currentType: '当前分类', maxAddSiteBtn: '最大化', minAddSiteBtn: '还原', addAction: '添加操作', crawlInfo: '模拟输入搜索', inputAction: '输入', clickAction: '点击', sleepAction: '等待', copyAction: '📄复制元素', submitCrawl: '☑️完成操作', inputOutput: '在元素#t1#内输入#t2#', clickOutput: '点击元素#t#', dblclickOutput: '双击元素#t#', rclickOutput: '右击元素#t#', copyOutput: '复制元素#t#', sleepOutput: '休眠#t#毫秒', inputNewValue: '请输入新值', deleteConfirm: '确定要删除此项吗?', sleepPrompt: '等待时间(毫秒)', startCache: '开始缓存,请耐心等待缓存完毕,勿关闭配置页!', cacheOver: '所有图标都已缓存完毕!', cspDisabled: '脚本样式被当前站点的 CSP 阻止,因此无法显示,请尝试安装 Allow CSP: Content-Security-Policy 扩展获取权限', Sunday: '星期日 (日)', Monday: '星期一 (月)', Tuesday: '星期二 (火)', Wednesday: '星期三 (水)', Thursday: '星期四 (木)', Friday: '星期五 (金)', Saturday: '星期六 (土)', template: '请设置【#t#】的值', recordAction: '⏺️录制操作', startRecord: '开始录制操作,按回车键结束录制', loopAction: '🔁开始循环', loopActionEnd: '⏹️循环结束', loopStart: '开始循环,循环次数为#t#', loopEnd: '结束循环', loopTimes: '循环次数,将遍历所有匹配元素并顺序执行', loadingCollection: '正在加载合集,请稍候……', emuInputTips: '在指定页面元素(例如输入框)内输入搜索词', emuClickTips: '单击指定页面元素(例如按钮)', emuWaitTips: '等待一段时间后继续执行,当某个操作需要一段时间才能完成时很有用', emuCopyTips: '复制指定元素的文本到剪贴板', emuRecordTips: '录制接下来的点击和输入操作', emuLoopTips: '开始循环,接下来的操作将遍历所有找到的元素并且重复指定次数', emuStopTips: '结束操作并生成规则' }; break; case "zh": case "zh-TW": case "zh-HK": config = { import: '導入', filter: '篩選', selectAll: '全選', importOrNot: '是否導入配置?', settings: '配置脚本', batchOpen: '批量打開', batchOpenConfirm: '確定要批量打開嗎?', postOver: '發送成功:', postError: '發送失敗:', copyOver: '複製成功', keywords: '請輸入搜尋詞', targetUrl: '請輸入搜尋URL', siteName: '站名', siteDesc: '描述', siteUrl: '地址', siteIcon: '圖標', siteTest: '測試', siteCancel: '取消', siteAdd: '添加', siteType: '分類', siteExist: '已存在相同規則,是否添加為克隆項?', siteAddOver: '站點添加成功', multiline: '是否以換行符分隔多行搜尋?', multilineTooMuch: '行數超過10行,是否繼續搜尋?', inputPlaceholder: '篩選引擎', inputTitle: '篩選引擎,支援 * ? 通配符,$代表末尾,^代表開頭,分組**站點 可篩選指定分組,例如 圖片**google,tab 下一項', inputKeywords: '輸入搜尋關鍵詞', inPageTips: '自定義分隔符:$c 加分隔符,例如 $c| search | jumper,默認空格作為分隔符\n原始文本不分隔:$o 加文本,例如$oopai liked by hero\n正則表達式:/re/,例如 $c, /google/i , /aPPle/\n添加提示文本:搜尋文本$t{提示文本},例如 linux$t{linux is not unix}\n添加自定義樣式:搜尋文本$s{背景;其他},例如 google$s{#333333;color:red;}\n左鍵點擊關鍵詞跳轉至下一個,右鍵點擊關鍵詞跳轉至上一個', inPagePlaceholder: '輸入文字,按下回車進行頁內查找', pickerBtn: '抓取元素', multiPickerBtn: '抓取元素,按住 Ctrl 或 Command 連續抓取', editBtn: '編輯查找文字', emptyBtn: '清空查找文字', copyInPageBtn: '複製查找文字', wordModeBtn: '單詞模式', copyEleBtn: '複製選中元素', openLinkBtn: '打開選中連結', maxEleBtn: '展開選中元素', minEleBtn: '收起選中元素', expandAll: '全部展開', collapseAll: '全部合起', rename: '重命名', recoverBtn: '恢復查找文字', pinBtn: '固定查找文字,在所有標籤頁中搜尋', locBtn: '定位側邊欄', filterSites: '搜尋引擎', searchInPage: '頁內查找', removeBtn: '移除搜尋詞', saveRuleBtn: '保存當前站點的搜尋詞', wordContent: '搜尋詞內容', wordHide: '隱藏父級元素', wordHideTips: '元素深度,0為當前父級', wordStyle: '搜尋詞樣式', wordTitle: '搜尋詞注釋', re: '正則', ignoreCase: '不區分大小寫', filterLink: '篩選鏈接', modify: '修改', cancel: '取消', modifyWord: '修改頁內搜尋詞', addSearchEngine: '添加搜尋引擎', noValidItemAsk: '未找到有效元素,是否手動編輯規則並添加?', expand: '展開剩餘站點', add: '添加', addWord: '添加新詞語', wordRange: '生效範圍', customInputFrame: '自定義搜尋參數', customSubmit: '提交搜尋', finalSearch: '目標搜尋字串', search: '搜尋此項', siteKeywords: '關鍵詞(多個關鍵詞以|分隔)', siteMatch: '站點 URL 匹配正則', openSelect: '打開選項', openInDefault: '默認', openInNewTab: '新標籤頁打開', openInCurrent: '當前頁打開', currentType: '當前分類', maxAddSiteBtn: '最大化', minAddSiteBtn: '還原', addAction: '添加操作', crawlInfo: '模擬輸入搜尋', inputAction: '輸入', clickAction: '點擊', sleepAction: '等待', copyAction: '📄複製元素', submitCrawl: '☑️完成操作', inputOutput: '在元素#t1#內輸入#t2#', clickOutput: '點擊元素#t#', dblclickOutput: '雙擊元素#t#', rclickOutput: '右擊元素#t#', copyOutput: '複製元素#t#', sleepOutput: '休眠#t#毫秒', inputNewValue: '請輸入新值', deleteConfirm: '確定要刪除此項嗎? ', sleepPrompt: '等待時間(毫秒)', startCache: '開始緩存,請耐心等待緩存完畢,勿關閉配置頁!', cacheOver: '所有圖標都已緩存完畢!', cspDisabled: '腳本樣式被當前站點的 CSP 阻止,因此無法顯示,請嘗試安裝 Allow CSP: Content-Security-Policy 擴展獲取權限', Sunday: '星期日 (日)', Monday: '星期一 (月)', Tuesday: '星期二 (火)', Wednesday: '星期三 (水)', Thursday: '星期四 (木)', Friday: '星期五 (金)', Saturday: '星期六 (土)', template: '請設置【#t#】的值', recordAction: '⏺️錄製動作', startRecord: '開始錄製操作,按下回車鍵結束錄製', loopAction: '🔁開始循環', loopActionEnd: '⏹️循環結束', loopStart: '開始循環,循環次數為#t#', loopEnd: '結束循環', loopTimes: '循環次數,將遍歷所有匹配元素並順序執行', loadingCollection: '正在載入合集,請稍候……', emuInputTips: '在指定頁面元素(例如輸入框)內輸入搜尋字詞', emuClickTips: '點擊指定頁面元素(例如按鈕)', emuWaitTips: '等待一段時間後繼續執行,當某個操作需要一段時間才能完成時很有用', emuCopyTips: '複製指定元素的文字到剪貼簿', emuRecordTips: '錄製接下來的點擊和輸入操作', emuLoopTips: '開始循環,接下來的操作將遍歷所有找到的元素並且重複指定次數', emuStopTips: '結束操作並產生規則' }; break; case 'ja': config = { import: 'インポート', filter: 'フィルター', selectAll: 'すべて選択', importOrNot: '設定をインポートしますか? ', settings: '構成スクリプト', batchOpen: 'バッチオープン', batchOpenConfirm: 'バッチオープンしてもよろしいですか? ', postOver: '正常に送信されました:', postError: '送信に失敗しました:', copyOver: 'コピーに成功しました', keywords: '検索語を入力してください', targetUrl: '検索 URL を入力してください', siteName: 'サイト名', siteDesc: '説明', siteUrl: 'アドレス', siteIcon: 'アイコン', siteTest: 'テスト', siteCancel: 'キャンセル', siteAdd: '追加', siteType: 'カテゴリ', siteExist: '同じルールがすでに存在します。クローンとして追加しますか? ', siteAddOver: 'サイトは正常に追加されました', multiline: '複数行の検索は改行で区切るべきですか? ', multilineTooMuch: '行数が 10 行を超えています。検索を続けますか? ', inputPlaceholder: 'フィルタリング エンジン', inputTitle: 'フィルタリング エンジン、*? ワイルドカードをサポート、$ は終わりを表し、^ は始まりを表します、グループ ** サイトは写真などの指定されたグループをフィルターできます ** Google、次の項目をタブします', inputKeywords: '検索キーワードを入力してください', inPageTips: 'カスタム区切り文字: $c と区切り文字 ($c| 検索 | ジャンパーなど)、デフォルトのスペースを区切り文字として使用\n元のテキストは分離されていません: $o と文字 (ヒーローが好んだ $oopai など)\n正規表現 :/re/ 、$c、/google/i、/aPPle/ など\nプロンプト テキストの追加: 検索テキスト $t{プロンプト テキスト}、たとえば linux$t{Linux は Unix ではありません}\nカスタム スタイルの追加: 検索テキスト $s{背景;other}、例: google$s{#333333;color:red;}\nキーワードを左クリックすると次のキーワードにジャンプし、キーワードを右クリックすると前のキーワードにジャンプします', inPagePlaceholder: 'ページ内を検索するには、テキストを入力して Enter キーを押してください', pickerBtn: '要素の取得', multiPickerBtn: '要素を取得するには、Ctrl または Command を押したまま継続的に取得します', editBtn: '検索テキストを編集', emptyBtn: '空の検索テキスト', copyInPageBtn: '検索テキストをコピー', wordModeBtn: 'ワードモード', copyEleBtn: '選択した要素をコピー', openLinkBtn: '選択したリンクを開く', maxEleBtn: '選択した要素を展開', minEleBtn: '選択した要素を折りたたむ', expandAll: 'すべて展開', collapseAll: 'すべて折り', rename: '名前を変更', reverseBtn: '検索テキストを復元', pinBtn: '検索テキストを修正、すべてのタブで検索', locBtn: 'サイドバーを検索', filterSites: '検索エンジン', searchInPage: 'ページ内を検索', removeBtn: '検索語を削除', saveRuleBtn: '現在のサイトの検索語を保存', wordContent: '単語の内容を検索', wordHide: '親要素を非表示', wordHideTips: '要素の深さ、0 が現在の親', wordStyle: '検索ワードスタイル', wordTitle: '検索単語の注釈', re: 'RegExp', ignoreCase: '大文字と小文字は区別されません', filterLink: 'フィルターリンク', modify: '変更', cancel: 'キャンセル', modifyWord: 'ページ上の検索ワードを変更します', addSearchEngine: '検索エンジンを追加', noValidItemAsk: '有効な要素が見つかりません。ルールを手動で編集して追加しますか? ', expand: '残りのサイトを展開します', add: '追加', addWord: '新しい単語を追加', wordRange: '有効範囲', customInputFrame: 'カスタム検索パラメータ', customSubmit: '検索を送信', finalSearch: '対象の検索文字列', search: 'このアイテムを検索', siteKeywords: 'キーワード (| で区切られた複数のキーワード)', siteMatch: '通常のサイト URL と一致', openSelect: 'オプションを開く', openInDefault: 'デフォルト', openInNewTab: '新しいタブが開きます', openInCurrent: '現在のページが開いています', currentType: '現在のカテゴリ', maxAddSiteBtn: '最大化', minAddSiteBtn: '復元', addAction: 'アクションを追加', rollInfo: '入力検索をシミュレート', inputAction: '入力', clickAction: 'クリック', sleepAction: '待機', copyAction: '📄要素のコピー', submitCrawl: '☑️操作を完了', inputOutput: '要素 #t1# 内に #t2# を入力します', clickOutput: 'クリック#t#', dblclickOutput: 'ダブルクリック#t#', rclickOutput: '右クリック#t#', copyOutput: 'コピー要素#t#', sleepOutput: 'スリープ#t# ミリ秒', inputNewValue: '新しい値を入力してください', deleteconfirm: 'この項目を削除してもよろしいですか? ', sleepPrompt: '待機時間 (ミリ秒)', startCache: 'キャッシュを開始します。キャッシュが完了するまで辛抱強く待ってください。設定ページは閉じないでください。 ', cacheOver: 'すべてのアイコンがキャッシュされました! ', cspDisabled: 'スクリプト スタイルは現在のサイトの CSP によってブロックされているため、表示できません。許可を取得するには、Allow CSP: Content-Security-Policy 拡張機能をインストールしてみてください', Sunday: '日曜日', Monday: '月曜日', Tuesday: '火曜日', Wednesday: '水曜日', Thursday: '木曜日', Friday: '金曜日', Saturday: '土曜日', template: '[#t#]の値を設定してください', recordAction: '⏺️記録操作', startRecord: '記録操作を開始します。記録を終了するには Enter キーを押してください', loopAction: '🔁ループの開始', loopActionEnd: '⏹️ループの終了', loopStart: 'ループを開始。ループ数は #t# です', loopEnd: 'ループの終了', loopTimes: 'ループの数。一致するすべての要素が走査され、順番に実行されます', loadingCollection: 'コレクションを読み込み中...', emuInputTips: '指定されたページ要素 (入力ボックスなど) に検索語を入力します', emuClickTips: '指定されたページ要素 (ボタンなど) をクリックします', emuWaitTips: '続行する前にしばらく待ってください。操作が完了するまでに時間がかかる場合に便利です', emuCopyTips: '指定された要素のテキストをクリップボードにコピーします', emuRecordTips: '次のクリックと入力操作を記録します', emuLoopTips: 'ループを開始します。次の操作は見つかったすべての要素を走査し、指定された回数だけ繰り返します', emuStopTips: '操作を終了してルールを生成' }; break; case 'ru': config = { import: 'Импортировать', filter: 'Фильтровать', selectAll: 'Выбрать всё', importOrNot: 'Импортировать эту конфигурацию?', settings: 'Настройки', batchOpen: 'Групповой поиск', batchOpenConfirm: 'Искать с помощью всех движков группы?', postOver: 'Post over: ', postError: 'Post fail: ', copyOver: 'Скопировано успешно', keywords: 'Input keywords', targetUrl: 'Input URL', siteName: 'Название', siteDesc: 'Описание', siteUrl: 'URL', siteIcon: 'Иконка', siteTest: 'Тест', siteCancel: 'Отменить', siteAdd: 'Добавить', siteType: 'Группа', siteExist: 'Движок уже существует. Добавить его как клон?', siteAddOver: 'Движок успешно добавлен', multiline: 'Использовать многострочный поиск?', multilineTooMuch: 'Количество строк превышает 10. Продолжить поиск?', inputPlaceholder: 'Фильтры', inputTitle: 'Filter engines, support * ? wildcards, $ means end, ^ means start, type name**site name to filter type like "image**google", tab to next. ', inputKeywords: 'Ввести ключевые слова поиска', inPageTips: 'Custom delimiter: $c + delimiter, such as $c| search | jumper, space as delimiter by default\nOriginal text without delimited: $o + text, such as $oopai liked by hero\nRegular expression: /re/, such as $c, /google/i , /aPPle/\nTips text: search text$t{tips text}, such as linux$t{linux is not unix}\nCustom style: Search text$s{background;other}, such as google$s{#333333;color:red;}\nLeft-click keyword to jump to the next, right-click keyword to jump to the previous', inPagePlaceholder: 'Для поиска введите текст и нажмите Enter', pickerBtn: 'Выбрать область', multiPickerBtn: 'Выбрать элемент или выбрать несколько элементов с помощью Ctrl или Command', editBtn: 'Редактировать текст поиска', emptyBtn: 'Очистить поле ввода', copyInPageBtn: 'Скопировать текст поика', wordModeBtn: 'Режим поиска по словам. В поле ввода можно ввести целое предложение, после чего на странице будут искаться все слова по отдельности из которого состоит предложение', copyEleBtn: 'Скопировать выбранные элементы', openLinkBtn: 'Открыть выбранные ссылки', maxEleBtn: 'Расширить выбранные элементы', minEleBtn: 'Сжать выбранные элементы', expandAll: 'Развернуть всё', collapseAll: 'Свернуть всё', rename: 'Rename', recoverBtn: 'Recover find text', pinBtn: 'Выделить цветом текущие ключевые слова поиска по странице во всех открытых вкладках', locBtn: 'Отображать совпадения справа на панели', filterSites: 'Фильтровать движки', searchInPage: 'Искать на странице', removeBtn: 'Удалить правило поиска', saveRuleBtn: 'Сохранить правило поиска текущего сайта', wordContent: 'Слово или фраза для поиска', wordHide: 'Hide parent element', wordHideTips: 'Глубина элемента, 0 - это текущее значение', wordStyle: 'Стиль выделения слова', wordTitle: 'Аннотация к искомому слову', re: 'RegExp', ignoreCase: 'Игнорировать регистр', filterLink: 'Фильтровать ссылку', modify: 'Готово', cancel: 'Отменить', modifyWord: 'Изменить параметры', addSearchEngine: 'Добавить движок', noValidItemAsk: 'Не найден подходящий элемент. Хотите вручную добавить сайт?', expand: 'Развернуть другие сайты', add: 'Добавить', addWord: 'Добавить новое слово', wordRange: 'Выделить область поиска', customInputFrame: 'Пользовательские параметры поиска', customSubmit: 'Принять', finalSearch: 'Целевая строка поиска', search: 'Искать это', siteKeywords: 'Ключевые слова (разделитель |)', siteMatch: 'Regexp для соответствия URL сайта', openSelect: 'Открыть в', openInDefault: 'По умолчанию', openInNewTab: 'Открыть в новой вкладке', openInCurrent: 'Открыть в текущей вкладке', currentType: 'Current', maxAddSiteBtn: 'Развернуть', minAddSiteBtn: 'Свернуть', addAction: 'Добавить действия', crawlInfo: 'Симуляция действий на сайте', inputAction: 'Ввод', clickAction: 'Клик мыши', sleepAction: 'Ожидание', copyAction: '📄Копировать элемент', submitCrawl: '☑️Завешить действие', inputOutput: 'Ввод #t2# в элемент #t1#', clickOutput: 'Клик по элементу #t#', dblclickOutput: 'Двойной клик #t#', rclickOutput: 'щелкните ПКМ #t#', copyOutput: 'Копировать элемент #t#', sleepOutput: 'Ждать #t# миллисекунд', inputNewValue: 'Введите новое значение', deleteConfirm: 'Хотите удалить этот элемент? ', sleepPrompt: 'Время ожидания (в миллисекундах)', startCache: 'Началось кширование закрывайте страницу!', cacheOver: 'Все иконки кэшированы!', cspDisabled: 'The style of SearchJumper is blocked by the CSP of current site, please try to install the Allow CSP: Content-Security-Policy extension to obtain permission', template: 'Установите значение "#t#"', recordAction: '⏺️Записать действие', startRecord: 'Сейчас начнется запись действия. После завершения нажмите Enter, чтобы вернуться в окно редактирования.', loopAction: '🔁Начать цикл', loopActionEnd: '⏹️Остановить цикл', loopStart: 'Начать цикл #t# раз', loopEnd: 'Остановить цикл', loopTimes: 'Количество циклов, все совпадающие элементы будут пройдены и выполнены последовательно', loadingCollection: 'Preparing collection for SearchJumper...', emuInputTips: 'Ввести поисковые запросы в указанные элементы страницы (например, в поля ввода).', emuClickTips: 'Кликнуть по указанному элементу страницы (например, по кнопке)', emuWaitTips: 'Подождите некоторое время, прежде чем продолжить. Полезно, когда операция требует некоторого времени для завершения', emuCopyTips: 'Копирование текста указанного элемента в буфер обмена', emuRecordTips: 'Записать следующие нажатия и операции ввода', emuLoopTips: 'Запустить цикл, следующая операция будет обходить все найденные элементы и повторяться указанное количество раз', emuStopTips: 'Завершить операцию и создать правило' }; break; default: config = { import: 'Import', filter: 'Filter', selectAll: 'SelectAll', importOrNot: 'Do you want to import this config?', settings: 'Settings', batchOpen: 'Batch open', batchOpenConfirm: 'Batch open urls?', postOver: 'Post over: ', postError: 'Post fail: ', copyOver: 'Copied successfully', keywords: 'Input keywords', targetUrl: 'Input URL', siteName: 'Site Name', siteDesc: 'Description', siteUrl: 'Site Url', siteIcon: 'Site Icon', siteTest: 'Test', siteCancel: 'Cancel', siteAdd: 'Add', siteType: 'Category', siteExist: 'Site is already exist, add it as clone?', siteAddOver: 'Site added successfully', multiline: 'Search as multilines?', multilineTooMuch: 'The number of lines exceeds 10, do you want to continue searching?', inputPlaceholder: 'Filter engines', inputTitle: 'Filter engines, support * ? wildcards, $ means end, ^ means start, type name**site name to filter type like "image**google", tab to next. ', inputKeywords: 'Enter search keywords', inPageTips: 'Custom delimiter: $c + delimiter, such as $c| search | jumper, space as delimiter by default\nOriginal text without delimited: $o + text, such as $oopai liked by hero\nRegular expression: /re/, such as $c, /google/i , /aPPle/\nTips text: search text$t{tips text}, such as linux$t{linux is not unix}\nCustom style: Search text$s{background;other}, such as google$s{#333333;color:red;}\nLeft-click keyword to jump to the next, right-click keyword to jump to the previous', inPagePlaceholder: 'Input text, press Enter to find in the page', pickerBtn: 'Pick a element', multiPickerBtn: 'Pick a element, pick multi elements with Ctrl or Command', editBtn: 'Edit search text', emptyBtn: 'Empty search text', copyInPageBtn: 'Copy search text', wordModeBtn: 'Word mode', copyEleBtn: 'Copy selected elements', openLinkBtn: 'Open selected links', maxEleBtn: 'Expand selected elements', minEleBtn: 'Collapse selected elements', expandAll: 'Expand All', collapseAll: 'Collapse All', rename: 'Rename', recoverBtn: 'Recover find text', pinBtn: 'Pin search text to search in all tabs', locBtn: 'Sidebar to locate', filterSites: 'Search engines', searchInPage: 'Find in page', removeBtn: 'Remove search term', saveRuleBtn: 'Save the search term of the current site', wordContent: 'Search word content', wordHide: 'Hide parent element', wordHideTips: 'Element depth, 0 means the current', wordStyle: 'Search word style', wordTitle: 'Search word annotation', re: 'RegExp', ignoreCase: 'Ignore case', filterLink: 'Filter link', modify: 'Modify', cancel: 'Cancel', modifyWord: 'Modify search word', addSearchEngine: 'Add search engine', noValidItemAsk: 'No valid element found, do you want to manually edit the rule and add it?', expand: 'Expand other sites', add: 'Add', addWord: 'Add new word', wordRange: 'Effective range', customInputFrame: 'Custom search parameters', customSubmit: 'Submit', finalSearch: 'Target search string', search: 'Search this', siteKeywords: 'Keywords(split by |)', siteMatch: 'Regexp to match site URL', openSelect: 'Open option', openInDefault: 'Default', openInNewTab: 'Open a new tab', openInCurrent: 'Open in current', currentType: 'Current', maxAddSiteBtn: 'Maximize', minAddSiteBtn: 'Restore', addAction: 'Add Actions', crawlInfo: 'Analog input search', inputAction: 'Input', clickAction: 'Click', sleepAction: 'Wait', copyAction: '📄Copy element', submitCrawl: '☑️Complete operation', inputOutput: 'Input #t2# in the element #t1#', clickOutput: 'Click on element #t#', dblclickOutput: 'Double click #t#', rclickOutput: 'Right click #t#', copyOutput: 'Copy element #t#', sleepOutput: 'Sleep for #t# milliseconds', inputNewValue: 'Please enter a new value', deleteConfirm: 'Are you sure you want to delete this item? ', sleepPrompt: 'Wait time (milliseconds)', startCache: 'Start cache icons of engines, do not close this page!', cacheOver: 'All icons cached!', cspDisabled: 'The style of SearchJumper is blocked by the CSP of current site, please try to install the Allow CSP: Content-Security-Policy extension to obtain permission', template: 'Please set the value of "#t#"', recordAction: '⏺️Record operation', startRecord: 'Start to record operation, press Enter to end', loopAction: '🔁Start loop', loopActionEnd: '⏹️Stop loop', loopStart: 'Start loop #t# times', loopEnd: 'Stop loop', loopTimes: 'Number of loops, all matching elements will be traversed and executed sequentially', loadingCollection: 'Preparing collection for SearchJumper...', emuInputTips: 'Enter search terms in specified page elements (such as input boxes)', emuClickTips: 'Click on a specified page element (such as a button)', emuWaitTips: 'Wait for a while before continuing, useful when an operation takes a while to complete', emuCopyTips: 'Copy the text of the specified element to the clipboard', emuRecordTips: 'Record the next clicks and input operations', emuLoopTips: 'Start the loop, the next operation will traverse all found elements and repeat the specified number of times', emuStopTips: 'End the operation and generate rules' }; break; } } function i18n(name, param) { return config[name] ? (param ? config[name].replace(/#t#/g, param).replace(/#t1#/g, param[0]).replace(/#t2#/g, param[1]) : config[name]) : name; }; const isMobile = ('ontouchstart' in document.documentElement); var enableDebug = true; var debug = (str, title) => { if(enableDebug) { console.log( `%c【SearchJumper v.${_GM_info.script.version}】 ${title ? title : 'debug'}`, 'color: orange;font-size: large;font-weight: bold;', str ); } }; var disabled = false; var isInConfigPage = false; var lastRequestUrl; function createHTML(html = "", doc) { const targetDoc = doc || document; const fragment = targetDoc.createDocumentFragment(); if (html === null || html === undefined || html === '') return fragment; parseHTMLToFragment(String(html), fragment, targetDoc); return fragment; } let canDirectSetHTML = true; let canPolicySetHTML = true; let escapeHTMLPolicy; let escapeHTMLCreator; const MY_POLICY_NAME = 'searchjumper_default'; const SVG_NS = 'http://www.w3.org/2000/svg'; const VOID_TAGS = { area: true, base: true, br: true, col: true, embed: true, hr: true, img: true, input: true, link: true, meta: true, param: true, source: true, track: true, wbr: true }; const RAW_TEXT_TAGS = { script: true, style: true, textarea: true, title: true, xmp: true, plaintext: true, noscript: true }; const HTML_ENTITIES = { amp: '&', lt: '<', gt: '>', quot: '"', apos: "'", nbsp: '\u00A0' }; function decodeEntities(text) { return text.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g, function(_, code) { if (code[0] === '#') { const isHex = code[1] === 'x' || code[1] === 'X'; const num = parseInt(code.slice(isHex ? 2 : 1), isHex ? 16 : 10); if (!isNaN(num)) { try { return String.fromCodePoint(num); } catch(e) {} } return '&' + code + ';'; } const key = code.toLowerCase(); return (key in HTML_ENTITIES) ? HTML_ENTITIES[key] : '&' + code + ';'; }); } function parseHTMLToFragment(html, fragment, doc) { const stack = [fragment]; const tokenRe = /|]*>|<\/?[a-zA-Z][^>]*>|[^<]+/gi; let match; while ((match = tokenRe.exec(html))) { const token = match[0]; if (token[0] !== '<') { const text = decodeEntities(token); if (text) stack[stack.length - 1].appendChild(doc.createTextNode(text)); continue; } if (token.indexOf('