Draggable Google 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.

  1. // ==UserScript==
  2. // @name Draggable Google Search Tool with Copy Link
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.27
  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 = '#d8d3ff';
  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 = '2px solid #ffff';
  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.style.border = '0px solid #cccccc'; // Optional: Border for visibility
  119. checkbox.style.borderRadius = '12px';
  120. checkbox.style.appearance = 'none'; // Remove default checkbox styles
  121. checkbox.style.backgroundColor = '#ffffff'; // Default to white
  122. checkbox.style.display = 'inline-block';
  123.  
  124. // Change background color when selected
  125. checkbox.addEventListener('change', () => {
  126. checkbox.style.backgroundColor = checkbox.checked ? '#0ad400' : '#ffffff'; // Green when checked, white otherwise
  127. });
  128.  
  129. inputWrapper.appendChild(checkbox);
  130. }
  131.  
  132.  
  133. return inputWrapper;
  134. };
  135.  
  136. // Create the input fields
  137. const input1 = createInputFieldWithExtras('input1', storedInput1Value, false);
  138. const input2 = createInputFieldWithExtras('input2', storedInput2Value, true);
  139. const input3 = createInputFieldWithExtras('input3', storedInput3Value, true);
  140.  
  141. // Create a container for the buttons
  142. const buttonContainer = document.createElement('div');
  143. buttonContainer.style.display = 'flex';
  144. buttonContainer.style.justifyContent = 'space-between'; // Align buttons to the edges
  145. buttonContainer.style.alignItems = 'center'; // Align items vertically
  146. buttonContainer.style.gap = '8px'; // Adjusted gap
  147.  
  148. // Create the copy dynamic link button
  149. const copyLinkButton = document.createElement('button');
  150. copyLinkButton.type = 'button';
  151. copyLinkButton.innerText = 'Copy Link';
  152. copyLinkButton.style.padding = '8px'; // Adjusted padding
  153. copyLinkButton.style.width = '48%';
  154. copyLinkButton.style.backgroundColor = '#8700ff';
  155. copyLinkButton.style.color = '#fff';
  156. copyLinkButton.style.border = 'none';
  157. copyLinkButton.style.borderRadius = '60px'; // Adjusted border radius
  158. copyLinkButton.style.cursor = 'pointer';
  159. copyLinkButton.style.fontSize = '12px'; // Adjusted font size
  160. copyLinkButton.style.fontWeight = 'bold';
  161. copyLinkButton.style.fontFamily = "'Inter', sans-serif";
  162. copyLinkButton.addEventListener('click', async () => {
  163. const link = getFirstAvailableLink();
  164. if (link) {
  165. await copyToClipboard(link);
  166. } else {
  167. showCustomAlert('No link available to copy.');
  168. }
  169. });
  170.  
  171. // Create the search button
  172. const searchButton = document.createElement('button');
  173. searchButton.type = 'button';
  174. searchButton.innerText = 'Search';
  175. searchButton.style.padding = '8px'; // Adjusted padding
  176. searchButton.style.width = '48%';
  177. searchButton.style.backgroundColor = '#000000';
  178. searchButton.style.color = '#fff';
  179. searchButton.style.border = 'none';
  180. searchButton.style.borderRadius = '60px'; // Adjusted border radius
  181. searchButton.style.cursor = 'pointer';
  182. searchButton.style.fontSize = '12px'; // Adjusted font size
  183. searchButton.style.fontWeight = 'bold';
  184. searchButton.style.fontFamily = "'Inter', sans-serif";
  185. searchButton.style.marginLeft = 'auto'; // Push search button to the right
  186. searchButton.addEventListener('click', () => {
  187. let query = input1.querySelector('input').value.trim();
  188. const checkbox2 = document.getElementById(`checkbox-input2`);
  189. const checkbox3 = document.getElementById(`checkbox-input3`);
  190. if (checkbox2.checked) query += ` ${input2.querySelector('input').value.trim()}`;
  191. if (checkbox3.checked) query += ` ${input3.querySelector('input').value.trim()}`;
  192. const searchUrl = `https://www.google.com/search?q=${encodeURIComponent(query)}`;
  193. window.location.href = searchUrl;
  194.  
  195. // Save values to localStorage
  196. localStorage.setItem('input1', input1.querySelector('input').value.trim());
  197. localStorage.setItem('input2', input2.querySelector('input').value.trim());
  198. localStorage.setItem('input3', input3.querySelector('input').value.trim());
  199. });
  200.  
  201. // Append buttons to the button container
  202. buttonContainer.appendChild(copyLinkButton);
  203. buttonContainer.appendChild(searchButton);
  204.  
  205. // Append input fields and button container to the form
  206. form.appendChild(input1);
  207. form.appendChild(input2);
  208. form.appendChild(input3);
  209. form.appendChild(buttonContainer);
  210.  
  211. // Append form to the container
  212. formContainer.appendChild(form);
  213.  
  214. // Append form container to the body
  215. document.body.appendChild(formContainer);
  216.  
  217. // Function to show custom alerts
  218. const showCustomAlert = (message) => {
  219. const alertBox = document.createElement('div');
  220. alertBox.textContent = message;
  221. alertBox.style.position = 'fixed';
  222. alertBox.style.bottom = '680px';
  223. alertBox.style.right = '20px';
  224. alertBox.style.padding = '10px';
  225. alertBox.style.backgroundColor = '#ff0000';
  226. alertBox.style.color = '#fff';
  227. alertBox.style.borderRadius = '5px';
  228. alertBox.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.2)';
  229. alertBox.style.zIndex = '10001';
  230. alertBox.style.fontFamily = "'Inter', sans-serif";
  231. alertBox.style.opacity = '1';
  232. alertBox.style.transition = 'opacity 0.5s ease'; // Smooth transition
  233. document.body.appendChild(alertBox);
  234.  
  235. setTimeout(() => {
  236. alertBox.style.opacity = '0';
  237. setTimeout(() => alertBox.remove(), 500); // Delay removal until fade-out completes
  238. }, 1000);
  239. };
  240.  
  241. // Draggable functionality
  242. formContainer.addEventListener('mousedown', (e) => {
  243. if (e.target === formContainer) {
  244. let offsetX = e.clientX - formContainer.getBoundingClientRect().left;
  245. let offsetY = e.clientY - formContainer.getBoundingClientRect().top;
  246.  
  247. const mouseMoveHandler = (e) => {
  248. formContainer.style.left = `${e.clientX - offsetX}px`;
  249. formContainer.style.top = `${e.clientY - offsetY}px`;
  250. };
  251.  
  252. const mouseUpHandler = () => {
  253. document.removeEventListener('mousemove', mouseMoveHandler);
  254. document.removeEventListener('mouseup', mouseUpHandler);
  255. };
  256.  
  257. document.addEventListener('mousemove', mouseMoveHandler);
  258. document.addEventListener('mouseup', mouseUpHandler);
  259. }
  260. });
  261.  
  262. // Function to copy text to clipboard
  263. const copyToClipboard = async (text) => {
  264. try {
  265. await navigator.clipboard.writeText(text);
  266. showCustomAlert('Link copied to clipboard!');
  267. } catch (err) {
  268. console.error('Failed to copy: ', err);
  269. showCustomAlert('Failed to copy link.');
  270. }
  271. };
  272.  
  273.  
  274.  
  275. const getFirstAvailableLink = () => {
  276. // Extended class names to check
  277. const classNamesToCheck = ['n1obkb', 'ab_button', 'ellip', 'PZPZlf'];
  278.  
  279. // Check for direct <a> tags and those within a <div> or other container
  280. for (const className of classNamesToCheck) {
  281. // Select all <a> elements with the class
  282. const links = document.querySelectorAll(`a.${className}[href]`);
  283.  
  284. // If direct matches are found, return the first href
  285. if (links.length > 0) {
  286. return links[0].href; // Return the href of the first found link
  287. }
  288.  
  289. // Check for <a> tags within other elements with the class name
  290. const containers = document.querySelectorAll(`div.${className}, button.${className}`);
  291. for (let container of containers) {
  292. const childLinks = container.querySelectorAll('a[href]');
  293. if (childLinks.length > 0) {
  294. return childLinks[0].href;
  295. }
  296. }
  297. }
  298.  
  299. // Return null if no link is found
  300. return null;
  301. };
  302.  
  303.  
  304.  
  305. })();