Automatic URL Decoder

Декодирует все найденные на странице ссылки, похожие на "%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%BC%D0%B8%D1%80", в удобочитаемое "привет мир"

目前為 2018-10-18 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Automatic URL Decoder
  3. // @namespace https://github.com/T1mL3arn
  4. // @author T1mL3arn
  5. // @description:ru Декодирует все найденные на странице ссылки, похожие на "%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%BC%D0%B8%D1%80", в удобочитаемое "привет мир"
  6. // @description:en It decodes all percent-encoded links on current page.
  7. // @match *://*/*
  8. // @exclude-match https://github.com/t1ml3arn-userscript-js/Automatic-URL-Decoder
  9. // @exclude https://github.com/t1ml3arn-userscript-js/Automatic-URL-Decoder
  10. // @exclude-match https://greasyfork.org/en/scripts/40305-automatic-url-decoder
  11. // @exclude https://greasyfork.org/en/scripts/40305-automatic-url-decoder
  12. // @version 2.1.1
  13. // @run-at document-end
  14. // @license GPLv3
  15. // @supportURL https://github.com/T1mL3arn/Automatic-URL-Decoder/issues
  16. // @homepageURL https://github.com/T1mL3arn/Automatic-URL-Decoder
  17. // @description Декодирует все найденные на странице ссылки, похожие на "%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%BC%D0%B8%D1%80", в удобочитаемое "привет мир"
  18. // ==/UserScript==
  19.  
  20. (() => {
  21. const DECODED_ELT_CLASS = 'auto-url-decoder-s739';
  22. const underlineCss = `
  23. .${DECODED_ELT_CLASS} {
  24. border-bottom: 2px solid currentColor !important;
  25. margin-bottom: -2px !important;
  26. }`;
  27. const CSS_greenBackground = getDefaultBackgroundCSS('#deffc3');
  28. const CSS_redBackground = getDefaultBackgroundCSS('#fcd7d7');
  29. const CSS_blueBackground = getDefaultBackgroundCSS('#d7ebfc');
  30.  
  31. function addStyle(css) {
  32. const id = 'auto-url-decoder-style-elt';
  33. const style = document.getElementById(id) || document.head.appendChild(document.createElement('style'));
  34. style.id = id;
  35. style.textContent = css;
  36. }
  37.  
  38. function getDefaultBackgroundCSS(color) {
  39. return `
  40. .${DECODED_ELT_CLASS} {
  41. background-color: ${color} !important;
  42. padding: 2px 2px !important;
  43. margin: 0 -2px !important;
  44. }`;
  45. }
  46.  
  47. function fixLinks(node) {
  48. if (node.nodeType === 3) {
  49. let content = node.textContent;
  50. if (content != '') {
  51. if (linkEregLocal.test(content) && percentEncodingEreg.test(content)) {
  52. if (decodedNodeCSS != null) replaceAndStyleLink(node);
  53. else {
  54. try {
  55. let decoded = content.replace(linkEreg, decodeURIComponent);
  56. if (decoded.length != content.length) node.textContent = decoded;
  57. } catch (e) {
  58. // URI mailformed, hust skip it
  59. }
  60. }
  61. }
  62. }
  63. } else if (node.nodeType === 1 && node.childNodes.length > 0 && isElementAllowed(node)) {
  64. node.childNodes.forEach(fixLinks);
  65. }
  66. }
  67. function isElementAllowed(elt) {
  68. return blockedTagsList.indexOf(elt.tagName) == -1 && !elt.matches(blockedClassesSelector);
  69. }
  70.  
  71. function replaceAndStyleLink(node) {
  72. let match;
  73. let sibling = node;
  74. let content = node.textContent;
  75. while ((match = linkEreg.exec(content)) != null) {
  76. let fullMatch = match[0];
  77. let decoded;
  78.  
  79. try {
  80. decoded = decodeURIComponent(fullMatch);
  81. } catch (e) {
  82. content = content.substring(linkEreg.lastIndex);
  83. linkEreg.lastIndex = 0;
  84. continue;
  85. }
  86.  
  87. if (decoded.length != fullMatch.length) {
  88. let span = document.createElement('span');
  89. span.classList.add(DECODED_ELT_CLASS);
  90.  
  91. if (storeOriginalURL) span.dataset.urlDecOriginalUrl = fullMatch;
  92. let range = document.createRange();
  93. range.setStart(sibling, linkEreg.lastIndex - match[0].length);
  94. range.setEnd(sibling, linkEreg.lastIndex);
  95. range.surroundContents(span);
  96. content = content.substring(linkEreg.lastIndex);
  97. span.textContent = decoded;
  98. linkEreg.lastIndex = 0;
  99. counter++;
  100.  
  101. sibling = getNextTextSibling(span);
  102. if (sibling == null) break;
  103. }
  104. }
  105. }
  106. function getNextTextSibling(node) {
  107. let next = node.nextSibling;
  108. while (next != null) {
  109. if (next.nodeType == 3) return next;
  110. else next = node.nextSibling;
  111. }
  112. return null;
  113. }
  114.  
  115. let linkEreg = /(?:[a-z][a-z0-9-+.]+:\/\/|www\.).+?(?=\s|$)/gi;
  116. let linkEregLocal = /(?:[a-z][a-z0-9-+.]+:\/\/|www\.).+?(?=\s|$)/i;
  117. let percentEncodingEreg = /%[a-f0-9]{2}/i;
  118. let obsOptions = { childList: true, subtree: true };
  119. let blockedTagsList = 'NOSCRIPT OPTION SCRIPT STYLE TEXTAREA SVG CANVAS BUTTON SELECT TEMPLATE METER PROGRESS MATH TIME HEAD CODE PRE'.split(' ');
  120. ///NOTE Use 'foo' (or any other dummy class) in this selector
  121. // if you need to make this variable "empty"
  122. // It allows to avoid SyntaxError: '' is not a valid selector
  123. let blockedClassesSelector = `${DECODED_ELT_CLASS}`.split(' ').map(class_ => `.${class_}`).join(', ');
  124. let counter = 0;
  125. let storeOriginalURL = false;
  126.  
  127. // set one of style from above to enable
  128. // custom style for fixed links
  129. let decodedNodeCSS = CSS_greenBackground;
  130. let obs = new MutationObserver((changes, obs) => {
  131. counter = 0;
  132. obs.disconnect();
  133. changes.forEach((change) => change.addedNodes.forEach((node) => fixLinks(node)) );
  134. obs.observe(document.body, obsOptions);
  135. //console.log('[ URL Decoder ] Decoded: ', counter);
  136. });
  137. if (decodedNodeCSS != null) addStyle(decodedNodeCSS);
  138. counter = 0;
  139. fixLinks(document.body);
  140. //console.log('[ URL Decoder ] Decoded: ', counter);
  141. obs.observe(document.body, obsOptions);
  142. })();