tweetcast/nodejs/client/js/script.js
changeset 332 738594562e44
parent 331 03c69425efa6
child 333 4389fd4ae58f
equal deleted inserted replaced
331:03c69425efa6 332:738594562e44
     1 var socket,
       
     2     tlPaper,
       
     3     twPaper,
       
     4     tweetData = {
       
     5         "tweetcount" : 0,
       
     6         "position" : 0,
       
     7         "followLast" : true,
       
     8         "zoomLevel" : 3,
       
     9         "timeLevel" : 2,
       
    10         "tweets" : [],
       
    11         "posIndex" : [],
       
    12         "tlChanged" : true
       
    13     },
       
    14     colors = {
       
    15         'positive' : "#1D973D",
       
    16         'reference' : "#C5A62D",
       
    17         'negative' : "#CE0A15",
       
    18         'question' : "#036AAE",
       
    19         'neutre' : "#585858"
       
    20     },
       
    21     annotations = {
       
    22         'positive' : '++',
       
    23         'negative' : '--',
       
    24         'reference' : '==',
       
    25         'question' : '??'
       
    26     },
       
    27     displaySplitting = [
       
    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">';
       
    59 	a_user = '<a href="http://twitter.com/' + tweet.user.screen_name + '" target="_blank" title="' + tweet.user.name + '">';
       
    60 	if (tweet.user.profile_image_url) {
       
    61 		html += a_user + '<img class="profile_image" src="' + tweet.user.profile_image_url + '" /></a>';
       
    62 	}
       
    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">';
       
    64 	lastend = 0;
       
    65 	txt = '';
       
    66 	entities = [];
       
    67 	for (var i in tweet.entities.hashtags) {
       
    68 	    entities.push({
       
    69 	        "start" : tweet.entities.hashtags[i].indices[0],
       
    70 	        "end" : tweet.entities.hashtags[i].indices[1],
       
    71 	        "html" : '<a href="http://twitter.com/search?q=%23' + tweet.entities.hashtags[i].text + '" target="_blank">#' + tweet.entities.hashtags[i].text + '</a>'
       
    72 	    });
       
    73 	}
       
    74 	for (var i in tweet.entities.urls) {
       
    75 	    entities.push({
       
    76 	        "start" : tweet.entities.urls[i].indices[0],
       
    77 	        "end" : tweet.entities.urls[i].indices[1],
       
    78 	        "html" : '<a href="' + tweet.entities.urls[i].expanded_url + '" target="_blank">' + tweet.entities.urls[i].display_url + '</a>'
       
    79 	    });
       
    80 	}
       
    81 	for (var i in tweet.entities.user_mentions) {
       
    82 	    entities.push({
       
    83 	        "start" : tweet.entities.user_mentions[i].indices[0],
       
    84 	        "end" : tweet.entities.user_mentions[i].indices[1],
       
    85 	        "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>'
       
    86 	    });
       
    87 	}
       
    88  	entities.sort(function(a, b) { return a.start - b.start });
       
    89     for (var i in entities) {
       
    90  		txt += tweet.text.substring(lastend, entities[i].start) + entities[i].html;
       
    91         lastend = entities[i].end;
       
    92     }
       
    93 	txt += tweet.text.substring(lastend);
       
    94 	html += txt + '</p></li></div>';
       
    95 	return html;
       
    96 }
       
    97 
       
    98 
       
    99 function getUpdate() {
       
   100     tweetData.posToDisplay = [];
       
   101     if (tweetData.followLast) {
       
   102         for (var i = tweetData.tweetcount; i >= tweetData.tweetcount - 52; i--) {
       
   103             tweetData.posToDisplay.push( i > 0 ? i : -1 );
       
   104         }
       
   105         tweetData.end = tweetData.tweetcount;
       
   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     }
       
   158 }
       
   159 
       
   160 function setTimeZoom(level) {
       
   161     if (level >= 0 && level <= 4) {
       
   162         tweetData.timeLevel = level;
       
   163         tweetData.tlChanged = true;
       
   164         updateDisplay();
       
   165     }
       
   166 }
       
   167 
       
   168 function drawTweetList() {
       
   169     html = '';
       
   170     var i = 0;
       
   171     while (i < tweetData.posIndex.length) {
       
   172         if (tweetData.posToDisplay.indexOf(tweetData.posIndex[i]) == -1) {
       
   173             tweetData.posIndex.splice(i,1);
       
   174             tweetData.tweets.splice(i,1);
       
   175         } else {
       
   176             i++;
       
   177         }
       
   178     }
       
   179     for (var i in tweetData.posToDisplay) {
       
   180         var ds = displaySplitting[tweetData.followLast ? 1 : 0];
       
   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     } */
       
   271 }
       
   272 
       
   273 $(document).ready(function() {
       
   274     tlPaper = Raphael("timeline", 220, 600);
       
   275     twPaper = Raphael("timewindow", 220, 600);
       
   276     socket = io.connect('http://' + S_IO_HOST + ':' + S_IO_PORT );
       
   277     socket.on('tweetSummary', function (data) {
       
   278         if (tweetData.tweetcount != data.tweetcount) {
       
   279             tweetData.tweetcount = data.tweetcount;
       
   280             tweetData.tlChanged = true;
       
   281             if (tweetData.followLast) {
       
   282                 tweetData.position = data.tweetcount;
       
   283             }
       
   284             updateDisplay();
       
   285         }
       
   286     });
       
   287     socket.on('tweets', function (data) {
       
   288         for (var i in data) {
       
   289             if (tweetData.posIndex.indexOf(data[i].pos) == -1) {
       
   290                 tweetData.tweets.push(data[i]);
       
   291                 tweetData.posIndex.push(data[i].pos);
       
   292             }
       
   293         }
       
   294         drawTweetList();
       
   295     });
       
   296     socket.on('timeline', function (data) {
       
   297         tweetData.timeline = data;
       
   298         drawTimeLine();
       
   299     });
       
   300     socket.on('display', function (data) {
       
   301         tweetData.tlChanged = false;
       
   302         for (var i in data.tweets) {
       
   303             if (tweetData.posIndex.indexOf(data.tweets[i].pos) == -1) {
       
   304                 tweetData.tweets.push(data.tweets[i]);
       
   305                 tweetData.posIndex.push(data.tweets[i].pos);
       
   306             }
       
   307         }
       
   308         if (data.timeline) {
       
   309             tweetData.timeline = data.timeline
       
   310         }
       
   311         tweetData.arcs = data.arcs;
       
   312         drawDisplay();
       
   313     });
       
   314     $("#tweetlist").mousewheel(function(e, d) {
       
   315         wheeldelta += d;
       
   316         if (Math.abs(wheeldelta) >= 1) {
       
   317             tweetData.position = Math.min( tweetData.tweetcount, Math.max(1, parseInt(wheeldelta) + tweetData.position ) );
       
   318             tweetData.followLast = (tweetData.position == tweetData.tweetcount);
       
   319             updateDisplay();
       
   320             wheeldelta = 0;
       
   321         }
       
   322         return false;
       
   323     });
       
   324     $("#timewindow").mousewheel(function(e, d) {
       
   325         wheeldelta += d;
       
   326         if (Math.abs(wheeldelta) >= 1) {
       
   327             if (wheeldelta > 0) {
       
   328                 setTimeZoom(tweetData.timeLevel + 1);
       
   329             } else {
       
   330                 setTimeZoom(tweetData.timeLevel - 1);
       
   331             }
       
   332             wheeldelta = 0;
       
   333         }
       
   334         return false;
       
   335     });
       
   336 });