# HG changeset patch # User Raphael Velt # Date 1319731073 -7200 # Node ID 6a073c4a8578bc7faa904680cbc055d857b09cb8 # Parent 60dff8a71024b09d0e586d87abdf333edc8d7dde Minor changes and bug fixes diff -r 60dff8a71024 -r 6a073c4a8578 tweetcast/nodejs/client/css/style.css --- a/tweetcast/nodejs/client/css/style.css Wed Oct 26 18:29:46 2011 +0200 +++ b/tweetcast/nodejs/client/css/style.css Thu Oct 27 17:57:53 2011 +0200 @@ -31,6 +31,10 @@ position: absolute; left: 50%; top: 50%; margin-left: -236px; margin-top: -300px; } +#modeselector { + position: absolute; width: 240px; top: 600px; border: 1px solid #999; font-size: 18px; text-align: center; padding: 5px; +} + #tweetlist { position: absolute; left: 0; top: 0; width: 250px; height: 600px; list-style: none; border: 1px solid #999; } @@ -40,7 +44,7 @@ } li.tweet { - position: relative; background: #fff; + position: relative; background: #fff; cursor: pointer; } li.full { diff -r 60dff8a71024 -r 6a073c4a8578 tweetcast/nodejs/client/index.html --- a/tweetcast/nodejs/client/index.html Wed Oct 26 18:29:46 2011 +0200 +++ b/tweetcast/nodejs/client/index.html Thu Oct 27 17:57:53 2011 +0200 @@ -13,6 +13,7 @@
+
Mode Flux
diff -r 60dff8a71024 -r 6a073c4a8578 tweetcast/nodejs/client/js/script.js --- a/tweetcast/nodejs/client/js/script.js Wed Oct 26 18:29:46 2011 +0200 +++ b/tweetcast/nodejs/client/js/script.js Thu Oct 27 17:57:53 2011 +0200 @@ -4,12 +4,18 @@ tweetData = { "tweetcount" : 0, "position" : 0, + "feedMode" : true, "followLast" : true, "zoomLevel" : 3, "timeLevel" : 2, "tweets" : [], "posIndex" : [], - "tlChanged" : true + "tlChanged" : true, + "tlLevelChanged" : true, + "blockUpdate" : false, + "waitUpdate" : true, + "htmlBuffer" : '', + "wheelDelta" : 0 }, displaySplitting = [ { @@ -20,17 +26,28 @@ positions : [ 1, 3, 5, 7, 13, 33, 53 ], classNames : [ 'full', 'half', 'half fade', 'quarter', 'quarter fade', 'icons', 'icons fade' ] } - ], - blockUpdate = false, - waitUpdate = true, - wheeldelta = 0; + ]; function placeHolder(className) { return '
  • '; } +function clicTweet(tweetPos) { + if (tweetPos != tweetData.position) { + tweetData.position = tweetPos; + tweetData.followLast = (tweetData.position == tweetData.tweetcount); + return false; + } else { + console.log("getting linkedTweets"); + socket.emit('linkedTweets',{"tweetpos":tweetPos}); + } +} + function tweetToHtml(tweet, className) { - html = '
  • 50) { + tweetData.splice(0,1); } - tweetData.arcs = data.arcs; - drawDisplay(); + drawTimeLine(); + }); + socket.on('linkedTweets', function(data) { + console.log(data); }); $("#tweetlist").mousewheel(function(e, d) { - wheeldelta += d; - if (Math.abs(wheeldelta) >= 1) { - tweetData.position = Math.min( tweetData.tweetcount, Math.max(1, parseInt(wheeldelta) + tweetData.position ) ); + tweetData.wheelDelta += d; + if (Math.abs(tweetData.wheelDelta) >= 1) { + tweetData.position = Math.min( tweetData.tweetcount, Math.max(1, parseInt(tweetData.wheelDelta) + tweetData.position ) ); tweetData.followLast = (tweetData.position == tweetData.tweetcount); updateDisplay(); - wheeldelta = 0; + tweetData.wheelDelta = 0; } return false; }); $("#timewindow").mousewheel(function(e, d) { - wheeldelta += d; - if (Math.abs(wheeldelta) >= 1) { - if (wheeldelta > 0) { + tweetData.wheelDelta += d; + if (Math.abs(tweetData.wheelDelta) >= 1) { + if (tweetData.wheelDelta > 0) { setTimeZoom(tweetData.timeLevel + 1); } else { setTimeZoom(tweetData.timeLevel - 1); } - wheeldelta = 0; + tweetData.wheelDelta = 0; } return false; }); + $("#timewindow").click(function(evt) { + var offsetY = evt.pageY - $(this).offset().top, + dtfintl = tweetData.timeline[ tweetData.timeline.length - 1 ].end, + dtdebtl = tweetData.timeline[0].start, + clicTime = dtdebtl + (1 - ( offsetY / 600 ) ) * ( dtfintl - dtdebtl ); + socket.emit('tweetPosByDate',{ date: clicTime }); + }); }); \ No newline at end of file diff -r 60dff8a71024 -r 6a073c4a8578 tweetcast/nodejs/node-direct.js --- a/tweetcast/nodejs/node-direct.js Wed Oct 26 18:29:46 2011 +0200 +++ b/tweetcast/nodejs/node-direct.js Thu Oct 27 17:57:53 2011 +0200 @@ -53,19 +53,19 @@ var requete = "CREATE TABLE IF NOT EXISTS tweets ( pos INTEGER PRIMARY KEY, tweet_id TEXT UNIQUE, created_at INTEGER, json TEXT" + annotationMap(function(a) { return ', a_' + a + ' INTEGER' }).join("") + " )"; db.execute(requete, function(err) { - if (err) throw err; - db.execute("CREATE INDEX IF NOT EXISTS idx_created_at ON tweets ( created_at )", function(err) { if (err) throw err; }); + if (err) { myLog("SQLITE error",err.stack); } + db.execute("CREATE INDEX IF NOT EXISTS idx_created_at ON tweets ( created_at )", function(err) { if (err) { myLog("SQLITE error",err.stack); } }); getSendLastPos(); }); - db.execute("CREATE TABLE IF NOT EXISTS tweet_refs ( id INTEGER PRIMARY KEY, from_id TEXT, to_id TEXT, ref_type TEXT )", function(err) { if (err) throw err; }); + db.execute("CREATE TABLE IF NOT EXISTS tweet_refs ( id INTEGER PRIMARY KEY, from_id TEXT, to_id TEXT, ref_type TEXT )", function(err) { if (err) { myLog("SQLITE error",err.stack); } }); } function commitReference(from_id, to_id, ref_type) { db.execute( "INSERT INTO tweet_refs ( from_id, to_id, ref_type ) VALUES ( ?, ?, ? )", [ from_id, to_id, ref_type ], - function(err) { if (err) throw err; } + function(err) { if (err) { myLog("SQLITE error",err.stack); } } ); } @@ -113,7 +113,7 @@ + " )", [ tweet.id, tweet.created_at.valueOf(), JSON.stringify(tweet) ].concat(annotationMap(function(a) { return ann.indexOf(a) == -1 ? 0 : 1 })), function(err) { - if (err) throw err; + if (err) { myLog("SQLITE error",err.stack); } getSendLastPos(); } ); @@ -126,47 +126,136 @@ commitTweet(newdata[i]); } } - myLog("New tweets received"); +// myLog("New tweets received"); +} + +function requestTwitter() { + myLog("Requesting Twitter to track keyword(s): "+tracking_keyword); + var req = https.request({ + host: "stream.twitter.com", + path: "/1/statuses/filter.json", + method: "POST", + headers: { + 'Authorization': 'Basic ' + new Buffer( TWITTER_USER + ":" + TWITTER_PASS ).toString('base64'), + 'Content-Type': 'application/x-www-form-urlencoded' + } + }, function(res) { + myLog('Reply from stream.twitter.com: ' + res.statusCode); + myLog('Headers: ' + JSON.stringify(res.headers)); + res.setEncoding('utf8'); + res.on('data', callBackNewTweets); + res.on('end', function() { + myLog('End Twitter Connection — Trying to reconnect'); + requestTwitter(); + }); + }); + + req.write('track=' + encodeURIComponent(tracking_keyword)); + req.socket.setTimeout(60000); + req.socket.on('timeout', function() { + myLog('TimeOut — Trying to reconnect'); + requestTwitter(); + }); + req.end(); } function getSendLastPos() { db.execute("SELECT MAX(pos) lastpos FROM tweets", function (err, results) { - if (err) throw err; - lastpos = results[0].lastpos ? results[0].lastpos : 0; - myLog("Broadcasting last pos = ",lastpos); - io.sockets.emit('tweetSummary', { - tweetcount : lastpos - }); + if (err) { myLog("SQLITE error",err.stack); } + if (results[0].lastpos != lastpos) { + lastpos = results[0].lastpos ? results[0].lastpos : 0; +// myLog("Broadcasting last pos = ",lastpos); + try { + io.sockets.emit('tweetSummary', { + tweetcount : lastpos + }); + } catch(err) { + myLog("SOCKET.IO error while Broadcasting tweetSummary",err.stack); + } + } + }); +} + +function getSendTweetPosByDate(date, socket) { + db.execute("SELECT pos, created_at, ABS(created_at-" + date + ") AS dist FROM tweets ORDER BY dist ASC LIMIT 0,1", function (err, results) { + if (err) { myLog("SQLITE error",err.stack); } + if (results.length) { + try { + socket.emit('tweetPosByDate', { + tweetpos : results[0].pos, + date : results[0].created_at + }); + } catch(err) { + myLog("SOCKET.IO error while sending tweetPosByDate",err.stack); + } + } }); } - -function getSendTweets(posList, socket) { - myLog("request for tweets ("+posList.join(',')+") from "+socket.id); - db.execute("SELECT * FROM tweets WHERE pos IN ( " + posList.join(',') + " )", function (err, results) { - if (err) throw err; - socket.emit('tweets', - results.map(function(line) { - var tw = JSON.parse(line.json); - tw.pos = line.pos; - return tw; - }) - ); +function getSendLinkedTweets(pos, socket) { + myLog("request for tweets linked to",pos); + db.execute("SELECT A.pos pos_a, A.tweet_id id_a, A.json json_a, B.pos pos_b, B.tweet_id id_b, B.json json_b, ref_type FROM tweets A, tweets B, tweet_refs WHERE id_a = from_id AND id_b = to_id AND (pos_a = ? OR pos_b = ?)", [ pos, pos ], function(err, results) { + if (err) { myLog("SQLITE error: ",err.stack); } + var struct = { + "tweetpos" : pos, + "referencing" : [], + "referenced_by" : [] + }; + for (var i in results) { + if (results[i].pos_a == pos) { + var tw = JSON.parse(results[i].json_b); + tw.pos = results[i].pos_b; + struct.referencing.push({ + "tweet" : tw, + "ref_type" : results[i].ref_type + }); + } else { + var tw = JSON.parse(results[i].json_a); + tw.pos = results[i].pos_a; + struct.referenced_by.push({ + "tweet" : tw, + "ref_type" : results[i].ref_type + }); + } + } + try { + socket.emit('linkedTweets', struct); + } catch(err) { + myLog("SOCKET.IO error while sending linkedTweets: ",err.stack); + } }); } -function getSendTimeline(level, socket) { - myLog("request for timeline ("+level+") from "+socket.id); - var lvl = date_levels[level], +function getSendTweets(posList, socket) { +// myLog("request for tweets ("+posList.join(',')+") from "+socket.id); + db.execute("SELECT * FROM tweets WHERE pos IN ( " + posList.join(',') + " )", function (err, results) { + if (err) { myLog("SQLITE error",err.stack); } + try { + socket.emit('tweets', + results.map(function(line) { + var tw = JSON.parse(line.json); + tw.pos = line.pos; + return tw; + }) + ); + } catch (err) { + myLog("SOCKET.IO error while sending tweets",err.stack); + } + }); +} + +function getSendTimeline(data, socket) { + myLog("request for timeline (",data.level, data.full,") from "+socket.id); + var lvl = date_levels[data.level], requete = "SELECT COUNT(*) AS nb, " + lvl + "*ROUND(created_at/" + lvl + ") AS tranche" + annotationMap(function (a) { return " , SUM(a_" + a + ") AS s_" + a }).join("") - + " FROM tweets GROUP BY tranche ORDER BY tranche DESC LIMIT 0,50"; + + " FROM tweets GROUP BY tranche ORDER BY tranche DESC LIMIT 0," + ( data.full ? "50" : "1" ); db.execute(requete, function (err, results) { - if (err) throw err; + if (err) { myLog("SQLITE error",err.stack); } var tbl = [], lastend = parseInt(results[results.length - 1].tranche); for (var i = results.length - 1; i >= 0; i--) { @@ -188,16 +277,35 @@ } tbl.push(struct); } - socket.emit('timeline', tbl); + try { + socket.emit('timeline', { + "full" : data.full, + "level" : data.level, + "data" : tbl + }); + } catch (err) { + myLog("SOCKET.IO error while sending timeline",err.stack); + } }); } function textids(object) { for (var key in object) { + // Workaround for Unicode bug in socket.io. + + if (typeof object[key] == "string") { + var tmp = ''; + for (var i = 0; i < object[key].length; i++) { + tmp += ( object[key].charCodeAt(i) < 128 ? object[key].charAt(i) : "&#" + object[key].charCodeAt(i) + ";" ); + } + object[key] = tmp; + } + if (key.substr(-2) == 'id') { object[key] = object[key + '_str']; delete object[key + '_str']; } + } } @@ -292,41 +400,34 @@ myLog("Listening on port: "+app_port); myLog("Opening SQLITE file: "+sqlfile); db.open(sqlfile , function(err) { - if (err) throw err; + if (err) { myLog("SQLITE error",err.stack); } createTables(); }); if (RECORD_NEW_TWEETS) { - myLog("Requesting Twitter to track keyword(s): "+tracking_keyword); - var req = https.request({ - host: "stream.twitter.com", - path: "/1/statuses/filter.json", - method: "POST", - headers: { - 'Authorization': 'Basic ' + new Buffer( TWITTER_USER + ":" + TWITTER_PASS ).toString('base64'), - 'Content-Type': 'application/x-www-form-urlencoded' - } - }, function(res) { - myLog('Reply from stream.twitter.com: ' + res.statusCode); - myLog('Headers: ' + JSON.stringify(res.headers)); - res.setEncoding('utf8'); - res.on('data', callBackNewTweets); - }); - - req.write('track=' + encodeURIComponent(tracking_keyword)); - req.end(); + requestTwitter(); } io.set('log level', 0); io.sockets.on('connection', function(socket) { myLog("New connection from", socket.handshake.address.address, "with id=", socket.id); - socket.emit('tweetSummary', { tweetcount : lastpos }); + try { + socket.emit('tweetSummary', { tweetcount : lastpos }); + } catch (err) { + myLog("SOCKET.IO error while sending tweetSummary",err.stack); + } socket.on('updateTweets', function(data) { if (data.tweets.length) { getSendTweets(data.tweets, socket); } }); socket.on('updateTimeline', function(data) { - getSendTimeline(data.level, socket); + getSendTimeline(data, socket); + }); + socket.on('tweetPosByDate', function(data) { + getSendTweetPosByDate(data.date, socket); + }); + socket.on('linkedTweets', function(data) { + getSendLinkedTweets(data.tweetpos, socket); }); }); \ No newline at end of file