Minor changes
authorRaphael Velt <raph.velt@gmail.com>
Tue, 15 Nov 2011 18:29:44 +0100
changeset 374 0c4acfa2aea1
parent 373 66092f867c03
child 375 cd6eedf7d2a5
Minor changes
tweetcast/nodejs-bis/client/css/style.css
tweetcast/nodejs-bis/client/js/script.js
tweetcast/nodejs-bis/conf.js
tweetcast/nodejs-bis/tweetcast.js
--- a/tweetcast/nodejs-bis/client/css/style.css	Tue Nov 15 11:38:14 2011 +0100
+++ b/tweetcast/nodejs-bis/client/css/style.css	Tue Nov 15 18:29:44 2011 +0100
@@ -65,7 +65,7 @@
 }
 
 .full p.tweet_text {
-	font-size: 12px; margin: 5px 0 5px 58px; height: 75px; width: 207px;
+	font-size: 12px; margin: 5px 0 5px 58px; height: 75px; width: 207px; color: #000000;
 }
 
 .half p.tweet_text {
--- a/tweetcast/nodejs-bis/client/js/script.js	Tue Nov 15 11:38:14 2011 +0100
+++ b/tweetcast/nodejs-bis/client/js/script.js	Tue Nov 15 18:29:44 2011 +0100
@@ -18,12 +18,17 @@
         deltaX : 30,
         tlWidth : 98,
         tlHeight : 450,
-        globalWords : {}
+        globalWords : {},
+        refMouse : { x : 0, y : 0},
+        refPosTl : { x : 0, y : 0},
+        tlMouseMoved : false,
+        tlMouseClicked : false
         },
     tlBuffer = '',
     relHover = null,
     wheelDelta = 0,
-    rx_word = /[^ \.&;,'"!\?@#\d\(\)\+\[\]\\\…\-«»:\/]{3,}/g;
+    rx_word = /[^ \.&;,'"!\?@#\d\(\)\+\[\]\\\…\-«»:\/]{3,}/g,
+    stop_list = [ 'and', 'les', 'the' ];
 
 function arc(source, target) {
     var x3 = .3 * target.y - .3 * source.y + .8 * source.x + .2 * target.x;
@@ -34,13 +39,15 @@
 }
 
 function countWords(text, wordobj) {
-    var tab = text.match(rx_word);
+    var tab = text.replace(/https?:\/\/[0-9a-zA-Z\.%\/-_]+/g,'').match(rx_word);
     for (var i in tab) {
         var word = tab[i].toLowerCase();
-        if (wordobj[word]) {
-            wordobj[word]++;
-        } else {
-            wordobj[word] = 1;
+        if (stop_list.indexOf(word) == -1 && tracking_keywords.indexOf(word) == -1) {
+            if (wordobj[word]) {
+                wordobj[word]++;
+            } else {
+                wordobj[word] = 1;
+            }
         }
     }
 }
@@ -61,12 +68,26 @@
         }
     }
     
-    var tab = tweet.text.split(/\&\#|\;/),
-        txt = '';
-    for (i = 0; i < tab.length; i++) {
-        txt += (i % 2 && parseInt(tab[i]) != NaN) ? String.fromCharCode(tab[i]) : tab[i];
+    var ann = [];
+    for (var j in annotations) {
+        if (j != "default") {
+            for (var k in annotations[j].keywords) {
+                if (tweet.text.search(annotations[j].keywords[k]) != -1) {
+                    ann.push(j);
+                    break;
+                }
+            }
+        }
     }
-    tweet.text = txt;
+    tweet.annotations = ann;
+    
+    var tab = tweet.text.match(/\&\#[0-9]+\;/g);
+    for (var i in tab) {
+        var n = parseInt(tab[i].substr(2));
+        if (n != NaN) {
+            tweet.text = tweet.text.replace(tab[i], String.fromCharCode(n   ));
+        }
+    }
     
     twCx.tweets.push(tweet);
     twCx.idIndex.push(tweet.id);
@@ -80,7 +101,7 @@
     
     countWords(tweet.text, twCx.globalWords);
     
-    var creadate = new Date(tweet.created_at).valueOf();
+    var creadate = tweet.date_value;
     if (!twCx.timeline.length) {
         twCx.timeline = [ populateDateStruct(0, twCx.date_levels[0] * parseInt(creadate / twCx.date_levels[0])) ]
     }
@@ -155,7 +176,7 @@
 }
 
 function insertIntoDateStruct(slices, tweet) {
-    var creadate = new Date(tweet.created_at).valueOf();
+    var creadate = tweet.date_value;
     for (var i in slices) {
         if (creadate < slices[i].end) {
             if (slices[i].slices) {
@@ -212,7 +233,7 @@
     a_user = '<a href="http://twitter.com/' + tweet.user.screen_name + '" var target="_blank" title="' + tweet.user.name + '">';
     html += '<div class="around_img">' + a_user + '<img class="profile_image" src="' + tweet.user.profile_image_url + '" /></a>';
     if (className == 'full') {
-        html += '<p class="created_at">' + new Date(tweet.created_at).toLocaleTimeString() + '</p>';
+        html += '<p class="created_at">' + new Date(tweet.date_value).toLocaleTimeString() + '</p>';
     }
     html += '</div>';
     if (className != 'icons') {
@@ -252,15 +273,16 @@
     return html;
 }
 
-function tlIdFromPos(x, y) {
-    if (x < twCx.deltaX) {
-        return null;
-    }
-    var ligne = Math.floor(( twCx.tlHeight - y ) / twCx.scaleY),
+function tlIdFromPos(x, y, outside) {
+    var ligne = Math.min( twCx.tlOnDisplay.length - 1, Math.max( 0, Math.floor(( twCx.tlHeight - y ) / twCx.scaleY) ) ),
         colonne = Math.floor(( x - twCx.deltaX ) / twCx.scaleX ),
         l = 0;
-    if (colonne >= twCx.tlOnDisplay[ligne].totalTweets) {
-        return null;
+    if (colonne >= twCx.tlOnDisplay[ligne].totalTweets || colonne < 0 ) {
+        if (outside) {
+            colonne = Math.min( twCx.tlOnDisplay[ligne].totalTweets - 1, Math.max( 0, colonne ));
+        } else {
+            return null;
+        }
     }
     for (var i in twCx.tlOnDisplay[ligne].displayData) {
         var nl = l + twCx.tlOnDisplay[ligne].displayData[i].length;
@@ -277,7 +299,7 @@
 function tlPosTweet(tweet, annotation) {
     var x,
         y,
-        dt = new Date(tweet.created_at).valueOf(),
+        dt = tweet.date_value,
         ann = ( annotation ? annotation : ( tweet.annotations.length ? tweet.annotations[0] : 'default' ) );
     for (var i = 0; i < twCx.tlOnDisplay.length; i++) {
         if (twCx.tlOnDisplay[i].end > dt) {
@@ -318,7 +340,7 @@
 
 function drawTweetPos(pos, color) {
     var rel = tlPaper.rect(pos.x - .5 * twCx.scaleX, pos.y - .5 * twCx.scaleY, twCx.scaleX, twCx.scaleY);
-    rel.attr({ "stroke" : color });
+    rel.attr({ "stroke" : color, "fill" : color, "fill-opacity" : .1 });
     return rel;
 }
 
@@ -326,6 +348,7 @@
     var p = twCx.position,
         l = twCx.tweets.length,
         lines = 0,
+        ppy = 0,
         html = '',
         tweetsOnDisplay = [],
         localWords = {};
@@ -342,18 +365,21 @@
     
     if (l > p + 18) {
         lines++;
+        ppy += 20;
         for (var i = p + 31; i >= p + 18; i--) {
             pushTweet(i, 'icons');
         }
     }
     if (l > p + 4) {
         lines++;
+        ppy += 20;
         for (var i = p + 17; i >= p + 4; i--) {
             pushTweet(i, 'icons');
         }
     }
     for (var k = 3; k >= 1; k--) {
         if (l > p + k) {
+            ppy += 47;
             lines++;
             pushTweet(p + k, 'half');
         }
@@ -387,19 +413,22 @@
     for (var j in localWords) {
         tab.push({
             "word": j,
-            "freq" : localWords[j] / Math.log(twCx.globalWords[j])
+            "freq" : localWords[j] / Math.log(1+twCx.globalWords[j])
         });
     }
-    tab.sort( function(a,b){ return b.freq - a.freq }).splice(10);
-    $("#motscles").html(tab.map(function(t) { return t.word }).join(", "))
+    tab.sort( function(a,b){ return ( b.freq - a.freq ) }).splice(15);
+    var minfreq = tab[tab.length - 1].freq,
+        maxfreq = Math.max(minfreq + .1, tab[0].freq),
+        echfreq = 8 / Math.sqrt( maxfreq - minfreq );
+    $("#motscles").html(tab.map(function(t) { return '<span style="font-size: ' + Math.floor( ( 12 + Math.sqrt( t.freq - minfreq ) * echfreq ) ) + 'px">' + t.word + '</span>' }).join(" "))
     
     twCx.tlOnDisplay = trimFDS(flattenDateStruct(twCx.timeline, twCx.timeLevel));
     twCx.scaleY = twCx.tlHeight / twCx.tlOnDisplay.length;
     var maxTweets = 0,
         startTl = 0,
         endTl = 0,
-        startTw = new Date(twCx.tweets[tweetsOnDisplay[tweetsOnDisplay.length - 1]].created_at).valueOf(),
-        endTw = new Date(twCx.tweets[tweetsOnDisplay[0]].created_at).valueOf();
+        startTw = twCx.tweets[tweetsOnDisplay[tweetsOnDisplay.length - 1]].date_value,
+        endTw = twCx.tweets[tweetsOnDisplay[0]].date_value;
     for (var i = 0; i < twCx.tlOnDisplay.length; i++) {
         if (startTw >= twCx.tlOnDisplay[i].start && startTw < twCx.tlOnDisplay[i].end) {
             startTl = i;
@@ -436,6 +465,13 @@
     tlPaper.clear();
     relHover = null;
     
+    // Dessin de la correspondance liste-timeline
+    
+    var startY = twCx.tlHeight - startTl * twCx.scaleY,
+        endY = twCx.tlHeight - ( endTl + 1 ) * twCx.scaleY,
+        path = "M0 " + twCx.tlHeight + "C" + .7*twCx.deltaX + " " + twCx.tlHeight + " " + .3*twCx.deltaX + " " + startY + " " + twCx.deltaX + " " + startY + "L" + twCx.tlWidth + " " + startY + "L" + twCx.tlWidth + " " + endY + "L" + twCx.deltaX + " " + endY + "C" + .3*twCx.deltaX + " " + endY + " " + .7*twCx.deltaX + " 0 0 0";
+    tlPaper.path( path ).attr({ "stroke" : "none", "fill" : "#000080", "opacity" : .2 });
+        
     // dessin de la date de début
     
     tlPaper.text(2, twCx.tlHeight - 7, new Date(twCx.tlOnDisplay[0].start).toTimeString().substr(0,5))
@@ -466,19 +502,19 @@
         }
     }
     
-    // Dessin de la correspondance liste-timeline
+    // dessin du tweet courant
     
-    var startY = twCx.tlHeight - startTl * twCx.scaleY,
-        endY = twCx.tlHeight - ( endTl + 1 ) * twCx.scaleY;
-    tlPaper.path("M0 " + twCx.tlHeight + "L" + twCx.deltaX + " " + startY + "L" + twCx.tlWidth + " " + startY + "L" + twCx.tlWidth + " " + endY + "L" + twCx.deltaX + " " + endY + "L0 0" )
-        .attr({ "stroke" : "none", "fill" : "#000080", "opacity" : .1 });
     
-    // dessin du tweet courant
-        
     var posp = tlPosTweet(twCx.tweets[p]);
     if (posp) {
         
         drawTweetPos(posp, "#ffff00");
+        var yy = posp.y - .5 * twCx.scaleY,
+            path = "M0 " + ppy + "C" + ( .7 * twCx.deltaX ) + " " + ppy + " " + ( .2 * twCx.deltaX ) + " " + yy + " " + ( twCx.deltaX ) + " " + yy + "L" + ( posp.x - .5 * twCx.scaleX ) + " " + yy;
+        yy = posp.y + .5 * twCx.scaleY;
+        ppy += 84;
+        path += "L" + ( posp.x - .5 * twCx.scaleX ) + " " + yy + "L" + twCx.deltaX + " " + yy + "C"  + ( .2 * twCx.deltaX ) + " " + yy + " " + ( .7 * twCx.deltaX ) + " " + ppy + " 0 " + ppy;
+        tlPaper.path( path ).attr({"stroke":"#ffff00", "fill" : "#ffff00", "fill-opacity" : .2});
         
         // dessin des liens entre tweets
         
@@ -517,6 +553,30 @@
     
 }
 
+function clicTl(evt) {
+    var o = $("#timeline").offset();
+    if (twCx.tlMouseClicked && twCx.tlMouseMoved) {
+        var twid = tlIdFromPos(evt.pageX - o.left + twCx.refPosTl.x - twCx.refMouse.x, evt.pageY - o.top + twCx.refPosTl.y - twCx.refMouse.y, true);
+        if (twid) {
+            selectTweet(twid.id);
+        }
+    } else {
+        var twid = tlIdFromPos(evt.pageX - o.left, evt.pageY - o.top, twCx.tlMouseClicked);
+        if (twCx.tlMouseMoved && !twCx.tlMouseClicked) { 
+            if (twid) {
+                rolloverTweet(twid.id, twid.annotation);
+            } else {
+                $("#hovertweet").hide();
+            }
+        }
+        if (twCx.tlMouseClicked && !twCx.tlMouseMoved) {
+            if (twid) {
+                selectTweet(twid.id);
+            }
+        }
+    }
+}
+
 $(document).ready(function() {
     tlPaper = Raphael("timeline", twCx.tlWidth, twCx.tlHeight);
     socket = io.connect('http://' + document.location.hostname );
@@ -570,20 +630,24 @@
         return false;
     });
     $("#timeline, #tweetlist").mouseout(function() {
+        twCx.tlMouseClicked = false;
+        twCx.tlMouseMoved = false;
         $("#hovertweet").hide();
     });
     $("#timeline").mousemove(function(evt) {
-        var twid = tlIdFromPos(evt.offsetX, evt.offsetY);
-        if (twid) {
-            rolloverTweet(twid.id, twid.annotation);
-        } else {
-            $("#hovertweet").hide();
-        }
+        twCx.tlMouseMoved = true;
+        clicTl(evt);
     });
-    $("#timeline").click(function(evt) {
-        var twid = tlIdFromPos(evt.offsetX, evt.offsetY);
-        if (twid) {
-            selectTweet(twid.id);
-        }
+    $("#timeline").mousedown(function(evt) {
+        twCx.tlMouseClicked = true;
+        twCx.tlMouseMoved = false;
+        var o = $(this).offset();
+        twCx.refMouse = { x : evt.pageX - o.left, y : evt.pageY - o.top };
+        twCx.refPosTl = tlPosTweet(twCx.tweets[twCx.position]) || twCx.refMouse;
+    });
+    $("#timeline").mouseup(function(evt) {
+        clicTl(evt);
+        twCx.tlMouseClicked = false;
+        twCx.tlMouseMoved = false;
     });
 });
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tweetcast/nodejs-bis/conf.js	Tue Nov 15 18:29:44 2011 +0100
@@ -0,0 +1,40 @@
+/* CONFIGURATION FILE USED BY BOTH CLIENT AND SERVER */
+
+var app_port = 8000,
+    tracking_keywords = [ "sarkozy" ],
+    annotations = {
+        "default" : {
+            "colors" : {
+                "tweet" : "#ffffff",
+                "timeline" : "#585858"
+            }
+        },
+        "positive" : {
+            "keywords" : [ /\+\+/ ],
+            "colors" : {
+                "tweet" : "#c5e7cd",
+                "timeline" : "#1D973D"
+            }
+        },
+        "negative" : {
+            "keywords" : [ /\-\-/ ],
+            "colors" : {
+                "tweet" : "#f6ced0",
+                "timeline" : "#CE0A15"
+            }
+        },
+        "reference" : {
+            "keywords" : [ /\=\=/ ],
+            "colors" : {
+                "tweet" : "#ecedc1",
+                "timeline" : "#C5A62D"
+            }
+        },
+        "question" : {
+            "keywords" : [ /\?\?/ ],
+            "colors" : {
+                "tweet" : "#bfdbec",
+                "timeline" : "#036AAE"
+            }
+        }
+    }
\ No newline at end of file
--- a/tweetcast/nodejs-bis/tweetcast.js	Tue Nov 15 11:38:14 2011 +0100
+++ b/tweetcast/nodejs-bis/tweetcast.js	Tue Nov 15 18:29:44 2011 +0100
@@ -6,7 +6,7 @@
 eval(fs.readFileSync(conf_file,'utf8'));
 
 tweet_file = flagOption('-f', (typeof tweet_file == "undefined" ? 'tweets.txt' : tweet_file ));
-tracking_keyword = flagOption('-t', (typeof tracking_keyword == "undefined" ? null : tracking_keyword ));
+tracking_keyword = flagOption('-t', (typeof tracking_keywords == "undefined" ? null : tracking_keywords.join(",") ));
 user_pass = flagOption('-u', (typeof user_pass == "undefined" ? null : user_pass ));
 
 if (!user_pass) {
@@ -75,18 +75,27 @@
         'profile_background_color',
         'profile_background_tile',
         'utc_offset'
-    ];
+    ],
+    content_types = {
+        css : "text/css",
+        html : "text/html",
+        js : "text/javascript",
+        png : "image/png"
+    };
 
 function httpHandler(req, res) {
     console.log("HTTP Request for URL "+req.url);
-    var url = ( req.url == "/config" ? conf_file : __dirname + "/client" + req.url + ( req.url[req.url.length - 1] == "/" ? "index.html" : "" ) );
+    var url = ( req.url == "/config" ? conf_file : __dirname + "/client" + req.url + ( req.url[req.url.length - 1] == "/" ? "index.html" : "" ) ),
+        ext = url.split('.').slice(-1)[0].toLowerCase();
     fs.readFile( url, function(err, data) {
         if (err) {
             console.log("Error 404");
             res.writeHead(404);
             return res.end('File not found');
         }
-        res.writeHead(200);
+        res.writeHead(200, {
+            "Content-Type" : ( content_types[ext] ? content_types[ext] : 'text/plain' )
+        });
         res.end(data);
     });
 }
@@ -101,6 +110,7 @@
         console.log("Error: Tweet already in list");
         return false;
     }
+    tweet.date_value = new Date(tweet.created_at).valueOf();
     tweets.push(tweet);
     tweet_ids.push(tweet.id);
     return true;
@@ -163,19 +173,7 @@
             try {
                 for (var i in newdata) {
                     if (newdata[i].length > 0) {
-                        var tweet = JSON.parse(newdata[i]),
-                            ann = [];
-                        for (var j in annotations) {
-                            if (j != "default") {
-                                for (var k in annotations[j].keywords) {
-                                    if (tweet.text.search(annotations[j].keywords[k]) != -1) {
-                                        ann.push(j);
-                                        break;
-                                    }
-                                }
-                            }
-                        }
-                        tweet.annotations = ann;
+                        var tweet = JSON.parse(newdata[i]);
                         textids(tweet);
                         for (var j in keys_to_delete) {
                             delete tweet[keys_to_delete[j]];
@@ -206,7 +204,7 @@
         });
     });
     
-    req.write('track=' + encodeURIComponent((tracking_keyword)));
+    req.write('track=' + encodeURIComponent( ( tracking_keyword ) ) );
     req.end();
 }