tweetcast/nodejs-bis/client/js/script.js
changeset 374 0c4acfa2aea1
parent 360 d49991fe4892
child 380 b3f66379fed7
equal deleted inserted replaced
373:66092f867c03 374:0c4acfa2aea1
    16         ],
    16         ],
    17         timeLevel : 2,
    17         timeLevel : 2,
    18         deltaX : 30,
    18         deltaX : 30,
    19         tlWidth : 98,
    19         tlWidth : 98,
    20         tlHeight : 450,
    20         tlHeight : 450,
    21         globalWords : {}
    21         globalWords : {},
       
    22         refMouse : { x : 0, y : 0},
       
    23         refPosTl : { x : 0, y : 0},
       
    24         tlMouseMoved : false,
       
    25         tlMouseClicked : false
    22         },
    26         },
    23     tlBuffer = '',
    27     tlBuffer = '',
    24     relHover = null,
    28     relHover = null,
    25     wheelDelta = 0,
    29     wheelDelta = 0,
    26     rx_word = /[^ \.&;,'"!\?@#\d\(\)\+\[\]\\\…\-«»:\/]{3,}/g;
    30     rx_word = /[^ \.&;,'"!\?@#\d\(\)\+\[\]\\\…\-«»:\/]{3,}/g,
       
    31     stop_list = [ 'and', 'les', 'the' ];
    27 
    32 
    28 function arc(source, target) {
    33 function arc(source, target) {
    29     var x3 = .3 * target.y - .3 * source.y + .8 * source.x + .2 * target.x;
    34     var x3 = .3 * target.y - .3 * source.y + .8 * source.x + .2 * target.x;
    30     var y3 = .8 * source.y + .2 * target.y - .3 * target.x + .3 * source.x;
    35     var y3 = .8 * source.y + .2 * target.y - .3 * target.x + .3 * source.x;
    31     var x4 = .3 * target.y - .3 * source.y + .2 * source.x + .8 * target.x;
    36     var x4 = .3 * target.y - .3 * source.y + .2 * source.x + .8 * target.x;
    32     var y4 = .2 * source.y + .8 * target.y - .3 * target.x + .3 * source.x;
    37     var y4 = .2 * source.y + .8 * target.y - .3 * target.x + .3 * source.x;
    33     return "M" + source.x + " " + source.y + "C" + [x3, y3, x4, y4, target.x, target.y].join(" ");
    38     return "M" + source.x + " " + source.y + "C" + [x3, y3, x4, y4, target.x, target.y].join(" ");
    34 }
    39 }
    35 
    40 
    36 function countWords(text, wordobj) {
    41 function countWords(text, wordobj) {
    37     var tab = text.match(rx_word);
    42     var tab = text.replace(/https?:\/\/[0-9a-zA-Z\.%\/-_]+/g,'').match(rx_word);
    38     for (var i in tab) {
    43     for (var i in tab) {
    39         var word = tab[i].toLowerCase();
    44         var word = tab[i].toLowerCase();
    40         if (wordobj[word]) {
    45         if (stop_list.indexOf(word) == -1 && tracking_keywords.indexOf(word) == -1) {
    41             wordobj[word]++;
    46             if (wordobj[word]) {
    42         } else {
    47                 wordobj[word]++;
    43             wordobj[word] = 1;
    48             } else {
       
    49                 wordobj[word] = 1;
       
    50             }
    44         }
    51         }
    45     }
    52     }
    46 }
    53 }
    47 
    54 
    48 function addTweet(tweet) {
    55 function addTweet(tweet) {
    59                 target.backRefs = [ brobj ]
    66                 target.backRefs = [ brobj ]
    60             }
    67             }
    61         }
    68         }
    62     }
    69     }
    63     
    70     
    64     var tab = tweet.text.split(/\&\#|\;/),
    71     var ann = [];
    65         txt = '';
    72     for (var j in annotations) {
    66     for (i = 0; i < tab.length; i++) {
    73         if (j != "default") {
    67         txt += (i % 2 && parseInt(tab[i]) != NaN) ? String.fromCharCode(tab[i]) : tab[i];
    74             for (var k in annotations[j].keywords) {
    68     }
    75                 if (tweet.text.search(annotations[j].keywords[k]) != -1) {
    69     tweet.text = txt;
    76                     ann.push(j);
       
    77                     break;
       
    78                 }
       
    79             }
       
    80         }
       
    81     }
       
    82     tweet.annotations = ann;
       
    83     
       
    84     var tab = tweet.text.match(/\&\#[0-9]+\;/g);
       
    85     for (var i in tab) {
       
    86         var n = parseInt(tab[i].substr(2));
       
    87         if (n != NaN) {
       
    88             tweet.text = tweet.text.replace(tab[i], String.fromCharCode(n   ));
       
    89         }
       
    90     }
    70     
    91     
    71     twCx.tweets.push(tweet);
    92     twCx.tweets.push(tweet);
    72     twCx.idIndex.push(tweet.id);
    93     twCx.idIndex.push(tweet.id);
    73 
    94 
    74     if (tweet.in_reply_to_status_id) {
    95     if (tweet.in_reply_to_status_id) {
    78         backRef( tweet.id,  tweet.retweeted_status.id, "retweet" );
    99         backRef( tweet.id,  tweet.retweeted_status.id, "retweet" );
    79     }
   100     }
    80     
   101     
    81     countWords(tweet.text, twCx.globalWords);
   102     countWords(tweet.text, twCx.globalWords);
    82     
   103     
    83     var creadate = new Date(tweet.created_at).valueOf();
   104     var creadate = tweet.date_value;
    84     if (!twCx.timeline.length) {
   105     if (!twCx.timeline.length) {
    85         twCx.timeline = [ populateDateStruct(0, twCx.date_levels[0] * parseInt(creadate / twCx.date_levels[0])) ]
   106         twCx.timeline = [ populateDateStruct(0, twCx.date_levels[0] * parseInt(creadate / twCx.date_levels[0])) ]
    86     }
   107     }
    87     while (creadate > twCx.timeline[twCx.timeline.length - 1].end) {
   108     while (creadate > twCx.timeline[twCx.timeline.length - 1].end) {
    88         twCx.timeline.push( populateDateStruct(0, twCx.timeline[twCx.timeline.length - 1].end) );
   109         twCx.timeline.push( populateDateStruct(0, twCx.timeline[twCx.timeline.length - 1].end) );
   153     }
   174     }
   154     return struct;
   175     return struct;
   155 }
   176 }
   156 
   177 
   157 function insertIntoDateStruct(slices, tweet) {
   178 function insertIntoDateStruct(slices, tweet) {
   158     var creadate = new Date(tweet.created_at).valueOf();
   179     var creadate = tweet.date_value;
   159     for (var i in slices) {
   180     for (var i in slices) {
   160         if (creadate < slices[i].end) {
   181         if (creadate < slices[i].end) {
   161             if (slices[i].slices) {
   182             if (slices[i].slices) {
   162                 insertIntoDateStruct(slices[i].slices, tweet);
   183                 insertIntoDateStruct(slices[i].slices, tweet);
   163             } else {
   184             } else {
   210     }
   231     }
   211     html += '<div class="twmain">';
   232     html += '<div class="twmain">';
   212     a_user = '<a href="http://twitter.com/' + tweet.user.screen_name + '" var target="_blank" title="' + tweet.user.name + '">';
   233     a_user = '<a href="http://twitter.com/' + tweet.user.screen_name + '" var target="_blank" title="' + tweet.user.name + '">';
   213     html += '<div class="around_img">' + a_user + '<img class="profile_image" src="' + tweet.user.profile_image_url + '" /></a>';
   234     html += '<div class="around_img">' + a_user + '<img class="profile_image" src="' + tweet.user.profile_image_url + '" /></a>';
   214     if (className == 'full') {
   235     if (className == 'full') {
   215         html += '<p class="created_at">' + new Date(tweet.created_at).toLocaleTimeString() + '</p>';
   236         html += '<p class="created_at">' + new Date(tweet.date_value).toLocaleTimeString() + '</p>';
   216     }
   237     }
   217     html += '</div>';
   238     html += '</div>';
   218     if (className != 'icons') {
   239     if (className != 'icons') {
   219         lastend = 0;
   240         lastend = 0;
   220         var txt = '',
   241         var txt = '',
   250     }
   271     }
   251     html += '</div></' + el + '>';
   272     html += '</div></' + el + '>';
   252     return html;
   273     return html;
   253 }
   274 }
   254 
   275 
   255 function tlIdFromPos(x, y) {
   276 function tlIdFromPos(x, y, outside) {
   256     if (x < twCx.deltaX) {
   277     var ligne = Math.min( twCx.tlOnDisplay.length - 1, Math.max( 0, Math.floor(( twCx.tlHeight - y ) / twCx.scaleY) ) ),
   257         return null;
       
   258     }
       
   259     var ligne = Math.floor(( twCx.tlHeight - y ) / twCx.scaleY),
       
   260         colonne = Math.floor(( x - twCx.deltaX ) / twCx.scaleX ),
   278         colonne = Math.floor(( x - twCx.deltaX ) / twCx.scaleX ),
   261         l = 0;
   279         l = 0;
   262     if (colonne >= twCx.tlOnDisplay[ligne].totalTweets) {
   280     if (colonne >= twCx.tlOnDisplay[ligne].totalTweets || colonne < 0 ) {
   263         return null;
   281         if (outside) {
       
   282             colonne = Math.min( twCx.tlOnDisplay[ligne].totalTweets - 1, Math.max( 0, colonne ));
       
   283         } else {
       
   284             return null;
       
   285         }
   264     }
   286     }
   265     for (var i in twCx.tlOnDisplay[ligne].displayData) {
   287     for (var i in twCx.tlOnDisplay[ligne].displayData) {
   266         var nl = l + twCx.tlOnDisplay[ligne].displayData[i].length;
   288         var nl = l + twCx.tlOnDisplay[ligne].displayData[i].length;
   267         if (colonne < nl) {
   289         if (colonne < nl) {
   268             return {
   290             return {
   275 }
   297 }
   276 
   298 
   277 function tlPosTweet(tweet, annotation) {
   299 function tlPosTweet(tweet, annotation) {
   278     var x,
   300     var x,
   279         y,
   301         y,
   280         dt = new Date(tweet.created_at).valueOf(),
   302         dt = tweet.date_value,
   281         ann = ( annotation ? annotation : ( tweet.annotations.length ? tweet.annotations[0] : 'default' ) );
   303         ann = ( annotation ? annotation : ( tweet.annotations.length ? tweet.annotations[0] : 'default' ) );
   282     for (var i = 0; i < twCx.tlOnDisplay.length; i++) {
   304     for (var i = 0; i < twCx.tlOnDisplay.length; i++) {
   283         if (twCx.tlOnDisplay[i].end > dt) {
   305         if (twCx.tlOnDisplay[i].end > dt) {
   284             y = twCx.tlHeight - (i + .5) * twCx.scaleY;
   306             y = twCx.tlHeight - (i + .5) * twCx.scaleY;
   285             var l = 0;
   307             var l = 0;
   316     }
   338     }
   317 }
   339 }
   318 
   340 
   319 function drawTweetPos(pos, color) {
   341 function drawTweetPos(pos, color) {
   320     var rel = tlPaper.rect(pos.x - .5 * twCx.scaleX, pos.y - .5 * twCx.scaleY, twCx.scaleX, twCx.scaleY);
   342     var rel = tlPaper.rect(pos.x - .5 * twCx.scaleX, pos.y - .5 * twCx.scaleY, twCx.scaleX, twCx.scaleY);
   321     rel.attr({ "stroke" : color });
   343     rel.attr({ "stroke" : color, "fill" : color, "fill-opacity" : .1 });
   322     return rel;
   344     return rel;
   323 }
   345 }
   324 
   346 
   325 function updateDisplay() {
   347 function updateDisplay() {
   326     var p = twCx.position,
   348     var p = twCx.position,
   327         l = twCx.tweets.length,
   349         l = twCx.tweets.length,
   328         lines = 0,
   350         lines = 0,
       
   351         ppy = 0,
   329         html = '',
   352         html = '',
   330         tweetsOnDisplay = [],
   353         tweetsOnDisplay = [],
   331         localWords = {};
   354         localWords = {};
   332         
   355         
   333     function pushTweet(tp, className) {
   356     function pushTweet(tp, className) {
   340         }
   363         }
   341     }
   364     }
   342     
   365     
   343     if (l > p + 18) {
   366     if (l > p + 18) {
   344         lines++;
   367         lines++;
       
   368         ppy += 20;
   345         for (var i = p + 31; i >= p + 18; i--) {
   369         for (var i = p + 31; i >= p + 18; i--) {
   346             pushTweet(i, 'icons');
   370             pushTweet(i, 'icons');
   347         }
   371         }
   348     }
   372     }
   349     if (l > p + 4) {
   373     if (l > p + 4) {
   350         lines++;
   374         lines++;
       
   375         ppy += 20;
   351         for (var i = p + 17; i >= p + 4; i--) {
   376         for (var i = p + 17; i >= p + 4; i--) {
   352             pushTweet(i, 'icons');
   377             pushTweet(i, 'icons');
   353         }
   378         }
   354     }
   379     }
   355     for (var k = 3; k >= 1; k--) {
   380     for (var k = 3; k >= 1; k--) {
   356         if (l > p + k) {
   381         if (l > p + k) {
       
   382             ppy += 47;
   357             lines++;
   383             lines++;
   358             pushTweet(p + k, 'half');
   384             pushTweet(p + k, 'half');
   359         }
   385         }
   360     }
   386     }
   361     pushTweet(p, 'full');
   387     pushTweet(p, 'full');
   385     }
   411     }
   386     var tab = [];
   412     var tab = [];
   387     for (var j in localWords) {
   413     for (var j in localWords) {
   388         tab.push({
   414         tab.push({
   389             "word": j,
   415             "word": j,
   390             "freq" : localWords[j] / Math.log(twCx.globalWords[j])
   416             "freq" : localWords[j] / Math.log(1+twCx.globalWords[j])
   391         });
   417         });
   392     }
   418     }
   393     tab.sort( function(a,b){ return b.freq - a.freq }).splice(10);
   419     tab.sort( function(a,b){ return ( b.freq - a.freq ) }).splice(15);
   394     $("#motscles").html(tab.map(function(t) { return t.word }).join(", "))
   420     var minfreq = tab[tab.length - 1].freq,
       
   421         maxfreq = Math.max(minfreq + .1, tab[0].freq),
       
   422         echfreq = 8 / Math.sqrt( maxfreq - minfreq );
       
   423     $("#motscles").html(tab.map(function(t) { return '<span style="font-size: ' + Math.floor( ( 12 + Math.sqrt( t.freq - minfreq ) * echfreq ) ) + 'px">' + t.word + '</span>' }).join(" "))
   395     
   424     
   396     twCx.tlOnDisplay = trimFDS(flattenDateStruct(twCx.timeline, twCx.timeLevel));
   425     twCx.tlOnDisplay = trimFDS(flattenDateStruct(twCx.timeline, twCx.timeLevel));
   397     twCx.scaleY = twCx.tlHeight / twCx.tlOnDisplay.length;
   426     twCx.scaleY = twCx.tlHeight / twCx.tlOnDisplay.length;
   398     var maxTweets = 0,
   427     var maxTweets = 0,
   399         startTl = 0,
   428         startTl = 0,
   400         endTl = 0,
   429         endTl = 0,
   401         startTw = new Date(twCx.tweets[tweetsOnDisplay[tweetsOnDisplay.length - 1]].created_at).valueOf(),
   430         startTw = twCx.tweets[tweetsOnDisplay[tweetsOnDisplay.length - 1]].date_value,
   402         endTw = new Date(twCx.tweets[tweetsOnDisplay[0]].created_at).valueOf();
   431         endTw = twCx.tweets[tweetsOnDisplay[0]].date_value;
   403     for (var i = 0; i < twCx.tlOnDisplay.length; i++) {
   432     for (var i = 0; i < twCx.tlOnDisplay.length; i++) {
   404         if (startTw >= twCx.tlOnDisplay[i].start && startTw < twCx.tlOnDisplay[i].end) {
   433         if (startTw >= twCx.tlOnDisplay[i].start && startTw < twCx.tlOnDisplay[i].end) {
   405             startTl = i;
   434             startTl = i;
   406         }
   435         }
   407         if (endTw >= twCx.tlOnDisplay[i].start && endTw < twCx.tlOnDisplay[i].end) {
   436         if (endTw >= twCx.tlOnDisplay[i].start && endTw < twCx.tlOnDisplay[i].end) {
   434     }
   463     }
   435     twCx.scaleX = ( twCx.tlWidth - twCx.deltaX ) / maxTweets;
   464     twCx.scaleX = ( twCx.tlWidth - twCx.deltaX ) / maxTweets;
   436     tlPaper.clear();
   465     tlPaper.clear();
   437     relHover = null;
   466     relHover = null;
   438     
   467     
       
   468     // Dessin de la correspondance liste-timeline
       
   469     
       
   470     var startY = twCx.tlHeight - startTl * twCx.scaleY,
       
   471         endY = twCx.tlHeight - ( endTl + 1 ) * twCx.scaleY,
       
   472         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";
       
   473     tlPaper.path( path ).attr({ "stroke" : "none", "fill" : "#000080", "opacity" : .2 });
       
   474         
   439     // dessin de la date de début
   475     // dessin de la date de début
   440     
   476     
   441     tlPaper.text(2, twCx.tlHeight - 7, new Date(twCx.tlOnDisplay[0].start).toTimeString().substr(0,5))
   477     tlPaper.text(2, twCx.tlHeight - 7, new Date(twCx.tlOnDisplay[0].start).toTimeString().substr(0,5))
   442         .attr({ "text-anchor" : "start", "font-size": "9px" });
   478         .attr({ "text-anchor" : "start", "font-size": "9px" });
   443     
   479     
   464             tlPaper.path("M0 "+posY+"L" + twCx.tlWidth +" "+posY).attr({"stroke":"#ccc"});
   500             tlPaper.path("M0 "+posY+"L" + twCx.tlWidth +" "+posY).attr({"stroke":"#ccc"});
   465             tlPaper.text(2, posY, new Date(twCx.tlOnDisplay[i].end).toTimeString().substr(0,5)).attr({ "text-anchor" : "start", "font-size": "9px" });
   501             tlPaper.text(2, posY, new Date(twCx.tlOnDisplay[i].end).toTimeString().substr(0,5)).attr({ "text-anchor" : "start", "font-size": "9px" });
   466         }
   502         }
   467     }
   503     }
   468     
   504     
   469     // Dessin de la correspondance liste-timeline
       
   470     
       
   471     var startY = twCx.tlHeight - startTl * twCx.scaleY,
       
   472         endY = twCx.tlHeight - ( endTl + 1 ) * twCx.scaleY;
       
   473     tlPaper.path("M0 " + twCx.tlHeight + "L" + twCx.deltaX + " " + startY + "L" + twCx.tlWidth + " " + startY + "L" + twCx.tlWidth + " " + endY + "L" + twCx.deltaX + " " + endY + "L0 0" )
       
   474         .attr({ "stroke" : "none", "fill" : "#000080", "opacity" : .1 });
       
   475     
       
   476     // dessin du tweet courant
   505     // dessin du tweet courant
   477         
   506     
       
   507     
   478     var posp = tlPosTweet(twCx.tweets[p]);
   508     var posp = tlPosTweet(twCx.tweets[p]);
   479     if (posp) {
   509     if (posp) {
   480         
   510         
   481         drawTweetPos(posp, "#ffff00");
   511         drawTweetPos(posp, "#ffff00");
       
   512         var yy = posp.y - .5 * twCx.scaleY,
       
   513             path = "M0 " + ppy + "C" + ( .7 * twCx.deltaX ) + " " + ppy + " " + ( .2 * twCx.deltaX ) + " " + yy + " " + ( twCx.deltaX ) + " " + yy + "L" + ( posp.x - .5 * twCx.scaleX ) + " " + yy;
       
   514         yy = posp.y + .5 * twCx.scaleY;
       
   515         ppy += 84;
       
   516         path += "L" + ( posp.x - .5 * twCx.scaleX ) + " " + yy + "L" + twCx.deltaX + " " + yy + "C"  + ( .2 * twCx.deltaX ) + " " + yy + " " + ( .7 * twCx.deltaX ) + " " + ppy + " 0 " + ppy;
       
   517         tlPaper.path( path ).attr({"stroke":"#ffff00", "fill" : "#ffff00", "fill-opacity" : .2});
   482         
   518         
   483         // dessin des liens entre tweets
   519         // dessin des liens entre tweets
   484         
   520         
   485         function tweetAndArc(a, b, aorb) {
   521         function tweetAndArc(a, b, aorb) {
   486             if (a && b) {
   522             if (a && b) {
   513             }
   549             }
   514         }
   550         }
   515         
   551         
   516     }
   552     }
   517     
   553     
       
   554 }
       
   555 
       
   556 function clicTl(evt) {
       
   557     var o = $("#timeline").offset();
       
   558     if (twCx.tlMouseClicked && twCx.tlMouseMoved) {
       
   559         var twid = tlIdFromPos(evt.pageX - o.left + twCx.refPosTl.x - twCx.refMouse.x, evt.pageY - o.top + twCx.refPosTl.y - twCx.refMouse.y, true);
       
   560         if (twid) {
       
   561             selectTweet(twid.id);
       
   562         }
       
   563     } else {
       
   564         var twid = tlIdFromPos(evt.pageX - o.left, evt.pageY - o.top, twCx.tlMouseClicked);
       
   565         if (twCx.tlMouseMoved && !twCx.tlMouseClicked) { 
       
   566             if (twid) {
       
   567                 rolloverTweet(twid.id, twid.annotation);
       
   568             } else {
       
   569                 $("#hovertweet").hide();
       
   570             }
       
   571         }
       
   572         if (twCx.tlMouseClicked && !twCx.tlMouseMoved) {
       
   573             if (twid) {
       
   574                 selectTweet(twid.id);
       
   575             }
       
   576         }
       
   577     }
   518 }
   578 }
   519 
   579 
   520 $(document).ready(function() {
   580 $(document).ready(function() {
   521     tlPaper = Raphael("timeline", twCx.tlWidth, twCx.tlHeight);
   581     tlPaper = Raphael("timeline", twCx.tlWidth, twCx.tlHeight);
   522     socket = io.connect('http://' + document.location.hostname );
   582     socket = io.connect('http://' + document.location.hostname );
   568             wheelDelta = 0;
   628             wheelDelta = 0;
   569         }
   629         }
   570         return false;
   630         return false;
   571     });
   631     });
   572     $("#timeline, #tweetlist").mouseout(function() {
   632     $("#timeline, #tweetlist").mouseout(function() {
       
   633         twCx.tlMouseClicked = false;
       
   634         twCx.tlMouseMoved = false;
   573         $("#hovertweet").hide();
   635         $("#hovertweet").hide();
   574     });
   636     });
   575     $("#timeline").mousemove(function(evt) {
   637     $("#timeline").mousemove(function(evt) {
   576         var twid = tlIdFromPos(evt.offsetX, evt.offsetY);
   638         twCx.tlMouseMoved = true;
   577         if (twid) {
   639         clicTl(evt);
   578             rolloverTweet(twid.id, twid.annotation);
   640     });
   579         } else {
   641     $("#timeline").mousedown(function(evt) {
   580             $("#hovertweet").hide();
   642         twCx.tlMouseClicked = true;
   581         }
   643         twCx.tlMouseMoved = false;
   582     });
   644         var o = $(this).offset();
   583     $("#timeline").click(function(evt) {
   645         twCx.refMouse = { x : evt.pageX - o.left, y : evt.pageY - o.top };
   584         var twid = tlIdFromPos(evt.offsetX, evt.offsetY);
   646         twCx.refPosTl = tlPosTweet(twCx.tweets[twCx.position]) || twCx.refMouse;
   585         if (twid) {
   647     });
   586             selectTweet(twid.id);
   648     $("#timeline").mouseup(function(evt) {
   587         }
   649         clicTl(evt);
       
   650         twCx.tlMouseClicked = false;
       
   651         twCx.tlMouseMoved = false;
   588     });
   652     });
   589 });
   653 });