]> source.dussan.org Git - sonarqube.git/commitdiff
Refactor widgets
authorStas Vilchik <vilchiks@gmail.com>
Tue, 29 Apr 2014 16:38:08 +0000 (22:38 +0600)
committerStas Vilchik <vilchiks@gmail.com>
Wed, 30 Apr 2014 05:35:17 +0000 (11:35 +0600)
sonar-server/Gruntfile.coffee
sonar-server/karma.conf.js
sonar-server/src/main/coffee/tests/widgets/BaseSpec.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/widgets/base.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/widgets/histogram.coffee [new file with mode: 0644]
sonar-server/src/main/coffee/widgets/word-cloud.coffee
sonar-server/src/main/js/tests/main.js
sonar-server/src/main/js/widgets/histogram.js [deleted file]
sonar-server/src/main/less/style.less

index 498263d1a7734f9bd2e0c578203210125b40cd61..3dc59ffafc98d369f161ba2ac206de20cd4e61ed 100644 (file)
@@ -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']
index 72936a7621dc9ba8bdd886a00b68fbff84d052e4..037950ad1c13ed043b98779f94592adb22e1d5a4 100644 (file)
@@ -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 (file)
index 0000000..797acf7
--- /dev/null
@@ -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 (file)
index 0000000..133c15e
--- /dev/null
@@ -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 (file)
index 0000000..2dacc0b
--- /dev/null
@@ -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
index 8b718ef50b74fcd6d39f3510851e702aefb5b77f..1e09b55a6d5c925d695a6a2b73901b89e3126403 100644 (file)
-# 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
index c012e4777e26d9ecf3222940b7e3681243b8f595..eb95545cb5b2e4949fb57b3f86cdf6085923a634 100644 (file)
@@ -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 (file)
index 3cce122..0000000
+++ /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 + ')';
-  }
-
-})();
index 5cdb6e32113fcc75da78058224c6cd72ec6c4d6c..e6309af27db87c4cfe3e5ca73c4a4ecb1b3798ac 100644 (file)
@@ -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 {