Replace shortened URLs with real URLs
// ==UserScript==
// @name FuckShortenedUrl
// @namespace https://greasyfork.org/en/users/173996-wtfjs
// @version 0.25
// @description Replace shortened URLs with real URLs
// @author wtfjs
// @license MIT
// @run-at document-start
// @match https://*/*
// @match http://*/*
// @grant none
// ==/UserScript==
;(function __FuckShortenedUrl() {
"use strict";
var utils = {
$: function $(selector, parent) {
var _parent = parent || document
return _parent.querySelector(selector)
},
$$: function $$(selector, parent) {
var _parent = parent || document
return _parent.querySelectorAll(selector)
},
forEachRight: function forEachRight(arr, fn) {
for (var i = (arr.length >>> 0) - 1; i >= 0; i -= 1) {
var it = arr[i]
var res = fn(it, i, arr)
if (res === false) {
break;
}
}
},
concurrent: function concurrent(job) {
var dispatch = window.requestIdleCallback || window.requestAnimationFrame || window.setTimeout
dispatch(job)
},
isElement: function isElement(any) {
return typeof any === "object" && any.nodeType === 1 // Node.ELEMENT_NODE
},
getSearchParams: function getSearchParams(urlStr) {
try {
return new URL(urlStr).searchParams
} catch (e) {}
return null
},
findParent: function findParent(el, elOrMatchFn) {
var matchFn = typeof elOrMatchFn === "function" ? elOrMatchFn : function matchFn(it) {
return it === elOrMatchFn
}
var node = el.parentElement
while (node !== null) {
if (matchFn(node)) {
return node
}
node = node.parentElement
}
return null
}
}
var handlers = [
[
/https?:\/\/link\.zhihu\.com/,
function transform($link) {
var searchParams = utils.getSearchParams($link.href)
if (searchParams && searchParams.get("target")) {
var realHref = searchParams.get("target")
$link.href = realHref
return realHref
}
}
],
[
/https?:\/\/douc\.cc/,
function transform($link) {
if (/https?:\/\//.test($link.title)) {
var realHref = $link.title
$link.textContent = $link.textContent.replace($link.href, realHref)
$link.href = realHref
return realHref
}
}
],
[
/https?:\/\/(www.)?douban\.com\/link2/,
function transform($link) {
var searchParams = utils.getSearchParams($link.href)
if (searchParams && searchParams.get("url")) {
var realHref = searchParams.get("url")
$link.href = realHref
return realHref
}
}
],
[
/https?:\/\/t\.co/,
function transform($link) {
var $cardWrapper = utils.findParent($link, function(node) {
return node.dataset.testid === "card.wrapper"
})
var isCardLink = !!$cardWrapper
var realHref
if (isCardLink) {
try {
var $textContent = $cardWrapper.parentElement.parentElement.parentElement.previousElementSibling
var $textLink = utils.$("a[href^=http]", $textContent)
if ($textLink) {
realHref = getTextLinkHref($textLink)
$link.href = realHref
return realHref
}
} catch (e) {}
} else {
realHref = getTextLinkHref($link)
$link.href = realHref
return realHref
}
function getTextLinkHref($el) {
return $el.textContent.replace(/((\.\.\.)|(…))$/, '')
}
}
],
[
/https?:\/\/(www\.)?youtube\.com\/redirect\?/,
function transform($link) {
var searchParams = utils.getSearchParams($link.href)
if (searchParams && searchParams.get("q")) {
var realHref = searchParams.get("q")
$link.href = realHref
return realHref
}
}
]
]
var REDIRECT_URL_SELECTORS = [
"t.co","link.zhihu.com","douc.cc","douban.com/link2",
"youtube.com/redirect"
]
.map(function(url) {return 'a[href*="' + url + '/"]'})
.join(",")
function selectLinks(parent) {
return utils.$$(REDIRECT_URL_SELECTORS, parent)
}
function transformLinks($links, cache) {
utils.forEachRight($links, function($link) {
utils.concurrent(function transform() {
if (!$link || !$link.href) return
var href = $link.href
var cachedHref = cache[href]
if (cachedHref) {
if (href !== cachedHref) {
$link.href = cachedHref
}
return
}
utils.forEachRight(handlers, function(handler) {
var pattern = handler[0]
var fn = handler[1]
if (pattern.test(href)) {
var res = fn($link)
if (res) {
cache[href] = res
}
return false
}
})
})
})
}
function onDomMutation(mutationList) {
var cache = {}
utils.forEachRight(mutationList, function(mutation) {
utils.concurrent(function () {
var target = mutation.target
if (!target || !utils.isElement(target)) return
var $newLinks = []
utils.forEachRight(selectLinks(target), function addNewLink($link) {
$newLinks.push($link)
})
transformLinks($newLinks, cache)
})
})
}
function start() {
console.log("----------- FuckShortenedUrl -------------")
transformLinks(selectLinks(), {})
var observer = new MutationObserver(onDomMutation)
observer.observe(document.body, {subtree: true, childList: true})
}
window.addEventListener("load", start)
})()