tweetcast/nodejs/client/js/script.js
author Raphael Velt <raph.velt@gmail.com>
Tue, 18 Oct 2011 16:19:08 +0200
changeset 326 c28048fb63b4
parent 325 7d9c576bfaac
child 331 03c69425efa6
child 336 d60efd677b50
permissions -rw-r--r--
Added visual timeline feature to node client

var socket,
    paper,
    tweetData = {
        "tweetcount" : 0,
        "position" : -1,
        "zoomLevel" : 3,
        "timeLevel" : 2
    },
    zoomLevels = [
        {
            "description" : "160 tweets per page",
            "className" : "icons",
            "displayCount" : 160
        },
        {
            "description" : "24 tweets per page",
            "className" : "quarter",
            "displayCount" : 24
        },
        {
            "description" : "12 tweets per page",
            "className" : "half",
            "displayCount" : 12
        },
        {
            "description" : "6 tweets per page",
            "className" : "full",
            "displayCount" : 6
        }
    ],
    colors = {
        'positive' : "#1D973D",
        'reference' : "#C5A62D",
        'negative' : "#CE0A15",
        'question' : "#036AAE"
    },
    timeWindow;

function tweetToHtml(tweet) {
	html = '<li class="tweet ' + zoomLevels[tweetData.zoomLevel].className;
	for (var i in tweet.annotations) {
		html += ' a_' + tweet.annotations[i]
	}
	html += '" id="tweet_' + tweet.id + '">';
	a_user = '<a href="http://twitter.com/' + tweet.user.screen_name + '" target="_blank" title="' + tweet.user.name + '">';
	if (tweet.user.profile_image_url) {
		html += a_user + '<img class="tweet_profile_image" src="' + tweet.user.profile_image_url + '" /></a>';
	}
	html += '<h4>' + a_user + '@' + tweet.user.screen_name + '</a></h4><p class="tweet_created_at">' + new Date(tweet.created_at).toLocaleTimeString() + '</p><p>';
	lastend = 0;
	txt = '';
	entities = [];
	for (var i in tweet.entities.hashtag) {
	    entities.push({
	        "start" : tweet.entities.hashtag[i].indices[0],
	        "end" : tweet.entities.hashtag[i].indices[1],
	        "html" : '<a href="http://twitter.com/search?q=%23' + tweet.entities.hashtag[i].text + '" target="_blank">#' + tweet.entities.hashtag[i].text + '</a>'
	    });
	}
	for (var i in tweet.entities.urls) {
	    entities.push({
	        "start" : tweet.entities.urls[i].indices[0],
	        "end" : tweet.entities.urls[i].indices[1],
	        "html" : '<a href="' + tweet.entities.urls[i].expanded_url + '" target="_blank">' + tweet.entities.urls[i].display_url + '</a>'
	    });
	}
	for (var i in tweet.entities.user_mentions) {
	    entities.push({
	        "start" : tweet.entities.user_mentions[i].indices[0],
	        "end" : tweet.entities.user_mentions[i].indices[1],
	        "html" : '<a href="http://twitter.com/' + tweet.entities.user_mentions[i].screen_name + '" target="_blank" title="' + tweet.entities.user_mentions[i].name + '">@' + tweet.entities.user_mentions[i].screen_name + '</a>'
	    });
	}
 	entities.sort(function(a, b) { return a.start - b.start });
    for (var i in entities) {
 		txt += tweet.text.substring(lastend, entities[i].start) + entities[i].html;
        lastend = entities[i].end;
    }
	txt += tweet.text.substring(lastend);
	html += txt + '</p></li>';
	return html;
}

function displayTweets() {
    var to = tweetData.position + 1,
        from = Math.max(0, to - zoomLevels[tweetData.zoomLevel].displayCount);
    socket.emit('getTweets',{ "from": from, "to": to, "callback" : "display" });
}

function setZoom(level) {
    tweetData.zoomLevel = Math.max(0, Math.min( zoomLevels.length - 1 , level ) );
    displayTweets();
}

function setTimeZoom(level) {
    tweetData.timeLevel = Math.max(0, Math.min( 3, level ));
    getTimeline();
}

function getTimeline() {
    socket.emit('getTimeline',{"level":tweetData.timeLevel});
}

function showTimeWindow() {
    if (timeWindow) {
        timeWindow.remove();
        timeWindow = null;
    }
    if (tweetData.tweetsOnDisplay && tweetData.timelineOnDisplay) {
        var dtfintl = new Date ( tweetData.timelineOnDisplay[ tweetData.timelineOnDisplay.length - 1 ].end ),
            dtdebtl = new Date ( tweetData.timelineOnDisplay[0].start ),
            dtfintw = new Date( tweetData.tweetsOnDisplay[0].created_at ),
            dtdebtw = new Date( tweetData.tweetsOnDisplay[ tweetData.tweetsOnDisplay.length - 1 ].created_at ),
            scaleY = 600 / ( dtfintl - dtdebtl );
        timeWindow = paper.rect( 0, scaleY * ( dtfintl - dtfintw ), 600, scaleY * ( dtfintw - dtdebtw )).attr({"stroke":"#000080","fill":"#8080ff","fill-opacity":.2});
    }
}

$(document).ready(function() {
    paper = Raphael("timeline", 160, 600);
    socket = io.connect('http://' + S_IO_HOST + ':' + S_IO_PORT );
    socket.on('tweetSummary', function (data) {
        if (tweetData.position == tweetData.tweetcount - 1) {
            tweetData.position = data.tweetcount - 1;
            displayTweets();
            getTimeline();
        }
        tweetData.tweetcount = data.tweetcount;
    });
    socket.on('tweets', function (data) {
        switch (data.callback) {
            case "display":
                tweetData.tweetsOnDisplay = data.tweets;
                tweetData.tweetsOnDisplay.reverse();
                html = '';
                for (var i in tweetData.tweetsOnDisplay) {
                    html += tweetToHtml(tweetData.tweetsOnDisplay[i]);
                }
                $("#tweetlist").html(html);
                showTimeWindow();
                break;
        }
    });
    socket.on('timeline', function (data) {
        tweetData.timelineOnDisplay = data.timeline;
        paper.clear();
        timeWindow = null;
        var max = 0;
        for (var i in data.timeline) {
            max = Math.max(max, data.timeline[i].tweets.length);
        }
        var scaleX = 160 / max,
            scaleY = 600 / data.timeline.length,
            tmptw = [];
        for (var i = 0; i < tweetData.tweetcount; i++) {
            tmptw.push({});
        }
        for (var i = 0; i < data.timeline.length; i++) {
            for (var j = 0; j < data.timeline[i].tweets.length; j++) {
                var coul = "#585858";
                for (var k in data.timeline[i].annotations) {
                    if (data.timeline[i].annotations[k].indexOf(data.timeline[i].tweets[j]) != -1) {
                        coul = colors[k];
                    }
                }
                tmptw[data.timeline[i].tweets[j]].y = i;
                tmptw[data.timeline[i].tweets[j]].x = j;
                paper.rect(scaleX * j + (scaleX > 3 ? .5 : 0), 600 - ((i+1) * scaleY) + (scaleY > 3 ? .5 : 0), scaleX - (scaleX > 3 ? 1 : 0), scaleY - (scaleY > 3 ? 1 : 0)).attr({"stroke":"none","fill":coul});
            }
        }
        for (var i = 0; i < data.arcs.length; i++) {
            var x1 = scaleX * (tmptw[data.arcs[i].from].x + .5),
                x2 = scaleX * (tmptw[data.arcs[i].to].x + .5),
                y1 = 600 - scaleY * (tmptw[data.arcs[i].from].y + .5),
                y2 = 600 - scaleY * (tmptw[data.arcs[i].to].y + .5),
                d = "M"+x1+" "+y1+"C";
            if (y1 == y2) {
                d += x1+" "+(y1 - 60)+" "+x2+" "+(y2 - 60);
            } else {
                d += (x1 + 60)+" "+y1+" "+(x2 + 60)+" "+y2;
            }
            paper.path(d+" "+x2+" "+y2);
        }
        showTimeWindow();
    });
});