fuck t.co

I hate click through trackers, so I created a Firefox extension that circumvents t.co on Twitter.

Click to install. Source is available on GitHub under the BITE ME™ license (in which nobody is permitted to use the software, including myself as the author, and its use is an act of intentional disobedience).

Why bother for such an inconsequential tweak? Because I know that people desperately need to reclaim control over the Internet for themselves, and I know that I want to be a part of the movement that helps them do that, and most importantly I know that I lack the patience and courage to participate constructively as a tool builder in that movement.

So I chose to be a saboteur instead.

How it works

On the classic Twitter web UI, this function is attached as a click handler. It catches URLs that get auto-linked in tweets, as well as the linked homepage field of a profile card.

function classicListener(event) {
    var maybeLink = event.target.nodeName === "A"
        ? event.target
        : event.target.parentElement;
    if (maybeLink && maybeLink.classList.contains("twitter-timeline-link")) {
        maybeLink.setAttribute("href", maybeLink.dataset.expandedUrl);
    } else if (maybeLink.relList && maybeLink.relList.contains("me")) {
        // This branch catches website links in a Twitter profile card.
        maybeLink.setAttribute("href", `https://${maybeLink.textContent.trim()}`);
        return;
    }
}

On the mobile Twitter web UI, which I am led to believe is the same as the "new" Twitter UI as of 2019, this function is attached as a click handler. There is less structured data to go on here, so it works by testing if the title attribute of a link is a well formed URL.

function mobileListener(event) {
    var maybeLink = event.target.nodeName === "A"
        ? event.target
        : event.target.parentElement;
    if (maybeLink && maybeLink.nodeName === "A") {

        // This branch catches website links in a Twitter profile card.
        if (maybeLink.parentElement.getAttribute("data-testid") === "UserProfileHeader_Items") {
            maybeLink.setAttribute("href", `http://${maybeLink.textContent.trim()}`);
            return;
        }

        // If the title is URL-like, then it's the address we want to rewrite.
        try {
            var url = new URL(maybeLink.getAttribute("title"));
            if (url) {
                maybeLink.setAttribute("href", url.toString());
            }
        } catch (_) {}
    }
}

Both listeners are attached to document.body for different reasons. On mobile.twitter.com, the page is just a React app, which loads its content asynchronously, so a more specific element doesn't exist by the time the extension loads. Similarly on classic Twitter web, if you visit a tweet URL directly, the page will load without the list of tweets, and won't load them until the Tweet popup is dismissed.

Limitations

This won't work on Twitter cards, those rich content links that are contained in a little rectangular pill, because the original URL data is not present in the markup.