Greasy Fork is available in English.

Automatic URL Decoder

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

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 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);
})();