# HG changeset patch # User Raphael Velt # Date 1321378184 -3600 # Node ID 0c4acfa2aea10040e5a5437562c91859ac3cafb0 # Parent 66092f867c039c6ac3f4c6faf652c7b73377f57f Minor changes diff -r 66092f867c03 -r 0c4acfa2aea1 tweetcast/nodejs-bis/client/css/style.css --- 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 { diff -r 66092f867c03 -r 0c4acfa2aea1 tweetcast/nodejs-bis/client/js/script.js --- 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 = ''; html += '
' + a_user + ''; if (className == 'full') { - html += '

' + new Date(tweet.created_at).toLocaleTimeString() + '

'; + html += '

' + new Date(tweet.date_value).toLocaleTimeString() + '

'; } html += '
'; 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 '' + t.word + '' }).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 diff -r 66092f867c03 -r 0c4acfa2aea1 tweetcast/nodejs-bis/conf.js --- /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 diff -r 66092f867c03 -r 0c4acfa2aea1 tweetcast/nodejs-bis/tweetcast.js --- 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(); }