]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-2074 Improve timeline widget
authorFabrice Bellingard <bellingard@gmail.com>
Tue, 23 Aug 2011 16:23:18 +0000 (18:23 +0200)
committerFabrice Bellingard <bellingard@gmail.com>
Tue, 23 Aug 2011 16:23:18 +0000 (18:23 +0200)
- Add event labels

plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/timeline.html.erb
sonar-server/src/main/webapp/javascripts/protovis-sonar.js

index a8d8d10ee459285a6d6ada0e90d6ca565316734a..0979c233583cb97f80ea1910844a447bd16eb3f3 100644 (file)
   js_snapshots += "]"
   js_metrics += "]"
   
+  # Prepare also event structure if required
+  if widget_properties["displayEvents"]
+    events = {}
+    Event.find(:all, :conditions => {:resource_id => @resource.id}, :order => 'event_date').each() do |event|
+      if events[event.event_date]
+        events[event.event_date] << event
+      else
+        date_entry = [event]
+        events[event.event_date] = date_entry
+      end
+    end
+    js_events = "["
+    events.keys().sort.each() do |e_date|
+      e_details = events[e_date]
+      js_events += "{\"d\":d("
+      js_events += e_date.year.to_s
+      js_events += ","
+      # Need to decrease by 1 the month as the JS Date object start months at 0 (= January)
+      js_events += (e_date.month - 1).to_s
+      js_events += ","
+      js_events += e_date.day.to_s
+      js_events += "),\"l\":["
+      e_details.each() do |e|
+        js_events += "{\"n\":\""
+        js_events += e.name
+        js_events +=  "\",\"ld\":\""
+        js_events += human_short_date(e_date)
+        js_events += "\"},"
+      end 
+      js_events += "]},"
+    end
+    js_events += "]"
+  end
+  
   # And prepare translations for labels
   js_translations = "{"
   js_translations += "\"date\":\"" + message("date") + "\""
   js_translations += "}"
   
+  # Check if the widget height was specified
+  widgetHeight = widget_properties["widgetHeight"].to_i == 0 ? "null" : widget_properties["widgetHeight"]
 %>
 
 <% if widget_properties["chartTitle"] %>
          var snapshots = <%= js_snapshots -%>;
          var metrics = <%= js_metrics -%>;
          var translations = <%= js_translations -%>;
+         var events = <%= js_events ? js_events : "null" -%>;
          var timeline = new SonarWidgets.Timeline('timeline-chart-<%= widget.id -%>')
-                                                       .height(<%= widget_properties["widgetHeight"] ? widget_properties["widgetHeight"] : "null" -%>)
+                                                       .height(<%= widgetHeight -%>)
                                                        .data(data)
                                                        .snapshots(snapshots)
                                                        .metrics(metrics)
-                                                       .translations(translations);
+                                                       .translations(translations)
+                                                       .events(events);
          timeline.render();
        
        </script>
index 1de76f4c5ac2b40a558717e4700e8d736620ca98..4067c2b0c6bc052eb338c23f28ebcc812aab39d2 100755 (executable)
@@ -7,6 +7,7 @@ SonarWidgets.Timeline = function (divId) {
        this.wSnapshots;
        this.wMetrics;
        this.wTranslations;
+       this.wEvents;
        this.height = function(height) {
                this.wHeight = height;
                return this;
@@ -27,6 +28,10 @@ SonarWidgets.Timeline = function (divId) {
                this.wTranslations = translations;
                return this;
        }
+       this.events = function(events) {
+               this.wEvents = events;
+               return this;
+       }
 }
 
 SonarWidgets.Timeline.prototype.render = function() {
@@ -35,10 +40,13 @@ SonarWidgets.Timeline.prototype.render = function() {
        var metrics = this.wMetrics;
        var snapshots = this.wSnapshots;
        var translations = this.wTranslations;
+       var events = this.wEvents;
        var widgetDiv = document.getElementById(this.wDivId);
        
+       var footerFont = "12px Arial,Helvetica,sans-serif";
+       
        /* Sizing and scales. */
-       var footerHeight = 30 + this.wMetrics.size() * 12;
+       var footerHeight = 30 + (events ? 3 : this.wMetrics.size()) * 12;
        var w = widgetDiv.parentNode.clientWidth - 60, 
                h = (this.wHeight == null ? 250 : this.wHeight) + footerHeight - 5,
                S=2;
@@ -46,7 +54,7 @@ SonarWidgets.Timeline.prototype.render = function() {
        var x = pv.Scale.linear(pv.blend(pv.map(data, function(d) {return d;})), function(d) {return d.x}).range(0, w);
        var y = new Array(data.length);
        for(var i = 0; i < data.length; i++){ 
-               y[i]=pv.Scale.linear(data[i], function(d) {return d.y;}).range(10, h-10)
+               y[i]=pv.Scale.linear(data[i], function(d) {return d.y;}).range(20, h-10)
        }
        var interpolate = "linear"; /* cardinal or linear */
        var idx = this.wData[0].size() - 1;
@@ -98,29 +106,57 @@ SonarWidgets.Timeline.prototype.render = function() {
 
        /* The mouseover dots and label in footer. */
        line.add(pv.Dot)
-               .visible(function() {return idx >= 0;})
                .data(function(d) {return [d[idx]];})
                .fillStyle(function() {return line.strokeStyle();})
                .strokeStyle("#000")
                .size(20) 
                .lineWidth(1)
                .add(pv.Dot)
-               .left(150)
+               .left(0)
                .bottom(function() {return 0 - 30 - this.parent.index * 12;})
                .anchor("right").add(pv.Label)
-               .font("12px Arial,Helvetica,sans-serif")
+               .font(footerFont)
                .text(function(d) {return metrics[this.parent.index] + ": " + d.y.toFixed(2);});
        
        /* The date of the selected dot in footer. */
        vis.add(pv.Label)
-               .left(0)
+               .left(w/2)
                .bottom(-36)
-               .font("12px Arial,Helvetica,sans-serif")
-               .text(function() {return translations.date + ": " + snapshots[idx].d});
+               .font(footerFont)
+               .text(function() {return translations.date + ": " + snapshots[idx].d;});
+       
+       /* The event labels */
+       if (events) {
+         eventColor = "rgba(75,159,213,1)";
+         eventHoverColor = "rgba(202,227,242,1)";
+         vis.add(pv.Line)
+               .strokeStyle("rgba(0,0,0,.001)")
+               .data(events)
+               .left(function(e) {return x(e.d);})
+               .bottom(0)
+               .anchor("top")
+               .add(pv.Dot)
+               .bottom(6)
+               .shape("triangle")
+               .strokeStyle("grey")
+               .fillStyle(function(e) {return e.l[0].ld == snapshots[idx].d ? eventHoverColor : eventColor})
+               .add(pv.Dot)
+               .visible(function(e) { return e.l[0].ld == snapshots[idx].d;})
+               .left(w/2+8)
+               .bottom(-42)
+               .shape("triangle")
+               .fillStyle(function(e) {return e.l[0].ld == snapshots[idx].d ? eventHoverColor : eventColor})
+               .strokeStyle("grey")
+               .anchor("right")
+               .add(pv.Label)
+               .font(footerFont)
+               .text(function(e) {return e.l[0].n + ( e.l[1] ? " (... +" + (e.l.size()-1) + ")" : "");});
+       }
        
        /* An invisible bar to capture events (without flickering). */
        vis.add(pv.Bar)
                .fillStyle("rgba(0,0,0,.001)")
+               .width(w+10)
                .event("mouseout", function() {
                        i = -1;
                        return vis;
@@ -129,6 +165,7 @@ SonarWidgets.Timeline.prototype.render = function() {
                        var mx = x.invert(vis.mouse().x);
                        idx = pv.search(data[0].map(function(d) {return d.x;}), mx);
                        idx = idx < 0 ? (-idx - 2) : idx;
+                       idx = idx < 0 ? 0 : idx;
                        return vis;
                });