idolpx / Reddit Base64 EnDecoder

// ==UserScript==
// @namespace    https://gist.github.com/idolpx
// @name         Reddit Base64 EnDecoder
// @version      2.1.1
// @description  Automatically base64 decode links in reddit posts
//               Encode/Decode selected text you enter when making a post
//                - Use CTRL + [SPACE] to toggle encode/decode of text entered
//                Decode selected text in page for people who post Base64 text without the trailing '=='
//                - Highlight the text in the browser and then press CTRL + [SPACE]
// @downloadURL  https://openuserjs.org/install/idolpx/Reddit_Base64_EnDecoder.user.js
// @updateURL    https://openuserjs.org/meta/idolpx/Reddit_Base64_EnDecoder.meta.js
// @author       Jaime Idolpx
// @attribution  Based on original code by DanielBlaze (https://openuserjs.org/users/DanielBlaze)
// @copyright    2020, idolpx (https://openuserjs.org/users/idolpx)
// @license      MIT
// @match        https://*.reddit.com/r/*
// @grant        none
// @require      http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js
// @require      https://gist.github.com/raw/2625891/waitForKeyElements.js
// ==/UserScript==


waitForKeyElements ("div[data-editor], textarea[placeholder]", main);

function create_links(text) {
  return (text || "").replace(
    /([^\S]|^)(((https?\:\/\/)|(www\.))(\S+))/gi,
    function(match, space, url){
      var hyperlink = url;
      if (!hyperlink.match('^https?:\/\/')) {
        hyperlink = 'http://' + hyperlink;
      }

      // Secure links = underline in blue. Insecure links = warning image + underlined in red
      if (hyperlink.startsWith('https://')) {
          return space + `<a style="border-bottom: 1px dotted #036;" target="_blank" href="${hyperlink}">${url}</a>`;
      } else if (hyperlink.startsWith('http://')) {
          return space + `<img style="vertical-align: middle;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAbwAAAG8B8aLcQwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAIVSURBVDiNhZNLaBNRFIb/OzNJmzakqQhqjFptrK1BIfRBJ1GDjzQ0kZiImdDSIqmKC1clQiDFR6QqSJaCC0UXUhdZim7UUgoiuBQ3GomaVMVHA2LRJE5mjgtpMZlRz+7+5z/fOfdwLyMi6EUknrZW5Wq09rOy22DgztyfyRT0fEwPEBhLW0DVJ4M9n50OyyJuzndUiTd3zman3zd6Bd32VE0eEovOk8EXwGtgY3u56cJdZxaAp9HKNQpDI0lba5M8ObI3v6J57CWsav3h9h1JuP4LEBh/Pup9a7K0yLj3dAMuPuwBU4GzoZcA6M4/Af7RVPdqa2UiLP7e1+1HDsy9agcAbLcuYeuar91DsUTgrwAe7NL4/jxvNKgAAJXYH3sBpgI5kKpe1wUMj6bEjrVLkQOuD41TrsT65go8ji82XyxxQgNgjF2Z8OfAmP67WI7JfXlAUTJ1gOHxVGjnltKuvq5FnRJWd2rjZBzue2fxSYlpAOAkSeI5wuVj/pymNDiwAOe67xo9PlAEU5XTkiQZ+U2ucNy74+PxkFjUGLvs3+C2ltBiVOp0AQRzmyw8LnTaORCdiu55o3vf5I1+HJ3p1c0Ft30C1WoxAQw2U0OH5Qi7C3j+zKyb4xiBQM0CVFybutWbDolFGA1aUKR/AYWyqU6TFQ5X5zaDMX6eERGCY6mDBDYI0n4uWS57dQZQGLjZB9nMuV9Nv60tCtUXugAAAABJRU5ErkJggg==" />&nbsp; <a style="border-bottom: 1px dotted #f00;" target="_blank" href="${hyperlink}">${url}</a>`;
      }
    }
  );
};

function decode_links() {
    $('div[theme] p').each(function(i) {
        var content = $(this).text();

        if(content.endsWith('=')){

            // split on the words we find, and make sure we have something before continuing
            const words = content.split(' ');
            if (words.length === 0)
                return;

            // decode the string
            const transformed = words.map(word => {
                try {
                    const decoded = atob(word);
                    const links = create_links(decoded);
                    return links.replace(/(?:\r\n|\r|\n)/g, '<br>');
                } catch (_) {
                }

                return word;
            });

            var url = transformed.join(' ');
            $(this).html(url);
        }
    });
}

function endecode_text() {
    $('div[data-contents] span[data-text]').each(function(index, value) {
        var content = $(this).text();

        if(content.endsWith('=')) {
           $(this).text(atob(content));
        } else {
           $(this).text(btoa(content));
        }
        console.log(`div${index}: ${this.id} {content}`);
    });
}

function decode_selection() {
    var range = document.getSelection().getRangeAt(0);
    var content = range.toString();
    if(content.length) {
        console.log(content);
        const decoded = atob(content);  // Decode Base64
        const links = create_links(decoded);  // Create Links

        var newNode = document.createElement('span');
        newNode.innerHTML = links.replace(/(?:\r\n|\r|\n)/g, '<br>'); // Replace CR/LF

        range.deleteContents();
        range.insertNode(newNode);
    }
}

function main() {
    // Automatically decode any Base64 text it finds on the page
    decode_links()

    // Setup encode key combination
    var ctrlPressed = false; //Variable to check if the the first button is pressed at this exact moment
    $(document).keydown(function(e) {
      if (e.ctrlKey) { //If it's ctrl key
        ctrlPressed = true; //Set variable to true
      }
    }).keyup(function(e) { //If user releases ctrl button
      if (e.ctrlKey) {
        ctrlPressed = false; //Set it to false
      }
    }); //This way you know if ctrl key is pressed. You can change e.ctrlKey to any other key code you want

    $(document).keydown(function(e) { //For any other keypress event
      if (e.which == 32) { //Checking if it's space button
        if(ctrlPressed == true){ //If it's space, check if ctrl key is also pressed

            // Encode/Decode selected text you enter when making a post
            // - Use CTRL + [SPACE] to toggle encode/decode of text entered
            endecode_text();

            // Decode selected text in page for people who post Base64 text without the trailing '=='
            // - Highlight the text in the browser and then press CTRL + [SPACE]
            decode_selection();

            ctrlPressed = false; //Important! Set ctrlPressed variable to false. Otherwise the code will work everytime you press the space button again
        }
      }
    })
}