From: Fabrice Bellingard Date: Wed, 7 Sep 2011 16:12:59 +0000 (+0200) Subject: SONAR-2200 Provide a new JS API to display a stack area X-Git-Tag: 2.11^2~76 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=ed35b0b18c6e68ed3077999f701d41622377c75e;p=sonarqube.git SONAR-2200 Provide a new JS API to display a stack area Using Protovis --- 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 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) + ")" : "");}); }