aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-server/src/main
diff options
context:
space:
mode:
authorFabrice Bellingard <bellingard@gmail.com>2011-09-07 18:12:59 +0200
committerFabrice Bellingard <bellingard@gmail.com>2011-09-07 18:31:24 +0200
commited35b0b18c6e68ed3077999f701d41622377c75e (patch)
tree61bad9deb68752caa2f2271ea729400d0e6e25ec /sonar-server/src/main
parent092ad0bbc8f0cd0ffc200b825cc6c1525150595c (diff)
downloadsonarqube-ed35b0b18c6e68ed3077999f701d41622377c75e.tar.gz
sonarqube-ed35b0b18c6e68ed3077999f701d41622377c75e.zip
SONAR-2200 Provide a new JS API to display a stack area
Using Protovis
Diffstat (limited to 'sonar-server/src/main')
-rwxr-xr-xsonar-server/src/main/webapp/javascripts/protovis-sonar.js194
1 files changed, 185 insertions, 9 deletions
diff --git a/sonar-server/src/main/webapp/javascripts/protovis-sonar.js b/sonar-server/src/main/webapp/javascripts/protovis-sonar.js
index 0aee692325c..6cb1c67fc33 100755
--- a/sonar-server/src/main/webapp/javascripts/protovis-sonar.js
+++ b/sonar-server/src/main/webapp/javascripts/protovis-sonar.js
@@ -1,6 +1,183 @@
window.SonarWidgets = {}
+//******************* STACK AREA CHART ******************* //
+
+SonarWidgets.StackArea = function (divId) {
+ this.wDivId = divId;
+ this.wHeight;
+ this.wData;
+ this.wSnapshots;
+ this.wMetrics;
+ this.wColors;
+ this.height = function(height) {
+ this.wHeight = height;
+ return this;
+ }
+ this.data = function(data) {
+ this.wData = data;
+ return this;
+ }
+ this.snapshots = function(snapshots) {
+ this.wSnapshots = snapshots;
+ return this;
+ }
+ this.metrics = function(metrics) {
+ this.wMetrics = metrics;
+ return this;
+ }
+ this.colors = function(colors) {
+ this.wColors = colors;
+ return this;
+ }
+}
+
+SonarWidgets.StackArea.prototype.render = function() {
+
+ var trendData = this.wData;
+ var metrics = this.wMetrics;
+ var snapshots = this.wSnapshots;
+ var colors = this.wColors;
+
+ var widgetDiv = $(this.wDivId);
+ var headerFont = "10.5px Arial,Helvetica,sans-serif";
+
+ /* Sizing and scales. */
+ var headerHeight = 40;
+ var w = widgetDiv.getOffsetParent().getWidth() - 60;
+ var h = (this.wHeight == null ? 120 : this.wHeight) + headerHeight;
+
+ var x = pv.Scale.linear(pv.blend(pv.map(trendData, function(d) {return d;})), function(d) {return d.x}).range(0, w);
+ var maxY = 0;
+ for (i=0; i<trendData[0].size(); i++) {
+ var currentYSum = 0;
+ for (j=0; j<trendData.size(); j++) {
+ currentYSum += trendData[j][i].y;
+ }
+ if (currentYSum > maxY) { maxY = currentYSum;}
+ }
+ var y = pv.Scale.linear(0, maxY).range(0, h-headerHeight);
+ var idx_numbers = trendData[0].size();
+ var idx = idx_numbers - 1;
+
+ /* The root panel. */
+ var vis = new pv.Panel()
+ .canvas(widgetDiv)
+ .width(w)
+ .height(h)
+ .left(20)
+ .right(20)
+ .bottom(30)
+ .top(20)
+ .strokeStyle("#CCC");
+
+ /* X-axis */
+ vis.add(pv.Rule)
+ .data(x.ticks())
+ .left(x)
+ .bottom(-10)
+ .height(10)
+ .anchor("bottom")
+ .add(pv.Label)
+ .text(x.tickFormat);
+
+ /* Y-axis and ticks. */
+ vis.add(pv.Rule)
+ .data(y.ticks())
+ .bottom(y)
+ .strokeStyle("rgba(128,128,128,.2)")
+ .anchor("left")
+ .add(pv.Label)
+ .text(y.tickFormat);
+
+ /* The stack layout */
+ var area = vis.add(pv.Layout.Stack)
+ .layers(trendData)
+ .x(function(d) {return x(d.x);})
+ .y(function(d) {return y(d.y);})
+ .layer
+ .add(pv.Area)
+ .fillStyle(function() {return colors[this.parent.index % colors.size()][0];})
+ .strokeStyle("rgba(128,128,128,.8)")
+ .add(pv.Dot)
+ .radius(1);
+
+ /* Stack labels. */
+ vis.add(pv.Panel)
+ .extend(area.parent)
+ .add(pv.Area)
+ .extend(area)
+ .fillStyle(null)
+ .strokeStyle(null)
+ .anchor(function() {return idx == 0 ? "left" : (idx == idx_numbers-1 ? "right" : "center");})
+ .add(pv.Label)
+ .visible(function(d) {return this.index == idx && d.y != 0;})
+ .font(function(d) { return Math.round(5 + Math.sqrt(y(d.y))) + "px sans-serif";})
+ .textStyle("#FFF")
+ .text(function(d) {return metrics[this.parent.index] + ": " + d.y;});
+
+ /* The date of the selected dot in the header. */
+ vis.add(pv.Label)
+ .left(8)
+ .top(16)
+ .font(headerFont)
+ .text(function() {return snapshots[idx].ld;});
+
+
+ /* The event labels */
+ if (true) {
+ eventColor = "rgba(75,159,213,1)";
+ eventHoverColor = "rgba(202,227,242,1)";
+ vis.add(pv.Line)
+ .strokeStyle("rgba(0,0,0,.001)")
+ .data(snapshots)
+ .left(function(s) {return x(s.d);})
+ .bottom(0)
+ .anchor("top")
+ .add(pv.Dot)
+ .bottom(-6)
+ .shape("triangle")
+ .angle(3.14159265)
+ .strokeStyle("grey")
+ .visible(function(s) {return s.e.size() > 0;})
+ .fillStyle(function() {return this.index == idx ? eventHoverColor : eventColor;})
+ .add(pv.Dot)
+ .radius(3)
+ .visible(function(s) {return s.e.size() > 0 && this.index == idx;})
+ .left(16)
+ .top(24)
+ .shape("triangle")
+ .fillStyle(function() {return this.index == idx ? eventHoverColor : eventColor;})
+ .strokeStyle("grey")
+ .anchor("right")
+ .add(pv.Label)
+ .font(headerFont)
+ .text(function(s) {return s.e.size() == 0 ? "" : s.e[0] + ( s.e[1] ? " (... +" + (s.e.size()-1) + ")" : "");});
+ }
+
+ /* An invisible bar to capture events (without flickering). */
+ vis.add(pv.Bar)
+ .fillStyle("rgba(0,0,0,.001)")
+ .width(w+30)
+ .height(h+30)
+ .event("mouseout", function() {
+ i = -1;
+ return vis;
+ })
+ .event("mousemove", function() {
+ var mx = x.invert(vis.mouse().x);
+ idx = pv.search(trendData[0].map(function(d) {return d.x;}), mx);
+ idx = idx < 0 ? (-idx - 2) : idx;
+ idx = idx < 0 ? 0 : idx;
+ return vis;
+ });
+
+ vis.render();
+
+}
+
+
+
//******************* TIMELINE CHART ******************* //
/*
* Displays the evolution of metrics on a line chart, displaying related events.
@@ -80,8 +257,7 @@ SonarWidgets.Timeline.prototype.render = function() {
var events = this.wEvents;
var widgetDiv = $(this.wDivId);
- var footerFont = "10.5px Arial,Helvetica,sans-serif";
- var show_y_axis = (trendData.size()==1)
+ var headerFont = "10.5px Arial,Helvetica,sans-serif";
/* Sizing and scales. */
var headerHeight = 4 + Math.max(this.wMetrics.size(), events ? 2 : 1) * 18;
@@ -95,7 +271,7 @@ SonarWidgets.Timeline.prototype.render = function() {
y[i]=pv.Scale.linear(trendData[i], function(d) {return d.y;}).range(20, yMaxHeight);
}
var interpolate = "linear"; /* cardinal or linear */
- var idx = this.wData[0].size() - 1;
+ var idx = trendData[0].size() - 1;
/* The root panel. */
var vis = new pv.Panel()
@@ -142,14 +318,14 @@ SonarWidgets.Timeline.prototype.render = function() {
.left(10)
.top(function() {return 10 + this.parent.index * 14;})
.anchor("right").add(pv.Label)
- .font(footerFont)
+ .font(headerFont)
.text(function(d) {return metrics[this.parent.index] + ": " + d.y.toFixed(2);});
- /* The date of the selected dot in footer. */
+ /* The date of the selected dot in the header. */
vis.add(pv.Label)
.left(w/2)
.top(16)
- .font(footerFont)
+ .font(headerFont)
.text(function() {return snapshots[idx].d;});
/* The event labels */
@@ -166,18 +342,18 @@ SonarWidgets.Timeline.prototype.render = function() {
.bottom(6)
.shape("triangle")
.strokeStyle("grey")
- .fillStyle(function(e) {return e.sid == snapshots[idx].sid ? eventHoverColor : eventColor})
+ .fillStyle(function(e) {return e.sid == snapshots[idx].sid ? eventHoverColor : eventColor;})
.add(pv.Dot)
.radius(3)
.visible(function(e) { return e.sid == snapshots[idx].sid;})
.left(w/2+8)
.top(24)
.shape("triangle")
- .fillStyle(function(e) {return e.sid == snapshots[idx].sid ? eventHoverColor : eventColor})
+ .fillStyle(function(e) {return e.sid == snapshots[idx].sid ? eventHoverColor : eventColor;})
.strokeStyle("grey")
.anchor("right")
.add(pv.Label)
- .font(footerFont)
+ .font(headerFont)
.text(function(e) {return e.l[0].n + ( e.l[1] ? " (... +" + (e.l.size()-1) + ")" : "");});
}