Greasy Fork 支持简体中文。

Open Links in NEW BACKGROUND Tab

Open links (esp. from different domains) in NEW BACKGROUND tab with normal left click

  1. // ==UserScript==
  2. // @name Open Links in NEW BACKGROUND Tab
  3. // @author Jerry
  4. // @description Open links (esp. from different domains) in NEW BACKGROUND tab with normal left click
  5. // @grant GM_openInTab
  6. // @include http*://*
  7. // @namespace https://greasyfork.org/users/28298
  8. // @run-at document-start
  9. // @version 1.2
  10. // @license GNU GPLv3
  11. // @homepage https://greasyfork.org/en/scripts/457107/versions/new
  12. // ==/UserScript==
  13.  
  14.  
  15. // source:
  16. // https://greasyfork.org/en/scripts/20694-all-links-open-all-in-new-background-tab/code
  17. // https://greasyfork.org/en/scripts/12367-open-links-in-new-tab/code
  18.  
  19. attachHandler([].slice.call(document.getElementsByTagName('a')));
  20.  
  21. setMutationHandler(document, 'a', function(nodes) {
  22. attachHandler(nodes);
  23. return true;
  24. });
  25.  
  26.  
  27. // Not use anymore, has been fixed by cond2 below
  28. // https://stackoverflow.com/a/26269087/2292993
  29. // ugly fix duckduckgo rightside bar search results
  30. // // document.addEventListener ("DOMContentLoaded", FnReady);
  31. // window.addEventListener ("load", FnReady);
  32. // function FnReady () {
  33. // if (location.href.startsWith('https://duckduckgo.com')) {
  34. // var nodes = document.getElementsByTagName('a');
  35. // for (var i=0; i<nodes.length; i++) {
  36. // var node = nodes[i];
  37. // if (node.className.includes("js-about-item")) {
  38. // node.replaceWith(node.cloneNode(true));
  39. // }
  40. // }
  41. // }
  42. // }
  43.  
  44. function attachHandler(nodes) {
  45. nodes.forEach(function(node) {
  46. // href will be auto expanded with window.location.origin, such as href="#" --> "https://example.com/#"
  47. var cond = (node.target != '_blank') && (node.href) && (!node.href.includes("javascript:")) && (!node.href.startsWith(window.location.origin));
  48. // Exclusion list from modification by this script to prevent malfunction of the original website:
  49. cond = cond && (node.className!="search-engine-a"); // search-engine-a: more compatible with Search Engine Switcher script
  50. cond = cond && (!node.className.includes("s-topbar--item")); // stackoverflow
  51. cond = cond && (!location.href.startsWith("https://app.raindrop.io"));
  52. cond = cond && (!location.href.startsWith("https://portal.discover.com"));
  53. cond = cond && (!node.href.startsWith("https://accounts.google.com")); // gdrive
  54. cond = cond && (!node.href.startsWith("https://identity.walmart.com")); // walmart login
  55. cond = cond && (!new URL(node.href).hostname.endsWith("fidelity.com")); // fidelity website
  56. cond = cond && (!node.href.startsWith("https://ttp.cbp.dhs.gov"));
  57. if ( cond ) {
  58. node.onclick = clickHandler;
  59. node.addEventListener('click', clickHandler);
  60.  
  61. }
  62. // to avoid circular loop (that I do not fully understand how it would happen)
  63. cond2 = cond && (!node.hasAttribute('processed'));
  64. // ddg right sidebar search result or image search result
  65. cond2 = cond2 && ( node.className.includes("js-about-item") || (cond && location.href.startsWith("https://duckduckgo.com") && location.href.includes("&ia=images")) );
  66. if ( cond2 ) {
  67. node.setAttribute("processed", "true");
  68. node.replaceWith(node.cloneNode(true));
  69. }
  70. });
  71. }
  72.  
  73. /*
  74. https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
  75. MouseEvent.buttons
  76. 0: No button or un-initialized
  77. 1: Primary button (usually the left button)
  78. 2: Secondary button (usually the right button)
  79. 4: Auxiliary button (usually the mouse wheel button or middle button)
  80. 8: 4th button (typically the "Browser Back" button)
  81. 16 : 5th button (typically the "Browser Forward" button)
  82.  
  83. https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
  84. KeyboardEvent.metaKey
  85. e.metaKey
  86.  
  87. https://stackoverflow.com/a/35936912/2292993
  88. e is short for event
  89. the element on which you clicked (event.target)
  90.  
  91. https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation
  92. The stopPropagation() method of the Event interface prevents further propagation of the current event in the capturing and bubbling phases. It does not, however, prevent any default behaviors from occurring; for instance, clicks on links are still processed. If you want to stop those behaviors, see the preventDefault() method. It also does not prevent immediate propagation to other event-handlers. If you want to stop those, see stopImmediatePropagation().
  93. */
  94. function clickHandler(e) {
  95. if (e.button > 1)
  96. return;
  97. e.preventDefault();
  98. e.stopPropagation();
  99. e.stopImmediatePropagation();
  100. // GM_openInTab(this.href, e.button || e.ctrlKey);
  101. GM_openInTab(this.href, true); // open in background
  102. }
  103.  
  104.  
  105.  
  106.  
  107. // https://greasyfork.org/scripts/12228/code/setMutationHandler.js
  108. // ==UserScript==
  109. // @name setMutationHandler
  110. // @description MutationObserver wrapper to wait for the specified CSS selector
  111. // @namespace wOxxOm.scripts
  112. // @author wOxxOm
  113. // @grant none
  114. // @version 3.0.2
  115. // ==/UserScript==
  116.  
  117. function setMutationHandler(target, selector, handler, options) {
  118. // or setMutationHandler(selector, handler, options) {
  119. // or setMutationHandler(options) {
  120. if (typeof arguments[0] == 'string') {
  121. options = arguments[2] || {};
  122. handler = arguments[1];
  123. selector = arguments[0];
  124. target = document;
  125. } else if (arguments.length == 1 && target && typeof target.handler == 'function') {
  126. options = arguments[0];
  127. handler = options.handler;
  128. selector = options.selector;
  129. target = options.target || document;
  130. } else if (!(target instanceof Node)) {
  131. throw 'Bad params for setMutationHandler.\n' +
  132. 'A: [optional Node] target, [String] selector, [Function] handler, [optional Object] options\n' +
  133. 'B: [Object] options\n' +
  134. 'Options: target, selector, handler, processExisting, childList, attributes, characterData, subtree, attributeOldValue, characterDataOldValue, attributeFilter';
  135. } else
  136. options = options || {};
  137.  
  138. if (options.processExisting && target.querySelector(selector))
  139. handler.call(null, Array.prototype.slice.call(target.querySelectorAll(selector)));
  140. if (!options.attributes && !options.characterData && !options.childList && options.subtree === undefined)
  141. options.childList = options.subtree = true;
  142.  
  143. var cb;
  144. if (/^#[\w\d-]+$/.test(selector)) {
  145. selector = selector.substr(1);
  146. cb = MOhandlerForId;
  147. } else {
  148. cb = MOhandler;
  149. }
  150. var observer = new MutationObserver(cb);
  151. observer.observe(target, options || {subtree:true, childList:true});
  152. return observer;
  153.  
  154. function MOhandler(mutations) {
  155. if (mutations.length > 100 && !document.querySelector(selector))
  156. return;
  157. var found = [];
  158. for (var i=0, m; (m = mutations[i++]); ) {
  159. switch (m.type) {
  160. case 'childList':
  161. var nodes = m.addedNodes, nl = nodes.length;
  162. var textNodesOnly = true;
  163. for (var j=0; j < nl; j++) {
  164. var n = nodes[j];
  165. textNodesOnly &= n.nodeType == 3; // TEXT_NODE
  166. if (n.nodeType != 1) // ELEMENT_NODE
  167. continue;
  168. if (n.matches(selector))
  169. found.push(n);
  170. else if (n.querySelector(selector)) {
  171. n = n.querySelectorAll(selector);
  172. if (n.length < 1000)
  173. found.push.apply(found, n);
  174. else
  175. found = found.concat(found.slice.call(n));
  176. }
  177. }
  178. if (textNodesOnly && m.target.matches(selector))
  179. found.push(m.target);
  180. break;
  181. case 'attributes':
  182. if (m.target.matches(selector))
  183. found.push(m.target);
  184. break;
  185. case 'characterData':
  186. if (m.target.parentNode && m.target.parentNode.matches(selector))
  187. found.push(m.target.parentNode);
  188. break;
  189. }
  190. }
  191. if (!found.length)
  192. return;
  193. if (handler.call(observer, found) === false)
  194. observer.disconnect();
  195. }
  196.  
  197. function MOhandlerForId(mutations) {
  198. var el = document.getElementById(selector);
  199. if (el && target.contains(el))
  200. if (handler.call(observer, [el]) === false)
  201. observer.disconnect();
  202. }
  203. }
  204.