TinyURL Decoder

By setomits Last update May 12, 2011 — Installed 74,170 times.

There are 70 previous versions of this script.

// ==UserScript==
// @name           TinyURL Decoder
// @namespace      http://matatabi.homeip.net
// @description    decode shortened URLs to original ones
// @include        http://*
// @exclude        http://0rz.tw/*
// @exclude        http://2tu.us/*
// @exclude        http://alturl.com/*
// @exclude        http://arm.in/*
// @exclude        http://awe.sm/*
// @exclude        http://b23.ru/*
// @exclude        http://bacn.me/*
// @exclude        http://bit.ly/*
// @exclude        http://bitly.com/*
// @exclude        http://budurl.com/*
// @exclude        http://chilp.it/*
// @exclude        http://cli.gs/*
// @exclude        http://doiop.com/*
// @exclude        http://elfurl.com/*
// @exclude        http://fat.ly/*
// @exclude        http://ff.im/*
// @exclude        http://fur.ly/*
// @exclude        http://fwd4.me/*
// @exclude        http://goo.gl/*
// @exclude        http://good.ly/*
// @exclude        http://hub.tm/*
// @exclude        http://icanhaz.com/*
// @exclude        http://idek.net/*
// @exclude        http://is.gd/*
// @exclude        http://kl.am/*
// @exclude        http://korta.nu/*
// @exclude        http://linkbee.com/*
// @exclude        http://ln-s.net/*
// @exclude        http://lnk.by/*
// @exclude        http://lnk.in/*
// @exclude        http://m2lb.info/*
// @exclude        http://merky.de/*
// @exclude        http://migre.me/*
// @exclude        http://minurl.org/*
// @exclude        http://moourl.com/*
// @exclude        http://nsfw.in/*
// @exclude        http://oneclip.jp/*
// @exclude        http://ow.ly/*
// @exclude        http://p.ly/*
// @exclude        http://ping.fm/*
// @exclude        http://ponyurl.com/*
// @exclude        http://r.im/*
// @exclude        http://reallytinyurl.com/*
// @exclude        http://retwt.me/*
// @exclude        http://sexyurl.to/*
// @exclude        http://short.to/*
// @exclude        http://shorturl.com/*
// @exclude        http://simurl.com/*
// @exclude        http://slnk.me/*
// @exclude        http://sn.im/*
// @exclude        http://snipr.com/*
// @exclude        http://snipurl.com/*
// @exclude        http://snurl.com/*
// @exclude        http://su.pr/*
// @exclude        http://thecow.me/*
// @exclude        http://tighturl.com/*
// @exclude        http://tcrn.ch/*
// @exclude        http://tiny.cc/*
// @exclude        http://tinyurl.com/*
// @exclude        http://to.ly/*
// @exclude        http://togoto.us/*
// @exclude        http://tr.im/*
// @exclude        http://tra.kz/*
// @exclude        http://traceurl.com/*
// @exclude        http://tweetburner.com/*
// @exclude        http://twurl.nl/*
// @exclude        http://u.nu/*
// @exclude        http://unhub.com/*
// @exclude        http://ur1.ca/*
// @exclude        http://url.az/*
// @exclude        http://url.ie/*
// @exclude        http://urlcut.com/*
// @exclude        http://urlenco.de/*
// @exclude        http://viigo.im/*
// @exclude        http://wurl.ws/*
// @exclude        http://www.x.se/*
// @exclude        http://xr.com/*
// @exclude        http://xrl.us/*
// @exclude        http://yatuc.com/*
// @exclude        http://yep.it/*
// ==/UserScript==

(function(d){
    function reset_cache() {
	var _cache = {};
	GM_setValue('cache', _cache.toSource());
	cache = _cache;
	show_cache();
    }
    GM_registerMenuCommand("Reset cache of TinyURL Decoder", reset_cache);

    function show_cache() {
	var _counter = 0;
	GM_log('==== Cached URLs are shown below: ====');
	for (key in cache) {
	    GM_log(key + ' --> ' + cache[key]);
	    _counter++;
	}
	GM_log('====  These ' + _counter + ' URLs are cached. ====');
    }
    GM_registerMenuCommand("Show cache of TinyURL Decoder", show_cache);

    function keep_text() {
	GM_setValue('keep_text', true);
    }
    GM_registerMenuCommand("Keep anchor text original", keep_text);

    function replace_text() {
	GM_setValue('keep_text', false);
    }
    GM_registerMenuCommand("Replace anchor text to decoded(default)", replace_text);

    function init_cache() {
	var _cache = GM_getValue('cache');
	if (typeof(_cache) === 'undefined')
	    _cache = {};
	else {
	    _cache = eval(_cache);
	    
	    var _counter = 0;
	    for (c in _cache) _counter++;
	    
	    var maxlength = GM_getValue('maxlength'); 
	    if (typeof(maxlength) === 'undefined') {
		maxlength = 100;
		GM_setValue('maxlength', maxlength);
	    }
	    
	    var oversize =  _counter - maxlength;
	    if (oversize > 0) {
		var __cache = {};
		for (key in _cache)
		    if (oversize > 0)
			oversize--;
		    else
			__cache[key] = _cache[key];
		_cache = __cache;
	    }
	}
	return _cache;
    }

    var cache = init_cache();
    var services = {
	'0rz.tw': true, '2tu.us': true,	'6url.com': true,
	'a2a.me': true, 'alturl.com': true, 'arm.in': true, 'awe.sm': true,
	'b23.ru': true, 'bacn.me': true, 'bit.ly': true, 'bitly.com': true,
	'budurl.com': true,
	'chilp.it': true, 'cli.gs': true,
	'dld.bz': true, 'doiop.com': true,
	'fat.ly': true, 'ff.im': true, 'fur.ly': true,
	'fwd4.me': true,
	'gog.is': true, 'goo.gl': true, 'good.ly': true,
	'htn.to': true, 'htxt.it': true, 'hub.tm': true,
	'icanhaz.com': true, 'idek.net': true, 'is.gd': true,
	'j.mp': true,
	'kl.am': true, 'korta.nu': true,
	'linkbee.com': true, 'lnk.by': true, 'lnk.in': true,
	'ln-s.net': true,
	'm2lb.info': true, 'merky.de': true, 'migre.me': true,
	'minurl.org': true, 'moourl.com': true,
	'nsfw.in': true,
	'oneclip.jp': true, 'ow.ly': true,
	'p.ly': true, 'ping.fm': true,
	'ponyurl.com': true, 'post.ly': true,
	'r.im': true, 'reallytinyurl.com': true, 'retwt.me': true,
	'sexyurl.to': true, 'shar.es': true, 'shorturl.com': true,
	'simurl.com': true,
	'sinaurl.cn': true, 'slnk.me': true, 'snurl.com': true,
	'sn.im': true, 'snipr.com': true,
	'snipurl.com': true, 'su.pr': true,
	't.co': true, 'tcrn.ch': true, 'tighturl.com': true,
	'tinyurl.com': true, 'thecow.me': true,
	'tiny.cc': true, 'togoto.us': true, 'to.ly': true, 'traceurl.com': true,
	'tra.kz': true,	'tr.im': true, 'twurl.nl': true,
	'u.nu': true, 'unhub.com': true, 'ur1.ca': true, 'urlcut.com': true,
	'urlenco.de': true, 'url.az': true, 'url.ie': true,
	'viigo.im': true,
	'webtooljungle.com': true, 'wp.me': true, 'wurl.ws': true, 'www.x.se': true,
	'xrl.us': true, 'xr.com': true,
	'yatuc.com': true, 'yep.it': true
    };

    function request_for(a) {
	if (a.hostname in {'bit.ly': true, 'j.mp': true} &&
	    a.pathname.lastIndexOf('/') === 0)
	    return 'http://api.bit.ly/expand?version=2.0.1&format=xml' +
	    '&login=tinyurldecoder&apiKey=R_42e55650000a21fa8cf2caa2778b5aaf' +
	    '&shortUrl=' + a.href;
	else if (a.hostname === 'chilp.it')
	    return 'http://p.chilp.it/api.php' + a.search;
	else if (a.hostname === 'cli.gs' && a.pathname.indexOf('/api') !== 0)
	    return 'http://cli.gs/api/v1/cligs/expand?clig=' + a.href;
	else if (a.hostname === 'dld.bz' &&
		 a.pathname.length > 1 && a.pathname.indexOf('/api') !== 0)
	    return 'http://dld.bz/api/expand?format=xml&url=' + a.href;
	else if (a.hostname === 'good.ly' && a.pathname.indexOf('/-') === 0)
	    return null;
	else if (a.hostname === 'idek.net' && a.search.indexOf('?idek-api') !== 0)
	    return a.href + '?idek-api=true';
	else if (a.hostname === 'migre.me' && a.pathname.indexOf('/api') !== 0)
	    return 'http://migre.me/api_redirect2.xml?url=' + a.href;
	else if (a.hostname === 'p.ly' && a.pathname.indexOf('/api') !== 0)
	    return 'http://p.ly/api/expand?code=' + a.pathname.substr(1);
	else if (a.hostname === 'post.ly')
	    return 'http://posterous.com/api/getpost?id=' + a.pathname.substr(1);
	else if (a.hostname === 'r.im' && a.pathname.indexOf('/api') !== 0)
	    return 'http://r.im/api/index.cfm?short_url=' + a.href;
	else if (a.hostname === 'su.pr' && a.pathname.indexOf('/api') !== 0)
	    return 'http://su.pr/api/expand?shortUrl=' + a.href;
	else if (a.hostname === 'u.nu' && a.href.indexOf('?') > 0)
	    return 'http://u.nu' + a.pathname;
	else
	    return a.href;
    }

    function extract_from_response(resp) {
	var pat = null;
	var a = document.createElement('a');

	a.href = resp.finalUrl;

	if (a.hostname === '0rz.tw')
	    pat = '<input\\s+type="text"\\s+class="url"\\s+id="original_url"\\s+value="(.*?)"/>';
	else if (a.hostname === '6url.com')
	    pat = '<title>Redirects\\s+to\\s+(.*?)</title>';
	else if (a.hostname === 'arm.in')
	    pat = 'noresize><frame\\s+src="(.*?)"\\s+name="ContentWindow"';
	else if (a.href.indexOf('http://api.bit.ly/expand') === 0)
	    pat = '<longUrl>(.*?)</longUrl>';
	else if (a.hostname === 'cuthut.com')
	    pat = '<a\\s+href="(.*?)"\\s+id="url">';
	else if (a.hostname === 'dld.bz' && a.pathname === '/api/expand')
	    pat = '<long_url>(.*?)</long_url>';
	else if (a.hostname === 'lnk.by')
	    pat = '<meta\\s+HTTP-EQUIV="REFRESH"\\s+content="0; url=(.*?)">'
	else if (a.hostname === 'lnk.in')
	    pat = "redirect\\('(.*?)'\\)";
	else if (a.hostname === 'm2lb.info')
	    pat = '<meta\\s+http-equiv="REFRESH"\\s+content="10;url=(.*?)">';
	else if (a.href.indexOf('http://migre.me/api_redirect2.xml') === 0)
	    pat = '<url><\\!\\[CDATA\\[(.*?)\\]\\]></url>';
	else if (a.hostname === 'minurl.org')
	    pat = 'getTwitterSearchResults\\("(.*?)"\\);';
	else if (a.hostname === 'webtooljungle.com')
	    pat = 'window.location.href=\\("(.*?)"\\);';
	else if (a.hostname === 'nsfw.in')
	    pat = 'id="link-go"\\s+class="span-6\\s+last">\\s+<a\\s+href="(.*?)"';
	else if (a.hostname === 'oneclip.jp')
	    pat = '<link\\s+rel="canonical"\\s+href="(.*?)"\\s+/>';
	else if (a.hostname === 'ow.ly')
	    pat = '<iframe\\s+frameborder="0"\\s+src="(.*?)"\\s+noresize="noresize"\\s+name="hootFrame"\\s+id="hootFrame"\\s+>';
	else if (a.hostname === 'p.ly')
	    pat = '<success>(.*?)</success>';
	else if (a.hostname === 'shar.es')
	    pat = 'content="0; URL=(.*?)"';
	else if (a.hostname === 'url.az')
	    pat = '<a\\s+href="(.*?)"\\s+id="close">';
	else if (a.hostname === 'posterous.com')
	    pat = '<link>(.*?)</link>';
	else if (a.hostname === 'sexyurl.to')
	    pat = "window.location='(.*?)';";
	else if (a.hostname === 'www.shortener.net')
	    pat = 'window.location="(.*?)";';
	else if (a.hostname === 'simurl.com')
	    pat = 'content="0;\\s+URL=(.*?)"';
	else if (a.hostname === 'su.pr')
	    pat = '"longUrl":(.*?)}';
	else if (a.href.indexOf('http://tinyurl.com/preview.php') === 0)
	    pat = 'id="redirecturl"\\s+href="(.*?)"';
	else if (a.hostname === 'thecow.me')
	    pat = 'id="glu"\\s+src="(.*?)"';
	else if (a.hostname === 'unhub.com')
	    pat = '<link\\s+href="(.*?)"\\s+rel="canonical"/>'
	else if (a.hostname === 'viigo.im')
	    pat = '<a\\s+href="(.*?)"\\s+title="Close">';
	else if (a.hostname === 'xr.com')
	    pat = '<a\\s+href="(.*?)"\\s+target=parent>';
	else if (a.href.indexOf('http://cli.gs/api/v1/cligs/expand') === 0 ||
		 (a.hostname === 'idek.net' && a.search === '?idek-api=true') ||
		 a.href.indexOf('http://p.chilp.it/api.php') === 0 ||
		 a.href.indexOf('http://r.im/api/index.cfm?short_url=') === 0)
	    return resp.responseText.replace(/[\n\r]/g, '');

	if (pat) 
	    try {
		theUrl = resp.responseText.match(pat, 'i')[1];
		if (a.hostname === 'su.pr')
		    theUrl = eval(theUrl);
	    } catch (err) {
		theUrl = null;
	    }
	else
	    theUrl = resp.finalUrl;
	
	return theUrl;
    }

    var working = {};
    function decode_shortened_url(_node) {
	if (cache[_node.href]) {
	    if (GM_getValue('keep_text') === true) {
		_node.title = 'leads to ' + cache[_node.href];
	    } else {
		_node.title = 'was ' + _node.href;
		_node.innerHTML = cache[_node.href];
	    }
	    _node.href = cache[_node.href];
	} else {
	    if (working[_node.href])
		return;
	    else
		working[_node.href] = true;

	    var _url = request_for(_node);
	    if (_url)
		GM_xmlhttpRequest({
		    method: "GET",
		    url: _url,
		    onload: function(resp) {
			finalUrl = extract_from_response(resp);
			if (finalUrl) {
			    cache[_node.href] = finalUrl.replace(/\\|\\/, '').replace('&amp;', '&');
			    working[_node.href] = false;
			}
		    },
		    onerror: function(resp) {
			GM_log('Oops! Access error to ' + _node.href);
		    }
		});
	}
    }

    function main() {
	var counter = 0;
	var anchors = d.getElementsByTagName('a');
	var anchors_length = anchors.length;
	for (var i=0; i<anchors_length; i++) {
	    if (anchors[i].pathname.length === 1 && anchors[i].search.length < 2)
		continue;
	    else if (anchors[i].hostname in services) {
		decode_shortened_url(anchors[i]);
		counter++;
	    }
	}
	GM_setValue('cache', cache.toSource());

	if (counter === 0) {
	    setTimeout(main, 60000);
	    working = {};
	} else {
	    setTimeout(main, 10000 / counter);
	}
    }

    main();
}(document));