updated player version.
authorhamidouk
Mon, 23 Jan 2012 16:53:54 +0100
changeset 430 6b848824f185
parent 421 c464acb185d6
child 431 d1051c45cff5
updated player version.
src/ldt/ldt/static/ldt/js/LdtPlayer-release.js
--- a/src/ldt/ldt/static/ldt/js/LdtPlayer-release.js	Thu Jan 19 10:48:06 2012 +0100
+++ b/src/ldt/ldt/static/ldt/js/LdtPlayer-release.js	Mon Jan 23 16:53:54 2012 +0100
@@ -1085,7 +1085,7 @@
 IriSP.annotation_template = "{{! template for an annotation displayed in a segmentWidget }}<div title='{{divTitle}}' id='{{id}}'	class='Ldt-iri-chapter' 	style='left: {{startPixel}}px;          width: {{pxWidth}}px;          background-color:#{{hexa_color}};' 	></div>";
 IriSP.annotationWidget_template = "{{! template for the annotation widget }}<div class='Ldt-AnnotationsWidget'>  <!-- ugly div because we want to have a double border -->  <div class='Ldt-Annotation-DoubleBorder'>      <div class='Ldt-AnnotationContent'>        <div class='Ldt-AnnotationShareIcons'>         <a target='_blank' class='Ldt-fbShare' href=''><img src='{{img_dir}}/facebook.png' alt='share on facebook'></img></a>         <a target='_blank' class='Ldt-TwShare' href=''><img src='{{img_dir}}/twitter.png' alt='share on twitter'></img></a>         <a target='_blank'  class='Ldt-GplusShare' href=''><img src='{{img_dir}}/google.png' alt='share on google+'></img></a>      </div>		  <div class='Ldt-SaTitle'></div>	  	<div class='Ldt-SaDescription'></div>    </div>  </div></div>";
 IriSP.annotation_loading_template = "{{! template shown while the annotation widget is loading }}<div id='Ldt-load-container'><div id='Ldt-loader'>&nbsp;</div> Chargement... </div>";
-IriSP.annotationsListWidget_template = "{{! template for the annotation list widget }}<div class='Ldt-AnnotationsListWidget'>  <!-- ugly div because we want to have a double border -->  <div class='Ldt-Annotation-DoubleBorder'>    <ul>    {{#annotations}}      <li>        <a href='#a={{id}}'>          <div style='overflow: auto; margin-top: 5px; margin-bottom: 5px;'>            <div class='Ldt-AnnotationsList-Caption'>              <img src='http://i.imgur.com/aoUlC.jpg'></img>            </div>            <div class='Ldt-AnnotationsList-Duration'>{{begin}} - {{end}}</div>            <div class='Ldt-AnnotationsList-Title'>{{title}}</div>            <div class='Ldt-AnnotationsList-Description'>{{desc}}</div>          </div>        </a>      </li>    {{/annotations}}    </ul>  </div></div>";
+IriSP.annotationsListWidget_template = "{{! template for the annotation list widget }}<div class='Ldt-AnnotationsListWidget'>  <!-- ugly div because we want to have a double border -->  <div class='Ldt-Annotation-DoubleBorder'>    <ul>    {{#annotations}}      <li>        <a href='#id={{id}}'>          <div style='overflow: auto; margin-top: 5px; margin-bottom: 5px;'>            <div class='Ldt-AnnotationsList-Caption'>              <img src='http://i.imgur.com/aoUlC.jpg'></img>            </div>            <div class='Ldt-AnnotationsList-Duration'>{{begin}} - {{end}}</div>            <div class='Ldt-AnnotationsList-Title'>{{title}}</div>            <div class='Ldt-AnnotationsList-Description'>{{desc}}</div>          </div>        </a>      </li>    {{/annotations}}    </ul>  </div></div>";
 IriSP.arrowWidget_template = "<div class='Ldt-arrowWidget'></div>";
 IriSP.createAnnotationWidget_template = "{{! template for the annotation creation widget }}<div class='Ldt-createAnnotationWidget'>  <!-- ugly div because we want to have a double border -->  <div class='Ldt-createAnnotation-DoubleBorder'>    <div class='Ldt-createAnnotation-startScreen'>      <div style='margin-bottom: 7px; overflow: auto;'>        <div class='Ldt-createAnnotation-Title'></div>        <div class='Ldt-createAnnotation-TimeFrame'></div>      </div>            <div class='Ldt-createAnnotation-Container'>        <textarea class='Ldt-createAnnotation-Description'></textarea>        <div class='Ldt-createAnnotation-profileArrow'>          <img src='{{img_dir}}/annotate_arrow.png'></img>        </div>        <div class='Ldt-createAnnotation-userAvatar'>                  <img src='https://si0.twimg.com/sticky/default_profile_images/default_profile_1_normal.png'></img>        </div>      </div>            <div class='Ldt-createAnnotation-keywords'>        Add keywords :             </div>                <div class='Ldt-createAnnotation-submitButton'>        <div style='position: absolute; bottom: 10pt; right: 11pt;'>Submit</div>      </div>    </div>    <div class='Ldt-createAnnotation-waitScreen' style='display: none; text-align: center'>      <img src='{{img_dir}}/spinner.gif'></img>      Please wait while your request is being processed...    </div>        <div class='Ldt-createAnnotation-endScreen' style='display: none'>      Thank you, your annotation has been saved.<br>      Would you like to share it on social networks ?      <div style='margin-top: 12px; text-align: center;'>          <a target='_blank' class='Ldt-createAnnotation-endScreen-TweetLink'><img src='{{img_dir}}/tweet_button.png' style='margin-right: 20px;'></img></a>          <a target='_blank' class='Ldt-createAnnotation-endScreen-FbLink'><img src='{{img_dir}}/facebook_button.png' style='margin-right: 20px;'></img></a>          <a target='_blank' class='Ldt-createAnnotation-endScreen-GplusLink'><img src='{{img_dir}}/gplus_button.png' style='margin-right: 20px;'></img></a>                          </div>    </div>  </div></div>";
 IriSP.createAnnotationWidget_festivalCinecast_template = "{{! template for the annotation creation widget specific for the cinecast festival}}<div class='Ldt-createAnnotationWidget'>  <!-- ugly div because we want to have a double border -->  <div class='Ldt-createAnnotation-DoubleBorder'>    <div style='margin-bottom: 7px; overflow: auto;'>      <div class='Ldt-createAnnotation-Title'></div>      <div class='Ldt-createAnnotation-TimeFrame'></div>    </div>        <div class='Ldt-createAnnotation-Container'>      <textarea class='Ldt-createAnnotation-Description'></textarea>      <div class='Ldt-createAnnotation-profileArrow'>        <img src='{{img_dir}}/annotate_arrow.png'></img>      </div>      <div class='Ldt-createAnnotation-userAvatar'>                <img src='https://si0.twimg.com/sticky/default_profile_images/default_profile_1_normal.png'></img>      </div>    </div>        <div class='Ldt-createAnnotation-keywords'>      Add keywords :           </div>              <div class='Ldt-createAnnotation-submitButton'>      <div style='position: absolute; bottom: 10pt; right: 11pt;'>Submit</div>    </div>        <div class='Ldt-createAnnotation-endScreen' style='display: none'>      Thank you, your annotation has been saved.<br>      Would you like to share it on social networks ?      <div style='margin-top: 12px; text-align: center;'>          <a target='_blank' class='Ldt-createAnnotation-endScreen-TweetLink'><img src='{{img_dir}}/tweet_button.png' style='margin-right: 20px;'></img></a>          <a target='_blank' class='Ldt-createAnnotation-endScreen-FbLink'><img src='{{img_dir}}/facebook_button.png' style='margin-right: 20px;'></img></a>          <a target='_blank' class='Ldt-createAnnotation-endScreen-GplusLink'><img src='{{img_dir}}/gplus_button.png' style='margin-right: 20px;'></img></a>                          </div>    </div>  </div></div>";
@@ -1466,6 +1466,14 @@
   return url;
 };
 
+/** Similar to substr but remove the last word if
+    we're breaking a word in two.
+ */ 
+IriSP.clean_substr = function(str, start, end) {
+  var s = str.substr(start, end).substr(start, end).split(" ");
+  s.pop();
+  return s.join(" ");
+};
 /* for ie compatibility
 if (Object.prototype.__defineGetter__&&!Object.defineProperty) {
    Object.defineProperty=function(obj,prop,desc) {
@@ -1584,6 +1592,7 @@
 
 IriSP.libdir = "/mdp/src/js/libs/";
 IriSP.jwplayer_swf_path = "/mdp/test/libs/player.swf";
+IriSP.platform_url = "http://localhost/pf";
 
 IriSP.lib = { 
 		jQuery : "http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js",
@@ -1636,12 +1645,21 @@
   },
   "Main" : {
       autoplay: true
-  }
-  
+  },
+  "AnnotationsListWidget" : {
+      ajax_mode: true, /* use ajax to get information about the annotations.
+                         if set to false, only search in the annotations for the
+                         current project. */
+      ajax_url: IriSP.platform_url + "/ldtplatform/api/ldt/segments", /* partial
+                                                                         url of 
+                                                                         where to
+                                                                         get the 
+                                                                         ajax */
+      ajax_granularity: 10000 /* how much ms should we look before and after the
+                                 current timecode */
+  }, 
 };
 
-IriSP.platform_url = "http://localhost/pf";
-
 IriSP.paths = {
 //  "imgs": "/tweetlive/res/metadataplayer/src/css/imgs"
   "imgs": "/mdp/src/css/imgs"
@@ -2237,6 +2255,8 @@
   IriSP.Widget.call(this, Popcorn, config, Serializer);
   this.__counter = 0;
   this.__oldList = [];
+  
+  this.ajax_mode = IriSP.widgetsDefaults["AnnotationsListWidget"].ajax_mode;
 };
 
 
@@ -2257,7 +2277,7 @@
 /** draw the annotation list */
 IriSP.AnnotationsListWidget.prototype.drawList = function(force_redraw) {
   var _this = this;
-
+  
   var view_type = this._serializer.getContributions();
   var annotations = this._serializer._data.annotations;
   var currentTime = this._Popcorn.currentTime();
@@ -2308,9 +2328,8 @@
   }
   
   this.__oldList = idList; /* save for next call */
-
+  
   if (typeof(force_redraw) !== "undefined") {
-    console.log("forced redraw");
     this.do_redraw(list);
   }
   
@@ -2323,11 +2342,74 @@
   
 };
 
+IriSP.AnnotationsListWidget.prototype.ajaxRedraw = function(timecode) {
+  var pre_url = IriSP.widgetsDefaults["AnnotationsListWidget"].ajax_url;
+  var media_id = this._serializer.currentMedia()["id"];
+  var duration = +this._serializer.currentMedia().meta["dc:duration"];
+  
+  var begin_timecode = (Math.floor(this._Popcorn.currentTime()) - 300) * 1000;
+  if (begin_timecode < 0)
+    begin_timecode = 0;
+    
+  var end_timecode = (Math.floor(this._Popcorn.currentTime()) + 300) * 1000;
+  if (end_timecode > duration)
+    end_timecode = duration;
+  
+  var templ = Mustache.to_html("{{pre_url}}/{{media_id}}/{{begin_timecode}}/{{end_timecode}}",
+                                {pre_url: pre_url, media_id: media_id, begin_timecode: begin_timecode,
+                                 end_timecode: end_timecode});
+  
+  /* we create on the fly a serializer to get the ajax */
+  var serializer = new IriSP.JSONSerializer(IriSP.__dataloader, templ);
+  serializer.sync(IriSP.wrap(this, function(json) { this.processJson(json, serializer) }));                  
+};
+
+/** process the received json - it's a bit hackish */
+IriSP.AnnotationsListWidget.prototype.processJson = function(json, serializer) {
+  /* FIXME: DRY the whole thing */
+  var annotations = serializer._data.annotations;
+  if (IriSP.null_or_undefined(annotations))
+    return;
+  
+  var view_types = serializer.getIds("Contributions");
+  var l = [];
+  
+  for (i = 0; i < annotations.length; i++) {
+      var annotation = annotations[i];
+
+      console.log(view_types, annotation.meta["id-ref"]);
+      /* filter the annotations whose type is not the one we want */
+      if (typeof(annotation.meta) !== "undefined" && typeof(annotation.meta["id-ref"]) !== "undefined"
+            && !IriSP.underscore.include(view_types, annotation.meta["id-ref"])) {
+          continue;
+      }
+  
+      var a = annotation;
+      var obj = {};
+
+      obj["id"] = a.id;
+      obj["title"] = a.content.title;
+      obj["desc"] = a.content.description;
+      obj["begin"] = IriSP.msToTime(annotation.begin);
+      obj["end"] = IriSP.msToTime(annotation.end);
+
+      l.push(obj);
+  }
+  console.log(l);
+  this.do_redraw(l);
+};
 IriSP.AnnotationsListWidget.prototype.draw = function() {
 
   this.drawList();
-  this._Popcorn.listen("IriSP.createAnnotationWidget.addedAnnotation", IriSP.wrap(this, function() { this.drawList(true); }));
-  this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.redraw));
+    
+  if (!this.ajax_mode) {    
+    this._Popcorn.listen("IriSP.createAnnotationWidget.addedAnnotation", IriSP.wrap(this, function() { this.drawList(true); }));
+    this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.redraw));
+  } else {
+    this._Popcorn.listen("seeked", IriSP.wrap(this, this.ajaxRedraw));
+    this._Popcorn.listen("paused", IriSP.wrap(this, this.ajaxRedraw));
+  }
+
 };
 
 IriSP.AnnotationsListWidget.prototype.redraw = function() {
@@ -2544,11 +2626,7 @@
 IriSP.createAnnotationWidget.prototype.draw = function() {
   var _this = this;
 
-  if (this.cinecast_version) {    
-    var annotationMarkup = IriSP.templToHTML(IriSP.createAnnotationWidget_festivalCinecast_template);
-  } else {
-    var annotationMarkup = IriSP.templToHTML(IriSP.createAnnotationWidget_template);
-  }
+  var annotationMarkup = IriSP.templToHTML(IriSP.createAnnotationWidget_template);
   
 	this.selector.append(annotationMarkup);
   
@@ -2761,14 +2839,21 @@
   } else {
     this.showWaitScreen();
     
-    this.sendLdtData(contents, function() {
-                    if (_this.cinecast_version) {
-                        if (_this._Popcorn.media.paused)
-                          _this._Popcorn.play();
-                    }
-
-                    _this.showEndScreen();
-                    window.setTimeout(IriSP.wrap(_this, function() { this.showStartScreen(); }), 5000);
+    this.sendLdtData(contents, function(annotation) {
+                      if (_this.cinecast_version) {
+                          if (_this._Popcorn.media.paused)
+                            _this._Popcorn.play();
+                      }
+
+                      _this.showEndScreen(annotation);
+                      if (_this.cinecast_version) {
+                        window.setTimeout(IriSP.wrap(_this, function() { this.showStartScreen(); }), 5000);
+                      }
+                      
+                      // hide the slicer widget
+                      if (!_this.cinecast_version) {                      
+                        _this._Popcorn.trigger("IriSP.SliceWidget.hide");
+                      }           
                     });
   }
 };
@@ -2784,12 +2869,17 @@
   var annotation = apiJson["annotations"][0];
   
   annotation["media"] = this._serializer.currentMedia()["id"];
+  var duration_part = Math.round(this._serializer.currentMedia().meta["dc:duration"] / 20);
   
-  if (this.cinecast_version) {
-    if (typeof(this._currentAnnotation) !== "undefined") {
-      annotation["begin"] = this._currentAnnotation.begin;
-      annotation["end"] = this._currentAnnotation.end;
-    }
+  if (this.cinecast_version) {   
+      annotation["begin"] = Math.round(this._Popcorn.currentTime() * 1000 - duration_part);
+      annotation["end"] = Math.round(this._Popcorn.currentTime() * 1000 + duration_part);
+      if (annotation["begin"] < 0)
+        annotation["begin"] = 0;
+      
+      if (annotation["end"] > this._serializer.currentMedia().meta["dc:duration"])
+        annotation["end"] = this._serializer.currentMedia().meta["dc:duration"];
+      
   } else {
     var duration = +this._serializer.currentMedia().meta["dc:duration"];    
     annotation["begin"] = +((duration * (this.sliceLeft / 100)).toFixed(0));
@@ -3632,7 +3722,7 @@
       /* the last segment has no segment following it */
       var pxWidth = endPixel - startPixel;
  
-    var divTitle = (annotation.content.title + " - " + annotation.content.description).substr(0,55);
+    var divTitle = IriSP.clean_substr(annotation.content.title + " -<br>" + annotation.content.description, 0, 132) + "...";
 
     if (typeof(annotation.content.color) !== "undefined")
       var color = annotation.content.color;
@@ -3819,41 +3909,44 @@
   */
   
   var currentX = this.leftHandle.position()["left"];
-  var rightHandleX = this.rightHandle.position()["left"];
+  var rightHandleX = Math.floor(this.rightHandle.position()["left"]);
   
-  if (currentX >= rightHandleX - 7 && ui.position.left >= this._leftHandleOldLeft) {
+  if (Math.floor(ui.position.left) >= rightHandleX - 7) {
     /* prevent the handle from moving past the right handle */
-    ui.position.left = this._leftHandleOldLeft;
+    ui.position.left = rightHandleX - 7;
   }
-  
-  var increment = this.zoneLeft - (currentX + 7);
+
+  this.zoneWidth = rightHandleX - Math.floor(ui.position.left) - 7;  
+  this.zoneLeft = Math.floor(ui.position.left) + 8;
   
-  this.zoneWidth += increment;
-  this.zoneLeft = currentX + 7;
   this.sliceZone.css("width", this.zoneWidth);
   this.sliceZone.css("left", this.zoneLeft + "px");
+  this._leftHandleOldLeft = Math.floor(this._leftHandleOldLeft);  
   this.broadcastChanges();
-  
-  this._leftHandleOldLeft = ui.position.left;  
+    
 };
 
 /** handle a dragging of the right handle */
 IriSP.SliceWidget.prototype.rightHandleDragged = function(event, ui) { 
-  var currentX = this.rightHandle.position()["left"];
-  var leftHandleX = this.leftHandle.position()["left"];
+  /* we have a special variable, this._leftHandleOldLeft, to keep the
+     previous position of the handle. We do that to know in what direction
+     is the handle being dragged
+  */
   
-  if (currentX <= leftHandleX + 7 && ui.position.left <= this._rightHandleOldLeft) {
-    /* prevent the handle from moving past the right handle */
-    ui.position.left = this._rightHandleOldLeft;
-  }
+  var currentX = this.leftHandle.position()["left"];
+  var leftHandleX = Math.floor(this.leftHandle.position()["left"]);
   
-  var increment = currentX - (this.zoneLeft + this.zoneWidth);  
-
-  this.zoneWidth += increment;  
+  if (Math.floor(ui.position.left) < leftHandleX + 7) {
+    /* prevent the handle from moving past the right handle */
+    ui.position.left = leftHandleX + 7;
+  }
+
+  this.zoneWidth = Math.floor(ui.position.left) - (leftHandleX + 7);    
+  
   this.sliceZone.css("width", this.zoneWidth);
+  //this.sliceZone.css("left", this.zoneLeft + "px");
+  this._rightHandleOldLeft = Math.floor(this._rightHandleOldLeft);  
   this.broadcastChanges();
-  
-  this._rightHandleOldLeft = ui.position.left; 
 };
 
 /** tell to the world that the coordinates of the slice have
@@ -4058,35 +4151,57 @@
     and the third is a div to react to clicks
   */
   
-  var num_columns = (this.selector.width()) / IriSP.widgetsDefaults["SparklineWidget"].column_width;
-  var duration = +this._serializer.currentMedia().meta["dc:duration"];
-  var time_step = duration / num_columns; /* the time interval between two columns */
-  var results = [];
-  var i = 0; /* the index in the loop */  
-
-  /* this algorithm makes one assumption : that the array is sorted 
-     (it's done for us by the JSONSerializer). We go through the array 
-     and count how many comments fall within a peculiar time piece.
-     As i is preserved between each iteration, it's O(n).
-  */
+  var views = this._serializer._data.views;
+  var stat_view;
+  if (!IriSP.null_or_undefined(views)) {
+    
+    var i;
+    for (i = 0; i < views.length; i++) {
+      var view = views[i];
+      if (view.id === "stat") {
+          stat_view = view;
+          break;
+      }
+    }
+  }
   
-  for(var j = 0; j < num_columns && i < this._serializer._data.annotations.length; j++) {    
-    var count = 0;
-    var annotation_begin = +(this._serializer._data.annotations[i].begin);
+  // If we've found the correct view, feed the directly the data from the view
+  // to jquery sparkline. Otherwise, compute it ourselves.
+  if (!IriSP.null_or_undefined(stat_view)) {
+    console.log("sparklinewidget : using stats embedded in the json");
+    var results = stat_view.meta.stat.split(",");      
+  } else {
+    console.log("sparklinewidget : computing stats ourselves");
+    var num_columns = (this.selector.width()) / IriSP.widgetsDefaults["SparklineWidget"].column_width;
+    var duration = +this._serializer.currentMedia().meta["dc:duration"];
+    var time_step = duration / num_columns; /* the time interval between two columns */
+    var results = [];
+    var i = 0; /* the index in the loop */  
+
+    /* this algorithm makes one assumption : that the array is sorted 
+       (it's done for us by the JSONSerializer). We go through the array 
+       and count how many comments fall within a peculiar time piece.
+       As i is preserved between each iteration, it's O(n).
+    */
     
-    while(annotation_begin >= j * time_step && annotation_begin <= (j + 1) * time_step ) {
-      count++;
-      i++;
-      if (i >= this._serializer._data.annotations.length)
-        break;
+    for(var j = 0; j < num_columns && i < this._serializer._data.annotations.length; j++) {    
+      var count = 0;
+      var annotation_begin = +(this._serializer._data.annotations[i].begin);
+      
+      while(annotation_begin >= j * time_step && annotation_begin <= (j + 1) * time_step ) {
+        count++;
+        i++;
+        if (i >= this._serializer._data.annotations.length)
+          break;
+          
+        annotation_begin = +(this._serializer._data.annotations[i].begin);
         
-      annotation_begin = +(this._serializer._data.annotations[i].begin);
+      }
       
+      results.push(count);
     }
-    
-    results.push(count);
   }
-
+  
   // save the results in an array so that we can re-use them when a new annotation
   // is added.
   this._results = results;
@@ -4127,15 +4242,14 @@
 
   var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000;
   var newTime = ((relX / width) * duration).toFixed(2);
-  
-  
+    
   this._Popcorn.trigger("IriSP.SparklineWidget.clicked", newTime);
   this._Popcorn.currentTime(newTime);                                 
 };
 
 /** react when a new annotation is added */
 IriSP.SparklineWidget.prototype.handleNewAnnotation = function(annotation) {
-  var num_columns = (this.selector.width()) / IriSP.widgetsDefaults["SparklineWidget"].column_width;
+  var num_columns = this._results.length;
   var duration = +this._serializer.currentMedia().meta["dc:duration"];
   var time_step = Math.round(duration / num_columns); /* the time interval between two columns */
   var begin = +annotation.begin;
@@ -4147,7 +4261,7 @@
                                                            width: this.width, height: this.height});
 };IriSP.StackGraphWidget = function(Popcorn, config, Serializer) {
   IriSP.Widget.call(this, Popcorn, config, Serializer);
-};
+}
 
 IriSP.StackGraphWidget.prototype = new IriSP.Widget();
 
@@ -4175,10 +4289,10 @@
             },
         ],
         _defaultDefColor = "#585858";
-    this.height = (this._config.height ? this._config.height : 50);
+    this.height =  this._config.height || 50;
     this.width = this.selector.width();
-    this.isStreamGraph = (this._config.streamgraph ? this._config.streamgraph : false);
-    this.sliceCount = (this._config.slices ? this._config.slices : ~~(this.width/(this.isStreamGraph ? 20 : 5)));
+    this.isStreamGraph = this._config.streamgraph || false;
+    this.sliceCount = this._config.slices || ~~(this.width/(this.isStreamGraph ? 20 : 5));
     this.tagconf = (this._config.tags
         ? this._config.tags
         : _defaultTags);
@@ -4196,17 +4310,19 @@
     
     var _annotationType = this._serializer.getTweets(),
         _sliceDuration = ~~ ( this.duration / this.sliceCount),
-        _annotations = IriSP._(this._serializer._data.annotations).filter(function(_a) {
-            return ( _a.meta && _a.meta["id-ref"] && ( _a.meta["id-ref"] == _annotationType ) );
-        }),
-        _groupedAnnotations = IriSP._(_annotations).groupBy(function(_a) {
-            return ~~ (_a.begin / _sliceDuration);
+        _annotations = this._serializer._data.annotations,
+        _groupedAnnotations = IriSP._.range(this.sliceCount).map(function(_i) {
+            return _annotations.filter(function(_a){
+                return (_a.begin <= (1 + _i) * _sliceDuration) && (_a.end >= _i * _sliceDuration)
+            });
         }),
         _max = IriSP._(_groupedAnnotations).max(function(_g) {
             return _g.length
         }).length,
         _scale = this.height / _max,
-        _width = this.width / this.sliceCount;
+        _width = this.width / this.sliceCount
+        _showTitle = !this._config.excludeTitle,
+        _showDescription = !this._config.excludeDescription;
     
     
     var _paths = this.tagconf.map(function() {
@@ -4221,7 +4337,7 @@
                 return 0;
             });
             for (var j = 0; j < _group.length; j++){
-                var _txt = _group[j].content.description;
+           var _txt = (_showTitle ? _group[j].content.title : '') + ' ' + (_showDescription ? _group[j].content.description : '')
                 var _tags = this.tagconf.map(function(_tag) {
                         return (_txt.search(_tag.regexp) == -1 ? 0 : 1)
                     }),
@@ -4301,11 +4417,16 @@
     this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.timeUpdateHandler));
     var _this = this;
     this.selector
-        .click(function(_e) {
-            _this.clickHandler(_e);
-        })
-        .mousemove(function(_e) {
-            _this.updateTooltip(_e);
+        .click(IriSP.wrap(this, this.clickHandler))
+        .mousemove(function(event) {
+            _this.updateTooltip(event);
+            
+            // Also tell the world where the mouse is hovering.
+            var relX = event.pageX - _this.selector.offset().left;
+            var duration = _this._serializer.currentMedia().meta["dc:duration"];
+            var Time = ((relX / _this.width) * duration).toFixed(2);
+            _this._Popcorn.trigger("IriSP.StackGraphWidget.mouseOver", Time);
+
         })
         .mouseout(function() {
             _this.TooltipWidget.hide();
@@ -4313,7 +4434,7 @@
                 "opacity" : 0,
             })
         })
-};
+}
 
 IriSP.StackGraphWidget.prototype.timeUpdateHandler = function() {
     var _currentTime = this._Popcorn.currentTime(),
@@ -4324,7 +4445,7 @@
     this.ligneProgress.attr({
         "path" : "M" + _x + " 0L" + _x + " " + this.height,
     })
-};
+}
 
 IriSP.StackGraphWidget.prototype.clickHandler = function(event) {
   /* Ctrl-C Ctrl-V'ed from another widget
@@ -4356,8 +4477,78 @@
         "x" : _segment * _width,
         "opacity" : .4,
     })
-};
-
+}
+
+IriSP.TagCloudWidget = function(Popcorn, config, Serializer) {
+  IriSP.Widget.call(this, Popcorn, config, Serializer);
+}
+
+IriSP.TagCloudWidget.prototype = new IriSP.Widget();
+
+IriSP.TagCloudWidget.prototype.draw = function() {
+    
+    var _stopwords = [
+            'aussi', 'and', 'avec', 'aux', 'car', 'cette', 'comme', 'dans', 'donc', 'des', 'elle', 'est',
+            'être', 'eux', 'fait', 'ici', 'ils', 'les', 'leur', 'leurs', 'mais', 'mes', 'même', 'mon', 'notre',
+            'nos', 'nous', 'ont', 'par', 'pas', 'peu', 'pour', 'que', 'qui', 'ses' ,'son', 'sont', 'sur',
+            'tes', 'très', 'the', 'ton', 'tous', 'tout', 'une', 'votre', 'vos', 'vous'
+        ],
+        _regexpword = /[^\s\.&;,'"!\?\d\(\)\+\[\]\\\…\-«»:\/]{3,}/g,
+        _words = {},
+        _showTitle = !this._config.excludeTitle,
+        _showDescription = !this._config.excludeDescription,
+        _tagCount = this._config.tagCount || 30;
+    
+    IriSP._(this._serializer._data.annotations).each(function(_annotation) {
+       if (_annotation.content && _annotation.content.description) {
+           var _txt = (_showTitle ? _annotation.content.title : '') + ' ' + (_showDescription ? _annotation.content.description : '')
+           IriSP._(_txt.toLowerCase().match(_regexpword)).each(function(_mot) {
+               if (_stopwords.indexOf(_mot) == -1) {
+                   _words[_mot] = 1 + (_words[_mot] || 0);
+               }
+           })
+       } 
+    });
+    
+    _words = IriSP._(_words)
+        .chain()
+        .map(function(_v, _k) {
+            return {
+                "word" : _k,
+                "count" : _v,
+            }
+        })
+        .filter(function(_v) {
+            return _v.count > 2;
+        })
+        .sortBy(function(_v) {
+            return - _v.count;
+        })
+        .first(_tagCount)
+        .value();
+    var _max = _words[0].count,
+        _min = Math.min(_words[_words.length - 1].count, _max - 1),
+        _scale = 16 / Math.sqrt(_max - _min),
+        _html = '<ul>'
+            + IriSP._(_words)
+                .chain()
+                .shuffle()
+                .map(function(_word) {
+                    var _size = 10 + _scale * Math.sqrt(_word.count - _min);
+                    return '<li style="font-size:'
+                        + _size
+                        + 'px;">'
+                        + _word.word
+                        + '</li>'
+                })
+                .value()
+                .join("")
+            + '</ul>';
+    this.selector
+        .addClass("Ldt-TagCloud")
+        .html(_html);
+    
+}
 /* this widget displays a small tooltip */
 IriSP.TooltipWidget = function(Popcorn, config, Serializer) {
   IriSP.Widget.call(this, Popcorn, config, Serializer);
@@ -4379,7 +4570,7 @@
 };
 
 IriSP.TooltipWidget.prototype.clear = function() {
-	this.selector.find(".tiptext").text("");
+	this.selector.find(".tiptext").html("");
 };
 
 IriSP.TooltipWidget.prototype.show = function(text, color, x, y) {
@@ -4389,7 +4580,7 @@
 
   this.selector.find(".tipcolor").css("background-color", color);
   this._displayedText = text;
-	this.selector.find(".tiptext").text(text);
+	this.selector.find(".tiptext").html(text);
   
   if (x < 0)
     x = 0;
@@ -4912,7 +5103,7 @@
     @param name of the ligne de temps
 */
 IriSP.JSONSerializer.prototype.getId = function(name) {
-  if (typeof(this._data.lists) === "undefined" || this._data.lists === null)
+  if (IriSP.null_or_undefined(this._data["annotation-types"]))
     return;
 
   name = name.toUpperCase();
@@ -4928,6 +5119,20 @@
   return id;
 };
 
+/** return the list of id's of the ligne de temps which contains name
+    @param name of the ligne de temps
+*/
+IriSP.JSONSerializer.prototype.getIds = function(name) {
+  if (IriSP.null_or_undefined(this._data["annotation-types"]))
+    return;
+
+  name = name.toUpperCase();
+  var e = [];  
+  e = IriSP.underscore.filter(this._data["annotation-types"], 
+                                  function(entry) { return (entry["dc:title"].toUpperCase().indexOf(name) !== -1) });
+  return IriSP.underscore.pluck(e, "id");  
+};
+
 /** return the id of the ligne de temps named "Chapitrage" */
 IriSP.JSONSerializer.prototype.getChapitrage = function() {
   var val = this.getId("Chapitrage");