summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2014-04-29 22:38:08 +0600
committerStas Vilchik <vilchiks@gmail.com>2014-04-30 11:35:17 +0600
commit9c811fbeea8de168d5927f610cc41af5bfffdc07 (patch)
treeea092327b345f54e5361330bae131a38e047da2a
parent74b529baad50b783069dfd23c68562ac1401ffbb (diff)
downloadsonarqube-9c811fbeea8de168d5927f610cc41af5bfffdc07.tar.gz
sonarqube-9c811fbeea8de168d5927f610cc41af5bfffdc07.zip
Refactor widgets
-rw-r--r--sonar-server/Gruntfile.coffee4
-rw-r--r--sonar-server/karma.conf.js1
-rw-r--r--sonar-server/src/main/coffee/tests/widgets/BaseSpec.coffee47
-rw-r--r--sonar-server/src/main/coffee/widgets/base.coffee50
-rw-r--r--sonar-server/src/main/coffee/widgets/histogram.coffee142
-rw-r--r--sonar-server/src/main/coffee/widgets/word-cloud.coffee184
-rw-r--r--sonar-server/src/main/js/tests/main.js1
-rw-r--r--sonar-server/src/main/js/widgets/histogram.js307
-rw-r--r--sonar-server/src/main/less/style.less19
9 files changed, 332 insertions, 423 deletions
diff --git a/sonar-server/Gruntfile.coffee b/sonar-server/Gruntfile.coffee
index 498263d1a77..3dc59ffafc9 100644
--- a/sonar-server/Gruntfile.coffee
+++ b/sonar-server/Gruntfile.coffee
@@ -65,6 +65,7 @@ module.exports = (grunt) ->
'<%= pkg.assets %>js/third-party/select2.js'
'<%= pkg.assets %>js/third-party/keymaster.js'
'<%= pkg.assets %>js/select2-jquery-ui-fix.js'
+ '<%= pkg.assets %>js/widgets/base.js'
'<%= pkg.assets %>js/widgets/widget.js'
'<%= pkg.assets %>js/widgets/bubble-chart.js'
'<%= pkg.assets %>js/widgets/timeline.js'
@@ -99,6 +100,7 @@ module.exports = (grunt) ->
'<%= pkg.assets %>js/third-party/select2.js'
'<%= pkg.assets %>js/third-party/keymaster.js'
'<%= pkg.assets %>js/select2-jquery-ui-fix.js'
+ '<%= pkg.assets %>js/widgets/base.js'
'<%= pkg.assets %>js/widgets/widget.js'
'<%= pkg.assets %>js/widgets/bubble-chart.js'
'<%= pkg.assets %>js/widgets/timeline.js'
@@ -262,4 +264,4 @@ module.exports = (grunt) ->
'uglify:build',
'requirejs', 'clean:js', 'copy:build', 'copy:requirejs', 'clean:build']
- grunt.registerTask 'test', ['coffee:build', 'handlebars:build', 'copy:js', 'karma:unit']
+ grunt.registerTask 'test', ['coffee:build', 'handlebars:build', 'copy:js', 'concat:dev', 'karma:unit']
diff --git a/sonar-server/karma.conf.js b/sonar-server/karma.conf.js
index 72936a7621d..037950ad1c1 100644
--- a/sonar-server/karma.conf.js
+++ b/sonar-server/karma.conf.js
@@ -29,6 +29,7 @@ module.exports = function(config) {
'require.js',
'translate.js',
'common/inputs.js',
+ 'widgets/base.js',
// libs
{ pattern: 'third-party/**/*.js', included: false },
diff --git a/sonar-server/src/main/coffee/tests/widgets/BaseSpec.coffee b/sonar-server/src/main/coffee/tests/widgets/BaseSpec.coffee
new file mode 100644
index 00000000000..797acf75588
--- /dev/null
+++ b/sonar-server/src/main/coffee/tests/widgets/BaseSpec.coffee
@@ -0,0 +1,47 @@
+$ = jQuery
+
+describe 'base widget suite', ->
+
+ it 'exists', ->
+ expect(window.SonarWidgets).toBeDefined()
+ expect(window.SonarWidgets.BaseWidget).toBeDefined()
+
+
+ it 'adds fields', ->
+ widget = new window.SonarWidgets.BaseWidget()
+ widget.addField 'fieldName', 1
+
+ expect(typeof widget.fieldName).toBe 'function'
+ expect(widget.fieldName()).toBe 1
+
+ expect(widget.fieldName(2)).toBe widget
+ expect(widget.fieldName()).toBe 2
+
+
+ it 'adds metrics', ->
+ widget = new window.SonarWidgets.BaseWidget()
+ widget.addField 'metrics', 'metricA': { name: 'Metric A', someField: 2 }
+ widget.addField 'metricsPriority', ['metricA']
+ widget.addMetric 'myMetric', 0
+
+ expect(widget.myMetric).toBeDefined()
+ expect(widget.myMetric.key).toBe 'metricA'
+ expect(widget.myMetric.name).toBe 'Metric A'
+ expect(widget.myMetric.someField).toBe 2
+ expect(typeof widget.myMetric.value).toBe 'function'
+ expect(typeof widget.myMetric.formattedValue).toBe 'function'
+
+
+ it 'has default properties', ->
+ widget = new window.SonarWidgets.BaseWidget()
+
+ expect(widget.components).toBeDefined()
+ expect(widget.metrics).toBeDefined()
+ expect(widget.metricsPriority).toBeDefined()
+ expect(widget.options).toBeDefined()
+
+
+ it 'created "translate" string', ->
+ widget = new window.SonarWidgets.BaseWidget()
+
+ expect(widget.trans(1, 2)).toBe 'translate(1,2)' \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/widgets/base.coffee b/sonar-server/src/main/coffee/widgets/base.coffee
new file mode 100644
index 00000000000..133c15e464d
--- /dev/null
+++ b/sonar-server/src/main/coffee/widgets/base.coffee
@@ -0,0 +1,50 @@
+window.SonarWidgets ?= {}
+
+class BaseWidget
+ lineHeight: 20
+
+
+ constructor: ->
+ @addField 'components', []
+ @addField 'metrics', []
+ @addField 'metricsPriority', []
+ @addField 'options', []
+ @
+
+
+ addField: (name, defaultValue) ->
+ privateName = "_#{name}"
+ @[privateName] = defaultValue
+ @[name] = (d) -> @param.call @, privateName, d
+ @
+
+
+ param: (name, value) ->
+ return @[name] unless value?
+ @[name] = value
+ @
+
+
+ addMetric: (property, index) ->
+ key = @metricsPriority()[index]
+ @[property] = _.extend @metrics()[key],
+ key: key
+ value: (d) -> d.measures[key]?.val
+ formattedValue: (d) -> d.measures[key]?.fval
+ @
+
+
+ trans: (left, top) ->
+ "translate(#{left},#{top})"
+
+
+ render: (container) ->
+ @update container
+ @
+
+
+ update: ->
+ @
+
+
+window.SonarWidgets.BaseWidget = BaseWidget \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/widgets/histogram.coffee b/sonar-server/src/main/coffee/widgets/histogram.coffee
new file mode 100644
index 00000000000..2dacc0b73bc
--- /dev/null
+++ b/sonar-server/src/main/coffee/widgets/histogram.coffee
@@ -0,0 +1,142 @@
+class Histogram extends window.SonarWidgets.BaseWidget
+ barHeight: 16
+ barFill: '#1f77b4'
+
+ constructor: ->
+ @addField 'width', 0
+ @addField 'height', window.SonarWidgets.Histogram.defaults.height
+ @addField 'margin', window.SonarWidgets.Histogram.defaults.margin
+ @addField 'legendWidth', window.SonarWidgets.Histogram.defaults.legendWidth
+ @addField 'maxResultsReached', false
+ super
+
+
+ isDataValid: ->
+ @components().reduce ((p, c) => p && !! c.measures[@mainMetric.key]), true
+
+
+ truncate: (text, type) ->
+ maxLength = 40
+ switch type
+ when "FIL", "CLA"
+ n = text.length
+ if n > maxLength
+ shortText = text.substr(n - maxLength + 2, n - 1)
+ dotIndex = shortText.indexOf(".")
+ return "..." + shortText.substr(dotIndex + 1)
+ else
+ return text
+ else
+ (if text.length > maxLength then text.substr(0, maxLength - 3) + "..." else text)
+
+
+ render: (container) ->
+ box = d3.select container
+
+ # Configure the main metric
+ @addMetric 'mainMetric', 0
+
+ unless @isDataValid()
+ box.text @options().noMainMetric
+ return
+
+ @width box.property 'offsetWidth'
+
+ # Create skeleton
+ @svg = box.append('svg').classed 'sonar-d3', true
+ @gWrap = @svg.append 'g'
+ @gWrap.attr 'transform', @trans(@margin().left, @margin().top)
+ @plotWrap = @gWrap.append('g').classed 'plot', true
+
+ # Configure scales
+ @x = d3.scale.linear()
+ @y = d3.scale.ordinal()
+
+ @metricLabel = @gWrap.append('text').text @mainMetric.name
+ @metricLabel.attr('dy', '9px').style 'font-size', '12px'
+
+ if @maxResultsReached()
+ @maxResultsReachedLabel = @gWrap.append('text').classed 'max-results-reached-message', true
+ @maxResultsReachedLabel.text @options().maxItemsReachedMessage
+
+ super
+
+
+ update: (container) ->
+ box = d3.select(container)
+
+ @width box.property 'offsetWidth'
+ availableWidth = @width() - @margin().left - @margin().right - @legendWidth()
+ availableHeight = @barHeight * @components().length + @lineHeight
+ totalHeight = availableHeight + @margin().top + @margin().bottom
+ totalHeight += @lineHeight if @maxResultsReached()
+ @height totalHeight
+
+ @svg.attr('width', @width()).attr 'height', @height()
+ @plotWrap.attr 'transform', @trans 0, @lineHeight
+
+ # Configure scales
+ xDomain = d3.extent @components(), (d) => @mainMetric.value d
+ unless @options().relativeScale
+ if @mainMetric.type == 'PERCENT'
+ xDomain = [0, 100]
+ else
+ xDomain[0] = 0
+
+ @x.domain xDomain
+ .range [0, availableWidth]
+
+ @y.domain @components().map (d, i) -> i
+ .rangeRoundBands [0, availableHeight], 0
+
+ # Configure bars
+ @bars = @plotWrap.selectAll('.bar').data @components()
+ @barsEnter = @bars.enter().append 'g'
+ .classed 'bar', true
+ .attr 'transform', (d, i) => @trans 0, i * @barHeight
+ @barsEnter.append 'rect'
+ .style 'fill', @barFill
+ @barsEnter.append 'text'
+ .classed 'legend-text component', true
+ .style 'text-anchor', 'end'
+ .attr 'dy', '-0.35em'
+ .text (d) => @truncate d.longName, d.qualifier
+ .attr 'transform', => @trans @legendWidth() - 10, @barHeight
+ @barsEnter.append 'text'
+ .classed 'legend-text value', true
+ .attr 'dy', '-0.35em'
+ .text (d) => @mainMetric.formattedValue d
+ .attr 'transform', (d) => @trans @legendWidth() + @x(@mainMetric.value d) + 5, @barHeight
+ @bars.selectAll 'rect'
+ .transition()
+ .attr 'x', @legendWidth()
+ .attr 'y', 0
+ .attr 'width', (d) => Math.max 2, @x(@mainMetric.value d)
+ .attr 'height', @barHeight
+ @bars.selectAll '.component'
+ .transition()
+ .attr 'transform', => @trans @legendWidth() - 10, @barHeight
+ @bars.selectAll '.value'
+ .transition()
+ .attr 'transform', (d) => @trans @legendWidth() + @x(@mainMetric.value d) + 5, @barHeight
+ @bars.exit().remove()
+ @bars.on 'click', (d) =>
+ url = @options().baseUrl + encodeURIComponent d.key
+ if d.qualifier == 'CLA' || d.qualifier == 'FIL'
+ url += '?metric=' + encodeURIComponent @mainMetric.key
+ window.location = url
+
+ @metricLabel.attr 'transform', @trans @legendWidth(), 0
+
+ if @maxResultsReached()
+ @maxResultsReachedLabel.attr 'transform', (@trans @legendWidth(), @height() - @margin().bottom - 3)
+
+ super
+
+
+
+window.SonarWidgets.Histogram = Histogram
+window.SonarWidgets.Histogram.defaults =
+ height: 300
+ margin: { top: 4, right: 50, bottom: 4, left: 10 }
+ legendWidth: 220 \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/widgets/word-cloud.coffee b/sonar-server/src/main/coffee/widgets/word-cloud.coffee
index 8b718ef50b7..1e09b55a6d5 100644
--- a/sonar-server/src/main/coffee/widgets/word-cloud.coffee
+++ b/sonar-server/src/main/coffee/widgets/word-cloud.coffee
@@ -1,114 +1,70 @@
-# Some helper functions
-
-# Gets or sets parameter
-param = (name, value) ->
- unless value?
- return this[name]
- else
- this[name] = value
- return @
-
-
-window.SonarWidgets ?= {}
-
-window.SonarWidgets.WordCloud = ->
- @_components = []
- @_metrics = []
- @_metricsPriority = []
- @_width = window.SonarWidgets.WordCloud.defaults.width
- @_height = window.SonarWidgets.WordCloud.defaults.height
- @_margin = window.SonarWidgets.WordCloud.defaults.margin
- @_legendWidth = window.SonarWidgets.WordCloud.defaults.legendWidth
- @_legendMargin = window.SonarWidgets.WordCloud.defaults.legendMargin
- @_detailsWidth = window.SonarWidgets.WordCloud.defaults.detailsWidth
- @_maxResultsReached = false
- @_options = {}
-
- @_lineHeight = 20
-
- # Export global variables
- @metrics = (_) -> param.call(this, '_metrics', _)
- @metricsPriority = (_) -> param.call(this, '_metricsPriority', _)
- @components = (_) -> param.call(this, '_components', _)
- @width = (_) -> param.call(this, '_width', _)
- @height = (_) -> param.call(this, '_height', _)
- @margin = (_) -> param.call(this, '_margin', _)
- @legendWidth = (_) -> param.call(this, '_legendWidth', _)
- @legendMargin = (_) -> param.call(this, '_legendMargin', _)
- @detailsWidth = (_) -> param.call(this, '_detailsWidth', _)
- @maxResultsReached = (_) -> param.call(this, '_maxResultsReached', _)
- @options = (_) -> param.call(this, '_options', _)
- @
-
-
-window.SonarWidgets.WordCloud.prototype.render = (container) ->
- @box = d3.select(container).append('div').classed 'sonar-d3', true
- @box.style 'text-align', 'center'
-
- # Configure metrics
- @colorMetric = @metricsPriority()[0]
- @getColorMetric = (d) => d.measures[@colorMetric]?.val
- @getFColorMetric = (d) => d.measures[@colorMetric]?.fval
- @sizeMetric = @metricsPriority()[1]
- @getSizeMetric = (d) => d.measures[@sizeMetric]?.val
- @getFSizeMetric = (d) => d.measures[@sizeMetric]?.fval
-
- # Configure scales
- @color = d3.scale.linear().domain([0, 100])
- if @metrics()[@colorMetric].direction == 1
- @color.range ['#d62728', '#1f77b4']
- else
- @color.range ['#1f77b4', '#d62728']
-
- sizeDomain = d3.extent @components(), (d) => @getSizeMetric d
- @size = d3.scale.linear().domain(sizeDomain).range([10, 24])
-
- @update container
- @
-
-
-window.SonarWidgets.WordCloud.prototype.update = ->
- # Configure words
- @words = @box.selectAll('a').data @components()
-
- wordsEnter = @words.enter().append('a')
- wordsEnter.classed 'cloud-word', true
- wordsEnter.text (d) -> d.name
- wordsEnter.attr 'href', (d) =>
- url = @options().baseUrl + encodeURIComponent(d.key)
- url += '?metric=' + encodeURIComponent(@colorMetric) if d.qualifier == 'CLA' || d.qualifier == 'FIL'
- url
- wordsEnter.attr 'title', (d) =>
- title = d.longName
- title += " | #{@metrics()[@colorMetric].name}: #{@getFColorMetric d}" if @getColorMetric(d)?
- title += " | #{@metrics()[@sizeMetric].name}: #{@getFSizeMetric d}" if @getSizeMetric(d)?
- title
-
- @words.style 'color', (d) =>
- if @getColorMetric(d)? then @color @getColorMetric d else '#999'
- @words.style 'font-size', (d) => "#{@size @getSizeMetric d}px"
-
- @words.sort (a, b) =>
- if a.name.toLowerCase() > b.name.toLowerCase() then 1 else -1
-
- # Show maxResultsReached message
- if @maxResultsReached()
- @maxResultsReachedLabel.remove() if @maxResultsReachedLabel?
- @maxResultsReachedLabel = @box.append('div').text @options().maxItemsReachedMessage
- @maxResultsReachedLabel.style 'color', '#777'
- @maxResultsReachedLabel.style 'font-size', '12px'
- @maxResultsReachedLabel.style 'text-align', 'center'
- @maxResultsReachedLabel.style 'margin-top', '10px'
-
- @words.exit().remove()
-
-
-
-
-
-window.SonarWidgets.WordCloud.defaults =
- width: 350
- height: 300
- margin: { top: 10, right: 10, bottom: 10, left: 10 }
- legendWidth: 160
- legendMargin: 30 \ No newline at end of file
+class WordCloud extends window.SonarWidgets.BaseWidget
+ colorLow: '#d62728'
+ colorHigh: '#1f77b4'
+ colorUnknown: '#777'
+ sizeLow: 10
+ sizeHigh: 24
+
+
+ constructor: ->
+ @addField 'width', []
+ @addField 'height', []
+ @addField 'maxResultsReached', false
+ super
+
+
+ renderWords: ->
+ words = @wordContainer.selectAll('.cloud-word').data @components()
+
+ wordsEnter = words.enter().append('a').classed 'cloud-word', true
+ wordsEnter.text (d) -> d.name
+ wordsEnter.attr 'href', (d) =>
+ url = @options().baseUrl + encodeURIComponent(d.key)
+ url += '?metric=' + encodeURIComponent(@colorMetric.key) if d.qualifier == 'CLA' || d.qualifier == 'FIL'
+ url
+ wordsEnter.attr 'title', (d) =>
+ title = d.longName
+ title += " | #{@colorMetric.name}: #{@colorMetric.formattedValue d}" if @colorMetric.value(d)?
+ title += " | #{@sizeMetric.name}: #{@sizeMetric.formattedValue d}" if @sizeMetric.value(d)?
+ title
+
+ words.style 'color', (d) =>
+ if @colorMetric.value(d)? then @color @colorMetric.value(d) else @colorUnknown
+ words.style 'font-size', (d) => "#{@size @sizeMetric.value d}px"
+
+ words.sort (a, b) =>
+ if a.name.toLowerCase() > b.name.toLowerCase() then 1 else -1
+
+
+ render: (container) ->
+ box = d3.select(container).append('div')
+ box.classed 'sonar-d3', true
+ box.classed 'cloud-widget', true
+ @wordContainer = box.append 'div'
+
+ # Configure metrics
+ @addMetric 'colorMetric', 0
+ @addMetric 'sizeMetric', 1
+
+ # Configure scales
+ @color = d3.scale.linear().domain([0, 100])
+ if @colorMetric.direction == 1
+ @color.range [@colorLow, @colorHigh]
+ else
+ @color.range [@colorHigh, @colorLow]
+
+ sizeDomain = d3.extent @components(), (d) => @sizeMetric.value d
+ @size = d3.scale.linear().domain(sizeDomain).range [@sizeLow, @sizeHigh]
+
+ # Show maxResultsReached message
+ if @maxResultsReached()
+ maxResultsReachedLabel = box.append('div').text @options().maxItemsReachedMessage
+ maxResultsReachedLabel.classed 'max-results-reached-message', true
+
+ @renderWords()
+
+ super
+
+
+
+window.SonarWidgets.WordCloud = WordCloud \ No newline at end of file
diff --git a/sonar-server/src/main/js/tests/main.js b/sonar-server/src/main/js/tests/main.js
index c012e4777e2..eb95545cb5b 100644
--- a/sonar-server/src/main/js/tests/main.js
+++ b/sonar-server/src/main/js/tests/main.js
@@ -9,7 +9,6 @@ for (var file in window.__karma__.files) {
}
}
-
requirejs.config({
baseUrl: '/base',
diff --git a/sonar-server/src/main/js/widgets/histogram.js b/sonar-server/src/main/js/widgets/histogram.js
deleted file mode 100644
index 3cce1220605..00000000000
--- a/sonar-server/src/main/js/widgets/histogram.js
+++ /dev/null
@@ -1,307 +0,0 @@
-/*global d3:false, _:false */
-/*jshint eqnull:true */
-
-window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets;
-
-(function () {
-
- window.SonarWidgets.Histogram = function () {
- // Set default values
- this._components = [];
- this._metrics = [];
- this._metricsPriority = [];
- this._width = window.SonarWidgets.Histogram.defaults.width;
- this._height = window.SonarWidgets.Histogram.defaults.height;
- this._margin = window.SonarWidgets.Histogram.defaults.margin;
- this._legendWidth = window.SonarWidgets.Histogram.defaults.legendWidth;
- this._maxResultsReached = false;
- this._options = {};
-
- this._lineHeight = 20;
-
-
- // Export global variables
- this.metrics = function (_) {
- return param.call(this, '_metrics', _);
- };
-
- this.metricsPriority = function (_) {
- return param.call(this, '_metricsPriority', _);
- };
-
- this.components = function (_) {
- return param.call(this, '_components', _);
- };
-
- this.width = function (_) {
- return param.call(this, '_width', _);
- };
-
- this.height = function (_) {
- return param.call(this, '_height', _);
- };
-
- this.margin = function (_) {
- return param.call(this, '_margin', _);
- };
-
- this.legendWidth = function (_) {
- return param.call(this, '_legendWidth', _);
- };
-
- this.maxResultsReached = function (_) {
- return param.call(this, '_maxResultsReached', _);
- };
-
- this.options = function (_) {
- return param.call(this, '_options', _);
- };
- };
-
- window.SonarWidgets.Histogram.prototype.render = function (container) {
- var widget = this,
- containerS = container;
-
- container = d3.select(container);
-
- var validData = this.components().reduce(function(p, c) {
- return p && !!c.measures[widget.metricsPriority()[0]];
- }, true);
-
- if (!validData) {
- container.text(this.options().noMainMetric);
- return;
- }
-
-
- this.width(container.property('offsetWidth'));
-
- this.svg = container.append('svg')
- .attr('class', 'sonar-d3');
- this.gWrap = this.svg.append('g');
-
- this.plotWrap = this.gWrap.append('g')
- .classed('plot', true);
-
- this.gWrap
- .attr('transform', trans(this.margin().left, this.margin().top));
-
-
- // Configure metrics
- this.mainMetric = this.metricsPriority()[0];
- this.getMainMetric = function(d) {
- return d.measures[widget.mainMetric].val;
- };
-
-
- // Configure scales
- this.x = d3.scale.linear();
- this.y = d3.scale.ordinal();
-
-
- // Configure truncate function
- this.truncate = function(text, type) {
- var maxLength = 40;
-
- switch (type) {
- case 'FIL':
- case 'CLA':
- var n = text.length;
- if (n > maxLength) {
- var shortText = text.substr(n - maxLength + 2, n - 1),
- dotIndex = shortText.indexOf('.');
- return '...' + shortText.substr(dotIndex + 1);
- } else {
- return text;
- }
- break;
- default:
- return text.length > maxLength ?
- text.substr(0, maxLength - 3) + '...' :
- text;
- }
- };
-
-
- // Configure metric label
- this.metricLabel = this.gWrap.append('text')
- .text(this.metrics()[this.mainMetric].name)
- .attr('dy', '9px')
- .style('font-size', '12px');
-
-
- // Show maxResultsReached message
- if (this.maxResultsReached()) {
- this.maxResultsReachedLabel = this.gWrap.append('text')
- .classed('max-results-reached', true)
- .style('font-size', '12px')
- .style('fill', '#777')
- .text(this.options().maxItemsReachedMessage);
- }
-
-
- // Update widget
- this.update(containerS);
-
- return this;
- };
-
-
-
- window.SonarWidgets.Histogram.prototype.update = function(container) {
- container = d3.select(container);
-
- var widget = this,
- barHeight = 16,
- width = container.property('offsetWidth');
- this.width(width > 100 ? width : 100);
-
-
- // Update available size
- this.availableWidth = this.width() - this.margin().left - this.margin().right - this.legendWidth();
- this.availableHeight = barHeight * this.components().length + this._lineHeight;
- var totalHeight = this.availableHeight + this.margin().top + this.margin().bottom;
- if (this.maxResultsReached()) {
- totalHeight += this._lineHeight;
- }
- this.height(totalHeight);
-
-
- // Update svg canvas
- this.svg
- .attr('width', this.width())
- .attr('height', this.height());
-
-
- // Update plot
- this.plotWrap
- .attr('transform', trans(0, this._lineHeight));
-
-
- // Update scales
- var xDomain = d3.extent(this.components(), function(d) {
- return widget.getMainMetric(d);
- });
-
- if (!this.options().relativeScale) {
- if (this.metrics()[this.mainMetric].type === 'PERCENT') {
- xDomain = [0, 100];
- } else {
- xDomain[0] = 0;
- }
- }
-
- this.x
- .domain(xDomain)
- .range([0, this.availableWidth]);
-
- this.y
- .domain(this.components().map(function(d, i) { return i; }))
- .rangeRoundBands([0, this.availableHeight], 0);
-
-
- // Configure bars
- this.bars = this.plotWrap.selectAll('.bar')
- .data(this.components());
-
- this.barsEnter = this.bars.enter()
- .append('g')
- .classed('bar', true)
- .attr('transform', function(d, i) { return trans(0, i * barHeight); });
-
- this.barsEnter
- .append('rect')
- .style('fill', '#1f77b4');
-
- this.barsEnter
- .append('text')
- .classed('legend-text component', true)
- .style('text-anchor', 'end')
- .attr('dy', '-0.35em')
- .text(function(d) { return widget.truncate(d.longName, d.qualifier); })
- .attr('transform', function() { return trans(widget.legendWidth() - 10, barHeight); });
-
- this.barsEnter
- .append('text')
- .classed('legend-text value', true)
- .attr('dy', '-0.35em')
- .text(function(d) { return d.measures[widget.mainMetric].fval; })
- .attr('transform', function(d) {
- return trans(widget.legendWidth() + widget.x(widget.getMainMetric(d)) + 5, barHeight);
- });
-
- this.bars.selectAll('rect')
- .transition()
- .attr('x', this.legendWidth())
- .attr('y', 0)
- .attr('width', function(d) { return Math.max(2, widget.x(widget.getMainMetric(d))); })
- .attr('height', barHeight);
-
- this.bars.selectAll('.component')
- .transition()
- .attr('transform', function() { return trans(widget.legendWidth() - 10, barHeight); });
-
- this.bars.selectAll('.value')
- .transition()
- .attr('transform', function(d) {
- return trans(widget.legendWidth() + widget.x(widget.getMainMetric(d)) + 5, barHeight);
- });
-
- this.bars
- .exit().remove();
-
- this.bars
- .on('click', function(d) {
- switch (d.qualifier) {
- case 'CLA':
- case 'FIL':
- window.location = widget.options().baseUrl + encodeURIComponent(d.key) +
- '?metric=' + encodeURIComponent(widget.mainMetric);
- break;
- default:
- window.location = widget.options().baseUrl + encodeURIComponent(d.key);
- }
- });
-
-
- // Configure metric label
- this.metricLabel
- .attr('transform', trans(this.legendWidth(), 0));
-
-
- // Show maxResultsReached message
- if (this.maxResultsReached()) {
- this.maxResultsReachedLabel
- .attr('transform', trans(this.legendWidth(), this.height() - this.margin().bottom - 3));
- }
- };
-
-
-
- window.SonarWidgets.Histogram.defaults = {
- width: 350,
- height: 300,
- margin: { top: 4, right: 50, bottom: 4, left: 10 },
- legendWidth: 220
- };
-
-
-
- // Some helper functions
-
- // Gets or sets parameter
- function param(name, value) {
- if (value == null) {
- return this[name];
- } else {
- this[name] = value;
- return this;
- }
- }
-
- // Helper for create the translate(x, y) string
- function trans(left, top) {
- return 'translate(' + left + ', ' + top + ')';
- }
-
-})();
diff --git a/sonar-server/src/main/less/style.less b/sonar-server/src/main/less/style.less
index 5cdb6e32113..e6309af27db 100644
--- a/sonar-server/src/main/less/style.less
+++ b/sonar-server/src/main/less/style.less
@@ -2743,6 +2743,10 @@ div.rule-title {
transition: all 0.3s ease;
}
+.sonar-d3.cloud-widget {
+ text-align: center;
+}
+
.sonar-d3 .cloud-word {
display: inline-block;
vertical-align: baseline;
@@ -2753,6 +2757,21 @@ div.rule-title {
&:hover { text-decoration: underline; }
}
+.sonar-d3 .max-results-reached-message {
+ font-size: 12px;
+}
+
+.sonar-d3 div.max-results-reached-message {
+ margin-top: 10px;
+ color: #777;
+ text-align: center;
+}
+
+.sonar-d3 text.max-results-reached-message {
+ fill: #777;
+}
+
+
/* ------------------- Admin pages ------------------- */
.admin-page-title {