🏠 Home 

Text to URL

Конвертирует текст в виде ссылок в реальные ссылки, на которые можно кликнуть.

// ==UserScript==
// @name Text to URL
// @namespace https://github.com/T1mL3arn
// @author T1mL3arn
// @description:ru Конвертирует текст в виде ссылок в реальные ссылки, на которые можно кликнуть.
// @description:en Converts url-like text into clickable url.
// @match *://*/*
// @version 1.1.1
// @run-at document-end
// @license GPLv3
// @supportURL https://greasyfork.org/en/scripts/367955-text-to-url/feedback
// @homepageURL https://greasyfork.org/en/scripts/367955-text-to-url
// @description Конвертирует текст в виде ссылок в реальные ссылки, на которые можно кликнуть.
// ==/UserScript==
///TODO improve ereg to match URI syntax (https://en.wikipedia.org/wiki/Uniform_Resource_Identifier#Generic_syntax) ?
let linkEreg = /(?:https|http|ftp|file):\/\/.+?(?=[,.]?(?:\s|$))/gi;
let linkEregLocal = /(?:https|http|ftp|file):\/\/.+?(?=[,.]?(?:\s|$))/i;
let obsOptions = { childList: true, subtree: true };
let wrappedCount = 0;
function printWrappedCount() {
if (wrappedCount > 0) {
console.info(`[ ${GM_info.script.name} ] wrapped links count: ${wrappedCount}`);
}
}
let obs = new MutationObserver((changes, obs) => {
wrappedCount = 0;
obs.disconnect();
changes.forEach((change) => change.addedNodes.forEach((node) => fixLinks(node)) );
obs.observe(document.body, obsOptions);
printWrappedCount();
});
function fixLinks(node) {
///TODO consider not to run script for form and input elements!
///TODO also search syntax-highlith libraries and also exclude them
if (node.tagName != 'A' && node.tagName != 'SCRIPT') {
// this is a text node
if (node.nodeType === 3) {
let content = node.textContent;
if (content && content != '') {
if (linkEregLocal.test(content)) {
wrapTextNode(node);
}
}
} else if (node.childNodes && node.childNodes.length > 0) {
node.childNodes.forEach(fixLinks);
}
}
}
function wrapTextNode(node) {
let match;
let sibling = node;
let content = node.textContent;
linkEreg.lastIndex = 0;
while ((match = linkEreg.exec(content)) != null) {
let fullMatch = match[0];
let anchor = document.createElement('a');
let range = document.createRange();
range.setStart(sibling, linkEreg.lastIndex - match[0].length);
range.setEnd(sibling, linkEreg.lastIndex);
range.surroundContents(anchor);
wrappedCount++;
anchor.href = fullMatch;
anchor.textContent = fullMatch;
anchor.target = '_blank';
anchor.title = 'open link in a new tab';
anchor.setAttribute('ttu-wrapped', '1');
linkEreg.lastIndex = 0;
sibling = getNextTextSibling(anchor);
if (sibling == null)
break;
else
content = sibling.textContent;
}
}
function getNextTextSibling(node) {
let next = node.nextSibling;
while (next != null) {
if (next.nodeType == 3)
return next;
else
next = node.nextSibling;
}
return null;
}
fixLinks(document.body);
printWrappedCount();
obs.observe(document.body, obsOptions);