tweetcast/nodejs/client/js/script.js
changeset 331 03c69425efa6
parent 326 c28048fb63b4
equal deleted inserted replaced
329:5064d838962b 331:03c69425efa6
     1 var socket,
     1 var socket,
     2     paper,
     2     tlPaper,
       
     3     twPaper,
     3     tweetData = {
     4     tweetData = {
     4         "tweetcount" : 0,
     5         "tweetcount" : 0,
     5         "position" : -1,
     6         "position" : 0,
       
     7         "followLast" : true,
     6         "zoomLevel" : 3,
     8         "zoomLevel" : 3,
     7         "timeLevel" : 2
     9         "timeLevel" : 2,
       
    10         "tweets" : [],
       
    11         "posIndex" : [],
       
    12         "tlChanged" : true
     8     },
    13     },
     9     zoomLevels = [
       
    10         {
       
    11             "description" : "160 tweets per page",
       
    12             "className" : "icons",
       
    13             "displayCount" : 160
       
    14         },
       
    15         {
       
    16             "description" : "24 tweets per page",
       
    17             "className" : "quarter",
       
    18             "displayCount" : 24
       
    19         },
       
    20         {
       
    21             "description" : "12 tweets per page",
       
    22             "className" : "half",
       
    23             "displayCount" : 12
       
    24         },
       
    25         {
       
    26             "description" : "6 tweets per page",
       
    27             "className" : "full",
       
    28             "displayCount" : 6
       
    29         }
       
    30     ],
       
    31     colors = {
    14     colors = {
    32         'positive' : "#1D973D",
    15         'positive' : "#1D973D",
    33         'reference' : "#C5A62D",
    16         'reference' : "#C5A62D",
    34         'negative' : "#CE0A15",
    17         'negative' : "#CE0A15",
    35         'question' : "#036AAE"
    18         'question' : "#036AAE",
       
    19         'neutre' : "#585858"
    36     },
    20     },
    37     timeWindow;
    21     annotations = {
    38 
    22         'positive' : '++',
    39 function tweetToHtml(tweet) {
    23         'negative' : '--',
    40 	html = '<li class="tweet ' + zoomLevels[tweetData.zoomLevel].className;
    24         'reference' : '==',
    41 	for (var i in tweet.annotations) {
    25         'question' : '??'
    42 		html += ' a_' + tweet.annotations[i]
    26     },
    43 	}
    27     displaySplitting = [
    44 	html += '" id="tweet_' + tweet.id + '">';
    28         {
       
    29             positions : [ 10, 20, 22, 24, 25, 26, 27, 28, 29, 31, 33, 43, 53 ],
       
    30             classNames : [ 'icons fade', 'icons', 'quarter fade', 'quarter', 'half fade', 'half', 'full', 'half', 'half fade', 'quarter', 'quarter fade', 'icons', 'icons fade' ]
       
    31         },
       
    32         {
       
    33             positions : [ 1, 3, 5, 7, 13, 33, 53 ],
       
    34             classNames : [ 'full', 'half', 'half fade', 'quarter', 'quarter fade', 'icons', 'icons fade' ]
       
    35         }
       
    36     ],
       
    37     blockUpdate = false,
       
    38     waitUpdate = true,
       
    39     wheeldelta = 0;
       
    40 
       
    41 function placeHolder(className) {
       
    42     return '<li class="placeholder ' + className + '"></li>';
       
    43 }
       
    44 
       
    45 function tweetToHtml(tweet, className) {
       
    46 	html = '<li class="tweet ' + className + '" id="tweet_' + tweet.pos + '"';
       
    47 	if (tweetData.followLast && tweet.pos == tweetData.position) {
       
    48 	    html += ' style="display: none"';
       
    49 	}
       
    50 	html += '>';
       
    51 	if (tweet.annotations.length) {
       
    52 	    html += '<div class="annotations">';
       
    53 	    for (var i in tweet.annotations) {
       
    54     		html += '<div class="annotation ' + tweet.annotations[i] + '" style="width :' + (100/tweet.annotations.length) + '%"></div>';
       
    55     	}
       
    56     	html += '</div>';
       
    57 	}
       
    58 	html += '<div class="twmain">';
    45 	a_user = '<a href="http://twitter.com/' + tweet.user.screen_name + '" target="_blank" title="' + tweet.user.name + '">';
    59 	a_user = '<a href="http://twitter.com/' + tweet.user.screen_name + '" target="_blank" title="' + tweet.user.name + '">';
    46 	if (tweet.user.profile_image_url) {
    60 	if (tweet.user.profile_image_url) {
    47 		html += a_user + '<img class="tweet_profile_image" src="' + tweet.user.profile_image_url + '" /></a>';
    61 		html += a_user + '<img class="profile_image" src="' + tweet.user.profile_image_url + '" /></a>';
    48 	}
    62 	}
    49 	html += '<h4>' + a_user + '@' + tweet.user.screen_name + '</a></h4><p class="tweet_created_at">' + new Date(tweet.created_at).toLocaleTimeString() + '</p><p>';
    63 	html += '<h4>' + a_user + '@' + tweet.user.screen_name + '</a></h4><p class="created_at">' + new Date(tweet.created_at).toLocaleTimeString() + '</p><p class="tweet_text">';
    50 	lastend = 0;
    64 	lastend = 0;
    51 	txt = '';
    65 	txt = '';
    52 	entities = [];
    66 	entities = [];
    53 	for (var i in tweet.entities.hashtag) {
    67 	for (var i in tweet.entities.hashtags) {
    54 	    entities.push({
    68 	    entities.push({
    55 	        "start" : tweet.entities.hashtag[i].indices[0],
    69 	        "start" : tweet.entities.hashtags[i].indices[0],
    56 	        "end" : tweet.entities.hashtag[i].indices[1],
    70 	        "end" : tweet.entities.hashtags[i].indices[1],
    57 	        "html" : '<a href="http://twitter.com/search?q=%23' + tweet.entities.hashtag[i].text + '" target="_blank">#' + tweet.entities.hashtag[i].text + '</a>'
    71 	        "html" : '<a href="http://twitter.com/search?q=%23' + tweet.entities.hashtags[i].text + '" target="_blank">#' + tweet.entities.hashtags[i].text + '</a>'
    58 	    });
    72 	    });
    59 	}
    73 	}
    60 	for (var i in tweet.entities.urls) {
    74 	for (var i in tweet.entities.urls) {
    61 	    entities.push({
    75 	    entities.push({
    62 	        "start" : tweet.entities.urls[i].indices[0],
    76 	        "start" : tweet.entities.urls[i].indices[0],
    75     for (var i in entities) {
    89     for (var i in entities) {
    76  		txt += tweet.text.substring(lastend, entities[i].start) + entities[i].html;
    90  		txt += tweet.text.substring(lastend, entities[i].start) + entities[i].html;
    77         lastend = entities[i].end;
    91         lastend = entities[i].end;
    78     }
    92     }
    79 	txt += tweet.text.substring(lastend);
    93 	txt += tweet.text.substring(lastend);
    80 	html += txt + '</p></li>';
    94 	html += txt + '</p></li></div>';
    81 	return html;
    95 	return html;
    82 }
    96 }
    83 
    97 
    84 function displayTweets() {
    98 
    85     var to = tweetData.position + 1,
    99 function getUpdate() {
    86         from = Math.max(0, to - zoomLevels[tweetData.zoomLevel].displayCount);
   100     tweetData.posToDisplay = [];
    87     socket.emit('getTweets',{ "from": from, "to": to, "callback" : "display" });
   101     if (tweetData.followLast) {
    88 }
   102         for (var i = tweetData.tweetcount; i >= tweetData.tweetcount - 52; i--) {
    89 
   103             tweetData.posToDisplay.push( i > 0 ? i : -1 );
    90 function setZoom(level) {
   104         }
    91     tweetData.zoomLevel = Math.max(0, Math.min( zoomLevels.length - 1 , level ) );
   105         tweetData.end = tweetData.tweetcount;
    92     displayTweets();
   106         tweetData.start = Math.max(1, tweetData.end - 52);
       
   107     } else {
       
   108         for (var i = tweetData.position + 26; i >= tweetData.position - 26; i--) {
       
   109             tweetData.posToDisplay.push( i > 0 && i <= tweetData.tweetcount ? i : -1 );
       
   110         }
       
   111         tweetData.end = Math.min(tweetData.tweetcount, tweetData.position + 26 );
       
   112         tweetData.start = Math.max(1, tweetData.position - 26);
       
   113     }
       
   114     var tweetsToGet = [];
       
   115     for (var i = tweetData.start; i <= tweetData.end; i++) {
       
   116         if (!tweetByPos(i)) {
       
   117             tweetsToGet.push(i);
       
   118         }
       
   119     }
       
   120     if (tweetsToGet.length) {
       
   121         socket.emit('updateTweets', {
       
   122             "tweets" : tweetsToGet
       
   123         });
       
   124     } else {
       
   125         drawTweetList();
       
   126         //drawDisplay();
       
   127     }
       
   128     if (tweetData.tlChanged) {
       
   129         socket.emit('updateTimeline', {
       
   130             "level" : tweetData.timeLevel
       
   131         });
       
   132     } else {
       
   133         drawTimeLine();
       
   134     }
       
   135 }
       
   136 
       
   137 function tweetByPos(pos) {
       
   138     var index = tweetData.posIndex.indexOf(pos);
       
   139     return (index == -1 ? false : tweetData.tweets[index]);
       
   140 }
       
   141 
       
   142 function delayedUpdate() {
       
   143     blockUpdate = false;
       
   144     if (waitUpdate) {
       
   145         updateDisplay();
       
   146     }
       
   147 }
       
   148 
       
   149 function updateDisplay() {
       
   150     if (blockUpdate) {
       
   151         waitUpdate = true;
       
   152     } else {
       
   153         waitUpdate = false;
       
   154         getUpdate();
       
   155         blockUpdate = true;
       
   156         setTimeout(delayedUpdate, 100);
       
   157     }
    93 }
   158 }
    94 
   159 
    95 function setTimeZoom(level) {
   160 function setTimeZoom(level) {
    96     tweetData.timeLevel = Math.max(0, Math.min( 3, level ));
   161     if (level >= 0 && level <= 4) {
    97     getTimeline();
   162         tweetData.timeLevel = level;
    98 }
   163         tweetData.tlChanged = true;
    99 
   164         updateDisplay();
   100 function getTimeline() {
   165     }
   101     socket.emit('getTimeline',{"level":tweetData.timeLevel});
   166 }
   102 }
   167 
   103 
   168 function drawTweetList() {
   104 function showTimeWindow() {
   169     html = '';
   105     if (timeWindow) {
   170     var i = 0;
   106         timeWindow.remove();
   171     while (i < tweetData.posIndex.length) {
   107         timeWindow = null;
   172         if (tweetData.posToDisplay.indexOf(tweetData.posIndex[i]) == -1) {
   108     }
   173             tweetData.posIndex.splice(i,1);
   109     if (tweetData.tweetsOnDisplay && tweetData.timelineOnDisplay) {
   174             tweetData.tweets.splice(i,1);
   110         var dtfintl = new Date ( tweetData.timelineOnDisplay[ tweetData.timelineOnDisplay.length - 1 ].end ),
   175         } else {
   111             dtdebtl = new Date ( tweetData.timelineOnDisplay[0].start ),
   176             i++;
   112             dtfintw = new Date( tweetData.tweetsOnDisplay[0].created_at ),
   177         }
   113             dtdebtw = new Date( tweetData.tweetsOnDisplay[ tweetData.tweetsOnDisplay.length - 1 ].created_at ),
   178     }
   114             scaleY = 600 / ( dtfintl - dtdebtl );
   179     for (var i in tweetData.posToDisplay) {
   115         timeWindow = paper.rect( 0, scaleY * ( dtfintl - dtfintw ), 600, scaleY * ( dtfintw - dtdebtw )).attr({"stroke":"#000080","fill":"#8080ff","fill-opacity":.2});
   180         var ds = displaySplitting[tweetData.followLast ? 1 : 0];
   116     }
   181         for (var j in ds.positions) {
       
   182             if (ds.positions[j] > i) {
       
   183                 var className = ds.classNames[j];
       
   184                 break;
       
   185             }
       
   186         }
       
   187         html += ( tweetData.posToDisplay[i] != -1 ? tweetToHtml(tweetByPos(tweetData.posToDisplay[i]), className) : placeHolder(className) );
       
   188     }
       
   189     $("#tweetlist").html(html);
       
   190     if (tweetData.followLast) {
       
   191         $("#tweet_" + tweetData.position).fadeIn(500);
       
   192     }
       
   193     drawTimeWindow();
       
   194 }
       
   195 
       
   196 function drawTimeWindow() {
       
   197     twPaper.clear();
       
   198     if (!tweetData.timeline || !tweetData.timeline.length) return;
       
   199     
       
   200     var dtfintl = new Date ( tweetData.timeline[ tweetData.timeline.length - 1 ].end ),
       
   201         dtdebtl = new Date ( tweetData.timeline[0].start ),
       
   202         dtfintw = new Date( tweetByPos( tweetData.end ).created_at ),
       
   203         dtdebtw = new Date( tweetByPos( tweetData.start ).created_at ),
       
   204         scY = 600 / ( dtfintl - dtdebtl );
       
   205     twPaper.rect( 0, scY * ( dtfintl - dtfintw ), 300, scY * ( dtfintw - dtdebtw )).attr({"stroke":"none","fill":"#8080ff","fill-opacity":.2});
       
   206     var dtcour = new Date( tweetByPos( tweetData.position ).created_at ),
       
   207         posY = scY * ( dtfintl - dtcour ); 
       
   208     twPaper.path("M0 "+posY+"L300 "+posY).attr({"stroke":"#ff0"});
       
   209 }
       
   210 
       
   211 function drawTimeLine() {
       
   212     tlPaper.clear();
       
   213     if (!tweetData.timeline || !tweetData.timeline.length) return;
       
   214     tweetData.tlTweetRects = [];
       
   215     var scaleY = 600 / tweetData.timeline.length,
       
   216         max = 0;
       
   217     for (var i = 0; i < tweetData.timeline.length; i++) {
       
   218         max = Math.max(max, tweetData.timeline[i].tweets);
       
   219     }
       
   220     var scaleX = 160 / max;
       
   221     
       
   222     // dessin de l'axe vertical
       
   223     
       
   224     tlPaper.path("M160 0L160 600").attr({"stroke":"#ccc"});
       
   225     
       
   226     // dessin de la date de début
       
   227     
       
   228     tlPaper.text(165, 592, new Date(tweetData.timeline[0].start).toLocaleTimeString()).attr({ "text-anchor" : "start", "font-size": "12px" });
       
   229     
       
   230     // dessin de la date de fin
       
   231     
       
   232     tlPaper.text(165, 7, new Date(tweetData.timeline[tweetData.timeline.length - 1].end).toLocaleTimeString()).attr({ "text-anchor" : "start", "font-size": "12px" });
       
   233     for (var i = 0; i < tweetData.timeline.length; i++) {
       
   234         var posY = 600 - (i * scaleY);
       
   235         
       
   236         // Si on est à une demi-heure, on trace un axe secondaire + heure
       
   237         
       
   238         if (i && !(new Date(tweetData.timeline[i].start).valueOf() % 1800000)) {
       
   239             tlPaper.path("M0 "+posY+"L165 "+posY).attr({"stroke":"#ccc"});
       
   240             tlPaper.text(165, posY, new Date(tweetData.timeline[i].start).toLocaleTimeString()).attr({ "text-anchor" : "start", "font-size": "12px" });
       
   241         }
       
   242         var anz = {
       
   243             "neutre" :tweetData.timeline[i].tweets
       
   244         };
       
   245         for (var j in tweetData.timeline[i].annotations) {
       
   246             anz.neutre -= tweetData.timeline[i].annotations[j];
       
   247             anz[j] = tweetData.timeline[i].annotations[j];
       
   248         }
       
   249         var posX = 0;
       
   250         for (var j in anz) {
       
   251             var largX = scaleX * anz[j];
       
   252             tlPaper.rect(posX, 600 - scaleY * (i+1), largX, scaleY).attr({"stroke": "none", "fill": colors[j]});
       
   253             posX += largX;
       
   254         }
       
   255     }
       
   256     
       
   257     drawTimeWindow();
       
   258 /*    for (var i = 0; i < tweetData.arcs.length; i++) {
       
   259         var x1 = scaleX * (tmptw[tweetData.arcs[i].from].x + .5),
       
   260             x2 = scaleX * (tmptw[tweetData.arcs[i].to].x + .5),
       
   261             y1 = 600 - scaleY * (tmptw[tweetData.arcs[i].from].y + .5),
       
   262             y2 = 600 - scaleY * (tmptw[tweetData.arcs[i].to].y + .5),
       
   263             d = "M"+x1+" "+y1+"C";
       
   264         if (y1 == y2) {
       
   265             d += x1+" "+(y1 - 30)+" "+x2+" "+(y2 - 30);
       
   266         } else {
       
   267             d += (x1 + 60)+" "+y1+" "+(x2 + 60)+" "+y2;
       
   268         }
       
   269         paper.path(d+" "+x2+" "+y2);
       
   270     } */
   117 }
   271 }
   118 
   272 
   119 $(document).ready(function() {
   273 $(document).ready(function() {
   120     paper = Raphael("timeline", 160, 600);
   274     tlPaper = Raphael("timeline", 220, 600);
       
   275     twPaper = Raphael("timewindow", 220, 600);
   121     socket = io.connect('http://' + S_IO_HOST + ':' + S_IO_PORT );
   276     socket = io.connect('http://' + S_IO_HOST + ':' + S_IO_PORT );
   122     socket.on('tweetSummary', function (data) {
   277     socket.on('tweetSummary', function (data) {
   123         if (tweetData.position == tweetData.tweetcount - 1) {
   278         if (tweetData.tweetcount != data.tweetcount) {
   124             tweetData.position = data.tweetcount - 1;
   279             tweetData.tweetcount = data.tweetcount;
   125             displayTweets();
   280             tweetData.tlChanged = true;
   126             getTimeline();
   281             if (tweetData.followLast) {
   127         }
   282                 tweetData.position = data.tweetcount;
   128         tweetData.tweetcount = data.tweetcount;
   283             }
       
   284             updateDisplay();
       
   285         }
   129     });
   286     });
   130     socket.on('tweets', function (data) {
   287     socket.on('tweets', function (data) {
   131         switch (data.callback) {
   288         for (var i in data) {
   132             case "display":
   289             if (tweetData.posIndex.indexOf(data[i].pos) == -1) {
   133                 tweetData.tweetsOnDisplay = data.tweets;
   290                 tweetData.tweets.push(data[i]);
   134                 tweetData.tweetsOnDisplay.reverse();
   291                 tweetData.posIndex.push(data[i].pos);
   135                 html = '';
   292             }
   136                 for (var i in tweetData.tweetsOnDisplay) {
   293         }
   137                     html += tweetToHtml(tweetData.tweetsOnDisplay[i]);
   294         drawTweetList();
   138                 }
       
   139                 $("#tweetlist").html(html);
       
   140                 showTimeWindow();
       
   141                 break;
       
   142         }
       
   143     });
   295     });
   144     socket.on('timeline', function (data) {
   296     socket.on('timeline', function (data) {
   145         tweetData.timelineOnDisplay = data.timeline;
   297         tweetData.timeline = data;
   146         paper.clear();
   298         drawTimeLine();
   147         timeWindow = null;
   299     });
   148         var max = 0;
   300     socket.on('display', function (data) {
   149         for (var i in data.timeline) {
   301         tweetData.tlChanged = false;
   150             max = Math.max(max, data.timeline[i].tweets.length);
   302         for (var i in data.tweets) {
   151         }
   303             if (tweetData.posIndex.indexOf(data.tweets[i].pos) == -1) {
   152         var scaleX = 160 / max,
   304                 tweetData.tweets.push(data.tweets[i]);
   153             scaleY = 600 / data.timeline.length,
   305                 tweetData.posIndex.push(data.tweets[i].pos);
   154             tmptw = [];
   306             }
   155         for (var i = 0; i < tweetData.tweetcount; i++) {
   307         }
   156             tmptw.push({});
   308         if (data.timeline) {
   157         }
   309             tweetData.timeline = data.timeline
   158         for (var i = 0; i < data.timeline.length; i++) {
   310         }
   159             for (var j = 0; j < data.timeline[i].tweets.length; j++) {
   311         tweetData.arcs = data.arcs;
   160                 var coul = "#585858";
   312         drawDisplay();
   161                 for (var k in data.timeline[i].annotations) {
   313     });
   162                     if (data.timeline[i].annotations[k].indexOf(data.timeline[i].tweets[j]) != -1) {
   314     $("#tweetlist").mousewheel(function(e, d) {
   163                         coul = colors[k];
   315         wheeldelta += d;
   164                     }
   316         if (Math.abs(wheeldelta) >= 1) {
   165                 }
   317             tweetData.position = Math.min( tweetData.tweetcount, Math.max(1, parseInt(wheeldelta) + tweetData.position ) );
   166                 tmptw[data.timeline[i].tweets[j]].y = i;
   318             tweetData.followLast = (tweetData.position == tweetData.tweetcount);
   167                 tmptw[data.timeline[i].tweets[j]].x = j;
   319             updateDisplay();
   168                 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});
   320             wheeldelta = 0;
   169             }
   321         }
   170         }
   322         return false;
   171         for (var i = 0; i < data.arcs.length; i++) {
   323     });
   172             var x1 = scaleX * (tmptw[data.arcs[i].from].x + .5),
   324     $("#timewindow").mousewheel(function(e, d) {
   173                 x2 = scaleX * (tmptw[data.arcs[i].to].x + .5),
   325         wheeldelta += d;
   174                 y1 = 600 - scaleY * (tmptw[data.arcs[i].from].y + .5),
   326         if (Math.abs(wheeldelta) >= 1) {
   175                 y2 = 600 - scaleY * (tmptw[data.arcs[i].to].y + .5),
   327             if (wheeldelta > 0) {
   176                 d = "M"+x1+" "+y1+"C";
   328                 setTimeZoom(tweetData.timeLevel + 1);
   177             if (y1 == y2) {
       
   178                 d += x1+" "+(y1 - 60)+" "+x2+" "+(y2 - 60);
       
   179             } else {
   329             } else {
   180                 d += (x1 + 60)+" "+y1+" "+(x2 + 60)+" "+y2;
   330                 setTimeZoom(tweetData.timeLevel - 1);
   181             }
   331             }
   182             paper.path(d+" "+x2+" "+y2);
   332             wheeldelta = 0;
   183         }
   333         }
   184         showTimeWindow();
   334         return false;
   185     });
   335     });
   186 });
   336 });