Open links in current tab

Open links in current tab regardless of _target or site's preferences. Ctrl-click: background tab, Ctrl-Shift-click: foreground tab, Shift-click: new window

当前为 2023-08-30 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Open links in current tab
  3. // @author wOxxOm
  4. // @description Open links in current tab regardless of _target or site's preferences. Ctrl-click: background tab, Ctrl-Shift-click: foreground tab, Shift-click: new window
  5. // @namespace http://target._blank.is.retarded
  6. // @version 2.2.9
  7. // @include *
  8. // @run-at document-start
  9. // @grant GM_openInTab
  10. // ==/UserScript==
  11.  
  12. if (window === top) {
  13. window.addEventListener('message', function (e) {
  14. if (!/^{/.test(e.data))
  15. return;
  16. var data = tryParse(e.data);
  17. if (data.name === GM_info.script.name)
  18. navigate(data.url);
  19. });
  20. }
  21.  
  22. var pageBase = location.href.split('#', 1)[0];
  23. var suppressing, clickedElement;
  24.  
  25. window.addEventListener('click', prevent, true);
  26. window.addEventListener('auxclick', prevent, true);
  27. window.addEventListener('mousedown', remember, true);
  28. window.addEventListener('mouseup', check, true);
  29.  
  30. function remember(e) {
  31. clickedElement = e.composedPath ? e.composedPath()[0] : e.target;
  32. }
  33.  
  34. function check(e) {
  35. var el = e.composedPath ? e.composedPath()[0] : e.target;
  36. if (e.button > 1 || el !== clickedElement)
  37. return;
  38. var a = el.closest('a');
  39. if (!a)
  40. return;
  41. blockWindowOpenAndMutations(a);
  42. var url = a.href;
  43. if (url.lastIndexOf('javascript:', 0) === 0)
  44. return;
  45. if (!url || url.split('#', 1)[0] === pageBase) {
  46. clearLinkTarget(a);
  47. return;
  48. }
  49. var b = e.button, c = e.ctrlKey, alt = e.altKey, s = e.shiftKey, m = e.metaKey;
  50. if (b === 1 || c && !alt && !m)
  51. GM_openInTab(url, !s || b === 1);
  52. else if ((window.chrome || !CSS.supports('-moz-appearance', 'none')) && b === 0 && s && !c && !alt && !m)
  53. a.cloneNode().dispatchEvent(new MouseEvent('click', {shiftKey: true}));
  54. else if (!c && !s && !m && !alt) {
  55. clearLinkTarget(a);
  56. return;
  57. } else
  58. return;
  59. suppressing = true;
  60. prevent(e);
  61. }
  62.  
  63. function clearLinkTarget(a) {
  64. if (a.target === '_blank')
  65. a.target = '';
  66. }
  67.  
  68. function prevent(e) {
  69. if (!suppressing)
  70. return;
  71. e.preventDefault();
  72. e.stopPropagation();
  73. e.stopImmediatePropagation();
  74. setTimeout(function () {
  75. suppressing = false;
  76. }, 50);
  77. }
  78.  
  79. function blockWindowOpenAndMutations(a) {
  80. var observer = new MutationObserver(function () {
  81. if (a.target === '_blank') {
  82. a.removeAttribute('target');
  83. console.log('[Open links in current tab] prevented dynamic target=_blank for', a.href);
  84. navigate(a.href);
  85. }
  86. });
  87. observer.observe(a, {attributes: true, attributeFilter: ['target'], characterData: true});
  88.  
  89. var _open = unsafeWindow.open;
  90. var timeout = setTimeout(function () {
  91. unsafeWindow.open = _open;
  92. observer.disconnect();
  93. }, 50);
  94.  
  95. unsafeWindow.open = function (url, name, opts) {
  96. let o0 = `${opts || ''}`, o1, o2, rel = [
  97. (o1 = o0.replace(/(^|,)\s*noopener\s*($|,)/gi, '')) !== o0 && 'noopener',
  98. (o2 = o1.replace(/(^|,)\s*noreferrer\s*($|,)/gi, '')) !== o1 && 'noreferrer',
  99. ];
  100. if (!o2.trim()) {
  101. console.log('[Open links in current tab] prevented window.open for', url);
  102. if (rel.some(Boolean)) a.relList.add.apply(a.relList, rel.filter(Boolean));
  103. navigate(a.href);
  104. } else
  105. _open(url, name, opts);
  106. unsafeWindow.open = _open;
  107. clearTimeout(timeout);
  108. };
  109. }
  110.  
  111. function navigate(url) {
  112. if (window === top) {
  113. var a = document.createElement('a');
  114. a.href = url;
  115. a.dispatchEvent(new MouseEvent('click'));
  116. } else
  117. top.postMessage(JSON.stringify({name: GM_info.script.name, url: url}), '*');
  118. }
  119.  
  120. function tryParse(str) {
  121. try {
  122. return JSON.parse(str);
  123. } catch (e) {}
  124. }