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, which should be self-explanatory.

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.