Google 高级搜索助手

在谷歌搜索页面顶部添加一个高级搜索表单

当前为 2024-08-05 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Advanced Search Assistant for Google
  3. // @name:zh-CN Google 高级搜索助手
  4. // @namespace https://github.com/ChinaGodMan/UserScripts
  5. // @version 0.1.9
  6. // @description Add an advanced search form to the top of the page
  7. // @description:zh-CN 在谷歌搜索页面顶部添加一个高级搜索表单
  8. // @author shiquda &人民的勤务员 <toniaiwanowskiskr47@gmail.com>
  9. // @namespace https://github.com/shiquda/shiquda_UserScript
  10. // @supportURL https://github.com/shiquda/shiquda_UserScript/issues
  11. // @match *://www.google.com/search*
  12. // @include *://*google*/search*
  13. // @grant GM_addStyle
  14. // @grant GM_setValue
  15. // @grant GM_getValue
  16. // @license MIT
  17.  
  18. // ==/UserScript==
  19.  
  20. (function () {
  21. "use strict"
  22. let isMobile = false
  23. if (
  24. navigator.userAgent.match(/Android/i) ||
  25. navigator.userAgent.match(/webOS/i) ||
  26. navigator.userAgent.match(/iPhone/i) ||
  27. navigator.userAgent.match(/iPad/i) ||
  28. navigator.userAgent.match(/iPod/i) ||
  29. navigator.userAgent.match(/BlackBerry/i) ||
  30. navigator.userAgent.match(/Windows Phone/i)
  31. ) {
  32. // On mobile device
  33. isMobile = true
  34. }
  35.  
  36. let isDarkMode = false
  37.  
  38. try {
  39. if (
  40. window.matchMedia &&
  41. window.matchMedia("(prefers-color-scheme: dark)").matches
  42. ) {
  43. // Dark mode is enabled
  44. isDarkMode = true
  45. console.log("Dark mode is enabled.")
  46. }
  47. } catch (error) {
  48. console.log("Failed to determine the color mode.", error)
  49. }
  50.  
  51. const userLanguage = "" // You can set your language config here manually. 'zh-CN' & 'en' are supported now.
  52.  
  53. const supportedLanguages = ["zh-CN", "en"]
  54.  
  55. const translation = {
  56. as_q: {
  57. "zh-CN": "搜索字词:",
  58. en: "Search word:",
  59. },
  60. as_epq: {
  61. "zh-CN": "与以下字词完全匹配:",
  62. en: "Match the following words exactly:",
  63. },
  64. as_oq: {
  65. "zh-CN": "包含以下任意字词:",
  66. en: "Contains any of the following words:",
  67. },
  68. as_eq: {
  69. "zh-CN": "排除以下字词:",
  70. en: "Exclude the following words:",
  71. },
  72. as_nlo: {
  73. "zh-CN": "包含的数字范围:从",
  74. en: "Number range: from",
  75. },
  76. as_nhi: {
  77. "zh-CN": "到:",
  78. en: "to:",
  79. },
  80. lr: {
  81. "zh-CN": "语言:",
  82. en: "Language:",
  83. },
  84. cr: {
  85. "zh-CN": "地区:",
  86. en: "Region:",
  87. },
  88. as_qdr: {
  89. "zh-CN": "最后更新时间:",
  90. en: "Last update time:",
  91. },
  92. as_sitesearch: {
  93. "zh-CN": "网站或域名:",
  94. en: "Website or domain:",
  95. },
  96. as_occt: {
  97. "zh-CN": "字词出现位置:",
  98. en: "Word position:",
  99. },
  100. as_filetype: {
  101. "zh-CN": "文件类型:",
  102. en: "File type:",
  103. },
  104. tbs: {
  105. "zh-CN": "使用权限:",
  106. en: "Usage rights:",
  107. },
  108. advancedSearch: {
  109. "zh-CN": "高级搜索",
  110. en: "Advanced Search",
  111. },
  112. search: {
  113. "zh-CN": "搜索",
  114. en: "Search",
  115. },
  116. clear: {
  117. "zh-CN": "清空",
  118. en: "Clear",
  119. },
  120. as_qdr_select: {
  121. "": {
  122. "zh-CN": "请选择",
  123. en: "Please select",
  124. },
  125. d: {
  126. "zh-CN": "一天内",
  127. en: "Past 24 hours",
  128. },
  129. w: {
  130. "zh-CN": "一周内",
  131. en: "Past week",
  132. },
  133. m: {
  134. "zh-CN": "一月内",
  135. en: "Past month",
  136. },
  137. y: {
  138. "zh-CN": "一年内",
  139. en: "Past year",
  140. },
  141. },
  142. as_occt_select: {
  143. "": {
  144. "zh-CN": "请选择",
  145. en: "Please select",
  146. },
  147. title: {
  148. "zh-CN": "网页标题中",
  149. en: "In the title of the web page",
  150. },
  151. body: {
  152. "zh-CN": "网页正文中",
  153. en: "In the body of the web page",
  154. },
  155. url: {
  156. "zh-CN": "网页网址中",
  157. en: "In the URL of the web page",
  158. },
  159. links: {
  160. "zh-CN": "指向网页的链接中",
  161. en: "In the links to the web page",
  162. },
  163. },
  164. }
  165. const style = `
  166. #advancedSearchToggleButton {
  167. margin-right: 10px;
  168. border: none;
  169. border-radius: 5px;
  170. background-color: #007bff;
  171. color: #fff;
  172. font-size: 14px;
  173. font-weight: bold;
  174. margin: 10px;
  175. }
  176.  
  177.  
  178. #advancedSearchFormContainer {
  179. position: fixed;
  180. ${isMobile ? "top: 150px;" : "top: 130px;"}
  181. ${isMobile ? "left: 15px;" : "left: 30px;"}
  182. display: none;
  183. padding: 10px;
  184. border: 1px solid #ccc;
  185. border-radius: 5px;
  186. font-size: 14px;
  187. font-weight: bold;
  188. ${isDarkMode
  189. ? "background-color: rgba(0, 0, 0, 1);"
  190. : "background-color: rgba(255, 255, 255, 1);"
  191. }
  192. ${isMobile ? "column-count: 2;" : ""} /* 在移动设备上分为两列 */
  193. z-index: 1000; // Make sure the button is on top of the search bar
  194. }
  195.  
  196.  
  197. #advancedSearchFormContainer label {
  198. display: block;
  199. margin-top: 5px;
  200. }
  201.  
  202.  
  203. #advancedSearchFormContainer input[type="text"] {
  204. margin-top: 5px;
  205. padding: 5px;
  206. border: 1px solid #ccc;
  207. border-radius: 5px;
  208. }
  209.  
  210. #advancedSearchFormContainer select {
  211. margin-top: 5px;
  212. padding: 5px;
  213. border-radius: 5px;
  214. }
  215.  
  216. #advancedSearchFormContainer button {
  217. border: none;
  218. border-radius: 5px;
  219. background-color: #007bff;
  220. color: #fff;
  221. font-size: 14px;
  222. font-weight: bold;
  223. margin: 20px;
  224. }
  225. `
  226. GM_addStyle(style)
  227.  
  228. let language = "en"
  229. if (userLanguage.length > 0) { // userLanguage is set manually
  230. if (supportedLanguages.includes(userLanguage)) {
  231. language = userLanguage
  232. } else {
  233. console.log(`Unsupported language: ${userLanguage}`)
  234. }
  235. } else {
  236. // Check if any of the user's preferred languages are supported
  237. language =
  238. navigator.languages
  239. .map((lang) => lang.split("-")[0]) // Consider only the language part, not the region
  240. .map((lang) => supportedLanguages.find((supportedLang) => supportedLang.split("-")[0] === lang)) // Match with the supported languages
  241. .filter(Boolean) // Remove undefined values
  242. .shift() // Take the first matched language
  243. || "en" // Default to 'en' if no match found
  244. console.log(`Here is the language: ${language}`)
  245. }
  246.  
  247. // Create user interface
  248. const toggleButton = document.createElement("button")
  249. toggleButton.className = "nfSF8e"
  250. toggleButton.textContent = translation["advancedSearch"][language]
  251. toggleButton.id = "advancedSearchToggleButton"
  252. if (isMobile) {
  253. document.querySelector(".Fh5muf").appendChild(toggleButton)
  254. } else {
  255. document.querySelector(".logo").appendChild(toggleButton)
  256. const searchContainer = document.querySelector(".RNNXgb")
  257. searchContainer.appendChild(toggleButton)
  258. }
  259.  
  260.  
  261.  
  262. // Add minimal style for positioning
  263. toggleButton.style.marginTop = "5px" // Add some space above the button
  264. toggleButton.style.marginLeft = "5px" // Add some space to the left of the button
  265. // Add any additional styles to match the search bar's height or other styling
  266.  
  267. const formContainer = document.createElement("div")
  268. formContainer.id = "advancedSearchFormContainer"
  269. document.body.appendChild(formContainer)
  270.  
  271. //
  272. const form = document.createElement("form")
  273. formContainer.appendChild(form)
  274.  
  275. const params = {
  276. as_q: translation["as_q"][language],
  277. as_epq: translation["as_epq"][language],
  278. as_oq: translation["as_oq"][language],
  279. as_eq: translation["as_eq"][language],
  280. as_nlo: translation["as_nlo"][language],
  281. as_nhi: translation["as_nhi"][language],
  282. // 'lr': translation['lr'][language],
  283. // 'cr': translation['cr'][language],
  284. as_qdr: {
  285. name: translation["as_qdr"][language],
  286. options: {
  287. "": translation["as_qdr_select"][""][language],
  288. d: translation["as_qdr_select"]["d"][language],
  289. w: translation["as_qdr_select"]["w"][language],
  290. m: translation["as_qdr_select"]["m"][language],
  291. y: translation["as_qdr_select"]["y"][language],
  292. },
  293. },
  294. as_sitesearch: translation["as_sitesearch"][language],
  295. as_occt: {
  296. name: translation["as_occt"][language],
  297. options: {
  298. "": translation["as_occt_select"][""][language],
  299. title: translation["as_occt_select"]["title"][language],
  300. body: translation["as_occt_select"]["body"][language],
  301. url: translation["as_occt_select"]["url"][language],
  302. links: translation["as_occt_select"]["links"][language],
  303. },
  304. },
  305. as_filetype: translation["as_filetype"][language],
  306. // 'tbs': translation['tbs'][language],
  307. }
  308.  
  309. for (const param in params) {
  310. if (typeof params[param] === "object") {
  311. const label = document.createElement("label")
  312. label.textContent = params[param].name
  313. const select = document.createElement("select")
  314. select.name = param
  315.  
  316. Object.keys(params[param]["options"]).forEach((option) => {
  317. const optionElement = document.createElement("option")
  318. optionElement.value = option
  319. optionElement.textContent = params[param]["options"][option]
  320. select.appendChild(optionElement)
  321. })
  322.  
  323. form.appendChild(label)
  324. form.appendChild(select)
  325. form.appendChild(document.createElement("br"))
  326. continue
  327. }
  328. const label = document.createElement("label")
  329. label.textContent = params[param]
  330. const input = document.createElement("input")
  331. input.name = param
  332. input.type = "text"
  333. form.appendChild(label)
  334. form.appendChild(input)
  335. form.appendChild(document.createElement("br"))
  336. }
  337.  
  338. const searchButton = document.createElement("button")
  339. searchButton.textContent = translation["search"][language]
  340. form.appendChild(searchButton)
  341.  
  342. // Add a clear button to reset the form
  343. const clearButton = document.createElement("button")
  344. clearButton.textContent = translation["clear"][language]
  345. clearButton.addEventListener("click", function (event) {
  346. event.preventDefault()
  347. form.reset()
  348. })
  349. form.appendChild(clearButton)
  350.  
  351. // Load saved data and fill the form when opening a new page
  352. window.addEventListener("load", function () {
  353. for (const param in params) {
  354. const savedValue = GM_getValue(param)
  355. if (savedValue) {
  356. form[param].value = savedValue
  357. }
  358. }
  359. })
  360.  
  361. // Save form data to Greasemonkey storage
  362. form.addEventListener("input", function () {
  363. for (const param in params) {
  364. GM_setValue(param, form[param].value)
  365. }
  366. })
  367.  
  368. // Toggle the form display
  369. toggleButton.addEventListener("click", function (event) {
  370. event.preventDefault()
  371. let status = formContainer.style.display
  372. status = status === "none" || status === "" ? "block" : "none"
  373. formContainer.style.display = status
  374. })
  375.  
  376. // Submit the form
  377. form.addEventListener("submit", function (event) {
  378. event.preventDefault()
  379. const searchParams = new URLSearchParams()
  380. for (const param in params) {
  381. const value = form[param].value
  382. if (value) {
  383. searchParams.set(param, value)
  384. }
  385. }
  386. const searchUrl =
  387. "https://www.google.com/search?" + searchParams.toString()
  388. window.location.href = searchUrl
  389. })
  390. })()