General URL Cleaner Revived

Cleans URLs from various popular sites and removes tracking parameters

  1. // ==UserScript==
  2. // @name General URL Cleaner Revived
  3. // @namespace https://greasyfork.org/en/users/594496-divided-by
  4. // @author dividedby
  5. // @description Cleans URLs from various popular sites and removes tracking parameters
  6. // @version 4.2.6
  7. // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
  8. // @contributionURL https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=dividedbyerror@gmail.com&item_name=Greasy+Fork+Donation
  9. // @contributionAmount $1
  10. // @include https://www.newegg.com/*
  11. // @include https://www.newegg.ca/*
  12. // @include /^https:\/\/[a-z.]*\.?bing(\.[a-z]{2,3})?(\.[a-z]+)?\/.*$/
  13. // @include https://www.youtube.com/*
  14. // @include https://www.imdb.com/*
  15. // @include https://www.facebook.com/*
  16. // @include https://disqus.com/embed/comments/*
  17. // @include https://www.target.com/*
  18. // @include https://www.linkedin.com/*
  19. // @include https://www.etsy.com/*
  20. // @include https://www.yahoo.com/*
  21. // @include /^https:\/\/[a-z0-9.]*\.?amazon(\.[a-z0-9]{2,3})?(\.[a-z]+)?\/.*$/
  22. // @include /^https:\/\/[a-z0-9.]*\.?google(\.[a-z0-9]{2,3})?(\.[a-z]+)?\/.*$/
  23. // @include /^https:\/\/[a-z0-9.]*\.?ebay(desc)?(\.[a-z0-9]{2,3})?(\.[a-z]+)?\/.*$/
  24. // @include /^https:\/\/[a-z0-9.]*twitter.com\/.*$/
  25. // @exclude /^https:\/\/[a-z0-9.]*\.?amazon(\.[a-z0-9]{2,3})?(\.[a-z]+)?\/(?:gp\/(?:cart|buy|css|legacy|your-account).*|sspa.*)$/
  26. // @exclude https://apis.google.com/*
  27. // @exclude https://accounts.google.com/*
  28. // @exclude https://support.google.com/*
  29. // @exclude https://www.google.com/recaptcha/*
  30. // @exclude https://hangouts.google.com/webchat/*
  31. // @exclude https://gsuite.google.com/*
  32. // @exclude https://calendar.google.com/*
  33. // @exclude https://docs.google.com/spreadsheets/*
  34. // @exclude https://takeout.google.com/*
  35. // @run-at document-idle
  36.  
  37. // ==/UserScript==
  38.  
  39. (() => {
  40. /*
  41. * Vars
  42. */
  43.  
  44. const currHost = location.host;
  45. const currPath = location.pathname;
  46. const currSearch = location.search;
  47.  
  48. const ebay = /^[a-z.]*\.?ebay(desc)?(\.[a-z]{2,3})?(\.[a-z]+)?$/;
  49. const amazon = /^[a-z.]*\.?amazon(\.[a-z]{2,3})?(\.[a-z]+)?$/;
  50. const google = /^[a-z.]*\.?google(\.[a-z]{2,3})?(\.[a-z]+)?$/;
  51. const target = /^[a-z.]*\.?target\.com$/;
  52. const bing = /^[a-z.]*\.?bing(\.[a-z]{2,3})?(\.[a-z]+)?$/;
  53.  
  54. const amazonParams =
  55. /&?_?(encoding|crid|sprefix|ref|th|url|ie|pf_rd_[^&#]*?|pd_rd_[^&#]*?|bbn|rw_html_to_wsrp|ref_|content-id)(=[^&#]*)?($|&)/g;
  56. const neweggParams = /&(cm_sp|icid|ignorebbr)(=[^&#]*)?($|&)/g;
  57. const imdbParams = /&(pf_rd_[a-z]|ref_)(=[^&#]*)?($|&)/g;
  58. const bingParams =
  59. /&(redig|toWww|ghpl|lq|ghc|ghsh|ghacc|ghpl|go|qs|form|FORM|filt|pq|s[cpk]|qpvt|cvid)(=[^&#]*)?(?=$|&)/g;
  60. const youtubeParams =
  61. /&(feature|src_vid|annotation_id|[gh]l)(=[^&#]*)?($|&)/g;
  62. const ebayParams = /[?&](_(o?sacat|odkw|from|trksid)|rt)(=[^&#]*)?(?=&|$)/g;
  63. const twitterParams = /&(src|ref_src|ref_url|vertical|s)(=[^&#]*)?($|&)/g;
  64. const targetParams = /&(lnk|tref|searchTermRaw)(=[^&#]*)?($|&)/g;
  65. const facebookParams = /&(set)(=[^&#]*)?($|&)/g;
  66. const googleParams =
  67. /(?:&|^)(uact|iflsig|sxsrf|ved|source(id)?|s?ei|tab|tbo|h[ls]|authuser|n?um|ie|aqs|as_qdr|bav|bi[wh]|bs|bvm|cad|channel|complete|cp|s?client|d[pc]r|e(ch|msg|s_sm)|g(fe|ws)_rd|gpsrc|noj|btnG|o[eq]|p(si|bx|f|q)|rct|rlz|site|spell|tbas|usg|xhr|gs_[a-z]+)(=[^&#]*)?(?=$|&)/g;
  68. const linkedinParams =
  69. /&(eBP|refId|trackingId|trk|flagship3_search_srp_jobs|lipi|lici)(=[^&#]*)?($|&)/g;
  70. const etsyParams =
  71. /&(click_key|click_sum|ref|pro|frs|ga_order|ga_search_type|ga_view_type|ga_search_query|sts|organic_search_click|plkey)(=[^&#]*)?($|&)/g;
  72. const yahooParams = /&(guccounter|guce_referrer|guce_referrer_sig)(=[^&#]*)?($|&)/g;
  73.  
  74. /*
  75. * Main
  76. */
  77.  
  78. if (bing.test(currHost)) {
  79. setCurrUrl(cleanBing(currSearch));
  80. cleanLinks(parserAll);
  81. return;
  82. }
  83.  
  84. if (currHost == "www.linkedin.com") {
  85. setCurrUrl(cleanLinkedin(currSearch));
  86. cleanLinks(parserAll);
  87. return;
  88. }
  89.  
  90. if (currHost == "www.etsy.com") {
  91. setCurrUrl(cleanEtsy(currSearch));
  92. cleanLinks(parserAll);
  93. return;
  94. }
  95.  
  96. if (currHost == "www.yahoo.com") {
  97. setCurrUrl(cleanYahoo(currSearch));
  98. cleanLinks(parserAll);
  99. return;
  100. }
  101.  
  102. if (currHost === "www.youtube.com") {
  103. if (currPath === "/redirect") {
  104. location.href = cleanYoutubeRedir(currSearch);
  105. }
  106.  
  107. if (currPath === "/watch") {
  108. setCurrUrl(cleanYoutube(currSearch));
  109. }
  110.  
  111. cleanLinks(parserYoutube);
  112. return;
  113. }
  114.  
  115. if (currHost.endsWith(".newegg.com") || currHost.endsWith(".newegg.ca")) {
  116. if (currSearch) {
  117. setCurrUrl(cleanNewegg(currSearch));
  118. }
  119.  
  120. cleanLinks(parserNewegg);
  121. return;
  122. }
  123.  
  124. if (currHost === "www.imdb.com") {
  125. if (currSearch) {
  126. setCurrUrl(cleanImdb(currSearch));
  127. }
  128.  
  129. cleanLinks(parserIMDB);
  130. onhashchange = deleteHash();
  131. return;
  132. }
  133.  
  134. if (google.test(currHost)) {
  135. if (currPath === "/url" || currPath === "/imgres") {
  136. location.href = cleanGenericRedir(currSearch);
  137. }
  138.  
  139. if (!currSearch && !/[&#]q=/.test(location.hash)) {
  140. return;
  141. }
  142.  
  143. setCurrUrl(cleanGoogle(currPath + currSearch));
  144. changeState(googleInstant);
  145.  
  146. if (currSearch.includes("tbm=isch")) {
  147. cleanLinksAlways(parserGoogleImages);
  148. } else {
  149. cleanLinks(parserGoogle);
  150. }
  151.  
  152. return;
  153. }
  154.  
  155. if (ebay.test(currHost)) {
  156. if (currPath.includes("/itm/")) {
  157. setCurrUrl(cleanEbayItem(location));
  158. } else if (currSearch) {
  159. setCurrUrl(cleanEbayParams(currSearch));
  160. }
  161.  
  162. cleanLinks(parserEbay);
  163. onhashchange = deleteHash;
  164. return;
  165. }
  166.  
  167. if (target.test(currHost)) {
  168. if (currPath.includes("/p/")) {
  169. setCurrUrl(cleanTargetItemp(location));
  170. } else if (currSearch) {
  171. setCurrUrl(cleanTargetParams(currSearch));
  172. }
  173.  
  174. cleanLinks(parserTarget);
  175. onhashchange = deleteHash;
  176. return;
  177. }
  178.  
  179. if (amazon.test(currHost)) {
  180. if (currPath.includes("/dp/")) {
  181. setCurrUrl(cleanAmazonItemdp(location));
  182. } else if (currPath.includes("/gp/product")) {
  183. setCurrUrl(cleanAmazonItemgp(location));
  184. } else if (currSearch) {
  185. setCurrUrl(cleanAmazonParams(currSearch));
  186. }
  187.  
  188. cleanLinks(parserAmazon);
  189. onhashchange = deleteHash();
  190. return;
  191. }
  192.  
  193. if (currHost == "twitter.com") {
  194. if (currSearch) {
  195. setCurrUrl(cleanTwitterParams(currSearch));
  196. }
  197.  
  198. cleanLinks(parserTwitter);
  199. return;
  200. }
  201.  
  202. if (currHost == "www.facebook.com") {
  203. if (currSearch) {
  204. setCurrUrl(cleanFacebookParams(currSearch));
  205. }
  206.  
  207. cleanLinks(parserFacebook);
  208. return;
  209. }
  210.  
  211. if (currHost == "disqus.com") {
  212. cleanLinks(parserDisqus);
  213. return;
  214. }
  215.  
  216. if (currHost === "app.getpocket.com") {
  217. cleanLinks(parserAll);
  218. return;
  219. }
  220.  
  221. /*
  222. * Boilerplate functions
  223. */
  224.  
  225. function setCurrUrl(url) {
  226. history.replaceState(null, null, url);
  227. }
  228.  
  229. function deleteHash() {
  230. history.replaceState(null, null, " ");
  231. }
  232.  
  233. function observe(func) {
  234. new MutationObserver(func).observe(document, {
  235. childList: true,
  236. subtree: true,
  237. });
  238. }
  239.  
  240. // Clean links once, mark as cleaned, then ignore them
  241. function cleanLinks(linkParser) {
  242. observe(function () {
  243. for (let a of document.links) {
  244. if (a.cleaned) {
  245. continue;
  246. }
  247.  
  248. if (a.protocol && a.protocol.startsWith("http")) {
  249. linkParser(a);
  250. }
  251.  
  252. a.cleaned = 1;
  253. }
  254. });
  255. }
  256.  
  257. // Always clean links
  258. function cleanLinksAlways(linkParser) {
  259. observe(function () {
  260. for (let a of document.links)
  261. if (a.protocol && a.protocol.startsWith("http")) {
  262. linkParser(a);
  263. }
  264. });
  265. }
  266.  
  267. function googleInstant(url) {
  268. let parts = url.split("#");
  269. if (parts.length !== 2) {
  270. return url;
  271. }
  272.  
  273. let hash = parts[1];
  274. if (hash === "imgrc=_") {
  275. return " ";
  276. }
  277.  
  278. if (/(^|&)q=/.test(hash)) {
  279. return "?" + hash;
  280. }
  281.  
  282. return "#" + hash;
  283. }
  284.  
  285. // Intercept & modify url passed into history.replaceState/pushState
  286. function changeState(mod) {
  287. history.realPushState = history.pushState;
  288. history.realReplaceState = history.replaceState;
  289.  
  290. history.pushState = function () {
  291. history.realPushState(null, null, mod(arguments[2]));
  292. };
  293.  
  294. history.replaceState = function () {
  295. history.realReplaceState(null, null, mod(arguments[2]));
  296. };
  297. }
  298.  
  299. /*
  300. * Link parsing functions
  301. */
  302.  
  303. function parserAll(a) {
  304. let host = a.host;
  305. let path = a.pathname;
  306. if (a.cleaned) {
  307. return;
  308. }
  309.  
  310. if (google.test(host)) {
  311. if (path === "/imgres" || path === "/url") {
  312. a.href = cleanGenericRedir(a.search);
  313. } else if (a.search) {
  314. a.search = cleanGoogle(a.search);
  315. }
  316.  
  317. return;
  318. }
  319.  
  320. if (host === "www.youtube.com") {
  321. if (path === "/watch") {
  322. a.search = cleanYoutube(a.search);
  323. } else if (path === "/redirect") {
  324. a.href = cleanYoutubeRedir(a.search);
  325. }
  326.  
  327. a.cleaned = 1;
  328. return;
  329. }
  330.  
  331. if (host === "getpocket.com") {
  332. if (path === "/redirect") {
  333. a.href = cleanPocketRedir(a.href);
  334. }
  335. }
  336.  
  337. parserAmazon(a);
  338. parserEbay(a);
  339. parserNewegg(a);
  340. parserIMDB(a);
  341.  
  342. if (a.search) {
  343. a.search = cleanUtm(a.search);
  344. }
  345.  
  346. if (a.hash) {
  347. a.hash = cleanUtm(a.hash);
  348. }
  349.  
  350. a.cleaned = 1;
  351. }
  352.  
  353. function parserGoogle(a) {
  354. a.removeAttribute("onmousedown");
  355. parserAll(a);
  356. }
  357.  
  358. function parserGoogleImages(a) {
  359. let jsaction = a.getAttribute("jsaction");
  360. if (jsaction && jsaction.includes("down:irc.rl")) {
  361. console.log(a);
  362. a.removeAttribute("jsaction");
  363. }
  364.  
  365. a.removeAttribute("onmousedown");
  366. parserAll(a);
  367. }
  368.  
  369. function parserYoutube(a) {
  370. parserAll(a);
  371. let text = a.innerText;
  372. let href = a.getAttribute("href");
  373. if (
  374. text === href ||
  375. (text.endsWith("...") && href.startsWith(text.slice(0, -3)))
  376. ) {
  377. a.innerText = href;
  378. }
  379. }
  380.  
  381. function parserTarget(a) {
  382. if (!target.test(a.host)) {
  383. return;
  384. }
  385.  
  386. if (a.pathname.includes("/p/")) {
  387. a.href = cleanTargetItemp(a);
  388. } else if (a.search) {
  389. a.href = cleanTargetParams(a.href);
  390. }
  391. }
  392.  
  393. function parserAmazon(a) {
  394. if (!amazon.test(a.host)) {
  395. return;
  396. }
  397.  
  398. if (a.pathname.includes("black-curtain-redirect.html")) {
  399. a.href = cleanAmazonRedir(location);
  400. } else if (a.pathname.includes("/dp/")) {
  401. a.href = cleanAmazonItemdp(a);
  402. } else if (a.pathname.includes("/gp/product")) {
  403. a.href = cleanAmazonItemgp(a);
  404. } else if (a.pathname.includes("/picassoRedirect")) {
  405. a.href = cleanGenericRedir(a.search);
  406. a.search = "";
  407. } else if (a.search) {
  408. a.href = cleanAmazonParams(a.href);
  409. }
  410.  
  411. if (a.pathname.includes("/ref=")) {
  412. a.pathname = cleanAmazonParams(a.pathname);
  413. }
  414. }
  415.  
  416. function parserEbay(a) {
  417. if (!ebay.test(a.host)) {
  418. return;
  419. }
  420.  
  421. if (a.pathname.includes("/itm/")) {
  422. a.href = cleanEbayItem(a);
  423. } else if (a.host.startsWith("pulsar.")) {
  424. a.href = cleanEbayPulsar(a.search);
  425. } else if (a.search) {
  426. a.search = cleanEbayParams(a.search);
  427. }
  428. }
  429.  
  430. function parserNewegg(a) {
  431. if (!a.host.endsWith(".newegg.com") && !a.host.endsWith(".newegg.ca")) {
  432. return;
  433. }
  434.  
  435. if (a.search && !a.pathname.includes("/marketplace/")) {
  436. a.search = cleanNewegg(a.search);
  437. }
  438. }
  439.  
  440. function parserIMDB(a) {
  441. if (a.host === "www.imdb.com" && a.search) {
  442. a.search = cleanImdb(a.search);
  443. }
  444. }
  445.  
  446. function parserTwitter(a) {
  447. if (a.host !== "t.co") {
  448. return;
  449. }
  450.  
  451. let fake = "t.co" + a.pathname;
  452. let real = a.getAttribute("data-expanded-url");
  453. if (real) {
  454. a.href = real;
  455. a.removeAttribute("data-expanded-url");
  456. sessionStorage.setItem(fake, real);
  457. return;
  458. }
  459.  
  460. if (!a.classList.contains("TwitterCard-container")) {
  461. return;
  462. }
  463.  
  464. real = sessionStorage.getItem(fake);
  465. if (real) {
  466. a.href = real;
  467. }
  468. }
  469.  
  470. function parserFacebook(a) {
  471. let onclick = a.getAttribute("onclick");
  472. if (!onclick || !onclick.startsWith("LinkshimAsyncLink")) {
  473. return;
  474. }
  475.  
  476. if (a.host !== "l.facebook.com") {
  477. return;
  478. }
  479.  
  480. a.href = cleanGenericRedir(a.search);
  481. a.removeAttribute("onclick");
  482. a.removeAttribute("onmouseover");
  483. }
  484.  
  485. function parserDisqus(a) {
  486. if (a.host === "disq.us" && a.pathname === "/url") {
  487. a.href = a.href.replace(/\:.*/, "");
  488. }
  489. a.href = cleanGenericRedir(a.search);
  490.  
  491. parserAll(a);
  492. }
  493.  
  494. /*
  495. * URL string functions
  496. */
  497.  
  498. function cleanGoogle(url) {
  499. return url.replace("?", "?&").replace(googleParams, "").replace("&", "");
  500. }
  501.  
  502. function cleanBing(url) {
  503. return url
  504. .replace("?", "?&")
  505. .replace(bingParams, "")
  506. .replace("&", "")
  507. .replace(/\?$/, "");
  508. }
  509.  
  510. function cleanLinkedin(url) {
  511. return url.replace("?", "?&").replace(linkedinParams, "").replace("&", "");
  512. }
  513.  
  514. function cleanEtsy(url) {
  515. return url.replace("?", "?&").replace(etsyParams, "").replace("&", "");
  516. }
  517.  
  518. function cleanYahoo(url) {
  519. return url.replace("?", "?&").replace(yahooParams, "").replace("&", "");
  520. }
  521.  
  522. function cleanTwitterParams(url) {
  523. return url.replace("?", "?&").replace(twitterParams, "").replace("&", "");
  524. }
  525.  
  526. function cleanYoutube(url) {
  527. return url.replace("?", "?&").replace(youtubeParams, "").replace("&", "");
  528. }
  529.  
  530. function cleanImdb(url) {
  531. return url
  532. .replace("?", "?&")
  533. .replace(imdbParams, "")
  534. .replace("&", "")
  535. .replace(/\?$/, "");
  536. }
  537.  
  538. function cleanNewegg(url) {
  539. return url.replace("?", "?&").replace(neweggParams, "").replace("&", "");
  540. }
  541.  
  542. function cleanTargetParams(url) {
  543. return url
  544. .replace("?", "?&", "#")
  545. .replace(targetParams, "")
  546. .replace("&", "");
  547. }
  548.  
  549. function cleanFacebookParams(url) {
  550. return url
  551. .replace("?", "?&", "#")
  552. .replace(facebookParams, "")
  553. .replace("&", "");
  554. }
  555.  
  556. function cleanAmazonParams(url) {
  557. return url
  558. .replace("?", "?&")
  559. .replace(amazonParams, "")
  560. .replace("&", "")
  561. .replace(/\?$/, "");
  562. }
  563.  
  564. function cleanEbayParams(url) {
  565. return url.replace("?", "?&").replace(ebayParams, "").replace("&", "");
  566. }
  567.  
  568. function cleanTargetItemp(a) {
  569. let item = a.pathname.replace(/(?<=\/p).*(?=\/A)/, "");
  570. return a.origin + item;
  571. }
  572.  
  573. function cleanAmazonItemgp(a) {
  574. let item = a.pathname.match(/\/[A-Z0-9]{10}/);
  575. return a.origin + "/gp/product" + item + a.hash;
  576. }
  577.  
  578. function cleanAmazonItemdp(a) {
  579. let item = a.pathname.match(/\/dp(\/[A-Z0-9]{10})/)[1];
  580. return a.origin + "/dp" + item + a.hash;
  581. }
  582.  
  583. function cleanEbayItem(a) {
  584. let item = a.pathname.match(/\/[0-9]{12}/);
  585. let origList = a.search.replace(/&/g, "?").match(/\?orig_cvip=[^?]+/) || "";
  586. return a.origin + "/itm" + item + origList + a.hash;
  587. }
  588.  
  589. function cleanEbayPulsar(url) {
  590. let item = url.match(/%7B%22mecs%22%3A%22([0-9]{12})/).pop();
  591. return location.origin + "/itm/" + item;
  592. }
  593.  
  594. function cleanYoutubeRedir(url) {
  595. return decodeURIComponent(url.match(/[?&]q=([^&]+)/).pop());
  596. }
  597.  
  598. function cleanAmazonRedir(url) {
  599. return decodeURIComponent(url.match(/[?&]redirectUrl=([^&]+)/).pop());
  600. }
  601.  
  602. function cleanGenericRedir(url) {
  603. return decodeURIComponent(url.match(/[?&](new|img)?u(rl)?=([^&]+)/i).pop());
  604. }
  605.  
  606. function cleanGenericRedir2(url) {
  607. return decodeURIComponent(url.match(/[?&]\w*url=([^&]+)/i).pop());
  608. }
  609.  
  610. function cleanUtm(url) {
  611. var urlparts = url.split("?");
  612. if (urlparts.length >= 2) {
  613. var pars = urlparts[1].split(/[&;]/g);
  614. //reverse iteration as may be destructive
  615. for (var i = pars.length; (i -= 1) > 0; ) {
  616. if (/^utm_/.test(pars[i])) {
  617. pars.splice(i, 1);
  618. }
  619. }
  620. return urlparts[0] + (pars.length > 0 ? "?" + pars.join("&") : "");
  621. }
  622. return url;
  623. }
  624.  
  625. function cleanPocketRedir(url) {
  626. return decodeURIComponent(
  627. url.replace("https://getpocket.com/redirect?url=", "")
  628. );
  629. }
  630. })();