Text to URL

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

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==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);