Greasy Fork

Greasy Fork is available in English.

Onestep Search - 一键快搜

无缝集成 划词搜索 + 快捷键搜索 + 搜索跳转 + 网址导航, 享受丝滑搜索体验

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        Onestep Search - 一键快搜
// @namespace   http://greasyfork.icu/zh-CN/scripts/440000
// @version     2.1.5
// @author      eyinwei
// @description 无缝集成 划词搜索 + 快捷键搜索 + 搜索跳转 + 网址导航, 享受丝滑搜索体验
// @homepageURL https://github.com/eyinwei/Quick_Search
// @icon        https://s2.loli.net/2022/08/16/kwm38v2TxY4OtCs.png
// @match       *://*/*
// @grant       GM_openInTab
// @grant       GM_setClipboard
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_xmlhttpRequest
// @run-at      document-end
// @license MIT
// @original-author smallx
// ==/UserScript==




(function () {
    'use strict';


    ///////////////////////////////////////////////////////////////////
    // 配置
    ///////////////////////////////////////////////////////////////////


	//=========================定义网站数据=======================================
	function SiteInfo(_name, _url, _homepage, _icon) {
		this.name = _name;
		this.url = _url;
		this.home = _homepage;
		this.icon = _icon;

		this.callSiteInformation = function (_enable = true) {
			return {
				name: _name,
				url: _url,
				home: _homepage,
				icon: _icon,
				enable: _enable,
			};
		};
	};

	// ========================= 前置统一定义所有网站 =========================
	// 百度系列
	const Baidu = new SiteInfo('百度', 'https://www.baidu.com/s?wd=%s&ie=utf-8', 'https://www.baidu.com/', 'https://www.baidu.com/favicon.ico');
	const Baidufanyi = new SiteInfo('百度翻译', 'https://fanyi.baidu.com/#auto/zh/%s', 'https://fanyi.baidu.com/', 'https://fanyi-cdn.cdn.bcebos.com/webStatic/translation/img/favicon/favicon.ico');
	const Baiduwangpan = new SiteInfo('百度网盘', 'https://pan.baidu.com/disk/home?#/search?key=%s', 'https://pan.baidu.com/', 'https://nd-static.bdstatic.com/m-static/v20-main/favicon-main.ico');
	const Baidubaike = new SiteInfo('百度百科', 'https://baike.baidu.com/search/word?pic=1&sug=1&word=%s', 'https://baike.baidu.com/', 'https://baike.baidu.com/favicon.ico');
	const Baiduzhidao = new SiteInfo('百度知道', 'https://zhidao.baidu.com/search?word=%s', 'https://zhidao.baidu.com/', 'https://www.baidu.com/favicon.ico?t=20171027');
	const Baiduxinwen = new SiteInfo('百度新闻', 'https://www.baidu.com/s?rtt=1&bsst=1&cl=2&tn=news&rsv_dl=ns_pc&word=%s', 'http://news.baidu.com/', 'https://www.baidu.com/favicon.ico');
	const Baiduwenku = new SiteInfo('百度文库', 'https://wenku.baidu.com/search?word=%s', '', 'https://www.baidu.com/favicon.ico');
	const Baidumap = new SiteInfo('百度地图', 'https://map.baidu.com/search?querytype=s&wd=%s', '', 'https://map.baidu.com/favicon.ico');
	const Baidutupian = new SiteInfo('百度图片', 'https://image.baidu.com/search/index?tn=baiduimage&ie=utf-8&word=%s', '', 'https://www.baidu.com/favicon.ico');
	const Baiduxueshu = new SiteInfo('百度学术', 'http://xueshu.baidu.com/s?wd=%s', '', 'https://www.baidu.com/favicon.ico');
	const Baidutieba = new SiteInfo('贴吧', 'https://tieba.baidu.com/f?kw=%s&ie=utf-8', 'https://tieba.baidu.com/', 'https://www.baidu.com/favicon.ico');

	// 谷歌系列
	const Google = new SiteInfo('谷歌', 'https://www.google.com/search?q=%s&ie=utf-8&oe=utf-8', 'https://www.google.com/', 'https://s2.loli.net/2022/08/16/QUL3cvA4t7Tx5sE.png');
	const Googlefanyi = new SiteInfo('谷歌翻译', 'https://translate.google.com/?q=%s', '', 'https://ssl.gstatic.com/translate/favicon.ico');
	const Googlemap = new SiteInfo('谷歌地图', 'https://www.google.com/maps/search/%s', 'https://www.google.com/maps/', 'https://s2.loli.net/2022/08/17/SloXZzf9nC6LPbq.png');
	const Googleearth = new SiteInfo('谷歌地球', 'https://earth.google.com/web/search/%s', 'https://earth.google.com/web/', 'https://s2.loli.net/2022/08/17/IOiPDl7YX3QnmsC.png');
	const Googlexueshu = new SiteInfo('谷歌学术', 'https://scholar.google.com/scholar?hl=zh-CN&q=%s', '', 'https://favicon.im/scholar.google.com');
	const Googlepic = new SiteInfo('谷歌图片', 'https://www.google.com/search?q=%s&tbm=isch', 'https://www.google.com/imghp?hl=zh-CN', Google.icon);
	const Googlenews = new SiteInfo('谷歌新闻', 'https://news.google.com/search?q=%s&hl=zh-CN&gl=CN&ceid=CN:zh-Hans', 'https://news.google.com/topstories?hl=zh-CN&gl=CN&ceid=CN:zh-Hans', 'https://s2.loli.net/2022/08/17/RTdZQMD2Aw8eobn.png');

	// 其他常用网站
	const StackOverflow = new SiteInfo('StackOverflow', 'https://stackoverflow.com/search?q=%s', '', 'https://favicon.im/stackoverflow.com');
	const Zhihu = new SiteInfo('知乎', 'https://www.zhihu.com/search?q=%s', 'https://www.zhihu.com/', 'https://static.zhihu.com/heifetz/favicon.ico');
	const Bing = new SiteInfo('必应', 'https://cn.bing.com/search?q=%s', 'https://cn.bing.com/', 'https://s2.loli.net/2022/08/16/3uWMUjDVAlS8c9T.png');
	const Bilibili = new SiteInfo('哔哩哔哩', 'https://search.bilibili.com/all?keyword=%s', 'https://www.bilibili.com/', 'https://www.bilibili.com/favicon.ico?v=1');
	const Taobao = new SiteInfo('淘宝', 'https://s.taobao.com/search?q=%s', 'https://www.taobao.com/', 'https://www.taobao.com/favicon.ico');
	const Jingdong = new SiteInfo('京东', 'https://search.jd.com/Search?keyword=%s&enc=utf-8', 'https://www.jd.com/', 'https://search.jd.com/favicon.ico');
	const Tianmao = new SiteInfo('天猫', 'https://list.tmall.com/search_product.htm?q=%s', 'https://www.tmall.com/', 'https://www.tmall.com/favicon.ico');
	const Maimai = new SiteInfo('脉脉', 'https://maimai.cn/web/search_center?type=gossip&query=%s&highlight=true', 'https://maimai.cn/feed_list', 'https://maimai.cn/favicon.ico');
	const Weibo = new SiteInfo('微博', 'https://s.weibo.com/weibo/%s', 'https://weibo.com/', 'https://s.weibo.com/favicon.ico');
	const GitHub = new SiteInfo('GitHub', 'https://github.com/search?q=%s','https://github.com', 'https://favicon.im/github.com');
	const Sougou = new SiteInfo('搜狗', 'https://www.sogou.com/web?query=%s','https://www.sogou.com', 'https://favicon.im/www.sogou.com');
	const So360 = new SiteInfo('360', 'https://www.so.com/s?ie=utf-8&q=%s', 'https://www.so.com/', 'https://s.ssl.qhimg.com/static/121a1737750aa53d.ico');
	const Quora = new SiteInfo('Quora', 'https://www.quora.com/search?q=%s', 'https://www.quora.com/', 'https://favicon.im/www.quora.com');
	const Wikipedia = new SiteInfo('维基百科', 'https://zh.wikipedia.org/wiki/%s', 'https://zh.wikipedia.org/', 'https://favicon.im/zh.wikipedia.org');
	const Moegirl = new SiteInfo('萌娘百科', 'https://zh.moegirl.org/%s', 'https://zh.moegirl.org.cn/', 'https://zh.moegirl.org.cn/favicon.ico');
	const Docin = new SiteInfo('豆丁文档', 'https://www.docin.com/search.do?searchcat=2&searchType_banner=p&nkey=%s', 'https://www.docin.com/', 'https://www.docin.com/favicon.ico');
	const DoubanBook = new SiteInfo('豆瓣读书', 'https://search.douban.com/book/subject_search?search_text=%s', 'https://book.douban.com/', 'https://www.douban.com/favicon.ico');
	const WeixinSogou = new SiteInfo('微信(搜狗)', 'https://weixin.sogou.com/weixin?ie=utf8&type=2&query=%s', 'https://weixin.sogou.com/', 'https://dlweb.sogoucdn.com/translate/favicon.ico?v=20180424');
	const Guokr = new SiteInfo('果壳', 'https://www.guokr.com/search/all/?wd=%s', 'https://www.guokr.com/', 'https://www.guokr.com/favicon.ico');
	const ApacheIssues = new SiteInfo('Apache Issues', 'https://issues.apache.org/jira/secure/QuickSearch.jspa?searchString=%s', 'https://issues.apache.org/jira/', 'https://favicon.im/issues.apache.org');
	const Maven = new SiteInfo('Maven', 'https://mvnrepository.com/search?q=%s', 'https://mvnrepository.com/', 'https://favicon.im/mvnrepository.com');
	const Youdao = new SiteInfo('有道词典', 'https://youdao.com/w/%s', 'https://youdao.com/', 'https://shared-https.ydstatic.com/images/favicon.ico');
	const Bingfanyi = new SiteInfo('必应翻译', 'https://cn.bing.com/dict/search?q=%s', 'https://www.bing.com/dict', Bing.icon);
	const Haici = new SiteInfo('海词词典', 'http://dict.cn/%s', 'http://dict.cn/', 'http://i1.haidii.com/favicon.ico');
	const CNKIfanyi = new SiteInfo('CNKI翻译', 'http://dict.cnki.net/dict_result.aspx?scw=%s', 'http://dict.cnki.net/', 'https://epub.cnki.net/favicon.ico');
	const Zdic = new SiteInfo('汉典', 'https://www.zdic.net/hans/%s', 'https://www.zdic.net/', 'https://www.zdic.net/favicon.ico');
	const Deepl = new SiteInfo('deepL', 'https://www.deepl.com/translator#en/zh/%s', 'https://www.deepl.com/', 'https://s2.loli.net/2022/08/17/m3H5BdLRAexbVsz.png');
	const GaodeMap = new SiteInfo('高德地图', 'https://www.amap.com/search?query=%s', 'https://www.amap.com/', 'https://a.amap.com/pc/static/favicon.ico');
	const SogouPic = new SiteInfo('搜狗图片', 'https://pic.sogou.com/pics?query=%s', 'https://pic.sogou.com/', 'https://dlweb.sogoucdn.com/translate/favicon.ico?v=20180424');
	const BingPic = new SiteInfo('必应图片', 'https://www.bing.com/images/search?q=%s', 'https://www.bing.com/images/trending', Bing.icon);
	const Pixiv = new SiteInfo('pixiv', 'https://www.pixiv.net/tags/%s', 'https://www.pixiv.net/', 'https://s2.loli.net/2022/08/17/OxGZLn26TlWyQt9.png');
	const Flickr = new SiteInfo('flickr', 'https://www.flickr.com/search/?q=%s', 'https://www.flickr.com/', 'https://combo.staticflickr.com/pw/favicon.ico');
	const Huaban = new SiteInfo('花瓣', 'https://huaban.com/search/?q=%s', 'https://huaban.com/', 'https://huaban.com/favicon.ico');
	const NeteaseMusic = new SiteInfo('网易云音乐', 'https://music.163.com/#/search/m/?s=%s', 'https://music.163.com/', 'https://s1.music.126.net/style/favicon.ico?v20180823');
	const QQMusic = new SiteInfo('QQ音乐', 'https://y.qq.com/portal/search.html#page=1&searchid=1&remoteplace=txt.yqq.top&t=song&w=%s', 'https://y.qq.com/', 'https://y.qq.com/favicon.ico?max_age=2592000');
	const KuwoMusic = new SiteInfo('酷我音乐', 'http://www.kuwo.cn/search/list?type=all&key=%s', 'http://www.kuwo.cn/', 'http://www.kuwo.cn/favicon.ico');
	const Kugou5sing = new SiteInfo('酷狗5sing', 'http://search.5sing.kugou.com/?keyword=%s', 'http://5sing.kugou.com/index.html', 'http://5sing.kugou.com/favicon.ico');
	const Dangdang = new SiteInfo('当当', 'http://search.dangdang.com/?key=%s&act=input', 'http://www.dangdang.com/', 'http://www.dangdang.com/favicon.ico');
	const Suning = new SiteInfo('苏宁', 'https://search.suning.com/%s/', 'https://www.suning.com/', 'https://www.suning.com/favicon.ico');
	const Amazon = new SiteInfo('亚马逊', 'https://www.amazon.cn/s?k=%s', 'https://www.amazon.cn/', 'https://www.amazon.cn/favicon.ico');
	const CNKI = new SiteInfo('知网', 'http://epub.cnki.net/kns/brief/default_result.aspx?txt_1_value1=%s&dbPrefix=SCDB&db_opt=CJFQ%2CCJFN%2CCDFD%2CCMFD%2CCPFD%2CIPFD%2CCCND%2CCCJD%2CHBRD&singleDB=SCDB&action=scdbsearch', 'http://epub.cnki.net/', 'https://epub.cnki.net/favicon.ico');
	const Wanfang = new SiteInfo('万方', 'http://www.wanfangdata.com.cn/search/searchList.do?searchType=all&searchWord=%s', 'http://www.wanfangdata.com.cn/', 'https://cdn.s.wanfangdata.com.cn/favicon.ico');
	const WOS = new SiteInfo('WOS', 'http://apps.webofknowledge.com/UA_GeneralSearch.do?fieldCount=3&action=search&product=UA&search_mode=GeneralSearch&max_field_count=25&max_field_notice=Notice%3A+You+cannot+add+another+field.&input_invalid_notice=Search+Error%3A+Please+enter+a+search+term.&input_invalid_notice_limits=+%3Cbr%2F%3ENote%3A+Fields+displayed+in+scrolling+boxes+must+be+combined+with+at+least+one+other+search+field.&sa_img_alt=Select+terms+from+the+index&value(input1)=%s&value%28select1%29=TI&value%28hidInput1%29=initVoid&value%28hidShowIcon1%29=0&value%28bool_1_2%29=AND&value%28input2%29=&value%28select2%29=AU&value%28hidInput2%29=initAuthor&value%28hidShowIcon2%29=1&value%28bool_2_3%29=AND&value%28input3%29=&value%28select3%29=SO&value%28hidInput3%29=initSource&value%28hidShowIcon3%29=1&limitStatus=collapsed&expand_alt=Expand+these+settings&expand_title=Expand+these+settings&collapse_alt=Collapse+these+settings&collapse_title=Collapse+these+settings&SinceLastVisit_UTC=&SinceLastVisit_DATE=×panStatus=display%3A+block&timeSpanCollapsedListStatus=display%3A+none&period=Range+Selection&range=ALL&ssStatus=display%3Anone&ss_lemmatization=On&ss_query_language=&rsStatus=display%3Anone&rs_rec_per_page=10&rs_sort_by=PY.D%3BLD.D%3BVL.D%3BSO.A%3BPG.A%3BAU.A&rs_refinePanel=visibility%3Ashow', 'http://apps.webofknowledge.com/', 'https://favicon.im/clarivate.com');
	const Springer = new SiteInfo('Springer', 'http://rd.springer.com/search?query=%s', 'http://rd.springer.com/', 'https://link.springer.com/oscar-static/img/favicons/darwin/favicon-de0c289efe.ico');
	const Letpub = new SiteInfo('Letpub', 'https://www.letpub.com.cn/index.php?page=journalapp&view=search&searchsort=relevance&searchname=%s', 'https://www.letpub.com.cn/', 'https://www.letpub.com.cn/images/favicon.ico');
	const Ablesci = new SiteInfo('科研通', 'https://www.ablesci.com/journal/index?keywords=%s', 'https://www.ablesci.com/', 'https://www.ablesci.com/favicon.ico/');
	const Douban = new SiteInfo('豆瓣', 'https://www.douban.com/search?q=%s', 'https://www.douban.com/', 'https://www.douban.com/favicon.ico');
	const Twitter = new SiteInfo('Twitter', 'https://twitter.com/search?q=%s', 'https://twitter.com/', 'https://s2.loli.net/2022/08/17/rsbLXJA1lG5hmfe.png');
	const Facebook = new SiteInfo('Facebook', 'https://www.facebook.com/search/results.php?q=%s', 'https://www.facebook.com/', 'https://s2.loli.net/2022/08/17/69R4ObX3kUctNvM.png');
	const Toutiao = new SiteInfo('今日头条', 'https://www.toutiao.com/search/?keyword=%s', 'https://www.toutiao.com/', 'https://lf3-search.searchpstatp.com/obj/card-system/favicon_5995b44.ico');

	//=========================定义网站数据=======================================

	const defaultConf = {
		//
		// 基本配置
		//
		showToolbar: true,              // 显示划词工具条
		showFrequentEngines: true,      // 显示常用搜索引擎
		showClassifiedEngines: true,    // 显示分类搜索引擎
		showPlaceholder: true,          // 显示使用方式提示信息(如搜索框placeholder)
		enableOnInput: true,            // 是否在input/textarea上启用划词和快捷键
		autoCopyToClipboard: false,     // 划词时自动复制到剪贴板(内容为文本格式)
		//
		// 搜索建议配置
		//
		// 可选值baidu|google, 可根据需要调整顺序
		engineSuggestions: [
			{
				name: 'google',
				showCount: 5,
				enable: false
			},
			{
				name: 'baidu',
				showCount: 5,
				enable: false
			},
		],
		//
		// 搜索框默认搜索引擎
		// 属性:
		//   - name 搜索引擎名称
		//   - url 搜索引擎搜索url
		//   - home 搜索引擎主页url
		//
		defaultEngine: {
			name: Bing.name,
			url: Bing.url,
			home: Bing.home,
		},
		//
		// 绑定快捷键的搜索引擎列表
		// 属性:
		//   - name 搜索引擎名称
		//   - url 搜索引擎搜索url
		//   - home 搜索引擎主页url
		//   - hotkeys 快捷键列表, 仅支持配置单字符按键的code值, 实际起作用的是Alt+单字符键, S/D/F/L键已被脚本征用
		//   - enable 是否启用
		//
		hotkeyEngines: [
			{
				name: '百度百科',
				url: Baidubaike.url,
				home: Baidubaike.home,
				hotkeys: ['KeyW'],
				enable: false,
			},
			{
				name: '百度翻译',
				url: Baidufanyi.url,
				home: Baidufanyi.home,
				hotkeys: ['KeyE'],
				enable: false,
			},
			{
				name: '百度',
				url: Baidu.url,
				home: Baidu.home,
				hotkeys: ['KeyB'],
				enable: false,
			},
			{
				name: 'Google',
				url: Google.url,
				home: Google.home,
				hotkeys: ['KeyG'],
				enable: false,
			},
		],
		//
		// 常用搜索引擎列表
		// 属性:
		//   - name 搜索引擎名称
		//   - url 搜索引擎搜索url
		//   - home 搜索引擎主页url
		//   - icon 搜索引擎图标, base64编码
		//   - enable 是否启用
		//
		frequentEngines: [
			Baidu.callSiteInformation(),
			Google.callSiteInformation(),
			Bing.callSiteInformation(),
			Baidufanyi.callSiteInformation(),
			GitHub.callSiteInformation(false),
			Zhihu.callSiteInformation(),
			Googlenews.callSiteInformation(false),
			Bilibili.callSiteInformation(),
			Taobao.callSiteInformation(false),
			Jingdong.callSiteInformation(),
			Tianmao.callSiteInformation(false),
			Baiduwangpan.callSiteInformation(false),
			Maimai.callSiteInformation(false),
		],
		//
		// 分类搜索引擎列表, 二维数组, 默认认为该配置包含了所有已配置搜索引擎
		// 一级分类属性:
		//   - name 分类名称
		//   - enable 该分类是否启用
		//   - engines 该分类下的搜索引擎列表
		// 二级搜索引擎属性:
		//   - name 搜索引擎名称
		//   - url 搜索引擎搜索url
		//   - home 搜索引擎主页url
		//   - icon 搜索引擎图标, base64编码
		//   - enable 搜索引擎是否启用
		//
		classifiedEngines: [
			{
				name: '搜索引擎',
				enable: true,
				engines: [
					Baidu.callSiteInformation(),
					Google.callSiteInformation(),
					Bing.callSiteInformation(),
					Sougou.callSiteInformation(),
					So360.callSiteInformation()
				]
			},
			{
				name: '知识',
				enable: true,
				engines: [
					Zhihu.callSiteInformation(),
					StackOverflow.callSiteInformation(),
					Maimai.callSiteInformation(),
					Quora.callSiteInformation(false),
					Baiduzhidao.callSiteInformation(),
					Wikipedia.callSiteInformation(),
					Baidubaike.callSiteInformation(),
					Moegirl.callSiteInformation(false),
					Docin.callSiteInformation(),
					DoubanBook.callSiteInformation(),
					WeixinSogou.callSiteInformation(),
					Guokr.callSiteInformation(false)
				]
			},
			{
				name: '开发',
				enable: true,
				engines: [
					StackOverflow.callSiteInformation(),
					ApacheIssues.callSiteInformation(),
					GitHub.callSiteInformation(),
					Maven.callSiteInformation()
				]
			},
			{
				name: '翻译',
				enable: true,
				engines: [
					Baidufanyi.callSiteInformation(),
					Googlefanyi.callSiteInformation(),
					Youdao.callSiteInformation(),
					Bingfanyi.callSiteInformation(),
					Haici.callSiteInformation(),
					CNKIfanyi.callSiteInformation(false),
					Zdic.callSiteInformation(false),
					Deepl.callSiteInformation()
				]
			},
			{
				name: '地图',
				enable: true,
				engines: [
					Baidumap.callSiteInformation(),
					GaodeMap.callSiteInformation(),
					Googlemap.callSiteInformation(),
					Googleearth.callSiteInformation()
				]
			},
			{
				name: '图片',
				enable: true,
				engines: [
					Baidutupian.callSiteInformation(),
					SogouPic.callSiteInformation(),
					Googlepic.callSiteInformation(),
					BingPic.callSiteInformation(),
					Pixiv.callSiteInformation(),
					Flickr.callSiteInformation(),
					Huaban.callSiteInformation()
				]
			},
			{
				name: '音乐',
				enable: true,
				engines: [
					NeteaseMusic.callSiteInformation(),
					QQMusic.callSiteInformation(),
					KuwoMusic.callSiteInformation(),
					Kugou5sing.callSiteInformation()
				]
			},
			{
				name: '购物',
				enable: true,
				engines: [
					Taobao.callSiteInformation(),
					Jingdong.callSiteInformation(),
					Tianmao.callSiteInformation(),
					Dangdang.callSiteInformation(false),
					Suning.callSiteInformation(false),
					Amazon.callSiteInformation(false)
				]
			},
			{
				name: '学术',
				enable: true,
				engines: [
					Googlexueshu.callSiteInformation(),
					Baiduxueshu.callSiteInformation(),
					CNKI.callSiteInformation(),
					Wanfang.callSiteInformation(),
					WOS.callSiteInformation(),
					Springer.callSiteInformation(),
					Letpub.callSiteInformation(),
					Ablesci.callSiteInformation()
				]
			},
			{
				name: '社交',
				enable: true,
				engines: [
					Weibo.callSiteInformation(),
					Baidutieba.callSiteInformation(),
					Zhihu.callSiteInformation(),
					Douban.callSiteInformation(),
					Twitter.callSiteInformation(false),
					Facebook.callSiteInformation(false)
				]
			},
			{
				name: '新闻',
				enable: false,
				engines: [
					Googlenews.callSiteInformation(),
					Baiduxinwen.callSiteInformation(),
					Toutiao.callSiteInformation(),
					Weibo.callSiteInformation(),
					Zhihu.callSiteInformation()
				]
			}
		],
	};
    ///////////////////////////////////////////////////////////////////
    // css样式
    ///////////////////////////////////////////////////////////////////
	const sheet = `
	/* —————— 主窗口整体美化(横向分组) —————— */

	.qs-main-background-layer {
		all: initial !important;
		position: fixed !important;
		inset: 0 !important;
		background: rgba(0,0,0,0.35) !important;
		backdrop-filter: blur(6px) !important;
		z-index: 20000 !important;
	}

	.qs-mainbox {
		all: initial !important;
		position: fixed !important;
		top: 50% !important;
		left: 50% !important;
		transform: translate(-50%, -50%) !important;
		width: 90vw !important;
		max-width: 1100px !important;
		max-height: 80vh !important;
		background: #ffffff !important;
		border-radius: 16px !important;
		box-shadow: 0 12px 32px rgba(0,0,0,0.2) !important;
		padding: 30px 40px 25px !important;
		overflow-y: auto !important;
		z-index: 30000 !important;
		font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif !important;
	}

	/* 搜索框 */
	.qs-main-search-input {
		all: initial !important;
		width: 100% !important;
		height: 48px !important;
		font-size: 18px !important;
		padding: 0 18px !important;
		border: 2px solid #e5e7eb !important;
		border-radius: 12px !important;
		outline: none !important;
		transition: border-color 0.2s !important;
	}
	.qs-main-search-input:focus {
		border-color: #3b82f6 !important;
	}

	/* 常用引擎图标 */
	.qs-main-frequent-box {
		margin: 25px 0 15px !important;
		text-align: center !important;
	}
	.qs-main-frequent-icon {
		width: 36px !important;
		height: 36px !important;
		margin: 0 8px !important;
		border-radius: 8px !important;
		cursor: pointer !important;
		transition: transform 0.15s !important;
	}
	.qs-main-frequent-icon:hover {
		transform: scale(1.1) !important;
	}

	/* ——— 横向分组核心 ——— */
	.qs-main-classified-box {
		display: flex !important;
		flex-wrap: wrap !important;
		gap: 30px !important;
		justify-content: center !important;
		margin-top: 25px !important;
	}
	.qs-main-classified-family-box {
		flex: 0 1 auto !important;
		min-width: 120px !important;
		max-width: 200px !important;
	}
	.qs-main-classified-family-title {
		font-size: 20px !important;
		font-weight: 600 !important;
		color: #374151 !important;
		margin-bottom: 8px !important;
		text-align: center !important;
	}
	.qs-main-classified-family-engine {
		display: flex !important;
		align-items: center !important;
		margin: 6px 0 !important;
		cursor: pointer !important;
		padding: 4px 6px !important;
		border-radius: 8px !important;
		transition: background 0.15s !important;
	}
	.qs-main-classified-family-engine:hover {
		background: #f3f4f6 !important;
	}
	.qs-main-classified-family-engine-icon {
		width: 20px !important;
		height: 20px !important;
		margin-right: 8px !important;
	}
	.qs-main-classified-family-engine-name {
		font-size: 15px !important;
		color: #4b5563 !important;
	}

	/* 底部菜单大字体 */
	.qs-main-help-info-box {
		margin-top: 30px !important;
		text-align: center !important;
		font-size: 16px !important;
		font-weight: 500 !important;
	}
	.qs-main-help-info-item {
		color: #3b82f6 !important;
		margin: 0 12px !important;
		cursor: pointer !important;
		text-decoration: none !important;
		transition: color 0.2s !important;
	}
	.qs-main-help-info-item:hover {
		color: #1d4ed8 !important;
		text-decoration: underline !important;
	}

	/* —— 其余原有样式(工具条/设置/提示层等)兼容 —— */
	.qs-toolbar {
		all: initial !important;
		position: absolute !important;
		display: block !important;
		height: 26px !important;
		padding: 2px !important;
		white-space: nowrap !important;
		border: 1px solid #F5F5F5 !important;
		box-shadow: 0px 0px 2px #BBB !important;
		background-color: #FFF !important;
		z-index: 10000 !important;
	}
	.qs-toolbar-icon {
		all: initial !important;
		display: inline-block !important;
		margin: 0px !important;
		padding: 2px !important;
		width: 20px !important;
		height: 20px !important;
		border: 1px solid #FFF !important;
		cursor: pointer !important;
	}
	.qs-toolbar-icon:hover {
		border: 1px solid #CCC !important;
	}
	/* 在原有样式基础上添加以下设置窗口样式 */
	.qs-setting-box {
		all: initial !important;
		position: fixed !important;
		top: 50% !important;
		left: 50% !important;
		transform: translate(-50%, -50%) !important;
		width: 80vw !important;
		max-width: 800px !important;
		max-height: 80vh !important;
		background: #ffffff !important;
		border-radius: 16px !important;
		box-shadow: 0 12px 32px rgba(0,0,0,0.2) !important;
		padding: 30px 40px 25px !important;
		overflow-y: auto !important;
		z-index: 40000 !important; /* 确保在遮罩层上方 */
		font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif !important;
	}

	/* 设置窗口内的标题样式 */
	.qs-setting-box h3 {
		font-size: 24px !important;
		color: #374151 !important;
		margin: 0 0 20px !important;
		padding: 0 !important;
	}

	/* 设置窗口内的文本区域样式 */
	.qs-setting-box textarea {
		width: 100% !important;
		min-height: 300px !important;
		font-size: 14px !important; /* 增大字体 */
		padding: 15px !important;
		border: 2px solid #e5e7eb !important;
		border-radius: 12px !important;
		margin-bottom: 20px !important;
		resize: vertical !important;
	}

	/* 设置窗口内的按钮样式 */
	.qs-setting-box button {
		font-size: 14px !important;
		padding: 10px 15px !important;
		margin-right: 10px !important;
		border-radius: 8px !important;
		cursor: pointer !important;
		border: none !important;
	}

	.qs-setting-box button:hover {
		opacity: 0.9 !important;
	}
	.qs-info-tips-layer,
	.qs-suggestions-layer {
		/* 保持原有定义即可 */
	}
	`;

    ///////////////////////////////////////////////////////////////////
    // 全局变量
    ///////////////////////////////////////////////////////////////////

    var conf = GM_getValue('qs-conf', defaultConf);

    var hotkey2Engine = {};             // 自定义快捷键搜索的hotkey到engine的映射表

    var qsPageLock = false;             // 是否在当前页面锁定快搜所有功能, 锁定之后仅响应解锁快捷键

    var qsToolbar = null;               // 快搜划词工具条
    var qsBackgroundLayer = null;       // 快搜主窗口背景层
    var qsMainBox = null;               // 快搜主窗口
    var qsSearchInput = null;           // 快搜主窗口搜索框
    var qsSettingBox = null;            // 快搜设置窗口
    var qsConfigTextarea = null;        // 快搜设置窗口配置框
    var qsInfoTipsLayer = null;         // 快搜信息提示浮层
    var qsSuggestionsLayer = null;      // 快搜搜索建议浮层
    var qsSuggestionItems = [];         // 快搜搜索建议所有item元素(不一定都显示)

    ///////////////////////////////////////////////////////////////////
    // 版本升级更新配置
    ///////////////////////////////////////////////////////////////////

    //
    // for 1.1 -> 1.2
    //
    if (!conf.engineSuggestions) {
        conf.engineSuggestions = defaultConf.engineSuggestions;
        GM_setValue('qs-conf', conf);
    }

    ///////////////////////////////////////////////////////////////////
    // 功能函数
    ///////////////////////////////////////////////////////////////////

    // 获取元素style属性, 包括css中的
    function getStyleByElement(e, styleProp) {
        if (window.getComputedStyle) {
            return document.defaultView.getComputedStyle(e, null).getPropertyValue(styleProp);
        } else if (e.currentStyle) {
            return e.currentStyle[styleProp];
        }
    }

    // 计算元素在文档(页面)中的绝对位置
    function getElementPosition(e) {
        return {
            top: e.getBoundingClientRect().top + window.scrollY,        // 元素顶部相对于文档顶部距离
            bottom: e.getBoundingClientRect().bottom + window.scrollY,  // 元素底部相对于文档顶部距离
            left: e.getBoundingClientRect().left + window.scrollX,      // 元素左边相对于文档左侧距离
            right: e.getBoundingClientRect().right + window.scrollX     // 元素右边相对于文档左侧距离
        };
    }

    // 获取可视窗口在文档(页面)中的绝对位置
    function getWindowPosition() {
        return {
            top: window.scrollY,
            bottom: window.scrollY + window.innerHeight,
            left: window.scrollX,
            right: window.scrollX + window.innerWidth
        };
    }

    // 判断元素在文档(页面)中是否可见
    function isVisualOnPage(ele) {
        if (getStyleByElement(ele, 'display') == 'none'
            || getStyleByElement(ele, 'visibility') == 'hidden'
            || getStyleByElement(ele, 'opacity') == '0') {
            return false;
        }
        if (getStyleByElement(ele, 'position') != 'fixed'
            && ele.offsetParent == null) {
            return false;
        }
        var elePos = getElementPosition(ele);
        if (elePos.bottom - elePos.top == 0 || elePos.right - elePos.left == 0
            || elePos.bottom <= 0 || elePos.right <= 0) {
            return false;
        }
        return true;
    }

    // 获取选中文本
    function getSelection() {
        return window.getSelection().toString().trim();
    }

    // 获取当前页面匹配的 搜索引擎 及 其在同类别的搜索引擎列表中的索引 及 同类别的搜索引擎列表.
    //
    // TODO 目前只是简单地匹配域名, 待完善.
    function getMatchedEngineInfo() {
        var hostname = window.location.hostname;
        hostname = hostname.replace(/^(www\.)/, '');

        // 因为想要在循环中返回最终结果, 因此不能使用forEach语法
        for (var classEngines of conf.classifiedEngines) {
            for (var i = 0; i < classEngines.engines.length; i++) {
                var engine = classEngines.engines[i];
                var engineHostname = new URL(engine.url).hostname;
                engineHostname = engineHostname.replace(/^(www\.)/, '');
                if (hostname == engineHostname) {
                    return {
                        engine: engine,
                        index: i,
                        classEngines: classEngines
                    };
                }
            }
        }

        return null;
    }

    // 获取搜索引擎url中query的key
    function getUrlQueryKey(engine) {
        var params = new URL(engine.url).searchParams;
        for (var param of params) {
            if (param[1].includes('%s')) {
                return param[0];
            }
        }
        return null;
    }

    // 移除url中的domain(protocol+host)
    function removeUrlDomain(url) {
        var u = new URL(url);
        var domain = `${u.protocol}//${u.host}`;
        return url.substring(domain.length);
    }

    // 获取当前页面url中的搜索词.
    // 返回值为经过URI解码的明文文本.
    //
    // 如果当前页面在配置的搜索引擎列表中, 尝试从url中解析参数, 分为engine.url中含有问号(?)和不含问号(?)两种情况.
    // 如果没有解析到或者当前页面不在配置的搜索引擎列表中, 尝试获取文本(纯数字除外)在url中完整出现的input/textarea的值.
    // 如果还是没有, 则认为当前页面url中没有搜索词.
    function getUrlQuery() {

        var urlTail = removeUrlDomain(window.location.href);
        var engineInfo = getMatchedEngineInfo();
        var engine = engineInfo ? engineInfo.engine : null;

        // 尝试利用配置的搜索引擎信息从url中获取搜索词
        if (engine && engine.url.includes('%s')) {
            if (engine.url.includes('?')) {    // engine.url中含有问号(?)
                var queryKey = getUrlQueryKey(engine);
                var params = new URLSearchParams(window.location.search);
                var query = params.get(queryKey);
                if (query) {
                    console.log(`Quick Search: get query by URL-KV, engine is ${engine.url}`);
                    return query;   // URLSearchParams已经decode过了
                }
            } else {    // engine.url中没有问号(?)
                var parts = removeUrlDomain(engine.url).split('%s');
                if (parts.length == 2 && urlTail.startsWith(parts[0]) && urlTail.endsWith(parts[1])) {
                    var query = urlTail.substring(parts[0].length, urlTail.length - parts[1].length);
                    var index = query.search(/[\/\?\=\&\#]/);   // 是否含有 / ? = & #
                    if (index != -1) {
                        query = query.substring(0, index);
                    }
                    if (query) {
                        console.log(`Quick Search: get query by URL-PART, engine is ${engine.url}`);
                        return decodeURIComponent(query);
                    }
                }
            }
        }

        // 尝试获取文本(纯数字除外)在url中完整出现的input/textarea的值
        var eles = document.querySelectorAll('input, textarea');
        for (var ele of eles) {
            if (isVisualOnPage(ele) && !qsMainBox.contains(ele) && !qsSettingBox.contains(ele)) {
                var eleValue = ele.value.trim();
                if (eleValue && !/^\d+$/.test(eleValue)) {
                    var encodedEleValue = encodeURIComponent(eleValue);
                    var index = urlTail.indexOf(encodedEleValue);
                    if (index != -1) {
                        var leftChar = urlTail[index - 1];
                        var rightChar = urlTail[index + encodedEleValue.length];
                        if ((!leftChar || /[\/\=\#]/.test(leftChar))
                            && (!rightChar || /[\/\?\&\#]/.test(rightChar))) {
                            console.log(`Quick Search: get query by ${ele.tagName}[id='${ele.id}'], engine is ${engine ? engine.url : 'NULL'}`);
                            return eleValue;
                        }
                    }
                }
            }
        }

        console.log(`Quick Search: query is NULL, engine is ${engine ? engine.url : 'NULL'}`);
        return null;
    }

    // 判断是否允许工具条
    function isAllowToolbar(event) {
        var target = event.target;
        if (!conf.showToolbar || qsPageLock) {
            return false;
        }
        if (!conf.enableOnInput && (target.tagName == 'INPUT' || target.tagName == 'TEXTAREA')) {
            return false;
        }
        if (qsMainBox && qsMainBox.contains(target)
            || qsSettingBox && qsSettingBox.contains(target)) {
            return false;
        }
        return true;
    }

    // 判断是否允许响应当前按键
    // 默认只响应: 单字符的Escape / Alt+单字符 / Cmd/Ctrl+Alt+单字符
    function isAllowHotkey(event) {
        var target = event.target;
        if (!qsPageLock && event.code == 'Escape') {
            return true;
        }
        if (!event.altKey) {
            return false;
        }
        if (event.shiftKey) {
            return false;
        }
        if (qsPageLock && event.code != 'KeyL') {
            return false;
        }
        if ((target.tagName == 'INPUT' || target.tagName == 'TEXTAREA') && !conf.enableOnInput) {
            return false;
        }
        if (qsSettingBox && qsSettingBox.contains(target)) {
            return false;
        }
        return true;
    }

    // 获取搜索引擎主页url
    function getEngineHome(engine) {
        if (engine.home) {
            return engine.home;
        } else {
            var url = new URL(engine.url);
            return `${url.protocol}//${url.hostname}/`;
        }
    }

    // 获取直达的网址. 网址优先级: 搜索框已有网址(若快搜主窗口可见) > 网页中选中网址
    // 返回 网址 及 网址来源
    function getUrl() {
        var url, source;

        if (isMainBoxVisual()) {
            url = qsSearchInput.value.trim();
            source = 'mainbox';
        } else {
            url = getSelection();
            source = 'selection';
        }

        // 补全网址
        if (url && !url.includes('://')) {
            var dotCount = (url.match(/\./g) || []).length;
            if (dotCount == 0) {
                url = 'www.' + url + '.com';
            } else if (dotCount == 1) {
                url = 'www.' + url;
            }
            url = 'http://' + url;
        }

        if (!url) {
            source = null;
        }
        return {
            url: url,
            source: source
        };
    }

    // 获取搜索词. 文本优先级: 搜索框已有文本(若快搜主窗口可见) > 网页中选中文本 > 当前页面搜索词
    // 返回 搜索词 及 搜索词来源
    function getQuery() {
        var query, source;

        if (isMainBoxVisual()) {
            query = qsSearchInput.value.trim();
            source = 'mainbox';
        } else {
            query = getSelection();
            source = 'selection';
            if (!query) {
                query = getUrlQuery();
                source = 'url';
            }
        }

        if (!query) {
            source = null;
        }
        return {
            query: query,
            source: source
        };
    }

    // 打开url.
    // 当按下Cmd(Mac系统)/Ctrl(Windows/Linux系统), 则后台打开url.
    function openUrl(url, event) {
        // console.log(`Quick Search: open url, url is ${url}`);
        if (!url) return;
        if (event.metaKey || event.ctrlKey) {
            GM_openInTab(url, true);
        } else {
            GM_openInTab(url, false);
        }
    }

    // 打开engine搜索结果或engine主页.
    function openEngine(engine, query, event) {
        // console.log(`Quick Search: open engine, engine is ${engine.url}, query is ${query}`);
        if (!engine) return;
        if (query) {
            var url = engine.url.replace('%s', encodeURIComponent(query));
            openUrl(url, event);
        } else {
            openUrl(getEngineHome(engine), event);
        }
    }

    // 快捷键搜索.
    // 同样, 当query为空时打开引擎主页, 否则正常搜索.
    function openEngineOnKey(engine, query, event) {
        openEngine(engine, query, event);
    }

    // 点击搜索引擎.
    // 当按下Alt, 则忽略查询词打开引擎主页, 否则正常搜索.
    function openEngineOnClick(engine, query, event) {
        if (event.altKey) {
            openEngine(engine, null, event);
        } else {
            openEngine(engine, query, event);
        }
    }

    // 点击划词工具条搜索引擎.
    function openEngineOnClickToolbar(engine, event) {
        var query = getSelection();
        openEngineOnClick(engine, query, event);
    }

    // 点击快搜主窗口搜索引擎.
    function openEngineOnClickMainBox(engine, event) {
        var query = qsSearchInput.value.trim();
        openEngineOnClick(engine, query, event);
    }

    // 切换快搜page lock状态
    function toggleQuickSearchPageLock() {
        qsPageLock = qsPageLock ? false : true;
        if (qsPageLock) {
            hideToolbar();
            hideMainBox();
            showInfoTipsLayer('已禁用(🔒)');
        } else {
            showInfoTipsLayer('已启用(🚀)');
        }
    }

    ///////////////////////////////////////////////////////////////////
    // 元素创建 与 元素事件响应
    ///////////////////////////////////////////////////////////////////

    // 加载css样式
    function loadSheet() {
        var css = document.createElement('style');
        css.type = 'text/css';
        css.id = 'qs-css';
        css.textContent = sheet;
        document.getElementsByTagName('head')[0].appendChild(css);
    }

    // 初始化热键映射表
    function initHotkeyEngineMapping() {
        conf.hotkeyEngines.forEach(engine => {
            if (!engine.enable) return; // 此处return搭配forEach, 请勿改为其他形式循环
            engine.hotkeys.forEach(key => {
                hotkey2Engine[key] = engine;
            });
        });
    }

    //
    // 划词工具条
    //

    // 创建划词工具条
    function createToolbar() {
        // 工具条container
        var toolbar = document.createElement('div');
        toolbar.id = 'qs-toolbar';
        toolbar.className = 'qs-toolbar';
        toolbar.style.setProperty('display', 'none', 'important');
        document.body.appendChild(toolbar);

        // 常用搜索引擎按钮
        conf.frequentEngines.forEach((engine, index) => {
            if (!engine.enable) return; // 此处return搭配forEach, 请勿改为其他形式循环
            var icon = document.createElement('img');
            icon.id = 'qs-toolbar-icon-' + index;
            icon.className = 'qs-toolbar-icon';
            icon.src = engine.icon;
            icon.addEventListener('click', function (e) {
                openEngineOnClickToolbar(engine, e);
            }, false);
            toolbar.appendChild(icon);
        });

        // 直达网址按钮
        var icon = document.createElement('img');
        icon.id = 'qs-toolbar-icon-url';
        icon.className = 'qs-toolbar-icon';
        icon.src = 'https://s2.loli.net/2025/08/20/UoYtpzmnkqrSKd6.png';
        icon.addEventListener('click', function (e) {
            openUrl(getUrl().url, e);
        }, false);
        toolbar.appendChild(icon);

        // 更多按钮
        var icon = document.createElement('img');
        icon.id = 'qs-toolbar-icon-more';
        icon.className = 'qs-toolbar-icon';
        icon.src = 'https://s2.loli.net/2025/08/20/r4QaEZVqn8O3lwX.png';
        icon.addEventListener('click', function (e) {
            showMainBox();
            hideToolbar();
        }, false);
        toolbar.appendChild(icon);

        qsToolbar = toolbar;
    }

    // 划词工具条是否处于显示状态
    function isToolbarVisual() {
        return qsToolbar && qsToolbar.style.display == 'block';
    }

    // 显示划词工具条
    function showToolbar(event) {

        ensureQuickSearchAlive();

        if (!qsToolbar || isToolbarVisual()) {
            return;
        }

        var toolbar = qsToolbar;

        toolbar.style.setProperty('top', '-10000px', 'important');
        toolbar.style.setProperty('left', '-10000px', 'important');
        toolbar.style.setProperty('display', 'block', 'important');

        var toolbarClientRect = toolbar.getBoundingClientRect();
        var toolbarWidth = toolbarClientRect.right - toolbarClientRect.left;
        var toolbarHeight = toolbarClientRect.bottom - toolbarClientRect.top;

        var toolbarNewTop = event.pageY + 15;
        var toolbarNewLeft = event.pageX - (toolbarWidth / 2);
        var windowPos = getWindowPosition();
        if (toolbarNewTop + toolbarHeight > windowPos.bottom) {
            toolbarNewTop = event.pageY - toolbarHeight - 15;
        }
        if (toolbarNewLeft < windowPos.left) {
            toolbarNewLeft = windowPos.left;
        } else if (toolbarNewLeft + toolbarWidth > windowPos.right) {
            toolbarNewLeft = windowPos.right - toolbarWidth;
        }

        toolbar.style.setProperty('top', toolbarNewTop + 'px', 'important');
        toolbar.style.setProperty('left', toolbarNewLeft + 'px', 'important');
    }

    // 隐藏划词工具条
    function hideToolbar() {
        if (qsToolbar) {
            qsToolbar.style.setProperty('display', 'none', 'important');
        }
    }

    //
    // 快搜主窗口
    //

    // 创建快搜主窗口
    function createMainBox() {
        // 快搜主窗口背景层
        //
        // 随快搜主窗口一同显示/隐藏, 铺满整个可视窗口. 其作用主要是:
        // 1. 想要实现点击快搜主窗口外面就隐藏快搜主窗口, 但如果点击target是页面中的cross-domain iframe的话,
        //    当前window就不能捕获到该iframe的click事件, 所以覆盖一层作为以便捕获点击事件.
        // 2. 也可以做背景虚化/遮罩效果.
        var backgroundLayer = document.createElement('div');
        backgroundLayer.id = 'qs-main-background-layer';
        backgroundLayer.className = 'qs-main-background-layer';
        backgroundLayer.addEventListener('click', hideMainBox, false); // ✅ 新增这行
		backgroundLayer.style.setProperty('display', 'none', 'important');
        document.body.appendChild(backgroundLayer);

        // 快搜主窗口container
        var mainBox = document.createElement('div');
        mainBox.id = 'qs-mainbox';
        mainBox.className = 'qs-mainbox';
        mainBox.style.setProperty('display', 'none', 'important');
        document.body.appendChild(mainBox);

        // 搜索框
        var searchBox = document.createElement('div');
        searchBox.id = 'qs-main-search-box';
        searchBox.className = 'qs-main-search-box';
        mainBox.appendChild(searchBox);
        var searchInput = document.createElement('input');
        searchInput.id = 'qs-main-search-input';
        searchInput.className = 'qs-main-search-input';
        searchInput.addEventListener('keydown', function (e) {
            if (e.code == 'Enter') {  // 回车键
                openEngineOnClickMainBox(conf.defaultEngine, e);
            }
        }, false);
        if (conf.showPlaceholder) {
            searchInput.placeholder = `回车以使用${conf.defaultEngine.name}搜索`;
        }
        searchBox.appendChild(searchInput);

        // 常用搜索引擎
        if (conf.showFrequentEngines) {
            var frequentBox = document.createElement('div');
            frequentBox.id = 'qs-main-frequent-box';
            frequentBox.className = 'qs-main-frequent-box';
            mainBox.appendChild(frequentBox);
            conf.frequentEngines.forEach((engine, index) => {
                if (!engine.enable) return; // 此处return搭配forEach, 请勿改为其他形式循环
                var icon = document.createElement('img');
                icon.id = 'qs-main-frequent-icon-' + index;
                icon.className = 'qs-main-frequent-icon';
                icon.src = engine.icon;
                icon.addEventListener('click', function (e) {
                    openEngineOnClickMainBox(engine, e);
                }, false);
                frequentBox.appendChild(icon);
            });
        }

        // 分类搜索引擎
        if (conf.showClassifiedEngines) {
            var classifiedBox = document.createElement('div');
            classifiedBox.id = 'qs-main-classified-box';
            classifiedBox.className = 'qs-main-classified-box';
            mainBox.appendChild(classifiedBox);
            conf.classifiedEngines.forEach((family, fIndex) => {
                if (!family.enable) return; // 此处return搭配forEach, 请勿改为其他形式循环
                // 一个分类一列
                var familyBox = document.createElement('div');
                familyBox.id = 'qs-main-classified-family-box-' + fIndex;
                familyBox.className = 'qs-main-classified-family-box';
                classifiedBox.appendChild(familyBox);
                // 分类标题
                var familyTitle = document.createElement('div');
                familyTitle.id = 'qs-main-classified-family-title-' + fIndex;
                familyTitle.className = 'qs-main-classified-family-title';
                familyTitle.textContent = family.name;
                familyBox.appendChild(familyTitle);
                family.engines.forEach((engine, eIndex) => {
                    if (!engine.enable) return; // 此处return搭配forEach, 请勿改为其他形式循环
                    // 搜索引擎
                    var engineBox = document.createElement('div');
                    engineBox.id = 'qs-main-classified-family-engine-' + fIndex + '-' + eIndex;
                    engineBox.className = 'qs-main-classified-family-engine';
                    engineBox.addEventListener('click', function (e) {
                        openEngineOnClickMainBox(engine, e);
                    }, false);
                    familyBox.appendChild(engineBox);
                    // 搜索引擎icon
                    var engineIcon = document.createElement('img');
                    engineIcon.id = 'qs-main-classified-family-engine-icon-' + fIndex + '-' + eIndex;
                    engineIcon.className = 'qs-main-classified-family-engine-icon';
                    engineIcon.src = engine.icon;
                    engineBox.appendChild(engineIcon);
                    // 搜索引擎name
                    var engineName = document.createElement('span');
                    engineName.id = 'qs-main-classified-family-engine-name-' + fIndex + '-' + eIndex;
                    engineName.className = 'qs-main-classified-family-engine-name';
                    engineName.textContent = engine.name;
                    engineBox.appendChild(engineName);
                });
            });
        }

        // 帮助信息
        var helpInfoBox = document.createElement('div');
        helpInfoBox.id = 'qs-main-help-info-box';
        helpInfoBox.className = 'qs-main-help-info-box';
        mainBox.appendChild(helpInfoBox);
        // 主页
        var homeLink = document.createElement('a');
        homeLink.id = 'qs-main-help-info-home';
        homeLink.className = 'qs-main-help-info-item';
        homeLink.textContent = '主页';
        homeLink.href = 'https://github.com/eyinwei/Quick_Search';
        homeLink.target = '_blank';
        helpInfoBox.appendChild(homeLink);

        // 设置
        var settingLink = document.createElement('a');
        settingLink.id = 'qs-main-help-info-setting';
        settingLink.className = 'qs-main-help-info-item';
        settingLink.textContent = '设置';
        settingLink.onclick = function (e) {
            showSettingBox();
        };
        helpInfoBox.appendChild(settingLink);
		// ✅ 新增:工具条设置按钮
		var toolbarSettingLink = document.createElement('a');
		toolbarSettingLink.id = 'qs-main-help-info-toolbar-setting';
		toolbarSettingLink.className = 'qs-main-help-info-item';
		toolbarSettingLink.textContent = '工具条设置';
		toolbarSettingLink.onclick = function (e) {
			showToolbarEditor();
		};
		helpInfoBox.appendChild(toolbarSettingLink);
		

        qsBackgroundLayer = backgroundLayer;
        qsMainBox = mainBox;
        qsSearchInput = searchInput;
    }

    // 快搜主窗口是否处于显示状态
    function isMainBoxVisual() {
        return qsMainBox.style.display == 'block';
    }

    // 显示快搜主窗口.
    // 可选输入text为没有经过URI编码的明文文本, 该参数一般在iframe发来消息时使用.
    // 快搜主窗口中搜索框的文本优先级: 参数指定文本 > 网页选中文本 > 搜索框已有文本 > 当前页面搜索词
    function showMainBox(text) {

        ensureQuickSearchAlive();

        // 快搜主窗口在iframe中不显示
        if (isMainBoxVisual() || window.self != window.top) {
            return;
        }

        // 设置搜索框内容
        var query = text;
        if (!query) {
            query = getSelection();
        }
        if (!query) {
            query = qsSearchInput.value.trim();
        }
        if (!query) {
            query = getUrlQuery();
        }
        qsSearchInput.value = query;

        qsBackgroundLayer.style.setProperty('display', 'block', 'important');
        qsMainBox.style.setProperty('display', 'block', 'important');

        // 选中搜索框文本
        qsSearchInput.select();

        // 隐藏划词工具条
        if (isToolbarVisual()) {
            hideToolbar();
        }
    }

    // 隐藏快搜主窗口
    function hideMainBox() {
        destroySuggestions();
        qsBackgroundLayer.style.setProperty('display', 'none', 'important');
        qsMainBox.style.setProperty('display', 'none', 'important');
    }

    //
    // 设置窗口
    //

    // 创建设置窗口
    function createSettingBox() {
        // 设置窗口container
        var settingBox = document.createElement('div');
        settingBox.id = 'qs-setting-box';
        settingBox.className = 'qs-setting-box';
        settingBox.style.setProperty('display', 'none', 'important');
        document.body.appendChild(settingBox);
        // 配置文本框
        var configTextarea = document.createElement('textarea');
        configTextarea.id = 'qs-setting-config-textarea';
        configTextarea.className = 'qs-setting-config-textarea';
        settingBox.appendChild(configTextarea);
        // 底部按钮container
        var buttonBar = document.createElement('div');
        buttonBar.id = 'qs-setting-button-bar';
        buttonBar.className = 'qs-setting-button-bar';
        settingBox.appendChild(buttonBar);
        // 重置按钮
        var resetButton = document.createElement('button');
        resetButton.id = 'qs-setting-button-reset';
        resetButton.className = 'qs-setting-button';
        resetButton.type = 'button';
        resetButton.textContent = '重置';
        resetButton.onclick = function (e) {
            configTextarea.value = JSON.stringify(defaultConf, null, 4);
        }
        buttonBar.appendChild(resetButton);
        // 关闭按钮
        var closeButton = document.createElement('button');
        closeButton.id = 'qs-setting-button-close';
        closeButton.className = 'qs-setting-button';
        closeButton.type = 'button';
        closeButton.textContent = '取消';
        closeButton.onclick = function (e) {
            hideSettingBox();
        }
        buttonBar.appendChild(closeButton);
        // 保存按钮
        var saveButton = document.createElement('button');
        saveButton.id = 'qs-setting-button-save';
        saveButton.className = 'qs-setting-button';
        saveButton.type = 'button';
        saveButton.textContent = '保存';
        saveButton.onclick = function (e) {
            var newConf = JSON.parse(configTextarea.value);
            GM_setValue('qs-conf', newConf);
            hideSettingBox();
            // 需用户手动刷新页面重新加载配置使其生效
            alert('设置已保存, 刷新页面后生效.');
        }
        buttonBar.appendChild(saveButton);

        qsSettingBox = settingBox;
        qsConfigTextarea = configTextarea;
    }

    // 设置窗口是否处于显示状态
    function isSettingBoxVisual() {
        return qsSettingBox.style.display == 'block';
    }

    // 显示设置窗口
    function showSettingBox() {

        ensureQuickSearchAlive();

        if (isSettingBoxVisual()) {
            return;
        }

        var confStr = JSON.stringify(conf, null, 4);
        qsConfigTextarea.value = confStr;
        qsSettingBox.style.setProperty('display', 'block', 'important');
    }

    // 隐藏设置窗口
    function hideSettingBox() {
        qsSettingBox.style.setProperty('display', 'none', 'important');
    }

    //
    // 信息提示浮层
    //

    // 创建信息提示浮层
    function createInfoTipsLayer() {
        var infoTipsLayer = document.createElement('div');
        infoTipsLayer.id = 'qs-info-tips-layer';
        infoTipsLayer.className = 'qs-info-tips-layer';
        infoTipsLayer.style.setProperty('display', 'none', 'important');
        document.body.appendChild(infoTipsLayer);

        qsInfoTipsLayer = infoTipsLayer;
    }

    // 显示信息提示浮层
    var idOfSettimeout = null;
    function showInfoTipsLayer(info) {
        qsInfoTipsLayer.textContent = 'Quick Search: ' + info;
        qsInfoTipsLayer.style.setProperty('display', 'block', 'important');
        if (idOfSettimeout) {
            clearTimeout(idOfSettimeout);
        }
        idOfSettimeout = setTimeout(function () {
            qsInfoTipsLayer.style.setProperty('display', 'none', 'important');
        }, 2000);
    }

    //
    // 搜索建议
    //

    var rawInputQuery = null;           // 输入框中的原始查询
    var multiEngineSuggestions = [];    // 多个搜索引擎的建议, 每个一个子数组
    var suggestionCount = 0;            // 多个搜索引擎的实际建议的总数
    var selectedSuggestionIndex = -1;   // 用户选中的搜索建议项对应的index

    // 搜索建议最大条数, 用于事先创建好相应元素
    function getMaxSuggestionCount() {
        var count = 0;
        conf.engineSuggestions.forEach(es => {
            if (!es.enable) return;
            count += es.showCount;
        });
        return count;
    }

    // 创建搜索建议浮层
    function createSuggestionsLayer() {

        var maxSuggestionCount = getMaxSuggestionCount();
        if (maxSuggestionCount == 0) {
            return;
        }

        // 搜索建议浮层
        var suggestionsLayer = document.createElement('div');
        suggestionsLayer.id = 'qs-suggestions-layer';
        suggestionsLayer.className = 'qs-suggestions-layer';
        suggestionsLayer.style.setProperty('display', 'none', 'important');
        document.body.appendChild(suggestionsLayer);

        // 搜索建议条目
        for (var i = 0; i < maxSuggestionCount; i++) {
            var suggestionItem = document.createElement('div');
            suggestionItem.id = 'qs-suggestion-item-' + i;
            suggestionItem.className = 'qs-suggestion-item';
            suggestionItem.addEventListener('click', function (e) {
                qsSearchInput.value = this.textContent;
                openEngineOnClickMainBox(conf.defaultEngine, e);
            }, true);
            suggestionsLayer.appendChild(suggestionItem);

            qsSuggestionItems.push(suggestionItem);
        }

        // 向搜索框添加响应函数
        qsSearchInput.addEventListener('input', function (e) {
            var query = qsSearchInput.value.trim();
            if (query) {
                triggerSuggestions(query);
            } else {
                destroySuggestions();
            }
        }, true);
        qsSearchInput.addEventListener('mousedown', function (e) {
            var query = qsSearchInput.value.trim();
            if (query) {
                triggerSuggestions(query);
            }
        }, true);
        qsSearchInput.addEventListener('keydown', function (e) {
            if (e.code == 'ArrowDown' && isSuggestionsLayerVisual()) {
                e.preventDefault();
                selectSuggestionItem(selectedSuggestionIndex + 1);
                return;
            }
            if (e.code == 'ArrowUp' && isSuggestionsLayerVisual()) {
                e.preventDefault();
                selectSuggestionItem(selectedSuggestionIndex - 1);
                return;
            }
            if (e.code == 'Tab' && isSuggestionsLayerVisual()) {
                e.preventDefault();
                if (e.shiftKey) {
                    selectSuggestionItem(selectedSuggestionIndex - 1);
                } else {
                    selectSuggestionItem(selectedSuggestionIndex + 1);
                }
                return;
            }
        }, true);

        qsSuggestionsLayer = suggestionsLayer;
    }

    // 判断搜索建议浮层是否显示
    function isSuggestionsLayerVisual() {
        return qsSuggestionsLayer && qsSuggestionsLayer.style.display == 'block';
    }

    // 显示搜索建议浮层
    function _showSuggestionsLayer() {
        if (!qsSuggestionsLayer || isSuggestionsLayerVisual()) {
            return;
        }

        var inputPos = qsSearchInput.getBoundingClientRect();
        var top = inputPos.bottom + 'px';
        var left = inputPos.left + 'px';
        var width = (inputPos.right - inputPos.left) + 'px';
        qsSuggestionsLayer.style.setProperty('top', top, 'important');
        qsSuggestionsLayer.style.setProperty('left', left, 'important');
        qsSuggestionsLayer.style.setProperty('width', width, 'important');

        qsSuggestionsLayer.style.setProperty('display', 'block', 'important');
    }

    // 隐藏搜索建议浮层
    function _hideSuggestionsLayer() {
        if (qsSuggestionsLayer) {
            qsSuggestionsLayer.style.setProperty('display', 'none', 'important');
        }
    }

    //
    // 请求百度搜索建议
    //
    const baiduSuggestionUrl = {
        'http:': 'http://suggestion.baidu.com/su?wd=%s&cb=callback',
        'https:': 'https://suggestion.baidu.com/su?wd=%s&cb=callback',
    }[window.location.protocol];

    // 油猴脚本的GM_xmlhttpRequest可以直接跨域请求, 不受同源策略限制, 这样就不用通过jQuery来发送jsonp请求了.
    function requestBaiduSuggestions(query, index, count) {

        function callback(res) {
            multiEngineSuggestions[index] = res.s.slice(0, count);
            loadSuggestions();
        }

        var url = baiduSuggestionUrl.replace('%s', encodeURIComponent(query));
        GM_xmlhttpRequest({
            method: 'GET',
            url: url,
            timeout: 3000,
            onload: response => {
                if (response.status == 200) {
                    eval(response.responseText);
                } else {
                    console.log(`Quick Search: Baidu Suggestions: code ${response.status}`);
                }
            }
        });
    }

    //
    // 请求Google搜索建议
    //
    const googleSuggestionUrl = {
        'http:': 'http://suggestqueries.google.com/complete/search?client=firefox&q=%s&jsonp=callback',
        'https:': 'https://suggestqueries.google.com/complete/search?client=firefox&q=%s&jsonp=callback',
    }[window.location.protocol];

    function requestGoogleSuggestions(query, index, count) {

        function callback(res) {
            multiEngineSuggestions[index] = res[1].slice(0, count);
            loadSuggestions();
        }

        var url = googleSuggestionUrl.replace('%s', encodeURIComponent(query));
        GM_xmlhttpRequest({
            method: 'GET',
            url: url,
            timeout: 3000,
            onload: response => {
                if (response.status == 200) {
                    eval(response.responseText);
                } else {
                    console.log(`Quick Search: Google Suggestions: code ${response.status}`);
                }
            }
        });
    }

    var suggestionHandlers = {
        'baidu': requestBaiduSuggestions,
        'google': requestGoogleSuggestions,
    }

    // 选中搜索建议项
    function selectSuggestionItem(newSelectedIndex) {
        if (qsSuggestionItems[selectedSuggestionIndex]) {
            qsSuggestionItems[selectedSuggestionIndex].className = 'qs-suggestion-item';
        }
        selectedSuggestionIndex = newSelectedIndex;
        selectedSuggestionIndex = selectedSuggestionIndex < -1 ? suggestionCount - 1 : selectedSuggestionIndex;
        selectedSuggestionIndex = selectedSuggestionIndex >= suggestionCount ? -1 : selectedSuggestionIndex;
        if (selectedSuggestionIndex == -1) {
            qsSearchInput.value = rawInputQuery;
        } else {
            qsSearchInput.value = qsSuggestionItems[selectedSuggestionIndex].textContent;
            qsSuggestionItems[selectedSuggestionIndex].className = 'qs-suggestion-item-selected';
        }
    }

    // 触发搜索建议
    function triggerSuggestions(query) {
        rawInputQuery = query;
        if (selectedSuggestionIndex != -1) {
            selectSuggestionItem(-1);
        }

        var index = 0;
        conf.engineSuggestions.forEach(es => {
            if (!es.enable) return;
            suggestionHandlers[es.name](query, index, es.showCount);
            index++;
        })
    }

    // 装载搜索建议
    function loadSuggestions() {
        // 由于装载是异步延迟的, 若用户已经删光了输入框内容, 则不显示搜索建议
        if (!qsSearchInput.value.trim()) {
            destroySuggestions();
            return;
        }

        // 多个搜索引擎的建议合并并去重
        var allSuggestions = multiEngineSuggestions.flat(1).filter((x, i, a) => a.indexOf(x) == i);
        suggestionCount = allSuggestions.length;

        allSuggestions.forEach((suggestion, i) => {
            qsSuggestionItems[i].textContent = suggestion;
            qsSuggestionItems[i].style.setProperty('display', 'block', 'important');
        });
        for (var i = allSuggestions.length; i < qsSuggestionItems.length; i++) {
            qsSuggestionItems[i].style.setProperty('display', 'none', 'important');
        }
        if (!isSuggestionsLayerVisual()) {
            _showSuggestionsLayer();
        }
    }

    // 销毁搜索建议
    function destroySuggestions() {
        _hideSuggestionsLayer();
        rawInputQuery = null;
        multiEngineSuggestions = [];
        suggestionCount = 0;
        selectedSuggestionIndex = -1;
    }

    //
    // 创建以上所有东东
    //

    function initQuickSearch() {
        loadSheet();
        initHotkeyEngineMapping();
        if (conf.showToolbar) {
            createToolbar();
        }
        createMainBox();
        createSettingBox();
        createInfoTipsLayer();
        createSuggestionsLayer();
    }

    // 百度等网页会在不刷新页面的情况下改变网页内容, 导致quick search除了js脚本之外的东东全部没了.
    // 此函数用于确保quick search处于可用状态, 需在toolbar或mainbox等窗口每次显示时调用.
    function ensureQuickSearchAlive() {
        var css = document.querySelector('#qs-css');
        var mainbox = document.querySelector('#qs-mainbox');
        if (!css || !mainbox) {
            initQuickSearch();
        }
    }

	// ✅ 工具条设置弹窗
	// ❗️ 仅定义逻辑,不立即创建 DOM
	function showToolbarEditor() {
		// 1. 先移除旧弹窗(如果已存在)
		const old = document.getElementById('qs-toolbar-editor');
		if (old) old.remove();

		// 2. 创建并插入 DOM
		const box = document.createElement('div');
		box.id = 'qs-toolbar-editor';
		box.className = 'qs-setting-box';
		box.style.cssText = `
			position: fixed !important;
			top: 50% !important;
			left: 50% !important;
			transform: translate(-50%, -50%) !important;
			width: 300px !important;
			background: #fff !important;
			border: 1px solid #ccc !important;
			box-shadow: 0 0 10px rgba(0,0,0,0.3) !important;
			padding: 15px !important;
			z-index: 99999 !important;
			font-family: Arial, sans-serif !important;
			font-size: 14px !important;
		`;
		document.body.appendChild(box);

		// 3. 标题
		const title = document.createElement('div');
		title.textContent = '编辑工具条网站';
		title.style.cssText = 'font-weight: bold; margin-bottom: 10px;';
		box.appendChild(title);

		// 4. 网站列表
		const list = document.createElement('div');
		box.appendChild(list);
		conf.frequentEngines.forEach((engine, i) => {
			const label = document.createElement('label');
			label.style.display = 'block';
			label.style.margin = '4px 0';

			const checkbox = document.createElement('input');
			checkbox.type = 'checkbox';
			checkbox.checked = engine.enable;
			checkbox.onchange = () => {
				conf.frequentEngines[i].enable = checkbox.checked;
			};

			label.appendChild(checkbox);
			label.appendChild(document.createTextNode(engine.name));
			list.appendChild(label);
		});

		// 5. 按钮区
		const addBtn = document.createElement('button');
		addBtn.textContent = '+ 添加网站';
		addBtn.onclick = () => {
			const name = prompt('网站名称:');
			const url = prompt('搜索URL(用 %s 代替关键词):');
			const icon = prompt('图标URL(如 favicon.ico):');
			if (name && url && icon) {
				conf.frequentEngines.push({ name, url, icon, home: '', enable: true });
				showToolbarEditor(); // 重新渲染
			}
		};
		box.appendChild(addBtn);

		const saveBtn = document.createElement('button');
		saveBtn.textContent = '保存';
		saveBtn.style.marginLeft = '10px';
		saveBtn.onclick = () => {
			GM_setValue('qs-conf', conf);
			if (qsToolbar) {
				qsToolbar.remove();
				createToolbar();
			}
			box.remove(); // 关闭弹窗
		};
		box.appendChild(saveBtn);

		const cancelBtn = document.createElement('button');
		cancelBtn.textContent = '取消';
		cancelBtn.style.marginLeft = '10px';
		cancelBtn.onclick = () => box.remove(); // 关闭弹窗
		box.appendChild(cancelBtn);
	}

	//GM_setValue('qs-conf', defaultConf); // 🔥 强制重置配置
    initQuickSearch();
	// ✅ 全局调用:仅由菜单按钮触发
	window.showToolbarEditor = showToolbarEditor;


    ///////////////////////////////////////////////////////////////////
    // 全局事件响应
    //
    // 我们将全局事件绑定在捕获阶段执行, 避免事件响应被网页自带的脚本拦截掉.
    ///////////////////////////////////////////////////////////////////

    //
    // top window和iframe共用的事件处理逻辑
    //

    window.addEventListener('mousedown', function (e) {
        var target = e.target;
        // 隐藏工具条
        if (isToolbarVisual() && !qsToolbar.contains(target)) {
            hideToolbar();
        }
    }, true);

    window.addEventListener('mouseup', function (e) {
        var target = e.target;
        // 显示/隐藏工具条
        if (isAllowToolbar(e)) {
            var selection = getSelection();
            if (selection && !isToolbarVisual()) {
                showToolbar(e);
            }
            if (!selection && isToolbarVisual()) {
                hideToolbar();
            }
        }

        // 划词时自动复制文本到剪贴板
        if (conf.autoCopyToClipboard
            && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA'
            && !qsMainBox.contains(target) && !qsSettingBox.contains(target)) {
            var selection = getSelection();
            if (selection) {
                GM_setClipboard(selection, 'text/plain');
            }
        }
    }, true);

    // 有时候selectionchange发生在mouseup之后, 导致没有selection时toolbar依然显示.
    // 故再添加selectionchange事件以隐藏toolbar.
    // 由于在鼠标划词拖动过程中会不停触发selectionchange事件, 所以最好不要以此事件来显示/调整toolbar位置.
    document.addEventListener('selectionchange', function (e) {
        var selection = getSelection();
        if (!selection && isToolbarVisual()) {
            hideToolbar();
        }
    }, true);

    window.addEventListener('keydown', function (e) {

        if (!isAllowHotkey(e)) {
            return;
        }

        // Alt+S键, 超级快搜. 优先级如下:
        // 1. 快搜主窗口可见, 使用默认搜索引擎搜索搜索框文本.
        // 2. 网页有选中文本, 使用默认搜索引擎搜索文本.
        // 3. 当前页面url中有搜索词, 挑选当前搜索引擎分类中的另一个搜索引擎搜索该词.
        // 4. 都没有则打开快搜主窗口.
        if (e.code == 'KeyS') {
            e.preventDefault();

            var engine = null;
            var query = getQuery();
            if (query.source == 'mainbox' || query.source == 'selection') {
                engine = conf.defaultEngine;
            } else if (query.source == 'url') {
                var nowEngineInfo = getMatchedEngineInfo();
                if (nowEngineInfo) {
                    var nowClassEngines = nowEngineInfo.classEngines.engines;
                    nowClassEngines.forEach((eng, i) => {
                        if (!engine && eng.enable && i != nowEngineInfo.index) {
                            engine = eng;
                        }
                    });
                }
            }

            if (engine && query.query) {
                openEngineOnKey(engine, query.query, e);
            } else if (!isMainBoxVisual()) {
                showMainBox();
            } else {
                hideMainBox();
            }

            return;
        }

        // Alt+D键, 网址直达. 网址优先级: 搜索框已有网址(若快搜主窗口可见) > 网页中选中网址
        if (e.code == 'KeyD') {
            e.preventDefault();
            openUrl(getUrl().url, e);
            return;
        }

        // Alt+自定义快捷键搜索. 文本优先级: 搜索框已有文本(若快搜主窗口可见) > 网页中选中文本 > 当前页面搜索词
        if (hotkey2Engine[e.code]) {
            e.preventDefault();
            var engine = hotkey2Engine[e.code];
            var query = getQuery();
            openEngineOnKey(engine, query.query, e);
            return;
        }
    }, true);

    //
    // 只在top window中使用的事件处理逻辑
    //

    if (window.self == window.top) {
        window.addEventListener('mousedown', function (e) {
            var target = e.target;
            // 隐藏快搜主窗口
            if (isMainBoxVisual()
                && !isSettingBoxVisual()
                && !qsMainBox.contains(target)
                && !qsSuggestionsLayer.contains(target)) {
                hideMainBox();
            }

            // 隐藏搜索建议浮层
            if (isSuggestionsLayerVisual()
                && !qsSuggestionsLayer.contains(target)
                && !qsSearchInput.contains(target)) {
                destroySuggestions();
            }
        }, true);

        window.addEventListener('keydown', function (e) {

            if (!isAllowHotkey(e)) {
                return;
            }

            // Alt+F键, 显示/隐藏快搜主窗口
            if (e.code == 'KeyF') {
                e.preventDefault();
                if (!isMainBoxVisual()) {
                    showMainBox();
                } else {
                    hideMainBox();
                }
                return;
            }

            // Esc键, 隐藏快搜主窗口
            if (e.code == 'Escape') {
                if (isMainBoxVisual() && !isSettingBoxVisual()) {
                    hideMainBox();
                }
                return;
            }

            // Alt+L键, 锁定/解锁快搜所有功能.
            if (e.code == 'KeyL') {
                e.preventDefault();
                toggleQuickSearchPageLock();
                return;
            }
        }, true);

        // 处理iframe发来的消息
        window.addEventListener('message', function (e) {
            if (e.data.source != 'qs-iframe') {
                return;
            }

            if (e.data.keydown) {
                if (e.data.keydown == 'Alt+KeyF') {
                    if (!qsPageLock) {
                        if (!isMainBoxVisual()) {
                            showMainBox(e.data.query);
                        } else {
                            hideMainBox();
                        }
                    }
                }
                if (e.data.keydown == 'Alt+KeyL') {
                    toggleQuickSearchPageLock();
                }
            }
        }, true);
    }

    //
    // 只在iframe中使用的事件处理逻辑
    //

    if (window.self != window.top) {
        // 向top窗口发送消息
        window.addEventListener('keydown', function (e) {
            if (e.altKey && e.code == 'KeyF') {
                var query = getSelection();
                // 跨域iframe中不能执行window.top.origin, 故此处使用*
                window.top.postMessage({
                    source: 'qs-iframe',
                    keydown: 'Alt+KeyF',
                    query: query
                }, '*');
            }
            if (e.altKey && e.code == 'KeyL') {
                window.top.postMessage({
                    source: 'qs-iframe',
                    keydown: 'Alt+KeyL'
                }, '*');
            }
        }, true);
    }
})();