链接净化器

复制时自动删除URL中的中文字符干扰符

  1. // ==UserScript==
  2. // @name 链接净化器
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0.1
  5. // @description 复制时自动删除URL中的中文字符干扰符
  6. // @author Flygeon
  7. // @match *://*/*
  8. // @grant none
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // 链接清洗白名单(RFC3986标准)
  16. const urlAllowedChars = /[^\w\-._~:/?#[\]@!$&'()*+,;=%]/g;
  17.  
  18. // ================= 核心函数 =================
  19. function cleanURL(url) {
  20. return url
  21. // 删除非白名单字符
  22. .replace(urlAllowedChars, '')
  23. // 修复协议头
  24. .replace(/^(h+)(t+)(p+)(s*)(:*\/?\/?)/i, (_, h, t, p, s, sep) => {
  25. const proto = `http${s ? 's' : ''}:${sep.includes('//') ? '//' : ''}`;
  26. return proto.startsWith('http') ? proto : '';
  27. })
  28. // 移除多余斜杠
  29. .replace(/(https?:\/)\/*/g, '$1/');
  30. }
  31.  
  32. // ============== 功能1:修复页面链接 ==============
  33. function processTextNode(node) {
  34. // 跳过已处理的节点和特殊元素
  35. if (node._linkProcessed || node.parentElement.tagName === 'A') return;
  36. node._linkProcessed = true;
  37.  
  38. const text = node.textContent;
  39. // 匹配疑似被干扰的URL(宽松匹配)
  40. const urlRegex = /\b(?:h[-_\w]*t[-_\w]*t[-_\w]*p[-_\w]*s?:\/\/|www\.)[^\s\u4e00-\u9fa5]+/gi;
  41.  
  42. let newHtml = text;
  43. let match;
  44.  
  45. while ((match = urlRegex.exec(text)) !== null) {
  46. const rawUrl = match[0];
  47. const cleaned = cleanURL(rawUrl);
  48.  
  49. // 仅当清理后是有效URL时替换
  50. if (/^https?:\/\/\w+/.test(cleaned)) {
  51. newHtml = newHtml.replace(rawUrl,
  52. `<a href="${cleaned}" target="_blank" style="color:#06c;">${cleaned}</a>`);
  53. }
  54. }
  55.  
  56. if (newHtml !== text) {
  57. const wrapper = document.createElement('span');
  58. wrapper.innerHTML = newHtml;
  59. node.parentNode.replaceChild(wrapper, node);
  60. }
  61. }
  62.  
  63. // 使用MutationObserver监听动态内容
  64. const observer = new MutationObserver(mutations => {
  65. mutations.forEach(mutation => {
  66. if (mutation.addedNodes) {
  67. mutation.addedNodes.forEach(node => {
  68. if (node.nodeType === Node.TEXT_NODE) {
  69. processTextNode(node);
  70. } else if (node.nodeType === Node.ELEMENT_NODE) {
  71. node.querySelectorAll('*').forEach(el => {
  72. if (el.childNodes.length === 1 && el.firstChild.nodeType === Node.TEXT_NODE) {
  73. processTextNode(el.firstChild);
  74. }
  75. });
  76. }
  77. });
  78. }
  79. });
  80. });
  81.  
  82. // 初始处理 + 启动监听
  83. document.querySelectorAll('*').forEach(el => {
  84. el.childNodes.forEach(node => {
  85. if (node.nodeType === Node.TEXT_NODE) processTextNode(node);
  86. });
  87. });
  88. observer.observe(document.body, {
  89. childList: true,
  90. subtree: true,
  91. characterData: true
  92. });
  93.  
  94. // ============== 功能2:复制时清理 ==============
  95. document.addEventListener('copy', e => {
  96. const selection = window.getSelection().toString();
  97. const cleaned = selection.replace(/(https?:\/\/[^\s]+)/gi, (url) => {
  98. return cleanURL(url).replace(/\s/g, '');
  99. });
  100.  
  101. if (cleaned !== selection) {
  102. e.clipboardData.setData('text/plain', cleaned);
  103. e.preventDefault();
  104. }
  105. });
  106. })();

QingJ © 2025

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