tweetcast/nodejs/client/js/script.js
changeset 404 89968844eb7d
parent 403 dd1686ae5506
child 405 6626b728b142
equal deleted inserted replaced
403:dd1686ae5506 404:89968844eb7d
     1 var socket,
       
     2     tlPaper,
       
     3     twPaper,
       
     4     tweetData = {
       
     5         "tweetcount" : 0,
       
     6         "position" : 0,
       
     7         "feedMode" : true,
       
     8         "followLast" : true,
       
     9         "zoomLevel" : 3,
       
    10         "timeLevel" : 2,
       
    11         "tweets" : [],
       
    12         "posIndex" : [],
       
    13         "tlChanged" : true,
       
    14         "tlLevelChanged" : true,
       
    15         "blockUpdate" : false,
       
    16         "waitUpdate" : true,
       
    17         "htmlBuffer" : '',
       
    18         "wheelDelta" : 0
       
    19     },
       
    20     displaySplitting = [
       
    21         {
       
    22             positions : [ 10, 20, 22, 24, 25, 26, 27, 28, 29, 31, 33, 43, 53 ],
       
    23             classNames : [ 'icons fade', 'icons', 'quarter fade', 'quarter', 'half fade', 'half', 'full', 'half', 'half fade', 'quarter', 'quarter fade', 'icons', 'icons fade' ]
       
    24         },
       
    25         {
       
    26             positions : [ 1, 3, 5, 7, 13, 33, 53 ],
       
    27             classNames : [ 'full', 'half', 'half fade', 'quarter', 'quarter fade', 'icons', 'icons fade' ]
       
    28         }
       
    29     ];
       
    30 
       
    31 function placeHolder(className) {
       
    32     return '<li class="placeholder ' + className + '"></li>';
       
    33 }
       
    34 
       
    35 function getLinkedTweets() {
       
    36     socket.emit('linkedTweets',{"tweetpos":tweetData.position});
       
    37 }
       
    38 
       
    39 function changeMode() {
       
    40     if (tweetData.feedMode) {
       
    41         getLinkedTweets();
       
    42     } else {
       
    43         tweetData.feedMode = true;
       
    44         updateDisplay();
       
    45     }
       
    46 }
       
    47 
       
    48 function clicTweet(tweetPos) {
       
    49     if (tweetPos != tweetData.position) {
       
    50         tweetData.position = tweetPos;
       
    51         if (tweetData.feedMode) {
       
    52             tweetData.followLast = (tweetData.position == tweetData.tweetcount);
       
    53         } else {
       
    54             getLinkedTweets();
       
    55         }
       
    56         return false;
       
    57     } else {
       
    58         changeMode();
       
    59     }
       
    60 }
       
    61 
       
    62 function tweetToHtml(tweet, className) {
       
    63     if (!tweet) {
       
    64         return placeHolder(className);
       
    65     }
       
    66 	var html = '<li class="tweet ' + className + '" id="tweet_' + tweet.pos + '" onclick="return clicTweet(' + tweet.pos + ')"';
       
    67 	if (tweetData.followLast && tweet.pos == tweetData.position) {
       
    68 	    html += ' style="display: none"';
       
    69 	}
       
    70 	html += '>';
       
    71 	if (tweet.annotations.length) {
       
    72 	    html += '<div class="annotations">';
       
    73 	    for (var i in tweet.annotations) {
       
    74     		html += '<div class="annotation" style="width:' + (100/tweet.annotations.length) + '%; background:' + annotations[tweet.annotations[i]].colors.tweet + '"></div>';
       
    75     	}
       
    76     	html += '</div>';
       
    77 	}
       
    78 	html += '<div class="twmain">';
       
    79 	a_user = '<a href="http://twitter.com/' + tweet.user.screen_name + '" var target="_blank" title="' + tweet.user.name + '">';
       
    80 	if (tweet.user.profile_image_url) {
       
    81 		html += a_user + '<img class="profile_image" src="' + tweet.user.profile_image_url + '" /></a>';
       
    82 	}
       
    83 	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">';
       
    84 	lastend = 0;
       
    85 	var tab = tweet.text.split(/\&\#|\;/);
       
    86 	var txta = '';
       
    87 	for (i = 0; i < tab.length; i++) {
       
    88 	    txta += (i % 2 && parseInt(tab[i]) != NaN) ? String.fromCharCode(tab[i]) : tab[i];
       
    89 	}
       
    90 	var txt = '',
       
    91 	    entities = [];
       
    92 	for (var i in tweet.entities.hashtags) {
       
    93 	    entities.push({
       
    94 	        "start" : tweet.entities.hashtags[i].indices[0],
       
    95 	        "end" : tweet.entities.hashtags[i].indices[1],
       
    96 	        "html" : '<a href="http://twitter.com/search?q=%23' + tweet.entities.hashtags[i].text + '" target="_blank">#' + tweet.entities.hashtags[i].text + '</a>'
       
    97 	    });
       
    98 	}
       
    99 	for (var i in tweet.entities.urls) {
       
   100 	    entities.push({
       
   101 	        "start" : tweet.entities.urls[i].indices[0],
       
   102 	        "end" : tweet.entities.urls[i].indices[1],
       
   103 	        "html" : '<a href="' + tweet.entities.urls[i].expanded_url + '" target="_blank">' + tweet.entities.urls[i].expanded_url + '</a>'
       
   104 	    });
       
   105 	}
       
   106 	for (var i in tweet.entities.user_mentions) {
       
   107 	    entities.push({
       
   108 	        "start" : tweet.entities.user_mentions[i].indices[0],
       
   109 	        "end" : tweet.entities.user_mentions[i].indices[1],
       
   110 	        "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>'
       
   111 	    });
       
   112 	}
       
   113  	entities.sort(function(a, b) { return a.start - b.start });
       
   114     for (var i in entities) {
       
   115  		txt += txta.substring(lastend, entities[i].start) + entities[i].html;
       
   116         lastend = entities[i].end;
       
   117     }
       
   118 	txt += txta.substring(lastend);
       
   119 	html += txt + '</p></li></div>';
       
   120 	return html;
       
   121 }
       
   122 
       
   123 
       
   124 function getUpdate() {
       
   125     tweetData.posToDisplay = [];
       
   126     if (tweetData.feedMode) {
       
   127         if (tweetData.followLast) {
       
   128             tweetData.position = tweetData.tweetcount;
       
   129             for (var i = tweetData.tweetcount; i >= tweetData.tweetcount - 52; i--) {
       
   130                 tweetData.posToDisplay.push( i > 0 ? i : -1 );
       
   131             }
       
   132             tweetData.end = tweetData.tweetcount;
       
   133             tweetData.start = Math.max(1, tweetData.end - 52);
       
   134         } else {
       
   135             for (var i = tweetData.position + 26; i >= tweetData.position - 26; i--) {
       
   136                 tweetData.posToDisplay.push( i > 0 && i <= tweetData.tweetcount ? i : -1 );
       
   137             }
       
   138             tweetData.end = Math.min(tweetData.tweetcount, tweetData.position + 26 );
       
   139             tweetData.start = Math.max(1, tweetData.position - 26);
       
   140         }
       
   141         if ($("#modeselector").text() != "Mode flux") {
       
   142             $("#modeselector").html("Mode Flux").css({"color":"#000000","background":"#ffffff"});
       
   143             $("#tweetlist").css({"background":"#ffffff"});
       
   144         }
       
   145     } else {
       
   146         tweetData.linkedTweets.referenced_by.sort(function(a,b) { return b.pos - a.pos });
       
   147         tweetData.linkedTweets.referencing.sort(function(a,b) { return b.pos - a.pos });
       
   148         for (var i in tweetData.linkedTweets.referenced_by) {
       
   149             tweetData.posToDisplay.push(tweetData.linkedTweets.referenced_by[i].pos);
       
   150         }
       
   151         tweetData.posToDisplay.push(tweetData.position);
       
   152         for (var i in tweetData.linkedTweets.referencing) {
       
   153             tweetData.posToDisplay.push(tweetData.linkedTweets.referencing[i].pos);
       
   154         }
       
   155         if ($("#modeselector").text() != "Conversation") {
       
   156             $("#modeselector").html("Conversation").css({"color":"#ffffff","background":"#000080"});
       
   157             $("#tweetlist").css({"background":"#000080"});
       
   158         }
       
   159     }
       
   160     var tweetsToGet = [];
       
   161     for (var i in tweetData.posToDisplay) {
       
   162         if (tweetData.posToDisplay[i] != -1 && !tweetByPos(tweetData.posToDisplay[i])) {
       
   163             tweetsToGet.push(tweetData.posToDisplay[i]);
       
   164         }
       
   165     }
       
   166     if (tweetsToGet.length) {
       
   167 //        console.log('We need to get '+tweetsToGet.join(','));
       
   168         socket.emit('updateTweets', {
       
   169             "tweets" : tweetsToGet
       
   170         });
       
   171     } else {
       
   172         drawTweetList();
       
   173     }
       
   174 /*        html = '';
       
   175         for (var i in tweetData.linkedTweets.referenced_by) {
       
   176             console.log(tweetData.linkedTweets.referenced_by[i]);
       
   177             html += tweetToHtml(tweetData.linkedTweets.referenced_by[i].tweet,'half');
       
   178         }
       
   179         html += tweetToHtml(tweetByPos(tweetData.position),'full');
       
   180         for (var i in tweetData.linkedTweets.referencing) {
       
   181             console.log(tweetData.linkedTweets.referencing[i]);
       
   182             html += tweetToHtml(tweetData.linkedTweets.referencing[i].tweet,'half');
       
   183         }
       
   184         if (tweetData.htmlBuffer != html) {
       
   185             $("#tweetlist").html(html);
       
   186             tweetData.htmlBuffer = html;
       
   187         } */
       
   188     if (tweetData.tlChanged || tweetData.tlLevelChanged) {
       
   189         socket.emit('updateTimeline', {
       
   190             "level" : tweetData.timeLevel,
       
   191             "full" : tweetData.tlLevelChanged
       
   192         });
       
   193     } else {
       
   194         drawTimeLine();
       
   195     }
       
   196 }
       
   197 
       
   198 function addTweet(tweet) {
       
   199     if (tweetData.posIndex.indexOf(tweet.pos) == -1) {
       
   200         tweetData.tweets.push(tweet);
       
   201         tweetData.posIndex.push(tweet.pos);
       
   202     }
       
   203 }
       
   204 
       
   205 function tweetByPos(pos) {
       
   206     var index = tweetData.posIndex.indexOf(pos);
       
   207     return (index == -1 ? false : tweetData.tweets[index]);
       
   208 }
       
   209 
       
   210 function delayedUpdate() {
       
   211     tweetData.blockUpdate = false;
       
   212     if (tweetData.waitUpdate) {
       
   213         updateDisplay();
       
   214     }
       
   215 }
       
   216 
       
   217 function updateDisplay() {
       
   218     if (tweetData.blockUpdate) {
       
   219         tweetData.waitUpdate = true;
       
   220     } else {
       
   221         tweetData.waitUpdate = false;
       
   222         getUpdate();
       
   223         tweetData.blockUpdate = true;
       
   224         setTimeout(delayedUpdate, 100);
       
   225     }
       
   226 }
       
   227 
       
   228 function setTimeZoom(level) {
       
   229     if (level >= 0 && level <= 4) {
       
   230         tweetData.timeLevel = level;
       
   231         tweetData.tlChanged = true;
       
   232         updateDisplay();
       
   233     }
       
   234 }
       
   235 
       
   236 function drawTweetList() {
       
   237     html = '';
       
   238     var i = 0;
       
   239     while (i < tweetData.posIndex.length) {
       
   240         if (tweetData.posToDisplay.indexOf(tweetData.posIndex[i]) == -1) {
       
   241             tweetData.posIndex.splice(i,1);
       
   242             tweetData.tweets.splice(i,1);
       
   243         } else {
       
   244             i++;
       
   245         }
       
   246     }
       
   247     if (tweetData.feedMode) {
       
   248         for (var i in tweetData.posToDisplay) {
       
   249             var ds = displaySplitting[tweetData.followLast ? 1 : 0];
       
   250             for (var j in ds.positions) {
       
   251                 if (ds.positions[j] > i) {
       
   252                     var className = ds.classNames[j];
       
   253                     break;
       
   254                 }
       
   255             }
       
   256             html += ( tweetData.posToDisplay[i] != -1 ? tweetToHtml(tweetByPos(tweetData.posToDisplay[i]), className) : placeHolder(className) );
       
   257         }
       
   258     } else {
       
   259         for (var i in tweetData.posToDisplay) {
       
   260             html += tweetToHtml(tweetByPos(tweetData.posToDisplay[i]), (tweetData.posToDisplay[i] == tweetData.position ? 'full' : 'half' ));
       
   261         }
       
   262     }
       
   263     if (tweetData.htmlBuffer != html) {
       
   264         $("#tweetlist").html(html);
       
   265         tweetData.htmlBuffer = html;
       
   266     }
       
   267     if (tweetData.followLast) {
       
   268         $("#tweet_" + tweetData.position).fadeIn(500);
       
   269     }
       
   270     drawTimeWindow();
       
   271 }
       
   272 
       
   273 function drawTimeWindow() {
       
   274     twPaper.clear();
       
   275     if (!tweetData.timeline || !tweetData.timeline.length) return;
       
   276     
       
   277     var dtfintl = tweetData.timeline[ tweetData.timeline.length - 1 ].end,
       
   278         dtdebtl = tweetData.timeline[0].start,
       
   279         scY = 600 / ( dtfintl - dtdebtl );
       
   280     if (tweetData.feedMode) {
       
   281         var dtfintw = new Date( tweetByPos( tweetData.end ).created_at ),
       
   282             dtdebtw = new Date( tweetByPos( tweetData.start ).created_at ),
       
   283             rTop = scY * ( dtfintl - dtfintw ),
       
   284             rHeight = scY * ( dtfintw - dtdebtw );
       
   285         if (rHeight > 0) {
       
   286             twPaper.rect( 0, rTop, 300, rHeight).attr({"stroke":"none","fill":"#8080ff","fill-opacity":.2});
       
   287         } 
       
   288     } else {
       
   289         for (var i in tweetData.posToDisplay) {
       
   290             if (tweetData.posToDisplay[i] != -1) {
       
   291                 var tw = tweetByPos(tweetData.posToDisplay[i]);
       
   292                 if (tw) {
       
   293                     var dtcour = new Date( tw.created_at ),
       
   294                         posY = scY * ( dtfintl - dtcour );
       
   295                     twPaper.path("M0 "+posY+"L300 "+posY).attr({"stroke":"#88f"});
       
   296                 }
       
   297             }
       
   298         }
       
   299     }
       
   300     var dtcour = new Date( tweetByPos( tweetData.position ).created_at ),
       
   301         posY = scY * ( dtfintl - dtcour ); 
       
   302     twPaper.path("M0 "+posY+"L300 "+posY).attr({"stroke":"#ff0"});
       
   303 }
       
   304 
       
   305 function drawTimeLine() {
       
   306     tlPaper.clear();
       
   307     if (!tweetData.timeline || !tweetData.timeline.length) return;
       
   308     tweetData.tlTweetRects = [];
       
   309     var scaleY = 600 / tweetData.timeline.length,
       
   310         max = 0;
       
   311     for (var i = 0; i < tweetData.timeline.length; i++) {
       
   312         max = Math.max(max, tweetData.timeline[i].tweets);
       
   313     }
       
   314     var scaleX = 160 / max;
       
   315     
       
   316     // dessin de l'axe vertical
       
   317     
       
   318     tlPaper.path("M160 0L160 600").attr({"stroke":"#ccc"});
       
   319     
       
   320     // dessin de la date de début
       
   321     
       
   322     tlPaper.text(165, 592, new Date(tweetData.timeline[0].start).toLocaleTimeString()).attr({ "text-anchor" : "start", "font-size": "12px" });
       
   323     
       
   324     // dessin de la date de fin
       
   325     
       
   326     tlPaper.text(165, 7, new Date(tweetData.timeline[tweetData.timeline.length - 1].end).toLocaleTimeString()).attr({ "text-anchor" : "start", "font-size": "12px" });
       
   327     for (var i = 0; i < tweetData.timeline.length; i++) {
       
   328         var posY = 600 - (i * scaleY);
       
   329         
       
   330         // Si on est à une demi-heure, on trace un axe secondaire + heure
       
   331         
       
   332         if (i && !(new Date(tweetData.timeline[i].start).valueOf() % 1800000)) {
       
   333             tlPaper.path("M0 "+posY+"L165 "+posY).attr({"stroke":"#ccc"});
       
   334             tlPaper.text(165, posY, new Date(tweetData.timeline[i].start).toLocaleTimeString()).attr({ "text-anchor" : "start", "font-size": "12px" });
       
   335         }
       
   336         var anz = {
       
   337             "default" :tweetData.timeline[i].tweets
       
   338         };
       
   339         for (var j in tweetData.timeline[i].annotations) {
       
   340             anz.default -= tweetData.timeline[i].annotations[j];
       
   341             anz[j] = tweetData.timeline[i].annotations[j];
       
   342         }
       
   343         var posX = 0;
       
   344         for (var j in anz) {
       
   345             var largX = scaleX * anz[j];
       
   346             if (largX > 0) {
       
   347                 tlPaper.rect(posX, 600 - scaleY * (i+1), largX, scaleY).attr({"stroke": "none", "fill": annotations[j].colors.timeline});
       
   348                 posX += largX;
       
   349             }
       
   350         }
       
   351     }
       
   352     
       
   353     drawTimeWindow();
       
   354 }
       
   355 
       
   356 $(document).ready(function() {
       
   357     tlPaper = Raphael("timeline", 220, 600);
       
   358     twPaper = Raphael("timewindow", 220, 600);
       
   359     socket = io.connect('http://' + document.location.hostname );
       
   360     socket.on('tweetSummary', function (data) {
       
   361         if (tweetData.tweetcount != data.tweetcount) {
       
   362             tweetData.tweetcount = data.tweetcount;
       
   363             tweetData.tlLevelChanged = true;
       
   364             updateDisplay();
       
   365         }
       
   366     });
       
   367     socket.on('tweetPosByDate', function (data) {
       
   368         tweetData.position = data.tweetpos;
       
   369         tweetData.feedMode = true;
       
   370         tweetData.followLast = (tweetData.position == tweetData.tweetcount);
       
   371         updateDisplay();
       
   372     });
       
   373     socket.on('tweets', function (data) {
       
   374         for (var i in data) {
       
   375             addTweet(data[i]);
       
   376         }
       
   377         drawTweetList();
       
   378     });
       
   379     socket.on('timeline', function (data) {
       
   380         tweetData.tlChanged = false;
       
   381         if (data.full) {
       
   382             tweetData.timeline = data.data;
       
   383         } else {
       
   384             if (tweetData.timeline[tweetData.timeline.length - 1].start == data.data[0].start) {
       
   385                 tweetData.timeline[tweetData.timeline.length - 1] = data.data[0];
       
   386             } else {
       
   387                 tweetData.timeline.push(data.data[0]);
       
   388             }
       
   389         }
       
   390         while (tweetData.length > 50) {
       
   391             tweetData.splice(0,1);
       
   392         }
       
   393         drawTimeLine();
       
   394     });
       
   395     socket.on('linkedTweets', function(data) {
       
   396         tweetData.followLast = false;
       
   397         tweetData.feedMode = false;
       
   398         tweetData.position = data.tweetpos;
       
   399         tweetData.linkedTweets = data;
       
   400         updateDisplay();
       
   401     });
       
   402     $("#tweetlist").mousewheel(function(e, d) {
       
   403         tweetData.wheelDelta += d;
       
   404         if (Math.abs(tweetData.wheelDelta) >= 1) {
       
   405             if (tweetData.feedMode) {
       
   406                 tweetData.position = Math.min( tweetData.tweetcount, Math.max(1, parseInt(tweetData.wheelDelta) + tweetData.position ) );
       
   407                 tweetData.followLast = (tweetData.position == tweetData.tweetcount);
       
   408                 updateDisplay();
       
   409             } else {
       
   410                 if (tweetData.wheelDelta > 0) {
       
   411                     if (tweetData.linkedTweets.referenced_by.length) {
       
   412                         tweetData.position = tweetData.linkedTweets.referenced_by[tweetData.linkedTweets.referenced_by.length - 1].pos;
       
   413                         getLinkedTweets();
       
   414                     }
       
   415                 } else {
       
   416                     if (tweetData.linkedTweets.referencing.length) {
       
   417                         tweetData.position = tweetData.linkedTweets.referencing[0].pos;
       
   418                         getLinkedTweets();
       
   419                     }
       
   420                 }
       
   421             }
       
   422             tweetData.wheelDelta = 0;
       
   423         }
       
   424         return false;
       
   425     });
       
   426     $("#timewindow").mousewheel(function(e, d) {
       
   427         tweetData.wheelDelta += d;
       
   428         if (Math.abs(tweetData.wheelDelta) >= 1) {
       
   429             if (tweetData.wheelDelta > 0) {
       
   430                 setTimeZoom(tweetData.timeLevel + 1);
       
   431             } else {
       
   432                 setTimeZoom(tweetData.timeLevel - 1);
       
   433             }
       
   434             tweetData.wheelDelta = 0;
       
   435         }
       
   436         return false;
       
   437     });
       
   438     $("#timewindow").click(function(evt) {
       
   439         var offsetY = evt.pageY - $(this).offset().top,
       
   440             dtfintl = tweetData.timeline[ tweetData.timeline.length - 1 ].end,
       
   441             dtdebtl = tweetData.timeline[0].start,
       
   442             clicTime = dtdebtl + (1 - ( offsetY / 600 ) ) * ( dtfintl - dtdebtl );
       
   443         socket.emit('tweetPosByDate',{ date: clicTime });
       
   444     });
       
   445     $("#modeselector").click(changeMode);
       
   446 });