您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Декодирует все найденные на странице ссылки, похожие на "%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%BC%D0%B8%D1%80", в удобочитаемое "привет мир"
- // ==UserScript==
- // @name Automatic URL Decoder
- // @namespace https://github.com/T1mL3arn
- // @author T1mL3arn
- // @description:ru Декодирует все найденные на странице ссылки, похожие на "%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%BC%D0%B8%D1%80", в удобочитаемое "привет мир"
- // @description:en It decodes all percent-encoded links on current page.
- // @match *://*/*
- // @exclude-match https://github.com/t1ml3arn-userscript-js/Automatic-URL-Decoder
- // @exclude https://github.com/t1ml3arn-userscript-js/Automatic-URL-Decoder
- // @exclude-match https://greasyfork.org/en/scripts/40305-automatic-url-decoder
- // @exclude https://greasyfork.org/en/scripts/40305-automatic-url-decoder
- // @version 2.1.2
- // @run-at document-end
- // @license GPLv3
- // @supportURL https://github.com/T1mL3arn/Automatic-URL-Decoder/issues
- // @homepageURL https://github.com/T1mL3arn/Automatic-URL-Decoder
- // @description Декодирует все найденные на странице ссылки, похожие на "%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%BC%D0%B8%D1%80", в удобочитаемое "привет мир"
- // ==/UserScript==
- (() => {
- const DECODED_ELT_CLASS = 'auto-url-decoder-s739';
- // Predefined css styles for decoded links //
- // To change css find `decodedNodeCSS` variable
- // and set one of the style below to it.
- // Set `null` to `decodedNodeCSS` to disable styling.
- const underlineCss = `
- .${DECODED_ELT_CLASS} {
- border-bottom: 2px solid currentColor !important;
- margin-bottom: -2px !important;
- }`;
- const CSS_greenBackground = getDefaultBackgroundCSS('#deffc3');
- const CSS_redBackground = getDefaultBackgroundCSS('#fcd7d7');
- const CSS_blueBackground = getDefaultBackgroundCSS('#d7ebfc');
- // ----------- //
- function addStyle(css) {
- const id = 'auto-url-decoder-style-elt';
- const style = document.getElementById(id) || document.head.appendChild(document.createElement('style'));
- style.id = id;
- style.textContent = css;
- }
- function getDefaultBackgroundCSS(color) {
- return `
- .${DECODED_ELT_CLASS} {
- background-color: ${color} !important;
- padding: 2px 2px !important;
- margin: 0 -2px !important;
- }`;
- }
- function fixLinks(node) {
- if (node.nodeType === 3) {
- let content = node.textContent;
- if (content != '') {
- if (linkEregLocal.test(content) && percentEncodingEreg.test(content)) {
- if (decodedNodeCSS != null) replaceAndStyleLink(node);
- else {
- try {
- let decoded = content.replace(linkEreg, decodeURIComponent);
- if (decoded.length != content.length) node.textContent = decoded;
- } catch (e) {
- // URI mailformed, hust skip it
- }
- }
- }
- }
- } else if (node.nodeType === 1 && node.childNodes.length > 0 && isElementAllowed(node)) {
- node.childNodes.forEach(fixLinks);
- }
- }
- function isElementAllowed(elt) {
- return blockedTagsList.indexOf(elt.tagName) == -1 && !elt.matches(blockedClassesSelector);
- }
- function replaceAndStyleLink(node) {
- let match;
- let sibling = node;
- let content = node.textContent;
- while ((match = linkEreg.exec(content)) != null) {
- let fullMatch = match[0];
- let decoded;
- try {
- decoded = decodeURIComponent(fullMatch);
- } catch (e) {
- content = content.substring(linkEreg.lastIndex);
- linkEreg.lastIndex = 0;
- continue;
- }
- if (decoded.length != fullMatch.length) {
- let span = document.createElement('span');
- span.classList.add(DECODED_ELT_CLASS);
- if (storeOriginalURL) span.dataset.urlDecOriginalUrl = fullMatch;
- let range = document.createRange();
- range.setStart(sibling, linkEreg.lastIndex - match[0].length);
- range.setEnd(sibling, linkEreg.lastIndex);
- range.surroundContents(span);
- content = content.substring(linkEreg.lastIndex);
- span.textContent = decoded;
- linkEreg.lastIndex = 0;
- counter++;
- sibling = getNextTextSibling(span);
- if (sibling == null) break;
- }
- }
- }
- function getNextTextSibling(node) {
- let next = node.nextSibling;
- while (next != null) {
- if (next.nodeType == 3) return next;
- else next = node.nextSibling;
- }
- return null;
- }
- let linkEreg = /(?:[a-z][a-z0-9-+.]+:\/\/|www\.).+?(?=\s|$)/gi;
- let linkEregLocal = /(?:[a-z][a-z0-9-+.]+:\/\/|www\.).+?(?=\s|$)/i;
- let percentEncodingEreg = /%[a-f0-9]{2}/i;
- let obsOptions = { childList: true, subtree: true };
- let blockedTagsList = 'NOSCRIPT OPTION SCRIPT STYLE TEXTAREA SVG CANVAS BUTTON SELECT TEMPLATE METER PROGRESS MATH TIME HEAD CODE PRE'.split(' ');
- ///NOTE Use 'foo' (or any other dummy class) in this selector
- // if you need to make this variable "empty"
- // It allows to avoid SyntaxError: '' is not a valid selector
- let blockedClassesSelector = `${DECODED_ELT_CLASS}`.split(' ').map(class_ => `.${class_}`).join(', ');
- let counter = 0;
- let storeOriginalURL = false;
- let decodedNodeCSS = CSS_greenBackground;
- let obs = new MutationObserver((changes, obs) => {
- counter = 0;
- obs.disconnect();
- changes.forEach((change) => change.addedNodes.forEach((node) => fixLinks(node)) );
- obs.observe(document.body, obsOptions);
- //console.log('[ URL Decoder ] Decoded: ', counter);
- });
- if (decodedNodeCSS != null) addStyle(decodedNodeCSS);
- counter = 0;
- fixLinks(document.body);
- //console.log('[ URL Decoder ] Decoded: ', counter);
- obs.observe(document.body, obsOptions);
- })();