Twitter upgrade

By Remy Sharp Last update Oct 26, 2008 — Installed 957 times.

There are 7 previous versions of this script.

// ==UserScript==
// @name           Twitter upgrade
// @namespace      com.remysharp.twitterUpgrade
// @description    Adds: twitter keys, search (via @joshr's script), lat/long conversion to real location and adds offline convesation link, now includes hashtags
// @include        *twitter.com/*
// ==/UserScript==

// Twitter keys, Twitter locations, offline link
// Version 1.1
// 2008-09-19
// Copyright (c) 2008, Left Logic
// Author Remy Sharp - http://twitter.com/rem
// Released under the GPL license
// http://www.gnu.org/copyleft/gpl.html

// Twitter Search script
// version 0.2
// 2008-09-19
// Copyright (c) 2008, Dash Labs
// Author David Stone - http://twitter.com/builtbydave
// Author Josh Russell - http://twitter.com/joshr
// Released under the GPL license
// http://www.gnu.org/copyleft/gpl.html

// Twitter hashtags
// http://userscripts.org/scripts/review/24528
// http://knoedeldealer.de/
// Copyright (c) 2008, KnoedelDealer
// Author KnoedelDealer - http://knoedeldealer.de/

// Follower
// Shows in an obvious way if a certain twitter user is following you
// http://icant.co.uk/sandbox/follower.user.js
// http://www.wait-till-i.com/2008/10/22/greasemonkey-script-to-show-if-a-twitter-user-follows-you-or-not/
// Author Chris Heilmann - http://www.wait-till-i.com/

function tweetStatusChange() {
    var timeline = document.getElementById('timeline'),
        statuses, i, j, id, tweet, spans, buttons,
        replyImg = '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAANCAYAAACgu+4kAAAACXBIWXMAAAsTAAALEwEAmpwYAAACiUlEQVQokU2SQU8bVxSFvzczQSM8HWywGwfkQQ5Vm7TBiEWUqKnYtKISUtW/0T/SXX9H2QDKFqk7REQqUxRBpIIJQXbwyIJoxp7aaDzz3u0GS9z9d8659x7FvRGREvCN1vqViKwAj4wxrogMgY+O4zRt234LtJRSKYC6Bz8DXmRZ9gp4qrWe7/f7xdvb2weu66ae5312XfejZVknlmXtA38rpdrOHVwBvs+y7NfhcPi82WzOJkli+b6vCoUCcRy7vV6vWKvVgtXV1a89z3uolBqLyGDi/vN4PP6z2+1+3tnZkW63K8YYSZJEer2e5HkuSZJIp9OR7e1t0+/3P6Vp+oeIvLQAjDEvtdZP9/b2Smtra1SrVU5OTtjd3SUMQ5rNJltbW1QqFer1urq4uPjStu0G8MwC0Fp/d3NzszA9Pc3s7CxxHHNwcMD6+jqNRgMRoVwuMzU1he/7RFFkG2MW8zx/7NzdsBLHsVcsFhXA5eUl5XIZz/MAiKKIWq0GwPX1NYVCQYnIDFCaJMiUUsYYw91K+L4PQJZlhGHIwsICIkKn0yEIAgFyIHcAHMf5UCwWn7RarWlABUHA8fExZ2dnhGFItVrl/PycwWDA/Pw8lUolN8aEtm13JgJv5+bmHgOP2u22HQSB2tjYoN1us7Kygm3btFotlpaWqNfrRms9sCzrvWVZ7ydv/DZN09+vrq7+3dzc1Kenp0ZrLVprMcaI1lrSNJXDw0N9dHQ0HI/HeyLym4jU7zfxpzzPfxkOhz/u7+9/NRqNHszMzEyKxGg0MouLi/8tLy8fO47zl+M4r5VS7+4LfAEEwGqWZT9orRtRFAVZlrmlUqnvuu4HpdQ/juO8Ad4B10qp0f9ttWhk8iW3zAAAAABJRU5ErkJggg==" />';

    // hashtag variables
	var tagsiteurl = GM_getValue('tagsiteurl');
	if (!tagsiteurl || tagsiteurl == 'undefined' || tagsiteurl == '') tagsiteurl = 'http://www.hashtags.org/tag/$1/';

    statuses = findEntries();
    
    // single status page
    if ((/statuses/).test(window.location.pathname)) {
        tweet = (statuses[0].getElementsByTagName('p')[0].textContent || "").replace(/^\s+|\s+$/g, '');
        id = window.location.pathname.split('/').pop();
        buttons = document.getElementById('status_actions_' + id);

        buttons.innerHTML += '<a title="Take tweet offline" href="mailto:?body=Tweet: ' + tweet.replace(/"/g, '%22') + '">' + replyImg + '</a>';
        
        // now do the hashtag
        tweet = statuses[0].getElementsByTagName('p')[0];
        tweet.innerHTML = tweet.innerHTML.replace(/#([\d\w\-]+)/g, '#<a href="' + tagsiteurl + '" title="tag: $1" class="hashtaglink">$1</a>');
    } else {
        for (i = 0; i < statuses.length; i++) {
            spans = statuses[i].getElementsByTagName('span');
            for (j = 0; j < spans.length; j++) {
                if (spans[j].className.indexOf('entry-content') !== -1) {
                    tweet = (spans[j].textContent || "").replace( /^\s+|\s+$/g, "" );
                    spans[j].innerHTML = spans[j].innerHTML.replace(/#([\d\w\-]+)/g, '#<a href="' + tagsiteurl + '" title="tag: $1" class="hashtaglink">$1</a>');
                    break;
                }
            }
            
            // insert offline link
            buttons = Array.prototype.slice.apply(statuses[i].getElementsByTagName('div')).pop();
            
            buttons.innerHTML += '<a title="Take tweet offline" href="mailto:?body=Tweet: ' + tweet.replace(/"/g, '%22') + '">' + replyImg + '</a>';
        }
    }
}

function tweetLocation() {
    var sidebar = document.getElementById('side'),
        spans, i, latlong, adr, script;
        
    if (sidebar) {
        spans = sidebar.getElementsByTagName('span');
        
        for (i = 0; i < spans.length; i++) {
            if (spans[i].className == 'adr') {
                adr = spans[i].textContent;
                break;
            }
        }

        if (adr && adr.indexOf('iPhone: ') !== -1) {
            latlong = adr.replace(/iPhone: /, '').replace(/,/, '%2C');
            loadLocationFromLatLong(latlong);
        }
    }
}

function loadLocationFromLatLong(latlong) {
    var code = function () {
        window.__tweetLocationDetailed = function (o) {
            var sidebar = window.parent.document.getElementById('side'),
                spans, i, adr;

            if (sidebar) {
                spans = sidebar.getElementsByTagName('span');
                
                // blur the location a little
                adr = o.Placemark[0].address.split(/,/);
                adr.shift();
                adr = adr.join(',');

                // reverse would be faster, but I know the span I'm looking for is higher up
                for (i = 0; i < spans.length; i++) {
                    if (spans[i].className == 'adr') {
                        spans[i].innerHTML = '<a href="http://maps.google.com/maps?q=' + o.Placemark[0].Point.coordinates[1] + '%2C' + o.Placemark[0].Point.coordinates[0] + '">' + adr + '</a>';
                        break;
                    }
                }
            }
        };
    };
    
    
    var apikey = 'ABQIAAAApp0H5C9pbTZl2ieJL3B98xSnhvsz13Tv4UkZBHR3eJwOymtuUxTX0Sc8LHhxJCe_ZUkapoD9UL07vw';
    // requires two hits to google's map api

    var url = 'http://maps.google.com/maps/geo?output=json&oe=utf-8&ll=' + latlong + '&key=' + apikey + '&callback=__tweetLocationDetailed';
    
    var iframeTemplate = [
        '<!' + 'DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"', 
        '    "http://www.w3.org/TR/html4/loose.dtd">', 
        '<' + 'html>', 
        '<' + 'head>', 
        '<' + 'script>',
        '%script%',
        '<' + '/script>',
        '<' + 'script src="' + url + '"><' + '/script>', 
        '<' + '/head>', 
        '<' + 'body>', 
        '<' + '/body>', 
        '<' + '/html>'
    ].join("\n");
    
    var frame = document.createElement('iframe');
    frame.id = '__twitterLocationCheck';
    frame.style.display = 'none';
    document.getElementsByTagName('body')[0].appendChild(frame);
    
    var win = frame.contentDocument || frame.contentWindow.document; 
    
    // I could use unsafeWindow - but I'm happier (I guess in my head), 
    // with the iframe injection
    // doing the '(' + code.toString() + ')()' was the only way I could
    // get code to run inside the *real* window 
    win.write(iframeTemplate.replace(/%script%/, '(' + code.toString() + ')()'));
    win.close();
    
}

function addSearch() {
    var side = document.getElementById('side');
    
    if (side) {
        var section_header = side.getElementsByTagName('div')[0];
        var sections = side.getElementsByTagName('div');

        // this means we have search for any page
        var insertPoint = document.getElementById('friends').parentNode.getElementsByTagName('div')[0];

        /* build div container */
        var main = document.createElement('div');
        main.className = 'section';
        main.style.padding = '0px 0px 15px 0px';

        var sh = document.createElement('div');
        sh.className = 'section-header';

        var a = document.createElement('a');
        a.href = 'http://search.twitter.com/advanced';
        a.className = 'section-links';
        a.innerHTML = 'advanced';

        var h1 = document.createElement('h1');
        h1.innerHTML = 'Search';

        var form = document.createElement('form');
        form.action = 'http://search.twitter.com/search';
        form.id = 'searchForm';
        form.method = 'get';
        form.name = 'searchForm';

        var se = document.createElement('div');
        se.id = 'searchEntry';

        var input = document.createElement('input');
        input.autosave = 'com.twitter.search';
        input.size = 11;
        input.id = 'searchBox';
        input.name = 'q';
        input.placeholder = 'Enter your query';
        input.results = 10;
        input.type = 'search';


        var submit = document.createElement('input');
        submit.type = 'submit';
        submit.value = 'Search';
        submit.style.margin = '0px 0px 0px 13px';

        sh.appendChild(a);
        sh.appendChild(h1);
        se.appendChild(input);
        se.appendChild(submit);
        form.appendChild(se);
        main.appendChild(sh);
        main.appendChild(form);

        /* insert */
        insertPoint.parentNode.insertBefore(main, insertPoint);
    }
}

// used by both offline and hashtags
function findEntries() {
    var entries = [], i;
    
    var timeline = document.getElementById('timeline');
    if (timeline) {
        var el = timeline.getElementsByTagName('tr');
        for (i in el) {
            if (el[i].className && el[i].className.indexOf('hentry') > -1) {
                entries.push(el[i]);
            }
        }
    }

    el = document.getElementsByTagName('p');
    for (i in el) {
        if (el[i].className && el[i].className.indexOf('entry-content') > -1) {
            entries.push(el[i]);
        }
    }

    var permalink = document.getElementById('permalink');
    if (permalink) {
        entries.push(permalink);
    }
    
    return entries;
}

function TwitterHashtags() {

    var entries = new Array();
    var tagsiteurl = GM_getValue('tagsiteurl');

    if (!tagsiteurl || tagsiteurl == 'undefined' || tagsiteurl == '') tagsiteurl = 'http://search.twitter.com/search?q=%23$1';

    this.useHashtagsOrg = function() {
        GM_setValue('tagsiteurl', 'http://www.hashtags.org/tag/$1/');
        document.location.reload();
    };


    this.useTwemesCom = function() {
        GM_setValue('tagsiteurl', 'http://twemes.com/$1');
        document.location.reload();
    };

    this.useSearchTwitterComWithHash = function() {
        GM_setValue('tagsiteurl', 'http://search.twitter.com/search?q=%23$1');
        document.location.reload();
    };

    this.useSearchTwitterComWithoutHash = function() {
        GM_setValue('tagsiteurl', 'http://search.twitter.com/search?q=$1');
        document.location.reload();
    };

}

function hashTags() {
    // note that I've moved the hashtag replacing in the tweetStatusChange function because they need to run politely together
    var twitterhashtags = new TwitterHashtags();

    GM_registerMenuCommand('Hastags: use hashtags.org', twitterhashtags.useHashtagsOrg);
    GM_registerMenuCommand('Hastags: use twemes.com',   twitterhashtags.useTwemesCom);
    GM_registerMenuCommand('Hastags: use search.twitter.com (with hash)',    twitterhashtags.useSearchTwitterComWithHash);
    GM_registerMenuCommand('Hastags: use search.twitter.com (without hash)', twitterhashtags.useSearchTwitterComWithoutHash);
}

function twitterKeys() {
    var keys = { 
        love : "♥",
        plane : "✈",
        smile : "☺",
        ':-)' : "☺",
        ':)' : "☺",
        music : "♬",
        boxtick : "☑",
        spade : "♠",
        phone : "☎",
        darksmile : "☻",
        song : "♫",
        box : "☒",
        whitespade : "♤",
        carrot : "☤",
        sad :  "☹",
        ':-(' : "☹",
        ':(' : "☹",
        note : "♪",
        female : "♀",
        star : "✩",
        letter : "✉",
        pirate : "☠",
        tick : "✔",
        male : "♂",
        darkstar : "★",
        wheel : "✇",
        recycle : "♺",
        retweet : "♺",
        rt : "♺",
        cross : "✖",
        cook : "♨",
        random1 : "❦",
        cloud : "☁",
        peaceout : "✌",
        king : "♛",
        rose : "❁",
        islam : "☪",
        umbrella : "☂",
        pen : "✏",
        bishop : "♝",
        flower : "❀",
        tools : "☭",
        snowman : "☃",
        right : "☛",
        darkknight : "♞",
        darkflower : "✿",
        peace : "☮",
        sun : "☼",
        left : "☚",
        knight : "♘",
        random2 : "✾",
        ying : "☯",
        moon : "☾",
        up : "☝",
        rook : "♖",
        snow : "✽",
        christ : "✝",
        comet : "☄",
        down : "☟",
        pawn : "♟",
        random3 : "✺",
        prince : "☥",
        cut : "✂",
        write : "✍",
        queen : "♕",
        darkstar2 : "✵",
        copy : "©",
        tm : "™",
        euro : "€",
        "<<" : "«",
        ">>" : "»",
        "yen" : "¥",
        "radioactive" : "☢"
        
    },
    status = document.getElementById('status'),
    completeMethod = GM_getValue('tk_autoCompletion') == 'false' ? 'tab' : 'auto',
    text, i, word, timer, autoComplete;
        
    // create a fast lookup regexp of all the terms used in a .test() later on
    var keyMatch = [];
    for (i in keys) {
        keyMatch.push(i.replace(/[\(\)\-]/g, function (m) {
            return '\\' + m;
        }));
    }
    
    keyMatch = new RegExp('(\\s*|^)' + keyMatch.join('|') + '(\\s+|$)');

    function useTabCompletion() {
        GM_setValue('tk_autoCompletion', 'false');
        document.location.reload();
    }
    
    function useAutoComplete() {
        GM_setValue('tk_autoCompletion', 'true');
        document.location.reload();        
    }
    
    GM_registerMenuCommand('Twitter keys: auto complete as I type', useAutoComplete);
    GM_registerMenuCommand('Twitter keys: tab completion', useTabCompletion);
    
    if (!status) return;
    
	if (completeMethod == 'tab') {
        status.addEventListener('keydown', function (event) {
            if (event.keyCode == 9) {
                // different function because we're only completing the last word in the tweet
                text = status.value;
                i = text.lastIndexOf(' ');
                word = text.substr(i + 1, text.length - i).toLowerCase();
                if (word.length) {
                    if (keys[word]) {
                        status.value = text.substr(0, i + 1) + keys[word];
                        event.preventDefault(); // prevent the tabbing away
                    }
                }
            } 
        }, true);	    
	} else {
	    autoComplete = function (event) {
	        clearTimeout(timer); // we do it twice, but it's because we might be called via the blur
	        
	        // do a fast .test to see if we need to run
	        if (keyMatch.test(status.value.toLowerCase())) {
                status.value = status.value.replace(/(\s*|^)([\S]*)(\s+|$)/g, function (m, c1, c2, c3) {
                    return (c2 && keys[c2.toLowerCase()]) ? keys[c2.toLowerCase()] + c3 : m;
                });
	        }
        };
        
	    status.addEventListener('keydown', function () {
	        clearTimeout(timer);
            timer = setTimeout(autoComplete, 100);
	    }, true);
	    
	    status.addEventListener('blur', autoComplete, true);
	    
	}
}

// @description    Shows in an obvious way if a certain twitter user is following you
function following() {
    // profile link means we're logged in
    if (document.getElementById('profile_link')) {
        var you = document.getElementById('profile_link').href.replace(/.*\//,''),
            friends = document.getElementById('friends'),
            header = document.getElementsByTagName('h2')[0],
            side = null,
            user = window.location.pathname.replace(/(^\/|\/$)/g, '');
            url = '';
        
        // better and faster to check the friends on the sidebar first,
        // but makes sure we're on their profile page
        if (user.split('/').length === 1 && friends.innerHTML.indexOf('/'+you)!==-1) {
            side = document.getElementById('friends');

            header.innerHTML += ' (follows you)';
        } else {
            // this will now check the most recent 100 friends - otherwise you're screwed - change by Remy Sharp
            url = 'http://twitter.com/statuses/friends/' + user + '.json';
            GM_xmlhttpRequest({ 
                method: 'GET', 
                url : url, 
                onload: function(responseDetails) { 
                    if (responseDetails.responseText.indexOf('"screen_name":"' + you + '"')!==-1) {
                        header.innerHTML += ' (follows you)';
                    }
                } 
            });
        }
      }
}

// main
try { following(); } catch (e) {}
try { twitterKeys(); } catch (e) {}
try { tweetStatusChange(); } catch (e) {}
try { tweetLocation(); } catch (e) {}
try { addSearch(); } catch (e) {}
try { hashTags(); } catch (e) {}