tweetcast/nodejs-bis/client/js/script.js
changeset 391 698e4280d270
parent 390 239f91ac1f31
child 403 dd1686ae5506
--- a/tweetcast/nodejs-bis/client/js/script.js	Tue Nov 22 18:33:51 2011 +0100
+++ b/tweetcast/nodejs-bis/client/js/script.js	Wed Nov 23 18:49:52 2011 +0100
@@ -5,19 +5,18 @@
 var socket,
     tlPaper,
     twCx = {
-        zoomLevel : 1,
         followLast : true,
         position : "0",
         date_levels : [
+            3600 * 1000,
             15 * 60 * 1000,
             5 * 60 * 1000,
-            60 * 1000,
-            15 * 1000
+            60 * 1000
         ],
-        timeLevel : 0,
+        timeLevel : 1,
         deltaX : 40,
         tlWidth : 150,
-        tlHeight : 450,
+        tlHeight : 480,
         globalWords : {},
         refMouse : { x : 0, y : 0},
         refPosTl : { x : 0, y : 0},
@@ -29,7 +28,7 @@
     relHover = [],
     wheelDelta = 0,
     scrollEnabled = false,
-    scrollExtent = 8000 - 452,
+    scrollExtent = 8000 - 480,
     lastScrollPos = 0,
     rx_url = /https?:\/\/[0-9a-zA-Z\.%\/-_]+/g,
     rx_word = /[^ \.&;,'"!\?\d\(\)\+\[\]\\\…\-«»:\/]{3,}/g,
@@ -170,8 +169,14 @@
 }
 
 function trimFDS() {
-    var centralTweet = ( twCx.centralTweet ? twCx.centralTweet : twCx.tweets[twCx.tweets.length - 1] )
-        slices = flattenDateStruct(twCx.timeline, twCx.timeLevel),
+    var slices = flattenDateStruct(twCx.timeline, twCx.timeLevel);
+    while (slices[0].tweets.length == 0) {
+        slices.splice(0,1);
+    }
+    while (slices[slices.length - 1].tweets.length == 0) {
+        slices.pop();
+    }    
+    var centralTweet = ( twCx.centralTweet ? twCx.centralTweet : twCx.tweets[twCx.tweets.length - 1] ),
         delta = 30 * twCx.date_levels[twCx.timeLevel],
         centre = Math.min(slices[slices.length - 1].end - delta , Math.max(slices[0].start + delta, centralTweet.date_value)),
         min = centre - delta,
@@ -235,16 +240,21 @@
 }
 
 function goToPos(nPos) {
-    twCx.position = twCx.idIndex[Math.min( twCx.tweets.length - 1, Math.max(0, nPos ) )];
-    twCx.followLast = (nPos == twCx.tweets.length - 1);
+    twCx.position = twCx.currentIdIndex[Math.min( twCx.currentIdIndex.length - 1, Math.max(0, nPos ) )];
+    twCx.followLast = (!twCx.filtre && nPos == twCx.tweets.length - 1);
     updateDisplay();
 }
 
 function movePos(delta) {
-    goToPos( delta + twCx.idIndex.indexOf(twCx.position) );
+    goToPos( delta + twCx.currentIdIndex.indexOf(twCx.position) );
 }
 
 function tweetToHtml(tweet, className, elName) {
+    
+    function highlight(texte) {
+        return ( twCx.filtre ? texte.replace(twCx.filtre, '<span class="highlight">$1</span>' ) : texte );
+    }
+    
     if (!tweet) {
         return placeHolder(className);
     }
@@ -261,12 +271,12 @@
     if (tweet.annotations.length) {
         html += '<div class="annotations">';
         for (var i in tweet.annotations) {
-            html += '<div class="annotation" style="width:' + (100/tweet.annotations.length) + '%; background:' + getColor(tweet.annotations[i], (className == 'icons' ? .4 : .8)).hex + '"></div>';
+            html += '<div class="annotation" style="width:' + (100/tweet.annotations.length) + '%; background:' + getColor(tweet.annotations[i], (className == 'icons' ? .4 : .85)).hex + '"></div>';
         }
         html += '</div>';
     }
     html += '<div class="twmain">';
-    a_user = '<a href="http://twitter.com/' + tweet.user.screen_name + '" var target="_blank" title="' + tweet.user.name + '">';
+    a_user = '<a href="http://twitter.com/' + tweet.user.screen_name + '" var target="_blank">';
     html += '<div class="around_img">' + a_user + '<img class="profile_image" src="' + tweet.user.profile_image_url + '" /></a>';
     if (className == 'full') {
         html += '<p class="created_at">' + new Date(tweet.date_value).toTimeString().substr(0,8) + '</p>';
@@ -280,7 +290,8 @@
             entities.push({
                 "start" : tweet.entities.hashtags[i].indices[0],
                 "end" : tweet.entities.hashtags[i].indices[1],
-                "html" : '<a href="http://twitter.com/search?q=%23' + tweet.entities.hashtags[i].text + '" target="_blank">#' + tweet.entities.hashtags[i].text + '</a>'
+                "link" : '<a href="http://twitter.com/search?q=%23' + tweet.entities.hashtags[i].text + '" target="_blank">',
+                "text" : '#' + tweet.entities.hashtags[i].text
             });
         }
         for (var i in tweet.entities.urls) {
@@ -292,23 +303,30 @@
             entities.push({
                 "start" : tweet.entities.urls[i].indices[0],
                 "end" : tweet.entities.urls[i].indices[1],
-                "html" : '<a href="' + linkurl  + '" target="_blank">' + dispurl + '</a>'
+                "link" : '<a href="' + linkurl  + '" target="_blank">',
+                "text" : dispurl
             });
         }
         for (var i in tweet.entities.user_mentions) {
             entities.push({
                 "start" : tweet.entities.user_mentions[i].indices[0],
                 "end" : tweet.entities.user_mentions[i].indices[1],
-                "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>'
+                "link" : '<a href="http://twitter.com/' + tweet.entities.user_mentions[i].screen_name + '" target="_blank">',
+                "text" : '@' + tweet.entities.user_mentions[i].screen_name
             });
         }
         entities.sort(function(a, b) { return a.start - b.start });
         for (var i in entities) {
-            txt += tweet.text.substring(lastend, entities[i].start) + entities[i].html;
+            txt += highlight( tweet.text.substring(lastend, entities[i].start) ) + entities[i].link + highlight( entities[i].text ) + '</a>';
             lastend = entities[i].end;
         }
-        txt += tweet.text.substring(lastend);
-        html += '<p class="tweet_text"><b>' + a_user + '@' + tweet.user.screen_name + '</b></a>: ' + txt + '</p>';
+        txt += highlight( tweet.text.substring(lastend) );
+        html += '<p class="tweet_text"><b>' + a_user + highlight('@' + tweet.user.screen_name) + '</a>' + ( className == 'full' ? ' (' + tweet.user.name + ')</b><br />' : '</b> : ') + txt + '</p>';
+        if (className == 'full' && el == 'li') {
+            html += '<div class="tweet_actions"><a href="http://twitter.com/intent/tweet?in_reply_to=' + tweet.id + '" target="_blank">répondre</a> · ';
+            html += '<a href="http://twitter.com/intent/retweet?tweet_id=' + tweet.id + '" target="_blank">retweeter</a> · ';
+            html += '<a href="http://twitter.com/intent/favorite?tweet_id=' + tweet.id + '" target="_blank">favori</a></div>';
+        }
     }
     html += '</div></' + el + '>';
     return html;
@@ -341,6 +359,9 @@
 }
 
 function tlPosTweet(tweet, annotation) {
+    if (!twCx.tweets) {
+        return;
+    }
     var x,
         y,
         dt = tweet.date_value,
@@ -437,11 +458,15 @@
 }
 
 function updateDisplay() {
-    
+    if (!twCx.tweets) {
+        return;
+    }
     if (twCx.filtre) {
         var tweets = twCx.tweets.filter(function(tweet) {
-            return ( tweet.text.search(twCx.filtre) != -1 ) || ( tweet.user.screen_name.search(twCx.filtre) != -1 )
+            var mention = '@' + tweet.user.screen_name;
+            return ( tweet.text.search(twCx.filtre) != -1 ) || ( mention.search(twCx.filtre) != -1 );
         });
+        $("#inp_q").val(twCx.filtreTexte + ' (' + tweets.length + ' tweets)');
         if (tweets.length) {
             var idIndex = tweets.map(function(tweet) {
                 return tweet.id;
@@ -451,10 +476,12 @@
                 for (p = idIndex.length - 1; p > 0 && idIndex[p] > twCx.position; p--) {
                 }
             }
-            console.log(p);
+            twCx.position = idIndex[p];
+            twCx.currentIdIndex = idIndex;
         }
         
     } else {
+        twCx.currentIdIndex = twCx.idIndex;
         var tweets = twCx.tweets;
         var p = twCx.idIndex.indexOf(twCx.position);
         if (p == -1) {
@@ -547,45 +574,49 @@
         
         /* Recherche des mots pertinents correspondant à la sélection */
         
-        for (var j in localWords) {
-            if (localWords[j].freq < 2) delete localWords[j];
-        }
-        var tab = [];
-        for (var j in localWords) {
-            tab.push({
-                "word": j,
-                "freq" : localWords[j].freq,
-                "annotations" : localWords[j].annotations,
-                "score" : localWords[j].freq / Math.log(1+twCx.globalWords[j])
-            });
+        var tab = _(localWords).map(function(v, k) {
+            return {
+                "word": k,
+                "freq" : v.freq,
+                "annotations" : v.annotations,
+                "score" : v.freq / Math.log( 2 + twCx.globalWords[j] )
+            };
+        }).filter(function(v) {
+            return v.freq > 1;
+        });
+        
+        if (tab.length) {
+            
+            tab = _(tab).sortBy( function(a) { return ( - a.score ) }).slice(0,20);
+            var minfreq = _(tab).min( function(a) { return a.freq} ).freq,
+                maxfreq = Math.max(minfreq + .1, _(tab).max( function(a) { return a.freq} ).freq),
+                echfreq = 8 / Math.sqrt( maxfreq - minfreq ),
+                html = '';
+            for (var j in tab) {
+                var maxann = 0,
+                    ann = "default";
+                for (var k in tab[j].annotations) {
+                    if (tab[j].annotations[k] == maxann) {
+                        ann = "default";
+                    }
+                    if (tab[j].annotations[k] > maxann) {
+                        ann = k;
+                        maxann = tab[j].annotations[k];
+                    }
+                }
+                if (ann == "default") {
+                    var coul = '';
+                } else {
+                    var c = getColor(ann, .6),
+                        coul = "background: rgba(" + [ Math.floor(c.r), Math.floor(c.g), Math.floor(c.b), ( tab[j].annotations[ann] / tab[j].freq )].join(',') + ")";
+                }
+                var fontsize = Math.floor( ( 12 + Math.sqrt( tab[j].freq - minfreq ) * echfreq ) );
+                html += '<span style="line-height: ' + (8 + fontsize) + 'px; font-size: ' + fontsize + 'px;' + coul + '" onclick="filtrerTexte(\'' + tab[j].word.replace(/('|")/g, '\\$1') + '\')">' + tab[j].word + '</span> ';
+            }
+            $("#motscles").html(html);
+        } else {
+            $("#motscles").html('');
         }
-        tab.sort( function(a,b){ return ( b.score - a.score ) }).splice(20);
-        var minfreq = tab[tab.length - 1].score,
-            maxfreq = Math.max(minfreq + .1, tab[0].score),
-            echfreq = 8 / Math.sqrt( maxfreq - minfreq ),
-            html = '';
-        for (var j in tab) {
-            var maxann = 0,
-                ann = "default";
-            for (var k in tab[j].annotations) {
-                if (tab[j].annotations[k] == maxann) {
-                    ann = "default";
-                }
-                if (tab[j].annotations[k] > maxann) {
-                    ann = k;
-                    maxann = tab[j].annotations[k];
-                }
-            }
-            if (ann == "default") {
-                var coul = '';
-            } else {
-                var c = getColor(ann, .6),
-                    coul = "background: rgba(" + [ Math.floor(c.r), Math.floor(c.g), Math.floor(c.b), ( tab[j].annotations[ann] / tab[j].freq )].join(',') + ")";
-            }
-            var fontsize = Math.floor( ( 12 + Math.sqrt( tab[j].score - minfreq ) * echfreq ) );
-            html += '<span style="line-height: ' + (8 + fontsize) + 'px; font-size: ' + fontsize + 'px;' + coul + '" onclick="filtrer(\'' + tab[j].word.replace(/('|")/g, '\\$1') + '\')">' + tab[j].word + '</span> ';
-        }
-        $("#motscles").html(html);
         twCx.centralTweet = tweets[p];
     } else {
         $("#tweetlist").html('');
@@ -700,7 +731,7 @@
             var yy = posp.y - .5 * twCx.scaleY,
                 path = "M0 " + ppy + "C" + ( .7 * twCx.deltaX ) + " " + ppy + " " + ( .2 * twCx.deltaX ) + " " + yy + " " + ( twCx.deltaX ) + " " + yy + "L" + ( posp.x - .5 * twCx.scaleX ) + " " + yy;
             yy = posp.y + .5 * twCx.scaleY;
-            ppy += 84;
+            ppy += 117;
             path += "L" + ( posp.x - .5 * twCx.scaleX ) + " " + yy + "L" + twCx.deltaX + " " + yy + "C"  + ( .2 * twCx.deltaX ) + " " + yy + " " + ( .7 * twCx.deltaX ) + " " + ppy + " 0 " + ppy;
             tlPaper.path( path ).attr({"stroke":"#ffff00", "fill" : "#ffff00", "fill-opacity" : .15});
             
@@ -709,11 +740,25 @@
     }
 }
 
-function filtrer(valeur) {
-    twCx.filtreTexte = valeur;
-    $("#inp_q").val(valeur).attr("class","rechercheCourante");
-    twCx.filtre = ( valeur ? new RegExp(valeur.replace(/(\W)/g, '\\$1'),'ig') : null );
-    twCx.followLast = !valeur && (twCx.position == twCx.idIndex[twCx.tweets.length - 1]);
+function filtrerAnnotation(annotation) {
+    if (annotations[annotation]) {
+        effectuerFiltrage(annotations[annotation].display_name,
+            new RegExp( "(" + annotations[annotation].keywords.map(function(a) { return a.source }).join("|") + ")", "gi" ) );
+    } else {
+        effectuerFiltrage('', null)
+    }
+}
+
+function filtrerTexte(valeur) {
+    effectuerFiltrage( valeur, valeur ? new RegExp("(" + valeur.replace(/(\W)/g, '\\$1') + ")" ,'gi') : null );
+}
+
+function effectuerFiltrage(filtreTexte, tabRegexp) {
+    $("#recherche_annot").slideUp();
+    $("#inp_q").val(filtreTexte).attr("class","rechercheCourante");
+    twCx.filtreTexte = filtreTexte;
+    twCx.filtre = tabRegexp;
+    twCx.followLast = !tabRegexp && (twCx.position == twCx.idIndex[twCx.idIndex.length - 1]);
     updateDisplay();
 }
 
@@ -755,17 +800,36 @@
 }
 
 function focusOutRecherche() {
+    $("#recherche_annot").slideUp();
     var inpq = $("#inp_q"),
         val = inpq.val();
     if (val == '' || val == twCx.filtreTexte) {
         if (twCx.filtre) {
             inpq.attr("class", "rechercheCourante").val(twCx.filtreTexte);
         } else {
-            inpq.attr("class", "greyed").val("Rechercher dans les tweets");
+            inpq.attr("class", "greyed").val("Rechercher");
         }
     }
 }
 
+function chaineTimeZoom() {
+    var chaine = "",
+        t = twCx.date_levels[twCx.timeLevel],
+        h = 3600*1000,
+        m = 60*1000,
+        s = 1000,
+        heures = Math.floor(t/h);
+    if (heures) { chaine += heures + ' h. ' };
+    t -= (heures * h);
+    var minutes = Math.floor(t/m);
+    if (minutes) { chaine += minutes + ' min. ' };
+    t -= (minutes * m);
+    if (t) { chaine += Math.floor(t/s) + ' sec.' }
+    $("#time_scale").html(chaine);
+    $("#time_zoomout").attr("class",(twCx.timeLevel == 0 ? "inactive" : ""));
+    $("#time_zoomin").attr("class",(twCx.timeLevel == twCx.date_levels.length - 1 ? "inactive" : ""));
+}
+
 $(document).ready(function() {
     tlPaper = Raphael("timeline", twCx.tlWidth, twCx.tlHeight);
     
@@ -791,6 +855,15 @@
         $.getScript("tweetdata.js");
     }
     
+    var html = '';
+    for (var j in annotations) {
+        if (j != "default") {
+            html += '<a href="#" style="background: ' + getColor(j, .7).hex + ';" onclick=filtrerAnnotation(\'' + j + '\'); return false;">' + annotations[j].display_name + '</a> '
+        }
+    }
+    $("#rech_list_annot").html(html);
+    
+    chaineTimeZoom();
     
     $("#tweetlist").mousewheel(function(e, d) {
         wheelDelta += d;
@@ -810,12 +883,27 @@
             }
             if (tl != twCx.timeLevel) {
                 twCx.timeLevel = tl;
+                chaineTimeZoom();
                 updateDisplay();
             }
             wheelDelta = 0;
         }
         return false;
     });
+    $("#time_zoomin").click(function() {
+        if (twCx.timeLevel < twCx.date_levels.length - 1) {
+            twCx.timeLevel++;
+            chaineTimeZoom();
+            updateDisplay();
+        }
+    });
+    $("#time_zoomout").click(function() {
+        if (twCx.timeLevel > 0) {
+            twCx.timeLevel--;
+            chaineTimeZoom();
+            updateDisplay();
+        }
+    });
     $("#timeline, #tweetlist").mouseout(function() {
         twCx.tlMouseClicked = false;
         twCx.tlMouseMoved = false;
@@ -838,6 +926,8 @@
         twCx.tlMouseMoved = false;
     });
     $("#inp_q").focus(function() {
+        $("#recherche_annot").slideDown();
+        $(this).val($(this).val().replace(/ \(.+\)$/, ''))
         if ($(this).hasClass("greyed")) {
             $(this).val("");
         }
@@ -847,25 +937,28 @@
         focusOutRecherche();
     });
     $("#inp_reset").click(function() {
+        $("#inp_q").val('');
         if (twCx.filtre) {
             twCx.filtre = null;
             updateDisplay();
         }
+        twCx.filtreTexte = '';
         focusOutRecherche();
         return false;
     })
-    $("#recherche").submit(function() {
+    $("#recherche").submit(function(evt) {
+        evt.preventDefault();
         if (!$("#inp_q").hasClass("greyed")) {
             var valeur = $("#inp_q").val();
-            filtrer(valeur);
+            filtrerTexte(valeur);
         }
         return false;
     });
     
     setInterval(function() {
         var sc = $("#scrollcont");
-        if (sc.scrollTop() != lastScrollPos) {
-            var p = Math.floor( twCx.tweets.length * ( 1 - sc.scrollTop() / scrollExtent ) );
+        if (sc.scrollTop() != lastScrollPos && twCx.tweets) {
+            var p = Math.floor( twCx.currentIdIndex.length * ( 1 - sc.scrollTop() / scrollExtent ) );
             goToPos(p);
         }