https://openuserjs.org/scripts/glebm/Render_Whitespace_on_GitHub

Are they tabs or spaces? How many of them are there? Never be confused again!

Copy-paste the file below into the console to see the whitespace like this:

whitespace

Also available as a bookmarklet:

javascript:(function()%7Bfunction%20e(e%2Co%2Cr)%7Bconst%20c%3De.nodeValue.split(%22%5Ct%22).map(e%3D%3Ee.split(%22%20%22))%3Bif(1%3D%3D%3Dc.length%26%261%3D%3D%3Dc%5B0%5D.length)return%3Bconst%20a%3De.parentNode%2Cd%3Dt%3D%3E%7Ba.insertBefore(t%2Ce)%7D%3Bn(c%2Ce%3D%3E1%3D%3D%3De.length%26%26%22%22%3D%3D%3De%5B0%5D%2Ce%3D%3Ed(t(o.repeat(e)))%2Ce%3D%3En(e%2Ce%3D%3E%22%22%3D%3D%3De%2Ce%3D%3Ed(t(r.repeat(e)))%2Ce%3D%3Ed(document.createTextNode(e))))%2Ca.removeChild(e)%7Dfunction%20t(e)%7Bconst%20t%3Ddocument.createElement(%22span%22)%3Breturn%20t.textContent%3De%2Ct.style.opacity%3Dc%2Ct%7Dfunction%20n(e%2Ct%2Cn%2Co)%7Bconst%20r%3De.length%3Be.reduce((e%2Cc%2Ca)%3D%3E%7Bconst%20d%3Dt(c)%3Breturn%20d%26%26a!%3D%3Dr-1%3Fe%2B1%3A(e%3E0%26%26n(e)%2Cd%7C%7Co(c)%2C1)%7D%2C0)%7Dvar%20o%3D%22%C2%B7%22%2Cr%3D%22%E2%86%92%22%2Cc%3D.8%3B!function()%7Bfor(const%20t%20of%20document.querySelectorAll(%22table%5Bdata-tab-size%5D%22))%7Bconst%20n%3Dr.padEnd(%2Bt.dataset.tabSize)%2Cc%3Ddocument.createTreeWalker(t%2CNodeFilter.SHOW_TEXT%2C%7BacceptNode(e)%7Blet%20t%3De.parentNode%3Bfor(%3B%22TABLE%22!%3Dt.nodeName%3B)%7Bif(t.classList.contains(%22blob-code-inner%22))return%20t.firstChild!%3D%3De%7C%7C%22%20%22!%3D%3De.nodeValue%3FNodeFilter.FILTER_ACCEPT%3ANodeFilter.FILTER_SKIP%3Bt%3Dt.parentNode%7Dreturn%20NodeFilter.FILTER_SKIP%7D%7D)%2Ca%3D%5B%5D%3Bfor(%3Bc.nextNode()%3B)a.push(c.currentNode)%3Bfor(const%20t%20of%20a)e(t%2Cn%2Co)%7D%7D()%7D)()

Re: @glebm:

Also available as a bookmarklet:
Wow... That is a throwback. I haven't seen a bookmarlet in years. Before I discovered userscripts.org and installed Greasemonkey for the first time, I developed bookmarklets right in the address bar with Notepad open to backup my progress. I felt so spoiled once I started writing userscripts.

But in the future, please leave bookmarklets in the past. They are illegible, especially when encoded, and it is hard to trust what you can't read. I'm sure what you posted is just your script source encoded as a bookmarklet, but I couldn't be absolutely certain without reversing the process. But I and others who can understand JavaScript, aren't going to bother and definitely would never use the bookmarklet because we can't read it. But some non-developer novice user might, and that is a problem because if there was something malicious hiding in there it would go unreported. Other users might take the silence to mean there is nothing wrong.

Bottom line: only post code that is readable. That goes double for comments, and you should use a code block with syntax highlighting (same markdown as used on GitHub).

Re: @sizzle:

you should use a code block with syntax highlighting

@glebm did use a code fence however I added the js to see if it would highlight and it doesn't do well with bookmarklets (inspect the HTML rendered at the pre/code tags and it will have a class of lang-js if the highlighter tried) . I'm usually validating these as much as I can and created this FAQ entry for this exact reason.

I do agree that bookmarklets shouldn't be posted especially since most of the browsers are starting to or have blocked these. Maybe the beautify option on a new script can make it look pretty for validation... haven't tried yet.

Re: @glebm:

Unescaping and beautifying the bookmarklet this comes out to be:

(function () {
  function e(e, o, r) {
    const c = e.nodeValue.split("\t").map(e => e.split(" "));
    if (1 === c.length && 1 === c[0].length) return;
    const a = e.parentNode,
      d = t => {
        a.insertBefore(t, e)
      };
    n(c, e => 1 === e.length && "" === e[0], e => d(t(o.repeat(e))), e => n(e, e => "" === e, e => d(t(r.repeat(e))), e => d(document.createTextNode(e)))), a.removeChild(e)
  }

  function t(e) {
    const t = document.createElement("span");
    return t.textContent = e, t.style.opacity = c, t
  }

  function n(e, t, n, o) {
    const r = e.length;
    e.reduce((e, c, a) => {
      const d = t(c);
      return d && a !== r - 1 ? e + 1 : (e > 0 && n(e), d || o(c), 1)
    }, 0)
  }
  var o = "·",
    r = "â",
    c = .8;
  ! function () {
    for (const t of document.querySelectorAll("table[data-tab-size]")) {
      const n = r.padEnd(+t.dataset.tabSize),
        c = document.createTreeWalker(t, NodeFilter.SHOW_TEXT, {
          acceptNode(e) {
            let t = e.parentNode;
            for (;
              "TABLE" != t.nodeName;) {
              if (t.classList.contains("blob-code-inner")) return t.firstChild !== e || " " !== e.nodeValue ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
              t = t.parentNode
            }
            return NodeFilter.FILTER_SKIP
          }
        }),
        a = [];
      for (; c.nextNode();) a.push(c.currentNode);
      for (const t of a) e(t, n, o)
    }
  }()
})()

... looks like the identifiers have been mangled and the code compressed. Bordering on the fine line between legible and obfuscation.