Google 高级搜索助手

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

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

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