src/js/widgets/sparklineWidget.js
branchpopcorn-port
changeset 834 573c7ca752e0
parent 820 7968346b9689
child 835 a8af9da7c622
--- a/src/js/widgets/sparklineWidget.js	Fri Mar 16 19:31:26 2012 +0100
+++ b/src/js/widgets/sparklineWidget.js	Mon Mar 19 18:46:17 2012 +0100
@@ -4,6 +4,21 @@
 
   this._oldAnnotation = null;
   this._results = [];
+  
+  this.lineColor = this._config.lineColor || "#7492b4";
+  this.fillColor = this._config.fillColor || "#aeaeb8";
+  this.lineWidth = this._config.lineWidth || 2;
+  this.slices = this._config.slices || Math.floor(this.width/20);
+  if (!this.width) {
+      this.width = this.selector.width();
+  }
+  if (!this.height) {
+      this.height = 40;
+  }
+  this.selector.css("height", this.height + "px");
+  if (this._config.background) {
+      this.selector.css("background", this._config.background);
+  }
 };
 
 
@@ -15,19 +30,13 @@
 
 /** draw the sparkline using jquery sparkline */
 IriSP.SparklineWidget.prototype.draw = function() {
-  var templ = Mustache.to_html(IriSP.SparklineWidget_template, {width: this.width, height: this.height});
-  /** this widget uses three divs -
-    the first is the sparkline, which is generated by jquery sparkline,
-    the second is an overlay div to display the progression in the video,
-    and the third is a div to react to clicks
-  */
+    this.duration = this._serializer.getDuration();
+    this.paper = new Raphael(this.selector[0], this.width, this.height);
   
   var views = this._serializer._data.views;
   var stat_view;
   if (!IriSP.null_or_undefined(views)) {
-    
-    var i;
-    for (i = 0; i < views.length; i++) {
+    for (var i = 0; i < views.length; i++) {
       var view = views[i];
       if (view.id === "stat") {
           stat_view = view;
@@ -36,105 +45,101 @@
     }
   }
   
+    var _ = IriSP.underscore;
   // 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.getDuration();
-    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).
-    */
-    
-    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);
-        
-      }
-      
-      results.push(count);
+    if (!IriSP.null_or_undefined(stat_view)) {
+        console.log("sparklinewidget : using stats embedded in the json");
+        var _results = stat_view.meta.stat.split(",");      
+    } else {
+        var _annotations = this._serializer._data.annotations,
+            _sliceDuration = Math.floor( this.duration / this.slices),
+            _results = _(_.range(this.slices)).map(function(_i) {
+                return _(_annotations).filter(function(_a){
+                    return (_a.begin <= (1 + _i) * _sliceDuration) && (_a.end >= _i * _sliceDuration)
+                }).length;
+            });
     }
-  }
+    var _max = Math.max(1, _(_results).max()),
+        _h = this.height,
+        _scale = (_h - this.lineWidth) / _max,
+        _width = this.width / this.slices,
+        _y = _(_results).map(function(_v) {
+            return _h - (_scale * _v);
+        }),
+        _d = _(_y).reduce(function(_memo, _v, _k) {
+               return _memo + ( _k
+                   ? 'C' + (_k * _width) + ' ' + _y[_k - 1] + ' ' + (_k * _width) + ' ' + _v + ' ' + ((_k + .5) * _width) + ' ' + _v
+                   : 'M0 ' + _v + 'L' + (.5*_width) + ' ' + _v )
+            },'') + 'L' + this.width + ' ' + _y[_y.length - 1],
+        _d2 = _d + 'L' + this.width + ' ' + this.height + 'L0 ' + this.height;
+    this.paper.path(_d2).attr({
+        "stroke" : "none",
+        "fill" : this.fillColor
+    });
+         
+    this.paper.path(_d).attr({
+        "fill" : "none",
+        "stroke" : this.lineColor,
+        "stroke-width" : this.lineWidth
+    });
   
+    this.rectangleProgress = this.paper.rect(0,0,0,this.height)
+        .attr({
+            "stroke" : "none",
+            "fill" : "#808080",
+            "opacity" : .3
+        });
+    this.ligneProgress = this.paper.path("M0 0L0 "+this.height).attr({"stroke":"#ff00ff", "line-width" : 2});
   // save the results in an array so that we can re-use them when a new annotation
   // is added.
-  this._results = results;
+  this._results = _results;
   
-  this.selector.append(templ);
-  this.selector.find(".Ldt-sparkLine").css("background", "#c7c8cc");
-  this.selector.find(".Ldt-sparkLine").sparkline(results, {lineColor: "#7492b4", fillColor: "#aeaeb8",
-                                                           spotColor: "#b70056",
-                                                           width: this.width, height: this.height});
   this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.timeUpdateHandler));
-  this._Popcorn.listen("IriSP.createAnnotationWidget.addedAnnotation", IriSP.wrap(this, this.handleNewAnnotation));
+//  this._Popcorn.listen("IriSP.createAnnotationWidget.addedAnnotation", IriSP.wrap(this, this.handleNewAnnotation));
   
-  IriSP.jQuery(".Ldt-sparkLineClickOverlay").click(IriSP.wrap(this, this.clickHandler));  
+  this.selector.click(IriSP.wrap(this, this.clickHandler));  
 };
 
 /** react to a timeupdate event */
 IriSP.SparklineWidget.prototype.timeUpdateHandler = function() {
-  var currentTime = this._Popcorn.currentTime();  
-  var duration = this._serializer.getDuration() / 1000;
-  var proportion = ((currentTime / duration) * 100).toFixed(4);
-  
-  IriSP.jQuery(".Ldt-sparkLinePositionMarker").css("width", proportion + "%");                                    
+    var _currentTime = this._Popcorn.currentTime(),
+        _x = (1000 * _currentTime / this.duration) * this.width;
+    this.rectangleProgress.attr({
+        "width" : _x
+    });
+    this.ligneProgress.attr({
+        "path" : "M" + _x + " 0L" + _x + " " + this.height
+    });
+                                  
 }
 
 /** handle clicks on the widget */
 IriSP.SparklineWidget.prototype.clickHandler = function(event) {
-  /* this piece of code is a little bit convoluted - here's how it works :
-     we want to handle clicks on the progress bar and convert those to seeks in the media.
-     However, jquery only gives us a global position, and we want a number of pixels relative
-     to our container div, so we get the parent position, and compute an offset to this position,
-     and finally compute the progress ratio in the media.
-     Finally we multiply this ratio with the duration to get the correct time
-  */
-
-  var parentOffset = this.selector.offset();
-  var width = this.selector.width();
-  var relX = event.pageX - parentOffset.left;
-
-  var duration = this._serializer.getDuration() / 1000;
-  var newTime = ((relX / width) * duration).toFixed(2);
+  var relX = event.pageX - this.selector.offset().left;
+  var newTime = ((relX / this.width) * this.duration/1000).toFixed(2);
     
   this._Popcorn.trigger("IriSP.SparklineWidget.clicked", newTime);
-  this._Popcorn.currentTime(newTime);                                 
+  this._Popcorn.currentTime(newTime);
 };
 
 /** react when a new annotation is added */
 IriSP.SparklineWidget.prototype.handleNewAnnotation = function(annotation) {
-  var num_columns = this._results.length;
-  var duration = this._serializer.getDuration();
-  var time_step = Math.round(duration / num_columns); /* the time interval between two columns */
-  var begin = +annotation.begin;
-  var end = +annotation.end;
-  
-  /* increment all the values between the beginning and the end of the annotation */
-  var index_begin = Math.floor(begin / time_step);
-  var index_end = Math.floor(end / time_step);
-  
-  for (var i = index_begin; i < Math.min(index_end, this._results.length); i++) {
-    this._results[i]++;
-  }
-  
-  this.selector.find(".Ldt-sparkLine").sparkline(this._results, {lineColor: "#7492b4", fillColor: "#aeaeb8",
-                                                           spotColor: "#b70056",
-                                                           width: this.width, height: this.height});
+//  var num_columns = this._results.length;
+//  var duration = this._serializer.getDuration();
+//  var time_step = Math.round(duration / num_columns); /* the time interval between two columns */
+//  var begin = +annotation.begin;
+//  var end = +annotation.end;
+//  
+//  /* increment all the values between the beginning and the end of the annotation */
+//  var index_begin = Math.floor(begin / time_step);
+//  var index_end = Math.floor(end / time_step);
+//  
+//  for (var i = index_begin; i < Math.min(index_end, this._results.length); i++) {
+//    this._results[i]++;
+//  }
+//  
+//  this.selector.find(".Ldt-sparkLine").sparkline(this._results, {lineColor: "#7492b4", fillColor: "#aeaeb8",
+//                                                           spotColor: "#b70056",
+//                                                           width: this.width, height: this.height});
 };
\ No newline at end of file