-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAutomatic URL Decoder.user.js
156 lines (134 loc) · 5.59 KB
/
Automatic URL Decoder.user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// ==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);
})();