Google Draggable Search Tool with Copy Link

Create a stylish, draggable search form with persistent inputs, paste buttons, and a copy dynamic link button for Google search results. The form retains user-given values and is initially positioned on the right side but can be moved anywhere. Includes a copy dynamic link button that copies the first available link from specified classes. Now with smaller text size, larger checkboxes, and aligned buttons.

当前为 2024-12-22 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Google Draggable Search Tool with Copy Link
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.26
  5. // @description Create a stylish, draggable search form with persistent inputs, paste buttons, and a copy dynamic link button for Google search results. The form retains user-given values and is initially positioned on the right side but can be moved anywhere. Includes a copy dynamic link button that copies the first available link from specified classes. Now with smaller text size, larger checkboxes, and aligned buttons.
  6. // @author Mahmudul Hasan Shawon
  7. // @match https://www.google.com/search?*
  8. // @license MIT
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // Load Inter font
  16. const interFontLink = document.createElement('link');
  17. interFontLink.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap';
  18. interFontLink.rel = 'stylesheet';
  19. document.head.appendChild(interFontLink);
  20.  
  21. // Default values
  22. const defaultInput1Value = 'Company Name';
  23. const defaultInput2Value = 'City,State';
  24. const defaultInput3Value = 'Headquarter';
  25.  
  26. // Retrieve stored values or use default
  27. const storedInput1Value = localStorage.getItem('input1') || defaultInput1Value;
  28. const storedInput2Value = localStorage.getItem('input2') || defaultInput2Value;
  29. const storedInput3Value = localStorage.getItem('input3') || defaultInput3Value;
  30.  
  31. // Create a container for the form
  32. const formContainer = document.createElement('div');
  33. formContainer.style.position = 'fixed';
  34. formContainer.style.top = '10px';
  35. formContainer.style.right = '20px'; // Positioning to the right side
  36. formContainer.style.padding = '10px';
  37. //formContainer.style.backgroundColor = '#ffffff';
  38.  
  39. formContainer.style.backdropFilter = 'blur(10px)';
  40. formContainer.style.backgroundColor = 'rgba(255, 255, 255, 0.2)'; // Optional for semi-transparent background
  41.  
  42. formContainer.style.border = '1px solid #ddd';
  43. formContainer.style.borderRadius = '16px';
  44. formContainer.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.1)';
  45. formContainer.style.zIndex = '10000';
  46. formContainer.style.fontFamily = "'Inter', sans-serif";
  47. formContainer.style.cursor = 'move';
  48. formContainer.style.maxWidth = '300px'; // Adjusted width for right-side positioning
  49. formContainer.style.boxSizing = 'border-box';
  50.  
  51. // Create the form element
  52. const form = document.createElement('form');
  53. form.style.display = 'flex';
  54. form.style.flexDirection = 'column';
  55. form.style.gap = '8px'; // Adjusted gap
  56.  
  57. // Function to create input field with paste button and optional checkbox
  58. const createInputFieldWithExtras = (id, value, includeCheckbox = false) => {
  59. const inputWrapper = document.createElement('div');
  60. inputWrapper.style.position = 'relative';
  61. inputWrapper.style.display = 'flex';
  62. inputWrapper.style.alignItems = 'center';
  63. inputWrapper.style.gap = '8px'; // Adjusted gap
  64.  
  65. const input = document.createElement('input');
  66. input.type = 'text';
  67. input.value = value;
  68. input.id = id;
  69. input.style.padding = '8px'; // Adjusted padding
  70. input.style.border = '0px solid #ffffff';
  71. input.style.borderRadius = '8px'; // Adjusted border radius
  72. input.style.fontFamily = "'Inter', sans-serif";
  73. input.style.fontSize = '12px'; // Adjusted font size
  74. //input.style.backgroundColor = '#ffffff';
  75. input.style.backdropFilter = 'blur(10px)';
  76. input.style.backgroundColor = 'rgba(255, 255, 255, 0.4)'; // Optional for semi-transparent background
  77.  
  78. input.style.boxShadow = '0 1px 3px rgba(0, 0, 0, 0.1)'; // Adjusted shadow
  79. input.style.color = '#000000';
  80.  
  81. // Add event listener to save to localStorage on change
  82. input.addEventListener('input', () => {
  83. localStorage.setItem(id, input.value.trim());
  84. });
  85.  
  86. inputWrapper.appendChild(input);
  87.  
  88. const pasteButton = document.createElement('button');
  89. pasteButton.type = 'button';
  90. pasteButton.innerText = '📝';
  91. pasteButton.style.padding = '8px 8px'; // Adjusted padding
  92. pasteButton.style.border = 'none';
  93. //pasteButton.style.backgroundColor = '#7469B6';
  94.  
  95. pasteButton.style.backdropFilter = 'blur(10px)';
  96. pasteButton.style.backgroundColor = 'rgba(255, 255, 255, 0.4)'; // Optional for semi-transparent background
  97.  
  98. pasteButton.style.color = '#fff';
  99. pasteButton.style.borderRadius = '8px'; // Adjusted border radius
  100. pasteButton.style.cursor = 'pointer';
  101. pasteButton.style.fontFamily = "'Inter', sans-serif";
  102. pasteButton.style.fontSize = '12px'; // Adjusted font size
  103. pasteButton.addEventListener('click', async () => {
  104. input.value = await navigator.clipboard.readText();
  105. localStorage.setItem(id, input.value.trim()); // Save updated value to localStorage
  106. });
  107.  
  108. inputWrapper.appendChild(pasteButton);
  109.  
  110. if (includeCheckbox) {
  111. const checkbox = document.createElement('input');
  112. checkbox.type = 'checkbox';
  113. checkbox.id = `checkbox-${id}`;
  114. checkbox.style.width = '20px'; // Adjusted width
  115. checkbox.style.height = '20px'; // Adjusted height
  116. checkbox.style.marginLeft = '8px'; // Adjusted margin
  117. checkbox.style.cursor = 'pointer';
  118. checkbox.addEventListener('change', () => {
  119. checkbox.style.backgroundColor = checkbox.checked ? '#3ccf4e' : '#ddd';
  120. });
  121. inputWrapper.appendChild(checkbox);
  122. }
  123.  
  124. return inputWrapper;
  125. };
  126.  
  127. // Create the input fields
  128. const input1 = createInputFieldWithExtras('input1', storedInput1Value, false);
  129. const input2 = createInputFieldWithExtras('input2', storedInput2Value, true);
  130. const input3 = createInputFieldWithExtras('input3', storedInput3Value, true);
  131.  
  132. // Create a container for the buttons
  133. const buttonContainer = document.createElement('div');
  134. buttonContainer.style.display = 'flex';
  135. buttonContainer.style.justifyContent = 'space-between'; // Align buttons to the edges
  136. buttonContainer.style.alignItems = 'center'; // Align items vertically
  137. buttonContainer.style.gap = '8px'; // Adjusted gap
  138.  
  139. // Create the copy dynamic link button
  140. const copyLinkButton = document.createElement('button');
  141. copyLinkButton.type = 'button';
  142. copyLinkButton.innerText = 'Copy Link';
  143. copyLinkButton.style.padding = '8px'; // Adjusted padding
  144. copyLinkButton.style.width = '48%';
  145. copyLinkButton.style.backgroundColor = '#007bff';
  146. copyLinkButton.style.color = '#fff';
  147. copyLinkButton.style.border = 'none';
  148. copyLinkButton.style.borderRadius = '60px'; // Adjusted border radius
  149. copyLinkButton.style.cursor = 'pointer';
  150. copyLinkButton.style.fontSize = '12px'; // Adjusted font size
  151. copyLinkButton.style.fontWeight = 'bold';
  152. copyLinkButton.style.fontFamily = "'Inter', sans-serif";
  153. copyLinkButton.addEventListener('click', async () => {
  154. const link = getFirstAvailableLink();
  155. if (link) {
  156. await copyToClipboard(link);
  157. } else {
  158. showCustomAlert('No link available to copy.');
  159. }
  160. });
  161.  
  162. // Create the search button
  163. const searchButton = document.createElement('button');
  164. searchButton.type = 'button';
  165. searchButton.innerText = 'Search';
  166. searchButton.style.padding = '8px'; // Adjusted padding
  167. searchButton.style.width = '48%';
  168. searchButton.style.backgroundColor = '#28a745';
  169. searchButton.style.color = '#fff';
  170. searchButton.style.border = 'none';
  171. searchButton.style.borderRadius = '60px'; // Adjusted border radius
  172. searchButton.style.cursor = 'pointer';
  173. searchButton.style.fontSize = '12px'; // Adjusted font size
  174. searchButton.style.fontWeight = 'bold';
  175. searchButton.style.fontFamily = "'Inter', sans-serif";
  176. searchButton.style.marginLeft = 'auto'; // Push search button to the right
  177. searchButton.addEventListener('click', () => {
  178. let query = input1.querySelector('input').value.trim();
  179. const checkbox2 = document.getElementById(`checkbox-input2`);
  180. const checkbox3 = document.getElementById(`checkbox-input3`);
  181. if (checkbox2.checked) query += ` ${input2.querySelector('input').value.trim()}`;
  182. if (checkbox3.checked) query += ` ${input3.querySelector('input').value.trim()}`;
  183. const searchUrl = `https://www.google.com/search?q=${encodeURIComponent(query)}`;
  184. window.location.href = searchUrl;
  185.  
  186. // Save values to localStorage
  187. localStorage.setItem('input1', input1.querySelector('input').value.trim());
  188. localStorage.setItem('input2', input2.querySelector('input').value.trim());
  189. localStorage.setItem('input3', input3.querySelector('input').value.trim());
  190. });
  191.  
  192. // Append buttons to the button container
  193. buttonContainer.appendChild(copyLinkButton);
  194. buttonContainer.appendChild(searchButton);
  195.  
  196. // Append input fields and button container to the form
  197. form.appendChild(input1);
  198. form.appendChild(input2);
  199. form.appendChild(input3);
  200. form.appendChild(buttonContainer);
  201.  
  202. // Append form to the container
  203. formContainer.appendChild(form);
  204.  
  205. // Append form container to the body
  206. document.body.appendChild(formContainer);
  207.  
  208. // Function to show custom alerts
  209. const showCustomAlert = (message) => {
  210. const alertBox = document.createElement('div');
  211. alertBox.textContent = message;
  212. alertBox.style.position = 'fixed';
  213. alertBox.style.bottom = '680px';
  214. alertBox.style.right = '20px';
  215. alertBox.style.padding = '10px';
  216. alertBox.style.backgroundColor = '#ff0000';
  217. alertBox.style.color = '#fff';
  218. alertBox.style.borderRadius = '5px';
  219. alertBox.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.2)';
  220. alertBox.style.zIndex = '10001';
  221. alertBox.style.fontFamily = "'Inter', sans-serif";
  222. alertBox.style.opacity = '1';
  223. alertBox.style.transition = 'opacity 0.5s ease'; // Smooth transition
  224. document.body.appendChild(alertBox);
  225.  
  226. setTimeout(() => {
  227. alertBox.style.opacity = '0';
  228. setTimeout(() => alertBox.remove(), 500); // Delay removal until fade-out completes
  229. }, 1000);
  230. };
  231.  
  232. // Draggable functionality
  233. formContainer.addEventListener('mousedown', (e) => {
  234. if (e.target === formContainer) {
  235. let offsetX = e.clientX - formContainer.getBoundingClientRect().left;
  236. let offsetY = e.clientY - formContainer.getBoundingClientRect().top;
  237.  
  238. const mouseMoveHandler = (e) => {
  239. formContainer.style.left = `${e.clientX - offsetX}px`;
  240. formContainer.style.top = `${e.clientY - offsetY}px`;
  241. };
  242.  
  243. const mouseUpHandler = () => {
  244. document.removeEventListener('mousemove', mouseMoveHandler);
  245. document.removeEventListener('mouseup', mouseUpHandler);
  246. };
  247.  
  248. document.addEventListener('mousemove', mouseMoveHandler);
  249. document.addEventListener('mouseup', mouseUpHandler);
  250. }
  251. });
  252.  
  253. // Function to copy text to clipboard
  254. const copyToClipboard = async (text) => {
  255. try {
  256. await navigator.clipboard.writeText(text);
  257. showCustomAlert('Link copied to clipboard!');
  258. } catch (err) {
  259. console.error('Failed to copy: ', err);
  260. showCustomAlert('Failed to copy link.');
  261. }
  262. };
  263.  
  264.  
  265.  
  266. const getFirstAvailableLink = () => {
  267. // Extended class names to check
  268. const classNamesToCheck = ['n1obkb', 'ab_button', 'ellip', 'PZPZlf'];
  269.  
  270. // Check for direct <a> tags and those within a <div> or other container
  271. for (const className of classNamesToCheck) {
  272. // Select all <a> elements with the class
  273. const links = document.querySelectorAll(`a.${className}[href]`);
  274.  
  275. // If direct matches are found, return the first href
  276. if (links.length > 0) {
  277. return links[0].href; // Return the href of the first found link
  278. }
  279.  
  280. // Check for <a> tags within other elements with the class name
  281. const containers = document.querySelectorAll(`div.${className}, button.${className}`);
  282. for (let container of containers) {
  283. const childLinks = container.querySelectorAll('a[href]');
  284. if (childLinks.length > 0) {
  285. return childLinks[0].href;
  286. }
  287. }
  288. }
  289.  
  290. // Return null if no link is found
  291. return null;
  292. };
  293.  
  294.  
  295.  
  296. })();