Decloak links and open directly

Open redirected/cloaked links directly

  1. // ==UserScript==
  2. // @name Decloak links and open directly
  3. // @description Open redirected/cloaked links directly
  4. // @version 2.2.5
  5. // @author wOxxOm
  6. // @namespace wOxxOm.scripts
  7. // @icon https://i.imgur.com/cfmXJHv.png
  8. // @resource icon https://i.imgur.com/cfmXJHv.png
  9. // @license MIT License
  10. // @run-at document-start
  11. // @grant GM_getResourceURL
  12. // @match *://*/*
  13. // ==/UserScript==
  14.  
  15. 'use strict';
  16.  
  17. const POPUP = document.createElement('a');
  18. POPUP.id = GM_info.script.name;
  19. POPUP.title = 'Original link';
  20. let isPopupStyled;
  21. let lastLink;
  22. let hoverTimer;
  23. let hoverStopTimer;
  24.  
  25. addEventListener('keypress', e => e.which === 13 && decloakLink(e), true);
  26. addEventListener('mousedown', decloakLink, true);
  27. addEventListener('mouseover', onHover, true);
  28.  
  29. function onHover(event) {
  30. const a = decloakLink(event);
  31. if (!a) return;
  32. if (lastLink)
  33. lastLink.removeEventListener('mouseout', cancelHover);
  34. lastLink = a;
  35. clearTimeout(hoverTimer);
  36. hoverTimer = setTimeout(showPopup, 500, a);
  37. a.addEventListener('mouseout', cancelHover);
  38. }
  39.  
  40. function cancelHover(e) {
  41. this.removeEventListener('mouseout', cancelHover);
  42. clearTimeout(hoverStopTimer);
  43. hoverStopTimer = setTimeout(hidePopup, 500, this);
  44. }
  45.  
  46. function showPopup(a) {
  47. if (!a.matches(':hover'))
  48. return;
  49. if (!isPopupStyled) {
  50. isPopupStyled = true;
  51. POPUP.style.cssText = //'all: unset;' +
  52. 'width: 18px;' +
  53. 'height: 18px;' +
  54. 'background: url("' + GM_getResourceURL('icon', false) + '") center no-repeat, white;' +
  55. 'background-size: 16px;' +
  56. 'opacity: 0;' +
  57. 'transition: opacity .5s;' +
  58. 'border: 1px solid #888;' +
  59. 'border-radius: 11px;' +
  60. 'z-index: 2147483647;' +
  61. 'margin-left: 0;' +
  62. 'cursor: pointer;' +
  63. 'position: absolute;'
  64. .replace(/;/g, '!important;');
  65. }
  66. const linkStyle = getComputedStyle(a);
  67. POPUP.href = a.hrefUndecloaked;
  68. POPUP.style.opacity = '0';
  69. POPUP.style.marginLeft = -(
  70. (parseFloat(linkStyle.paddingRight) || 0) +
  71. (parseFloat(linkStyle.marginRight) || 0) +
  72. (parseFloat(linkStyle.borderRightWidth) || 0) +
  73. Math.max(0, a.getBoundingClientRect().right + 32 - innerWidth)
  74. ) + 'px';
  75. setTimeout(() => (POPUP.style.opacity = '1'));
  76. a.parentElement.insertBefore(POPUP, a.nextSibling);
  77. POPUP.addEventListener('click', openOriginal);
  78. }
  79.  
  80. function hidePopup(a) {
  81. if (POPUP.matches(':hover') || lastLink && lastLink.matches(':hover')) {
  82. cancelHover.call(a);
  83. } else if (POPUP.style.opacity === '1') {
  84. POPUP.style.opacity = '0';
  85. cancelHover.call(a);
  86. } else {
  87. lastLink = null;
  88. POPUP.remove();
  89. }
  90. }
  91.  
  92. function openOriginal(e) {
  93. this.href = '';
  94. e.preventDefault();
  95. e.stopPropagation();
  96. e.stopImmediatePropagation();
  97. setTimeout(() => {
  98. lastLink.href = lastLink.hrefUndecloaked;
  99. lastLink.dispatchEvent(new MouseEvent('click', {bubbles: true}));
  100. });
  101. }
  102.  
  103. function decloakLink(event) {
  104. const a = getClosestLink(event);
  105. if (!a || a === POPUP || !/^https?:$/.test(a.protocol))
  106. return;
  107. if (a.hrefUndecloaked)
  108. return a;
  109.  
  110. if (/\bthis\.href\s*=[^=]/.test(a.getAttribute('onmousedown')))
  111. a.onmousedown = null;
  112. if (/\bthis\.href\s*=[^=]/.test(a.getAttribute('onclick')))
  113. a.onclick = null;
  114.  
  115. const href = a.href.baseVal || a.href;
  116. const m = href.match(/([?&][-\w]*referrer[-\w]*(?==))?[=?/]((ftps?|https?)((:|%3[Aa])\/\/[^+&]+|%3[Aa]%2[Ff]%2[Ff][^+&/]+))/);
  117. if (!m ||
  118. m[1] ||
  119. a.hostname === 'disqus.com' && a.pathname.startsWith('/embed/comments/')) {
  120. return;
  121. }
  122.  
  123. let realUrl = decodeURIComponent(m[2]);
  124. if (a.hostname === 'disq.us' &&
  125. realUrl.lastIndexOf(':') !== realUrl.indexOf(':')) {
  126. realUrl = realUrl.substr(0, realUrl.lastIndexOf(':'));
  127. }
  128.  
  129. if (new URL(realUrl).hostname === a.hostname ||
  130. href.match(/[?&=/]\w*([Ss]ign|[Ll]og)[io]n/)) {
  131. console.debug('Decloak skipped: assumed a login redirection.');
  132. return;
  133. }
  134.  
  135. a.hrefUndecloaked = href;
  136. a.setAttribute('href', realUrl);
  137. a.rel = 'external noreferrer nofollow noopener';
  138. return a;
  139. }
  140.  
  141. function getClosestLink(event) {
  142. return event.composedPath
  143. ? event.composedPath().find(el => el.tagName === 'A')
  144. : event.target.closest('a');
  145. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址