Tweet Timeline

By Abhinav Sarkar Last update Feb 22, 2009 — Installed 270 times.
// ==UserScript==
// @name           Tweet Timeline
// @namespace      abhinavsarkar.net
// @include        http://twitter.com/home*
// @require        http://ajax.googleapis.com/ajax/libs/jquery/1.3.0/jquery.min.js
// ==/UserScript==

jQuery(document).ready(function() {
    var TimelineScript = document.createElement('script');
    with(TimelineScript) {
        src = 'http://static.simile.mit.edu/timeline/api-2.2.0/timeline-api.js';
        type = 'text/javascript';
    }
    jQuery("head")[0].appendChild(TimelineScript);

    function embedFunctions(funcs) {
        var scriptNode = document.createElement('script');
        scriptNode.type = 'text/javascript';
        funcs.forEach(function(func){
            jQuery(scriptNode).append(func.toString().replace(/([\s\S]*?return;){2}([\s\S]*)}/,'$2') + "\n");
        });
        document.body.appendChild(scriptNode);
    }

    function loadEventData(eventSource, page, pageChanger) {
        page = Math.max(page, 1);
        console.log("page = " + page);
        var eventData = {
            'dateTimeFormat': 'iso8601',
            'events' : []
        };

        loadData = function (data) {
            jQuery('.hentry', jQuery(data)).each(function (index, elem) {
                var $elem = jQuery(elem);
                var id = $elem.attr('id');
                if (eventSource.getEvent(id) == undefined) {
                    var description = $elem.find('.entry-content').html()
                        + " [<a tagert='_blank' href='" + $elem.find('.entry-date')[0].href + "'>§</a>]";
                    eventData.events.push({
                        id:             id,
                        start:          $elem.find('.published')[0].getAttribute('title'),
                        title:          $elem.find('.status-body a:first').text(),
                        description:    description,
                        instant:        true,
                        image:          $elem.find('.photo')[0].src,
                        link:           $elem.find('.status-body a:first').attr('href'),
                        caption:        $elem.find('.status-body a:first').attr('title')
                    });
                }
            });
            eventSource.loadJSON(eventData, "");
            pageChanger(page);
        };

        /*if (page == getCurrentPage())
            loadData(document.documentElement);
        else*/
        jQuery.get("http://twitter.com/home?page="+page, loadData);
    }

    function getCurrentPage() {
        var s = location.search;
        if (s) {
            s = s.replace("?","");
            s = s.split("&");
            var d = {};
            for (var i=0; i<s.length; i++) {
                var u = s[i].split("=");
                d[u[0]] = u[1];
            }
            if ("page" in d)
                return parseInt(d["page"]);
        }
        return 1;
    }

    function getTweetRate(eventSource) {
        var esMinDate = eventSource.getEarliestDate();
        var esMaxDate = eventSource.getLatestDate();
        var count = eventSource.getCount();
        return count/(esMaxDate - esMinDate)*1000;
    }

    function createTimeline() {
        modifyInfoBubbleTime();
        var eventSource = new Timeline.DefaultEventSource(0);

        var theme = Timeline.ClassicTheme.create();
        theme.event.overviewTrack.offset = 10;
        var bandInfos = [
            Timeline.createBandInfo({
                width:          "70%",
                intervalUnit:   Timeline.DateTime.MINUTE,
                intervalPixels: 80,
                eventSource:    eventSource,
                timeZone:       - new Date().getTimezoneOffset()/60
            }),
            Timeline.createBandInfo({
                overview:       true,
                eventSource:    eventSource,
                showEventText:  false,
                width:          "15%",
                intervalUnit:   Timeline.DateTime.HOUR,
                intervalPixels: 160,
                timeZone:       - new Date().getTimezoneOffset()/60,
                theme:          theme
            }),
            Timeline.createBandInfo({
                overview:       true,
                eventSource:    eventSource,
                showEventText:  false,
                width:          "15%",
                intervalUnit:   Timeline.DateTime.DAY,
                intervalPixels: 160,
                timeZone:       - new Date().getTimezoneOffset()/60,
                theme:          theme
            })
        ];
        bandInfos[1].syncWith = 0;
        bandInfos[1].highlight = true;
        bandInfos[2].syncWith = 0;
        bandInfos[2].highlight = true;

        var timelineDiv = document.createElement("div");
        timelineDiv.id = "timelineContainer";
        timelineDiv.style.height = "150px";
        timelineDiv.style.fontSize = "xx-small";
        timelineDiv.style.border = "1px solid #aaa";
        jQuery('#header').after(timelineDiv);

        var page = 1;
        var fetchingPage = null;
        var tl = Timeline.create(timelineDiv, bandInfos);

        var autoScrollTimeline= function () {
            if (typeof timelineAutoScroller != undefined)
                clearTimeout(timelineAutoScroller);
            return setInterval(function() {
                tl.getBand(0).setCenterVisibleDate(new Date());
            }, 1000*30)
        };

        var timelineAutoScroller = autoScrollTimeline();

        var pageChanger = function (newPage){
            setTimeout(function () {timelineAutoScroller = autoScrollTimeline();},60*1000);
            page = newPage;
            fetchingPage = null;
        };

        tl.getBand(0).addOnScrollListener(function(band) {
            var bandMinDate = band.getMinVisibleDate();
            var bandMaxDate = band.getMaxVisibleDate();
            var esMinDate = eventSource.getEarliestDate();
            var esMaxDate = eventSource.getLatestDate();
            var tweetRate = getTweetRate(eventSource);

            if (bandMinDate < esMinDate) {
                var diff = (esMinDate - bandMinDate)/1000;
                var dp = Math.ceil(diff/(20/tweetRate));
                if (fetchingPage != page + dp) {
                    for (var i=1; i<=dp; i++) {
                        clearInterval(timelineAutoScroller);
                        fetchingPage = page + i;
                        loadEventData(eventSource, fetchingPage, pageChanger);
                    }
                }
            } else if (bandMaxDate > esMaxDate) {
                var diff = (bandMaxDate - esMaxDate)/1000;
                var dp = Math.ceil(diff/(20/tweetRate));
                if (fetchingPage != page - dp) {
                    for (var i=1; i<=dp; i++) {
                        clearInterval(timelineAutoScroller);
                        fetchingPage = page - i;
                        loadEventData(eventSource, fetchingPage, pageChanger);
                    }
                }
            }
        });

        if (!fetchingPage) {
            fetchingPage = page;
            loadEventData(eventSource, fetchingPage, pageChanger);
        }
    }

    function modifyInfoBubbleTime() {
        var oldFillInfoBubble =
            Timeline.DefaultEventSource.Event.prototype.fillInfoBubble;

        Timeline.DefaultEventSource.Event.prototype.fillInfoBubble =
            function(elmt, theme, labeller) {
                oldFillInfoBubble.call(this, elmt, theme, labeller);
                var timeDiv = jQuery(elmt).find('.timeline-event-bubble-time');
                timeDiv.text(timeDiv.text().replace(" GMT", ""));
            }
    }

    embedFunctions([
        modifyInfoBubbleTime,
        loadEventData,
        getTweetRate,
        getCurrentPage,
        createTimeline
    ]);

    var checker = setInterval(function(){
        if(typeof unsafeWindow.Timeline != "undefined" &&
           typeof unsafeWindow.Timeline.DefaultEventSource != "undefined" &&
           typeof unsafeWindow.Timeline.OverviewEventPainter != "undefined" &&
           typeof unsafeWindow.Timeline.GregorianDateLabeller.monthNames != "undefined") {
            clearInterval(checker);
            unsafeWindow.createTimeline();
        }
    },500);
});